/* AURA Aesthetics — Daylight
   ---------------------------------------------------------------------------
   A reconstruction. The dark build is kept at site-dark.css.

   The idea: the clinic is a bright room. Warm plaster, cove light, stone.
   Photographing it against black fought the architecture; on warm paper the
   rooms open up and the glow becomes daylight falling through an arch rather
   than neon in the dark. The arch, the wide capitals and the peach hairline
   carry over — the palette inverts around them.
   --------------------------------------------------------------------------- */

:root{
  /* paper — never pure white, which reads cold against this photography */
  --paper:#FCF9F5; --paper-2:#F6EFE5; --paper-3:#EFE4D5; --card:#FFFFFF;

  /* ink */
  /* Measured against both grounds the page uses — the paper #FCF9F5 and the
     peach band #F3DCC7 — because the old values only ever passed on paper.
     ink-mute was 4.84 on paper but 3.84 on the band, and ink-faint 2.89 on
     paper, which failed everywhere it was used. Now: mute 6.97 / 5.53, faint
     5.99 / 4.75. Both clear 4.5:1 on either ground with room to spare. */
  --ink:#2A1D16; --ink-soft:#4C3A2E; --ink-mute:#655343; --ink-faint:#6E5D4E;

  /* accents */
  --peach:#D98B5F; --peach-soft:#F0CDB4; --peach-wash:#FBEFE4;
  --terracotta:#9F5335; --terracotta-deep:#80412A;
  --gold:#C9974F; --gold-soft:#E0C08A; --silver:#B9B5AE;

  --line:rgba(159,83,53,.14);
  --line-strong:rgba(159,83,53,.30);
  --line-gold:rgba(201,151,79,.34);

  --font-display:"Julius Sans One","Optima","Gill Sans",sans-serif;
  --font-core:"Figtree",system-ui,-apple-system,"Segoe UI",sans-serif;

  --tracking-eyebrow:.32em; --tracking-name:.18em; --tracking-wordmark:.4em;

  /* The type floor.

     There was no scale here: forty-four font sizes chosen one element at a
     time, eleven of them between .68 and .98rem, and a bottom end of .54rem —
     8.6px. The spec strip labels on every treatment page sat at .58rem, 9.3px,
     which is the smallest type on the site carrying the most useful answer on
     the page. For an audience in its thirties to fifties reading on a phone
     that is past asking.

     Anything a person reads now sits at or above --t-label. The three
     exceptions are the wordmark's "aesthetics", which is part of the logo
     lockup rather than text, and the embossed facsimile on the back of the
     membership cards, which nobody reads because it is a picture of a card.

     Tracking belongs with size. .32em is right at 13px and destroys word
     shape at 9px, where the gaps are wider than the letters — so label-sized
     type takes --tracking-label, not the eyebrow value. */
  --t-label:.72rem;   /* 11.5px — uppercase eyebrows, spec labels, kickers */
  --t-meta:.78rem;    /* 12.5px — chips, breadcrumbs, link labels */
  --tracking-label:.18em;
  --radius-card:4px; --radius-panel:6px; --radius-pill:999px;
  --arch:50% 50% 6px 6px / 38% 38% 6px 6px;

  --shadow-soft:0 2px 6px rgba(106,64,36,.05), 0 12px 32px rgba(106,64,36,.07);
  --shadow-lift:0 4px 10px rgba(106,64,36,.07), 0 22px 54px rgba(106,64,36,.12);
  --shadow-deep:0 30px 70px rgba(106,64,36,.18);

  --ease-out:cubic-bezier(.23,1,.32,1);
  --ease-in-out:cubic-bezier(.77,0,.175,1);
  /* for travel: leaves quickly, settles slowly, never whips through the middle */
  --ease-slide:cubic-bezier(.32,.72,0,1);
  /* After Effects' Easy Ease: creeps away, quick through the middle, settles
     slowly into the stop. Symmetric — it eases in and out, not just out. */
  --ease-ae:cubic-bezier(.33,0,.67,1);
  /* Long, almost entirely deceleration — the curve for scroll reveals. */
  --ease-glide:cubic-bezier(.16,1,.3,1);

  /* Eased mask ramps. A two-stop linear fade steps in brightness faster than
     the eye expects and shows as a visible stripe where it starts; these
     approximate a smoothstep curve across many stops, so a band dissolves
     into the paper with no edge to catch on. --fade-both fades at the top and
     bottom, --fade-down only at the top. */
  --fade-both:linear-gradient(180deg,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.015) 4%, rgba(0,0,0,.06) 8%, rgba(0,0,0,.15) 12%,
    rgba(0,0,0,.29) 16%, rgba(0,0,0,.46) 20%, rgba(0,0,0,.64) 24%, rgba(0,0,0,.8) 28%,
    rgba(0,0,0,.92) 32%, rgba(0,0,0,.98) 36%, #000 40%,
    #000 60%, rgba(0,0,0,.98) 64%, rgba(0,0,0,.92) 68%, rgba(0,0,0,.8) 72%,
    rgba(0,0,0,.64) 76%, rgba(0,0,0,.46) 80%, rgba(0,0,0,.29) 84%,
    rgba(0,0,0,.15) 88%, rgba(0,0,0,.06) 92%, rgba(0,0,0,.015) 96%, rgba(0,0,0,0) 100%);
  /* Same easing, shorter ramp — for a band that carries text all the way
     down, where a 40% fade at each end would reach the words. */
  /* The ramps are short and the solid middle is long: 18% in and 18% out,
     leaving nearly two thirds of the band at full strength. The earlier
     version faded across a third at each end, which left only the middle
     third actually dark. */
  --fade-both-tight:linear-gradient(180deg,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.01) 2%, rgba(0,0,0,.04) 4%, rgba(0,0,0,.1) 6%,
    rgba(0,0,0,.2) 8%, rgba(0,0,0,.33) 10%, rgba(0,0,0,.49) 12%, rgba(0,0,0,.65) 14%,
    rgba(0,0,0,.79) 15.5%, rgba(0,0,0,.9) 16.5%, rgba(0,0,0,.97) 17.5%, #000 19%,
    #000 81%, rgba(0,0,0,.97) 82.5%, rgba(0,0,0,.9) 83.5%, rgba(0,0,0,.79) 84.5%,
    rgba(0,0,0,.65) 86%, rgba(0,0,0,.49) 88%, rgba(0,0,0,.33) 90%,
    rgba(0,0,0,.2) 92%, rgba(0,0,0,.1) 94%, rgba(0,0,0,.04) 96%,
    rgba(0,0,0,.01) 98%, rgba(0,0,0,0) 100%);
  --fade-down:linear-gradient(180deg,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.015) 5%, rgba(0,0,0,.06) 10%, rgba(0,0,0,.15) 15%,
    rgba(0,0,0,.29) 20%, rgba(0,0,0,.46) 25%, rgba(0,0,0,.64) 30%, rgba(0,0,0,.8) 35%,
    rgba(0,0,0,.92) 40%, rgba(0,0,0,.98) 45%, #000 52%);
  /* --fade-down's mirror: solid at the top, gone by the bottom. Same eased
     ramp, because a two-stop linear fade leaves a visible stripe where the
     slope changes. */
  --fade-up:linear-gradient(180deg,
    #000 48%, rgba(0,0,0,.98) 55%, rgba(0,0,0,.92) 60%, rgba(0,0,0,.8) 65%,
    rgba(0,0,0,.64) 70%, rgba(0,0,0,.46) 75%, rgba(0,0,0,.29) 80%,
    rgba(0,0,0,.15) 85%, rgba(0,0,0,.06) 90%, rgba(0,0,0,.015) 95%, rgba(0,0,0,0) 100%);
  --dur-press:160ms; --dur-hover:150ms; --dur-ui:220ms; --dur-reveal:640ms;

  --gutter-l:max(22px,env(safe-area-inset-left,0px));
  --gutter-r:max(22px,env(safe-area-inset-right,0px));
  color-scheme:light;
}
*{box-sizing:border-box}
/* The glow field is deliberately wider than the viewport, which leaves the
   document measuring wider than the screen on a phone. `overflow-x: hidden`
   on body alone only suppresses the scrollbar; clipping at the root stops the
   page being pannable at all. */
html{
  scroll-behavior:smooth;overflow-x:clip;
  /* iOS Safari inflates text when a phone is turned to landscape, which it
     does on its own and without warning. The site sets its own sizes and
     does not want help. */
  -webkit-text-size-adjust:100%;text-size-adjust:100%;
  /* Every tap on Android and iOS otherwise paints a grey-blue box over the
     thing being tapped for a moment. It is the single loudest tell that a
     page is a website someone opened rather than something installed, and
     the site has its own :active states to say the same thing quietly. */
  -webkit-tap-highlight-color:transparent;
}
body{
  margin:0;color:var(--ink-soft);
  /* Was 300. At 17px in a warm grey on cream, a 300 weight is elegant on a
     good screen and the first thing to fall apart on a cheap phone in
     daylight — which is most of the traffic this clinic will get. 400 holds
     its shape without reading as heavy; the headings stay light, which is
     where the delicacy was doing the work anyway. */
  font-family:var(--font-core);font-weight:400;font-size:17px;line-height:1.72;
  /* No overflow-x here. `hidden` on body makes body a scroll container, and
     a scroll container that never scrolls freezes any view timeline resolved
     against it — which is what stopped the photographs below drifting. The
     clipping is done by `overflow-x: clip` on html, which clips without
     creating one. */
  -webkit-font-smoothing:antialiased;
  /* The paper is never flat. Warm light pools down the page in peach, amber
     and gold, each pool placed over a section rather than tiled, so the
     background reads as lit rather than printed. Sized in pixels and left
     unattached: a fixed attachment would repaint the whole page on scroll. */
  background-color:var(--paper);
  background-repeat:no-repeat;
  background-image:
    radial-gradient(900px 560px at 6% 620px,  rgba(255,205,160,.88), transparent 68%),
    radial-gradient(760px 520px at 98% 1320px, rgba(217,139,95,.34),  transparent 70%),
    radial-gradient(880px 600px at 2% 2250px,  rgba(201,151,79,.30),  transparent 70%),
    radial-gradient(820px 560px at 96% 3200px, rgba(255,205,160,.80), transparent 70%),
    radial-gradient(900px 620px at 10% 4300px, rgba(217,139,95,.30),  transparent 70%),
    radial-gradient(860px 580px at 92% 5400px, rgba(255,205,160,.74), transparent 70%);
}

/* ---------- backdrop ----------
   Fixed behind everything, never scrolled past. Each piece drifts at its own
   rate as the page moves, which is the whole effect: the arches pass the
   content at different speeds and the page gains depth it cannot get from a
   flat wash. Only transform and opacity animate, so this stays on the
   compositor. */
.backdrop{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;contain:strict}
.bd-orb,.bd-grain{position:absolute;display:block;will-change:transform}
/* No hard edge anywhere: the gradient reaches zero well inside the element's
   own box, and a wide blur softens what is left. Oversized and pushed past
   the viewport on every side so a pool is never seen to begin or end. */
/* No filter. These pools were a radial gradient that already fades to fully
   transparent inside its own box, with a 110px blur laid over the top — the
   blur had no edge left to soften and cost a megapixel of Gaussian per orb,
   per frame. The stops are stretched to the edge of the box instead, which
   is the same softness for nothing. */
.bd-orb{
  border-radius:50%;
  background:radial-gradient(closest-side, rgba(217,139,95,.34) 0%, rgba(217,139,95,.2) 34%, rgba(217,139,95,.07) 62%, rgba(217,139,95,0) 100%);
}
.bd-orb-a{left:-32vmin;top:18vh;width:104vmin;aspect-ratio:1}
.bd-orb-b{
  right:-38vmin;top:-16vh;width:118vmin;aspect-ratio:1;
  background:radial-gradient(closest-side, rgba(201,151,79,.3) 0%, rgba(201,151,79,.17) 34%, rgba(201,151,79,.06) 62%, rgba(201,151,79,0) 100%);
}
.bd-orb-c{
  left:22vw;top:58vh;width:92vmin;aspect-ratio:1;
  background:radial-gradient(closest-side, rgba(240,205,180,.4) 0%, rgba(240,205,180,.22) 36%, rgba(240,205,180,.08) 64%, rgba(240,205,180,0) 100%);
}
/* Grain never moves — a texture that drifts reads as dirt on the lens.
   No mix-blend-mode either: multiply on a fixed full-viewport layer makes the
   compositor blend the whole screen on every frame of every scroll, which is
   the single most expensive thing a backdrop can ask for. Plain alpha at the
   same strength is indistinguishable here and free. */
