/* ============================================================
   Soul Luna Bella — Design System
   Twilight Plum & Terracotta palette (2026 refresh)
   ============================================================ */

:root {
  /* Brand palette */
  --navy: #2C2A4A;          /* deep plum ink — primary dark bg */
  --navy-deep: #221F3D;     /* darkest panel — hero fallback */
  --footer-bg: #242240;     /* darkest panel — footer / chrome */
  --navy-light: #4A4470;    /* mid plum-lavender — hero gradient, scrolled nav */
  --gold: #D9A46E;          /* terracotta — primary accent */
  --gold-2: #C2905C;        /* terracotta, darkened for hover */
  /* The accent for text on a light ground. --gold-2 is a hover state for
     buttons, not a text colour: at 2.37:1 on --cream-2 it fails even the 3:1
     large-text minimum. This is the same hue taken down far enough to read,
     4.9:1 on cream-2 and 5.9:1 on cream. */
  --gold-on-light: #8A5A28;
  --gold-soft: rgba(217, 164, 110, 0.18);
  --blush: #B0A0B8;         /* dusty mauve-lavender — secondary accent */
  --cream: #F4EFE6;         /* cream/off-white — headline text, light bg */
  --cream-2: #EFE9F2;       /* soft lavender-white — body text, light bg 2 */
  --ink: #241F33;           /* derived: dark plum-grey for text on light bg */

  /* Divider / muted UI lines (dark contexts) */
  --divider-1: #403C68;
  --divider-2: #332C57;
  --divider-3: #453F6E;

  /* Text on light / dark */
  --text-on-cream: #241F33;
  --text-on-cream-muted: #6B6480;  /* derived: muted plum-grey */
  --text-on-navy: #F4EFE6;
  --text-on-navy-muted: #B5AED0;   /* secondary/caption text on dark */
  --footer-text-muted: #8B84AC;    /* footer-specific muted text */
  --on-accent: #33230F;            /* dark brown — text on terracotta bg */

  /* Ombré ramp stops — plum dissolving up through lavender into cream */
  --plum-mid: #5B5480;
  --plum-lavender: #8C7EA8;
  --lavender-pale: #C9BFE0;

  /* Hero gradient — every page header except the home page.
     Resolves to --cream at 100% so the header dissolves into the page
     body below it instead of banding against it. */
  --gradient-hero: linear-gradient(180deg,
    #2C2A4A 0%,
    #5B5480 45%,
    #8C7EA8 70%,
    #C9BFE0 90%,
    #F4EFE6 100%);

  /* Type */
  --font-serif: 'Cormorant Garamond', Georgia, serif;
  --font-sans: 'Jost', 'Helvetica Neue', Arial, sans-serif;

  /* Rhythm */
  --section-pad: clamp(4.5rem, 9vw, 8rem);
  --container-w: 1180px;
  --radius-sm: 3px;
  --radius-md: 8px;
  --radius-lg: 18px;

  --ease-lux: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* The nav is position:fixed — 88px at rest, 74px once scrolled — so an element
   jumped to by its id would land underneath it. scroll-margin-top reserves
   that space, so the target arrives below the bar rather than behind it.
   Applied to :target so it covers any anchor added later, not just the one
   that needed it.

   The clearance is deliberately generous rather than exactly the nav height:
   landing flush against the bar reads as cut off even when nothing is
   actually hidden. On free-chart.html the iframe above also settles after
   the jump, which main.js corrects for separately — this value alone cannot,
   because that is a race rather than an offset. */
:target { scroll-margin-top: clamp(118px, 16vh, 170px); }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol { margin: 0; }
ul[class], ol[class] { list-style: none; padding: 0; }
img, picture { max-width: 100%; display: block; }
input, button, textarea, select { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
button { cursor: pointer; background: none; border: none; }

body {
  font-family: var(--font-sans);
  background: var(--cream);
  color: var(--text-on-cream);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--font-serif);
  font-weight: 500;
  color: var(--navy);
  line-height: 1.15;
  letter-spacing: 0.01em;
}

h1 { font-size: clamp(2.6rem, 3.2vw + 1.6rem, 4.6rem); font-weight: 400; }
h2 { font-size: clamp(2rem, 1.6vw + 1.5rem, 3rem); }
h3 { font-size: clamp(1.35rem, 0.6vw + 1.15rem, 1.7rem); }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--gold-2);
}

.lede {
  font-size: clamp(1.05rem, 0.3vw + 1rem, 1.2rem);
  line-height: 1.75;
  color: var(--text-on-cream-muted);
}

.container {
  max-width: var(--container-w);
  margin-inline: auto;
  padding-inline: clamp(1.5rem, 4vw, 3rem);
}

.section {
  padding-block: var(--section-pad);
  position: relative;
}
.section--tight { padding-block: clamp(3rem, 5vw, 4.5rem); }
.section--navy { background: var(--navy); color: var(--text-on-navy); }
.section--navy h2, .section--navy h3 { color: var(--text-on-navy); }
.section--navy .lede { color: var(--text-on-navy-muted); }
.section--cream2 { background: var(--cream-2); }

.section-head {
  max-width: 700px;
  margin-bottom: clamp(2.5rem, 4vw, 3.5rem);
}
.section-head.center { margin-inline: auto; text-align: center; }
.section-head h2 { margin-top: 0.6em; }

hr.rule {
  border: none;
  width: 64px;
  height: 1px;
  background: var(--gold);
  opacity: 0.6;
  margin: 1.5rem 0;
}
.center hr.rule { margin-inline: auto; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  padding: 1rem 2.1rem;
  min-height: 48px;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-radius: var(--radius-sm);
  transition: background 0.35s var(--ease-lux), color 0.35s var(--ease-lux), border-color 0.35s var(--ease-lux), transform 0.35s var(--ease-lux);
  white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
/* nowrap keeps a label on one line, which is right until the label is longer
   than a phone is wide — then the button hangs off the page and drags a
   horizontal scrollbar with it. On small screens a long label wraps and the
   button centres its lines instead. */
@media (max-width: 560px) {
  .btn { white-space: normal; text-align: center; max-width: 100%; }
}

.btn--gold {
  background: var(--gold);
  color: var(--on-accent);
}
.btn--gold:hover { background: var(--gold-2); }

/* A gated button, not a broken one. Used by the Circle card on
   free-chart.html, which stays shut until a chart has been drawn. It keeps
   its shape and colour and only loses weight, so it reads as "not yet"
   rather than "something went wrong" — and the note directly beneath it
   says which chart it is waiting for. The hover is neutralised so the button
   does not answer a pointer it cannot act on. */
.btn:disabled,
.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn--gold:disabled:hover,
.btn--gold[disabled]:hover { background: var(--gold); }

.btn--outline {
  background: transparent;
  color: var(--text-on-navy);
  border: 1px solid rgba(244, 239, 230, 0.45);
}
.btn--outline:hover { border-color: var(--gold); color: var(--gold); }

.btn--outline-dark {
  background: transparent;
  color: var(--navy);
  border: 1px solid rgba(44, 42, 74, 0.35);
}
.btn--outline-dark:hover { border-color: var(--gold-2); color: var(--gold-2); }

.btn--ghost {
  background: transparent;
  color: var(--gold-2);
  padding: 0.4rem 0;
  min-height: 0;
  border-bottom: 1px solid rgba(194, 144, 92, 0.4);
  border-radius: 0;
}
.btn--ghost:hover { border-color: var(--gold-2); }

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
}

