/* ==========================================================================
   Mirabelle — main.css
   Imports tokens.css first. No hard-coded hex, px font size, or duration
   below this line — everything routes through the tokens.
   ========================================================================== */

@import url("tokens.css");

/* --- Fonts, self-hosted -------------------------------------------------- */

@font-face {
  font-family: "Prata";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/prata-400.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../assets/fonts/cormorant-garamond-300.woff2") format("woff2");
}
@font-face {
  font-family: "Jost";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../assets/fonts/jost-300.woff2") format("woff2");
}
@font-face {
  font-family: "Jost";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/jost-400.woff2") format("woff2");
}

/* --- Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-body);
  font-weight: var(--fw-light);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);
}
img, picture { max-width: 100%; display: block; }
a { color: inherit; }
h1, h2, h3, p, figure { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* Visible focus states everywhere, per studio accessibility baseline. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 2px solid var(--c-olive);
  outline-offset: 3px;
}

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

.skip-link {
  position: absolute;
  top: -100%;
  left: var(--sp-md);
  background: var(--c-ink);
  color: var(--c-white);
  padding: var(--sp-sm) var(--sp-lg);
  z-index: 200;
  font-family: var(--font-body);
  font-size: var(--fs-small);
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--sp-md); }

/* --- Typography ------------------------------------------------------------ */

.display {
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: var(--fs-display);
  letter-spacing: var(--ls-display);
  line-height: var(--lh-display);
}
h1, .h1 {
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: var(--fs-h1);
  letter-spacing: var(--ls-h1);
  line-height: var(--lh-h1);
}
h2, .h2 {
  font-family: var(--font-subhead);
  font-weight: var(--fw-light);
  font-size: var(--fs-h2);
  letter-spacing: var(--ls-h2);
  line-height: var(--lh-h2);
}
.eyebrow {
  display: block;
  font-family: var(--font-subhead);
  font-weight: var(--fw-light);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--ls-eyebrow);
  line-height: var(--lh-eyebrow);
  text-transform: uppercase;
  padding-bottom: var(--sp-sm);
  border-bottom: 1px solid var(--border-hairline);
  margin-bottom: var(--sp-lg);
}
.eyebrow--reversed { border-bottom-color: rgba(255,255,255,0.4); }
/* Bigger section-title reading of the same tracked-caps device, per
   Jason's adjustment request ("The Range" homepage label read too small
   against the hero eyebrow). Reuses --fs-h2 rather than a new size. */
.eyebrow--large { font-size: var(--fs-h2); }
.small { font-size: var(--fs-small); letter-spacing: var(--ls-small); line-height: var(--lh-small); }
.label {
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  line-height: var(--lh-label);
  text-transform: uppercase;
}
p { max-width: var(--measure); }
p + p { margin-top: var(--sp-lg); }
/* A heading directly above body copy had no gap at all in most contexts
   (only .prose-block, .simple-hero and .product-detail had a rule for
   it) — read as too tight sitewide per Jason's request. One rule, applied
   wherever a paragraph immediately follows a heading, rather than
   patching each component. Jason asked for "maybe 20px"; used --sp-lg
   (24px) instead of a new one-off value, since that is already the gap
   the three existing contexts above use. */
h1 + p, .h1 + p, .display + p,
h2 + p, .h2 + p { margin-top: var(--sp-lg); }

.text-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-sm);
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 2px;
  transition: opacity var(--dur) var(--ease);
}
.text-link:hover { opacity: 0.65; }

.button {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--c-white);
  background: var(--c-olive);
  padding: var(--sp-md) var(--sp-xl);
  border-radius: var(--radius);
  transition: background var(--dur) var(--ease);
}
.button:hover { background: var(--c-ink); }

/* --- Layout ----------------------------------------------------------------- */

