/* ============================================================================
   DISCOVER SAINT LUCIA WELL — PAGE COMPONENTS
   ----------------------------------------------------------------------------
   Loaded by the consumer and professional pages only — NOT by Foundations,
   which brings its own page styles. See css/chrome.css for why that split
   exists and what belongs on each side of it.
   ========================================================================== */

/* ══════════════════════════════════════════════════════════════════════════
   BASE — consumer pages only (Foundations brings its own)
   ══════════════════════════════════════════════════════════════════════════ */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ivory-ink);
  font-family: var(--serif);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--serif-display);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.01em;
  margin: 0 0 0.5em;
  text-wrap: balance;
}
h1 { font-size: var(--fs-hero); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }

h1 em, h2 em, .lead em {
  font-style: italic;
  font-family: var(--serif);
}

p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

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

ul, ol { margin: 0; padding: 0; list-style: none; }

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

/* Focus rings and the skip link live in chrome.css — they are universal. */

/* ══════════════════════════════════════════════════════════════════════════
   HERO
   ══════════════════════════════════════════════════════════════════════════ */
.hero {
  position: relative;
  min-height: min(94svh, 900px);
  display: flex;
  align-items: flex-end;
  padding-block: clamp(6rem, 14vh, 10rem) clamp(3rem, 7vh, 5rem);
  background: var(--ink);
  color: var(--paper);
  overflow: hidden;
}
/* Pull the hero up under the transparent header — only when there IS one. */
body[data-enhanced="true"][data-hero="dark"] .hero { margin-top: -73px; }
.hero-media { position: absolute; inset: 0; }
/* The <picture> wrapper MUST be told to fill. Without this it has auto height,
   so `height: 100%` on the <img> resolves against nothing, the image falls back
   to its natural ratio, and a band of bare --ink shows below it. Cost us a
   ~90px dark strip under the hero and under every final CTA before it was
   caught by actually looking at a render. */
.hero-media picture { display: block; width: 100%; height: 100%; }
.hero-media img,
.hero-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Ambient cinemagraphs sit ON TOP of an always-present still and fade in only
   once they can actually play. The image is the real LCP element and the whole
   experience for anyone the loader skips. */
.ambient-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.1s var(--ease-out-quint);
  pointer-events: none;
}
.ambient-video.is-ready { opacity: 1; }

/* Tuned to the hero photograph rather than applied generically. The image
   already carries its own vignette — the tree canopy frames the top and both
   sides — and its subject (the Pitons through the gap, the path to the water)
   sits centre-right and luminous. A heavy all-over bottom scrim would crush
   exactly the detail the picture is for.

   So: weight the scrim to the LEFT, where the headline sits over dark foliage
   that can absorb it, keep a light top wash for nav legibility, and let the
   centre-right stay bright. */
/* Solved against the actual photograph rather than eyeballed — the pixel
   luminance behind each text block was sampled, the gradients composited over
   it, and the worst-case contrast measured (see README).

   The finding that shaped it: this frame is high-frequency everywhere (dappled
   canopy light), so there is no large reliably-dark area — but the foreground
   foliage and shadowed path across the bottom third ARE consistently dark. So
   the text sits down there, the vista through the gap stays bright and
   uncrushed, and the scrim does its work low and to the left.

   The text scrim is sized in PIXELS and anchored to the bottom, not expressed
   as a percentage of the hero. The hero's height is viewport-dependent
   (min(94svh, 900px)), so a percentage-based scrim drifts relative to the text:
   on a 720px-tall screen the block sat at 45% of the hero instead of 55% and
   landed straight in the bright gap — measured 2.65:1. A bottom-anchored band
   keeps the scrim under the words at every height.

   Verified worst case across 900 / 1280 / 2000px wide: 5.16:1. */
.hero-veil {
  position: absolute;
  inset: 0;
  background:
    /* 1 · left wash — full height, in % because it tracks the image crop */
    linear-gradient(95deg,
      rgba(13,20,32,.44) 0%,
      rgba(13,20,32,.32) 28%,
      rgba(13,20,32,.16) 52%,
      rgba(13,20,32,0)   70%),
    /* 2 · the text scrim — anchored to the BOTTOM and sized in px, not % */
    linear-gradient(180deg,
      rgba(13,20,32,0)   0%,
      rgba(13,20,32,.22) 12%,
      rgba(13,20,32,.44) 36%,
      rgba(13,20,32,.62) 66%,
      rgba(13,20,32,.74) 100%),
    /* 3 · top wash — keeps the transparent header legible over the photo.
       Also px-anchored: the nav is a fixed distance from the top, so tying its
       scrim to a percentage of a variable-height hero has the same drift bug. */
    linear-gradient(180deg,
      rgba(13,20,32,.62) 0%,
      rgba(13,20,32,.24) 45%,
      rgba(13,20,32,0)   100%);
  background-size: 100% 100%, 100% 600px, 100% 200px;
  background-position: top left, bottom left, top left;
  background-repeat: no-repeat;
}

/* The scrim is deliberately lighter than pure contrast maths would choose,
   and a text shadow makes up the difference. Scrim alone, strong enough to
   clear AA against every pixel behind the words, desaturates the whole frame:
   the turquoise goes grey and the sunlit palms lose their glow. A shadow buys
   the same legibility locally, at the glyph, and costs the photograph nothing.

   Verified by measuring contrast under the ACTUAL GLYPH PIXELS (rendering the
   text to a mask and sampling the composited backdrop beneath it) rather than
   across the whole text bounding box, which is far stricter than WCAG asks —
   most of that box is the gaps between words and lines. Worst 5th-percentile
   across headline, lead, eyebrow and nav: 4.80:1. */
.hero .hero-inner > *,
body[data-hero="dark"] .site-header[data-at-top="true"] .nav {
  text-shadow:
    0 2px 18px rgba(8, 12, 20, .62),
    0 2px 18px rgba(8, 12, 20, .62);
}

/* ── Mobile: stack, do not overlay ────────────────────────────────────────
   A narrow viewport crops this 16:9 frame to its middle 29% — which is exactly
   the bright gap: sky, the Pitons, open water. Measured worst-case contrast for
   text over that crop is 1.5:1, and no scrim fixes it without flattening the
   photograph into mud.

   So below 860px the hero stops being an overlay and becomes a stacked
   composition: the photograph keeps its full frame in a band of its own, the
   text sits beneath it on ink where it is unconditionally legible, and the gold
   journey line carries the eye across the seam. Legibility stops depending on
   what happens to be behind the words. */
@media (max-width: 860px) {
  .hero {
    display: block;
    min-height: 0;
    padding-block: 0;
    background: var(--ink);
  }
  .hero-media {
    position: relative;
    inset: auto;
    aspect-ratio: 16 / 10;
  }
  /* A short fade into the ink so the band joins the copy instead of stopping. */
  .hero-media::after {
    content: '';
    position: absolute;
    inset: auto 0 0 0;
    height: 28%;
    background: linear-gradient(180deg, rgba(18,48,47,0), var(--ink));
    pointer-events: none;
  }
  .hero-veil { display: none; }
  .hero-wrap { padding-block: clamp(1.75rem, 6vw, 2.5rem) clamp(2.5rem, 9vw, 3.5rem); }
  .hero h1 { font-size: clamp(2.3rem, 8.8vw, 3rem); }
  /* Text sits on solid ink here — a shadow would only muddy it. */
  .hero .hero-inner > * { text-shadow: none; }
}

.hero-wrap { position: relative; z-index: 2; width: 100%; }
.hero-inner { max-width: min(58rem, 100%); }

/* The hero headline is deliberately smaller than the raw --fs-hero cap.
   Keeping the whole text block inside the photograph's dark foreground band
   is what makes it legible; an extra 16px of display type costs two more
   lines, pushes the block up into the bright gap, and fails contrast. */
.hero h1 {
  font-size: clamp(2.4rem, 5.2vw, 4.1rem);
  margin-bottom: 0.32em;
}
.hero .lead { max-width: min(48rem, 100%); }
.hero-ctas { margin-top: clamp(1.4rem, 2.2vw, 1.75rem); }
.hero .eyebrow { margin-bottom: 0.85em; }

.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
}
.hero-note {
  font-family: var(--sans);
  font-size: var(--fs-small);
  color: var(--mist);
  margin-top: 1.5rem;
}

/* The signature: ring → gold journey line → coordinates. */
.hero-signature {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  position: absolute;
  right: var(--gutter);
  bottom: 0;
  text-align: center;
}
.hero-signature .thread {
  width: 1px;
  height: clamp(48px, 9vh, 96px);
  background: linear-gradient(180deg, var(--gold), rgba(217,160,60,0));
}
.coords {
  font-family: var(--sans);
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mist);
  margin: 0;
}
.scroll-cue {
  font-family: var(--sans);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mist);
  margin: 0.5rem 0 0;
  writing-mode: vertical-rl;
  opacity: .8;
}


/* ══════════════════════════════════════════════════════════════════════════
   PHOTOGRAPHY + ART-DIRECTED PLACEHOLDERS
   A placeholder must always read AS a placeholder. Never let it resolve into
   something that could be mistaken for a photograph we actually have.
   ══════════════════════════════════════════════════════════════════════════ */
.photo { margin: 0; }
.photo-frame-media { position: relative; border-radius: 2px; overflow: hidden; }
.photo img { width: 100%; border-radius: 2px; display: block; }
.photo figcaption {
  font-family: var(--sans);
  font-size: 0.76rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-top: 0.9rem;
}

.photo--placeholder .photo-frame,
.hero-placeholder {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.5rem;
  padding: 1.5rem;
  border: 1px dashed rgba(251,248,241,.34);
  border-radius: 2px;
  aspect-ratio: 4 / 5;
  background: linear-gradient(155deg, var(--ink) 0%, var(--teal) 58%, var(--charcoal) 100%);
  color: var(--paper);
}
.hero-placeholder {
  aspect-ratio: auto;
  height: 100%;
  border: 0;
  border-radius: 0;
  padding: clamp(1.5rem, 4vw, 3rem);
  justify-content: flex-start;
}
[data-tone="twilight"] { background: linear-gradient(160deg, #0D1420 0%, #1D3244 55%, #0B1018 100%); }
[data-tone="copper"]   { background: linear-gradient(160deg, #2A1D14 0%, #9B7455 62%, #1A130E 100%); }
[data-tone="ocean"]    { background: linear-gradient(160deg, #0B3B44 0%, #3F7B7B 58%, #08252B 100%); }
[data-tone="rainforest"]{ background: linear-gradient(160deg, #16261A 0%, #566E4E 58%, #0E1A12 100%); }

.photo-flag {
  font-family: var(--sans);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0;
}
.photo-dir {
  font-family: var(--sans);
  font-size: 0.78rem;
  line-height: 1.5;
  color: rgba(251,248,241,.82);
  margin: 0;
  max-width: 52ch;
}
.photo-ratio {
  font-family: var(--sans);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  color: var(--mist);
  margin: 0;
}
.hero .photo-dir { max-width: 40ch; }


/* ══════════════════════════════════════════════════════════════════════════
   LENS GRID — the island's six elements
   ══════════════════════════════════════════════════════════════════════════ */
.lens-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(2rem, 4vw, 3.5rem) clamp(2rem, 4vw, 4rem);
}
.lens-item { border-top: 1px solid var(--hairline-l); padding-top: 1.4rem; }
.lens-item h3 {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--gold-deep);
  margin-bottom: 0.9em;
}
.lens-item p { margin: 0; color: var(--ivory-ink); }
.lens-item--link h3 a {
  text-decoration: none;
  display: inline-flex;
  align-items: baseline;
  gap: 0.4em;
  color: var(--gold-deep);
}
.lens-item--link h3 a::after {
  content: '→';
  transition: transform .3s var(--ease-out-quint);
}
@media (hover: hover) and (pointer: fine) {
  .lens-item--link h3 a:hover::after { transform: translateX(4px); }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE WELL COMPASS
   ══════════════════════════════════════════════════════════════════════════ */
.compass-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: center;
}
.compass-figure { max-width: 560px; width: 100%; margin-inline: auto; }
.compass { width: 100%; height: auto; overflow: visible; }
.compass-ring {
  fill: none;
  stroke: var(--hairline-d);
  stroke-width: 1;
}
.compass-ring--inner { stroke-dasharray: 3 7; }
.compass-tick { stroke: var(--gold); stroke-width: 1.2; opacity: .75; }
.compass-label {
  font-family: var(--sans);
  font-size: 21px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  fill: var(--paper);
}
.compass-center {
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.2em;
  fill: var(--gold);
}
.compass-section .section-head { margin-bottom: 2rem; }

.note-card {
  border-left: 2px solid var(--gold);
  padding: 0.25rem 0 0.25rem 1.4rem;
  margin-top: 2.25rem;
}
.note-card h3 {
  font-family: var(--sans);
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--paper);
  margin-bottom: 0.5em;
}
.note-card p { color: var(--mist); margin: 0; font-size: var(--fs-small); }


/* ══════════════════════════════════════════════════════════════════════════
   VILLAGE CARDS
   Each card carries its village accent through --v / --v-ink.
   ══════════════════════════════════════════════════════════════════════════ */
.village-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
}
.village-card {
  background: var(--paper);
  border: 1px solid var(--hairline-l);
  border-radius: 2px;
  padding: clamp(1.5rem, 2.6vw, 2.1rem);
  transition: border-color .3s var(--ease-out-quint),
              transform .3s var(--ease-out-quint),
              box-shadow .3s var(--ease-out-quint);
}
.village-link {
  text-decoration: none;
  display: flex;
  flex-direction: column;
  height: 100%;
}
/* Movement-based hover is gated behind a real pointer. On touch, :hover fires
   on tap and then STAYS — the card lifts and never settles back, which reads as
   a stuck card rather than a hover affordance. Colour-only hovers elsewhere are
   left ungated: they flash and clear harmlessly. */
@media (hover: hover) and (pointer: fine) {
  .village-card:hover {
    border-color: var(--v);
    transform: translateY(-3px);
    box-shadow: 0 14px 40px -22px rgba(28,43,41,.5);
  }
}
.village-rule {
  display: block;
  width: 34px;
  height: 2px;
  background: var(--v);
  margin-bottom: 1.4rem;
}
.village-card h3,
.village-card h4 {
  font-size: 1.32rem;
  color: var(--v-ink);
  margin-bottom: 0.35em;
}
.village-subline {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1rem;
  color: var(--v-ink);
  margin-bottom: 0.9em;
}
.village-body {
  font-size: 0.97rem;
  color: var(--muted);
  flex: 1;
}
.village-more {
  font-family: var(--sans);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--v-ink);
  margin-top: 1.4rem;
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
}
.village-more::after {
  content: '→';
  transition: transform .3s var(--ease-out-quint);
}
@media (hover: hover) and (pointer: fine) {
  .village-card:hover .village-more::after { transform: translateX(4px); }
}

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 1.2rem;
}
.chip-list li {
  font-family: var(--sans);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--v-ink, var(--muted));
  border: 1px solid var(--hairline-l);
  border-radius: 100px;
  padding: 0.3em 0.8em;
}


/* ══════════════════════════════════════════════════════════════════════════
   PAGE HEADER — opener for interior pages
   ══════════════════════════════════════════════════════════════════════════ */
.page-header {
  background: var(--ink);
  color: var(--paper);
  padding-block: clamp(4.5rem, 11vw, 8rem) clamp(3rem, 7vw, 5rem);
}
.page-header h1 { max-width: min(46rem, 100%); margin-bottom: 0.35em; }
.page-header .lead { max-width: var(--measure); }
.page-header-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 2rem;
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline-d);
}
.page-header-meta li {
  font-family: var(--sans);
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}