/* ---------- Nav ---------- */
.site-nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  padding: 1.35rem 0;
  transition: background 0.4s var(--ease-lux), padding 0.4s var(--ease-lux), border-color 0.4s var(--ease-lux), box-shadow .4s var(--ease-lux);
  border-bottom: 1px solid transparent;
}
.site-nav.is-scrolled {
  background: rgba(44, 42, 74, 0.92);
  backdrop-filter: blur(10px);
  padding: 0.9rem 0;
  border-bottom-color: rgba(217, 164, 110, 0.18);
  box-shadow: 0 10px 30px rgba(0,0,0,0.18);
}
.site-nav .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-serif);
  font-size: 1.25rem;
  letter-spacing: 0.03em;
  color: var(--text-on-navy);
}
/* The real moon artwork, same as the footer lockup — see logo-moon-nav.svg
   for why it is a separate file rather than the footer's. Shrinks with the
   nav on scroll so the bar does not stay 100px tall down the whole page. */
.brand-mark img {
  width: 60px;
  height: 60px;
  flex-shrink: 0;
  transition: width 0.4s var(--ease-lux), height 0.4s var(--ease-lux);
}
.site-nav.is-scrolled .brand-mark img { width: 46px; height: 46px; }
.brand-mark strong { font-weight: 500; }

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(1.2rem, 2vw, 2.4rem);
  list-style: none;
  margin: 0; padding: 0;
}
.nav-links a {
  font-size: 0.86rem;
  letter-spacing: 0.04em;
  /* "What is a Soul Contract" broke across two lines and threw the bar's
     baseline off. Dropping the Home item freed the room to hold it. */
  white-space: nowrap;
  color: var(--text-on-navy-muted);
  padding: 0.4rem 0.1rem;
  position: relative;
  transition: color 0.3s ease;
}
.nav-links a::after {
  content: '';
  position: absolute;
  left: 0; right: 100%;
  bottom: -2px;
  height: 1px;
  background: var(--gold);
  transition: right 0.3s var(--ease-lux);
}
.nav-links a:hover, .nav-links a[aria-current="page"] { color: var(--text-on-navy); }
.nav-links a:hover::after, .nav-links a[aria-current="page"]::after { right: 0; }

.nav-cta { display: flex; align-items: center; gap: 1rem; }

.nav-toggle {
  display: none;
  width: 44px; height: 44px;
  align-items: center;
  justify-content: center;
  color: var(--text-on-navy);
}
.nav-toggle svg { width: 22px; height: 22px; }

/* Collapses at 1023px rather than 900px. With five links — one of them
   "Readings & Coaching" — plus the logo and the CTA, anything narrower than
   about 1024px squeezes the wordmark until it wraps to three lines. Measured:
   131px on one line at 1024, 44px over three lines at 920. */
