/* =========================================================
   DJ ECHARRY — Design Tokens
   ========================================================= */
:root {
  /* Color */
  --color-bg: #0b0a09;
  --color-bg-alt: #14120f;
  --color-bg-elevated: #1c1917;
  --color-text: #f5f1ea;
  /* Lightened from the original gray (#a8a29e/#6b6560) sitewide —
     body copy, descriptions, and labels were reading as low-contrast
     gray-on-black. Both stay a shade softer than --color-text so
     headings keep visual priority, muted still reads clearly above
     faint (labels/eyebrows) to preserve that hierarchy. */
  --color-text-muted: #d6d1c8;
  --color-text-faint: #9a948c;
  --color-gold: #d4af37;
  --color-gold-bright: #e8c766;
  --color-gold-dark: #a8842a;
  --color-gold-soft: rgba(212, 175, 55, 0.12);
  --color-border: #2a2622;
  --color-border-soft: rgba(245, 241, 234, 0.08);
  --color-white: #ffffff;
  --color-success: #4ade80;
  --color-error: #f87171;

  /* Typography */
  --font-display: "Playfair Display", "Georgia", serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  /* Bold condensed/geometric poster face — used only by the Featured
     Mix section's title, where the brief calls for something heavier
     and more festival-poster than the site's serif/sans pairing. */
  --font-mix-display: "Anton", "Arial Narrow", sans-serif;

  --fs-hero: clamp(2.75rem, 5vw + 1.4rem, 6.25rem);
  --fs-h1: clamp(2.25rem, 3vw + 1.2rem, 3.75rem);
  --fs-h2: clamp(1.85rem, 2.2vw + 1rem, 2.75rem);
  --fs-h3: clamp(1.25rem, 1vw + 0.9rem, 1.625rem);
  --fs-lead: clamp(1.05rem, 0.6vw + 0.9rem, 1.35rem);
  --fs-body: 1rem;
  --fs-small: 0.875rem;
  --fs-xs: 0.75rem;

  /* Spacing (8px rhythm) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-24: 6rem;
  --space-32: 8rem;

  /* Layout */
  --container-max: 1240px;
  --radius-sm: 4px;
  --radius-md: 10px;
  --radius-lg: 20px;
  --radius-full: 999px;

  /* Motion */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 180ms;
  --dur-med: 320ms;
  --dur-slow: 600ms;

  --nav-height: 84px;
}

/* =========================================================
   Reset
   ========================================================= */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* overflow-x must NOT be set here. Setting overflow-x to anything but
     visible/clip forces the UA to compute an implicit overflow-y: auto
     (CSS Overflow spec), turning <html> into its own scroll container
     distinct from the viewport — which breaks position:sticky for every
     descendant (the hero's pinned logo/photo stage included: it stops
     tracking the viewport and just scrolls away like a normal element,
     prematurely exposing .hero-pin-wrapper's raw black background —
     this was the actual cause of the "black rectangle" between section
     1 and 2, not a spacing/gradient issue). body's own overflow-x:
     hidden below is sufficient horizontal-overflow protection on its
     own and does not have this side effect. */
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  min-height: 100dvh;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img,
picture,
video {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

ul,
ol {
  list-style: none;
}

button,
input,
textarea,
select {
  font: inherit;
  color: inherit;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

::selection {
  background: var(--color-gold);
  color: var(--color-bg);
}

/* =========================================================
   Utility
   ========================================================= */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-6);
}

@media (min-width: 768px) {
  .container {
    padding-inline: var(--space-8);
  }
}

.section {
  padding-block: var(--space-24);
  position: relative;
  scroll-margin-top: var(--nav-height);
}

@media (max-width: 640px) {
  .section {
    padding-block: var(--space-16);
  }
}

.section-alt {
  /* Very slightly translucent (not fully opaque) so the shared global
     particle canvas behind the page bleeds through faintly instead of
     being fully blocked — visually indistinguishable from the flat
     var(--color-bg-alt) it replaces. #contact re-themes this section
     white via a higher-specificity ID rule (see "Contact / Booking"
     below) and stays fully opaque there, which is the desired
     lighter-section behavior. */
  background: color-mix(in srgb, var(--color-bg-alt) 95%, transparent);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: var(--space-4);
}

.eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--color-gold);
}

.section-head {
  max-width: 640px;
  margin-bottom: var(--space-16);
}

.section-head.center {
  margin-inline: auto;
  text-align: center;
}

.section-head h2 {
  font-size: var(--fs-h2);
  margin-bottom: var(--space-4);
}

.section-head p {
  color: var(--color-text-muted);
  font-size: var(--fs-lead);
}

.text-gold {
  color: var(--color-gold);
}

.italic-serif {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
}

.reveal {
  opacity: 0;
  transform: translateY(28px);
}

.js-ready .reveal {
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
  }
}

/* =========================================================
   Buttons
   ========================================================= */
.btn {
  --btn-fg: var(--color-bg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 1rem 2rem;
  min-height: 48px;
  border-radius: var(--radius-full);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  cursor: pointer;
  border: 1px solid transparent;
  white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-med) var(--ease-out),
    background var(--dur-med) var(--ease-out), border-color var(--dur-med) var(--ease-out), color var(--dur-med) var(--ease-out);
}

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

.btn-primary {
  background: transparent;
  border-color: var(--color-gold);
  color: var(--color-gold-bright);
}

.btn-primary:hover {
  background: var(--color-gold);
  color: var(--color-bg);
  transform: translateY(-2px);
}

.btn-outline {
  background: transparent;
  border-color: rgba(245, 241, 234, 0.35);
  color: var(--color-text);
}

.btn-outline:hover {
  border-color: var(--color-gold);
  color: var(--color-gold);
  transform: translateY(-2px);
}

.btn-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

/* =========================================================
   Preloader
   ========================================================= */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: #000000;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  /* 0.8s — matches EXIT_MS in js/main.js's preloaderController(). */
  transition: opacity 0.8s var(--ease-out), visibility 0.8s var(--ease-out);
}

.preloader.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Locks the page underneath while the preloader is up. */
body.preloader-active {
  overflow: hidden;
  height: 100%;
}

/* Layer order (back to front) — kept in sync with js/main.js:
   0  .preloader's own solid black background
   1  ambient particles (#preloaderParticles)
   2  logo-formation particles (#preloaderLogoParticles)
   3  clean logo + progress bar + phrase (.preloader-content) */
.preloader-particles,
.preloader-logo-particles {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.preloader-particles {
  z-index: 1;
  /* The soft radial "atmosphere" falloff (dots dimming toward the
     edges) used to live here as a CSS mask-image. Moved to plain
     per-dot alpha math in ambientParticles() (js/preloader.js) —
     some embedded in-app browsers didn't reliably support CSS masks
     on first paint, which showed the un-masked canvas edge-to-edge
     instead of centered/atmospheric. Same visual falloff, no longer
     dependent on that CSS feature at all.
     Driven entirely by JS (loop() in preloaderController()), one
     frame at a time via inline style — NOT a CSS animation/transition.
     A fill-mode animation here was tried once before on a sibling
     element (.preloader-logo-fallback, see below) and it permanently
     overrode later opacity changes; plain inline-style updates every
     frame have no such trap and stay perfectly in sync with the rest
     of the timeline off one shared elapsed-time clock. Default 0 so
     the short/reduced-motion path (which never runs that loop) simply
     never shows this layer, which is correct for that path. */
  opacity: 0;
}

.preloader-logo-particles {
  z-index: 2;
}

.preloader-content {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-8);
  padding-inline: var(--space-6);
}

.preloader-logo-frame {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 0 36px rgba(212, 175, 55, 0.4)) drop-shadow(0 18px 40px rgba(0, 0, 0, 0.7));
}

.preloader-glow {
  position: absolute;
  inset: -45%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(168, 132, 42, 0.32) 0%, rgba(168, 132, 42, 0.1) 45%, transparent 72%);
  filter: blur(36px);
  animation: preloader-glow-breathe 4.5s ease-in-out infinite;
  z-index: -1;
}

@keyframes preloader-glow-breathe {
  0%, 100% { opacity: 0.55; transform: scale(0.92); }
  50% { opacity: 0.95; transform: scale(1.05); }
}

.preloader-logo-fallback {
  width: min(58vw, 340px);
  height: auto;
  display: block;
  /* Default hidden. Two independent, non-conflicting ways this becomes
     visible:
     1. Full cinematic path — js/main.js sets .style.opacity directly,
        once per frame, off the same elapsed-time clock that drives
        everything else. No transition property here on purpose: a
        transition would fight per-frame inline updates (each one
        re-triggering its own animated chase), and — as found and
        fixed once already — a fill-mode CSS *animation* on this exact
        property previously got stuck holding opacity:1 forever,
        permanently hiding the particle canvas underneath it. Inline
        style has neither failure mode.
     2. Short path (reduced motion / returning visitor this session) —
        runSimplified() in main.js adds .is-visible below, which DOES
        transition (that path has no per-frame loop to fight with). */
  opacity: 0;
}

.preloader-logo-fallback.is-visible {
  opacity: 1;
  transition: opacity 400ms var(--ease-in-out);
}

@media (min-width: 640px) {
  .preloader-logo-fallback {
    width: min(30vw, 420px);
  }
}

.preloader-bar {
  width: min(52vw, 200px);
  height: 2px;
  border-radius: var(--radius-full);
  background: rgba(245, 241, 234, 0.1);
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.3s var(--ease-out);
}

.preloader-bar.is-visible {
  opacity: 1;
}

.preloader-bar-fill {
  height: 100%;
  width: 0%;
  border-radius: var(--radius-full);
  background: linear-gradient(90deg, var(--color-gold-dark), var(--color-gold-bright), var(--color-gold));
  box-shadow: 0 0 10px rgba(212, 175, 55, 0.75), 0 0 2px rgba(255, 244, 214, 0.9);
}

.preloader-phrase {
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: rgba(245, 241, 234, 0.75);
  text-align: center;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out);
}

.preloader-phrase.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .preloader-glow {
    animation: none;
    opacity: 0.7;
  }
}

/* =========================================================
   Nav
   ========================================================= */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--nav-height);
  display: flex;
  align-items: center;
  background: transparent;
  transition: background var(--dur-med) var(--ease-out), height var(--dur-med) var(--ease-out),
    border-color var(--dur-med) var(--ease-out), box-shadow var(--dur-med) var(--ease-out);
  border-bottom: 1px solid transparent;
}

.nav.is-scrolled {
  background: rgba(11, 10, 9, 0.86);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  height: 68px;
  border-color: var(--color-border-soft);
  box-shadow: 0 10px 30px -20px rgba(0, 0, 0, 0.6);
}

.nav-inner {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-6);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}

@media (min-width: 768px) {
  .nav-inner {
    padding-inline: var(--space-8);
  }
}

.nav-logo {
  display: flex;
  align-items: center;
}

.logo-mark {
  height: 32px;
  width: auto;
  display: block;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.nav-logo:hover .logo-mark {
  opacity: 0.85;
}

@media (min-width: 768px) {
  .logo-mark {
    height: 36px;
  }
}

.nav-links {
  display: none;
  align-items: center;
  gap: var(--space-8);
}

@media (min-width: 1024px) {
  .nav-links {
    display: flex;
  }
}

.nav-links a {
  position: relative;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding-block: var(--space-2);
  transition: color var(--dur-fast) var(--ease-out), letter-spacing var(--dur-med) var(--ease-out);
}

.nav-links a:hover {
  letter-spacing: 0.18em;
}

.nav-links a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 0;
  height: 1px;
  background: var(--color-gold);
  transition: width var(--dur-med) var(--ease-out);
}

.nav-links a:hover {
  color: var(--color-text);
}

.nav-links a:hover::after {
  width: 100%;
}

.nav-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.nav-cta {
  display: none;
}

@media (min-width: 1024px) {
  .nav-cta {
    display: inline-flex;
    padding: 0.75rem 1.5rem;
  }
}

.nav-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-full);
  cursor: pointer;
}

@media (min-width: 1024px) {
  .nav-toggle {
    display: none;
  }
}

/* Stacked + crossfaded (not display:none/block) so the hamburger can
   morph into the close icon with a subtle rotate — the first beat of
   the menu's open animation sequence. */
.nav-toggle svg {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  transform: translate(-50%, -50%) rotate(0deg);
  opacity: 1;
  transition: opacity var(--dur-med) var(--ease-out), transform var(--dur-med) var(--ease-out);
}

.nav-toggle .icon-close {
  opacity: 0;
  transform: translate(-50%, -50%) rotate(-45deg);
}

body.menu-open .nav-toggle .icon-menu {
  opacity: 0;
  transform: translate(-50%, -50%) rotate(45deg);
}

body.menu-open .nav-toggle .icon-close {
  opacity: 1;
  transform: translate(-50%, -50%) rotate(0deg);
}

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: rgba(11, 10, 9, 0.98);
  backdrop-filter: blur(10px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  transition: opacity var(--dur-med) var(--ease-out), visibility var(--dur-med) var(--ease-out),
    transform var(--dur-med) var(--ease-out);
}

body.menu-open .mobile-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

body.menu-open,
body.privacy-modal-open,
body.concierge-modal-open {
  overflow: hidden;
}

.mobile-menu a:not(.btn) {
  position: relative;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.5rem, 5.3vw, 2.15rem);
  color: var(--color-text);
  transition: color 280ms var(--ease-out), transform 280ms var(--ease-out);
}

.mobile-menu a:not(.btn):hover,
.mobile-menu a:not(.btn):active {
  color: var(--color-gold);
  transform: translateX(7px);
}

/* Thin gold accent line to the left — a quiet cue on hover/tap rather
   than anything flashy. */
.mobile-menu a:not(.btn)::before {
  content: "";
  position: absolute;
  left: -18px;
  top: 50%;
  width: 2px;
  height: 0.85em;
  background: var(--color-gold);
  opacity: 0;
  transform: translateY(-50%) scaleY(0.5);
  transform-origin: center;
  transition: opacity 280ms var(--ease-out), transform 280ms var(--ease-out);
}

.mobile-menu a:not(.btn):hover::before,
.mobile-menu a:not(.btn):active::before {
  opacity: 1;
  transform: translateY(-50%) scaleY(1);
}