/* ══════════════════════════════════════════════════════════════════════════
   VILLAGE BLOCKS (/explore)
   ══════════════════════════════════════════════════════════════════════════ */
.village-blocks { display: grid; gap: clamp(3.5rem, 8vw, 7rem); }
.village-block {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  scroll-margin-top: 90px;   /* sticky header clearance for deep links */
}
.village-block--flip .village-block-media { order: 2; }

/* Every third village breaks the zigzag: full-measure photograph, copy in two
   columns beneath it. Stops the alternating rows reading as a metronome and
   gives two of the six a larger moment.

   The copy is TWO REAL CONTAINERS, not one flat list with grid-column applied
   per child. The flat version fought auto-placement: the intro took rows 1-4 in
   column one, so the themes list landed on row 5 of column two and floated far
   below its own heading with a large gap above it. */
.village-block--wide {
  grid-template-columns: 1fr;
  gap: clamp(1.75rem, 3vw, 2.5rem);
}
.village-block--wide .village-block-media img { aspect-ratio: 21 / 9; }
.village-block--wide .village-block-copy {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
}
.village-block--wide .village-themes-title { margin-top: 0; }
/* Positioning context for the ambient loop. The <img> stays in flow and keeps
   defining the band's height — the video is layered over it, so a village
   without a loop is unaffected and the poster remains the LCP candidate. */
.village-block-media { position: relative; }
.village-block-media img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 2px;
}
.village-block-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 2px;
  opacity: 0;
  transition: opacity .7s var(--ease-out-quint);
}
.village-block-media video.is-ready { opacity: 1; }
/* The wide bands crop to 21:9, so the loop must crop with them or the video
   would show a 3:2 frame behind a 21:9 photograph. */
.village-block--wide .village-block-media video { aspect-ratio: 21 / 9; height: auto; inset: 0 0 auto 0; }
.village-block-copy h3 {
  font-size: clamp(1.6rem, 2.6vw, 2.1rem);
  color: var(--v-ink);
  margin-bottom: 0.3em;
}
.village-block-copy .village-subline {
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--fs-lead);
  color: var(--v-ink);
  margin-bottom: 1em;
}
.village-block-copy > p { color: var(--muted); max-width: 46ch; }
.village-themes-title {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--v-ink);
  margin: 1.8rem 0 0.9rem;
}
.village-block .tick-list li::before { background: var(--v); }
.village-note {
  font-size: var(--fs-small);
  color: var(--muted);
  border-left: 2px solid var(--v);
  padding-left: 1rem;
  margin-top: 1.6rem;
}
.village-eclipse { margin-top: 1.4rem; }
.village-eclipse a {
  font-family: var(--sans);
  font-size: 0.84rem;
  color: var(--v-ink);
  text-underline-offset: 4px;
}


/* ══════════════════════════════════════════════════════════════════════════
   EXPERIENCE GROUPS (/explore)
   ══════════════════════════════════════════════════════════════════════════ */
.exp-groups {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(2.5rem, 5vw, 4rem);
}
.exp-group-head { margin-bottom: 1.6rem; }
.exp-group-head h3 {
  font-size: clamp(1.3rem, 2vw, 1.6rem);
  color: var(--v-ink);
  margin-bottom: 0.25em;
}
.exp-group-village {
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--muted);
  margin: 0;
}
.exp-group-village a { color: var(--v-ink); text-underline-offset: 3px; }
.exp-list { display: grid; gap: 1.2rem; }
.exp-list li { border-top: 1px solid var(--hairline-l); padding-top: 1rem; }
.exp-list h4 {
  font-family: var(--serif-display);
  font-size: 1.1rem;
  font-weight: 400;
  margin-bottom: 0.3em;
}
.exp-list p { font-size: 0.94rem; color: var(--muted); margin: 0; }

/* — pointer response on the intention groups —
   Eleven experiences sat completely inert. They respond PER ITEM rather than
   per group: hovering a group and having eleven things move at once would be
   noise, and the thing the reader is actually pointing at is one experience.

   The hairline is the existing `border-top` overlaid, not a new border — so
   nothing reflows, and the accent replaces the grey exactly in place. */
.exp-list li { position: relative; }
.exp-list li::after {
  content: '';
  position: absolute;
  top: -1px; left: 0; right: 0;
  height: 1px;
  background: var(--v);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .5s var(--ease-out-expo);
}
.exp-list h4 { transition: transform .4s var(--ease-out-quint), color .4s var(--ease-out-quint); }
.village-rule { transition: width .5s var(--ease-out-expo); }

@media (hover: hover) and (pointer: fine) {
  .exp-list li:hover::after { transform: scaleX(1); }
  .exp-list li:hover h4 { transform: translateX(4px); color: var(--v-ink); }
  /* The group's signature dash extends while anywhere in that group is under
     the pointer — the only group-level response, and it moves nothing but
     itself. */
  .exp-group:hover .village-rule { width: 56px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   PROPERTY DIRECTORY (/explore)
   ══════════════════════════════════════════════════════════════════════════ */
.prop-groups { display: grid; gap: clamp(3rem, 6vw, 4.5rem); }
.prop-group-title {
  font-size: clamp(1.2rem, 1.9vw, 1.45rem);
  color: var(--v-ink);
  margin-bottom: 1.6rem;
}
.prop-group-title a { text-decoration: none; }
.prop-group-title a:hover { text-decoration: underline; text-underline-offset: 4px; }
.prop-group-title .village-rule { margin-bottom: 0.9rem; }
.prop-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.25rem, 2.5vw, 2rem);
}
.prop-card {
  border: 1px solid var(--hairline-l);
  border-radius: 2px;
  overflow: hidden;
  background: var(--paper);
  display: flex;
  flex-direction: column;
}
.prop-card-media img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}
.prop-card-body { padding: 1.1rem 1.25rem 1.35rem; }
.prop-card h4 {
  font-size: 1.08rem;
  margin-bottom: 0.3em;
  color: var(--ivory-ink);
}
.prop-role {
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--muted);
  margin: 0;
}
/* No photography yet — a typographic card in the village accent, which reads
   as an editorial choice rather than a hole in the grid. */
.prop-card--text {
  border-color: var(--v);
  background: linear-gradient(150deg, var(--paper-2), var(--paper));
  min-height: 190px;
  justify-content: flex-end;
}
.prop-card--text h4 { color: var(--v-ink); }


/* ══════════════════════════════════════════════════════════════════════════
   JOURNEY FINDER — promo strip on the homepage
   ══════════════════════════════════════════════════════════════════════════ */
.finder-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(2rem, 4vw, 3.5rem);
  counter-reset: step;
}
.finder-steps li { border-top: 1px solid var(--hairline-l); padding-top: 1.4rem; }
.finder-step-n {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--gold-deep);
  display: block;
  margin-bottom: 1rem;
}
.finder-steps h3 { font-size: 1.22rem; margin-bottom: 0.4em; }
.finder-steps p { color: var(--muted); margin: 0; font-size: 0.97rem; }


/* ══════════════════════════════════════════════════════════════════════════
   JOURNEY FINDER — the quiz itself (/journey)
   ══════════════════════════════════════════════════════════════════════════ */
.finder-static-intro { max-width: var(--measure); color: var(--muted); font-size: var(--fs-lead); }
.finder-static .village-grid { margin-top: 3rem; }

.finder-form { max-width: 760px; }

.finder-q { border: 0; margin: 0; padding: 0; }
.finder-q legend { display: block; width: 100%; padding: 0; margin-bottom: 2rem; }
.finder-q-n {
  display: block;
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-deep);
  margin-bottom: 1rem;
}
.finder-q-text {
  display: block;
  font-family: var(--serif-display);
  font-size: var(--fs-h2);
  line-height: 1.16;
  margin-bottom: 0.4em;
  text-wrap: balance;
}
/* js/journey.js moves focus here on each step so screen-reader and keyboard
   users are not stranded on a fieldset that just became hidden. It is a
   heading, not a control — the ring is meaningful to assistive tech via the
   focus event, and only noise on screen, where a whole new question appearing
   is already unmissable. */
.finder-q-text:focus { outline: none; }

.finder-q-help {
  display: block;
  font-size: var(--fs-small);
  color: var(--muted);
  max-width: 52ch;
  /* `pretty`, not `balance`: it only pulls back the last line, which is the
     actual complaint ("Nothing else / changes."). `balance` re-flows every line
     to equal length and leaves short help text visibly ragged. */
  text-wrap: pretty;
}

/* Two, three or five options should not all stack in one column on a wide
   screen — a five-item list reads as a form, a two-item list as a decision. */
.finder-opts { display: grid; gap: 0.75rem; }
@media (min-width: 720px) {
  .finder-opts--2 { grid-template-columns: 1fr 1fr; }
  .finder-opts--3 { grid-template-columns: repeat(3, 1fr); }
  .finder-opts--4 { grid-template-columns: 1fr 1fr; }
  /* Five stacked cards plus a headline and the actions do not fit a laptop
     viewport, and a question that scrolls is not one decision on one screen.

     The fifth card does NOT span the row. It did, and a full-width block among
     four half-width ones reads as recommended — Reconnect was being given
     visual weight the scoring does not give it. It sits in the left column and
     the last cell stays empty; an uneven grid is the honest shape of five
     options. */
  .finder-opts--5 { grid-template-columns: 1fr 1fr; }
}

/* Every card the same height. The `li` stretches to the grid row but the
   bordered box is the `label` inside it, so a short answer like "Gentle" drew
   a visibly smaller box than its neighbours. */
