/* Page sections — hero, home-*, collection, my-event, gallery, quote wizard. */

/* ═══════════════════════════════════════ */
/*  HOME                                   */
/* ═══════════════════════════════════════ */
.view-home { display: flex; flex-direction: column; }

/* Hero — Editorial Split. */
.hero {
  position: relative;
  min-height: 520px;
  overflow: hidden;
  display: flex;
  align-items: start;
  flex-shrink: 0;
}
/* Hero-quote glass card hidden — body reviews carousel below is the
   canonical version. Keep in DOM so synced setReview() doesn't error. */
.hero .hero-quote { display: none; }
.hero-bg, .hero-bg img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
/* Hero bg: the photo's own soft bokeh IS the backdrop — no scrim, no filter.
   The blurred top of the frame carries the cream headline on its own, so the
   image shows untreated. object-position pulled up to keep the tent canopy +
   string lights visible behind the copy. */
.hero-bg img {
  transform: scale(1.02);
  object-position: 50% 30%;
  transition: transform 8s var(--ease-out);
}
.view-home.active .hero-bg img { transform: scale(1); }
.hero-inner {
  position: relative;
  z-index: 1;
  padding: 80px 56px 48px;
  max-width: var(--max-w);
  width: 100%;
  margin: 0 auto;
  color: var(--cream);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 56px;
  align-items: start;
}
.hero-content { max-width: 620px; }

/* Micro-trust row — small, restrained, hairline-divided */
.hero-trust {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--champagne-light);
  margin-bottom: 24px;
  opacity: 0.85;
}
.hero-trust .dot {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--champagne);
  opacity: 0.7;
}
.hero-trust strong { color: var(--champagne); font-weight: 500; }

.hero h1 {
  font-size: clamp(48px, 5.4vw, 80px);
  color: #3a2540;
  margin-bottom: 32px;
  line-height: 1.05;
  font-weight: 400;
  letter-spacing: -0.02em;
  /* Brand aubergine headline (cream washed out over the bright bokeh photo).
     A soft diffuse WHITE glow lifts the dark type off the busy/darker parts
     of the frame — a premium halo, not a hard-edged shadow. */
  text-shadow:
    0 2px 12px rgba(255, 255, 255, 0.6),
    0 4px 30px rgba(255, 255, 255, 0.45),
    0 1px 3px rgba(255, 255, 255, 0.5);
}
/* "enchanted" reads as one continuous headline — the em inherits the h1's
   cream colour AND weight (no accent, no bold), so it's indistinguishable
   from the rest of the sentence. Matching weight also keeps the shared h1
   text-shadow from reading heavy on it (it was bold-600 over a 300 headline
   on mobile). */
.hero h1 em { color: inherit; }
.hero-sub {
  font-size: 18px;
  line-height: 1.6;
  color: var(--cream);
  max-width: 460px;
  margin-bottom: 32px;
  font-weight: 500;
  opacity: 0.96;
  text-shadow:
    0 1px 3px rgba(0, 0, 0, 0.62),
    0 2px 12px rgba(0, 0, 0, 0.52),
    0 4px 24px rgba(0, 0, 0, 0.4);
}
.hero-trust { text-shadow: 0 1px 6px rgba(0, 0, 0, 0.45); }
.hero-ctas { display: flex; gap: 14px; flex-wrap: wrap; }

/* Floating quote card — bottom-right, breaks symmetry */
.hero-quote {
  align-self: end;
  max-width: 320px;
  background: rgba(var(--cream-rgb),0.06);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(var(--cream-rgb),0.16);
  border-radius: 18px;
  padding: 22px 24px;
  color: var(--cream);
  box-shadow: var(--inset-highlight);
  position: relative;
}
.hero-quote::before {
  content: '\201C';
  position: absolute;
  top: 14px;            /* was -8px — quote mark now sits inside the box with breathing room above */
  left: 22px;
  font-family: var(--serif);
  font-size: 56px;
  font-weight: 300;
  color: var(--champagne);
  line-height: 1;
  font-style: italic;
}
.hero-quote-text {
  font-family: var(--serif);
  font-style: italic;
  font-size: 16.5px;
  font-weight: 300;
  line-height: 1.5;
  color: var(--cream);
  margin: 45px 0 14px;  /* gap above text trimmed 5px so text sits closer to quote mark + stars */
}
.hero-quote-meta {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--champagne);
  font-weight: 500;
}
/* Stars positioned top-right so the quote mark on the left has room to breathe */
.hero-quote-stars {
  position: absolute;
  top: 22px;
  right: 24px;
  display: inline-flex;
  gap: 2px;
  color: var(--champagne);
  opacity: 0.85;
}
.hero-quote-stars svg { width: 12px; height: 12px; }

/* Scroll cue inside the hero — gentle bouncing chevron + label.
   Fades to 0 when the home view scrolls past ~40px (.is-scrolled class
   added in JS). pointer-events:none so it never intercepts touches. */
.hero-scroll-cue {
  position: absolute;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--champagne);
  opacity: 0.85;
  pointer-events: none;
  z-index: 2;
  transition: opacity 0.35s var(--ease);
  animation: scroll-cue-bounce 2.2s var(--ease-out) infinite;
}
.hero-scroll-cue-label {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--champagne-light);
}
.hero-scroll-cue svg { width: 16px; height: 16px; }
.view-home.is-scrolled .hero-scroll-cue { opacity: 0; }
@keyframes scroll-cue-bounce {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 6px); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-scroll-cue { animation: none; }
}

/* ── Fireflies ──────────────────────────────────────────────────
   Gentle warm glows in the bottom-left of the hero, evoking the
   candlelit reception. Designed to feel organic:
   • unique duration + delay + position per firefly (no synchronization)
   • brightness ramps up and fades (not a square wave)
   • slight J-curve drift up during the flash phase
   • mix-blend-mode: screen so they brighten the photo, not overlay it
   • pointer-events: none so they never block clicks/CTAs
   Honors prefers-reduced-motion (still flashes, no drift). */
.hero-fireflies {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 1;
}
.firefly {
  position: absolute;
  width: var(--fly-size, 4px);
  height: var(--fly-size, 4px);
  border-radius: 50%;
  background: radial-gradient(circle, #fff6c2 0%, #ffd56b 40%, transparent 70%);
  /* Box-shadow intensity scales with --fly-peak so far/dim fireflies have
     proportionally softer halos (avoids the "tiny pinprick with huge halo"
     mismatch when peak is low). */
  box-shadow:
    0 0 8px  rgba(255, 213, 107, calc(var(--fly-peak, 0.95) * 0.85)),
    0 0 16px rgba(255, 213, 107, calc(var(--fly-peak, 0.95) * 0.45)),
    0 0 26px rgba(255, 213, 107, calc(var(--fly-peak, 0.95) * 0.18));
  /* Depth-of-field: blur in px, varied per firefly (0 = sharp/near, 2+ = soft/far) */
  filter: blur(var(--fly-blur, 0.3px));
  opacity: 0;
  mix-blend-mode: screen;
  animation: firefly-flash var(--fly-dur, 5s)
             cubic-bezier(0.45, 0.05, 0.55, 0.95)
             var(--fly-delay, 0s) infinite;
}
/* Peak opacity is parameterized via --fly-peak so distant fireflies feel
   dimmer in addition to softer + smaller. Intermediate keyframe opacities
   scale proportionally to the peak. */
@keyframes firefly-flash {
  0%   { opacity: 0;
         transform: translate(0, 0); }
  12%  { opacity: calc(var(--fly-peak, 0.95) * 0.26); }
  22%  { opacity: calc(var(--fly-peak, 0.95) * 0.89);
         transform: translate(calc(var(--fly-drift-x, 0px) * 0.5),
                              calc(var(--fly-drift-y, -12px) * 0.5)); }
  30%  { opacity: var(--fly-peak, 0.95);
         transform: translate(var(--fly-drift-x, 0px),
                              var(--fly-drift-y, -12px)); }
  40%  { opacity: calc(var(--fly-peak, 0.95) * 0.32);
         transform: translate(var(--fly-drift-x, 0px),
                              calc(var(--fly-drift-y, -12px) * 1.2)); }
  55%  { opacity: 0; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .firefly {
    animation: firefly-flash-reduced var(--fly-dur, 5s) ease-in-out
               var(--fly-delay, 0s) infinite;
  }
  @keyframes firefly-flash-reduced {
    0%, 55%, 100% { opacity: 0; }
    25%, 32%      { opacity: 0.7; }
  }
}

/* Hero-quote carousel — grid-stack so card sizes to longest review.
   Synced with the main home-reviews carousel below: same setReview()
   advances both. Mobile hides the whole hero-quote (display:none). */
.hero-quote-stage { display: grid; }
.hero-quote-slide {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateY(6px);
  /* s21: bumped 0.95s → 1.45s (+500ms) to stay in sync with the bumped
     .review-slide transition. Both carousels share the setReview() index
     so the cross-fades start simultaneously. */
  transition: opacity 1.45s var(--ease), transform 1.45s var(--ease);
  pointer-events: none;
}
.hero-quote-slide.active {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.hero-quote-dots {
  display: flex;
  gap: 8px;
  margin-top: 16px;
  justify-content: flex-start;
}
.hero-quote-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(var(--cream-rgb), 0.30);
  border: none;
  padding: 0;
  cursor: pointer;
  position: relative;
  transition: background 0.3s var(--ease), transform 0.3s var(--ease);
}
/* 40×40 hit area without changing visible dot size (skill rule: minimum hit area). */
.hero-quote-dot::after {
  content: '';
  position: absolute;
  inset: -17.5px;
}
.hero-quote-dot.active {
  background: var(--champagne);
  transform: scale(1.4);
}

/* Featured strip — asymmetric (1 wide + 2 stacked, NOT 3 equal cards) */
.home-featured {
  padding: 64px 56px 36px;
  max-width: var(--max-w);
  width: 100%;
  margin: 0 auto;
}
.home-featured-head {
  display: flex;
  justify-content: space-between;
  align-items: end;
  margin-bottom: 32px;
  gap: 32px;
}
.home-featured-head h2 {
  font-size: clamp(28px, 3.2vw, 42px);
  max-width: 540px;
}
.home-featured-link {
  font-size: 12px;
  color: var(--brand);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  transition: color 0.25s var(--ease), gap 0.25s var(--ease);
  flex-shrink: 0;
}
/* Was animating `width` 18→26 which triggers reflow each frame.
   Switched to scaleX with a left-anchored origin — pure GPU composite. */
.home-featured-link::after {
  content: '';
  width: 26px;
  height: 1px;
  background: currentColor;
  transform-origin: left center;
  transform: scaleX(0.7);
  transition: transform 0.25s var(--ease);
}
.home-featured-link:hover { color: var(--accent); }
.home-featured-link:hover::after { transform: scaleX(1); }

/* Asymmetric bento: hero card + 2 stacked */
.home-featured-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 18px;
  min-height: 460px;
}
.feat-hero {
  grid-row: 1 / span 2;
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--ring-hair), var(--shadow-card);
  transition: box-shadow 0.35s var(--ease);
}
.feat-hero:hover { box-shadow: var(--ring-hair), var(--shadow-card-hover); }
.feat-hero img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.9s var(--ease-out);
  /* Hairline outline so the photo reads as 'placed' on the cream bg
     rather than pasted-in (per make-interfaces-feel-better polish layer). */
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}
.feat-hero:hover img { transform: scale(1.04); }
.feat-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 30%, rgba(35,24,15,0.85) 100%);
}
.feat-hero-body {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  padding: 32px;
  color: var(--cream);
  z-index: 1;
}
.feat-hero-tag {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--champagne);
  font-weight: 500;
  margin-bottom: 10px;
}
.feat-hero-title {
  font-family: var(--serif);
  font-size: clamp(26px, 2.6vw, 36px);
  font-weight: 300;
  color: var(--cream);
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin-bottom: 10px;
}
.feat-hero-meta { font-size: 13px; color: var(--champagne-light); opacity: 0.9; }

