/* ==========================================================================
   Blissporium — Homepage (2026-08 luxury elevation)

   Page-specific composition only. Design tokens, buttons, forms, nav and
   footer live in static/css/global/*; nothing here redefines them. The
   `.page-home .product-card` rules in section 6 are deliberate,
   page-scoped ELEVATIONS of the shared card (utilities.css) — the base
   card is untouched, so /shop and /category are unaffected.

   Motion contract for this page:
     - Only transform / opacity / filter are animated (compositor-safe).
     - Every scroll- or pointer-driven value arrives as a CSS custom
       property written by JS inside a rAF (see js/customer/home.js).
       No layout reads in scroll handlers; no width/top/left animation.
     - Everything degrades to a static, fully-visible page under
       prefers-reduced-motion (one authoritative block, section 12).
   ========================================================================== */

/* ==========================================================================
   0. PALETTE — 5-color system (2026-08 homepage pass)

   Homepage-scoped token overrides. Every color below cascades through
   the rest of this file (and the shared component CSS it borrows, e.g.
   .product-card / .btn / .footer) purely because those rules already
   read from var(--color-*) — nothing on /shop, /category, or any other
   page changes, since these custom properties are declared on
   .page-home, not :root.

   The five colors and their jobs, per spec:
     White  #FFFFFF — page background AND card/surface fill. Distinguished
                       from each other by a hairline border + shadow, not
                       by a tonal difference (deliberately not "cream" —
                       see the 2026-08-09 revision below).
     Peach  #F1D8C6 — decorative only: dividers, hairlines, section
                       washes, blob shapes. NEVER sits directly behind
                       text — anywhere a peach wash used to carry a
                       heading, that heading now lives on a white card
                       instead (see section 3/7/10 below).
                       NOT icon chip backgrounds (2026-08-09 rev 2) —
                       at small-icon-chip size/saturation, peach reads
                       as cream/tan to the eye, not peach, confirmed by
                       pixel-sampled screenshot. Icon/badge chip fills
                       (mega-menu category icons, trust-card icons,
                       feature-card icons, checkout step numbers) use
                       #FDEEF0 (light coral tint) instead — do not put
                       peach back on an icon chip; it will look cream.
     Coral  #F7AEB5 — the one accent (lightened twice, 2026-08-09:
                       #EF6F7E → #F4959F → #F7AEB5, each step softer).
                       Verified against white at THIS value: coral TEXT
                       is only 1.80:1 (fails AA even at the 3:1 large-text
                       floor — every lightening step moves further from
                       AA, not closer), so coral never carries text
                       directly — it is used as a FILL (buttons, sale
                       tags — both paired with ink text, which clears
                       9.37:1 on coral) or as a non-text accent (underlines, dividers,
                       tinted hover washes behind ink text).
     Ink    #2A1912 — the only text color. Where a section needs a
                       lighter/secondary tier (captions, meta text), it's
                       a darker or lighter SHADE of this same ink, never
                       a different hue — see --home-text-secondary/muted.
   ========================================================================== */

.page-home {
  /* Surfaces — page bg and card fill both pure white; cards read via
     border + shadow (section 6/etc.), not a tonal shift. */
  --home-secondary:          #FFFFFF;
  --home-background:         #FFFFFF;
  --home-surface:             #FFFFFF;
  --home-surface-soft:        #FFFFFF;
  --color-surface-secondary:   #FFFFFF;
  --color-ivory:                #FFFFFF;
  --color-pearl:                #FFFFFF;

  /* Peach — decorative fill/line-work only. */
  --home-primary:             #F1D8C6;  /* peach */
  --home-primary-light:       #F8E9DD;  /* pale peach wash */
  --home-primary-dark:        #E3BFA3;  /* deeper peach, placeholders/blobs */
  --color-rose-dust:           #FBEFE6;
  --color-rose-dust-deep:      #F1D8C6;
  --home-champagne:           #F1D8C6;  /* hairline rules */
  --color-champagne-deep:      #E3BFA3;  /* stronger rule */
  --color-champagne-soft:      #FBF0E6;  /* panel washes */

  /* Coral — the one accent, fill/non-text-only (see note above). */
  --home-primary-pressed:     #F7AEB5;
  --color-rose-deep:           #F7AEB5;
  --color-rose-gold:           #F7AEB5;

  /* Ink — the only text hue. Secondary/muted tiers are darker/lighter
     SHADES of this ink (verified AA below), never a separate color.
     Ink   on white: 16.85:1   Secondary on white: 9.08:1
     Muted on white:  6.50:1   — all comfortably clear the 4.5:1 floor. */
  --home-ink:                 #2A1912;
  --home-ink-hover:           #180E08;
  --home-ink-soft:            #2A1912;
  --home-text:                #2A1912;
  --home-text-secondary:      #5A4436;
  --home-text-muted:          #6E5A4C;
  --color-champagne-ink:       #2A1912;  /* eyebrow labels stay ink, not peach */
  --home-accent-dark:         #2A1912;  /* link/hover text stays ink — see 1e/1d */

  /* Borders — visible hairlines now that surface == background. */
  --home-border:              rgba(42, 25, 18, 0.09);
  --home-border-strong:       rgba(42, 25, 18, 0.16);

  /* Radius — iOS-typical generous rounding, consistent across every
     surface. radius-lg (cards/tiles/product photos) stays 16px;
     radius-xl (the bigger banners/campaign slider/newsletter panel)
     comes down from the sitewide 24px to 20px so nothing on the page
     reads as more or less "rounded" than anything else. */
  --home-radius-lg: 16px;
  --home-radius-xl: 20px;

  /* Keyboard focus stays solid ink, not coral — coral only clears 2.9:1
     against white (fails the 3:1 a focus ring needs), ink clears 16.85:1
     and is unmistakably distinct from every coral/peach hover treatment
     on this page. */
  --home-shadow-focus: 0 0 0 2px #FFFFFF, 0 0 0 4px #2A1912, 0 0 0 8px rgba(42, 25, 18, 0.14);

  /* One heading scale for the whole page. Every section-level heading
     (hero/section/banner/campaign/newsletter) and every card-level
     heading (category tile, product card, quick view) reads from one of
     these three tiers, so no two heading levels on the page render at
     the same size — see each usage below for which tier it takes. */
  --home-text-h1: clamp(2.75rem, 2.1rem + 3vw, 4.5rem);
  --home-text-h2: clamp(2rem, 1.6rem + 1.8vw, 3rem);
  --home-text-h3: clamp(1.375rem, 1.2rem + 0.6vw, 1.75rem);
}

