/* Custom serif — WOFF2 (universal modern-browser support; smallest payload).
   Only Regular + Bold WOFF2s exist; italic text gets browser-synthesized
   slant from the regular face (enabled via font-synthesis below). */
@font-face {
  font-family: "RB5 Serif";
  src: url("fonts/RB5Serif-Regular.woff2?v=354") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "RB5 Serif";
  src: url("fonts/RB5Serif-Bold.woff2?v=354") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

html {
  font-synthesis: style weight;
  font-size: 15px;
}

/* Root font-size bumps for bigger-than-phone viewports.
   Every text class uses rem-based clamps, so raising the root size
   scales every body / heading / button proportionally without having
   to touch the individual rules. vw factors inside clamp() aren't
   affected, which keeps mid-range fluid scaling intact — only the
   rem-based min/max bounds grow.

   Gated on (min-height: 600px) so landscape phones (height < 600)
   don't get the desktop-tier sizes and overflow their tight modals. */
@media (min-width: 768px) and (min-height: 600px) {
  html {
    font-size: 16px;
  }
}

@media (min-width: 1280px) and (min-height: 600px) {
  html {
    font-size: 17px;
  }
}

:root {
  --bg-0: #050410;

  --indigo-deep: #14104a;
  --indigo: #2a1f7a;
  --indigo-soft: #4a3aa8;

  --gold: #c9a449;
  --gold-bright: #f0d875;
  --gold-deep: #8a6d1f;

  --bone: #f4eac1;

  --green-1: #0f8a5f;
  --green-2: #1eff7e;
  --green-3: #7affd6;
  --green-4: #00d4a8;

  /* card-w is constrained by viewport-height too so the full deck (cards +
     vertical breathing room + masthead/footer) reliably fits in shorter
     viewports without the rotated corners getting clipped at the top edge. */
  /* Mobile/tablet: cards stack vertically, so no vh cap is needed —
     letting them size by viewport width gives bigger, more legible art.
     The vh cap stays on desktop (in the media query below) because the
     row layout requires the deck to fit horizontally. */
  /* Mobile/tablet: cards stack vertically. Width is constrained by viewport
     height too so the first card finishes well above the bottom edge and
     the *top* of the second card peeks in — visual cue to scroll. 50vh / 1.5
     accounts for the logo + (potentially wrapping) invocation eating ~35–40%
     of viewport height, leaving room for the next card's peek. */
  --card-w: min(86vw, 380px, calc(50vh / 1.5));
  --card-h: calc(var(--card-w) * 1.5);
  --card-radius: 6px;

  /* RB5 Serif is the only loaded face. Georgia / Times New Roman are
     system-serif fallbacks only if the custom WOFF2 fails to fetch. */
  --font-display: "RB5 Serif", Georgia, "Times New Roman", serif;
  --font-body: "RB5 Serif", Georgia, "Times New Roman", serif;

  --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
  --ease-in-out: cubic-bezier(0.65, 0.05, 0.35, 1);

  --grow-dur: 0.72s;
  --flip-dur: 0.72s;

  /* Velvet bg — single URL var so the debug panel can swap images live
     without touching the rest of the body background stack. */
  --velvet-bg: url("images/linen2.jpg?v=278");

  /* Card art filter — defaults are no-ops (1.00), but the debug panel can
     override them inline on <body> for live tuning. Saturate sits last so
     it doesn't interact unexpectedly with the brightness/contrast lift. */
  --card-bright: 1.85;
  --card-contrast: 1.12;
  --card-sat: 0.64;
  --card-warmth: 0.00;
  /* Card-art "whites" tint — overlay layer (mix-blend-mode: darken) lets
     you re-color the bright/white pixels in the card PNG without touching
     dark areas. Defaults to white so the overlay is a no-op. */
  --card-white-tint: #fffcdb;

  /* Card hover — only the inset gold rim brightens; no outer halo. The
     border-color shift to var(--gold) on hover is the main affordance. */
  --card-hover-rim-op: 0.25;

  /* Logo art filter — tunable via the debug panel. Defaults match the
     committed values (mute the colorful badge into the cards' palette). */
  --logo-bright: 0.95;
  --logo-contrast: 1.20;
  --logo-sat: 0.77;
  --logo-warmth: 0;

  /* Modal heading-image color (shared across all three cards — one set of
     dials tints every per-card title PNG identically). */
  --title-bright: 1.00;
  --title-contrast: 1.00;
  --title-sat: 1.00;
  --title-warmth: 0.00;
  --title-hue: 0deg;

  /* Vignette around the entire viewport — tunable in the panel. */
  --vignette-op: 0.42;
  --vignette-start: 48%;
  --vignette-rgb: 0, 0, 0;

  /* Background dim — a flat dark wash over the velvet + dust, beneath all
     content. 0 = no change to the committed look; tunable in the panel. */
  --bg-dim: 0;

  /* Logo halo — matches the chips' dark-grape backing color so the logo
     reads as the same lighting system. y/blur/op still tunable in the panel. */
  --logo-shadow-y: 0px;
  --logo-shadow-blur: 20px;
  --logo-shadow-op: 0.92;
}

* {
  box-sizing: border-box;
}

/* Images should never be selectable or draggable — prevents the blue highlight
   on click-drag and the ghost-image when accidentally dragging a card. */
img {
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg-0);
  color: #ece6d3;
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Reserve scrollbar space at all times so toggling overflow:hidden during
     the modal doesn't cause a horizontal layout shift (which would move the
     deck slot relative to where we captured the click origin, producing a
     visible position jump at the end of the close animation). */
  scrollbar-gutter: stable;
}

body {
  min-height: 100dvh;
  /* Layer stack, top to bottom:
       1. Gold dust — ~12 static warm pinpricks scattered across the viewport,
          like dust catching the candle.
       2. Vignette — gently darkens the corners so the eye is pulled to center.
       3. Velvet image — the tarot reader's table.
       4. Fallback color in case the image fails to load. */
  background:
    radial-gradient(1px 1px at 8% 14%, rgba(240, 200, 140, 0.55), transparent 60%),
    radial-gradient(1px 1px at 22% 38%, rgba(220, 180, 130, 0.40), transparent 60%),
    radial-gradient(1.2px 1.2px at 16% 78%, rgba(255, 210, 170, 0.50), transparent 60%),
    radial-gradient(1px 1px at 86% 28%, rgba(240, 200, 140, 0.45), transparent 60%),
    radial-gradient(1px 1px at 72% 58%, rgba(255, 210, 170, 0.40), transparent 60%),
    radial-gradient(1.2px 1.2px at 94% 84%, rgba(240, 200, 140, 0.50), transparent 60%),
    radial-gradient(1px 1px at 4% 92%, rgba(220, 180, 130, 0.35), transparent 60%),
    radial-gradient(1px 1px at 38% 88%, rgba(255, 210, 170, 0.45), transparent 60%),
    radial-gradient(1px 1px at 62% 6%, rgba(240, 200, 140, 0.40), transparent 60%),
    radial-gradient(1.2px 1.2px at 48% 44%, rgba(220, 180, 130, 0.30), transparent 60%),
    radial-gradient(1px 1px at 90% 50%, rgba(255, 210, 170, 0.35), transparent 60%),
    radial-gradient(1px 1px at 28% 60%, rgba(240, 200, 140, 0.35), transparent 60%),
    radial-gradient(ellipse 95% 85% at 50% 50%, transparent 0%, transparent var(--vignette-start), rgba(var(--vignette-rgb), var(--vignette-op)) 100%),
    var(--bg-0) var(--velvet-bg) center/cover no-repeat;
  /* Apply `fixed` to ALL layers (the shorthand only sets it on the last). */
  background-attachment: fixed;
  /* `overflow-x: hidden` turns body into a scroll container and can clip
     vertically overflowing content (the rotated cards' high/low corners).
     `overflow-x: clip` clips horizontal overflow only, no scroll-container
     side effect — keeps the rotated tops visible. */
  overflow-x: clip;
}

/* Background dim — a fixed dark wash above the velvet/dust background but
   below .cosmos (z-index 1) and the social link (z-index 5), so it darkens
   only the backdrop and never the content. Driven by --bg-dim. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: #000;
  opacity: var(--bg-dim);
}

.cosmos {
  position: relative;
  z-index: 1;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Heavy top padding pushes the title down from the very edge of the page —
     gives the composition a "poster / invitation" cadence instead of a top-
     anchored dashboard. Footer is pinned to the bottom with margin-top: auto. */
  padding: clamp(0.25rem, 1.5vh, 4.5rem) clamp(0.75rem, 5vw, 4rem) 0;
}

/* Instagram link — pinned to top-right of viewport, subtle parchment gold. */
.social-link {
  position: fixed;
  top: clamp(1rem, 2.4vw, 2rem);
  right: clamp(1rem, 2.4vw, 2rem);
  z-index: 5;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 4px;
  color: rgba(236, 230, 211, 0.55);
  transition: color 0.25s ease, transform 0.25s ease;
  text-decoration: none;
}

.social-link:hover {
  color: rgba(240, 216, 117, 0.95);
  transform: scale(1.08);
}

.social-link:focus-visible {
  outline: 1px solid rgba(122, 255, 214, 0.75);
  outline-offset: 4px;
  border-radius: 50%;
}

.social-link svg {
  width: 100%;
  height: 100%;
}

