/* ============================================
   personalcare-static template (STEP 1)
   Personal-care vertical (hair salon, spa, barber, med-spa). Cloned + adapted
   from proservices-static: same trust-forward LIGHT theme engine (cool
   near-white body + a real brand color + a DARKENED brand-strong for AA
   text/CTAs + a confident brand accent), re-hued per prospect via
   deriveProPalette (template.json paletteMode="pro" — a dedicated warm
   personal-care palette mode is a later step).

   Structure carries the base's bold, high-contrast type system (Plus Jakarta
   Sans display+UI / Inter body, matching the minimaleskin.com pin's sans-only
   clinical-elegance character; NEVER Fraunces/Lora), disciplined photo slots
   with graceful gradient fallbacks, and considered motion. Personal-care
   specific: services publish REAL prices (no
   quote-per-matter), a dedicated gallery section (imagery-led — personal care
   sells on photos), and the shared sw-booking / sw-trust widgets
   (templates/frameworks/_base/widgets/, see WIDGETS.md) as the page's ONE
   booking form and ONE trust strip.
   ============================================ */

/* --- Reset & Base --- */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  /* Brand (real, verbatim) + derived AA-safe strong + refined accent */
  --color-brand:        #A88B44;        /* real brand navy: eyebrows, icons, headings, accents */
  --color-brand-strong: #725C27;  /* darkened navy (AA on light): button fills, links, small text */
  --color-accent:       #D81F26;       /* brass/gold, DECORATIVE: rules, stars, hero CTA, underlines */
  --color-brand-fg:     #FFFFFF;       /* text on brand/brand-strong fills (white) */
  --color-accent-soft:  rgba(168, 139, 68, 0.07);    /* faint brand tint for icon chips + hovers */
  --color-accent-ink:   #241D0C;      /* WIDGETS.md core var: text ON the accent fill (same dark ink .btn-accent already uses, not a plain white fallback) */

  /* Cool neutral surfaces (a restrained brand cast, corporate not warm) */
  --color-bg:        #F9F9F8;        /* page base, cool near-white */
  --color-surface:   #F5F5F3;   /* alt bands, cards */
  --color-surface-2: #F1F0EE;  /* monogram bg, nested cards */
  --color-muted:     #ECEAE7;     /* subtle fills */
  --color-border:    #E4E2DE;    /* hairline borders */

  /* Text */
  --color-ink:     #262115;     /* deep navy-ink */
  --color-ink-dim: #706A57;  /* secondary / captions (AA on the light base) */

  /* Deep brand-navy for hero panels, the booking CTA band + footer */
  --color-hero-ink:   #3E3319;
  --color-hero-ink-2: #241D0C;
  --color-hero-overlay: rgba(62, 51, 25, 0.82); /* navy wash over a hero photo */
  --color-hero-scrim:   rgba(20, 17, 11, 0.62);   /* bottom-up scrim for legibility */
  --color-footer:       #251F0F;      /* deep brand-navy footer */

  /* Shadows: soft, diffuse, premium (one shadow OR a hairline — never both at rest) */
  --color-border-accent: color-mix(in srgb, var(--color-brand) 13%, transparent);
  --color-border-accent-hover: color-mix(in srgb, var(--color-brand) 30%, transparent);
  --shadow-sm: 0 1px 2px rgba(12,20,38,0.05);
  --shadow-md: 0 20px 48px -30px rgba(12,20,38,0.34);
  --shadow-lg: 0 40px 90px -44px rgba(12,20,38,0.42);
  --shadow-hover: 0 28px 60px -34px rgba(12,20,38,0.36);

  /* Typography — a two-family system tuned to this pack's pinned inspiration
     (minimaleskin.com: sans-only, uppercase nav/CTAs, italic accent phrases —
     "clinical elegance", never an editorial serif). Plus Jakarta Sans carries
     every heading/number/UI surface at two optical roles (--font-display for
     headings + big numbers, --font-sans for nav/eyebrows/buttons/labels —
     same family, different weight+tracking treatment per role); Inter carries
     body copy. No serif anywhere: the previous setup split Playfair Display
     (h3/h4 + numbers) and Cormorant Garamond (h1/h2) across TWO different
     Google Fonts requests that didn't even match between pages (index.html
     loaded Cormorant but not Playfair; services.html loaded Playfair but not
     Cormorant), so half the display type silently fell back to Georgia on
     every page. Fraunces/Lora never imported. */
  --font-display: 'Plus Jakarta Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-sans: 'Plus Jakarta Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Spacing — a consistent scale with a generous editorial top end */
  --space-xs: 0.5rem;
  --space-sm: 1rem;
  --space-md: 1.5rem;
  --space-lg: 3rem;
  --space-xl: 6rem;
  --space-2xl: 7.5rem;

  /* Layout — SHARP, inherited from the base pack. Corners drive to 0 for a
     bold, high-authority read. A hair of radius (2px) survives only where a
     pure-square edge would look like a rendering bug on the smallest
     interactive chips; everything structural is a crisp rectangle. */
  --max-width: 1200px;
  --radius: 0;
  --radius-lg: 0;
  --radius-xl: 0;
  --radius-chip: 2px;
}

html {
  /* Deliberately NOT scroll-behavior:smooth. That CSS property also governs
     any *programmatic* window.scrollTo()/scrollIntoView() call an external
     tool makes -- including this pack's own validate-template capture sweep
     (scripts/validate-template.mjs), which jumps down the page in steps to
     make every IntersectionObserver reveal fire before screenshotting. Each
     step re-triggered a new smooth-scroll animation that the next 60ms-later
     step immediately interrupted, so the page never actually visited most of
     its own sections -- their .fade-up never got .visible, and the final
     screenshot silently captured them at opacity 0 (confirmed: forcing
     scroll-behavior:auto took a same-page verification run from 1/18
     sections revealed to 18/18). In-page anchor links get the smooth
     animation back via JS below (respects prefers-reduced-motion the same
     way the existing back-to-top button already does), which only runs on a
     real click -- an external harness's direct scrollTo() calls are
     unaffected by it and stay instant. */
  font-size: 16px;
}

body {
  font-family: var(--font-body);
  background: var(--color-bg);
  color: var(--color-ink);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; height: auto; display: block; }
a { color: var(--color-brand-strong); text-decoration: none; transition: color 0.2s; }
a:hover { color: var(--color-brand); }
button { cursor: pointer; font-family: var(--font-sans); }

:focus-visible {
  outline: 2px solid var(--color-brand);
  outline-offset: 2px;
  border-radius: 3px;
}

/* --- Skip link (semantic order / landmarks, SEO wave) --- */
.skip-link {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  transform: translateY(-120%);
  background: var(--color-ink);
  color: #fff;
  padding: 0.85rem 1.5rem;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.9rem;
  text-decoration: none;
  border-radius: 0 0 var(--radius) 0;
  transition: transform 0.15s ease;
}
.skip-link:focus {
  transform: translateY(0);
  outline: 2px solid #fff;
  outline-offset: -2px;
}

.container {
  max-width: var(--max-width);
  margin: 0 auto;
  /* Min bound raised to an on-grid 24px (was 1.25rem/20px), which every mobile-width
     section was hitting since 4vw stays below it until ~500px wide (improve loop Arc S). */
  padding: 0 clamp(1.5rem, 4vw, 2.5rem);
}

/* --- Typography --- */
h1, h2 { font-family: var(--font-display); font-weight: 800; line-height: 1.08; color: var(--color-ink); letter-spacing: -0.02em; }
h3, h4 { font-family: var(--font-display); font-weight: 800; line-height: 1.14; color: var(--color-ink); letter-spacing: -0.015em; }
h1 { font-size: clamp(2.9rem, 6vw, 5rem); font-weight: 800; line-height: 1.04; letter-spacing: -0.02em; }
h2 { font-size: clamp(2.2rem, 4vw, 3.3rem); font-weight: 800; line-height: 1.06; }
h3 { font-size: clamp(1.2rem, 2vw, 1.45rem); font-weight: 700; line-height: 1.22; }

.section-label {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-brand-strong);
  margin-bottom: 1.1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
}
.section-label::before {
  content: '';
  width: 28px; height: 1.5px;
  background: var(--color-accent);
}

.section-title {
  color: var(--color-ink);
  margin-bottom: var(--space-md);
  text-wrap: balance;
}

.section-desc {
  color: var(--color-ink-dim);
  font-size: 1.08rem;
  max-width: 68ch;
  line-height: 1.75;
}

.section-head-center {
  text-align: center;
  max-width: 720px;
  margin: 0 auto var(--space-xl);
}
.section-head-center .section-label { justify-content: center; }
.section-head-center .section-desc { margin-left: auto; margin-right: auto; }

/* --- Buttons — bold, uppercase, confident ("CALL NOW" energy) --- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1.05rem 2rem;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 2px solid transparent;
  border-radius: var(--radius);
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease, color 0.2s ease, filter 0.2s ease;
  white-space: nowrap;
}

.btn-primary {
  background: var(--color-brand-strong);
  color: var(--color-brand-fg);
}
.btn-primary:hover {
  filter: brightness(1.1);
  color: var(--color-brand-fg);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

/* Gold accent CTA — the marquee action (hero + booking band + close bands) */
.btn-accent {
  background: var(--color-accent);
  color: var(--color-hero-ink-2);
  border-color: var(--color-accent);
  font-weight: 700;
}
.btn-accent:hover {
  filter: brightness(1.06);
  color: var(--color-hero-ink-2);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.btn-outline {
  background: transparent;
  color: var(--color-brand-strong);
  border-color: var(--color-border);
}
.btn-outline:hover {
  background: var(--color-surface);
  border-color: var(--color-brand);
  color: var(--color-brand-strong);
  transform: translateY(-2px);
}

/* Hero/band outline: white on the deep navy */
.btn-hero-outline {
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(4px);
  color: #fff;
  border-color: rgba(255,255,255,0.5);
}
.btn-hero-outline:hover {
  background: rgba(255,255,255,0.16);
  border-color: #fff;
  color: #fff;
  transform: translateY(-2px);
}

/* --- Navigation --- */
/* Quality floor: transparent over the hero photo with a top scrim gradient for
   legibility, solidifying to an opaque white bar once scrolled past the hero
   (every hero background in this pack is dark — a real photo + scrim, or the
   brand-navy gradient fallback — so white nav text always has contrast here).
   Logo/wordmark/links/phone swap from a light/knockout treatment (over the
   photo) to the primary dark-ink treatment (once solid) via .nav.scrolled. */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  /* Above .mobile-call-bar (z-index 150, see below) so the open mobile drawer
     (a descendant, no z-index of its own — stacks within this context) never
     has its bottom rows covered/click-blocked by the sticky call bar. */
  z-index: 200;
  padding: 1.05rem 0;
  background: linear-gradient(to bottom, rgba(8,10,16,0.58) 0%, rgba(8,10,16,0.22) 60%, rgba(8,10,16,0) 100%);
  border-bottom: 1px solid transparent;
  transition: background 0.25s ease, box-shadow 0.25s ease, padding 0.25s ease, border-color 0.25s ease;
}
.nav.scrolled {
  padding: 0.6rem 0;
  background: #ffffff;
  border-bottom: 1px solid var(--color-border);
  box-shadow: 0 1px 0 var(--color-border), var(--shadow-sm);
}