.bd-grain{
  inset:0;width:100%;height:100%;opacity:.03;will-change:auto;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* The drift itself. `scroll(root block)` ties the animation to the document's
   own scroll, so it runs off the main thread and needs no listener. The JS
   fallback in SiteScript covers browsers without it. */
@supports (animation-timeline: scroll()) {
  .bd-orb{animation:bd-drift linear both;animation-timeline:scroll(root block)}
  .bd-orb-a{--bd-from:-30vh; --bd-to:30vh}
  .bd-orb-b{--bd-from:42vh;  --bd-to:-42vh}
  .bd-orb-c{--bd-from:-18vh; --bd-to:18vh}
}
@keyframes bd-drift{
  from{transform:translate3d(0,var(--bd-from,0),0)}
  to{transform:translate3d(0,var(--bd-to,0),0)}
}
/* Under reduced motion the light stays and only the drift stops: it is part
   of the design, not decoration layered on top of it. */
@media (prefers-reduced-motion: reduce){
  .bd-orb{animation:none!important;transform:none!important;will-change:auto}
}
/* There is no blur left to reduce on a phone — the pools are plain gradients
   now. The third one still goes, because three overlapping washes on a narrow
   screen is more colour than the layout wants, not because of cost. */
@media (max-width:720px){
  .bd-orb-c{display:none}
}

/* ---------- photo reveal ----------
   What a photograph does the first time you reach it, as distinct from the
   ambient drift that used to live here. Two things happen at once:

     1. A panel the colour of the empty frame covers the photograph and slides
        up out of it. The photograph is uncovered rather than faded in.
     2. The photograph itself starts a quarter larger and settles back to its
        true size — and it takes longer to settle than the panel takes to
        clear, so it is still easing when you can already see all of it.

   That lag between the two is the whole effect. A reveal where everything
   finishes together reads as a transition; one where the image is still
   settling after the curtain has gone reads as weight.

   It plays once. Both properties are transform, both on layers that exist
   anyway, and the existing --rd carries the stagger across a row. */
.js-reveal .photo-reveal{position:relative}
/* ::before, not ::after. The family colour bar already owns ::after on the
   treatment cards, and a curtain that inherits that rule''s 3px height slides
   three pixels and reveals nothing. */
.js-reveal .photo-reveal::before{
  content:"";position:absolute;inset:-1px;z-index:6;pointer-events:none;
  /* Soft-edged, not a solid panel. A hard edge sweeping across a photograph
     is a stage curtain and announces itself; a veil with no edge to track
     just lifts, and you register the photograph arriving rather than the
     thing that uncovered it. It fades as it goes, so it never fully clears
     the frame in view. */
  background:linear-gradient(to top,
    var(--paper-3) 38%, rgba(239,228,213,.6) 70%, rgba(239,228,213,0) 100%);
  transform:translateY(0);opacity:1;
  transition:transform 900ms var(--ease-glide), opacity 620ms var(--ease-glide);
  transition-delay:var(--rd,0ms);
}
.js-reveal .photo-reveal img{
  /* A tenth larger, settling back. Enough to feel, not enough to notice. */
  transform:scale(1.1);
  transition:transform 1200ms var(--ease-glide);
  transition-delay:var(--rd,0ms);
  will-change:transform;
}
.js-reveal .is-in .photo-reveal::before,
.js-reveal .photo-reveal.is-in::before{transform:translateY(-42%);opacity:0}
.js-reveal .is-in .photo-reveal img,
.js-reveal .photo-reveal.is-in img{transform:none;will-change:auto}

@media (prefers-reduced-motion: reduce){
  .js-reveal .photo-reveal::before{display:none}
  .js-reveal .photo-reveal img{transform:none!important;transition:none;will-change:auto}
}

/* ---------- reveal on scroll ----------
   A short rise and fade as each block first comes into view, and then it is
   done: the class is added once and never removed, so nothing re-animates
   when you scroll back up. Re-playing on every pass is what makes a page
   feel like it is performing at you.

   Only transform and opacity, so each reveal is a compositor job. The hidden
   state lives behind .js-reveal (set in the head) — without JS every block
   simply renders as it always did. */
/* Longer and gentler than it was. --ease-glide starts almost still and spends
   most of its time decelerating, which is what reads as butter; a shorter,
   punchier curve reads as a snap however smooth the frames are. The opacity
   is deliberately quicker than the movement, so a block is legible well
   before it finishes settling. */
.js-reveal .reveal{
  opacity:0;transform:translate3d(0,22px,0);
  transition:opacity 620ms var(--ease-glide), transform 1000ms var(--ease-glide);
  transition-delay:var(--rd,0ms);
}
.js-reveal .reveal.is-in{opacity:1;transform:none}
/* No blanket will-change here. Setting it on every pending block promoted
   thirty-odd compositor layers at once, which costs more in raster and memory
   than it saves — and a plain opacity/transform transition gets promoted for
   its own duration anyway. */
@media (prefers-reduced-motion: reduce){
  .js-reveal .reveal{opacity:1;transform:none;transition:none}
}

/* ---------- treatment rail ----------
   Tall photographs you move through sideways. The scrolling is the browser's
   own — overflow plus scroll-snap — so a trackpad, a touch swipe, the arrow
   buttons and the keyboard all drive one mechanism. The buttons only call
   scrollBy; nothing here reimplements momentum. */
.rail-wrap{position:relative;z-index:1}
.rail{
  list-style:none;margin:0;padding:4px 0 8px;display:flex;gap:clamp(12px,1.4vw,18px);
  overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;
  scrollbar-width:none;
}
.rail::-webkit-scrollbar{display:none}
.rail-item{flex:0 0 calc((100% - 3*clamp(12px,1.4vw,18px)) / 4);scroll-snap-align:start;min-width:0}
.rail-card{
  position:relative;display:block;aspect-ratio:3/4.1;overflow:hidden;
  border-radius:4px;box-shadow:var(--shadow-soft);text-decoration:none;isolation:isolate;
  transition:box-shadow 420ms var(--ease-out);
}
.rail-card:hover{box-shadow:var(--shadow-lift)}
/* The rail scrolls sideways, which makes it a scroll container — a view
   timeline resolved inside it would never advance. The timeline is named on
   .rail-wrap instead, which is not one, so the six tiles share the section's
   own progress and drift together as the row passes. Side by side at the same
   height, that is what they should do anyway. */
.rail-img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;

}

/* Type sits on the photograph, so it needs its own ground to stand on. */
.rail-scrim{
  position:absolute;inset:0;
  background:
    linear-gradient(180deg, rgba(32,18,11,.62) 0%, rgba(32,18,11,.18) 34%, rgba(32,18,11,0) 52%),
    linear-gradient(0deg, rgba(32,18,11,.78) 0%, rgba(32,18,11,.3) 26%, rgba(32,18,11,0) 50%);
}
.rail-top{position:absolute;top:0;left:0;right:0;padding:22px 20px;display:grid;gap:7px;text-align:center}
.rail-kicker{
  font-family:var(--font-display);text-transform:uppercase;font-size:.68rem;
  letter-spacing:.22em;color:var(--peach-soft);
}
.rail-name{
  font-family:var(--font-display);text-transform:uppercase;font-size:clamp(.82rem,1.1vw,1rem);
  letter-spacing:var(--tracking-name);color:#FFF8F2;line-height:1.3;
}
.rail-foot{position:absolute;left:0;right:0;bottom:0;padding:20px;display:grid;gap:6px}
.rail-label{
  font-family:var(--font-display);text-transform:uppercase;font-size:.7rem;
  letter-spacing:.2em;color:var(--peach-soft);
}
.rail-helps{font-size:.8rem;line-height:1.5;color:rgba(255,248,242,.9)}
.rail-card:focus-visible{outline:2px solid var(--peach);outline-offset:3px}

/* The track used to take every pixel the count and the buttons did not, so it
   ran the full width of the page and read as a rule under the section rather
   than a progress indicator for the rail. Short, and the three parts sit
   together in the middle as one control. */
.rail-controls{display:flex;align-items:center;justify-content:center;gap:20px;margin-top:26px}
.rail-track{flex:0 1 200px;height:1px;background:var(--line);position:relative;overflow:hidden;border-radius:2px}
.rail-bar{
  position:absolute;left:0;top:0;height:100%;width:40%;background:var(--peach);
  transform-origin:left center;transition:transform 220ms var(--ease-out), width 220ms var(--ease-out);
}
.rail-count{font-family:var(--font-display);font-size:.74rem;letter-spacing:.16em;color:var(--ink-mute);white-space:nowrap;margin:0}
.rail-btns{display:flex;gap:10px;flex:0 0 auto}

@media (max-width:1040px){ .rail-item{flex-basis:calc((100% - 2*14px) / 3)} }
@media (max-width:780px){ .rail-item{flex-basis:calc((100% - 14px) / 2)} }
@media (max-width:520px){ .rail-item{flex-basis:82%} .rail-card{aspect-ratio:3/3.8} }

/* One column, deliberately. Two columns of accordions reflow across the gap
   every time one is opened, which moves the question you just clicked. Ten
   collapsed rows is a short section anyway. */
.faq-wide{max-width:860px;margin-inline:auto}

/* ---------- booking dialog ---------- */
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.book-dialog{
  width:min(640px,calc(100vw - 32px));max-height:min(88vh,860px);
  padding:0;border:0;background:transparent;overflow:visible;color:var(--ink-soft);
}
.book-dialog::backdrop{background:rgba(42,24,16,.46);opacity:0}
.book-dialog[open]::backdrop{opacity:1;transition:opacity 260ms var(--ease-out)}
/* Never from scale(0): a panel that grows out of nothing has no counterpart
   in the real world. 0.97 is enough to read as arriving. */
.book-dialog[open]{animation:book-in 260ms var(--ease-out)}
@keyframes book-in{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}
.book-dialog .book{
  max-height:min(88vh,860px);overflow-y:auto;overscroll-behavior:contain;
  border-radius:var(--radius-panel);box-shadow:var(--shadow-deep);
}
.book-close{
  position:absolute;top:-14px;right:-14px;z-index:2;
  width:38px;height:38px;border-radius:50%;cursor:pointer;
  display:grid;place-items:center;
  background:var(--card);border:1px solid var(--line);box-shadow:var(--shadow-soft);
  color:var(--ink-mute);transition:transform var(--dur-press) var(--ease-out),color var(--dur-hover) var(--ease-out);
}
.book-close svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round}
.book-close:hover{color:var(--terracotta)}
.book-close:active{transform:scale(.94)}
.book-close:focus-visible{outline:2px solid var(--terracotta);outline-offset:2px}
@media (max-width:520px){ .book-close{top:6px;right:6px} }
@media (prefers-reduced-motion: reduce){
  .book-dialog[open],.book-dialog[open]::backdrop{animation:none;transition:none}
}

/* ---------- section tint ----------
   The page used to run white-card-on-pale-paper from the hero all the way to
   the footer. Alternate sections now sit on a full-bleed peach wash, so the
   eye gets white, colour, white, dark, colour as it goes down. The wash is a
   pseudo-element rather than a wrapper so the section keeps its .wrap. */
.band-tint{position:relative}
.band-tint::before{
  content:"";position:absolute;inset:0;left:50%;width:100vw;transform:translateX(-50%);
  z-index:0;pointer-events:none;
  /* Translucent, and masked to nothing at the top and bottom edges. A tint
     with a hard boundary draws a line across the page and the section reads
     as a box stuck on; faded out at both ends it reads as the paper itself
     warming up and cooling down again. */
  background:
    radial-gradient(60% 70% at 14% 0%, rgba(217,139,95,.22), transparent 68%),
    linear-gradient(176deg, rgba(251,239,228,.94) 0%, rgba(243,220,199,.96) 52%, rgba(250,237,225,.94) 100%);
  -webkit-mask-image:var(--fade-both);
  mask-image:var(--fade-both);
}
.band-tint > *{position:relative;z-index:1}

/* One step deeper than .band-tint, for a single section in the back half of
   the home page. Measured, that half runs 4,144px — membership, clients, FAQ,
   visit and the closing — with no tonal punctuation at all between the dark
   Clinic band and the dark footer. Half the page at one pitch.

   Deliberately not a second dark band. The one dark band took three rounds to
   settle and another would compete with both it and the footer; this is a
   middle tone that marks the place without adding weight. It also stays light
   enough to carry the page's normal ink, so no text colour changes and no new
   contrast to verify. */
.band-deep{position:relative}
.band-deep::before{
  content:"";position:absolute;inset:0;left:50%;width:100vw;transform:translateX(-50%);
  z-index:0;pointer-events:none;
  background:
    /* Pulled back from a deeper mix: at that strength the muted body ink
       measured 4.6:1 against the band's darkest point — over the line, but
       with nothing to spare, and the figure was an estimate of the composite
       rather than a reading of it. A tone this close to the threshold is not
       worth the risk for a decorative band. */
    radial-gradient(64% 72% at 16% 0%, rgba(197,118,76,.19), transparent 70%),
    linear-gradient(176deg, rgba(245,228,213,.97) 0%, rgba(237,214,194,.98) 52%, rgba(244,226,210,.97) 100%);
  -webkit-mask-image:var(--fade-both);
  mask-image:var(--fade-both);
}
.band-deep > *{position:relative;z-index:1}
body::before{content:"";position:fixed;inset:0 0 auto;height:env(safe-area-inset-top,0px);z-index:31;background:var(--paper)}
img{max-width:100%;display:block}
h1,h2,h3,h4{margin:0;color:var(--ink);font-weight:400;text-wrap:balance;line-height:1.16}
p{margin:0}
a{color:inherit}
.wrap{width:100%;max-width:1140px;margin-inline:auto;padding-left:var(--gutter-l);padding-right:var(--gutter-r)}
.skip{position:absolute;left:-9999px;top:0;background:var(--ink);color:var(--paper);padding:10px 16px;z-index:40}
.skip:focus{left:0}

/* ---------- light ----------
   The glow survives the inversion as sunlight: warm, diffuse, low contrast.
   Same geometry as the dark build, a tenth of the intensity. */
.glowfield{position:absolute;left:50%;transform:translateX(-50%);width:150%;max-width:1900px;pointer-events:none;z-index:0}
.arc{
  position:absolute;left:50%;transform:translateX(-50%);
  width:100%;aspect-ratio:2/1;border-radius:50%;
  background:radial-gradient(closest-side,
    rgba(255,224,196,.92) 0%, rgba(244,206,170,.62) 38%,
    rgba(217,139,95,.26) 62%, rgba(217,139,95,.08) 82%, rgba(217,139,95,0) 100%);
}
/* This one was a solid block of paper with a hard edge, feathered by a 58px
   blur — the only place the blur was doing real work. A gradient that fades
   out over its own last third gives the same soft hole for no filter pass. */