@media (max-width: 1023px) {
  .nav-links {
    position: fixed;
    inset: 0;
    top: 0;
    background: var(--navy);
    flex-direction: column;
    justify-content: center;
    gap: 2.2rem;
    transform: translateY(-8px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.35s ease, transform 0.35s ease, visibility 0.35s;
  }
  .nav-links.is-open { opacity: 1; visibility: visible; transform: translateY(0); }
  .nav-links a { font-size: 1.2rem; }
  .nav-toggle { display: inline-flex; }
  /* Keep the toggle above the open panel. The panel is position:fixed
     inset:0 inside the same stacking context, so it painted straight over
     the button that opens it — elementFromPoint at the button's centre
     returned the panel. With body scroll locked at the same time, the only
     way out of an open menu was to follow a link or hit browser back. */
  .nav-toggle { position: relative; z-index: 2; }
  .nav-cta .btn--outline { display: none; }
  /* Smaller once the nav collapses to a hamburger and the bar gets tight. */
  .brand-mark img,
  .site-nav.is-scrolled .brand-mark img { width: 44px; height: 44px; }
}

/* Below the hamburger breakpoint the bar carries three things: the lockup,
   the Free Chart button and the toggle. At 375px those measured 334px
   against 327px of usable width, and the 7px of overflow landed on the only
   wrappable thing in the bar — the wordmark broke to three lines and took
   the bar to 96px tall. Measured: img 44 + wordmark 42 wrapped + button 157
   + toggle 34, with 24px gaps and a 24px inset either side.

   Nothing here lets it wrap again. The room comes from the gaps, the bar's
   own inset and the button, in that order; the bar is given a tighter inset
   than the page content below it, which is normal for a fixed bar and buys
   16px. On the narrowest phones there is no arrangement that fits, so the
   wordmark goes and the moon carries the mark on its own. */
@media (max-width: 700px) {
  .site-nav .container { gap: 0.7rem; padding-inline: 1rem; }
  .brand-mark { font-size: 1.05rem; gap: 0.45rem; }
  .brand-mark strong { white-space: nowrap; }
  .nav-cta .btn {
    padding: 0.7rem 1.05rem;
    min-height: 42px;
    font-size: 0.69rem;
    letter-spacing: 0.1em;
  }
  /* The toggle was being shrunk by the overflow above — it measured 34px
     against the 44px its own rule asks for, which is below the minimum
     comfortable tap target. Hold it and the button at their stated size and
     let the wordmark be the thing that gives. */
  .nav-toggle, .nav-cta .btn { flex-shrink: 0; }
  .brand-mark { min-width: 0; }
  .brand-mark strong { overflow: hidden; text-overflow: ellipsis; }
}
/* 360px is the narrowest widely-used phone and the tightest real case: the
   bar needed 328px against exactly 328px usable, i.e. no slack at all, and a
   fallback face rendering the wordmark a few px wider would have spilled it.
   Trimming the moon and the wordmark here buys ~14px of margin. */
@media (max-width: 400px) {
  .site-nav .container { gap: 0.6rem; }
  .brand-mark { font-size: 1rem; }
  .brand-mark img,
  .site-nav.is-scrolled .brand-mark img { width: 38px; height: 38px; }
}
@media (max-width: 359px) {
  .brand-mark strong { display: none; }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  color: var(--text-on-navy);
  overflow: hidden;
  background: var(--navy-deep);
}
.hero-media {
  position: absolute;
  inset: 0;
  z-index: 0;
}
/* hero.jpg is portrait (1024x1536) in a full-viewport box, so a landscape
   window keeps only a band of its height — 640px of 1536 at 1440x900. These
   two values decide which band, and they are the whole reason the Merkabah's
   lower half used to be cut off on desktop:

   object-position 38% (was 20%) drops the band down off the sky and onto the
   figure. The zoom ceiling of 1.05 (was 1.09) is the other half: every extra
   percent of zoom is a percent of height thrown away, and 1.09 cost about
   50px of the band — enough to clip the lower vertex at the far end of the
   drift even when the framing was right. The drift itself is unchanged in
   duration and easing, it just travels a little less far. */
.hero-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 38%;
  animation: hero-kenburns 34s var(--ease-lux) infinite alternate;
}
@keyframes hero-kenburns {
  from { transform: scale(1); }
  to { transform: scale(1.05); }
}
.hero-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(34,31,61,0.55) 0%, rgba(34,31,61,0.35) 32%, rgba(34,31,61,0.72) 78%, rgba(34,31,61,0.94) 100%);
  z-index: 1;
}
.hero-glow {
  position: absolute;
  left: 50%; top: 46%;
  width: min(60vw, 720px);
  height: min(60vw, 720px);
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, var(--gold-soft) 0%, transparent 68%);
  mix-blend-mode: screen;
  z-index: 1;
  animation: hero-breathe 7s ease-in-out infinite;
  pointer-events: none;
}
@keyframes hero-breathe {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}
#merkabah-canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 1;
  pointer-events: none;
}
.hero-content {
  position: relative;
  z-index: 2;
  width: 100%;
  text-align: center;
  padding-top: 6rem;
}
.hero-content .eyebrow { color: var(--gold); }
.hero h1 {
  color: var(--text-on-navy);
  max-width: 34ch;
  margin: 0.9rem auto 1.3rem;
}
.hero .cta-row { justify-content: center; margin-top: 2.2rem; margin-bottom: 3rem; }

