/* ==========================================================================
   FIRM HANDS — DESIGN SYSTEM v2
   Rebuilt to the client's onboarding form (Form A, 30 questions).

   The four answers that drive this stylesheet:
     Q09  Mostly black and white, blue accents only
     Q10  Alternating dark and light sections
     Q11  Blocky headlines, clean body text
     Q12  Purposeful animation only, on scroll

   So: a neutral carbon/paper spine, ONE blue used only as accent, Anton for
   display and Poppins for everything else (no editorial serif), and no
   ambient/infinite motion anywhere — reveals on scroll and hover states only.

   Every section is either dark (default / .sec--panel) or light (.sec--light)
   and the page alternates between them. Section 3b is the complete light-ground
   theme: any component that can appear on paper is re-grounded there. If you
   add a component, add its light variant in 3b or it will render invisible.
   ========================================================================== */

/* ---------- 1. TOKENS ---------- */
:root {
  /* "carbon" — deliberately navy, not neutral black. Client feedback 2026-08-26:
     "not so dark... more white, more blue, less black... bright and cheery."
     Any dark ground on this site is now a deep BLUE, never a true black, and it
     is used sparingly (hero, closing CTA) rather than as the page default — see
     `body` below and the per-section notes further down. */
  --ink:        #05101c;
  --ink-2:      #091827;
  --ink-3:      #0e2032;
  --ink-4:      #152a40;
  --hair:       rgba(233, 236, 240, .10);
  --hair-2:     rgba(233, 236, 240, .20);

  /* paper (light grounds) */
  --paper:      #f7f8fa;
  --paper-2:    #ebeef2;
  --paper-3:    #ffffff;
  --paper-hair: rgba(10, 10, 11, .10);
  --paper-hair-2: rgba(10, 10, 11, .16);
  --paper-ink:  #12141a;   /* body copy on paper — 15.1:1 */
  --paper-mid:  #474c55;   /* secondary copy on paper — 8.0:1 */
  --paper-mute: #5c626c;   /* small caps on paper — 5.8:1 */

  /* neutrals on carbon */
  --chalk:      #edeff2;
  --silver:     #b6bac1;
  --mute:       #90959c;
  --mute-2:     #888d95;   /* >=4.5:1 on --ink for small caps */

  /* the one accent. blue only — never a second hue.
     #0085CA is the client's brand blue and drives --blue-core (fills) and
     --blue-deep (paper text). --blue itself is a lightened step of the same
     hue, not the raw brand value — raw #0085CA text fails 4.5:1 on more than
     one of the navy carbon tones above, --blue clears all of them:
       --blue      6.76:1 on --ink, 6.33:1 on --ink-2, 5.83:1 on --ink-3, 5.16:1 on --ink-4
       --blue-core as a FILL takes --ink text (4.75:1). White on it fails at
                   4.03:1, so no white-on-blue anywhere
       --blue-deep 6.94:1 on --paper, 7.38:1 the other way with white text */
  --blue:       #2ba3e0;   /* accent TEXT on carbon */
  --blue-lift:  #2ba3e0;   /* alias of --blue — both carbon tones are the same value now */
  --blue-core:  #0085ca;   /* solid FILLS; pairs with --ink text, never white */
  --blue-deep:  #005b87;   /* accent text on paper grounds */
  --blue-soft:  rgba(43, 163, 224, .16);

  /* type — blocky display, clean body. no serif. */
  --display: 'Anton', 'Haettenschweiler', 'Arial Narrow', sans-serif;
  --sans:    'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* scale */
  --pad: clamp(1.25rem, 5vw, 5rem);
  --maxw: 1320px;
  --narrow: 780px;

  /* motion */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --ease-2: cubic-bezier(.65, 0, .35, 1);

  --radius: 2px;
  --shadow: 0 30px 80px -20px rgba(0, 0, 0, .75);
  --shadow-light: 0 24px 60px -26px rgba(10, 12, 20, .28);
}

/* ---------- 2. RESET ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0;
  background: var(--ink);
  color: var(--chalk);
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(1rem, .95rem + .2vw, 1.0625rem);
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
h1, h2, h3, h4 { margin: 0; font-weight: 400; }
p { margin: 0 0 1.1em; }
/* the UA sheet gives figure 0 40px and blockquote 1em 40px. Left unreset,
   a <figure> card inside a grid track is inset 40px each side and renders
   ~80px narrower than its column — which is exactly what the video wall did
   the first time it was built. */
figure, blockquote { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
::selection { background: var(--blue-core); color: var(--ink); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--chalk); color: var(--ink);
  padding: .8rem 1.2rem; font-weight: 600; letter-spacing: .06em;
}
.skip:focus { left: 1rem; top: 1rem; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

/* ---------- 3. TYPE PRIMITIVES ---------- */
.d1, .d2, .d3, .d4 {
  font-family: var(--display);
  font-weight: 400;
  /* pure white, not --chalk: display type is the one place the extra stop of
     brightness reads, and it keeps headings consistent with the section
     headers that set #fff directly. */
  color: #fff;
  text-transform: uppercase;
  line-height: .86;
  letter-spacing: -.005em;
  word-spacing: .04em;
}
.d1 { font-size: clamp(2.9rem, 11.2vw, 10.5rem); }
.d2 { font-size: clamp(2.3rem, 6.6vw, 5.6rem); }
.d3 { font-size: clamp(1.75rem, 3.6vw, 3rem); }
.d4 { font-size: clamp(1.35rem, 2vw, 1.85rem); line-height: .95; }

/* varsity outline treatment — echoes the outlined logo lettering */
.outline {
  color: transparent;
  -webkit-text-stroke: 1.5px var(--silver);
  text-stroke: 1.5px var(--silver);
}
@supports not (-webkit-text-stroke: 1px red) { .outline { color: var(--silver); } }

.eyebrow {
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--blue);
  display: flex; align-items: center; gap: .85rem;
  margin: 0 0 1.5rem;
}
.eyebrow::before {
  content: ''; width: clamp(24px, 4vw, 54px); height: 1px;
  background: currentColor; flex: none; opacity: .75;
}
.eyebrow--center { justify-content: center; }
.eyebrow--center::after {
  content: ''; width: clamp(24px, 4vw, 54px); height: 1px;
  background: currentColor; opacity: .75;
}

.lede { font-size: clamp(1.05rem, 1.55vw, 1.3rem); line-height: 1.62; color: var(--silver); font-weight: 300; }
.quiet { color: var(--mute); }
/* the old build used Playfair italic for emphasis. Q11 asked for clean body
   text, so emphasis is now weight + a blue rule, never a second typeface. */
.em { font-style: normal; font-weight: 500; color: var(--chalk); box-shadow: inset 0 -.14em 0 var(--blue-core); }

/* ---------- 3b. LIGHT GROUND (the alternating half of Q10) ---------- */
.sec--light {
  background: var(--paper);
  color: var(--paper-ink);
}
.sec--light .lede         { color: var(--paper-mid); }
.sec--light .quiet        { color: var(--paper-mute); }
.sec--light .eyebrow      { color: var(--blue-deep); }
.sec--light .d1,
.sec--light .d2,
.sec--light .d3,
.sec--light .d4           { color: var(--ink); }
.sec--light .outline      { -webkit-text-stroke: 1.5px rgba(5,16,28,.55); }
@supports not (-webkit-text-stroke: 1px red) { .sec--light .outline { color: var(--paper-mute); } }
.sec--light .em           { color: var(--ink); box-shadow: inset 0 -.14em 0 rgba(0,91,135,.35); }
.sec--light .rule         { background: var(--paper-hair); }

.sec--light .tlink        { color: var(--blue-deep); }
/* order matters: the solid variant is re-grounded first, then --ghost undoes the
   fill. Flip these two and every ghost button on paper renders solid, because
   the selectors carry identical specificity. */
/* .drawer shares these — it is a light-ground overlay outside the normal
   .sec--light section flow, so it needs the same button treatment directly */
.sec--light .btn, .drawer .btn                   { --bg: var(--ink); --fg: var(--paper); }
.sec--light .btn::before, .drawer .btn::before    { background: var(--blue-core); }
.sec--light .btn:hover, .drawer .btn:hover        { --fg: var(--ink); box-shadow: 0 14px 34px -14px rgba(0,91,135,.45); }
.sec--light .btn--ghost, .drawer .btn--ghost                 { --bg: transparent; --fg: var(--ink); box-shadow: inset 0 0 0 1px var(--paper-hair-2); }
.sec--light .btn--ghost::before, .drawer .btn--ghost::before { background: var(--ink); }
.sec--light .btn--ghost:hover, .drawer .btn--ghost:hover     { --fg: var(--paper); box-shadow: inset 0 0 0 1px transparent, 0 14px 34px -14px rgba(10,12,20,.4); }

.sec--light .checks li    { color: var(--paper-mid); }
.sec--light .checks svg   { color: var(--blue-deep); }
.sec--light .chip         { background: rgba(0,91,135,.07); border-color: var(--paper-hair); color: var(--paper-mid); }
.sec--light .chip:hover   { background: rgba(0,91,135,.14); color: var(--ink); border-color: var(--paper-hair-2); }
.sec--light .tag          { border-color: var(--paper-hair-2); color: var(--blue-deep); }

.sec--light .pillar       { background: var(--paper-3); border-color: var(--paper-hair); box-shadow: var(--shadow-light); }
.sec--light .pillar::before { background: linear-gradient(170deg, rgba(0,91,135,.09), transparent 62%); }
.sec--light .pillar h3    { color: var(--ink); }
.sec--light .pillar p     { color: var(--paper-mid); }
.sec--light .pillar__n    { -webkit-text-stroke: 1px rgba(0,91,135,.55); }