.arc::after{
  content:"";position:absolute;border-radius:50%;
  background:radial-gradient(closest-side,
    var(--paper) 0%, var(--paper) 46%, rgba(252,249,245,.55) 76%, rgba(252,249,245,0) 100%);
}
.arc.top{top:-32%}
.arc.top::after{inset:6% 12% 26% 12%}
.arc.bot{top:14%}
.arc.bot::after{inset:26% 12% 6% 12%}
/* This settles the arcs into the page: transparent over them, opaque paper
   outside. The catch is that "outside" means the whole rest of the box, so
   the box's own bottom edge was a straight cut from solid paper to nothing —
   a hard line clean across the page. It used to fall below anything you could
   see; the hero grew when the buttons moved under the strip and it ended up
   running right through them. Masked so it fades out instead of stopping. */
.glowfield .veil{
  position:absolute;inset:-20% -10%;
  background:radial-gradient(70% 58% at 50% 42%, rgba(252,249,245,0) 46%, rgba(252,249,245,.72) 82%, var(--paper) 100%);
  -webkit-mask-image:var(--fade-up);
  mask-image:var(--fade-up);
}
.glow-soft{
  position:absolute;left:50%;transform:translateX(-50%);
  width:132%;max-width:1700px;aspect-ratio:2.2/1;border-radius:50%;
  pointer-events:none;z-index:0;
  background:radial-gradient(closest-side, rgba(244,206,170,.72), rgba(217,139,95,.26) 50%, rgba(217,139,95,.08) 76%, transparent 100%);
}
.band > .glow-soft,.band-tight > .glow-soft{opacity:.88}

/* ---------- shared ---------- */
.badge-row{display:flex;align-items:center;justify-content:center;gap:16px}
.badge-row.left{justify-content:flex-start}
.badge-row .dash{flex:0 0 42px;height:1px;background:linear-gradient(90deg,transparent,var(--line-strong))}
.badge-row .dash.r{background:linear-gradient(90deg,var(--line-strong),transparent)}
.badge{
  font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-eyebrow);
  /* Was .62rem — 9.9px. The display face is wide and the tracking is wider
     still, which is exactly where small sizes stop being legible. */
  font-size:.72rem;color:var(--terracotta);padding:7px 16px;border-radius:var(--radius-pill);
  border:1px solid var(--line);background:var(--paper-2);white-space:nowrap;
}
/* Cards catch the light too: a faint warm gradient from the top-left corner,
   so a white panel sitting in a warm wash does not read as a cut-out. */
.glass{
  position:relative;
  background:
    linear-gradient(152deg, rgba(255,232,210,.5) 0%, rgba(255,255,255,0) 46%),
    var(--card);
  border:1px solid var(--line);box-shadow:var(--shadow-soft);
}
.accent-line{display:block;width:100%;height:1px;border:0;margin:0;background:linear-gradient(90deg,transparent,var(--line-strong) 24%,var(--peach) 50%,var(--line-strong) 76%,transparent)}
.accent-stub{display:block;width:44px;height:1.5px;border:0;margin:0;background:linear-gradient(90deg,var(--peach),rgba(217,139,95,.15))}
.lede{font-size:1.06rem;color:var(--ink-mute);max-width:60ch}

.btn{
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--font-core);font-size:.78rem;font-weight:500;letter-spacing:.1em;text-transform:uppercase;
  padding:14px 28px;border-radius:var(--radius-pill);cursor:pointer;text-decoration:none;
  background:var(--terracotta);color:#FFF6EE;border:1px solid var(--terracotta);
  transition:transform var(--dur-press) var(--ease-out),background var(--dur-hover) var(--ease-out),box-shadow var(--dur-hover) var(--ease-out);
}
.btn:active{transform:scale(.97)}
.btn-ghost{background:transparent;color:var(--terracotta);border-color:var(--line-strong)}
.btn-ghost:hover{background:var(--peach-wash)}
.btn-sm{font-size:.68rem;padding:10px 20px}
@media (hover:hover) and (pointer:fine){
  .btn:hover{background:var(--terracotta-deep);transform:translateY(-2px);box-shadow:var(--shadow-lift)}
  .btn-ghost:hover{background:var(--peach-wash);box-shadow:none;transform:translateY(-2px)}
}
/* Peach measured 2.56:1 against the page and 2.69:1 against a white card.
   A focus indicator needs 3:1, so the ring a keyboard user follows was the
   one thing on the site that failed contrast. Terracotta is 5.32:1 and is
   already the interactive colour everywhere else.

   The rings over the dark hero — the fan cards, their buttons, the rail and
   the footer — keep peach on purpose: the measurement inverts there, and
   terracotta on a dark photograph is worse than what it replaced. */
:where(a,button,input,select,textarea,summary):focus-visible{outline:2px solid var(--terracotta);outline-offset:3px;border-radius:4px}

/* ---------- header ---------- */
.site-head{
  position:sticky;top:env(safe-area-inset-top,0px);z-index:20;
  /* The backdrop-filter is gone. A sticky bar spanning the viewport makes the
     browser re-sample and re-blur everything passing underneath it on every
     scroll frame, which is the most expensive thing on this page and the one
     cost that is paid continuously rather than once. Under a background that
     is already 86% opaque almost none of it was visible; at 94% none of it
     is, and the bar still reads as a surface over the page rather than a
     solid lid. */
  background:rgba(252,249,245,.94);
  border-bottom:1px solid var(--line);transition:box-shadow var(--dur-ui) var(--ease-out);
}
.site-head[data-scrolled]{box-shadow:var(--shadow-soft)}
.head-shell{width:100%;max-width:1140px;margin-inline:auto;padding-left:var(--gutter-l);padding-right:var(--gutter-r)}
.head-in{display:flex;align-items:center;gap:26px;padding-block:11px;transition:padding var(--dur-ui) var(--ease-out)}
.site-head[data-scrolled] .head-in{padding-block:7px}
/* Tracked lines sit left of their own box; the negative margin pulls the
   trailing letter-space back so both lines share one optical centre. */
/* The two lines of the wordmark come to 34px, and this is the way back to the
   home page from every other one — the link a thumb reaches for most. The
   padding takes the target to 44px and the matching negative margin gives the
   space straight back to the layout, so the bar is exactly as tall as it was
   and only the area that answers a tap has changed. */
.wordmark{
  display:flex;flex-direction:column;align-items:center;line-height:1;
  text-decoration:none;flex:0 0 auto;
  padding-block:5px;margin-block:-5px;
}
.wordmark .a{
  font-family:var(--font-display);font-size:1.2rem;color:var(--ink);
  letter-spacing:var(--tracking-wordmark);margin-right:calc(var(--tracking-wordmark) * -1);
}
.wordmark .b{font-size:.54rem;color:var(--terracotta);margin-top:6px;letter-spacing:.3em;margin-right:-.3em;text-transform:lowercase}
.nav{display:flex;gap:24px;margin-inline:auto;flex-wrap:wrap}
.nav a{
  font-size:.78rem;letter-spacing:.05em;text-decoration:none;color:var(--ink-mute);
  padding-block:5px;border-bottom:1.5px solid transparent;transition:color var(--dur-hover) var(--ease-out),border-color var(--dur-hover) var(--ease-out);
}
.nav a:hover,.nav a[aria-current="page"]{color:var(--ink);border-bottom-color:var(--peach)}
.head-cta{flex:0 0 auto}
/* The bar is thin now, so the button has to be too or it sets the height.

   Written `.head-cta .btn` for a long time, which is a descendant selector and
   matched nothing: head-cta is a class on the button itself, not a wrapper
   round it (`class="btn btn-sm head-cta"`). The button has been taking its
   size from .btn-sm all along. Corrected to .btn.head-cta. */
.btn.head-cta{padding:10px 22px;font-size:.74rem;position:relative}
/* Which leaves the pill 41px tall, just under the 44px a thumb wants. Padding
   would fix the target and break the bar, since the pill is a filled shape
   and would simply grow. An empty overlay centred on it takes the tappable
   area to 44px while the pill keeps its own size. */
.btn.head-cta::after{
  content:"";position:absolute;left:0;right:0;top:50%;
  transform:translateY(-50%);height:44px;
}

/* ---------- mobile navigation ----------
   Hidden on desktop, where the row nav does the job. Below the breakpoint it
   is the only route to a treatment family from the header. 44px square, which
   is the smallest target a thumb reliably hits. */
.nav-toggle{
  display:none;flex:0 0 auto;width:44px;height:44px;margin-right:-10px;
  align-items:center;justify-content:center;
  background:none;border:0;cursor:pointer;color:var(--ink);border-radius:50%;
  transition:transform var(--dur-press) var(--ease-out),background var(--dur-hover) var(--ease-out);
}
.nav-toggle:active{transform:scale(.94)}
.nav-toggle:focus-visible{outline:2px solid var(--terracotta);outline-offset:2px}
.nav-bars{display:grid;gap:5px;width:19px}
.nav-bars span{display:block;height:1.5px;background:currentColor;border-radius:2px;
  transition:transform 260ms var(--ease-out),opacity 180ms var(--ease-out)}