/* Marks the current page within the shared mobile menu (e.g. "Link in
   Bio" while actually on /linkinbio) — set aria-current="page" on the
   relevant .mobile-link, nothing else needs to change per page. A thin
   centered underline reads as more considered than a plain color swap. */
.mobile-link[aria-current="page"] {
  color: var(--color-gold);
}

.mobile-link[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.4em;
  transform: translateX(-50%);
  width: 26px;
  height: 1px;
  background: var(--color-gold);
}

/* max-width/box-sizing so no menu item can exceed the fixed, inset:0
   .mobile-menu box on the narrowest phones — .btn's global white-space:
   nowrap otherwise forces "CHECK AVAILABILITY" wider than a 320px
   screen, and since nothing here was clipping that overflow, it was
   stretching the whole page's layout viewport (the real cause of #nav
   rendering wider than the screen at 320px). */
.mobile-menu > * {
  max-width: 100%;
  box-sizing: border-box;
}

/* Entrance sequence: the persistent header (logo + close icon, see
   .nav-toggle above) settles first, then each item cascades in with a
   subtle stagger, ending with the CTA — restarts every time the menu
   opens since the animation only applies while body.menu-open matches. */
.mobile-menu > * {
  opacity: 0;
  transform: translateY(10px);
}

body.menu-open .mobile-menu > * {
  animation: mobile-menu-item-in 480ms var(--ease-out) forwards;
}

body.menu-open .mobile-menu > *:nth-child(1) { animation-delay: 90ms; }
body.menu-open .mobile-menu > *:nth-child(2) { animation-delay: 140ms; }
body.menu-open .mobile-menu > *:nth-child(3) { animation-delay: 190ms; }
body.menu-open .mobile-menu > *:nth-child(4) { animation-delay: 240ms; }
body.menu-open .mobile-menu > *:nth-child(5) { animation-delay: 290ms; }
body.menu-open .mobile-menu > *:nth-child(6) { animation-delay: 340ms; }
body.menu-open .mobile-menu > *:nth-child(7) { animation-delay: 400ms; }

@keyframes mobile-menu-item-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.mobile-menu .btn {
  margin-top: var(--space-4);
  min-height: 44px;
  padding: 0.85rem 1.75rem;
  box-shadow: 0 6px 18px -8px rgba(212, 175, 55, 0.4);
  white-space: normal;
  text-align: center;
}

.mobile-menu .btn:hover {
  box-shadow: 0 8px 20px -8px rgba(212, 175, 55, 0.55);
}

/* =========================================================
   Cinematic Hero — pinned scroll-scrub logo intro
   ========================================================= */
.hero-pin-wrapper {
  position: relative;
  /* Scroll runway (this minus one viewport). The bottom-of-hero darken/
     blur (see updateIndicatorAndOverlay() in main.js) tracks the RAW
     linear scroll fraction across this entire runway — unlike the
     logo dissolve's intentionally front-loaded easing, it never
     plateaus early, so there's no stretch of scroll where the screen
     just sits at a flat, unchanging frame before section 2 appears. */
  height: 125vh;
  background: #000;
}

@media (max-width: 640px) {
  .hero-pin-wrapper {
    /* Shorter runway on phones so section 2 arrives sooner. */
    height: 115vh;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-pin-wrapper {
    height: 100dvh;
  }
}

.hero-pin {
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 1600px;
}

/* Background: video + fallback atmosphere + effects */
.hero-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.hero-bg-fallback {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 60% 50% at 50% 40%, rgba(212, 175, 55, 0.14) 0%, transparent 60%),
    radial-gradient(circle at 20% 15%, #241c10 0%, transparent 55%),
    radial-gradient(circle at 82% 78%, #1c1005 0%, transparent 50%),
    linear-gradient(160deg, #14120f 0%, #0b0a09 55%, #050403 100%);
  overflow: hidden;
}

.hero-bg-fallback::before,
.hero-bg-fallback::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.5;
}

.hero-bg-fallback::before {
  width: 46vw;
  height: 46vw;
  left: -10%;
  top: 10%;
  background: radial-gradient(circle, rgba(212, 175, 55, 0.28), transparent 70%);
  animation: smoke-drift-a 22s ease-in-out infinite;
}

.hero-bg-fallback::after {
  width: 38vw;
  height: 38vw;
  right: -8%;
  bottom: 5%;
  background: radial-gradient(circle, rgba(168, 132, 42, 0.22), transparent 70%);
  animation: smoke-drift-b 26s ease-in-out infinite;
}

@keyframes smoke-drift-a {
  0%, 100% { transform: translate(0, 0) scale(1); opacity: 0.5; }
  50% { transform: translate(6%, 8%) scale(1.15); opacity: 0.75; }
}

@keyframes smoke-drift-b {
  0%, 100% { transform: translate(0, 0) scale(1); opacity: 0.4; }
  50% { transform: translate(-8%, -6%) scale(1.1); opacity: 0.65; }
}

.hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.4s var(--ease-out);
}

.hero-video.is-ready {
  opacity: 1;
}

.hero-video.has-error {
  display: none;
}

.hero-particles,
.hero-logo-particles {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  mix-blend-mode: screen;
}

.hero-particles {
  opacity: 0.85;
  /* Soft radial falloff so the ambient dust reads as atmosphere with
     no edge, rather than a lit rectangle. */
  mask-image: radial-gradient(ellipse 80% 75% at 50% 45%, black 35%, transparent 90%);
  -webkit-mask-image: radial-gradient(ellipse 80% 75% at 50% 45%, black 35%, transparent 90%);
}

.hero-streaks {
  position: absolute;
  inset: 0;
  overflow: hidden;
  mix-blend-mode: screen;
}

.hero-streaks span {
  position: absolute;
  height: 1px;
  width: 140%;
  left: -20%;
  background: linear-gradient(90deg, transparent, rgba(212, 175, 55, 0.4), transparent);
  opacity: 0;
  animation: streak-sweep 9s linear infinite;
}

.hero-streaks span:nth-child(1) { top: 22%; animation-delay: 0s; }
.hero-streaks span:nth-child(2) { top: 48%; animation-delay: 3s; }
.hero-streaks span:nth-child(3) { top: 68%; animation-delay: 6s; }
.hero-streaks span:nth-child(4) { top: 84%; animation-delay: 1.5s; }

@keyframes streak-sweep {
  0% { opacity: 0; transform: translateX(-8%) rotate(-3deg); }
  10% { opacity: 0.55; }
  50% { opacity: 0.12; }
  90% { opacity: 0; }
  100% { opacity: 0; transform: translateX(8%) rotate(-3deg); }
}

.hero-grid {
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(245, 241, 234, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(245, 241, 234, 0.035) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 45%, black 0%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 45%, black 0%, transparent 75%);
  opacity: 0.5;
}

.hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0.85;
  /* Final stop matches --color-bg exactly (rather than a plain black)
     so the fully-darkened bottom edge is pixel-identical to section
     2's own background — no seam at the handoff. */
  background: linear-gradient(180deg, rgba(5, 4, 3, 0.55) 0%, rgba(5, 4, 3, 0.3) 35%, rgba(5, 4, 3, 0.6) 75%, var(--color-bg) 100%);
}

/* Logo stage */
.hero-stage {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transform-style: preserve-3d;
}

.logo-tilt,
.logo-float {
  display: flex;
  align-items: center;
  justify-content: center;
  transform-style: preserve-3d;
  will-change: transform;
}

.logo-float {
  position: relative;
  animation: logo-idle-float 11s ease-in-out infinite;
}

@keyframes logo-idle-float {
  0%   { transform: translateY(0) rotateX(0deg) rotateY(0deg); }
  25%  { transform: translateY(-9px) rotateX(2deg) rotateY(-3deg); }
  50%  { transform: translateY(2px) rotateX(-1.5deg) rotateY(2.5deg); }
  75%  { transform: translateY(7px) rotateX(1deg) rotateY(-2deg); }
  100% { transform: translateY(0) rotateX(0deg) rotateY(0deg); }
}

/*
  The logo never scales or moves as the visitor scrolls — only its
  pixel content changes (solid → eroding at the edges → drifting
  glow particles → gone). .hero-logo-frame holds the drop-shadow;
  .hero-logo is the always-present crisp fallback (no-JS / reduced
  motion); .hero-logo-canvas is absolutely positioned over it and
  is what actually renders the scroll-driven dissolve once JS has
  rasterized the SVG above into particle data (see main.js).
*/
.hero-logo-frame {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 0 50px rgba(212, 175, 55, 0.45)) drop-shadow(0 30px 60px rgba(0, 0, 0, 0.65));
}

.hero-logo {
  display: block;
  position: relative;
  width: min(58vw, 460px);
  height: auto;
  transition: opacity 0.3s ease;
}

@media (min-width: 768px) {
  .hero-logo {
    width: min(38vw, 560px);
  }
}

.hero-logo.is-behind-canvas {
  opacity: 0;
}

.hero-logo-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
}

.hero-logo-canvas.is-ready {
  opacity: 1;
}

.hero-logo-glow {
  position: absolute;
  inset: -30%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(212, 175, 55, 0.35) 0%, rgba(212, 175, 55, 0.08) 45%, transparent 70%);
  filter: blur(30px);
  animation: glow-breathe 6s ease-in-out infinite;
  z-index: -1;
}

@keyframes glow-breathe {
  0%, 100% { opacity: 0.6; transform: scale(0.94); }
  50% { opacity: 1; transform: scale(1.06); }
}

/* Scroll indicator */
.hero-scroll-indicator {
  position: absolute;
  left: 50%;
  bottom: var(--space-8);
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  color: var(--color-text-faint);
  font-size: var(--fs-xs);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  transition: opacity var(--dur-med) var(--ease-out);
}

.hero-scroll-indicator .line {
  width: 1px;
  height: 48px;
  background: linear-gradient(var(--color-gold), transparent);
  animation: scroll-line 1.8s var(--ease-in-out) infinite;
}

@keyframes scroll-line {
  0% {
    transform: scaleY(0);
    transform-origin: top;
  }
  50% {
    transform: scaleY(1);
    transform-origin: top;
  }
  50.1% {
    transform-origin: bottom;
  }
  100% {
    transform: scaleY(0);
    transform-origin: bottom;
  }
}

@media (prefers-reduced-motion: reduce) {
  .logo-float { animation: none; }
  .hero-logo-glow { animation: none; opacity: 0.8; }
  .hero-bg-fallback::before,
  .hero-bg-fallback::after { animation: none; }
  .hero-streaks span { animation: none; opacity: 0; }
  .hero-scroll-indicator .line { animation: none; }
}

/* ============================================================
   Hero background photo slideshow — purely additive layer.
   Lives inside .hero-bg between .hero-bg-fallback and .hero-video,
   so it inherits that stacking position: above the base gradient,
   below the video/particles/streaks/grid/overlay and entirely below
   .hero-stage (logo). Nothing above touches or reorders any of the
   existing hero rules.
   ============================================================ */
.hero-bg-photos {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.hero-bg-photo {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: 50% 40%;
  opacity: 0;
  filter: blur(5px) brightness(0.92) contrast(1.04) saturate(1.05);
  transform: scale(1.04);
  transition: opacity 1s cubic-bezier(0.22, 0.61, 0.36, 1), filter 1s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: opacity, filter;
}

.hero-bg-photo.is-active {
  opacity: 1;
  filter: blur(0) brightness(0.92) contrast(1.04) saturate(1.05);
  animation: hero-photo-kenburns-a 8s ease-in-out forwards;
}

.hero-bg-photo.is-active.kb-alt {
  animation-name: hero-photo-kenburns-b;
}

/* Near-imperceptible continuous push-in/drift while a photo is showing */
@keyframes hero-photo-kenburns-a {
  0%   { transform: scale(1.035) translate(0, 0); }
  100% { transform: scale(1.075) translate(-0.8%, -0.5%); }
}
@keyframes hero-photo-kenburns-b {
  0%   { transform: scale(1.075) translate(-0.7%, 0.45%); }
  100% { transform: scale(1.035) translate(0.55%, -0.4%); }
}

/* Intelligent, per-photo focal points (not simple center-crop) so the
   people in each frame stay in view whether the crop is squeezing
   width (narrow/mobile) or height (wide/ultrawide). */
.hero-bg-photo:nth-child(1) { background-position: 64% 36%; }
.hero-bg-photo:nth-child(2) { background-position: 52% 46%; }
.hero-bg-photo:nth-child(3) { background-position: 50% 40%; }
.hero-bg-photo:nth-child(4) { background-position: 42% 50%; }
.hero-bg-photo:nth-child(5) { background-position: 50% 38%; }

/* Retina/high-DPR source selection — background-size:cover on a
   landscape photo must upscale well past its native resolution to
   cover a tall PORTRAIT mobile viewport (e.g. covering a DPR-3 phone's
   height from a 2400px-wide source needs ~1.9x upscale), which is what
   read as blurry/pixelated on mobile — desktop's much closer landscape
   aspect ratio rarely needed real upscaling, so it looked fine there.
   Same crop/content/position as the 1x file in every case — this is a
   resolution fix, not a design change. */
.hero-bg-photo:nth-child(1) { background-image: url("../assets/Hero/pic-1.jpg?v=3"); }
.hero-bg-photo:nth-child(2) { background-image: url("../assets/Hero/pic-2.jpg?v=3"); }
.hero-bg-photo:nth-child(3) { background-image: url("../assets/Hero/pic-3.jpg?v=3"); }
.hero-bg-photo:nth-child(4) { background-image: url("../assets/Hero/pic-4.jpg?v=3"); }
.hero-bg-photo:nth-child(5) { background-image: url("../assets/Hero/pic-5.jpg?v=3"); }

/* The classic retina-background-image media query — NOT image-set()
   (that was tried first here, but Safari only understands it unprefixed
   from iOS 17 on; anything older silently ignores it and keeps the
   1x/blurry file, which is exactly the bug this was meant to fix).
   -webkit-min-device-pixel-ratio has worked in Mobile Safari since
   iOS ~4-5, so this covers every iPhone in practical use, plus every
   Android/Chrome version via min-resolution.
   ?v=2 cache-busts the @2x files specifically — they were replaced
   in place (same filename, new content) while diagnosing the mobile
   blur report, and unversioned image URLs get cached hard by mobile
   Safari; bump this ?v= number any time these specific files change
   again so devices that already cached v=1 are forced to refetch. */
@media (min-width: 1800px), (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi), (min-resolution: 2dppx) {
  .hero-bg-photo:nth-child(1) { background-image: url("../assets/Hero/pic-1@2x.jpg?v=3"); }
  .hero-bg-photo:nth-child(2) { background-image: url("../assets/Hero/pic-2@2x.jpg?v=3"); }
  .hero-bg-photo:nth-child(3) { background-image: url("../assets/Hero/pic-3@2x.jpg?v=3"); }
  .hero-bg-photo:nth-child(4) { background-image: url("../assets/Hero/pic-4@2x.jpg?v=3"); }
  .hero-bg-photo:nth-child(5) { background-image: url("../assets/Hero/pic-5@2x.jpg?v=3"); }
}

@media (max-width: 767px) {
  .hero-bg-photo:nth-child(1) { background-position: 66% 34%; }
  .hero-bg-photo:nth-child(2) { background-position: 54% 44%; }
  .hero-bg-photo:nth-child(3) { background-position: 50% 38%; }
  .hero-bg-photo:nth-child(4) { background-position: 38% 46%; }
  .hero-bg-photo:nth-child(5) { background-position: 50% 34%; }
}

@media (min-width: 1800px) {
  .hero-bg-photo:nth-child(4) { background-position: 42% 58%; }
}

/* Automatic exposure/color harmonization across the 5 source photos:
   a shared gold/black multiply wash pulls differing lighting (bright
   daylight, sunset, indoor uplighting) toward one consistent grade. */
.hero-bg-photos::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(20, 14, 6, 0.35) 0%, rgba(10, 8, 5, 0.12) 40%, rgba(10, 8, 5, 0.4) 100%),
    radial-gradient(ellipse 90% 70% at 50% 50%, transparent 40%, rgba(5, 4, 3, 0.45) 100%);
  mix-blend-mode: multiply;
  pointer-events: none;
}