/* Masthead */
.masthead {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.9rem, 2vw, 1.4rem);
  text-align: center;
  /* Roughly balances the vertical gap above and below the logo: matches the
     invocation's margin-top so logo sits visually centered between top of
     viewport and the "pick up a card" line. .cosmos padding-top already
     provides a small minimum so this never collapses against the edge. */
  margin-top: clamp(0.5rem, 1.5vh, 3.5rem);
}

.masthead__logo {
  /* Outer min() lets the logo shrink below its floor on short viewports
     (e.g. landscape laptops) so the cluster + footer fit without scrolling.
     On tall portrait/desktop, the clamp drives. */
  width: min(clamp(140px, 22vw, 230px), 22vh);
  height: auto;
  display: block;
  /* Significantly muted to push it toward the cards' gold-on-dark monochrome
     palette — they're different visual languages (full-color illustration vs.
     line art), so we close the gap by desaturating the logo + warming the
     cards. */
  filter:
    brightness(var(--logo-bright))
    contrast(var(--logo-contrast))
    saturate(var(--logo-sat))
    sepia(var(--logo-warmth))
    drop-shadow(0 var(--logo-shadow-y) var(--logo-shadow-blur) rgba(48, 18, 36, var(--logo-shadow-op)));
  transition: filter 0.4s ease;
}

@media (hover: hover) {
  /* Hover affordance: small relative bumps on top of the tunable base values,
     so dialing the rest filter keeps the hover ratio sensible. */
  .masthead__logo:hover {
    filter:
      brightness(calc(var(--logo-bright) + 0.07))
      contrast(var(--logo-contrast))
      saturate(calc(var(--logo-sat) + 0.12))
      sepia(var(--logo-warmth))
      drop-shadow(0 var(--logo-shadow-y) var(--logo-shadow-blur) rgba(48, 18, 36, var(--logo-shadow-op)));
  }
}

/* Invocation — thin gold rule + italic prompt; bridges the gap between the
   logo and the deck so the page reads as one composition. */
.invocation {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(18px, 2.6vw, 34px);
  width: clamp(230px, 68vw, 820px);
  max-width: calc(100vw - 1.5rem);
  /* Pill chip covers the entire invocation (text + flanking deco lines).
     `isolation: isolate` so the ::before's z-index: -1 stays inside this
     stacking context and doesn't leak behind the masthead. */
  position: relative;
  isolation: isolate;
  padding: 0.4em clamp(0.9rem, 2vw, 1.4rem);
  /* Above-invocation gap grown to absorb what was previously above the logo,
     so the logo can sit higher without shifting the invocation/deck. Below
     stays tight so the subtitle reads as part of the cards.
     (top gap + masthead's margin-top should roughly equal the old combined
     value, so this section's position doesn't change.) */
  margin: clamp(1.5rem, 4vh, 3.5rem) 0 clamp(0.4rem, 1.5vh, 1.2rem);
  /* Warm parchment cream at full opacity — bright cream against dark velvet
     gives plenty of contrast on its own; no text-shadow needed. */
  color: #ece6d3;
}

.invocation__line {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(236, 230, 211, 0.85), transparent);
}

.invocation__text {
  font-family: var(--font-body);
  font-weight: 500;
  /* Smaller minimum so the text fits inside a narrow chip without overflowing.
     Still scales up generously on large viewports. */
  font-size: clamp(1.25rem, 4vw, 2.2rem);
  letter-spacing: 0.06em;
  /* Brighter ivory than --bone (#f4eac1) for the prompt itself. */
  color: #ece6d3;
  text-align: center;
  /* Hyphenate / wrap if there's no horizontal room rather than overflow. */
  overflow-wrap: anywhere;
  line-height: 1.25;
}

/* Portrait mobile: invocation reads small relative to the card. Bump min
   and scale so it sits more confidently above the card art. The phrase
   wraps anyway at this width, so the wider type doesn't risk overflow. */
@media (max-width: 559px) {
  .invocation__text {
    font-size: clamp(1.5rem, 5.5vw, 2rem);
  }
}

/* On viewports with enough room for the full phrase on one line, force it
   to stay on one line instead of wrapping after "learn". Narrow phones
   keep the multi-line wrap so the chip never overflows. */
@media (min-width: 560px) {
  .invocation__text {
    white-space: nowrap;
    overflow-wrap: normal;
  }
}


/* Soft pill backing — mauve in the linen's hue family + heavy blur so the
   chip dissolves into the velvet folds rather than reading as a UI box. */
.invocation::before {
  content: "";
  position: absolute;
  inset: -4px -10px;
  background: rgba(78, 42, 70, 0.65);
  border-radius: 999px;
  filter: blur(20px);
  z-index: -1;
}

.title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2rem, 7vw, 3.5rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin: 0 0 0.4rem;
  background: linear-gradient(180deg, var(--gold-bright) 0%, var(--gold) 60%, var(--gold-deep) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: 0 0 28px rgba(201, 164, 73, 0.18);
}

.subtitle {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(1.15rem, 3vw, 1.65rem);
  letter-spacing: 0.04em;
  margin: 0;
  color: rgba(236, 230, 211, 0.78);
}

/* Visually hidden but available to screen readers / SEO — the h1 brand name
   stays in the DOM (it's already in every card's artwork) but isn't drawn. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Deck */
.deck {
  display: flex;
  flex-direction: column;
  gap: 2rem;
  align-items: center;
  width: 100%;
  margin-top: 0;
  /* Vertical padding gives the rotated outer cards' high/low corners breathing
     room from the invocation above and footer below. */
  padding-block: clamp(0.5rem, 2.5vh, 3rem);
}

@media (min-width: 780px) {
  :root {
    --card-w: min(28vw, 300px, calc(45vh / 1.5));
  }
  .deck {
    flex-direction: row;
    justify-content: center;
    gap: clamp(1.5rem, 3vw, 3rem);
    align-items: flex-start;
  }
}

/* Landscape mobile (phones held sideways, viewport < 780px wide and short).
   Show all three cards in a row so users don't have to scroll past the
   first card to see what else is on offer. */
/* Brand text next to the logo — hidden by default; shown only in landscape
   mobile where the masthead becomes a compact top-left chip. */
.masthead__brand {
  display: none;
}

@media (orientation: landscape) and (max-height: 599px) {
  :root {
    /* Cards take ~58vh leaving room for the fixed top-bar + footer. */
    --card-w: min(30vw, calc(58vh / 1.5));
  }
  /* Push cosmos contents below the fixed top-bar (brand + Instagram) so the
     leftmost card doesn't slide under the brand. */
  .cosmos {
    padding-top: clamp(3rem, 7vh, 4.5rem);
  }
  .deck {
    flex-direction: row;
    justify-content: center;
    gap: clamp(0.4rem, 1.5vw, 1.5rem);
    align-items: flex-start;
    /* Auto block margins center the deck vertically between the fixed top
       bar and the footer. */
    margin-block: auto;
  }
  /* Masthead becomes a fixed top-left brand chip — logo + wordmark, on the
     same horizontal line as the Instagram link in the top-right. Includes
     a feathered dark backdrop (same recipe as the invocation/footer chips)
     so the brand reads clearly against the linen. */
  .masthead {
    position: fixed;
    top: clamp(0.5rem, 1.2vh, 1rem);
    left: clamp(0.6rem, 1vw, 1rem);
    z-index: 5;
    flex-direction: row;
    align-items: center;
    gap: 0.55rem;
    margin: 0;
    padding: 0.3rem 0.7rem;
    isolation: isolate;
  }
  .masthead::before {
    content: "";
    position: absolute;
    inset: -4px -10px;
    background: rgba(78, 42, 70, 0.65);
    border-radius: 999px;
    filter: blur(20px);
    z-index: -1;
  }
  .masthead__logo {
    width: clamp(36px, 5.5vh, 52px);
  }
  .masthead__brand {
    display: inline-block;
    font-family: var(--font-display);
    color: #ece6d3;
    font-size: clamp(0.78rem, 1.8vw, 1rem);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    white-space: nowrap;
  }
  /* Invocation removed in landscape — three cards are an obvious CTA, and
     the top-bar row reads cleaner without a competing chip in the middle. */
  .invocation {
    display: none;
  }
}

/* Card */
.card {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  width: var(--card-w);
  height: var(--card-h);
  position: relative;
  font: inherit;
  color: inherit;
  /* Rest transform is just the per-card rotation (set via data-rot for the
     fanned deck spread). Hover effects go on filter only — NOT on transform —
     because a hover translateY would shift origin.centerY at click time and
     cause a position jump at the end of close (mouse over close-× → no
     hover → card lands at non-lifted layout position).  */
  --card-rot: 0deg;
  transform: rotate(var(--card-rot));
  transition: transform 0.5s var(--ease-out), filter 0.4s ease;
  /* contain hints the browser that this element's layout + style are
     isolated — Firefox can skip recomputing surrounding content while the
     card animates. Excluding `paint` so the card's outer drop-shadow halo
     isn't clipped at the element box. */
  contain: layout style;
  /* No `will-change` here — making it static keeps a GPU compositor layer
     reserved forever, which Firefox accumulates across repeated open/close
     cycles. The 3D transform on .card__inner is enough hint to promote the
     layer on demand. */
  /* Perspective is now folded into the transform on .card__inner so the
     parent isn't simultaneously a perspective container and an animated
     transform target — which Firefox couldn't promote to a stable GPU
     layer. */
  flex: 0 0 auto;
}