.nav-toggle[aria-expanded="true"] .nav-bars span:first-child{transform:translateY(3.25px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] .nav-bars span:last-child{transform:translateY(-3.25px) rotate(-45deg)}

.nav-panel{
  border-top:1px solid var(--line);
  background:var(--paper);
  box-shadow:var(--shadow-soft);
}
.nav-panel ul{list-style:none;margin:0;padding:6px 0 14px;display:grid}
.nav-panel a{
  display:block;text-decoration:none;color:var(--ink-soft);
  font-size:.92rem;padding:13px 2px;min-height:44px;
  border-bottom:1px solid var(--line);
}
.nav-panel li:last-child a{border-bottom:0}
.nav-panel a[aria-current="page"]{color:var(--terracotta)}
.nav-panel a:focus-visible{outline:2px solid var(--terracotta);outline-offset:-2px}

@media (max-width:860px){
  .nav-toggle{display:flex}
  .head-in{justify-content:space-between}
}

/* ---------- hero ---------- */
/* ---------- hero ----------
   A full-bleed room behind everything, type in white over it, and the cards
   fanned across the base — the structure of the reference, in AURA's material. */
/* 14px put the headline almost against the bar, and because the bar is sticky
   it closed over the type the moment the page moved at all. Enough room now
   that the heading is clear of it at rest and does not look pinned. */
/* The bottom padding used to be 0 because the button row carried 42px of its
   own below the strip. With the buttons back above the strip, the strip is
   the last thing in the hero and the hero has to close the gap itself. */
.hero{position:relative;padding-block:clamp(26px,5.5vw,58px) clamp(22px,3.4vw,42px);text-align:center;overflow:hidden}
.hero-bg{position:absolute;inset:0;margin:0;z-index:0;overflow:hidden}
.hero-bg img{
  width:100%;height:100%;object-fit:cover;object-position:50% 46%;
  /* A slow Ken Burns used to run here forever. Scaling a full-bleed photograph
     is the single most expensive thing that can sit behind a moving strip:
     the whole image re-rasters as the scale changes, non-stop. It is held
     still instead, at the scale the crop was built for. */
  transform:scale(1.04);
}
/* Warm scrim: enough to carry white type, not so much that the room goes dark. */
.hero-bg::after{
  content:"";position:absolute;inset:0;
  /* The scrim was tuned for a darker corridor photograph. The arch colonnade
     is a bright, white-walled room, so the same scrim left the copy floating
     on a pale wash — the subtitle measured 3.72:1, under the 4.5 body text
     needs. It is deeper now, and held deeper further down the frame, so the
     type has a ground to sit on and the hero has a tonal floor. */
  background:
    linear-gradient(to bottom,
      rgba(52,28,16,.72) 0%,
      rgba(52,28,16,.62) 30%,
      rgba(52,28,16,.44) 56%,
      rgba(252,249,245,.08) 84%,
      var(--paper) 100%),
    radial-gradient(78% 56% at 50% 28%, rgba(44,24,13,.34), rgba(120,64,36,.34) 58%, rgba(120,64,36,.5) 100%);
}
.hero .glowfield{top:-240px;height:760px;opacity:.5;z-index:1}
/* The gap here is the headline-to-subtitle space and nothing else. The button
   row is a third child but sets its own margin, for exactly this reason:
   one gap cannot serve both measures. 12px looked
   like 20: the headline carries about 2px of half-leading under its baseline
   and the subtitle about 6px above its own, and both land inside the gap.
   Set against that, not against what the number reads as. */
.hero-inner{position:relative;z-index:2;display:flex;flex-direction:column;align-items:center;gap:2px}
.hero h1{
  /* One line at every width: the size is driven by the viewport so the line
     shrinks rather than breaking. */
  font-size:clamp(1.95rem,7.4vw,4.6rem);font-weight:400;letter-spacing:-.018em;line-height:1.06;
  max-width:none;white-space:nowrap;
  color:#FFF8F2;text-shadow:0 2px 30px rgba(50,24,10,.45);
}
/* 48ch broke the line in three. The copy is one sentence now and the measure
   is wide enough to hold it on a single line at any desktop width; a phone is
   too narrow for that at this size whatever the limit says, so it takes two
   there rather than being shrunk to fit. */
.hero .lede{max-width:70ch;font-size:1.12rem;color:#FFF6EE;text-wrap:balance}
.hero .badge{
  color:#FFF6EE;border-color:rgba(255,246,238,.4);background:rgba(255,246,238,.12);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
}
.hero .badge-row .dash{background:linear-gradient(90deg,transparent,rgba(255,246,238,.5))}
.hero .badge-row .dash.r{background:linear-gradient(90deg,rgba(255,246,238,.5),transparent)}
/* The row sits between the subtitle and the strip. .hero-inner's own gap is
   2px — that is the headline-to-subtitle measure and nothing else — so the
   space above the buttons is set here rather than by widening a gap that
   would push the subtitle off the headline too. */
.hero-actions{display:flex;flex-wrap:wrap;gap:13px;justify-content:center;margin-top:26px}

/* Transparent with a 30%-alpha border, this read as an outline someone had
   forgotten to fill — next to a solid terracotta pill, over a photograph, it
   barely registered as a button. It gets a surface of its own, and a border
   at a strength you can actually see. Still clearly the second of the two:
   paper against terracotta, not another filled brand-colour pill.

   The fill is what makes this safe up here. The pill is near-opaque paper
   whatever is behind it, so terracotta-deep is measured against paper, not
   against the dark top of the scrim — the same reading it had at the foot of
   the hero. The old white-outline treatment for this position is gone: it
   depended on the button being transparent, which it no longer is. */
.hero-inner .btn-ghost{
  color:var(--terracotta-deep);
  background:rgba(252,249,245,.94);
  border-color:rgba(159,83,53,.5);
  box-shadow:0 1px 2px rgba(74,40,22,.06), 0 8px 22px rgba(74,40,22,.10);
}
@media (hover:hover) and (pointer:fine){
  .hero-inner .btn-ghost:hover{
    background:#FFF;border-color:var(--terracotta);color:var(--terracotta-deep);
  }
}

/* ---------- the strip ----------
   A flat row of arch cards, evenly spaced, the middle one grown and fully lit
   while the rest shrink and fade off toward both edges. No perspective and no
   tilt: depth is read from size and light alone, which is what keeps a row of
   seven legible. The strip steps sideways a slot at a time. */
/* 8px when the subtitle sat directly above. The buttons are a heavier object
   than a line of text and need clearing properly, or the strip reads as
   hanging off them. */
.fan-wrap{position:relative;z-index:2;margin-top:30px;padding-bottom:4px}
.fan-halo{
  /* The ambient bloom behind the strip — the reference's single loudest move.
     Wide, warm and soft enough that the end cards dissolve into it. */
  position:absolute;left:50%;top:48%;transform:translate(-50%,-50%);
  width:min(960px,84vw);aspect-ratio:1.9/1;border-radius:50%;pointer-events:none;z-index:0;
  background:radial-gradient(closest-side, rgba(255,214,176,.5), rgba(208,122,72,.26) 46%, rgba(208,122,72,.08) 74%, transparent 100%);
}
/* The strip is a continuous track, not a set of slots.
   Every card is absolutely placed and the script derives its position, size
   and light from one number: how far it is from the middle. That number moves
   smoothly, so the cards do too — there is no slot to jump between, nothing
   to reorder, and nothing to recycle from one end to the other. */
/* Declared on .hero rather than on .fan so the phone override below has a
   single place to retune both. */
.hero{--fan-w:clamp(148px,17.2vw,248px);--fan-h:clamp(330px,41vw,468px)}
.fan{
  position:relative;z-index:1;
  height:var(--fan-h);
  opacity:0;transition:opacity 700ms var(--ease-out);
}
.fan.is-ready{opacity:1}
.fan-card{
  position:absolute;left:50%;top:50%;margin:0;display:block;
  text-decoration:none;color:inherit;
  width:var(--fan-w);aspect-ratio:2/3;
  border-radius:14px;overflow:hidden;background:var(--paper-3);
  /* The script writes the destination; CSS carries the card there. */
  transform-origin:50% 50%;
  /* --ease-ae was symmetric: slow out, slow in. The slow start is the part
     the eye is watching, and it reads as weight rather than grace. This curve
     commits straight away and spends the rest of its time settling, which is
     what "premium" motion almost always turns out to be. The delay is written
     by the script, a little larger the further a card sits from the middle,
     so the strip settles like a fan instead of a rigid block. */
  transition:
    transform 920ms var(--ease-glide),
    opacity 760ms var(--ease-glide);
  transition-delay:var(--fd,0ms);
  /* Kept on permanently so each card holds its own layer. Promoting at the
     start of a move and dropping it at the end costs a hitch at both ends,
     which is exactly where the motion is most watched. Seven small layers. */
  will-change:transform;
}
.fan-veil{transition:opacity 760ms var(--ease-glide);transition-delay:var(--fd,0ms)}
/* The photograph drifts a little against its own frame as the card travels —
   a few pixels, opposite the direction of movement. It is the cheapest way to
   suggest depth: two planes moving at different rates read as space between
   them. Transform only, on a layer that already exists. */
.fan-card img{
  transition:transform 1100ms var(--ease-glide);
  transition-delay:var(--fd,0ms);
  will-change:transform;
}
/* A press answers through the card's own overlay, not through transform or
   filter — the script owns both of those and rewrites them every frame. */
.fan-card:active::before{background:rgba(32,18,10,.2)}
/* Every card carries a hairline edge, as the reference's posters do. */
.fan-card::before{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:2;
  box-shadow:inset 0 0 0 1px rgba(255,238,222,.22);
  /* A peach wash over every card, so the strip reads as one set of images in
     the page's own light rather than seven separate photographs. */
  background:linear-gradient(168deg, rgba(255,214,176,.17), rgba(217,139,95,.12));
  transition:background 120ms var(--ease-out);
}
/* Slightly oversized, so the drift below never pulls an edge into view. */
.fan-card img{width:100%;height:100%;object-fit:cover;transform:scale(1.08)}

/* ---------- photo grade ----------
   The clinic's photographs were shot on different days under different light,
   and against a warm page they read as stock. One grade across all of them —
   a touch of sepia, the saturation pulled back, the warmth pushed — settles
   them into the same room as the rest of the site. Static, so each image is
   rasterised once; it is animating a filter that costs, not having one.
   The membership card artwork is deliberately left out: it is already brand
   colour and grading it would shift the gold and silver. */
.fan-card img,
.step-photo img,
.tile-img,
.arch-img,
.cat-hero img,
.t-hero img{
  filter:sepia(.18) saturate(1.08) brightness(1.02) contrast(1.02);
}
/* The dimming layer. Its opacity is the only thing that changes as a card
   moves out of focus, and opacity on its own layer is composited — the
   photograph underneath is never repainted. */
.fan-veil{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:#1B0F08;opacity:0;
}
/* Every card names itself, the way every poster in the reference carries its
   title. The dimming is done by the card, so a label fades with its card and
   the one in focus is simply the one you can read. */
.fan-card .fan-label{
  /* above the peach wash, which would otherwise tint the type too */
  position:absolute;left:0;right:0;bottom:0;z-index:3;
  display:flex;flex-direction:column;align-items:flex-start;gap:4px;
  padding:44px 11px 12px;text-align:left;
  background:linear-gradient(to top,rgba(32,20,14,.92),rgba(32,20,14,.55) 44%,rgba(32,20,14,0));
}
.fan-name{
  font-family:var(--font-display);text-transform:uppercase;letter-spacing:.12em;
  font-size:.74rem;line-height:1.4;color:#FFF6EE;
  transition:font-size 420ms var(--ease-out);
}
.fan-meta{
  font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;
  color:rgba(255,246,238,.78);
}
/* On a phone the strip has about 435px to place six overlapping cards, so
   each one shows roughly 55px of itself before the next covers it — less than
   a label needs. The result was a row of sliced words: "TONI", "3 TREA",
   "ENTS". Only the card in focus keeps its label at that width; the rest are
   the images they were always meant to be behind it. */
@media (max-width:720px){
  .fan-label{transition:opacity 240ms var(--ease-out)}
  .fan-card:not([data-focus]) .fan-label{opacity:0}
}
/* the card in focus says it a size louder */
.fan-card[data-focus] .fan-name{font-size:.86rem}
.fan-card[data-focus] .fan-meta{color:rgba(255,246,238,.82)}
/* A ring on the card facing you, so it reads as the one you would open. */
.fan-card[data-focus]::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 0 0 1px rgba(255,236,216,.5);
}
.fan-card:focus-visible{outline:2px solid var(--peach);outline-offset:3px}

/* Anything that advances on its own needs a way to stop it and a way to step
   it by hand — otherwise the only way to read a card is to wait for it. */
.fan-controls{
  position:relative;z-index:3;
  display:flex;align-items:center;justify-content:center;gap:10px;
  margin-top:10px;
}
.fan-btn{
  /* 34px, which is a mouse target rather than a thumb one — and these two
     are the only way to step the hero strip by hand on a phone. */
  display:grid;place-items:center;width:44px;height:44px;padding:0;
  border-radius:50%;cursor:pointer;
  border:1px solid rgba(255,240,228,.5);
  background:rgba(48,26,14,.5);
  /* No backdrop-filter here: it sits directly under the moving cards, and a
     backdrop filter must re-sample everything behind it on every frame that
     anything behind it moves. */
  color:#FFF6EE;
  transition:transform 160ms var(--ease-out),background 160ms var(--ease-out),
             border-color 160ms var(--ease-out);
}
.fan-btn svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.fan-btn .i-play{display:none;fill:currentColor;stroke:none}
.fan-btn[aria-pressed="true"] .i-pause{display:none}
.fan-btn[aria-pressed="true"] .i-play{display:block}
@media (hover:hover) and (pointer:fine){
  .fan-btn:hover{background:rgba(60,32,18,.52);border-color:rgba(255,240,228,.56)}
}
.fan-btn:active{transform:scale(.94)}
.fan-btn:focus-visible{outline:2px solid var(--peach);outline-offset:3px}
/* the slide position, for screen readers only */
.fan-status{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* The shadow under the card in focus. Left to CSS because it is the one thing
   here that is not worth recomputing every frame. */
.fan-card[data-focus]{
  box-shadow:0 40px 74px rgba(48,24,12,.5), 0 6px 18px rgba(48,24,12,.3);
}

/* Pointing at a card slides the strip until that card is the one in focus —
   the script does the travelling, so there is no in-place hover transform. */
@media (hover:hover) and (pointer:fine){
  .fan-card{cursor:pointer}
}
@media (max-width:760px){
  /* Smaller cards and a shorter track; the script fades out whatever falls
     past the edge on its own, so nothing needs hiding by hand. */
  .hero{--fan-w:clamp(104px,30vw,150px);--fan-h:clamp(240px,46vw,250px)}
}
/* ---------- the dark band ----------
   Everything between the hero and the footer used to be white cards on pale
   paper at one container width. This section is the break: full bleed, dark,
   and the photography carries it. */
/* The dark is carried by a masked pseudo-element, not by the section itself.
   Laying a sheet of paper-coloured gradient over the dark to fade it out
   leaves a milky haze at the join; masking the dark away instead lets the
   real page show through, so the band has no edge at all. */
/* Generous padding is doing real work: the fade ramps live in it, so the
   copy always sits on the solid part of the band.

   The band fades in two stages, not one. A single ramp from paper straight to
   #2A1810 has to cross a long way in a short distance, and the midpoint of
   that crossing is a desaturated grey — visible as a haze at both joins however
   well the ramp is eased. So the element itself carries a wide, soft warm wash
   with the alpha baked into the gradient, and the dark sits inside it. The eye
   then travels paper â†’ warm â†’ dark â†’ warm â†’ paper, and each step is short
   enough to disappear. */
.clinic-band{
  /* Lighter again, and the ramps at the top and bottom are longer — the band
     now takes a quarter of its height to arrive instead of a sixth, so the
     edge between pale page and dark band is a transition rather than an
     event. That abruptness was reading as harshness as much as the colour. */
  position:relative;color:rgba(255,246,238,.9);padding-block:164px;
  background:linear-gradient(180deg,
    rgba(96,56,36,0) 0%, rgba(96,56,36,.04) 4%, rgba(96,56,36,.12) 8%,
    rgba(96,56,36,.24) 13%, rgba(96,56,36,.36) 18%, rgba(96,56,36,.46) 24%,
    rgba(96,56,36,.5) 30%,
    rgba(96,56,36,.5) 70%, rgba(96,56,36,.46) 76%, rgba(96,56,36,.36) 82%,
    rgba(96,56,36,.24) 87%, rgba(96,56,36,.12) 92%, rgba(96,56,36,.04) 96%,
    rgba(96,56,36,0) 100%);
}
.clinic-band::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  /* The glow over this was the brand terracotta at half strength across most
     of the band, which is what made it read red rather than dark — a lit
     surface, not a quiet one. It is a third of that now and pulled toward
     brown, so it still shapes the light without colouring the whole band.
     The base loses a little of its red too. Both get darker rather than
     lighter, so the type on top gains contrast rather than losing it. */
  background:
    radial-gradient(90% 70% at 18% 20%, rgba(150,100,74,.2), transparent 72%),
    /* Lighter, and a much tighter range top to bottom. The old one ran from
       #3A2318 down to #22130C — nearly black at the foot — and that internal
       drop is as much of the harshness as the colour was: a band that gets
       steadily darker as you read down it feels like it is closing. These
       three sit close together, so it reads as one soft surface. */
    linear-gradient(160deg, #5E4738 0%, #564133 100%);
  -webkit-mask-image:var(--fade-both-tight);
  mask-image:var(--fade-both-tight);
}
.clinic-band .wrap{position:relative;z-index:2}
.clinic-band .sec-head{margin-bottom:46px}
.clinic-band .sec-head h2{color:#FFF6EE}
.clinic-band .sec-head .lede{color:rgba(255,246,238,.8);max-width:62ch}
.clinic-band .sec-head .dash{background:linear-gradient(90deg,transparent,rgba(255,240,228,.4))}
.clinic-band .sec-head .dash.r{background:linear-gradient(90deg,rgba(255,240,228,.4),transparent)}
/* The copy column is only the list now, so it sits with the photographs. */
.clinic-band .clinic-grid{align-items:center}
.clinic-band h2{color:#FFF6EE}
.clinic-band .lede{color:rgba(255,246,238,.78)}
.clinic-band .badge{color:rgba(255,246,238,.72);border-color:rgba(255,240,228,.26);background:rgba(255,240,228,.06)}
.clinic-band .dash{background:linear-gradient(90deg,transparent,rgba(255,240,228,.4))}
.clinic-band .dash.r{background:linear-gradient(90deg,rgba(255,240,228,.4),transparent)}
.clinic-band .clinic-copy{
  background:transparent;border:0;box-shadow:none;padding:0;
}
.clinic-band .accent-line{background:linear-gradient(90deg,transparent,rgba(255,240,228,.3) 24%,var(--peach) 50%,rgba(255,240,228,.3) 76%,transparent)}
.clinic-band .why-list li{border-color:rgba(255,240,228,.16);color:rgba(255,246,238,.76)}
.clinic-band .why-list li span:first-child{color:var(--peach-soft)}
.clinic-band .arch-img{filter:sepia(.14) saturate(1.04) brightness(1.04)}

/* The attribution row: a face, the name, the treatment and the rating, all
   on one line at the foot of the card — which is how a review reads as a
   person rather than a block of text. */

.voice-face{
  flex:0 0 auto;width:52px;height:52px;border-radius:50%;overflow:hidden;
  display:grid;place-items:center;background:var(--paper-3);
}
.voice-face img{width:100%;height:100%;object-fit:cover;display:block}
.voice-face-empty{
  font-family:var(--font-display);font-size:1.1rem;color:var(--terracotta);
  border:1px solid var(--line-strong);background:var(--peach-wash);
}
.voice-who{display:grid;gap:3px;min-width:0}

/* ---------- client voices ---------- */
.voices{
  position:relative;z-index:1;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(272px,1fr));gap:18px;
  margin-top:40px;
}
.voice{
  position:relative;margin:0;padding:0 28px 28px;border-radius:var(--radius-panel);
  overflow:hidden;min-width:0;display:flex;flex-direction:column;
}
.voice .accent-line{margin-inline:-28px;width:calc(100% + 56px)}
.voice-mark{
  display:block;font-family:var(--font-display);font-size:2.6rem;line-height:1;
  color:var(--peach);margin-top:20px;
}
.voice blockquote{
  margin:10px 0 0;font-size:.98rem;line-height:1.7;color:var(--ink-mute);
}
/* The attribution is a row now — face, then name, treatment and rating in a
   column beside it. This rule used to stack them and, sitting later in the
   file than .voice-by, it won; the two are merged here so there is one place
   that decides. */
.voice figcaption{
  /* auto, not a fixed margin: the three quotes are different lengths, so
     without this each attribution sits wherever its quote happens to end and
     the row of cards reads as ragged. Pinned to the foot, the faces, names
     and ratings line up across all three. */
  margin-top:auto;padding-top:20px;border-top:1px solid var(--line);
  display:flex;flex-direction:row;align-items:center;gap:14px;
  align-self:stretch;width:100%;text-align:left;
}
.voice-stars{display:flex;gap:3px;margin-bottom:3px}
.voice-stars svg{width:13px;height:13px;fill:var(--line-strong)}
.voice-stars svg.on{fill:var(--gold)}
.voice.is-pending .voice-stars svg.on{fill:var(--gold-soft)}
.voice-name{
  font-family:var(--font-display);text-transform:uppercase;
  letter-spacing:var(--tracking-name);font-size:.82rem;color:var(--ink);
}
.voice-meta{font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-faint)}
/* While the quotes are placeholders they are visibly marked, so nobody can
   mistake them for real reviews or ship them by accident. */
.voice.is-pending blockquote{font-style:italic;color:var(--ink-faint)}
.voice.is-pending .voice-name,
.voice.is-pending .voice-meta{color:var(--ink-faint);opacity:.75}
.voices-note{
  position:relative;z-index:1;margin-top:22px;text-align:center;
  font-size:.8rem;letter-spacing:.02em;color:var(--terracotta);
  max-width:60ch;margin-inline:auto;
}

/* a photograph at the head of each treatment-family card */
.card.has-photo{padding-top:0;overflow:hidden}
.card-photo{
  display:block;margin:0 -26px 20px;aspect-ratio:16/10;overflow:hidden;
  background:var(--paper-3);
}
/* Same drift as the How-it-works row. The timeline is named on .card — which
   is itself clipped, but that only matters for its descendants; the timeline
   declared on it resolves against .card's own nearest scroll container, which
   is the page.

   The hover scale that used to live here is gone. A CSS animation owns the
   transform for its whole duration, so a hover transition on the same
   property would simply never win; the card still answers to hover through
   its border and shadow. */
.card-photo img{
  width:100%;height:100%;object-fit:cover;display:block;

}

.card.has-photo h3{margin-top:0}

/* Equal-height cards with the link pinned to the foot, a taller crop so faces
   are not cut at the chin, and the concerns set as a ruled list rather than
   pills — chips read as shop filters and were the loudest thing on the row. */
.cards > .card{display:flex;flex-direction:column}
.card.has-photo{border:0;box-shadow:var(--shadow-soft);border-radius:4px}
.card-photo{aspect-ratio:4/3}
/* Julius Sans One ships a single weight, so font-weight does nothing to these
   titles — measured, 400 and 700 render pixel-identically and the browser
   does not synthesize a faux bold. A hairline stroke in the text's own colour
   thickens the real letterforms instead, which keeps the display face. The
   alternative was swapping these to the body font at 600, which gets weight
   by changing the typeface — heavier, but it stops looking like the brand. */
.card.has-photo h3{font-size:.82rem;line-height:1.5;-webkit-text-stroke:0.4px currentColor}
.card.has-photo > p{min-height:3.4em}
.card-helps{list-style:none;margin:18px 0 22px;padding:0;display:grid}
.card-helps li{
  font-size:.82rem;color:var(--ink-mute);padding-block:9px;
  border-top:1px solid var(--line);
}
.card-helps li:first-child{border-top:0;padding-top:0}
.cards > .card .card-link{margin-top:auto}
.start-links{display:flex;flex-wrap:wrap;gap:4px 30px;margin-top:auto;padding-top:16px}
.start-links .card-link{padding-top:0}

/* "Not sure where to start?" used to be the last cell of the families grid,
   where `grid-column: 1 / -1` gave it the full row. It stands on its own
   under the rail now, so it needs its own top padding back — .card zeroes the
   top because every other one begins with a photograph or a rule flush to the
   edge — and a little air between it and the rail above. */
.start-card{
  margin-top:34px;
  padding-top:0;
  /* A solid white slab directly under a row of photographs reads as a
     different page element that happened to land there. Letting the section's
     own tint through, with a lighter edge and no lift, makes it part of the
     same block — a closing note under the rail rather than a panel on top of
     it. */
  background:rgba(255,255,255,.5);
  border-color:rgba(159,83,53,.12);
  box-shadow:none;
}
.start-card h3{margin-top:26px}
.start-card h2{margin-top:26px;font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:.82rem;line-height:1.5;-webkit-text-stroke:0.4px currentColor}

/* ---------- concerns index ----------
   A reference list, not a feature: the job is to let someone find their own
   words fast, so it is set as rows rather than cards. Two columns from the
   point there is room for them, because seventy-five single-file rows is a
   long scroll for something you are scanning rather than reading. */
.concern-group{margin-top:52px}
.concern-group h2{
  font-family:var(--font-display);text-transform:uppercase;
  letter-spacing:var(--tracking-name);font-size:.86rem;color:var(--terracotta);
  padding-bottom:12px;border-bottom:1px solid var(--line-strong);
}
.concern-list{list-style:none;margin:0;padding:0}
.concern-list li{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 18px;
  padding-block:13px;border-bottom:1px solid var(--line);
}
/* The concern leads, because that is what is being scanned for; the treatment
   follows as the answer to it.

   That was the intent and the colour contradicted it. Every treatment link was
   terracotta, and there are a hundred of them on this page — so the accent
   stopped being an accent and the loudest thing on screen became the answers
   rather than the questions. The names carry the weight now; the links keep an
   underline so they still read as clickable, and take the accent on hover,
   which is the one moment it means something. */
.concern-name{flex:1 1 15ch;font-size:.98rem;color:var(--ink);font-weight:500}
.concern-to{flex:1 1 20ch;font-size:.9rem;color:var(--ink-mute)}
.concern-to a{
  /* A hundred links in a dense list, each one 19px tall on a phone, so they
     carry vertical padding to make a thumb target. Padding on an INLINE box
     does that without touching the layout — no negative margin to undo, and
     no box to push the line apart.

     The underline has to be a text decoration rather than a border for the
     same reason. A border is painted on the padded box, so at 11px it parted
     company with the words it belonged to and, wherever a row wrapped, ran
     straight through the line underneath — which read as text crossed out.
     A decoration follows the text itself, on every line it occupies. */
  display:inline;padding-block:11px;
  color:var(--ink-soft);
  text-decoration:underline;
  text-decoration-color:var(--line-strong);
  text-decoration-thickness:1px;
  text-underline-offset:3px;
}
.concern-to a:hover{color:var(--terracotta);text-decoration-color:var(--terracotta)}
.concern-sep{margin-inline:7px;color:var(--ink-faint)}
@media (min-width:860px){
  /* `columns: 2` fills down the left column and wraps to the right, so the two
     sides have no row relationship: the dividing rules landed at different
     heights across the gutter and the right column ended ragged. It read as
     broken rather than as a deliberate two-column list. A grid puts real rows
     across the gutter, and alphabetical order now reads across, which is how
     people scan a list like this anyway. */
  .concern-list{display:grid;grid-template-columns:1fr 1fr;column-gap:52px}
}

/* The family's own hue. A bar over the photograph, the rule under the
   heading and the link all take it, so the six cards read as six things
   rather than one thing six times. Falls back to peach if no tone is set. */
.card.has-photo{--fam:var(--peach)}
.card-photo::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:3px;
  background:linear-gradient(90deg,var(--fam),rgba(255,255,255,0));
}
.card-photo{position:relative}
.card.has-photo .accent-stub{width:52px;background:linear-gradient(90deg,var(--fam),transparent)}
/* The family hue stays on the bar and the rule, which are decoration and
   carry no contrast requirement. It does not go on the link: at 11.2px the
   gold read 2.49:1 and the peach 2.56:1 against the card, where 4.5 is the
   floor. Terracotta is 5.32 and still unmistakably the brand. */
.card.has-photo .card-link{color:var(--terracotta)}
.card.has-photo .helps li{border-color:color-mix(in srgb,var(--fam) 32%,transparent)}
.card.has-photo:hover{border-color:color-mix(in srgb,var(--fam) 42%,transparent)}

/* ---------- policies ---------- */
.policy-jump{
  position:relative;z-index:1;
  display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin-top:30px;
}
.policy-jump a{
  font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;text-decoration:none;
  color:var(--ink-mute);padding:8px 16px;border-radius:var(--radius-pill);
  border:1px solid var(--line);background:var(--paper-2);
  transition:color 160ms var(--ease-out),border-color 160ms var(--ease-out);
}
@media (hover:hover) and (pointer:fine){
  .policy-jump a:hover{color:var(--terracotta);border-color:var(--line-strong)}
}
.policy-set{position:relative;z-index:1;display:grid;gap:20px;margin-top:34px}
.policy{padding:30px 32px 34px;scroll-margin-top:96px}
.policy h3{
  font-family:var(--font-display);text-transform:uppercase;
  letter-spacing:var(--tracking-name);font-size:1.02rem;margin-top:22px;
}
.policy-lead{margin-top:12px;max-width:62ch}
.policy-block{margin-top:26px;padding-top:22px;border-top:1px solid var(--line)}
.policy-block h4{
  font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-faint);font-weight:400;
}
.policy-block p{margin-top:10px;color:var(--ink-mute);max-width:70ch}
.policy-block p + p{margin-top:12px}
.policy-note{
  position:relative;z-index:1;margin-top:28px;text-align:center;
  font-size:.84rem;color:var(--ink-faint);max-width:62ch;margin-inline:auto;
}
.form-privacy{margin-top:14px;font-size:.78rem;color:var(--ink-faint)}
.form-privacy a{color:var(--terracotta)}
@media (max-width:620px){
  .policy{padding:26px 22px 28px}
}