.feat-card {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  cursor: pointer;
  background: var(--cream-2);
  box-shadow: var(--ring-hair), var(--shadow-card);
  transition: box-shadow 0.35s var(--ease), transform 0.35s var(--ease);
  display: grid;
  grid-template-columns: 1fr 1.1fr;
}
.feat-card:hover {
  box-shadow: var(--ring-hair), var(--shadow-card-hover);
  transform: translateY(-2px);
}
.feat-card-img { overflow: hidden; }
.feat-card-img img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.9s var(--ease-out);
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}
.feat-card:hover .feat-card-img img { transform: scale(1.05); }
/* Press feedback on the tappable featured cards (home).
   :active sticks on the parent so the lift inverts. */
.feat-hero:active { transform: scale(0.99); }
.feat-card:active { transform: translateY(-2px) scale(0.99); }
.feat-card-body {
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
}
.feat-card-tag {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
}
.feat-card-name {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.01em;
  line-height: 1.15;
}
.feat-card-meta { font-size: 13px; color: var(--muted); margin-top: 2px; }
.feat-card-meta strong { color: var(--brand); font-weight: 500; }

/* Reviews — single rotating editorial quote (NEW in v7) */
.home-reviews {
  padding: 80px 56px 80px;
  max-width: 980px;
  width: 100%;
  margin: 0 auto;
  text-align: center;
  position: relative;
}
.home-reviews-eyebrow { display: inline-flex; margin-bottom: 28px; }
/* Grid-stack the slides so the stage sizes to the LARGEST review's natural
   height (Margaux's is ~3× the others). All slides occupy the same cell;
   opacity + translateY drives the cross-fade. Same pattern as the
   collection-card icon cross-fade. */
/* .review-stage carries the `swiper` class — Swiper handles layout
   when its JS initializes. Explicit defensive CSS for the load-race
   window BEFORE init: iOS Safari can grab horizontal pan on an
   un-touch-actioned element and the user perceives the whole page
   moving. Setting touch-action up-front via our rule (not waiting
   for Swiper's JS to set it) closes the gap. overflow:hidden is also
   declared explicitly so it doesn't depend on Swiper's CSS file loading. */
.review-stage {
  touch-action: pan-y;
  overflow: hidden;
  position: relative;
}
.review-slide {
  /* Inner layout for text / meta. Swiper handles slide
     positioning + transitions; we only style the slide's contents.
     The quote+attribution sit in a 760px block CENTERED on the page
     (lead call s53), with the text itself left-justified — quote +
     attribution share a left edge; the eyebrow + nav stay centered. */
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: left;
  /* Section-wide tap-to-pause needs the cursor to feel interactive. */
  cursor: pointer;
}
/* Paused indicator — small "Paused" pill below the dots when the user
   has clicked to halt auto-rotate. Hover-pause is invisible by design.
   Absolute-positioned inside the section's bottom padding so toggling
   the pill on/off doesn't push any other elements around the page.
   Section is position:relative already (from .home-reviews / .corp-reviews
   base rules). Vertical placement: ~24px above the section's bottom
   edge, well inside the bottom padding (80px desktop / 48-64px mobile). */
.home-reviews.is-paused::after,
.corp-reviews.is-paused::after {
  content: "Paused — tap to resume";
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
  white-space: nowrap;
  pointer-events: none;
}
/* Editorial opening-quote mark. line-height:1 (not the old 0.6 trick)
   so the glyph sits inside its own line box. Explicit width + text-align
   are essential — flex parents (align-items: center) shrink block items
   to their content's measured box, but Source Serif 4 italic's left
   double-quote has overhang the browser doesn't include in that
   measurement, so without the explicit width the right tick gets clipped. */
.review-text {
  /* Quote uses the UPRIGHT Source Serif 4 cut (self-hosted, fonts.css) with a
     browser-synthesized italic slant — the readable upright letterforms the
     client preferred over the true cursive italic (synced from Webflow, s44).
     opsz:14 pins a sturdier optical size (thicker strokes / less contrast).
     font-synthesis:style is explicit so the per-glyph slant is guaranteed. */
  font-family: 'Source Serif 4 Up', Georgia, serif;
  font-variation-settings: 'opsz' 14;
  font-synthesis: style;
  font-size: clamp(22px, 2.4vw, 28px);
  font-weight: 300;
  font-style: italic;
  color: var(--ink);
  line-height: 1.45;
  letter-spacing: -0.01em;
  max-width: 760px;
  width: 100%;
  margin-bottom: 28px;
}
.review-meta {
  max-width: 760px;
  width: 100%;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}
.review-meta strong { color: var(--brand); font-weight: 500; }
/* Nav row — prev arrow + dots + next arrow.
   ═══════════════════════════════════════════════════════════════════
   iOS SAFARI BUG NOTE (s21):
   The earlier shipped version (PR #42) stacked these vertically on
   iOS Safari because the default <button> styling there enforces a
   min-width that overflows the row + the children render as
   inline-block-ish rather than honoring the flex layout. Three
   defensive rules below prevent that:
     1. `flex-direction: row` and `flex-wrap: nowrap` declared
        explicitly on the parent (don't rely on flex defaults — iOS
        sometimes computes them differently when buttons are involved).
     2. `-webkit-appearance: none` on all children strips Safari's
        default `button` paintbox styling.
     3. `flex: 0 0 auto` + `min-width: 0` on all children pins them at
        their intrinsic size and allows shrinking if needed instead of
        forcing a wrap.
   ═══════════════════════════════════════════════════════════════════ */