@media (hover: hover) {
  /* Hover affordance: brighter inset rim + border-color shift to bright gold.
     No outer glow or shadow — the active/open card is the moment that gets
     the gold drop-shadow halo. */
  .card:not(.is-active):hover .card__face--image {
    border-color: var(--gold);
    box-shadow:
      inset 0 1px 0 rgba(240, 216, 117, 0.7),
      inset 0 6px 8px -6px rgba(240, 216, 117, 0.45),
      inset 0 0 0 1px rgba(240, 216, 117, var(--card-hover-rim-op));
  }
  .card__face--image {
    transition: border-color 0.35s ease, box-shadow 0.35s ease;
  }
}

.card:focus-visible {
  outline: none;
}
.card:focus-visible .card__frame {
  box-shadow:
    inset 0 0 0 1px rgba(201, 164, 73, 0.18),
    inset 0 0 40px rgba(0, 0, 0, 0.5),
    0 0 0 2px rgba(122, 255, 214, 0.75),
    0 18px 40px rgba(0, 0, 0, 0.55);
}

/* 3D rotateY flip — Chrome/Safari/Edge run this; Firefox skips the FLIP
   entirely via JS (IS_FIREFOX) so this CSS never visibly animates there. */
.card__inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: perspective(1600px) rotateY(0deg);
  transition: transform var(--flip-dur) var(--ease-in-out);
}

.card.is-flipped .card__inner {
  transform: perspective(1600px) rotateY(180deg);
}

.card__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  /* Belt-and-suspenders backface hiding: instant opacity swap at the 90°
     midpoint of the flip prevents both faces from briefly showing. */
  transition: opacity 0s linear calc(var(--flip-dur) * 0.5);
}

/* Skip-flip path (Firefox / reduced-motion / mobile). Flatten the 3D entirely
   and swap faces with pure opacity — no rotateY, no backface dependency. This
   is what the dead @-moz-document block below used to do for Firefox (that
   at-rule no longer applies in author stylesheets), and doing it via the JS-
   toggled .is-instant-flip class fixes the open-card flash in every browser
   that skips the flip. The instant transition also kills the 360ms opacity
   delay that would otherwise read as "card disappears, then modal opens." */
.card.is-instant-flip .card__inner {
  transform: none;
  transition: none;
  transform-style: flat;
}
.card.is-instant-flip .card__face {
  backface-visibility: visible;
  -webkit-backface-visibility: visible;
  transition: none;
}
.card.is-instant-flip .card__face--back {
  transform: none;
}

.card__face--front {
  opacity: 1;
}

.card__face--back {
  transform: rotateY(180deg);
  opacity: 0;
}

.card.is-flipped .card__face--front {
  opacity: 0;
}

/* Firefox-only: disable the flip transitions entirely so the face swap is
   instant. JS already skips the FLIP grow path for Firefox; this kills the
   CSS-driven rotateY transition that would otherwise still animate. */
@-moz-document url-prefix() {
  .card__inner {
    transition: none;
    transform: none;
    transform-style: flat;
  }
  .card.is-flipped .card__inner {
    transform: none;
  }
  .card__face {
    backface-visibility: visible;
    -webkit-backface-visibility: visible;
    transition: none;
  }
  .card__face--back {
    transform: none;
  }
}

/* Drop the front face's box-shadow once the card is in modal mode. The
   shadows (grape halo, dark cast, inset highlights) only matter when the
   card sits in the deck. Keeping them on the active card lets Firefox leak
   a thin shadow rectangle outside the rotated card. */
.card.is-active .card__face--image {
  box-shadow: none;
}

.card.is-flipped .card__face--back {
  opacity: 1;
}

/* Image-based front face — the artwork fills the card; we keep the CSS-drawn
   gold double-line frame on top so the cards still feel like framed tarot
   cards on the page (not just floating images). */
.card__face--image {
  border-radius: var(--card-radius);
  /* Same dark teal-green as the back so the card body color is consistent
     across the flip and around any transparent edges of the front art. */
  background: #012225;
  /* Thin gold outer border (rounded). No overflow:hidden and no padding here
     — those would clip the image's corner ornaments. Instead the artwork
     lives inside .card__art-frame, an inset, aspect-locked container. */
  border: 1px solid var(--gold-deep);
  /* Slimmer shadows: the card scales ~0.56× during the FLIP animation, which
     scales these shadows along with it. A smaller spread = less of a visible
     "thickening" when the transform clears at the end of close.
     First two insets: top-edge highlight — a 1px sharp gold line + a soft
     6px fade beneath it, suggesting overhead light catching the card's
     top bevel. Last outer shadow: a wide warm halo behind the card, so
     each card looks lit from within against the dark velvet. */
  box-shadow:
    inset 0 1px 0 rgba(240, 216, 117, 0.45),
    inset 0 6px 8px -6px rgba(240, 216, 117, 0.30),
    inset 0 0 0 1px rgba(201, 164, 73, 0.22),
    0 10px 22px rgba(0, 0, 0, 0.55),
    /* Wide warm-purple halo — matches the dark-grape chips behind the
       invocation and footer, so card glows and text chips read as the same
       lighting system. box-shadow blur is ~2× filter blur, so 40px here
       roughly matches the chip's filter blur(20px). */
    0 0 40px 4px rgba(48, 18, 36, 0.92);
}

/* Inset wrapper holding the artwork. We use asymmetric percentages chosen so
   that on a 1:1.5 (W:H) card box, the inset is the SAME pixel value on all
   four sides — and crucially it stays proportional to the card box at any
   size. Using `calc(0.035 * var(--card-w))` (a fixed px) caused the art-frame
   to render larger than its rest size during the FLIP animation (since the
   card is laid out at modal-target dimensions and scaled down by transform),
   producing a visible shrink at the end of the close animation.
   3.5%-of-width / 1.5 ≈ 2.333%-of-height keeps margins uniform across sizes. */
.card__art-frame {
  position: absolute;
  inset: 2.333% 3.5%;
  overflow: hidden;
}

/* White-pixel tint overlay — darken blend means the visible result for each
   channel is min(image, tint). Pure-white pixels in the card art become the
   tint color; gold and dark pixels are largely unaffected as long as the
   tint is brighter than them in those channels. Default tint = white = no-op. */
.card__art-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--card-white-tint);
  mix-blend-mode: darken;
  pointer-events: none;
}

.card__art-frame img {
  display: block;
  width: 100%;
  height: 100%;
  /* Frame is uniform-inset (not aspect-locked), so `fill` allows a tiny
     stretch (~1.7%) to perfectly fit, avoiding both cropping (which would
     cut the corner ornaments) and letterboxing (which would make the
     margins look uneven). The stretch is imperceptible. */
  object-fit: fill;
  filter:
    brightness(var(--card-bright))
    contrast(var(--card-contrast))
    saturate(var(--card-sat))
    sepia(var(--card-warmth));
}


.card__frame {
  position: absolute;
  inset: 0;
  border-radius: var(--card-radius);
  background:
    radial-gradient(ellipse at 50% 0%, rgba(74, 58, 168, 0.4), transparent 60%),
    linear-gradient(180deg, #14104a 0%, #0a0830 60%, #050420 100%);
  border: 1px solid var(--gold-deep);
  box-shadow:
    inset 0 0 0 1px rgba(201, 164, 73, 0.18),
    inset 0 0 40px rgba(0, 0, 0, 0.5),
    0 18px 40px rgba(0, 0, 0, 0.55);
  overflow: hidden;
  padding: 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
}

.card__frame::before {
  content: "";
  position: absolute;
  inset: 8px;
  border: 1px solid rgba(201, 164, 73, 0.55);
  border-radius: calc(var(--card-radius) - 4px);
  pointer-events: none;
}

.card__frame::after {
  content: "";
  position: absolute;
  inset: 12px;
  border: 1px solid rgba(201, 164, 73, 0.15);
  border-radius: calc(var(--card-radius) - 6px);
  pointer-events: none;
}

.card__numeral,
.card__name {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.3em;
  color: var(--gold-bright);
  position: relative;
  z-index: 1;
  font-size: 0.85rem;
}

.card__numeral {
  margin-top: 8px;
  padding-left: 0.3em;
}

.card__name {
  margin-bottom: 8px;
  padding-left: 0.3em;
  font-size: 0.95rem;
}

.card__glyph {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  position: relative;
  z-index: 1;
  padding: 6px 0;
}

.card__glyph svg {
  width: 62%;
  height: auto;
  max-height: 100%;
  filter: drop-shadow(0 0 16px rgba(30, 255, 126, 0.25));
}

/* Card back / expanded modal interior — aubergine + emerald, blending the
   warm purple of the page with a cool green accent that ties to the
   iridescent ✦ divider and the green tones already in the cards. */
.card__frame--back {
  background: #012225;
  justify-content: center;
  align-items: center;
}


.card__stars {
  position: absolute;
  inset: 12px;
  border-radius: calc(var(--card-radius) - 6px);
  pointer-events: none;
  background-image:
    radial-gradient(1px 1px at 18% 22%, rgba(201, 164, 73, 0.5), transparent 50%),
    radial-gradient(1px 1px at 72% 30%, rgba(255, 180, 220, 0.45), transparent 50%),
    radial-gradient(1px 1px at 38% 68%, rgba(201, 164, 73, 0.4), transparent 50%),
    radial-gradient(1px 1px at 82% 82%, rgba(180, 200, 255, 0.45), transparent 50%),
    radial-gradient(1px 1px at 26% 50%, rgba(122, 255, 214, 0.35), transparent 50%),
    radial-gradient(1px 1px at 60% 14%, rgba(236, 230, 211, 0.45), transparent 50%),
    radial-gradient(1px 1px at 88% 60%, rgba(236, 230, 211, 0.3), transparent 50%);
}

.card__brand {
  /* Hidden — we no longer surface the brand mark during the click→expand flow.
     Markup is kept so we can re-enable it later (e.g. a hover-flip on resting cards). */
  display: none;
}

.mark__t,
.mark__rb5 {
  font-size: 0.7rem;
  color: var(--gold-bright);
  padding-left: 0.3em;
}

.mark__star {
  font-size: 1.8rem;
  background: linear-gradient(135deg, var(--green-1), var(--green-2) 35%, var(--green-3) 65%, var(--green-4));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 10px rgba(30, 255, 126, 0.4));
  line-height: 1;
}

