/* ============================================================
   RegenerAxis — Brand Style System
   One palette, no theme switch. Section "stages" carry the tone.
   Quiet luxury. Editorial. 40%+ white space.
   ============================================================ */

:root {
  /* Raw brand palette — the ten colors of the brandbook, nothing else. */
  --ivory:        #F4F0E8;
  --bone:         #EAE4D8;
  --river-stone:  #B9AF9E;
  --champagne:    #D9C6A0;
  --gold:         #C7A96B;
  --deep-stone:   #8E8574;
  --olive:        #6E7154;
  --olive-deep:   #565839;
  --graphite:     #3B3A37;
  --warm-black:   #1B1A17;

  /* Type */
  --serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --sans:  "Jost", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Rhythm */
  --maxw: 1180px;
  --readw: 720px;
  --gap: clamp(1rem, 2vw, 1.75rem);
  --section-y: clamp(4.5rem, 10vw, 9rem);

  /* Motion */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 480ms;
}

/* ============================================================
   STAGES — the section is the theme scope
   ------------------------------------------------------------
   There is no global light/dark mode. Instead each section is a
   "stage" that sets its own surface tokens, and every component
   inside it (cards, kickers, hairlines, buttons) reads those
   tokens and recolors automatically. Scrolling the page walks
   through tones: night, graphite, olive, bone, ivory. That
   tonal rhythm is where the contrast and the life come from.

   Two rules make it hold together:
     1. --accent is gold on dark stages and olive-deep on light
        ones. Gold on a light surface is 1.8:1 and disappears, so
        light stages accent in olive instead.
     2. Card surfaces are a translucent veil of the stage's own
        ink, never a fixed hex, so they sit correctly on any tone.

   Every text pairing below is measured, small text >= 4.5:1.
   ============================================================ */

:root {
  /* Page default = the night stage, so any unstaged section is safe. */
  --bg:            var(--warm-black);
  --ink:           var(--ivory);        /* 15.0:1 */
  --muted:         var(--river-stone);  /*  8.0:1 */
  --kicker-ink:    var(--champagne);    /* 10.1:1 */
  --accent:        var(--gold);         /*  7.7:1 */
  --accent-ink:    var(--warm-black);   /* text on an --accent fill */
  --hairline:      rgba(199, 169, 107, 0.50);
  --hairline-soft: rgba(244, 240, 232, 0.14);
  --surface:       rgba(244, 240, 232, 0.045);
  --surface-hover: rgba(244, 240, 232, 0.075);
  --logo-stroke:   var(--ivory);
}

/* --- Night. The anchor tone: hero, footer, closing CTA. --- */
.stage--night {
  --bg: var(--warm-black); --ink: var(--ivory); --muted: var(--river-stone);
  --kicker-ink: var(--champagne); --accent: var(--gold); --accent-ink: var(--warm-black);
  --hairline: rgba(199, 169, 107, 0.50); --hairline-soft: rgba(244, 240, 232, 0.14);
  --surface: rgba(244, 240, 232, 0.045); --surface-hover: rgba(244, 240, 232, 0.075);
}

/* --- Graphite. A half-step up from night: warmer, closer, still dark. --- */
.stage--graphite {
  --bg: var(--graphite); --ink: var(--ivory);        /*  9.8:1 */
  --muted: var(--bone);                              /*  8.9:1 */
  --kicker-ink: var(--champagne);                    /*  6.6:1 */
  --accent: var(--gold);                             /*  4.9:1 */
  --accent-ink: var(--warm-black);
  --hairline: rgba(199, 169, 107, 0.55); --hairline-soft: rgba(244, 240, 232, 0.16);
  --surface: rgba(244, 240, 232, 0.055); --surface-hover: rgba(244, 240, 232, 0.09);
}

/* --- Olive. The saturated middle tone. Used once or twice per page,
       where the story should feel like it changes register. Gold drops
       to 3.3:1 here, so it stays decorative and bone carries the eyebrows. --- */
.stage--olive {
  --bg: var(--olive-deep); --ink: var(--ivory);      /*  6.5:1 */
  --muted: var(--bone);                              /*  5.8:1 */
  --kicker-ink: var(--bone);                         /*  5.8:1 */
  --accent: var(--champagne);                        /*  4.4:1, decorative + large only */
  --accent-ink: var(--warm-black);
  --hairline: rgba(217, 198, 160, 0.55); --hairline-soft: rgba(244, 240, 232, 0.20);
  --surface: rgba(244, 240, 232, 0.07); --surface-hover: rgba(244, 240, 232, 0.11);
}

/* --- Bone. The exhale. Light, warm, never white. --- */
.stage--bone {
  --bg: var(--bone); --ink: var(--warm-black);       /* 13.7:1 */
  --muted: var(--graphite);                          /*  9.0:1 */
  --kicker-ink: var(--olive-deep);                   /*  5.8:1 */
  --accent: var(--olive-deep);                       /*  5.8:1 */
  --accent-ink: var(--ivory);
  --hairline: rgba(86, 88, 57, 0.42); --hairline-soft: rgba(27, 26, 23, 0.14);
  --surface: rgba(255, 255, 255, 0.55); --surface-hover: rgba(255, 255, 255, 0.85);
  --logo-stroke: var(--warm-black);
}

/* --- Ivory. The brightest stage, for the widest reading passages. --- */
.stage--ivory {
  --bg: var(--ivory); --ink: var(--warm-black);      /* 15.0:1 */
  --muted: var(--graphite);                          /*  9.3:1 */
  --kicker-ink: var(--olive-deep);                   /*  6.0:1 */
  --accent: var(--olive-deep);                       /*  6.0:1 */
  --accent-ink: var(--ivory);
  --hairline: rgba(86, 88, 57, 0.38); --hairline-soft: rgba(27, 26, 23, 0.12);
  --surface: rgba(255, 255, 255, 0.7); --surface-hover: #FFFFFF;
  --logo-stroke: var(--warm-black);
}

/* --- Gold. One per page at most: the closing ask. --- */
.stage--gold {
  --bg: var(--gold); --ink: var(--warm-black);       /*  7.7:1 */
  --muted: rgba(27, 26, 23, 0.78);                   /*  6.2:1 */
  --kicker-ink: var(--olive-deep);                   /*  4.6:1 */
  --accent: var(--warm-black); --accent-ink: var(--gold);
  --hairline: rgba(27, 26, 23, 0.40); --hairline-soft: rgba(27, 26, 23, 0.18);
  --surface: rgba(244, 240, 232, 0.28); --surface-hover: rgba(244, 240, 232, 0.45);
  --logo-stroke: var(--warm-black);
}

.stage--night, .stage--graphite, .stage--olive,
.stage--bone, .stage--ivory, .stage--gold {
  background: var(--bg);
  color: var(--ink);
}

/* Legacy alias: markup that still says .strip-dark renders as the graphite stage. */
.strip-dark {
  --bg: var(--graphite); --ink: var(--ivory); --muted: var(--bone);
  --kicker-ink: var(--champagne); --accent: var(--gold); --accent-ink: var(--warm-black);
  --hairline: rgba(199, 169, 107, 0.55); --hairline-soft: rgba(244, 240, 232, 0.16);
  --surface: rgba(244, 240, 232, 0.055); --surface-hover: rgba(244, 240, 232, 0.09);
  background: var(--bg); color: var(--ink);
}

/* ---- Stage atmosphere. Every stage carries a quiet wash of its neighbors'
   tones — champagne warming a corner, olive grounding another — so no section
   reads as a flat sheet of one hex. All washes reuse the ten brand colors at
   low alpha; nothing new enters the palette. ---- */