.review-nav {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: 36px;
  /* Defensive: clip if a child somehow overflows rather than wrapping. */
  max-width: 100%;
}
.review-dots {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 8px;
  /* iOS Safari armor — pin in row, don't grow/shrink unexpectedly. */
  flex: 0 0 auto;
  min-width: 0;
}
.review-dot {
  /* iOS Safari armor — strip default button paintbox + pin size. */
  -webkit-appearance: none;
  appearance: none;
  flex: 0 0 auto;
  min-width: 0;
}
.review-nav-btn {
  display: inline-flex;
  flex: 0 0 auto;
  min-width: 0;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  padding: 0;
  width: 32px;
  height: 32px;
  cursor: pointer;
  color: var(--muted);
  opacity: 0.55;
  /* iOS Safari armor — strip default button paintbox. */
  -webkit-appearance: none;
  appearance: none;
  transition: opacity 0.25s var(--ease), color 0.25s var(--ease), transform 0.25s var(--ease);
  position: relative;
  border-radius: 50%;
}
/* WCAG 2.5.5 hit target: visible glyph 32×32 + 16px halo = 48×48 actual. */
.review-nav-btn::after {
  content: '';
  position: absolute;
  inset: -8px;
}
/* Swiper v11 overrides — three rules below, all required.
   ─────────────────────────────────────────────────────────────────────
   Swiper's bundled CSS loads BEFORE sections.css (see <link> order in
   mockup.html), so our 2-class chained selectors win the cascade
   cleanly. If you ever move the Swiper <link> back below ours, bump
   specificity to 3 classes or these will silently break. */

/* 1. swiper-button-prev/next would absolute-position our arrows at the
      section edges. Force them back into flex flow. */
.review-nav-btn.swiper-button-prev,
.review-nav-btn.swiper-button-next {
  position: static;
  top: auto;
  left: auto;
  right: auto;
  width: 32px;
  height: 32px;
  margin-top: 0;
}
.review-nav-btn.swiper-button-disabled {
  opacity: 0.25;
  cursor: default;
}

/* 2. swiper-pagination-bullets-horizontal gives the dots container
      width: 100%, which makes it consume all of .review-nav's flex
      space and pushes the prev/next arrows out to the section edges.
      Shrink-wrap it instead so prev | dots | next sit together. */
.review-dots.swiper-pagination-bullets {
  width: auto;
  position: static;
  bottom: auto;
  left: auto;
}

/* 3. .swiper-slide forces display: block, which kills our flex column
      on .review-slide. Restore flex with higher specificity. The 760px
      review-text block is CENTERED in the slide (lead call s53); the
      text inside it stays left-justified, so quote + attribution share
      a left edge while the block sits centered on the page. */
.review-stage .swiper-slide.review-slide {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.review-nav-btn:hover,
.review-nav-btn:focus-visible {
  opacity: 1;
  color: var(--accent);
  outline: none;
}
.review-nav-btn:active { transform: scale(0.92); }
.review-nav-btn svg { display: block; flex-shrink: 0; }
.review-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--border-strong);
  cursor: pointer;
  transition: background 0.3s var(--ease), transform 0.3s var(--ease);
  position: relative;
  border: none;
  padding: 0;
}
/* Extend hit area to 44×44 without resizing the visible dot.
   Pure black/white image outline rule applies to images, not dots. */
.review-dot::after {
  content: '';
  position: absolute;
  inset: -18px;  /* 8px dot + 18px on each side = 44px hit area */
}
.review-dot.active { background: var(--accent); transform: scale(1.3); }

/* Trust strip — 4 stat numbers with hairline above. Editorial Luxury
   calls for macro-whitespace; was 32px top, now 80px to let it breathe. */
.home-trust {
  padding: 48px 56px 48px;
  max-width: var(--max-w);
  width: 100%;
  margin: 0 auto;
  border-top: 1px solid var(--border);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px;
}
/* Center each stat's number + label within its grid column so the
   overall strip reads balanced on wide viewports. Without this, the
   left-aligned content within 1fr columns makes visual mass skew
   left of the section's geometric center. */
.trust-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}
.trust-stat-n {
  font-family: var(--serif);
  font-size: clamp(32px, 3.2vw, 44px);
  font-weight: 300;
  color: var(--brand);
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.trust-stat-n em { font-style: italic; color: var(--accent); }
.trust-stat-l {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  margin-top: 2px;
  max-width: 240px;
  line-height: 1.4;
}

/* Events we've served — typographic placeholder strip. Swap each
   .client-mark for an <img> with the real wordmark when assets land. */
.clients-strip {
  padding: 32px 56px 48px;
  max-width: var(--max-w);
  width: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  border-top: 1px solid var(--border);
}
.clients-strip-eyebrow {
  font-family: var(--serif);
  font-size: 16px;
  font-style: italic;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
  font-weight: 400;
}
.clients-strip-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 56px;
  flex-wrap: wrap;
}
.client-mark {
  display: inline-flex;
  align-items: center;
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 400;
  color: var(--brand);
  letter-spacing: 0.02em;
  opacity: 0.55;
  text-decoration: none;
  transition: opacity 0.3s var(--ease), filter 0.3s var(--ease);
  white-space: nowrap;
}
.client-mark:hover { opacity: 0.9; }
.client-mark em {
  font-style: italic;
  color: var(--accent);
  opacity: 0.85;
}
/* Real-logo variant: image inside the strip. Desaturated + dimmed at
   rest so brand colors don't fight the cream/navy palette; restored
   on hover for recognition. Transition on the IMG (not the parent
   anchor) so the filter actually animates between states. */
.client-mark-img {
  display: block;
  height: 38px;
  width: auto;
  filter: grayscale(1) contrast(0.9);
  opacity: 0.55;
  transition: filter 0.5s var(--ease), opacity 0.5s var(--ease);
}
.client-mark:hover .client-mark-img,
.client-mark:focus-visible .client-mark-img {
  filter: grayscale(0) contrast(1);
  opacity: 0.95;
}
/* Optical alignment: each logo's bounding box has different padding
   around its wordmark, so geometric centering reads as visually off.
   These nudges line up the wordmarks' visual mass, not the image edges. */
/* Per-logo optical nudges. The Airshow H-variant (3.1:1) has mountain
   silhouettes stacked above the "AIRSHOW" wordmark; the wordmark is
   the heaviest visual mass and sits in the bottom third of the bounding
   box. Measured centroid-to-geom-center delta @38px is ~10–11px. The
   -10px nudge pulls the wordmark up onto the row's centerline alongside
   Cloverdale and ScotFest, which are already balanced (deltas < 2px). */
.client-mark img[src*="cloverdale"]      { transform: translateY(0); }
.client-mark img[src*="abbotsford"]      { transform: translateY(-10px); }
.client-mark img[src*="scotfest"]        { transform: translateY(0); }

/* Site footer — dark aubergine, 4-column. Canonical layout adopted from
   the Webflow footer (s37) as the more appropriate footer pattern. Bronze
   section headings, cream links on a deep-aubergine pane, hairline-divided
   legal row. Social icons retained from the prior contact strip. */
.site-footer {
  background: var(--brand-deep);
  color: var(--cream);
  padding: 64px 56px 32px;
}
.footer-inner {
  max-width: var(--max-w);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 56px;
}
.footer-col { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.footer-heading {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--champagne);
  margin: 0 0 4px;
}
.footer-logo-img {
  display: inline-block;
  height: 40px;
  aspect-ratio: 303 / 84;
  background-color: var(--cream);
  -webkit-mask: url("../photos/brand/logo.png") no-repeat center / contain;
  mask: url("../photos/brand/logo.png") no-repeat center / contain;
}
.footer-text {
  font-size: 14px;
  line-height: 1.6;
  color: color-mix(in srgb, var(--cream) 78%, transparent);
  margin: 0;
  max-width: 44ch;
}
/* Service-area list: middot BETWEEN locations only. site.js tags the first
   item of each wrapped row so its leading separator is dropped; separators are
   ::before (never trailing), so no dot ever sits at a row's start or end. */