.scroll-cue {
  position: absolute;
  bottom: clamp(1.2rem, 3vw, 2.2rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  color: var(--text-on-navy-muted);
  font-size: 0.68rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
}
.scroll-cue .line {
  width: 1px; height: 34px;
  background: linear-gradient(var(--gold), transparent);
  animation: scroll-cue-move 2.2s ease-in-out infinite;
}
@keyframes scroll-cue-move {
  0% { transform: scaleY(0.4); transform-origin: top; opacity: 0.4; }
  50% { transform: scaleY(1); transform-origin: top; opacity: 1; }
  100% { transform: scaleY(0.4); transform-origin: bottom; opacity: 0.4; }
}

/* ---------- Page header (non-home hero) ---------- */
.page-header {
  background: var(--navy);
  color: var(--text-on-navy);
  padding: clamp(8.5rem, 14vw, 11rem) 0 clamp(3.5rem, 6vw, 5rem);
  position: relative;
  overflow: hidden;
  text-align: center;
  /* Baseline height for the pages that carry a form or tool directly below
     the header — tall enough to breathe, short enough that the first field
     stays on screen. */
  min-height: 58vh;
  display: flex;
  align-items: center;
}
/* The header's decorative layers (::before glow, .hero-glow, the merkabah
   canvas) are all absolutely positioned, so the container is the only
   flex item and can safely own the full width. */
.page-header > .container { width: 100%; }
/* Full-screen landing — About, What is a Soul Contract, Offerings. These
   have prose below rather than something to fill in, so nothing actionable
   gets pushed off screen. See the retimed ramp further down: a taller box
   changes where the text lands on the gradient, which changes what colour
   the text has to be. */
.page-header--full { min-height: 100vh; }
/* Used on every page header except the home page — deep plum dissolving
   through lavender into cream, top to bottom. */
.page-header--gradient {
  background: var(--gradient-hero);
}
/* The eyebrow and h1 sit in the dark-to-mid band and stay cream, with a
   soft dark halo for contrast against the lavender stops. */
.page-header--gradient .eyebrow,
.page-header--gradient h1 {
  text-shadow: 0 2px 24px rgba(34, 31, 61, 0.55);
}
.page-header::before {
  content: '';
  position: absolute;
  top: -30%; left: 50%;
  width: 900px; height: 900px;
  transform: translateX(-50%);
  background: radial-gradient(circle, var(--gold-soft) 0%, transparent 65%);
  pointer-events: none;
}
/* Decorative moon in a page header. Absolutely positioned, so it sits
   alongside the ::before glow and outside the flex flow — the header's
   overflow:hidden crops whatever hangs past the edge. Faint, and held to the
   upper right because the headline is centred and sits over it; the h1 has a
   text-shadow, but the cheapest way to protect contrast is to not put the
   moon behind the words in the first place. */
.page-header-moon {
  position: absolute;
  top: 8%;
  right: -4%;
  width: clamp(180px, 24vw, 360px);
  height: auto;
  opacity: 0.2;
  pointer-events: none;
  user-select: none;
}
@media (max-width: 700px) {
  /* A narrow header is a squeeze: the nav toggle ends around 66px and the
     headline starts around 223px, so the moon has to live inside a gap of
     roughly 157px. At its desktop size it did not, and the hamburger ended up
     sitting on top of it — legible, since the nav paints above, but it read
     as an icon on a smudge.

     Sized to fit that gap rather than positioned as a fraction of the header,
     because the header's height changes with how many lines the headline
     takes and a percentage moved the moon with it. The toggle is at a fixed
     offset, so this is measured from the top in pixels.

     120px rather than the 140 that also fits: 140 left seven pixels between
     the moon and the headline, which any change to the headline or lede
     would have closed. This leaves about twenty-five at each end. */
  .page-header-moon { top: 78px; right: -14%; width: 120px; opacity: 0.14; }
}

.page-header .eyebrow { color: var(--gold); justify-content: center; }
.page-header h1 { color: var(--text-on-navy); max-width: 18ch; margin: 0.8em auto 0.9em; position: relative; }
.page-header .lede { max-width: 620px; margin-inline: auto; color: var(--text-on-navy-muted); position: relative; }
/* Must follow `.page-header .lede` above — equal specificity, so this only
   wins on source order. The lede lands in the pale band at the foot of the
   ombré, where cream text would disappear; it flips to dark ink and drops
   the halo it would otherwise inherit from the block above. */
.page-header--gradient .lede {
  color: var(--ink);
  text-shadow: none;
}

/* ---- Full-screen headers: same colours, retimed ramp ----
   The text block is a fixed pixel height, so in a 100vh header it occupies
   roughly 46–62% of the box instead of the 40–81% it spans in the short
   one. The stops below therefore hold the plum through that band and do
   all the lightening beneath it, still resolving to cream at 100% so the
   dissolve into the page body is preserved.
   Retiming to put the lede back on the pale end instead is not an option:
   the h1 sits only 8% above it, and any ramp light enough for dark ink at
   62% drops the cream h1 to about 2.4:1. */
.page-header--full {
  background: linear-gradient(180deg,
    #2C2A4A 0%,
    #5B5480 72%,
    #8C7EA8 88%,
    #C9BFE0 96%,
    #F4EFE6 100%);
}
/* Consequently the lede here sits on mid-plum, not the pale end, so it
   stays light rather than flipping to ink as it does on the short headers.
   Full cream, not --text-on-navy-muted: the muted lavender only reaches
   about 2.7:1 against this background. */
.page-header--full .lede {
  color: var(--cream-2);
  text-shadow: 0 2px 24px rgba(34, 31, 61, 0.55);
}

/* ---------- Reveal on scroll ---------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.9s var(--ease-lux), transform 0.9s var(--ease-lux);
}
[data-reveal].is-visible { opacity: 1; transform: translateY(0); }
[data-reveal-delay="1"] { transition-delay: 0.1s; }
[data-reveal-delay="2"] { transition-delay: 0.2s; }
[data-reveal-delay="3"] { transition-delay: 0.3s; }

/* ---------- Prose blocks ---------- */
.prose { max-width: 700px; }
.prose p + p { margin-top: 1.2em; }
.prose.center { margin-inline: auto; text-align: center; }

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: center;
}
/* Centring works while the two columns are a similar height. Once one is much
   taller — a long bio beside a portrait — the short column floats down the
   page instead of starting level with the heading. */
.split--top { align-items: start; }

/* The about page portrait. Fills its 4:5 frame rather than letterboxing,
   since the photograph is 3:4 and the difference is about 50px at each end. */
.portrait {
  display: block;
  width: 100%;
  /* height:auto is load-bearing. The img carries width and height attributes
     so the browser reserves the right space before the file arrives, but that
     height attribute is a presentational hint that beats aspect-ratio — the
     frame rendered 506x1466 instead of 4:5, and cover then cropped the photo
     to a tall strip of sea. auto hands the height back to aspect-ratio. */
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: 50% 45%;
  border-radius: var(--radius-lg);
}
@media (max-width: 860px) { .split { grid-template-columns: 1fr; } }

/* ---------- Cards ---------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1.5rem, 2.5vw, 2rem);
}
/* Offerings page: fixed 2-up so tier cards run wide, in pairs
   (Essentials + Illumination, then Bespoke + Live), not auto-fit. */
.tier-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.5rem, 2.5vw, 2rem);
}
@media (max-width: 760px) {
  .tier-grid { grid-template-columns: 1fr; }
}
.card {
  background: #fff;
  border: 1px solid rgba(44, 42, 74, 0.08);
  border-radius: var(--radius-md);
  padding: clamp(1.8rem, 2.5vw, 2.4rem);
  transition: transform 0.4s var(--ease-lux), box-shadow 0.4s var(--ease-lux), border-color .4s;
}
.card:hover {
  transform: translateY(-6px);
  box-shadow: 0 24px 48px rgba(44, 42, 74, 0.1);
  border-color: rgba(217, 164, 110, 0.35);
}
.card .moon-icon { margin-bottom: 1.1rem; }
.card h3 { margin-bottom: 0.6rem; }
.card p { color: var(--text-on-cream-muted); font-size: 0.96rem; }

/* ---------- Card crest: the phase moon on a dark ground ----------
   The moons are drawn for a night sky — glow, and an ombre that resolves to
   near-black — so on a white card they wash out. This gives them a plum band
   at the head of the card while title, copy and CTA stay on white.

   The band bleeds to the card edge by cancelling the card's own padding, so
   the negative margins below must match each card type's padding exactly.
   Top corners are rounded here rather than with overflow:hidden on the card,
   because the featured card's .tier-badge sits deliberately outside its box
   and would be clipped. */
