/* ============================================================
   Voix-Essence - design system
   Palette: the VEA brand, matched to quiz.veacademy.studio.
   teal #146183, button yellow #E6B325, cream #F8F4EE, ink #2F1525.
   Yellow is a BUTTON colour only: it measures 1.77:1 on cream, so text
   and icon accents use the darker gold #8A6410 (4.90:1) instead.

   Radius rule (documented, applied everywhere):
     buttons  -> pill (999px)
     cards    -> 16px
     inputs   -> 10px
     media    -> 16px
   ============================================================ */

/* ---------- fonts (self-hosted, no third-party request) ----------
   ?v=2 on the Cormorant files: they were replaced in place after shipping a
   wrong subset, and browsers that had already cached the old one at the same
   URL kept serving it. Bump the number whenever a font file is replaced.
   Pairing 01: Cormorant Garamond for display, DM Sans for everything else.
   Both variable, one file each covers every weight. Montserrat was dropped:
   it was a third face doing neither job.
   Latin subset only: French accents live in U+00C0-00FF, so latin-ext
   would have added ~300 KB for glyphs this site never renders. */
@font-face {
  font-family: 'DMSans';
  src: url('../fonts/dmsans.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Cormorant';
  src: url('../fonts/cormorant.woff2?v=2') format('woff2');
  font-weight: 300 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Cormorant';
  src: url('../fonts/cormorant-italic.woff2?v=2') format('woff2');
  font-weight: 300 700; font-style: italic; font-display: swap;
}

/* ---------- tokens: light is the base definition ---------- */
:root {
  /* Sampled from "Ta voix en trois dimensions". Ground colours are exact;
     the accents are NOT interchangeable between grounds. Measured against
     cream #FFFEFA and teal #0E4459:
       #68B8AF teal      2.30:1 on cream   5.65:1 on dark
       #E6C897 champagne 1.59:1 on cream   8.15:1 on dark
     Both fail as text on cream, so cream gets darkened variants and the
     deck values are kept for dark ground only. */
  --bg: #FFFEFA;
  --bg-alt: #F7F3EC;
  --surface: #FFFFFF;

  --panel: #0E4459;
  --panel-alt: #125670;

  /* accents ON DARK */
  --teal: #68B8AF;
  --gold: #E6C897;

  /* accents ON CREAM, darkened to clear AA */
  --teal-ink: #2E7C74;
  --gold-ink: #8A6528;
  --gold-display: #9C7434;

  --ink: #1A1A1A;
  --ink-soft: #565656;
  --ink-faint: #6E6E6E;

  --line:        rgb(26 26 26 / .12);
  --line-strong: rgb(26 26 26 / .22);
  --line-dark:   rgb(232 226 214 / .22);

  --on-dark: #F2EDE4;
  --on-dark-soft: #BFD3CE;

  --btn-bg: #C9973F;
  --btn-bg-hover: #B4862F;
  --btn-fg: #1A1A1A;

  --shadow-sm: 0 1px 2px rgb(26 26 26 / .05), 0 4px 14px rgb(26 26 26 / .05);
  --shadow-md: 0 2px 6px rgb(26 26 26 / .06), 0 18px 48px rgb(26 26 26 / .10);

  --r-card:  14px;
  --r-input: 8px;
  --r-pill:  999px;

  --ease: cubic-bezier(.16, 1, .3, 1);
  --shell: 1240px;

  /* Legacy names kept as aliases. The palette was renamed to the deck's
     vocabulary; aliasing means the 36 rules that still say var(--green) or
     var(--amber) keep resolving instead of silently computing to nothing. */
  --green:        var(--teal-ink);
  --green-deep:   var(--panel-alt);
  --green-soft:   var(--teal-ink);
  --amber:        var(--gold-ink);
  --amber-bright: var(--gold);

  /* One fixed look, cream and deep teal, matching
     quiz.veacademy.studio/tu-voz-autentica.html which has no dark variant.
     The dark token blocks were removed rather than left dormant: they turned
     the cream band into a second navy, so the page read blue on blue and the
     cream never appeared for anyone browsing with a dark OS. */
  color-scheme: light;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* scroll-padding clears the 72px sticky header. Without it every in-page
   jump, including the skip link's #main, lands under the bar. */
html {
  -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 88px;
  /* clip, not hidden: hidden turns <html> into a scroll container and the
     sticky header stops sticking. Needed so 100vw children cannot add a
     horizontal scrollbar. */
  overflow-x: clip;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: 'DMSans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 17px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

:where(a, button, input, textarea, summary):focus-visible {
  outline: 2.5px solid var(--gold-display);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------- type ---------- */
h1, h2, .display {
  /* The deck uses one high-contrast serif two ways: the cover sets the line
     ROMAN and italicises a single accent word, while interior titles run
     fully italic. Roman is the default here and <em> carries the accent;
     .h-italic switches a whole title to the interior treatment. */
  font-family: 'Cormorant', Georgia, 'Times New Roman', serif;
  /* Cormorant Garamond is a Garamond derivative: small x-height, fine
     hairlines. At 400 and the previous sizes it read frail, so titles run at
     500 and the scale is up about 12% to land at the same optical weight
     Playfair had. */
  font-weight: 500;
  font-style: normal;
  letter-spacing: 0;
  color: var(--ink);
  margin: 0;
  line-height: 1.14;
  /* distribute the remainder evenly rather than leaving one or two words
     stranded on the last line */
  text-wrap: balance;
}
h1 em, h2 em, .display em {
  font-style: italic;
  color: var(--teal-ink);
  /* italic descenders (g j p q y) clip against a 1.14 line-height */
  line-height: 1.2;
}
/* on the dark ground the accent takes the champagne, as it does in the deck */
.panel-dark h1 em, .panel-dark h2 em, .hero-stage h1 em { color: var(--gold-ink); }
h1 { font-size: clamp(2.7rem, 6.8vw, 4.7rem); }
h2 { font-size: clamp(2.1rem, 4.5vw, 3.3rem); line-height: 1.14; }
/* A long heading that should hold one line on desktop. Measured: the story
   title needed 1208px inside a 1160px column, so 4% over. Slightly smaller
   ceiling plus balanced wrapping, which splits the remainder evenly instead
   of orphaning the closing punctuation on its own line. */
h3 {
  /* sans, same family as the body. Cormorant is a display face: below about
     24px its thin strokes thin out and it reads weak, which is exactly what
     happened on the benefit cards over the dark teal. */
  font-family: 'DMSans', ui-sans-serif, system-ui, sans-serif;
  font-size: clamp(1.08rem, 1.6vw, 1.22rem);
  font-weight: 600;
  line-height: 1.32;
  letter-spacing: -.005em;
  color: var(--ink);
  margin: 0;
}
p  { margin: 0 0 1.1em; max-width: 65ch; }
p.lede {
  font-size: clamp(1.06rem, 1.7vw, 1.28rem);
  color: var(--ink-soft);
  line-height: 1.6;
}
strong { font-weight: 600; color: var(--ink); }
.mono {
  font-family: 'DMSans', ui-sans-serif, system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
}
.eyebrow {
  font-family: 'DMSans', ui-sans-serif, sans-serif;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .2em;
  color: var(--ink-faint);
  margin: 0 0 1.1rem;
  font-weight: 500;
}

/* ---------- deck vocabulary ----------
   Four devices carry the look of "Ta voix en trois dimensions". They are the
   difference between using its colours and actually looking like it. */

/* 1. the short rule that sits under every title in the deck */
.rule {
  width: 92px; height: 1px; border: 0; margin: 1.15rem 0 1.5rem;
  background: var(--gold-ink);
}
.rule.teal { background: var(--teal-ink); }

/* 2. eyebrow: uppercase sans, wide tracking. On dark it takes the teal and a
      small leading bar, exactly as the deck sets it. */
.eyebrow.bar {
  display: flex; align-items: center; gap: .7rem;
  color: var(--teal-ink);
}
.eyebrow.bar::before {
  content: ""; width: 3px; height: 1.05em; flex-shrink: 0;
  background: var(--teal-ink);
}

/* 3. gold bullet list */
.gold-list { list-style: none; padding: 0; margin: 0; display: grid; gap: .95rem; }
.gold-list li { display: flex; gap: .85rem; align-items: baseline; max-width: 62ch; }
.gold-list li::before {
  content: ""; flex-shrink: 0;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--gold-ink); transform: translateY(-.2em);
}

/* 5. roman numeral, serif, teal */
.numeral {
  font-family: 'Cormorant', Georgia, serif;
  font-style: normal; font-weight: 400;
  font-size: clamp(1.9rem, 3.4vw, 2.7rem);
  line-height: 1; color: var(--teal-ink);
  margin-bottom: 1.4rem;
}

/* ---------- layout ---------- */
.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: clamp(1.15rem, 4vw, 2.5rem); }
section { padding-block: clamp(4rem, 9vw, 8.5rem); }

/* Bands meet on a straight edge. A gradient wash was tried here and pulled:
   the seam is meant to be a clean horizontal break between cream and teal,
   not a blur between them. */
/* ---------- device mockup ----------
   The source files are bare screenshots (course-laptop.png 1000x525,
   course-phone.png 252x560). The frames are drawn in CSS rather than baked
   into a flat composite, so they stay sharp at any size and re-colour with
   the theme. */
.devices { position: relative; width: 100%; padding: 7% 4% 5%; display: block; }
.devices .lap {
  width: 86%; margin-left: auto;
  border: 7px solid #0D3A50; border-bottom-width: 12px;
  border-radius: 12px; background: #0D3A50; box-shadow: var(--shadow-md);
}
.devices .lap img { display: block; width: 100%; border-radius: 4px; }
.devices .ph {
  position: absolute; left: 2%; bottom: 0; width: 22%;
  border: 5px solid #0D3A50; border-radius: 14px;
  background: #0D3A50; box-shadow: var(--shadow-md);
}
.devices .ph img { display: block; width: 100%; border-radius: 9px; }
@media (max-width: 560px) { .devices .ph { width: 26%; } }

/* ---------- full-bleed stage hero ----------
   The stage photos are only 640px wide, so the image is used as atmosphere
   behind a heavy scrim, never for detail. The sharp portrait sits on top. */
.hero-stage { position: relative; isolation: isolate; overflow: hidden; }
.hero-stage::before {
  content: ""; position: absolute; inset: -2%; z-index: -2;
  background: url("../img/slide-11.jpg") center 34% / cover no-repeat;
  filter: blur(2px) saturate(115%);
}
.hero-stage::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(100deg, rgb(10 40 52 / .93) 0%, rgb(10 40 52 / .82) 42%, rgb(10 40 52 / .55) 100%),
    linear-gradient(to top, rgb(10 40 52 / .85), transparent 55%);
}

.hero-stage,
.panel-dark,
.site-foot {
  /* On the deck's dark ground the accents are the bright values; on cream
     they are the darkened ones. Remapping here means every rule that uses
     an accent token is correct on both grounds without a second selector. */
  --ink:          #F2EDE4;
  --ink-soft:     #BFD3CE;
  --ink-faint:    #9FBAB4;
  --teal-ink:     #68B8AF;
  --gold-ink:     #E6C897;
  --gold-display: #E6C897;
  /* The aliases must be restated here. var() in a custom property is
     substituted where the property is DECLARED and the result then inherits,
     so redefining --teal-ink alone does not re-resolve a --green declared on
     :root: every var(--green) on a dark band kept the cream value. */
  --green:        #68B8AF;
  --green-soft:   #68B8AF;
  --amber:        #E6C897;
  --amber-bright: #E6C897;
  --surface:      rgb(242 237 228 / .06);
  --line:         rgb(232 226 214 / .18);
  --line-strong:  rgb(232 226 214 / .32);
}
.panel-dark { background: var(--panel); color: var(--ink); }
.panel-alt { background: var(--bg-alt); }
.rule { height: 1px; background: var(--line); border: 0; margin: 0; }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .55rem;
  /* one line at desktop, never wraps */
  white-space: nowrap;
  padding: .95rem 1.7rem;
  border-radius: var(--r-pill);
  font-family: 'DMSans', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  border: 1.5px solid transparent;
  cursor: pointer;
  transition: transform .22s var(--ease), background-color .22s var(--ease),
              border-color .22s var(--ease), color .22s var(--ease);
}
/* primary: deep green + bone on light ground, amber + deep green on dark.
   Both directions verified at WCAG AA or better. */