.finder-opts > li { display: grid; }
.finder-opts label { height: 100%; }
.finder-opts input {
  position: absolute;
  opacity: 0;
  width: 1px; height: 1px;
  pointer-events: none;
}
.finder-opts label {
  display: grid;
  gap: 0.3rem;
  border: 1px solid var(--hairline-l);
  border-radius: 2px;
  padding: 1.05rem 1.3rem;
  cursor: pointer;
  min-height: 44px;
  background: var(--paper);
  transition: border-color .22s var(--ease-out-quint),
              background-color .22s var(--ease-out-quint);
}
/* Hover lifts and warms — gated, because a touch device that reports hover
   incorrectly leaves cards stuck in a raised state. Transform is on the label
   itself rather than a wrapper so the whole card moves as one object. */
@media (hover: hover) and (pointer: fine) {
  .finder-opts label:hover {
    border-color: var(--gold-deep);
    background: var(--paper-2);
    transform: translateY(-2px);
    box-shadow: 0 6px 18px -12px var(--ink);
  }
}
.finder-opts input:checked + label {
  border-color: var(--gold-deep);
  background: var(--paper-2);
  box-shadow: inset 3px 0 0 var(--gold-deep);
}
/* Selection has to read as committed, not just hovered — the two states use
   the same warmed background, so the mark is what separates them. */
.finder-opts input:checked + label::after {
  content: "";
  position: absolute;
  top: 1.05rem;
  right: 1.1rem;
  width: 9px;
  height: 5px;
  border-left: 1.5px solid var(--gold-deep);
  border-bottom: 1.5px solid var(--gold-deep);
  transform: rotate(-45deg);
}
.finder-opts label { position: relative; }
.finder-opts input:focus-visible + label {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}
.opt-compass {
  font-family: var(--sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-deep);
}
.opt-label { font-size: 1.1rem; font-family: var(--serif-display); }
.opt-note { font-size: 0.9rem; color: var(--muted); }

.finder-actions {
  display: flex;
  gap: 0.9rem;
  margin-top: 2.5rem;
  align-items: center;
}

/* ══════════════════════════════════════════════════════════════════════════
   APP MODE — /journey stops being a page
   --------------------------------------------------------------------------
   Everything here is keyed on html[data-finder="app"], stamped by an inline
   script in <head> (lib/page.js) so it applies on the FIRST PAINT. It cannot
   move to a deferred stylesheet or script: the whole point is that the website
   is never visible on this route, and a promotion that happens one frame late
   shows the header and footer and then snatches them away.

   Without that attribute — no JavaScript — none of this applies and /journey
   is an ordinary destination page with the six villages explained in full.
   That fallback is the reason the markup is structured this way; see the note
   in lib/components.js finderApp().

   THESE STYLES LIVE IN site.css ON PURPOSE. Do not move them to a css/finder
   stylesheet: lib/page.js drops site.css entirely for any page declaring its
   own `styles` array, so giving /journey a stylesheet would silently strip the
   whole consumer component sheet from it.
   ══════════════════════════════════════════════════════════════════════════ */

/* The website goes away. */
html[data-finder="app"] .site-header,
html[data-finder="app"] .site-footer,
html[data-finder="app"] #top,
html[data-finder="app"] #other-ways,
html[data-finder="app"] #begin { display: none; }

/* THE PAGE SCROLLS. IT MUST NOT BE LOCKED.
   This block used to set `overflow: hidden` on html and body, on the theory
   that the shell owned the viewport and every state fit inside it. Two of the
   three states do. The RESULT DOES NOT — measured at 1280x860 it is 2247px
   tall in a 704px viewport — and the lock made everything past the first
   screen unreachable: the experiences, Eclipse, the advisor CTA and the entire
   email capture.

   The inner `overflow-y: auto` did not save it either, and the reason is worth
   keeping. `min-height: 100dvh` lets the section GROW to fit its content
   instead of constraining it, so `.wrap` grew with it (clientHeight 2173 =
   scrollHeight 2173) and never overflowed — an auto-scroll container with
   nothing to scroll.

   So: no lock, no nested scroller. The section keeps `min-height: 100dvh`, so
   a question still fills exactly one screen and has nothing to scroll, while
   the result is simply taller than the viewport and the page scrolls the way
   pages do. Fewer moving parts than a flex column wrapping a scroll container,
   which is what broke.

   100dvh, never 100vh: on iOS the address bar makes vh taller than what is
   actually visible, so the Continue button lands under the chrome. */
/* Anything scrolled into view has to clear the sticky bar, or it lands
   underneath it. Covers focus(), in-page anchors and back-forward restore. */
html[data-finder="app"] { scroll-padding-top: 6rem; }
html[data-finder="app"] .finder-section {
  position: relative;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: 0;
}
html[data-finder="app"] .finder-section > .wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* The section head is page furniture — the tool bar says where you are. */
html[data-finder="app"] .finder-section > .wrap > .section-head { display: none; }

/* THE GROUND GOES DEEPER SO THE CARD CAN LIFT OFF IT.
   The tool used to be paper on paper, which is why it still read as a page.
   The shell now sits on a deeper ground and each state is a raised card, so
   the thing being decided is visibly an object rather than a column of text.
   Launch, questions and result all use the same card, so the three states read
   as one surface rather than three screens. */
html[data-finder="app"] .finder-section {
  background: color-mix(in srgb, var(--ink) 5%, var(--paper-2));
}
html[data-finder="app"] .finder-card {
  background: var(--paper);
  border: 1px solid var(--hairline-l);
  border-radius: 3px;
  box-shadow: 0 18px 48px -34px var(--ink);
  padding: clamp(1.6rem, 3.4vw, 3rem);
  margin-inline: auto;
  width: 100%;
  max-width: 62rem;
}

html[data-finder="app"] .finder-app {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(1.5rem, 4vh, 3rem);
}
/* The result is read rather than decided on, so it starts at the top — and it
   needs air under the sticky bar, which the section's `padding: 0` removed. */
html[data-finder="app"] .finder-app:has(.finder-result:not([hidden])) {
  justify-content: flex-start;
  padding-top: clamp(2rem, 5vh, 3.5rem);
}
/* Wider than the 760px reading measure: this is a control surface, not a
   column of prose, and the extra width is what lets the option cards sit two
   across instead of overflowing the screen. */
html[data-finder="app"] .finder-form { max-width: 62rem; }
/* Tighter vertical rhythm than the page version — every line spent on the
   question is a line the options do not have. */
html[data-finder="app"] .finder-q legend { margin-bottom: 1.5rem; }
html[data-finder="app"] .finder-q-n { margin-bottom: 0.6rem; }
html[data-finder="app"] .finder-q-text { font-size: clamp(1.9rem, 3.6vw, 3rem); }
html[data-finder="app"] .finder-actions { margin-top: 1.8rem; }
/* The form is the card, so it must not also carry the narrower form measure. */
html[data-finder="app"] .finder-form.finder-card { max-width: 62rem; }

/* — the wash —
   The environmental response, in one element. Its colour is set to the
   leading village's accent by js/journey.js; it is kept faint enough to read
   as the room changing temperature rather than as a picture. */
.finder-wash {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background:
    radial-gradient(120% 80% at 50% 0%, var(--wash, transparent) 0%, transparent 62%);
  transition: opacity .9s var(--ease-out-quint),
              background-color .9s var(--ease-out-quint);
}
.finder-wash.is-on { opacity: 0.13; }
html:not([data-finder="app"]) .finder-wash { display: none; }

/* — tool bar — */
/* The strip: full-bleed background and rule, no horizontal padding of its own.
   Everything inside is positioned by .wrap, exactly as the body is. */
.finder-bar {
  /* Sticky, now that the page scrolls: on a long result the step rail and the
     way out must not scroll away with the content. */
  position: sticky;
  top: 0;
  z-index: 3;
  padding-block: 0.9rem;
  border-bottom: 1px solid var(--hairline-l);
  background: var(--paper);
}
.finder-bar-inner {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 4vw, 2.5rem);
}
.finder-bar-brand {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
}
.finder-exit {
  margin-left: auto;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--sans);
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0.5rem;
  min-height: 44px;
}
.finder-exit:hover { color: var(--ink); }

/* — the step rail —
   Named steps, not "question 2 of 4": the name says what is being asked, the
   position says how much is left. Deliberately NOT .finder-steps — that class
   belongs to the homepage 1-2-3 list and is driven by motion.js. */
.finder-rail {
  display: flex;
  gap: clamp(0.6rem, 2vw, 1.4rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.finder-rail li {
  font-family: var(--sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0.45;
  padding-bottom: 3px;
  border-bottom: 1.5px solid transparent;
  transition: opacity .35s var(--ease-out-quint), color .35s var(--ease-out-quint);
}
.finder-rail li.is-done { opacity: 0.8; color: var(--ink); }
.finder-rail li.is-active {
  opacity: 1;
  color: var(--gold-deep);
  border-bottom-color: var(--gold-deep);
}

/* — launch — */
.finder-launch { max-width: 46rem; margin-inline: auto; text-align: center; }
.finder-launch-title {
  font-size: var(--fs-hero);
  line-height: 1.1;
  margin: 0.5em 0 0.4em;
  text-wrap: balance;
}
.finder-launch-lead {
  font-size: var(--fs-lead);
  color: var(--muted);
  max-width: 40rem;
  margin-inline: auto;
  text-wrap: pretty;
}
.finder-launch-actions { margin: clamp(1.8rem, 4vh, 2.6rem) 0 1.1rem; }
.finder-launch-alt { font-family: var(--sans); font-size: 0.84rem; color: var(--muted); }
.finder-launch-alt a { color: var(--gold-deep); }
/* `margin-inline: auto` is the load-bearing part. chrome.css styles
   .finder-privacy alongside .section-footnote and .cta-note, which hands it
   `max-width: var(--measure)` and `margin: 2rem 0 0` — so inside the centred
   launch block it sat left of everything above it (measured centre 481 against
   the block's 627). The max-width is fine; the missing auto margin was not. */
.finder-privacy {
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--muted);
  margin-inline: auto;
}

/* — shaping —
   Four beats settling. The sequence times the transition; it is not a
   simulated computation, and it never runs on a restored shared result. */
.finder-shaping { display: grid; place-items: center; flex: 1; }
.shaping-beats {
  display: grid;
  gap: 0.7rem;
  margin: 0; padding: 0;
  list-style: none;
  text-align: center;
}
.shaping-beats li {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0;
}
.finder-shaping.is-running .shaping-beats li {
  animation: shaping-beat .95s var(--ease-out-quint) forwards;
}
.finder-shaping.is-running .shaping-beats li:nth-child(2) { animation-delay: .16s; }
.finder-shaping.is-running .shaping-beats li:nth-child(3) { animation-delay: .32s; }
.finder-shaping.is-running .shaping-beats li:nth-child(4) { animation-delay: .48s; }
@keyframes shaping-beat {
  from { opacity: 0; transform: translateY(6px); }
  60%  { opacity: 1; }
  to   { opacity: 0.55; transform: none; }
}

/* — the result head —
   Copy left, the Compass ring right. Two columns only where there is room for
   the ring to be legible; below that it drops under the copy. */
html[data-finder="app"] .result-head {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3rem);
  /* Centred against each other — the copy reads level with the ring rather
     than hanging off its top edge.

     This was briefly `start`, because centring appeared to slide RESTORE (the
     12 o'clock direction) under the sticky bar. That was a misread: the ring
     is the taller item, so it defines the row and its own top edge cannot rise
     above it. The real cause was the result having no top padding and
     `resultEl.focus()` scrolling the head under the bar — both fixed since, so
     centring is safe. */
  align-items: center;
  max-width: none;
}
@media (min-width: 900px) {
  html[data-finder="app"] .result-head { grid-template-columns: 1fr 19rem; }
}
html[data-finder="app"] .result-head-copy { max-width: var(--measure); }
html[data-finder="app"] .result-head { margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }

/* THE RING IS DRAWN FOR A DARK GROUND. IT SITS ON PAPER HERE.
   .compass-label is `fill: var(--paper)` and .compass-ring strokes
   --hairline-d, both tuned for section--ink on the homepage. Dropped onto the
   result card unchanged, the labels are paper on paper — an empty circle. */
.result-compass-figure { margin-inline: auto; max-width: 22rem; width: 100%; }

/* NO ENTRANCE ANIMATION HERE.
   The homepage compass draws itself as you scroll to it: rings stroke on,
   labels fade in on a stagger driven by --i, ticks scale up. All of that is
   gated on body[data-motion="ready"] and only settles once .is-drawn runs its
   keyframes. On the result the compass is not being scrolled to — it is part
   of an answer the visitor is already reading — so it must arrive finished.
   Without these it appears as a bare centre mark and a half-drawn arc. */
body[data-motion="ready"] .result-compass-figure .compass-ring,
body[data-motion="ready"] .result-compass-figure .compass.is-drawn .compass-ring {
  stroke-dashoffset: 0;
  animation: none;
}
body[data-motion="ready"] .result-compass-figure .compass-ring--inner {
  stroke-dasharray: 3 7;
}
body[data-motion="ready"] .result-compass-figure .compass-label,
body[data-motion="ready"] .result-compass-figure .compass-center {
  opacity: 1;
  animation: none;
}
body[data-motion="ready"] .result-compass-figure .compass-tick {
  opacity: .75;
  transform: none;
  animation: none;
}
.result-compass-figure .compass-label { fill: var(--ink); font-size: 24px; }
.result-compass-figure .compass-ring { stroke: var(--hairline-l); }
.result-compass-figure .compass-center { fill: var(--muted); }
.result-compass-figure .compass.is-probed .compass-center { fill: var(--muted); }
/* Lit directions take gold; the rest recede rather than disappear — a person
   should still be able to read the whole compass they were measured against. */
.result-compass-figure .compass.is-probed .compass-point:not(.is-active) .compass-label { fill-opacity: .34; }
.result-compass-figure .compass-point.is-active .compass-label { fill: var(--gold-deep); fill-opacity: 1; }
.result-compass-figure .compass-hit { pointer-events: none; }

/* — the Compass readout —
   Directions named, never scored. See the note in js/journey.js for why there
   are no dots here. */
.result-compass { margin-top: clamp(1.6rem, 3vw, 2.2rem); }
.result-compass-label {
  font-family: var(--sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-deep);
  margin: 0 0 0.6rem;
}
.result-compass-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin: 0; padding: 0;
  list-style: none;
}
.result-compass-list li {
  font-family: var(--sans);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  border: 1px solid var(--hairline-l);
  border-radius: 999px;
  padding: 0.32rem 0.8rem;
}
/* Where the ring is showing, the chips say the same thing twice — and its own
   label sits at the centre of the diagram, so the heading above them is a
   third copy. Both are hidden from sight and kept in the accessibility tree,
   because an SVG of eight positioned words cannot be read as a list. */
