/* Fonts are loaded via <link> in each page <head> (with preconnect) so the
   request kicks off in parallel with this stylesheet instead of being
   discovered only after it downloads and parses. Do not re-add @import here. */

/* ── Custom Properties ── */
:root {
  --color-cream: #F7F5F0;
  --color-sage: #8FAF8A;
  --color-sage-light: #A8C4A3;
  --color-sage-dark: #7A9B75;
  --color-taupe: #C4B9A8;
  --color-taupe-light: #D6CEC1;
  --color-olive: #4A5E46;
  --color-olive-dark: #3C4C39;               /* filled-button hover; 8.4:1 on cream ink */
  --color-amber: #E8AA14;
  /* The amber pill's own two values. They were hardcoded at the component for a
     long time, which let DESIGN.md drift to a different ink (#7A5C00) than the
     one actually shipping. Tokens so there is one place to change them. */
  --color-amber-ink: #3D2600;                /* 6.9:1 on amber, 8.0:1 on amber-hover */
  --color-amber-hover: #F0BA3D;
  --color-rust: #D74E09;
  --color-slate: #3A5683;
  --color-text: #3D3B37;
  --color-text-light: #6B6760;
  --color-cream-dark: #EDE9E0;

  /* Tell the UA (form controls, scrollbars, default canvas) which way this
     theme leans. Flipped to `dark` in the theme block at the foot of the file. */
  color-scheme: light;

  /* ── Semantic (themed) tokens ──
     The primitives above are brand constants — fixed hues the shader's baked
     palette also references. These semantic tokens are the layer that actually
     flips between light and dark; their dark values live in the theme block at
     the end of this file. Two primitives are deliberately NOT flipped:
       • --color-olive stays olive because it is a drenched *surface* on the
         hero / CTA banner / Book portal. Heading *text* reads from --heading.
       • --color-cream stays light because it is the ink on those drenched
         surfaces. Page *surfaces* read from --surface-page and friends. */
  --surface-page: var(--color-cream);
  --surface-alt: var(--color-cream-dark);
  --surface-warm: #F0ECE3;
  --surface-raised: var(--color-cream-dark); /* book-step marker */
  --surface-header: var(--color-cream);
  --surface-drawer: var(--surface-header); /* mobile drawer matches the navbar */
  --veil: var(--color-cream);               /* page + booking transition veils */

  --heading: var(--color-olive);            /* h1–h4, wordmarks, serif asides */
  --eyebrow: var(--color-olive);            /* section labels / eyebrows */
  --link: var(--color-olive);               /* inline links, ghost button ink */
  --link-hover: var(--color-sage-dark);
  --accent-marker: var(--color-sage-dark);  /* small meaning-carrying icons */

  --divider: rgba(196, 185, 168, 0.45);     /* hairline separators */
  --divider-soft: rgba(196, 185, 168, 0.25);
  /* Faded hairline: full strength in the middle, dissolving to nothing at both
     ends. The site-wide separator treatment — applied to horizontal rules via
     `border-image: var(--divider-fade) 1` (with a transparent border), and to
     standalone divider elements as a background-image. -h runs left↔right for
     horizontal rules; -v runs top↔bottom for vertical ones. */
  --divider-fade: linear-gradient(90deg, transparent, var(--divider) 50%, transparent);
  --divider-fade-v: linear-gradient(180deg, transparent, var(--divider) 50%, transparent);
  --divider-fade-soft: linear-gradient(90deg, transparent, var(--divider-soft) 50%, transparent);
  --focus-ring: var(--color-olive);         /* on capsules over light surfaces */

  /* Focus-tag chips carry text, so their fg meets the 4.5:1 text floor. */
  --tag-sage-bg: rgba(143, 175, 138, 0.15);
  --tag-sage-fg: var(--color-olive);
  --tag-amber-bg: rgba(232, 170, 20, 0.15);
  --tag-amber-fg: #7A5C00;
  --tag-neutral-bg: rgba(196, 185, 168, 0.25);
  --tag-neutral-fg: var(--color-text);

  /* Services-index chips hold an icon glyph, not text — a looser 3:1 floor, so
     their fg can be the fuller hue the icon rotation wants. */
  --chipicon-sage-bg: rgba(143, 175, 138, 0.16);
  --chipicon-sage-fg: var(--color-sage-dark);
  --chipicon-amber-bg: rgba(232, 170, 20, 0.15);
  --chipicon-amber-fg: var(--color-amber);
  --chipicon-rust-bg: rgba(215, 78, 9, 0.12);
  --chipicon-rust-fg: var(--color-rust);

  --font-heading: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --font-body: 'Source Sans 3', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* Accessible type scale. Paragraph copy starts at 16px; smaller sizes are
     reserved for supporting UI and short labels, never long-form reading. */
  --font-size-label: 0.875rem;
  --font-size-supporting: 1rem;
  --font-size-body: 1rem;
  --font-size-lede: 1.1875rem;
  --font-size-title: clamp(1.3125rem, 1.15rem + 0.7vw, 1.5625rem);
  --font-size-subheading: clamp(1.6875rem, 1.5rem + 0.9vw, 1.9375rem);
  --font-size-headline: clamp(1.875rem, 3.25vw, 2.625rem);
  --font-size-display: clamp(2.375rem, 4.75vw, 3.375rem);

  --max-width-text: 720px;
  --max-width-wide: 1080px;
  --section-padding: 3rem 2rem;
  --section-padding-mobile: 3rem 2rem;

  /* Radius scale, anchored on the booking pill. Three shapes, one rule each:
     anything you press is a capsule, every icon/number marker is a circle, and
     framed media takes the single soft surface radius. Nothing in between —
     the in-between radii (6/10/12px) are what made the old buttons and chips
     read as a stack of unrelated boxes. */
  --radius-pill: 999px;
  --radius-circle: 50%;
  --radius-surface: 16px;
  /* Focus rings only: an inline text link can wrap, and a capsule outline
     around a wrapped link reads as a mistake. */
  --radius-focus: 6px;

  /* Calm, decisive deceleration curves — no bounce, no elastic. */
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* Guard against any decorative element that overhangs its section padding on
     a narrow viewport: an overhanging box, even a transparent one, makes the
     *document* wider than the viewport, and the fixed header is sized to the
     viewport — so the moment the page can pan sideways it slides out of
     alignment. Clip the overflow here so the document never grows sideways.
     `clip`, not `hidden` — hidden would make the root a scroll container and
     take `scroll-behavior: smooth` and any sticky positioning down with it. */
  overflow-x: clip;
}

body {
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: 400;
  line-height: 1.75;
  color: var(--color-text);
  background-color: var(--surface-page);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--link);
  text-decoration: none;
  transition: color 0.25s ease;
}

a:hover {
  color: var(--link-hover);
}

a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--color-sage);
  outline-offset: 3px;
  border-radius: var(--radius-focus);
}

/* The rule above rounds the focused element itself, which would flatten a
   capsule back into a rounded rect the moment it takes focus. Anything already
   pill-shaped restates its own radius here so the outline traces the capsule. */
.btn:focus-visible,
.cta-pill:focus-visible,
.skip-link:focus-visible {
  border-radius: var(--radius-pill);
}

.btn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
}

ul, ol {
  list-style: none;
}

/* ── Typography ── */
h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 500;
  color: var(--heading);
  line-height: 1.25;
  text-wrap: balance;
}

h1 {
  font-size: var(--font-size-display);
  font-weight: 400;
  letter-spacing: -0.01em;
}

h2 {
  font-size: var(--font-size-headline);
  margin-bottom: 1.5rem;
}

h3 {
  font-size: var(--font-size-title);
  margin-bottom: 1rem;
}

p {
  margin-bottom: 1.25rem;
  text-wrap: pretty;
}

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

/* ── Utility ── */
.container {
  max-width: var(--max-width-text);
  margin: 0 auto;
  padding: 0 2rem;
}

.container--wide {
  max-width: var(--max-width-wide);
}

.text-center {
  text-align: center;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Keyboard skip link — held off-screen until focused, then it drops in
   above the fixed header so keyboard users can jump past the nav. */
.skip-link {
  position: absolute;
  top: -4rem;
  left: 1rem;
  z-index: 1100;
  padding: 0.625rem 1.25rem;
  background-color: var(--color-olive);
  color: var(--color-cream);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-label);
  font-weight: 500;
  transition: top 0.2s var(--ease-out-quart);
}

.skip-link:focus,
.skip-link:hover {
  color: var(--color-cream);
}

.skip-link:focus-visible {
  top: 1rem;
  outline: 2px solid var(--color-cream);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .skip-link {
    transition: none;
  }
}

/* ── Buttons ── */
/* Secondary actions only. Every booking action — the one amber moment in the
   system — is a .cta-pill instead (see the Book page section); these are the
   calmer sage/outline actions that sit around it. */