.section.stage--night {
  background-image:
    radial-gradient(50% 60% at 85% 0%, rgba(110, 113, 84, 0.22), transparent 62%),
    radial-gradient(45% 55% at 8% 100%, rgba(199, 169, 107, 0.08), transparent 60%);
}
.stage--graphite {
  background-image:
    radial-gradient(55% 60% at 85% 8%, rgba(199, 169, 107, 0.10), transparent 60%),
    radial-gradient(60% 70% at 8% 100%, rgba(110, 113, 84, 0.18), transparent 62%);
}
.stage--olive {
  background-image:
    radial-gradient(60% 70% at 85% 6%, rgba(217, 198, 160, 0.16), transparent 60%),
    radial-gradient(70% 80% at 6% 96%, rgba(27, 26, 23, 0.32), transparent 65%);
}
.stage--bone {
  background-image:
    radial-gradient(55% 65% at 10% 0%, rgba(199, 169, 107, 0.18), transparent 58%),
    radial-gradient(50% 60% at 92% 100%, rgba(110, 113, 84, 0.13), transparent 60%);
}
.stage--ivory {
  background-image:
    radial-gradient(60% 60% at 88% 0%, rgba(217, 198, 160, 0.22), transparent 60%),
    radial-gradient(45% 55% at 6% 92%, rgba(110, 113, 84, 0.09), transparent 58%);
}
.stage--gold {
  background-image:
    radial-gradient(70% 60% at 50% 0%, rgba(244, 240, 232, 0.30), transparent 62%),
    radial-gradient(55% 60% at 90% 100%, rgba(86, 88, 57, 0.16), transparent 60%),
    linear-gradient(180deg, rgba(27, 26, 23, 0) 55%, rgba(27, 26, 23, 0.10));
}

/* Cards need physical presence on the light stages: a soft drop instead of
   relying on the hairline alone. Dark stages keep their translucent veils. */
.stage--bone .card, .stage--ivory .card {
  box-shadow: 0 1px 2px rgba(27, 26, 23, 0.04), 0 14px 34px rgba(27, 26, 23, 0.07);
}

/* ---- Fixed chrome. The header floats over every stage, so it keeps one
   dark identity instead of trying to match whatever is underneath. ---- */
:root {
  --header-bg:    rgba(27, 26, 23, 0.88);
  --header-scrim: rgba(20, 19, 15, 0.42);
  --header-ink:   var(--ivory);
  /* Nav links read in bone, not river-stone: at 0.72rem uppercase the extra
     two contrast steps (8.9:1 vs 8.0:1) is the difference between "faint"
     and "quietly present". */
  --header-muted: var(--bone);
  --footer-bg:    #131210;

  /* Hero aurora washes */
  --aurora-1: rgba(110, 113, 84, 0.50);   /* cordillera olive */
  --aurora-2: rgba(199, 169, 107, 0.34);  /* champagne */
  --aurora-3: rgba(142, 133, 116, 0.32);  /* deep stone */
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(1.06rem, 0.5vw + 0.96rem, 1.15rem);
  line-height: 1.74;
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
body, .card, .site-header, .strip-dark, .site-footer {
  transition: background-color 400ms var(--ease), color 400ms var(--ease), border-color 400ms var(--ease);
}
img, svg, video { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }

/* ---------- Typography ---------- */
h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 300;
  line-height: 1.08;
  letter-spacing: -0.01em;
  margin: 0 0 0.5em;
  color: var(--ink);
}
h1 { font-size: clamp(2.6rem, 6vw, 4.75rem); }
h2 { font-size: clamp(2rem, 4.2vw, 3.25rem); }
h3 { font-size: clamp(1.4rem, 2.4vw, 1.9rem); }
p  { margin: 0 0 1.1em; max-width: var(--readw); }

.kicker {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 0.8rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--kicker-ink);
  margin: 0 0 1.4rem;
  display: block;
}
.lead { font-size: 1.22rem; color: var(--muted); }
/* .lead carries max-width: var(--readw) from the base `p` rule, which makes
   its own box narrower than the centered parent (.narrow etc). Without
   margin-inline: auto that box hugs the left edge, so text-align: center
   only centers the text within an off-center box — the line reads shifted
   left under its title. Re-center the box itself wherever an ancestor
   already centers text; left-aligned .lead usages (e.g. testimonials) have
   no .center ancestor and stay untouched. */
.center .lead { margin-inline: auto; }
.serif-accent { color: var(--accent); }

/* ---------- Layout ---------- */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(1.25rem, 5vw, 3.5rem); }
.section { padding-block: var(--section-y); }
/* Anchored sections land clear of the fixed header. --nav-h is measured in
   main.js from the header's real height; the fallback covers the instant
   before that first measurement runs. */
main section[id] { scroll-margin-top: var(--nav-h, 84px); }
.narrow { max-width: 820px; margin-inline: auto; }
.center { text-align: center; }
.stack > * + * { margin-top: 1.1rem; }

.hairline { border: 0; height: 1px; background: var(--hairline); margin: 0; opacity: 0.8; }
.divider-wrap { display: flex; justify-content: center; padding-block: clamp(2rem, 5vw, 4rem); }
.divider-wrap .hairline { width: min(120px, 30%); }

/* ---------- Buttons / CTA ---------- */
.btn {
  --bc: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--sans);
  font-weight: 400;
  font-size: 0.8rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 1.05em 2.2em;
  border: 1px solid var(--bc);
  color: var(--ink);
  background: transparent;
  position: relative;
  overflow: hidden;
  transition: color 200ms var(--ease);
  z-index: 0;
}
.btn::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 200ms var(--ease);
  z-index: -1;
}
.btn:hover, .btn:focus-visible { color: var(--accent-ink); }
.btn:hover::after, .btn:focus-visible::after { transform: scaleX(1); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Legacy modifier kept for existing markup; the stage now decides the tone,
   so it is just the base button. */
.btn--dark { color: var(--ink); }
.btn--dark:hover, .btn--dark:focus-visible { color: var(--accent-ink); }

.btn-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }
.link-quiet {
  font-size: 0.8rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); border-bottom: 1px solid var(--hairline);
  padding-bottom: 2px; transition: color 200ms var(--ease);
}
.link-quiet:hover { color: var(--accent); }

/* An inline link inside running text: same size and case as its sentence,
   underlined so it still reads as a link without shouting. */
.link-inline {
  color: inherit; text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--accent); transition: color 200ms var(--ease);
}
.link-inline:hover { color: var(--accent); }

/* Present for search engines and screen readers, absent from the page. Not
   display:none — that would hide it from assistive tech too. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Booking summary on the confirmation page. */
.booking-summary {
  margin: 1.6rem auto 0; max-width: 27rem; text-align: left;
  border-top: 1px solid var(--hairline);
}
.booking-summary > div {
  display: flex; gap: 1rem; justify-content: space-between;
  padding: 0.75rem 0; border-bottom: 1px solid var(--hairline);
}
.booking-summary dt {
  font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); flex: 0 0 auto;
}
.booking-summary dd { margin: 0; text-align: right; }
/* Site-wide, p carries only a bottom margin (see the base rule), so a
   paragraph stacked after a block element sits flush against it. The line
   after the summary needs the gap put back. */
.booking-summary + p { margin-top: 1.7rem; }

/* Cal.com scrolls its own embed into view on a route change (calendar -> form
   -> error), and scrollIntoView aligns to the very top of the viewport, which
   here is behind the fixed header. scroll-margin-top is what the browser
   leaves clear when it honors that call, so the top of the form stays
   readable instead of hiding under the nav. */
#cal-inline-embed,
#cal-inline-embed cal-inline {
  scroll-margin-top: calc(var(--nav-h, 5rem) + 1.5rem);
}

/* ---------- Header / Nav ----------
   The header is fixed above stages of every tone, so it opts out of the
   stage system and keeps one dark identity of its own. */