.pill{font-size:.7rem;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-mute);padding:7px 15px;border-radius:var(--radius-pill);border:1px solid var(--line);background:var(--paper-2)}

/* page hero */
.page-hero{position:relative;padding-block:62px 0;overflow:hidden}
.page-hero .glowfield{top:-340px;height:700px}
.page-hero-inner{position:relative;z-index:1;display:flex;flex-direction:column;gap:18px;max-width:64ch}
.page-hero h1{font-size:clamp(2.1rem,4.8vw,3.3rem)}
.crumbs{font-size:.74rem;letter-spacing:.08em;color:var(--ink-faint);display:flex;flex-wrap:wrap;gap:8px}
.crumbs a{text-decoration:none;color:var(--ink-mute)}
.crumbs a:hover{color:var(--terracotta)}
.page-hero-actions{display:flex;flex-wrap:wrap;gap:13px;margin-top:6px}

/* ---------- the name, at scale ----------
   The wordmark set enormous behind the arch, with the photograph crossing it.
   Decorative only — the real wordmark is in the header, so this is aria-hidden
   and never read twice. */
.hero-stage{
  position:relative;z-index:1;width:100%;margin-top:40px;
  display:flex;justify-content:center;
  padding-top:clamp(36px,6vw,80px);
}
.hero-wordmark{
  position:absolute;left:50%;top:0;transform:translateX(-50%);
  z-index:0;pointer-events:none;user-select:none;white-space:nowrap;
  font-family:var(--font-display);text-transform:uppercase;
  font-size:clamp(5.5rem,24vw,18rem);line-height:.78;
  letter-spacing:.02em;margin-right:-.02em;
  /* deep enough to read as a decision rather than a misprint */
  color:var(--peach);opacity:.34;
  animation:rise 1100ms var(--ease-out) 240ms backwards;
}
@media (min-width:1000px){
  /* Centred on the arch, it runs out past the words on the left — the subject
     crossing the letters is the whole point of the composition. */
  .hero-wordmark{font-size:clamp(8rem,15vw,15rem);top:-6%}
}