.sec--light .svc          { background: var(--paper-3); border-color: var(--paper-hair); }
.sec--light .svc__body h2,
.sec--light .svc__body h3 { color: var(--ink); }
.sec--light .svc__body p  { color: var(--paper-mid); }
.sec--light .prices       { background: var(--paper-hair); border-color: var(--paper-hair); }
.sec--light .price        { background: var(--paper-2); }
.sec--light .price:hover  { background: #e2e6ec; }
.sec--light .price b      { color: var(--ink); }
.sec--light .price span   { color: var(--paper-mute); }

.sec--light .addons       { background: var(--paper-hair); border-color: var(--paper-hair); }
.sec--light .addon        { background: var(--paper-3); }
.sec--light .addon:hover  { background: var(--paper-2); }
.sec--light .addon h4     { color: var(--ink); }
.sec--light .addon p      { color: var(--paper-mid); }
.sec--light .addon b      { color: var(--blue-deep); }
.sec--light .addon i      { color: var(--paper-mute); }

.sec--light .info         { background: var(--paper-hair); border-color: var(--paper-hair); }
.sec--light .info__c      { background: var(--paper-3); }
.sec--light .info__k      { color: var(--blue-deep); }
.sec--light .info__v      { color: var(--paper-ink); }
.sec--light .info__v a:hover { color: var(--blue-deep); }
.sec--light .hours dt     { color: var(--paper-mute); }
.sec--light .hours dd     { color: var(--paper-ink); }

.sec--light .field label  { color: var(--paper-mute); }
.sec--light .field input,
.sec--light .field select,
.sec--light .field textarea { background-color: var(--paper-3); border-color: var(--paper-hair-2); color: var(--paper-ink); }
.sec--light .field input:focus,
.sec--light .field select:focus,
.sec--light .field textarea:focus { border-color: var(--blue-core); background-color: #fff; }
.sec--light .field input::placeholder,
.sec--light .field textarea::placeholder { color: var(--paper-mute); }
.sec--light .field select { background-image: linear-gradient(45deg, transparent 50%, var(--paper-mute) 50%), linear-gradient(135deg, var(--paper-mute) 50%, transparent 50%); }
.sec--light .rule { background: var(--paper-hair); }
.sec--light .form__note   { color: var(--paper-mute); }

/* photography needs a lighter scrim on paper or the cards read as holes */
.sec--light .shot::before { background: linear-gradient(180deg, rgba(5,16,28,.04) 0%, rgba(5,16,28,.34) 100%); }
.sec--light .shot { background: var(--paper-2); }

/* ---------- 4. LAYOUT ---------- */
.wrap { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--pad); }
.wrap--narrow { max-width: var(--narrow); }
.sec { padding-block: clamp(3rem, 6.5vh, 5.75rem); position: relative; }
.sec--tight { padding-block: clamp(2.25rem, 4.5vh, 3.75rem); }
/* the header is sticky, so anything an in-page link targets (#prices, #youth, #intake, #book, #enquiry, #availability) has to reserve room for it
   or the jump lands with the heading tucked underneath. */
[id] { scroll-margin-top: clamp(84px, 11vh, 108px); }
.sec--panel { background: var(--ink-2); }
.rule { height: 1px; background: var(--hair); border: 0; margin: 0; }

.grid { display: grid; gap: clamp(1.5rem, 3vw, 3rem); }
/* a grid item defaults to min-width:auto, so one unbreakable child (a nowrap
   button, a display-type phone number) can force its track wider than the
   container and push the whole document past the viewport at 320px. */
.grid > *, .split > * { min-width: 0; }
.g-2 { grid-template-columns: repeat(2, 1fr); }
.g-3 { grid-template-columns: repeat(3, 1fr); }
.g-4 { grid-template-columns: repeat(4, 1fr); }
.g-5 { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 1080px) { .g-5 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 940px) { .g-3, .g-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px) { .g-5 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .g-2, .g-3, .g-4 { grid-template-columns: 1fr; } }

/* ---------- 5. IMAGE AMPLIFICATION ----------
   Q09 originally called for monochrome photography with a whisper of accent
   blue. Client feedback 2026-08-26 reversed that explicitly ("use colored
   photos... bright and cheery") — the forced grayscale is gone. What is left
   is a light polish (a little contrast and saturation, no darkening) and a
   much softer version of the same blue cohesion wash, cut roughly in half so
   it unifies the palette without casting real skin tones and real colour. */
.shot { position: relative; overflow: hidden; background: var(--ink-3); isolation: isolate; }
.shot > img {
  width: 100%; height: 100%; object-fit: cover;
  filter: contrast(1.04) saturate(1.08) brightness(1.01);
  transition: transform 1.4s var(--ease), filter .8s var(--ease);
  will-change: transform;
}
.shot::before {
  content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(5,16,28,.02) 0%, rgba(5,16,28,.24) 100%),
    radial-gradient(120% 90% at 50% 15%, transparent 45%, rgba(5,16,28,.26) 100%);
}
/* the accent blue, now a light cohesion wash rather than the only colour present */
.shot::after {
  content: ''; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: linear-gradient(155deg, rgba(0,133,202,.12), rgba(16,17,20,.04) 58%, rgba(51,163,224,.08));
  mix-blend-mode: soft-light;
}
.shot--flat::before { background: linear-gradient(180deg, rgba(5,16,28,.0), rgba(5,16,28,.14)); }
.shot--clean::after { display: none; }
.shot:hover > img { transform: scale(1.055); filter: contrast(1.06) saturate(1.1) brightness(1.03); }

/* film grain — one shared SVG noise layer */
.grain::after,
body::after {
  content: '';
  position: absolute; inset: 0; z-index: 4; pointer-events: none;
  opacity: .16; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
body::after { position: fixed; opacity: .045; z-index: 900; }

/* aspect helpers */
.ar-1  { aspect-ratio: 1 / 1; }
.ar-45 { aspect-ratio: 4 / 5; }
.ar-34 { aspect-ratio: 3 / 4; }
.ar-32 { aspect-ratio: 3 / 2; }
.ar-169{ aspect-ratio: 16 / 9; }

/* ---------- BRUCE ----------
   The mascot is called Bruce, and the studio is attached to him. Earlier builds
   worked from a 512px JPEG lockup with no alpha channel — silver-on-black,
   cropped at the waist — and had to fake a shirt colour with a luminance mask.
   Q28's vector arrived 2026-08-25 (Firm_Hands_Mascot_FINAL.pdf, full body,
   true alpha) and replaced all of that: Bruce is real colour now (black tee
   with "FIRMHANDS" printed on it, light-grey skin, black linework), exactly
   as designed. Two flat PNGs, cut from that PDF at 6x/864pt:

     bruce-mark.png   bust crop, real colour   -> header / footer / gift card
     bruce-solid.png  full-body silhouette     -> the footer watermark

   bruce-mark.png is fixed-palette art, not a tintable silhouette, so it
   renders as a plain <img> — no mask-image, no --blue/--silver switching, and
   nothing here needs a light/dark variant (the physical studio poster proves
   this exact palette already reads fine on white).

   A blue wristband was added in post on 2026-08-25 and removed again the same
   day on client instruction ("we don't like the wristbands, keep him
   normal") — his open palm was also tried as the list bullet (`.bhand`,
   masking `bruce-palm.png`) and dropped the same round ("remove the blue
   hand, it looks weird"). Both reverted. Bruce is not stylised beyond his own
   artwork anywhere on this site; the client has said so twice now. */
.bruce { position: relative; display: block; flex: none; aspect-ratio: 900 / 746; }
.bruce-mark { display: block; width: 100%; height: 100%; object-fit: contain; }

/* ---------- 6. BUTTONS ----------
   On carbon the primary button is white-on-black and fills blue on hover; on
   paper it is black-on-white and fills blue on hover (see 3b). Blue is the
   accent, never the resting state of a large surface. */
.btn {
  --bg: var(--chalk); --fg: var(--ink);
  display: inline-flex; align-items: center; justify-content: center; gap: .7rem;
  padding: 1.05rem 1.9rem;
  background: var(--bg); color: var(--fg);
  font-size: .74rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  border-radius: var(--radius);
  position: relative; overflow: hidden; isolation: isolate;
  transition: color .45s var(--ease), transform .45s var(--ease), box-shadow .45s var(--ease);
  white-space: nowrap;
}
.btn::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: var(--blue-core);
  transform: translateY(101%);
  transition: transform .5s var(--ease);
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 14px 34px -12px rgba(0,133,202,.55); --fg: var(--ink); }
.btn:hover::before { transform: translateY(0); }
.btn__arrow { transition: transform .45s var(--ease); }
.btn:hover .btn__arrow { transform: translateX(4px); }

.btn--ghost { --bg: transparent; --fg: var(--chalk); box-shadow: inset 0 0 0 1px var(--hair-2); }
.btn--ghost:hover { --fg: var(--ink); box-shadow: inset 0 0 0 1px transparent, 0 14px 34px -14px rgba(0,133,202,.5); }
.btn--blue { --bg: var(--blue-core); --fg: var(--ink); }
.btn--blue::before { background: var(--chalk); }
.btn--blue:hover { --fg: var(--ink); }
.btn--sm { padding: .82rem 1.4rem; font-size: .68rem; }
.btn--wide { padding-inline: 2.6rem; }
.btn[disabled] { opacity: .45; pointer-events: none; }
/* nowrap is right on a phone-and-up label, but some of these run long
   ("Book the Youth Special · $95") and at 320px an unbreakable button is the
   single widest thing on the page. */
@media (max-width: 480px) {
  .btn { white-space: normal; text-align: center; line-height: 1.4; }
  .btn--wide { padding-inline: 1.6rem; }
}

/* text link with animated underline */
.tlink {
  display: inline-flex; align-items: center; gap: .55rem;
  font-size: .74rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  color: var(--blue); position: relative; padding-bottom: .35rem;
}
.tlink::after {
  content: ''; position: absolute; left: 0; bottom: 0; height: 1px; width: 100%;
  background: currentColor; transform-origin: left;
  transform: scaleX(0); transition: transform .5s var(--ease);
}
.tlink:hover::after { transform: scaleX(1); }

/* ---------- 7. HEADER ----------
   Client feedback 2026-08-26: "not so dark... more white, more blue, less
   black... bright and cheery." The header and topbar are the one piece of
   chrome visible on every page at every scroll position, so converting them
   to paper is the single highest-leverage brightening move on the site — far
   more of the page is "header" over a session than any one content section.
   The old near-black backdrop existed to stop a luminance-masked logo from
   shifting hue as content scrolled behind it; Bruce is a flat-colour PNG now
   (see the BRUCE block below), so that constraint is gone. */
.topbar {
  background: var(--blue-core);
  /* ink at full opacity, not white: white-on-blue-core is 4.03:1 and fails —
     see the TOKENS note above. A translucent ink also fails once it blends
     toward the fill, so this stays solid. */
  color: var(--ink);
  font-size: .6875rem; letter-spacing: .16em; text-transform: uppercase;
  font-weight: 500;
}
.topbar__in {
  display: flex; align-items: center; justify-content: center; gap: clamp(.8rem, 3vw, 2.4rem);
  min-height: 40px; flex-wrap: wrap; padding-block: .5rem;
}
.topbar b { font-weight: 700; }
.topbar a:hover { text-decoration: underline; }
.topbar__dot { width: 3px; height: 3px; background: var(--ink); opacity: .5; border-radius: 50%; flex: none; }
@media (max-width: 760px) { .topbar__hide { display: none; } }

.hdr {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(18px) saturate(1.1);
  -webkit-backdrop-filter: blur(18px) saturate(1.1);
  border-bottom: 1px solid var(--paper-hair);
  transition: background .4s var(--ease), border-color .4s var(--ease), box-shadow .4s var(--ease);
}
.hdr.is-stuck { background: rgba(255, 255, 255, .98); border-bottom-color: var(--paper-hair-2); box-shadow: 0 12px 32px -24px rgba(5,16,28,.35); }
.hdr__in { display: flex; align-items: center; gap: 1.25rem; min-height: 78px; transition: min-height .4s var(--ease); }
.hdr.is-stuck .hdr__in { min-height: 64px; }

.brand { display: flex; align-items: center; gap: .85rem; flex: none; margin-right: auto; }
.brand__mark { width: clamp(54px, 6vw, 68px); flex: none; transition: width .4s var(--ease); }
.hdr.is-stuck .brand__mark { width: 52px; }
.brand__txt { display: flex; flex-direction: column; line-height: 1; }
.brand__name { font-family: var(--display); font-size: 1.06rem; letter-spacing: .05em; text-transform: uppercase; color: var(--ink); }
.brand__sub { font-size: .5625rem; letter-spacing: .28em; text-transform: uppercase; color: var(--blue-deep); font-weight: 600; margin-top: .3rem; }
/* keep the wordmark at phone widths — the cropped mark has no lettering in it */
@media (max-width: 470px) { .brand__sub { display: none; } .brand__name { font-size: .95rem; } }
@media (max-width: 340px) { .brand__txt { display: none; } }

.nav { display: flex; align-items: center; gap: clamp(.4rem, 1.6vw, 1.5rem); }
.nav__a {
  font-size: .7rem; font-weight: 500; letter-spacing: .17em; text-transform: uppercase;
  color: var(--paper-mid); padding: .55rem .2rem; position: relative; transition: color .3s;
}
.nav__a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: var(--blue-deep); transform: scaleX(0); transform-origin: center;
  transition: transform .45s var(--ease);
}
.nav__a:hover, .nav__a[aria-current="page"] { color: var(--ink); }
.nav__a:hover::after, .nav__a[aria-current="page"]::after { transform: scaleX(1); }
@media (max-width: 1080px) { .nav { display: none; } }

/* the header's ground is now white, so the CTA needs its own fill or a white
   "Book Online" button would sit invisibly on a white bar — blue is also the
   right colour for the single most important action on the site (Q03). */