.site-header {
  --ink: var(--header-ink);
  --muted: var(--header-muted);
  --accent: var(--gold);
  --accent-ink: var(--warm-black);
  --logo-stroke: var(--ivory);
  --hairline: rgba(199, 169, 107, 0.5);
  --hairline-soft: rgba(244, 240, 232, 0.14);
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  background: var(--header-scrim);
  backdrop-filter: blur(6px);
  transition: background 300ms var(--ease), backdrop-filter 300ms var(--ease), border-color 300ms var(--ease);
  border-bottom: 1px solid transparent;
}
.site-header.scrolled {
  background: var(--header-bg);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--hairline-soft);
}
.nav { display: flex; align-items: center; justify-content: space-between; padding-block: 1.1rem; gap: 1rem; }
.nav__brand { display: flex; align-items: center; gap: 0.7rem; }
.nav__brand svg { width: 42px; height: 42px; }
.nav__brand .logo-stroke { stroke: var(--logo-stroke); transition: stroke 400ms var(--ease); }
.nav__wordmark { font-family: var(--serif); font-size: 1.35rem; letter-spacing: 0.01em; color: var(--ink); }
.nav__wordmark b { font-weight: 500; color: var(--gold); }
.nav__links { display: flex; align-items: center; gap: 1.5rem; }
.nav__links a {
  font-size: 0.8rem; font-weight: 400; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--muted);
  transition: color 200ms var(--ease);
}
.nav__links a:hover, .nav__links a.active { color: var(--gold); }
.nav__links a.nav__enquire {
  border: 1px solid var(--gold); padding: 0.7em 1.4em; color: var(--ink);
  white-space: nowrap;
  transition: background 200ms var(--ease), color 200ms var(--ease);
}
.nav__links a.nav__enquire:hover, .nav__links a.nav__enquire:focus-visible {
  background: var(--gold); color: var(--warm-black);
}

.lang-toggle { display: inline-flex; border: 1px solid var(--hairline-soft); }
.lang-toggle a { padding: 0.4em 0.8em; font-size: 0.8rem; letter-spacing: 0.15em; color: var(--muted); }
.lang-toggle a.active { background: var(--gold); color: var(--warm-black); }

.nav__toggle { display: none; background: none; border: 0; padding: 0.5rem; }
.nav__toggle span { display: block; width: 24px; height: 1px; background: var(--ink); margin: 5px 0; transition: 250ms var(--ease); }

@media (max-width: 900px) {
  /* The drawer is a child of .site-header, and the header carries a
     backdrop-filter — which makes it the containing block for its own
     fixed-position descendants. So `bottom: 0` resolved against the header's
     ~70px-tall box, not the viewport: the panel was a short strip and the
     links spilled out below it with no background behind them (which is what
     read as a transparency bug). The header spans the top edge full width, so
     top/right still coincide with the viewport; only the height has to be
     stated outright. */
  .nav__links {
    position: fixed; top: 0; right: 0; bottom: auto; left: auto;
    width: min(80vw, 360px); height: 100vh; height: 100dvh;
    flex-direction: column; align-items: flex-start; justify-content: center;
    gap: 1.5rem; padding: 2rem 2.2rem; background: var(--warm-black);
    overflow-y: auto; overscroll-behavior: contain;
    transform: translateX(100%); transition: transform 360ms var(--ease); z-index: 90;
  }
  .nav__links a { color: var(--bone); font-size: 0.85rem; }
  .nav.open .nav__links { transform: translateX(0); }
  .nav__toggle { display: block; z-index: 95; }
  .nav.open .nav__toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); background: var(--ivory); }
  .nav.open .nav__toggle span:nth-child(2) { opacity: 0; }
  .nav.open .nav__toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); background: var(--ivory); }
}

/* ---------- Hero (CSS aurora + breathing Axis Bloom) ---------- */
.hero {
  min-height: 92vh; display: flex; align-items: center; position: relative;
  background: var(--bg); color: var(--ink); overflow: hidden;
}
/* Aurora: three slow-drifting radial washes in brand tones. GPU-friendly,
   pure CSS, tiny. Falls back to a static wash under reduced-motion. */
.hero__aurora { position: absolute; inset: -20% -10% -10% -10%; z-index: 0; pointer-events: none; }
.hero__aurora span {
  position: absolute; display: block; border-radius: 50%;
  filter: blur(60px); opacity: 0.9; will-change: transform;
}
.hero__aurora span:nth-child(1) { width: 55vw; height: 55vw; left: -6vw; top: -8vw;
  background: radial-gradient(circle at 30% 30%, var(--aurora-1), transparent 62%);
  animation: auroraA 22s var(--ease) infinite alternate; }
.hero__aurora span:nth-child(2) { width: 48vw; height: 48vw; right: -4vw; top: 4vw;
  background: radial-gradient(circle at 60% 40%, var(--aurora-2), transparent 60%);
  animation: auroraB 27s var(--ease) infinite alternate; }
.hero__aurora span:nth-child(3) { width: 50vw; height: 50vw; left: 22vw; bottom: -14vw;
  background: radial-gradient(circle at 50% 50%, var(--aurora-3), transparent 62%);
  animation: auroraC 31s var(--ease) infinite alternate; }
@keyframes auroraA { from { transform: translate3d(0,0,0) scale(1); } to { transform: translate3d(6vw,4vw,0) scale(1.12); } }
@keyframes auroraB { from { transform: translate3d(0,0,0) scale(1.05); } to { transform: translate3d(-5vw,5vw,0) scale(0.95); } }
@keyframes auroraC { from { transform: translate3d(0,0,0) scale(1); } to { transform: translate3d(3vw,-5vw,0) scale(1.1); } }

/* Axis Bloom: a slow-floating brand mark. Gentle rotate + scale reads as "alive"
   without spinning like a loader. A soft gold glow gives it depth. On the home
   hero it lives in the right column of .hero__inner (see .hero--home below), so
   it sits beside the copy at any width instead of drifting to the far edge. */
.hero__bloom { position: relative; z-index: 1; width: min(42vw, 460px); aspect-ratio: 1;
  margin-inline: auto; opacity: 0.62; pointer-events: none;
  filter: drop-shadow(0 0 46px rgba(199,169,107,0.14)); }
.hero__bloom svg { width: 100%; height: 100%; display: block;
  animation: bloomFloat 20s ease-in-out infinite; transform-origin: center; }
.hero__bloom .b-stroke { stroke: var(--ink); opacity: 0.5; }
@keyframes bloomFloat { 0%,100% { transform: rotate(-4deg) scale(0.97); } 50% { transform: rotate(4deg) scale(1.04); } }

/* Hero DNA canvas: the living version of the Axis Bloom, layered between
   the aurora (z 0) and the copy (z 2). hero-dna.js switches on .hero--dna
   only once it is actually rendering, so the static bloom is the fallback
   for reduced motion, narrow screens, no JS and no canvas. The mask keeps
   the headline half of the hero calm no matter what the dive is doing. */
.hero__dna { position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: 0; transition: opacity 1600ms var(--ease);
  -webkit-mask-image: linear-gradient(100deg, rgba(0,0,0,0.28) 6%, rgba(0,0,0,0.55) 38%, #000 60%);
          mask-image: linear-gradient(100deg, rgba(0,0,0,0.28) 6%, rgba(0,0,0,0.55) 38%, #000 60%); }
.hero--dna .hero__dna { opacity: 1; }
.hero--dna .hero__bloom { display: none; }

.hero__inner { position: relative; z-index: 2; padding-block: clamp(7rem, 12vh, 9rem) clamp(3rem, 8vh, 4.5rem); }
.hero h1 { color: var(--ink); max-width: 15ch; }
/* Home headline: three scannable sentence-lines with breathing room and a
   gold accent on each payoff noun, so it reads as rhythm rather than a block. */
.hero--home h1 { max-width: 18ch; line-height: 1.1; }
.hero__line { display: block; }
/* No extra gap between the lines: the same rhythm as within a line, so the
   two halves of the sentence read as one breath. */
.hero__line + .hero__line { margin-top: 0.04em; }
/* The payoff line reads in champagne, a step brighter than gold on the night
   stage, with a whisper of glow so it carries against the DNA canvas. */
.hero__key { color: var(--champagne); text-shadow: 0 0 28px rgba(217, 198, 160, 0.28); }
.hero .kicker { color: var(--kicker-ink); }
.hero .subline { color: var(--muted); max-width: 42ch; font-size: 1.22rem; margin-bottom: 2.2rem; }
/* Program hero: the "ideal for" line now lives here (took over the subline's
   spot), right before the CTA — same width/weight discipline as the subline
   it replaced, no separate heading. */
.hero__ideal { color: var(--muted); max-width: 42ch; margin: 0 0 2.2rem; }

/* Home hero: two balanced columns so the copy and the Axis Bloom read as a pair,
   with a controlled gap instead of a wide empty band between them. */
.hero--home .hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.85fr);
  align-items: center;
  gap: clamp(1.5rem, 5vw, 4.5rem);
}
.hero--home .hero__content { max-width: 42ch; }
.hero--home .hero__visual { display: flex; align-items: center; justify-content: center; }
@media (max-width: 860px) {
  .hero--home .hero__inner { grid-template-columns: 1fr; }
  .hero--home .hero__visual { display: none; }
}

/* ---------- Cards / grids ---------- */
.grid { display: grid; gap: var(--gap); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; } }