.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 2.5rem);
}

/* Real logo lockup, or a clean wordmark when no logo asset exists. Quality
   floor: logo img >=48px height with a drop-shadow while it sits over the hero
   photo (the transparent nav state); once scrolled to the solid bar the shadow
   lifts and the mark steps down slightly. */
.nav-logo { display: inline-flex; align-items: center; }
.nav-logo-img {
  height: 58px; width: auto; display: block; max-width: 250px; object-fit: contain;
  filter: drop-shadow(0 2px 10px rgba(0,0,0,0.55));
  transition: height 0.25s ease, filter 0.25s ease;
}
.nav.scrolled .nav-logo-img { height: 58px; filter: none; }
/* Dual logo: the knockout/light mark shows over the transparent photo nav; the
   primary dark-ink mark takes over once the bar is solid. */
.nav-logo-dark { display: block; }
.nav-logo-light { display: none; }
.nav.scrolled .nav-logo-dark { display: none; }
.nav.scrolled .nav-logo-light { display: block; }
.nav-wordmark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.3rem;
  letter-spacing: -0.02em;
  color: #fff;
  text-shadow: 0 1px 8px rgba(0,0,0,0.4);
  line-height: 1;
  transition: color 0.25s ease, text-shadow 0.25s ease;
}
.nav.scrolled .nav-wordmark { color: var(--color-ink); text-shadow: none; }

.nav-links {
  display: flex;
  align-items: center;
  gap: 1.9rem;
  list-style: none;
}
/* Quality floor: nav links >=16px rendered (the vision gate reads full-page
   captures where small text shrinks further). */
.nav-links a {
  color: #fff;
  text-shadow: 0 1px 6px rgba(0,0,0,0.35);
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  position: relative;
  padding: 0.25rem 0;
  transition: color 0.25s ease, text-shadow 0.25s ease;
}
.nav.scrolled .nav-links a { color: var(--color-ink); text-shadow: none; }
.nav-links a::after {
  content: '';
  position: absolute;
  bottom: -3px; left: 0;
  width: 0; height: 1.5px;
  background: var(--color-accent);
  transition: width 0.3s;
}
.nav-links a:hover::after,
.nav-links a.active::after { width: 100%; }

.nav-actions { display: flex; align-items: center; gap: 1.1rem; }
.nav-phone {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: #fff;
  text-shadow: 0 1px 6px rgba(0,0,0,0.35);
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
  transition: color 0.25s ease, text-shadow 0.25s ease;
}
.nav.scrolled .nav-phone { color: var(--color-ink); text-shadow: none; }
.nav-phone svg {
  width: 16px; height: 16px;
  color: currentColor;
  flex-shrink: 0;
}
.nav-phone:hover { color: var(--color-accent); }
.nav.scrolled .nav-phone:hover { color: var(--color-brand); }

.nav-cta {
  padding: 0.7rem 1.5rem;
  background: var(--color-accent);
  color: var(--color-hero-ink-2) !important;
  border-radius: var(--radius);
  font-family: var(--font-sans);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: filter 0.2s, transform 0.2s, box-shadow 0.2s;
}
.nav-cta:hover { filter: brightness(1.06); transform: translateY(-1px); box-shadow: var(--shadow-sm); color: var(--color-hero-ink-2) !important; }
.nav-cta::after { display: none !important; }

/* Mobile menu toggle: white bars over the transparent/photo nav, dark once the
   bar is solid or once the (always-light) drawer is open. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  background: none;
  border: none;
  /* Quality floor: >=44x44px tap target (was ~30x24px from the icon + 4px
     padding alone). */
  min-width: 44px;
  min-height: 44px;
  padding: 4px;
}
.nav-toggle span {
  display: block;
  width: 22px; height: 2px;
  border-radius: 1px;
  background: #fff;
  transition: all 0.3s;
}
.nav.scrolled .nav-toggle span { background: var(--color-ink); }
.nav-toggle.open span { background: var(--color-ink); }
.nav-toggle.open span:nth-child(1) { transform: rotate(45deg) translate(5px, 5px); }
.nav-toggle.open span:nth-child(2) { opacity: 0; }
.nav-toggle.open span:nth-child(3) { transform: rotate(-45deg) translate(5px, -5px); }

/* --- Hero --- */
.hero {
  position: relative;
  min-height: 92vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  /* Deep brand-navy gradient fallback when no hero photo is staged */
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
}
/* A faint architectural grid over the gradient hero fallback — quiet texture so
   the no-photo hero never reads flat/empty. */
.hero::before {
  content: '';
  position: absolute; inset: 0; z-index: 0;
  background-image:
    linear-gradient(rgba(255,255,255,0.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.028) 1px, transparent 1px);
  background-size: 68px 68px;
  mask-image: radial-gradient(120% 120% at 15% 0%, #000 20%, transparent 78%);
  pointer-events: none;
}
.hero-media { position: absolute; inset: 0; z-index: 0; }
/* Top-weighted crop: the hero photo is the tallest, narrowest crop on the
   page (portrait at phone width), most likely to slice through a face or
   the subject's upper body if left center-cropped. */
.hero-media img { width: 100%; height: 100%; object-fit: cover; object-position: center 22%; }
.hero-media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-video-poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: none; }
@media (prefers-reduced-motion: reduce) {
  .hero-media video { display: none; }
  .hero-video-poster { display: block; }
}
.hero-overlay {
  position: absolute; inset: 0; z-index: 1;
  background: var(--color-hero-overlay);
  background:
    linear-gradient(0deg, var(--color-hero-scrim) 0%, transparent 58%),
    linear-gradient(97deg,
      color-mix(in srgb, var(--color-hero-ink) 92%, transparent) 0%,
      color-mix(in srgb, var(--color-hero-ink) 80%, transparent) 34%,
      color-mix(in srgb, var(--color-hero-ink) 54%, transparent) 66%,
      color-mix(in srgb, var(--color-hero-ink) 40%, transparent) 100%);
}
.hero-inner { position: relative; z-index: 2; width: 100%; }
.hero-content { max-width: 700px; padding: 4rem 0 4rem; }

/* Bold "RECORD-SETTING RESULTS" pill — a confident accent-bordered chip that
   sets the results-forward tone the moment the page loads. */
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 14%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--color-accent) 55%, transparent);
  border-radius: var(--radius-chip);
  margin-bottom: var(--space-md);
}
.hero-badge::before { content: ''; width: 8px; height: 8px; background: var(--color-accent); }

/* Oversized hero result number. Content-gated (hero.resultStat), so a
   business without a headline number drops it cleanly. */
.hero-result {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  flex-wrap: wrap;
  margin-bottom: var(--space-lg);
  padding-left: 1.15rem;
  border-left: 4px solid var(--color-accent);
}
.hero-result-value {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3rem, 7vw, 5.25rem);
  line-height: 0.9;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: #fff;
}
.hero-result-label {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
  max-width: 15ch;
  line-height: 1.35;
}

.hero h1 {
  color: #fff;
  /* Genuinely large per the operator's nav/hero-too-small flag: a geometric
     sans reads visually smaller than a serif at the same size, so this sits
     above the old 4.5rem ceiling to hold equal dominance under Jakarta Sans. */
  font-size: clamp(2.9rem, 6.2vw, 4.85rem);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.02em;
  text-wrap: balance;
  text-shadow: 0 1px 30px rgba(0, 0, 0, 0.3);
  margin-bottom: var(--space-md);
}
/* The accent is the ONE phrase that carries the eye: set in Plus Jakarta Sans
   ITALIC, in brass. A coloured italic key phrase reads editorial + premium
   (matches minimaleskin.com's italic-accent treatment) and never collides
   with descenders. Keep it short at authoring time (2-4 words). */
.hero h1 em {
  font-style: italic;
  font-weight: 600;
  color: var(--color-accent);
}

.hero-sub {
  font-size: 1.18rem;
  color: rgba(255,255,255,0.9);
  margin-bottom: var(--space-lg);
  max-width: 590px;
  line-height: 1.65;
}

.hero-actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: var(--space-lg);
}
/* In the hero, the primary action reads as the gold marquee CTA */
.hero .btn-primary {
  background: var(--color-accent);
  color: var(--color-hero-ink-2);
  border-color: var(--color-accent);
  font-weight: 700;
}
.hero .btn-primary:hover { filter: brightness(1.06); color: var(--color-hero-ink-2); }

/* Quality floor: exactly ONE dominant solid-accent CTA in the hero (the
   .btn-primary above) — every other hero action is a quiet underlined text
   link so nothing competes with it. Rating/years/area trust facts live ONLY in
   the sw-trust strip directly below the hero, never repeated here. */
.hero-call-link {
  display: inline-flex;
  align-items: center;
  color: rgba(255,255,255,0.88);
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(255,255,255,0.5);
  transition: color 0.2s, text-decoration-color 0.2s;
}
.hero-call-link:hover { color: #fff; text-decoration-color: #fff; }

/* Scroll cue on the gradient (no-media) hero — a quiet considered detail */
.hero-scrollcue {
  position: absolute; z-index: 2;
  left: 50%; bottom: 1.75rem;
  transform: translateX(-50%);
  width: 24px; height: 38px;
  border: 1.5px solid rgba(255,255,255,0.4);
  border-radius: 14px;
  display: flex; justify-content: center;
}
.hero-scrollcue span {
  width: 3px; height: 8px; margin-top: 7px;
  border-radius: 2px;
  background: var(--color-accent);
  animation: scrollcue 1.8s ease-in-out infinite;
}
@keyframes scrollcue { 0%,100% { opacity: 0.4; transform: translateY(0); } 50% { opacity: 1; transform: translateY(7px); } }
@media (prefers-reduced-motion: reduce) { .hero-scrollcue span { animation: none; } }

/* --- Hero archetypes: split + bandoverlay (fullbleed = the base above) --- */
.hero-split { min-height: auto; }
.hero-split .hero-inner {
  max-width: 100%;
  padding: 0;
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  align-items: stretch;
  min-height: 90vh;
}
.hero-split .hero-content {
  max-width: none;
  padding: clamp(7.5rem, 9vw, 9rem) clamp(1.5rem, 5vw, 4.5rem) clamp(3rem, 6vw, 4.5rem);
  background: linear-gradient(160deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.hero-figure {
  position: relative;
  overflow: hidden;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-accent) 175%);
}
.hero-figure::after {
  content: '';
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 3px; background: var(--color-accent); opacity: 0.85;
}
.hero-figure img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 22%; display: block; }