.hdr__cta { flex: none; }
.hdr__cta .btn { --bg: var(--blue-core); --fg: var(--ink); }
.hdr__cta .btn::before { background: var(--ink); }
.hdr__cta .btn:hover { --fg: #fff; }
/* brand + "Book Online" + burger is the widest fixed row on the site. Between
   340px (where .brand__txt is still shown) and ~520px it needs more than the
   viewport, so the row tightens rather than the label being dropped — Q03 makes
   this button the one thing that must never leave the header. */
@media (max-width: 560px) {
  .hdr__in { gap: .6rem; }
  .brand { gap: .55rem; }
  .brand__mark,
  .hdr.is-stuck .brand__mark { width: 44px; }
  .hdr__cta .btn { padding: .68rem .8rem; font-size: .6rem; letter-spacing: .1em; gap: .35rem; }
  .hdr__cta .btn__arrow { display: none; }
}

.burger { display: none; width: 44px; height: 44px; flex: none; position: relative; }
.burger span {
  position: absolute; left: 11px; right: 11px; height: 1.5px; background: var(--ink);
  transition: transform .4s var(--ease), opacity .3s;
}
.burger span:nth-child(1) { top: 17px; }
.burger span:nth-child(2) { bottom: 17px; }
body.nav-open .burger span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
body.nav-open .burger span:nth-child(2) { transform: translateY(-6.5px) rotate(-45deg); }
@media (max-width: 1080px) { .burger { display: block; } }

/* mobile drawer — light to match the header it opens from */
.drawer {
  position: fixed; inset: 0; z-index: 99;
  background: var(--paper);
  padding: 7rem var(--pad) 3rem;
  display: flex; flex-direction: column; gap: .35rem;
  opacity: 0; visibility: hidden; transform: translateY(-1.5rem);
  transition: opacity .45s var(--ease), transform .55s var(--ease), visibility .45s;
  overflow-y: auto;
}
body.nav-open .drawer { opacity: 1; visibility: visible; transform: none; }
body.nav-open { overflow: hidden; }
.drawer__a {
  font-family: var(--display); font-size: clamp(1.6rem, 6.4vw, 2.5rem); text-transform: uppercase;
  color: var(--ink); padding: .3rem 0; border-bottom: 1px solid var(--paper-hair);
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
}
.drawer__a span { font-family: var(--sans); font-size: .625rem; letter-spacing: .2em; color: var(--blue-deep); font-weight: 600; }
.drawer__a:hover { color: var(--blue-deep); }
.drawer__foot { margin-top: auto; padding-top: 2.5rem; display: grid; gap: 1rem; }
.drawer__foot .btn { width: 100%; }
.drawer__meta { font-size: .78rem; color: var(--paper-mute); letter-spacing: .04em; }

/* ---------- 8. HERO ----------
   Q05: the homepage leads with a photograph of hands working on a client, so
   the crop is set on the hands, not the therapist's face.
   Q12: no ambient drift animation. The entrance runs once on load and stops. */
/* the height is deliberately short of a full viewport: the topbar and header
   take ~118px above it, and Q03 makes "Book online" the single most important
   action on the site — it must not sit below the fold on a laptop. */
.hero { position: relative; min-height: min(80svh, 780px); display: flex; align-items: flex-end; overflow: hidden; }
.hero__bg { position: absolute; inset: 0; z-index: 0; }
/* the crop keeps the hands centre-right, clear of the left-aligned headline.
   Q13: when the studio shoot lands, the replacement hero frame needs the same
   composition — hands as the subject, sitting right of centre. */
.hero__bg img {
  width: 100%; height: 100%; object-fit: cover; object-position: 58% 42%;
  filter: contrast(1.06) saturate(1.05) brightness(1.02);
  transform: scale(1.02);
}
/* the diagonal layer is what actually sits behind the headline (left-aligned,
   Q05), so it stays strong for contrast; the vertical layer only has to cover
   the badge pill (which has its own background) and the fade into the section
   below, so it is the one that got lighter. */
.hero__veil {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, rgba(5,16,28,.68) 0%, rgba(5,16,28,.20) 30%, rgba(5,16,28,.78) 76%, var(--ink) 100%),
    linear-gradient(96deg, rgba(5,16,28,.90) 0%, rgba(5,16,28,.44) 48%, transparent 80%);
}
.hero__tint {
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(140deg, rgba(0,133,202,.34), transparent 50%, rgba(51,163,224,.22));
  mix-blend-mode: soft-light;
}
.hero__in { position: relative; z-index: 5; width: 100%; padding-block: clamp(3rem, 9vh, 6.5rem) clamp(2.5rem, 5vh, 4rem); }
.hero__h1 { max-width: 16ch; font-size: clamp(2.5rem, 8vw, 7.6rem); }
.hero__h1 .ln { display: block; overflow: hidden; }
.hero__h1 .ln > span {
  display: block;
  transform: translateY(105%);
  animation: riseIn 1.05s var(--ease) forwards;
}
.hero__h1 .ln:nth-child(1) > span { animation-delay: .10s; }
.hero__h1 .ln:nth-child(2) > span { animation-delay: .20s; }
.hero__h1 .ln:nth-child(3) > span { animation-delay: .30s; }
@keyframes riseIn { to { transform: translateY(0); } }
.hero__sub {
  margin: 1.5rem 0 2.1rem; max-width: 46ch;
  opacity: 0; animation: fadeUp .9s var(--ease) .54s forwards;
}
.hero__acts { display: flex; flex-wrap: wrap; align-items: center; gap: .9rem; opacity: 0; animation: fadeUp .9s var(--ease) .68s forwards; }
@keyframes fadeUp { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }

.hero__badge {
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .45rem .95rem .45rem .6rem; margin-bottom: 1.9rem;
  background: rgba(233,236,240,.07); border: 1px solid var(--hair-2);
  border-radius: 100px; backdrop-filter: blur(8px);
  font-size: .625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: #fff;
  opacity: 0; animation: fadeUp .85s var(--ease) .08s forwards;
}
.hero__badge i { width: 6px; height: 6px; border-radius: 50%; background: var(--blue); box-shadow: 0 0 0 4px rgba(51,163,224,.20); flex: none; }

/* stat rail under hero */
.stats { border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair); background: var(--ink-2); }
.stats__in { display: grid; grid-template-columns: repeat(4, 1fr); }
.stat { padding: clamp(1.6rem, 4vw, 2.6rem) clamp(1rem, 2.5vw, 2rem); border-right: 1px solid var(--hair); }
.stat:last-child { border-right: 0; }
.stat__n { font-family: var(--display); font-size: clamp(1.9rem, 3.6vw, 3.1rem); line-height: 1; color: #fff; }
.stat__n em { font-style: normal; color: var(--blue); }
.stat__l { margin-top: .7rem; font-size: .625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--mute); line-height: 1.6; }
@media (max-width: 780px) {
  .stats__in { grid-template-columns: repeat(2, 1fr); }
  .stat:nth-child(2) { border-right: 0; }
  .stat:nth-child(-n+2) { border-bottom: 1px solid var(--hair); }
}

/* ---------- 9. PROOF STRIP (Q22 / Q23) ----------
   Q23: proof woven through, one per section. Q22: only quotes the client has
   approved. So this component takes EITHER an approved quote or a plain
   verifiable fact — never an invented testimonial. See README for the list of
   slots still waiting on Jonathan's selections. */
.proof {
  display: flex; align-items: flex-start; gap: clamp(.9rem, 2vw, 1.5rem);
  padding: clamp(1rem, 2vw, 1.35rem) clamp(1.1rem, 2.4vw, 1.7rem);
  border-left: 2px solid var(--blue-core);
  background: linear-gradient(90deg, var(--blue-soft), transparent 85%);
  font-size: .95rem; line-height: 1.6; color: var(--silver);
  max-width: 74ch;
}
.proof svg { width: 17px; height: 17px; flex: none; margin-top: .34rem; color: var(--blue); }
.proof b { color: var(--chalk); font-weight: 500; }
.proof cite { display: block; margin-top: .45rem; font-style: normal; font-size: .625rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--mute-2); }
.proof--center { margin-inline: auto; }
.sec--light .proof { color: var(--paper-mid); border-left-color: var(--blue-deep); background: linear-gradient(90deg, rgba(0,91,135,.09), transparent 85%); }
.sec--light .proof b { color: var(--ink); }
.sec--light .proof svg { color: var(--blue-deep); }
.sec--light .proof cite { color: var(--paper-mute); }

/* ---------- 10. PILLARS / PAIN GRID (Q15) ---------- */
.pillar {
  position: relative; padding: clamp(1.9rem, 3.4vw, 2.9rem) clamp(1.5rem, 2.6vw, 2.2rem);
  background: var(--ink-2); border: 1px solid var(--hair);
  overflow: hidden; isolation: isolate;
  transition: transform .6s var(--ease), border-color .6s var(--ease), background .6s var(--ease);
}
.pillar::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(170deg, rgba(0,133,202,.16), transparent 62%);
  opacity: 0; transition: opacity .6s var(--ease);
}
.pillar:hover { transform: translateY(-6px); border-color: var(--hair-2); }
.pillar:hover::before { opacity: 1; }
.pillar__n {
  font-family: var(--display); font-size: 2.6rem; line-height: 1;
  color: transparent; -webkit-text-stroke: 1px rgba(51,163,224,.55);
  margin-bottom: 1.4rem; display: block;
}
.pillar h3 { font-family: var(--display); font-size: 1.28rem; text-transform: uppercase; letter-spacing: .01em; color: #fff; margin-bottom: .8rem; line-height: 1.05; }
.pillar p { font-size: .95rem; color: var(--mute); margin: 0; line-height: 1.7; }
.pillar__link { margin-top: 1.2rem; display: inline-flex; }

/* the problem-you-solve grid (Q15): each tile is a complaint, not a service.
   v1 of this used a full-bleed generated dark poster behind each tile. Client
   feedback 2026-08-26 ("not so dark... more white... bright and cheery") and
   the two Bruce reversals in this same round both point the same way: prefer
   the site's own real content over a moody generated look. These are bright
   cards now — a small stroke icon, not artwork — and the six poster images
   this replaced are unused (kept on disk in case a future use wants them). */
.pain { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--paper-hair); border: 1px solid var(--paper-hair); }
.pain__c {
  /* the whole tile is the link, not just the line at the bottom — a 300px card
     with a 12px hit target was throwing away the click */
  color: inherit; text-decoration: none;
  position: relative; isolation: isolate;
  min-height: clamp(240px, 20vw, 280px);
  display: flex; flex-direction: column;
  padding: clamp(1.3rem, 2.4vw, 1.9rem);
  background: var(--paper-3);
  transition: background .35s var(--ease), transform .35s var(--ease);
}
.pain__c:hover { background: #fff; transform: translateY(-3px); }
.pain__ic {
  width: 30px; height: 30px; flex: none; margin-bottom: 1.1rem;
  color: var(--blue-deep);
}
.pain__c h3 { font-family: var(--display); font-size: clamp(1.15rem, 1.9vw, 1.45rem); text-transform: uppercase; color: var(--ink); line-height: 1.05; margin-bottom: .6rem; max-width: 18ch; }
.pain__c p { font-size: .88rem; color: var(--paper-mid); line-height: 1.6; margin: 0 0 1rem; max-width: 34ch; }
.pain__fix {
  margin-top: auto; padding-top: .85rem; border-top: 1px solid var(--paper-hair);
  font-size: .6875rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--blue-deep);
  display: inline-flex; align-items: center; gap: .45rem;
}
.pain__c:hover .pain__fix { color: var(--ink); }
.pain__c:focus-visible { outline: 2px solid var(--blue-core); outline-offset: -3px; }
@media (max-width: 900px) { .pain { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .pain { grid-template-columns: 1fr; } .pain__c { min-height: 250px; } }

/* ---------- 11. FULL PRICE LIST (Q04 / Q16 / Q21) ----------
   Q04: full price list, front and centre. Q16: grouped massage / recovery /
   specialty. Q21: FSA/HSA called out prominently, right next to the prices. */
.plist { display: grid; gap: clamp(1.75rem, 3.5vw, 2.75rem); }
.plist__grp { }
.plist__hd {
  display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: .8rem;
  padding-bottom: 1rem; border-bottom: 2px solid var(--hair-2);
}
.plist__hd h2,
.plist__hd h3 { font-family: var(--display); font-size: clamp(1.4rem, 3vw, 2.2rem); text-transform: uppercase; color: #fff; line-height: 1; }
.plist__hd span { font-size: .625rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--blue); }
.sec--light .plist__hd { border-bottom-color: var(--ink); }
.sec--light .plist__hd h2,
.sec--light .plist__hd h3 { color: var(--ink); }
.sec--light .plist__hd span { color: var(--blue-deep); }

.pl {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(.8rem, 2.5vw, 2.5rem); align-items: center;
  padding: clamp(.85rem, 1.5vw, 1.15rem) 0;
  border-bottom: 1px solid var(--hair);
  transition: background .4s var(--ease);
}
.sec--light .pl { border-bottom-color: var(--paper-hair); }
.pl__name { display: block; font-family: var(--display); font-size: clamp(1.1rem, 2.1vw, 1.6rem); text-transform: uppercase; line-height: 1.05; color: var(--chalk); }
.sec--light .pl__name { color: var(--ink); }
.pl a.pl__name:hover { color: var(--blue); }
.sec--light .pl a.pl__name:hover { color: var(--blue-deep); }
.pl__desc { display: block; font-size: .86rem; color: var(--mute); margin-top: .45rem; max-width: 58ch; line-height: 1.6; }
.sec--light .pl__desc { color: var(--paper-mid); }
.pl__p { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: flex-end; }
.pl__p b {
  display: inline-flex; flex-direction: column; align-items: center; gap: .1rem;
  min-width: 4.6rem; padding: .5rem .55rem;
  background: var(--ink-3); border: 1px solid var(--hair);
  font-family: var(--display); font-weight: 400; font-size: 1.12rem; color: #fff; line-height: 1;
}
.pl__p b i { font-family: var(--sans); font-style: normal; font-size: .5rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--mute-2); }
.sec--light .pl__p b { background: var(--paper-3); border-color: var(--paper-hair-2); color: var(--ink); }
.sec--light .pl__p b i { color: var(--paper-mute); }
@media (max-width: 700px) {
  .pl { grid-template-columns: 1fr; gap: 1rem; }
  .pl__p { justify-content: flex-start; }
}

