/* ============================================================
   HARMONIC WELLNESS CENTRE — v4 "DARK TO LIGHT"
   For women. Soulful, spiritual, cinematic — not technical.
   The page runs dark through the problem, reaches its deepest black
   at the silence, and comes out into light. That IS the transformation.
   ============================================================ */

:root{
  /* the dark half — measured from her own chamber photographs */
  --pitch:#050409; --void:#0A0812; --deep:#15111F;
  --bone:#F4F0F7; --bone-soft:rgba(244,240,247,.72); --bone-mute:rgba(244,240,247,.56);
  --rim-d:rgba(244,240,247,.13); --rim-d-soft:rgba(244,240,247,.07);

  /* the light half — lilac-white, never cream */
  --light:#F7F4FA; --light-warm:#FCFAFD; --light-deep:#EEE7F3;
  --ink:#2B2334; --ink-soft:#5C5169; --ink-mute:#6E6280;
  --rim-l:rgba(43,35,52,.13); --rim-l-soft:rgba(43,35,52,.07);

  /* shared accents — the colours the chamber cycles */
  --violet:#7A46A0; --orchid:#C793C2; --lilac:#B79BD0;
  /* Kathleen's own mark, sampled off the logo on harmonicwellnesscentre.ca. Her wordmark
     there is a dark green, which is a wider brand question than this file settles. */
  --brand-pink:#E93293; --mark:var(--brand-pink);
  --plum:#9C4279; --plum-soft:#C46FA0; --gold:#D8B273;
  /* the accent inside a scene's eyebrow pill sits on a translucent plate over photography,
     not on the page ground, so it needs to run deeper than brand plum to hold AA there.
     Re-measured against Kay's #9C4279: brand plum reads 3.73 on work-with-kathleen and 4.05
     on egg, both under 4.5, so this variant stays. It doubles as the .btn-key hover. */
  --plum-deep:#902E69;
  --hue:var(--orchid);

  --sans:"Plus Jakarta Sans",-apple-system,"Segoe UI",sans-serif;
  --serif:"Fraunces","Iowan Old Style",Georgia,serif;

  /* concentric squircle radii — outer shell, then the core inside it */
  --r-xl:2.25rem; --r-lg:1.75rem; --r-md:1.15rem; --r-sm:.8rem;
  --shell:7px;

  /* diffused ambient depth. never a hard drop shadow — these are lifts, not edges */
  --lift-1:0 1px 2px rgba(43,35,52,.035), 0 8px 20px -12px rgba(43,35,52,.14);
  --lift-2:0 2px 6px rgba(43,35,52,.04), 0 26px 60px -32px rgba(43,35,52,.30);
  --lift-3:0 4px 10px rgba(43,35,52,.05), 0 40px 90px -44px rgba(43,35,52,.38);
  --gloss:inset 0 1px 1px rgba(255,255,255,.9);
  --hair:0 0 0 1px rgba(43,35,52,.055);

  /* what the floating nav pill occupies: its own top offset plus its min-height. The first
     plate clears this instead of starting under it — the nav was cutting 38px into the hero. */
  --nav-h:calc(clamp(12px,1.5vw,20px) + 62px);
  /* Capped so the hero headline sets on two lines, not three. Measured in the 724px column:
     76px still broke to three, 72px was the threshold but left only 12px of slack on the
     longest line, so 4.25rem/68px takes the two-line break with 44px to spare. Three lines
     cost ~110px of column height, which is what was forcing the plate taller. */
  /* The hero headline has to break on two lines at every desktop width, so the type has to
     grow at the same rate its column does. The column is 52% of the plate, and the plate is
     the viewport until it caps at --maxw — so 4.3vw tracks it below the cap, and 4.25rem
     holds the line once the plate stops growing. 6vw outran the column and broke to three
     lines below 1600px; three lines is what was making the hero tall. */
  --fs-display:clamp(2.15rem,4.15vw,4.25rem);
  /* Brought down with the display scale. At 4rem the section headings were nearly the size
     of the hero's, which left no hierarchy between them — and the guide plate's column became
     the tallest on the page, so its heading was what set --plate-h's floor once the hero
     stopped being the binding one. */
  --fs-h2:clamp(1.7rem,3.4vw,3.2rem);
  --fs-h3:clamp(1.3rem,2vw,1.85rem);
  --door-h:clamp(230px,25vw,400px);
  --fs-deck:clamp(1.02rem,1.5vw,1.26rem);
  --fs-lead:clamp(1rem,1.3vw,1.34rem);
  --fs-body:1.12rem;
  --fs-small:1rem;
  /* Kay: increase by 1 point, it is too small. .74rem was 11.84px; a point is ~1.33px. */
  --fs-label:.82rem;

  --s2:12px; --s3:16px; --s4:24px; --s5:36px; --s6:52px;
  --s7:78px; --s8:118px; --s9:170px;
  /* Every photographic plate is this tall, so the page keeps one rhythm instead of each
     scene being sized by whatever copy it happens to carry. The floor is not cosmetic: the
     hero is no longer the tallest column — once its headline set on two lines it dropped to
     665, and the guide plate's 773 (heading, lead, four credentials, story link) became the
     binding one. after widening the rail column that guide block sets on 708 lines, and 708 + 2*--s6 = 812,
     so the floor is 830 — the spare 18px is slack against font-loading variance, not
     decoration; an earlier floor sat 3px off the binding column and would have broken the
     set the moment a fallback face measured differently. The hero now carries ~110px of
     slack inside the shared height; that is the price of every plate matching. */
  --plate-h:clamp(700px,58vh,780px);
  /* One boundary size for the whole page. A plate contributes its own --s6 of padding plus a
     --frame margin, and the prose beside it contributes --s8 — so that pairing IS the number,
     and every other pairing is derived from it rather than guessed at. */
  --gap-sec:calc(var(--s6) + var(--frame) + var(--s8));
  --act:clamp(118px,14vw,250px);
  --gutter:clamp(22px,4.6vw,84px);
  --maxw:1520px;

  /* plates: full-bleed blocks become inset rounded surfaces floating on the ground.
     Radius is fluid so the corner keeps its proportion from phone to ultrawide. */
  --frame:clamp(12px,2.4vw,44px);
  --plate-r:clamp(18px,2.6vw,40px);

  --ease:cubic-bezier(.22,.61,.19,1);
  --spring:cubic-bezier(.32,.72,0,1);
}

/* ---- theming: sections declare which half of the story they are in ---- */
/* the site is light throughout — .dark is retained only so older markup resolves correctly */
.dark,.light{ --bg:var(--light); --fg:var(--ink); --fg-soft:var(--ink-soft); --fg-mute:var(--ink-mute);
        --rim:var(--rim-l); --rim-soft:var(--rim-l-soft); --key:var(--plum); }
.pitch{ --bg:var(--light-warm) }

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; scroll-behavior:smooth; background:var(--light)}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0; background:var(--light); color:var(--ink);
  font-family:var(--sans); font-size:var(--fs-body); font-weight:300; line-height:1.72;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
body::after{
  content:""; position:fixed; inset:0; z-index:70; pointer-events:none; opacity:.045;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url(%23n)'/%3E%3C/svg%3E");
}
img{max-width:100%; display:block}
a{color:inherit}

/* ---- type: one family, weight contrast does the work ---- */
h1,h2,h3,.disp{
  font-family:var(--serif); font-weight:300; margin:0;
  /* Fraunces carries an optical-size axis. Leaving it on auto lets opsz track the rendered
     size — the display cut (thin hairlines, tight joins) at 90px, the sturdier text cut at
     23px. Pinning "opsz" 144 in font-variation-settings overrides that and makes small
     serif type render with hairlines too fine to hold. */
  font-optical-sizing:auto;
  /* never break-word on display type — it split "underneath" into "underne / ath" at 74px.
     Containers are sized to the longest word instead (see the widest-word check). */
  line-height:1.04; letter-spacing:-.016em; text-wrap:balance; overflow-wrap:normal;
}
h1{font-size:var(--fs-display)}
h2{font-size:var(--fs-h2); max-width:16em}
h3{font-size:var(--fs-h3); font-weight:400; line-height:1.22; letter-spacing:-.006em}
/* h3 also does duty as a tracked micro-label in the footer — those stay sans */
.foot h3{font-family:var(--sans)}
p{margin:0 0 1.05em} p:last-child{margin-bottom:0}
/* display-weight type that isn't an h1-h3 — the silence, the price, the statement */
.silence .line,.statement p,.ticket .fig,.foot .fb,.sheet a,.said,
.voice blockquote{font-family:var(--serif); font-optical-sizing:auto}
/* .sheet a also matches the CTA pill inside the menu, and out-specifies .btn's own family */
.sheet a.btn{font-family:var(--sans)}
strong,b{font-weight:400}