/* ---------- the portal ----------
   One arch framing the clinic's own backlit arch. On paper it reads as a
   window cut into the page, with light spilling around its edge. */
.hero-portal{position:relative;z-index:1;margin:0;width:min(440px,76vw);aspect-ratio:3/4.05}
.portal-halo{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:185%;aspect-ratio:1/1;border-radius:50%;pointer-events:none;z-index:0;
  background:radial-gradient(closest-side,
    rgba(255,226,198,.95) 0%, rgba(244,206,170,.6) 40%,
    rgba(217,139,95,.2) 64%, rgba(217,139,95,.06) 82%, transparent 100%);
}
@keyframes halo{
  0%,100%{opacity:.8;transform:translate(-50%,-50%) scale(1)}
  50%{opacity:1;transform:translate(-50%,-50%) scale(1.09)}
}
.portal-frame{
  position:absolute;inset:0;display:block;overflow:hidden;border-radius:var(--arch);
  background:var(--paper-3);
  box-shadow:var(--shadow-deep), 0 0 0 1px rgba(159,83,53,.1), 0 0 70px rgba(217,139,95,.26);
  animation:portal-open 1200ms var(--ease-in-out) 420ms backwards;
}
@keyframes portal-open{from{clip-path:inset(100% 42% 0 42%)}to{clip-path:inset(0 0 0 0)}}
.portal-frame img{
  width:100%;height:100%;object-fit:cover;object-position:50% 28%;
  filter:saturate(1.02) contrast(1.02);
  animation:kenburns 26s ease-in-out infinite alternate;
}
/* Gentle: a portrait cannot take the zoom an interior can before it crops the face. */
@keyframes kenburns{from{transform:scale(1)}to{transform:scale(1.05)}}
.portal-label{
  position:absolute;left:0;right:0;bottom:0;z-index:2;padding:24px 16px 20px;text-align:center;
  background:linear-gradient(to top,rgba(42,29,22,.62),rgba(42,29,22,0));
  font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-label);
  font-size:var(--t-label);color:#FFF6EE;animation:rise 700ms var(--ease-out) 1500ms backwards;
}

/* ---------- marquee ---------- */
.marquee{
  position:relative;z-index:1;margin-top:68px;overflow:hidden;
  border-block:1px solid var(--line);padding-block:17px;background:var(--paper-2);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);
}
.marquee-track{display:flex;width:max-content;animation:marquee 64s linear infinite}
.marquee-item{
  display:inline-flex;align-items:center;gap:22px;padding-right:22px;white-space:nowrap;
  font-family:var(--font-display);text-transform:uppercase;letter-spacing:.2em;
  font-size:var(--t-label);color:var(--ink-mute);
}
.marquee-item i{font-style:normal;color:var(--peach)}
@keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.marquee:hover .marquee-track{animation-play-state:paused}

/* ---------- sections ---------- */
section{scroll-margin-top:104px}
.band{padding-block:104px;position:relative}
.band-tight{padding-block:72px;position:relative}
/* There was a clipping rule here for glows that hang below their section.
   Clipping was the wrong tool twice over: a glow is 132% of its section's
   width on purpose — meant to bleed past both edges and dissolve — so
   clipping both axes drew a hard seam down each side. The glow that actually
   overhung is anchored to its section's foot now, in the Glow component,
   which is where the problem was. Nothing needs clipping. */
.sec-head{display:flex;flex-direction:column;align-items:center;gap:18px;text-align:center;margin-bottom:56px;position:relative;z-index:1}
.sec-head.left{align-items:flex-start;text-align:left}
.sec-head h2{font-size:clamp(1.9rem,3.6vw,2.7rem)}
.sec-head .lede{max-width:56ch}

/* ---------- rows ---------- */
.rows{display:grid;gap:18px;position:relative;z-index:1}
.row{
  border-radius:var(--radius-panel);padding:18px;display:grid;grid-template-columns:320px 1fr;gap:34px;align-items:center;
  transition:transform var(--dur-hover) var(--ease-out),box-shadow var(--dur-hover) var(--ease-out),border-color var(--dur-hover) var(--ease-out);
}
.row:hover{border-color:var(--line-strong);box-shadow:var(--shadow-lift)}
@media (hover:hover) and (pointer:fine){ .row:hover{transform:translateY(-2px)} }
.tile{position:relative;aspect-ratio:4/3;border-radius:var(--radius-card);overflow:hidden;background:var(--paper-3)}
/* And on the category pages, where a column of rows is most of the page.
   .row carries its own hover lift via transform, so the timeline is named on
   .rows' child rather than on anything whose transform is already spoken for;
   the image inside is what moves. */

.row-body{min-width:0}
.row-body .kicker{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-label);font-size:var(--t-label);color:var(--terracotta)}
.row-body h3{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:1.04rem;line-height:1.45;margin-top:14px}
.row-body h3 a{text-decoration:none}
.row-body h3 a:hover{color:var(--terracotta)}
.row-body .accent-stub{margin-block:16px}
.row-body p{color:var(--ink-mute);font-size:.96rem;max-width:58ch}
.row-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px}

.helps{display:flex;flex-wrap:wrap;gap:7px;margin-top:16px;list-style:none;padding:0}
.helps li{font-size:.7rem;letter-spacing:.07em;color:var(--ink-mute);padding:6px 12px;border-radius:var(--radius-pill);border:1px solid var(--line);background:var(--paper-2)}

/* ---------- cards ---------- */
/* Fixed three across rather than auto-fit. auto-fit gave four columns at this
   width, which left the six families as 4 + 2 and the prompt card stranded
   beside a hole. Three columns divide six exactly, and the prompt card spans
   whatever the row has — so there is never an empty cell at any width. */
.cards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;position:relative;z-index:1}
.cards > .card:last-child{grid-column:1 / -1}
@media (max-width:980px){.cards{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:640px){.cards{grid-template-columns:minmax(0,1fr)}}
.card{
  border-radius:var(--radius-panel);padding:0 24px 26px;overflow:hidden;min-width:0;display:flex;flex-direction:column;
  transition:transform var(--dur-hover) var(--ease-out),box-shadow var(--dur-hover) var(--ease-out),border-color var(--dur-hover) var(--ease-out);
}
.card .accent-line{margin-inline:-24px;width:calc(100% + 48px)}
.card h3{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:.8rem;line-height:1.5;margin-top:26px}
.card h3 a{text-decoration:none}
.card h3 a:hover{color:var(--terracotta)}
.card .accent-stub{margin-block:14px}
.card p{font-size:.92rem;color:var(--ink-mute)}
/* inline-flex with a min-height, because as a plain inline link this was a
   22px tap target carrying the card's only call to action. The padding was
   already there; an inline box just does not take it as height. */
.card .card-link{
  margin-top:auto;padding-top:20px;min-height:44px;
  display:inline-flex;align-items:flex-end;align-self:flex-start;
  font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;color:var(--terracotta);text-decoration:none;
}
.card .card-link:hover{color:var(--terracotta-deep)}
.card:hover{border-color:var(--line-strong);box-shadow:var(--shadow-lift)}
@media (hover:hover) and (pointer:fine){ .card:hover{transform:translateY(-2px)} }

/* ---------- chips ---------- */
.cat-nav{display:flex;flex-wrap:wrap;gap:9px;justify-content:center;margin-bottom:34px;position:relative;z-index:1}
.chip{
  /* A chip is a navigation control, not a caption — 10.2px was the smallest
     clickable thing on the site. */
  font-family:var(--font-display);text-transform:uppercase;letter-spacing:.15em;font-size:var(--t-meta);
  padding:9px 16px;border-radius:var(--radius-pill);border:1px solid var(--line);
  color:var(--ink-mute);background:var(--card);cursor:pointer;text-decoration:none;
  transition:color var(--dur-hover) var(--ease-out),border-color var(--dur-hover) var(--ease-out),background var(--dur-hover) var(--ease-out);
}
.chip:hover{color:var(--ink);border-color:var(--line-strong)}
.chip[aria-selected="true"],.chip[aria-current="page"]{background:var(--terracotta);border-color:var(--terracotta);color:#FFF6EE}

/* ---------- treatment page ---------- */
/* 68ch looked like a correct measure and was not. The `ch` unit is the width
   of the "0" glyph, and in Figtree that is 10.91px at 17px — 27% wider than
   the 8.62px average of its lowercase letters. So `max-width:68ch` resolved
   to 741px and put 86 characters on a line, not 68. Comfortable is 55–75;
   past about 80 the eye starts losing the return sweep, which matters here
   because the treatment explanations are the longest reading on the site.
   55ch measures out at roughly 70 actual characters. */
.prose{display:grid;gap:18px;max-width:55ch;position:relative;z-index:1}
.prose p{color:var(--ink-soft)}
.panel{border-radius:var(--radius-panel);padding:40px 36px;position:relative;overflow:hidden;background:var(--card);border:1px solid var(--line);box-shadow:var(--shadow-soft)}
.panel > .accent-line{position:absolute;inset:0 0 auto}
.panel h2{font-size:clamp(1.5rem,3vw,2rem)}

.glance{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:1px;background:var(--line);border-radius:var(--radius-card);overflow:hidden;margin-top:28px;border:1px solid var(--line)}
.glance div{background:var(--paper-2);padding:22px 20px}
/* 9.3px with .32em of tracking. These five labels — Time, Downtime, Sessions,
   Results, Comfort — head the most practical block on a treatment page. */
.glance dt{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-label);font-size:var(--t-label);color:var(--terracotta)}
.glance dd{margin:10px 0 0;font-size:.94rem;color:var(--ink)}

.who{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px;margin-top:26px}
.who-card{border-radius:var(--radius-card);padding:24px 22px;background:var(--card);border:1px solid var(--line);box-shadow:var(--shadow-soft)}
.who-card h3{font-size:.98rem;font-family:var(--font-core);font-weight:400;color:var(--ink)}
.who-card p{margin-top:10px;font-size:.9rem;color:var(--ink-mute)}
.who-card.exclude{background:var(--peach-wash);border-color:var(--line-strong)}
.who-card.exclude h3{color:var(--terracotta)}

/* ---------- how it works ----------
   Four equal white boxes in a row read as a feature grid, not as a clinic.
   The boxes are gone: the photographs sit directly on the paper in portrait,
   the columns are offset against each other so the row is not a wall, and the
   step number is set large and thin in the display face. Nothing is enclosed,
   which is most of what makes it feel considered. */
.steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:30px;margin-top:40px}
.step{position:relative;background:none;border:0;box-shadow:none;padding:0;border-radius:0}

/* Only the version with photographs gets the editorial treatment; the plain
   step lists on treatment pages keep an even row. */
.steps:has(.step-photo){grid-template-columns:repeat(4,minmax(0,1fr));gap:clamp(20px,2.6vw,38px);margin-top:56px}
.steps:has(.step-photo) .step:nth-child(even){margin-top:clamp(28px,5vw,68px)}

.step-photo{
  margin:0 0 24px;aspect-ratio:3/4;overflow:hidden;border-radius:3px;
  background:var(--paper-3);box-shadow:var(--shadow-soft);
}
/* The frame clips; the reveal above moves the photograph inside it. */
.step-photo img{width:100%;height:100%;object-fit:cover;display:block}

.step .n,.step-n{
  font-family:var(--font-display);font-size:1.9rem;line-height:1;letter-spacing:.06em;
  color:var(--peach);display:block;width:auto;height:auto;border:0;background:none;border-radius:0;
}
.step-rule{display:block;width:100%;height:1px;border:0;margin:15px 0 14px;background:linear-gradient(90deg,var(--line-strong),transparent 85%)}
.step h3{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:.76rem;margin-top:0}
.step p{margin-top:11px;font-size:.9rem;color:var(--ink-mute);max-width:34ch}

@media (max-width:860px){
  .steps:has(.step-photo){grid-template-columns:repeat(2,minmax(0,1fr))}
  .steps:has(.step-photo) .step:nth-child(even){margin-top:0}
  .steps:has(.step-photo) .step:nth-child(2){margin-top:clamp(24px,6vw,48px)}
  .steps:has(.step-photo) .step:nth-child(4){margin-top:clamp(24px,6vw,48px)}
}
@media (max-width:540px){
  .steps:has(.step-photo){grid-template-columns:minmax(0,1fr)}
  .steps:has(.step-photo) .step{margin-top:0!important}
  .step-photo{aspect-ratio:4/3}
}