.mark__rule {
  width: 60%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(201, 164, 73, 0.6), transparent);
  margin: 2px 0;
}

/* Detail panel — lives on the back face. Always visible visually; the back face
   itself is invisible (opacity 0) until the flip happens, so we don't need a
   separate opacity stage on the detail. We only gate pointer-events on .is-active. */
.card__detail {
  position: absolute;
  inset: 18px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  padding: 1.6rem 1.2rem 1.4rem;
  /* The inner .detail__body is the scroll container (see below) so the
     absolutely-positioned back link + close × stay pinned to the modal
     frame instead of scrolling away with the content. */
  overflow: hidden;
  pointer-events: none;
}

/* Scroll the body, not the whole modal — keeps the top chrome (back/close)
   fixed. flex:1 + min-height:0 lets it bound to the modal's height so it
   actually scrolls; teas/events already set these in their scoped rules. */
.detail__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* Mask scrolled content as it passes under the pinned back-link bar in the
   tea/events detail views. Solid modal-teal up top, quick fade so resting
   content (which starts ~3rem down to clear the absolute back link) is never
   veiled. Sits above the scrolling body (z-index auto) but below the
   back/close controls (z-index 4). */
.card__detail.is-tea-detail::before,
.card__detail.is-event-detail::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 70px;
  background: linear-gradient(to bottom, #012225, #012225 56px, rgba(1, 34, 37, 0) 70px);
  z-index: 3;
  pointer-events: none;
}

.card.is-active .card__detail {
  pointer-events: auto;
}

.detail__close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 52px;
  height: 52px;
  /* Above the scroll mask (z-index 3) so the × stays clickable + visible. */
  z-index: 4;
  padding: 8px;
  background: none;
  border: none;
  cursor: pointer;
  display: grid;
  place-items: center;
  color: rgba(240, 216, 117, 0.55);
  transition: color 0.25s ease, transform 0.25s ease;
}

.detail__close:hover {
  color: var(--gold-bright);
  transform: scale(1.1);
}

.detail__close:focus-visible {
  outline: 1px solid rgba(122, 255, 214, 0.75);
  outline-offset: 4px;
  border-radius: 50%;
}

.detail__close svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
}

.detail__numeral {
  font-family: var(--font-display);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--gold-bright);
  font-size: 0.8rem;
  text-align: center;
  margin: 0.4rem 0 0.4rem;
  padding-left: 0.4em;
}

.detail__title {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.25em;
  text-align: center;
  font-size: clamp(1.4rem, 4vw, 2rem);
  margin: 0 0 0.4rem;
  padding-left: 0.25em;
  background: linear-gradient(180deg, var(--gold-bright), var(--gold) 70%, var(--gold-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.detail__divider {
  text-align: center;
  font-size: 1rem;
  margin: 0 auto 1rem;
  background: linear-gradient(135deg, var(--green-1), var(--green-2), var(--green-3), var(--green-4));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 8px rgba(30, 255, 126, 0.3));
}

/* Heading image (used in place of numeral/title/divider when a card has a
   `headingImage`). Fixed height so all three modals' titles line up
   regardless of word length / aspect ratio. */
.detail__heading-image {
  display: block;
  width: auto;
  height: 126px;
  max-width: 90%;
  margin: 0.2rem auto 0.9rem;
  user-select: none;
  -webkit-user-drag: none;
  filter:
    brightness(var(--title-bright))
    contrast(var(--title-contrast))
    saturate(var(--title-sat))
    sepia(var(--title-warmth))
    hue-rotate(var(--title-hue));
}

/* Per-card height tweaks (the SVGs have different internal padding so the
   same box height produces different visible letter sizes). */
.card[data-card-id="teas"] .detail__heading-image {
  height: 62px;
}

.card[data-card-id="about"] .detail__heading-image {
  height: 68px;
}

.card[data-card-id="events"] .detail__heading-image {
  height: 68px;
}


.detail__body {
  font-family: var(--font-body);
  font-size: clamp(1.15rem, 1.5vw, 1.3rem);
  line-height: 1.65;
  /* Same warm cream the gallery uses, so Events/About read consistently. */
  color: rgba(236, 230, 211, 0.95);
}

.detail__body p {
  margin: 0;
}
.detail__body p + p {
  margin-top: 1rem;
}

.detail__body a {
  color: rgba(240, 216, 117, 0.95);
  text-decoration: underline;
  text-decoration-color: rgba(240, 216, 117, 0.45);
  text-underline-offset: 3px;
  transition: color 0.25s ease, text-decoration-color 0.25s ease;
}

.detail__body a:hover {
  color: var(--gold-bright);
  text-decoration-color: rgba(240, 216, 117, 0.85);
}

/* Active state — card lifts out of the deck and grows into the panel.
   The card is laid out at the FINAL (target) size; growth is done via
   `transform: translate + scale + rotate` so the inner layout (and text wrap)
   never changes during the animation. transform-origin stays at the default
   center so the FLIP math pivots the same way the rest CSS rotation does. */
.card.is-active {
  z-index: 100;
  cursor: default;
  --card-rot: 0deg;
  /* Card grows; gold halo blooms in *after* the grow finishes. Filter on an
     animating element forces Firefox to re-rasterize every frame, so we
     delay the filter transition until the size transform has completed —
     halo simply fades in at the end. */
  transition: transform var(--grow-dur) var(--ease-in-out),
              filter 0.35s ease var(--grow-dur);
  filter: drop-shadow(0 0 60px rgba(255, 238, 170, 0.5));
}
.card.is-active:hover {
  filter: drop-shadow(0 0 60px rgba(255, 238, 170, 0.5));
}

/* Fan the outer cards — left tilts back, right tilts forward. Only on
   ≥780px viewports; on mobile the deck is a column and rotation would
   feel off. :not(.is-active) so the rotation never applies to the opened
   modal card — that one is always upright. */
@media (min-width: 780px) {
  .card[data-rot="-5"]:not(.is-active) { --card-rot: -5deg; }
  .card[data-rot="5"]:not(.is-active)  { --card-rot: 5deg; }
}

.card-placeholder {
  flex: 0 0 auto;
}

/* Backdrop — fades in beneath the growing card. Low-opacity vignette so the
   velvet stays visible (the blur softens it into a depth-of-field effect),
   and the edge-darkening pools attention on the centered open card. */
.backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  /* No backdrop-filter — Firefox blurs the entire layered body bg every
     frame, which dominates the animation cost. Instead we lean on a slightly
     darker radial overlay to set focal contrast. */
  background: radial-gradient(ellipse at center, rgba(5, 4, 16, 0.40), rgba(5, 4, 16, 0.75));
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}

.backdrop.is-visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.4s ease;
}

/* Footer — pinned to the bottom edge of the viewport-tall .cosmos */
.footer {
  text-align: center;
  margin-top: auto;
  padding-top: clamp(0.75rem, 5vh, 5rem);
  padding-bottom: clamp(0.5rem, 2.5vh, 3rem);
}

/* Inner span is the dark chip — block-level <footer> stays full-width so the
   span centers, but only the span carries the background. */
.footer > span {
  display: inline-block;
  position: relative;
  isolation: isolate;
  font-family: var(--font-body);
  font-size: clamp(0.95rem, 1.4vw, 1.1rem);
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: #ece6d3;
  padding: 0.3em 0.9em;
  padding-left: calc(0.9em + 0.25em); /* preserves the prior 0.25em left-only tracking */
}

.footer > span::before {
  content: "";
  position: absolute;
  inset: -4px -10px;
  background: rgba(78, 42, 70, 0.65);
  border-radius: 999px;
  filter: blur(20px);
  z-index: -1;
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .card,
  .card__inner,
  .card__brand,
  .card__detail,
  .backdrop {
    transition-duration: 0.001ms !important;
  }
}

/* =========================================================================
   Events list — numbered ritual-style list inside the Events modal. Each
   row collapses to glyph + title + blurb; clicking expands inline.
   ========================================================================= */

/* List and detail panels swap via display — no animation, instant swap.
   (Animated transitions on changing heights kept jittering on Firefox.) */
.events {
  margin: 2rem 0 0;
}