/* Bandoverlay: full-bleed photo + a solid brand CTA band low on the hero. */
.hero-bandoverlay .hero-inner {
  display: flex;
  align-items: flex-end;
  min-height: 92vh;
  padding: 7rem 0 clamp(2rem, 5vw, 3.5rem);
}
.hero-bandoverlay .hero-overlay { background: linear-gradient(0deg, var(--color-hero-scrim) 0%, transparent 68%); }
.hero-bandoverlay .hero-content {
  max-width: 920px;
  padding: clamp(2rem, 3.5vw, 3rem) clamp(2rem, 4vw, 3.25rem);
  background: linear-gradient(140deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  border-top: 3px solid var(--color-accent);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}

/* --- Sections --- */
section {
  /* Bounds tightened to the 8px rhythm scale (32px/56px, both n*--space-xs) so the fluid
     clamp lands on-grid at both the mobile (390) and desktop (1440) reference widths instead
     of resolving to 34px/57.6px mid-range values (improve loop Arc S). */
  padding: clamp(2rem, 4vw, 3.5rem) 0;
  position: relative;
  scroll-margin-top: 72px;
}
.about-section { background: var(--color-bg); }
.services-section { background: var(--color-surface); border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }
.experience-section { background: var(--color-bg); }
.team-section { background: var(--color-surface); border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }
.certifications-section { background: var(--color-bg); }
.results-section { background: var(--color-surface); border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }
.faq-section { background: var(--color-bg); }
.contact-section { background: var(--color-bg); border-top: 1px solid var(--color-border); }

/* --- Sub-niche variant emphasis --------------------------------------------
   fill-static (orchestrator/src/lib/fillStatic.ts) resolves a per-prospect
   sub-niche variant from templates/variants/{hair-salon,barber,spa}.json
   (whichever niche matches; a pet-groomer prospect can also land here via
   pet-grooming.json) and exposes it as `variant` {key, mood} plus structural
   `variantEmphasis`/`variantCompact` maps keyed by the profile's own
   "emphasize"/"deemphasize" section names. index.html turns those into a
   `data-emph="1"` / `data-compact="1"` attribute on the matching <section>.
   Section-key mapping (profile key -> this template's section):
     gallery            -> #gallery          (data-el="gallery")
     reviews            -> #results          (data-el="reviews")
     about              -> #about            (data-el="about")
     approach            \
     experience           }-> #experience    (data-el="experience";
                                               profiles use "approach", kept
                                               as an alias for "experience")
     services, pricing  -> #services         (data-el="services.cards")
     team, stylists     -> #team             (data-el="team")
   ("trustStats", used by pet-grooming.json's daycare-boarding variant, has no
   matching block in this template's markup and is a harmless no-op here.)

   [data-emph="1"] promotes a section the resolved variant leads with: a
   wider, accent-tinted band, a larger title and extra vertical rhythm.
   Deliberately NOT a left-border accent card treatment (flagged AI-slop tell):
   the "featured" read comes from scale plus a soft top-down tint, matching
   this pack's sharp/editorial language. [data-compact="1"] condenses a
   section the variant recedes: tighter rhythm, a smaller title, a tighter
   grid gap. Both are purely additive: a page with no resolved variant (or a
   variant whose emphasize/deemphasize lists are empty) renders byte-identical
   to before this existed. --- */
[data-emph="1"] {
  padding-top: clamp(3.25rem, 7vw, 6rem);
  padding-bottom: clamp(3.25rem, 7vw, 6rem);
  background-image: linear-gradient(180deg, color-mix(in srgb, var(--color-accent) 7%, transparent), transparent 65%);
}
[data-emph="1"] .section-head-center { margin-bottom: var(--space-xl); }
[data-emph="1"] .section-title { font-size: clamp(2.5rem, 4.8vw, 3.8rem); }
[data-emph="1"] .section-label { color: var(--color-accent); }
[data-emph="1"] .section-label::before { width: 44px; background: var(--color-accent); }
[data-emph="1"] .section-desc { font-size: 1.15rem; }
[data-emph="1"] .services-grid,
[data-emph="1"] .gallery-grid,
[data-emph="1"] .team-grid,
[data-emph="1"] .reviews-grid,
[data-emph="1"] .steps-grid { gap: calc(var(--space-lg) + var(--space-xs)); } /* 56px, on-grid (was *1.2 = 57.6px) */

[data-compact="1"] {
  /* Max bound + coefficient retuned so the desktop (1440) value pins to an on-grid 40px
     instead of resolving mid-range to 37.44px (improve loop Arc S). */
  padding-top: clamp(1.5rem, 2.8vw, 2.5rem);
  padding-bottom: clamp(1.5rem, 2.8vw, 2.5rem);
}
[data-compact="1"] .section-head-center { margin-bottom: var(--space-md); }
[data-compact="1"] .section-title { font-size: clamp(1.7rem, 2.8vw, 2.2rem); }
[data-compact="1"] .section-desc { font-size: 0.96rem; }
[data-compact="1"] .services-grid,
[data-compact="1"] .gallery-grid,
[data-compact="1"] .team-grid,
[data-compact="1"] .reviews-grid,
[data-compact="1"] .steps-grid { gap: var(--space-sm); }

/* --- About --- */
.about-grid {
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: clamp(3rem, 6.5vw, 5.5rem);
  align-items: center;
}
.about-grid.no-media { grid-template-columns: 1.15fr 1fr; }
/* Editorial framed photo: the image sits on a soft shadow with a thin brass
   frame offset behind it toward the lower-right (a considered magazine detail). */
.about-media { position: relative; }
.about-media::before {
  content: '';
  position: absolute;
  inset: 20px -20px -20px 20px;
  border: 1px solid color-mix(in srgb, var(--color-accent) 55%, transparent);
  border-radius: var(--radius-lg);
  z-index: 0;
}
.about-media img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  min-height: 440px;
  object-fit: cover;
  object-position: center 25%;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
/* Real-data credential badge overlapping the image corner (optional). */
.about-badge {
  position: absolute;
  z-index: 2;
  left: -14px; bottom: 26px;
  padding: 0.9rem 1.25rem;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  border-top: 3px solid var(--color-accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}
.about-badge-value { font-family: var(--font-display); font-weight: 800; font-size: 1.7rem; font-variant-numeric: tabular-nums; color: #fff; }
.about-badge-label { font-family: var(--font-sans); font-size: 0.68rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-accent); margin-top: 0.2rem; max-width: 150px; }
.about-text p { color: var(--color-ink-dim); margin-bottom: 1.15rem; line-height: 1.8; }

.proof-points {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-top: var(--space-lg);
}
/* Typography-first (no icon square): a thin accent rule on the left reads as
   an intentional editorial mark instead of an SVG glyph. */
.proof-point {
  padding: 1rem 1.1rem 1rem 1.15rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 2px solid var(--color-accent);
  border-radius: var(--radius);
  transition: border-color 0.2s, transform 0.2s;
}
.proof-point:hover { border-color: color-mix(in srgb, var(--color-brand) 40%, var(--color-border)); border-left-color: var(--color-accent); transform: translateY(-2px); }
.about-section .proof-point { background: var(--color-surface); }
.proof-point strong { display: block; color: var(--color-ink); font-family: var(--font-display); font-size: 0.95rem; margin-bottom: 0.15rem; }
.proof-point span { color: var(--color-ink-dim); font-size: 0.84rem; line-height: 1.5; }

.about-signature {
  display: flex;
  flex-direction: column;
  margin-top: var(--space-md);
  padding-left: 1rem;
  border-left: 2px solid var(--color-accent);
}
.about-signature-name { font-family: var(--font-display); font-style: italic; font-weight: 600; font-size: 1.15rem; color: var(--color-ink); }
.about-signature-role { font-family: var(--font-sans); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-ink-dim); margin-top: 0.2rem; }

/* --- About: media-side-swap alternate (layout-variety wave) ---
   The restorative/refined sub-niches -- calm (day-spa, wellness-holistic,
   spa general) and classic (medical-spa, bridal-event, luxury-grooming,
   classic-traditional) -- read as a composed editorial spread with the photo
   trailing the copy instead of the default photo-leads/copy-follows order,
   which stays the read for the warm/bold energetic sub-niches (colour
   specialist, modern fades, blowout, nails, general). Pure `order` swap of
   the SAME two grid children (about-media/about-text; no new markup), with
   the column-width bias and the offset picture-frame + credential-badge
   details mirrored so the editorial framing still reads correctly on the
   new side. `.no-media` (a business with no about photo) is excluded --
   nothing to swap. Selection rides `data-mood`, exposed on <body> by the
   pack's existing (variant.key/variant.mood) render data -- no new
   fillStatic logic. */
[data-mood="calm"] .about-grid:not(.no-media),
[data-mood="classic"] .about-grid:not(.no-media) {
  grid-template-columns: 1.05fr 1fr;
}
[data-mood="calm"] .about-media,
[data-mood="classic"] .about-media { order: 2; }
[data-mood="calm"] .about-text,
[data-mood="classic"] .about-text { order: 1; }
[data-mood="calm"] .about-media::before,
[data-mood="classic"] .about-media::before { inset: 20px 20px -20px -20px; }
[data-mood="calm"] .about-badge,
[data-mood="classic"] .about-badge { left: auto; right: -14px; }

/* --- Services (personal care publishes a real price menu) --- */
.services-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}
/* Refined card: a hairline border at rest (no resting shadow), a soft shadow +
   lift + an accent hairline that slides across the top on hover. */
.service-card {
  position: relative;
  padding: 2.25rem 2rem 2.15rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
.service-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
}
.service-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--color-accent) 35%, var(--color-border));
}
.service-card:hover::before { transform: scaleX(1); }
.service-card h3 { margin-bottom: var(--space-xs); } /* 8px, on-grid (was 0.65rem/10.4px) */
.service-card p { color: var(--color-ink-dim); font-size: 0.96rem; line-height: 1.68; }
/* Soft background-image variant: a per-item `services.items[].image` or the shared
   `services.image` fallback turns a card photographic — the photo sits behind a
   brand scrim so the heading + copy stay legible. Icon-only cards are unaffected. */
.service-card-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  z-index: 0;
}
.service-card-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    165deg,
    color-mix(in srgb, var(--color-brand) 60%, transparent) 0%,
    color-mix(in srgb, var(--color-brand) 88%, transparent) 100%
  );
}
.service-card:has(.service-card-bg) { border-color: transparent; }
.service-card:has(.service-card-bg) > :not(.service-card-bg):not(.service-card-scrim) {
  position: relative;
  z-index: 2;
}
.service-card::before { z-index: 3; }
.service-card:has(.service-card-bg) h3 { color: #fff; }
.service-card:has(.service-card-bg) p { color: rgba(255, 255, 255, 0.9); }
/* Real price, published up front (personal care, unlike quote-per-matter
   professional services). */
.service-price {
  display: inline-block;
  margin-top: 0.9rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  color: var(--color-brand-strong);
}
.service-card:has(.service-card-bg) .service-price { color: #fff; }
.service-meta {
  margin-top: 0.6rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--color-border);
  font-family: var(--font-sans);
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--color-brand-strong);
}
.service-card:not(:has(.service-price)) .service-meta { margin-top: 1.1rem; }
.services-cta {
  display: flex;
  justify-content: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-top: var(--space-xl);
}

/* --- Services: layout alternates (layout-variety wave) ---
   Two structural alternates to the default photo-led card grid above, both a
   pure CSS reflow of the SAME `.service-card` markup (no new elements, so the
   existing motion-wave nth-child stagger keeps working unchanged) and both
   scoped under `.services-section` so the full price-menu page
   (services.html, `.full-services-section`) never picks them up even though
   it reuses the `.service-card` class. Selection rides `data-mood`
   (variant.mood, already exposed on <body>) -- no new fillStatic logic. */

/* calm (day-spa, wellness-holistic, spa general): an unhurried, text-led
   editorial list instead of an energetic card grid -- restorative niches
   read better slowed down. A CSS-counted index numeral (same considered-list
   language as the certifications section's `.certification-index`) replaces
   the card border/shadow/hover-hairline. A real photo (the image wave's
   per-item `image` or the shared `services.image` fallback) still renders --
   it collapses from a full-bleed card background to a small framed
   thumbnail trailing the row instead of disappearing, so a business with
   real service photos never loses them here. */