.expect{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:18px;margin-top:26px}
.expect-block h3{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:.74rem;color:var(--terracotta)}
.expect-block .accent-stub{margin-block:12px}
.expect-block ul{list-style:none;margin:0;padding:0;display:grid}
.expect-block li{display:flex;gap:11px;font-size:.92rem;color:var(--ink-mute);padding-block:10px;border-top:1px solid var(--line)}
.expect-block li:first-child{border-top:0}
.expect-block li span{color:var(--peach);flex:0 0 auto}

.faq{display:grid;gap:7px;margin-top:26px;position:relative;z-index:1}
details.qa{border-radius:var(--radius-card);border:1px solid var(--line);background:var(--card);overflow:hidden;box-shadow:var(--shadow-soft)}
details.qa summary{
  cursor:pointer;list-style:none;padding:13px 18px;font-size:.9rem;color:var(--ink);
  display:flex;align-items:flex-start;gap:12px;line-height:1.45;transition:background var(--dur-hover) var(--ease-out);
}
details.qa summary::-webkit-details-marker{display:none}
details.qa summary::after{content:"+";margin-left:auto;color:var(--terracotta);font-size:1rem;line-height:1.3;flex:0 0 auto}
details.qa[open] summary::after{content:"–"}
details.qa summary:hover{background:var(--peach-wash)}
details.qa .answer{padding:0 18px 15px;font-size:.88rem;line-height:1.62;color:var(--ink-mute);max-width:70ch}

.related{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-top:26px}
.related a{
  border-radius:var(--radius-card);padding:22px 20px;text-decoration:none;display:block;
  background:var(--card);border:1px solid var(--line);box-shadow:var(--shadow-soft);
  transition:border-color var(--dur-hover) var(--ease-out),transform var(--dur-hover) var(--ease-out),box-shadow var(--dur-hover) var(--ease-out);
}
.related a:hover{border-color:var(--line-strong);box-shadow:var(--shadow-lift)}
@media (hover:hover) and (pointer:fine){ .related a:hover{transform:translateY(-2px)} }
.related .kicker{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-label);font-size:var(--t-label);color:var(--terracotta)}
.related h3{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:.78rem;margin-top:12px}

.why-list{display:grid;gap:0;margin-top:22px;list-style:none;padding:0}
.why-list li{display:flex;gap:14px;align-items:baseline;font-size:.94rem;color:var(--ink-soft);padding-block:13px;border-top:1px solid var(--line)}
.why-list li:first-child{border-top:0}
.why-list span{color:var(--peach);flex:0 0 auto}