section{padding-block:var(--act); position:relative; background:var(--bg); color:var(--fg)}
.wrap{max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter)}
/* plain sections sit in a measured column, not pinned to the left of a 1520px page */
.hold{max-width:76rem; margin-inline:auto}
.hold .prose,.hold .said,.hold .lead{margin-left:0}
.said{margin-left:0}
.prose{max-width:38rem; color:var(--fg-soft); font-weight:300; font-size:1.16rem; line-height:1.75}
.lead{font-size:var(--fs-lead); line-height:1.55; color:var(--fg-soft); max-width:33rem; font-weight:200}
/* Kay's hero arrived as a headline and a tagline on two separate lines, so they are set as
   two. The three steps run display -> deck -> lead and are carried by family and weight, not
   size alone: Fraunces at display, the sans at 500 for the positioning line, the sans at 200
   for the body. Margin is top-only — p's 1.05em bottom would otherwise stack with the lead's
   own margin-top wherever the copy column is a grid item and sibling margins cannot collapse. */
.deck{font-size:var(--fs-deck); line-height:1.35; font-weight:500; color:var(--ink);
  max-width:34rem; margin:var(--s4) 0 0; text-wrap:balance}

.label{
  /* block, not flex: as flex items the eyebrow's separator stranded on its own line at 375px */
  display:block; margin:0 0 var(--s5);
  font-size:var(--fs-label); font-weight:500; letter-spacing:.26em;
  text-transform:uppercase; color:var(--fg-mute); text-wrap:balance;
}
.label .k{color:var(--key)}
/* Stars sit a shade larger than the caps beside them so the rating reads as a rating, and
   drop the label's .26em tracking — inherited, it spaces them like letters rather than one
   group. Decorative: the words 'Five stars' already carry the meaning, hence aria-hidden. */
.label .stars{display:inline-flex; align-items:center; gap:.16em; margin-left:.6em;
  letter-spacing:normal; vertical-align:-.08em}
.label .stars svg{display:block; width:1.2em; height:1.2em; fill:currentColor}

/* ---- buttons: soft, rounded, unhurried ---- */
/* the trailing arrow never sits naked beside the label — it gets its own circular
   well, set flush with the button's right inner padding, and carries the hover physics */
.btn{display:inline-flex; align-items:center; gap:14px; text-decoration:none;
  font-family:var(--sans); font-size:.95rem; font-weight:400; line-height:1; letter-spacing:.01em;
  padding:9px 9px 9px 30px; border-radius:100px; border:1px solid transparent; cursor:pointer;
  transition:background .7s var(--ease), color .7s var(--ease), border-color .7s var(--ease),
             box-shadow .7s var(--ease), transform .45s var(--spring)}
.btn .ar{width:34px; height:34px; flex:0 0 34px; padding:10.5px; border-radius:100px; box-sizing:border-box;
  opacity:1; transition:transform .7s var(--spring), background .7s var(--ease)}
.btn:hover .ar{transform:translate(3px,-1px) scale(1.06)}
.btn:active{transform:scale(.98)}
/* a button with no arrow keeps a symmetrical pill */
.btn:not(:has(.ar)){padding:18px 32px}

.btn-key{background:var(--plum); color:#fff; box-shadow:var(--lift-1)}
.btn-key .ar{background:rgba(255,255,255,.17)}
/* Deepens on hover, it does not lighten. --plum-soft put white text at 3.43:1, under the 4.5
   this size needs, and a filled button going paler on hover reads as disabled rather than
   active. --plum-deep is 7.55:1 and already in the palette for the eyebrow accents. */
.btn-key:hover{background:var(--plum-deep); box-shadow:var(--lift-2)}
.btn-key:hover .ar{background:rgba(255,255,255,.26)}

.light .btn-line{border-color:rgba(43,35,52,.16); color:var(--ink); background:rgba(255,255,255,.42)}
.light .btn-line .ar{background:rgba(43,35,52,.06)}
.light .btn-line:hover{border-color:rgba(43,35,52,.3); background:rgba(255,255,255,.8); box-shadow:var(--lift-1)}
.light .btn-line:hover .ar{background:rgba(43,35,52,.1)}
.on-img .btn-line{border-color:rgba(255,255,255,.45); color:#fff; background:rgba(255,255,255,.1)}
.on-img .btn-line .ar{background:rgba(255,255,255,.18)}
.on-img .btn-line:hover{background:rgba(255,255,255,.2)}
a:focus-visible,button:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--plum-soft); outline-offset:4px}

.tlink{display:inline-flex; align-items:center; gap:10px; text-decoration:none; color:inherit;
  font-size:var(--fs-small); font-weight:300; letter-spacing:.05em; padding-block:5px;
  border-bottom:1px solid var(--rim); transition:border-color .7s var(--ease), color .7s var(--ease)}
.tlink:hover{border-color:currentColor}
.tlink .ar{width:12px;height:12px;transition:transform .7s var(--ease)}
.tlink:hover .ar{transform:translateX(4px)}

.skip{position:absolute; left:-9999px; top:0; z-index:200; background:var(--ink); color:var(--light); padding:14px 20px}
.skip:focus{left:0}

/* ---- nav ---- */
/* at scroll 0 the bar is transparent, which is invisible over a dark hero — a soft scrim
   keeps the links readable on every page without reading as a filled bar */
/* a floating glass island, detached from the top edge — never a bar glued to the viewport.
   The pill carries its own backdrop, so no page-wide scrim is needed to keep links legible
   over the hero plate. backdrop-filter is safe here: the element is fixed, not scrolling. */
.nav{position:fixed; inset:0 0 auto 0; z-index:60; pointer-events:none;
  padding:clamp(12px,1.5vw,20px) var(--gutter) 0}




.nav-in{pointer-events:auto; max-width:var(--maxw); margin-inline:auto; padding:8px 8px 8px 26px;
  display:flex; align-items:center; justify-content:space-between; gap:var(--s5); min-height:62px;
  border-radius:100px; background:rgba(252,250,253,.62);
  backdrop-filter:blur(26px) saturate(1.7); -webkit-backdrop-filter:blur(26px) saturate(1.7);
  box-shadow:var(--gloss), var(--hair), 0 20px 50px -30px rgba(43,35,52,.34);
  transition:background .7s var(--ease), box-shadow .7s var(--ease)}
.nav.stuck .nav-in{background:rgba(252,250,253,.84);
  box-shadow:var(--gloss), 0 0 0 1px rgba(43,35,52,.07), 0 26px 60px -32px rgba(43,35,52,.42)}
.brand{display:flex; align-items:center; gap:12px; text-decoration:none}
/* Kay: the logo is too small. 32px is as large as the mark goes before it crowds the
   wordmark inside the nav pill and starts driving the pill's own height. */
.brand svg{width:32px;height:32px;flex:0 0 32px}
.brand b{font-size:.68rem; font-weight:500; letter-spacing:.2em; text-transform:uppercase; color:var(--ink-soft); transition:color .7s var(--ease)}
.nav-links{display:flex; gap:var(--s6); list-style:none; margin:0; padding:0}
.nav-links a{text-decoration:none; font-size:.84rem; font-weight:400; letter-spacing:.005em; color:var(--ink-soft);
  padding-block:9px; display:inline-block; min-height:24px; transition:color .6s var(--ease)}
.nav-links a:hover,.nav-links a[aria-current]{color:var(--ink)}
.nav .btn{padding:5px 5px 5px 22px; font-size:.84rem; gap:11px}
.nav .btn .ar{width:30px;height:30px;flex:0 0 30px;padding:9.5px}
.nav-links{gap:clamp(24px,2.6vw,var(--s6))}
.burger{display:none; width:44px;height:44px; background:rgba(43,35,52,.045); border:0; border-radius:100px;
  cursor:pointer; position:relative; box-shadow:var(--hair); transition:background .6s var(--ease)}
.burger:hover{background:rgba(43,35,52,.08)}
.burger i{position:absolute; left:13px; width:18px; height:1.2px; border-radius:2px; background:var(--ink);
  transition:transform .6s var(--spring)}
.burger i:nth-child(1){top:18px} .burger i:nth-child(2){top:25px}
.nav[data-open="true"] .burger i:nth-child(1){transform:translateY(3.5px) rotate(45deg)}
.nav[data-open="true"] .burger i:nth-child(2){transform:translateY(-3.5px) rotate(-45deg)}
.sheet{position:fixed; inset:0; z-index:59; background:rgba(247,244,250,.9);
  backdrop-filter:blur(40px) saturate(1.6); -webkit-backdrop-filter:blur(40px) saturate(1.6);
  display:flex; flex-direction:column; justify-content:center; padding:0 var(--gutter);
  opacity:0; pointer-events:none; transition:opacity .8s var(--ease)}
.nav[data-open="true"] ~ .sheet{opacity:1; pointer-events:auto}

.sheet ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--s4)}
.sheet a{font-weight:300; font-size:clamp(2rem,9vw,3.2rem); letter-spacing:-.02em; color:var(--ink);
  text-decoration:none; opacity:0; transform:translateY(34px); transition:opacity .8s var(--ease), transform .9s var(--spring)}
.nav[data-open="true"] ~ .sheet a{opacity:1; transform:none}
.nav[data-open="true"] ~ .sheet li:nth-child(1) a{transition-delay:.06s}
.nav[data-open="true"] ~ .sheet li:nth-child(2) a{transition-delay:.12s}
.nav[data-open="true"] ~ .sheet li:nth-child(3) a{transition-delay:.18s}
.nav[data-open="true"] ~ .sheet li:nth-child(4) a{transition-delay:.24s}
.sheet .btn{margin-top:var(--s7); align-self:flex-start}
/* The desktop links need ~1080px to sit on one line. Below that they wrapped and the pill
   grew from 62px to 80px — a cramped two-line nav, and it broke the first plate's clearance
   because --nav-h is derived from the 62px min-height. The burger takes over before that. */