.container {
  max-width: var(--measure-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  position: relative;
  padding-block: var(--section-y);
}
.section--tight { padding-block: var(--sp-3xl); }

.section--surface       { background: var(--surface); }
.section--surface-raised{ background: var(--surface-raised); }
.section--surface-accent{ background: var(--surface-accent); }
.section--surface-secondary { background: var(--surface-secondary); color: var(--text); }
.section--surface-primary   { background: var(--surface-primary); color: var(--text); }
.section--surface-deep  { background: var(--surface-deep); color: var(--text-on-deep); }

/* Grain: every flat brand-colour field. Photographs are exempt. */
.section--grain { isolation: isolate; }
.section--grain::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../assets/texture/grain.png");
  background-repeat: repeat;
  background-size: 128px 128px;
  opacity: var(--grain-opacity);
  mix-blend-mode: overlay;
  pointer-events: none;
  z-index: 0;
}
.section--grain > * { position: relative; z-index: 1; }

.measure { max-width: var(--measure); }

.editorial-row {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-lg);
  padding-block: var(--sp-md);
  border-bottom: 1px solid var(--border-hairline);
}
.editorial-row:first-child { border-top: 1px solid var(--border-hairline); }

/* --- Reveal (opt-out motion, resolves through tokens) ----------------------- */

.reveal {
  opacity: 0;
  transform: translateY(var(--sp-lg));
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* --- Header / nav ------------------------------------------------------------ */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--surface);
  border-bottom: 1px solid var(--border-hairline);
}
.site-header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--sp-md);
}
.site-header__mark {
  display: block;
  width: 44px;
  height: auto;
}
.site-header__mark img { display: block; width: 100%; height: auto; }

.primary-nav { display: none; }
@media (min-width: 900px) {
  .primary-nav {
    display: flex;
    align-items: center;
    gap: var(--sp-xl);
  }
  .primary-nav a {
    font-family: var(--font-body);
    font-weight: var(--fw-regular);
    font-size: var(--fs-label);
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
    text-decoration: none;
  }
  .primary-nav a:not(.button):hover { opacity: 0.6; }
}

.menu-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-sm);
  font-family: var(--font-body);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}
@media (min-width: 900px) {
  .menu-toggle { display: none; }
}
.menu-toggle__bars {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 22px;
}
.menu-toggle__bars span {
  display: block;
  height: 1px;
  background: currentColor;
}

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  padding: var(--sp-lg) var(--gutter) var(--sp-xl);
  transform: translateY(-100%);
  transition: transform var(--dur-slow) var(--ease);
}
.mobile-menu[data-open="true"] { transform: translateY(0); }
.mobile-menu__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.mobile-menu__close {
  font-family: var(--font-body);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}
.mobile-menu__links {
  margin-top: var(--sp-3xl);
  display: flex;
  flex-direction: column;
  gap: var(--sp-lg);
}
.mobile-menu__links a {
  font-family: var(--font-subhead);
  font-weight: var(--fw-light);
  font-size: var(--fs-h2);
  letter-spacing: var(--ls-h2);
  text-decoration: none;
}

/* --- Hero (archetype A2: full-bleed photograph, reserved region) ----------- */

.hero {
  position: relative;
  overflow: hidden;
  min-height: 92vh;
  display: flex;
  align-items: flex-start;
}
.hero__media {
  position: absolute;
  inset: -6% -6% -6% -6%; /* headroom for parallax translate without exposing edges */
  z-index: 0;
}
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1);
  animation: kenburns var(--kenburns-duration) var(--ease) forwards;
  animation-iteration-count: 1;
}
/* Rotating hero background (Home only, per Jason's adjustment request):
   several frames crossfading in place, rather than one static/Ken-Burns
   image. Each slide is absolutely stacked; JS toggles .is-active on a
   timer. Slides don't carry the kenburns scale (the crossfade itself is
   the motion here), only the currently active one needs to be visible to
   assistive tech. .hero__media is already `position: absolute`, which is
   a valid containing block for these `position: absolute` slides on its
   own — no modifier needed here. (An earlier `position: relative`
   override on this class was a real bug: for a relatively positioned box,
   `inset` only offsets it rather than sizing it like it does under
   absolute, so with no explicit width/height and every child pulled out
   of flow, the container collapsed to zero height and the whole hero
   background went invisible.) */
.hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--dur-crossfade) var(--ease);
}
.hero__slide.is-active { opacity: 1; z-index: 1; }
.hero__slide picture,
.hero__slide img { width: 100%; height: 100%; object-fit: cover; animation: none; }