/* Push About body text down to match the teas/events list start. */
.card[data-card-id="about"] .detail__body {
  margin-top: 2.5rem;
  font-size: clamp(1.2rem, 1.7vw, 1.4rem);
  line-height: 1.6;
}

/* Bottom scroll cue — fades the About copy into the modal floor while there's
   more to read, signaling "scroll for more." The fade rises from the top edge
   of the pinned CTA footer (bottom: 100% of .detail__cta) so it stays visible
   above the footer instead of being hidden behind it. JS toggles .is-scroll-end
   on the card__detail once the body reaches the bottom, fading the cue out so
   the final line reads cleanly. (The footer — and this fade — are hidden in the
   inquiry/thanks views, so no extra handling needed there.) */
.card[data-card-id="about"] .detail__cta::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 96px;
  background: linear-gradient(to top, #012225, rgba(1, 34, 37, 0));
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.3s ease;
}

.card[data-card-id="about"] .card__detail.is-scroll-end .detail__cta::before {
  opacity: 0;
}

/* About hero photo placeholder — same proportions + frame as the
   tea-detail gradient slot so all three modals share an image rhythm. */
.about__photo {
  width: 62%;
  max-width: 260px;
  aspect-ratio: 16 / 10;
  border-radius: 4px;
  background: linear-gradient(135deg, #1a3540 0%, #6b8a8c 60%, #d8c5a0 100%);
  box-shadow:
    inset 0 0 0 1px rgba(240, 216, 117, 0.45),
    inset 0 0 0 4px rgba(0, 0, 0, 0.55),
    inset 0 0 0 5px rgba(201, 164, 73, 0.22),
    0 6px 20px rgba(0, 0, 0, 0.4);
  /* Centered smaller hero rather than full-bleed. */
  margin: 0 auto 1.4rem;
}

@media (max-width: 599px) {
  /* Phones: the card is short, so tighten type + spacing enough that the full
     About copy fits without scrolling (verified across 360–430px widths). */
  .card[data-card-id="about"] .detail__body {
    margin-top: 0.4rem;
    font-size: clamp(1.05rem, 1.5vw, 1.26rem);
    line-height: 1.46;
  }
  .card[data-card-id="about"] .detail__body p + p {
    margin-top: 0.5rem;
  }
}

/* Make the events container fill the modal's available height so the
   list-footer can be pushed to the bottom via margin-top: auto. */
.card[data-card-id="events"] .detail__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.card[data-card-id="events"] .events {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* Tighten gap between EVENTS title and first row on portrait mobile —
   default 1.6rem heading-bottom + 2rem events-top reads too generous on
   the narrower viewport. */
@media (max-width: 599px) {
  .card[data-card-id="events"] .detail__heading-image {
    margin-bottom: 0.4rem;
  }
  .card[data-card-id="events"] .events {
    margin-top: 0.4rem;
  }

  /* Portrait mobile modal is narrow enough that the desktop back-link
     (1rem letter-spaced uppercase) crashes into the close × on the right.
     Shrink both controls just enough to clear each other. */
  .events__back,
  .gallery__back {
    font-size: 0.82rem;
    letter-spacing: 0.18em;
    height: 44px;
    gap: 0.35rem;
  }

  .detail__close {
    width: 44px;
    height: 44px;
    padding: 7px;
  }
}

.events__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* One-line framing note under the EVENTS title. */
.events__intro {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(1.05rem, 1.6vw, 1.2rem);
  color: rgba(236, 230, 211, 0.78);
  text-align: center;
  margin: auto 0 1rem;
}

.events__list {
  margin-bottom: auto;
}

.events[data-events-view="detail"] .events__list,
.events[data-events-view="detail"] .events__intro,
.events[data-events-view="detail"] .events__list-footer,
.events[data-events-view="inquiry"] .events__list,
.events[data-events-view="inquiry"] .events__intro,
.events[data-events-view="inquiry"] .events__list-footer,
.events[data-events-view="thanks"] .events__list,
.events[data-events-view="thanks"] .events__intro,
.events[data-events-view="thanks"] .events__list-footer {
  display: none;
}

.events__detail {
  display: none;
}

.events__detail.is-active {
  display: flex;
  flex-direction: column;
  /* Fill the modal and distribute the slack evenly between every element
     (space-between) so the content spreads top-to-bottom instead of crowding
     at the top — Inquire lands at the bottom, title at the top. The gap is the
     minimum spacing when content is tall. */
  flex: 1;
  min-height: 0;
  justify-content: space-between;
  gap: clamp(0.5rem, 1.5vh, 1.05rem);
  padding: 0.4rem 0.2rem 0.6rem;
}

/* flex:0 0 auto stops the shared .events__btn flex:1 from stretching the
   Inquire button in this flex column. */
.events__detail .events__btn--inquire {
  flex: 0 0 auto;
}

/* Inquiry + thanks panels — now card-level (one copy per card), visibility
   driven by .card__detail.is-inquiry / .is-thanks. */
.events__inquiry,
.events__thanks {
  display: none;
  flex-direction: column;
  gap: 1rem;
  padding: 0.4rem 0.2rem 0.6rem;
}

.card__detail.is-inquiry .events__inquiry,
.card__detail.is-thanks .events__thanks {
  display: flex;
  /* Fill + scroll within the modal (the body/CTA are hidden beneath). */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* When the inquiry/thanks panel is up it owns the modal: hide the card body,
   the sticky CTA, and the card heading (the panel has its own title). */
.card__detail.is-inquiry .detail__body,
.card__detail.is-inquiry .detail__cta,
.card__detail.is-thanks .detail__body,
.card__detail.is-thanks .detail__cta,
.card__detail.is-inquiry .detail__heading-image,
.card__detail.is-inquiry .detail__numeral,
.card__detail.is-inquiry .detail__title,
.card__detail.is-inquiry .detail__divider,
.card__detail.is-thanks .detail__heading-image,
.card__detail.is-thanks .detail__numeral,
.card__detail.is-thanks .detail__title,
.card__detail.is-thanks .detail__divider {
  /* !important so the inquiry/thanks panel cleanly takes over the modal — the
     card-specific `.card[data-card-id="…"] .detail__body { display:flex }` rules
     are equal-specificity and would otherwise win by source order. */
  display: none !important;
}

/* Shared "Reach out" CTA, pinned to the modal floor (it's the last flex child
   of .card__detail, which doesn't scroll). Solid modal-teal bg + raised z so it
   sits cleanly above the About scroll fade. */
.detail__cta {
  position: relative;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  padding: 0.8rem 0.5rem 0.3rem;
  border-top: 2px solid rgba(240, 216, 117, 0.23);
  background: #012225;
}

.detail__cta .events__btn--inquire {
  flex: 0 0 auto;
  min-width: 12rem;
}

.detail__cta-note {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(0.95rem, 1.4vw, 1.05rem);
  color: rgba(236, 230, 211, 0.72);
  margin: 0;
  text-align: center;
}

/* Hide the CTA in the individual tea/event detail views (per request). */
.card__detail.is-tea-detail .detail__cta,
.card__detail.is-event-detail .detail__cta {
  display: none;
}

/* Soft custom-request line + Inquire button under the events list.
   margin-top: auto pushes it to the bottom of .events (flex column). */
.events__list-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  margin-top: auto;
  padding: 1.2rem 0.5rem 0.4rem;
  border-top: 2px solid rgba(240, 216, 117, 0.23);
}

.events__custom-note {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(0.95rem, 1.4vw, 1.05rem);
  color: rgba(236, 230, 211, 0.72);
  margin: 0;
  text-align: center;
}

.events__list-footer .events__btn--inquire {
  min-width: 12rem;
}

.events__inquiry-intro {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(1rem, 1.4vw, 1.1rem);
  color: rgba(236, 230, 211, 0.85);
  margin: 0 0 0.4rem;
  text-align: center;
}

.events__thanks-text {
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 1.5vw, 1.2rem);
  color: rgba(236, 230, 211, 0.92);
  text-align: center;
  margin: 0;
}

.events__thanks .events__btn--inquire {
  align-self: center;
  /* .events__btn sets flex:1; the thanks panel is a tall flex column, so reset
     to natural height or the button grows to fill it. */
  flex: 0 0 auto;
  min-width: 12rem;
  margin-top: 0.6rem;
}

/* Inquiry form — fields stacked, label above input, gold-on-dark aesthetic. */
.inquiry-form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin: 0.2rem 0 0;
  /* Fill the inquiry panel so the message field can grow into the space and
     the submit button sits at the bottom. */
  flex: 1 1 auto;
  min-height: 0;
}

/* Message field takes all the leftover vertical room — generous text area. */
.inquiry-form__field--message {
  flex: 1 1 auto;
  min-height: 0;
}

.inquiry-form__field--message textarea {
  flex: 1 1 auto;
}

/* .events__btn sets flex:1; in the form's flex column that makes the submit
   button grow too. Pin it so only the message field absorbs the extra space. */
.inquiry-form .events__btn--inquire {
  flex: 0 0 auto;
}

.inquiry-form__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.inquiry-form__label {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 0.72rem;
  color: rgba(240, 216, 117, 0.85);
}

.inquiry-form input,
.inquiry-form select,
.inquiry-form textarea {
  appearance: none;
  font-family: var(--font-body);
  font-size: 1rem;
  color: #ece6d3;
  background: rgba(20, 12, 28, 0.55);
  border: 1px solid rgba(240, 216, 117, 0.32);
  border-radius: 2px;
  padding: 0.7rem 0.85rem;
  transition: border-color 0.25s ease, background 0.25s ease;
}