.footer-areas { display: flex; flex-wrap: wrap; }
.footer-areas .area::before { content: '·'; margin: 0 0.45em; opacity: 0.6; }
.footer-areas .area:first-child::before,
.footer-areas .area.row-start::before { content: none; }
.footer-links { display: flex; flex-direction: column; gap: 10px; }
.footer-link {
  font-size: 14px;
  color: color-mix(in srgb, var(--cream) 78%, transparent);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.2s var(--ease);
}
.footer-link:hover { color: var(--champagne); }
.footer-follow { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.footer-social { display: flex; gap: 12px; margin-top: 0; }
.footer-social-link {
  width: 40px;
  height: 40px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: color-mix(in srgb, var(--cream) 92%, transparent);
  border: 1px solid color-mix(in srgb, var(--cream) 34%, transparent);
  transition: color 0.25s var(--ease), background-color 0.25s var(--ease), border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.footer-social-link:hover { color: var(--brand-deep); background: var(--champagne); border-color: var(--champagne); }
.footer-social-link:active { transform: scale(0.96); }
.footer-social-link svg { width: 18px; height: 18px; }
.footer-legal {
  max-width: var(--max-w);
  margin: 40px auto 0;
  padding-top: 24px;
  border-top: 1px solid color-mix(in srgb, var(--cream) 12%, transparent);
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 12px;
  color: color-mix(in srgb, var(--cream) 55%, transparent);
}

/* ═══════════════════════════════════════ */
/*  COLLECTION  (the showcase)             */
/* ═══════════════════════════════════════ */
.view-collection {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}
/* Tan intro band (site-wide "text intro" pattern — cream-2) holding the
   eyebrow/title/subtext, full-width above the [filter | cards] body. Matches
   the WF collection hero band; the pattern repeats on gallery + journal. */
.coll-intro {
  background: var(--cream-2);
  padding: 32px 32px 28px;
  flex-shrink: 0;
}
.coll-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 180px 1fr;
  grid-template-rows: minmax(0, 1fr); /* constrain the row to the flex height so
                                         .coll-main scrolls instead of clipping */
  gap: 24px;
  padding: 24px;
  overflow: hidden;
}

/* Filter sidebar — clean minimal list (parity with the WF filter-sidebar):
   a "Category" eyebrow + a vertical button list; active = filled ink pill,
   inactive = plain muted text. The per-pill counts stay as the canonical
   feature WF adopts in PR-C. */
.coll-filter-bezel {
  align-self: start;
  max-height: calc(100vh - var(--nav-h) - var(--mockup-h) - 48px);
  overflow: hidden;
}
.coll-filter {
  overflow-y: auto;
  max-height: calc(100vh - var(--nav-h) - var(--mockup-h) - 60px);
}
/* padding-left matches the pill's inner padding so the eyebrow optically
   heads the pill LABELS, not the pill border (border-flush reads too far left). */
.coll-filter-label { margin-bottom: 14px; padding-left: 12px; }
.filter-opts { display: flex; flex-direction: column; gap: 2px; }
.filter-opt {
  padding: 9px 12px;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  text-align: left;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
  color: var(--muted);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}
.filter-opt:hover { background: rgba(58,40,24,0.05); color: var(--ink); }
.filter-opt.active {
  background: var(--ink);
  color: var(--cream);
}
.filter-opt:active { transform: scale(0.98); }
.filter-opt-count {
  font-size: 12px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.filter-opt.active .filter-opt-count { color: var(--champagne-light); }

.coll-main {
  overflow-y: auto;
  min-height: 0;
  padding-bottom: 32px;
}
.coll-head {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 32px;
}
.coll-head-left { max-width: 720px; }
.coll-head-eyebrow { margin-bottom: 14px; }
.coll-head h2 {
  font-size: clamp(32px, 3.6vw, 44px);
  letter-spacing: -0.025em;
}
.coll-head h2 em { color: var(--accent); }
.coll-head-sub {
  font-size: 14px;
  color: var(--muted);
  margin-top: 10px;
  max-width: 520px;
  line-height: 1.55;
}
.coll-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}

/* Collection card — Double-Bezel showcase.
   No transition on .product itself — the entrance animation inline-sets
   transform, and a CSS transition here makes the items snap down before
   Motion One catches them, producing a visible bounce. Hover effects all
   live on .product-shell (a child), so .product never needs to transition. */
.product {
  position: relative;
}
.product.hidden { display: none; }
.product-shell {
  background: linear-gradient(180deg, rgba(var(--cream-3-rgb),0.40) 0%, rgba(var(--cream-2-rgb),0.75) 100%);
  border-radius: 22px;
  padding: 6px;
  box-shadow: var(--ring-hair), var(--shadow-card);
  transition: box-shadow 0.45s var(--ease), transform 0.45s var(--ease);
}
.product:hover .product-shell {
  box-shadow: var(--ring-hair), var(--shadow-card-hover);
  transform: translateY(-3px);
}
.product.added .product-shell {
  box-shadow: var(--ring-accent), var(--shadow-card);
}
.product-core {
  background: var(--cream);
  border-radius: 16px;
  box-shadow: var(--inset-highlight);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.product-img {
  aspect-ratio: 4/3;
  background: var(--cream-3);
  position: relative;
  overflow: hidden;
  border-radius: 16px 16px 0 0;
}
.product-img img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.9s var(--ease-out);
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}
.product:hover .product-img img { transform: scale(1.04); }
/* Tag chip on collection card photos. Was backdrop-blur — but this lives
   inside a scrolling list with 9 instances, which triggers GPU repaints
   on every scroll frame. Switched to fully opaque cream + ring; visually
   similar (the photo gets covered, not blurred) without the cost. */
.product-tag {
  position: absolute;
  top: 14px;
  left: 14px;
  background: var(--cream);
  padding: 5px 10px;
  border-radius: 999px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  box-shadow: var(--ring-hair), var(--inset-highlight);
}
.product-body {
  padding: 22px 22px 22px;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.product-name {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 400;
  color: var(--ink);
  margin-bottom: 14px;
  letter-spacing: -0.015em;
  line-height: 1.12;
}
.product-specs {
  display: flex;
  gap: 16px;
  margin-bottom: 14px;
  padding: 12px 0 14px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.product-spec { display: flex; align-items: center; gap: 9px; flex: 1; }
.product-spec-icon {
  width: 14px; height: 14px;
  color: var(--accent);
  opacity: 0.9;
  flex-shrink: 0;
}
.product-spec-text { display: flex; flex-direction: column; gap: 1px; line-height: 1.2; }
.product-spec-l {
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
}
.product-spec-v {
  font-weight: 500;
  color: var(--brand);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.product-desc {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.55;
  margin-bottom: 18px;
  flex: 1;
}

/* Add CTA — Button-in-Button trailing icon */
.product-add {
  width: 100%;
  padding: 11px 14px 11px 18px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 999px;
  transition: background 0.3s var(--ease), color 0.3s var(--ease), box-shadow 0.3s var(--ease);
  background: transparent;
  letter-spacing: 0.04em;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
}
.product-add:hover {
  background: var(--accent);
  color: var(--cream);
  box-shadow: 0 6px 18px rgba(184,90,59,0.22);
}
.product-add:active { transform: scale(0.96); }
.product-add .product-add-label { flex: 1; text-align: left; }
.product-add .product-add-icon {
  width: 24px; height: 24px;
  border-radius: 999px;
  background: rgba(184,90,59,0.10);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.3s var(--ease), transform 0.35s var(--ease);
}
.product-add:hover .product-add-icon {
  background: rgba(var(--cream-rgb),0.22);
  transform: translate(2px, -1px);
}
/* Two-icon cross-fade — arrow visible by default, check visible when .added.
   Both icons stay in the DOM so we get true enter+exit transitions
   without the innerHTML hard-toggle that flashed the layout.
   CSS grid stacks both icons in the same cell, place-items:center keeps
   them centered as scale/blur animates without position drift. */
.product-add .product-add-icon { display: grid; place-items: center; }
.product-add .product-add-icon svg {
  width: 12px;
  height: 12px;
  grid-area: 1 / 1;
  transform-origin: center;
  transition: opacity 0.3s cubic-bezier(0.2, 0, 0, 1),
              transform 0.3s cubic-bezier(0.2, 0, 0, 1),
              filter 0.3s cubic-bezier(0.2, 0, 0, 1);
}
.product-add .product-add-icon .icon-arrow {
  opacity: 1;
  transform: scale(1);
  filter: blur(0);
}
.product-add .product-add-icon .icon-check {
  opacity: 0;
  transform: scale(0.25);
  filter: blur(4px);
}
/* Stroke-dasharray draw animation on the check path. pathLength=100 in
   the SVG markup means dasharray:100 / dashoffset:100 hides the path
   regardless of actual rendered length. Default state (dashoffset 0)
   shows a fully-drawn check — important so restored cart items render
   with a visible check on page load, no animation. cart.js sets
   dashoffset to 100 BEFORE toggling .added on user tap, then Motion
   One animates it back to 0 with 220ms delay so the existing icon
   opacity/scale transition (300ms) lands first. The check appears to
   draw on top of the fully-faded-in icon.
   Research basis: /tmp/cart-confirm-microinteraction-research-2026-05-19.md */
.icon-check-path {
  stroke-dasharray: 100;
  /* stroke-dashoffset defaults to 0 = fully drawn */
}
@media (prefers-reduced-motion: reduce) {
  /* Information preserved (check visible); motion stripped. cart.js
     also gates the Motion One animation on this media query. */
  .icon-check-path { stroke-dashoffset: 0 !important; }
}
.product-add.added .product-add-icon .icon-arrow {
  opacity: 0;
  transform: scale(0.25);
  filter: blur(4px);
}
.product-add.added .product-add-icon .icon-check {
  opacity: 1;
  transform: scale(1);
  filter: blur(0);
}
.product-add.added {
  background: var(--brand);
  color: var(--cream);
  border-color: var(--brand);
}
.product-add.added .product-add-icon { background: rgba(201,176,138,0.22); }

/* ═══════════════════════════════════════ */
/*  MY EVENT (cart preview)                */
/* ═══════════════════════════════════════ */
/* My event is now a lightweight cart-preview surface: list items, "Continue
   to quote" CTA, "+ Add more". The full inquiry form lives on /request-a-quote.
   Pre-s23 this view hosted the form (espresso submit-pane). All of that CSS
   has been dropped — the form-host pattern was replaced by the single-page
   RAQ form. */
.view-my-event {
  overflow-y: auto;
  background: linear-gradient(180deg, var(--cream-2) 0%, var(--cream) 100%);
}
.me-cart-shell {
  max-width: 720px;
  margin: 0 auto;
  padding: 56px 48px 64px;
}
.me-head { margin-bottom: 32px; max-width: 560px; }
.me-head .eyebrow { margin-bottom: 14px; }
.me-head h2 {
  font-size: clamp(32px, 3.4vw, 42px);
  margin-bottom: 14px;
}
.me-head h2 em { color: var(--accent); }
.me-head p {
  font-size: 14.5px;
  color: var(--muted);
  line-height: 1.65;
}
.me-empty {
  text-align: center;
  padding: 80px 20px;
}
.me-empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 18px;
  color: var(--accent);
  opacity: 0.85;
}
.me-empty-icon svg { width: 44px; height: 44px; }
.me-empty h3 {
  font-family: var(--serif);
  font-size: 26px;
  font-weight: 400;
  color: var(--ink);
  margin-bottom: 10px;
}
.me-empty p {
  font-size: 14px;
  color: var(--muted);
  margin-bottom: 24px;
  max-width: 380px;
  margin-left: auto;
  margin-right: auto;
  line-height: 1.6;
}

/* My Event items — hairline-divided rows */
.me-items {
  display: flex;
  flex-direction: column;
  background: var(--cream);
  border-radius: 16px;
  box-shadow: var(--ring-hair), 0 4px 14px rgba(58,40,24,0.04);
  overflow: hidden;
}
.me-item {
  display: grid;
  grid-template-columns: 76px 1fr auto;
  gap: 16px;
  align-items: center;
  padding: 14px 16px;
  transition: background 0.25s var(--ease);
  border-bottom: 1px solid var(--border);
}
.me-item:last-child { border-bottom: none; }
.me-item:hover { background: rgba(58,40,24,0.025); }
.me-item-img {
  width: 76px;
  height: 64px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--cream-3);
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}
.me-item-img img { width: 100%; height: 100%; object-fit: cover; }
.me-item-info { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.me-item-tag {
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
}
.me-item-name {
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.me-item-remove {
  color: var(--faint);
  padding: 8px 10px;
  border-radius: 999px;
  transition: color 0.25s var(--ease), background 0.25s var(--ease);
  font-size: 14px;
}
.me-item-remove:hover { color: var(--accent); background: var(--cream-2); }
.me-item-remove:active { transform: scale(0.92); }

/* Continue row — surfaces below the items list once cart is non-empty.
   "Continue to quote" is the primary path; "+ Add more" routes back to
   Collection. The empty-state has its own browse CTA so this row hides
   when items.length === 0. */
.me-continue-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 14px;
  margin-top: 28px;
}
.me-continue-btn {
  padding: 14px 18px 14px 22px;
  border-radius: 14px;
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.me-add-more-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 16px;
  border: 1px dashed var(--border-strong);
  border-radius: 12px;
  font-size: 13px;
  color: var(--brand);
  font-weight: 500;
  cursor: pointer;
  transition: background 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.me-add-more-link:hover { background: rgba(58,40,24,0.04); border-color: var(--accent); color: var(--accent); }
.me-add-more-link:active { transform: scale(0.98); }

/* ═══════════════════════════════════════ */
/*  GALLERY                                */
/* ═══════════════════════════════════════ */
.view-gallery { padding: 36px 48px 48px; max-width: var(--max-w); margin: 0 auto; }
.gallery-head {
  display: flex;
  justify-content: space-between;
  align-items: end;
  margin-bottom: 28px;
  gap: 32px;
}
.gallery-head-left { max-width: 720px; }
.gallery-head .eyebrow { margin-bottom: 12px; }
.gallery-head h2 {
  font-size: clamp(32px, 3.4vw, 44px);
  letter-spacing: -0.025em;
}
.gallery-head h2 em { color: var(--accent); }
.gallery-head-sub {
  font-size: 14px;
  color: var(--muted);
  margin-top: 10px;
  max-width: 520px;
  line-height: 1.55;
}
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 220px;
  grid-auto-flow: dense;
  gap: 14px;
}
/* Bento rhythm — tile footprints come from POSITION (nth-child), not per-item
   data, so the grid tessellates and reflows at ANY photo count (3 or 30).
   grid-auto-flow:dense backfills the gaps the feature tiles leave. This is the
   whole point of the CMS-bound gallery: the client adds photos in the Editor
   and the layout stays intentional with zero manual work. Translates 1:1 to
   the Webflow Collection List — nth-child targets the repeating .gallery-img
   items directly, so NO per-item span field and NO span-class JS are needed. */
.gallery-grid > .gallery-img:nth-child(8n + 1) { grid-column: span 2; grid-row: span 2; } /* feature 2×2 */
.gallery-grid > .gallery-img:nth-child(8n + 4) { grid-column: span 2; }                    /* wide 2×1 */
.gallery-grid > .gallery-img:nth-child(8n + 6) { grid-row: span 2; }                        /* tall 1×2 */
/* Exactly-3 photos (launch / low-count CMS state): the feature 2×2 forces a
   2-row block, but two 1×1 tiles fill only row 1 — leaving the bottom-right of
   the 4-col grid empty. Give the two trailing tiles full height so feature +
   two tall tiles fill a clean 4×2 block. Scoped to the 4-col range (>1180px):
   the 3-col and 2-col breakpoints already tessellate at 3 tiles, and span-2
   there would break them. :has() quantity-query limits it to the exactly-3
   case; any other count is untouched (no-op). */
@media (min-width: 1181px) {
  .gallery-grid:has(> .gallery-img:nth-child(3):last-child) > .gallery-img:nth-child(2),
  .gallery-grid:has(> .gallery-img:nth-child(3):last-child) > .gallery-img:nth-child(3) {
    grid-row: span 2;
  }
}
/* Exactly-2 photos: on the 4-col desktop grid the second tile becomes a
   matching 2×2 so feature + partner fill a clean 4×2 block (no dead cells);
   on the 2-col phone grid the second tile spans full width beneath the
   feature. The 3-col mid range tessellates acceptably as feature + one tile. */
@media (min-width: 1181px) {
  .gallery-grid:has(> .gallery-img:nth-child(2):last-child) > .gallery-img:nth-child(2) {
    grid-column: span 2;
    grid-row: span 2;
  }
}
@media (max-width: 1023px) {
  .gallery-grid:has(> .gallery-img:nth-child(2):last-child) > .gallery-img:nth-child(2) {
    grid-column: span 2;
  }
}
/* Gallery — same fix as .product: removed transform from .gallery-img's
   transition list so the entrance animation doesn't double-bounce. Hover
   lift now lives on the IMG (combined with the existing scale) — same
   optical effect, no transform-on-wrapper conflict. */
.gallery-img {
  overflow: hidden;
  border-radius: 14px;
  background: var(--cream-3);
  cursor: pointer;
  position: relative;
  box-shadow: var(--ring-hair);
  transition: box-shadow 0.45s var(--ease);
  /* iOS Safari: suppress the double-tap-to-zoom gesture on thumbnails so
     a single tap reliably opens PhotoSwipe instead of the OS smart-zoom
     (which on small images can put the tab into a wedged zoom state that
     crashes Safari when the user then pinches further). */
  touch-action: manipulation;
}
/* Drop the default link underline / colour shift on gallery anchors —
   the thumbnail IS the affordance. */
.gallery-img, .gallery-img:visited { color: inherit; text-decoration: none; }

/* PhotoSwipe lightbox: match the gallery thumbnail's 14px corner radius
   on the main image and the placeholder so the lightbox feels continuous
   with the grid (instead of going from rounded thumb → sharp fullscreen).
   Bonus: smooths over an iOS Safari half-state where PhotoSwipe parks the
   placeholder visible after a vertical-drag gesture — at least it looks
   intentional rather than broken if it ever surfaces again. */
.pswp__img,
.pswp__img--placeholder {
  border-radius: 14px;
}
/* Lightbox backdrop — aubergine tint + blur (lead call s53; the approved
   A/B option B). PhotoSwipe colours .pswp__bg from --pswp-bg (tinted
   aubergine here) at bgOpacity 0.82 (set in the lightbox config), and the
   backdrop-filter blurs the gallery behind it. Blur is gated OFF on touch /
   ≤1023px — same iOS-Safari backdrop-filter caution as the nav (§10). */
.pswp { --pswp-bg: var(--brand-deep); }
.pswp__bg {
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  backdrop-filter: blur(8px) saturate(120%);
}
@media (max-width: 1023px), (pointer: coarse) {
  .pswp__bg { -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.gallery-img img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.7s var(--ease-out), filter 0.4s var(--ease);
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}
.gallery-img:hover {
  box-shadow: var(--ring-hair), var(--shadow-card-hover);
}
.gallery-img:hover img { transform: scale(1.05); filter: brightness(1.04); }
/* Press feedback for tappable gallery tiles (open lightbox on click). */
.gallery-img:active { transform: scale(0.99); }

/* ═══════════════════════════════════════ */
/*  REQUEST A QUOTE (single-page form)     */
/* ═══════════════════════════════════════ */
/* 3-block single-page form, locked in s23 after independent UX research
   (project_wizard_research_findings). Replaces the prior 5-step wizard.
   Required: name + (email OR phone) + date + location. Guest count is
   "strongly suggested" — visually emphasized but not required at submit.
   Phone uses IMask for (XXX) XXX-XXXX formatting; ext optional. */
.view-quote {
  overflow-y: auto;
  background: linear-gradient(180deg, var(--cream-2) 0%, var(--cream) 100%);
}
.raq-form { display: block; }
.raq-shell {
  max-width: 720px;
  margin: 0 auto;
  padding: 56px 48px 64px;
}

.raq-head { text-align: center; margin-bottom: 40px; }
.raq-head .eyebrow { margin-bottom: 14px; }
.raq-head h2 {
  font-size: clamp(32px, 3.4vw, 44px);
  max-width: 580px;
  margin: 0 auto 14px;
  letter-spacing: -0.025em;
}
.raq-head h2 em { color: var(--accent); }
.raq-head-sub {
  font-size: 14.5px;
  color: var(--muted);
  max-width: 540px;
  margin-left: auto;
  margin-right: auto;
  line-height: 1.65;
}
.raq-head-phone { color: var(--accent); white-space: nowrap; }
.raq-head-phone strong { font-weight: 500; }

/* Block — fieldset reset + spacing */
.raq-block {
  border: 0;
  padding: 28px 0 0;
  margin: 0 0 12px;
  border-top: 1px solid var(--border);
}
.raq-block:first-of-type { border-top: 0; padding-top: 8px; }
.raq-block-title {
  font-family: var(--serif);
  font-size: 26px;
  /* Site headline convention is serif 300 (h1-h4 in base.css and
     .feat-hero-title). <legend> doesn't inherit those rules, so they
     get restated here. */
  font-weight: 300;
  color: var(--ink);
  letter-spacing: -0.015em;
  margin-bottom: 6px;
  padding: 0;
  display: block;
  /* opsz pinned to 14 globally on body — matches the Webflow static cut. */
}
/* Italic emphasis fragment — 2-4 emotional words. Matches the design
   system rule on every headline. Source Serif 4's variable italic axis
   covers W300/400 (loaded via Google Fonts); 300 here matches the title
   weight. */
.raq-block-title em {
  font-style: italic;
  font-weight: 300;
  color: var(--accent);
}
.raq-block-sub {
  font-size: 13.5px;
  color: var(--muted);
  margin-bottom: 22px;
  line-height: 1.55;
}

/* Field — shared container for input + label */
.raq-field {
  background: var(--cream);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 13px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease), background 0.25s var(--ease);
  min-width: 0;
  margin-bottom: 12px;
}
.raq-field:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(184,90,59,0.10);
}
.raq-field.is-error,
.raq-field.is-error:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(184,90,59,0.16);
}
.raq-field label {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  /* Was --faint (2.65:1 on cream — WCAG AA fail). Swapped to --muted
     (6.0:1) per s23 a11y review. */
  color: var(--muted);
  font-weight: 500;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.raq-field-hint {
  font-size: 11px;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--muted);
  font-weight: 400;
  opacity: 0.95;
}
.raq-field input,
.raq-field textarea,
.raq-field select {
  background: transparent;
  border: none;
  color: var(--ink);
  /* 16px is iOS Safari's auto-zoom threshold for form inputs — below
     this, tapping into a field triggers a viewport zoom on iPhone.
     Setting 16px prevents the zoom without resorting to
     `maximum-scale=1` on the meta viewport (which blocks pinch-zoom
     and fails WCAG 1.4.4 Resize Text). Reference:
     https://stackoverflow.com/questions/2989263/disable-auto-zoom-in-input-text-tag-safari-on-iphone */
  font-size: 16px;
  padding: 0;
  outline: none;
  /* W400 to match body. The user's typed string is not emphasis —
     hierarchy in the form is carried by size + case + color, not
     weight (Refactoring UI §"Establishing a visual hierarchy" — don't
     use weight to create hierarchy when size and color already do the
     work). Editorial Luxury convention: Aesop, Stripe Press, Linear,
     NYT product all sit filled values at body weight. W500-on-filled
     is a Material/Android convention and reads wrong here. */
  font-weight: 400;
  /* Explicit `var(--sans)` — iOS Safari is unreliable about inheriting
     `font-family` into form controls; declares a UA system font in some
     cases (notably <input type="date">, where it also center-aligns the
     value). Forcing Geist here keeps the typed value consistent with
     the rest of the page. */
  font-family: var(--sans);
  line-height: 1.4;
  width: 100%;
  min-width: 0;
}
.raq-field textarea {
  resize: vertical;
  min-height: 96px;
  line-height: 1.55;
}
.raq-field input::placeholder,
.raq-field textarea::placeholder { color: var(--faint); font-weight: 400; }

/* iOS Safari renders <input type="date"> with its own system font,
   centered alignment, and a larger size by default — visible as the
   centered "Sep 19, 2026" rendering. Strip the appearance + force our
   font to match the other fields. */
.raq-field input[type="date"] {
  text-align: left;
  -webkit-appearance: none;
  appearance: none;
  font-family: var(--sans);
  font-size: 16px;
  /* iOS adds its own min-height; lock it to match other inputs. */
  min-height: 22px;
}
.raq-field input[type="date"]::-webkit-date-and-time-value {
  text-align: left;
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 400;
}
/* iOS 14+ exposes granular date sub-pseudos. The single
   ::-webkit-date-and-time-value rule above covers most cases, but
   separator + text segments can fall back to SF System on some iOS
   Safari versions without these explicit rules. Belt-and-suspenders. */
.raq-field input[type="date"]::-webkit-datetime-edit,
.raq-field input[type="date"]::-webkit-datetime-edit-fields-wrapper,
.raq-field input[type="date"]::-webkit-datetime-edit-text,
.raq-field input[type="date"]::-webkit-datetime-edit-month-field,
.raq-field input[type="date"]::-webkit-datetime-edit-day-field,
.raq-field input[type="date"]::-webkit-datetime-edit-year-field {
  font-family: var(--sans);
  font-weight: 400;
  color: var(--ink);
}

/* iOS auto-fill paints a yellow background + uses a system-font color
   that overrides our --ink. Box-shadow inset trick is the standard
   workaround — it forces our cream-on-ink rendering through autofill.
   Explicit font-weight !important — autofill re-asserts the UA weight
   (usually 500) and would re-introduce the heavy filled-value problem. */
.raq-field input:-webkit-autofill,
.raq-field input:-webkit-autofill:hover,
.raq-field input:-webkit-autofill:focus,
.raq-field input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0 1000px var(--cream) inset;
  caret-color: var(--ink);
  font-weight: 400 !important;
  transition: background-color 5000s ease-in-out 0s;
}