/* Golden light sweep bridging one photo to the next */
.hero-bg-photo-sweep {
  position: absolute;
  inset: -10% -30%;
  background: linear-gradient(115deg, transparent 42%, rgba(212, 175, 55, 0.22) 50%, transparent 58%);
  opacity: 0;
  transform: translateX(-40%);
  pointer-events: none;
  mix-blend-mode: screen;
}

.hero-bg-photos.is-transitioning .hero-bg-photo-sweep {
  animation: hero-photo-sweep 1.4s ease-in-out;
}

@keyframes hero-photo-sweep {
  0%   { opacity: 0; transform: translateX(-40%); }
  35%  { opacity: 1; }
  70%  { opacity: 0.4; }
  100% { opacity: 0; transform: translateX(40%); }
}

/* Bridge each transition with the existing ambient particle system —
   a scoped override only; the base .hero-particles rule and the
   particle JS logic itself are both untouched. */
.hero-bg.is-photo-transitioning .hero-particles {
  opacity: 1;
  transition: opacity 0.6s ease;
}

@media (prefers-reduced-motion: reduce) {
  .hero-bg-photo {
    transition: opacity 0.6s ease;
    animation: none !important;
    transform: none !important;
    filter: brightness(0.92) contrast(1.04) saturate(1.05) !important;
  }
  .hero-bg-photo-sweep {
    display: none;
  }
}

/* =========================================================
   Global ambient particles — a single fixed, viewport-covering
   canvas mounted once (see js/global-particles.js for the shared
   engine, instantiated in main.js), shared by the whole page
   rather than confined to one section. z-index:-1 is deliberate:
   it must always paint behind every
   section's own background, never over content. Most dark
   sections (.intro-reveal, .faq-section, .section-alt, .stats,
   .performed, .footer) are either fully transparent or only ~95%
   opaque for exactly this reason, so the same single canvas reads
   as one continuous atmosphere site-wide instead of restarting per
   section. #contact (Booking) is deliberately left fully opaque
   white — the one "light section" case, where particles are
   expected to be fully suppressed rather than dimmed.
   ========================================================= */
.global-particles {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

/* =========================================================
   Hero Statement — "Let's Make It Epic". Section 2, the site's
   signature cinematic moment, rebuilt as a fully layered scene
   rather than a heading with effects bolted on:

     1. deep black section background (transparent — .global-particles
        shows through)
     2. atmosphere: drifting glow orbs + soft volumetric haze
     3. a continuously breathing golden light wave (canvas, see
        heroStatementCinematic() in main.js)
     4. a one-time particle-formation reveal that converges into the
        actual letterforms of EPIC (canvas, real glyph pixel sampling)
     5. typography — brushed-metal EPIC with a periodic light sweep,
        stroke-then-fill "LET'S MAKE IT" above it
     6. a couple of very occasional gold motes drifting in front of
        the text

   All JS-driven pieces are gated behind an IntersectionObserver
   (drawing/animating only while the section is near the viewport)
   and prefers-reduced-motion (see the media query at the bottom).
   ========================================================= */
.hero-statement {
  position: relative;
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-block: var(--space-8);
  overflow: hidden;
}

/* ---- Atmosphere: orbs + haze -------------------------------- */
.hs-atmosphere {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.hs-orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.4;
}

.hs-orb-1 {
  width: 42vw;
  height: 42vw;
  left: -8%;
  top: 6%;
  background: radial-gradient(circle, rgba(212, 175, 55, 0.22), transparent 70%);
  animation: smoke-drift-a 24s ease-in-out infinite;
}

.hs-orb-2 {
  width: 34vw;
  height: 34vw;
  right: -6%;
  bottom: 2%;
  background: radial-gradient(circle, rgba(168, 132, 42, 0.18), transparent 70%);
  animation: smoke-drift-b 28s ease-in-out infinite;
}

/* Broad, very low-opacity fog layer — distinct from the orbs above:
   no defined shape, just a wash of depth so the section never reads
   as flat black even between the orbs. Drifts almost imperceptibly. */
.hs-haze {
  position: absolute;
  inset: -10%;
  background: radial-gradient(ellipse 80% 60% at 50% 55%, rgba(232, 199, 102, 0.05), transparent 70%);
  filter: blur(50px);
  opacity: 0.6;
  animation: hs-haze-drift 42s ease-in-out infinite;
}

@keyframes hs-haze-drift {
  0%,
  100% {
    transform: translate(0, 0) scale(1);
  }
  50% {
    transform: translate(2%, -1.5%) scale(1.06);
  }
}

/* ---- The golden light wave (canvas) -------------------------- */
/* Draws a sharp, slowly-undulating gradient ribbon every frame;
   the blur is applied here as a CSS filter (a compositor/GPU post
   effect) rather than inside the canvas 2D context, which keeps the
   actual per-frame canvas drawing cheap — see drawWave() in
   heroStatementCinematic(). Runs continuously (not just during the
   reveal) and independently from the typography, per spec. */
.hs-wave-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  filter: blur(clamp(20px, 3vw, 40px));
  opacity: 0.9;
  pointer-events: none;
}

/* ---- The particle-formation canvas ---------------------------- */
/* Plays once: hundreds of tiny gold points, sampled from the actual
   rendered glyph shapes of "EPIC" (see sampleGlyphPoints() in
   main.js), converge from scattered positions into the real letter
   outlines, then fade out as the real (brushed-metal) DOM text
   crossfades in underneath. Faded out via CSS opacity in lockstep
   with .hs-line-gold's own fade-in below. */
.hs-formation-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.7s ease;
}

.hero-statement.is-text-done .hs-formation-canvas {
  opacity: 0;
}

/* ---- Typography ------------------------------------------------ */
.hero-statement-inner {
  position: relative;
  width: 100%;
  max-width: 1400px;
  margin-inline: auto;
  padding-inline: var(--space-6);
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-statement-title {
  position: relative;
  z-index: 2;
  font-family: var(--font-mix-display);
  font-weight: 400;
  text-transform: uppercase;
  text-align: center;
  line-height: 1;
  letter-spacing: 0.01em;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  row-gap: var(--space-2);
}

.hs-line {
  position: relative;
  display: inline-block;
  white-space: nowrap;
}

/* "LET'S MAKE IT" — the supporting line: a lighter, cheaper
   stroke-then-fill reveal (no canvas needed for four short words).
   EPIC, below, gets the full particle-formation treatment as the
   section's visual hero. */
.hs-line-white {
  color: var(--color-text);
  font-size: clamp(2.75rem, 4vw + 9vh, 7.5rem);
  clip-path: inset(0 100% 0 0);
}

.hs-line-white::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(232, 199, 102, 0.85);
  text-stroke: 1.5px rgba(232, 199, 102, 0.85);
  opacity: 0;
  pointer-events: none;
}

.hero-statement.is-revealed .hs-line-white {
  animation: hs-fill-wipe 0.9s var(--ease-out) forwards;
  animation-delay: 0.1s;
}

.hero-statement.is-revealed .hs-line-white::before {
  animation: hs-stroke-draw 0.85s ease-out forwards;
}

.hero-statement.is-done .hs-line-white {
  clip-path: none;
  animation: none;
  filter: drop-shadow(0 0 10px rgba(232, 199, 102, 0.18));
}

.hero-statement.is-done .hs-line-white::before {
  opacity: 0;
}

@keyframes hs-fill-wipe {
  from {
    clip-path: inset(0 100% 0 0);
  }
  to {
    clip-path: inset(0 0% 0 0);
  }
}

@keyframes hs-stroke-draw {
  0% {
    opacity: 0;
  }
  40% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* EPIC — the visual hero. Real text starts invisible (the canvas
   formation above stands in for it) and stays that way until JS
   adds .is-text-done once the particles finish converging. A subtle
   brushed-metal finish: a fine repeating grain (never shiny/chrome)
   under a soft diagonal highlight band whose position is what
   .hero-statement.is-done animates — the "light travelling across
   brushed metal" every ~14s. */
.hs-line-gold {
  font-size: clamp(6rem, 9vw + 22vh, 17rem);
  color: transparent;
  opacity: 0;
  transition: opacity 0.6s ease;
  /* Hollow — just a glowing gold outline on each letter, no fill,
     per request (was a solid gradient-filled gold block before). */
  -webkit-text-stroke: 2px var(--color-gold-bright);
  text-stroke: 2px var(--color-gold-bright);
}

.hero-statement.is-text-done .hs-line-gold {
  opacity: 1;
  animation: hs-bloom-pulse 1.3s var(--ease-out) forwards;
}

/* Belt-and-suspenders final state: main.js adds .is-done a beat
   after the bloom finishes, hard-locking the settled look (persistent
   rim glow, brushed metal at rest) via plain property values rather
   than relying solely on animation-fill-mode — then hands off to the
   slow, infinite sheen-sweep loop as the only ongoing animation. */
.hero-statement.is-done .hs-line-gold {
  filter: drop-shadow(0 0 26px rgba(232, 199, 102, 0.42));
  animation: hs-metal-sheen 14s ease-in-out infinite;
  animation-delay: 1s;
}

/* Single cinematic bloom as EPIC's particles finish converging and
   the real text crossfades in — never a loop, never a flicker. */
@keyframes hs-bloom-pulse {
  0% {
    filter: drop-shadow(0 0 0 rgba(232, 199, 102, 0));
  }
  55% {
    filter: drop-shadow(0 0 46px rgba(232, 199, 102, 0.7));
  }
  100% {
    filter: drop-shadow(0 0 26px rgba(232, 199, 102, 0.42));
  }
}

/* Only the first background layer (the highlight band) moves — the
   grain and base-tone layers stay put. Spends most of the 14s cycle
   parked off to the left (invisible); the sweep itself takes ~3s,
   "light travelling across brushed metal" per spec. */
@keyframes hs-metal-sheen {
  0%,
  78% {
    background-position: -170% 0, 0 0, 0 0;
  }
  92% {
    background-position: 170% 0, 0 0, 0 0;
  }
  100% {
    background-position: 280% 0, 0 0, 0 0;
  }
}

/* ---- Occasional motes drifting in front of the typography ------ */
.hs-front-mote {
  position: absolute;
  z-index: 3;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 244, 214, 0.9), rgba(232, 199, 102, 0.4) 60%, transparent 75%);
  filter: blur(0.5px);
  opacity: 0;
  pointer-events: none;
}

.hs-front-mote-1 {
  top: 36%;
  left: 22%;
}

.hs-front-mote-2 {
  top: 64%;
  right: 20%;
}

.hero-statement.is-done .hs-front-mote-1 {
  animation: hs-front-drift 17s ease-in-out infinite;
  animation-delay: 3s;
}

.hero-statement.is-done .hs-front-mote-2 {
  animation: hs-front-drift 21s ease-in-out infinite;
  animation-delay: 9s;
}

@keyframes hs-front-drift {
  0%,
  88% {
    opacity: 0;
    transform: translate(0, 0) scale(0.8);
  }
  92% {
    opacity: 0.85;
  }
  96% {
    opacity: 0.85;
    transform: translate(38px, -22px) scale(1.1);
  }
  100% {
    opacity: 0;
    transform: translate(60px, -34px) scale(0.9);
  }
}