[data-mood="calm"] .services-section .services-grid {
  counter-reset: svc;
  display: flex;
  flex-direction: column;
  gap: 0;
  max-width: 900px;
  margin: 0 auto;
}
[data-mood="calm"] .services-section .service-card {
  counter-increment: svc;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  column-gap: 1.5rem;
  row-gap: 0.35rem;
  padding: 1.85rem 0.25rem;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--color-border);
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
  transform: none;
}
[data-mood="calm"] .services-section .service-card:first-child { border-top: 1px solid var(--color-border); }
[data-mood="calm"] .services-section .service-card:hover {
  transform: none;
  box-shadow: none;
  border-bottom-color: var(--color-border);
  background: color-mix(in srgb, var(--color-accent) 5%, transparent);
}
[data-mood="calm"] .services-section .service-card::before { content: none; }
[data-mood="calm"] .services-section .service-card::after {
  content: counter(svc, decimal-leading-zero);
  order: -1;
  flex: 0 0 2.75rem;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.02em;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}
[data-mood="calm"] .services-section .service-card h3 { flex: 1 1 220px; margin-bottom: 0; font-size: 1.15rem; }
[data-mood="calm"] .services-section .service-card p { flex: 2 1 320px; font-size: 0.94rem; }
[data-mood="calm"] .services-section .service-card .service-price { flex: 0 0 auto; order: 5; margin-top: 0; }
[data-mood="calm"] .services-section .service-card .service-meta { flex: 0 0 100%; order: 6; margin-top: 0; padding-top: 0.35rem; border-top: none; }
[data-mood="calm"] .services-section .service-card:has(.service-card-bg) {
  padding-right: calc(0.25rem + 64px + 1.25rem);
  position: relative;
  border-color: var(--color-border);
}
[data-mood="calm"] .services-section .service-card-bg {
  inset: auto 0.25rem auto auto;
  top: 50%;
  transform: translateY(-50%);
  width: 64px;
  height: 64px;
}
[data-mood="calm"] .services-section .service-card-scrim { display: none; }
[data-mood="calm"] .services-section .service-card:has(.service-card-bg) h3 { color: var(--color-ink); }
[data-mood="calm"] .services-section .service-card:has(.service-card-bg) p { color: var(--color-ink-dim); }
[data-mood="calm"] .services-section .service-card:has(.service-card-bg) .service-price { color: var(--color-brand-strong); }

/* bold (colour specialist, extensions, modern fades, barber general): a
   bento-style asymmetric grid instead of a uniform card wall -- the FIRST
   service (whatever the business leads its menu with) reads as a featured
   tile, spanning two columns with a tinted accent field and larger type; the
   rest stay regular cards. Works for any item count (1-item and 2-item
   menus just render a wider featured row, never a broken grid) since it
   only touches `:first-child`, never an nth-child count assumption. */
[data-mood="bold"] .services-section .services-grid {
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
[data-mood="bold"] .services-section .service-card:first-child {
  grid-column: span 2;
  padding: clamp(2.25rem, 4vw, 3.25rem) clamp(2rem, 4vw, 3rem);
  background: linear-gradient(150deg, color-mix(in srgb, var(--color-accent) 10%, var(--color-bg)) 0%, var(--color-bg) 100%);
  border-color: color-mix(in srgb, var(--color-accent) 32%, var(--color-border));
}
[data-mood="bold"] .services-section .service-card:first-child h3 { font-size: clamp(1.45rem, 2.3vw, 1.85rem); }
[data-mood="bold"] .services-section .service-card:first-child p { font-size: 1.04rem; max-width: 48ch; }
[data-mood="bold"] .services-section .service-card:first-child .service-price { font-size: 1.28rem; }

/* --- Experience: what a visit is like (steps) --- */
.steps-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}
/* Quiet connector line linking the numbered medallions (desktop only). */
@media (min-width: 901px) {
  .steps-grid::before {
    content: '';
    position: absolute;
    top: calc(2.85rem + 44px);
    left: 18%; right: 18%;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--color-border) 15%, var(--color-border) 85%, transparent);
    z-index: 0;
  }
}
.step-card {
  padding: 2.85rem 1.9rem 2.35rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  position: relative;
  z-index: 1;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 88px; height: 88px;
  border-radius: 50%;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 2.3rem;
  letter-spacing: 0.01em;
  margin-bottom: 1.6rem;
  position: relative;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08), 0 16px 34px -16px rgba(12,20,38,0.6);
}
.step-num::after {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 1px solid var(--color-accent);
  opacity: 0.6;
}
.step-card h3 { margin-bottom: var(--space-xs); } /* 8px, on-grid (was 0.6rem/9.6px) */
.step-card p { color: var(--color-ink-dim); font-size: 0.96rem; line-height: 1.6; max-width: 34ch; }

/* --- Credibility / office statement band (deep-navy editorial moment) --- */
.credibility {
  position: relative;
  overflow: hidden;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  color: #fff;
}
.credibility-media { position: absolute; inset: 0; z-index: 0; }
.credibility-media img { width: 100%; height: 100%; object-fit: cover; object-position: center 30%; }
.credibility-overlay {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  opacity: 0.85;
}
.credibility::after {
  content: '';
  position: absolute; top: 0; left: 0; width: min(160px, 34%); height: 3px;
  background: var(--color-accent); z-index: 2;
}
.credibility .container { position: relative; z-index: 2; }
.credibility-inner { max-width: 940px; }
.credibility .section-label { color: var(--color-accent); }
.credibility-statement {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.7rem, 3.6vw, 2.85rem);
  line-height: 1.22;
  letter-spacing: -0.01em;
  color: #fff;
  text-wrap: balance;
}
.credibility-statement em, .credibility-statement i { font-style: italic; color: var(--color-accent); }
.credibility-text {
  margin-top: var(--space-md);
  max-width: 66ch;
  color: rgba(255,255,255,0.82);
  font-size: 1.05rem;
  line-height: 1.75;
}
.credibility-markers {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem 2.25rem;
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid rgba(255,255,255,0.16);
}
.credibility-markers li {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-sans);
  font-size: 0.92rem;
  font-weight: 600;
  color: #fff;
}
.credibility-marker-icon { display: inline-flex; }
.credibility-marker-icon svg { width: 20px; height: 20px; color: var(--color-accent); }
.credibility .btn-accent { margin-top: var(--space-lg); }

/* --- Areas we serve (service-area / locations band, for mobile/multi-location
   personal-care businesses). Fully content-gated on the serviceAreas key; a
   business without a service-area footprint drops the whole band. Bold, sharp
   location cards with a left accent edge and an address/note line. No image
   slots (never an empty image). --- */
.areas-section { background: var(--color-bg); border-top: 1px solid var(--color-border); }
.areas-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--space-md);
  max-width: 1040px;
  margin: 0 auto;
}
.area-card {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  padding: 1.6rem 1.7rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-accent);
  border-radius: var(--radius);
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.area-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border));
}
.area-card svg { width: 24px; height: 24px; color: var(--color-accent); flex-shrink: 0; margin-top: 0.15rem; }
.area-card-body { display: flex; flex-direction: column; }
.area-name { font-family: var(--font-display); font-weight: 800; font-size: 1.12rem; color: var(--color-ink); letter-spacing: -0.01em; }
.area-note { color: var(--color-ink-dim); font-size: 0.88rem; line-height: 1.5; margin-top: 0.15rem; }
.areas-note { text-align: center; color: var(--color-ink-dim); font-size: 0.9rem; margin-top: var(--space-lg); }
.areas-directions { text-align: center; margin-top: 0.6rem; }
.areas-directions a {
  color: var(--color-brand-strong);
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.9rem;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.areas-directions a:hover { color: var(--color-accent); }

/* --- Gallery (imagery-led — personal care sells on photos). A simple
   responsive grid of real photos; each tile is an image with an optional
   caption. --- */
.gallery-section { background: var(--color-surface); border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }
/* Row-gap is deliberately larger than column-gap: figcaptions only appear on
   some tiles, so a wider row-gap keeps the vertical rhythm between rows
   reading even regardless of which tiles carry a caption. */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  column-gap: var(--space-md);
  row-gap: clamp(1.9rem, 3vw, 2.4rem);
  align-items: start;
}
.gallery-item { margin: 0; }
.gallery-item img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.gallery-item:hover img { transform: translateY(-3px); box-shadow: var(--shadow-hover); }
.gallery-item figcaption {
  margin-top: 0.65rem;
  text-align: center;
  color: var(--color-ink-dim);
  font-size: 0.85rem;
}

/* --- Team --- */
.team-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
  max-width: 1040px;
  margin: 0 auto;
}
/* Editorial portrait cards: a tall photo (or navy monogram panel) above the body. */
.team-card {
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
.team-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--color-accent) 30%, var(--color-border));
}
.team-photo { margin: 0; overflow: hidden; }
.team-photo img {
  width: 100%;
  aspect-ratio: 4 / 4.4;
  object-fit: cover;
  /* Headshot bias: a tall 4/4.4 crop centered on a standing/seated portrait
     tends to cut off the top of the head; weight toward the top third. */
  object-position: center 18%;
  display: block;
}
.team-monogram {
  width: 100%;
  aspect-ratio: 4 / 4.4;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  color: var(--color-brand-fg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 3rem;
  letter-spacing: 0.02em;
  position: relative;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.1);
}
.team-monogram::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--color-accent);
}
.team-body { padding: 1.6rem 1.7rem 1.85rem; text-align: left; }
.team-body h3 { margin-bottom: var(--space-xs); } /* 8px, on-grid (was 0.3rem/4.8px) */
.team-credentials {
  color: var(--color-brand-strong);
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  margin-bottom: 0.3rem;
}
.team-role {
  color: var(--color-ink-dim);
  font-family: var(--font-sans);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-weight: 600;
  margin-bottom: 1rem;
}
.team-body p { color: var(--color-ink-dim); font-size: 0.92rem; line-height: 1.65; }

/* --- Certifications / associations ---
   Typography-first credential rows: a CSS-counted index numeral (same
   accent-display language as .category-index on the services page) replaces
   a repeated identical award-ribbon icon, so the list reads as considered
   type rather than a generic icon-grid. */
.certifications-wrap { max-width: 980px; margin: 0 auto; }
.certifications-grid {
  counter-reset: cert;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--space-md);
}
/* Correction 92 (hierarchy-composition): the auto-fit grid renders exactly 3
   tracks at this wrapper's max-width (980px comfortably fits 3x230px+gaps,
   not 4), so any item count landing on 3n+1 (4, 7, 10 certs...) leaves a
   single card stranded alone at the grid's left edge in the final row --
   visually unbalanced next to the full rows above it. Centering that lone
   trailing card under the middle column reads as a deliberate closing
   statement instead of an orphan. Scoped to the width where 3 columns are
   actually guaranteed so it never misfires at 2-column/1-column widths. */
@media (min-width: 901px) {
  .certifications-grid > .certification-card:nth-child(3n+1):last-child {
    grid-column: 2;
  }
}
.certification-card {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1.5rem 1.6rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  transition: border-color 0.2s, transform 0.2s;
}
.certification-card:hover { border-color: color-mix(in srgb, var(--color-accent) 35%, var(--color-border)); transform: translateY(-2px); }
.certification-index {
  counter-increment: cert;
  flex-shrink: 0;
  padding-top: 0.15rem;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}