.btn {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--font-size-supporting);
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 0.875rem 2rem;
  border-radius: var(--radius-pill);
  border: none;
  cursor: pointer;
  transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
  text-decoration: none;
}

/* Filled on olive, not sage. White on sage measures 2.4:1 — sage is a light
   mid-tone and simply cannot carry white text at any weight. Olive is the same
   family two steps down, already the link/heading colour, and takes cream ink at
   ~6:1. Amber stays rationed to booking, so a secondary CTA has nowhere else to
   go. */
.btn--primary {
  background-color: var(--color-olive);
  color: var(--color-cream);
}

.btn--primary:hover {
  background-color: var(--color-olive-dark);
  color: var(--color-cream);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(74, 94, 70, 0.2);
}

.btn--outline-light {
  background-color: transparent;
  color: var(--color-cream);
  border: 1.5px solid rgba(247, 245, 240, 0.55);
}

.btn--outline-light:hover {
  background-color: var(--color-cream);
  border-color: var(--color-cream);
  color: var(--color-olive);
  transform: translateY(-1px);
}

/* ── Header / Navigation ── */
.site-header {
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  position: fixed;
  background-color: var(--surface-header);
  transition: box-shadow 0.3s ease;
}

.site-header--scrolled {
  box-shadow: 0 1px 8px rgba(74, 94, 70, 0.08);
}

.site-header__inner {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  padding: 1rem 2rem;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.site-header__logo {
  display: flex;
  align-items: center;
  gap: 10px;
}

.site-header__logotext {
  font-family: var(--font-heading);
  font-size: 1.5rem;
  letter-spacing: -0.03em;
  font-weight: 600;
  color: var(--heading);
  text-decoration: none;
  transition: opacity 0.25s ease;
}

.site-header__logo:hover {
  opacity: 0.8;
  color: var(--heading);
}

.site-nav {
  margin-left: auto; /* keep the links flush to the right edge on desktop */
}

.site-nav__list {
  display: flex;
  gap: 1.85rem;
  align-items: center;
}

.site-nav__link {
  font-size: 1rem;
  font-weight: 400;
  color: var(--color-text);
  text-decoration: none;
  position: relative;
  padding: 0.5rem 0;
  transition: color 0.25s ease;
}

.site-nav__link::after {
  content: '';
  position: absolute;
  bottom: 0.15rem;
  left: 50%;
  width: 0;
  height: 1.5px;
  background-color: var(--link);
  /* Grows out from the centre in both directions, matching the toggle ring's
     bottom-centre origin, rather than sweeping in from the left. Animating
     width (not transform: scaleX) keeps the hairline crisp mid-transition —
     scaling a sub-2px layer blurs it until the transform settles. */
  transform: translateX(-50%);
  transition: width 0.3s ease;
}

.site-nav__link:hover,
.site-nav__link--active {
  color: var(--heading);
}

.site-nav__link:hover::after,
.site-nav__link--active::after {
  width: 100%;
}

/* Booking pill + hamburger: the header's action cluster, sitting to the right of
   the links on desktop and beside the toggle on mobile, so the booking action is
   never more than a glance away at any width. */
.site-header__actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-left: 1.85rem;
}

/* ── Theme toggle ──
   A quiet sun/moon switch, never amber — the rationed warmth belongs to the
   booking pill alone. Sits in the header actions on desktop and drops into the
   drawer footer on mobile so the spare top bar stays uncluttered. It shows the
   icon for the theme you'd switch TO, driven by the same signals as the palette
   so it can never disagree with what's on screen. */
.theme-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  transition: color 0.25s ease;
}

/* No filled hover shape — it read as a stray circle against a flat, underline-
   driven header. The nav variant instead grows the same sage underline the
   links use; both variants shift the icon toward --heading on hover. */
.theme-toggle:hover,
.theme-toggle:focus-visible {
  color: var(--heading);
}

.theme-toggle:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-focus);
}

/* A sage ring that draws itself around the icon: two mirrored semicircle arcs
   both starting at bottom-centre and sweeping up opposite sides to meet at
   top-centre — the circular cousin of the nav links' centre-out underline.
   Arc length = pi * r (r = 13) ≈ 40.84; offset that far hides it, 0 draws it. */
.theme-toggle__ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.theme-toggle__ring-arc {
  fill: none;
  stroke: var(--link);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-dasharray: 40.84;
  stroke-dashoffset: 40.84;
  transition: stroke-dashoffset 0.35s var(--ease-out-quart);
}

.theme-toggle--nav:hover .theme-toggle__ring-arc,
.theme-toggle--nav:focus-visible .theme-toggle__ring-arc {
  stroke-dashoffset: 0;
}

@media (prefers-reduced-motion: reduce) {
  .theme-toggle__ring-arc {
    transition: none;
  }
}

.theme-toggle__label {
  display: none;
}

/* Both marks share one 20px slot and crossfade with a quarter-turn when the
   theme flips, rather than hard-swapping via display. */
.theme-toggle__icons {
  position: relative;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.theme-toggle__icons .ico {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transition: opacity 0.4s var(--ease-out-quart),
              transform 0.4s var(--ease-out-quart);
}

/* Which mark is showing: moon on a light surface, sun on a dark one. The
   hidden one is faded out and tucked a quarter-turn away so the swap spins.
   Explicit [data-theme] rules follow the media query so a pinned choice wins
   over the OS preference at equal specificity. */
.theme-toggle__sun { opacity: 0; transform: rotate(-90deg) scale(0.5); }
.theme-toggle__moon { opacity: 1; transform: none; }

:root[data-theme="dark"] .theme-toggle__sun { opacity: 1; transform: none; }
:root[data-theme="dark"] .theme-toggle__moon { opacity: 0; transform: rotate(90deg) scale(0.5); }
:root[data-theme="light"] .theme-toggle__sun { opacity: 0; transform: rotate(-90deg) scale(0.5); }
:root[data-theme="light"] .theme-toggle__moon { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .theme-toggle__icons .ico { transition: none; }
}


/* Contact block — only rendered inside the mobile drawer */
.site-nav__footer {
  display: none;
}

/* ── Mobile Menu Toggle ── */
.menu-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
  color: var(--heading);
  font-size: 1.25rem;
}

/* Three-line hamburger that morphs into an X. The icon element is the middle
   bar; its ::before/::after are the top and bottom bars. On open (driven by the
   button's aria-expanded, which the JS already toggles) the outer bars rotate
   into an X and the middle bar fades out — a smooth, non-bouncy ease-out morph
   that doubles as the open/close feedback. */
.menu-toggle__icon,
.menu-toggle__icon::before,
.menu-toggle__icon::after {
  display: block;
  width: 22px;
  height: 2px;
  background-color: currentColor;
  transition: transform 0.3s var(--ease-out-quart),
              opacity 0.2s var(--ease-out-quart),
              background-color 0.2s var(--ease-out-quart);
}

.menu-toggle__icon {
  position: relative;
}

.menu-toggle__icon::before,
.menu-toggle__icon::after {
  content: '';
  position: absolute;
  left: 0;
}

.menu-toggle__icon::before { top: -7px; }
.menu-toggle__icon::after  { top: 7px; }

.menu-toggle[aria-expanded="true"] .menu-toggle__icon {
  background-color: transparent;
}

.menu-toggle[aria-expanded="true"] .menu-toggle__icon::before {
  transform: translateY(7px) rotate(45deg);
}

.menu-toggle[aria-expanded="true"] .menu-toggle__icon::after {
  transform: translateY(-7px) rotate(-45deg);
}

/* Load state: the bars ease in shortly after first paint (opacity only, so it
   never fights the morph's transforms). Mobile-only in practice — the button is
   display:none on desktop. */
@keyframes menu-icon-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.js .menu-toggle__icon {
  animation: menu-icon-in 0.5s var(--ease-out-quart) 0.2s both;
}

/* ── Hero Section ── */
.hero {
  position: relative;
  background-color: var(--color-olive);
  overflow: hidden;
}

.hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.hero__inner {
  position: relative;
  z-index: 1;
  padding: 7.5rem 2rem 4.5rem;
  max-width: var(--max-width-wide);
  margin: 0 auto;
  display: flex;
  align-items: flex-end;
  gap: clamp(2rem, 5vw, 4.5rem);
}

/* On mobile the hero owns the first screen: the visitor lands on the whole
   statement — headline, portrait, nothing else — and scrolls on from there. On
   tablet there's enough width that a full-screen hero just reads as dead space,
   so it stays in normal flow. min-height (not height) so the box still grows if
   the copy needs more room than the viewport, which also keeps the centring from
   ever clipping the top; svh so mobile browser chrome doesn't crop the bottom. */
@media (max-width: 768px) {
  .hero {
    display: flex;
    align-items: center;
    min-height: 100vh;
    min-height: 100svh;
  }

  .hero__inner {
    flex: 1 1 auto;
    width: 100%;
  }
}