.inquiry-form textarea {
  resize: vertical;
  min-height: 5rem;
  font-family: var(--font-body);
}

.inquiry-form input:focus,
.inquiry-form select:focus,
.inquiry-form textarea:focus {
  outline: none;
  border-color: rgba(240, 216, 117, 0.85);
  background: rgba(20, 12, 28, 0.78);
}

.inquiry-form select {
  background-image:
    linear-gradient(45deg, transparent 50%, rgba(240, 216, 117, 0.75) 50%),
    linear-gradient(135deg, rgba(240, 216, 117, 0.75) 50%, transparent 50%);
  background-position:
    calc(100% - 18px) 50%,
    calc(100% - 12px) 50%;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2rem;
}

.inquiry-form select option {
  color: #1a1224;
}

.inquiry-form__error {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: rgba(240, 216, 117, 0.92);
  min-height: 1.2em;
}

.events__btn--inquire {
  flex: 0 0 auto;
  align-self: stretch;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.85rem 1.4rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 0.86rem;
  color: rgba(34, 16, 30, 1);
  background: rgba(224, 210, 168, 0.94);
  border: 1px solid transparent;
  border-radius: 2px;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.3s ease, transform 0.18s ease, color 0.3s ease;
}

.events__btn--inquire:hover {
  background: rgba(240, 216, 117, 1);
  transform: translateY(-1px);
}

.events__btn--inquire:disabled {
  opacity: 0.6;
  cursor: progress;
  transform: none;
}

.events__item {
  /* Thin gold-thread divider between entries — no box, no rectangle. */
  border-top: 2px solid rgba(240, 216, 117, 0.23);
}

.events__item:first-child {
  border-top: 0;
}

.events__row {
  appearance: none;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: clamp(0.9rem, 2vw, 1.4rem);
  width: 100%;
  background: transparent;
  border: 0;
  color: inherit;
  padding: 1.1rem 0.5rem;
  cursor: pointer;
  text-align: left;
  font: inherit;
  transition: padding-bottom 0.25s ease;
}

.events__row:hover .events__title {
  color: #ece6d3;
}

.events__row:focus-visible {
  outline: 1px solid rgba(122, 255, 214, 0.55);
  outline-offset: 2px;
}

/* Big display Roman numeral, gold gradient — sets the grimoire tone. */
.events__numeral {
  font-family: var(--font-display);
  letter-spacing: 0.12em;
  font-size: clamp(1.6rem, 3.2vw, 2.2rem);
  line-height: 1;
  background: linear-gradient(180deg, var(--gold-bright) 0%, var(--gold) 60%, var(--gold-deep) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  min-width: 2.4ch;
  text-align: center;
}

/* Square gradient placeholder left of each event — matches the Teas list
   thumbnails (.gallery__swatch) so both lists share an image rhythm. */
.events__swatch {
  display: block;
  width: clamp(80px, 13vw, 100px);
  height: clamp(80px, 13vw, 100px);
  border-radius: 6px;
  background-size: cover;
  background-position: center;
  box-shadow:
    inset 0 0 0 1px rgba(240, 216, 117, 0.45),
    0 2px 8px rgba(0, 0, 0, 0.35);
}

.events__head {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-width: 0;
}

.events__title {
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 1.9vw, 1.3rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-bright);
  transition: color 0.25s ease;
}

.events__blurb {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(0.98rem, 1.4vw, 1.1rem);
  line-height: 1.4;
  color: rgba(236, 230, 211, 0.86);
}

/* Events card hides the heading image only while a single offering is being
   viewed — the detail view has its own prominent title. List view keeps the
   EVENTS title visible. JS toggles .is-event-detail on .card__detail. */
.card[data-card-id="events"] .card__detail.is-event-detail .detail__heading-image,
.card[data-card-id="teas"] .card__detail.is-tea-detail .detail__heading-image {
  display: none;
}

/* "All offerings" back link pinned to the modal's top-left, mirroring the
   close × on the right so both sit on the same horizontal line. Absolute-
   positioned relative to .card__detail (its first positioned ancestor). */
.events__back {
  appearance: none;
  background: transparent;
  border: 0;
  position: absolute;
  top: 12px;
  left: 12px;
  height: 52px; /* matches .detail__close so they share a baseline */
  z-index: 4;
  color: rgba(240, 216, 117, 0.7);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 1rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0 0.5rem;
  transition: color 0.3s ease, transform 0.3s ease;
}

.events__back:hover {
  color: rgb(240, 216, 117);
  transform: translateX(-2px);
}

.events__back-mark {
  font-size: 1.5em;
  line-height: 0.8;
}

/* Detail title — bigger, prominent, takes the place of both the (now hidden)
   heading image and the Roman numeral. */
.events__detail-title {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #ece6d3;
  margin: 0;
  text-align: center;
}

/* Photo placeholder framed in gold linework, matching the card backs. */
.events__photo {
  position: relative;
  width: 80%;
  align-self: center;
  aspect-ratio: 16 / 9;
  display: grid;
  place-items: center;
  border-radius: 2px;
  box-shadow:
    inset 0 0 0 1px rgba(240, 216, 117, 0.55),
    inset 0 0 0 4px rgba(0, 0, 0, 0.6),
    inset 0 0 0 5px rgba(201, 164, 73, 0.22),
    0 6px 22px rgba(0, 0, 0, 0.55);
}

.events__photo-label {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.28em;
  font-size: clamp(0.9rem, 1.5vw, 1.1rem);
  color: rgba(236, 230, 211, 0.94);
  text-shadow: 0 0 18px rgba(0, 0, 0, 0.6);
  padding-left: 0.3em;
}

.events__longtext {
  font-family: var(--font-body);
  font-size: clamp(1.15rem, 1.8vw, 1.35rem);
  line-height: 1.5;
  color: rgba(236, 230, 211, 0.95);
  margin: 0;
  /* small drop cap for grimoire feel */
}

.events__longtext::first-letter {
  font-family: var(--font-display);
  float: left;
  font-size: 2.6em;
  line-height: 0.9;
  padding: 0.15em 0.15em 0 0;
  background: linear-gradient(180deg, var(--gold-bright) 0%, var(--gold) 60%, var(--gold-deep) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Sub-options as alchemical chips — small, centered, ornamental — instead of
   form rows with left bars. */
.events__subs {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}

.events__sub {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.3rem;
  padding: 0.2rem 0;
}

.events__sub-label {
  position: relative;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.28em;
  font-size: 0.75rem;
  color: rgba(240, 216, 117, 0.9);
  padding: 0 1.4em;
}

/* Tiny gold flourishes to the left + right of each sub label, like a card
   title's ornament. */
.events__sub-label::before,
.events__sub-label::after {
  content: "✦";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.7em;
  color: rgba(240, 216, 117, 0.55);
}

.events__sub-label::before { left: 0; }
.events__sub-label::after  { right: 0; }

.events__sub-desc {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(1.05rem, 1.5vw, 1.2rem);
  line-height: 1.5;
  color: rgba(236, 230, 211, 0.92);
  max-width: 36em;
}

/* Booking link — soft, centered, links to a calendar (TBD). Same iridescent
   ✦ as the rest of the brand. */
.events__booking {
  display: inline-flex;
  align-self: center;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.4rem;
  padding: 0.4rem 0.9rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 0.78rem;
  color: rgba(236, 230, 211, 0.9);
  text-decoration: none;
  border-bottom: 1px solid rgba(240, 216, 117, 0.35);
  transition: color 0.3s ease, border-color 0.3s ease;
}

.events__booking:hover {
  color: rgb(240, 216, 117);
  border-bottom-color: rgba(240, 216, 117, 0.85);
}

.events__booking-mark {
  background: linear-gradient(135deg, var(--green-1), var(--green-2) 35%, var(--green-3) 65%, var(--green-4));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 6px rgba(30, 255, 126, 0.3));
}

.events__cta {
  display: flex;
  gap: 0.7rem;
  margin-top: 0.4rem;
}

.events__btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem 1rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 0.82rem;
  color: rgba(34, 16, 30, 1);
  background: rgba(224, 210, 168, 0.94);
  border: 1px solid transparent;
  border-radius: 2px;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.3s ease, transform 0.18s ease, color 0.3s ease, border-color 0.3s ease;
}

.events__btn:hover {
  background: rgba(240, 216, 117, 1);
  transform: translateY(-1px);
}

.events__btn--email {
  display: grid;
  place-items: center;
}

.events__btn--email > .events__btn-default,
.events__btn--email > .events__btn-reveal {
  grid-area: 1 / 1;
  transition: opacity 220ms ease;
}

.events__btn--email > .events__btn-reveal {
  opacity: 0;
  text-transform: none;
  letter-spacing: 0.02em;
  font-size: 0.95rem;
  pointer-events: none;
}

.events__btn--email:hover > .events__btn-default,
.events__btn--email:focus-visible > .events__btn-default {
  opacity: 0;
}

.events__btn--email:hover > .events__btn-reveal,
.events__btn--email:focus-visible > .events__btn-reveal {
  opacity: 1;
}

.events__btn--alt {
  background: transparent;
  color: rgba(236, 230, 211, 0.92);
  border-color: rgba(244, 234, 193, 0.4);
}