.certification-index::before { content: counter(cert, decimal-leading-zero); }
.certification-card strong { display: block; color: var(--color-ink); font-family: var(--font-display); font-size: 1rem; margin-bottom: 0.2rem; }
.certification-card span { color: var(--color-ink-dim); font-size: 0.88rem; line-height: 1.55; }
.certifications-note { text-align: center; color: var(--color-ink-dim); font-size: 0.92rem; margin-top: var(--space-lg); }

/* --- Results / testimonials --- */
.results-section .section-head-center { margin-bottom: var(--space-lg); }
.rating-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: var(--space-md);
  padding: 0.75rem 1.5rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-top: 3px solid var(--color-accent);
  border-radius: var(--radius);
  font-family: var(--font-sans);
  font-size: 0.92rem;
  color: var(--color-ink-dim);
  box-shadow: var(--shadow-sm);
}
.rating-score { font-family: var(--font-display); font-weight: 800; color: var(--color-ink); font-size: 1.8rem; line-height: 1; font-variant-numeric: tabular-nums; }
.rating-stars { color: var(--color-accent); letter-spacing: 0.14em; font-size: 1.15rem; }
/* Honest star fill: a `--stars-fill` percentage (set inline from a real
   rating via CSS calc()) clips the gold to the true score instead of always
   claiming a full 5/5 — mirrors the .hero-stars mechanism used in sibling
   packs (dental/food/proservices/realestate/trades), reimplemented here in
   pure CSS since this pack's rating lives in the reviews/sw-trust widgets
   rather than the hero. Falls back to a full row when no style is set. */
.rating-stars[style*="--stars-fill"], .review-stars[style*="--stars-fill"], .services-trust-stars[style*="--stars-fill"] {
  background: linear-gradient(90deg, currentColor var(--stars-fill, 100%), var(--color-border) var(--stars-fill, 100%));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.rating-meta { font-weight: 600; }

/* Confident lead pull-quote with an oversized brass quote mark */
.review-lead {
  position: relative;
  max-width: 860px;
  margin: 0 auto var(--space-xl);
  padding: 2.75rem 3rem 2.5rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-align: center;
}
.review-lead-mark {
  position: absolute;
  top: -0.3rem; left: 1.5rem;
  font-family: var(--font-display);
  font-size: 6rem;
  line-height: 1;
  color: color-mix(in srgb, var(--color-accent) 32%, transparent);
  pointer-events: none;
}
.review-lead .review-stars { justify-content: center; }
.review-lead p {
  position: relative;
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(1.25rem, 2.3vw, 1.65rem);
  font-weight: 600;
  line-height: 1.5;
  color: var(--color-ink);
  margin: 0.5rem 0 1.1rem;
  text-wrap: balance;
}
.review-lead footer { font-size: 0.92rem; }
.reviews-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}
.review-card {
  padding: 2.15rem 2rem 2.25rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-align: left;
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
.review-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); border-color: color-mix(in srgb, var(--color-accent) 30%, var(--color-border)); }
.review-stars { color: var(--color-accent); font-size: 0.9rem; letter-spacing: 0.1em; margin-bottom: 1rem; display: flex; }
.review-card blockquote { color: var(--color-ink); font-size: 1.02rem; line-height: 1.72; margin-bottom: 1.35rem; }
/* Hairline divider separates the quote from its attribution, giving the card a
   clear two-tier hierarchy (quote, then who said it) instead of a flat stack. */
.review-author { font-family: var(--font-display); font-weight: 700; color: var(--color-ink); font-size: 0.95rem; padding-top: 0.9rem; border-top: 1px solid var(--color-border); }
.review-source { color: var(--color-ink-dim); font-family: var(--font-sans); font-size: 0.8rem; margin-top: 0.2rem; }
/* Stylist attribution: personal care converts on trust in a named person, so
   a review tied to a real stylist gets the same accent-brand treatment as
   .team-credentials, tying the two trust systems together visually. Content-
   gated (reviews.items[].stylist / reviews.lead.stylist) — never inferred. */
.review-stylist {
  color: var(--color-brand-strong);
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  margin-top: 0.35rem;
}
.review-lead footer .review-stylist { color: var(--color-ink-dim); font-weight: 600; font-size: 0.92rem; letter-spacing: normal; margin-top: 0; }

/* --- FAQ (native <details> accordion) --- */
.faq-grid {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: start;
}
.faq-intro { position: sticky; top: 100px; }
.faq-intro .section-desc { margin-bottom: var(--space-md); }
.faq-aside {
  margin-top: var(--space-md);
  padding: 1.5rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.faq-aside p { color: var(--color-ink-dim); font-size: 0.95rem; margin-bottom: 1rem; }
.faq-list { display: flex; flex-direction: column; }
.faq-item {
  border-bottom: 1px solid var(--color-border);
}
.faq-item:first-child { border-top: 1px solid var(--color-border); }
.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 1.55rem 0.6rem;
  margin: 0 -0.6rem;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.02rem, 1.6vw, 1.15rem);
  color: var(--color-ink);
  border-radius: 4px;
  transition: color 0.2s, background 0.2s;
}
.faq-item summary::-webkit-details-marker { display: none; }
/* A quiet background tint on hover/open signals the row is interactive before
   the visitor even reaches the +/- indicator. */
.faq-item summary:hover { color: var(--color-brand-strong); background: color-mix(in srgb, var(--color-brand) 4%, transparent); }
.faq-item[open] summary { color: var(--color-brand-strong); }
/* +/- indicator built from two accent bars */
.faq-icon {
  position: relative;
  flex-shrink: 0;
  width: 22px; height: 22px;
}
.faq-icon::before, .faq-icon::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  background: var(--color-accent);
  transition: transform 0.28s ease, opacity 0.28s ease;
}
.faq-icon::before { width: 14px; height: 2px; }
.faq-icon::after { width: 2px; height: 14px; }
.faq-item[open] .faq-icon::after { transform: translate(-50%, -50%) rotate(90deg); opacity: 0; }
.faq-answer {
  padding: 0 0.25rem 1.5rem;
  max-width: 68ch;
}
.faq-answer p { color: var(--color-ink-dim); font-size: 1rem; line-height: 1.75; }

/* --- Shared form vocabulary (WIDGETS.md §2): generic form-row/form-group
   infrastructure, kept available for any hand-authored section even though the
   sw-booking widget below is fully self-contained and doesn't depend on it. --- */
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); }
.form-group { margin-bottom: var(--space-sm); }
.form-group label {
  display: block;
  font-size: 0.7rem;
  font-family: var(--font-sans);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--color-ink-dim);
  margin-bottom: 0.4rem;
}
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 0.8rem 0.95rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 0.95rem;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--color-brand);
  box-shadow: 0 0 0 3px var(--color-accent-soft);
  outline: none;
}
.form-group textarea { resize: vertical; min-height: 104px; }
.form-group select { cursor: pointer; }

/* --- Contact / Map --- */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: clamp(2.5rem, 5vw, 4rem); /* max 64px, on-grid (was 3.75rem/60px) */
  align-items: stretch;
}
/* Vertically centers the details column within the stretched row so it reads
   balanced against the taller map panel instead of hugging the top edge. */
.contact-info { display: flex; flex-direction: column; justify-content: center; height: 100%; }
.contact-details { display: flex; flex-direction: column; gap: 0.9rem; }
.contact-detail {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  padding: 1.2rem 1.35rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
.contact-detail svg { width: 22px; height: 22px; color: var(--color-brand); flex-shrink: 0; margin-top: 0.1rem; }
.contact-detail strong { display: block; color: var(--color-ink); font-family: var(--font-display); font-size: 0.98rem; margin-bottom: 0.15rem; }
.contact-detail span { color: var(--color-ink-dim); font-size: 0.9rem; line-height: 1.55; }
.contact-detail a { color: var(--color-brand-strong); font-weight: 600; }
.contact-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-top: var(--space-md); }
.contact-note { color: var(--color-ink-dim); font-size: 0.88rem; margin-top: var(--space-sm); }
.map-container {
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
  min-height: 400px;
}
.map-container iframe { width: 100%; height: 100%; min-height: 400px; border: 0; display: block; }

/* --- Page hero (services.html) --- */
.page-hero {
  position: relative;
  overflow: hidden;
  padding: calc(5rem + var(--space-lg)) 0 var(--space-xl);
  text-align: center;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
}
.page-hero-media { position: absolute; inset: 0; z-index: 0; }
.page-hero-media img { width: 100%; height: 100%; object-fit: cover; object-position: center 22%; }
.page-hero-overlay {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  opacity: 0.86;
}
.page-hero .container { position: relative; z-index: 2; }
.page-hero .section-label { color: var(--color-accent); justify-content: center; }
.page-hero .section-title,
.page-hero h1 { color: #fff; font-size: clamp(2.2rem, 4.5vw, 3.5rem); }
.page-hero .section-desc { color: rgba(255,255,255,0.9); margin: 0 auto; max-width: 68ch; }
.page-hero-actions { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; margin-top: var(--space-lg); }

/* --- Services overview intro (services.html) --- */
.services-overview { background: var(--color-bg); padding: clamp(1.75rem, 3vw, 2.75rem) 0; border-bottom: 1px solid var(--color-border); }
.services-overview-inner { max-width: 820px; margin: 0 auto; text-align: center; }
.services-overview-inner .section-label { justify-content: center; }
.services-overview-lead {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 2.6vw, 1.9rem);
  font-weight: 600;
  line-height: 1.42;
  color: var(--color-ink);
  text-wrap: balance;
}

/* --- Full services page --- */
.full-services-section { padding: clamp(2.125rem, 4vw, 3.75rem) 0; background: var(--color-bg); }
.full-services-category { margin-bottom: var(--space-2xl); }
.category-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}
.category-index {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--color-accent);
  letter-spacing: 0.02em;
}
.category-header h2 { font-size: clamp(1.5rem, 2.8vw, 2.1rem); white-space: nowrap; }
.category-header .line { flex: 1; height: 1px; background: var(--color-border); }
.category-desc { color: var(--color-ink-dim); font-size: 1.02rem; max-width: 72ch; margin-bottom: var(--space-lg); line-height: 1.7; }
.full-services-section .service-card { background: var(--color-surface); }