.card { border: 1px solid var(--hairline-soft); padding: clamp(1.6rem, 3vw, 2.4rem); background: var(--surface); transition: border-color 300ms var(--ease), transform 300ms var(--ease), background-color 300ms var(--ease); position: relative; height: 100%; }
.card:hover { border-color: var(--hairline); background: var(--surface-hover); }
.card__num { font-size: 0.8rem; letter-spacing: 0.2em; color: var(--accent); margin-bottom: 1rem; }
.card h3 { margin-bottom: 0.6rem; }
.card p { color: var(--muted); margin-bottom: 0; font-size: 1.04rem; }
.card--link { display: flex; flex-direction: column; }
.card--link:hover { transform: translateY(-3px); }
/* Distinct CTA card: fills the 6th grid cell so the program grid reads as a
   balanced 2x3, and inverts to the stage accent so the primary action pops. */
.card--cta { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); display: flex; flex-direction: column; justify-content: center; }
.card--cta h3 { color: var(--accent-ink); }
.card--cta p { color: var(--accent-ink); opacity: 0.82; margin-bottom: 0; }
.card--cta .card__cta { color: var(--accent-ink); margin-top: 1.4rem; }
.card--cta:hover { transform: translateY(-3px); border-color: var(--accent); background: var(--accent); }
.card__cta { margin-top: auto; padding-top: 1.4rem; font-size: 0.8rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--accent); }

/* ---------- Figure art: line-drawn placeholder illustrations ----------
   Human gestures and city skylines drawn as single-weight strokes in the
   stage accent, with one gold point of life each. They stand in for real
   photography without breaking the quiet-luxury register. */
.fig-stroke path {
  stroke: var(--accent); stroke-width: 1.7; stroke-linecap: round;
  stroke-linejoin: round; fill: none;
}
.fig-gold { fill: var(--gold); }
.fig-dash { opacity: 0.45; }

.sede-fig { margin: 0 0 1.4rem; }
.sede-fig svg { width: 100%; height: auto; }

/* ---- Living loops: the line art never fully rests ----
   Each figure breathes (a slow bob), its gold point pulses like a heartbeat,
   its dashed guide lines drift, and marked arcs ripple. Delays are negative
   or long so the loops start offset and never read as synchronized. All of
   it dies under prefers-reduced-motion via the global animation kill. */
@keyframes goldPulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.5; transform: scale(1.4); }
}
.sede-fig .fig-gold {
  transform-box: fill-box; transform-origin: center;
  animation: goldPulse 3.4s ease-in-out 2.4s infinite;
}

/* Dashed lines drift along their own length. */
@keyframes dashDriftPx { to { stroke-dashoffset: -12; } }
.sede-fig .fig-dash {
  stroke-dasharray: 5 7;
  animation: dashDriftPx 5.5s linear infinite;
}

/* ---------- "Is this for you": the symptom river ----------
   Replaces the old archetype card grid. Copy is data-driven (see
   assets/js/symptom-river.js — one lane per program, each carrying that
   program's full `signals` list, read straight from the locale JSON,
   never duplicated here). Chips are floating phrases with no card/pill
   chrome — just text that brightens and underlines on hover — flowing
   right to left in a few slow lanes. Movement is per-chip (each one
   individually positioned via a --x custom property, animated in JS with
   transform, not layout), so a single copy of each symptom exists and can
   be recycled to the right edge either when it drifts off the left or when
   it's clicked away. Reduced motion and no-JS both fall back to a static
   wrapped list — see .river-static. */
.river { position: relative; margin-top: clamp(2rem, 5vw, 3.2rem); }
.river__lanes--marquee {
  display: flex; flex-direction: column; gap: clamp(0.4rem, 1.5vw, 0.9rem);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.river-lane {
  position: relative; overflow: hidden;
  height: clamp(2.7rem, 7vw, 3.4rem);
}
.river-lane:nth-child(2) { margin-top: 0.1rem; }
.river-lane:nth-child(3) { margin-top: 0.2rem; }
.river-lane:nth-child(4) { margin-top: 0.1rem; }
.river-lane:nth-child(5) { margin-top: 0.2rem; }

.river-static {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0.5rem 1.6rem;
}

.river-chip {
  position: absolute; top: 50%; left: 0;
  transform: translate(var(--x, 0), -50%);
  appearance: none; font: inherit; cursor: pointer;
  border: 0; border-radius: 0; background: none; box-shadow: none;
  padding: 0.9em 0.2em; color: var(--muted);
  font-size: 1rem; line-height: 1.5; white-space: nowrap;
  will-change: transform;
  /* No `transform` in this transition: --x is re-driven every animation frame
     by symptom-river.js, and easing toward a target that moves each frame is
     exactly what read as tiny stutters/glitches in the drifting text. */
  transition: opacity 700ms var(--ease), color 220ms var(--ease);
}
/* position:relative (not static) so the ::after underline still has this
   element as its positioning context — it just no longer floats/animates.
   Static chips don't move per-frame, so they keep the transform ease for
   their hover lift. */
.river-static .river-chip {
  position: relative; transform: none;
  transition: opacity 700ms var(--ease), color 220ms var(--ease), transform 220ms var(--ease);
}
.river-static .river-chip:hover, .river-static .river-chip:focus-visible { transform: translateY(-1px); }
/* The click affordance: a hairline underline that draws in on hover/focus,
   not a resting border — the chip itself carries no box. */
.river-chip::after {
  content: ""; position: absolute; left: 0.2em; right: 0.2em; bottom: 0.55em;
  height: 1px; background: var(--gold);
  transform: scaleX(0); transform-origin: left;
  transition: transform 320ms var(--ease);
}
.river-chip:hover, .river-chip:focus-visible { color: var(--ink); }
.river-chip:hover::after, .river-chip:focus-visible::after { transform: scaleX(1); }
.river-chip:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

/* ---- The river's outro: a pinned scene ----
   The closing line ("Entonces llegaste al lugar adecuado…") used to simply
   follow the lanes in flow, which meant the olive stage below was already
   creeping into view as the line revealed. Instead the whole scene pins:
   .river-scene is a track taller than the viewport, .river-scene__stage
   sticks to it for exactly one viewport, and symptom-river.js drives the
   choreography from the track's position (so it plays in reverse on the way
   back up, and resolves correctly after a jump). Because the stage owns the
   full viewport for the length of the track, the olive section physically
   cannot appear until the pin releases — which is after the lanes have gone.

   Everything below is scoped to .river-scene--pinned, the class the script
   only adds when it decided to pin. Unpinned (short viewport, reduced
   motion, no JS) this is ordinary flow and the bridge reveals on the shared
   .reveal--late observer, exactly as before. */
.river-bridge {
  /* Wider than the old gap: the sentence now reads as its own beat rather
     than crowding the last lane. */
  margin-top: clamp(3.5rem, 10vh, 6.5rem);
  margin-bottom: var(--section-y);
}
/* The bridge is two beats now: a short statement in the section title's own
   voice (serif, light weight, ink, large), then the original quiet .lead
   caption underneath explaining what comes next. */
.river-bridge__lead {
  display: block;
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2rem, 4.2vw, 3.25rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-bottom: 5rem;
}
.river-bridge__caption {
  display: block;
  font-family: var(--sans);
  font-size: 1.22rem;
  color: var(--muted);
}

.river-scene { position: relative; }
/* Height is set inline by the script (viewport + the exact travel the
   choreography needs); this is only the fallback before it measures. */
.river-scene--pinned { height: 190vh; }
.river-scene--pinned .river-scene__stage {
  position: sticky; top: 0;
  /* svh, not vh: a mobile browser's vh is the toolbar-hidden height, so the
     stage would be taller than what is actually on screen and the sentence
     could sit below the fold for the whole pin. svh is the smallest state,
     which always fits; the script reads the real height back for the travel. */
  height: 100vh; height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
}
.river-scene--pinned .river-bridge { margin-bottom: 0; }
/* The stage already ends flush with the viewport, so the section's own
   closing padding would be dead scroll between the last lane leaving and the
   olive stage appearing — and the two are meant to be the same instant. */
.section.river-scene-host { padding-bottom: 0; }

/* The three groups the script drives, each on its own --ry. Driven per frame,
   so no transition may ease them — the same reason the drifting chips carry
   no transform transition. Every selector names .reveal deliberately: these
   elements carry it, and `.reveal.in { transform: none }` is declared later
   in this file, so at equal specificity it would win. */
.river-scene--pinned .river-scene__head.reveal,
.river-scene--pinned .river.reveal,
.river-scene--pinned .river-bridge.reveal {
  transform: translate3d(0, var(--ry, 0px), 0);
  transition: none;
  will-change: transform;
}
/* Only the sentence is faded; the lanes simply scroll away, so that "the last
   lane is leaving" stays a plain geometric fact. */
.river-scene--pinned .river-bridge.reveal {
  opacity: var(--ro, 0); will-change: transform, opacity;
}

/* Click: the whole button fragments at once — particles scattered across its
   own area (not just its center) while it fades out in the same motion, no
   separate visible steps. BURST_MS below must match symptom-river.js. */
.river-chip.is-disintegrating {
  opacity: 0; transform: translate(var(--x, 0), -50%) scale(0.92);
  transition: opacity 700ms var(--ease), transform 700ms var(--ease);
  pointer-events: none;
}
.river-particle {
  position: absolute; top: 0; left: 0; width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px;
  border-radius: 50%; background: var(--gold); opacity: 0.9; pointer-events: none;
  animation: river-burst 700ms var(--ease) forwards;
}
@keyframes river-burst {
  0% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 0.9; }
  100% { transform: translate(var(--tx), var(--ty)) scale(0.2) rotate(var(--rot)); opacity: 0; }
}