@media (max-width: 640px) {
  .hero-statement {
    min-height: 58vh;
    padding-block: var(--space-6);
  }
  .hs-line-white {
    font-size: clamp(2.2rem, 9vw + 2vh, 3.5rem);
  }
  /* EPIC becomes almost edge-to-edge on mobile, per spec, while
     staying inside the viewport at every phone width. */
  .hs-line-gold {
    font-size: clamp(5rem, 26vw + 6vh, 9rem);
  }
  .hs-orb-1,
  .hs-orb-2 {
    filter: blur(60px);
  }
  .hs-wave-canvas {
    filter: blur(22px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hs-line-white {
    clip-path: none;
  }
  .hs-line-white::before {
    display: none;
  }
  .hero-statement.is-revealed .hs-line-white {
    animation: none;
  }
  .hs-line-gold {
    opacity: 1;
    transition: none;
  }
  .hero-statement.is-text-done .hs-line-gold,
  .hero-statement.is-done .hs-line-gold {
    animation: none;
    filter: drop-shadow(0 0 18px rgba(232, 199, 102, 0.3));
  }
  .hs-formation-canvas {
    display: none;
  }
  .hs-orb-1,
  .hs-orb-2,
  .hs-haze,
  .hs-front-mote {
    animation: none !important;
    display: none;
  }
}

/* =========================================================
   Intro reveal — the payoff section right after the hero.
   Full-bleed cinematic media (real wedding photo + optional
   video layered on top), a left-to-right overlay that stays
   dark behind the text and lets the imagery read more clearly
   toward the right edge, then the text/highlights/CTAs on top.
   ========================================================= */
.intro-reveal {
  position: relative;
  overflow: hidden;
  min-height: 88vh;
  display: flex;
  align-items: center;
  /* Top padding kept modest on purpose: the hero's own darken/blur (see
     .hero-overlay / #heroBgPhotos in main.js) already completes the
     visual transition by the time this section starts, so this only
     needs to give the heading room to breathe — not act as a second,
     separate black spacer stacked on top of that fade. Bottom padding
     is generous so the overlay has room to fade to solid black before
     the gold marquee strip begins right after this section. */
  padding-block: var(--space-16) var(--space-24);
}

.intro-reveal-media {
  position: absolute;
  /* Extra vertical buffer (beyond the usual -2px anti-seam inset) so
     the parallax translateY in introRevealParallax() (js/main.js) —
     a max ~18px shift — never reveals a gap at the section's top or
     bottom edge. */
  inset: -40px -2px;
  z-index: 0;
  overflow: hidden;
}

.intro-reveal-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  animation: intro-ken-burns 26s ease-in-out infinite alternate;
}

@keyframes intro-ken-burns {
  0% {
    transform: scale(1);
  }
  100% {
    transform: scale(1.08);
  }
}

@media (prefers-reduced-motion: reduce) {
  .intro-reveal-img {
    animation: none;
  }
}

.intro-reveal-overlay {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(
      90deg,
      rgba(6, 5, 4, 0.9) 0%,
      rgba(6, 5, 4, 0.86) 46%,
      rgba(6, 5, 4, 0.5) 72%,
      rgba(6, 5, 4, 0.38) 100%
    ),
    linear-gradient(180deg, rgba(6, 5, 4, 0.35) 0%, transparent 22%, transparent 68%, rgba(6, 5, 4, 0.95) 100%);
  box-shadow: inset 0 0 16vw rgba(0, 0, 0, 0.45);
}

@media (max-width: 900px) {
  .intro-reveal-overlay {
    background: linear-gradient(
        180deg,
        rgba(6, 5, 4, 0.92) 0%,
        rgba(6, 5, 4, 0.78) 45%,
        rgba(6, 5, 4, 0.88) 78%,
        rgba(6, 5, 4, 0.97) 100%
      );
  }
}

.intro-reveal-inner {
  position: relative;
  z-index: 1;
  max-width: 820px;
}

.intro-reveal .hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--color-gold-bright);
  margin-bottom: var(--space-6);
}

.intro-reveal .hero-eyebrow .pulse {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-gold);
  box-shadow: 0 0 0 0 rgba(212, 175, 55, 0.7);
  animation: pulse 2.2s var(--ease-in-out) infinite;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(212, 175, 55, 0.55);
  }
  70% {
    box-shadow: 0 0 0 10px rgba(212, 175, 55, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(212, 175, 55, 0);
  }
}

.intro-reveal h1 {
  font-size: var(--fs-hero);
  max-width: 22ch;
  text-shadow: 0 2px 28px rgba(0, 0, 0, 0.5);
}

.intro-reveal h1 em {
  display: block;
  margin-top: var(--space-2);
  color: var(--color-gold);
  font-style: italic;
}

.intro-reveal .hero-sub {
  max-width: 48ch;
  font-size: var(--fs-lead);
  color: var(--color-text-muted);
  margin-top: var(--space-4);
  text-shadow: 0 1px 18px rgba(0, 0, 0, 0.6);
}

.intro-reveal .hero-sub + .hero-sub {
  margin-top: var(--space-3);
}

.intro-reveal-highlights {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4) var(--space-8);
  margin-top: var(--space-8);
}

.intro-reveal-highlights li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.intro-reveal-highlights svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 3px;
  color: var(--color-gold);
}

.intro-reveal-highlights strong {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-small);
  margin-bottom: 2px;
}

.intro-reveal-highlights span {
  display: block;
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
  line-height: 1.5;
}

.intro-reveal .hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-8);
}

@media (max-width: 640px) {
  .intro-reveal {
    min-height: 0;
    padding-block: var(--space-16);
  }

  .intro-reveal-highlights {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
}

/* =========================================================
   Stats strip
   ========================================================= */
.stats {
  background: color-mix(in srgb, var(--color-bg-alt) 95%, transparent);
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-8);
  padding-block: var(--space-12);
  text-align: center;
}

@media (min-width: 768px) {
  .stats-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

.stat-num {
  font-family: var(--font-display);
  font-size: clamp(2rem, 3vw, 2.75rem);
  color: var(--color-gold);
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.15em;
}

.stat-label {
  margin-top: var(--space-2);
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* =========================================================
   About
   ========================================================= */
.about-grid {
  display: grid;
  gap: var(--space-12);
  align-items: center;
}

@media (min-width: 900px) {
  .about-grid {
    grid-template-columns: 0.85fr 1fr;
    gap: var(--space-24);
  }
}

.about-media {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: linear-gradient(160deg, #201a10, #0b0a09 70%);
}

.about-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.8s var(--ease-out);
}

.about-media img.is-loaded {
  opacity: 1;
}

.about-media .placeholder-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-gold-soft);
}

.about-media .placeholder-icon svg {
  width: 30%;
  height: 30%;
  opacity: 0.5;
}

.about-media-frame {
  position: absolute;
  inset: 18px;
  border: 1px solid rgba(245, 241, 234, 0.25);
  border-radius: calc(var(--radius-lg) - 6px);
  pointer-events: none;
}

.about-badge {
  position: absolute;
  bottom: var(--space-6);
  left: var(--space-6);
  right: var(--space-6);
  background: rgba(11, 10, 9, 0.72);
  backdrop-filter: blur(10px);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-6);
}

.about-badge .badge-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
}

.about-badge .badge-sub {
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-top: 2px;
}

.about-copy h2 {
  font-size: var(--fs-h2);
  margin-bottom: var(--space-6);
}

.about-copy p {
  color: var(--color-text-muted);
  margin-bottom: var(--space-4);
}

.about-copy p:last-of-type {
  margin-bottom: 0;
}

.about-teaser-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-6);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-gold);
  transition: gap var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.about-teaser-link svg {
  width: 16px;
  height: 16px;
  transition: transform var(--dur-fast) var(--ease-out);
}

.about-teaser-link:hover {
  color: var(--color-gold-bright);
  gap: var(--space-3);
}

.about-teaser-link:hover svg {
  transform: translateX(3px);
}

/* =========================================================
   Services — six full-bleed cinematic cards in a horizontal,
   snap-scrolling sequence (see servicesScroller() in main.js for
   the active-card detection, prev/next buttons, and progress
   dots). Each card layers a treated photo background beneath a
   left-anchored glass content panel, reusing the same photo-bg +
   overlay technique already established for .testimonial-slide
   above — one real background image per card, blurred/desaturated/
   darkened just enough to stay legible behind text while still
   reading as a real photo on the right side, where nothing else
   is layered on top of it.
   ========================================================= */
.services-section .container {
  padding-inline: var(--space-6);
}

@media (min-width: 900px) {
  .services-section .container {
    padding-inline: var(--space-8);
  }
}

.services-scroller {
  position: relative;
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  margin-top: var(--space-8);
  cursor: grab;
  /* Single source of truth for the outer gutter, consumed by both
     scroll-padding-inline below (so the browser's own snap engine
     knows this inset exists) and .services-track's padding-inline
     (the actual visual gutter). Mobile: 20-24px. */
  --services-gutter: clamp(20px, 5vw, 24px);
  /* Without this, scroll-snap-align:start on the first card snaps
     scrollLeft to the point where the CARD's edge (not the track's
     padding) touches the scrollport edge — which silently scrolls
     the leading gutter away on load, leaving the card flush against
     the viewport. scroll-padding-inline tells the native snap
     algorithm this inset is part of the scrollport, so it correctly
     snaps to scrollLeft:0 (gutter visible) instead. */
  scroll-padding-inline: var(--services-gutter);
}

@media (min-width: 640px) {
  .services-scroller {
    /* Tablet: 48-72px. */
    --services-gutter: clamp(48px, 6vw, 72px);
  }
}

@media (min-width: 900px) {
  .services-scroller {
    /* Desktop: matches the exact alignment of every .container-based
       section on the page (Hero/About/Featured Mix/Testimonials/
       Booking/FAQ all use the same max-width:var(--container-max),
       centered, + var(--space-8) padding formula) — never just a
       flat clamp, so the carousel is never misaligned with the rest
       of the site's grid on wide monitors, with an 80-140px floor/
       ceiling for the range where the container math alone would
       otherwise sit under 80px. */
    --services-gutter: max(clamp(80px, 8vw, 140px), calc((100vw - var(--container-max)) / 2 + var(--space-8)));
  }
}

.services-scroller.is-dragging {
  cursor: grabbing;
  scroll-snap-type: none;
  user-select: none;
}

.services-scroller::-webkit-scrollbar {
  display: none;
}

.services-scroller:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: -2px;
}

.services-track {
  display: flex;
  gap: var(--space-6);
  /* Leading gutter only — the trailing gutter is a dedicated spacer
     flex item below, not padding-inline-end. Padding at the trailing
     edge of a scrolled flex container isn't reliably counted toward
     scrollWidth, so relying on it here left the last card unable to
     actually scroll far enough to reveal a matching right-side gap. */
  padding-inline-start: var(--services-gutter);
  padding-block: var(--space-2) var(--space-4);
}

.services-track-spacer {
  /* Full gutter width, unconditionally — the flex `gap` that would
     normally land between the last card and this spacer is canceled
     with a matching negative margin, so this element's own width is
     the ONLY source of trailing space (mirrors padding-inline-start
     being the only source of leading space). Subtracting the gap
     from the spacer's width instead (rather than canceling it) broke
     down on mobile, where gutter (20px) is smaller than gap (24px):
     scroll-snap-align:end had no real scrollable room left to create
     trailing space at all, so the last card landed flush with zero
     gap instead of the intended ~20px. */
  flex: 0 0 var(--services-gutter);
  width: var(--services-gutter);
  margin-inline-start: calc(-1 * var(--space-6));
  scroll-snap-align: end;
  pointer-events: none;
}

.service-card {
  position: relative;
  flex: 0 0 auto;
  width: min(88vw, 1180px);
  /* All six cards reserve enough vertical room for the longest title
     and description. The former 620px ceiling let the CTA fall below
     the clipped content panel on shorter desktop windows. */
  height: clamp(640px, 78vh, 700px);
  border-radius: var(--radius-lg);
  overflow: hidden;
  scroll-snap-align: start;
  border: 1px solid rgba(212, 175, 55, 0.16);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 24px 60px -30px rgba(0, 0, 0, 0.6);
  transition: transform 0.6s var(--ease-out), border-color 300ms var(--ease-out);
}

/* The 72vh clamp above only has a width-based safety net (the
   max-width:900px rule further down) — a wide-but-short viewport
   (a phone in landscape, a short browser window) is "desktop width"
   so it never gets that rule, yet 72vh of a short viewport can still
   land well under what the card's content actually needs, clipping
   it regardless of which edge it's anchored from. This catches that
   case directly by viewport height instead of width. */
@media (max-height: 700px) and (min-width: 901px) {
  .service-card {
    height: 660px;
    min-height: 660px;
  }
}


.service-card.is-active {
  transform: scale(1.012);
  border-color: rgba(212, 175, 55, 0.3);
}

/* Oversized by a couple of px (inset:-2px) so the compositor's own
   layer edge is always clipped clean by the card's overflow:hidden,
   regardless of sub-pixel rounding — the same fix used for the
   Featured Mix background video earlier in this file. */
.service-card-bg {
  position: absolute;
  inset: -2px;
  width: calc(100% + 4px);
  height: calc(100% + 4px);
  object-fit: cover;
  object-position: center;
  /* Lighter touch than the first pass: less blur, more contrast, so
     people and moments stay recognizable — the photo should tell
     the story, not disappear behind the treatment.
     No blur() here on purpose — combining a blur filter with
     transform:scale() on a large image is a known trigger for a
     visible tile-seam rendering artifact in some browsers/GPUs (a
     hard line where the browser's internal tiling splits the
     blurred layer). saturate/brightness/contrast don't have that
     problem, and 1px of blur was subtle enough that removing it
     isn't a visible loss. */
  filter: saturate(0.92) brightness(0.88) contrast(1.08);
  transform: scale(1.08);
  transition: transform 8s linear;
}

/* Per-photo focal points, desktop only. Each source photo is a square
   (1600x1600) Moments crop, but on desktop .service-card is a wide
   landscape box (~1.95:1) — object-fit:cover on a square-into-
   landscape crop only shows the middle ~51% of the photo's height,
   and in every one of these 6 photos the people's heads sit well
   above center, so a plain "center" position was cropping straight
   through their faces. On mobile the card is closer to portrait
   (taller than wide), so cover crops the sides instead of the top/
   bottom and this isn't an issue there — left untouched. */
@media (min-width: 900px) {
  .service-card[data-index="0"] .service-card-bg { object-position: center 20%; }
  .service-card[data-index="1"] .service-card-bg { object-position: center 15%; }
  .service-card[data-index="2"] .service-card-bg { object-position: center 28%; }
  .service-card[data-index="3"] .service-card-bg { object-position: center 16%; }
  .service-card[data-index="4"] .service-card-bg { object-position: center 12%; }
  .service-card[data-index="5"] .service-card-bg { object-position: center 28%; }
}

/* Ken Burns: a slow, continuous zoom while a card is the active one
   — "slowly zoom", never a snap, and it simply stops (holding
   whatever scale it reached) the moment the card stops being active,
   rather than resetting. */
.service-card.is-active .service-card-bg {
  transform: scale(1.18);
  transition: transform 8s linear;
}

.service-card-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(6, 5, 4, 0.8) 0%, rgba(6, 5, 4, 0.6) 40%, rgba(6, 5, 4, 0.22) 66%, rgba(6, 5, 4, 0.08) 100%),
    linear-gradient(180deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.22) 100%);
  box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