.card-crest {
  display: flex;
  justify-content: center;
  padding: 1.5rem 0 1.4rem;
  margin-bottom: 1.5rem;
  background: radial-gradient(circle at 50% 58%, var(--navy-light) 0%, var(--navy) 68%);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.card-crest .moon-phase {
  width: 76px;
  height: 76px;
  display: block;
}

/* Variant carrying a word under the moon — the About page's Soul / Luna /
   Bella cards. Set in the serif with the logo wordmark's letter-spacing, so
   the three read as brand marks rather than as card labels. */
.card-crest--titled {
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding-bottom: 1.6rem;
}
/* Scoped through .card-crest so it outranks `.card p`, which sets 0.96rem in
   the muted colour and would otherwise win on specificity. Third time a broad
   two-part selector in this file has beaten a single-class modifier — see also
   .page-header--gradient .lede and .premium-card > .premium-hook. */
.card-crest .crest-word {
  font-family: var(--font-serif);
  font-size: 1.9rem;
  line-height: 1;
  letter-spacing: 0.13em;
  color: var(--cream);
  /* the trailing letter-space would otherwise push the word off-centre */
  text-indent: 0.13em;
}

/* .card and .premium-card share padding: clamp(1.8rem, 2.5vw, 2.4rem) */
.card > .card-crest,
.premium-card > .card-crest {
  margin-inline: calc(-1 * clamp(1.8rem, 2.5vw, 2.4rem));
  margin-top: calc(-1 * clamp(1.8rem, 2.5vw, 2.4rem));
}
/* On both tier and premium cards the crest sits inside a .tier-head /
   .premium-head wrapper rather than being a direct child of the card, so the
   negative margins have to hang off the wrapper. Matching only
   `.tier-card > .card-crest` silently did nothing and the band sat inset. */
/* .tier-card padding: clamp(2rem, 3vw, 2.8rem), radius --radius-lg */
.tier-head > .card-crest {
  margin-inline: calc(-1 * clamp(2rem, 3vw, 2.8rem));
  margin-top: calc(-1 * clamp(2rem, 3vw, 2.8rem));
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
/* .premium-card padding: clamp(1.8rem, 2.5vw, 2.4rem) */
.premium-head > .card-crest {
  margin-inline: calc(-1 * clamp(1.8rem, 2.5vw, 2.4rem));
  margin-top: calc(-1 * clamp(1.8rem, 2.5vw, 2.4rem));
}

/* ---------- Offerings: free-chart entry banner ---------- */
.entry-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  flex-wrap: wrap;
  background: #fff;
  border: 1px solid rgba(217, 164, 110, 0.4);
  border-radius: var(--radius-md);
  padding: clamp(1.6rem, 2.5vw, 2.2rem) clamp(1.8rem, 3vw, 2.6rem);
}
.entry-banner h3 { margin: 0.5rem 0 0.6rem; }
.entry-banner p { color: var(--text-on-cream-muted); font-size: 0.96rem; max-width: 60ch; }
.entry-banner .btn { flex-shrink: 0; }

/* ---------- Email signup card ----------
   Same white-card-on-cream treatment as .entry-banner, so it reads as an
   offer rather than another page section. */
.signup-card {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(1.8rem, 4vw, 3.5rem);
  align-items: center;
  background: #fff;
  border: 1px solid rgba(217, 164, 110, 0.4);
  border-radius: var(--radius-md);
  padding: clamp(1.8rem, 3vw, 2.8rem) clamp(1.8rem, 3.5vw, 3rem);
}
.signup-copy h3 { margin: 0.5rem 0 0.7rem; }
.signup-copy p { color: var(--text-on-cream-muted); font-size: 0.96rem; }
/* The card carried a single paragraph until the Circle copy gave it two, and
   the reset zeroes p margins, so they ran together as one block. */
.signup-copy p + p { margin-top: 0.85rem; }
.signup-field { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.9rem; }
.signup-field label {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-on-cream-muted);
}
.signup-field select {
  padding: 0.85rem 1rem;
  border: 1px solid rgba(44, 42, 74, 0.18);
  border-radius: var(--radius-sm);
  background: #fff;
  font-size: 0.98rem;
}
.signup-field select:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 1px;
  border-color: transparent;
}
.signup-field input {
  padding: 0.85rem 1rem;
  border: 1px solid rgba(44, 42, 74, 0.18);
  border-radius: var(--radius-sm);
  background: #fff;
  font-size: 0.98rem;
}
.signup-field input:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 1px;
  border-color: transparent;
}
.signup-form .btn { width: 100%; margin-top: 0.3rem; }
.signup-note {
  margin-top: 0.8rem;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--text-on-cream-muted);
}
@media (max-width: 760px) {
  .signup-card { grid-template-columns: 1fr; }
}

/* ---------- Offerings: premium band ----------
   Five offerings that are not comparable to one another, so they are not
   ranked in a row. The flagship spans the full width and the remaining four
   sit 2x2 beneath it, which also avoids the orphan a 3-up grid would leave.
   Detail lives in <details> rather than inline: the full tier-card format
   runs ~975px tall, and five of those would push this page past 8 screens. */
/* minmax(0, ...) rather than a bare 1fr: a bare 1fr track has an automatic
   minimum, so a card whose content will not shrink past some width pushes the
   track wider than the grid and the whole card hangs off the right edge. The
   full-width featured card did exactly that below ~760px. */
.premium-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 2.5vw, 2rem);
}
.premium-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  background: #fff;
  border: 1px solid rgba(44, 42, 74, 0.08);
  border-radius: var(--radius-md);
  padding: clamp(1.8rem, 2.5vw, 2.4rem);
  transition: transform 0.4s var(--ease-lux), box-shadow 0.4s var(--ease-lux), border-color .4s;
}
.premium-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 24px 48px rgba(44, 42, 74, 0.1);
  border-color: rgba(217, 164, 110, 0.35);
}
.premium-card--wide { grid-column: 1 / -1; }
.premium-card.is-featured {
  border-color: var(--gold);
  box-shadow: 0 30px 60px rgba(44, 42, 74, 0.1);
}
/* .premium-card is a flex column with align-items: flex-start, so this would
   otherwise shrink to its content and the crest band would stop short of the
   card's right edge — visible on the full-width featured card. */
.premium-head { width: 100%; }
.premium-card h3 { margin: 0.3rem 0 0.7rem; }
.premium-card > p { color: var(--text-on-cream-muted); font-size: 0.96rem; }
/* Overlay is the first card to carry two body paragraphs; without this they
   set as one unbroken block. */
.premium-card > p + p { margin-top: 0.9rem; }
/* Qualifier line above the CTA — either who the offering suits, or what it
   requires first. Same slot, same treatment. */
/* Lead question on a card whose copy opens with a hook rather than a
   description. Serif and a size up, so it reads as a turn of the head into the
   body copy rather than as the first sentence of it. */
/* Must outrank `.premium-card > p` above, which is also two parts — hence the
   child combinator rather than a bare class. */
.premium-card > .premium-hook {
  font-family: var(--font-serif);
  font-size: 1.22rem;
  line-height: 1.35;
  color: var(--navy);
  margin-bottom: 0.9rem;
}
.premium-for,
.premium-prereq {
  margin-top: 0.9rem;
  font-size: 0.92rem;
  color: var(--text-on-cream-muted);
}
.premium-for strong,
.premium-prereq strong { color: var(--navy); font-weight: 500; }
.premium-card .btn { margin-top: auto; }
.premium-card > *:not(.btn) + .btn { margin-top: 1.6rem; }

/* Detail on demand — native <details>, so it works without JS */
.premium-detail { width: 100%; margin-top: 1.2rem; }
.premium-detail summary {
  cursor: pointer;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold-2);
  padding: 0.5rem 0;
  border-top: 1px solid rgba(44, 42, 74, 0.1);
}
.premium-detail summary:hover { color: var(--navy); }
.premium-detail ul {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.premium-detail li {
  position: relative;
  padding-left: 1.1rem;
  font-size: 0.92rem;
  color: var(--text-on-cream-muted);
}
.premium-detail li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.62em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--gold);
}
@media (max-width: 760px) {
  .premium-grid { grid-template-columns: minmax(0, 1fr); }
  .entry-banner { flex-direction: column; align-items: flex-start; }
}