/* ---------- Steps ----------
   Shared by program-page phase timelines (assets/js/programs.js renders
   .step/.step__n for each program's "How your program unfolds" section). */
.steps { counter-reset: step; }
.step { display: grid; grid-template-columns: auto 1fr; gap: 1.5rem; padding-block: 1.8rem; border-top: 1px solid var(--hairline-soft); }
.step:last-child { border-bottom: 1px solid var(--hairline-soft); }
.step__n { counter-increment: step; font-family: var(--serif); font-size: 2.2rem; color: var(--accent); line-height: 1; }
.step__n::before { content: "0" counter(step); }
.step h3 { font-size: 1.35rem; margin-bottom: 0.3rem; }
.step p { margin: 0; color: var(--muted); }

/* ---------- Method: the pinned "how it works" stage ----------
   Unpinned (default, and what mobile and reduced-motion always get) this is
   an ordinary two-column section with every beat legible. method.js adds
   .is-pinned only on wide viewports with motion allowed, which turns the
   track into a tall scroll runway and sticks the stage inside it. */
.method__inner { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: clamp(2rem, 6vw, 5rem); align-items: start; }
@media (max-width: 900px) { .method__inner { grid-template-columns: 1fr; } }

.method__beats { list-style: none; margin: 0; padding: 0; display: grid; --beat-gap: clamp(1.6rem, 3vw, 2.4rem); gap: var(--beat-gap); }
/* --lp ("local progress", 0–1) defaults to fully drawn: the resting state
   for mobile, reduced-motion, and the instant before method.js's pinned
   loop takes over. While pinned, method.js overrides it per beat every
   frame. The two derived phases keep the gold wave continuous: the ring
   finishes filling before the connector below it starts, so the trace
   reads as one unbroken stroke down the list. */
.beat {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1.4rem;
  align-items: start;
  --lp: 1;
  --ring-p: min(var(--lp) / 0.6, 1);
  --line-p: max((var(--lp) - 0.6) / 0.4, 0);
}
/* Numbered ring, matching the circular waypoints used elsewhere on the site. */
.beat__n {
  position: relative;
  width: 60px;
  height: 60px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--accent);
  border-radius: 50%;
  background: transparent;
  font-family: var(--serif);
  font-size: 1.4rem;
  color: var(--accent);
  line-height: 1;
}
/* Connector from the bottom of one ring to the top of the next — the same
   gold wave that traces the ring below fills this line top-down. */
.beat:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 29.5px;
  top: 60px;
  bottom: calc(-1 * var(--beat-gap));
  width: 1px;
  background: linear-gradient(to bottom, var(--accent) calc(var(--line-p) * 100%), var(--hairline-soft) 0);
}
.beat h3 { font-size: clamp(1.25rem, 2vw, 1.6rem); margin-bottom: 0.35rem; }
.beat p { color: var(--muted); margin: 0; font-size: 1.02rem; }

/* Ring trace: on top of the plain ring above, fill it top-to-bottom in gold
   as the beat activates, matching the connector line's vertical sweep. A
   radial mask crops the fill to just the ring's circumference. Gated
   behind feature support for that mask (prefixed or not) so browsers
   without it simply keep the solid gold ring that was already the base
   style. */
@supports ((-webkit-mask: linear-gradient(#000, #000)) or (mask: linear-gradient(#000, #000))) {
  .beat__n { border-color: var(--hairline-soft); }
  .beat__n::before {
    content: "";
    position: absolute;
    inset: -1px;
    border-radius: 50%;
    background: linear-gradient(to bottom, var(--accent) calc(var(--ring-p) * 100%), transparent 0);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 1px), #000 calc(100% - 1px));
            mask: radial-gradient(farthest-side, transparent calc(100% - 1px), #000 calc(100% - 1px));
  }
}

/* Pinned mode. --beat-count sets the runway length: one viewport to read the
   stage plus roughly a third of one per beat, so the whole section resolves
   in about two swipes instead of four. */
.method.is-pinned .method__track {
  height: calc(100vh + (var(--beat-count, 3) - 1) * 34vh);
  position: relative;
}
.method.is-pinned .method__stage {
  position: sticky; top: 0; min-height: 100vh;
  display: flex; align-items: center;
}
/* Section padding would fight the sticky stage, so the track owns the space. */
.method.is-pinned { padding-block: 0; }
.method.is-pinned .method__stage > .wrap { padding-block: clamp(5rem, 10vh, 7rem); width: 100%; }

/* Beats dim until their turn. The transition makes scrolling back up
   un-highlight them just as smoothly as scrolling down lit them. */
.method.is-pinned .beat {
  opacity: 0.3;
  transform: translateY(6px);
  transition: opacity 420ms var(--ease), transform 420ms var(--ease);
}
.method.is-pinned .beat.is-active { opacity: 1; transform: none; }

/* ---------- Signature motion: the Axis Bloom comes alive ----------
   The nav mark and the hero mark are the same atom. Gold electrons travel the
   ellipse paths with offset-path, which needs no JS and no library. Browsers
   without offset-path simply show the static mark, which is the current
   design, so there is no broken intermediate state. */
@supports (offset-path: path("M 0 0")) {
  .mark__e, .bloom__e {
    offset-rotate: 0deg;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
  }
  /* Nav mark: two electrons, one per ellipse, slow enough to read as alive
     rather than as a spinner. */
  .mark__e--a { offset-path: path("M 50 22 A 15 28 0 1 1 50 78 A 15 28 0 1 1 50 22"); animation-name: orbit; animation-duration: 9s; }
  .mark__e--b { offset-path: path("M 22 50 A 28 15 0 1 1 78 50 A 28 15 0 1 1 22 50"); animation-name: orbit; animation-duration: 13s; animation-direction: reverse; }

  /* Hero bloom: three electrons across the three orbitals. */
  .bloom__e--1 { offset-path: path("M 50 16 A 14 34 0 1 1 50 84 A 14 34 0 1 1 50 16"); animation-name: orbit; animation-duration: 14s; }
  .bloom__e--2 { offset-path: path("M 16 50 A 34 14 0 1 1 84 50 A 34 14 0 1 1 16 50"); animation-name: orbit; animation-duration: 19s; animation-direction: reverse; }
  .bloom__e--3 { offset-path: path("M 50 24 A 26 26 0 1 1 50 76 A 26 26 0 1 1 50 24"); animation-name: orbit; animation-duration: 24s; }
}
@keyframes orbit { from { offset-distance: 0%; } to { offset-distance: 100%; } }

/* The nav mark draws itself in once on load, then settles. */
.nav__brand .mark { animation: markSettle 900ms var(--ease) both; }
@keyframes markSettle {
  from { opacity: 0; transform: scale(0.86) rotate(-12deg); }
  to   { opacity: 1; transform: none; }
}

/* Program cards get a deeper entrance than the generic stagger: a longer rise
   with a touch of scale, so the grid assembles instead of just fading. This
   rides the existing .reveal-stagger transition and its per-child delays, so
   there is no second animation to keep in sync and reduced-motion already
   neutralizes it. */
.reveal-stagger > .card--link,
.reveal-stagger > .card--cta { transform: translateY(18px) scale(0.985); }
.reveal-stagger.in > .card--link,
.reveal-stagger.in > .card--cta { transform: none; }
.reveal-stagger.in > .card--link:nth-child(6) { transition-delay: 400ms; }

/* ---------- Programs section: the showcase moment of the home page ----------
   The stage gets a quiet aurora of its own, and the cards get life: a gold
   spotlight that follows the cursor inside each card (via --mx/--my set by
   initCardLife in main.js), a 3D tilt toward the pointer, a hairline that
   warms to gold, and a ghost number that anchors each card like a chapter. */
#programas, #programs {
  background-image:
    radial-gradient(55% 60% at 78% 12%, rgba(110, 113, 84, 0.35), transparent 62%),
    radial-gradient(45% 50% at 12% 88%, rgba(199, 169, 107, 0.10), transparent 60%);
}

.card--link, .card--cta { overflow: hidden; transform-style: preserve-3d; }
.card--link::before {
  content: "";
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
              rgba(199, 169, 107, 0.13), transparent 65%);
  opacity: 0; transition: opacity 350ms var(--ease);
}
.card--link:hover::before, .card--link:focus-visible::before { opacity: 1; }
.card--link:hover { border-color: var(--hairline); box-shadow: 0 18px 44px rgba(0, 0, 0, 0.22); }
.card--cta:hover { box-shadow: 0 18px 44px rgba(0, 0, 0, 0.26); }