/* Scroll cue: a single point of light drifting down a hairline — the quietest
   possible "there is more below". It only earns its place where the hero fills
   the screen; from tablet up the next section is already peeking above the fold
   and does the job for it. */
.hero__scroll {
  display: none;
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  bottom: 1.5rem;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  pointer-events: none;
}

/* The system's Label type, one step smaller and dimmed — a whisper, not an
   instruction. */
.hero__scroll-label {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(247, 245, 240, 0.55);
}

@media (max-width: 768px) {
  .hero__scroll {
    display: flex;
  }
}

.hero__scroll-line {
  display: block;
  position: relative;
  width: 1px;
  height: 34px;
  background: linear-gradient(180deg,
    rgba(247, 245, 240, 0) 0%,
    rgba(247, 245, 240, 0.4) 40%,
    rgba(247, 245, 240, 0) 100%);
}

.hero__scroll-line::after {
  content: '';
  position: absolute;
  top: 0;
  left: -1px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background-color: var(--color-cream);
  animation: hero-scroll-drift 2.8s var(--ease-out-quart) infinite;
}

@keyframes hero-scroll-drift {
  0%   { transform: translateY(31px); opacity: 0; }
  20%  { opacity: 0.85; }
  70%  { opacity: 0.85; }
  100% { transform: translateY(0); opacity: 0; }
}

/* Reduced motion: the cue stays, the drift doesn't — the dot simply rests on
   the line. */
@media (prefers-reduced-motion: reduce) {
  .hero__scroll-line::after {
    top: 15px;
    opacity: 0.85;
    animation: none;
  }
}

.hero__content {
  flex: 1;
  max-width: 640px;
  padding-bottom: 1.25rem;
}

/* Framed portrait: a real rounded-rectangle photo baseline-aligned with the
   copy — a person in a room, not a floating avatar. Sits above the ambient
   shader (z-index on __inner) and settles into the olive via the inset
   vignette below. */
.hero__photo {
  flex: none;
  width: 250px;
  max-width: 42vw;
  aspect-ratio: 3 / 4;
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(247, 245, 240, 0.42);
  border-radius: var(--radius-surface);
}

.hero__photo a,
.hero__photo img {
  display: block;
}

.hero__photo a {
  width: 100%;
  height: 100%;
}

.hero__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Quiet inset vignette melts the portrait into the drenched surface —
   an inset, not a resting drop shadow (Earned Shadow Rule holds). */
.hero__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 42px 6px rgba(74, 94, 70, 0.28);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .hero__photo img { transition: none; }
}

.hero__tagline {
  font-family: var(--font-body);
  font-size: var(--font-size-label);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
color: color-mix(in srgb, var(--color-cream) 75%, transparent);
  margin-bottom: 1.5rem;
}

.hero h1 {
  margin: 0 0 1.5rem;
  color: var(--color-cream);
}

/* Last line in the hero now that the booking pill has moved to the header — the
   CTA it used to lead into lives in the bar above, always in view. */
.hero__subtitle {
  font-size: var(--font-size-lede);
  color: rgba(247, 245, 240, 0.92);
  max-width: 500px;
  margin: 0;
  line-height: 1.55;
}

/* ── Icons ── */
/* Inline Lucide SVGs sized in em so existing font-size rules control them,
   the same way FontAwesome glyphs flowed with text. */
.ico {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.125em;
  flex-shrink: 0;
}

/* ── Color Accent Utilities ── */
.icon--sage { color: var(--color-sage); }
.icon--amber { color: var(--color-amber); }
.icon--rust { color: var(--color-rust); }
.icon--slate { color: var(--color-slate); }
.icon--olive { color: var(--color-olive); }

/* ── Delight: quiet asides ── */
/* A single quiet, human aside, set in the serif italic like a note left
   in the margin. One deliberate moment, not a recurring pattern. */
.reassurance {
  max-width: 480px;
  margin: 2.75rem auto 0;
  text-align: center;
  font-family: var(--font-heading);
  font-size: var(--font-size-lede);
  font-style: italic;
  line-height: 1.5;
  color: var(--heading);
}

/* ── Section Styles ── */
.section {
  padding: var(--section-padding);
}

.section--alt {
  background-color: var(--surface-alt);
}

.section__label {
  font-family: var(--font-body);
  font-size: var(--font-size-label);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--eyebrow);
  margin-bottom: 1rem;
}

.section__header {
  max-width: var(--max-width-text);
  margin: 0 auto 3rem;
}

/* The line under a section heading: narrower than the heading's measure, so the
   two don't set to the same width and flatten into a block. */
.section__lede {
  max-width: 540px;
  margin: 0 auto;
  color: var(--color-text-light);
}

/* ── Who I Work With (Home) ── */
.audience-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 2rem;
  max-width: var(--max-width-wide);
  margin: 0 auto;
}

.audience-card {
  padding: 1.5rem 0;
}

.audience-card__icon {
  font-size: 2rem;
  margin-bottom: 1rem;
}

.audience-card h3 {
  font-size: var(--font-size-title);
  margin-bottom: 0.75rem;
}

.audience-card p {
  font-size: var(--font-size-body);
  color: var(--color-text-light);
  line-height: 1.7;
}

/* ── Services Preview (Home) ── */
.services-preview {
  max-width: var(--max-width-text);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

.service-preview-item {
  display: flex;
  align-items: flex-start;
  gap: 1.5rem;
}

.service-preview-item__icon {
  flex-shrink: 0;
  font-size: 1.5rem;
  margin-top: 1.5rem;
}

.service-preview-item h3 {
  margin-bottom: 0.5rem;
}

.service-preview-item p {
  font-size: var(--font-size-body);
  color: var(--color-text-light);
}

/* ── Services Index (Home "What I Offer") ──
   A compact, left-headed contents band: the section title sits in its own
   column beside a tight, hairline-separated list of services with soft
   tinted icon chips (sage / amber / rust, matching the icon rotation). */
.services-index {
  padding: clamp(2.5rem, 5vw, 4rem) 2rem;
  background-color: var(--surface-alt);
}

.services-index__inner {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.5fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.services-index__eyebrow {
  font-family: var(--font-body);
  font-size: var(--font-size-label);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--eyebrow);
  margin: 0 0 0.6rem;
}

.services-index__title {
  font-size: clamp(2rem, 3.5vw, 3rem);
  line-height: 1.1;
  margin: 0;
}

.services-index__items {
  list-style: none;
  margin: 0;
  padding: 0;
}

.services-index__item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1.1rem;
  align-items: start;
  padding-block: 1.35rem;
}

.services-index__item + .services-index__item {
  border-top: 1px solid transparent;
  border-image: var(--divider-fade) 1;
}

/* Icon holder — a circle, the same marker shape as the numbered booking steps,
   so every small round-up of an icon or a number reads as one family. */
.services-index__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: var(--radius-circle);
  font-size: 1.15rem;
  flex-shrink: 0;
}

.services-index__chip--sage { background-color: var(--chipicon-sage-bg); color: var(--chipicon-sage-fg); }
.services-index__chip--amber { background-color: var(--chipicon-amber-bg); color: var(--chipicon-amber-fg); }
.services-index__chip--rust { background-color: var(--chipicon-rust-bg); color: var(--chipicon-rust-fg); }

.services-index__name {
  font-size: var(--font-size-title);
  margin: 0 0 0.25rem;
}

.services-index__desc {
  font-size: var(--font-size-body);
  line-height: 1.65;
  color: var(--color-text);
  margin: 0;
}

@media (max-width: 768px) {
  .services-index__inner {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
}

/* ── CTA Banner ── */
.cta-banner {
  position: relative;
  overflow: hidden;
  padding: var(--section-padding);
  text-align: center;
  background-color: var(--color-olive);
  color: rgba(247, 245, 240, 0.9);
}

.cta-banner__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Lift the banner's copy above the ambient shader canvas. */
.cta-banner > :not(.cta-banner__canvas) {
  position: relative;
  z-index: 1;
}

.cta-banner h2 {
  color: var(--color-cream);
  margin-bottom: 1rem;
}

.cta-banner p {
  color: rgba(247, 245, 240, 0.88);
  max-width: 500px;
  margin: 0 auto 2rem;
  font-size: var(--font-size-body);
}

/* The booking CTA on the drenched banner is the amber .cta-pill — the
   one action worth the glow, and the same object the visitor will meet between
   the hands on the Book page.

   The banner's *other* action (the "Email Me" support button on the Book page)
   is .btn--outline-light: a drenched-surface outline button, cream ink on a
   hairline cream border, filling solid cream on hover. A .btn--primary
   must never be used here — its olive fill is the banner's own background, so it
   vanishes into it. Left as a guard so it can't come back invisible. */
.cta-banner .btn--primary {
  background-color: transparent;
  color: var(--color-cream);
  border: 1.5px solid rgba(247, 245, 240, 0.55);
}

/* The base rule's olive-tinted lift is invisible against olive; the 1px rise and
   the fill carry the state, as they do on .btn--outline-light everywhere else. */
.cta-banner .btn--primary:hover {
  background-color: var(--color-cream);
  border-color: var(--color-cream);
  color: var(--color-olive);
  box-shadow: none;
}

/* Olive on olive would be invisible — the same swap the amber pill makes here. */
.cta-banner .btn:focus-visible {
  outline-color: var(--color-cream);
}

/* ── About Page ── */
.about-intro-split {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 4rem;
}

.about-intro-split__text {
  flex: 1;
  max-width: var(--max-width-text);
}

.about-intro-split__photo {
  flex: 0 0 280px;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-surface);
}