/* ==========================================================================
   0b. PAGE SHELL
   ========================================================================== */

.page-home { background-color: var(--home-secondary); }

/* NOTE — `content-visibility: auto` was tried here and deliberately
   removed. It interacts badly with this page's scroll-reveal system:
   inside a skipped subtree, descendants have no layout box, so
   getBoundingClientRect() returns zeros and both the IntersectionObserver
   in global/animations.js and the safety-net sweep in home.js
   mis-classify what is on screen — leaving sections stuck at opacity 0
   on mobile.

   It was also scoped as `.page-home > section`, which matched nothing at
   all (.page-home is on <body>; the sections live inside <main>), so it
   had never actually been doing any work. Correctness wins over an
   optimisation that was never measurably running. Revisit only with a
   reveal mechanism that doesn't depend on element geometry. */

/* Tighter section rhythm than the sitewide --section-padding default —
   with many product shelves back to back, full padding reads as dead
   space between blocks that are all doing the same job. */
.py-section--tight {
  padding-block: clamp(3rem, 2rem + 3.6vw, 5.5rem);
}

/* Full-bleed tonal band, used to break the white run of product shelves
   into chapters. Hairline champagne rules top and bottom so the band
   reads as an intentional editorial spread, not a grey box. */
/* White, same as the page — no tonal wash. The "chapter" break reads
   entirely through the peach hairlines above/below (::before/::after),
   which is exactly peach's spec'd job (decorative divider), and never
   sits behind the section-heading text this band wraps. */
.bg-editorial {
  position: relative;
  background-color: var(--home-secondary);
}

.bg-editorial::before,
.bg-editorial::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--home-champagne) 22%, var(--home-champagne) 78%, transparent);
  pointer-events: none;
}

.bg-editorial::before { top: 0; }
.bg-editorial::after  { bottom: 0; }

/* ==========================================================================
   1. EDITORIAL PRIMITIVES
   Shared by every section below, so the page reads as one authored
   system rather than a stack of independently-styled blocks.
   ========================================================================== */

/* --- 1a. Eyebrow ----------------------------------------------------- */

.lux-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-heading);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-champagne-ink);
}

/* The rule draws itself in when its section reveals — the page's
   signature micro-interaction, repeated at every section head. */
.lux-eyebrow::before {
  content: '';
  width: 40px;
  height: 2px;
  /* Coral, solid — the eyebrow rule is one of the page's most-repeated
     marks (fires at every section head), so it's a deliberate place to
     push the "white + coral" identity rather than a soft peach blend. */
  background: var(--home-primary-pressed);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--duration-cinematic) var(--ease-lux) 200ms;
}

.is-visible .lux-eyebrow::before,
.lux-eyebrow.is-visible::before { transform: scaleX(1); }

.lux-eyebrow--center::before { display: none; }

/* --- 1b. Split-text reveal -------------------------------------------- */

/* JS (splitWords) wraps each word of a [data-split] heading in
   .lux-word > span. The outer element is the mask; the inner span
   travels, so words rise out of nothing rather than fading in place.
   The heading is real text in the HTML, so it degrades to plain type if
   JS never runs. */
.lux-word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* Without this, the mask clips descenders (g, y, p). */
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}

.lux-word > span {
  display: inline-block;
  transform: translateY(110%) rotate(2deg);
  opacity: 0;
  transition:
    transform var(--duration-epic) var(--ease-lux),
    opacity var(--duration-cinematic) var(--ease-out);
  transition-delay: calc(var(--w, 0) * 55ms);
}

.is-visible .lux-word > span { transform: translateY(0) rotate(0deg); opacity: 1; }

/* --- 1c. Reveal variants (extend .reveal from global/animations.css) --- */

/* Stagger index written by JS onto each child of [data-stagger]. A
   custom property rather than an inline transition-delay keeps the
   delay overridable per-breakpoint and lets reduced-motion zero every
   one of them out in a single rule. */
[data-stagger] > * { transition-delay: calc(var(--i, 0) * 90ms); }

/* Blur-in — reserved for imagery and glass panels. Animating blur is
   expensive, so it is used on only a handful of large elements, and JS
   releases `will-change` once the transition ends. */
.reveal--blur {
  opacity: 0;
  filter: blur(14px);
  transform: translateY(28px) scale(0.985);
  transition:
    opacity var(--duration-cinematic) var(--ease-out),
    filter var(--duration-cinematic) var(--ease-out),
    transform var(--duration-epic) var(--ease-lux);
}

.reveal--blur.is-visible { opacity: 1; filter: blur(0); transform: none; }

/* Curtain — an image uncovered by a sliding panel. */
.reveal--curtain { position: relative; opacity: 1; transform: none; }

.reveal--curtain::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: var(--z-raised);
  background-color: var(--color-pearl);
  transform-origin: right center;
  transition: transform var(--duration-epic) var(--ease-lux);
  pointer-events: none;
}

.reveal--curtain.is-visible::after { transform: scaleX(0); }

/* The image starts over-scaled and settles as the curtain clears — the
   editorial "photo lands" move. */
.reveal--curtain img { transform: scale(1.12); transition: transform 1800ms var(--ease-lux); }
.reveal--curtain.is-visible img { transform: scale(1); }

/* --- 1d. Section masthead --------------------------------------------- */

.section-heading {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  margin-bottom: var(--space-12);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--home-border);
  text-align: left;
}