@media (max-width:1100px){ .nav-desk{display:none} .burger{display:block} }
@media (min-width:1101px){ .sheet{display:none} }

/* ---- full-screen scene: image with copy laid over it ---- */
.scene{min-height:var(--plate-h); display:flex; overflow:hidden; padding-block:var(--s6)}
.scene.mid{align-items:center} .scene.low{align-items:flex-end}
.scene-img{position:absolute; inset:0; z-index:0}
.scene-img img{width:100%; height:100%; object-fit:cover}
/* per-scene focal points — a 2:3 portrait in a 21:9 window crops the head off by default */
.focus-up .scene-img img{object-position:50% 18%}
.focus-left .scene-img img{object-position:22% 50%}
.focus-right .scene-img img{object-position:78% 50%}
/* A 2:3 portrait on a landscape plate is cropped hard top and bottom, which leaves the
   subject sitting low and dead centre. Lifting the crop window down the source raises her
   on screen; moving it left pushes her right, clear of the copy. The zoom comes from
   oversizing the image box rather than a transform — .scene already clips it, and a scaled
   box keeps the compositor out of it. Desktop only: below 901 the band is its own element
   with a set height, and negative insets would break it. */
@media (min-width:901px){
  /* The box is widened to the right only, with its left edge still on the plate's, so the
     plate stays covered and the extra width hangs off the right. That does two things at
     once: the image is sized to the wider box, so it crops in, and the box's centre moves
     right of the plate's, so she moves right with it. object-position's X is no use here —
     a 2:3 source in a landscape box overflows vertically only, so there is no horizontal
     slack to slide. Y still works, and 70% lifts her by showing a lower band of the source. */
  .focus-portrait .scene-img{inset:0 -12% 0 0}
  .focus-portrait .scene-img img{object-position:50% 58%}
}
/* Thinned from a .72 peak that cleared only at 96% — that washed the whole frame, subject
   included. Two changes: a lower peak, and the clear point pulled to 76% so the right quarter
   of the photograph is untouched. The left cannot be cleared much: on about.html the headline
   runs to x=0.49 and the lead to 0.39, and that hero is the binding case — its lead sits over
   dark foliage, so it is what caps the reduction, not the brighter index hero (8:1 headroom).
   Sampled per-channel over the real glyph line-boxes: headline 4.31, lead 5.47, label 5.32
   at the 5th percentile, against WCAG AA of 3 for the display type and 4.5 for the rest.
   Raised again when the hero was reframed: cropping in and lifting the subject brought
   darker foliage behind the copy and the headline fell to 3.70, which clears AA for
   display type but leaves nothing spare. Only about.html uses this veil. */
.veil-dark::after{content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(72deg, rgba(247,244,250,.76) 0%, rgba(247,244,250,.67) 28%, rgba(247,244,250,.40) 50%, rgba(247,244,250,.10) 64%, transparent 76%)}
/* The home hero moved from a pale wall to the inside of the Egg, so this veil carries the same
   load .veil-chamber does on egg.html: violet at roughly .10-.30 luminance behind dark ink.
   The .44 body that suited coping.webp leaves the headline at about 2:1 there. These are
   .veil-chamber's stops — the one class the site already has tuned against this exact chamber.
   It stays its own rule rather than folding into .veil-chamber because the hero's layout hangs
   off .veil-bright: the 52% copy cap, the CTA margin, and the ::before seam fix all key on it,
   and .veil-chamber is shared with egg.html, which wants none of them. Only this hero uses it.
   The tail is pulled in from 94% to 76% and the mid stops steepened: .veil-chamber's long ramp
   was written for a plate whose subject sits centre-right, and here it kept washing the Egg
   itself. The copy ends at 40% of the plate, so the strong half is untouched — at that edge
   the veil still reads .66 against a measured headroom of 9.99:1. */
.veil-bright::after{content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(72deg, rgba(247,244,250,.9) 0%,
    rgba(247,244,250,.84) 28%, rgba(247,244,250,.58) 46%, rgba(247,244,250,.18) 62%, transparent 76%)}
/* the violet chamber sits several stops darker than every other plate */
.veil-strong::after{content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(72deg, rgba(247,244,250,.74) 0%, rgba(247,244,250,.62) 28%, rgba(247,244,250,.34) 52%, rgba(247,244,250,.1) 78%, transparent 94%)}
.veil-light-r::after{content:""; position:absolute; inset:0; z-index:1;
  /* lifts ONLY the right third where the type sits — the subject on the left stays rich */
  background:linear-gradient(to left, rgba(247,244,250,.74) 0%, rgba(247,244,250,.64) 28%, rgba(247,244,250,.4) 48%, rgba(247,244,250,.14) 70%, transparent 88%)}
.veil-light-r .wrap{display:flex; justify-content:flex-end}
.veil-light-r .wrap > div{max-width:44rem}
@media (max-width:900px){
  .veil-light-r .wrap{justify-content:flex-start}
}

/* The rail plate is the one landscape frame of Kathleen: she stands centre-left against the
   tree with the sunlit path filling the right. veil-light-r reaches .40 by 48% of the frame,
   which lands squarely on her face at 43% and ghosted her out. This lifts only the path —
   fully clear left of 58%, hard by the right edge — and the path is already near-white, so
   the wash is doing contrast insurance rather than real lifting. */
.veil-rail::after{content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(to left, rgba(247,244,250,.94) 0%, rgba(247,244,250,.88) 14%,
    rgba(247,244,250,.64) 25%, rgba(247,244,250,.24) 34%, transparent 42%)}
.veil-rail .wrap{display:flex; justify-content:flex-end}
/* The copy has to live inside the right 40% so it never crosses her — she ends at 61%. Now
   The band right of her is not a fixed width: cover crops harder as the plate narrows, which
   pushes her edge from 61.8% of the plate at 1520 out to 67% at 975 — so the band shrinks from
   522px to 287px. A flat 28rem overflowed her below ~1200 and a flat 32rem overflowed her
   further; the cap has to be relative. A flat percentage does not fit either — it is too mean
   at 1732 (435px used of 530 available, which wrapped the column to 749 and broke the shared
   plate height) and exactly right at 1024. The band measures 287px at 1024, 452 at 1400 and
   530 at 1732, which is linear in the viewport at 0.343px per px — hence 34.3vw, less 80px:
   the fit alone lands exactly on her edge at 1024, so the extra 16px is the clearance, with
   32rem taking over once the plate stops growing. This column is the tallest on the page, so
   its wrapping is what sets --plate-h for every scene. */
.veil-rail .wrap > div{max-width:min(32rem, calc(34.3vw - 80px))}
/* The copy was pinned to the plate's right edge at 96%, leaving a wide dead band between
   Kathleen and the first character. Insetting the right side walks the column back toward
   the middle; she occupies the left ~50% of the frame, so there is room to move without
   the copy running onto her. */
.scene.veil-rail .wrap{padding-right:clamp(64px,12vw,230px)}
/* The credentials make this the densest copy column on the page. It shares --plate-h with
   every other scene, so the list runs a tighter rhythm than .creds does on open ground —
   that is what keeps the column inside the shared height instead of pushing past it. */
/* .scene.veil-rail, not .veil-rail: `.scene .creds` further down the sheet is also (0,2,0)
   and wins on order, so the plain-class version of this never applied and the list kept the
   52px it was meant to give up. */
.scene.veil-rail .creds{margin-top:var(--s4)}
.scene.veil-rail .creds li{padding:8px 0}
.scene.veil-rail .creds + p{margin-top:var(--s3)}
.scene.veil-rail h1,.scene.veil-rail h2{font-size:clamp(1.9rem,3vw,3.2rem); max-width:none}
.scene.veil-rail .lead{max-width:none}
@media (max-width:900px){
  .veil-rail .wrap{justify-content:flex-start}
  .veil-rail .wrap > div{max-width:none}
}
/* A 2:3 portrait full-bleed has no horizontal crop slack, so the subject sits centre frame
   and the headline had been running across her shoulder and hand at 2.96:1. Bands 0-20% are
   the quietest part of this plate (measured variance 0.006) and near-black: lifting them hard
   costs no detail, and the copy column is pulled in so it never reaches her. */
.veil-portrait::after{content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(96deg, rgba(247,244,250,.95) 0%, rgba(247,244,250,.92) 22%,
    rgba(247,244,250,.7) 38%, rgba(247,244,250,.24) 54%, transparent 68%)}
/* .scene h1 is 0,2,1 — these must out-specify it or the copy runs back across the subject */
/* This plate's quiet band is only the left third, so the display type is sized to that column
   rather than to the global scale: at the 4.05rem ceiling "underneath" needs 380px and
   "the session." 368px, both inside 27rem at every viewport. */
.scene.veil-portrait h1,.scene.veil-portrait h2{font-size:clamp(2.2rem,3.7vw,4.05rem); max-width:27rem}
.scene.veil-portrait .lead{max-width:22rem}
@media (max-width:900px){
  .scene.veil-portrait h1,.scene.veil-portrait h2,.scene.veil-portrait .lead{max-width:none}
}
/* The violet chamber is the darkest plate on the site — measured luminance 0.05-0.30 right
   across the frame, with the Egg's own glow at 0.30 sitting under the headline. veil-dark is
   shared with two light plates that already pass, so this gets its own weight rather than
   re-bleaching those. */