.service-card-content {
  position: relative;
  z-index: 1;
  height: 100%;
  width: clamp(460px, 50%, 500px);
  display: flex;
  flex-direction: column;
  /* Top-anchored with the CTA pushed down via margin-top:auto on
     .service-card-cta-row (not justify-content:flex-end on this
     whole container) — flex-end silently clipped content off the
     TOP edge (the icon) on any card/viewport combination where the
     total content was taller than the card's height, since
     overflow:hidden + flex-end grows the overflow upward first.
     Anchoring from the top instead means the icon/eyebrow/title are
     always the first things laid out and visible; if anything is
     ever too tall to fit, it's the bottom of the description that
     gets clipped instead, which is a far less damaging failure. */
  padding: var(--space-12);
  overflow: hidden;
}

/* Glass reflection — an extremely faint diagonal highlight that
   slowly drifts across the content panel while its card is active,
   "glass reflections should subtly move" per the brief. */
.service-card-content::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg, transparent 30%, rgba(255, 255, 255, 0.05) 50%, transparent 70%);
  background-size: 240% 240%;
  background-position: 0% 0%;
  opacity: 0;
  transition: opacity 0.8s ease;
  pointer-events: none;
}

.service-card.is-active .service-card-content::before {
  opacity: 1;
  animation: service-glass-shine 10s ease-in-out infinite;
}

@keyframes service-glass-shine {
  0%,
  100% {
    background-position: 0% 0%;
  }
  50% {
    background-position: 100% 100%;
  }
}

.service-card-icon {
  position: relative;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-md);
  background: rgba(212, 175, 55, 0.14);
  border: 1px solid rgba(212, 175, 55, 0.32);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-gold);
  margin-bottom: var(--space-5);
  flex-shrink: 0;
}

.service-card-icon svg {
  width: 21px;
  height: 21px;
}

/* The category label is the primary navigation cue while scanning
   the carousel, not a footnote — sized and weighted to be read at a
   glance, not squinted at. */
.service-card-eyebrow {
  position: relative;
  display: block;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-gold);
  opacity: 1;
  margin-bottom: var(--space-4);
}

.service-card-content h3 {
  position: relative;
  font-size: clamp(1.85rem, 1.1rem + 2vw, 3rem);
  color: #fff;
  margin: 0 0 var(--space-5);
  line-height: 1.18;
}

.service-card-content p {
  position: relative;
  color: rgba(255, 255, 255, 0.82);
  font-size: 1rem;
  line-height: 1.5;
  margin: 0 0 var(--space-5);
  max-width: 42ch;
}

.service-card-checklist {
  position: relative;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin: 0;
  padding: 0;
}

.service-card-checklist li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: 0.88rem;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.86);
}

.service-card-checklist svg {
  width: 15px;
  height: 15px;
  margin-top: 2px;
  color: var(--color-gold);
  flex-shrink: 0;
}

/* Smaller than the site's default .btn — luxury interfaces lean on
   whitespace, not oversized buttons, to draw the eye. */
.service-card-cta {
  position: relative;
  padding: 0.7rem 1.75rem;
  min-height: 40px;
  font-size: 0.78rem;
}

@media (hover: hover) and (pointer: fine) {
  .service-card:hover .service-card-bg {
    transform: scale(1.12);
  }
}

/* ---- Prev/next + progress dots ---------------------------- */
.services-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  margin-top: var(--space-8);
}

.services-nav-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  flex-shrink: 0;
  transition: border-color 250ms var(--ease-out), background 250ms var(--ease-out), color 250ms var(--ease-out),
    translate 250ms var(--ease-out);
}

.services-nav-btn svg {
  width: 20px;
  height: 20px;
}

.services-nav-btn:hover {
  border-color: var(--color-gold);
  background: var(--color-gold-soft);
  color: var(--color-gold);
}

.services-nav-btn:active {
  translate: 0 1px;
}

.services-progress {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.services-progress-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  border: none;
  padding: 0;
  background: var(--color-border);
  cursor: pointer;
  transition: background 250ms var(--ease-out), transform 250ms var(--ease-out);
}

.services-progress-dot:hover {
  background: var(--color-gold-soft);
}

.services-progress-dot.is-active {
  background: var(--color-gold);
  transform: scale(1.3);
}

/* ---- One-time discovery nudge --------------------------------- */
/* Plays once, the first time the section enters the viewport (see
   servicesScroller() in main.js), unless the visitor has already
   interacted with the carousel — a pure transform animation on the
   track itself (GPU-accelerated, never touches scrollLeft), so it
   can never fight the real scroll position or the active-card
   detection logic. */
@keyframes services-nudge {
  0% {
    transform: translateX(0);
  }
  40% {
    transform: translateX(-24px);
  }
  62% {
    transform: translateX(-24px);
  }
  100% {
    transform: translateX(0);
  }
}

.services-track.services-nudge-play {
  animation: services-nudge 1.4s var(--ease-out) both;
}

/* ---- "Drag/Swipe to explore" hint ------------------------------ */
.services-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(212, 175, 55, 0.55);
  transition: opacity 0.6s ease;
}

.services-hint.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.services-hint-arrow {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
}

.services-hint-mobile {
  display: none;
}

@media (max-width: 640px) {
  .services-hint-desktop {
    display: none;
  }
  .services-hint-mobile {
    display: inline;
  }
}

@media (prefers-reduced-motion: reduce) {
  .services-track.services-nudge-play {
    animation: none;
  }
}

@media (max-width: 900px) {
  .service-card {
    width: 88vw;
    height: auto;
    min-height: 560px;
  }

  .service-card-content {
    width: 100%;
    justify-content: flex-end;
    padding: var(--space-8) var(--space-6);
  }

  .service-card-overlay {
    background: linear-gradient(180deg, rgba(6, 5, 4, 0.2) 0%, rgba(6, 5, 4, 0.45) 38%, rgba(6, 5, 4, 0.86) 72%, rgba(6, 5, 4, 0.92) 100%);
  }

  .services-nav-btn {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .service-card,
  .service-card-bg,
  .services-nav-btn,
  .services-progress-dot {
    transition: none !important;
  }
  .service-card.is-active .service-card-content::before {
    animation: none;
    opacity: 0;
  }
  .service-card.is-active .service-card-bg {
    transform: scale(1.08);
  }
}

/* =========================================================
   Gallery — "Moments" moving editorial photo wall
   ---------------------------------------------------------
   .moments-carousel/.moments-track/.moments-set/.moments-col
   handle the staggered layout + infinite-loop duplication +
   edge fade. .gallery-item and its children (img/placeholder-
   icon/caption/hover overlay) keep their existing look and the
   existing lightbox click wiring untouched — they're just laid
   out inside carousel columns instead of a static CSS grid.
   ========================================================= */
.moments-carousel {
  --ch: 460px;
  position: relative;
  overflow: hidden;
  margin-top: var(--space-8);
  cursor: grab;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, black 6%, black 94%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, black 6%, black 94%, transparent 100%);
}

.moments-carousel.is-dragging {
  cursor: grabbing;
}

.moments-track {
  display: flex;
  align-items: flex-start;
  gap: var(--space-6);
  width: max-content;
  will-change: transform;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.moments-set {
  display: flex;
  align-items: flex-start;
  gap: var(--space-6);
  flex-shrink: 0;
}

.moments-col {
  height: var(--ch);
  flex-shrink: 0;
}

.moments-col-tall {
  width: 240px;
}

.moments-col-stack {
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.moments-col-stack .gallery-item:first-child {
  flex: 0 0 var(--split, 55%);
}

.moments-col-stack .gallery-item:last-child {
  flex: 1 1 auto;
}

@media (max-width: 1024px) {
  .moments-carousel {
    --ch: 380px;
  }
  .moments-col-tall {
    width: 190px;
  }
  .moments-col-stack {
    width: 250px;
  }
}

@media (max-width: 640px) {
  .moments-carousel {
    --ch: 300px;
  }
  .moments-col-tall {
    width: 150px;
  }
  .moments-col-stack {
    width: 200px;
  }
  .moments-track,
  .moments-set {
    gap: var(--space-3);
  }
}

.moments-carousel img {
  -webkit-user-drag: none;
  user-drag: none;
}

@media (prefers-reduced-motion: reduce) {
  .moments-carousel {
    cursor: default;
  }
}

.gallery-item {
  position: relative;
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  background: linear-gradient(160deg, #201a10, #0b0a09 70%);
}

.moments-col .gallery-item {
  width: 100%;
  height: 100%;
}

.gallery-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: transform 0.6s var(--ease-out), opacity 0.6s var(--ease-out);
}

.gallery-item img.is-loaded {
  opacity: 1;
}

.gallery-item:hover img {
  transform: scale(1.08);
}

/* Loading state — a slow, subtle shimmer over the dark gradient
   background instead of a generic image/broken-image icon. Sits
   behind the <img> in paint order (::before, unlike the hover
   overlay below which needs to stay ::after/on top), so once the
   photo fades in over it the shimmer is simply covered — :has()
   below also halts the animation outright once loaded, both so nothing
   is left running under an opaque image and to respect
   prefers-reduced-motion via the site-wide transition-duration
   override (this uses animation, so it's covered separately below). */
.gallery-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 35%, rgba(212, 175, 55, 0.07) 50%, transparent 65%);
  background-size: 200% 100%;
  background-position: -40% 0;
  animation: gallery-shimmer 2.4s ease-in-out infinite;
  pointer-events: none;
}

.gallery-item:has(img.is-loaded)::before {
  animation: none;
  opacity: 0;
}

@keyframes gallery-shimmer {
  0%,
  100% {
    background-position: -40% 0;
  }
  50% {
    background-position: 140% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gallery-item::before {
    animation: none;
    opacity: 0;
  }
}

.gallery-item::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 50%, rgba(11, 10, 9, 0.85) 100%);
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out);
}

.gallery-item:hover::after {
  opacity: 1;
}

.gallery-item .gallery-caption {
  position: absolute;
  left: var(--space-4);
  right: var(--space-4);
  bottom: var(--space-4);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-med) var(--ease-out), transform var(--dur-med) var(--ease-out);
}

.gallery-item:hover .gallery-caption {
  opacity: 1;
  transform: translateY(0);
}

/* Lightbox */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: rgba(6, 5, 4, 0.94);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-8);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-med) var(--ease-out), visibility var(--dur-med) var(--ease-out);
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
}

.lightbox img {
  max-width: min(90vw, 900px);
  max-height: 85vh;
  border-radius: var(--radius-md);
  object-fit: contain;
}

.lightbox-close {
  position: absolute;
  top: var(--space-6);
  right: var(--space-6);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--color-border-soft);
  background: rgba(255, 255, 255, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.lightbox-close svg {
  width: 18px;
  height: 18px;
}

/* =========================================================
   Content protection — media drag/select/callout guards +
   Privacy Notice modal
   ---------------------------------------------------------
   Scoped to actual <img>/<video> elements only (never their
   containers), so captions, overlays, buttons and links stay
   completely normal — this only ever changes how the media
   element itself responds to drag/long-press/selection, never
   click or tap. See js/content-protection.js for the JS side
   (contextmenu/dragstart guards + the modal's open/close logic).
   ========================================================= */
img,
video {
  -webkit-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

.privacy-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: rgba(6, 5, 4, 0.6);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 320ms var(--ease-out), visibility 0s linear 320ms;
}

.privacy-modal-backdrop.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 320ms var(--ease-out), visibility 0s linear 0s;
}

.privacy-modal {
  position: relative;
  width: 100%;
  max-width: 480px;
  max-height: 85vh;
  overflow-y: auto;
  border-radius: 28px;
  padding: var(--space-12) var(--space-8);
  background: linear-gradient(165deg, rgba(255, 226, 150, 0.05), rgba(0, 0, 0, 0) 45%),
    linear-gradient(160deg, rgba(30, 24, 14, 0.92), rgba(10, 8, 6, 0.98));
  border: 1px solid rgba(212, 175, 55, 0.22);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 30px 80px -30px rgba(0, 0, 0, 0.7),
    0 0 60px -20px rgba(212, 175, 55, 0.15);
  transform: scale(0.95);
  opacity: 0;
  transition: transform 320ms var(--ease-out), opacity 320ms var(--ease-out);
}

.privacy-modal-backdrop.is-open .privacy-modal {
  transform: scale(1);
  opacity: 1;
}

.privacy-modal-title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--color-text);
  margin-bottom: var(--space-5);
  text-align: center;
}

.privacy-modal-body {
  font-size: var(--fs-small);
  line-height: 1.7;
  color: var(--color-text-muted);
  text-align: center;
}

.privacy-modal-body p {
  margin-bottom: var(--space-4);
}

.privacy-modal-body a {
  color: var(--color-gold);
  font-weight: 600;
  transition: color var(--dur-fast) var(--ease-out);
}

.privacy-modal-body a:hover {
  color: var(--color-gold-bright);
}

.privacy-modal-actions {
  display: flex;
  justify-content: center;
  margin-top: var(--space-6);
}

.privacy-modal-actions .btn {
  min-width: 160px;
}

@media (max-width: 480px) {
  .privacy-modal {
    padding: var(--space-8) var(--space-6);
    border-radius: 24px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .privacy-modal {
    transform: none;
  }
}

/* =========================================================
   Mixes
   ---------------------------------------------------------
   The mix-card layout itself lives in css/mix-card.css
   (shared MixCard component, driven by js/mix-card.js) —
   only the note/platform-links below stay here.
   ========================================================= */
.mixes-note {
  margin-top: var(--space-6);
  font-size: var(--fs-xs);
  color: var(--color-text-faint);
  text-align: center;
}

.platform-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: center;
  margin-top: var(--space-12);
}

.platform-links a,
.platform-links .platform-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border: 1px solid rgba(255, 255, 255, 0.92);
  border-radius: var(--radius-full);
  font-size: var(--fs-small);
  font-weight: 700;
  color: #111;
  background: #fff;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}

.platform-links a:hover {
  color: #111;
  background: #f4f4f4;
  border-color: #fff;
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.24);
}

.platform-links a:focus-visible {
  outline: 2px solid var(--color-gold-bright);
  outline-offset: 4px;
}

.platform-links .is-disabled {
  opacity: 0.56;
  cursor: not-allowed;
}

.platform-links svg {
  width: 18px;
  height: 18px;
}

/* =========================================================
   Where We've Performed — venue/client logo marquee
   ========================================================= */
.performed {
  position: relative;
  background: color-mix(in srgb, var(--color-bg-alt) 95%, transparent);
  padding-block: var(--space-24);
}