/* Ghost numeral: the card number, restated as a large serif watermark. */
.card--link .card__num {
  font-family: var(--serif); font-size: 2rem; letter-spacing: 0.04em;
  color: var(--accent); opacity: 0.9;
}
.card--link:hover .card__cta { color: var(--ink); }
.card__cta { transition: color 250ms var(--ease), letter-spacing 250ms var(--ease); }
.card--link:hover .card__cta, .card--cta:hover .card__cta { letter-spacing: 0.24em; }

/* ---------- Manifesto ---------- */
.manifesto p { font-family: var(--serif); font-size: clamp(1.6rem, 3.4vw, 2.4rem); line-height: 1.5; color: var(--ink); max-width: 22ch; }
.manifesto p + p { margin-top: 1.2rem; }

/* ---------- Asymmetric feature ---------- */
.feature { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: clamp(2rem, 5vw, 5rem); align-items: center; }
/* Placeholder visual for the Medellin section. Swap the .feature__bloom SVG for
   an <img> when a real photo is ready; the img rule below already fits it. */
.feature__media { position: relative; aspect-ratio: 4/5; overflow: hidden; border: 1px solid var(--hairline-soft);
  background: radial-gradient(120% 90% at 72% 18%, var(--aurora-2), transparent 58%), var(--surface); }
.feature__bloom { position: absolute; inset: 0; margin: auto; width: 66%; height: auto; color: var(--ink);
  opacity: 0.5; animation: bloomFloat 22s ease-in-out infinite; transform-origin: center; }
.feature__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 800px) { .feature { grid-template-columns: 1fr; } }

/* ---------- What to expect strip ---------- */
.expect { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); padding: clamp(1.6rem,3vw,2.2rem) 0; border-block: 1px solid var(--hairline); margin-bottom: 2.5rem; list-style: none; padding-inline: 0; }
.expect li { list-style: none; }
.expect .kicker { margin-bottom: 0.5rem; }
.expect p { font-size: 1rem; color: var(--muted); margin: 0; }
@media (max-width: 700px) { .expect { grid-template-columns: 1fr; gap: 1.4rem; } }

/* ---------- FAQ accordion ---------- */
.faq-cat { margin-bottom: 3rem; }
.faq-cat > h3 { font-size: 1.1rem; letter-spacing: 0.04em; color: var(--muted); font-family: var(--sans); font-weight: 400; text-transform: uppercase; margin-bottom: 1rem; }
.faq-item { border-top: 1px solid var(--hairline-soft); }
.faq-item:last-child { border-bottom: 1px solid var(--hairline-soft); }
.faq-item summary { list-style: none; cursor: pointer; padding: 1.4rem 2.5rem 1.4rem 0; position: relative; font-family: var(--serif); font-size: 1.25rem; color: var(--ink); }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; position: absolute; right: 0.2rem; top: 50%; transform: translateY(-50%); font-family: var(--sans); font-weight: 300; font-size: 1.5rem; color: var(--accent); transition: transform 250ms var(--ease); }
.faq-item[open] summary::after { content: "\2013"; }
.faq-item__body { overflow: hidden; }
.faq-item[open] .faq-item__body { animation: faqOpen 320ms var(--ease); }
.faq-item__body p { color: var(--muted); padding-bottom: 1.4rem; margin: 0; }
@keyframes faqOpen { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* ---------- Testimonials ---------- */
.tst {
  border: 1px solid var(--hairline-soft); padding: 2rem; margin: 0;
  background: var(--surface); height: 100%; position: relative;
  transition: border-color 300ms var(--ease), transform 300ms var(--ease),
              box-shadow 300ms var(--ease), background-color 300ms var(--ease);
}
.tst:hover {
  border-color: var(--hairline); background: var(--surface-hover);
  transform: translateY(-3px); box-shadow: 0 16px 38px rgba(27, 26, 23, 0.12);
}
.tst blockquote { font-family: var(--serif); font-size: 1.3rem; line-height: 1.5; margin: 0 0 1.2rem; color: var(--ink); }
/* One oversized serif quotation mark carries the voice; the text stays clean. */
.tst blockquote::before {
  content: "\201C"; display: block; font-family: var(--serif);
  font-size: 3.4rem; line-height: 0.5; color: var(--accent); opacity: 0.55;
  margin: 0.5rem 0 0.9rem;
}
/* Meta row: a small round portrait beside the name when one exists. */
.tst__meta { display: flex; align-items: center; gap: 0.9rem; }
.tst__avatar {
  width: 52px; height: 52px; border-radius: 50%; object-fit: cover;
  border: 1px solid var(--hairline); flex: none;
}
.tst__meta { font-size: 0.9rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--kicker-ink); }
.tst__meta b { color: var(--accent); font-weight: 400; }
/* --muted is already the de-emphasized token for each stage; stacking an extra
   opacity on top of it pushed this under AA on the gold stage. */
.tst-note { font-size: 0.9rem; color: var(--muted); font-style: italic; }
.filter-row { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 2.5rem; }
.filter-row button { background: none; border: 1px solid var(--hairline-soft); padding: 0.55em 1.2em; font-size: 0.8rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); transition: 200ms var(--ease); }
.filter-row button.active, .filter-row button:hover { border-color: var(--accent); color: var(--accent); }

/* ---------- Forms ---------- */
.form { display: grid; gap: 1.2rem; max-width: 560px; }
.form label { display: block; font-size: 0.8rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--kicker-ink); margin-bottom: 0.5rem; }
.form input, .form select, .form textarea { width: 100%; padding: 0.9em 1em; border: 1px solid var(--hairline-soft); background: var(--surface); font-family: var(--sans); font-size: 1rem; color: var(--ink); transition: border-color 200ms var(--ease); }
.form input:focus, .form select:focus, .form textarea:focus { outline: none; border-color: var(--accent); }
/* The native <option> popup renders outside this section's stage colors and
   commonly inherits `.form select`'s `color` (often a light --ink on a dark
   stage) onto the OS's own light popup background — pale-on-pale, effectively
   unreadable. Force solid, guaranteed-readable colors here instead of reusing
   stage tokens. */