/* ---------- Price ----------
   Sits under the offering title, in the serif at display size, because price
   is a decision factor rather than metadata — the small uppercase
   .tier-price-label above it carries format and delivery only. */
.offer-price {
  font-family: var(--font-serif);
  font-size: 1.9rem;
  line-height: 1;
  color: var(--navy);
  margin-top: 0.55rem;
  /* The price is the last thing in the card head, so it owns the gap down to
     the description — neither head has a bottom margin of its own. */
  margin-bottom: 1.1rem;
}
/* Currency rides small and raised beside the figure, so "$288" still reads as
   the number and USD as a qualifier rather than part of it. */
.offer-currency {
  font-family: var(--font-sans);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  color: var(--text-on-cream-muted);
  vertical-align: 0.45em;
  margin-left: 0.15em;
}
/* Where there is no figure to show. Deliberately not set at display size —
   it should read as an invitation, not as a price. */
/* A qualifier under a price — what the number buys, and what more costs.
   Sits in the card head, so it owns the gap down to the description that
   .offer-price would otherwise carry. */
.offer-note {
  font-size: 0.86rem;
  color: var(--text-on-cream-muted);
  margin-top: -0.7rem;
  margin-bottom: 1.1rem;
}

.offer-price--enquire {
  font-family: var(--font-sans);
  font-size: 0.82rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-2);
  margin-top: 0.8rem;
}

/* ---------- Offering tier cards ---------- */
.tier-card {
  background: #fff;
  border: 1px solid rgba(44, 42, 74, 0.08);
  border-radius: var(--radius-lg);
  padding: clamp(2rem, 3vw, 2.8rem);
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
  position: relative;
}
.tier-card.is-featured {
  border-color: var(--gold);
  box-shadow: 0 30px 60px rgba(44, 42, 74, 0.1);
}
.tier-card .tier-badge,
.premium-card .tier-badge {
  position: absolute;
  top: -13px; left: clamp(2rem, 3vw, 2.8rem);
  background: var(--gold);
  color: var(--on-accent);
  font-size: 0.65rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.35rem 0.8rem;
  border-radius: 100px;
}
.tier-head .moon-icon { margin-bottom: 0.9rem; }
.tier-price-label {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: var(--text-on-cream-muted);
  text-transform: uppercase;
}
.tier-list { display: flex; flex-direction: column; gap: 0.55rem; }
.tier-list-title {
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-2);
  margin-bottom: 0.4rem;
}
.tier-list ul { display: flex; flex-direction: column; gap: 0.5rem; list-style: none; }
.tier-list li {
  font-size: 0.92rem;
  color: var(--text-on-cream-muted);
  padding-left: 1.3em;
  position: relative;
  line-height: 1.55;
}
.tier-list li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.55em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--gold);
}
.tier-card .btn { margin-top: auto; align-self: flex-start; }

/* ---------- Moon icon system (SVG, no emoji) ---------- */
.moon-icon { width: 34px; height: 34px; display: inline-block; }
.moon-icon circle.disc { fill: var(--cream-2); }
.moon-icon circle.shadow { fill: var(--navy); }
.moon-icon.on-navy circle.disc { fill: rgba(244,239,230,0.14); }
.moon-icon.on-navy circle.shadow { fill: var(--navy-deep); }
.moon-icon.gold circle.disc { fill: var(--gold); }

/* ---------- Quote / pull ---------- */
.pull-quote {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: clamp(1.3rem, 1.4vw + 1rem, 1.9rem);
  line-height: 1.5;
  color: var(--navy);
  max-width: 780px;
}
.section--navy .pull-quote { color: var(--text-on-navy); }
.pull-quote .gold { color: var(--gold-on-light); font-style: normal; }
.section--navy .pull-quote .gold { color: var(--gold); }

/* The small decorative Star on the home page. Kept when the Soul Contract
   page's diagram was replaced by the worked chart below — without this it
   still filled its column, but only by falling back to how browsers size a
   viewBox'd SVG that has no width. */
.contract-diagram { width: 100%; max-width: 640px; margin-inline: auto; display: block; }

/* ---------- Chart (Star of David / Merkabah) ----------
   A worked chart for the name "Soul Luna Bella". The glyphs are the same
   22 Hebrew-letter symbols the reading tool draws, so they are vector and
   stay crisp; the source PDF was a flat bitmap and could not be used. */
/* The chart carries a lot of small type, and an even 1fr 1fr column caps at
   about 505px whatever the monitor — the container maxes out at 1180px — which
   renders its labels at 8.8px. Giving the chart 60% buys ~612px and lifts them
   over 11px, which is why this split is deliberately lopsided. */
.split--chart { grid-template-columns: 0.8fr 1.2fr; gap: clamp(1.5rem, 2.5vw, 2.75rem); }
/* Stacks earlier than the usual 860px: below about 1150px the chart column
   drops under ~560px and the labels fall back below 11px, where the chart is
   better off stacked at full width than squeezed beside the text. */
@media (max-width: 1150px) { .split--chart { grid-template-columns: 1fr; } }
.slb-chart { width: 100%; max-width: 640px; margin-inline: auto; display: block; }

/* Up-pointing triangle = spiritual, down-pointing = physical, matching the
   prose beside it and the colour split the old diagram used. */
.slb-tri-spirit { fill: none; stroke: var(--gold); stroke-width: 1.3; opacity: 0.9; }
.slb-tri-phys   { fill: none; stroke: var(--navy); stroke-width: 1.3; opacity: 0.55; }

.slb-chart-name {
  font-family: var(--font-serif);
  font-size: 21px;
  fill: var(--ink);
}
.slb-chart-code {
  font-family: var(--font-sans);
  font-size: 9.5px;
  letter-spacing: 0.04em;
  fill: var(--text-on-cream-muted);
}
.slb-chart-label {
  font-family: var(--font-sans);
  font-size: 9.5px;
  letter-spacing: 0.05em;
  fill: var(--navy);
}
.slb-chart-calc {
  font-family: var(--font-sans);
  font-size: 9px;
  fill: var(--text-on-cream-muted);
}
.slb-chart-num {
  font-family: var(--font-serif);
  font-size: 15px;
  fill: var(--navy);
}
.slb-chart-num--soul { font-size: 17.5px; fill: var(--gold-2); }
/* Every element in the chart also carries literal fill/stroke presentation
   attributes, so it renders correctly on its own — an SVG <polygon> with no
   fill defaults to solid black, which is what the star did when this
   stylesheet was cached or slow. These class rules simply win over those
   attributes and swap in the tokens. */