@keyframes kenburns {
  from { transform: scale(1); }
  to   { transform: scale(var(--kenburns-scale)); }
}
.hero__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
}
.hero__content {
  position: relative;
  z-index: 2;
  /* --edge-inset sits in margin, not padding: box-sizing is border-box
     site-wide (see the reset near the top of this file), so max-width
     includes padding — putting a several-hundred-px --edge-inset in
     padding on a very wide screen was eating straight into the 560px
     budget and crushing the readable column down to almost nothing (a
     real bug from when --edge-inset was first added, not just a scrim
     issue). Margin sits outside the box model, so it moves the box
     without touching how much width is left for the text itself. */
  margin-left: var(--edge-inset);
  padding: var(--sp-3xl) var(--gutter) var(--sp-2xl);
  max-width: 560px;
  color: var(--c-white);
}
/* Reserved region on the right third rather than the left. Narrower than
   the default hero column: a right third is tighter than a left half, and
   the box needs to stay clear of the product itself, not just reach the
   right edge. Swaps which side gets --edge-inset (as margin, same
   reasoning as above), since this variant's true edge is now the right. */
.hero__content--right {
  margin-left: auto;
  margin-right: var(--edge-inset);
  max-width: 420px;
}

/* Scoped scrim behind the reserved-region text itself, rather than a
   viewport-wide fade. A directional ink gradient (still within the 45%
   ceiling) that plateaus across the text column and only tapers at its own
   edges, instead of thinning out across the whole hero as a viewport-wide
   left-fade would. See NOTES.md section 7/8: the manifest's stated 25%
   viewport-wide gradient measured at 1.95-2.5:1 against the actual
   rendered pixels; this holds 4.5:1+ throughout. */
/* The scrim box's own extension used to be a percentage of
   .hero__content's width (-8%) — but that box is capped at max-width:
   560px, so the extension never grew past ~45px regardless of screen
   size, and on a wide screen the scrim just stopped a short way past the
   text with a visible hard edge against the photo behind it. Jason's
   request: extend it further toward page centre, at least ~100px of
   breathing room past the text on larger screens. Replaced the
   percentage with a flat 220px so the buffer is real and doesn't shrink
   back down to nothing on a capped-width box; the gradient's own plateau
   now ends around 70% of that enlarged box (comfortably past the text
   edge) rather than 92% of the old, much narrower one, so the fade
   itself is longer and softer too, not just pushed further out. */
.hero__content--scrim::before {
  content: "";
  position: absolute;
  top: -6%;
  bottom: -6%;
  left: -8%;
  right: -220px;
  z-index: -1;
  background: linear-gradient(
    100deg,
    rgba(47, 44, 38, 0.45) 0%,
    rgba(47, 44, 38, 0.45) 70%,
    rgba(47, 44, 38, 0) 100%
  );
}
/* Mirror of --scrim for a right-third reserved region: strongest at the
   scrim box's right edge, tapering left. Same ceiling, same mechanism,
   same fixed-extension reasoning as above but mirrored to the left. */
.hero__content--scrim-right::before {
  content: "";
  position: absolute;
  top: -6%;
  bottom: -6%;
  left: -220px;
  right: -8%;
  z-index: -1;
  background: linear-gradient(
    260deg,
    rgba(47, 44, 38, 0.45) 0%,
    rgba(47, 44, 38, 0.45) 70%,
    rgba(47, 44, 38, 0) 100%
  );
}
/* Mirror of --scrim for a lower-third reserved region: strongest toward
   the bottom of the scrim box, tapering upward. Same ceiling, same
   mechanism, rotated 90 degrees. */
.hero__content--scrim-bottom::before {
  content: "";
  position: absolute;
  inset: -8% -6%;
  z-index: -1;
  background: linear-gradient(
    10deg,
    rgba(47, 44, 38, 0.45) 0%,
    rgba(47, 44, 38, 0.45) 92%,
    rgba(47, 44, 38, 0) 100%
  );
}
.hero__content .eyebrow { border-bottom-color: rgba(255,255,255,0.4); }
.hero__content .display { color: var(--c-white); }
.hero__content p { color: var(--c-white); }

/* --- Two-column intro / closing (A1 variant: colour field, generous air) --- */