.events__btn--alt:hover {
  background: rgba(244, 234, 193, 0.08);
  color: #ece6d3;
  border-color: rgba(240, 216, 117, 0.85);
}

/* =========================================================================
   Tea gallery — master/detail product picker rendered inside the Teas modal
   ========================================================================= */

.gallery {
  display: flex;
  flex-direction: column;
  gap: clamp(0.6rem, 1.5vh, 1rem);
  text-align: left;
  margin-top: 2rem;
}

/* Teas list — mirrors the events list pattern: thin gold dividers,
   title + chevron rows, swap to detail-per-tea on click. */
.gallery__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}

.gallery[data-gallery-view="detail"] .gallery__list {
  display: none;
}

/* One-line framing note under the TEAS title (mirrors .events__intro). */
.gallery__intro {
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(1.05rem, 1.6vw, 1.2rem);
  color: rgba(236, 230, 211, 0.86);
  text-align: center;
  margin: 0 0 0.8rem;
}

.gallery[data-gallery-view="detail"] .gallery__intro {
  display: none;
}

.gallery__item {
  border-top: 2px solid rgba(240, 216, 117, 0.23);
}

.gallery__item:first-child {
  border-top: 0;
}

.gallery__row {
  appearance: none;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: clamp(0.85rem, 2vw, 1.2rem);
  width: 100%;
  background: transparent;
  border: 0;
  color: inherit;
  padding: 0.65rem 0.5rem;
  cursor: pointer;
  text-align: left;
  font: inherit;
}

.gallery__row:hover .gallery__title {
  color: #ece6d3;
}

.gallery__row:focus-visible {
  outline: 1px solid rgba(122, 255, 214, 0.55);
  outline-offset: 2px;
}

/* Square gradient thumbnail left of the title — placeholder for a real
   product photo. Sized for a recognizable product slot. */
.gallery__swatch {
  display: block;
  width: clamp(86px, 19vw, 112px);
  height: clamp(86px, 19vw, 112px);
  border-radius: 8px;
  background-size: cover;
  background-position: center;
  box-shadow:
    inset 0 0 0 1px rgba(240, 216, 117, 0.45),
    0 2px 8px rgba(0, 0, 0, 0.35);
}

.gallery__head {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.gallery__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.16rem, 2vw, 1.36rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* Brand gold so the name reads as the row's anchor, distinct from the
     cream ingredient line beneath it. No glow — crisp. */
  color: var(--gold-bright);
  transition: color 0.25s ease;
}

.gallery__ingredients {
  font-family: var(--font-body);
  font-size: clamp(0.98rem, 1.5vw, 1.14rem);
  line-height: 1.38;
  color: #ece6d3;
}

/* Detail panels: hidden by default; the active one shows when the gallery
   view flips to "detail". */
.gallery__detail {
  display: none;
}

.gallery[data-gallery-view="detail"] .gallery__detail.is-active {
  display: flex;
  flex-direction: column;
  gap: clamp(0.6rem, 1.5vh, 1rem);
  /* Extra top padding so the gradient image clears the absolute
     "Return to all teas" link + leaves a breathing gap, matching how
     the events detail title spaces away from its back link. */
  padding: 3rem 0.2rem 0.6rem;
  /* Grow to fill the modal's available height so .gallery__purchase
     can float to the bottom via margin-top: auto. */
  flex: 1;
  min-height: 0;
}

/* Flex chain so the detail can actually grow vertically: detail body
   fills the modal, gallery fills the detail body, and .gallery__details
   inside the active detail fills what remains. */
.card[data-card-id="teas"] .detail__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.card[data-card-id="teas"] .gallery {
  flex: 1;
  min-height: 0;
}

.gallery[data-gallery-view="detail"] .gallery__detail.is-active .gallery__details {
  /* Grow to fill slack (so the button floats to the bottom) but never shrink —
     the image gives up height instead, keeping all the text visible. */
  flex: 1 0 auto;
  min-height: 0;
}

/* The image holds its 3/2 box, but may shrink (never grow past it) when a tea
   has a longer ingredient list, so the whole detail fits without scrolling. */
.gallery[data-gallery-view="detail"] .gallery__detail.is-active .gallery__primary {
  flex: 0 1 auto;
  aspect-ratio: 3 / 2;
  min-height: 120px;
}

.gallery__purchase {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  /* Float the order button + note to the bottom of the detail panel,
     regardless of how short the ingredient list above it is. */
  margin-top: auto;
}

/* Back-to-list control on each tea detail — visually mirrors .events__back. */
.gallery__back {
  appearance: none;
  background: transparent;
  border: 0;
  position: absolute;
  top: 12px;
  left: 12px;
  height: 52px;
  z-index: 4;
  color: rgba(240, 216, 117, 0.7);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 1rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0 0.5rem;
  transition: color 0.3s ease, transform 0.3s ease;
}

.gallery__back:hover {
  color: rgb(240, 216, 117);
  transform: translateX(-2px);
}

.gallery__back-mark {
  font-size: 1.5em;
  line-height: 0.8;
}

/* Tighten the gap between the TEAS title and the first row on mobile,
   matching the events treatment. */
@media (max-width: 599px) {
  .card[data-card-id="teas"] .detail__heading-image {
    margin-bottom: 0.4rem;
  }
  .card[data-card-id="teas"] .gallery {
    margin-top: 0.4rem;
  }
}

/* Primary image area (with arrows) — landscape aspect keeps the whole gallery
   visible inside the portrait modal without scrolling. */
.gallery__primary {
  position: relative;
  width: 100%;
}

.gallery__image-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 4px;
  overflow: hidden;
  background: var(--bg-0);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Two layered gradient backgrounds — only one is .is-front (opacity 1) at a
   time. JS sets the new gradient on the back layer and toggles which is in
   front, producing a true cross-fade between gradients with no swap snap. */
.gallery__image-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}

.gallery__image-layer.is-front {
  opacity: 1;
}

/* Cosmic texture sits on top of both layers and stays constant during swaps. */
.gallery__image-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 28% 28%, rgba(255, 255, 255, 0.18), transparent 55%),
    radial-gradient(1.2px 1.2px at 18% 70%, rgba(255, 255, 255, 0.5), transparent 50%),
    radial-gradient(1px 1px at 76% 36%, rgba(255, 255, 255, 0.45), transparent 50%),
    radial-gradient(1px 1px at 62% 72%, rgba(255, 255, 255, 0.4), transparent 50%),
    radial-gradient(1px 1px at 32% 84%, rgba(255, 255, 255, 0.35), transparent 50%);
  pointer-events: none;
}

.gallery__image-label {
  position: relative;
  z-index: 2;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: clamp(0.85rem, 1.6vw, 1.05rem);
  color: rgba(236, 230, 211, 0.92);
  text-shadow: 0 0 12px rgba(0, 0, 0, 0.4);
  padding-left: 0.3em;
}

/* Prev / next arrows */
.gallery__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  padding: 4px;
  border: 0;
  background: rgba(8, 4, 16, 0.55);
  color: rgba(236, 230, 211, 0.85);
  cursor: pointer;
  border-radius: 50%;
  display: grid;
  place-items: center;
  transition: background 0.25s ease, color 0.25s ease, transform 0.25s ease;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.gallery__nav:hover {
  background: rgba(20, 10, 28, 0.8);
  color: rgb(240, 216, 117);
  transform: translateY(-50%) scale(1.06);
}

.gallery__nav:focus-visible {
  outline: 1px solid rgba(122, 255, 214, 0.75);
  outline-offset: 3px;
}

.gallery__nav--prev { left: 8px; }
.gallery__nav--next { right: 8px; }

.gallery__nav svg {
  width: 100%;
  height: 100%;
}

/* Details — tightened up so the whole gallery fits in the portrait modal. */
.gallery__details {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  transition: opacity 0.25s ease;
}

.gallery__product-name {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 2.1vw, 1.4rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #ece6d3;
  margin: 0;
  padding-left: 0.18em;
  /* Lock height so single-vs-double-line names don't shift the layout. */
  min-height: 1.4em;
  line-height: 1.4em;
}

/* BODY text (~85% of L1 title) — description, ingredients, properties.
   .is-placeholder modifier drops it to "caption" sizing for "coming soon"
   text so the empty-state reads quieter than real content. */
.gallery__product-description,
.gallery__product-ingredients {
  font-family: var(--font-body);
  font-size: clamp(1rem, 1.6vw, 1.2rem);
  line-height: 1.5;
  color: rgba(236, 230, 211, 0.95);
  margin: 0;
}

.gallery__product-description {
  /* Reserve 2 lines so short descriptions don't leave a big gap while still
     keeping the blocks roughly aligned across teas. */
  min-height: calc(1.5em * 2);
}

.gallery__product-description.is-placeholder,
.gallery__product-ingredients.is-placeholder {
  font-size: clamp(0.82rem, 1.2vw, 0.92rem);
  color: rgba(236, 230, 211, 0.55);
  font-style: italic;
}

/* Lovely paired ingredient list — hanging gold ✦ marker, name in regular
   serif paired with its metaphysical property via a soft gold em-dash.
   Italic on the property side carries the visual emphasis, not weight.
   Multi-column layout: --ingr-cols (set inline from JS) splits the list
   into N columns once an ingredient count crosses 4 per column. */