/* FSA/HSA banner — Q21 asked for this to sit prominently near prices */
.fsa {
  display: flex; align-items: center; gap: clamp(1rem, 2.5vw, 1.8rem); flex-wrap: wrap;
  padding: clamp(1.2rem, 2.6vw, 1.8rem) clamp(1.3rem, 3vw, 2.2rem);
  background: var(--blue-core); color: var(--ink);
  border-radius: var(--radius);
}
.fsa svg { width: 30px; height: 30px; flex: none; color: var(--ink); }
.fsa__t { font-family: var(--display); font-size: clamp(1.15rem, 2.2vw, 1.7rem); text-transform: uppercase; line-height: 1; }
.fsa p { margin: .4rem 0 0; font-size: .9rem; line-height: 1.55; color: var(--ink); max-width: 62ch; }
.fsa__in { flex: 1 1 340px; }
/* the banner is already blue, so the button here inverts to ink on hover
   instead of filling blue-on-blue and disappearing */
.fsa .btn { --bg: #fff; --fg: var(--blue-deep); }
.fsa .btn::before { background: var(--ink); }
.fsa .btn:hover { --fg: #fff; box-shadow: 0 14px 34px -14px rgba(0,0,0,.5); }

/* ---------- 12. SERVICE CARD (services page) ---------- */
.svc {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  border: 1px solid var(--hair); background: var(--ink-2); overflow: hidden;
}
.svc:nth-child(even) .svc__media { order: 2; }
/* The media column must contribute a sane minimum height AND still stretch when
   the copy runs taller. Two traps: letting the <img> size the column (its
   intrinsic ratio inflates the row and strands dead space beside the copy), and
   `aspect-ratio` (a definite height, which kills the stretch). So the image is
   taken out of flow and a fluid min-height sets the floor. Note ::before and
   ::after on .shot are already the scrim and duotone — no spacer pseudo free. */
.svc__media { position: relative; min-height: clamp(300px, 27vw, 440px); }
.svc__media > img { position: absolute; inset: 0; }
.svc__body { padding: clamp(1.8rem, 3.4vw, 3.2rem); display: flex; flex-direction: column; }
.svc__body h2,
.svc__body h3 { font-family: var(--display); font-size: clamp(1.5rem, 2.6vw, 2.15rem); text-transform: uppercase; line-height: 1; color: #fff; margin-bottom: 1rem; }
.svc__body p { font-size: .95rem; color: var(--mute); line-height: 1.72; }
.svc__tags { display: flex; flex-wrap: wrap; gap: .4rem; margin: 0 0 1.3rem; }
.tag {
  font-size: .5625rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  padding: .34rem .65rem; border: 1px solid var(--hair-2); color: var(--blue); border-radius: 100px;
}
.prices { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 1px; margin-top: auto; background: var(--hair); border: 1px solid var(--hair); }
.price { background: var(--ink-3); padding: .95rem .6rem; text-align: center; transition: background .35s var(--ease); }
.price:hover { background: var(--ink-4); }
.price b { display: block; font-family: var(--display); font-size: 1.32rem; color: #fff; line-height: 1; }
.price span { display: block; font-size: .5625rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--mute-2); margin-top: .45rem; }
@media (max-width: 860px) {
  .svc { grid-template-columns: 1fr; }
  .svc:nth-child(even) .svc__media { order: 0; }
  .svc__media { min-height: 260px; }
}

/* compact add-on grid */
.addons { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 1px; background: var(--hair); border: 1px solid var(--hair); }
.addon { background: var(--ink-2); padding: clamp(1.2rem, 2.2vw, 1.7rem); transition: background .4s var(--ease); }
.addon:hover { background: var(--ink-3); }
.addon h4 { font-family: var(--display); font-size: 1.1rem; text-transform: uppercase; color: #fff; line-height: 1.05; margin-bottom: .5rem; }
.addon p { font-size: .82rem; color: var(--mute); margin: 0 0 .9rem; line-height: 1.6; }
.addon b { font-family: var(--display); font-size: 1.35rem; color: var(--blue); }
/* one tier per line — a service with a 30 and a 60 minute price used to run
   "$65 30 MIN $105 60 MIN" together as a single unreadable string. */
.addon__t { display: block; }
.addon__t + .addon__t { margin-top: .3rem; }
.addon i { font-style: normal; font-size: .6rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--mute-2); margin-left: .45rem; }

/* ---------- 13. RECOVERY CARDS (Q06 — the differentiator, so it leads) ---------- */
.rec { position: relative; overflow: hidden; isolation: isolate; display: flex; flex-direction: column; justify-content: flex-end; min-height: clamp(340px, 42vh, 450px); }
.rec > .shot { position: absolute; inset: 0; z-index: 0; }
/* copy sits on these photos, so the scrim has to be far heavier than the
   default .shot grade — the sauna and plunge shots are bright and busy. */
.rec > .shot::before {
  background:
    linear-gradient(180deg, rgba(5,16,28,.20) 0%, rgba(5,16,28,.42) 38%, rgba(5,16,28,.84) 78%, rgba(5,16,28,.92) 100%),
    radial-gradient(130% 100% at 50% 0%, transparent 20%, rgba(5,16,28,.42) 100%);
}
.rec__in { position: relative; z-index: 5; padding: clamp(1.6rem, 3vw, 2.4rem); }
.rec__k { font-size: .5625rem; font-weight: 600; letter-spacing: .24em; text-transform: uppercase; color: var(--blue); margin-bottom: .9rem; }
.rec h3 { font-family: var(--display); font-size: clamp(1.5rem, 2.4vw, 2.1rem); text-transform: uppercase; line-height: .95; color: #fff; margin-bottom: .8rem; }
.rec p { font-size: .88rem; color: rgba(237,239,242,.80); line-height: 1.65; margin: 0 0 1.2rem; }
.rec__price {
  display: inline-flex; align-items: baseline; gap: .45rem;
  font-family: var(--display); font-size: 1.5rem; color: #fff;
  padding-top: 1rem; border-top: 1px solid rgba(255,255,255,.18); width: 100%;
}
.rec__price small { font-family: var(--sans); font-size: .6rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--silver); }
.rec:hover .shot > img { transform: scale(1.07); }

/* feature banner (Ultimate Recovery, Youth Special) */
.feat { position: relative; overflow: hidden; isolation: isolate; border: 1px solid var(--hair-2); }
.feat__bg { position: absolute; inset: 0; z-index: 0; }
/* horizontal scrim: copy lives on the left, photograph breathes on the right */
.feat__bg.shot::before {
  background:
    linear-gradient(95deg, rgba(5,16,28,.88) 0%, rgba(5,16,28,.78) 34%, rgba(5,16,28,.32) 66%, rgba(5,16,28,.14) 100%),
    linear-gradient(180deg, rgba(5,16,28,.34), rgba(5,16,28,.20));
}
.feat__in { position: relative; z-index: 5; padding: clamp(2.2rem, 5vw, 4.5rem); max-width: 62ch; }
.feat__steps { display: grid; gap: .55rem; margin: 1.6rem 0 2rem; }
.feat__steps li { display: flex; gap: .9rem; align-items: baseline; font-size: .92rem; color: rgba(237,239,242,.86); }
.feat__steps b { font-family: var(--display); font-size: .8rem; color: var(--blue); flex: none; letter-spacing: .08em; }
.feat__tag {
  display: inline-flex; align-items: baseline; gap: .6rem; margin-bottom: 1.4rem;
  font-family: var(--display); font-size: clamp(2rem, 4vw, 3rem); color: #fff; line-height: 1;
}
.feat__tag small { font-family: var(--sans); font-size: .625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--blue); }

/* ---------- 15. FIRST VISIT WALKTHROUGH (Q27) ---------- */
.steps { display: grid; gap: 0; counter-reset: s; }
.steps__i {
  display: grid; grid-template-columns: clamp(3rem, 7vw, 5rem) minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2.2rem);
  padding: clamp(1.4rem, 3vw, 2.2rem) 0;
  border-top: 1px solid var(--hair);
}
.steps__i:last-child { border-bottom: 1px solid var(--hair); }
.sec--light .steps__i { border-top-color: var(--paper-hair); }
.sec--light .steps__i:last-child { border-bottom-color: var(--paper-hair); }
.steps__n {
  font-family: var(--display); font-size: clamp(1.6rem, 3.4vw, 2.6rem); line-height: 1;
  color: transparent; -webkit-text-stroke: 1px var(--blue);
}
.sec--light .steps__n { -webkit-text-stroke: 1px var(--blue-deep); }
@supports not (-webkit-text-stroke: 1px red) { .steps__n { color: var(--blue); } .sec--light .steps__n { color: var(--blue-deep); } }
.steps__i h3 { font-family: var(--display); font-size: clamp(1.15rem, 2.2vw, 1.6rem); text-transform: uppercase; color: #fff; line-height: 1.05; margin-bottom: .6rem; }
.sec--light .steps__i h3 { color: var(--ink); }
.steps__i p { font-size: .95rem; color: var(--mute); line-height: 1.7; margin: 0; max-width: 62ch; }
.sec--light .steps__i p { color: var(--paper-mid); }
.steps__t { display: inline-block; margin-top: .8rem; font-size: .6rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--blue); }
.sec--light .steps__t { color: var(--blue-deep); }

/* compact walkthrough — home page only. The full .steps version is on
   first-visit.html; this says the same six things in a quarter of the height. */
.mini { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1px; background: var(--hair); border: 1px solid var(--hair); }
.mini__i { background: var(--ink-2); padding: clamp(1rem, 1.8vw, 1.4rem); }
.mini__n { display: block; font-family: var(--display); font-size: 1.05rem; color: var(--blue); line-height: 1; margin-bottom: .6rem; }
.mini__i h3 { font-size: .82rem; font-weight: 500; line-height: 1.35; color: #fff; margin-bottom: .35rem; text-transform: none; font-family: var(--sans); }
.mini__i p { font-size: .76rem; color: var(--mute); line-height: 1.5; margin: 0; }
.sec--light .mini { background: var(--paper-hair); border-color: var(--paper-hair); }
.sec--light .mini__i { background: var(--paper-3); }
.sec--light .mini__n { color: var(--blue-deep); }
.sec--light .mini__i h3 { color: var(--ink); }
.sec--light .mini__i p { color: var(--paper-mid); }
@media (max-width: 1080px) { .mini { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 620px)  { .mini { grid-template-columns: repeat(2, 1fr); } }

/* ---------- 16. LIVE AVAILABILITY (Q24) ----------
   Q24: "Yes, real openings from the booker." The slots must come from the
   booking system — inventing them would put wrong times in front of clients.
   So this is the shell: point BOOK_URL / the booker's availability embed at it
   (see README, "Before going live") and the real openings render here. Until
   then it shows today's hours honestly and sends people to book online. */
.avail { border: 1px solid var(--hair); background: var(--ink-2); padding: clamp(1.5rem, 3vw, 2.4rem); }
.sec--light .avail { border-color: var(--paper-hair); background: var(--paper-3); box-shadow: var(--shadow-light); }
.avail__hd { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; margin-bottom: 1.4rem; }
.avail__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex: none; box-shadow: 0 0 0 4px rgba(51,163,224,.18); }
.avail__st { font-size: .6875rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--blue); }
.sec--light .avail__st { color: var(--blue-deep); }
.sec--light .avail__dot { background: var(--blue-deep); box-shadow: 0 0 0 4px rgba(0,91,135,.14); }
.avail__slots { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.5rem; }
.avail__slot {
  padding: .7rem 1.05rem; border: 1px solid var(--hair-2); background: var(--ink-3);
  font-size: .82rem; font-weight: 500; color: var(--chalk); letter-spacing: .04em;
  transition: border-color .35s var(--ease), background .35s var(--ease), transform .35s var(--ease);
}
.avail__slot:hover { border-color: var(--blue); background: var(--ink-4); transform: translateY(-2px); }
.sec--light .avail__slot { border-color: var(--paper-hair-2); background: var(--paper); color: var(--paper-ink); }
.sec--light .avail__slot:hover { border-color: var(--blue-core); background: #fff; }
.avail__note { font-size: .82rem; color: var(--mute); margin: 0; line-height: 1.6; }
.sec--light .avail__note { color: var(--paper-mute); }
.avail__hours { display: grid; gap: .45rem; margin-bottom: 1.5rem; }
.avail__hours div { display: flex; justify-content: space-between; gap: 1rem; font-size: .9rem; padding-bottom: .45rem; border-bottom: 1px solid var(--hair); }
.sec--light .avail__hours div { border-bottom-color: var(--paper-hair); }
.avail__hours dt { color: var(--mute); }
.sec--light .avail__hours dt { color: var(--paper-mute); }
.avail__hours dd { margin: 0; color: var(--chalk); font-weight: 500; }
.sec--light .avail__hours dd { color: var(--ink); }
.avail__hours .is-now dd { color: var(--blue); }
.sec--light .avail__hours .is-now dd { color: var(--blue-deep); }

/* ---------- 17. TEAM (Q07 equal billing / Q08 full bios) ---------- */
.person { position: relative; }
.person .shot { margin-bottom: 1.2rem; }
.person h3 { font-family: var(--display); font-size: 1.3rem; text-transform: uppercase; color: #fff; line-height: 1; }
.sec--light .person h3 { color: var(--ink); }
.person__role { font-size: .5625rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--blue); margin-top: .6rem; }
.sec--light .person__role { color: var(--blue-deep); }
.person__bio { font-size: .86rem; color: var(--mute); line-height: 1.7; margin: .95rem 0 0; }
.sec--light .person__bio { color: var(--paper-mid); }
.person__tags { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: 1rem; }

/* full bio roster card (team page) — portrait beside the bio, not above it,
   so a 4:5 crop can't balloon the card to 700px tall in a two-up grid. */
.person--full {
  display: grid; grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
  gap: clamp(1.2rem, 2.4vw, 2rem); align-items: start;
}
.person--full .shot { margin-bottom: 0; }
.person--full .person__txt { position: relative; }
.person--full h3 { font-size: clamp(1.35rem, 2.1vw, 1.8rem); padding-right: 3rem; }
.person--full .person__bio { font-size: .92rem; }
.person--full .person__n {
  position: absolute; top: -.35rem; right: 0;
  font-family: var(--display); font-size: clamp(1.9rem, 3vw, 2.6rem); line-height: 1;
  color: transparent; -webkit-text-stroke: 1px rgba(51,163,224,.5);
  pointer-events: none;
}
.sec--light .person--full .person__n { -webkit-text-stroke: 1px rgba(0,91,135,.45); }
@media (max-width: 520px) {
  .person--full { grid-template-columns: 1fr; }
  .person--full .shot { max-width: 260px; }
}

/* ---------- 18. QUOTE (Q22 — approved quotes only) ---------- */
.quote { position: relative; text-align: center; }
.quote__mark {
  font-family: var(--display); font-size: clamp(3.5rem, 9vw, 7rem); line-height: .7;
  color: transparent; -webkit-text-stroke: 1px rgba(0,133,202,.5);
  display: block; margin-bottom: .1em; user-select: none;
}
.quote blockquote {
  margin: 0; font-family: var(--sans); font-weight: 300;
  font-size: clamp(1.3rem, 3.2vw, 2.4rem); line-height: 1.38; color: #fff;
  letter-spacing: -.01em;
}
.quote blockquote em { font-style: normal; font-weight: 500; box-shadow: inset 0 -.16em 0 var(--blue-core); }
.quote__by { margin-top: 2rem; font-size: .6875rem; font-weight: 600; letter-spacing: .24em; text-transform: uppercase; color: var(--mute); }
.sec--light .quote blockquote { color: var(--ink); }
.sec--light .quote blockquote em { box-shadow: inset 0 -.16em 0 rgba(0,91,135,.4); }
.sec--light .quote__mark { -webkit-text-stroke: 1px rgba(0,91,135,.45); }
.sec--light .quote__by { color: var(--paper-mute); }
.sec--light .stars { color: var(--blue-deep); }
.stars { display: flex; justify-content: center; gap: .3rem; margin-bottom: 1.8rem; color: var(--blue); }
.stars svg { width: 15px; height: 15px; }

.qcard { padding: clamp(1.6rem, 3vw, 2.3rem); background: var(--ink-2); border: 1px solid var(--hair); display: flex; flex-direction: column; transition: transform .6s var(--ease), border-color .6s; }
.qcard:hover { transform: translateY(-5px); border-color: var(--hair-2); }
/* :not() is load-bearing. `.qcard p` is (0,1,1) and outranks `.qcard__by`
   (0,1,0) whatever the source order, so an unscoped rule silently repaints the
   attribution as 1.02rem body copy. The component was written in section 18
   long before anything used it, and this only surfaced when reviews.html
   became its first consumer. */
.qcard p:not(.qcard__by):not(.qcard__topic) { font-size: 1.02rem; font-weight: 300; line-height: 1.62; color: var(--chalk); }
/* match the hero quote's emphasis treatment — the site has no serif and no
   italic anywhere else, so a browser-default <em> reads as a foreign font. */
.qcard em { font-style: normal; font-weight: 500; box-shadow: inset 0 -.16em 0 var(--blue-core); }
.sec--light .qcard em { box-shadow: inset 0 -.16em 0 rgba(0,91,135,.4); }
.qcard__topic { display: block; margin-bottom: .9rem; font-size: .5625rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--blue); }
.qcard__by { display: block; margin-top: auto; padding-top: 1.2rem; font-style: normal; font-size: .625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--blue); }
.sec--light .qcard { background: var(--paper-3); border-color: var(--paper-hair); box-shadow: var(--shadow-light); }
.sec--light .qcard p:not(.qcard__by):not(.qcard__topic) { color: var(--paper-ink); }
.sec--light .qcard__by, .sec--light .qcard__topic { color: var(--blue-deep); }