.prose-block { max-width: var(--measure); }
.prose-block .h2 { margin-bottom: var(--sp-lg); }

/* --- Split feature (A3: split field, photo against colour) ----------------
   New in rev 2 for Product in use / Membership tease / Sourcing tease.
   50/50 per the knowledge file's split ratios (never a thin strip). Image
   panel and text panel never bridge the join. */

.split-feature__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-xl);
  align-items: center;
}
@media (min-width: 900px) {
  .split-feature__grid {
    grid-template-columns: 1fr 1fr;
    gap: var(--gutter);
  }
  /* Default order: text first (left), media second (right). */
  .split-feature--reverse .split-feature__text { order: 2; }
  .split-feature--reverse .split-feature__media { order: 1; }
}
.split-feature__text { max-width: var(--measure); }
.split-feature__media { overflow: hidden; }

/* --- Sourcing grid (A5 variant): true edge-to-edge grid ---------------------
   Built against a reference Jason supplied. Distinct from .split-feature
   above: no .container, no gutter between the two columns, no gap between
   stacked rows — the image cells bleed to the viewport edge and into
   their neighbours, only the text cells carry their own padding. Eight
   cells (four text, four image) placed by plain grid auto-flow in
   row-major order, alternating which side the text sits on — see
   sourcing.html for why the markup order has to stay alternating for
   this to work without explicit grid-column assignments. */
.sourcing-grid {
  display: grid;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .sourcing-grid { grid-template-columns: 1fr 1fr; }
}
.sourcing-grid__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--sp-3xl) var(--gutter);
}
/* Every --pale cell sits in the left column, every --white cell in the
   right (see sourcing.html's markup order) — whichever side is the true
   page edge gets --edge-inset instead of --gutter, same reasoning as the
   hero content boxes. The inward edge, facing the paired image at
   roughly page-centre, stays at the ordinary --gutter regardless of
   viewport width, since that side was never the "off-balance" edge. */
.sourcing-grid__text--pale { background: var(--surface-secondary); color: var(--text); padding-left: var(--edge-inset); }
.sourcing-grid__text--white { background: var(--surface-raised); padding-right: var(--edge-inset); }
.sourcing-grid__media { overflow: hidden; aspect-ratio: 4 / 3; }
.sourcing-grid__media picture,
.sourcing-grid__media img { width: 100%; height: 100%; display: block; object-fit: cover; }
@media (min-width: 900px) {
  /* Above the two-column breakpoint the media cell's aspect-ratio sets a
     tall enough row that the default grid stretch (unset here on
     purpose) makes the paired text cell match it automatically — no
     fixed height needed on either side. */
  .sourcing-grid__media { aspect-ratio: auto; }
}
.split-feature__media picture,
.split-feature__media img { width: 100%; height: 100%; display: block; object-fit: cover; }

/* --- Feature parallax (About, "How we make it"; A2 variant) ---------------
   Large background photograph with the section's own text overlaid, and a
   deeper parallax drift than the hero/pair pairing (--parallax-feature,
   25%, added for this section per Jason's adjustment request). Generous
   inset gives the drift room without exposing edges on a normally-sized
   source image. */

.feature-parallax {
  position: relative;
  overflow: hidden;
  min-height: 70vh;
  display: flex;
  align-items: center;
}
.feature-parallax__media { position: absolute; inset: -20% -10%; z-index: 0; }
.feature-parallax__media img { width: 100%; height: 100%; object-fit: cover; }
.feature-parallax__overlay { position: absolute; inset: 0; z-index: 1; }
.feature-parallax__content {
  position: relative;
  z-index: 2;
  /* --edge-inset in margin, not padding — same border-box/max-width
     reasoning as .hero__content above: padding would have eaten into
     the 560px budget instead of just moving the box. */
  margin-left: var(--edge-inset);
  padding: var(--sp-3xl) var(--gutter);
  max-width: 560px;
  color: var(--c-white);
}
.feature-parallax__content .h2 { color: var(--c-white); }
.feature-parallax__content p { color: var(--c-white); }
/* Right-aligned variant: used when a second feature-parallax section
   follows the first directly (About: "How we make it" then this one), so
   the two full-bleed beats read as a considered pair rather than a
   repeated block. Content moves to the right; pair with a mirrored
   overlay gradient (dark on the right, per section). Swaps which side
   gets --edge-inset (as margin, same reasoning as above). */