/* A champagne hairline draws in over the static border, in step with
   the eyebrow rule above it. */
.section-heading::after {
  content: '';
  grid-column: 1 / -1;
  height: 2px;
  margin-bottom: calc((var(--space-6) + 2px) * -1);
  background: linear-gradient(90deg, var(--home-primary-pressed), transparent 45%);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--duration-epic) var(--ease-lux) 120ms;
}

.section-heading.is-visible::after { transform: scaleX(1); }

.section-heading h2 {
  font-size: var(--home-text-h2);
  font-weight: var(--weight-medium);
  line-height: 1.04;
  letter-spacing: -0.025em;
  margin: 0;
  text-wrap: balance;
}

.section-heading__sub {
  max-width: 52ch;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--home-text-secondary);
}

.section-heading__action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--home-text-secondary);
  white-space: nowrap;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
  transition: color var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out);
}

.section-heading__action svg { transition: transform var(--duration-base) var(--ease-out); }
.section-heading__action:hover {
  color: var(--home-accent-dark);
  border-bottom-color: var(--home-primary-pressed);
}
.section-heading__action:hover svg { transform: translateX(5px); }

@media (min-width: 768px) {
  .section-heading {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    column-gap: var(--space-10);
  }
  .section-heading .lux-eyebrow,
  .section-heading h2,
  .section-heading__sub { grid-column: 1; }
  .section-heading__action { grid-column: 2; grid-row: 2; justify-self: end; }
}

.section-heading--center {
  justify-items: center;
  text-align: center;
  border-bottom: 0;
}

.section-heading--center::after { display: none; }
.section-heading--center .section-heading__sub { text-align: center; }

/* --- 1e. Link with an underline that wipes in -------------------------- */

.lux-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-heading);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--home-text);
  padding-bottom: 2px;
}

.lux-link::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 2px;
  /* Coral is the accent here, not the text — the underline carries it
     so the link stays ink (readable) while still visibly "going coral"
     on interaction. */
  background-color: var(--home-primary-pressed);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform var(--duration-slow) var(--ease-lux);
}

.lux-link:hover { color: var(--home-accent-dark); }
.lux-link:hover::after { transform: scaleX(1); transform-origin: left center; }
.lux-link svg { transition: transform var(--duration-base) var(--ease-out); }
.lux-link:hover svg { transform: translateX(4px); }

/* ==========================================================================
   2. HERO — silent full-bleed campaign poster
   The image carries the first impression on its own. Nothing is
   overlaid: no headline, no CTA, no figures, no content-bearing scrim.
   All of that lives in .hero-intro directly beneath (section 2f).
   ========================================================================== */

.hero-block { position: relative; }

.hero {
  position: relative;
  isolation: isolate;
  /* The poster owns the first viewport. svh (not vh) so mobile browser
     chrome collapsing doesn't jump the height mid-scroll; the vh line is
     the fallback for engines without svh.

     Raised ~18% (2026-08) from 78vh/860px for a more cinematic presence.
     Deliberately short of full-screen: with the 122px sticky navbar
     above it, anything past ~92vh means the poster can never be seen
     whole, which reads as a broken viewport rather than a grand one.

     A taller box also crops the source LESS, not more — `cover` scales
     to the width, so growing the height widens the vertical window onto
     the image and brings more of the subject into frame. */
  min-height: 620px;
  height: min(92vh, 1000px);
  height: min(92svh, 1000px);
  background-color: var(--color-pearl);
  overflow: hidden;
}

/* --- 2a. The poster ---------------------------------------------------- */

.hero__poster {
  position: absolute;
  left: 0;
  right: 0;
  /* Deliberately taller than the section, anchored above it. Parallax
     translates this layer DOWNWARD as the page scrolls; without the 12%
     of headroom the layer's own top edge would slide into view and
     expose the background behind the hero. */
  top: -12%;
  height: 112%;
  z-index: 0;
  overflow: hidden;
  transform: translate3d(0, calc(var(--hero-parallax, 0) * 1px), 0);
  will-change: transform;
}

.hero__img {
  width: 100%;
  height: 100%;
  /* `cover` plus a focal point tuned to the current hero asset, which is
     a portrait head-and-shoulders shot in a landscape frame.

     The Y value is NOT a generic "centre the subject" number — it is the
     start of the visible window as a fraction of the vertical overflow.
     At 5% the brow line clears the navbar and the lips stay inside the
     lower edge; by 15% the brows are already cut. Verified against the
     live render at 1440x900 and 1920x1080 rather than estimated.

     Re-tune this if the hero image is replaced, especially with a proper
     landscape campaign shot — a wide source has far less vertical
     overflow, and this value would then sit much closer to 50%. */
  object-fit: cover;
  object-position: center 5%;
  /* Colour grade, applied to the pixels rather than as an overlay so the
     image itself is warmed rather than veiled. Restrained on purpose —
     enough to pull a neutral studio shot toward the brand's peach and
     champagne register without looking filtered. */
  filter: saturate(1.04) contrast(1.03) brightness(1.01);
}

/* --- 2b. Grade & vignette -----------------------------------------------
   Carries no content. A blush/champagne wash at the edges plus a soft
   vignette, so the poster reads as a graded campaign frame rather than a
   raw photograph dropped into a container. */

.hero__grade {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    /* Corner warmth — champagne top-left, blush bottom-right. */
    radial-gradient(70% 90% at 0% 0%, rgba(244, 235, 221, 0.4), transparent 62%),
    radial-gradient(70% 90% at 100% 100%, rgba(216, 169, 160, 0.3), transparent 62%),
    /* Vignette. Kept very soft: a heavy one reads as a cheap photo
       filter, not a graded campaign image. */
    radial-gradient(120% 90% at 50% 45%, transparent 55%, rgba(31, 26, 23, 0.3) 100%);
  mix-blend-mode: multiply;
  opacity: 0.85;
}

/* A second, additive pass for the highlight bloom — separated from the
   block above because it needs `screen`, not `multiply`. */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(60% 50% at 50% 18%, rgba(255, 246, 238, 0.22), transparent 70%);
  mix-blend-mode: screen;
}