@media (min-width: 900px) {
  html[data-finder="app"] .result-compass-list,
  html[data-finder="app"] .result-compass-label {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  html[data-finder="app"] .result-compass { margin-top: 0; }
}

/* — result cards —
   The closest match is the recommendation and reads like one: its accent runs
   the full top edge, the card is lifted, and the runners-up sit back. */
html[data-finder="app"] .result-villages {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}
@media (min-width: 860px) {
  html[data-finder="app"] .result-villages { grid-template-columns: repeat(3, 1fr); }
}
.result-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--paper);
  border: 1px solid var(--hairline-l);
  border-radius: 3px;
}
/* THE RECOMMENDATION HAS TO WIN AT A GLANCE.
   The first attempt was too polite to work: a 45%-alpha accent border and a
   background 3% off the others. Measured side by side the lead was
   `srgb .958 .951 .920` against `.984 .973 .945` — a difference you can prove
   but cannot see, so all three cards read as equals with a caption.

   Now the accent is used at full strength in three places: a solid edge, a
   filled label, and real elevation. The runners-up are pushed back rather than
   left alone, because ranking is a comparison — making one louder does half
   the job. */
.result-card--lead {
  border: 1.5px solid var(--v);
  background: color-mix(in srgb, var(--v) 8%, var(--paper));
  box-shadow: 0 22px 50px -24px color-mix(in srgb, var(--v) 75%, var(--ink));
}
.result-card--lead::before {
  content: "";
  display: block;
  height: 6px;
  background: var(--v);
}
/* The rank stops being a caption and becomes a badge — reversed out of the
   village's own accent, so it reads before any of the words around it. */
.result-card--lead .result-rank {
  display: inline-block;
  background: var(--v);
  color: var(--paper);
  font-weight: 600;
  letter-spacing: 0.16em;
  padding: 0.34rem 0.7rem;
  border-radius: 2px;
}
/* The hairline under the rank was separating a label from a heading. With a
   filled badge doing that job it is one line too many. */
.result-card--lead .village-rule { display: none; }

/* And the other two step back: no shadow, a softer edge, and the card sits a
   shade into the ground rather than on top of it. */
.result-card:not(.result-card--lead) {
  background: color-mix(in srgb, var(--ink) 2%, var(--paper));
  border-color: color-mix(in srgb, var(--ink) 8%, transparent);
}
.result-card-body { padding: clamp(1.1rem, 2vw, 1.5rem); }
/* One aspect ratio across all three. The lead card briefly had a taller frame,
   which pushed its text far below the other two and made the row read as
   broken rather than ranked. The accent edge and the lift do the ranking. */
.result-media { aspect-ratio: 16 / 10; overflow: hidden; background: var(--paper-2); }
.result-media img { width: 100%; height: 100%; object-fit: cover; }

/* Rhythm through the result, so it reads as passages rather than one wall. */
html[data-finder="app"] .result-block,
html[data-finder="app"] .result-eclipse {
  border-top: 1px solid var(--hairline-l);
  padding-top: clamp(2rem, 4vw, 3rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

/* Copy left, the Eclipse mark right. The block's right half was dark nothing;
   the mark fills it with the identity of the thing being offered rather than
   with decoration. Below the breakpoint it drops away entirely — stacked under
   the copy it would just be a logo taking up a screen of phone. */
html[data-finder="app"] .result-eclipse {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
}
html[data-finder="app"] .result-eclipse-mark { display: none; }
@media (min-width: 900px) {
  html[data-finder="app"] .result-eclipse {
    grid-template-columns: 1fr minmax(10rem, 15rem);
  }
  html[data-finder="app"] .result-eclipse-mark {
    display: block;
    justify-self: center;
    width: 100%;
    /* It is a mark, not an illustration — it should register as the Eclipse
       sign without competing with the heading next to it. */
    opacity: 0.9;
  }
  html[data-finder="app"] .result-eclipse-mark img {
    display: block;
    width: 100%;
    height: auto;
  }
}

/* Reduced motion: the wash still changes colour — it carries meaning — but
   nothing travels, and the shaping sequence does not run at all (js skips it). */
@media (prefers-reduced-motion: reduce) {
  .finder-wash { transition: none; }
  .finder-opts label { transition: none; }
  @media (hover: hover) and (pointer: fine) {
    .finder-opts label:hover { transform: none; }
  }
  .shaping-beats li { opacity: 0.55; animation: none; }
}

@media (max-width: 640px) {
  /* Gap belongs to the inner row now, and the horizontal inset comes from
     .wrap's gutter — the same one the content below uses. */
  .finder-bar-inner { gap: 0.75rem; }
  .finder-bar-brand { display: none; }   /* the rail is what matters on a phone */
  .finder-rail { gap: 0.5rem; }
  .finder-rail li { font-size: 0.6rem; letter-spacing: 0.12em; }
}

/* — result — */
.finder-result:focus { outline: none; }
.result-head { max-width: var(--measure); margin-bottom: clamp(2rem, 4vw, 3rem); }
.result-head h3 {
  font-size: var(--fs-h2);
  margin-bottom: 0.3em;
}
.result-intention {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-deep);
  margin-bottom: 1rem;
}
.result-villages { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.25rem, 2.5vw, 2rem); }
.result-rank {
  font-family: var(--sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  display: block;
  margin-bottom: 0.8rem;
}
.result-anchors {
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--hairline-l);
}
.result-anchors b {
  display: block;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.66rem;
  color: var(--v-ink);
  margin-bottom: 0.5em;
}

.result-block { margin-top: clamp(3rem, 6vw, 5rem); }
.result-block > h4 {
  font-family: var(--sans);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--gold-deep);
  margin-bottom: 1.6rem;
}
.result-exp { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem 2.5rem; }
.result-exp li { border-top: 1px solid var(--hairline-l); padding-top: 1rem; }
.result-exp h5 { font-family: var(--serif-display); font-size: 1.08rem; font-weight: 400; margin: 0 0 0.35em; }
.result-exp p { font-size: 0.92rem; color: var(--muted); margin: 0; }

.result-eclipse {
  background: var(--eclipse-midnight);
  color: var(--eclipse-sand);
  border-radius: 2px;
  padding: clamp(1.75rem, 4vw, 3rem);
  margin-top: clamp(3rem, 6vw, 5rem);
}
.result-eclipse h4 {
  font-family: var(--serif-display);
  font-size: var(--fs-h3);
  color: var(--eclipse-sand);
  margin-bottom: 0.4em;
}
.result-eclipse p { color: rgba(232,223,210,.78); max-width: 60ch; }

.result-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--hairline-l);
}

.capture { display: grid; gap: 0.75rem; margin-top: 2rem; max-width: 520px; }
.capture label {
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 600;
}
.capture-row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.capture input[type="email"] {
  flex: 1 1 240px;
  min-height: 44px;
  padding: 0.7rem 0.95rem;
  border: 1px solid var(--hairline-l);
  border-radius: 2px;
  font-family: var(--sans);
  font-size: 0.95rem;
  background: var(--paper);
  color: var(--ivory-ink);
}
.capture-note { font-family: var(--sans); font-size: 0.8rem; color: var(--muted); margin: 0; }
/* Sits below the input, one step quieter than the note above it. Fine print
   that has to be readable — it is a consent statement, so it stays at 0.74rem
   rather than shrinking to the 0.68rem the eye expects of a disclaimer, and
   keeps `--muted` rather than fading further. */
.capture-consent {
  font-family: var(--sans);
  font-size: 0.74rem;
  line-height: 1.5;
  color: var(--muted);
  margin: 0;
  /* No max-width. `.capture` is already clamped to 520px, and giving this a
     tighter measure than the note above it left the block visibly ragged —
     two paragraphs in the same card wrapping at different widths. */
}
.capture-status { font-family: var(--sans); font-size: 0.86rem; margin: 0; }
.capture-status[data-state="ok"] { color: var(--v-ocean, var(--teal)); }
.capture-status[data-state="error"] { color: var(--movement, #A33F28); }


/* ══════════════════════════════════════════════════════════════════════════
   ECLIPSE FEATURE
   ══════════════════════════════════════════════════════════════════════════ */
.eclipse-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: start;
}
.eclipse-title { color: var(--eclipse-sand); }
.eclipse-copy p { color: rgba(232,223,210,.78); }
.eclipse-copy strong { color: var(--eclipse-sand); font-weight: 400; letter-spacing: 0.06em; }

.arc-label {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--eclipse-copper-l);
  margin-bottom: 1.75rem;
}
.arc-list { position: relative; padding-left: 1.9rem; }
.arc-list::before {
  content: '';
  position: absolute;
  left: 5px; top: 8px; bottom: 8px;
  width: 1px;
  background: linear-gradient(180deg, var(--eclipse-copper), rgba(155,116,85,.15));
}
.arc-list li {
  position: relative;
  padding-bottom: 1.5rem;
}
.arc-list li:last-child { padding-bottom: 0; }
.arc-list li::before {
  content: '';
  position: absolute;
  left: -1.9rem; top: 7px;
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1px solid var(--eclipse-copper);
  background: var(--eclipse-midnight);
}
.arc-key {
  display: block;
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--eclipse-sand);
}
.arc-sub {
  display: block;
  font-size: 0.92rem;
  color: rgba(232,223,210,.62);
  margin-top: 0.25em;
}


/* ══════════════════════════════════════════════════════════════════════════
   ECLIPSE PAGE COMPONENTS
   ══════════════════════════════════════════════════════════════════════════ */
.section--eclipse h2,
.section--eclipse h3 { color: var(--eclipse-sand); }
.section--eclipse p,
.section--eclipse li { color: rgba(232, 223, 210, .78); }
.section--eclipse .lead { color: rgba(232, 223, 210, .72); }
.section--eclipse .eyebrow { color: var(--eclipse-copper-l); }
.section--eclipse .section-footnote { color: rgba(232, 223, 210, .62); }
.pullquote--eclipse {
  color: var(--eclipse-sand);
  max-width: min(34rem, 100%);
  border-top: 1px solid rgba(155, 116, 85, .5);
  padding-top: 1.6rem;
}
.section--eclipse .photo--placeholder .photo-frame { border-color: rgba(232,223,210,.3); }

/* — comparison — */
.cmp {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
}
.cmp-col {
  border: 1px solid rgba(232, 223, 210, .16);
  border-radius: 2px;
  padding: clamp(1.4rem, 3vw, 2.2rem);
}
.cmp-col--ours {
  border-color: var(--eclipse-copper);
  background: rgba(155, 116, 85, .08);
}
.cmp-col h3 {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  margin-bottom: 1.4rem;
}
.cmp-col--ours h3 { color: var(--eclipse-copper-l); }
.cmp-col ul { display: grid; gap: 0.85rem; }
.cmp-col li {
  position: relative;
  padding-left: 1.4rem;
  font-size: 0.97rem;
}
.cmp-col li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.72em;
  width: 7px; height: 1px;
  background: rgba(232, 223, 210, .45);
}
.cmp-col--ours li::before { background: var(--eclipse-copper-l); width: 10px; }