.about-intro {
  max-width: var(--max-width-text);
  margin: 0 auto;
}

.about-intro p {
  color: var(--color-text-light);
}

/* Quiet inline trust-signal icons inside prose (e.g. CRPO registration badge) */
.about-intro p > .ico {
  margin-right: 0.4em;
  color: var(--accent-marker);
}

.approach-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  max-width: var(--max-width-wide);
  margin: 0 auto;
}

.approach-card {
  padding: 2rem 0;
}

.approach-card__icon {
  font-size: 2rem;
  margin-bottom: 1rem;
}

.approach-card h3 {
  margin-bottom: 0.75rem;
}

.approach-card p {
  font-size: var(--font-size-body);
  color: var(--color-text-light);
  line-height: 1.7;
}

.values-list {
  max-width: var(--max-width-text);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 1.75rem;
}

.values-list__item {
  display: flex;
  align-items: flex-start;
  gap: 1.25rem;
}

/* Faded hairline between values. The item keeps the list's 1.75rem flex gap
   above the rule; a matching padding-top drops it below, so the divider sits
   centred in the space between each pair. */
.values-list__item + .values-list__item {
  padding-top: 1.75rem;
  border-top: 1px solid transparent;
  border-image: var(--divider-fade) 1;
}

.values-list__marker {
  flex-shrink: 0;
  color: var(--accent-marker);
  font-size: 1.25rem;
  margin-top: 0.1em;
}

.values-list__item h3 {
  font-size: var(--font-size-title);
  margin-bottom: 0.375rem;
}

.values-list__item p {
  font-size: var(--font-size-body);
  color: var(--color-text-light);
}

/* ── Services Page ── */
.service-detail {
  max-width: var(--max-width-text);
  margin: 0 auto;
}

.service-detail + .service-detail {
  margin-top: 4rem;
}

.service-detail__icon {
  font-size: 2rem;
  margin-bottom: 1rem;
}

.service-detail h2 {
  font-size: var(--font-size-subheading);
  margin-bottom: 1rem;
}

.service-detail p {
  font-size: var(--font-size-body);
  color: var(--color-text-light);
}

.service-detail .focus-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.25rem;
}

.focus-tag {
  display: inline-block;
  font-size: var(--font-size-label);
  font-weight: 500;
  padding: 0.375rem 0.875rem;
  border-radius: var(--radius-pill);
  background-color: var(--tag-sage-bg);
  color: var(--tag-sage-fg);
}

.focus-tag:nth-child(3n+2) {
  background-color: var(--tag-amber-bg);
  color: var(--tag-amber-fg);
}

.focus-tag:nth-child(3n+3) {
  background-color: var(--tag-neutral-bg);
  color: var(--tag-neutral-fg);
}

/* Pill highlight for the registration number — focus-tag shape/colour, but
   keeps the surrounding body text size instead of shrinking like a tag. */
.reg-pill {
  display: inline-block;
  font-weight: 400;
  padding: 0.2em 0.7em;
  border-radius: var(--radius-pill);
  background-color: var(--tag-sage-bg);
  color: var(--tag-sage-fg);
}

/* ── Credentials list ──
   A flat, scannable list of qualifications, training, and memberships. Each row
   pairs a sage check-badge with the credential text so the section reads as a
   record rather than a paragraph. Flat by default, matching the section. */
.cred-list__heading {
  margin-top: 1.75rem;
}

.cred-list {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: grid;
  gap: 1.25rem;
}

.cred-list__item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}

.cred-list__item .ico {
  flex: none;
  width: 1rem;
  height: 1rem;
  margin-top: 0.20em;
  color: var(--tag-sage-fg);
}

.cred-list__name {
  color: var(--color-text-light);
  display: block;
  line-height: 1.5;
}

/* Details ride the eyebrow treatment — small caps, tracked out, olive — so the
   credential name reads first and the institution/year sits quietly beneath. */
.cred-list__meta {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--font-size-label);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--eyebrow);
}

/* ── Session Format: In-Person & Virtual ──
   Two plain option cards — a Lucide icon, a heading, a line of copy — matching
   the service blocks above. No canvas, no glow, no bespoke animation; they ride
   the site-wide data-reveal fade like everything else. */
.format-options {
  max-width: var(--max-width-text);
  margin: 0 auto;
  display: grid;
  place-items: center;
  grid-template-columns: 1fr auto 1fr;
  gap: clamp(2rem, 6vw, 4rem);
}

/* Hairline separator between the two formats — vertical when they sit
   side by side, flipped horizontal once they stack. */
.format-divider {
  align-self: stretch;
  width: 1px;
  background-image: var(--divider-fade-v);
}