.slb-chart-sym > svg { fill: var(--navy); stroke: var(--navy); }
.slb-chart-sym--soul > svg { fill: var(--gold-2); stroke: var(--gold-2); }

/* ---------- Booking page: one block per session type ----------
   The page serves both live readings, so each gets its own titled block with
   its own price and inclusions, separated by a rule. */

/* ---------- Epigraph ----------
   The site's only quotation of an outside voice. Deliberately quieter than
   .pull-quote: an epigraph frames the section below it, and one set at
   pull-quote size would read as a second headline competing with the h2 just
   above. The rule sits on the left because the text is left-aligned here —
   a centred treatment would fight the prose it introduces. */
.epigraph {
  margin: 0 0 clamp(1.6rem, 2.4vw, 2.2rem);
  padding-left: clamp(1rem, 1.6vw, 1.4rem);
  border-left: 2px solid var(--gold);
}
.epigraph blockquote {
  margin: 0;
  font-family: var(--font-serif);
  font-size: clamp(1.15rem, 0.5vw + 1.05rem, 1.4rem);
  font-style: italic;
  line-height: 1.45;
  color: var(--text-on-cream);
}
/* The attribution line. Shared by the left-ruled epigraph and by quotations
   set as a figure inside a band, so a source credit looks the same wherever
   an outside voice is quoted. */
.epigraph figcaption,
.quote-source {
  margin-top: 0.55rem;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-on-light);
}
.section--navy .quote-source { color: var(--gold); }

/* Centred variant, for sections whose head and prose are already centred.
   The left rule exists to anchor a left-aligned column; against centred text
   it reads as a stray mark, so it gives way to the short gold rule the rest
   of the site uses as a divider. */
.epigraph--center {
  padding-left: 0;
  border-left: 0;
  max-width: 640px;
  margin-inline: auto;
  text-align: center;
}
.epigraph--center::before {
  content: "";
  display: block;
  width: 64px;
  height: 1px;
  background: var(--gold);
  opacity: 0.6;
  margin: 0 auto 1.5rem;
}

/* A pull-quote that carries a source credit. The figure owns the width and
   centring so the quote inside it needs no inline overrides. */
.quote-figure { margin: 0 auto; max-width: 700px; }
.quote-figure .pull-quote { margin: 0 auto; }

/* What happens after you press send. Quiet, directly under the button, so it
   reads as the button's own small print rather than a new idea. */
.form-note {
  margin-top: 0.9rem;
  font-size: 0.9rem;
  color: var(--text-on-cream-muted);
}

/* The quiet alternative under the contact form. Set small and muted: it is a
   fallback, not a competing call to action, and anything louder would argue
   with the form directly above it. */
.form-alt {
  margin-top: clamp(1.4rem, 2.5vw, 2rem);
  text-align: center;
  font-size: 0.92rem;
  color: var(--text-on-cream-muted);
}
.form-alt a { color: var(--gold-on-light); }

/* ---------- Privacy policy ----------
   A long reading page rather than a marketing one: wider measure than .prose
   alone would give, headings that step down quietly, and lists that look like
   part of the prose instead of a feature grid. */
.policy { max-width: 46rem; margin-inline: auto; }
.policy h2 {
  font-size: clamp(1.3rem, 0.6vw + 1.15rem, 1.6rem);
  margin-top: clamp(2.4rem, 4vw, 3.2rem);
  margin-bottom: 0.6rem;
}
.policy h2:first-child { margin-top: 0; }
.policy p { margin-bottom: 1.1em; }
.policy .policy-list { margin: 0 0 1.4em; padding-left: 1.1rem; list-style: disc; }
.policy .policy-list li { margin-bottom: 0.55em; padding-left: 0.2rem; }
.policy a { color: var(--gold-on-light); }
/* The identity block under the title: three lines, not a paragraph. */
.policy-address { font-size: 0.95rem; line-height: 1.7; }
.policy strong { color: var(--ink); }

/* ---------- Sample previews ----------
   Page thumbnails above the waiting list, so a reader can see the shape of
   what they would receive before deciding. The page frame is an element in
   its own right rather than part of the picture: when the real sample pages
   exist they drop in as an <img> inside the same frame, and the border,
   radius, shadow and hover all carry over unchanged. */
.sample-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 2.4rem);
}
.sample-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.sample-page {
  display: block;
  width: 100%;
  /* US Letter, so a real page image drops in without the frame changing size */
  aspect-ratio: 1 / 1.294;
  background: #fff;
  border: 1px solid rgba(44, 42, 74, 0.1);
  border-radius: var(--radius-sm);
  box-shadow: 0 18px 36px rgba(44, 42, 74, 0.1);
  overflow: hidden;
}
.sample-page > svg,
.sample-page > img {
  display: block;
  width: 100%;
  height: 100%;
}
/* An img only once a real page is in place: cover from the top keeps the
   masthead visible rather than centring on the middle of the page. */
.sample-page > img { object-fit: cover; object-position: top center; }
a.sample-page { transition: transform 0.4s var(--ease-lux), box-shadow 0.4s var(--ease-lux); }
a.sample-page:hover { transform: translateY(-6px); box-shadow: 0 26px 50px rgba(44, 42, 74, 0.14); }
.sample-card h3 { font-size: 1.05rem; margin: 1.3rem 0 0.35rem; }
.sample-meta {
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-on-light);
}
/* The state before the sample pages exist. Italic and muted so it reads as a
   note about the preview rather than as a caption belonging to the report. */
.sample-note {
  margin-top: 0.8rem;
  font-size: 0.85rem;
  font-style: italic;
  color: var(--text-on-cream-muted);
}
.sample-card .btn { margin-top: 1rem; }
@media (max-width: 760px) {
  .sample-grid { grid-template-columns: minmax(0, 1fr); max-width: 320px; margin-inline: auto; }
}