/* ---------- clinic ---------- */
.clinic-grid{display:grid;grid-template-columns:.92fr 1.08fr;gap:52px;align-items:center;position:relative;z-index:1}
.arch-pair{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.arch-sm{
  position:relative;aspect-ratio:3/4.2;border-radius:var(--arch);overflow:hidden;margin:0;
  background:var(--paper-3);box-shadow:var(--shadow-lift);
}
.arch-sm.warm{margin-top:34px}
.detail-list{list-style:none;margin:24px 0 0;padding:0;display:grid}
.detail-list li{display:flex;gap:14px;align-items:baseline;font-size:.94rem;color:var(--ink-soft);padding-block:13px;border-top:1px solid var(--line)}
.detail-list li:first-child{border-top:0}
.detail-list .bullet{color:var(--peach);flex:0 0 auto}

/* ---------- memberships ---------- */
.tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(256px,1fr));gap:18px;position:relative;z-index:1}
.tier{border-radius:var(--radius-panel);padding:0 26px 30px;overflow:hidden;min-width:0}
.tier .tier-line{display:block;height:2px;border:0;margin:0 -26px}
.tier.gold .tier-line{background:linear-gradient(90deg,transparent,var(--gold-soft) 28%,var(--gold) 70%,transparent)}
.tier.terra .tier-line{background:linear-gradient(90deg,transparent,var(--peach-soft) 28%,var(--terracotta) 70%,transparent)}
.tier.plat .tier-line{background:linear-gradient(90deg,transparent,#DCD8D1 28%,var(--silver) 70%,transparent)}
.tier-card{
  position:relative;margin:26px 0 0;aspect-ratio:1050/650;border-radius:14px;overflow:hidden;
  box-shadow:var(--shadow-lift);transition:transform var(--dur-ui) var(--ease-out),box-shadow var(--dur-ui) var(--ease-out);
}
.tier-card img{width:100%;height:100%;object-fit:cover;display:block}

/* The card turns over on hover, the way you would turn a real one. The
   container holds the perspective, the inner layer does the rotating, and the
   two faces hide their own backs so only one is ever readable. Overflow has
   to be visible here or the back face is clipped away mid-turn. */
.tier-card{
  /* The container only holds the perspective. It must not move or carry a
     transform: a transform here would animate the 3D scene the card lives
     inside.

     The perspective was 1100px against a card that renders about 300px wide —
     nearly four times its own width, which is so far back that the turn had
     almost no foreshortening. The card appeared to squash horizontally rather
     than rotate, which is the flat, mechanical look. At roughly 2.5x the
     rendered width the near edge genuinely swings toward the viewer. */
  overflow:visible;perspective:760px;
  box-shadow:none;transform:none;transition:none;
}
/* The shadow lives here, on a layer that never rotates.

   It used to sit on each face. That put a 54px-blur shadow on two surfaces
   that turn in 3D — repainted through the whole animation — and, worse, the
   shadow turned with the card and mirrored itself halfway through. A card
   lifted off a surface keeps its shadow where it is; only the card moves. */
.tier-card::before{
  content:"";position:absolute;inset:0;border-radius:14px;
  box-shadow:var(--shadow-lift);
  transform:translateZ(-1px);
}
.tier-flip{
  position:relative;width:100%;height:100%;
  transform-style:preserve-3d;
  /* Read this before changing the curve again. A rotateY is not linear on
     screen even when it is linear in degrees: foreshortening makes the card
     appear to collapse and expand fastest as it passes edge-on, at 90°. So
     the midpoint already carries a built-in acceleration, and any easing that
     is also fastest in the middle lands a second one on top of it.

     That is what was here. cubic-bezier(.5,0,.2,1) holds flat for the first
     half and then whips, and the whip coincided with the edge-on crossing —
     which is why a curve that is not remotely linear read as mechanical.

     An ease-out puts the speed at the start, while the card is still face-on
     and legible, and lets the edge-on crossing happen during the settle. That
     is how a real card behaves when you flick it over: all the energy goes in
     at the beginning and friction takes it from there.

     Then 560ms was still quick enough to read as a UI transition rather than
     an object with mass. Expensive things move slowly and are hard to stop,
     so this runs nearly a second, and the curve carries a little resistance
     at the start — a heavy card does not leap, it is pushed. Peak speed lands
     around a third of the way through, still before the edge-on crossing, so
     the settle is the longest part of the turn and nothing whips. */
  transition:transform 900ms cubic-bezier(.4,.05,.2,1);
  will-change:transform;
}
.tier-face{
  /* No overflow:hidden. Clipping a face that is rotating in 3D forces the
     browser to rebuild the mask every frame; rounding the face and the image
     gets the same corners for nothing. No box-shadow either — see ::before. */
  position:absolute;inset:0;border-radius:14px;
  -webkit-backface-visibility:hidden;backface-visibility:hidden;
  background:var(--paper);
  /* The two faces used to sit at exactly the same depth. Coplanar surfaces in
     a preserve-3d scene can z-fight as they pass through each other at the
     halfway point, which shows up as a flicker. A hair of separation each way
     removes the tie without being visible. */
  transform:translateZ(.6px);
}
.tier-front img{border-radius:14px}
.tier-back{
  transform:rotateY(180deg) translateZ(.6px);
  display:flex;flex-direction:column;justify-content:center;align-items:center;
  gap:9px;padding:18px;text-align:center;
}
.tier-back-mark{
  font-family:var(--font-display);letter-spacing:.42em;font-size:1.05rem;
  margin-right:-.42em;
}
.tier-back-name{
  font-size:.56rem;letter-spacing:.3em;text-transform:uppercase;
}
/* the signature strip a real card carries on its reverse */
.tier-back-strip{display:block;width:62%;height:9px;border-radius:2px;margin-top:4px}
.tier-back-note{font-size:.5rem;letter-spacing:.16em;text-transform:uppercase;opacity:.72}

.tier.gold .tier-back{background:linear-gradient(145deg,#E8C489,#C9974F 58%,#A97C38);color:#4A3417}
.tier.gold .tier-back-strip{background:rgba(74,52,23,.3)}
.tier.terra .tier-back{background:linear-gradient(145deg,#B8653F,#9F5335 58%,#7A3C25);color:#FBE6D6}
.tier.terra .tier-back-strip{background:rgba(251,230,214,.32)}
.tier.plat .tier-back{background:linear-gradient(145deg,#EDEBE8,#CFCBC4 58%,#AFAAA2);color:#3A3630}
.tier.plat .tier-back-strip{background:rgba(58,54,48,.26)}

@media (hover:hover) and (pointer:fine){
  .tier:hover .tier-flip,
  .tier:focus-within .tier-flip{transform:rotateY(180deg)}
}
/* The card used to lift on hover as well as turn. Two transforms on nested
   elements, one of them the 3D scene's own container, is what made the turn
   stutter — and the lift was invisible once the card was rotating anyway. */
.tier h3{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:.96rem;margin-top:24px}
.tier-kicker{font-size:.76rem;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-faint);margin-top:8px}
.tier ul{list-style:none;margin:18px 0 0;padding:0;display:grid}
.tier li{font-size:.9rem;color:var(--ink-mute);display:flex;gap:11px;padding-block:11px;border-top:1px solid var(--line)}
.tier li:first-child{border-top:0}
.tier li span{color:var(--peach)}

/* .card-link is scoped to .card, and a tier is not one, so the link needs the
   styling repeated here. Terracotta rather than the tier's own gold or silver
   for the same reason the family cards use it: at this size the metallics
   measure around 2.5:1 against the panel, well under the 4.5 floor. */
.tier-link{
  /* min-height, because these three are the membership section's only call
     to action and a 22px inline link is a poor thumb target. */
  display:inline-flex;align-items:center;min-height:44px;margin-top:12px;
  font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--terracotta);text-decoration:none;
  border-bottom:1px solid transparent;
}
.tier-link:hover{color:var(--terracotta-deep);border-bottom-color:currentColor}
/* The tiers carry no price, by design. Without a line saying where the number
   comes from, three cards that list what you get and never say what it costs
   read as an omission rather than a policy. */
.tiers-note{
  position:relative;z-index:1;
  max-width:62ch;margin:26px auto 0;text-align:center;
  font-size:.88rem;color:var(--ink-mute);
}

/* ---------- visit + form ---------- */
/* stretch, not start: the two panels are a pair and should end on the same
   line. The form keeps its natural height and the visit panel grows to meet
   it, with its last block pushed to the foot. */
.visit-grid{display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:stretch;position:relative;z-index:1}
.visit-panel,form.book{border-radius:var(--radius-panel);padding:40px 34px;position:relative;overflow:hidden}
.visit-panel{display:flex;flex-direction:column}
.visit-panel .contact-line{margin-top:auto}
.visit-panel > .accent-line,form.book > .accent-line{position:absolute;inset:0 0 auto}
.branch{padding-block:22px;border-top:1px solid var(--line)}
.branch h3{font-family:var(--font-display);text-transform:uppercase;letter-spacing:var(--tracking-name);font-size:.78rem;color:var(--terracotta)}
.branch address{font-style:normal;font-size:1rem;color:var(--ink);margin-top:10px}
.branch .hours{font-size:.86rem;color:var(--ink-faint);margin-top:8px}
.contact-line{margin-top:22px;padding-top:22px;border-top:1px solid var(--line);display:grid;gap:8px}
.contact-line .label{font-size:.76rem;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-faint)}
.contact-line .num{font-size:1.32rem;color:var(--ink);letter-spacing:.04em;font-variant-numeric:tabular-nums}
.copy-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.copy-btn{
  font-size:.68rem;letter-spacing:.13em;text-transform:uppercase;font-family:var(--font-core);font-weight:500;
  background:var(--paper-2);border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  padding:7px 14px;color:var(--terracotta);cursor:pointer;transition:background var(--dur-hover) var(--ease-out);
}
.copy-btn:hover{background:var(--peach-wash)}
.field{display:grid;gap:7px;margin-bottom:16px}
/* Was .68rem — 10.88px, under the 12px floor, on the one form that has to
   convert. Tracking pulled back a little too: wide letter-spacing costs more
   legibility at small sizes than the size itself buys back. */
.field label{font-size:.78rem;letter-spacing:.11em;text-transform:uppercase;color:var(--ink-faint)}
.field input,.field select,.field textarea{
  font-family:var(--font-core);font-size:1rem;font-weight:400;color:var(--ink);
  background:var(--paper);border:1px solid var(--line);border-radius:8px;padding:12px 14px;width:100%;
  transition:border-color var(--dur-hover) var(--ease-out),background var(--dur-hover) var(--ease-out);
}
.field input::placeholder,.field textarea::placeholder{color:var(--ink-faint)}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--peach);background:var(--card)}
/* The field the message is about, marked so it can be seen as well as heard.
   Terracotta rather than a signal red: this is "we cannot ring that number",
   not an error state, and the palette already carries the emphasis. Colour is
   not doing the work alone — the message names the field and the cursor is
   sent to it. */
.field input[aria-invalid="true"],
.field select[aria-invalid="true"],
.field textarea[aria-invalid="true"]{
  border-color:var(--terracotta);
  box-shadow:0 0 0 3px rgba(159,83,53,.12);
}
.field textarea{resize:vertical;min-height:84px}
.two-up{display:grid;grid-template-columns:1fr 1fr;gap:16px}
form.book .btn{width:100%;margin-top:6px}
.form-note{font-size:.78rem;color:var(--ink-faint);margin-top:14px}
.form-ack{margin-top:14px;padding:13px 16px;border-radius:8px;background:var(--peach-wash);border:1px solid var(--line-strong);color:var(--terracotta);font-size:.88rem}

.reassure{list-style:none;margin:22px 0 0;padding:0;display:grid}
.reassure li{display:flex;gap:12px;font-size:.92rem;color:var(--ink-mute);padding-block:11px;border-top:1px solid var(--line)}
.reassure li:first-child{border-top:0}
.reassure span{color:var(--peach);flex:0 0 auto}

/* ---------- closing ---------- */
.closing{position:relative;padding-block:112px 90px;text-align:center;overflow:hidden}
.closing .glowfield{top:-200px;height:680px}
.closing-inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:20px}
.closing h2{font-size:clamp(1.9rem,4.4vw,3rem);max-width:18ch}
/* The social channels. These were `.handles`: three centred plain-text links
   whose label was the raw URL, 27px tall on a phone against the 44px tap
   minimum, centred inside a card where everything else is left-aligned.

   In this market they are not footer decoration. Messenger is where most
   clinic bookings actually begin, so after the form this is the most
   important control on the page, and it was the quietest thing in the card.
   Buttons now: named, with the handle underneath, at a thumb-sized target. */
.channels{list-style:none;margin:16px 0 0;padding:0;display:flex;flex-wrap:wrap;gap:10px}
.channels-centred{justify-content:center;margin-top:8px}
.channel{
  display:flex;align-items:center;gap:11px;
  min-height:44px;padding:7px 17px 7px 14px;
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  background:var(--card);color:var(--ink);text-decoration:none;
  box-shadow:var(--shadow-soft);
  transition:border-color var(--dur-hover) var(--ease-out),
             color var(--dur-hover) var(--ease-out),
             box-shadow var(--dur-hover) var(--ease-out);
}
/* Each channel carries its own brand colour rather than three identical
   terracotta marks. Recognition is the entire point of these icons — people
   find Facebook by its blue before they read the word — and that is worth
   more here than palette discipline, because this is a control people have
   to spot rather than a decoration they have to live with.

   --brand is the icon and the border tint; --brand-fill is what the pill
   becomes on hover, so Instagram can use its gradient rather than one flat
   pink. The blues and pinks are darkened from the official values: white
   text at 13px needs 4.5:1, and Facebook's own #1877F2 only reaches 3.6. */
.channel[data-brand="Facebook"]{--brand:#0B63CE}
.channel[data-brand="Instagram"]{
  --brand:#C21F73;
  --brand-fill:linear-gradient(48deg,#C7511F,#C21F73 46%,#7A2BA8 76%,#3F4BC4);
}
.channel[data-brand="TikTok"]{--brand:#17161A}
.channel{border-color:color-mix(in srgb,var(--brand,var(--terracotta)) 34%,var(--line-strong))}
.channel svg{flex:0 0 auto;color:var(--brand,var(--terracotta));transition:color var(--dur-hover) var(--ease-out)}
.channel-text{display:grid;line-height:1.25;min-width:0}
.channel-name{font-size:.82rem;font-weight:500}
/* 11.84px — above the floor, and it is the subordinate half of a pair that
   already says what the thing is. */
.channel-handle{font-size:.74rem;color:var(--ink-mute);overflow:hidden;text-overflow:ellipsis}
@media (hover:hover) and (pointer:fine){
  .channel:hover{
    background:var(--brand-fill,var(--brand,var(--terracotta)));
    border-color:var(--brand,var(--terracotta));
    color:#FFF;box-shadow:var(--shadow-lift);
  }
  .channel:hover svg,
  .channel:hover .channel-handle{color:#FFF}
}
/* Keyboard users never hover, so the same fill arrives on focus. */
.channel:focus-visible{
  background:var(--brand-fill,var(--brand,var(--terracotta)));
  border-color:var(--brand,var(--terracotta));color:#FFF;
}
.channel:focus-visible svg,
.channel:focus-visible .channel-handle{color:#FFF}

/* The louder option: the pill wears the brand colour at rest, not only on
   hover. Switched on by adding `brand-solid` to the list. */
.channels.brand-solid .channel{
  background:var(--brand-fill,var(--brand));
  border-color:var(--brand);color:#FFF;
}
.channels.brand-solid .channel svg,
.channels.brand-solid .channel .channel-handle{color:#FFF}
.channels.brand-solid .channel .channel-handle{opacity:.82}
.channel:active{transform:scale(.98)}
/* On a narrow phone three pills side by side shrink the handles to nothing.
   One per row, full width, which is also the easiest thing to hit. */
@media (max-width:460px){
  .channels{display:grid;gap:8px}
  .channel{width:100%}
}
/* The page used to end on bare paper, which read as the design running out
   rather than finishing. The footer is now a band in the same dark the Clinic
   section uses, so the page closes on colour. Same masked-fade trick at the
   top edge: the dark arrives without a line across the page. */
/* Same two-stage approach as the Clinic band: a wide warm wash on the element
   itself, with the dark masked inside it. */
/* The dark used to arrive in the last 150px or so, which read as a strip
   under the page rather than the page closing on colour. The wash is deeper
   and reaches full strength sooner, and the footer itself is taller, so there
   is a real band of dark at the foot instead of a line of it. */
.foot-band{
  position:relative;margin-top:72px;
  background:linear-gradient(180deg,
    rgba(96,56,36,0) 0%, rgba(96,56,36,.10) 8%, rgba(96,56,36,.30) 16%,
    rgba(96,56,36,.52) 26%, rgba(96,56,36,.64) 36%, rgba(96,56,36,.68) 46%,
    rgba(96,56,36,.68) 100%);
}
.foot-band::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(70% 140% at 16% 0%, rgba(159,83,53,.42), transparent 72%),
    linear-gradient(170deg, #35200F 0%, #2A1810 58%, #20120B 100%);
  -webkit-mask-image:var(--fade-down);
  mask-image:var(--fade-down);
}
.foot{
  /* The bottom pad clears the iPhone home indicator, which otherwise sits
     over the last row of links. max() rather than adding, so it costs
     nothing on a phone without one. */
  padding-block:118px max(52px,calc(env(safe-area-inset-bottom,0px) + 30px));
  display:flex;flex-wrap:wrap;gap:12px 28px;
  /* The nav links are 44px tall for the sake of thumbs; the two lines of text
     beside them are not. Without this they sit at different heights. */
  justify-content:space-between;align-items:center;
  font-size:.78rem;color:rgba(255,246,238,.62);
  position:relative;z-index:1;
}
.foot nav{display:flex;flex-wrap:wrap;gap:4px 10px}
/* The footer links were 14px tall, which is a miss on a phone more often
   than a hit. The padding makes each one a 44px row without changing how
   the line reads; the negative inline margin keeps the row aligned with the
   text beside it. */
.foot a{
  text-decoration:none;color:rgba(255,246,238,.66);
  display:inline-flex;align-items:center;min-height:44px;padding-inline:8px;
  transition:color var(--dur-hover) var(--ease-out);
}
.foot nav{margin-inline:-8px}
.foot a:hover{color:var(--peach-soft)}
.foot a:focus-visible{outline:2px solid var(--peach-soft);outline-offset:3px;border-radius:2px}

/* ---------- photography ----------
   On paper the images need no scrim. They run at full brightness; only the
   captions sit on a gradient, and only where they overlap the picture. */
.tile .tile-img,.arch-sm .arch-img,.hero-banner .banner-img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
}
/* Oversized so the scroll drift never pulls an edge into the frame. */

.hero-banner{
  position:relative;z-index:1;margin:40px auto 0;max-width:1140px;
  border-radius:var(--radius-panel);overflow:hidden;aspect-ratio:21/8;
  box-shadow:var(--shadow-lift);
}
.hero-banner .banner-img{position:relative}
@media (hover:hover) and (pointer:fine){
  /* The tile image is driven by scroll now, so it is out of the hover scale:
     an animation owns the transform for its whole duration and the hover
     would never take. The row still lifts and its shadow still deepens. */
  .arch-sm .arch-img{transition:transform 640ms var(--ease-out)}
  .arch-sm:hover .arch-img{transform:scale(1.06)}
}

/* ---------- motion ---------- */
/* The opening. The room arrives first and settles out of a slow push-in, then
   the words, then the strip spreads open beneath them. One pass only — none of
   this loops, so it costs nothing after the first second and a half. */
@keyframes room-in{from{opacity:0;transform:scale(1.1)}to{opacity:1;transform:scale(1.04)}}
.hero-bg img{animation:room-in 1600ms var(--ease-out) backwards}
@keyframes scrim-in{from{opacity:0}to{opacity:1}}
.hero-bg::after{animation:scrim-in 1200ms var(--ease-out) backwards;animation-delay:120ms}

@keyframes rise{from{opacity:0;transform:translateY(26px)}to{opacity:1;transform:none}}
.hero-inner > *{animation:rise 760ms var(--ease-out) backwards}
.hero-inner > *:nth-child(1){animation-delay:80ms}
.hero-inner > *:nth-child(2){animation-delay:150ms}
.hero-inner > *:nth-child(3){animation-delay:220ms}
.hero-inner > *:nth-child(4){animation-delay:290ms}
.hero-inner > *:nth-child(5){animation-delay:360ms}

@keyframes word-in{
  from{opacity:0;transform:translateY(42px);filter:blur(8px)}
  to{opacity:1;transform:none;filter:blur(0)}
}
.hero h1 .w{display:inline-block;animation:word-in 900ms var(--ease-out) backwards}
.hero h1.split{animation:none;opacity:1;transform:none}

@keyframes breathe{
  0%,100%{opacity:.7;transform:translateX(-50%) scale(1) translateY(0)}
  50%{opacity:1;transform:translateX(-50%) scale(1.07) translateY(-12px)}
}
@keyframes drift{
  0%,100%{transform:translateX(-50%) translateY(0)}
  50%{transform:translateX(-50%) translateY(20px)}
}
/* The glows used to breathe and drift on a loop. Each one is a 70-90px blur
   across most of the viewport, and animating a blur that size means the
   browser re-renders it every frame, forever — all of it competing with the
   card strip for the same compositor. They are decorative, nobody watches
   them, and they cost more than anything else on the page. They stay, lit;
   they just hold still now. */

@keyframes draw{from{transform:scaleX(0)}to{transform:scaleX(1)}}
[data-reveal="in"] > .accent-line,
[data-reveal="in"] .accent-stub{transform-origin:left center;animation:draw 760ms var(--ease-in-out) backwards;animation-delay:220ms}

/* The reveal used to wipe with clip-path. clip-path is not a composited
   property — it repaints on the main thread — and the observer's safety net
   can release a couple of dozen of them at once. Transform and opacity read
   almost identically here and cost nothing to composite. */
[data-reveal]{
  opacity:0;transform:translateY(26px);
  transition:
    transform var(--dur-reveal) var(--ease-out),
    opacity 480ms var(--ease-out);
}
[data-reveal="in"]{opacity:1;transform:none}
[data-reveal][data-step="1"]{transition-delay:90ms}
[data-reveal][data-step="2"]{transition-delay:180ms}
[data-reveal][data-step="3"]{transition-delay:270ms}

@media (max-width:900px){
  .clinic-grid,.visit-grid{grid-template-columns:1fr;gap:40px}
  .row{grid-template-columns:1fr;gap:20px}
  .nav{display:none}
  .band{padding-block:70px}
  .band-tight{padding-block:56px}
  .hero{padding-block:58px 0}
  .hero-portal{margin-top:46px;width:min(440px,86vw)}
  .marquee{margin-top:54px}
  .panel,.visit-panel,form.book{padding:30px 22px}
  .hero-banner{aspect-ratio:16/9;margin-top:30px}
}
@media (max-width:560px){
  .two-up{grid-template-columns:1fr}
  .head-in{gap:14px}
  .badge-row .dash{flex-basis:22px}
  .closing{padding-block:80px 64px}
}

/* Reduced motion: keep the fades that aid comprehension, drop the movement. */
@media (prefers-reduced-motion:reduce){
  .hero-inner > *,
  .hero h1 .w,
  .portal-label{animation:none;opacity:1;filter:none;transform:none}
  .hero .arc,.closing .arc,.glow-soft,.portal-halo,.fan-halo{animation:none}
  .hero-bg img,.hero-bg::after{animation:none;opacity:1}
  .portal-frame{animation:none;clip-path:none}
  .portal-frame img{animation:none;transform:none}
  .fan{transition:none;opacity:1}
  .fan-card,.fan-veil{transition-duration:120ms}
  /* a card turning over is movement for its own sake; the front face stays */
  .tier-flip{transition:none}
  .tier:hover .tier-flip,.tier:focus-within .tier-flip{transform:none}
  .marquee-track{animation:none;width:100%;overflow:hidden}
  [data-reveal="in"] > .accent-line,
  [data-reveal="in"] .accent-stub{animation:none}
  [data-reveal]{opacity:1;transform:none;transition:opacity 200ms ease}
  .row:hover,.card:hover,.related a:hover,.btn:hover,.tier:hover .tier-card{transform:none}
  .arch-sm:hover .arch-img{transform:none}
  *{transition-duration:200ms!important;animation-duration:.01ms!important}
}