/* Hairline that closes the poster off from the content below, so the two
   read as separate, deliberate blocks rather than a photo bleeding into
   a page. */
.hero-block .hero + .hero-intro { border-top: 1px solid var(--home-champagne); }

/* --- 2c. Slides --------------------------------------------------------
   Each slide is a full-bleed layer inside .hero__poster. Crossfade plus
   a slow Ken Burns push, so a multi-slide hero reads as a campaign reel
   rather than a slideshow. */

.hero__slide {
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0;
  transform: scale(1.08);
  transition:
    opacity var(--duration-epic) var(--ease-out),
    transform 9000ms linear;
}

.hero__slide.is-active { opacity: 1; transform: scale(1); }

/* --- 2d. Hero intro — the editorial statement beneath the poster -------- */

.hero-intro {
  position: relative;
  background:
    radial-gradient(80% 100% at 50% 0%, rgba(255, 255, 255, 0.9), transparent 65%),
    linear-gradient(180deg, var(--color-ivory) 0%, var(--home-secondary) 100%);
  padding-block: clamp(var(--space-16), 8vw, var(--space-24));
}

.hero-intro__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.hero-intro__content {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 46rem;
}

.hero-intro__title {
  font-size: var(--home-text-h1);
  font-weight: var(--weight-medium);
  line-height: 1.02;
  letter-spacing: -0.035em;
  color: var(--home-text);
  margin-block: var(--space-6) var(--space-5);
  text-wrap: balance;
}

.hero-intro__lead {
  max-width: 54ch;
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
  color: var(--home-ink-soft);
  margin-bottom: var(--space-10);
  text-wrap: pretty;
}

.hero-intro__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-4);
}

.hero-intro__actions .btn { gap: var(--space-3); }

.hero-intro__actions .btn svg {
  transition: transform var(--duration-base) var(--ease-out);
}

.hero-intro__actions .btn:hover svg { transform: translateX(4px); }

/* Coral is the lead theme color per spec. Re-verified at #F7AEB5 (the
   second, softer lightening pass) — the trend holds: lighter coral
   keeps making white text WORSE, so ink-on-coral remains the only
   compliant pairing:
     white on #F7AEB5: 1.80:1  — FAILS AA (even the 3:1 large-text floor)
     ink   on #F7AEB5: 9.37:1  — PASSES AA comfortably (best margin yet —
                                  the lighter the fill, the more it
                                  contrasts with dark ink)
   So button/badge labels stay ink; only the fill keeps getting softer. */
.page-home .btn-primary {
  background-color: var(--home-primary-pressed);
  color: var(--home-ink);
  box-shadow: 0 4px 14px rgba(247, 174, 181, 0.4);
}

.page-home .btn-primary:hover {
  background-color: #F0899A;
  color: var(--home-ink);
  box-shadow: 0 8px 22px rgba(247, 174, 181, 0.48);
}

.page-home .btn-primary:active {
  background-color: #F0899A;
  transform: scale(0.97);
}

.page-home .btn,
.page-home .product-card__quick-add,
.page-home .product-card__wishlist {
  transition-timing-function: var(--ease-out);
}

.page-home .btn:active { transform: scale(0.97); }

/* Secondary CTA — a champagne-ruled pill rather than a second filled
   button, so the pair reads as one primary action with a quieter
   alternative beside it instead of two competing buttons. */
.btn-quiet {
  background-color: transparent;
  border: 1px solid var(--color-champagne-deep);
  color: var(--home-text);
  transition: background-color var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}

.btn-quiet:hover {
  background-color: var(--home-surface);
  border-color: var(--home-text);
  color: var(--home-text);
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
}

/* Slide dots sit above the copy, centred, reading as a quiet index of
   the campaign rather than a control bar stamped on the photo. */
.hero__dots {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
  margin-bottom: var(--space-10);
}

.hero__dot {
  width: 34px;
  height: 2px;
  border-radius: var(--radius-full);
  background-color: var(--home-border-strong);
  transition: background-color var(--duration-base) var(--ease-out);
}

.hero__dot:hover { background-color: var(--home-primary-dark); }
.hero__dot.is-active { background-color: var(--home-primary-pressed); }

/* --- 2e. Figures -------------------------------------------------------- */

.hero__figures {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(var(--space-8), 5vw, var(--space-16));
  margin-top: clamp(var(--space-12), 6vw, var(--space-16));
  padding-top: clamp(var(--space-10), 5vw, var(--space-12));
  border-top: 1px solid var(--home-border);
  width: 100%;
  max-width: 56rem;
}

.hero__figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
}

.hero__figure-value {
  font-family: var(--font-heading);
  font-size: clamp(2rem, 1.5rem + 1.6vw, 2.75rem);
  font-weight: var(--weight-bold);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--home-text);
  /* Tabular figures stop the counter from reflowing its own width as
     digits change — the commonest cause of jitter in animated counters. */
  font-variant-numeric: tabular-nums;
}

.hero__figure-label {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--home-text-muted);
}

/* Section 2f (the hero trust rail — free shipping / authenticity /
   secure checkout / order tracking) was removed 2026-08 along with its
   markup in templates/customer/home.html. */

/* --- 2f. Mobile ---------------------------------------------------------
   The poster stays full-bleed and still leads. A portrait phone crops a
   landscape campaign image hard, so the focal point moves up to keep the
   subject in frame rather than centring on empty background. */