.veil-chamber::after{content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(72deg, rgba(247,244,250,.9) 0%, rgba(247,244,250,.83) 30%,
    rgba(247,244,250,.62) 54%, rgba(247,244,250,.28) 76%, transparent 94%)}
.veil-light::after{content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(72deg, rgba(247,244,250,.7) 0%, rgba(247,244,250,.58) 32%, rgba(247,244,250,.32) 60%, rgba(247,244,250,.09) 86%, transparent 100%)}
.scene .wrap{width:100%; position:relative; z-index:3}
/* The frame-wide sweep tilts upward at 72deg, so the eyebrow — the topmost, smallest type in
   the block — sat where coverage was thinnest and measured 1.38-3.32:1. The lift is anchored
   to the type block instead of the frame, so it follows the copy at every viewport and in both
   .mid and .low alignments. That in turn lets the frame sweeps come back and keep the plate. */
.scene .wrap > div{position:relative; isolation:isolate}
/* A gradient is clipped to its own box, so an ellipse wider than that box terminates
   mid-ramp and leaves a hard seam around the whole wash — it measured a 15-level RGB step.
   The box is grown well past the copy and the radii kept small enough that the ramp
   reaches transparent before every edge: left edge 0.32/0.30, top edge 0.50/0.46. */
.scene .wrap > div::before{content:""; position:absolute; z-index:-1; pointer-events:none;
  inset:-70% -80% -70% -80%;
  background:radial-gradient(30% 46% at 32% 50%, rgba(247,244,250,.82) 0%,
    rgba(247,244,250,.72) 38%, rgba(247,244,250,.44) 66%, rgba(247,244,250,.14) 84%, transparent 96%)}
/* Not on the bright hero. This lift is sized in percentages of the copy column, so capping
   that column shrank the ellipse with it — the wash now centres near the copy's own left
   edge and the plate's overflow:hidden cuts it mid-ramp, which is the vertical line down the
   left of the plate. It is also redundant there: the veil alone already clears AA over the
   chamber, and those figures were taken with the veil alone — this lift was never counted in
   them, so dropping it costs nothing that was being relied on. */
.scene.veil-bright .wrap > div::before{display:none}
/* The copy column on a veil-dark plate is bound to where the subject actually starts, the
   same way .veil-portrait is. The chair occupies roughly 47%-100% of egg-rest.webp, and because the
   plate is full-bleed while .wrap is capped at --maxw, that boundary lands at a different
   place at every width — hence two limits rather than one. 40rem binds on wide screens
   (46.9% of the plate at 1920); 46vw binds on narrow desktops, where cover crops the image
   hard and pushes her in to 58.8%. Without this the headline ran to 60.4% at 1920, over a
   0.13-alpha stretch of veil — which is to say, straight across her face. Capping the whole
   column and not just the h1 is deliberate: it pulls the .facts row in off her arm too. */
/* The nav floats over the page, so the first plate has to start below it rather than behind
   it — at 1920 the pill's lower edge sat 38px inside the hero's top corner. */
@media (min-width:901px){
  main > section:first-child{margin-top:calc(var(--nav-h) + var(--s5))}
}
@media (min-width:901px){
  /* Bound to where the subject starts, as a share of the plate rather than the viewport —
     now that the plate itself caps at --maxw, a vw-based limit no longer tracks it and left
     the copy at 34% of the plate on a wide monitor. 52% of the wrap puts the copy's edge at
     ~51.9% of the plate at every width, against a subject that begins at 55% or later. */
  .scene.veil-dark .wrap > div,.scene.veil-bright .wrap > div{max-width:min(46rem, 52%)}
  /* The flex row was built for the full wrap width; inside the narrower column its --s8
     gutters push the third item onto a line of its own. A three-track grid keeps the row
     intact and lets the long awarding-body label wrap within its own column instead. */
  .scene.veil-dark .facts,.scene.veil-bright .facts{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--s4)}
  /* The hero carries more stacked content than any other plate — label, display headline,
     four-line lead, two CTAs, three facts. Its internal rhythm is what sets --plate-h's
     floor, so it runs one step tighter than --s7 here; that alone took the plate from 1040
     to the measured minimum without thinning any of the type. */
  .scene.veil-bright .hero-cta{margin-top:var(--s6)}
  .scene.veil-bright .facts{margin-top:var(--s6)}
}
.scene h1,.scene h2{max-width:12.5em}
/* Over photography the eyebrow is the topmost and smallest type, and it sat where the veil
   was thinnest (measured 1.38-3.32:1). Giving it its own glass pill guarantees the ratio at
   every viewport regardless of what is behind it, and it no longer depends on the wash. */
/* inline-block, never inline-flex: as flex items the eyebrow's separator strands on its own
   line at 375px (the same trap the base .label rule documents). The trailing letter-space
   is subtracted from the right padding so the pill reads optically centred. */
@media (min-width:901px){
  .scene .label{display:inline-block; color:var(--ink);
    padding:7px 11px 7px 15px; border-radius:100px; letter-spacing:.22em; font-weight:500;
    /* No backdrop-filter. These pills scroll, and blur on scrolling content is a repaint
       cost paid on every frame — the one place the page broke its own rule. The ink was
       never leaning on it (7.5:1 at worst), but the plum accent was: blur averages the
       backdrop, so losing it drops the eyebrow's accent from 4.66:1 to 4.23:1 against the
       darkest pixel under it. The plate goes to .80 to buy that back — 4.5 needs .78, and
       the extra .02 is margin, not taste. */
    background:rgba(252,250,253,.80);
    box-shadow:var(--gloss), var(--hair)}
}
/* Below 901px the copy is on the page ground, not over a plate, so it needs no pill — and one
   would wrap to a two-line stadium at 375px anyway. Plain tracked type there. */
/* This used to be --plum-deep: brand plum measured 3.7:1 here, under the 4.5 small tracked
   caps need. Two things changed since — the eyebrow gained its near-opaque pill, and the
   home hero's veil was rebuilt for the chamber. Re-measured over the real glyph boxes with
   the pill composited, Kay's #9C4279 now reads 5.42 on index, 5.37 on egg, 5.19 on
   work-with-kathleen and 4.82 on about. About is the tight one: swap that hero photograph
   for anything darker behind the eyebrow and this needs re-measuring, not assuming. */
.scene .label .k{color:var(--plum)}
.scene .lead{margin-top:var(--s5)}
/* over photography the soft tone measured 4.2-4.3:1 — every scene lead takes full ink */
.scene .lead{color:var(--ink)}
.scene.veil-strong .lead{max-width:27rem}
@media (max-width:900px){
  /* The wordmark wraps to two lines beside the burger at 375px — and inside the island pill
     that wrap makes the whole bar double-height. The pill's left inset is pulled in and the
     wordmark tightened so "HARMONIC WELLNESS CENTRE" holds one line at 375px. */
  .nav-in{padding:7px 7px 7px 17px; gap:var(--s3)}
  .brand b{font-size:.58rem; letter-spacing:.1em; white-space:nowrap}
  .brand{gap:9px}
  /* the mark drives the link's height here — keep the target at the 24px minimum */
  .brand{min-height:24px}
  .brand svg{width:28px;height:28px;flex:0 0 28px}
  /* tighter eyebrow tracking so it holds one or two clean lines rather than three */
  .scene .label{letter-spacing:.2em}
}

/* ── mobile scenes: the plate stops being a backdrop and becomes the picture ──────────
   Six identical bottom-up washes used to cover .93/.86 across the whole frame here, with
   the radial lift stacked on top of that — both plates read as flat lilac. Lightening them
   is not available: at 375px a scene's copy runs 700-950px on its own, taller than the
   frame, so there is no arrangement that keeps type over the plate AND leaves plate to see.
   The scene splits instead — a real photographic band, then the copy on the page ground,
   where it carries full contrast for free and needs no wash at all. */
@media (max-width:900px){
  .scene{display:block; min-height:0; padding-block:0 var(--s8)}
  /* Below 901px the <picture> serves the -tall plates, which are cut 4:5 (0.80). Sizing the
     band to 58vh/460px puts it at 375:460 = 0.815, so cover crops ~9px and the subject's head
     stays in frame — at 420px the box went to 0.89 and clipped it. */
  .scene-img{position:relative; inset:auto; height:min(58vh,460px); height:min(58dvh,460px)}
  /* the band dissolves into the page rather than butting against it on a hard edge */
  .scene-img::after{content:""; position:absolute; inset:auto 0 0 0; height:26%; z-index:2;
    background:linear-gradient(to top, var(--light) 0%, rgba(247,244,250,.68) 42%, transparent 100%)}
  .veil-dark::after,.veil-bright::after,.veil-strong::after,.veil-light::after,
  .veil-light-r::after,.veil-portrait::after,.veil-chamber::after{display:none}
  .scene .wrap > div::before{display:none}
  /* the fade already separates band from copy — s7 on top of it read as a dead gap */
  .scene .wrap{padding-top:var(--s6)}
  /* off photography, the lead goes back to the soft tone every plain section uses */
  .scene .lead{color:var(--fg-soft)}
}