/* Strongly-suggested treatment — soft clay tint to draw the eye without
   requiring the field. Used on guest count. */
.raq-field-emphasis {
  background: linear-gradient(180deg, rgba(184,90,59,0.04), rgba(184,90,59,0.02));
  border-color: rgba(184,90,59,0.20);
}
.raq-field-emphasis label { color: var(--accent); }

/* Two-column row layout (collapses to one column on mobile via mobile.css) */
.raq-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 12px;
}

/* Phone field — main input + small ext input */
.raq-phone-row {
  display: flex;
  gap: 10px;
  align-items: center;
  width: 100%;
}
.raq-phone-row input[type="tel"] {
  flex: 1 1 auto;
}
.raq-phone-ext-input {
  flex: 0 0 56px;
  /* Match other inputs at 16px to avoid iOS auto-zoom; the visual
     size difference vs. surrounding labels is fine here. */
  font-size: 16px !important;
  color: var(--muted) !important;
  font-weight: 500;
  text-align: center;
  border-left: 1px solid var(--border) !important;
  padding-left: 10px !important;
}

/* Inline error message under a field */
.raq-error {
  font-size: 12px;
  color: var(--accent);
  margin-top: 2px;
  line-height: 1.4;
  font-weight: 500;
}
.raq-error-block {
  margin-top: 8px;
  padding: 10px 14px;
  background: rgba(184,90,59,0.08);
  border-radius: 8px;
}