@media (max-width: 640px) {
  /* 2026-08-09 — was 74svh (capped 670px): on every phone size tested
     (360-430px wide) that put the CTA button 1-2 full screens below the
     fold, since the hero photo carries zero content of its own (title/
     lead/CTA all live in .hero-intro right below it — see that section's
     own comment for why) and the photo alone was eating the entire first
     viewport. Capped much shorter so the headline + CTA are reachable
     without scrolling, matching what the same hero already does on
     desktop (a quieter, shorter poster with the real content directly
     beneath it, not competing with it for the same screen). */
  .hero {
    min-height: 240px;
    height: min(34svh, 320px);
  }
  /* At phone widths the portrait source is TALLER than the box is wide,
     so `cover` scales to the height and the crop becomes horizontal —
     the whole image height is already visible and the Y value has no
     effect. Centred on both axes is therefore the correct framing here. */
  .hero__img { object-position: center center; }
  /* 2026-08-10 — trimmed again (was space-6/space-10 padding, space-5
     lead margin, 48px buttons): the persistent bottom nav now
     permanently occupies 60px at the bottom of every mobile page, which
     ate into the room the CTA had on the shortest tested viewport
     (Galaxy S8+). Same content, just less padding around it. */
  .hero-intro { padding-block: var(--space-4) var(--space-6); }
  .lux-eyebrow--center { margin-bottom: var(--space-1); }
  .hero-intro__title {
    font-size: clamp(1.75rem, 1.35rem + 3.5vw, 2.5rem);
    margin-block: var(--space-2);
  }
  /* 2026-08-10 — subtext dropped on mobile only (display:none, not
     removed from the markup — still renders on desktop/tablet above
     this breakpoint, and un-hiding it later is a one-line revert). The
     headline + CTAs + stats close the gap on their own since they're
     plain document flow with no fixed height reserved for this
     paragraph. */
  .hero-intro__lead { display: none; }
  .hero-intro__actions {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
    width: 100%;
  }
  .hero-intro__actions .btn { width: 100%; height: 44px; }
  .hero__dots { margin-bottom: var(--space-5); }
  .hero__figures {
    gap: var(--space-8);
    margin-top: var(--space-8);
    padding-top: var(--space-6);
  }
  /* Parallax off on phones — the travel distance is a large share of the
     viewport there and reads as jitter rather than depth. */
  .hero__poster { transform: none; }
}

/* ==========================================================================
   3. SHOP BY CATEGORY — editorial numbered tiles
   ========================================================================== */

.category-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}

/* 4/5 rather than a taller 3/4: the existing category images are
   landscape-ish with copy baked into them, and an aggressive portrait
   crop slices that copy off. Portrait-shot category art would support
   a taller ratio here. */
.category-tile {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  border-radius: var(--home-radius-lg);
  overflow: hidden;
  background-color: var(--home-surface-soft);
  box-shadow: 0 2px 6px rgba(42, 25, 18, 0.05), 0 10px 26px rgba(42, 25, 18, 0.06);
  isolation: isolate;
  transition: box-shadow var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-lux);
}

.category-tile:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); }

.category-tile__media { position: absolute; inset: 0; z-index: -1; }

.category-tile__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1200ms var(--ease-lux);
}

.category-tile:hover .category-tile__media img { transform: scale(1.09); }

/* Scrim keeps the label legible over any photo the admin uploads,
   including very bright ones. */
.category-tile__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(31, 26, 23, 0.05) 0%, rgba(31, 26, 23, 0.28) 45%, rgba(31, 26, 23, 0.78) 100%);
}

.category-tile__body {
  position: absolute;
  inset: auto var(--space-4) var(--space-4) var(--space-4);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
}

.category-tile__name {
  font-family: var(--font-brand);
  font-size: var(--home-text-h3);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  color: var(--color-text-inverse);
  transform: translateY(0);
  transition: transform var(--duration-slow) var(--ease-lux);
}

.category-tile:hover .category-tile__name { transform: translateY(-4px); }

/* 2026-08-11 — back, styled deliberately transparent this time: no
   circle/background fill at all (previous version's opaque hover-only
   fill, and before that a bare translucent ring, both got reverted).
   Legibility against ANY photo — not just the ones with a dark bottom-
   right corner — comes from drop-shadow on the icon itself, the same
   fix already proven for the index number's text-shadow above (removed
   with the number, but the technique is exactly transferable: a soft
   dark halo behind a white glyph reads on light or dark backgrounds
   alike, which a flat opacity/fill toggle never reliably did). */
.category-tile__arrow {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: #FFFFFF;
  filter: drop-shadow(0 1px 3px rgba(31, 26, 23, 0.65));
  transition: transform var(--duration-slow) var(--ease-lux);
}

.category-tile:hover .category-tile__arrow { transform: translateX(4px); }

@media (min-width: 640px)  { .category-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .category-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--space-5); } }

/* 2026-08-10 — same 2-column grid, same 5 tiles, just a shorter crop
   (4/5 -> 5/6) and a tighter gap so the category section doesn't eat a
   full screen on its own before the product grids below it. */
@media (max-width: 640px) {
  .category-grid { gap: var(--space-3); }
  .category-tile { aspect-ratio: 5 / 6; }
}

/* ==========================================================================
   4. SHOP BY BRAND — continuous marquee
   JS duplicates the track once, so translating exactly -50% (one full
   copy of the content) loops seamlessly.
   ========================================================================== */

.brand-marquee {
  position: relative;
  overflow: hidden;
  /* Fade the edges rather than hard-cutting logos at the viewport. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.brand-marquee__track {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-10), 6vw, var(--space-24));
  width: max-content;
  /* --marquee-duration is set by JS from the measured track width, so
     logos travel at a constant physical speed regardless of how many
     brands the admin has enabled. */
  animation: marquee var(--marquee-duration, 40s) linear infinite;
  will-change: transform;
}

.brand-marquee:hover .brand-marquee__track { animation-play-state: paused; }

@keyframes marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.brand-marquee__item {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  padding-block: var(--space-6);
}

.brand-marquee__item img {
  max-height: 42px;
  max-width: 140px;
  object-fit: contain;
  /* Desaturated at rest so the row reads as one calm ribbon of marks;
     full colour on hover rewards the interaction. */
  filter: grayscale(1);
  opacity: 0.55;
  transition: filter var(--duration-slow) var(--ease-out),
              opacity var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-spring);
}

.brand-marquee__item:hover img { filter: grayscale(0); opacity: 1; transform: scale(1.08); }