/* A supporting row, not a headline — it was set at 1.3rem/.74rem, big enough that both the
   middle value and its awarding body wrapped to two lines each and the block ran 100px tall
   under the CTAs. Smaller across the board, and the tracking eases off so the uppercase
   labels stay on one line. */
.facts{display:flex; flex-wrap:wrap; gap:var(--s4) var(--s8); margin-top:var(--s7); padding-top:var(--s5); border-top:1px solid var(--rim)}
.facts div{display:grid; gap:4px}
.facts .v{font-size:1.02rem; font-weight:400; line-height:1.25; letter-spacing:-.005em}
.facts .l{font-size:.66rem; font-weight:300; letter-spacing:.17em; text-transform:uppercase; color:var(--fg-mute)}

/* ---- two doors: full-height halves ---- */
/* Two side-by-side cards cropped both photographs to a tall plate, which is the cut-off Kay
   objected to. Each door is now a row: the whole image on the left, the copy beside it on the
   right. The image column's width is derived from --door-h, so the row height governs and both
   images come out the same size — which works because both are 3:2. Swap either photograph for
   another ratio and the two rows stop lining up; object-fit:contain keeps whatever lands here
   whole rather than cropping it, so the failure is a letterbox band, not a cut-off subject. */
/* Every section is position:relative with an opaque background, so a later one paints over
   whatever the previous one draws outside its box — which is what was cutting the doors'
   hover shadow along a hard horizontal line. Nothing is clipping it; it is paint order.
   z-index:1 lifts this section above the ones that follow. The nav sits at 60, so this
   stays well underneath it. */
.doors{display:grid; grid-template-columns:1fr; gap:var(--s6); padding:0; z-index:1}
.door{position:relative; display:grid; grid-template-columns:auto minmax(0,1fr);
  align-items:stretch; min-height:var(--door-h); text-decoration:none; overflow:hidden;
  color:var(--ink);
  background:linear-gradient(to top, rgba(252,250,253,.995), rgba(252,250,253,.965));
  box-shadow:var(--gloss), 0 0 0 1px rgba(43,35,52,.05);
  transition:transform .9s var(--spring), box-shadow .9s var(--ease)}
.door:hover{transform:translateY(-6px);
  box-shadow:var(--gloss), 0 0 0 1px rgba(43,35,52,.09), 0 44px 90px -46px rgba(43,35,52,.5)}
.door-img{position:relative; align-self:stretch; width:calc(var(--door-h) * 1.5); overflow:hidden}
/* width:auto/height:100% would size the box from the file, and the width/height attributes are
   presentational hints that win over an unset dimension — so both axes are set explicitly. */
.door-img img{display:block; width:100%; height:100%; object-fit:contain; object-position:center;
  transition:transform 2.4s var(--ease)}
.door:hover .door-img img{transform:scale(1.03)}
.door-panel{display:flex; flex-direction:column; justify-content:center;
  padding:clamp(24px,2.8vw,52px)}
.door h2{font-size:clamp(1.7rem,2.9vw,2.7rem); font-weight:300; margin:var(--s3) 0 var(--s4); max-width:none}
.door p{color:var(--ink-soft); max-width:34rem; margin-bottom:var(--s5); font-size:var(--fs-small)}
.door .label{color:var(--ink-soft); margin-bottom:0}
.door .tlink{border-color:var(--rim-l); color:var(--ink); align-self:flex-start}
@media (max-width:900px){
  .doors{grid-template-columns:1fr}
  .door{grid-template-columns:1fr; min-height:0}
  .door-img{width:100%; height:auto}
  .door-img img{height:auto; aspect-ratio:3/2}
}



/* ---- what changes: the four gains, folded into the scene column ----
   The plate leaves the left ~44% as clean pale wall, so these sit beside the subject
   rather than in a section of their own. Two columns capped at 34rem so the block
   never reaches her; one column below 901px, where the copy is on the page ground. */
.notice{list-style:none; margin:var(--s7) 0 0; padding:0;
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s5) var(--s6); max-width:34rem}
.notice li{padding-top:var(--s4); border-top:1px solid var(--rim)}
.notice b{display:block; margin-bottom:9px; font-family:var(--serif); font-optical-sizing:auto;
  font-weight:400; font-size:clamp(1.05rem,1.5vw,1.35rem); line-height:1.2;
  letter-spacing:-.01em; color:var(--fg)}
.notice span{display:block; font-size:.97rem; line-height:1.6; color:var(--fg-soft)}
@media (max-width:900px){ .notice{grid-template-columns:1fr; gap:var(--s5); max-width:none} }

/* ---- split ---- */
/* portrait scenes: the subject sits in one third, the type in the other */
.scene .creds{margin-top:var(--s6); max-width:30rem}
.scene .creds li{border-top-color:var(--rim)}
.split{display:grid; grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr); gap:clamp(30px,4.5vw,88px); align-items:stretch}
/* The figure has a floor and no ceiling. ::before sets the floor off the figure's own width
   — padding-% always resolves against inline size, so a square floor needs no knowledge of
   the height — and align-self:stretch lets it grow past that whenever the copy beside it is
   longer. So the image is never shorter than square and never shorter than the text, which a
   plain aspect-ratio could not do: the copy blocks differ by hundreds of pixels between
   splits, so any single ratio is far too tall beside one and far too short beside another. */
.split .fig{margin:0; min-height:0; position:relative; align-self:stretch}
.split .fig::before{content:""; display:block; padding-top:100%}
/* Opt-out for a landscape source. Stretching one of those to a tall copy column means cover
   throws away most of its width — gizmo.webp is 3:2 and was showing 43% of itself. This keeps
   the source's own ratio, so nothing is cropped, and centres it beside the copy instead of
   stretching. align-self must come after the .fig rule above: same specificity, so order decides. */
.split .fig-wide{align-self:center}
.split .fig-wide::before{padding-top:66.67%}
/* Opposite opt-out: this figure is meant to overhang the copy rather than match it. The row
   is still sized by the copy, and the negative block margin shrinks the figure's margin box
   so its border box grows past the track by --s6 top and bottom. */
.split .fig-over{margin-block:calc(var(--s6) * -1)}
/* the copy sits centred against whichever of the two is taller */
.split > *:not(.fig){align-self:center}
/* absolute fill, so the image tracks the figure rather than its own intrinsic height — the
   <img>'s width/height attributes land in the presentational-hints origin as a real height,
   which is why these once rendered at their natural pixel size (1067px, 1804px) */
.split .fig img{position:absolute; inset:0; width:100%; height:100%; display:block; object-fit:cover; border-radius:var(--r-lg); box-shadow:var(--lift-2)}
@media (max-width:940px){
  /* stacked, there is no copy column beside it, so the square floor is the whole story */
  .split{grid-template-columns:1fr; gap:var(--s6); align-items:start}
  .split .fig{align-self:auto}
}

.creds{list-style:none; margin:var(--s7) 0 0; padding:0}
.creds li{padding:var(--s4) 0; border-top:1px solid var(--rim-soft); color:var(--fg-soft); font-size:1.05rem; font-weight:300}

/* ---- the session ---- */
.steps{margin-top:var(--s8); border-top:1px solid var(--rim); max-width:76rem; margin-inline:auto}
.step{display:grid; grid-template-columns:11rem 1fr 1.1fr; gap:clamp(18px,3vw,60px);
  padding:var(--s6) 0; border-bottom:1px solid var(--rim-soft); align-items:start}
.step .when{font-size:var(--fs-label); font-weight:300; letter-spacing:.24em; text-transform:uppercase; color:var(--key); line-height:1.8}
.step p{color:var(--fg-soft); font-size:1.05rem; line-height:1.7; margin:0}
@media (max-width:860px){ .step{grid-template-columns:1fr; gap:var(--s2)} }

/* Four promises, each in its own card, read as four things offered rather than four lines of
   a list. The card language is .voice's — same gradient, same hairline, same lift — because
   these sit two sections apart and should look like they came from the same set. The plum rule
   that used to sit beside each line becomes the mark at the head of each card; the reviews gave
   that motif up when they took the stars, so it is free to be this section's own.
   align-content:start on the text keeps short and long promises optically level at the top. */
.pledge{list-style:none; margin:var(--s7) auto 0; padding:0; max-width:76rem;
  display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:var(--s3)}