/* ---------- 18b. RATING BAND (aggregate score + count) ---------- */
.rate { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: clamp(.9rem, 2.5vw, 2.2rem);
        padding: clamp(1.1rem, 2.4vw, 1.7rem) clamp(1.2rem, 3vw, 2.4rem);
        background: var(--paper-3); border: 1px solid var(--paper-hair); box-shadow: var(--shadow-light); }
.rate__n { font-family: var(--display); font-size: clamp(2.4rem, 6vw, 3.6rem); line-height: .9; color: var(--ink); }
.rate__n sup { font-family: var(--sans); font-size: .26em; font-weight: 600; letter-spacing: .16em; vertical-align: .9em; color: var(--paper-mute); margin-left: .25em; }
.rate__m { text-align: left; }
.rate__c { font-size: .6875rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--paper-mute); margin: .5rem 0 0; }
.rate .stars { margin-bottom: 0; justify-content: flex-start; }
@media (max-width: 560px) { .rate { justify-content: flex-start; } }

/* ---------- 19. SPLIT / EDITORIAL ---------- */
.split { display: grid; grid-template-columns: 1.05fr 1fr; gap: clamp(2rem, 5vw, 5rem); align-items: center; }
.split--rev > *:first-child { order: 2; }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } .split--rev > *:first-child { order: 0; } }
.stack-media { position: relative; }
.stack-media__b {
  position: absolute; right: -8%; bottom: -12%; width: 46%; z-index: 3;
  border: 6px solid var(--ink); box-shadow: var(--shadow);
}
.sec--light .stack-media__b { border-color: var(--paper); }
/* the -8% overhang only has somewhere to go while .split is still two columns.
   Once it collapses (900px) the media is full-width, so the offset photo would
   hang 8% past the right edge and push the document wider than the viewport —
   hence this breakpoint matches .split's, not a smaller one. */
@media (max-width: 900px) { .stack-media__b { position: static; width: 62%; margin: -18% 0 0 auto; border-width: 4px; } }

.checks { display: grid; gap: .8rem; margin: 1.6rem 0 2rem; }
.checks li { display: flex; gap: .85rem; align-items: flex-start; font-size: .95rem; color: var(--silver); line-height: 1.6; }
.checks svg { width: 16px; height: 16px; flex: none; margin-top: .3rem; color: var(--blue); }
/* .sec--light .checks svg lives in section 3b, near the rest of the light-ground overrides */

/* inline pull-quote — sans now, per Q11 */
.pullq {
  font-size: clamp(1.1rem, 1.9vw, 1.5rem); font-weight: 300; line-height: 1.45; color: #fff;
  padding-left: clamp(1rem, 2vw, 1.6rem); border-left: 2px solid var(--blue-core);
  max-width: 32ch; margin-bottom: .9rem;
}
.sec--light .pullq { color: var(--ink); border-left-color: var(--blue-deep); }
.pullq__by { font-size: .5625rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--mute); padding-left: clamp(1rem, 2vw, 1.6rem); }
.sec--light .pullq__by { color: var(--paper-mute); }