/* ==========================================================================
   5. HOMEPAGE SECTIONS — admin-managed "Banner + Products + View All"
   ========================================================================== */

.homepage-section__banner {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 21 / 7;
  border-radius: var(--home-radius-xl);
  overflow: hidden;
  background-color: var(--home-surface-soft);
  box-shadow: var(--shadow-md);
  margin-bottom: var(--space-12);
  isolation: isolate;
}

.homepage-section__banner-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1400ms var(--ease-lux);
}

.homepage-section__banner:hover .homepage-section__banner-img { transform: scale(1.05); }

.homepage-section__banner-overlay {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(0deg, rgba(31, 26, 23, 0.68) 0%, rgba(31, 26, 23, 0.1) 55%, transparent 100%),
    linear-gradient(90deg, rgba(31, 26, 23, 0.35) 0%, transparent 55%);
}

.homepage-section__banner-copy {
  position: absolute;
  left: clamp(var(--space-6), 4vw, var(--space-12));
  right: clamp(var(--space-6), 4vw, var(--space-12));
  bottom: clamp(var(--space-6), 3vw, var(--space-10));
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}

.homepage-section__banner-title {
  font-family: var(--font-heading);
  font-size: var(--home-text-h2);
  font-weight: var(--weight-semibold);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--color-text-inverse);
}

.homepage-section__banner-subtitle {
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  color: rgba(255, 255, 255, 0.86);
  max-width: 48ch;
}

.homepage-section__banner-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  padding-inline: var(--space-6);
  margin-top: var(--space-1);
  border-radius: var(--radius-full);
  border: 1px solid rgba(255, 255, 255, 0.5);
  background-color: rgba(255, 255, 255, 0.1);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--color-text-inverse);
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  transition: background-color var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}

.homepage-section__banner:hover .homepage-section__banner-cta {
  background-color: var(--home-surface);
  border-color: var(--home-surface);
  color: var(--home-text);
  transform: translateY(-2px);
}

@media (max-width: 640px) {
  .homepage-section__banner { aspect-ratio: 4 / 5; margin-bottom: var(--space-8); }
}

/* ==========================================================================
   6. PRODUCT CARD — homepage-specific grid layout only
   All card styling (borderless container, image, name, price) lives in
   the shared component now (global/utilities.css) and applies here
   unmodified — that's the whole point of the 2026-08-10 minimal
   redesign, one card everywhere. This section used to also carry a
   full page-scoped "elevation" restyle (border/shadow/hover-lift,
   badge, wishlist chip, Quick View bar, brand line, bold price) left
   over from the pre-redesign card; all of that referenced elements
   that no longer exist in the markup (badges/wishlist/quick-view/
   brand were removed from the shared card two passes ago) and, worse,
   silently overrode the shared title/price styling on this page only
   — .product-card__title was rendering at 27.84px here vs 15px
   everywhere else. Removed entirely rather than patched, since every
   selector in it was dead weight actively fighting the shared design. */

.page-home .grid-products { gap: clamp(var(--space-4), 2vw, var(--space-8)); }

/* Each grid cell holds a .reveal wrapper around the card (see
   home.html), so the card itself isn't the direct grid item — grid's
   default row-stretch reaches the .reveal wrapper, not the card nested
   inside it. These two lines carry that stretch the rest of the way
   down to the actual card. */
.page-home .grid-products > * { display: flex; }
.page-home .grid-products > * > .product-card { width: 100%; }

@media (min-width: 640px)  { .page-home .grid-products { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .page-home .grid-products { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1440px) { .page-home .grid-products { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

/* ==========================================================================
   7. CAMPAIGN SLIDER — full-width admin-managed promotional banner
   ========================================================================== */

.campaign-slider {
  position: relative;
  height: clamp(440px, 44vw, 560px);
  border-radius: var(--home-radius-xl);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  isolation: isolate;
}

.campaign-slide {
  position: absolute;
  inset: 0;
  display: flex;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-epic) var(--ease-out);
}

.campaign-slide.is-active { opacity: 1; pointer-events: auto; }

.campaign-slide__media {
  position: relative;
  flex: 0 0 62%;
  overflow: hidden;
  background-color: var(--home-primary-dark);
}

.campaign-slide__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1);
  transition: transform 9s linear;
}

.campaign-slide.is-active .campaign-slide__img { transform: scale(1.07); }

.campaign-slide__overlay {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(45, 42, 42, 0) 58%, var(--color-champagne-soft) 100%),
    linear-gradient(180deg, rgba(45, 42, 42, 0.1) 0%, rgba(45, 42, 42, 0.3) 100%);
}

.campaign-slide__panel {
  position: relative;
  flex: 0 0 38%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--space-4);
  padding: clamp(var(--space-6), 3vw, var(--space-12));
  /* White surface, not the peach wash this used to be — the badge,
     title, subtitle and description all sit directly on this panel, so
     per spec (peach never behind text) it has to be a white card. The
     peach the panel used to carry now lives only in the thin edge below. */
  background-color: var(--home-surface);
  color: var(--home-text);
  text-align: left;
  border-left: 1px solid var(--home-champagne);
}

/* Panel content drifts in when its slide becomes active, so switching
   slides is a composed transition rather than a hard crossfade. */
.campaign-slide__panel > * {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--duration-slower) var(--ease-out),
              transform var(--duration-cinematic) var(--ease-lux);
}

.campaign-slide.is-active .campaign-slide__panel > * { opacity: 1; transform: none; }
.campaign-slide.is-active .campaign-slide__panel > :nth-child(1) { transition-delay: 120ms; }
.campaign-slide.is-active .campaign-slide__panel > :nth-child(2) { transition-delay: 200ms; }
.campaign-slide.is-active .campaign-slide__panel > :nth-child(3) { transition-delay: 280ms; }
.campaign-slide.is-active .campaign-slide__panel > :nth-child(4) { transition-delay: 360ms; }
.campaign-slide.is-active .campaign-slide__panel > :nth-child(5) { transition-delay: 440ms; }
.campaign-slide.is-active .campaign-slide__panel > :nth-child(6) { transition-delay: 520ms; }