/* Chip group */
.raq-chipgroup {
  margin-bottom: 18px;
}
.raq-chipgroup-label {
  display: block;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  margin-bottom: 10px;
}
.raq-chipgroup-optional {
  text-transform: none;
  letter-spacing: 0.02em;
  font-weight: 400;
  opacity: 0.85;
}
.raq-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.raq-chip {
  /* WCAG 2.5.5 — 44×44px hit target. Visible padding gives ~32px tall
     but content-box maths from font + line-height + padding keeps the
     touch area above 44px on mobile. */
  min-height: 44px;
  padding: 10px 16px;
  background: var(--cream);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease), box-shadow 0.25s var(--ease), transform 0.25s var(--ease);
  letter-spacing: 0.01em;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
}
.raq-chip:hover {
  border-color: var(--accent);
  color: var(--accent);
  transform: translateY(-1px);
}
.raq-chip.selected {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--cream);
  box-shadow: var(--inset-highlight-dark), 0 4px 12px rgba(58,40,24,0.18);
}
.raq-chip.selected:hover { color: var(--cream); }
.raq-chip:active { transform: scale(0.97); }

/* Conditional reveal input — "Other" chip exposes this */
.raq-chip-reveal {
  width: 100%;
  margin-top: 10px;
  padding: 12px 16px !important;
  background: var(--cream);
  border: 1px solid var(--border);
  border-radius: 12px;
  /* 16px to prevent iOS auto-zoom — same rule as .raq-field inputs. */
  font-size: 16px;
  color: var(--ink);
  font-family: var(--sans);
  outline: none;
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.raq-chip-reveal:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(184,90,59,0.10);
}
.raq-chip-reveal::placeholder { color: var(--faint); }