/* two-column benefit chips */
.chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.chip {
  font-size: .7rem; font-weight: 500; letter-spacing: .04em;
  padding: .5rem .95rem; border-radius: 100px;
  background: rgba(51,163,224,.09); border: 1px solid var(--hair);
  color: var(--silver); transition: all .35s var(--ease);
}
.chip:hover { background: rgba(51,163,224,.18); color: #fff; border-color: var(--hair-2); transform: translateY(-2px); }

/* ---------- 20. GIFT / CTA ---------- */
.cta { position: relative; overflow: hidden; isolation: isolate; text-align: center; }
.cta__bg { position: absolute; inset: 0; z-index: 0; }
/* full-bleed display type sits on this, so the scrim is still heavy — trimmed
   from the v2 grayscale numbers now that the photo underneath carries colour,
   but this is one of the sections that stays deliberately dark for contrast
   rather than converting to paper; see the TOKENS note on the carbon palette. */
.cta__bg.shot::before {
  background:
    linear-gradient(180deg, rgba(5,16,28,.82) 0%, rgba(5,16,28,.60) 45%, rgba(5,16,28,.84) 100%),
    radial-gradient(90% 80% at 50% 50%, rgba(5,16,28,.42) 0%, rgba(5,16,28,.78) 100%);
}
.cta__bg.shot > img { filter: contrast(1.1) saturate(1.05) brightness(.88); }
.cta__in { position: relative; z-index: 5; }
.cta__acts { display: flex; flex-wrap: wrap; gap: .9rem; justify-content: center; margin-top: 2.4rem; }
.cta__sub { margin-top: 1.6rem; font-size: .82rem; color: var(--mute); letter-spacing: .04em; }

/* ---------- 20b. GIFT CARD (built, not stock) ----------
   The source site used a generic grey clip-art gift card. This is the same
   object rendered in the brand instead: carbon stock, silver mascot, varsity
   wordmark, one blue foil edge. */
.gcard {
  position: relative; aspect-ratio: 1.586 / 1; overflow: hidden;
  background:
    radial-gradient(120% 140% at 12% 8%, #1e2229 0%, transparent 55%),
    linear-gradient(128deg, #171a1f 0%, #0b0c0e 46%, #12151b 78%, #0a0b0c 100%);
  border: 1px solid var(--hair-2);
  box-shadow: 0 40px 90px -34px rgba(0,0,0,.9), inset 0 1px 0 rgba(233,236,240,.07);
  padding: clamp(1.15rem, 3.2vw, 2.1rem);
  display: flex; flex-direction: column; justify-content: space-between;
  isolation: isolate;
}
/* engraved guilloche field */
.gcard::before {
  content: ''; position: absolute; inset: 0; z-index: 0; opacity: .5;
  background:
    repeating-linear-gradient(58deg, rgba(51,163,224,.06) 0 1px, transparent 1px 9px),
    repeating-linear-gradient(-58deg, rgba(233,236,240,.03) 0 1px, transparent 1px 13px);
}
.gcard__row { position: relative; z-index: 3; display: flex; align-items: center; gap: .85rem; }
.gcard__mark { width: clamp(38px, 5vw, 54px); }
.gcard__wm { font-family: var(--display); text-transform: uppercase; line-height: .95; }
.gcard__wm b { display: block; font-weight: 400; font-size: clamp(.95rem, 1.7vw, 1.35rem); color: #fff; letter-spacing: .04em; }
.gcard__wm span { display: block; font-family: var(--sans); font-size: .5rem; font-weight: 600; letter-spacing: .26em; color: var(--blue); margin-top: .3rem; }
.gcard__chip {
  margin-left: auto; width: clamp(30px, 4vw, 42px); aspect-ratio: 4/3; border-radius: 3px;
  background: linear-gradient(135deg, #d3d6da, #8b9096 45%, #e7e9ec 70%, #7f8389);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.35);
  position: relative;
}
.gcard__chip::before, .gcard__chip::after {
  content: ''; position: absolute; left: 12%; right: 12%; height: 1px; background: rgba(0,0,0,.3);
}
.gcard__chip::before { top: 33%; } .gcard__chip::after { top: 66%; }
.gcard__foot { position: relative; z-index: 3; }
.gcard__big {
  font-family: var(--display); text-transform: uppercase; line-height: .84;
  font-size: clamp(1.65rem, 5vw, 3.2rem);
  color: transparent; -webkit-text-stroke: 1.2px var(--silver);
}
.gcard__meta {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  margin-top: .85rem; padding-top: .8rem; border-top: 1px solid rgba(233,236,240,.14);
  font-size: .5625rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--mute);
}
.gcard__meta b { font-family: var(--display); font-weight: 400; font-size: 1.1rem; letter-spacing: .02em; color: var(--blue); }

/* ---------- 21. INFO / CONTACT ---------- */
.info { display: grid; gap: 1px; background: var(--hair); border: 1px solid var(--hair); grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
/* forced two-up, but it has to give up below 640px — the display-type phone
   number has a wide min-content and would blow the track out. */
.info--2 { grid-template-columns: 1fr 1fr; }
@media (max-width: 640px) { .info--2 { grid-template-columns: 1fr; } }
.info__c { background: var(--ink-2); padding: clamp(1.5rem, 2.8vw, 2.2rem); }
.info__k { font-size: .5625rem; font-weight: 600; letter-spacing: .24em; text-transform: uppercase; color: var(--blue); margin-bottom: 1rem; }
.info__v { font-size: 1rem; color: var(--chalk); line-height: 1.65; }
.info__v a:hover { color: var(--blue); }
.info__v b { font-family: var(--display); font-size: 1.4rem; font-weight: 400; letter-spacing: .01em; }
.hours { display: grid; gap: .5rem; }
.hours div { display: flex; justify-content: space-between; gap: 1rem; font-size: .9rem; }
.hours dt { color: var(--mute); }
.hours dd { margin: 0; color: var(--chalk); }
.hours .is-closed dd { color: var(--mute-2); }

.form { display: grid; gap: 1.1rem; }
.field { display: grid; gap: .5rem; }
.field label { font-size: .5625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--mute); }
/* background-COLOR, never the `background` shorthand: the shorthand resets
   background-image/position/size, which detonates the <select> arrow below —
   the two arrow gradients lose their 5px sizing and tile as full-box triangles. */
.field input, .field select, .field textarea {
  font-family: var(--sans); font-size: .95rem; font-weight: 300; color: var(--chalk);
  background-color: var(--ink-2); border: 1px solid var(--hair); border-radius: var(--radius);
  padding: .95rem 1.1rem; width: 100%;
  transition: border-color .35s var(--ease), background-color .35s var(--ease);
}
.field textarea { min-height: 130px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--blue); background-color: var(--ink-3); }
.field input::placeholder, .field textarea::placeholder { color: var(--mute-2); }
.field select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--mute) 50%), linear-gradient(135deg, var(--mute) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2.6rem;
}
.f-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
@media (max-width: 560px) { .f-2 { grid-template-columns: 1fr; } }
.form__note { font-size: .75rem; color: var(--mute-2); line-height: 1.6; }
.fieldset { display: grid; gap: 1.1rem; padding: 0; margin: 0; border: 0; }
.fieldset legend { padding: 0; font-family: var(--display); font-size: 1.15rem; text-transform: uppercase; color: var(--ink); margin-bottom: .2rem; }
.checkline { display: flex; gap: .7rem; align-items: flex-start; font-size: .9rem; line-height: 1.55; color: var(--paper-mid); }
.checkline input { width: 17px; height: 17px; flex: none; margin-top: .22rem; accent-color: var(--blue-core); }

.map { position: relative; border: 1px solid var(--hair); overflow: hidden; min-height: 300px; background: var(--ink-2); }
.map iframe { width: 100%; height: 100%; min-height: 300px; border: 0; filter: grayscale(1) invert(.92) contrast(.9); }
.sec--light .map { border-color: var(--paper-hair); background: var(--paper-2); }
.sec--light .map iframe { filter: grayscale(1) contrast(1.05); }

/* ---------- 22. FOOTER ---------- */
.ftr { background: var(--ink-2); border-top: 1px solid var(--hair); padding-block: clamp(3rem, 7vh, 5rem) 2rem; position: relative; }
.ftr__top { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.2fr; gap: clamp(1.8rem, 4vw, 3.5rem); }
@media (max-width: 900px) { .ftr__top { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .ftr__top { grid-template-columns: 1fr; } }
.ftr__k { font-size: .5625rem; font-weight: 600; letter-spacing: .24em; text-transform: uppercase; color: var(--blue); margin-bottom: 1.2rem; }
.ftr__l { display: grid; gap: .7rem; }
.ftr__l a { font-size: .9rem; color: var(--mute); transition: color .3s, transform .3s; display: inline-block; }
.ftr__l a:hover { color: #fff; transform: translateX(3px); }
.ftr__mark { width: 124px; display: block; margin-bottom: 1.3rem; }
.ftr__tag { font-size: 1.05rem; font-weight: 300; color: var(--silver); max-width: 26ch; }
.social { display: flex; gap: .55rem; margin-top: 1.5rem; }
.social a {
  width: 38px; height: 38px; display: grid; place-items: center;
  border: 1px solid var(--hair); border-radius: 50%; color: var(--silver);
  transition: all .4s var(--ease);
}
.social a:hover { background: var(--blue-core); color: var(--ink); border-color: var(--blue-core); transform: translateY(-3px); }
.social svg { width: 15px; height: 15px; }
.ftr__bot {
  margin-top: clamp(2.5rem, 5vw, 4rem); padding-top: 1.8rem; border-top: 1px solid var(--hair);
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; align-items: center;
  font-size: .72rem; color: var(--mute-2); letter-spacing: .04em;
}
.ftr__bot a:hover { color: var(--silver); }

/* giant watermark — Bruce, not the wordmark. His silhouette is far more
   distinctive than the type, and it bleeds off the bottom edge on purpose. */
.watermark {
  height: clamp(150px, 22vw, 360px);
  margin-top: clamp(1.5rem, 4vw, 3rem);
  margin-bottom: calc(clamp(150px, 22vw, 360px) * -0.30);
  background: currentColor;
  color: rgba(233, 236, 240, .07);
  -webkit-mask: url(../img/bruce-solid.png) no-repeat center bottom / contain;
          mask: url(../img/bruce-solid.png) no-repeat center bottom / contain;
  pointer-events: none;
}

/* ---------- 23. PAGE HEAD (inner pages) ---------- */
.phead { position: relative; overflow: hidden; padding-block: clamp(4.5rem, 13vh, 8.5rem) clamp(3rem, 8vh, 5rem); isolation: isolate; }
.phead__bg { position: absolute; inset: 0; z-index: 0; }
.phead__bg img { width: 100%; height: 100%; object-fit: cover; filter: contrast(1.08) saturate(1.05) brightness(.8); }
.phead__veil { position: absolute; inset: 0; z-index: 1; background: linear-gradient(180deg, rgba(5,16,28,.74), rgba(5,16,28,.56) 45%, var(--ink) 100%), linear-gradient(100deg, rgba(5,16,28,.74), transparent 70%); }
.phead__in { position: relative; z-index: 5; }
.crumb { display: flex; align-items: center; gap: .6rem; font-size: .625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--mute); margin-bottom: 1.6rem; }
.crumb a:hover { color: var(--blue); }
.crumb span { color: var(--blue); }
/* the page head already carries generous bottom padding — don't double it */
.phead + .sec { padding-top: clamp(2.5rem, 6vh, 4.5rem); }

/* ---------- 24. REVEAL (Q12 — the only ambient motion on the site) ---------- */
.rv { opacity: 0; transform: translateY(26px); transition: opacity 1s var(--ease), transform 1.1s var(--ease); }
.rv.in { opacity: 1; transform: none; }
.rv-d1 { transition-delay: .08s; } .rv-d2 { transition-delay: .16s; }
.rv-d3 { transition-delay: .24s; } .rv-d4 { transition-delay: .32s; }
.rv-d5 { transition-delay: .4s; }  .rv-d6 { transition-delay: .48s; }
@media (prefers-reduced-motion: reduce) {
  .rv { opacity: 1; transform: none; transition: none; }
  .hero__h1 .ln > span, .hero__sub, .hero__acts, .hero__badge { animation: none; opacity: 1; transform: none; }
}

/* ---------- 25. UTIL ---------- */
.mt-0 { margin-top: 0; } .mb-0 { margin-bottom: 0; }
.mt-1 { margin-top: 1rem; } .mt-2 { margin-top: 2rem; } .mt-3 { margin-top: 3rem; }
.tc { text-align: center; } .mx-auto { margin-inline: auto; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.max-60 { max-width: 60ch; } .max-46 { max-width: 46ch; }
.sticky-side { position: sticky; top: 110px; }
@media (max-width: 900px) { .sticky-side { position: static; } }

/* ==========================================================================
   26. BOOKING  (/book.html)
   Additive only — nothing above this line is modified.

   Ground rules inherited from this stylesheet, not invented here:
     · --blue-core is a FILL that takes --ink text. White on it is 4.03:1 and
       fails, so the selected/bookable day numeral is --ink, never white.
       This is the single most-clicked element on the page; it does not get to
       be the one thing that fails contrast.
     · Tap targets are >=44px. The calendar has to work one-thumbed at 375px,
       which is where most massage bookings actually happen.
     · Steps cross-fade in 200ms and are removed from the a11y tree when
       inactive, so a screen reader never reads four steps at once.
   ========================================================================== */

.bk { max-width: 780px; margin-inline: auto; }

/* ---- progress dots ---- */
.bk__prog { display: flex; align-items: center; justify-content: center; gap: .5rem; margin-bottom: clamp(1.6rem, 4vw, 2.6rem); }
.bk__dot {
  width: 9px; height: 9px; border-radius: 100px; flex: none;
  background: var(--paper-hair-2);
  transition: background .3s var(--ease), width .3s var(--ease);
}
.bk__dot.is-done { background: var(--blue-deep); }
.bk__dot.is-now  { background: var(--blue-core); width: 30px; }
.bk__prog span { font-size: .5625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--paper-mute); margin-left: .6rem; }

/* ---- step shell ----
   display:none rather than [hidden] alone so the cross-fade has something to
   animate from; .is-in is set on the next frame. */
.bk__step { display: none; }
.bk__step.is-active { display: block; animation: bkIn .2s var(--ease) both; }
@keyframes bkIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .bk__step.is-active { animation: none; } }

.bk__hd { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: 1.2rem; }
.bk__back {
  font-size: .5625rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--paper-mute); display: inline-flex; align-items: center; gap: .4rem;
  min-height: 44px; padding-inline: .2rem;
}
.bk__back:hover { color: var(--blue-deep); }