/* Coral fill + ink text, not coral text — raw coral only clears 2.9:1
   against white (fails AA), but ink-on-coral clears 5.8:1. This is the
   "sale tag" job coral is spec'd for. Sized up 2026-08-09 — 11px read as
   an afterthought next to the panel's large heading below it; this
   badge IS the "up to X% off" headline number and needed to look like
   one at a glance, not require a second look. */
.campaign-slide__badge {
  display: inline-block;
  padding: var(--space-2) var(--space-6);
  margin-bottom: var(--space-1);
  font-family: var(--font-heading);
  font-size: 1.5rem;
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--home-ink);
  background-color: var(--home-primary-pressed);
  border-radius: var(--radius-full);
}

.campaign-slide__title {
  font-family: var(--font-heading);
  font-size: var(--home-text-h2);
  font-weight: var(--weight-medium);
  line-height: 1.02;
  letter-spacing: -0.03em;
  color: var(--home-text);
}

.campaign-slide__subtitle {
  max-width: 32ch;
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  color: var(--home-ink-soft);
}

.campaign-slide__description {
  max-width: 36ch;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  color: var(--home-text-secondary);
}

.campaign-slide__countdown { display: flex; gap: var(--space-2); }

.countdown-unit {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 54px;
  padding: var(--space-2) var(--space-1);
  background-color: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--home-champagne);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xs);
}

.countdown-unit span {
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: 1;
  color: var(--home-text);
  font-variant-numeric: tabular-nums;
}

.countdown-unit small {
  margin-top: 3px;
  font-size: 10px;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--home-text-muted);
}

.campaign-slider__dots {
  position: absolute;
  left: var(--space-6);
  bottom: var(--space-6);
  z-index: var(--z-raised);
  display: flex;
  gap: var(--space-2);
}

.campaign-slider__dot {
  width: 28px;
  height: 2px;
  border-radius: var(--radius-full);
  background-color: rgba(255, 255, 255, 0.45);
  transition: background-color var(--duration-base) var(--ease-out);
}

.campaign-slider__dot.is-active { background-color: var(--home-primary-pressed); }

@media (max-width: 767px) {
  .campaign-slider { height: auto; }
  .campaign-slide { position: relative; inset: auto; flex-direction: column; display: none; }
  .campaign-slide.is-active { display: flex; }
  .campaign-slide__media { flex-basis: auto; height: 240px; }
  .campaign-slide__panel { flex-basis: auto; }
  /* Dots sit over the pale panel on mobile, so they need a dark tone. */
  .campaign-slider__dots { bottom: var(--space-3); left: 50%; transform: translateX(-50%); }
  .campaign-slider__dot { background-color: var(--home-primary-dark); }
  .campaign-slider__dot.is-active { background-color: var(--home-primary-pressed); }
}

/* 2026-08-10 — on phones (<=640px) this banner sat between the hero and
   the actual category/product grids, and at its previous size (240px photo
   + roomy panel padding/gaps) it alone pushed real products past a 3rd
   screen of scrolling. Same content, just tightened — nothing here is
   removed, the photo, badge, title, subtitle, description and CTA are all
   still present, just given less breathing room so the sections below
   become reachable within 1-2 scrolls like the rest of the homepage. */
@media (max-width: 640px) {
  .campaign-slide__media { height: 160px; }
  .campaign-slide__panel {
    padding: var(--space-5) var(--space-5);
    gap: var(--space-2);
  }
  .campaign-slide__badge { padding: var(--space-1) var(--space-4); font-size: 1.125rem; margin-bottom: 0; }
  .campaign-slide__title { font-size: clamp(1.5rem, 1.2rem + 3vw, 2rem); line-height: 1.1; }
  .campaign-slide__subtitle { font-size: var(--text-sm); }
  .campaign-slide__description { font-size: var(--text-xs); line-height: var(--leading-normal); }
  .campaign-slide__cta { padding-block: var(--space-2); }
}

/* ==========================================================================
   8. POSTERS — admin-managed, location-targeted marketing content
   ========================================================================== */

.poster-block + .poster-block { margin-top: var(--space-6); }

.poster-banner {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--home-radius-lg);
  background-color: var(--home-primary-light);
  box-shadow: var(--shadow-xs);
  isolation: isolate;
  transition: box-shadow var(--duration-slower) var(--ease-out),
              transform var(--duration-slower) var(--ease-lux);
}

.poster-banner:hover { box-shadow: var(--shadow-lg); transform: translateY(-6px); }

.poster-banner__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1200ms var(--ease-lux);
}

.poster-banner:hover .poster-banner__img { transform: scale(1.06); }

.poster-banner__content {
  position: relative;
  z-index: var(--z-raised);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-6);
  color: var(--color-text-inverse);
  background: linear-gradient(180deg, rgba(31, 26, 23, 0) 30%, rgba(31, 26, 23, 0.66) 100%);
}

.poster-banner__content .eyebrow,
.poster-banner__content .lux-eyebrow { color: rgba(255, 255, 255, 0.82); }

.poster-banner__content h3 {
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: -0.02em;
}

.poster-banner__content p {
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.9);
  max-width: 46ch;
}

.poster-banner__cta {
  align-self: flex-start;
  margin-top: var(--space-2);
  pointer-events: none;
}

.poster-block--full .poster-banner--full { min-height: 260px; }

.poster-block--half { display: grid; grid-template-columns: 1fr; gap: var(--space-5); }
.poster-block--half .poster-banner--half { min-height: 230px; }

.poster-block--cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.poster-block--cards .poster-banner--card { min-height: 160px; }
.poster-block--cards .poster-banner--card .poster-banner__content { padding: var(--space-4); }
.poster-block--cards .poster-banner--card .poster-banner__content h3 { font-size: var(--text-md); }