.form option { background: #ffffff; color: #1a1a1a; }
.form textarea { min-height: 130px; resize: vertical; }

/* ---------- Footer ----------
   Like the header, the footer keeps its own dark identity so the page
   always closes on the same anchor tone whatever stage precedes it. */
.site-footer {
  --ink: var(--ivory); --muted: var(--river-stone); --accent: var(--gold);
  background: var(--footer-bg); color: var(--river-stone); padding-block: clamp(3rem, 6vw, 5rem);
}
.site-footer a { color: var(--river-stone); transition: color 200ms var(--ease); }
.site-footer a:hover { color: var(--gold); }
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 2.5rem; margin-bottom: 3rem; }
@media (max-width: 720px) { .footer-grid { grid-template-columns: 1fr; gap: 1.8rem; } }
.footer-grid h3 { color: var(--ivory); font-family: var(--sans); font-weight: 400; font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase; margin-bottom: 1rem; }
.footer-grid ul { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.6rem; font-size: 0.9rem; }
.disclaimer { font-size: 0.9rem; line-height: 1.7; color: var(--river-stone); opacity: 0.82; border-top: 1px solid rgba(185,175,158,0.15); padding-top: 2rem; max-width: 100%; }

/* ---------- Reveal animation ----------
   A longer rise than the default fade-up so sections feel like they surface
   from a layer below, not just blink in. */
/* PJAX language toggle: a brief dip-and-return while the dict swaps and every
   [data-i18n] node re-renders in place, so the text change reads as one soft
   crossfade instead of a jump cut. prefers-reduced-motion already forces
   `transition: none !important` globally (see below), which turns this into
   an instant, un-animated swap for free. */
body.i18n-fade { transition: opacity 150ms var(--ease); opacity: 0.35; }

.reveal { opacity: 0; transform: translateY(22px); transition: opacity 640ms var(--ease), transform 640ms var(--ease); }
.reveal.in { opacity: 1; transform: none; }
.reveal-stagger > * { opacity: 0; transform: translateY(16px); transition: opacity 640ms var(--ease), transform 640ms var(--ease); }
.reveal-stagger.in > * { opacity: 1; transform: none; }
.reveal-stagger.in > *:nth-child(2) { transition-delay: 80ms; }
.reveal-stagger.in > *:nth-child(3) { transition-delay: 160ms; }
.reveal-stagger.in > *:nth-child(4) { transition-delay: 240ms; }
.reveal-stagger.in > *:nth-child(5) { transition-delay: 320ms; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  /* !important because the per-component entrance offsets (.card--link and
     friends) outrank a bare .reveal-stagger > * selector. Without it, content
     would sit permanently nudged off its resting position. */
  .reveal, .reveal-stagger > *, .beat { opacity: 1 !important; transform: none !important; }
  /* The DNA canvas never starts under reduced motion (hero-dna.js checks
     the same query); hiding it here also covers a preference flip mid-run. */
  .hero__dna { display: none !important; }
  /* The pinned stage is never switched on under reduced motion, but if the
     preference flips after load the section must fall back cleanly. */
  .method.is-pinned .method__track { height: auto !important; }
  .method.is-pinned .method__stage { position: static !important; min-height: 0 !important; }
  .method.is-pinned { padding-block: var(--section-y) !important; }
}

/* ---------- Utilities ---------- */
.skip-link { position: absolute; left: -999px; }
.skip-link:focus { left: 1rem; top: 1rem; z-index: 200; background: var(--warm-black); color: var(--ivory); padding: 0.7em 1.2em; }
.hidden { display: none !important; }
.mt-lg { margin-top: clamp(2rem, 5vw, 4rem); }
.eyebrow-center { text-align: center; margin-bottom: clamp(2.5rem, 5vw, 4rem); }

/* ---------- Program page bits ---------- */
.hero--program { min-height: 62vh; }
/* Program pages carry the brand name as the H1: give it real presence, a
   step larger and a weight up from the elsewhere-featherweight serif. */
[data-program-page] .hero--program h1 { font-size: clamp(3rem, 7vw, 5.4rem); font-weight: 500; }
[data-program-page] h2 { font-weight: 400; }
/* The expect strip on program pages carries two items now, not three. */
.expect--2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 700px) { .expect--2 { grid-template-columns: 1fr; } }
.benefits { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.9rem; }
.benefits li { position: relative; padding-left: 1.6rem; color: var(--muted); }
.benefits li::before { content: ""; position: absolute; left: 0; top: 0.7em; width: 7px; height: 7px; background: var(--accent); border-radius: 50%; }
.price { border-top: 1px solid var(--hairline); margin-top: 2.5rem; padding-top: 2rem; }
.price__amount { font-family: var(--serif); font-size: clamp(2rem, 4vw, 2.8rem); color: var(--accent); line-height: 1; margin-bottom: 0.6rem; }
.doping-note { border: 1px solid var(--hairline-soft); padding: 1.1rem 1.3rem; font-size: 0.9rem; color: var(--muted); margin-top: 1.5rem; }

/* Program preview cards lead with the outcome — the sentence the visitor
   actually wants — instead of restating the bridge/tagline pair. */
.card__outcome {
  font-family: var(--serif); font-size: 1.18rem; line-height: 1.5;
  color: var(--ink); margin: 0.2rem 0 0; max-width: 26ch;
}

/* Block titles inside program pages: the working headings ("Does this sound
   like you?", "Ideal candidate", "Before you start") set in the serif with a
   short accent rule, so they read as headings rather than whispered kickers. */
.block-title {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.35rem, 2.2vw, 1.7rem); line-height: 1.2;
  color: var(--ink); margin: 0 0 1rem;
}
.block-title::after {
  content: ""; display: block; width: 44px; height: 1px;
  background: var(--accent); margin-top: 0.6rem;
}
.block-title--gap { margin-top: 2.2rem; }

/* Bridge line: the searchable outcome phrasing that sits under the brand
   name of a program, on cards and on the program hero. */
.card__bridge {
  font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--kicker-ink); margin-bottom: 0.7rem; line-height: 1.5;
}
.hero__bridge {
  font-size: 0.82rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--kicker-ink); margin-bottom: 1.2rem;
}

/* The one concrete outcome per program, set large so it reads before the prose. */
.outcome-line {
  font-family: var(--serif); font-size: clamp(1.5rem, 3vw, 2.1rem);
  line-height: 1.5; color: var(--ink); max-width: 22ch; margin-bottom: 2.4rem;
}

/* Aside block: optional complements, advisories, anything set apart from
   the running copy without shouting. */
.note-block {
  border-left: 2px solid var(--hairline); padding: 0.2rem 0 0.2rem 1.4rem;
  margin-top: 2.4rem;
}
.note-block p { color: var(--muted); margin-bottom: 0; }

/* Gateway: the compliant close on every program page */
.gateway { position: relative; border: 1px solid var(--hairline); background: var(--surface); padding: clamp(1.8rem, 4vw, 3rem); margin-top: clamp(2rem, 5vw, 3.5rem); }
.gateway__statement { font-family: var(--serif); font-weight: 300; font-size: clamp(1.2rem, 2.4vw, 1.6rem); line-height: 1.5; color: var(--ink); max-width: 42ch; margin: 0 auto 1.8rem; }

/* On the light stages the gateway needs real depth, not just a flat tint a
   shade off the page background: a stronger card-like shadow plus a faint
   olive wash in the corner. */
.stage--bone .gateway, .stage--ivory .gateway {
  box-shadow: 0 1px 2px rgba(27, 26, 23, 0.05), 0 20px 46px rgba(27, 26, 23, 0.10);
  background-image: radial-gradient(80% 100% at 100% 100%, rgba(86, 88, 57, 0.07), transparent 60%);
}

/* The program page's closing stage (bone): the rule lines read as gold
   against the light ground, the pay checks stay green, and the primary CTA
   picks up the same gold treatment as the nav's enquire button. */
.stage--bone .expect, .stage--bone .pay-strip { border-block-color: var(--gold); }
.stage--bone .pay-strip svg { stroke: var(--olive); }
.stage--bone .gateway .btn {
  --accent: var(--gold); --accent-ink: var(--warm-black);
  background: var(--warm-black); color: var(--ivory);
}