/* ---- the open calendar ---- */
.bk__cal { background: var(--paper-3); border: 1px solid var(--paper-hair); box-shadow: var(--shadow-light); padding: clamp(.9rem, 3vw, 1.5rem); }
.bk__month { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; }
.bk__month b { font-family: var(--display); font-size: clamp(1.3rem, 3.4vw, 1.7rem); font-weight: 400; letter-spacing: .01em; color: var(--ink); text-transform: uppercase; }
.bk__nav {
  width: 44px; height: 44px; flex: none; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--paper-hair-2); border-radius: var(--radius); background: var(--paper-3);
  color: var(--ink); transition: all .3s var(--ease);
}
.bk__nav:hover:not(:disabled) { background: var(--blue-core); border-color: var(--blue-core); color: var(--ink); }
.bk__nav:disabled { opacity: .3; cursor: not-allowed; }

/* minmax(0,1fr), never plain 1fr. `1fr` is minmax(auto,1fr) and therefore
   floors each track at the cell's min-content width — which the 44px tap
   target sets. On a 375px screen that resolved to seven 44px tracks inside a
   304px box and hung Saturday 12px outside the card. The page itself never
   scrolled sideways, so an overflow check on <html> reports clean while the
   calendar is visibly broken; minmax(0,1fr) is what makes the track able to
   shrink at all. The narrow-screen block below then buys back the width the
   cells need to stay a real 44px. */
.bk__dow, .bk__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.bk__dow { margin-bottom: 6px; }
.bk__dow span { text-align: center; font-size: .5625rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--paper-mute); padding-block: .3rem; }

.bk__day {
  aspect-ratio: 1; min-height: 44px; width: 100%;
  display: flex; align-items: center; justify-content: center;
  font-size: .95rem; font-weight: 500; font-variant-numeric: tabular-nums;
  border: 1px solid transparent; border-radius: var(--radius);
  background: var(--paper-2); color: var(--paper-mid);
  transition: transform .2s var(--ease), background .2s var(--ease), box-shadow .2s var(--ease);
}
.bk__day--pad { background: none; pointer-events: none; }
/* open day: solid brand blue, dark numeral — see contrast note at top */
.bk__day--open { background: var(--blue-core); color: var(--ink); font-weight: 600; cursor: pointer; }
.bk__day--open:hover { transform: translateY(-2px); box-shadow: 0 8px 20px -8px rgba(0,133,202,.7); }
/* Two different "not available" states, and they are not the same thing to a
   client. A past day is noise — fade it right back. A future day that is FULL
   still carries information ("that day exists, it's taken, try the next one"),
   so it stays legible: --paper-mute at full opacity clears 5:1 on --paper-2,
   where the old shared 0.45 opacity landed around 1.9:1 and could not be read. */
.bk__day--past { color: var(--paper-mute); opacity: .38; cursor: default; }
.bk__day--full { color: var(--paper-mute); cursor: not-allowed; }
.bk__day--full::after {
  content: ''; position: absolute; width: 16px; height: 1px;
  background: currentColor; opacity: .5;
}
.bk__day--full { position: relative; }
.bk__day--sel { background: var(--ink); color: var(--paper); box-shadow: 0 0 0 2px var(--blue-core); }
.bk__day--today { box-shadow: inset 0 0 0 1.5px var(--blue-deep); }
.bk__day:focus-visible { outline: 2px solid var(--blue-deep); outline-offset: 2px; }

.bk__key { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1rem; padding-top: .9rem; border-top: 1px solid var(--paper-hair); }
.bk__key span { display: inline-flex; align-items: center; gap: .45rem; font-size: .7rem; color: var(--paper-mute); font-weight: 300; }
.bk__key i { width: 13px; height: 13px; border-radius: 2px; flex: none; }

/* ---- time pills ---- */
.bk__tgroup { margin-bottom: 1.5rem; }
.bk__tgroup h3 { font-size: .5625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--paper-mute); margin-bottom: .7rem; }
.bk__times { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: .5rem; }
.bk__time {
  min-height: 48px; padding: .7rem .5rem;
  display: flex; align-items: center; justify-content: center;
  font-size: .9rem; font-weight: 500; font-variant-numeric: tabular-nums;
  background: var(--paper-3); border: 1px solid var(--paper-hair-2); border-radius: var(--radius);
  color: var(--ink); transition: all .2s var(--ease);
}
.bk__time:hover { background: var(--blue-core); border-color: var(--blue-core); color: var(--ink); transform: translateY(-2px); }
.bk__time.is-sel { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* ---- service cards ---- */
.bk__svcs { display: grid; gap: .7rem; }
.bk__group {
  font-size: .5625rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  color: var(--paper-mute); margin-top: 1.1rem; padding-bottom: .5rem;
  border-bottom: 1px solid var(--paper-hair);
}
.bk__group:first-child { margin-top: 0; }
.bk__svc {
  display: flex; align-items: center; gap: 1rem; text-align: left; width: 100%;
  padding: 1.1rem 1.2rem; min-height: 44px;
  background: var(--paper-3); border: 1px solid var(--paper-hair); border-radius: var(--radius);
  transition: all .2s var(--ease);
}
.bk__svc:hover { border-color: var(--blue-core); transform: translateY(-2px); box-shadow: var(--shadow-light); }
.bk__svc.is-sel { border-color: var(--blue-core); box-shadow: inset 0 0 0 1px var(--blue-core); }
.bk__svc__in { flex: 1; min-width: 0; }
.bk__svc h4 { font-size: 1rem; font-weight: 500; color: var(--ink); margin-bottom: .2rem; }
.bk__svc p { font-size: .8rem; color: var(--paper-mute); font-weight: 300; margin: 0; }
.bk__svc b { font-size: 1rem; font-weight: 600; color: var(--blue-deep); flex: none; font-variant-numeric: tabular-nums; }
.bk__svc--ask { border-style: dashed; border-color: var(--paper-hair-2); background: rgba(0,91,135,.04); }
.bk__svc--ask h4 { color: var(--blue-deep); }

/* ---- pain intake ---- */
.bk__pain { margin-top: 1.2rem; padding: clamp(1rem,3vw,1.4rem); background: var(--paper-3); border: 1px solid var(--paper-hair); }
.bk__pain > p:first-child { font-size: .85rem; color: var(--paper-mid); font-weight: 300; margin-bottom: 1.1rem; }
.bk__chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.bk__chip {
  font-size: .8rem; font-weight: 500; padding: .7rem 1.1rem; min-height: 44px;
  display: inline-flex; align-items: center; border-radius: 100px;
  background: rgba(0,91,135,.07); border: 1px solid var(--paper-hair); color: var(--paper-mid);
  transition: all .2s var(--ease);
}
.bk__chip:hover { background: rgba(0,91,135,.14); color: var(--ink); }
.bk__chip.is-on { background: var(--blue-core); border-color: var(--blue-core); color: var(--ink); font-weight: 600; }
.bk__count { font-size: .7rem; color: var(--paper-mute); text-align: right; margin-top: .3rem; font-variant-numeric: tabular-nums; }

/* ---- recap + confirm ---- */
.bk__recap { background: var(--paper-3); border: 1px solid var(--paper-hair); border-left: 3px solid var(--blue-core); padding: 1.1rem 1.3rem; margin-bottom: 1.4rem; }
.bk__recap dl { display: grid; gap: .55rem; margin: 0; }
.bk__recap dl > div { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; }
.bk__recap dt { font-size: .5625rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--paper-mute); }
.bk__recap dd { margin: 0; font-size: .95rem; color: var(--ink); font-weight: 500; text-align: right; }