.format-option {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.format-option h3 {
  font-size: var(--font-size-title);
  text-align: center;
  margin-bottom: 0.5rem;
}

.format-option p {
  color: var(--color-text-light);
  max-width: 240px;
  text-align: center;
}

@media (max-width: 640px) {
  .format-options {
    grid-template-columns: 1fr;
    gap: 2.5rem;
  }

  .format-divider {
    align-self: center;
    justify-self: stretch;
    width: auto;
    height: 1px;
    background-image: var(--divider-fade);
  }
}

/* ── Book Page: the hand-off ── */
/* The booking moment is a composition, not a card: two photographic arms reach
   in from the section edges and present one amber pill, held in a soft
   membrane of warm light. Flat cream everywhere else — the light and the button
   are the only event on the screen, so the next step needs no explaining. */
.booking {
  /* Hands track the free space beside the 560px central zone so ~120px of
     fingertips always reaches into the portal light, whatever the viewport.
     --cta-offset lifts the aim point from the section's geometric center up to
     the button (the meta + email lines hang below it in the flow). */
  --hand-w: min(520px, calc((100vw - 560px) / 2 + 120px));
  --hand-h: calc(var(--hand-w) * 0.4);
  --cta-offset: -2.75rem;
  /* Hands rest bled this far past the section edge, so the hover lean
     (translating them the same distance inward, see main.js) brings them
     flush with the edge instead of opening a gap behind them. */
  --hand-lean: 8px;
  position: relative;
  overflow: hidden;
  /* Top padding stays generous (and never below 6.5rem) so the enlarged warm
     pool on wide screens clears the section's top edge — overflow:hidden must
     never crop it. The bottom is trimmed hard: past ~1050px the reaching hands
     sit mid-height, so all the slack below the hand-off was dead cream before
     "What to Expect". Trimming only the bottom keeps the mid-height hands framed
     (this bottom value is overridden below ~1050px, where the hands drop into a
     band of their own). */
  padding: clamp(6.5rem, 8vw, 7rem) 2rem clamp(3rem, 5vw, 5rem);
  background-color: var(--surface-page);
}

/* Two arms reach toward each other from the section edges, presenting the
   button between their open palms. Decorative only: hidden from AT, inert to
   the pointer. The 5/2 cover crop trims the source images' transparent padding
   so positioning tracks the arm itself, not the empty canvas around it. */
.booking__hand {
  position: absolute;
  z-index: 0;
  width: var(--hand-w);
  height: var(--hand-h);
  object-fit: cover;
  pointer-events: none;
  user-select: none;
}

/* Fingertips sit ~62% down the cropped band; these offsets aim the left hand's
   fingers just above the button's midline and the right hand's just below —
   the slight diagonal keeps the reach human rather than symmetrical. */
.booking__hand--left {
  left: calc(var(--hand-lean) * -1);
  top: calc(50% + var(--cta-offset) - var(--hand-h) * 0.66);
}

.booking__hand--right {
  right: calc(var(--hand-lean) * -1);
  top: calc(50% + var(--cta-offset) - var(--hand-h) * 0.5);
}

/* Below ~1050px there is no room beside the composition, so both hands drop
   into a single band beneath it, still reaching toward each other from the
   viewport edges. Only the bottom padding grows to make room for the band. */
@media (max-width: 1050px) {
  .booking {
    --hand-w: min(48vw, 360px);
    padding-bottom: calc(3rem + var(--hand-h));
  }

  .booking__hand--left {
    top: auto;
    bottom: 3rem;
  }
  .booking__hand--right {
    top: auto;
    bottom: 0.75rem;
  }
}

/* Narrow mobile: the page header and the hand-off share one full-viewport flex
   column (like the home hero), so the whole composition fits on the first
   screen. The header takes its natural height at the top; the booking fills the
   rest with the pill centred and the two reaching hands closing it at the very
   bottom edge — the last thing in view before the fold. min-height (not height)
   so a short viewport grows and scrolls instead of clipping the hands. */
@media (max-width: 768px) {
  .book-lede {
    min-height: 100vh;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
  }

  .book-lede .page-header {
    flex: 0 0 auto;
  }

  .booking {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-top: 2rem;
    padding-bottom: calc(var(--hand-h) + 1.5rem);
  }

  /* Nudge the stage a touch above optical centre so the copy under the pill and
     the hands below both breathe. */
  .booking__stage {
    margin-top: -0.5rem;
  }

  /* Bottom offsets are height-aware: on a tall phone they stay at their
     original 5.75rem / 4.25rem (the clamp cap), preserving the diagonal reach.
     As the viewport gets shorter the vh term wins and drops the hands toward
     the bottom edge, so they keep clearing the copy under the pill instead of
     climbing into it. The cap means they never rise higher than before. */
  .booking__hand--left {
    bottom: clamp(1.5rem, 7vh, 5.75rem);
  }

  .booking__hand--right {
    bottom: clamp(0.5rem, 5vh, 4.25rem);
  }
}

/* Tablet (769–1050px): there's still enough width to keep the hand-off
   horizontal like widescreen, so the hands flank the button at its own level
   instead of dropping into the band beneath it (the 1050px rules above). The
   stage and pill step down a size, and the hands scale with the side margin —
   a reach term added to that margin keeps the fingertips landing in the portal
   at every tablet width, the cap only trimming the widest end. This block sits
   after the 768px mobile rules so it wins for 769–1050px and never touches the
   mobile composition. */
@media (min-width: 769px) and (max-width: 1050px) {
  .booking {
    /* Each hand spans only the side margin of a notional 440px zone, so its
       inner edge lands ~20px *outside* the 400px stage — a steady gap of
       roughly a finger's length between the fingertips and the pill for most of
       the range, never an overlap. The 330px cap trims the widest end; the
       220px floor stops the hands shrinking away as the viewport nears the
       mobile breakpoint (the gap narrows a little there but stays clear). */
    --hand-w: clamp(270px, calc((100vw - 440px) / 2), 330px);
    /* Drop the band's bottom padding; the hands are back beside the pill. */
    padding-bottom: clamp(3rem, 5vw, 5rem);
  }

  /* Narrower stage so the flanking hands have room to close in on the pill. */
  .booking__stage {
    max-width: 400px;
  }

  /* The pill steps down to sit comfortably between the smaller hands. */
  .booking__cta {
    font-size: clamp(1.0625rem, 0.7rem + 0.9vw, 1.1875rem);
    padding: 1.125rem 2.25rem;
  }

  /* Restore the widescreen side placement — the 1050px band rules above pinned
     these to the bottom edge. */
  .booking__hand--left {
    top: calc(50% + var(--cta-offset) - var(--hand-h) * 0.66);
    bottom: auto;
  }
  .booking__hand--right {
    top: calc(50% + var(--cta-offset) - var(--hand-h) * 0.5);
    bottom: auto;
  }
}

/* ── The stage: portal, button, one supporting line ── */
.booking__stage {
  position: relative;
  z-index: 1;
  max-width: 560px;
  margin: 0 auto;
  text-align: center;
  padding-bottom: 1rem;
}

/* The portal: an irregular oval of warm blurred light behind the button — calm
   and slightly surreal, never glossy. Purposeful diffusion (the membrane blurs
   the fingertips that reach behind it), not decorative glassmorphism. The
   element itself only positions; entrance/hover motion lands on
   its children so GSAP and the CSS keyframe never fight over one transform. */
.booking__portal {
  position: absolute;
  left: 50%;
  top: 0.5rem;
  width: min(320px, 40vw);
  aspect-ratio: 29 / 18;
  transform: translate(-50%, -46%);
  pointer-events: none;
}

.booking__portal-glow {
  position: absolute;
  inset: -4%;
  background: radial-gradient(closest-side at 50% 52%,
    rgba(255, 244, 214, 0.95),
    rgba(250, 236, 200, 0.45) 55%,
    rgba(247, 245, 240, 0) 78%);
}

/* Dark mode: on cream the near-white glow melts away, but on the near-black it
   flares as a hard-edged spotlight. Dim it to a soft amber lamp-halo, shrink
   it, and fade it out earlier so it stays a compact bloom centred behind the
   pill (same top anchor as light) without a hard clip edge. */
:root[data-theme="dark"] .booking__portal {
  width: min(320px, 40vw);
  /* Pinned so the flatter light-theme aspect (see the wide-screen block below)
     never leaks into dark, which keeps its compact lamp-halo. */
  aspect-ratio: 29 / 18;
}

:root[data-theme="dark"] .booking__portal-glow {
  inset: 0;
  background: radial-gradient(closest-side at 50% 56%,
    rgba(244, 212, 138, 0.34),
    rgba(233, 174, 30, 0.16) 52%,
    rgba(232, 170, 20, 0) 74%);
  filter: blur(28px);
}

/* Wide screens: on cream the flat expanse around the hand-off read empty, so
   the warm light pool grows to fill the centre behind the amber pill — wider
   and flatter, so it spreads across the void horizontally without gaining
   height that would climb into the header and get clipped by overflow:hidden.
   It still reads as one soft diffuse membrane, and it sits *behind* the CTA, so
   it lifts the one amber action rather than competing with it (no second amber
   element, no resting shadow). Light theme only: dark keeps its compact
   lamp-halo (pinned above), and the ≤768px stacked layout keeps the base pool.
   Placed after the dark blocks so it wins the cascade in light without
   out-specifying them in dark. */
@media (min-width: 769px) {
  .booking__portal {
    width: min(560px, 64vw);
    aspect-ratio: 48 / 17;
  }

  .booking__portal-glow {
    background: radial-gradient(ellipse farthest-side at 50% 52%,
      rgba(255, 244, 214, 0.95),
      rgba(250, 236, 200, 0.42) 58%,
      rgba(247, 245, 240, 0) 100%);
  }
}

/* ── The amber booking pill: the brand's one booking action ──
   The same capsule language as .btn — flat at rest, no resting shadow — but
   filled with the rationed amber. Its shadow is earned on hover/focus, where
   it reads as a warm glow rather than a lift (Earned Shadow Rule). */
.cta-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-body);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--color-amber-ink);
  text-decoration: none;
  padding: 1.125rem 2.5rem;
  border-radius: var(--radius-pill);
  background-color: var(--color-amber);
  transition: background-color 0.3s ease, box-shadow 0.3s ease;
}

.cta-pill:hover,
.cta-pill:focus-visible {
  color: var(--color-amber-ink);
  background-color: var(--color-amber-hover);
  box-shadow:
    0 0 0 5px rgba(232, 170, 20, 0.18),
    0 0 28px rgba(232, 170, 20, 0.4);
}

.cta-pill:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 4px;
}

.cta-pill__arrow {
  width: 1.05em;
  height: 1.05em;
  flex-shrink: 0;
  transition: transform 0.3s var(--ease-out-quart);
}

.cta-pill:hover .cta-pill__arrow,
.cta-pill:focus-visible .cta-pill__arrow {
  transform: translateX(4px);
}

/* On the drenched olive CTA banner only the focus ring has to change:
   olive on olive would be invisible. */
.cta-banner .cta-pill:focus-visible {
  outline-color: var(--color-cream);
}

/* Header: the same button, made small enough to live in a 64px bar without
   shouting. The hover glow pulls in close so it stays quiet on the cream bar.
   This slim capsule is the shape at *every* width — the mobile bar doesn't get a
   squatter, chunkier variant of the site's one booking action. */
.cta-pill--nav {
  font-size: var(--font-size-supporting);
  gap: 0.45rem;
  padding: 0.6875rem 1.375rem;
}

/* The capsule is ~37px tall, under the 44px touch minimum. Rather than fatten it
   on mobile (which is what made it morph between breakpoints), the tap target is
   extended past the paint: an invisible 4px collar top and bottom takes the
   press, so the finger gets 45px and the eye keeps the slim pill. */
.cta-pill--nav::after {
  content: "";
  position: absolute;
  inset: -4px 0;
}

.cta-pill--nav:hover,
.cta-pill--nav:focus-visible {
  box-shadow:
    0 0 0 4px rgba(232, 170, 20, 0.16),
    0 0 18px rgba(232, 170, 20, 0.35);
}
.cta-pill--nav:active {
  color: rgba(61, 38, 0, 0.72);
  background-color: var(--color-taupe);
  box-shadow: none;
}

.cta-pill--nav .cta-pill__arrow {
  width: 0.95em;
  height: 0.95em;
}

/* Book page: the header carries no Book pill at all — the one amber action on
   that screen is the button between the hands (one booking action per screen,
   the Rationed Amber Rule). The markup simply omits it. */