/* ---------- Rich text (privacy / legal body from data-i18n-html) ---------- */
.rich h2 { font-family: var(--sans); font-weight: 400; font-size: clamp(1.05rem, 2vw, 1.3rem);
  letter-spacing: 0.02em; text-transform: none; color: var(--kicker-ink); margin: 1.9rem 0 0.5rem; }
.rich p { color: var(--muted); }

/* ---------- Payment reassurance strip ---------- */
.pay-strip { display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 0.6rem 1.4rem; border-block: 1px solid var(--hairline); padding: 1.1rem 0; margin: 1.5rem 0;
  font-size: 0.9rem; letter-spacing: 0.06em; color: var(--muted); }
.pay-strip .pay-item { display: inline-flex; align-items: center; gap: 0.5rem; color: var(--ink); }
.pay-strip svg { width: 16px; height: 16px; stroke: var(--gold); stroke-width: 2.5; fill: none; flex: none; }

/* ---------- Floating WhatsApp button (injected by main.js) ---------- */
.wa-fab { position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem);
  z-index: 120; width: 54px; height: 54px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; background: var(--warm-black);
  border: 1px solid var(--gold); color: var(--gold);
  box-shadow: 0 6px 24px rgba(0,0,0,0.28); transition: transform 200ms var(--ease), background 200ms var(--ease), color 200ms var(--ease), opacity 220ms var(--ease); }
.wa-fab:hover, .wa-fab:focus-visible { transform: translateY(-2px); background: var(--gold); color: var(--warm-black); outline: none; }
.wa-fab svg { width: 26px; height: 26px; display: block; fill: currentColor; }

/* The mobile drawer covers the full viewport, and both floating controls
   outrank it (the header caps its own children at z-index 100), so they step
   aside while it is open. Declared here, after their base rules, so the
   opacity transition in those rules actually applies in both directions. */
body.nav-open .wa-fab, body.nav-open .atmos { opacity: 0; pointer-events: none; }

/* ---------- Testimonial media: still image now, lazy video later ---------- */
.tst__photo { position: relative; aspect-ratio: 16/9; margin: 0 0 1.2rem; overflow: hidden;
  border: 1px solid var(--hairline-soft); background: var(--surface); }
.tst__photo img { width: 100%; height: 100%; object-fit: cover; opacity: 0.92; }

/* ---------- Testimonial video facade (lazy, no heavy player) ---------- */
.tst__video { position: relative; aspect-ratio: 16/9; margin: 0 0 1.2rem; overflow: hidden;
  border: 1px solid var(--hairline-soft); background: var(--surface); cursor: pointer; }
.tst__video img { width: 100%; height: 100%; object-fit: cover; opacity: 0.9; }
.tst__video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.tst__play { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.tst__play span { width: 56px; height: 56px; border-radius: 50%; background: rgba(27,26,23,0.55);
  border: 1px solid var(--gold); display: flex; align-items: center; justify-content: center; }
.tst__play span::after { content: ""; width: 0; height: 0; margin-left: 3px;
  border-style: solid; border-width: 9px 0 9px 14px; border-color: transparent transparent transparent var(--gold); }

/* ---------- Text selection carries the brand ---------- */
::selection { background: var(--gold); color: var(--warm-black); }

/* ---------- Reading progress: a gold hairline under the header ---------- */
.scroll-progress {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: linear-gradient(90deg, var(--champagne), var(--gold));
  transform: scaleX(0); transform-origin: left; pointer-events: none;
  opacity: 0; transition: opacity 300ms var(--ease);
}
.site-header.scrolled .scroll-progress { opacity: 0.9; }

/* ---------- Stage layering: each section surfaces over the previous one.
   A soft shadow at the top edge of every stage reads as depth without adding
   a single new color to the system. ---------- */
main .section { position: relative; }
main .section::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 88px;
  background: linear-gradient(to bottom, rgba(15, 14, 12, 0.16), transparent);
  pointer-events: none;
}
/* The gold ask keeps its clean saturated edge. */
main .stage--gold::before { background: linear-gradient(to bottom, rgba(27, 26, 23, 0.10), transparent); }

/* ---------- Sonic Atmosphere control ----------
   A floating pill, bottom-left (mirror of the WhatsApp button). Five hairline
   bars stand still while muted and undulate while the atmosphere sounds.
   The microcopy chip unfolds from the pill on first visit and on hover. */
.atmos {
  position: fixed; left: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem);
  z-index: 120; display: inline-flex; align-items: center; gap: 0;
  height: 54px; padding: 0 1.05rem; border-radius: 27px;
  background: rgba(27, 26, 23, 0.82); backdrop-filter: blur(8px);
  border: 1px solid rgba(199, 169, 107, 0.45); color: var(--gold);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
  transition: border-color 250ms var(--ease), transform 250ms var(--ease), box-shadow 250ms var(--ease), opacity 220ms var(--ease);
}
.atmos:hover, .atmos:focus-visible { border-color: var(--gold); transform: translateY(-2px); outline: none;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.34), 0 0 24px rgba(199, 169, 107, 0.12); }
.atmos:focus-visible { border-color: var(--champagne); }

.atmos__bars { display: inline-flex; align-items: center; gap: 3px; height: 22px; }
.atmos__bars i {
  width: 2px; height: 6px; border-radius: 1px; background: currentColor;
  transition: height 350ms var(--ease), opacity 350ms var(--ease);
  opacity: 0.85;
}
/* Muted: a flat, resting waveform. */
.atmos__bars i:nth-child(1) { height: 5px; }
.atmos__bars i:nth-child(2) { height: 9px; }
.atmos__bars i:nth-child(3) { height: 13px; }
.atmos__bars i:nth-child(4) { height: 9px; }
.atmos__bars i:nth-child(5) { height: 5px; }
/* Sounding: the bars breathe on offset phases, slow enough to feel like sound
   from a bowl rather than an equalizer. */
.atmos.is-on .atmos__bars i { animation: atmosWave 2.6s ease-in-out infinite; }
.atmos.is-on .atmos__bars i:nth-child(2) { animation-delay: 0.28s; }
.atmos.is-on .atmos__bars i:nth-child(3) { animation-delay: 0.56s; }
.atmos.is-on .atmos__bars i:nth-child(4) { animation-delay: 0.84s; }
.atmos.is-on .atmos__bars i:nth-child(5) { animation-delay: 1.12s; }
@keyframes atmosWave {
  0%, 100% { height: 6px; opacity: 0.7; }
  50% { height: 18px; opacity: 1; }
}

.atmos__hint {
  max-width: 0; overflow: hidden; white-space: nowrap;
  font-family: var(--sans); font-weight: 400; font-size: 0.8rem;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--champagne);
  transition: max-width 500ms var(--ease), padding 500ms var(--ease), opacity 400ms var(--ease);
  opacity: 0;
}
.atmos:hover .atmos__hint, .atmos:focus-visible .atmos__hint, .atmos.show-hint .atmos__hint,
/* Muted is the state that needs explaining, so its label stays open instead of
   folding away after the first-visit hint: the invitation reads at a glance.
   Once the atmosphere is on, the label folds back to the bars and only
   returns on hover. */
.atmos:not(.is-on) .atmos__hint {
  /* Wide enough for the longest state label ("Atmósfera regenerativa
     encendida", tracked uppercase) — 300px used to truncate it. */
  max-width: 460px; padding-left: 0.8rem; opacity: 1;
}

/* ---------- Cursor veil: the atom canvas ----------
   No blend mode here: the canvas itself switches between additive gold (dark
   stages) and plain olive ink (light stages), so a CSS blend would fight it. */
.cursor-veil {
  position: fixed; inset: 0; z-index: 200; pointer-events: none;
  width: 100vw; height: 100vh;
}

@media (max-width: 700px) {
  /* Keep the two floating controls clear of each other on small screens. */
  .atmos { height: 48px; padding: 0 0.9rem; }
  .atmos__hint { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .cursor-veil { display: none !important; }
  .atmos.is-on .atmos__bars i { animation: none !important; height: 13px; }
}