.pledge li{display:flex; flex-direction:column; padding:clamp(22px,2.1vw,32px);
  border-radius:var(--r-lg); color:var(--fg-soft); font-size:var(--fs-small); line-height:1.65;
  background:linear-gradient(168deg, #FFFFFF 0%, var(--light-warm) 100%);
  box-shadow:var(--gloss), var(--hair), var(--lift-1);
  transition:transform .7s var(--spring), box-shadow .7s var(--ease)}
.pledge li:hover{transform:translateY(-4px); box-shadow:var(--gloss), var(--hair), var(--lift-2)}
.pledge li::before{content:""; display:block; width:22px; height:1px; background:var(--plum);
  margin-bottom:var(--s4); flex:0 0 auto}
@media (prefers-reduced-motion:reduce){ .pledge li{transition:none!important} .pledge li:hover{transform:none} }
@media (max-width:1100px){ .pledge{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:600px){ .pledge{grid-template-columns:1fr} }

/* ---- the silence ---- */
/* Bare type on the open ground — no plate, no fill, no glow. It is the one breath between
   two photographic plates, so anything drawn behind it turns the pause back into a panel.
   Sized by its own symmetric padding; keep it symmetric or place-items:center lies. */
.silence{padding-block:var(--s8); display:grid; place-items:center; text-align:center;
  color:var(--ink); position:relative; padding-inline:var(--gutter)}
/* The padding is metrically symmetric, but the headline's line box carries leading above
   its cap that the label has no counterpart for below its baseline, so the optical centre
   sits low. Trimmed in em so the correction tracks the clamped type instead of one width. */
.silence .line{position:relative; font-weight:300; font-size:clamp(2rem,6.4vw,5.6rem); line-height:1.08; letter-spacing:-.026em; margin:-.27em 0 0; color:var(--ink)}
/* the base .label carries margin-bottom:--s5; left on, it lands under the centred block
   and pulls the line off centre */
.silence .label{margin:var(--s6) 0 0; color:var(--ink-mute); justify-content:center}
/* The silence supplies all of its own air, symmetrically. A neighbour that also pads into it
   doubles up on one side only — the reviews section's --act bottom padding put 331px above
   the line against 133px below it. Let the pause own the whole gap. */
section:not(.scene):not(.silence):has(+ .silence){padding-bottom:0}

/* ---- stages ---- */
.stages{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(26px,3.4vw,64px); margin-top:var(--s8); max-width:76rem; margin-inline:auto}
.stage{padding-top:var(--s4); border-top:1px solid var(--key)}
.stage h3{margin:var(--s3) 0; font-weight:200}
.stage p{color:var(--fg-soft); font-size:1.05rem; line-height:1.7}
@media (max-width:860px){ .stages{grid-template-columns:1fr; gap:var(--s5)} }

.sift{display:grid; grid-template-columns:1fr 1fr; gap:clamp(28px,4.4vw,90px); margin-top:var(--s8); padding-top:var(--s7); border-top:1px solid var(--rim); max-width:76rem; margin-inline:auto}
.sift h3{margin-bottom:var(--s5); font-weight:200}
.sift ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--s3)}
.sift li{display:flex; gap:var(--s4); font-size:1.05rem; color:var(--fg-soft); line-height:1.7}
.sift .y li::before{content:""; width:16px;height:1px;background:var(--key);flex:0 0 16px;margin-top:.78em}
.sift .n li::before{content:""; width:16px;height:1px;background:var(--rim);flex:0 0 16px;margin-top:.78em}
@media (max-width:860px){ .sift{grid-template-columns:1fr; gap:var(--s6)} }

.said{font-weight:300; color:var(--fg); font-size:clamp(1.55rem,3vw,2.6rem); line-height:1.26; letter-spacing:-.018em; max-width:19em; margin:var(--s8) 0 0}

/* ---- proof ---- */
/* overflow-x:auto forces overflow-y to compute to auto as well, so the rail clips its cards
   on the vertical axis too. --lift-2 reaches 36px above a card and 24px below it, and the
   hover lifts another 4px, so the padding has to clear both — it had 0 above and 36 below.
   The extra top padding is taken back out of margin-top so the gap above the rail is unchanged. */
.rail{display:grid; grid-auto-flow:column; grid-auto-columns:min(30rem,84vw); gap:clamp(20px,2.2vw,34px);
  overflow-x:auto; scroll-snap-type:x mandatory;
  margin-top:calc(var(--s8) - var(--s6)); padding-block:var(--s6);
  scrollbar-width:thin; scrollbar-color:var(--rim) transparent}
.rail::-webkit-scrollbar{height:2px} .rail::-webkit-scrollbar-thumb{background:var(--rim)}
.voice{scroll-snap-align:start; padding:clamp(28px,2.6vw,40px); border-radius:var(--r-lg);
  background:linear-gradient(168deg, #FFFFFF 0%, var(--light-warm) 100%);
  box-shadow:var(--gloss), var(--hair), var(--lift-1)}
.voice blockquote{margin:0; font-weight:300; font-size:clamp(1.2rem,1.6vw,1.44rem); line-height:1.5;
  letter-spacing:-.012em; color:var(--fg)}
/* Each card opened with a 22px plum rule; it is replaced by the rating it was standing in for.
   Unlike the section eyebrow, no text here says "five stars", so this carries its own label. */
.voice .rate{display:flex; gap:.2em; margin-bottom:var(--s5); color:var(--plum)}
.voice .rate svg{display:block; width:14px; height:14px; fill:currentColor}
.voice cite{display:block; margin-top:var(--s5); font-style:normal; font-size:var(--fs-small); color:var(--fg-mute)}
.voice cite b{color:var(--fg); font-weight:400}
/* the only card on the page that answered to nothing under the pointer */
.voice{transition:transform .7s var(--spring), box-shadow .7s var(--ease)}
.voice:hover{transform:translateY(-4px); box-shadow:var(--lift-2)}
@media (prefers-reduced-motion:reduce){ .voice{transition:none!important} .voice:hover{transform:none} }

/* ---- begin ---- */
/* Was two columns, ticket beside the list. The ticket now runs full width across the top
   and the offerings sit under it as a grid, so the first session reads as the decision and
   the rest as what follows it. */
.begin-grid{display:grid; grid-template-columns:1fr; gap:var(--s7); align-items:start; margin-top:var(--s8)}
/* The offer card was a 1px hairline box at 4px radius — it read as a form field, not the
   moment the whole page is built toward. Outer tray + inner core, concentric radii, and the
   padding is (shell + core) so the copy sits inside the core without extra markup. */
/* Full width left the right half of the card empty. The copy keeps its own measure on the left
   and the chamber fills the rest, so the width is carrying something. The art stretches to the
   copy's height rather than the reverse, so a longer paragraph never leaves a gap beside it. */
.ticket{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,.72fr);
  gap:clamp(28px,3.4vw,64px); align-items:center}
.ticket-art{align-self:stretch; position:relative; min-height:clamp(240px,20vw,340px);
  border-radius:var(--r-lg); overflow:hidden; box-shadow:var(--hair)}
.ticket-art img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block}
@media (max-width:900px){
  .ticket{grid-template-columns:1fr; gap:var(--s6)}
  .ticket-art{min-height:220px; order:-1}
}
.ticket{position:relative; border:0; border-radius:var(--r-xl);
  padding:calc(var(--shell) + clamp(28px,3.2vw,50px));
  background:rgba(43,35,52,.032); box-shadow:var(--hair), var(--lift-3)}