/* Book page: the pill is the whole composition there, held between the palms
   with nothing else on the stage, so it steps up a size. */
.booking__cta {
  font-size: clamp(1.125rem, 0.8rem + 1vw, 1.3125rem);
  padding: 1.375rem 2.875rem;
}

/* One concise editorial line beneath the pill — no icon row, no columns.
   It breathes open a touch while the visitor is on the button. */
.booking__meta {
  position: relative;
  margin: 2rem auto 0;
  font-size: var(--font-size-supporting);
  letter-spacing: 0.02em;
  color: var(--color-text);
}

.booking__meta span {
  color: var(--color-text-light);
}

/* Low-pressure fallback, quiet beneath the metadata line */
.booking__alt {
  position: relative;
  margin: 0.875rem auto 0;
  font-size: var(--font-size-supporting);
  color: var(--color-text-light);
}

.booking__alt a {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Shown only when the hand-off fails (main.js unhides it). Full ink, not the
   muted grey — this is the one line standing between the visitor and a dead
   end, so it does not get to be quiet. No alarm colour, no card: the tone is
   "here's the other door", not "error". */
.booking__error {
  margin: 1rem auto 0;
  max-width: 32ch;
  font-size: var(--font-size-supporting);
  color: var(--color-text);
}

.booking__error a {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* ── Entrance pre-hide ──
   The hand-off's entrance is a GSAP timeline (see main.js), and GSAP cannot
   hide these until three network-fetched scripts have landed. The cream
   .page-veil, meanwhile, lifts on a 0.45s CSS clock that owes the network
   nothing — so on a slow CDN the veil uncovers a fully-formed amber pill, GSAP
   then arrives and yanks it to opacity 0, and the entrance replays. (Safari
   loses that race routinely; Chromium usually wins it and hides the seam.)
   Hiding them in CSS instead puts the start state on the same clock as the
   veil. Scoped to .js, mirroring the [data-reveal] system above; main.js
   restores them if GSAP never arrives, so nothing is ever stranded. */
.js .booking__hand,
.js .booking__portal-glow,
.js .booking__cta,
.js .booking__meta,
.js .booking__alt {
  opacity: 0;
}

/* Reduced motion plays no entrance at all — main.js bails before the timeline,
   so the section must simply ship visible. */
@media (prefers-reduced-motion: reduce) {
  .js .booking__hand,
  .js .booking__portal-glow,
  .js .booking__cta,
  .js .booking__meta,
  .js .booking__alt {
    opacity: 1;
  }
}

/* ── Click transition: the portal light expands to the whole screen before the
   booking page opens. Driven entirely by JS; inert at rest and under reduced
   motion. Sits above the fixed header (1000) and the skip link (1100). ── */
.booking__veil {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--veil);
  clip-path: circle(0px at 50% 50%);
  visibility: hidden;
  pointer-events: none;
}

.booking__veil-note {
  font-family: var(--font-heading);
  font-style: italic;
  font-size: 1.375rem;
  color: var(--heading);
  opacity: 0;
}

@media (max-width: 640px) {
  .booking__cta {
    padding: 1.2rem 2.3rem;
  }

  .booking__portal {
    width: min(480px, 96vw);
  }
}

/* Reduced motion: everything parked in place and fully legible — no breathing,
   no veil (the link navigates plainly). */
@media (prefers-reduced-motion: reduce) {
  .booking__portal {
    animation: none;
  }

  .cta-pill {
    transition: none;
  }

  .cta-pill__arrow,
  .booking__meta {
    transition: none;
  }

  .cta-pill:hover .cta-pill__arrow,
  .cta-pill:focus-visible .cta-pill__arrow {
    transform: none;
  }

  .booking__veil {
    display: none;
  }
}

/* The three steps read as one ordered sequence: numbers sit in olive rings
   linked by a hairline connector. Amber is deliberately kept off these
   markers so it stays reserved for the booking action (Rationed Amber Rule). */
.book-steps {
  max-width: var(--max-width-text);
  margin: 0 auto;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3rem;
  text-align: center;
  position: relative;
}

.book-steps::before {
  content: "";
  position: absolute;
  top: 1.5rem;
  left: 16.666%;
  right: 16.666%;
  height: 1px;
  background-color: var(--divider);
  z-index: 0;
}

.book-step {
  position: relative;
  z-index: 1;
}

.book-step__number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-circle);
  border: 1.5px solid var(--heading);
  background-color: var(--surface-raised);
  font-family: var(--font-heading);
  font-size: 1.35rem;
  font-weight: 600;
  color: var(--heading);
  line-height: 1;
  margin-bottom: 1rem;
}

.book-step h3 {
  font-size: var(--font-size-title);
  margin-bottom: 0.5rem;
}

.book-step p {
  font-size: var(--font-size-body);
  color: var(--color-text-light);
}

@media (max-width: 640px) {
  .book-steps {
    grid-template-columns: 1fr;
  }
  .book-steps::before {
    display: none;
  }
}

/* ── Page Header (inner pages) ── */
.page-header {
  padding: 9rem 2rem 4rem;
  text-align: center;
}

.page-header h1 {
  margin-bottom: 1rem;
}

.page-header p {
  font-size: var(--font-size-lede);
  color: var(--color-text-light);
  max-width: 540px;
  margin: 0 auto;
}

/* ── Footer ── */
.site-footer {
  padding: 3.5rem 2rem 2.5rem;
  border-top: 1px solid transparent;
  border-image: var(--divider-fade-soft) 1;
}

.site-footer__inner {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 2rem;
}

.site-footer__brand {
  font-family: var(--font-heading);
  font-size: var(--font-size-title);
  letter-spacing: -0.03em;
  font-weight: 600;
  color: var(--heading);
  margin-bottom: 0.5rem;
}

.site-footer__contact {
  font-size: var(--font-size-supporting);
  color: var(--color-text-light);
  line-height: 1.8;
}

.site-footer__contact a {
  color: var(--color-text-light);
}

.site-footer__contact a:hover {
  color: var(--heading);
}

.site-footer__nav {
  display: flex;
  gap: 1.75rem;
}

.site-footer__nav a {
  font-size: var(--font-size-supporting);
  color: var(--color-text-light);
}

.site-footer__nav a:hover {
  color: var(--heading);
}

/* ── Crisis line ──
   A private practice cannot answer a 2am message, and people in acute distress
   do land here. So the other door is permanent, on every page, and it says so
   plainly. Deliberately not amber, not a banner, not a modal: alarm styling
   would frighten the many to serve the few. It sits between the footer body
   and the copyright — a calm, findable line that reads as competence. Full ink
   on the number itself, because this is the one string that must be legible at
   a glance to someone who is not really reading. */
.site-footer__crisis {
  max-width: var(--max-width-wide);
  margin: 2rem auto 0;
  font-size: var(--font-size-supporting);
  line-height: 1.7;
  color: var(--color-text-light);
}

.site-footer__crisis strong {
  font-weight: 600;
  color: var(--color-text);
}

.site-footer__crisis a {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.site-footer__crisis a:hover {
  color: var(--heading);
}

.site-footer__bottom {
  max-width: var(--max-width-wide);
  margin: 1.75rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid transparent;
  border-image: var(--divider-fade-soft) 1;
  text-align: center;
  font-size: var(--font-size-label);
  color: var(--color-text-light);
}

/* ── GSAP scroll choreography (home page) ──
   Reveal targets start hidden (opacity only — no layout shift) and are brought
   in by GSAP ScrollTrigger in main.js. Scoped to .js so no-JS visitors ship
   fully visible; forced visible under reduced motion (GSAP short-circuits to a
   static reveal there too). If GSAP fails to load, main.js reveals them on load
   so nothing is ever stranded at opacity 0. */
.js [data-reveal],
.js [data-reveal-group] > * {
  opacity: 0;
}

/* The hero copy carries a scrubbed parallax as the hero scrolls away — a sink,
   not a fade: it stays fully legible until it leaves the viewport. */
.js .hero__content {
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-reveal-group] > * {
    opacity: 1 !important;
    transform: none !important;
  }
  .js .hero__content {
    will-change: auto;
  }
}