/* Closing CTA band on the services page */
.services-close {
  margin-top: var(--space-xl);
  padding: clamp(2.75rem, 5vw, 4.5rem) clamp(1.75rem, 4vw, 3.5rem);
  background: linear-gradient(150deg, var(--color-hero-ink) 0%, var(--color-hero-ink-2) 100%);
  border-radius: var(--radius-xl);
  border-top: 3px solid var(--color-accent);
  text-align: center;
  box-shadow: var(--shadow-lg);
}
.services-close-inner { max-width: 680px; margin: 0 auto; }
.services-close h2 { color: #fff; margin-bottom: var(--space-sm); }
/* Compact rating moment for the price page's single trust placement, styled
   as a light chip on the dark closing band (same "light-on-dark" language as
   .hero-badge/.hero-chip on index.html, just re-tinted for this card). */
.services-trust {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem 1.05rem;
  margin-bottom: var(--space-sm);
  background: rgba(255,255,255,0.09);
  border: 1px solid rgba(255,255,255,0.24);
  border-radius: var(--radius-chip);
}
.services-trust-stars { color: var(--color-accent); letter-spacing: 0.1em; font-size: 1rem; }
.services-trust-rating { font-family: var(--font-display); font-weight: 800; color: #fff; font-size: 1rem; font-variant-numeric: tabular-nums; }
.services-trust-label { font-family: var(--font-sans); font-weight: 600; font-size: 0.86rem; color: rgba(255,255,255,0.85); }
.services-close .section-desc { color: rgba(255,255,255,0.85); margin: 0 auto 0.5rem; }
.services-cta-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; justify-content: center; margin-top: var(--space-md); }

/* --- Footer --- */
.footer {
  background: var(--color-footer);
  /* Correction 91 (spacing-consistency): every other section on the page
     breathes with clamp(2rem, 4vw, 3.5rem) top+bottom (~56px desktop); the
     footer's bottom edge sat at var(--space-md)/24px, less than half that --
     the exact "less generous vertical padding" gap vs. the rest of the
     page's rhythm. Bumped to --space-lg (48px) to land back in the same
     band; top stays as-is (the .footer-bottom bar's own border+padding
     already adds visual weight there). */
  padding: calc(var(--space-xl) / 2) 0 var(--space-lg);
  color: rgba(255,255,255,0.75);
}
.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1.4fr 1.4fr;
  align-items: start;
  gap: var(--space-lg);
  margin-bottom: var(--space-lg);
}
.footer-logo { height: 40px; width: auto; max-width: 240px; object-fit: contain; display: block; }
.footer-wordmark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.35rem;
  letter-spacing: -0.02em;
  color: #fff;
}
/* Both the brand mark and the plain-text column headings occupy the same
   40px header band, bottom-aligned, so the list/paragraph content underneath
   every column starts flush on one shared line instead of drifting per column. */
.footer-brand .nav-logo,
.footer-brand .footer-wordmark {
  min-height: 40px;
  display: flex;
  align-items: flex-end;
  margin-bottom: var(--space-sm);
}
.footer-brand p { color: rgba(255,255,255,0.72); font-size: 0.88rem; max-width: 320px; line-height: 1.65; }
.footer-tag { margin-top: 0.75rem; color: var(--color-accent) !important; font-weight: 600; font-size: 0.85rem !important; }
.footer-col h3 {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fff;
  min-height: 40px;
  display: flex;
  align-items: flex-end;
  margin-bottom: var(--space-sm);
}
.footer-col ul { list-style: none; }
.footer-col li { margin-bottom: 0.15rem; font-size: 0.88rem; color: rgba(255,255,255,0.72); }
/* Correction 92 (hierarchy-composition): footer links carried the exact same
   color/size as .footer-brand p's plain body copy above -- "link styling
   lacks visual distinction from body text". font-weight:600 is Inter's
   other loaded cut (400/600 shipped, no synthesis), so links now read
   semibold against the regular-weight paragraph without any new color. */
.footer-col a { color: rgba(255,255,255,0.72); font-size: 0.88rem; font-weight: 600; }
.footer-col a:hover { color: #fff; }
/* Tap-target floor for the plain Explore link list only (not
   .footer-contact, whose <a> sits in a flex row next to an icon —
   growing its box with padding would misalign it against the icon).
   A bare text link at this font-size/line-height is only ~20px tall;
   inline-block + vertical padding grows the real hit area without
   inflating the visual rhythm much (the list's own margin-bottom above
   was trimmed since this padding now carries most of the row spacing). */
.footer-col ul:not(.footer-contact) a { display: inline-block; padding: 0.4rem 0; }
.footer-contact li { display: flex; align-items: flex-start; gap: 0.5rem; }
.footer-contact li svg { width: 15px; height: 15px; flex: none; margin-top: 0.2rem; color: rgba(255,255,255,0.85); }
.footer-hours { display: flex; flex-direction: column; gap: 0.4rem; }
.footer-hours-row { display: flex; justify-content: space-between; gap: var(--space-sm); font-size: 0.85rem; color: rgba(255,255,255,0.72); }
.footer-hours-row span:first-child { color: #fff; font-weight: 600; }
.footer-hours-row span:last-child { text-align: right; white-space: nowrap; }
.footer-bottom {
  padding-top: var(--space-md);
  border-top: 1px solid rgba(255,255,255,0.14);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-sm);
  font-size: 0.78rem;
  color: rgba(255,255,255,0.65);
}

/* --- Modal --- */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(10,18,34,0.55);
  z-index: 250;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; visibility: hidden;
  transition: all 0.3s;
  padding: var(--space-md);
}
.modal-overlay.open { opacity: 1; visibility: visible; }
.modal {
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  max-width: 440px;
  width: 100%;
  text-align: center;
  box-shadow: var(--shadow-lg);
  transform: scale(0.95);
  transition: transform 0.3s;
}
.modal-overlay.open .modal { transform: scale(1); }
.modal-icon {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--color-accent-soft);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto var(--space-sm);
}
.modal-icon svg { width: 28px; height: 28px; color: var(--color-brand); }
.modal h3 { margin-bottom: var(--space-xs); }
.modal p { color: var(--color-ink-dim); margin-bottom: var(--space-md); font-size: 0.92rem; line-height: 1.6; }

/* --- Back to top --- */
.back-to-top {
  position: fixed;
  bottom: 2rem; right: 2rem;
  width: 44px; height: 44px;
  border-radius: var(--radius);
  background: var(--color-brand-strong);
  color: #fff;
  border: none;
  display: flex; align-items: center; justify-content: center;
  z-index: 90;
  opacity: 0; visibility: hidden;
  transform: translateY(10px);
  transition: all 0.3s ease;
  box-shadow: var(--shadow-md);
}
.back-to-top.visible { opacity: 1; visibility: visible; transform: translateY(0); }
.back-to-top:hover { filter: brightness(1.1); transform: translateY(-2px); }
.back-to-top svg { width: 20px; height: 20px; }

/* --- Animations ---
   Personalcare flavor: calm, editorial. Reveals sit at the slow/soft end of
   the brief's 12-20px / 400-600ms range with a gentle expo-out ease, not the
   snappier default-ease used on higher-energy packs (fitness). Everything
   here is transform+opacity only (compositor-safe). The whole system is
   opt-in via html.js-anim (added by main.js only when JS runs AND
   prefers-reduced-motion is off) -- so with JS disabled, or with reduced
   motion on, .fade-up/.service-card/etc. simply never match these hidden-
   state rules and stay at their normal, fully-visible layout. That is also
   why the failsafe/no-JS story needs no separate reduced-motion override
   below for the reveal or its stagger: only the two unconditional (non-JS-
   gated) additions -- hero entrance and hover transforms -- need one. */
html.js-anim .fade-up {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
html.js-anim .fade-up.visible { opacity: 1; transform: translateY(0); }

/* Card-grid cascade: a section's cards settle a beat after one another once
   the SECTION's own .visible lands -- no second IntersectionObserver, riding
   the one that already exists. Delays are capped at 6 explicit steps; any
   further items in a longer grid (e.g. an 8-item price list) share the
   smallest remaining delay rather than growing the wait indefinitely. The
   gallery mosaic uses this same mechanism -- its per-image stagger is the
   one place this cascade is most visible, per the pack's photo-led brief.
   Budget note: validate-template's capture sweep (scripts/validate-template.mjs)
   scrolls the whole page then waits 700ms before shooting -- this pack was
   ONE of the two false-"blank section" repeat offenders that comment calls
   out by name. Worst-case settle (last step's delay + transition duration)
   is kept at 0.65s, under that 700ms budget with margin, so the LAST card in
   the LAST grid is still fully opaque at capture time, not just "close". */
html.js-anim .fade-up .service-card,
html.js-anim .fade-up .gallery-item,
html.js-anim .fade-up .team-card,
html.js-anim .fade-up .certification-card,
html.js-anim .fade-up .review-card,
html.js-anim .fade-up .step-card,
html.js-anim .fade-up .area-card {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1), transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
html.js-anim .fade-up.visible .service-card,
html.js-anim .fade-up.visible .gallery-item,
html.js-anim .fade-up.visible .team-card,
html.js-anim .fade-up.visible .certification-card,
html.js-anim .fade-up.visible .review-card,
html.js-anim .fade-up.visible .step-card,
html.js-anim .fade-up.visible .area-card {
  opacity: 1;
  transform: translateY(0);
}
html.js-anim .fade-up.visible .service-card:nth-child(2),
html.js-anim .fade-up.visible .gallery-item:nth-child(2),
html.js-anim .fade-up.visible .team-card:nth-child(2),
html.js-anim .fade-up.visible .certification-card:nth-child(2),
html.js-anim .fade-up.visible .review-card:nth-child(2),
html.js-anim .fade-up.visible .step-card:nth-child(2),
html.js-anim .fade-up.visible .area-card:nth-child(2) { transition-delay: 0.05s; }
html.js-anim .fade-up.visible .service-card:nth-child(3),
html.js-anim .fade-up.visible .gallery-item:nth-child(3),
html.js-anim .fade-up.visible .team-card:nth-child(3),
html.js-anim .fade-up.visible .certification-card:nth-child(3),
html.js-anim .fade-up.visible .review-card:nth-child(3),
html.js-anim .fade-up.visible .step-card:nth-child(3),
html.js-anim .fade-up.visible .area-card:nth-child(3) { transition-delay: 0.10s; }
html.js-anim .fade-up.visible .service-card:nth-child(4),
html.js-anim .fade-up.visible .gallery-item:nth-child(4),
html.js-anim .fade-up.visible .team-card:nth-child(4),
html.js-anim .fade-up.visible .certification-card:nth-child(4),
html.js-anim .fade-up.visible .review-card:nth-child(4),
html.js-anim .fade-up.visible .step-card:nth-child(4),
html.js-anim .fade-up.visible .area-card:nth-child(4) { transition-delay: 0.15s; }
html.js-anim .fade-up.visible .service-card:nth-child(5),
html.js-anim .fade-up.visible .gallery-item:nth-child(5),
html.js-anim .fade-up.visible .team-card:nth-child(5),
html.js-anim .fade-up.visible .certification-card:nth-child(5),
html.js-anim .fade-up.visible .review-card:nth-child(5),
html.js-anim .fade-up.visible .step-card:nth-child(5),
html.js-anim .fade-up.visible .area-card:nth-child(5) { transition-delay: 0.20s; }
html.js-anim .fade-up.visible .service-card:nth-child(n+6),
html.js-anim .fade-up.visible .gallery-item:nth-child(n+6),
html.js-anim .fade-up.visible .team-card:nth-child(n+6),
html.js-anim .fade-up.visible .certification-card:nth-child(n+6),
html.js-anim .fade-up.visible .review-card:nth-child(n+6),
html.js-anim .fade-up.visible .step-card:nth-child(n+6),
html.js-anim .fade-up.visible .area-card:nth-child(n+6) { transition-delay: 0.25s; }

/* Hover restoration for cascaded cards (confirmed regression, matching
   trades-static's proven fix -- templates/trades-static/css/style.css
   ~line 1473): `html.js-anim .fade-up.visible .service-card` above (4
   classes + html) permanently pins `transform: translateY(0)` once a card
   has settled, which outguns the plain 2-component `.service-card:hover`
   rule declared earlier in this file -- so once JS has run and the card
   scrolled into view, hovering it silently does nothing (confirmed via
   Playwright getComputedStyle, see layoutwave-personalcare.md). Only
   service-card/team-card/certification-card/review-card/area-card are
   affected: they're the cascaded types that ALSO define their own
   `transform` on `:hover` (gallery-item's hover transform targets the
   child <img>, not the cascaded element itself, and step-card has no hover
   transform, so neither collides). Fix: an explicit hover rule that MIRRORS
   the settle selector's class chain (html.js-anim .fade-up.visible
   .<card>:hover -- 5 components, beats the settle rule's 4 outright rather
   than relying on a source-order tie) restoring each card's ORIGINAL hover
   transform/shadow/border, with a fast transition and `transition-delay:
   0s` so hover never inherits a settled card's lingering nth-child reveal
   delay (0.05s-0.25s, still attached since `.visible` never gets removed).
   The plain `.service-card:hover` etc. rules earlier stay as-is for the
   no-JS / reduced-motion path (html.js-anim never applies there). */
html.js-anim .fade-up.visible .service-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--color-accent) 35%, var(--color-border));
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
  transition-delay: 0s;
}
html.js-anim .fade-up.visible .team-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--color-accent) 30%, var(--color-border));
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
  transition-delay: 0s;
}
html.js-anim .fade-up.visible .certification-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--color-accent) 35%, var(--color-border));
  transition: border-color 0.2s, transform 0.2s;
  transition-delay: 0s;
}
html.js-anim .fade-up.visible .review-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--color-accent) 30%, var(--color-border));
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
  transition-delay: 0s;
}
html.js-anim .fade-up.visible .area-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border));
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
  transition-delay: 0s;
}
/* calm-mode services list (layout-variety wave, above) deliberately kills
   the lift -- list rows shouldn't float -- so the restoration above must
   not resurrect it there. `.services-section` is the ANCESTOR of `.fade-up`
   (the .services-grid div carries that class, not the section), so it has
   to come first in the chain -- the reverse order silently fails to match
   anything (confirmed via Playwright: the first attempt still lifted).
   Six class-level components + html outguns the restoration's five,
   regardless of source order. */