.feature-parallax--right { justify-content: flex-end; }
.feature-parallax--right .feature-parallax__content {
  margin-left: 0;
  margin-right: var(--edge-inset);
}

/* --- Pull statement (A2 variant: full-bleed image, centred lockup text) ---- */

.pull-statement {
  position: relative;
  overflow: hidden;
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.pull-statement__media { position: absolute; inset: -6%; z-index: 0; }
.pull-statement__media picture { height: 100%; }
.pull-statement__media img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1);
  animation: kenburns var(--kenburns-duration) var(--ease) forwards;
  animation-iteration-count: 1;
}
.pull-statement__overlay { position: absolute; inset: 0; z-index: 1; }
.pull-statement__content {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-inline: var(--gutter);
}
.pull-statement__eyebrow {
  color: var(--c-white);
  font-family: var(--font-subhead);
  font-weight: var(--fw-light);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--ls-eyebrow);
  line-height: var(--lh-eyebrow);
  text-transform: uppercase;
}
.pull-statement__keyline {
  width: 48px;
  height: 1px;
  background: var(--c-pale-sage);
  border: 0;
  margin: var(--sp-lg) 0;
}
.pull-statement__text {
  color: var(--c-white);
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: var(--fs-display);
  letter-spacing: var(--ls-display);
  line-height: var(--lh-display);
  max-width: 16ch;
}

/* --- Membership tease / closing columns ------------------------------------ */

.split-cta {
  display: flex;
  flex-direction: column;
  gap: var(--sp-xl);
  align-items: flex-start;
}
@media (min-width: 700px) {
  .split-cta { flex-direction: row; align-items: flex-end; justify-content: space-between; }
}

/* --- Footer ------------------------------------------------------------------ */

.site-footer {
  position: relative;
  overflow: hidden;
  background: var(--surface-secondary);
  color: var(--text);
}
.site-footer__inner {
  position: relative;
  z-index: 1;
  padding-block: var(--sp-3xl) var(--sp-xl);
}
.site-footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-2xl);
}
@media (min-width: 700px) {
  .site-footer__grid { grid-template-columns: repeat(3, 1fr); }
}
.site-footer__grid .label { margin-bottom: var(--sp-md); color: var(--c-ink); opacity: 0.6; }
.site-footer__grid ul { display: flex; flex-direction: column; gap: var(--sp-sm); }
.site-footer__grid a { text-decoration: none; font-size: var(--fs-small); }
.site-footer__grid a:hover { opacity: 0.7; }
.site-footer__legal {
  margin-top: var(--sp-3xl);
  padding-top: var(--sp-lg);
  border-top: 1px solid var(--border-hairline);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-md);
  justify-content: space-between;
  font-size: var(--fs-small);
}

/* The oversized wordmark texture device. Decorative only: aria-hidden,
   never a heading, kept clear of anything that has to be read. Uses the
   supplied artwork (pale-grey ink, transparent) rather than live type,
   per the knowledge-file rule against ever typesetting the brand name in
   a substitute face. See NOTES.md section 5 for the conflict this
   resolves. */
.site-footer__wordmark {
  position: absolute;
  left: 50%;
  bottom: -6%;
  transform: translateX(-50%);
  width: min(140%, 1600px);
  z-index: 0;
  opacity: 0.11; /* was 0.9, then 0.22 (~25%); halved again to ~12.5% of
                     the original per Jason's follow-up request, "barely
                     perceptible" */
  pointer-events: none;
}
.site-footer__wordmark img { width: 100%; height: auto; display: block; }

/* --- Simple hero, no image (interior page title bands) --------------------- */

.simple-hero { padding-top: var(--sp-4xl); }
.simple-hero .display { margin-top: var(--sp-md); }
.simple-hero p { margin-top: var(--sp-lg); }

/* --- Category section (The Range) ------------------------------------------- */

.category-section { padding-block: var(--sp-2xl); border-top: 1px solid var(--border-hairline); }
.category-section:first-of-type { border-top: none; }
.category-section__head { max-width: var(--measure); margin-bottom: var(--sp-xl); }