.performed-head {
  text-align: center;
  margin-bottom: var(--space-16);
}

.performed-head h2 {
  font-size: var(--fs-h2);
}

.performed-head p {
  margin-top: var(--space-4);
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-body);
  letter-spacing: 0.02em;
  color: var(--color-text-muted);
}

.logo-marquee {
  position: relative;
  overflow: hidden;
  padding-block: var(--space-6);
  /* Soft fade on both edges so logos enter/exit the frame smoothly
     instead of clipping abruptly. */
  mask-image: linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%);
}

.logo-marquee-track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: logo-marquee-scroll 42s linear infinite;
}

.logo-marquee:hover .logo-marquee-track {
  animation-play-state: paused;
}

.logo-marquee-group {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.logo-marquee-item {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding-inline: var(--space-8);
}

.logo-marquee-item img {
  height: 124px;
  width: auto;
  display: block;
  opacity: 0.88;
  filter: brightness(1) saturate(1);
  transition: transform var(--dur-med) var(--ease-out), filter var(--dur-med) var(--ease-out),
    opacity var(--dur-med) var(--ease-out);
}

.logo-marquee-item:hover img {
  opacity: 1;
  filter: brightness(1.25);
  transform: scale(1.05);
}

/* Exactly 1/4 of the track's width — four identical groups tile so
   this lands precisely back at the start of the next group, with no
   visible seam, jump, or restart. */
@keyframes logo-marquee-scroll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-25%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .logo-marquee-track {
    animation: none;
  }
}

@media (max-width: 1024px) {
  .logo-marquee-item img {
    height: 104px;
  }
}

@media (max-width: 640px) {
  .performed {
    padding-block: var(--space-16);
  }
  .logo-marquee-item {
    padding-inline: var(--space-6);
  }
  .logo-marquee-item img {
    height: 80px;
  }
}

/* =========================================================
   Testimonials — single-card crossfade carousel
   ---------------------------------------------------------
   All slides are stacked (position:absolute; top/left/width
   only, no bottom) so each keeps its own natural content
   height regardless of active state — .testimonial-viewport
   reads that height in JS and animates to it, so slides of
   different lengths never jump or clip.
   ========================================================= */
.testimonial-carousel {
  position: relative;
  max-width: 960px;
  margin-inline: auto;
  /* The ambient glow pseudo-element below intentionally bleeds past
     this box (inset: -12% -30%) for a soft, edge-less look — but
     without a clip here that bleed was genuine, unclipped horizontal
     overflow with nothing to contain it, which was stretching the
     page's layout viewport (and, on narrow phones, every fixed-
     position element sized against it — nav included). */
  overflow: hidden;
}

.testimonial-carousel::before {
  content: "";
  position: absolute;
  inset: -12% -30%;
  z-index: -1;
  background: radial-gradient(ellipse 55% 55% at 50% 38%, rgba(212, 175, 55, 0.07), transparent 72%);
  pointer-events: none;
}

.testimonial-viewport {
  position: relative;
}

.testimonial-viewport.is-ready {
  transition: height 0.5s var(--ease-out);
}

.testimonial-slide {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  text-align: center;
  opacity: 0;
  transform: translateY(14px);
  visibility: hidden;
  pointer-events: none;
  /* Premium smoked-glass card. Kept on the slide itself (rather than a
     separate wrapper) so the card crossfades as one unit with the
     testimonial content — exactly what's already happening via the
     opacity/transform transition below, untouched. `translate` (the
     standalone property, distinct from `transform`) carries the hover
     lift further down so it never fights the crossfade's own
     `transform`. Note: the dark-glass gradient that used to live on
     this element's own `background` now lives on the dedicated
     .testimonial-slide-overlay child instead (see below) — a box's
     own background always paints beneath every child regardless of
     z-index, so it had to move once a real photo needed to sit
     between the card and that gradient. */
  border-radius: 28px;
  padding: 3.25rem 3.5rem;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(212, 175, 55, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), inset 0 0 40px rgba(212, 175, 55, 0.035),
    0 18px 46px -24px rgba(212, 175, 55, 0.28), 0 24px 60px -30px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out), visibility 0s linear 0.7s,
    translate 300ms var(--ease-out), border-color 300ms var(--ease-out), backdrop-filter 300ms var(--ease-out),
    box-shadow 300ms var(--ease-out);
}

/* Atmospheric background photo — bottom-most layer (z-index:-2, still
   paints above the card's own background/border per normal stacking
   rules, but below everything else here). object-fit:cover fills the
   card edge-to-edge (top/right/bottom/left:0 via `inset:0`, same
   thing, shorthand); the oversize scale is clipped by the card's own
   overflow:hidden so the blurred edge never shows past the rounded
   corners. Recalibrated brighter than the first pass — 0.3 opacity
   under the original overlay strength read as basically just the
   dark card again, not a recognizable photo underneath the glass. */
.testimonial-slide-photo-bg {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  filter: blur(22px) saturate(0.85);
  transform: scale(1.12);
  opacity: 0.55;
  pointer-events: none;
}

/* Dark glass over the photo — lighter than the first pass (was
   0.74/0.88) specifically so the photo stays visible through it;
   still dark enough to keep the white text/name/quote fully readable
   over any bright area of the underlying photo. Same warm-gold tint
   and soft vignette as before. */
.testimonial-slide-overlay {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(165deg, rgba(255, 226, 150, 0.06), rgba(0, 0, 0, 0) 45%),
    linear-gradient(160deg, rgba(24, 19, 11, 0.52), rgba(10, 8, 6, 0.7)),
    radial-gradient(ellipse 60% 55% at 50% 32%, rgba(212, 175, 55, 0.08), transparent 70%);
  box-shadow: inset 0 0 70px rgba(0, 0, 0, 0.55);
  transition: background 300ms var(--ease-out);
  pointer-events: none;
}

.testimonial-slide.is-active {
  position: relative;
  opacity: 1;
  transform: translateY(0);
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out), translate 300ms var(--ease-out),
    border-color 300ms var(--ease-out), backdrop-filter 300ms var(--ease-out), box-shadow 300ms var(--ease-out);
}

/* Large, extremely subtle decorative quotation mark — purely
   ornamental (aria-hidden via being a pseudo-element with no text
   role), sits behind the header/quote via z-index. */
.testimonial-slide::before {
  content: "\201C";
  position: absolute;
  top: -1rem;
  left: 1.25rem;
  font-family: var(--font-display);
  font-size: 10rem;
  line-height: 1;
  color: rgba(232, 199, 102, 0.08);
  filter: blur(1px);
  pointer-events: none;
  user-select: none;
  z-index: 0;
}

@media (hover: hover) and (pointer: fine) {
  .testimonial-slide.is-active:hover {
    translate: 0 -4px;
    border-color: rgba(212, 175, 55, 0.32);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), inset 0 0 46px rgba(212, 175, 55, 0.045),
      0 22px 54px -26px rgba(212, 175, 55, 0.34), 0 30px 70px -32px rgba(0, 0, 0, 0.55);
  }

  /* Same brightening the card's own background used to do on hover,
     now applied to the relocated overlay layer instead. */
  .testimonial-slide.is-active:hover .testimonial-slide-overlay {
    background: linear-gradient(165deg, rgba(255, 226, 150, 0.08), rgba(0, 0, 0, 0) 45%),
      linear-gradient(160deg, rgba(30, 24, 14, 0.56), rgba(13, 11, 8, 0.74)),
      radial-gradient(ellipse 60% 55% at 50% 32%, rgba(212, 175, 55, 0.1), transparent 70%);
  }
}

/*
  Horizontal identity block: photo on the left, name/location/rating
  stacked and left-aligned to its right — the pair centered as one
  unified group within the card. .testimonial-quote below is
  untouched (still centered, own rule further down).
*/
.testimonial-header {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-bottom: var(--space-8);
  text-align: left;
}

/* Extremely soft warm glow behind the profile block — almost
   imperceptible, just enough that the header doesn't feel like it's
   floating in isolation on the card's dark glass. */
.testimonial-header::before {
  content: "";
  position: absolute;
  inset: -30% -12%;
  z-index: -1;
  background: radial-gradient(ellipse 60% 65% at 32% 50%, rgba(212, 175, 55, 0.1), transparent 72%);
  pointer-events: none;
}

.testimonial-photo {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  object-fit: cover;
  object-position: center;
  border: 2px solid var(--color-gold-soft);
  box-shadow: 0 8px 22px -8px rgba(0, 0, 0, 0.5);
  flex-shrink: 0;
}

.testimonial-info {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  gap: 4px;
  text-align: left;
}

.testimonial-author {
  font-size: var(--fs-body);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--color-text);
  line-height: 1.25;
}

.testimonial-role {
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
  opacity: 0.8;
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: none;
}

.testimonial-rating {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  row-gap: 4px;
  margin-top: 2px;
}

.testimonial-stars {
  display: flex;
  gap: 3px;
}

.testimonial-stars svg {
  width: 15px;
  height: 15px;
  color: var(--color-gold);
}

/*
  Premium verification seal — a 12-point scalloped/rounded-starburst
  outline (not a plain circle) in a rich royal blue, with a white
  checkmark centered inside. Sized to roughly match the star row.
*/
.testimonial-verified {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  transition: transform var(--dur-fast) var(--ease-out);
}

.testimonial-verified:hover {
  transform: scale(1.05);
}

.badge-seal {
  width: 100%;
  height: 100%;
  overflow: visible;
}

.badge-seal-fill {
  fill: #2451c4;
  filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.35));
}

.testimonial-quote {
  position: relative;
  z-index: 1;
  font-family: var(--font-display);
  font-style: normal;
  font-size: clamp(1.5rem, 1vw + 1.3rem, 2.125rem);
  line-height: 1.68;
  color: var(--color-text);
  max-width: 660px;
  margin-inline: auto;
}

.testimonial-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

.testimonial-dots button {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: none;
  background: rgba(245, 241, 234, 0.28);
  cursor: pointer;
  padding: 0;
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

.testimonial-dots button:hover {
  background: rgba(245, 241, 234, 0.5);
}

.testimonial-dots button.is-active {
  background: var(--color-gold);
  transform: scale(1.35);
}

.testimonial-dots button:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 3px;
}

@media (max-width: 640px) {
  .testimonial-photo {
    width: 66px;
    height: 66px;
  }
  .testimonial-header {
    gap: var(--space-3);
  }
  .testimonial-author {
    font-size: var(--fs-small);
  }
  .testimonial-role {
    font-size: 0.7rem;
  }
  .testimonial-stars svg {
    width: 13px;
    height: 13px;
  }
  .testimonial-verified {
    width: 18px;
    height: 18px;
  }
  .testimonial-quote {
    font-size: 1.5rem;
  }
  .testimonial-slide {
    padding: 2.25rem 1.5rem;
    border-radius: 24px;
  }
  .testimonial-slide::before {
    font-size: 6rem;
    top: -0.5rem;
    left: 0.75rem;
  }
}

@media (max-width: 380px) {
  .testimonial-photo {
    width: 60px;
    height: 60px;
  }
  .testimonial-header {
    gap: var(--space-2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .testimonial-viewport.is-ready {
    transition: none;
  }
  .testimonial-slide {
    transition: opacity 0.3s ease;
    transform: none;
  }
  .testimonial-slide.is-active {
    transition: opacity 0.3s ease;
  }
}

/* =========================================================
   Featured Mix
   ---------------------------------------------------------
   Standalone, self-contained section — its own background/overlay
   treatment, its own type scale (--font-mix-display), its own CTA
   and card components. Nothing here is reused elsewhere on the site
   and nothing from elsewhere is reused here (aside from design
   tokens), by design.
   ========================================================= */
.featured-mix {
  position: relative;
  overflow: hidden;
  background: var(--color-bg);
  padding-block: var(--space-24);
  scroll-margin-top: var(--nav-height);
  /* No isolation:isolate here (removed) — position:relative already
     gives .featured-mix-bg/.featured-mix-inner's z-index:0/1 a local
     stacking context to compare against each other, which is all
     that's actually needed; nothing outside this section has a
     conflicting z-index for isolate to guard against. isolation
     forces its own extra GPU compositing boundary for the whole
     section, and that — combined with the scaled+blurred video below
     also getting promoted to its own layer — was the real source of
     a hairline seam at this section's top edge: the composited
     layer's rasterized edge can land a sub-device-pixel off from the
     plain, non-composited bottom edge of the Moments section above
     it. (Two adjacent block-level <section> siblings with no margin
     between them can never have an actual layout gap — this was a
     compositing/rasterization artifact, not spacing.) */
}

@media (max-width: 640px) {
  .featured-mix {
    padding-block: var(--space-16);
  }
}

.featured-mix-bg {
  position: absolute;
  /* Oversized 2px past .featured-mix on every edge (was inset:0) —
     .featured-mix's own overflow:hidden clips it straight back down
     to the section's exact box, so this changes nothing visually.
     What it does do: guarantee the video/overlay layer's own
     (transform+filter-affected) rasterized bounds always extend past
     the section's true edge, so there's no sub-pixel gap for the
     seam described above to appear in, regardless of how the
     browser's compositor rounds this layer at render time. */
  inset: -2px;
  z-index: 0;
  overflow: hidden;
  background: var(--color-bg);
}

.featured-mix-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Blur lives ONLY on this layer — a touch of scale keeps the blur
     from revealing soft edges around the frame. */
  filter: blur(6px) saturate(1.05);
  transform: scale(1.08);
  opacity: 0;
  transition: opacity 900ms var(--ease-out);
}

.featured-mix-video.is-ready {
  opacity: 1;
}

.featured-mix-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6, 5, 4, 0.88) 0%, rgba(6, 5, 4, 0.82) 45%, rgba(8, 6, 4, 0.92) 100%),
    radial-gradient(ellipse 70% 55% at 78% 35%, rgba(212, 175, 55, 0.16), transparent 65%);
  box-shadow: inset 0 0 18vw rgba(0, 0, 0, 0.55);
}

.featured-mix-inner {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--space-16);
  align-items: center;
}

@media (min-width: 900px) {
  .featured-mix-inner {
    /* Asymmetric on purpose — content column noticeably wider than
       the card, card nudged off the vertical center for a less
       predictable, less "mirrored-grid" composition. */
    grid-template-columns: 1.15fr 0.85fr;
    gap: var(--space-12);
  }

  .featured-mix-card {
    margin-top: var(--space-12);
  }
}