.ticket::before{content:""; position:absolute; inset:var(--shell); z-index:0;
  border-radius:calc(var(--r-xl) - var(--shell));
  background:linear-gradient(168deg, #FFFFFF 0%, var(--light-warm) 58%, #F6F1F8 100%);
  box-shadow:var(--gloss), 0 0 0 1px rgba(43,35,52,.05)}
.ticket > *{position:relative; z-index:1}

.ticket .fig{font-weight:300; font-size:clamp(3.2rem,6vw,5.2rem); line-height:.94; letter-spacing:-.03em; margin:var(--s4) 0 var(--s2)}
.ticket p.b{color:var(--fg-soft); font-size:var(--fs-small); margin:var(--s5) 0 var(--s6)}
/* Everything in the ticket aligns left now that the card is full width. The button loses
   width:100% with it — stretched across the whole card it stopped reading as a button — and
   the prose takes a measure so the lines do not run the full 1200px. */
/* 44rem never bit: the copy column is 670px, so both paragraphs ran its full width at roughly
   90 characters a line. 30rem holds them to a readable measure and keeps them stacked under
   the price rather than reaching toward the picture. */
.ticket p.b{max-width:30rem}
.ticket .btn{width:auto; justify-content:center}
.ticket .alt{display:block; width:max-content; text-align:left; margin-top:var(--s3); padding-block:8px; min-height:24px; font-size:var(--fs-small); color:var(--fg-mute); text-decoration:none}
.ticket .alt:hover{color:var(--fg)}
.ticket .risk{margin-top:var(--s5); font-size:.92rem; color:var(--fg-mute); text-align:left; line-height:1.6; max-width:30rem}
.tar{list-style:none;margin:var(--s5) 0 0;padding:0}
.tar li{display:flex; justify-content:space-between; gap:var(--s4); padding:var(--s4) 0; border-bottom:1px solid var(--rim-soft); font-size:var(--fs-small)}
.tar li span:first-child{color:var(--fg-soft)}
.tar li span:last-child{color:var(--fg); font-weight:400; white-space:nowrap}
/* Kay read the old list as an invoice, and structurally it was one: name on the left, amount
   right-aligned on the same baseline, hairline rules stacking into a table. Each offering is
   now its own block and the price comes last, after what it is — a card to weigh rather than
   a line to total. .tar stays for egg.html, which still uses it. */
/* Six tracks so five cards can make two full rows: three spanning two each, then two
   spanning three each. An even column count would leave the last row short. */
.offers{list-style:none; margin:var(--s5) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:var(--s3)}
.offer{grid-column:span 2}
.offer:nth-child(4),.offer:nth-child(5){grid-column:span 3}
@media (max-width:1100px){ .offers{grid-template-columns:repeat(2,minmax(0,1fr))}
  .offer,.offer:nth-child(4),.offer:nth-child(5){grid-column:auto} }
@media (max-width:600px){ .offers{grid-template-columns:1fr} }
.offer{padding:clamp(18px,1.9vw,26px) clamp(20px,2.1vw,30px); border-radius:var(--r-lg);
  background:rgba(43,35,52,.028); box-shadow:var(--hair)}
.offer h4{font-size:1.06rem; font-weight:400; line-height:1.3; margin:0; color:var(--fg)}
.offer .meta{display:block; margin-top:4px; font-size:.72rem; font-weight:500;
  letter-spacing:.16em; text-transform:uppercase; color:var(--fg-mute)}
.offer p{margin:var(--s3) 0 0; font-size:var(--fs-small); color:var(--fg-soft); line-height:1.6}
.offer .price{margin-top:var(--s3); font-size:1.4rem; font-weight:300; letter-spacing:-.01em;
  color:var(--fg)}
.offer .price small{font-size:.8rem; font-weight:400; letter-spacing:0; color:var(--fg-mute)}
@media (max-width:940px){ .begin-grid{grid-template-columns:1fr} }

.sub-form{display:grid; gap:var(--s3); margin-top:var(--s4); max-width:22rem}
.foot .sub-form{max-width:none; gap:var(--s2); margin-top:var(--s5)}
.foot .fld input{padding:12px 18px}
.foot .sub-form .btn{margin-top:var(--s2); justify-self:start}
.fld{display:grid; gap:8px}
/* The labels sit inside the fields now. They stay in the DOM rather than becoming placeholders
   alone: a placeholder is not a reliable accessible name, and it disappears the moment someone
   starts typing, which leaves no way back to what the field was for. */
.vh{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap; border:0}
.fld label:not(.vh){font-size:var(--fs-label); font-weight:300; letter-spacing:.22em; text-transform:uppercase; color:var(--fg-mute)}
.fld input::placeholder{color:var(--fg-mute); opacity:1}
.fld input{font-family:var(--sans); font-weight:400; font-size:.98rem; padding:15px 22px; color:var(--fg);
  background:#fff; border:0; border-radius:100px;
  box-shadow:var(--gloss), 0 0 0 1px rgba(43,35,52,.08), var(--lift-1);
  transition:box-shadow .6s var(--ease)}
.fld input:hover{box-shadow:var(--gloss), 0 0 0 1px rgba(43,35,52,.14), var(--lift-1)}

.fld input:focus{outline:2px solid var(--plum-soft); outline-offset:2px}
.note{font-size:.95rem; color:var(--fg-mute); margin-top:var(--s3)}

/* ---- FAQs ----
   <details>/<summary> rather than a scripted accordion: it opens without JS, it is in the
   tab order already, and a browser's own find-in-page can open a closed answer, which a
   div-and-click version silently cannot. */
.faq{margin-top:var(--s7); border-top:1px solid var(--rim-soft)}
.faq details{border-bottom:1px solid var(--rim-soft)}
.faq summary{display:flex; align-items:baseline; justify-content:space-between; gap:var(--s5);
  padding:var(--s4) 0; cursor:pointer; list-style:none; color:var(--ink);
  font-size:clamp(1.02rem,1.35vw,1.16rem); font-weight:400; line-height:1.4}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+"; flex:0 0 auto; color:var(--plum); font-weight:300;
  font-size:1.45rem; line-height:1; transition:transform .45s var(--ease)}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq summary:hover{color:var(--plum-deep)}
.faq summary:focus-visible{outline:2px solid var(--plum-soft); outline-offset:4px; border-radius:4px}
.faq .a{margin:0; padding:0 0 var(--s5); max-width:46rem;
  color:var(--fg-soft); font-size:var(--fs-small); line-height:1.75}
@media (prefers-reduced-motion:reduce){ .faq summary::after{transition:none} }
/* The honeypot is for bots, so it is removed from the page rather than merely invisible:
   display:none is exactly what a real person never sees and a naive crawler still fills. */
.trap{position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden}
.form-msg:empty{display:none}
.form-msg.is-ok{color:var(--plum-deep)}
.form-msg.is-bad{color:var(--ink)}

/* ---- footer ---- */
.foot{background:var(--light-deep); color:var(--ink-soft); padding-block:var(--s8) var(--s6); border-top:1px solid var(--rim-l)}
.foot a{color:var(--ink-soft); text-decoration:none} .foot a:hover{color:var(--ink)}
/* three columns since the header already carries the navigation: identity, where to find
   her, and the sign-up. The last column is widest because the inputs live in it. */
/* No border-bottom: that hairline separated this grid from the legal block when the small
   print still sat inside the footer. With it moved out the rule had nothing under it. */
.foot-grid{display:grid; grid-template-columns:1.15fr 1fr 1.25fr; gap:var(--s6) var(--s7); align-items:start}
.foot h3{font-size:var(--fs-label); font-weight:300; letter-spacing:.24em; text-transform:uppercase; color:var(--ink-mute); margin:0 0 var(--s4)}
.foot ul{list-style:none;margin:0;padding:0;display:grid;gap:8px;font-size:var(--fs-small)}
.foot ul a{display:inline-block; padding-block:3px; min-height:24px}
.foot .hours b{color:var(--ink); font-weight:400}
.foot .hours{line-height:1.75}
.foot .fb{font-weight:300; font-size:clamp(1.6rem,2.8vw,2.4rem); line-height:1.14; letter-spacing:-.02em; margin-bottom:var(--s4); color:var(--ink)}
.foot .cov{font-size:var(--fs-small); max-width:22rem}
/* Lifted out of the footer card: this is site-wide small print, not part of the footer, so it
   sits on the open ground beneath the plate. */
/* No top padding: the footer plate already carries --frame beneath it, and that frame is the
   distance every plate on the page sits from what follows. Anything added here reads as a
   second, larger gap on top of it. */
.legal-bar{padding-block:0 var(--s6)}
/* Below 901 the footer is a band with no bottom margin, so with no padding here the small
   print sat flush against it at 0px. This supplies the gap the frame gives on desktop. */
@media (max-width:900px){ .legal-bar{padding-top:var(--s5)} }
.legal-bar p{font-size:.88rem; line-height:1.7; color:var(--ink-mute); max-width:60rem; margin:0}
.legal-bar .copyline{margin-top:var(--s3)}
@media (max-width:1080px){ .foot-grid{grid-template-columns:1fr 1fr; gap:var(--s6) var(--s7)} }
@media (max-width:860px){ .foot-grid{grid-template-columns:1fr; gap:var(--s6)} }

/* ---- entry ---- */
.rv{opacity:0; transform:translateY(28px)}
.rv-in{opacity:1; transform:none; transition:opacity 1.2s var(--ease), transform 1.25s var(--spring), filter 1.1s var(--ease)}
/* the focus pull is the cinematic half of the entry. Held to pointer:fine + wide viewports so
   phones never pay for a compositor blur on a long scrolling page. */
@media (min-width:900px) and (pointer:fine){
  .rv{filter:blur(7px)}
  .rv-in{filter:blur(0)}
}
@media (prefers-reduced-motion:reduce){
  .rv{opacity:1!important; transform:none!important; filter:none!important} .rv-in{transition:none!important}
  .door-img img,.btn,.btn .ar,.tlink{transition:none!important}
  /* the sheet's reveal IS its stagger, so it has to resolve instantly rather than be
     switched off — otherwise the links sit at opacity:0 and the menu looks empty */
  .sheet a,.sheet > .btn{opacity:1!important; transform:none!important;
    transition:none!important; transition-delay:0s!important}
}
/* the no-js fallback has to clear the focus pull too — resetting only opacity/transform
   would leave every revealed block blurred if main.js never runs */
.no-js .rv{opacity:1; transform:none; filter:none}


/* ============================================================
   EDITORIAL BREAKS — the clean type sections between scenes.
   Previously these were a single left column floating in dead
   space. They now sit on a real grid: a pinned label column,
   a measured prose column, and a statement that spans.
   ============================================================ */
.editorial{display:grid; grid-template-columns:13rem minmax(0,40rem) 1fr; gap:clamp(20px,4vw,84px); align-items:start}
.editorial > .side{position:sticky; top:120px}
.editorial > .side .label{margin:0}
.editorial > .side .rule{display:block; width:100%; height:1px; background:var(--rim); margin-top:var(--s5)}
.editorial > .body > * + *{margin-top:var(--s5)}
.editorial > .body > h2 + *{margin-top:var(--s6)}
@media (max-width:1000px){
  .editorial{grid-template-columns:1fr; gap:var(--s5)}
  .editorial > .side{position:static}
  .editorial > .side .rule{display:none}
}

/* a statement that owns its own band, with rules above and below */
.statement{
  margin:var(--s9) auto 0; max-width:64rem; text-align:center;
  padding-block:var(--s8);
}
/* When the statement is a section of its own, the section's own padding already carries
   the space — the s9 margin and s8 padding then stack on top of it and open a ~480px hole
   above the line. Inside a section that has other content above it (the stages on
   work-with-kathleen) the margin is what separates them, so it stays. */