html.js-anim [data-mood="calm"] .services-section .fade-up.visible .service-card:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--color-border);
  background: color-mix(in srgb, var(--color-accent) 5%, transparent);
  transition-delay: 0s;
}

/* Hero entrance: a pure-CSS keyframe (paint-triggered, no JS) so it plays
   identically with JS disabled and is fully settled well under a second
   after paint -- long before any post-load screenshot. Composed order:
   badge, headline, result stat, subhead, actions, ~70ms apart, finishing at
   0.78s total (under the 800ms budget). Unlike the scroll reveal above this
   ISN'T gated by html.js-anim (it has to run without JS too), so it gets its
   own explicit reduced-motion override below. */
@keyframes heroReveal {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.hero-badge, .hero h1, .hero-result, .hero-sub, .hero-actions {
  animation: heroReveal 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.hero-badge   { animation-delay: 0s; }
.hero h1      { animation-delay: 0.07s; }
.hero-result  { animation-delay: 0.14s; }
.hero-sub     { animation-delay: 0.21s; }
.hero-actions { animation-delay: 0.28s; }

@media (prefers-reduced-motion: reduce) {
  .fade-up { opacity: 1; transform: none; transition: none; }
  .hero-badge, .hero h1, .hero-result, .hero-sub, .hero-actions { animation: none; }
  .btn:hover, .back-to-top:hover, .service-card:hover, .team-card:hover, .review-card:hover, .certification-card:hover, .proof-point:hover, .area-card:hover, .gallery-item:hover img, .nav-cta:hover, .sw-btn-primary:hover { transform: none; }
}

/* --- Preview disclosure badge (required on every preview) --- */
.preview-badge {
  position: fixed;
  left: 12px; bottom: 12px;
  z-index: 2000;
  display: inline-flex;
  align-items: stretch;
  background: rgba(10, 18, 34, 0.86);
  font-family: var(--font-sans);
  font-size: 0.72rem;
  line-height: 1;
  border-radius: var(--radius);
  overflow: hidden;
  opacity: 0.92;
}
.preview-badge:hover, .preview-badge:focus-within { opacity: 1; }
.preview-badge a { display: inline-flex; align-items: center; padding: 7px 10px; text-decoration: none; white-space: nowrap; }
.preview-badge a:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.preview-badge-info, .preview-badge-info:hover { color: #fff; }
.preview-badge-cta { background: var(--color-accent); color: var(--color-hero-ink-2); font-weight: 700; }
.preview-badge-cta:hover { background: var(--color-accent); filter: brightness(1.06); color: var(--color-hero-ink-2); }

/* --- Mobile sticky call bar (mobile only, 6048202474-gated in
   index.html/services.html; ported from templates/trades-static/css/
   style.css). The nav phone pill (`.nav-phone`) disappears at the same
   1024px breakpoint this bar turns on at, and the nav CTA drops again under
   620px, so this becomes the one always-visible way to call or book. Uses
   this pack's own palette vocabulary rather than trades-static's (no
   --color-accent-strong/--color-accent-fg here): call mirrors `.btn-accent`
   (accent fill), book mirrors `.btn-primary` (brand-strong fill). Sits above
   the booking form/back-to-top/preview badge but under the booking-request
   modal overlay; `.sw-booking`/`.footer` get matching extra bottom padding at
   the same breakpoint below so the bar never covers the booking submit
   button or the footer's last row. --- */
.mobile-call-bar {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 150;
  padding-bottom: env(safe-area-inset-bottom, 0);
  box-shadow: 0 -8px 24px -12px rgba(12,20,38,0.35);
}
.mobile-call-bar a {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.95rem 1rem;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.86rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.mobile-call-btn { background: var(--color-accent); color: var(--color-hero-ink-2); }
.mobile-call-btn svg { width: 19px; height: 19px; }
.mobile-book-btn { background: var(--color-brand-strong); color: var(--color-brand-fg); }

/* --- Responsive --- */
@media (max-width: 1024px) {
  .nav-toggle { display: flex; }
  .nav-links {
    position: fixed;
    top: 0; right: -100%;
    width: 280px; height: 100vh;
    background: var(--color-bg);
    flex-direction: column;
    align-items: flex-start;
    padding: 5rem var(--space-lg) var(--space-lg);
    gap: 1.5rem;
    /* Closed but still off-screen (right:-100%) is not the same as hidden:
       without visibility toggling these links stayed in the tab order while
       invisible, so keyboard users tabbed through ghost links before ever
       reaching the toggle. visibility is delayed on close so the slide-out
       animation still plays, and is instant on open. */
    visibility: hidden;
    transition: right 0.3s ease, visibility 0s linear 0.3s;
    box-shadow: var(--shadow-lg);
  }
  .nav-links.open { right: 0; visibility: visible; transition: right 0.3s ease; }
  .nav:not(.scrolled) .nav-links a { color: var(--color-ink); text-shadow: none; }
  .nav-phone { display: none; }
  .faq-intro { position: static; }
  /* The nav phone pill is gone below this breakpoint: the sticky call bar
     replaces it so the phone/booking CTA is never more than a tap away. */
  .mobile-call-bar { display: flex; }
  .back-to-top { bottom: calc(64px + 1rem); }
  .preview-badge { bottom: calc(64px + 12px); }
  .footer { padding-bottom: calc(64px + var(--space-md)); }
  .sw-booking { padding-bottom: calc(64px + clamp(56px, 9vw, 104px)); }
}

@media (max-width: 900px) {
  .about-grid, .about-grid.no-media { grid-template-columns: 1fr; }
  .about-media { max-width: 560px; }
  .about-media img { height: auto; min-height: 0; aspect-ratio: 4 / 3.4; }
  .about-media::before { inset: 14px -14px -14px 14px; }
  /* Media-side-swap alternate (calm/classic) collapses to a single column
     here like the default; reset the desktop-only order swap and mirrored
     frame/badge offsets so mobile always leads with the photo, matching the
     default reading order and every other layout on the page. MUST repeat
     the `[data-mood=...]` prefix on every reset below: a plain `.about-grid
     { grid-template-columns: 1fr }` (or `.about-media { order: 0 }`) here is
     LOWER specificity than the unconditional swap rule above, so it silently
     loses the cascade even inside this narrower media query -- confirmed via
     a real render (the swap rule's 2-column track stayed active and the
     photo rendered as a stray thumbnail instead of collapsing to full width,
     screenshot: calm-mobile-about.png before this fix). */
  [data-mood="calm"] .about-grid:not(.no-media),
  [data-mood="classic"] .about-grid:not(.no-media) { grid-template-columns: 1fr; }
  [data-mood="calm"] .about-media,
  [data-mood="classic"] .about-media,
  [data-mood="calm"] .about-text,
  [data-mood="classic"] .about-text { order: 0; }
  [data-mood="calm"] .about-media::before,
  [data-mood="classic"] .about-media::before { inset: 14px -14px -14px 14px; }
  [data-mood="calm"] .about-badge,
  [data-mood="classic"] .about-badge { left: -14px; right: auto; }
  .services-grid,
  .gallery-grid,
  .reviews-grid,
  .steps-grid,
  .team-grid { grid-template-columns: 1fr 1fr; }
  .contact-grid,
  .faq-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; gap: var(--space-md); }
  /* Split + bandoverlay collapse to a single column on smaller screens. */
  .hero-split .hero-inner { grid-template-columns: 1fr; min-height: auto; }
  .hero-split .hero-content { padding: 7rem clamp(1.5rem, 6vw, 2.5rem) 3rem; }
  .hero-split .hero-figure { order: 2; min-height: 44vh; }
  .hero-split .hero-figure::after { display: none; }
  .hero-bandoverlay .hero-content { max-width: none; }
}

@media (max-width: 620px) {
  /* On phones the mobile nav is logo + hamburger only; the drawer links + the
     prominent hero CTA carry the action, so the long nav CTA can drop and give
     a long wordmark room to breathe. */
  .nav-cta { display: none; }
  .hero { min-height: 100svh; }
  /* Vertical rhythm tightened on phones so the hero (badge + h1 + sub +
     actions) plus the fixed nav clearance actually fits inside one
     screen's worth of height on common phone viewports (~667-780px) —
     otherwise the CTA lands below the fold, the opposite of the thumb-zone
     goal. Nothing here shrinks the h1 (that stays large/dominant per the
     type wave); only the badge/sub/actions spacing and sub copy size. */
  .hero-content { padding-top: 6.25rem; padding-bottom: 2.5rem; }
  .hero-split .hero-content { padding-top: 6.25rem; }
  .hero-bandoverlay .hero-inner { min-height: 100svh; }
  .hero-badge { margin-bottom: 0.85rem; }
  .hero-sub { font-size: 1.02rem; line-height: 1.55; margin-bottom: 1.75rem; }
  .hero-actions { flex-direction: column; align-items: center; margin-bottom: 1.25rem; }
  .hero-actions .btn { width: 100%; }
  /* Thumb zone: the ONE dominant CTA (book) sits closest to the thumb at
     the bottom of the stacked pair; DOM order is unchanged so screen
     readers/keyboard tab order still meet the primary action first
     (arguably a plus, not a tradeoff) — this is a presentation-only
     reorder via flex `order`. */
  .hero-actions .btn-primary { order: 2; }
  .hero-call-link { order: 1; }
  .services-grid,
  .reviews-grid,
  .steps-grid,
  .team-grid,
  .proof-points { grid-template-columns: 1fr; }
  .team-grid { max-width: 380px; }
  /* Gallery: personal care sells on photos, and a single-column stack of
     identical 4:5 tiles reads as a monotonous wall at phone width. Two
     columns plus a staggered aspect ratio on every third tile (align-items:
     start on .gallery-grid means the shorter tile just leaves a gap rather
     than being stretched) keeps it reading as a considered mosaic instead
     of a same-height wall. */
  .gallery-grid { grid-template-columns: repeat(2, 1fr); column-gap: 0.85rem; row-gap: 0.85rem; }
  .gallery-item:nth-child(3n+2) img { aspect-ratio: 1 / 1.15; }
  .form-row { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }
  .footer-bottom { flex-direction: column; gap: 0.5rem; text-align: center; }
  .contact-detail span br { display: none; }
  .about-badge { left: 0; }
  /* Same specificity trap as the 900px reset above: the swap rule's
     `[data-mood=...] .about-badge` (2 components) beats this plain
     `.about-badge` (1 component) at any width, so it must be repeated here
     too for the swap moods to pick up the tighter phone-width edge value. */
  [data-mood="calm"] .about-badge,
  [data-mood="classic"] .about-badge { left: 0; right: auto; }
  .review-lead { padding: 2.25rem 1.75rem; }
  /* calm-mode services list: shrink the trailing photo thumbnail so it never
     crowds the title/description columns at phone width. */
  [data-mood="calm"] .services-section .service-card:has(.service-card-bg) { padding-right: calc(0.25rem + 44px + 1rem); }
  [data-mood="calm"] .services-section .service-card-bg { width: 44px; height: 44px; }
  /* bold-mode services bento: the featured first card no longer needs a
     2-column span once the grid itself is single-column here. */
  [data-mood="bold"] .services-section .service-card:first-child { grid-column: auto; }
}

/* Short viewports (iPhone SE class, ~667-700px tall, independent of width):
   even after the 620px pass above, the hero's primary CTA button landed
   underneath the fixed mobile-call-bar (and, tighter still, the always-on
   preview disclosure badge) on the shortest common phone screens — measured
   the button bottom at y=640 against the call bar starting at y=615 on a
   375x667 viewport. The badge/sub/actions get a second, height-driven trim;
   the sub copy is visually clamped to 3 lines (full text stays in the DOM
   unchanged, this is a presentational clamp for a very constrained
   viewport, not a copy rewrite) rather than hidden outright, so the value
   prop is still mostly readable while the CTA clears the fixed overlays. */
@media (max-width: 620px) and (max-height: 720px) {
  .hero-content { padding-top: 5.5rem; padding-bottom: 1.5rem; }
  .hero-badge { margin-bottom: 0.6rem; }
  .hero-sub {
    margin-bottom: 1.25rem;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
  }
  .hero-actions { margin-bottom: 0.5rem; }
}

/* Small phones (iPhone SE and similar, ~390px): the emphasis/compact rhythm
   above is tuned for tablet/desktop scale and needs its own pass here so a
   "featured" section doesn't overwhelm a small viewport and a "condensed"
   one doesn't crush its title past readable. */
@media (max-width: 390px) {
  [data-emph="1"] { padding-top: 2.5rem; padding-bottom: 2.5rem; }
  [data-emph="1"] .section-title { font-size: 2.05rem; }
  [data-emph="1"] .section-desc { font-size: 1rem; }
  [data-emph="1"] .services-grid,
  [data-emph="1"] .gallery-grid,
  [data-emph="1"] .team-grid,
  [data-emph="1"] .reviews-grid,
  [data-emph="1"] .steps-grid { gap: var(--space-md); }
  [data-compact="1"] { padding-top: 1.5rem; padding-bottom: 1.5rem; } /* 24px, on-grid (was 1.25rem/20px) */
  [data-compact="1"] .section-title { font-size: 1.5rem; }
  /* Keep vertical padding at the base 0.95rem here (only trim horizontal +
     font-size for the tighter width): the base rule's 0.85rem vertical
     padding + this size text drops the tap target under the 44px floor on
     the smallest phones. */
  .mobile-call-bar a { padding: 0.95rem 0.5rem; font-size: 0.78rem; }
}

/* ============================================
   Shared widgets (templates/frameworks/_base/widgets/), appended verbatim per
   WIDGETS.md §5 step 1 ("include the widget CSS"). Do not hand-tune these rules
   per pack — that defeats the "one fix propagates to every pack" contract; they
   already theme correctly from this file's :root core vars (§1) above.
   ============================================ */

/* sw-booking — intake/booking form widget (WIDGETS.md §4).
   Styles reference ONLY core theme vars (§1) with fallbacks, so this drops into any
   pack. Quality floors baked in: 16px controls, ONE solid-accent primary CTA,
   ghost secondary. */
.sw-booking {
  position: relative;
  padding: clamp(56px, 9vw, 104px) 20px;
  background: var(--color-bg, #fff);
}
/* Photo-backed variant (widgets.booking.image present): a dimmed real photo
   behind the band, brand-gradient scrim ~62% opaque (clears the >=55% AA
   floor on the white heading/desc). No image = the flat --color-bg fallback
   above, never a bare/broken slot. */
.sw-booking[data-media="1"] {
  overflow: hidden;
  background: linear-gradient(150deg, var(--color-hero-ink, #16202b) 0%, var(--color-hero-ink-2, #0c1219) 100%);
  color: #fff;
}
.sw-booking-media { position: absolute; inset: 0; z-index: 0; }
.sw-booking-media img { width: 100%; height: 100%; object-fit: cover; }
.sw-booking-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(150deg, var(--color-hero-ink, #16202b) 0%, var(--color-hero-ink-2, #0c1219) 100%);
  opacity: 0.62;
}
.sw-booking[data-media="1"] .sw-booking-inner { position: relative; z-index: 2; }
.sw-booking[data-media="1"] .sw-booking-head .section-label { color: var(--color-accent); }
.sw-booking[data-media="1"] .sw-booking-head .section-title,
.sw-booking[data-media="1"] .sw-booking-head .section-desc { color: #fff; }
.sw-booking[data-media="1"] .sw-booking-head .section-desc { color: rgba(255, 255, 255, 0.88); }
.sw-booking[data-media="1"] .sw-booking-form {
  background: var(--color-bg, #fff);
  box-shadow: 0 8px 18px -10px rgba(15, 23, 32, 0.3), var(--shadow-lg);
}
.sw-booking[data-media="1"] .sw-booking-actions-callonly a.sw-btn-ghost { color: rgba(255, 255, 255, 0.85); }
.sw-booking-inner {
  max-width: 760px;
  margin: 0 auto;
}
.sw-booking-head { text-align: center; margin-bottom: 28px; }

.sw-booking-form {
  background: color-mix(in srgb, var(--color-brand, #1d2733) 4%, var(--color-bg, #fff));
  border: 1px solid var(--color-border, rgba(0, 0, 0, 0.1));
  border-radius: var(--radius, 12px);
  padding: clamp(20px, 4vw, 32px);
}
.sw-booking-form .form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
@media (max-width: 640px) { .sw-booking-form .form-row { grid-template-columns: 1fr; } }
.sw-booking-form .form-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
/* Matches the eyebrow/label typography used across the rest of the pack
   (uppercase, tracked, muted) instead of a plain sentence-case caption, so the
   form reads as part of the same system rather than a bolted-on widget. */
.sw-booking-form label {
  font-family: var(--font-sans, inherit);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-ink-dim, #5b6673);
}
.sw-booking-form input,
.sw-booking-form textarea {
  font: inherit;
  font-size: 1rem; /* 16px floor: no zoom-on-focus on iOS, readable in captures */
  color: var(--color-ink, #16202b);
  background: var(--color-bg, #fff);
  border: 1px solid var(--color-border, rgba(0, 0, 0, 0.14));
  border-radius: calc(var(--radius, 12px) * 0.66);
  padding: 13px 15px;
}
.sw-booking-form input:focus,
.sw-booking-form textarea:focus {
  outline: 2px solid var(--color-accent, #2f6bff);
  outline-offset: 1px;
  border-color: var(--color-accent, #2f6bff);
}

.sw-booking-actions {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 6px;
}
/* No published email means no honest mailto target, so the booking form is not
   rendered at all and this call-first row takes its place. Centred because it
   stands alone in the section rather than closing a form. */
.sw-booking-actions-callonly {
  justify-content: center;
  margin-top: 0;
}
/* Marker class on the logo <img> when only a single logo variant exists; all
   visual styling comes from the co-applied .nav-logo-img rule. */
.nav-logo-single { display: block; }
/* THE primary CTA: solid accent, 16px, generous hit area, subtle lift. Exactly one. */
.sw-btn-primary {
  font: 600 1rem/1.2 inherit;
  color: var(--color-accent-ink, #fff);
  background: var(--color-accent, #2f6bff);
  border: 0;
  border-radius: 999px;
  padding: 14px 28px;
  cursor: pointer;
  box-shadow: 0 8px 22px -10px color-mix(in srgb, var(--color-accent, #2f6bff) 70%, transparent);
  transition: transform 0.15s ease, filter 0.15s ease;
}
.sw-btn-primary:hover { transform: translateY(-1px); filter: brightness(1.06); }
/* Secondary: quiet by design so it never competes with the primary. */
.sw-btn-ghost {
  font: 600 0.9375rem/1.2 inherit;
  color: var(--color-ink-dim, #5b6673);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.sw-btn-ghost:hover { color: var(--color-ink, #16202b); }

/* sw-trust — single-instance trust strip (WIDGETS.md §4). Core vars + fallbacks only. */
.sw-trust {
  background: color-mix(in srgb, var(--color-brand, #1d2733) 92%, black);
  color: #fff;
  padding: clamp(22px, 3.4vw, 30px) 20px;
}
.sw-trust-inner {
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(32px, 6vw, 76px);
  flex-wrap: wrap;
}
.sw-trust-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  text-align: center;
}
.sw-trust-value {
  font: 700 clamp(1.2rem, 2.2vw, 1.45rem)/1.15 var(--font-display, inherit);
  letter-spacing: 0.01em;
  white-space: nowrap;
}
/* Uppercase + tracked label matches the section-label typography used
   throughout the rest of the pack, giving the strip the same deliberate feel
   instead of a plain sentence-case caption. */
.sw-trust-label {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.78;
  white-space: nowrap;
}

/* --- Mobile fixed-widget clearance (hero-trust clip fix, 2026-08) ----------
   At phone widths the sticky .mobile-call-bar (64px tall) plus the
   .preview-badge pill pinned above it (bottom: 64px + 12px, ~26px tall) own
   the viewport's bottom ~104px. The bandoverlay hero rests bottom-anchored at
   full viewport height, which parked its last trust chip inside that band,
   clipped behind both badges. Extra bottom padding keeps the hero's last row
   above the fixed stack. Appended last so it wins the cascade over the
   archetype/variant hero rules above. */
@media (max-width: 620px) {
  .hero-bandoverlay .hero-inner { padding-bottom: calc(64px + 12px + 2.5rem); }
}