.product-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gutter);
}
@media (min-width: 700px) {
  .product-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1100px) {
  .product-grid { grid-template-columns: repeat(3, 1fr); }
}
.product-card { display: block; text-decoration: none; }
.product-card__media { aspect-ratio: 1 / 1; overflow: hidden; margin-bottom: var(--sp-md); }
.product-card__media img { width: 100%; height: 100%; object-fit: cover; }
.product-card__media--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--c-pale-sage);
}
.product-card__name { font-family: var(--font-subhead); font-weight: var(--fw-light); font-size: var(--fs-h2); letter-spacing: var(--ls-h2); }
.product-card__desc { margin-top: var(--sp-xs); }
.product-card__meta { margin-top: var(--sp-sm); font-size: var(--fs-small); letter-spacing: var(--ls-small); }

/* --- Product page frame ------------------------------------------------------ */

.product-frame {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-xl);
  align-items: start;
}
@media (min-width: 900px) {
  .product-frame { grid-template-columns: 1fr 1fr; gap: var(--gutter); }
}
.product-frame__media { aspect-ratio: 1 / 1; overflow: hidden; }
.product-frame__media img { width: 100%; height: 100%; object-fit: cover; }
.product-frame__info .eyebrow { margin-bottom: var(--sp-md); }
.product-frame__meta { margin-top: var(--sp-md); font-size: var(--fs-small); }
.product-frame__actions { margin-top: var(--sp-xl); display: flex; gap: var(--sp-lg); flex-wrap: wrap; }

.product-detail { padding-block: var(--sp-2xl); border-top: 1px solid var(--border-hairline); }
.product-detail:first-of-type { border-top: none; }
.product-detail h2 { margin-bottom: var(--sp-md); }

/* --- Three-plan comparison (Membership; density exception, knowledge file 7.1) */

.plan-section { position: relative; overflow: hidden; }
.plan-ground { position: absolute; inset: 0; z-index: 0; opacity: 0.10; }
.plan-ground img { width: 100%; height: 100%; object-fit: cover; }
.plan-section > .container { position: relative; z-index: 1; }

.plan-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-xl);
}
@media (min-width: 800px) {
  .plan-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 0;
  }
  .plan-grid > * { padding-inline: var(--sp-lg); border-left: 1px solid var(--border-hairline); }
  .plan-grid > *:first-child { padding-left: 0; border-left: none; }
  .plan-grid > *:last-child { padding-right: 0; }
}
.plan { display: flex; flex-direction: column; }
.plan__name { font-family: var(--font-subhead); font-weight: var(--fw-light); font-size: var(--fs-h2); letter-spacing: var(--ls-h2); }
.plan__body { margin-top: var(--sp-md); }
.plan__includes { margin-top: var(--sp-lg); display: flex; flex-direction: column; gap: var(--sp-xs); font-size: var(--fs-small); }
.plan__price { margin-top: var(--sp-lg); font-size: var(--fs-small); letter-spacing: var(--ls-small); }

/* --- Bulleted list (What arrives, anti-pattern free: type only, no icons) --- */

.plain-list { display: flex; flex-direction: column; gap: var(--sp-md); max-width: var(--measure); }
.plain-list li { padding-left: var(--sp-lg); position: relative; }
.plain-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.65em;
  width: 6px;
  height: 1px;
  background: var(--c-ink);
}

/* --- Four-step sequence, oversized faint numerals (Membership only) -------- */

.step-sequence {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-2xl);
  margin-top: var(--sp-3xl); /* was sp-xl; the keyline above read as
                                almost touching the numerals */
}
@media (min-width: 700px) {
  .step-sequence { grid-template-columns: repeat(2, 1fr); }
}
.step { position: relative; padding-top: var(--sp-lg); }
.step__num {
  position: absolute;
  top: -0.3em;
  left: -0.1em;
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: clamp(4rem, 8vw, 7rem);
  line-height: 1;
  color: var(--c-pale-sage);
  z-index: 0;
  user-select: none;
}
.step__body { position: relative; z-index: 1; padding-top: var(--sp-2xl); }
.step__body h3 { font-family: var(--font-body); font-weight: var(--fw-regular); font-size: var(--fs-body); margin-bottom: var(--sp-xs); }