/* Block 3 — adaptive cart preview */
.raq-want-cart {
  background: var(--cream);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 18px 20px;
  margin-bottom: 16px;
}
.raq-cart-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.raq-cart-count {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
}
.raq-cart-edit {
  font-size: 13px;
  color: var(--accent);
  cursor: pointer;
  font-weight: 500;
  border-bottom: 1px dotted transparent;
  transition: border-color 0.2s var(--ease);
}
.raq-cart-edit:hover { border-bottom-color: var(--accent); }
.raq-cart-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.raq-cart-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  font-size: 14.5px;
  color: var(--ink);
}
.raq-cart-list li::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  opacity: 0.7;
}

/* Block 3 — chip alternative + browse link */
.raq-want-chips { margin-bottom: 16px; }
.raq-browse-link {
  display: inline-block;
  margin-top: 14px;
  font-size: 13px;
  /* Demoted from clay → muted per s23 design review: keep clay on the
     submit CTA so it reads as the primary action, not competing with
     this secondary link. */
  color: var(--muted);
  cursor: pointer;
  font-weight: 500;
  border-bottom: 1px dotted transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.raq-browse-link:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* Submit row */
.raq-submit-row {
  margin-top: 36px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
/* RAQ submit — the form's climax CTA. Editorial-pattern primary
   action: wide presence on mobile (full-row, content centered), capped
   on desktop so it doesn't sprawl. justify-content: center is the
   important bit — the inherited .btn-primary flex-start would leave a
   visible gap inside the button between text and arrow icon at this
   width. We carry the wide-button pattern only for climax/commit
   actions (submit, hero CTAs); navigation/inline CTAs stay content-
   snug at the .btn-primary base size. */
.raq-submit {
  width: 100%;
  max-width: 360px;
  padding: 18px 32px;
  font-size: 15px;
  letter-spacing: 0.02em;
  justify-content: center;
  cursor: pointer;
}
.raq-submit-reassurance {
  /* Curator's-note tone — serif italic at the foot of the form
     instead of Geist-synthesized oblique (Geist italic isn't loaded;
     the browser would fake the slant otherwise). Reads as editorial
     reassurance rather than a regular UI line. */
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: 14px;
  color: var(--muted);
  letter-spacing: 0;
}

/* ═══════════════════════════════════════ */
/*  CORPORATE EVENTS                       */
/* ═══════════════════════════════════════ */
.view-corporate { display: flex; flex-direction: column; }

/* Corporate hero — same architecture as .hero but a shade more
   restrained: shorter min-height (this isn't the home page), still
   uses the parallax bg pattern + cream-on-image text stack. */
.corp-hero {
  position: relative;
  min-height: 440px;
  overflow: hidden;
  display: flex;
  align-items: end;
  flex-shrink: 0;
}
/* Vignette scrim — concentrated bottom-left where the text sits so it
   reads cleanly over the lighter tent-canvas portion of the photo
   without dulling the brightest parts of the image (the stage lighting
   and city skyline). Sits above .corp-hero-bg, below .corp-hero-inner. */
.corp-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(0,0,0,0.28) 0%, rgba(0,0,0,0.12) 30%, rgba(0,0,0,0.55) 62%, rgba(0,0,0,0.90) 100%),
    linear-gradient(90deg, rgba(0,0,0,0.70) 0%, rgba(0,0,0,0.38) 55%, rgba(0,0,0,0.06) 85%);
  z-index: 0;
  pointer-events: none;
}
.corp-hero-bg, .corp-hero-bg img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.corp-hero-bg img {
  transform: scale(1.02);
  /* Concert shot is already twilight-dark; saturation bump keeps the
     purple stage lighting vivid against the navy sky, no brightness cut. */
  filter: saturate(1.08);
  object-position: 50% 50%;
  transition: transform 8s var(--ease-out);
}
.view-corporate.active .corp-hero-bg img { transform: scale(1); }
.corp-hero-inner {
  position: relative;
  z-index: 1;
  padding: 80px 56px 56px;
  max-width: var(--max-w);
  width: 100%;
  margin: 0 auto;
  color: var(--cream);
}
.corp-hero-content { max-width: 720px; }
.corp-hero-content .eyebrow {
  color: var(--cream);
  margin-bottom: 16px;
  text-shadow: 0 1px 3px rgba(0,0,0,0.7), 0 1px 8px rgba(0,0,0,0.45);
}
.corp-hero h2 {
  font-size: clamp(40px, 4.6vw, 64px);
  color: var(--cream);
  margin-bottom: 22px;
  line-height: 1.06;
  font-weight: 400;
  letter-spacing: -0.02em;
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.45),
    0 2px 6px rgba(0, 0, 0, 0.55),
    0 4px 14px rgba(0, 0, 0, 0.35);
}
.corp-hero h2 em { color: var(--champagne-light); font-weight: 600; font-style: normal; }
.corp-hero-sub {
  font-size: 17px;
  line-height: 1.6;
  color: var(--cream);
  max-width: 580px;
  font-weight: 500;
  opacity: 0.96;
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.40),
    0 2px 6px rgba(0, 0, 0, 0.50);
}

/* Differentiators — 3-up grid of cards. Each card carries the gravity
   of the page: tag, headline, body, and (optionally) a pull-quote that
   gives the abstract claim a name attached to it. */
.corp-diff {
  padding: 72px 56px 32px;
  max-width: var(--max-w);
  width: 100%;
  margin: 0 auto;
}
.corp-diff-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
.corp-diff-card {
  background: var(--cream-2);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 28px 26px 26px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.corp-diff-tag {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}
.corp-diff-card h3 {
  font-family: var(--serif);
  font-size: 22px;
  line-height: 1.2;
  color: var(--brand);
  margin: 0;
  font-weight: 400;
  letter-spacing: -0.01em;
}
.corp-diff-card p {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--muted);
  margin: 0;
}
.corp-diff-quote {
  margin: 8px 0 0;
  padding: 16px 0 0;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.corp-diff-quote blockquote {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  font-style: italic;
  margin: 0;
}
.corp-diff-quote figcaption {
  font-size: 12px;
  color: var(--faint);
  letter-spacing: 0.04em;
}

/* Corporate reviews carousel — mirrors home-reviews visually but uses
   prefixed class names so the global review JS doesn't double-control
   both carousels. Independent state, independent rotation. */
.corp-reviews {
  padding: 80px 56px 96px;
  max-width: 980px;
  width: 100%;
  margin: 0 auto;
  text-align: center;
  position: relative;
  border-top: 1px solid var(--border);
  margin-top: 32px;
}
.corp-reviews-eyebrow { display: inline-flex; margin-bottom: 28px; }
/* All inner widget rules (.review-stage / .review-slide /
   .review-text / .review-meta / .review-dots / .review-dot) live with
   the home-reviews section above and apply here via the same shared
   classes. .corp-reviews above only defines section-level styling
   (padding, border, eyebrow). */

/* ═══════════════════════════════════════ */
/*  JOURNAL INDEX                             */
/* ═══════════════════════════════════════ */
.view-journal { padding: 36px 48px 64px; max-width: var(--max-w); margin: 0 auto; }
.journal-head { display: flex; justify-content: space-between; align-items: end; margin-bottom: 36px; gap: 32px; }
.journal-head-left { max-width: 720px; }
.journal-head .eyebrow { margin-bottom: 12px; }
.journal-head h2 {
  font-size: clamp(32px, 3.4vw, 44px);
  letter-spacing: -0.025em;
  text-wrap: balance;
}
.journal-head h2 em { color: var(--accent); }
.journal-head-sub {
  font-size: 14px;
  color: var(--muted);
  margin-top: 10px;
  max-width: 560px;
  line-height: 1.55;
  text-wrap: pretty;
}

/* Category filter — horizontal chip strip under the journal head.
   Scoped class (.journal-filter-opt), NOT the collection sidebar's
   .filter-opt, so the global collection-filter handler ignores it. */
.journal-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 28px;
}
.journal-filter-opt {
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 13px;
  color: var(--ink);
  background: rgba(58, 40, 24, 0.05);
  cursor: pointer;
  transition: background 0.25s var(--ease), color 0.25s var(--ease), transform 0.15s var(--ease);
}
.journal-filter-opt:hover { background: rgba(58, 40, 24, 0.1); }
.journal-filter-opt.active {
  background: var(--brand);
  color: var(--cream);
  box-shadow: var(--inset-highlight-dark);
}
.journal-filter-opt:active { transform: scale(0.97); }
.journal-card.hidden { display: none; }