.gallery__ingredients-list {
  list-style: none;
  padding: 0;
  margin: 0;
  column-count: var(--ingr-cols, 1);
  column-gap: clamp(1rem, 2.5vw, 1.75rem);
}

.gallery__ingredient {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.5em;
  font-family: var(--font-body);
  font-size: clamp(0.95rem, 1.4vw, 1.1rem);
  line-height: 1.45;
  color: #ece6d3;
  /* Hanging indent: marker sits in negative space so wrapped lines tuck
     neatly under the ingredient name. */
  padding-left: 1.25em;
  position: relative;
  /* Multi-column safety: keep each pair on a single column instead of
     splitting "name —" on column 1 and "property" on column 2. Margin
     stands in for the gap (which doesn't apply to multi-column flow). */
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  margin-bottom: 0.5rem;
}

.gallery__ingredient:last-child {
  margin-bottom: 0;
}

/* Long ingredient names (e.g. "Lapsang Souchong (smoky)") get promoted
   to span all columns when JS measures they don't fit in their column.
   Keeps the single-line read order intact instead of wrapping awkwardly. */
.gallery__ingredient.is-full {
  -webkit-column-span: all;
  column-span: all;
}

.gallery__ingredient::before {
  content: "✦";
  position: absolute;
  left: 0;
  top: 0.05em;
  color: rgba(240, 216, 117, 0.5);
  font-size: 0.82em;
}

.gallery__ingredient-name {
  font-weight: 400;
  letter-spacing: 0.03em;
}

.gallery__ingredient-sep {
  color: rgba(240, 216, 117, 0.45);
}

.gallery__ingredient-properties {
  color: rgba(236, 230, 211, 0.7);
  font-style: italic;
}

/* Labelled field group — small uppercase tag above an input area (sizes,
   thumbnails). Makes the purchase flow read clearly top-to-bottom. */
.gallery__field {
  display: flex;
  flex-direction: column;
  gap: 0.32rem;
  margin-top: 0.45rem;
}

/* LEVEL 2 section labels — INGREDIENTS, CORRESPONDENCES, SIZE.
   Eyebrow treatment: small, wide-tracked, muted so the L1 title and the
   list items below read as the dominant content. */
.gallery__field-label {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.26em;
  font-size: clamp(0.78rem, 1.2vw, 0.92rem);
  color: rgba(236, 230, 211, 0.78);
  padding-left: 0.22em;
}

.gallery__field--thumbs {
  margin-top: 0.6rem;
}

/* Sizes row — three buttons spread evenly. Clear "choose size" step. */
.gallery__sizes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

.gallery__size {
  appearance: none;
  background: transparent;
  border: 1px solid rgba(224, 210, 168, 0.45);
  color: rgba(236, 230, 211, 0.95);
  padding: 0.5rem 0.5rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.9rem;
  cursor: pointer;
  border-radius: 2px;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.gallery__size:hover {
  border-color: rgba(240, 216, 117, 0.8);
  color: rgb(240, 216, 117);
}

.gallery__size.is-selected {
  background: rgba(224, 210, 168, 0.9);
  color: rgba(34, 16, 30, 1);
  border-color: rgba(224, 210, 168, 0.9);
}

/* Scoped under .detail__body so the dark text + no-underline win over the
   gold .detail__body a link styling (which is more specific than a bare
   .gallery__cart). */
.detail__body .gallery__cart {
  appearance: none;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.55rem;
  margin-top: 0.45rem;
  padding: 0.65rem 1rem;
  background: rgba(224, 210, 168, 0.94);
  border: 0;
  color: rgba(34, 16, 30, 1);
  text-decoration: none;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-size: 0.86rem;
  cursor: pointer;
  border-radius: 2px;
  white-space: nowrap;
  transition: background 0.25s ease, transform 0.18s ease;
}

.gallery__cart-divider {
  opacity: 0.45;
}

.detail__body .gallery__cart:hover {
  background: rgba(240, 216, 117, 1);
  color: rgba(34, 16, 30, 1);
  transform: translateY(-1px);
}

/* Small note under the order button while online checkout is unavailable. */
.gallery__order-note {
  margin: 0.55rem 0 0;
  text-align: center;
  font-style: italic;
  font-size: 0.88rem;
  letter-spacing: 0.04em;
  color: rgba(224, 210, 168, 0.85);
}

/* Thumbnail strip */
.gallery__thumbnails {
  display: flex;
  gap: clamp(0.5rem, 1.4vw, 0.9rem);
  overflow-x: auto;
  padding: 0.2rem 0 0.4rem;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.gallery__thumbnails::-webkit-scrollbar { height: 4px; }
.gallery__thumbnails::-webkit-scrollbar-thumb { background: rgba(224, 210, 168, 0.2); }

.gallery__thumb {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.22rem;
  width: clamp(46px, 12vw, 56px);
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  color: rgba(236, 230, 211, 0.6);
  font-family: var(--font-body);
  font-size: 0.6rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  transition: color 0.25s ease;
}

.gallery__thumb-image {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 3px;
  position: relative;
  border: 1px solid transparent;
  transition: border-color 0.25s ease, transform 0.25s ease;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

.gallery__thumb:hover .gallery__thumb-image {
  border-color: rgba(240, 216, 117, 0.7);
}

.gallery__thumb.is-active .gallery__thumb-image {
  border-color: rgba(240, 216, 117, 1);
  transform: scale(1.04);
}

.gallery__thumb.is-active {
  color: rgb(240, 216, 117);
}

.gallery__thumb-label {
  width: 100%;
  text-align: center;
  line-height: 1.1;
  pointer-events: none;
}

.gallery__thumb:focus-visible .gallery__thumb-image {
  outline: 1px solid rgba(122, 255, 214, 0.75);
  outline-offset: 3px;
}

/* Details soft-dip during product switch. Image cross-fades on its own layers
   (no opacity dip there — gradients smoothly blend), but the text content has
   to be swapped, so we hide it mid-transition behind a gentle opacity dip. */
.gallery__details {
  transition: opacity 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}

.gallery.is-transitioning .gallery__details {
  opacity: 0.3;
}

@media (prefers-reduced-motion: reduce) {
  .gallery__image-layer,
  .gallery__details {
    transition: none;
  }
}

/* ======================================================================
   Background debug panel — fixed bottom-right gear button, slides a panel
   in for live-tuning the body background gradient.
   ====================================================================== */

/* `display: grid` below overrides the HTML `hidden` attribute, so we need
   an explicit selector to keep the gear button hidden when prod-shipped. */
.debug-toggle[hidden] { display: none; }

.debug-toggle {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 200;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(224, 210, 168, 0.4);
  background: rgba(20, 12, 28, 0.85);
  color: rgba(236, 230, 211, 0.7);
  font-size: 18px;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: color 0.2s ease, transform 0.2s ease;
  backdrop-filter: blur(6px);
}
.debug-toggle:hover {
  color: rgb(240, 216, 117);
  transform: rotate(45deg);
}

.debug-panel {
  position: fixed;
  bottom: 64px;
  right: 16px;
  z-index: 200;
  width: 280px;
  max-height: 80vh;
  overflow-y: auto;
  background: rgba(14, 8, 16, 0.96);
  border: 1px solid rgba(224, 210, 168, 0.3);
  border-radius: 6px;
  color: rgba(236, 230, 211, 0.92);
  font-family: -apple-system, "SF Mono", Menlo, monospace;
  font-size: 11px;
  backdrop-filter: blur(8px);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
}

.debug-panel[hidden] { display: none; }

.debug-panel__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 12px;
  border-bottom: 1px solid rgba(224, 210, 168, 0.15);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.debug-panel__head button {
  background: none;
  border: 0;
  color: inherit;
  font-size: 18px;
  cursor: pointer;
  padding: 0 4px;
  line-height: 1;
}

.debug-panel__body {
  padding: 8px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.debug-cardart, .debug-hover, .debug-velvet {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 8px;
  background: rgba(255, 255, 255, 0.03);
  border-radius: 4px;
}

.debug-cardart label, .debug-hover label, .debug-velvet label {
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 4px;
  color: rgba(240, 216, 117, 0.9);
}

.debug-row {
  display: grid;
  grid-template-columns: 50px 1fr 36px;
  align-items: center;
  gap: 6px;
}

.debug-row > span {
  color: rgba(236, 230, 211, 0.5);
}

.debug-row input[type="range"] {
  width: 100%;
  accent-color: rgba(240, 216, 117, 0.9);
}

.debug-row input[type="color"] {
  width: 100%;
  height: 22px;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}

.debug-row output {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: rgba(236, 230, 211, 0.65);
}

.debug-row select {
  flex: 1;
  background: rgba(0, 0, 0, 0.4);
  color: rgba(236, 230, 211, 0.9);
  border: 1px solid rgba(224, 210, 168, 0.2);
  border-radius: 3px;
  padding: 4px 6px;
  font-family: inherit;
  font-size: 11px;
}

.debug-actions {
  display: flex;
  gap: 6px;
  margin-top: 4px;
}

.debug-actions button {
  flex: 1;
  padding: 6px 10px;
  background: rgba(224, 210, 168, 0.12);
  border: 1px solid rgba(224, 210, 168, 0.3);
  border-radius: 3px;
  color: rgba(236, 230, 211, 0.92);
  font: inherit;
  cursor: pointer;
  transition: background 0.2s ease;
}

.debug-actions button:hover {
  background: rgba(240, 216, 117, 0.2);
}