/* — the arc — */
.arc-track {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0;
  counter-reset: arc;
}
.arc-track li {
  position: relative;
  padding: 2.6rem 1rem 0 0;
  border-top: 1px solid rgba(155, 116, 85, .35);
}
.arc-track li::before {
  content: '';
  position: absolute;
  top: -5px; left: 0;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--eclipse-midnight);
  border: 1px solid var(--eclipse-copper);
}
.arc-n {
  display: block;
  font-family: var(--sans);
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  color: var(--eclipse-copper-l);
  margin-bottom: 0.6rem;
}
.arc-track .arc-key {
  font-family: var(--sans);
  font-size: 0.84rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--eclipse-sand);
}
.arc-track .arc-sub {
  display: block;
  font-size: 0.88rem;
  color: rgba(232, 223, 210, .6);
  margin-top: 0.4em;
}

/* — day plan — */
/* — the five days, as a vertical timeline —
   Selectors are `> li` throughout: each day contains its own <ul> of items and
   must not inherit the track's own rules. */
.day-track { display: grid; }
.day-track > li {
  position: relative;
  display: grid;
  /* 4.5rem fits "DAY 1" and no more. At 8rem the rail left ~70px of dead
     gutter between the label and the day it belongs to, which read as a
     column break rather than a timeline. */
  grid-template-columns: minmax(0, 4.5rem) minmax(0, 1fr);
  gap: clamp(1rem, 2vw, 1.75rem);
  padding: 0 0 clamp(2.25rem, 4vw, 3.25rem) clamp(1.75rem, 3vw, 2.5rem);
  border-left: 1px solid rgba(155, 116, 85, .22);
}
/* Nothing follows the last day, so its spine would be a line to nowhere. */
.day-track > li:last-child { border-left-color: transparent; padding-bottom: 0; }
.day-track > li::before {
  content: '';
  position: absolute;
  left: -5px; top: 0.15em;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--eclipse-midnight);
  border: 1px solid var(--eclipse-copper);
}
.day-rail { padding-top: 0.05em; }
.day-n {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--eclipse-copper-l);
  margin-bottom: 0.7rem;
}
.day-n { margin-bottom: 0; }
.day-track h3 { font-size: 1.16rem; margin-bottom: 0.7em; }
.day-track ul { display: grid; gap: 0.55rem; }
.day-track ul li { font-size: 0.92rem; color: rgba(232, 223, 210, .7); }

/* — signature experience tiles — */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: rgba(232, 223, 210, .14);
  border: 1px solid rgba(232, 223, 210, .14);
}
.tile-grid li {
  background: var(--eclipse-midnight);
  padding: clamp(1.4rem, 3vw, 2rem);
  transition: background-color .3s var(--ease-out-quint);
}
.tile-grid li:hover { background: var(--eclipse-deep); }
.tile-grid h3 {
  font-size: 1.14rem;
  margin-bottom: 0.35em;
}
.tile-grid p { font-size: 0.9rem; color: rgba(232, 223, 210, .6); margin: 0; }

/* — tiles carrying a photograph —
   The media variant keeps the same 1px-gap grid so the two states can be mixed
   while art is still arriving: an illustrated tile and a typographic tile sit
   side by side without the grid breaking. */
.tile-grid--media li { display: flex; flex-direction: column; padding: 0; }
.tile-grid--media .tile-copy { padding: clamp(1.4rem, 3vw, 2rem); }
/* The copper hairline that draws across each tile on arrival is a ::before on
   the li. `.tile-media` is positioned and comes later in the DOM, so without
   this it paints over the hairline and the animation silently disappears the
   moment a tile gets a photograph. */
body[data-motion="ready"] .tile-grid--media li::before { z-index: 2; }
.tile-media {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--eclipse-deep);
}
.tile-media picture { display: block; width: 100%; height: 100%; }
.tile-media img,
.tile-media video {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.tile-media video { position: absolute; inset: 0; opacity: 0; transition: opacity .6s var(--ease-out-quint); }
.tile-media video.is-ready { opacity: 1; }

/* The village this experience belongs to, in that village's own accent. This
   is the whole point of the tag — Eclipse reading as the six villages
   sequenced, rather than as a product parked beside them. */
.tile-village {
  display: block;
  font-family: var(--sans);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-bottom: 0.7em;

  /* Village accents were chosen to sit on cream, and the darkest of them
     (Longevity #2E5E5C) lands at 4.44:1 on the Eclipse midnight — under AA for
     text this size.

     Blending toward paper rather than filtering: `brightness()` is a guess that
     has to be re-tuned every time an accent changes, and it lifts the already-
     bright accents further than they need. A fixed 55% mix is deterministic,
     preserves hue, and puts the whole six-accent range between 6.9:1 and
     9.4:1 — verified against rendered pixels, not against the token.

     The first declaration is the fallback for engines without color-mix; it
     resolves to the known-good copper rather than to an unlifted accent. */
  color: var(--eclipse-copper-l);
  color: color-mix(in srgb, var(--v, var(--eclipse-copper-l)) 55%, var(--paper));
}

.tile-placeholder {
  height: 100%;
  display: flex; flex-direction: column; justify-content: center;
  gap: .5rem;
  padding: clamp(1.2rem, 3vw, 1.8rem);
  background: var(--eclipse-deep);
  border-bottom: 1px solid rgba(232, 223, 210, .14);
}
.tile-placeholder .photo-flag {
  font-family: var(--sans); font-size: .6rem; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--eclipse-copper-l); margin: 0;
}
.tile-placeholder .photo-dir {
  font-size: .82rem; line-height: 1.45;
  color: rgba(232, 223, 210, .55); margin: 0;
}

/* — the arc's atmospheric band — */
.arc-band {
  position: relative;
  aspect-ratio: 21 / 9;
  overflow: hidden;
  margin-bottom: clamp(2.5rem, 6vw, 4rem);
  background: var(--eclipse-deep);
}
.arc-band picture { display: block; width: 100%; height: 100%; }
.arc-band img,
.arc-band video { width: 100%; height: 100%; object-fit: cover; display: block; }
.arc-band video { position: absolute; inset: 0; opacity: 0; transition: opacity .6s var(--ease-out-quint); }
.arc-band video.is-ready { opacity: 1; }
.arc-band .hero-placeholder { position: static; height: 100%; }

/* — page header carrying full-bleed media —
   /eclipse was the only page whose first screen was flat colour. */
/* Given a photograph, the header earns height. Not full-viewport — that is the
   homepage's move and repeating it would make Eclipse read as a second front
   door rather than as a journey inside the site. 66vh capped at 620px keeps the
   type on the first screen alongside it.

   The cap also keeps the 21:9 source from being upscaled: at 2000px wide the
   frame is 857px tall, so 620px is always covering, never stretching. */
.page-header--media {
  position: relative;
  isolation: isolate;
  min-height: min(66vh, 620px);
  display: flex;
  align-items: center;
}
.page-header--media > .wrap { position: relative; z-index: 2; width: 100%; }
.page-header-media { position: absolute; inset: 0; z-index: 0; }
.page-header-media picture { display: block; width: 100%; height: 100%; }
.page-header-media img,
.page-header-media video {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.page-header-media video { position: absolute; inset: 0; opacity: 0; transition: opacity .8s var(--ease-out-quint); }
.page-header-media video.is-ready { opacity: 1; }
/* The art-direction card is a working state, not a bug — but it is what Duncan
   looks at while deciding what to shoot, so it gets composed rather than left
   jammed in a corner: the brief sits centred, at readable width, under the type
   it will replace. */
.page-header-media .hero-placeholder {
  position: static;
  height: 100%;
  justify-content: flex-end;
  align-items: center;
  text-align: center;
  padding-bottom: clamp(2rem, 5vw, 3.5rem);
}
.page-header-media .hero-placeholder .photo-dir {
  max-width: 40rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: rgba(251, 248, 241, .5);
}
.page-header-media .hero-placeholder .photo-flag { color: var(--gold); }
/* Type sits on photography here, so the scrim is structural, not decorative:
   it is what guarantees the headline's contrast whatever the frame turns out
   to be. Stronger at the bottom, where the lead and meta row sit. */
.page-header-veil {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, rgba(10, 16, 26, .60) 0%, rgba(10, 16, 26, .72) 55%, rgba(10, 16, 26, .88) 100%);
}
.page-header--media h1,
.page-header--media .lead { text-shadow: 0 1px 24px rgba(10, 16, 26, .55); }

@media (max-width: 900px) {
  .tile-grid--media .tile-media { aspect-ratio: 16 / 10; }
}

/* — fit checklist + safety note — */
.checklist-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  /* Centred, not top-aligned. The safety note is a third the height of the
     six-item list beside it, so `start` pinned it to the top and left a void
     below that read as section padding — which is why the gap into the next
     section looked twice as large as it is. */
  align-items: center;
}
.check-list { display: grid; gap: 1rem; }
.check-list li {
  position: relative;
  padding-left: 1.9rem;
  font-size: 1.02rem;
}
.check-list li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.44em;
  width: 11px; height: 11px;
  border: 1px solid var(--eclipse-copper);
  border-radius: 2px;
}
/* The safety note is load-bearing, not decoration. It stays visually distinct
   so it cannot be skimmed past as more marketing copy. */
.safety-note {
  border: 1px solid rgba(232, 223, 210, .2);
  border-left: 3px solid var(--eclipse-copper);
  border-radius: 2px;
  padding: clamp(1.3rem, 3vw, 1.8rem);
  background: rgba(232, 223, 210, .04);
}
.safety-note h3 {
  font-family: var(--sans);
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--eclipse-copper-l);
  margin-bottom: 0.9em;
}
.safety-note p { font-size: 0.94rem; margin: 0; }


/* ══════════════════════════════════════════════════════════════════════════
   SPLIT
   ══════════════════════════════════════════════════════════════════════════ */
.split {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(2.5rem, 6vw, 5.5rem);
  align-items: center;
}
.split--flip .split-media { order: 2; }
.split-copy .section-head { margin-bottom: 1.75rem; }

.tick-list { margin-top: 1.5rem; display: grid; gap: 0.7rem; }
.tick-list li {
  position: relative;
  padding-left: 1.5rem;
  color: var(--muted);
}
.tick-list li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.68em;
  width: 8px; height: 1px;
  background: var(--gold-deep);
}


/* ══════════════════════════════════════════════════════════════════════════
   EXPERTISE
   ══════════════════════════════════════════════════════════════════════════ */
.expertise-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.75rem, 3.5vw, 3rem);
}
.expertise-grid li { border-top: 1px solid var(--hairline-d); padding-top: 1.4rem; }
.expertise-grid h3 {
  font-family: var(--sans);
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--paper);
  margin-bottom: 0.8em;
}
.expertise-grid p { color: var(--mist); font-size: 0.95rem; margin: 0; }


/* ══════════════════════════════════════════════════════════════════════════
   CREDIBILITY
   ══════════════════════════════════════════════════════════════════════════ */
.credibility { padding-block: clamp(3rem, 6vw, 4.5rem); border-top: 1px solid var(--hairline-l); }
/* This section draws its own top rule, so it is the one case that must keep
   its top padding when it follows another paper section. `.section--paper +
   .section--paper { padding-top: 0 }` in chrome.css exists to stop two light
   bands stacking their padding — correct in general, but here it left the
   hairline sitting 1px above "IN COLLABORATION WITH" while 154px of the
   previous section's padding floated above it. More space above the rule than
   below is right: the rule separates from what came before and binds to what
   follows. One pixel below it is not. */
.section--paper + .section--paper.credibility { padding-top: clamp(3rem, 6vw, 4.5rem); }
.cred-label {
  font-family: var(--sans);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
  margin: 0 0 1.5rem;
}
.cred-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem clamp(2rem, 5vw, 4rem);
}
.cred-list li {
  font-family: var(--serif-display);
  font-size: clamp(1rem, 1.6vw, 1.25rem);
  color: var(--ivory-ink);
}
.credibility .section-footnote { text-align: center; margin-inline: auto; }


/* ══════════════════════════════════════════════════════════════════════════
   PATHWAY (/advisors)
   ══════════════════════════════════════════════════════════════════════════ */
.path-list {
  display: grid;
  gap: 0;
  counter-reset: path;
  max-width: min(56rem, 100%);
}
.path-step {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  gap: 1.5rem;
  padding: clamp(1.5rem, 3vw, 2.1rem) 0;
  border-top: 1px solid var(--hairline-l);
  position: relative;
}
.path-step:last-child { border-bottom: 1px solid var(--hairline-l); }
.path-n {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--gold-deep);
  padding-top: 0.3em;
}
.path-body h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); margin-bottom: 0.35em; }
.path-body p { color: var(--muted); max-width: 52ch; margin-bottom: 1rem; }