/* ---------- Lists (What you may gain / expect) ---------- */
.check-list { display: flex; flex-direction: column; gap: 1rem; }
.check-list li {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  font-size: 1rem;
  color: var(--text-on-cream-muted);
  line-height: 1.6;
}
.check-list .num {
  flex-shrink: 0;
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 1px solid var(--gold);
  color: var(--gold-2);
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.85rem;
  display: flex; align-items: center; justify-content: center;
}
/* Sub-points nested under a numbered step. The parent li is a flex row, so the
   step's text and this list are wrapped in a div to stack them below the
   number rather than beside it. `.check-list li` also matches these nested
   items, hence the display override. */
.check-list .sub-list {
  list-style: none;
  margin: 0.7rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.check-list .sub-list li {
  display: block;
  position: relative;
  padding-left: 1.05rem;
  font-size: 0.94rem;
  line-height: 1.55;
}
.check-list .sub-list li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.6em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--gold);
}

/* ---------- Forms ---------- */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.4rem; }
.form-field { display: flex; flex-direction: column; gap: 0.55rem; }
.form-field.full { grid-column: 1 / -1; }
.form-field label {
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-on-cream-muted);
}
.form-field input,
.form-field select,
.form-field textarea {
  background: #fff;
  border: 1px solid rgba(44,42,74,0.16);
  border-radius: var(--radius-sm);
  padding: 0.9rem 1rem;
  min-height: 48px;
  font-size: 0.98rem;
  color: var(--text-on-cream);
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.form-field textarea { min-height: 140px; resize: vertical; }
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--gold-2);
  box-shadow: 0 0 0 3px rgba(217,164,110,0.16);
}
@media (max-width: 700px) { .form-grid { grid-template-columns: 1fr; } }

/* honeypot field, hidden from humans, visible to bots */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- Placeholder / booking embed shell ---------- */
.embed-shell {
  border: 1px dashed rgba(44,42,74,0.28);
  border-radius: var(--radius-lg);
  background: var(--cream-2);
  padding: clamp(2.5rem, 5vw, 4rem);
  text-align: center;
}
.embed-shell .moon-icon { margin-inline: auto; margin-bottom: 1.2rem; }
/* Once the Acuity calendar replaces the placeholder the dashed "coming soon"
   frame and its padding would box the calendar in, so the shell becomes a
   plain card: solid hairline, no padding, clipped corners. */
/* Acuity's own page is set to our cream (#F4EFE6) with navy panels on it, so
   this card carries no frame of its own: cream on the cream page body, no
   border, no shadow. A card edge here would outline a block the same colour
   as everything around it, and the white it used to be showed as a seam
   between our page and theirs. What reads as the object now is Acuity's navy
   panels, which is the same figure-on-ground the rest of the site uses. */
.embed-shell--live {
  border: 0;
  background: var(--cream);
  padding: 0;
  overflow: hidden;
  box-shadow: none;
  /* The calendar used to share the row with the session descriptions, which
     left it 508px wide at a 1280px window — tight for a date grid. It now has
     the row to itself. Capped short of the full 1180px because Acuity centres
     its own content: at container width the card is mostly empty margin. */
  max-width: 900px;
  margin-inline: auto;
}


/* ---------- The one key word, under the free chart ----------
   The word is the hero, not the number: the number is already on the chart
   directly above this, so repeating it larger would say nothing new. It is
   kept, smaller, so the two read as belonging to each other. */
.kw-pair {
  font-family: var(--font-serif);
  font-size: clamp(1.6rem, 0.8vw + 1.4rem, 2.1rem);
  color: var(--text-on-navy);
  margin-top: 0.7rem;
  letter-spacing: 0.04em;
}
.kw-word {
  font-family: var(--font-serif);
  font-size: clamp(2.1rem, 2.2vw + 1.3rem, 3.1rem);
  line-height: 1.15;
  color: var(--gold);
  margin-top: 0.3rem;
}
/* Larger than a caption: this line now carries the sentence that explains
   what a key word is, so it reads as a statement under the word rather than
   as fine print beneath it. Wider measure to match, or the longer sentence
   breaks into four short lines. */
.kw-note {
  font-size: 1.08rem;
  color: var(--text-on-navy-muted);
  max-width: 620px;
  margin: 1.5rem auto 0;
}

/* ---------- Free chart tool embed ---------- */
.chart-embed {
  border: 1px solid rgba(44, 42, 74, 0.1);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(44, 42, 74, 0.1);
}
/* Height is a starting point only: main.js measures the generator's own
   document and grows the frame to match, so the tool never scrolls inside
   its own box. This value covers the empty form state at phone widths,
   which is the tallest the tool gets before anyone generates a chart. */
.chart-embed iframe {
  display: block;
  width: 100%;
  height: 560px;
  border: none;
}

/* ---------- Footer ---------- */
.site-footer {
  background: var(--footer-bg);
  color: var(--footer-text-muted);
  padding: clamp(3.5rem, 6vw, 5rem) 0 2rem;
}
.footer-top {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 2.5rem;
  padding-bottom: 2.8rem;
  border-bottom: 1px solid var(--divider-2);
}
/* Footer lockup — the one place the full moon artwork runs large enough
   for its surface texture and star cluster to actually read. */
.footer-lockup {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.8rem;
  margin-bottom: 1.1rem;
  color: var(--text-on-navy);
}
.footer-lockup img {
  display: block;
  width: 110px;
  height: 110px;
  margin-left: -0.35rem; /* optical: the moon's glow reads as left padding */
}
.footer-lockup strong {
  font-family: var(--font-serif);
  font-size: 1.55rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  line-height: 1;
}
.footer-brand p { max-width: 320px; font-size: 0.92rem; line-height: 1.7; }
.footer-cols { display: flex; gap: clamp(2.5rem, 5vw, 5rem); flex-wrap: wrap; }
.footer-col h4 {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-on-navy);
  font-weight: 500;
  margin-bottom: 1rem;
}
.footer-col ul { display: flex; flex-direction: column; gap: 0.7rem; list-style: none; margin: 0; padding: 0; }
.footer-col a { font-size: 0.92rem; transition: color 0.3s ease; }
.footer-col a:hover { color: var(--gold); }
.footer-bottom {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding-top: 1.8rem;
  font-size: 0.8rem;
}
.footer-bottom .pull-quote { font-size: 0.95rem; color: var(--footer-text-muted); }

@media (max-width: 700px) {
  .footer-top, .footer-bottom { flex-direction: column; }
}

/* ---------- Utility ---------- */
.text-center { text-align: center; }
.mt-lg { margin-top: clamp(2rem, 4vw, 3rem); }
.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;
}