.featured-mix-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.4em 0.9em;
  border: 1px solid var(--color-gold);
  border-radius: var(--radius-full);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: var(--space-6);
}

.featured-mix-title {
  font-family: var(--font-mix-display);
  font-weight: 400;
  text-transform: uppercase;
  line-height: 0.98;
  letter-spacing: 0.01em;
  margin: 0 0 var(--space-4);
}

.featured-mix-title .fm-line {
  display: block;
  font-size: clamp(2.6rem, 3.6vw + 1.4rem, 4.75rem);
  /* Progressive enhancement: if a line ever has to wrap (mobile, see
     below), prefer an even split over the browser's default greedy
     wrap, which can leave one word stranded alone on its own line. */
  text-wrap: balance;
}

.featured-mix-title .fm-white {
  color: var(--color-text);
}

.featured-mix-title .fm-gold {
  color: var(--color-gold);
}

.featured-mix-by {
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--color-text);
  margin-bottom: var(--space-6);
}

.featured-mix-desc {
  max-width: 46ch;
  font-size: var(--fs-lead);
  color: var(--color-text-muted);
  margin-bottom: var(--space-8);
}

.featured-mix-cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.85rem 1.75rem;
  border-radius: var(--radius-full);
  background: transparent;
  border: 1.5px solid var(--color-gold);
  color: var(--color-gold-bright);
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

.featured-mix-cta-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  transition: transform var(--dur-fast) var(--ease-out);
}

.featured-mix-cta:hover,
.featured-mix-cta:focus-visible {
  transform: translateY(-2px);
  background: var(--color-gold);
  color: var(--color-bg);
}

.featured-mix-cta:hover .featured-mix-cta-icon,
.featured-mix-cta:focus-visible .featured-mix-cta-icon {
  transform: scale(1.05);
}

.featured-mix-cta:focus-visible {
  outline: 2px solid var(--color-gold-bright);
  outline-offset: 4px;
}

.featured-mix-card {
  position: relative;
  display: block;
  padding: 0;
  /* 16:9 — this card shows an actual YouTube video thumbnail now,
     not the original portrait promo photo the 4:5 ratio was tuned
     for. object-fit:cover on the image still crops for any source
     that isn't exactly 16:9, but starting from the right shape means
     it's a light crop instead of chopping off most of the frame. */
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--color-border-soft);
  background: var(--color-bg-elevated);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.7);
  cursor: pointer;
  transition: transform var(--dur-med) var(--ease-out), border-color var(--dur-med) var(--ease-out),
    box-shadow var(--dur-med) var(--ease-out);
}

.featured-mix-card.is-playing {
  cursor: default;
}

.featured-mix-card-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.2s var(--ease-out), filter var(--dur-med) var(--ease-out);
}

.featured-mix-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6, 5, 4, 0.02) 34%, rgba(6, 5, 4, 0.9) 100%);
  /* Faint glass sheen along the top edge. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.featured-mix-card-badge {
  position: absolute;
  top: clamp(12px, 2.5vw, 18px);
  left: clamp(12px, 2.5vw, 18px);
  z-index: 1;
  width: clamp(36px, 9vw, 48px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
  pointer-events: none;
}

.featured-mix-card-badge svg {
  width: 72%;
  height: 72%;
}

.featured-mix-card-title {
  position: absolute;
  z-index: 1;
  left: clamp(18px, 4vw, 34px);
  right: clamp(18px, 4vw, 34px);
  bottom: clamp(18px, 4vw, 30px);
  color: #fff;
  font-size: clamp(1rem, 3vw, 1.55rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.01em;
  text-align: center;
  text-transform: uppercase;
  text-shadow: 0 3px 18px rgba(0, 0, 0, 0.9);
  pointer-events: none;
}

.featured-mix-card-player {
  position: absolute;
  inset: 0;
  z-index: 5;
}

.featured-mix-card-player iframe {
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
}

@media (hover: hover) and (pointer: fine) {
  .featured-mix-card:hover,
  .featured-mix-card:focus-visible {
    transform: translateY(-6px);
    border-color: var(--color-gold);
    box-shadow: 0 40px 90px -30px rgba(212, 175, 55, 0.35), 0 30px 70px -30px rgba(0, 0, 0, 0.75);
  }

  .featured-mix-card:hover .featured-mix-card-img,
  .featured-mix-card:focus-visible .featured-mix-card-img {
    transform: scale(1.06);
  }

}

.featured-mix-card:focus-visible {
  outline: 2px solid var(--color-gold-bright);
  outline-offset: 5px;
}

@media (max-width: 899px) {
  .featured-mix-inner {
    gap: var(--space-8);
  }

  .featured-mix-content {
    text-align: center;
  }

  /* ~25-30% up from the original clamp(2.1rem, 8vw, 3rem) — the title
     is the visual hero of the mobile layout now, not just a header
     line. "Reggaeton Old School" is long enough in this bold/condensed
     face that a size increase this size genuinely needs will wrap on
     the narrowest phones (~375px and below) — text-wrap:balance
     (above) keeps that wrap an even, deliberate-looking split rather
     than a lopsided accident when it happens. */
  .featured-mix-title .fm-line {
    font-size: clamp(2.5rem, 6vw + 1.3rem, 3.75rem);
  }

  .featured-mix-desc {
    margin-inline: auto;
    margin-bottom: 0;
  }

  .featured-mix-card {
    /* width:100% + justify-self:center (not margin-inline:auto) —
       .featured-mix-card has no in-flow content of its own (the <img>
       and badges are all position:absolute), so as a grid item,
       auto margins would trigger CSS Grid's shrink-to-fit sizing
       instead of stretch and collapse it to near-zero width. An
       explicit width sidesteps that entirely. */
    width: 100%;
    /* Slightly wider than before (420px) for a more immersive feel —
       aspect-ratio is unchanged, so it only grows taller with it. */
    max-width: 460px;
    aspect-ratio: 16 / 9;
    justify-self: center;
  }

}

@media (prefers-reduced-motion: reduce) {
  .featured-mix-video {
    display: none;
  }
}

/* =========================================================
   Contact / Booking
   ---------------------------------------------------------
   Scoped re-theme only: redefining the neutral bg/text/border
   tokens on #contact (plus an explicit `color`) recolors the
   whole section to white/black via normal inheritance & the
   existing var(--color-*) references below — no other section
   is touched, and gold/success/error accent tokens are left
   exactly as defined in :root.
   ========================================================= */
#contact {
  background: #ffffff;
  color: #111111;
  --color-bg: #ffffff;
  --color-bg-alt: #fbfaf8;
  --color-bg-elevated: #f5f4f1;
  --color-text: #111111;
  --color-text-muted: #4a4a4a;
  --color-text-faint: #8c8c8c;
  --color-border: #e4e1dc;
  --color-border-soft: rgba(17, 17, 17, 0.08);
}

.contact-grid {
  display: grid;
  gap: var(--space-12);
}

/* Grid items default to a min-width of "auto" (their own max-content
   size), which stops this single (pre-960px) column from ever
   shrinking below that size on the narrowest phones — the same class
   of bug fixed on .mix-row. min-width: 0 lets .contact-info/.contact-
   form actually respect the container width at every viewport. */
.contact-grid > * {
  min-width: 0;
}

@media (min-width: 960px) {
  .contact-grid {
    grid-template-columns: 1fr 1.1fr;
    gap: var(--space-24);
  }
}

.contact-info h2 {
  font-size: var(--fs-h2);
  margin-bottom: var(--space-3);
}

.contact-info > p.booking-subtitle {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: var(--space-6);
}

.contact-info > p {
  color: var(--color-text-muted);
  margin-bottom: var(--space-8);
}

.contact-detail {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-border-soft);
}

.contact-detail:last-of-type {
  border-bottom: 1px solid var(--color-border-soft);
}

.contact-detail-icon {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--color-gold-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.contact-detail-icon svg {
  width: 18px;
  height: 18px;
  color: var(--color-gold);
}

.contact-detail h4 {
  font-size: var(--fs-small);
  font-weight: 600;
  margin-bottom: 2px;
}

.contact-detail p,
.contact-detail a {
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}

.contact-detail a:hover {
  color: var(--color-gold);
}

.contact-socials {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-8);
  /* Safety net for the AI Concierge button added alongside the icons
     below: if the row ever gets too narrow to fit everything, it wraps
     rather than overflowing — the existing "justify-content:center"
     mobile rule further down already centers whatever ends up on its
     own line, so no extra centering rule is needed here. */
  flex-wrap: wrap;
}

/*
  Phone-only reorder support (main.js physically relocates the
  existing .contact-socials/.contact-detail nodes under 767px —
  nothing here runs on desktop/tablet). Tightens the grid gap for
  the new stacked sequence and removes the redundant top margin
  that appears once .contact-socials sits directly after
  .contact-info instead of nested inside it, and centers the
  icons per spec.
*/
@media (max-width: 767px) {
  .contact-grid {
    gap: var(--space-8);
  }
  .contact-info + .contact-socials {
    margin-top: 0;
    justify-content: center;
  }
}

.social-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--color-border-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.social-btn:hover {
  border-color: var(--color-gold);
  color: var(--color-gold);
  transform: translateY(-3px);
}

.social-btn svg {
  width: 18px;
  height: 18px;
}

/* AI Concierge trigger — same circular footprint and tap target as the
   social icons (belongs to the same row/system), but a subtle gold
   fill by default rather than only on hover, so it reads as slightly
   more prominent without looking like a generic chatbot bubble. */
.social-btn-concierge {
  border-color: rgba(212, 175, 55, 0.4);
  background: rgba(212, 175, 55, 0.1);
  color: var(--color-gold);
  cursor: pointer;
}

.social-btn-concierge:hover,
.social-btn-concierge:focus-visible {
  background: rgba(212, 175, 55, 0.18);
  border-color: var(--color-gold);
  color: var(--color-gold-bright);
  transform: translateY(-3px);
}

.social-btn-concierge svg {
  width: 17px;
  height: 17px;
}

/* Labeled variant (Footer) — same border/fill/hover treatment as the
   icon-only circle above, just an auto-width pill with a visible
   label beside the icon instead of a fixed 44px circle. */
.social-btn-concierge-labeled {
  width: auto;
  height: 40px;
  min-width: 44px;
  padding-inline: var(--space-4);
  border-radius: var(--radius-full);
  gap: var(--space-2);
}

.social-btn-concierge-label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ---- Service card CTA row (Get a Quote + concierge trigger) --- */
.service-card-cta-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  align-self: flex-start;
  margin-top: auto;
  padding-top: var(--space-5);
}

@media (max-width: 340px) {
  .service-card-cta-row {
    justify-content: center;
  }
}

.contact-form {
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-8);
}

#contact .contact-form {
  box-shadow: 0 30px 70px -30px rgba(17, 17, 17, 0.12), 0 2px 8px rgba(17, 17, 17, 0.04);
}

.form-row {
  display: grid;
  gap: var(--space-5);
  margin-bottom: var(--space-5);
}

@media (min-width: 560px) {
  .form-row.two-col {
    grid-template-columns: 1fr 1fr;
  }
}

.field {
  position: relative;
}

.field label {
  display: block;
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

.field .required {
  color: var(--color-gold);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.85rem 1rem;
  min-height: 48px;
  color: var(--color-text);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}

.field textarea {
  min-height: 120px;
  resize: vertical;
}

.field input::placeholder,
.field textarea::placeholder {
  color: var(--color-text-faint);
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--color-gold);
  box-shadow: 0 0 0 3px var(--color-gold-soft);
}

#contact .field input,
#contact .field select,
#contact .field textarea,
#contact .custom-select-trigger {
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

#contact .field input:focus,
#contact .field select:focus,
#contact .field textarea:focus,
#contact .custom-select-trigger:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 0 0 3px var(--color-gold-soft), 0 6px 16px -8px rgba(17, 17, 17, 0.18);
}

#contact .field input:hover,
#contact .field select:hover,
#contact .field textarea:hover {
  border-color: var(--color-gold-dark);
}

.field.has-error input,
.field.has-error select,
.field.has-error textarea {
  border-color: var(--color-error);
}

.field-error {
  display: none;
  font-size: var(--fs-xs);
  color: var(--color-error);
  margin-top: var(--space-2);
}

.field.has-error .field-error {
  display: block;
}

.field select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23A8A29E' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  padding-right: 2.75rem;
}

/* ---------------------------------------------------------
   Custom "How did you hear about us?" dropdown
   A fully custom listbox (not a restyled native <select>, so
   the open panel can actually be animated/shadowed) backed by
   a hidden input for form submission — the generic field
   validation/blur wiring in main.js keeps working unmodified.
   --------------------------------------------------------- */
.custom-select {
  position: relative;
}

.custom-select-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.85rem 1rem;
  min-height: 48px;
  font: inherit;
  font-size: var(--fs-body);
  color: var(--color-text-faint);
  text-align: left;
  cursor: pointer;
}

.custom-select-trigger:hover {
  border-color: var(--color-gold-dark);
}

.custom-select-value.has-value {
  color: var(--color-text);
}

.custom-select-chevron {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--color-text-faint);
  transition: transform var(--dur-med) var(--ease-out);
}

.custom-select.is-open .custom-select-chevron {
  transform: rotate(180deg);
  color: var(--color-gold);
}

.custom-select-panel {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 0.5rem);
  z-index: 20;
  max-height: 280px;
  overflow-y: auto;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 24px 48px -16px rgba(17, 17, 17, 0.22), 0 4px 12px rgba(17, 17, 17, 0.06);
  padding: 0.4rem;
  list-style: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px) scale(0.98);
  transform-origin: top center;
  transition: opacity var(--dur-med) var(--ease-out), transform var(--dur-med) var(--ease-out),
    visibility 0s linear var(--dur-med);
}

.custom-select.is-open .custom-select-panel {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
  transition: opacity var(--dur-med) var(--ease-out), transform var(--dur-med) var(--ease-out), visibility 0s;
}

.custom-select-option {
  padding: 0.65rem 0.85rem;
  border-radius: var(--radius-sm);
  font-size: var(--fs-small);
  color: var(--color-text);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
    padding-left var(--dur-fast) var(--ease-out);
}