/* --- Journal index grid ------------------------------------------------------ */

.journal-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gutter);
}
@media (min-width: 700px) {
  .journal-grid { grid-template-columns: repeat(3, 1fr); }
}
/* Adjustment (2026-08, second pass): reworked again against a reference
   Jason supplied. The whole card now lifts slightly on hover/focus as one
   unit (image, text, all of it), rather than the image zooming and the
   title fading independently — a single, quieter motion instead of two
   competing ones. Resolves through --dur-slow, so prefers-reduced-motion
   zeroes it out like every other effect on the site. */
.journal-card {
  display: block;
  text-decoration: none;
  transform: translateY(0);
  transition: transform var(--dur-slow) var(--ease);
}
a.journal-card:hover,
a.journal-card:focus-visible {
  transform: translateY(-6px);
}
.journal-card__media { aspect-ratio: 3 / 2; overflow: hidden; margin-bottom: var(--sp-md); background: var(--c-pale-sage); }
.journal-card__media img { width: 100%; height: 100%; object-fit: cover; }
.journal-card__date { display: block; font-size: var(--fs-small); letter-spacing: var(--ls-small); opacity: 0.7; margin-bottom: var(--sp-xs); }
/* Category kicker reuses the site's own .eyebrow device (small caps,
   wide tracking, its own bottom keyline baked in) rather than a
   bespoke one, so the "keyline under a small label" Jason asked for
   comes for free and matches every other eyebrow on the site. */
.journal-card .eyebrow { margin-bottom: var(--sp-md); }
/* Title back in the display serif (was moved to the body sans in the
   first pass at this — see NOTES.md R6/R7), but with normal tracking
   rather than the wide --ls-h2 used on real page headings, and sitting
   under a category label this time rather than alone: together that
   reads as an article title, not a duplicate of an H1. */
.journal-card__title {
  font-family: var(--font-subhead);
  font-weight: var(--fw-light);
  font-size: var(--fs-h2);
  letter-spacing: 0;
  line-height: 1.25;
}
.journal-card__standfirst { max-width: var(--measure); }
.journal-card__cta { margin-top: var(--sp-lg); }

/* --- Journal article template ------------------------------------------------ */

.article { max-width: var(--measure); margin-inline: auto; }
.article__meta { margin-bottom: var(--sp-lg); }
.article__body > * + * { margin-top: var(--sp-lg); }
.article__inset { margin-block: var(--sp-2xl); aspect-ratio: 3 / 2; overflow: hidden; }
.article__inset img { width: 100%; height: 100%; object-fit: cover; }

/* --- Contact form ------------------------------------------------------------- */

.form { max-width: var(--measure); }
.form-field { margin-top: var(--sp-xl); }
.form-field:first-child { margin-top: 0; }
.form-field label {
  display: block;
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  margin-bottom: var(--sp-sm);
}
.form-field input[type="text"],
.form-field input[type="email"],
.form-field select,
.form-field textarea {
  width: 100%;
  font-family: var(--font-body);
  font-weight: var(--fw-light);
  font-size: var(--fs-body);
  color: var(--text);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: var(--sp-sm) var(--sp-md);
}
.form-field textarea { min-height: 8rem; resize: vertical; }
.form-field input:focus-visible,
.form-field select:focus-visible,
.form-field textarea:focus-visible {
  outline: 2px solid var(--c-olive);
  outline-offset: 2px;
}
.form-field__error {
  display: block;
  margin-top: var(--sp-xs);
  font-size: var(--fs-small);
  color: var(--c-ink);
  text-decoration: underline;
}
.form-checkbox { display: flex; align-items: flex-start; gap: var(--sp-sm); }
.form-checkbox input { margin-top: 0.3em; }
.form-checkbox label { font-size: var(--fs-small); text-transform: none; letter-spacing: var(--ls-small); }
.form-submit { margin-top: var(--sp-xl); }
.form-status { margin-top: var(--sp-lg); font-size: var(--fs-small); }
.form-status[hidden] { display: none; }

/* --- Utility: placeholder markers ------------------------------------------- */

.placeholder {
  background: var(--c-pale-sage);
  padding: 0 var(--sp-xs);
}