.wrap > .statement:only-child{margin-top:0; padding-block:0}
/* one line of type does not need the full --act section rhythm around it */
.statement-only{padding-block:var(--s8)}
.statement p{
  font-weight:300; font-size:clamp(1.7rem,3.4vw,3rem); line-height:1.22; letter-spacing:-.018em;
  margin:0 auto; max-width:22em; color:var(--fg);
}
.statement .after{margin-top:var(--s6); font-size:var(--fs-lead); font-weight:200; color:var(--fg-soft); max-width:32em}
.statement .mark{display:block; width:26px; height:1px; background:var(--key); margin:0 auto var(--s6)}

/* section intros inside plain sections get the same measured column */
.intro{max-width:76rem; margin-inline:auto}
.intro h2{margin-bottom:var(--s5)}

/* a centred lede — the one place the page speaks to the reader head-on rather than
   from the side of a photograph, so it sits on the centre line instead of the left rail */
.lede{max-width:52rem; margin-inline:auto; text-align:center}
.lede .label{margin-bottom:var(--s5)}
.lede h2{max-width:none; margin-inline:auto}
.lede .lead{max-width:38rem; margin:var(--s6) auto 0}

/* ── the gap between a full-bleed plate and the copy under it ──────────────────
   Two gaps stack wherever a plain section follows a scene: the section's own top
   padding (--act, up to 250px at wide viewports) and the first block's margin-top
   (s8, 118px). Together that is ~370px of nothing between the image and the first
   line of copy, which reads as a hole rather than as breathing room. The section's
   top padding carries the separation on its own; the block margin collapses. */
/* :not(.scene) matters — a scene following a scene must keep its own padding, and on
   mobile it must keep padding-top:0 or a gap opens above the image band. */
/* ── the closing beat ──────────────────────────────────────────────────────────
   The first visit and the offer belong to the same moment, so they share one
   section: the four steps, the pledge, then the price read as one block instead of
   a walkthrough followed by a separate pricing page. Address and contact live in
   the footer, which is the only place they appear now.
   It carries no photograph, so it is a plain section and not a .scene — that alone
   gives it the ground label, the ground lead colour and normal --act padding. */
/* the head blocks used to get their lead spacing from .scene .lead; on the ground they
   need it stated, and 33rem orphaned "go." onto a line of its own */
/* A heading block that introduces the list under it, sitting on open ground rather than
   over a photograph. Same job as .closing's head blocks, so it needs the same lead spacing
   that .scene .lead would otherwise have supplied. */
.closing .lead,.intro-lede > .wrap > .rv:first-child .lead{margin-top:var(--s5); max-width:38rem}
.intro-lede .stages{margin-top:var(--s7)}
.closing .steps{margin-top:var(--s7)}
/* the walkthrough and the offer share one section, so the change of subject needs a mark
   of its own — an <hr> because that is exactly a thematic break, carrying brand colour
   rather than the grey hairline the rest of the page uses for list rules */
.divide{height:2px; border:0; margin:var(--s8) 0 0; border-radius:2px;
  background:linear-gradient(to right, transparent 0%, var(--key) 12%, var(--plum) 50%, var(--key) 88%, transparent 100%);
  opacity:.55}
.closing .begin-head{margin-top:var(--s8)}
.closing .divide + .begin-head{margin-top:var(--s7)}
.closing .begin-grid{margin-top:var(--s7)}

/* ── section rhythm ────────────────────────────────────────────────────────────
   Every boundary between two sections opens the same gap, whatever sits on either
   side. What each side contributes differs, so each pairing is derived rather than
   guessed: --gap-sec is the plate-to-prose case (a plate's own --s6 + its --frame
   margin + --s8 from the prose), and everything else is solved back from it.
   Both breakpoints are stated in full because a plate is a different shape in each:
   above 901px it is an inset plate with a frame margin, below it is a band with the
   copy beneath, so it contributes nothing above and its own --s8 below. Leaving one
   set unscoped lets it out-specify the other and leak across the breakpoint. */
@media (min-width:901px){
  .scene + section:not(.scene):not(.doors):not(.silence){padding-top:var(--s8)}
  /* .doors used to fill its plate edge to edge with two full-height cards, so it put no
     padding into the boundary and the prose either side carried its share — which left those
     neighbours lopsided: 118 one end, 170 the other, visible as a statement sitting low in
     its own band. Now that the doors are inset rows, the section carries its own --s6 and
     both neighbours go back to a symmetric --s8. The boundary total is unchanged at
     118 + frame + 52. */
  /* .doors contributes nothing to the boundary, exactly as .scene does. Both are plates: what
     the eye reads as the edge is the card or the photograph, and a plate's own padding sits
     inside that edge where it is not whitespace at all. Giving .doors real padding made the
     white above the statement 205 against 153 below it, because the scene under it was hiding
     its 52 inside the plate. Neighbours carry --s8 on both sides and every boundary reads the
     same 118 + frame. */
  /* The rhythm rules all key off what comes next, so the last section matches none of them and
     kept the raw --act (202px at 1440) against a --s8 top. The footer plate carries its own
     118px of head room, so this only needs to match every other plain section. */
  main > section:last-child{padding-bottom:var(--s8)}
  .doors + section:not(.scene):not(.doors):not(.silence){padding-top:var(--s8)}
  section:not(.scene):not(.doors):not(.silence):has(+ .scene, + .doors){padding-bottom:var(--s8)}
  /* two prose sections have no plate padding and no frame between them, so they split
     the boundary evenly — half each lands on the same total as every other pairing */
  section:not(.scene):not(.doors):not(.silence) + section:not(.scene):not(.doors):not(.silence){padding-top:calc(var(--gap-sec) / 2)}
  section:not(.scene):not(.doors):not(.silence):has(+ section:not(.scene):not(.doors):not(.silence)){padding-bottom:calc(var(--gap-sec) / 2)}
}
.scene + section:not(.scene):not(.doors):not(.silence) > .wrap > :first-child{margin-top:0}
@media (max-width:900px){
  /* Stacked, a scene is a band plus copy on the ground: it starts at its band, so it
     contributes nothing above and its own --s8 below. That --s8 is the whole boundary
     here, and the prose after it adds nothing on top. */
  .scene + section:not(.scene):not(.doors):not(.silence){padding-top:0}
  .doors + section:not(.scene):not(.doors):not(.silence){padding-top:var(--s8)}
  section:not(.scene):not(.doors):not(.silence):has(+ .scene, + .doors){padding-bottom:var(--s8)}
  section:not(.scene):not(.doors):not(.silence) + section:not(.scene):not(.doors):not(.silence){padding-top:calc(var(--s8) / 2)}
  section:not(.scene):not(.doors):not(.silence):has(+ section:not(.scene):not(.doors):not(.silence)){padding-bottom:calc(var(--s8) / 2)}
  /* the pause carries the full boundary on the side facing a stacked scene */
  .scene:has(+ .silence){padding-bottom:0}
}


/* ══ PLATES ═══════════════════════════════════════════════════════════════════
   Every full-bleed block becomes an inset rounded plate on the page ground.
   Above 901px a scene is one plate, because the copy sits on the photograph.
   Below 901px the scene is already split into a band plus copy on the ground,
   so only the band is plated — rounding the whole section there would round
   around body copy that is meant to sit on the open ground. */
@media (min-width:901px){
  /* Held to --maxw and centred, like .doors, rather than stretching edge to edge. Full-bleed
     stopped working past ~1600px: the plate kept growing while the copy column did not, so at
     1990 a 640px column sat inside a 1902px plate — a third of the width, marooned, with a
     dead band of wall between it and the subject. Capping the plate is what closes that. */
  .scene{
    width:min(var(--maxw), 100% - var(--frame)*2);
    margin:var(--frame) auto; border-radius:var(--plate-r); overflow:hidden;
    /* adjacent plate margins collapse, so the gutter between plates stays one frame */
    min-height:var(--plate-h);
  }
  /* two doors read better as two cards than as one split panel */
  /* the copy plate is sized by its content, so every extra pixel of card height goes to
     the photograph above it — 88vh left only a thin band of image showing */
  /* Two cards, not one full-bleed photograph — so unlike a .scene these hold to --maxw and
     centre, instead of stretching to 1832px on a wide monitor. The gap opens past --frame as
     well: at this size the frame-width gutter read as a seam between halves rather than as
     space between two separate things. */
  .doors{width:min(var(--maxw), 100% - var(--frame)*2); margin:var(--frame) auto; gap:var(--s7)}
  .door{border-radius:var(--plate-r); overflow:hidden}
  .door + .door{border-left:0}
  .foot{width:min(var(--maxw), 100% - var(--frame)*2); margin:var(--frame) auto; border-radius:var(--plate-r)}
  /* the plate already carries a frame of its own, so the copy needs less gutter */
  .scene .wrap{padding-inline:clamp(18px,3.4vw,64px)}
}

@media (max-width:900px){
  /* only the photographic band is plated; the copy stays on the open ground */
  .scene .scene-img{
    margin-inline:var(--frame); border-radius:var(--plate-r); overflow:hidden;
  }
  .doors{padding-inline:var(--frame); gap:var(--frame)}
  .door{border-radius:var(--plate-r); overflow:hidden}
  .door + .door{border-top:0}
  .foot{margin-inline:var(--frame); border-radius:var(--plate-r)}
}