.custom-select-option.is-active {
  background: var(--color-gold-soft);
  color: var(--color-gold-dark);
  padding-left: 1.1rem;
}

.custom-select-option.is-selected {
  color: var(--color-gold-dark);
  font-weight: 600;
}

.custom-select-option.is-selected::after {
  content: "";
  float: right;
  width: 6px;
  height: 6px;
  margin-top: 0.4rem;
  border-radius: 50%;
  background: var(--color-gold);
}

@media (prefers-reduced-motion: reduce) {
  .custom-select-panel {
    transition: opacity var(--dur-fast) linear, visibility 0s;
    transform: none;
  }
  .custom-select.is-open .custom-select-panel {
    transform: none;
  }
}

.form-submit {
  width: 100%;
  margin-top: var(--space-2);
  position: relative;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-med) var(--ease-out),
    background var(--dur-med) var(--ease-out), color var(--dur-med) var(--ease-out);
}

.btn-submit-icons {
  position: relative;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.btn-submit-icons .btn-icon {
  position: absolute;
  inset: 0;
  width: 20px;
  height: 20px;
  opacity: 0;
  transform: scale(0.6) rotate(-15deg);
  transition: opacity 0.3s var(--ease-out), transform 0.3s var(--ease-out);
}

.btn-icon-arrow {
  opacity: 1;
  transform: scale(1) rotate(0deg);
}

.form-submit.is-loading .btn-icon-arrow {
  opacity: 0;
  transform: scale(0.6) rotate(15deg);
}

.form-submit.is-loading .btn-icon-spinner {
  opacity: 1;
  transform: scale(1) rotate(0);
  animation: btn-spin 0.9s linear infinite;
}

@keyframes btn-spin {
  to {
    transform: rotate(360deg);
  }
}

.form-submit.is-loading {
  cursor: progress;
}

.form-submit[disabled] {
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .btn-icon-spinner {
    animation: none !important;
  }
}

/* ---------------------------------------------------------
   Loading → confirmation handoff: the whole form (button
   included) gently dissolves away while the existing
   confirmation card crossfades in over the same span, so the
   two states read as one continuous motion instead of a cut.
   The confirmation card's own look (icon/heading/copy) and the
   .contact-form.is-submitted / .form-success.is-visible rules
   that control its final resting state are untouched below —
   .is-crossfading only adds a temporary overlay position so it
   can fade in on top of the form before the form is removed.
   --------------------------------------------------------- */
.contact-form {
  position: relative;
}

#bookingForm {
  transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1), transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),
    filter 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

#bookingForm.is-leaving {
  opacity: 0;
  transform: scale(0.98);
  filter: blur(4px);
  pointer-events: none;
}

.form-success.is-crossfading {
  display: flex !important;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1), transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.form-success.is-crossfading.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.form-note {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  justify-content: center;
  font-size: var(--fs-xs);
  color: var(--color-text-faint);
  margin-top: var(--space-4);
}

/* Honeypot — visually and functionally hidden from real visitors
   (off-screen, not display:none, so bots that check computed
   visibility before filling still get caught) without affecting
   this field's own layout in the form's row flow. */
.field-honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* :not([hidden]) rather than a plain display:flex — the class
   selector alone has equal specificity to the UA stylesheet's
   [hidden]{display:none} and wins on source order, which was
   showing this element on page load despite the hidden attribute
   being set. */
.form-submit-error:not([hidden]) {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-small);
  color: var(--color-error);
  background: rgba(248, 113, 113, 0.08);
  border: 1px solid rgba(248, 113, 113, 0.25);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-top: var(--space-4);
}

.form-submit-error a {
  color: inherit;
  text-decoration: underline;
}

.form-note svg {
  width: 14px;
  height: 14px;
}

.form-success {
  display: none;
  text-align: center;
  padding: var(--space-12) var(--space-4);
}

.form-success.is-visible {
  display: block;
}

.contact-form.is-submitted form {
  display: none;
}

.form-success-icon {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--color-gold-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-inline: auto;
  margin-bottom: var(--space-6);
}

.form-success-icon svg {
  width: 26px;
  height: 26px;
  color: var(--color-gold);
}

.form-success h3 {
  margin-bottom: var(--space-3);
}

.form-success p {
  color: var(--color-text-muted);
  font-size: var(--fs-small);
}

/* =========================================================
   FAQ
   ---------------------------------------------------------
   Reuses the site's existing .reveal/.eyebrow/.section-head
   utilities as-is (no changes to them). The ambient glow blobs
   reuse the exact technique + keyframes already defined for
   .hero-bg-fallback (smoke-drift-a/b) further up this file.
   ========================================================= */
.faq-section {
  position: relative;
  /* No explicit background — see .intro-reveal above: lets the shared
     global particle canvas show through instead of an opaque fill
     that's identical in color to the page background anyway. */
  overflow: hidden;
}

.faq-section::before,
.faq-section::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.35;
  pointer-events: none;
}

.faq-section::before {
  width: 34vw;
  height: 34vw;
  left: -8%;
  top: 5%;
  background: radial-gradient(circle, rgba(212, 175, 55, 0.22), transparent 70%);
  animation: smoke-drift-a 24s ease-in-out infinite;
}

.faq-section::after {
  width: 28vw;
  height: 28vw;
  right: -6%;
  bottom: 8%;
  background: radial-gradient(circle, rgba(168, 132, 42, 0.18), transparent 70%);
  animation: smoke-drift-b 28s ease-in-out infinite;
}

.faq-section .section-head p {
  margin-inline: auto;
}

.faq-list {
  position: relative;
  z-index: 1;
  max-width: 860px;
  margin-inline: auto;
  border-top: 1px solid var(--color-border-soft);
}

.faq-item {
  border-bottom: 1px solid var(--color-border-soft);
  transition: border-color var(--dur-med) var(--ease-out);
}

.faq-item:hover {
  border-color: rgba(212, 175, 55, 0.28);
}

.faq-heading {
  font-size: inherit;
  font-family: inherit;
  font-weight: inherit;
  line-height: inherit;
  letter-spacing: normal;
}

.faq-trigger {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding-block: var(--space-6);
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
}

.faq-number {
  font-family: var(--font-display);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--color-gold);
  transition: color var(--dur-med) var(--ease-out);
}

.faq-item:hover .faq-number,
.faq-item.is-open .faq-number {
  color: var(--color-gold-bright);
}

.faq-title {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--fs-lead);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text);
  transition: transform var(--dur-med) var(--ease-out), color var(--dur-med) var(--ease-out);
}

.faq-item:hover .faq-title,
.faq-trigger:focus-visible .faq-title {
  transform: translateX(6px);
  color: var(--color-gold-bright);
}

.faq-icon {
  position: relative;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--color-text-faint);
  transition: color var(--dur-med) var(--ease-out);
}

.faq-item:hover .faq-icon,
.faq-item.is-open .faq-icon {
  color: var(--color-gold);
}

.faq-icon-bar {
  position: absolute;
  inset: 0;
  margin: auto;
  background: currentColor;
  border-radius: 1px;
  transition: transform var(--dur-med) var(--ease-out);
}

.faq-icon-bar-h {
  width: 14px;
  height: 1.5px;
}

.faq-icon-bar-v {
  width: 1.5px;
  height: 14px;
}

.faq-item.is-open .faq-icon-bar-v {
  transform: scaleY(0);
}

.faq-panel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transform: translateY(-6px);
  transition: grid-template-rows 0.45s var(--ease-out), opacity 0.35s ease, transform 0.4s var(--ease-out);
}

.faq-item.is-open .faq-panel {
  grid-template-rows: 1fr;
  opacity: 1;
  transform: translateY(0);
}

.faq-panel-inner {
  overflow: hidden;
  min-height: 0;
}

.faq-panel-content {
  max-width: 640px;
  padding-left: 2.75rem;
  padding-right: var(--space-8);
  padding-bottom: var(--space-6);
}

.faq-panel-content p {
  color: var(--color-text-muted);
  line-height: 1.75;
  margin-bottom: var(--space-4);
}

.faq-panel-content p:last-child {
  margin-bottom: 0;
}

.faq-highlight {
  color: var(--color-gold-bright);
  font-weight: 500;
}

.faq-subhead {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-lead);
  color: var(--color-gold-bright);
  margin-bottom: var(--space-3);
}

@media (max-width: 640px) {
  .faq-trigger {
    gap: var(--space-3);
  }
  .faq-number {
    font-size: var(--fs-xs);
  }
  .faq-title {
    font-size: var(--fs-body);
    letter-spacing: 0.02em;
  }
  .faq-panel-content {
    padding-left: 1.75rem;
    padding-right: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .faq-section::before,
  .faq-section::after {
    animation: none;
  }
  .faq-item:hover .faq-title,
  .faq-trigger:focus-visible .faq-title {
    transform: none;
  }
}

/* =========================================================
   Footer
   ========================================================= */
.footer {
  background: color-mix(in srgb, #060504 95%, transparent);
  padding-block: var(--space-16) var(--space-8);
}

.footer-grid {
  display: grid;
  gap: var(--space-12);
  margin-bottom: var(--space-12);
  /* Explicit left alignment on every grid item — belt-and-suspenders
     against any browser/engine default that could otherwise center
     block-level grid children (justify-items) or their inline
     content (text-align) on some devices. */
  justify-items: start;
  text-align: left;
}

@media (min-width: 768px) {
  .footer-grid {
    grid-template-columns: 1.4fr 1fr 1fr 1fr;
  }
}

.footer-brand .nav-logo {
  margin-bottom: var(--space-4);
}

.logo-mark-footer {
  height: 40px;
}

.footer-col h5 {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: var(--space-4);
}

.footer-col ul {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}

.footer-col a {
  font-size: var(--fs-small);
  color: var(--color-text-muted);
  transition: color var(--dur-fast) var(--ease-out);
}

.footer-col a:hover {
  color: var(--color-gold);
}

/* Connect column's icon row — reuses .social-btn (same component as
   the Booking section's socials) so the icon style/hover lift is
   identical everywhere on the site. Sits above the link list within
   the column on desktop; footerMobileReorder() in main.js physically
   moves this block to right after .footer-brand under 767px (never
   cloned) so it reads as a single quick-access row beneath the logo,
   matching the same move-not-clone pattern already used for
   .contact-socials in the Booking section. */
.footer-socials {
  display: flex;
  justify-content: flex-start;
  width: 100%;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

/* Sits on its own line below the social icons rather than crowding
   into that row (a labeled pill beside three bare circles would read
   inconsistently) — kept subtle, no extra vertical height beyond a
   small top margin. */
.footer-concierge-row {
  margin-top: var(--space-1);
  margin-bottom: var(--space-2);
}

/* Mobile only: the logo + social-icon row read as one centered brand
   block, distinct from the left-aligned Explore/Services/Connect
   columns below (those keep .footer-grid's justify-items:start —
   only these two items get justify-self:center). Logo goes up ~20%
   (40px -> 48px, height-only so proportions/crispness are untouched)
   and the gap to the icons is pulled in from the grid's normal
   var(--space-12) section gap down to var(--space-6), so the two feel
   like one intentional unit rather than two separate footer rows —
   the gap from the icons down to Explore is left at the default. */
@media (max-width: 767px) {
  .footer-brand {
    justify-self: center;
  }

  .logo-mark-footer {
    height: 48px;
  }

  .footer-socials {
    justify-self: center;
    justify-content: center;
    width: auto;
    margin-top: calc(var(--space-6) - var(--space-12));
  }
}

.footer-bottom {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-top: var(--space-8);
  border-top: 1px solid var(--color-border-soft);
  font-size: var(--fs-xs);
  color: var(--color-text-faint);
}

@media (min-width: 640px) {
  .footer-bottom {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

/* Credit link — same restrained hover convention as .footer-col a
   (color shift only, no underline), just a touch heavier in weight
   so it reads as a discreet signature rather than another nav link. */
.footer-credit-link {
  font-weight: 600;
  color: inherit;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}

.footer-credit-link:hover {
  color: var(--color-gold);
}

.footer-credit-link:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* =========================================================
   Back to top
   ========================================================= */
.back-to-top {
  position: fixed;
  right: var(--space-6);
  bottom: var(--space-6);
  z-index: 60;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--color-gold);
  color: var(--color-bg);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 10px 24px -8px rgba(212, 175, 55, 0.55);
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity var(--dur-med) var(--ease-out), transform var(--dur-med) var(--ease-out),
    visibility var(--dur-med) var(--ease-out);
}

.back-to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.back-to-top svg {
  width: 20px;
  height: 20px;
}

/* =========================================================
   Marquee (availability strip)
   ========================================================= */
.marquee {
  border-top: 1px solid var(--color-border-soft);
  border-bottom: 1px solid var(--color-border-soft);
  background: var(--color-gold);
  color: var(--color-bg);
  overflow: hidden;
  padding-block: var(--space-3);
}

.marquee-track {
  display: flex;
  width: max-content;
  animation: marquee 26s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track {
    animation: none;
  }
}

.marquee-track span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding-inline: var(--space-6);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.marquee-track span::after {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-bg);
  opacity: 0.6;
}

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

/* =========================================================
   Cookie notice — notice-only, not a consent gate (see
   js/cookie-notice.js). Sits below the preloader's z-index
   (300) so it's simply not visible until the preloader clears,
   no extra timing coordination needed.
   ========================================================= */
.cookie-notice {
  position: fixed;
  left: var(--space-4);
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: 250;
  max-width: 460px;
  margin-inline: auto;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
  box-shadow: 0 20px 50px -20px rgba(0, 0, 0, 0.6);
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out);
}

.cookie-notice.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.cookie-notice-text {
  flex: 1;
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-small);
  color: var(--color-text-muted);
  line-height: 1.5;
}

.cookie-notice-btn {
  flex-shrink: 0;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-bg);
  background: var(--color-gold);
  border: none;
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
  transition: background 0.2s var(--ease-out), transform 0.2s var(--ease-out);
}

.cookie-notice-btn:hover {
  background: var(--color-gold-bright);
  transform: translateY(-1px);
}

.cookie-notice-btn:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 2px;
}

@media (max-width: 640px) {
  .cookie-notice {
    flex-direction: column;
    align-items: stretch;
    text-align: center;
  }
  .cookie-notice-btn {
    width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cookie-notice {
    transition: opacity 0.2s linear;
    transform: none;
  }
}