/* A step that does not exist yet: stated, never linked. */
.path-step--future .path-n,
.path-step--future .path-body h3 { opacity: .62; }
.path-status {
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--hairline-l);
  border-radius: 100px;
  padding: 0.35em 0.9em;
}


/* ══════════════════════════════════════════════════════════════════════════
   CONTACT
   ══════════════════════════════════════════════════════════════════════════ */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.75rem, 4vw, 3rem);
}
.contact-grid li { border-top: 1px solid var(--hairline-l); padding-top: 1.4rem; }
.contact-grid h3 {
  font-family: var(--sans);
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  margin-bottom: 0.8em;
}
.contact-grid p { color: var(--muted); font-size: 0.97rem; }
.contact-link {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-deep);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}
.contact-link::after { content: '→'; transition: transform .3s var(--ease-out-quint); }
@media (hover: hover) and (pointer: fine) {
  .contact-link:hover::after { transform: translateX(4px); }
}
.contact-grid .ph {
  font-family: var(--sans);
  font-size: 0.86rem;
}

/* Unconfirmed content, marked rather than invented — the same convention the
   brochure uses. Never remove one of these without a confirmed fact. */
.ph { font-style: italic; opacity: 0.66; }


/* ══════════════════════════════════════════════════════════════════════════
   FINAL CTA
   ══════════════════════════════════════════════════════════════════════════ */
.final-cta {
  position: relative;
  overflow: hidden;
  padding-block: clamp(6rem, 14vw, 11rem);
  text-align: center;
}
.final-cta-media { position: absolute; inset: 0; }
.final-cta-media picture { display: block; width: 100%; height: 100%; }   /* see .hero-media picture */
.final-cta-media img,
.final-cta-media .hero-placeholder { width: 100%; height: 100%; object-fit: cover; }
/* The final CTA cannot reuse the hero's veil. That one is bottom-anchored,
   because the hero's text sits low; this section's text is CENTRED, so the
   hero veil leaves the middle almost unscrimmed and the copy floats over bright
   water. Centred scrim + the same glyph-level text shadow the hero uses. */
.final-cta .hero-veil {
  background:
    radial-gradient(ellipse 78% 62% at 50% 48%,
      rgba(13,20,32,.74) 0%,
      rgba(13,20,32,.52) 55%,
      rgba(13,20,32,.30) 100%),
    linear-gradient(180deg,
      rgba(13,20,32,.42) 0%,
      rgba(13,20,32,.24) 28%,
      rgba(13,20,32,.24) 72%,
      rgba(13,20,32,.52) 100%);
  background-size: 100% 100%, 100% 100%;
  background-position: center, center;
}
.final-cta-inner > *:not(.btn):not(.hero-ctas) {
  text-shadow: 0 2px 18px rgba(8,12,20,.62), 0 2px 18px rgba(8,12,20,.62);
}
/* --mist is a secondary-text token tuned for solid ink; over a photograph it
   lands at 3.93:1 even scrimmed. The coordinate mark is small text, so it takes
   the full paper value here rather than the muted one. */
.final-cta-inner .coords { color: var(--paper); }

.final-cta .wrap { position: relative; z-index: 2; }
.final-cta-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  max-width: min(40rem, 100%);
  margin-inline: auto;
}
.final-cta-inner h2 { margin-top: 1.25rem; }
.final-cta-inner .lead { margin-bottom: 0; }
.final-cta-inner .coords { margin-top: 2rem; }


/* ══════════════════════════════════════════════════════════════════════════
   MOTION
   ----------------------------------------------------------------------------
   Everything here is scoped to body[data-motion="ready"], which js/motion.js
   sets ONLY once it knows something will reveal the content. Reduced-motion
   users and automated agents never get the attribute, so nothing is ever
   hidden from them — no opacity:0 waiting for a script that will not run.

   `.settled` is the failsafe: 3.6s after reveal it force-shows regardless of
   what the transition did. It must stay last and stay !important.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Split headlines: words rise out of masked slots ──────────────────── */

/* THE SLOT MUST BE BIGGER THAN THE LINE BOX, or it cuts the type.

   An `overflow: hidden` inline-block is only as tall as its line box, and at
   `line-height: 1.14` a Caslon Display line box is SHORTER than the face's
   descenders. So the mask sat across the glyphs and sheared the tails off
   every g, y, p and comma in every split headline — and, because italic
   glyphs overhang their advance width, the right edge of italic words too
   ("more than a break?" lost the question mark's tail).

   The fix is to grow the clip region without moving anything: pad the slot out
   past the glyphs, then pull the padding back with matching negative margins.
   The margin box — which is what `vertical-align: bottom` aligns and what the
   line box measures — is unchanged, so metrics, word spacing and baselines are
   all exactly as before. Only the area the browser is willing to paint grows.

   Values are in em so they track the font size through every clamp(). */
body[data-motion="ready"] [data-splitted] .w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding: 0.08em 0.16em 0.3em;
  margin: -0.08em -0.16em -0.3em;
}
body[data-motion="ready"] [data-splitted] .wi {
  display: inline-block;
  /* Clears the PADDED slot, not just the line box. At a plain 108% the word
     would sit inside the new bottom padding and be visible before it rises,
     which is the mask failing open. */
  transform: translateY(calc(108% + 0.4em));
  transition: transform .78s var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 42ms);
  will-change: transform;
}
body[data-motion="ready"] [data-splitted].is-revealed .wi { transform: none; }
/* The hero opens a beat later — the photograph should land first. */
body[data-motion="ready"] .hero [data-splitted].is-revealed .wi {
  transition-delay: calc(180ms + var(--i, 0) * 52ms);
}

/* ── Staggered grids ──────────────────────────────────────────────────── */
body[data-motion="ready"] [data-stagger] > * {
  opacity: 0;
  transform: translateY(16px);
}
body[data-motion="ready"] [data-stagger].is-revealed > * {
  opacity: 1;
  transform: none;
  transition: opacity .58s var(--ease-out-quint), transform .58s var(--ease-out-quint);
  transition-delay: calc(var(--i, 0) * 62ms);
}

/* ── Block-level reveals ──────────────────────────────────────────────── */
body[data-motion="ready"] .reveal-item,
body[data-motion="ready"] .village-block,
body[data-motion="ready"] .split,
body[data-motion="ready"] .safety-note {
  opacity: 0;
  transform: translateY(18px);
}
body[data-motion="ready"] .reveal-item.is-revealed,
body[data-motion="ready"] .village-block.is-revealed,
body[data-motion="ready"] .split.is-revealed,
body[data-motion="ready"] .safety-note.is-revealed {
  opacity: 1;
  transform: none;
  transition: opacity .62s var(--ease-out-quint), transform .62s var(--ease-out-quint);
}

/* ── Parallax layers get their own compositing hint ───────────────────── */
body[data-motion="ready"] .hero-media,
body[data-motion="ready"] .village-block-media picture,
body[data-motion="ready"] .split-media .photo picture {
  will-change: transform;
}

/* ── 3D tilt + accent glare ───────────────────────────────────────────────
   The glare takes the card's own --v accent, so each village catches the
   light in its own colour rather than all six sharing one gold. */
[data-tilt] {
  transform-style: preserve-3d;
  transition: transform .5s var(--ease-out-quint);
  position: relative;
}
[data-tilt]::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .35s var(--ease-out-quint);
  background: radial-gradient(
    340px circle at var(--gx, 50%) var(--gy, 50%),
    color-mix(in srgb, var(--v, #D9A03C) 26%, transparent),
    transparent 62%);
}
@media (hover: hover) and (pointer: fine) {
  [data-tilt]:hover::after { opacity: 1; }
  [data-tilt]:hover { transition-duration: .12s; }
}
.tile-grid [data-tilt]::after {
  background: radial-gradient(
    300px circle at var(--gx, 50%) var(--gy, 50%),
    rgba(198, 152, 120, .20), transparent 62%);
}

/* ── Magnetic CTA: pull toward the pointer, plus a slow sheen ─────────── */
.magnetic {
  transition: transform .38s var(--ease-out-quint),
              background-color .24s var(--ease-out-quint);
  position: relative;
  overflow: hidden;
}
.magnetic::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg,
    transparent 38%,
    rgba(255, 255, 255, .34) 50%,
    transparent 62%);
  transform: translateX(-120%);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .magnetic:hover::after {
    transform: translateX(120%);
    transition: transform .78s var(--ease-out-quint);
  }
}

/* ── Reading-progress thread ──────────────────────────────────────────── */
.read-thread {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: 200;
  transform-origin: left center;
  transform: scaleX(0);
  background: linear-gradient(90deg, var(--teal-bright), var(--gold));
  pointer-events: none;
}

/* ── The failsafe. Must stay last. ────────────────────────────────────── */
body[data-motion="ready"] [data-splitted].settled .wi,
body[data-motion="ready"] [data-stagger].settled > *,
body[data-motion="ready"] .settled {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}


/* ══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   Breakpoints sit where each grid actually breaks, not at device widths.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1080px) {
  .expertise-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 1080px) {
  .prop-grid { grid-template-columns: repeat(2, 1fr); }
  .arc-track { grid-template-columns: repeat(3, 1fr); row-gap: 2.5rem; }
}

@media (max-width: 940px) {
  .lens-grid,
  .village-grid,
  .finder-steps,
  .contact-grid,
  .result-villages { grid-template-columns: repeat(2, 1fr); }
  .compass-wrap,
  .eclipse-grid,
  .split,
  .village-block,
  .exp-groups { grid-template-columns: 1fr; }
  .split--flip .split-media,
  .village-block--flip .village-block-media { order: 0; }
  .village-block--wide .village-block-copy { grid-template-columns: 1fr; }
  .village-block--wide .village-themes-title { margin-top: 1.8rem; }
  .village-block--wide .village-block-media img { aspect-ratio: 16 / 10; }
  .split-media { max-width: 480px; }
  .compass-figure { max-width: 420px; }
  .village-blocks { gap: clamp(3rem, 8vw, 4.5rem); }
  .tile-grid { grid-template-columns: repeat(2, 1fr); }
  .checklist-wrap { grid-template-columns: 1fr; }
}

@media (max-width: 700px) {
  .lens-grid,
  .village-grid,
  .finder-steps,
  .result-villages,
  .result-exp,
  .prop-grid,
  .cmp,
  .contact-grid,
  .tile-grid { grid-template-columns: 1fr; }
  .arc-track { grid-template-columns: repeat(2, 1fr); }
  /* The day rail stacks above its content; the spine and nodes stay. */
  .day-track > li { grid-template-columns: 1fr; gap: 0.5rem; }
  .expertise-grid { grid-template-columns: 1fr; }
  .hero-signature { display: none; }
  .hero-inner { max-width: none; }
  .hero { min-height: 88svh; }
}

@media (max-width: 520px) {
  .hero-ctas .btn,
  .finder-actions .btn { width: 100%; }
  .capture-row { flex-direction: column; }
  .capture-row .btn { width: 100%; }
}



/* ══════════════════════════════════════════════════════════════════════════
   PRINT
   ══════════════════════════════════════════════════════════════════════════ */