.poster-carousel {
  display: flex;
  gap: var(--space-5);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  /* Vertical padding keeps the lifted hover shadow from being clipped by
     the scroll container. */
  padding-block: var(--space-2);
}

.poster-carousel::-webkit-scrollbar { display: none; }

.poster-banner--carousel-item {
  flex: 0 0 clamp(260px, 72vw, 380px);
  min-height: 220px;
  scroll-snap-align: start;
}

@media (min-width: 768px) {
  .poster-block--half  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .poster-block--cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Section 9 (Testimonials) was removed 2026-08 along with its markup in
   templates/customer/home.html. models/testimonial.py, the admin CRUD and
   the `testimonials` collection are all still in place — only the
   homepage rendering is gone. */

/* ==========================================================================
   10. NEWSLETTER — glass panel on a warm editorial ground
   ========================================================================== */

/* Very slow, very small drift for the bloom behind the panel — the
   movement stays below the threshold of conscious notice, which is the
   point: the panel should feel lit, not animated. */
@keyframes ambient-drift {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1.05); }
  50%      { transform: translate3d(6%, -4%, 0) scale(0.95); }
}

/* White-to-coral (2026-08-09, was a cream/peach champagne->primary->
   rose-dust gradient) — the outer band behind the .newsletter__inner
   card, which is untouched. */
.newsletter {
  position: relative;
  padding-block: clamp(var(--space-16), 8vw, var(--space-24));
  background:
    radial-gradient(70% 90% at 20% 10%, rgba(255, 255, 255, 0.85), transparent 60%),
    linear-gradient(135deg, #FFFFFF 0%, #FDEEF0 100%);
  overflow: hidden;
}

/* Slow-breathing bloom behind the panel. */
.newsletter::before {
  content: '';
  position: absolute;
  top: -30%;
  right: -10%;
  width: min(60vw, 640px);
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  background: radial-gradient(circle, rgba(255, 255, 255, 0.75), transparent 68%);
  filter: blur(60px);
  pointer-events: none;
  animation: ambient-drift 30s var(--ease-in-out) infinite;
}

.newsletter__inner {
  position: relative;
  z-index: var(--z-raised);
  max-width: 640px;
  margin-inline: auto;
  padding: clamp(var(--space-8), 5vw, var(--space-16));
  text-align: center;
  border-radius: var(--home-radius-xl);
  background-color: rgba(255, 255, 255, 0.72);
  -webkit-backdrop-filter: blur(22px) saturate(1.4);
  backdrop-filter: blur(22px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: var(--shadow-lg);
}

.newsletter .lux-eyebrow { color: var(--color-champagne-ink); }

.newsletter__title {
  font-size: var(--home-text-h2);
  font-weight: var(--weight-medium);
  line-height: 1.05;
  letter-spacing: -0.03em;
  color: var(--home-text);
  margin-block: var(--space-4);
  text-wrap: balance;
}

.newsletter__lead {
  max-width: 46ch;
  margin-inline: auto;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--home-ink-soft);
  margin-bottom: var(--space-8);
}

.newsletter__form { display: flex; gap: var(--space-3); flex-direction: column; }

.newsletter__form .form-control {
  background-color: rgba(255, 255, 255, 0.9);
  border-color: var(--home-champagne);
  color: var(--home-text);
  transition: border-color var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}

.newsletter__form .form-control::placeholder { color: var(--home-text-muted); }

.newsletter__form .form-control:focus {
  border-color: var(--home-primary-pressed);
  box-shadow: var(--home-shadow-focus);
}

/* Left as the coral fill from .page-home .btn-primary (not overridden
   back to ink) — same button styling everywhere on the page, per the
   "coral as the confident lead accent, used often" brief. */

.newsletter__note {
  margin-top: var(--space-5);
  font-size: var(--text-xs);
  color: var(--home-text-muted);
  transition: color var(--duration-base) var(--ease-out);
}

.newsletter__note.is-success { color: var(--color-success); }

@media (min-width: 640px) {
  .newsletter__form { flex-direction: row; }
  .newsletter__form .form-control { flex: 1; }
}

/* ==========================================================================
   10b. FOOTER — the .footer/.footer__link/.footer__social base styling
   is now white/coral/ink globally (static/css/global/utilities.css,
   2026-08-09 navbar+footer pass), so only a homepage-specific line-height
   bump is left to scope here.
   ========================================================================== */

.page-home .footer__link { line-height: var(--leading-relaxed); }

/* ==========================================================================
   11. QUICK VIEW MODAL
   ========================================================================== */

.quick-view__media {
  aspect-ratio: 4 / 5;
  border-radius: var(--home-radius-lg);
  overflow: hidden;
  background-color: var(--home-surface-soft);
  margin-bottom: var(--space-5);
}

.quick-view__media img { width: 100%; height: 100%; object-fit: cover; }

.quick-view__name {
  font-family: var(--font-heading);
  font-size: var(--home-text-h3);
  font-weight: var(--weight-medium);
  letter-spacing: -0.015em;
}

/* ==========================================================================
   12. REDUCED MOTION
   One authoritative block. Everything above must stay legible and fully
   functional with all of it switched off — nothing on this page depends
   on an animation in order to become visible.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .page-home *,
  .page-home *::before,
  .page-home *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  /* Anything that starts hidden or displaced is forced to its resting
     state: the transition that would have revealed it is now effectively
     instant, but may never be triggered at all. */
  .lux-eyebrow::before,
  .section-heading::after { transform: scaleX(1); }

  .lux-word > span { transform: none; opacity: 1; }

  .reveal--blur { opacity: 1; filter: none; transform: none; }
  .reveal--curtain::after { transform: scaleX(0); }
  .reveal--curtain img { transform: none; }

  .hero__poster { transform: none; }
  .hero__slide { transform: none; }

  .campaign-slide__panel > * { opacity: 1; transform: none; }

  .brand-marquee__track { animation: none; }
  /* With the animation off, the duplicated half would be dead space, so
     let the row scroll manually instead. */
  .brand-marquee { overflow-x: auto; }
}