/* ── Hero Entrance Motion ──
   Hidden-then-reveal states are scoped to .js so the page always ships
   fully visible when JavaScript is unavailable — no blank sections.
   Scroll reveals are handled by the GSAP data-reveal system above; this
   block is the hero's one-time page-load choreography.

   Hero page-load choreography: content rises in sequence on first paint.
   Pure CSS with a forwards fill, so it always completes on its own; the
   .js scope keeps no-JS visitors on a fully static hero. */
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes hero-rise-photo {
  from { opacity: 0; transform: translateY(18px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.js .hero__tagline,
.js .hero h1,
.js .hero__subtitle {
  opacity: 0;
  animation: hero-rise 0.8s var(--ease-out-quart) forwards;
}

.js .hero__photo {
  opacity: 0;
  animation: hero-rise-photo 0.9s var(--ease-out-quart) forwards;
}

/* The cue arrives last — only once the visitor has had the headline. Opacity
   only, so it never fights the drift running inside it. */
.js .hero__scroll {
  opacity: 0;
  animation: hero-scroll-in 0.8s var(--ease-out-quart) 1.1s forwards;
}

@keyframes hero-scroll-in {
  to { opacity: 1; }
}

.js .hero__tagline  { animation-delay: 0.15s; }
.js .hero h1        { animation-delay: 0.28s; }
.js .hero__subtitle { animation-delay: 0.42s; }
.js .hero__photo    { animation-delay: 0.34s; }

/* ── Reduced Motion ── */
@media (prefers-reduced-motion: reduce) {
  .js .hero__tagline,
  .js .hero h1,
  .js .hero__subtitle,
  .js .hero__photo,
  .js .hero__scroll,
  .js .site-nav__link,
  .js .site-nav__footer {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
    transition: none !important;
  }

  /* Hamburger still morphs to an X (it's a state indicator, not decoration) —
     just without the tween or the load-in fade. */
  .js .menu-toggle__icon,
  .menu-toggle__icon,
  .menu-toggle__icon::before,
  .menu-toggle__icon::after,
  .menu-overlay {
    animation: none !important;
    transition: none !important;
  }

  html {
    scroll-behavior: auto;
  }
}

/* ── Responsive ── */
@media (max-width: 768px) {
  :root {
    --section-padding: var(--section-padding-mobile);
  }

  .site-header__inner {
    padding: 0.875rem 1.25rem;
  }

  /* The toggle drops into the drawer here, so the pill ends up sitting directly
     beside the hamburger — it needs real breathing room between the two, not the
     tight gap that made sense in a three-item cluster. */
  .site-header__actions {
    gap: 0.75rem;
    margin-left: 0;
  }

  /* The booking pill deliberately stays *under* the open drawer (z-index:999) —
     the menu is a modal moment, and the pill glowing on top of it would be one
     invitation too many. Only the toggle is lifted, so the X stays tappable;
     without an explicit position the static button would paint under the
     positioned drawer regardless of the numbers. */
  .menu-toggle {
    display: flex;
    position: relative;
    z-index: 1001;
  }

  /* The pill is deliberately *not* reshaped here: same type size, same padding,
     same slim capsule as the desktop bar, so the site's one booking action never
     morphs as the bar narrows. Its tap collar (see .cta-pill--nav::after) already
     holds the 44px touch target. Only the tightest phones trim its horizontal
     padding — see the 480px block. */

  .site-nav {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(80vw, 310px);
    background-color: var(--surface-drawer);
    padding: 5.25rem 1.75rem 2rem;
    transform: translateX(100%);
    transition: transform 0.35s var(--ease-out-quart);
    box-shadow: -8px 0 30px rgba(0, 0, 0, 0.12);
    z-index: 999;
    border-left: 1px solid var(--divider-soft);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }

  .site-nav--open {
    transform: translateX(0);
  }

  .site-nav__list {
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0;
    align-items: stretch;
    margin-top: 0.25rem;
  }

  /* Each list item grows to share the panel height equally (the <li> is the
     flex child of the list, so the distribution has to live here), and the
     link stretches to fill its row — so the four links spread from the header
     down to the contact block and fill the drawer instead of clustering up top. */
  .site-nav__list li {
    flex: 1 1 0;
    display: flex;
  }

  /* Full-width divider rows give the four links real structure. */
  .site-nav__link {
    display: flex;
    flex: 1;
    align-items: center;
    min-height: 56px;
    font-size: 1.5rem;
    color: var(--color-text);
    border-bottom: 1px solid transparent;
    /* Left-anchored fade so the divider starts with the left-aligned links
       and trails off to the right, instead of peaking in the center. */
    border-image: linear-gradient(90deg, var(--divider), transparent 80%) 1;
  }

  .site-nav__list li:last-child .site-nav__link {
    border-image: none;
    border-bottom: none;
  }

  /* The desktop grow-underline clashes with the row dividers here */
  .site-nav__link::after {
    display: none;
  }

  .site-nav__link:hover,
  .site-nav__link--active {
    color: var(--heading);
    font-weight: 500;
  }

  /* Contact block anchored to the bottom — fills what was empty space below
     the links without piling on another booking CTA. */
  .site-nav__footer {
    display: block;
    margin-top: auto;
    padding-top: 1.75rem;
    text-align: center;
  }

  /* The header instance would crowd the 360px bar; the toggle lives in the
     drawer footer on mobile instead, as a centred capsule above the contact
     block. */
  .theme-toggle--nav {
    display: none;
  }

  /* A quiet centred pill sitting above the contact block — no separator, no
     full-width row. Capsule shape (the system's "anything you press" radius)
     with a hairline outline that warms to sage on hover; centred by the
     footer's text-align. */
  .theme-toggle--drawer {
    width: auto;
    height: auto;
    min-height: 44px;
    justify-content: center;
    gap: 0.5rem;
    margin: 0 0 0.25rem;
    padding: 0.6rem 1.25rem;
    border: 1px solid var(--divider);
    border-radius: var(--radius-pill);
    color: var(--link);
    font-family: var(--font-body);
    font-size: var(--font-size-supporting);
    font-weight: 500;
    letter-spacing: 0.02em;
  }

  .theme-toggle--drawer:hover,
  .theme-toggle--drawer:focus-visible {
    color: var(--heading);
    border-color: var(--color-sage);
    background-color: var(--tag-sage-bg);
  }

  .theme-toggle--drawer:focus-visible {
    border-radius: var(--radius-pill);
  }

  .theme-toggle--drawer .theme-toggle__label {
    display: inline;
  }

  .site-nav__contact {
    margin-top: 1.25rem;
    font-size: var(--font-size-supporting);
    line-height: 1.7;
    text-align: center;
    color: var(--color-text-light);
  }

  /* Open choreography: links and the contact block ease in from the right in a
     gentle sequence once the drawer is open. The stagger delays live only on the
     open state, so closing lets everything fade straight out with the sliding
     drawer rather than lingering. Scoped to .js so a no-JS drawer (which never
     opens) keeps its links visible. */
  .js .site-nav__link,
  .js .site-nav__footer {
    opacity: 0;
    transform: translateX(14px);
    transition: opacity 0.35s var(--ease-out-quart),
                transform 0.35s var(--ease-out-quart),
                color 0.25s ease;
  }

  .js .site-nav--open .site-nav__link,
  .js .site-nav--open .site-nav__footer {
    opacity: 1;
    transform: none;
  }

  .js .site-nav--open .site-nav__list li:nth-child(1) .site-nav__link { transition-delay: 0.10s; }
  .js .site-nav--open .site-nav__list li:nth-child(2) .site-nav__link { transition-delay: 0.16s; }
  .js .site-nav--open .site-nav__list li:nth-child(3) .site-nav__link { transition-delay: 0.22s; }
  .js .site-nav--open .site-nav__footer { transition-delay: 0.28s; }

  /* When GSAP owns the reveal it sets opacity/visibility/transform inline; drop
     the CSS transition on those props here so the browser doesn't also tween
     them (which would smear GSAP's overshoot). Only colour still transitions. */
  .site-nav--gsap .site-nav__link,
  .site-nav--gsap .site-nav__footer {
    transition: color 0.25s ease;
  }

  /* Overlay crossfades in/out with the drawer (opacity + visibility, so it can
     transition — display can't). */
  .menu-overlay {
    position: fixed;
    inset: 0;
    background-color: rgba(0, 0, 0, 0.25);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s var(--ease-out-quart), visibility 0.3s var(--ease-out-quart);
    z-index: 998;
  }

  .menu-overlay--visible {
    opacity: 1;
    visibility: visible;
  }

  /* Asymmetric padding on purpose: the box is vertically centred in the
     full-height hero, so the heavier top keeps the stack a touch below the
     optical centre, clear of the header. */
  .hero__inner {
    padding: 6.5rem 1.25rem 3.5rem;
    flex-direction: column;
    align-items: center;
    gap: 2.5rem;
    text-align: center;
  }

  .hero__photo {
    flex: none;
    width: 420px;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    order: -1;
    margin: 0 auto;
  }

  .hero h1 {
    font-size: var(--font-size-display);
  }

  .hero__subtitle {
    font-size: var(--font-size-body);
  }

  .about-intro-split {
    flex-direction: column;
    gap: 2rem;
  }

  .about-intro-split__photo {
    flex: none;
    width: 200px;
    clip-path: circle(50%);
    margin: 0 auto;
    position: static;
  }

  .about-intro-split__photo img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
}

  .audience-grid {
    grid-template-columns: 1fr;
  }

  .approach-grid {
    grid-template-columns: 1fr;
  }

  .site-footer__inner {
    flex-direction: column;
    gap: 1.5rem;
  }

  .site-footer__nav {
    flex-wrap: wrap;
    gap: 1rem;
  }

  .page-header {
    padding: 7rem 1.25rem 2rem;
  }
}

@media (max-width: 480px) {
  .btn {
    width: 100%;
    max-width: 280px;
    text-align: center;
  }

  /* Narrower waist so the logo, the pill and the hamburger still share a 360px
     bar. The capsule's height and type stay put — only its width gives. */
  .cta-pill--nav {
    padding-left: 1.125rem;
    padding-right: 1.125rem;
  }
}

/* ── Page transition ──
   Every page in the site sits on the same cream, so navigation crossfades
   through it: the outgoing page washes to cream, the incoming one emerges from
   it. The veil is real markup (not JS-injected) so it is already painted on the
   first frame and the arrival has nothing to catch up with. Scoped to .js — no
   JavaScript means no veil, and plain, instant navigation. */
.page-veil {
  position: fixed;
  inset: 0;
  z-index: 1300;
  background-color: var(--veil);
  opacity: 0;
  pointer-events: none;
}

/* Arrival: the veil is opaque on first paint and lifts, so the new page is
   uncovered rather than swapped in. Pure CSS with a forwards fill — it
   completes on its own even if GSAP never loads. */
.js .page-veil {
  opacity: 1;
  animation: pageVeilLift 0.45s var(--ease-out-quart) forwards;
}

@keyframes pageVeilLift {
  to { opacity: 0; }
}

/* Departure (JS-driven, see main.js): the veil washes back in before the
   browser leaves. It has to swallow clicks while it does, or a second click
   lands on a page that is already on its way out. */
.page-veil--leaving {
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  .js .page-veil {
    display: none;
  }
}

/* ══ Dark theme ══════════════════════════════════════════════════════════════
   The night variant of the Quiet Consulting Room: a warm near-black tinted
   toward the brand olive — never a cold charcoal, so the Warm-Neutral Rule
   holds after dark. The drenched hero / CTA / Book-portal surfaces stay olive
   (their WebGL shader palette is baked and carries over unchanged) and become
   the *lit* panels lifting off the darker page. Amber stays rationed to the one
   booking pill; it only reads warmer against the dark ground.

   A single selector drives the dark theme. The page's JS resolves the active
   theme — a saved pin, else the OS preference — and writes it onto data-theme
   before first paint (see the head script), re-writing it live if the OS flips
   while un-pinned. So the CSS only ever needs to match one attribute; there is
   no separate prefers-color-scheme block to keep in sync. ── */
:root[data-theme="dark"] {
  color-scheme: dark;

  --surface-page: #13160F;
  --surface-alt: #191D14;
  --surface-warm: #1D2016;
  --surface-raised: #232819;
  --surface-header: #13160F;
  --veil: #13160F;

  --color-text: #E6E2D6;
  --color-text-light: #ABA898;

  --heading: #DCE7D6;
  --eyebrow: #A9C0A2;
  --link: #A8C4A3;
  --link-hover: #C6DBC0;
  --accent-marker: #A8C4A3;

  --divider: rgba(203, 193, 173, 0.14);
  --divider-soft: rgba(203, 193, 173, 0.09);
  --focus-ring: #A8C4A3;

  --tag-sage-bg: rgba(143, 175, 138, 0.2);
  --tag-sage-fg: #A8C4A3;
  --tag-amber-bg: rgba(232, 170, 20, 0.18);
  --tag-amber-fg: var(--color-amber);
  --tag-neutral-bg: rgba(203, 193, 173, 0.14);
  --tag-neutral-fg: #E6E2D6;

  --chipicon-sage-bg: rgba(143, 175, 138, 0.22);
  --chipicon-sage-fg: #A8C4A3;
  --chipicon-amber-bg: rgba(232, 170, 20, 0.2);
  --chipicon-amber-fg: var(--color-amber);
  --chipicon-rust-bg: rgba(215, 78, 9, 0.24);
  --chipicon-rust-fg: #E8896A;
}

/* ══ Long-string resilience ═══════════════════════════════════════════════════
   Every string on the page is fixed, short copy today, but the contact blocks
   carry the one class of value likely to be swapped for something longer later —
   a real email address or a longer domain. Let those break rather than force the
   document wider than a 320px viewport (which would slide the fixed header out of
   alignment, since it's sized to the viewport). Cheap insurance, no visible
   change to the current copy. */
.site-footer__contact,
.site-nav__contact,
.site-footer__crisis {
  overflow-wrap: break-word;
}

/* ══ Forced colors (Windows High Contrast) ════════════════════════════════════
   When the OS replaces the palette with its own high-contrast system colours,
   two things break: box-shadows vanish (so the amber pill loses the only thing
   marking it as a button), and the decorative WebGL fields / gradient glows
   can't be recoloured — they'd keep rendering olive under system text that's now
   forced to a contrasting-with-*white* colour, trapping unreadable copy over
   them. Drop the decoration, let the guaranteed Canvas/CanvasText pairing govern
   those surfaces, and restate the button + focus affordances in system colours.
   No forced-color-adjust:none anywhere — we respect the user's palette, we just
   make sure nothing meaningful disappears inside it. */
@media (forced-colors: active) {
  /* Raster shader fields and gradient glows can't be force-adjusted; remove them
     so system colours provide the contrast instead of an un-adjusted olive. */
  .hero__canvas,
  .cta-banner__canvas,
  .booking__portal {
    display: none;
  }

  /* The one booking action: no fill survives, so give the capsule a real system
     border and shift it to the highlight colour when engaged, since the hover
     glow (a box-shadow) is gone. Applies to the header pill too. */
  .cta-pill {
    border: 2px solid ButtonText;
  }
  .cta-pill:hover,
  .cta-pill:focus-visible {
    border-color: Highlight;
  }

  /* Outline buttons draw a translucent hairline that some themes render
     invisible — pin it to a system colour. */
  .btn--outline-light {
    border-color: ButtonText;
  }

  /* Keep every focus ring on a reserved, always-visible system colour. */
  a:focus-visible,
  button:focus-visible,
  .btn:focus-visible,
  .cta-pill:focus-visible,
  .skip-link:focus-visible {
    outline-color: Highlight;
  }
}

/* ══ Print ════════════════════════════════════════════════════════════════════
   People do print a therapist's page — for the contact details, the insurance /
   cancellation notes, or the crisis numbers. Give paper an ink-friendly, chrome-
   free version. The load-bearing fix is the reveal reset: [data-reveal] content
   ships at opacity:0 and is only un-hidden when scrolled into view, so any
   below-the-fold section would otherwise print blank. Force every animated
   target to its resting state so the whole page prints. */
@media print {
  /* White ground, black ink, no drenched fills — for both themes. */
  :root,
  :root[data-theme="dark"] {
    --surface-page: #fff;
    --surface-alt: #fff;
    --surface-warm: #fff;
    --surface-raised: #fff;
    --heading: #000;
    --color-text: #000;
    --color-text-light: #222;
    --link: #000;
  }

  body {
    background: #fff;
    color: #000;
  }

  /* Screen-only chrome and decoration the reader doesn't need on paper. */
  .site-header,
  .menu-overlay,
  .page-veil,
  .booking__veil,
  .hero__canvas,
  .cta-banner__canvas,
  .hero__scroll,
  .booking__hand,
  .booking__portal,
  .theme-toggle,
  .menu-toggle,
  .skip-link {
    display: none !important;
  }

  /* Drenched surfaces print as plain white panels with black ink. */
  .hero,
  .cta-banner,
  .booking {
    background: #fff !important;
    color: #000 !important;
  }
  .hero h1,
  .hero__tagline,
  .hero__subtitle,
  .cta-banner h2,
  .cta-banner p {
    color: #000 !important;
  }

  /* Reclaim the space the fixed header reserved at the top of the first page. */
  .page-header,
  .hero__inner {
    padding-top: 1rem !important;
  }

  /* Reveal-gated content must never print blank: pin every animated target to
     its resting, fully-visible state regardless of scroll position. */
  .js [data-reveal],
  .js [data-reveal-group] > *,
  .js .booking__hand,
  .js .booking__portal-glow,
  .js .booking__cta,
  .js .booking__meta,
  .js .booking__alt,
  .js .hero__tagline,
  .js .hero h1,
  .js .hero__subtitle,
  .js .hero__photo {
    opacity: 1 !important;
    transform: none !important;
  }

  /* Keep meaning-bearing blocks whole across page breaks. */
  .values-list__item,
  .book-step,
  .service-detail,
  .cred-list__item,
  .site-footer__crisis {
    break-inside: avoid;
  }

  /* The booking action is opaque on paper ("Book a Session") — print where it
     leads so the page is actionable offline. */
  .booking__cta {
    border: 1px solid #000;
  }
  .booking__cta::after {
    content: " — " attr(href);
    font-size: 0.8em;
    font-weight: 400;
  }
}