.bk__err { background: rgba(178,42,42,.07); border: 1px solid rgba(178,42,42,.3); color: #8c1f1f; padding: .9rem 1.1rem; font-size: .85rem; margin-bottom: 1rem; }
.bk__note { font-size: .78rem; color: var(--paper-mute); font-weight: 300; line-height: 1.6; }

.bk__spin { display: inline-block; width: 15px; height: 15px; border: 2px solid rgba(255,255,255,.3); border-top-color: currentColor; border-radius: 50%; animation: bkSpin .7s linear infinite; }
@keyframes bkSpin { to { transform: rotate(360deg); } }
.bk__load { text-align: center; padding: 2.5rem 1rem; color: var(--paper-mute); font-size: .85rem; }

/* ---- success ---- */
.bk__ok { text-align: center; padding: clamp(1.5rem,4vw,2.5rem) 0; }
.bk__ok__mark { width: 62px; height: 62px; margin: 0 auto 1.2rem; border-radius: 50%; background: var(--blue-core); color: var(--ink); display: flex; align-items: center; justify-content: center; }

@media (max-width: 420px) {
  /* Seven 44px cells + gaps need more than the 335px a 375px screen leaves
     inside .wrap, so the card reclaims half the page gutter. It still stops
     10px short of the viewport edge, and it reads as a deliberate near-bleed
     calendar rather than a cramped one. */
  .bk__cal { margin-inline: calc(var(--pad) * -.5); padding: .7rem; }
  .bk__dow, .bk__grid { gap: 3px; }
  .bk__day { font-size: .85rem; }
  .bk__times { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
  .bk__svc { padding: .95rem 1rem; gap: .7rem; }
  .bk__recap dl > div { flex-direction: column; gap: .1rem; }
  .bk__recap dd { text-align: left; }
}

/* ==========================================================================
   27. BRUCE + THE WALL (mascot section, video testimonials, photo wall)
   Added 2026-08-31 for Jonathan's two asks: a testimonial spot, and a section
   about Bruce.

   Ground: light. The 2026-08-26 client note ("more white, more blue, less
   black... bright and cheery") means Bruce does NOT get a dark plate even
   though his artwork was drawn for one. Instead `.brucesec` is a blue-washed
   paper plate so the section still separates from the light sections either
   side of it without reaching for carbon.
   ========================================================================== */

/* ---------- 27a. THE BRUCE PLATE ---------- */
.brucesec { position: relative; overflow: hidden; isolation: isolate; }
/* the wash. two stops only — a flat tint reads as a printing error next to
   --paper, and anything stronger than .10 alpha starts competing with the
   photography further down the page. */
.brucesec::before {
  content: ''; position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(120% 90% at 88% 8%, rgba(0,133,202,.13), transparent 62%),
    linear-gradient(180deg, rgba(0,133,202,.07), rgba(0,133,202,.02) 70%, transparent);
}
/* bruce-solid.png is the tintable full-body silhouette (white ink, real alpha),
   so it is masked rather than painted — the same trick the footer watermark
   uses. Painting it directly would put white-on-white on this ground. */
.brucesec__ghost {
  position: absolute; z-index: -1; pointer-events: none;
  right: -6%; bottom: -14%; width: clamp(280px, 34vw, 560px); aspect-ratio: 900 / 1666;
  background: var(--blue-core); opacity: .07;
  -webkit-mask: url('../img/bruce-solid.png') no-repeat center / contain;
          mask: url('../img/bruce-solid.png') no-repeat center / contain;
}
@media (max-width: 900px) { .brucesec__ghost { right: -18%; bottom: -8%; width: 62vw; opacity: .05; } }
/* 900px is where .split collapses to one column. Bruce is the whole point of
   the section, and in source order he follows ~250 words of copy — which on a
   phone means two full screens before the mascot appears. Pull him up. */
@media (max-width: 900px) { .brucesec .split > .bruce-first { order: -1; margin-bottom: clamp(1rem, 3vw, 2rem); } }
@media (max-width: 900px) { .bruce-portrait { max-width: 420px; margin-inline: auto; } }

/* the portrait. bruce-mark.png is fixed-palette art with true alpha, so it sits
   straight on the plate with no card behind it — a bounding box would fight the
   die-cut silhouette that makes him read as a character. */
.bruce-portrait { position: relative; display: block; container-type: inline-size; }
.bruce-portrait__art { position: relative; display: block; }
.bruce-portrait .bruce { width: 100%; }
.bruce-portrait .bruce-mark { filter: drop-shadow(0 26px 44px rgba(5,16,28,.22)); }
/* the halo behind him — keeps his grey body from dissolving into --paper.
   It hangs off __art, not .bruce-portrait, so adding the name plate below
   cannot drag the highlight off his chest. */
.bruce-portrait__art::before {
  content: ''; position: absolute; z-index: -1; left: 50%; top: 46%;
  width: 96%; aspect-ratio: 1; transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(255,255,255,.95) 30%, rgba(255,255,255,0) 68%);
}
/* his name, set as a plate under him. sized in cqi against .bruce-portrait so
   it tracks the artwork it labels rather than the viewport — at 900px the
   split is still two columns, and a vw-sized name would overrun the artwork.
   The clamp floor/ceiling are the fallback anywhere cqi is unsupported. */
.bruce-name {
  font-family: var(--display); font-size: clamp(2.6rem, 12cqi, 6rem); line-height: .82;
  text-transform: uppercase; text-align: center; color: var(--ink);
  /* bruce-mark.png is a bust crop — his tee runs to the file's bottom edge, so
     any negative margin here puts near-black display type on a black shirt. */
  margin-top: .1em; letter-spacing: .01em;
}
.bruce-name span { display: block; font-family: var(--sans); font-size: .1875em; font-weight: 600; letter-spacing: .24em; color: var(--blue-deep); margin-top: .9em; line-height: 1.5; }

/* ---------- 27b. BRUCE'S SPEC SHEET ----------
   A joke with a straight face: real studio facts in a deadpan spec table. It is
   labelled as Bruce's, so nothing here reads as a business claim — but every
   row still has to be true, which is why "Shift" is the studio's real hours
   token and not a punchline. */
.spec { border-top: 1px solid var(--paper-hair); margin-top: 1.6rem; }
.spec__r {
  display: grid; grid-template-columns: minmax(0, 8.5rem) minmax(0, 1fr);
  gap: 1rem; padding: .8rem 0; border-bottom: 1px solid var(--paper-hair);
  align-items: baseline;
}
.spec__k { font-size: .5625rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: var(--blue-deep); }
.spec__v { font-size: .92rem; color: var(--paper-mid); line-height: 1.55; }
.spec__v b { color: var(--ink); font-weight: 500; }
@media (max-width: 460px) {
  .spec__r { grid-template-columns: 1fr; gap: .25rem; }
}

/* ---------- 27c. BRUCE'S RULES ----------
   Numbered house rules in his voice. Every rule is lifted from first-visit.html
   — this component is a friendlier restatement of policy, never new policy. */
.rules { display: grid; gap: 1px; background: var(--paper-hair); border: 1px solid var(--paper-hair); margin-top: 1.8rem; }
.rules__i { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 1.1rem; align-items: start; padding: 1.15rem clamp(1rem, 2vw, 1.5rem); background: var(--paper-3); }
.rules__n {
  font-family: var(--display); font-size: 1.5rem; line-height: 1; color: transparent;
  -webkit-text-stroke: 1px rgba(0,91,135,.6); min-width: 1.4ch;
}
@supports not (-webkit-text-stroke: 1px red) { .rules__n { color: var(--blue-deep); } }
.rules__i h4 { font-family: var(--sans); font-size: .95rem; font-weight: 600; color: var(--ink); line-height: 1.35; }
.rules__i p { font-size: .86rem; color: var(--paper-mid); line-height: 1.65; margin: .4rem 0 0; }

/* ---------- 27d. VIDEO TESTIMONIALS ----------
   Real client video. `.vcard` is deliberately NOT autoplay — these are people
   talking, and a grid of six voices firing at once is a bad experience and a
   bandwidth bill. `preload="metadata"` means the poster frame paints and the
   bytes wait for a click.

   Aspect is set per card from the file's own dimensions (see video_dims in
   build-pages.py) so a landscape testimonial cannot be letterboxed into a
   portrait box, and nothing shifts while the metadata loads. */
/* align-items:start matters. Cards default to stretching to the tallest item
   in their row, so one landscape clip beside a portrait one leaves a column
   of empty card background below the short card's caption. */
.vwall { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: clamp(1rem, 2vw, 1.6rem); align-items: start; }
.vcard { position: relative; display: flex; flex-direction: column; background: var(--ink); border: 1px solid var(--paper-hair); overflow: hidden; }
.vcard__v { position: relative; display: block; width: 100%; height: auto; background: var(--ink); aspect-ratio: var(--ar, 9 / 16); object-fit: cover; }
.vcard__cap { padding: .95rem 1.05rem 1.05rem; background: var(--paper-3); border-top: 1px solid var(--paper-hair); }
.vcard__who { font-family: var(--display); font-size: 1.05rem; text-transform: uppercase; color: var(--ink); line-height: 1.05; }
.vcard__what { margin-top: .45rem; font-size: .6875rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--blue-deep); }
.vcard__note { margin-top: .6rem; font-size: .84rem; color: var(--paper-mid); line-height: 1.6; }
/* carbon variant, in case the wall is ever moved to a dark section */
.sec:not(.sec--light) .vcard { background: var(--ink-2); border-color: var(--hair); }
.sec:not(.sec--light) .vcard__cap { background: var(--ink-2); border-top-color: var(--hair); }
.sec:not(.sec--light) .vcard__who { color: #fff; }
.sec:not(.sec--light) .vcard__what { color: var(--blue); }
.sec:not(.sec--light) .vcard__note { color: var(--silver); }

/* ---------- 27e. THE PHOTO WALL ----------
   The physical Bruce board in the studio, and the people who pose with it.
   Two sizes: `.wall__i--wide` spans two columns. The grid is plain CSS grid
   with a fixed row height rather than masonry — masonry would reflow as each
   image decodes, and this block sits above the fold on a short page. */
/* grid-auto-rows is what makes this a mosaic rather than a column of very
   tall portraits: without it each frame takes its source image's own height,
   --tall has nothing to be twice as tall as, and phone photos render a
   metre high. Fixed rows also mean nothing reflows as each image decodes. */
.wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
        grid-auto-rows: clamp(140px, 13vw, 200px); gap: clamp(.6rem, 1.2vw, 1rem); }
.wall__i { position: relative; overflow: hidden; background: var(--paper-2); height: 100%; }
.wall__i img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease); }
.wall__i:hover img { transform: scale(1.045); }
.wall__i--tall { grid-row: span 2; }
/* Two columns AND two rows. --wide alone is 2.7:1, which halves a 4:3 event
   photograph; this lands at ~1.3:1, which is what the camera actually shot. */
.wall__i--big { grid-column: span 2; grid-row: span 2; }
/* Below ~560px the grid is a single column, so a tile is the full 335px wide
   while grid-auto-rows is still clamped to its 140px floor — a 2.4:1 letterbox
   out of photographs that were shot at 4:3, which cuts heads off group shots.
   One column wants a row height derived from that column, and no spans. */
@media (max-width: 560px) {
  .wall { grid-auto-rows: clamp(150px, 67vw, 300px); }
  .wall__i--big, .wall__i--tall { grid-row: span 1; }
  .wall__i--big { grid-column: span 1; }
}
.wall__i--wide { grid-column: span 2; }
@media (max-width: 520px) { .wall__i--wide { grid-column: span 1; } }
/* the caption sits inside the frame so a wall of portraits still reads as one
   surface rather than a list of captioned cards */
.wall__cap {
  position: absolute; inset: auto 0 0 0; padding: 2.6rem .9rem .8rem;
  font-size: .625rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: #fff;
  background: linear-gradient(180deg, transparent, rgba(5,16,28,.82));
}

/* ---------- 27f. LEAVE A REVIEW ----------
   Mirrors .fsa's shape (icon / copy / action) so the page closes on a component
   the rest of the site already taught the visitor to read. */
.leave { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: clamp(1rem, 2.4vw, 2rem); align-items: center;
  padding: clamp(1.3rem, 2.6vw, 2rem); background: var(--paper-3); border: 1px solid var(--paper-hair); box-shadow: var(--shadow-light); }
.leave > svg { width: 30px; height: 30px; flex: none; color: var(--blue-deep); }
.leave__t { font-family: var(--display); font-size: clamp(1.2rem, 2.4vw, 1.7rem); text-transform: uppercase; color: var(--ink); line-height: 1; }
/* :not() matters here — `.leave p` (0-1-1) outranks `.leave__t` (0-1-0), so an
   unscoped rule silently repaints the title as body copy however it is
   ordered. Specificity, not source order, is what decides it. */
.leave p:not(.leave__t) { font-size: .88rem; color: var(--paper-mid); line-height: 1.65; margin: .55rem 0 0; }
@media (max-width: 760px) {
  .leave { grid-template-columns: auto minmax(0, 1fr); }
  .leave .btn { grid-column: 1 / -1; justify-self: start; }
}

/* ---------- 28. IMAGE-LESS SERVICE PLATE ----------
   Used when a treatment has no honest photograph yet (Q13 is still open). The
   plate has to look chosen, not broken: it borrows .brucesec's blue wash so it
   reads as part of the system, and sets the treatment name in the display face
   at the same scale the photo cards give their subject. Swapping a real photo
   in later is a one-argument change in build-pages.py — no CSS involved. */
.svc__plate {
  position: relative; display: grid; place-items: center; overflow: hidden;
  padding: clamp(1.4rem, 3vw, 2.4rem); min-height: 100%;
  background:
    radial-gradient(120% 100% at 78% 12%, rgba(0,133,202,.20), transparent 64%),
    linear-gradient(160deg, var(--paper-2), var(--paper-3));
  border-bottom: 2px solid var(--blue-core);
}
.svc__plate span {
  font-family: var(--display); font-size: clamp(1.6rem, 4.2cqi, 3.1rem); line-height: .92;
  text-transform: uppercase; text-align: center; color: var(--ink);
  /* the accent underline the rest of the site uses for emphasis */
  box-shadow: inset 0 -.12em 0 rgba(0,133,202,.35);
}
.svc { container-type: inline-size; }
/* on carbon the plate inverts rather than punching a light hole in the section */
.sec:not(.sec--light) .svc__plate {
  background:
    radial-gradient(120% 100% at 78% 12%, rgba(0,133,202,.26), transparent 64%),
    linear-gradient(160deg, var(--ink-3), var(--ink-2));
}
.sec:not(.sec--light) .svc__plate span { color: #fff; }

/* ---------- 29. BRUCE FILM ----------
   The explainer sits where his portrait was, and its poster is the video's own
   first frame — so the section looks exactly as it did until someone presses
   play. No autoplay: it has a voiceover, and a page that starts talking at you
   is a page people close. preload="none", not "metadata": metadata still
   pulled ~85 KB of mp4 on every homepage load, and the poster already shows
   the exact frame the video opens on, so there is nothing to gain for it.

   Captions are deliberately NOT burned into the video. Sterling names every
   treatment aloud, and the full price list is real text further up this same
   page — text in HTML is selectable, indexable, screen-reader readable and
   renders at any resolution, none of which is true of type baked into 720p. */
.bruce-film { position: relative; display: block; }
.bruce-film__v {
  display: block; width: 100%; height: auto; aspect-ratio: 71 / 40;
  background: var(--paper-2); border: 1px solid var(--paper-hair);
  box-shadow: var(--shadow-light);
}
.bruce-film__cap {
  margin-top: .7rem; text-align: center;
  font-size: .625rem; font-weight: 600; letter-spacing: .2em;
  text-transform: uppercase; color: var(--blue-deep);
}