.journal-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
}

/* Journal card — mirrors .feat-card structure but stacks image-over-body
   (vs side-by-side) so excerpts have room to breathe. Shared by the
   journal index and the optional home "Latest from the journal" teaser. */
.journal-card {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  cursor: pointer;
  background: var(--cream-2);
  box-shadow: var(--ring-hair), var(--shadow-card);
  /* transform transition is scoped to .has-entered so it doesn't race with
     Motion One's WAAPI entrance animation. Firefox in particular composites
     a stray CSS transition between WAAPI frames, producing a brief
     down-then-snap-up artifact on first navigate-to-journal after refresh. */
  transition: box-shadow 0.35s var(--ease);
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
}
.journal-card.has-entered {
  transition: box-shadow 0.35s var(--ease), transform 0.35s var(--ease);
}
.journal-card:hover {
  box-shadow: var(--ring-hair), var(--shadow-card-hover);
  transform: translateY(-2px);
}
.journal-card:active { transform: translateY(-2px) scale(0.99); }
.journal-card-img { overflow: hidden; aspect-ratio: 16 / 10; }
.journal-card-img img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.9s var(--ease-out);
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}
.journal-card:hover .journal-card-img img { transform: scale(1.05); }
.journal-card-body {
  padding: 22px 24px 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.journal-card-tag {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
}
.journal-card-title {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.01em;
  line-height: 1.2;
  text-wrap: balance;
  margin: 0;
  /* Reserve + cap at 3 lines so cards hold a fixed size regardless of title
     length (mirrors the Webflow journal-card-title clamp). */
  min-height: 3.6em;
  max-height: 3.6em;
  overflow: hidden;
}
.journal-card-excerpt {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--muted);
  text-wrap: pretty;
  margin: 0;
  /* Reserve 3 lines so cards hold a fixed size regardless of excerpt length. */
  min-height: 4.65em;
  /* Clamp to keep card heights aligned in the grid without crushing
     long titles. 3 lines reads as a paragraph, more starts to feel padded. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.journal-card-meta {
  font-size: 12px;
  color: var(--faint);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  margin-top: auto;
  padding-top: 4px;
  font-variant-numeric: tabular-nums;
}
.journal-card-author { color: var(--muted); font-weight: 500; }
.journal-card-sep { color: var(--border-strong); }

/* "Latest from the journal" — home teaser. Reuses .journal-card. */
.home-journal {
  padding: 64px 56px 36px;
  max-width: var(--max-w);
  width: 100%;
  margin: 0 auto;
}
.home-journal-head {
  display: flex;
  justify-content: space-between;
  align-items: end;
  margin-bottom: 32px;
  gap: 32px;
}
.home-journal-head h2 {
  font-size: clamp(28px, 3.2vw, 42px);
  max-width: 560px;
  text-wrap: balance;
}
.home-journal-head h2 em { color: var(--accent); }
.home-journal-sub {
  font-size: 14px;
  color: var(--muted);
  margin-top: 10px;
  max-width: 560px;
  line-height: 1.55;
  text-wrap: pretty;
}
.home-journal-link {
  font-size: 12px;
  color: var(--brand);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  transition: color 0.25s var(--ease);
  flex-shrink: 0;
}
.home-journal-link::after {
  content: '';
  width: 0;
  height: 0;
  display: none;
  background: currentColor;
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 0.25s var(--ease);
}
.home-journal-link:hover { color: var(--accent); }
.home-journal-link:hover::after { transform: scaleX(1); }
.home-journal-grid {
  display: grid;
  /* minmax(0,1fr) not 1fr: 1fr's implicit min-content floor lets the card
     image refuse to shrink on iOS Safari, overflowing the row and cutting off
     the 3rd card in iPhone landscape. minmax(0,...) lets tracks shrink. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}

/* ═══════════════════════════════════════ */
/*  JOURNAL POST (template shape)             */
/* ═══════════════════════════════════════ */
/* .view-journal-post stays as default block layout. Earlier flex-column wrapping
   collapsed .journal-post-hero's aspect-ratio-derived height to 0 (the inner
   <img height:100%> created a circular sizing dependency the flex algorithm
   resolved to nothing). Block layout lets aspect-ratio resolve normally. */

.journal-post-hero {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 560px;
  overflow: hidden;
  background: var(--cream-2);
}
.journal-post-hero picture,
.journal-post-hero img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.journal-post-hero img {
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}

.journal-post-shell {
  max-width: 760px;
  width: 100%;
  margin: 0 auto;
  padding: 80px 24px 120px;
}

.journal-post-header { margin-bottom: 40px; }
.journal-post-header .eyebrow { display: inline-flex; margin-bottom: 14px; }
.journal-post-header h1 {
  font-size: clamp(32px, 4.4vw, 56px);
  font-weight: 300;
  letter-spacing: -0.025em;
  line-height: 1.05;
  color: var(--ink);
  margin: 0 0 20px;
  text-wrap: balance;
}
.journal-post-header h1 em { color: var(--accent); font-style: italic; }
.journal-post-meta {
  font-family: var(--sans);
  font-size: 15px;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  font-variant-numeric: tabular-nums;
}
.journal-post-author { color: var(--ink); font-weight: 500; }
.journal-post-sep { color: var(--border-strong); }

/* Rich-text body: Source Serif 4 19px / 1.65 for editorial reading register.
   Body copy capped at 65ch for measure; figures, blockquotes, and rules
   span the wider 760px shell. Long-form selectors scoped under
   .journal-post-body so the same markup pattern can be reused inside any
   future long-form view (about, FAQ deep-dive) without leaking globally. */
.journal-post-body {
  font-family: var(--serif);
  font-size: 19px;
  line-height: 1.65;
  font-weight: 400;
  color: var(--ink);
}
.journal-post-body > * + * { margin-top: 1.4em; }
.journal-post-body p,
.journal-post-body ul,
.journal-post-body ol {
  max-width: 65ch;
}
.journal-post-body p { text-wrap: pretty; margin: 0; }
.journal-post-body > p:first-of-type,
.journal-post-body .journal-post-lede {
  font-size: 22px;
  line-height: 1.5;
  color: var(--ink);
}
/* WP-imported posts use <br>-heavy paragraph wrapping (Word-paste habit
   carried through the migration). At this body size the line breaks read
   choppy; kill them inside the rich-text region. */
.journal-post-body br { display: none; }
.journal-post-body h2 {
  font-family: var(--serif);
  font-size: clamp(24px, 2.4vw, 30px);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--ink);
  margin-top: 2.2em;
  text-wrap: balance;
}
.journal-post-body h2 em { color: var(--accent); }
.journal-post-body h3 {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--ink);
  margin-top: 1.8em;
}
.journal-post-body ul,
.journal-post-body ol {
  padding-left: 1.4em;
  margin: 0;
}
.journal-post-body li + li { margin-top: 0.6em; }
.journal-post-body li::marker { color: var(--accent); }
.journal-post-body strong { color: var(--brand); font-weight: 600; }
.journal-post-body a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(184,90,59,0.4);
  transition: text-decoration-color 0.2s var(--ease);
}
.journal-post-body a:hover { text-decoration-color: var(--accent); }

.journal-post-body .journal-post-figure {
  margin: 2em 0;
}
.journal-post-body .journal-post-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 14px;
  outline: 1px solid rgba(0,0,0,0.1);
  outline-offset: -1px;
}
.journal-post-body .journal-post-figure figcaption {
  margin-top: 10px;
  font-family: var(--sans);
  font-size: 14px;
  color: var(--faint);
  font-style: italic;
  line-height: 1.5;
  max-width: 65ch;
}

.journal-post-body blockquote {
  margin: 2em 0;
  padding: 4px 0 4px 28px;
  border-left: 2px solid var(--accent);
  font-family: var(--serif);
  font-size: 24px;
  line-height: 1.4;
  font-style: italic;
  color: var(--ink);
  max-width: 65ch;
}
.journal-post-body blockquote p { margin: 0; max-width: none; }
.journal-post-body blockquote cite {
  display: block;
  margin-top: 14px;
  font-family: var(--sans);
  font-style: normal;
  font-size: 14px;
  color: var(--faint);
  letter-spacing: 0.02em;
}

.journal-post-body .journal-post-rule {
  margin: 3em 0;
  border: 0;
  height: 1px;
  background: var(--border);
}

.journal-post-body .journal-post-back {
  font-family: var(--sans);
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}
.journal-post-body .journal-post-back a {
  color: var(--brand);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.25s var(--ease);
}
.journal-post-body .journal-post-back a:hover { color: var(--accent); }