.btn-primary { background: var(--btn-bg); color: var(--btn-fg); }
.btn-primary:hover { background: var(--btn-bg-hover); }
.btn-primary:active { transform: translateY(1px); }

/* primary on dark ground: amber + deep green text. Contrast ~5.6:1 */
.panel-dark .btn-primary,
.btn-amber { background: var(--amber-bright); color: #2F1525; }
.panel-dark .btn-primary:hover,
.btn-amber:hover { background: var(--amber); color: #2F1525; }

.btn-ghost { background: transparent; color: var(--green); border-color: var(--line-strong); }
.btn-ghost:hover { border-color: var(--green); background: rgb(18 53 42 / .05); }
.panel-dark .btn-ghost { color: var(--on-dark); border-color: rgb(237 239 232 / .34); }
.panel-dark .btn-ghost:hover { border-color: var(--on-dark); background: rgb(237 239 232 / .08); }
.btn-ghost:active { transform: translateY(1px); }

.btn-block { width: 100%; }
.btn[aria-disabled="true"] {
  background: transparent; color: var(--ink-faint);
  border-color: var(--line); cursor: not-allowed; pointer-events: none;
}

/* ---------- header ---------- */
.site-head {
  position: sticky; top: 0; z-index: 50;
  height: 72px;                      /* cap: never above 80 */
  display: flex; align-items: center;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  border-bottom: 1px solid var(--line);
}
@media (prefers-reduced-transparency: reduce) {
  .site-head { background: var(--bg); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
.head-in { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; }
.brand { display: inline-flex; align-items: center; text-decoration: none; flex-shrink: 0; }
.brand img { height: 30px; width: auto; }
.brand img.on-dark { filter: brightness(0) invert(1); }
/* nav must render on ONE line at desktop */
.nav { display: flex; align-items: center; gap: clamp(1rem, 2vw, 1.9rem); }
/* :not(.btn) matters: `.nav a` outranks `.btn-primary` on specificity,
   so without it the header CTA inherits the muted link colour and
   renders grey text on the amber pill. */
.nav a:not(.btn) {
  font-size: .93rem; font-weight: 500; text-decoration: none;
  color: var(--ink-soft); white-space: nowrap;
  transition: color .18s var(--ease);
}
.nav a:not(.btn):hover { color: var(--green); }
/* The current page needs a cue that is not colour alone, and one that does
   not read identically to hover. Underline rather than weight: a heavier
   font widens the item and the nav has to stay on one line. */
.nav a:not(.btn)[aria-current="page"] {
  color: var(--green);
  text-decoration: underline;
  text-underline-offset: 6px;
  text-decoration-thickness: 2px;
}
.nav .btn { padding: .6rem 1.2rem; font-size: .9rem; }
.nav-toggle { display: none; background: none; border: 0; padding: .5rem; color: var(--ink); cursor: pointer; }

/* Back to 1040px after Contact left the nav. Measured: the six links plus the
   button need 922px, and the shell adds about 80px of padding, so one line is
   safe from roughly 1000px up.
   Below this the row cannot be guaranteed to stay on one line, so the
   hamburger takes over instead of letting it wrap.
   MUST stay in sync with MOBILE_NAV in js/site.js. */
@media (max-width: 1040px) {
  .nav-toggle { display: inline-flex; }
  .nav {
    position: fixed; inset: 72px 0 auto; flex-direction: column;
    align-items: stretch; gap: 0;
    background: var(--bg); border-bottom: 1px solid var(--line);
    padding: .5rem clamp(1.15rem, 4vw, 2.5rem) 1.5rem;
    box-shadow: var(--shadow-md);
  }
  .nav[hidden] { display: none; }
  .nav a { padding: .95rem 0; border-bottom: 1px solid var(--line); font-size: 1.05rem; }
  .nav .btn { margin-top: 1rem; justify-content: center; border-bottom: 0; }
}

/* ---------- hero ---------- */
.hero { padding-top: clamp(2.5rem, 5vw, 6rem); padding-bottom: clamp(3rem, 6vw, 6rem); }
.hero-grid {
  display: grid; gap: clamp(2rem, 5vw, 4.5rem);
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 900px) {
  /* asymmetric split, not centred */
  .hero-grid { grid-template-columns: 1.15fr .85fr; }
}
/* Hero headline is 7 words, so it sits below the global h1 scale:
   sized so it lands on 2 lines at desktop, never 3. */
.hero h1 { margin-bottom: 1.1rem; font-size: clamp(2.05rem, 4vw, 3.15rem); }
.hero .lede { margin-bottom: 2rem; max-width: 34ch; }
.hero-cta { display: flex; flex-wrap: wrap; gap: .8rem; }
.hero-media {
  border-radius: var(--r-card); overflow: hidden;
  box-shadow: var(--shadow-md);
  aspect-ratio: 4 / 5;
}
.hero-media img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 899px) { .hero-media { aspect-ratio: 3 / 2; } }

/* ---------- offer cards (the three products) ---------- */
.offers { display: grid; gap: 1.4rem; grid-template-columns: 1fr; }
@media (min-width: 760px)  { .offers { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1060px) {
  /* deliberately NOT three equal columns: the lead offer spans wider */
  .offers { grid-template-columns: repeat(3, 1fr); }
}
.offer {
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.offer:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
/* 4:3 rather than 16:10: seances-01.jpg is a 2040x3060 portrait with the face
   in the top quarter, and a landscape crop cut her head off. Taller box plus
   a raised object-position on that image keeps the face in frame. */
.offer-media { aspect-ratio: 4 / 3; overflow: hidden; background: var(--bg-alt); }
.offer-media img { width: 100%; height: 100%; object-fit: cover; }
.offer-body { padding: 1.6rem 1.5rem 1.7rem; display: flex; flex-direction: column; flex: 1; }
.offer h3 { margin-bottom: .5rem; }
.offer p { font-size: .95rem; color: var(--ink-soft); margin-bottom: 1.2rem; }
.offer .price {
  font-variant-numeric: tabular-nums;
  font-family: 'DMSans', sans-serif; font-weight: 500;
  font-size: 1.5rem; color: var(--green); line-height: 1;
  margin-bottom: .3rem;
}
.offer .price-note { font-size: .82rem; color: var(--ink-faint); margin-bottom: 1.4rem; }
.offer .btn { margin-top: auto; }

/* ---------- pricing tiers (séances) ---------- */
.tiers { display: grid; gap: 1.3rem; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 880px) { .tiers { grid-template-columns: repeat(3, 1fr); } }
.tier {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: 1.8rem 1.6rem;
  display: flex; flex-direction: column; height: 100%;
}
.tier.featured { border-color: var(--amber); border-width: 2px; box-shadow: var(--shadow-md); }
.tier-flag {
  display: inline-block; align-self: flex-start;
  font-family: 'DMSans', sans-serif; font-size: .66rem;
  text-transform: uppercase; letter-spacing: .14em; font-weight: 600;
  background: var(--amber-bright); color: #2F1525;
  padding: .3rem .6rem; border-radius: var(--r-pill); margin-bottom: 1rem;
}
.tier h3 { margin-bottom: .9rem; }
.tier .amount {
  font-variant-numeric: tabular-nums;
  font-family: 'DMSans', sans-serif; font-weight: 500; color: var(--green);
  font-size: clamp(2.1rem, 4vw, 2.7rem); line-height: 1; letter-spacing: -.03em;
}
.tier .per { font-size: .88rem; color: var(--ink-faint); margin: .5rem 0 0; }
.tier .save {
  font-size: .88rem; font-weight: 600; color: var(--amber);
  margin: .55rem 0 0;
}
.tier ul { list-style: none; margin: 1.4rem 0 1.7rem; padding: 0; font-size: .93rem; }
.tier li { display: flex; gap: .6rem; padding: .38rem 0; color: var(--ink-soft); }
.tier li svg { flex-shrink: 0; margin-top: .34em; color: var(--amber); }
.tier .btn { margin-top: auto; }

/* ---------- cohort grid (cercle) ---------- */

/* ---------- method: numbered pillars, not equal cards ---------- */
.pillars { display: grid; gap: 0; }
.pillar {
  display: grid; gap: .6rem 2rem;
  grid-template-columns: 1fr;
  padding: clamp(1.6rem, 3vw, 2.4rem) 0;
  border-top: 1px solid var(--line);
}
@media (min-width: 820px) { .pillar { grid-template-columns: 4.5rem 1fr 1.1fr; align-items: start; } }
.pillar .n {
  font-family: 'DMSans', sans-serif; font-size: .85rem; font-weight: 500;
  color: var(--amber); padding-top: .35rem;
}
.pillar p { margin: 0; font-size: .98rem; color: var(--ink-soft); }

/* ---------- benefits: two columns of grouped items ---------- */
.benefits { display: grid; gap: .1rem 3rem; grid-template-columns: 1fr; }
@media (min-width: 760px) { .benefits { grid-template-columns: 1fr 1fr; } }
.benefit { display: flex; gap: .8rem; padding: .75rem 0; align-items: flex-start; }
.benefit svg { flex-shrink: 0; margin-top: .3em; color: var(--amber); }
.benefit span { color: var(--ink-soft); font-size: 1rem; }
.panel-dark .benefit span { color: var(--on-dark-soft); }
.panel-dark .benefit svg { color: var(--amber-bright); }

/* ---------- testimonials: horizontal scroll-snap, not a wall of cards ---------- */
.quotes {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: minmax(280px, 340px);
  gap: 1.2rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 1.2rem;
  scrollbar-width: thin;
}
.quotes > * { scroll-snap-align: start; }
.quote {
  /* figure carries a UA default of margin: 1em 40px. Left in, it added 80px
     between every card on top of the track gap, which is what made the row
     look loosely spaced no matter how small the gap got. */
  margin: 0;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: 1.15rem 1.2rem;
  display: flex; flex-direction: column;
}
.quote blockquote { margin: 0 0 1.2rem; font-size: 1rem; line-height: 1.55; color: var(--ink); }
/* stars and the Google mark sit on one line, so the card reads wide rather
   than tall. Google's own attribution guidance wants the mark next to the
   rating, not floating on its own. */
.stars-row { display: flex; align-items: center; gap: .55rem; margin-bottom: .7rem; }
.stars-row .stars { margin-bottom: 0; }
.stars-row .g { flex-shrink: 0; display: block; }
.rating-head .stars-row { margin-bottom: .15rem; }
.quote figcaption { display: flex; align-items: center; gap: .8rem; margin-top: auto; }
.quote img { width: 56px; height: 56px; border-radius: var(--r-pill); object-fit: cover; flex-shrink: 0; }
.quote .who { font-size: .88rem; font-weight: 600; color: var(--green); line-height: 1.3; }
.quote .role { font-size: .78rem; color: var(--ink-faint); }


/* ---------- reassurance strip, sits directly under a paying CTA ---------- */
.cta-trust {
  display: flex; flex-wrap: wrap; gap: .5rem 1.1rem;
  margin: .9rem 0 0; padding: 0; list-style: none;
}
.cta-trust li {
  display: inline-flex; align-items: center; gap: .38rem;
  font-size: .82rem; color: var(--ink-soft); line-height: 1.3;
}
.cta-trust svg { flex-shrink: 0; color: var(--amber); }
.panel-dark .cta-trust li { color: var(--on-dark-soft); }

/* ---------- FAQ, native details/summary so it works with no JS ---------- */
.faq { max-width: 46rem; }
.faq details {
  border-bottom: 1px solid var(--line);
}
.faq summary {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1.5rem;
  padding: 1.15rem 0;
  cursor: pointer;
  list-style: none;
  font-family: 'Cormorant', Georgia, serif; font-weight: 500;
  font-size: clamp(1.02rem, 1.7vw, 1.15rem); line-height: 1.35;
  color: var(--green);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  flex-shrink: 0; width: 13px; height: 13px; margin-top: .34em;
  border-right: 2px solid var(--amber); border-bottom: 2px solid var(--amber);
  transform: rotate(45deg);
  transition: transform .25s var(--ease);
}
.faq details[open] summary::after { transform: rotate(-135deg); }
.faq summary:hover { color: var(--amber); }
.faq .answer { padding: 0 0 1.35rem; }
.faq .answer p { font-size: .99rem; color: var(--ink-soft); margin-bottom: .7rem; }
.faq .answer p:last-child { margin-bottom: 0; }
.faq .answer a { color: var(--green); text-decoration: underline; text-underline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .faq summary::after { transition: none; }
}


/* ---------- benefit cards with a heading each ---------- */
.gains { display: grid; gap: 1.1rem; grid-template-columns: 1fr; }
@media (min-width: 700px)  { .gains { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1020px) { .gains { grid-template-columns: repeat(3, 1fr); } }
.gain {
  /* no rule above each item: the icon badge already anchors it, and six
     yellow bars across a teal band read as decoration, not structure */
  padding-top: .25rem;
}
/* small icon badge sitting beside each benefit title */
.gain h3 { font-size: 1.05rem; margin-bottom: .4rem; display: flex; align-items: center; gap: .6rem; }
.gain h3 .ico {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 10px;
  display: grid; place-items: center;
  background: rgb(230 179 37 / .18); color: var(--amber);
}
.panel-dark .gain h3 .ico { background: rgb(230 179 37 / .20); color: #E6B325; }
.gain p { font-size: .95rem; color: var(--ink-soft); margin: 0; }
.panel-dark .gain p { color: var(--on-dark-soft); }

/* ---------- trust bar: hard numbers, directly under the hero ---------- */
.trustbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: 1.4rem 0;
  border-block: 1px solid var(--line);
  padding-block: clamp(1.5rem, 3vw, 2.3rem);
}
.stat {
  /* icon and figure on one row, caption spanning under both. Padding rather
     than gap on the parent, so the hairline rules below sit midway between
     two stats instead of hard against one. */
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas: "ico num" "cap cap";
  justify-content: center;
  align-items: center;
  gap: .45rem .65rem;
  text-align: center;
  padding-inline: clamp(1.4rem, 4vw, 3.4rem);
}
/* separators only once the row is genuinely side by side; below that the
   stats wrap and a left border would land under the previous one */
@media (min-width: 760px) {
  .stat + .stat { border-left: 1px solid var(--line); }
}
.stat .ico { grid-area: ico; }
.stat b {
  font-variant-numeric: tabular-nums; grid-area: num; }
.stat > span:not(.ico) {
  grid-area: cap;
  font-size: .82rem;
  line-height: 1.35;
  color: var(--ink-soft);
  max-width: 17ch;
  margin-inline: auto;
}
.stat .ico {
  width: 29px; height: 29px; border-radius: var(--r-pill);
  display: grid; place-items: center;
  background: transparent; color: var(--gold-ink);
  border: 1px solid color-mix(in srgb, var(--gold-ink) 45%, transparent);
}
.panel-dark .stat .ico, .hero-stage .stat .ico {
  background: rgb(230 179 37 / .2); color: #E6B325; border-color: rgb(230 179 37 / .4);
}
.stat b {
  display: block;
  font-family: 'DMSans', sans-serif; font-weight: 500;
  font-size: clamp(1.45rem, 2.4vw, 1.9rem); line-height: 1;
  color: var(--green); letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.stat span {
  display: block; margin-top: .45rem;
  font-size: .84rem; line-height: 1.35; color: var(--ink-soft);
}

/* ---------- reviews ---------- */
.rating-head {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  margin-bottom: 1.8rem;
}
.rating-score {
  font-variant-numeric: tabular-nums;
  font-family: 'DMSans', sans-serif; font-weight: 500;
  font-size: 2.4rem; line-height: 1; color: var(--green);
}
.stars { color: var(--amber); letter-spacing: .1em; font-size: 1rem; line-height: 1; }
.rating-meta { font-size: .88rem; color: var(--ink-soft); }
.quote .stars { margin-bottom: .7rem; }
.quote-src { font-size: .76rem; color: var(--ink-faint); margin-top: .1rem; }
/* avatar sits with the name; initial-letter fallback when there is no photo */
.avatar-fb {
  width: 40px; height: 40px; border-radius: var(--r-pill); flex-shrink: 0;
  display: grid; place-items: center;
  background: var(--bg-alt); color: var(--green);
  font-family: 'DMSans', sans-serif; font-weight: 500; font-size: 1.05rem;
  border: 1px solid var(--line);
}

/* ---------- self-rolling rows ----------
   The track holds the content TWICE and slides exactly -50%, so the loop is
   seamless. Duplicating in markup is what makes that work: without the second
   copy the row would visibly snap back. Pauses on hover and on keyboard focus,
   and collapses to a normal scroller under reduced motion. */
.marquee.bleed {
  /* escape the centred shell and run the full viewport width */
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  max-width: 100vw;
}
.marquee {
  overflow: hidden;
  /* Now that the rows run to 100vw they would otherwise be sliced mid-photo
     at both screen edges. This fades the first and last 4% out instead. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
}
@media (prefers-reduced-motion: reduce) {
  /* the rows become plain scrollers there, and a mask on a scroller hides
     content the user is actively dragging into view */
  .marquee { -webkit-mask-image: none; mask-image: none; }
}
.marquee-track {
  /* .3rem across every row. The photo bands sat at .8rem while the reviews
     were at .3rem, which read as two different systems on one page. */
  display: flex; gap: .3rem; width: max-content;
  /* start, not stretch: a two line review should not be padded out to the
     height of a six line one */
  align-items: flex-start;
  animation: marquee-slide var(--speed, 52s) linear infinite;
}
/* second row runs the other way so the two read as one moving wall */
.marquee.rev .marquee-track { animation-direction: reverse; }
/* review rows sit tight together and stack with almost no gutter */
.marquee.reviews { --speed: 72s; }
.marquee.reviews + .marquee.reviews { margin-top: .3rem; }
.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track { animation-play-state: paused; }
@keyframes marquee-slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.marquee-track > img {
  flex: 0 0 auto;
  width: clamp(170px, 24vw, 260px);
  aspect-ratio: 3 / 4; object-fit: cover; border-radius: 12px;
}
/* the community row runs bigger and the other way, so the two never look like
   the same band repeated */
/* Full viewport width, not the 1240px shell. It is a photo band, so the
   edges should run off the screen rather than stop at a text measure.
   Done by lifting it out of .shell in the markup instead of a 100vw hack,
   which would overflow horizontally by the width of the scrollbar. */
.marquee.people { --speed: 64s; }
.marquee.people .marquee-track { animation-direction: reverse; }
.marquee.people .marquee-track > img {
  width: clamp(230px, 32vw, 380px);
  aspect-ratio: 4 / 3;
  border-radius: 14px;
}
.marquee-track > .quote { flex: 0 0 auto; width: clamp(255px, 25vw, 310px); }

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

/* ---------- discography grid ---------- */
.discs { display: grid; gap: 1.1rem; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 720px)  { .discs { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1020px) { .discs { grid-template-columns: repeat(6, 1fr); } }
.disc { text-decoration: none; display: block; }
.disc img {
  width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 10px;
  box-shadow: var(--shadow-sm);
  transition: transform .28s var(--ease), box-shadow .28s var(--ease);
}
.disc:hover img { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.disc b { display: block; margin-top: .6rem; font-size: .86rem; font-weight: 600; color: var(--green); line-height: 1.3; }
.disc span { display: block; font-size: .76rem; color: var(--ink-faint); }
@media (prefers-reduced-motion: reduce) { .disc img, .disc:hover img { transition: none; transform: none; } }

/* ---------- credential list ---------- */
.creds { list-style: none; margin: 1.4rem 0 0; padding: 0; display: grid; gap: .55rem; }
.creds li { display: flex; gap: .7rem; font-size: .97rem; color: var(--ink-soft); }
.creds li svg { flex-shrink: 0; margin-top: .34em; color: var(--amber); }
.creds li strong { color: var(--ink); font-weight: 600; }

/* ---------- practical details list, grouped not hairlined ---------- */
.facts { display: grid; gap: 1.4rem 2.5rem; grid-template-columns: 1fr; }
@media (min-width: 700px) { .facts { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1020px) { .facts { grid-template-columns: repeat(4, 1fr); } }
.fact dt {
  font-family: 'DMSans', sans-serif; font-size: .68rem;
  text-transform: uppercase; letter-spacing: .16em;
  color: var(--ink-faint); margin-bottom: .45rem;
}
.fact dd { margin: 0; font-size: 1rem; color: var(--ink); line-height: 1.5; }
.panel-dark .fact dd { color: var(--on-dark); }
.panel-dark .fact dt { color: var(--green-soft); }

/* ---------- media strip ---------- */
.strip { display: grid; gap: .7rem; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 700px) { .strip { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1020px) { .strip { grid-template-columns: repeat(6, 1fr); } }
.strip img { border-radius: 12px; aspect-ratio: 1; object-fit: cover; width: 100%; }

/* ---------- forms ---------- */
.field { display: grid; gap: .4rem; margin-bottom: 1.1rem; }
.field label { font-size: .88rem; font-weight: 600; color: var(--ink); }
.field input, .field textarea, .field select {
  font-family: inherit; font-size: 1rem;
  padding: .8rem .95rem;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-input);
  background: var(--surface);
  color: var(--ink);
  width: 100%;
}
/* placeholder contrast: AA against --surface */
.field ::placeholder { color: var(--ink-faint); opacity: 1; }
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--green); outline: 2px solid var(--amber); outline-offset: 1px;
}
.field .help { font-size: .82rem; color: var(--ink-faint); }

/* Error text needs a different red per mode: the light-mode red measures
   2.84:1 on the dark ground. Kept as a class, never inline, so the
   dark-mode override is not outranked by a style attribute. */
.field .err, .form-err { font-size: .88rem; font-weight: 500; color: #B3261E; }
/* ---------- footer ---------- */
.site-foot { background: var(--panel); color: var(--on-dark); padding-block: clamp(3rem, 6vw, 4.5rem); }
.foot-grid { display: grid; gap: 2.4rem; grid-template-columns: 1fr; }
@media (min-width: 820px) { .foot-grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.site-foot img.mark { height: 32px; width: auto; filter: brightness(0) invert(1); margin-bottom: 1rem; }
.site-foot h4 {
  font-family: 'DMSans', sans-serif; font-size: .68rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .16em;
  color: var(--green-soft); margin: 0 0 1rem;
}
.site-foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.site-foot a { color: var(--on-dark-soft); text-decoration: none; font-size: .95rem; }
.site-foot a:hover { color: var(--on-dark); text-decoration: underline; text-underline-offset: 3px; }
.site-foot p { color: var(--on-dark-soft); font-size: .9rem; }
.foot-base {
  margin-top: 2.8rem; padding-top: 1.6rem;
  border-top: 1px solid rgb(237 239 232 / .16);
  font-size: .84rem; color: var(--on-dark-soft);
  display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; justify-content: space-between;
}
.foot-base p { margin: 0; font-size: .84rem; }

/* ---------- scroll reveal (IntersectionObserver driven) ----------
   Scoped to .js, which a tiny inline script in each <head> sets. Nearly
   every element on the site carries .reveal, so hiding by default would
   render the whole page blank if site.js ever failed to load, while the
   links stayed focusable. Hidden only once we know JS is running. */
.js .reveal { opacity: 0; transform: translateY(20px); }
/* Both selectors on purpose. `.js .reveal` and `.reveal.on` have equal
   specificity, so the reveal would win only by source order. `.js .reveal.on`
   is a step higher and settles it no matter how the file is later reordered. */
.reveal.on,
.js .reveal.on {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}

/* ---------- le voile se leve ----------
   Large images arrive veiled and clear as they enter view. This is the one
   motion idea on the site that is not decoration: every Higgsfield carousel
   frames the voice behind something (breath, fogged glass, a pane), and the
   promise is that it clears. The page performs the same thing.

   Rides the existing .reveal observer rather than adding a second system, so
   there is no extra JavaScript: .veil only restyles what .on already toggles.

   Applied to the five large images only. The marquees hold about forty small
   photos and blurring those would cost real GPU time for an effect nobody
   would read at that size. */
.js .reveal.veil {
  opacity: 0;
  transform: scale(1.035);
  filter: blur(12px) saturate(.72);
}
.reveal.veil.on,
.js .reveal.veil.on {
  opacity: 1;
  transform: scale(1);
  filter: blur(0) saturate(1);
  /* filter runs longer than opacity so the image is already present while it
     is still resolving: it reads as clearing, not as fading in */
  transition: opacity .8s var(--ease), transform 1.3s var(--ease), filter 1.3s var(--ease);
}

/* Tried dissolving the hero photo into the band with a bottom mask, the way
   the carousels do. It was wrong here: the fade sits below the fold, so all
   anyone saw was the squared-off corners it left behind. The framed card with
   rounded corners reads better against the teal. */

/* The hero photo is the LCP element. An element at opacity 0 does not count
   as painted, so veiling it the normal way would push Largest Contentful Paint
   out to whenever the observer fires. It clears from blur and scale only, and
   stays fully opaque, which still reads as the veil lifting but lets the
   browser record the paint immediately. */
.js .hero .reveal.veil { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  /* no blur pass at all: the image is simply there */
  .js .reveal.veil { opacity: 1; transform: none; filter: none; }
  .reveal.veil.on, .js .reveal.veil.on { transition: none; }
}
.reveal-d1.on { transition-delay: .07s; }
.reveal-d2.on { transition-delay: .14s; }
.reveal-d3.on { transition-delay: .21s; }
@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal.on { opacity: 1; transform: none; transition: none; }
  .btn:active, .offer:hover { transform: none; }
}

/* ---------- utilities ---------- */
.center { text-align: center; margin-inline: auto; }
.center p { margin-inline: auto; }
.max-60 { max-width: 60ch; }
.mt-0 { margin-top: 0; }
.stack-sm > * + * { margin-top: .6rem; }
.head-block { margin-bottom: clamp(2.2rem, 4vw, 3.4rem); }
.head-block p { margin-bottom: 0; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--panel); color: #F1F0EA;
  padding: .8rem 1.2rem; border-radius: 0 0 var(--r-input) 0;
}
.skip:focus { left: 0; }