@media print {
  .site-header, .site-footer, .hero-media, .hero-veil,
  .final-cta-media, .scroll-cue { display: none !important; }
  body { background: #fff; color: #000; }
  .section { padding-block: 1.5rem; break-inside: avoid; }
}


/* ══════════════════════════════════════════════════════════════════════════
   SIGNATURE ANIMATIONS
   ----------------------------------------------------------------------------
   Five additions, each with a job. None of them loop forever for decoration's
   sake: the ring and thread mark the hero's invitation to scroll, and the other
   four are scroll-linked, so they report progress rather than perform.

   Everything here is scoped to body[data-motion="ready"] and collapses under
   the reduced-motion block at the end of this file.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · Hero signature: a ring that breathes, a thread that draws ────────
   The scroll cue said "the island opens" while nothing moved. Now the outer
   ring pulses slowly outward like a sonar ping and the gold thread draws
   downward on a loop, so the eye is led down the page rather than told to
   look down. */
body[data-motion="ready"] .hero-signature .ring-mark { overflow: visible; }
body[data-motion="ready"] .hero-signature .ring-mark circle:first-of-type {
  transform-origin: 50% 50%;
  animation: sig-ping 3.6s var(--ease-out-quint) infinite;
}
@keyframes sig-ping {
  0%   { transform: scale(1);    opacity: .95; }
  55%  { transform: scale(1.55); opacity: 0; }
  100% { transform: scale(1.55); opacity: 0; }
}
body[data-motion="ready"] .hero-signature .thread {
  background: linear-gradient(180deg, var(--gold), rgba(217,160,60,0));
  transform-origin: top center;
  animation: sig-draw 3.6s var(--ease-out-quint) infinite;
}
@keyframes sig-draw {
  0%   { transform: scaleY(0);   opacity: 0; }
  22%  { transform: scaleY(1);   opacity: 1; }
  78%  { transform: scaleY(1);   opacity: 1; }
  100% { transform: scaleY(1);   opacity: 0; }
}
body[data-motion="ready"] .scroll-cue { animation: sig-cue 3.6s ease-in-out infinite; }
@keyframes sig-cue {
  0%, 100% { opacity: .55; }
  40%      { opacity: .95; }
}

/* ── 2 · The WELL Compass draws itself ────────────────────────────────────
   Rings sweep in, ticks extend from the centre, then the eight directions
   arrive one at a time clockwise from Restore. It reads as an instrument
   being set, which is what the Compass is. */
/* Only the OUTER ring draws. The inner ring is dotted by design
   (stroke-dasharray: 3 7) and a draw animation has to own stroke-dasharray to
   work — so animating it would silently convert the dotted ring to a solid
   one and leave it that way. It fades and settles instead.
   Dash length is the outer ring's own circumference: r=168 → 2πr ≈ 1056. */
body[data-motion="ready"] .compass-ring:not(.compass-ring--inner) {
  stroke-dasharray: 1060;
  stroke-dashoffset: 1060;
}
body[data-motion="ready"] .compass.is-drawn .compass-ring:not(.compass-ring--inner) {
  animation: compass-ring 1.5s var(--ease-out-expo) forwards;
}
@keyframes compass-ring { to { stroke-dashoffset: 0; } }

body[data-motion="ready"] .compass-ring--inner {
  opacity: 0;
  transform: scale(.93);
  transform-origin: 310px 310px;
}
body[data-motion="ready"] .compass.is-drawn .compass-ring--inner {
  animation: compass-inner .9s var(--ease-out-expo) .22s forwards;
}
@keyframes compass-inner { to { opacity: 1; transform: scale(1); } }

body[data-motion="ready"] .compass-tick { opacity: 0; transform-origin: 310px 310px; transform: scale(.86); }
body[data-motion="ready"] .compass.is-drawn .compass-tick {
  animation: compass-tick .5s var(--ease-out-quint) forwards;
  animation-delay: calc(.42s + var(--i, 0) * 90ms);
}
@keyframes compass-tick { to { opacity: .75; transform: scale(1); } }

body[data-motion="ready"] .compass-label { opacity: 0; }
body[data-motion="ready"] .compass.is-drawn .compass-label {
  animation: compass-label .58s var(--ease-out-quint) forwards;
  animation-delay: calc(.52s + var(--i, 0) * 90ms);
}
@keyframes compass-label { to { opacity: 1; } }

body[data-motion="ready"] .compass-center { opacity: 0; }
body[data-motion="ready"] .compass.is-drawn .compass-center {
  animation: compass-label .7s var(--ease-out-quint) 1.25s forwards;
}

/* ── 2b · Hovering a direction explains it ────────────────────────────────
   Eight words with no definitions was the gap: the section asks the visitor
   to "begin with how you want to feel" without saying what Nourish or Return
   mean. Pointing at one brings it forward, recedes the other seven, and reads
   its line out beneath the figure.

   The state lives on the .compass element (is-probed + the hovered point's
   own is-active) rather than on :hover, because the hit target is a circle
   that overlaps its neighbours' at this radius — JS picks the nearest one so
   two directions can never light at once. */
.compass-hit { fill: transparent; stroke: none; }
@media (hover: hover) and (pointer: fine) {
  .compass-hit { cursor: pointer; }
}

/* The dimming rides on fill-opacity / stroke-opacity, NOT opacity, and that is
   load-bearing: the entrance animations above end in `forwards`, so their
   final opacity keeps applying from the animation origin, which outranks any
   ordinary author rule. Anything hover-driven written as `opacity` here would
   simply never take effect. These two properties are untouched by the
   entrance, so the two systems compose instead of fighting — the tick's
   resting .75 opacity multiplies with whatever stroke-opacity says. */
.compass-point .compass-tick,
.compass-point .compass-label {
  transition: fill-opacity .38s var(--ease-out-quint),
              stroke-opacity .38s var(--ease-out-quint),
              stroke-width .38s var(--ease-out-quint),
              fill .38s var(--ease-out-quint),
              stroke .38s var(--ease-out-quint);
}
/* Recede the rest. The dimming is what makes the active one read as chosen —
   brightening alone barely registers against paper-white labels.

   .55 is a contrast floor, not a taste call: paper at .3 over ink composites
   to roughly 2.5:1, and these are 21px labels, so AA large-text does not
   apply. .55 measures 5.0:1. The active direction carries the distinction on
   hue anyway — it turns gold — so the recede can afford to be gentle. */
.compass.is-probed .compass-point:not(.is-active) .compass-label { fill-opacity: .55; }
.compass.is-probed .compass-point:not(.is-active) .compass-tick { stroke-opacity: .25; }
.compass-point.is-active .compass-label { fill: var(--gold); }
.compass-point.is-active .compass-tick { stroke: var(--gold); stroke-opacity: 1; stroke-width: 2.2; }

/* The centre mark holds a soft gold ring while a direction is held, so the
   diagram reads as an instrument taking a bearing rather than a list. */
.compass-mark { transition: opacity .38s var(--ease-out-quint); }
.compass.is-probed .compass-center { fill: var(--paper); }
.compass-center { transition: fill .38s var(--ease-out-quint); }

/* Reserved height — the readout must never reflow the figure as it changes. */
.compass-readout {
  min-height: 4.8em;
  margin: 1.4rem auto 0;
  max-width: 34rem;
  text-align: center;
  font-family: var(--serif);
  font-size: 0.98rem;
  line-height: 1.5;
  color: rgba(251, 248, 241, .82);
}
.compass-readout-inner {
  display: inline-block;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .3s var(--ease-out-quint), transform .3s var(--ease-out-quint);
}
.compass.is-probed ~ .compass-readout .compass-readout-inner,
.compass-readout.is-shown .compass-readout-inner { opacity: 1; transform: none; }
.compass-readout b {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  display: block;
  margin-bottom: 0.45em;
}

/* Same eight lines as plain copy. On a pointer device this is the version
   assistive technology reads — hidden from sight, not from the a11y tree, so
   nobody has to hover something to find out what it says. Where there is no
   hover, it simply becomes the visible treatment and the readout stands down. */
@media (hover: hover) and (pointer: fine) {
  .compass-legend {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}
@media not all and (hover: hover) {
  .compass-readout { display: none; }
  .compass-legend {
    margin-top: 2rem;
    display: grid;
    gap: 1rem;
    text-align: left;
  }
  .compass-legend dt {
    font-family: var(--sans);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--gold);
    margin-bottom: 0.3em;
  }
  .compass-legend dd {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.5;
    color: rgba(251, 248, 241, .82);
  }
}

/* ── 3 · Finder steps light up in sequence as you pass them ───────────────
   The numeral fills gold and the hairline above the step draws left to right,
   so 01 / 02 / 03 reads as progress rather than three static columns. */
body[data-motion="ready"] .finder-steps li { position: relative; }
body[data-motion="ready"] .finder-steps li::before {
  content: '';
  position: absolute;
  inset: -1px auto auto 0;
  height: 2px;
  width: 100%;
  background: var(--gold-deep);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .7s var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 140ms);
}
/* Per item and scroll-linked, not once on the group. `.is-active` is toggled
   by js/motion.js as each step crosses the reading line, so the three draw in
   as you arrive and retract as you scroll back — 01/02/03 tracks where you
   are rather than reporting that you once passed. The stagger delay goes with
   it: on a scroll-linked state a fixed per-item delay fights the scroll. */
body[data-motion="ready"] .finder-steps li::before { transition-delay: 0s; }
body[data-motion="ready"] .finder-steps li.is-active::before { transform: scaleX(1); }
body[data-motion="ready"] .finder-step-n {
  color: var(--muted);
  transition: color .5s var(--ease-out-quint);
}
body[data-motion="ready"] .finder-steps li.is-active .finder-step-n { color: var(--gold-deep); }

/* ── 3a · The Wellness Continuum reads as a range ─────────────────────────
   Six stages laid out in a 3×2 grid do not look like a progression — they
   look like six equal boxes, which is precisely the misreading this section
   exists to correct ("not everything has to transform you" only lands if you
   can see that Relax and Transform sit at different ends of one range).

   So each stage is numbered, and the rules light in order as you scroll.
   Scoped to .lens--sequence: the other five lenses on the site are sets, and
   numbering a set invents a hierarchy that is not there. */
.lens--sequence .lens-item { position: relative; counter-increment: continuum; }
.lens--sequence .lens-grid { counter-reset: continuum; }
.lens--sequence .lens-item h3::before {
  content: counter(continuum, decimal-leading-zero);
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  color: var(--muted);
  margin-bottom: 0.5em;
  transition: color .45s var(--ease-out-quint);
}
body[data-motion="ready"] .lens--sequence .lens-item::before {
  content: '';
  position: absolute;
  inset: -1px auto auto 0;
  height: 2px;
  width: 100%;
  background: var(--gold-deep);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .7s var(--ease-out-expo);
}
body[data-motion="ready"] .lens--sequence .lens-item.is-active::before { transform: scaleX(1); }
body[data-motion="ready"] .lens--sequence .lens-item.is-active h3::before { color: var(--gold-deep); }

/* Hovering one stage brings it forward without moving anything: this is a
   range you scan, and being able to settle on one point of it is the whole
   interaction. */
.lens--sequence .lens-item p { transition: color .4s var(--ease-out-quint); }
@media (hover: hover) and (pointer: fine) {
  .lens--sequence .lens-item:hover::before { background: var(--gold); }
  .lens--sequence .lens-item:hover p { color: var(--ink); }
}

/* ── 3b · The homepage Journey Architecture follows the scroll ────────────
   The vertical spine and its six nodes on the homepage teaser were static.
   They now light downward as you reach each phase and unlight on the way
   back, matching the five-day timeline on /eclipse — same idea, same
   vocabulary, so the teaser and the page it teases move alike.

   The spine fills per segment rather than as one bar down the whole list,
   for the same reason the /eclipse arc does: a single element cannot fill
   progressively without knowing every item's height. */
body[data-motion="ready"] .arc-list::before { opacity: .25; }
body[data-motion="ready"] .arc-list > li::after {
  content: '';
  position: absolute;
  left: calc(-1.9rem + 5px);
  top: 12px;
  height: calc(100% + 1.5rem - 12px);
  width: 1px;
  background: var(--eclipse-copper);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform .6s var(--ease-out-expo);
}
body[data-motion="ready"] .arc-list > li:last-child::after { display: none; }
body[data-motion="ready"] .arc-list > li.is-lit::after { transform: scaleY(1); }

body[data-motion="ready"] .arc-list > li::before {
  transition: background-color .45s var(--ease-out-quint),
              border-color .45s var(--ease-out-quint),
              box-shadow .45s var(--ease-out-quint);
}
body[data-motion="ready"] .arc-list > li.is-lit::before {
  background: var(--eclipse-copper-l);
  border-color: var(--eclipse-copper-l);
  box-shadow: 0 0 0 4px rgba(198, 152, 120, .16);
}
body[data-motion="ready"] .arc-list .arc-key { transition: color .45s var(--ease-out-quint); }
body[data-motion="ready"] .arc-list > li.is-lit .arc-key { color: #FFFFFF; }

/* ── 3c · The specialists' rules draw as you reach them ───────────────────
   Four hairlines sat inert above four columns. Each now draws left to right
   on arrival and retracts on the way back, and brightens under the pointer —
   the rule is the only thing that moves, so four columns of type stay still. */
body[data-motion="ready"] .expertise-grid li { position: relative; }
body[data-motion="ready"] .expertise-grid li::before {
  content: '';
  position: absolute;
  inset: -1px auto auto 0;
  height: 2px;
  width: 100%;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .75s var(--ease-out-expo), background-color .4s var(--ease-out-quint);
}
body[data-motion="ready"] .expertise-grid li.is-active::before { transform: scaleX(1); }
/* This component also renders on /eclipse, where gold would be the wrong
   world. The rule takes the copper the rest of that page uses. */
body[data-motion="ready"] .section--eclipse .expertise-grid li::before { background: var(--eclipse-copper-l); }
@media (hover: hover) and (pointer: fine) {
  body[data-motion="ready"] .expertise-grid li:hover::before { background: var(--paper); }
  body[data-motion="ready"] .section--eclipse .expertise-grid li:hover::before { background: var(--eclipse-sand); }
}

/* ── 4 · The Eclipse arc lights as a path ─────────────────────────────────
   Each phase's rule draws left-to-right and its node ignites as the scroll
   reaches it, so the six phases read as a route being travelled. `is-lit` is
   toggled per node by js/motion.js from the track's scroll progress.

   The rule is drawn PER SEGMENT rather than as one line across the track,
   because the arc reflows to 3 columns and then 2 — a single track-spanning
   line would leave every row after the first without a rule at all.

   All of it is gated on data-motion: without JS the arc keeps its original
   solid-copper nodes and rules rather than sitting permanently dimmed. */
body[data-motion="ready"] .arc-track li { border-top-color: rgba(155, 116, 85, .18); }
body[data-motion="ready"] .arc-track li::after {
  content: '';
  position: absolute;
  top: -1px; left: 0; right: 0;
  height: 1px;
  background: var(--eclipse-copper);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .55s var(--ease-out-expo);
}
body[data-motion="ready"] .arc-track li.is-lit::after { transform: scaleX(1); }

body[data-motion="ready"] .arc-track li::before {
  border-color: rgba(155, 116, 85, .45);
  transition: background-color .45s var(--ease-out-quint),
              border-color .45s var(--ease-out-quint),
              box-shadow .45s var(--ease-out-quint);
}
body[data-motion="ready"] .arc-track li.is-lit::before {
  background: var(--eclipse-copper-l);
  border-color: var(--eclipse-copper-l);
  box-shadow: 0 0 0 4px rgba(198, 152, 120, .16);
}
body[data-motion="ready"] .arc-track .arc-key { transition: color .45s var(--ease-out-quint); }
body[data-motion="ready"] .arc-track li.is-lit .arc-key { color: #FFFFFF; }

/* ══════════════════════════════════════════════════════════════════════════
   THE ECLIPSE MARK
   ----------------------------------------------------------------------------
   Two jobs. As a `sign` it sits above the page title, small, and is the first
   thing that says this journey has its own identity. As a `watermark` it is
   very large and very faint behind a section, bled off the measure — present
   enough to be felt on the second or third section, never enough to compete
   with the type in front of it.

   The mark's own artwork peaks at 73% alpha and carries a raking light: bright
   at the top, copper down the sides, receding at the base. On the midnight
   ground that reads as a corona, which is why it is used un-recoloured.
   ══════════════════════════════════════════════════════════════════════════ */
.eclipse-mark { display: block; line-height: 0; }
.eclipse-mark img { display: block; width: 100%; height: auto; }

.eclipse-mark--sign {
  width: 110px;
  margin-bottom: clamp(1.25rem, 3vw, 2rem);
}
/* Arrives with the header rather than being there from the first frame — the
   ring resolving out of the dark is the whole idea of the name.

   It rides the site's own reveal system rather than a bespoke keyframe. A
   standalone `opacity: 0` + animation leaves the mark permanently invisible if
   the animation never runs; `.reveal-item` carries the 3.6s `settled`
   guarantee that every other revealed element on the site already has. */
body[data-motion="ready"] .eclipse-mark--sign.reveal-item {
  transform: translateY(10px) scale(.9);
}
body[data-motion="ready"] .eclipse-mark--sign.reveal-item.is-revealed {
  transform: none;
  transition: opacity .9s var(--ease-out-expo), transform 1.1s var(--ease-out-expo);
}

.eclipse-mark--watermark {
  position: absolute;
  top: 50%;
  right: -14vw;
  width: min(46rem, 90vw);
  transform: translateY(-50%);
  opacity: .07;
  pointer-events: none;
  z-index: 0;
}
/* Everything in the section must sit above it. `.wrap` is not positioned by
   default, so without this the watermark paints over the copy. */
.arc-section > .wrap,
.checklist > .wrap { position: relative; z-index: 1; }

/* Alternate the side so two watermarked sections on one page do not read as a
   repeating tile. */
.checklist .eclipse-mark--watermark { right: auto; left: -16vw; }

@media (max-width: 900px) {
  /* At narrow widths the mark would sit directly behind the measure rather
     than beside it, so it goes. */
  .eclipse-mark--watermark { display: none; }
  .eclipse-mark--sign { width: 84px; }
}

/* ── 4b · The five days light down the page ───────────────────────────────
   The same idea as the arc rotated onto the Y axis: the spine fills downward
   and each day's node ignites as it is reached. Gated on data-motion so that
   without JS the timeline keeps its solid copper spine and nodes rather than
   sitting permanently dimmed. */
body[data-motion="ready"] .day-track > li { border-left-color: rgba(155, 116, 85, .14); }
/* Must be restated here: the rule above out-specifies the base `:last-child`
   reset, so without this the dim spine runs on past the final day — a line to
   nowhere under the last item. */
body[data-motion="ready"] .day-track > li:last-child { border-left-color: transparent; }
body[data-motion="ready"] .day-track > li::after {
  content: '';
  position: absolute;
  left: -1px; top: 0; bottom: 0;
  width: 1px;
  background: var(--eclipse-copper);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform .7s var(--ease-out-expo);
}
body[data-motion="ready"] .day-track > li:last-child::after { display: none; }
body[data-motion="ready"] .day-track > li.is-lit::after { transform: scaleY(1); }

body[data-motion="ready"] .day-track > li::before {
  transition: background-color .45s var(--ease-out-quint),
              border-color .45s var(--ease-out-quint),
              box-shadow .45s var(--ease-out-quint);
}
body[data-motion="ready"] .day-track > li.is-lit::before {
  background: var(--eclipse-copper-l);
  border-color: var(--eclipse-copper-l);
  box-shadow: 0 0 0 4px rgba(198, 152, 120, .16);
}
body[data-motion="ready"] .day-track > li .day-n,
body[data-motion="ready"] .day-track > li h3 { transition: color .45s var(--ease-out-quint); }
body[data-motion="ready"] .day-track > li.is-lit h3 { color: #FFFFFF; }

/* ── 5 · Signature experience tiles ───────────────────────────────────────
   A copper hairline draws across the top of each tile as it arrives, and the
   title lifts a little on hover. Nine identical boxes needed an entrance. */
body[data-motion="ready"] .tile-grid li { position: relative; overflow: hidden; }
body[data-motion="ready"] .tile-grid li::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--eclipse-copper-l), transparent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .8s var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 55ms);
}
body[data-motion="ready"] .tile-grid.is-revealed li::before { transform: scaleX(1); }
.tile-grid h3 { transition: transform .35s var(--ease-out-quint); }
@media (hover: hover) and (pointer: fine) {
  .tile-grid li:hover h3 { transform: translateY(-2px); }

  /* The tile now holds a photograph, so the hover can come from the image
     rather than from the type: a slow 1.04 push inside a fixed frame, with the
     village chip warming at the same time. Transform only — animating width or
     filter here would repaint nine tiles at once. */
  .tile-grid--media .tile-media picture,
  .tile-grid--media .tile-media img {
    transition: transform .8s var(--ease-out-quint);
    will-change: transform;
  }
  .tile-grid--media li:hover .tile-media img { transform: scale(1.04); }

  .tile-village { transition: color .4s var(--ease-out-quint); }
  .tile-grid--media li:hover .tile-village {
    color: color-mix(in srgb, var(--v, var(--eclipse-copper-l)) 30%, var(--paper));
  }

  /* Eclipse's own column in the comparison is the one that should feel alive;
     the "typical wellness stay" column deliberately stays inert. */
  .cmp-col--ours li { transition: color .35s var(--ease-out-quint); }
  .cmp-col--ours:hover li { color: var(--eclipse-sand); }

  /* Roles lift a hair on approach. No shadow — on midnight a shadow is
     invisible, so the only honest affordance is position. */
  .expertise-grid li { transition: transform .4s var(--ease-out-quint); }
  .expertise-grid li:hover { transform: translateY(-3px); }
}

/* Each day's items arrive with the day that carries them, rather than all
   twenty-three appearing at once when the section reveals. */
body[data-motion="ready"] .day-track > li .day-body > * {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .55s var(--ease-out-quint), transform .55s var(--ease-out-quint);
}
body[data-motion="ready"] .day-track > li.is-lit .day-body > * {
  opacity: 1;
  transform: none;
}
body[data-motion="ready"] .day-track > li.is-lit .day-body > ul { transition-delay: .08s; }
/* Failsafe: js/motion.js sets `settled` four seconds in, whatever happened to
   the scroll loop. Hidden copy must never depend on an animation succeeding. */
body[data-motion="ready"] .day-track.settled > li .day-body > * {
  opacity: 1;
  transform: none;
}

/* ══════════════════════════════════════════════════════════════════════════
   PROSE — privacy, terms, accessibility
   --------------------------------------------------------------------------
   Documents, not compositions. Someone arrives wanting one fact and should be
   able to scan for it: a strict measure, headings that are findable, and no
   reveal choreography. These pages deliberately carry none of the site's
   motion — a privacy policy that fades in as you scroll is the wrong idea.
   ══════════════════════════════════════════════════════════════════════════ */
.prose { max-width: var(--measure); }
.prose h2 {
  font-size: var(--fs-h3);
  margin: clamp(2.25rem, 4vw, 3rem) 0 0.6em;
  scroll-margin-top: 6rem;      /* in-page links must clear the sticky header */
}
.prose h2:first-child { margin-top: 0; }
.prose p { color: var(--muted); }
.prose p strong { color: var(--ivory-ink); font-weight: 600; }
.prose a { color: var(--gold-deep); text-underline-offset: 3px; }

.prose-list { margin: 0 0 1.1em; padding-left: 1.1rem; list-style: disc; }
.prose-list li { color: var(--muted); margin-bottom: 0.4em; }

/* Term/definition pairs — "what we collect" reads far better as a two-column
   table than as another bulleted list. Stacks on narrow screens. */
.prose-rows { margin: 0 0 1.4em; display: grid; gap: 0.9rem; }
.prose-rows > div {
  display: grid;
  gap: 0.15rem 1.5rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--hairline-l);
}
@media (min-width: 620px) {
  .prose-rows > div { grid-template-columns: 12rem 1fr; }
}
.prose-rows dt {
  font-family: var(--sans);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}
.prose-rows dd { margin: 0; color: var(--muted); }

.prose-updated {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: 1.2rem;
  border-top: 1px solid var(--hairline-l);
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--muted);
}

/* ══════════════════════════════════════════════════════════════════════════
   SHARE PANEL — handing a Journey to an advisor
   --------------------------------------------------------------------------
   The one place on this site that asks for a name. It sits inside the result
   rather than replacing it, so the thing the visitor came for stays on screen
   while they decide.
   ══════════════════════════════════════════════════════════════════════════ */
.share-panel { margin-top: clamp(1.5rem, 3vw, 2.25rem); }
.share-form {
  background: var(--paper);
  border: 1px solid var(--hairline-l);
  border-left: 3px solid var(--gold-deep);
  border-radius: 2px;
  padding: clamp(1.25rem, 3vw, 2rem);
  max-width: 46rem;
}
.share-title { font-size: var(--fs-h4); margin: 0 0 0.4em; }
.share-note { color: var(--muted); font-size: var(--fs-small); margin: 0 0 1.4em; max-width: 52ch; }

.share-grid { display: grid; gap: 0.9rem; }
@media (min-width: 640px) { .share-grid { grid-template-columns: 1fr 1fr; } }
.share-grid .share-wide { grid-column: 1 / -1; }

.share-grid label {
  display: grid;
  gap: 0.3rem;
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}
.share-opt { font-weight: 400; color: var(--muted); letter-spacing: 0; }
.share-grid input,
.share-grid select,
.share-grid textarea {
  font: inherit;
  font-weight: 400;
  font-family: var(--serif);
  font-size: 1rem;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--hairline-l);
  border-radius: 2px;
  background: var(--paper-2);
  color: var(--ivory-ink);
  width: 100%;
  min-height: 44px;
}
.share-grid textarea { resize: vertical; }
.share-grid :is(input, select, textarea):focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

/* The honeypot. Removed from the layout and from the accessibility tree, but
   still a real field a script will happily fill in. */
.share-hp {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.share-consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.6rem;
  align-items: start;
  margin: 1.3rem 0 0;
  font-family: var(--sans);
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--muted);
}
.share-consent input { margin-top: 0.2rem; width: 1rem; height: 1rem; }
.share-consent a { color: var(--gold-deep); }

.share-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.4rem; }
.share-status { font-family: var(--sans); font-size: 0.82rem; margin: 0.9rem 0 0; }
.share-status[data-state="error"] { color: var(--movement, #A33F28); }

/* The sensitive-data reminder under the free-text field. Guide §A and §I both
   ask for it exactly here — the one place on the site where a person could
   volunteer something they should not. */
.share-hint {
  display: block;
  margin-top: 0.35rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.45;
  color: var(--muted);
}
