/* =============================================================================
   MASTER: clinical-ivyrehab-static - concept preview by PXLNorth
   Parameterized from the operator-approved northsimcoephysio.com recreation of
   the ivyrehab blueprint (templates/blueprints/clinical/physio-ivyrehab).
   Structure and density come from the blueprint; every color and font below is
   a slot the tailor pass fills per prospect.

   TYPE PAIRING
   ------------
   The font custom properties and the Google Fonts link are content-driven
   (content.json fonts object). The shipped default is the source build's
   pairing: Newsreader, a humanist serif with a genuinely drawn italic that the
   mission / friction / testimonial headlines lean on, plus Instrument Sans for
   the dense practical detail a patient actually reads. The tailor pass picks a
   pairing for each prospect's identity and documents the choice in the run.
   Never Fraunces (operator taste rule).

   COLOR SLOTS - every slot reads the fill-static palette (paletteMode pro);
   theme.json SHOULD pin the full scale via its pro object (see template.json).
   The prospect's real site scheme drives the slots, logo colors are accents:
     teal slot          : the prospect's PRIMARY brand color (nav links, pills,
                          footer, mission band, star rows)
     wine slot          : the prospect's secondary HOT color - the blueprint
                          spends it on exactly ONE band (friction) plus the
                          pull-quote text. Map it to a real sampled secondary,
                          never an imported hue they don't use.
     tint slots         : pale tints of the primary (testimonial field, form
                          card, tile placeholders)
     ink slots          : near-black text scale
   Master-specific slot names fall back to derived standard keys when unpinned
   so an unpinned theme still renders a coherent AA page, never an empty var.
   Brand-derived alpha tints use color-mix from the vars, so the whole page
   re-themes from the palette alone.
   ============================================================================= */

:root {
  --teal: #0a2a6a;
  --teal-deep: #082153;
  --teal-darker: #04122d;
  --teal-light: #8eaae1;
  --tint: #dee5f2;
  --tint-soft: #f0f3f9;
  --wine: #524327;
  --wine-light: #665332;
  --wine-deep: #3f341a;
  --ink: #191d24;
  --ink-soft: #3a3f4b;
  --grey: #616671;
  --grey-line: #dde0e7;
  --paper: #ffffff;
  --paper-warm: #fbfbfb;
  /* Review gold. NOT a brand slot: a filled review star is a cross-site
     convention the reader decodes before they read the palette, the same way
     the Google mark beside it is Google's colours and not ours. The default is
     the deep gold measured on the approved medical demo (3.15:1 on its tint,
     4.06:1 on the white chip - both clear the 3:1 bar for a meaningful
     graphic). A prospect whose testimonial tint kills that contrast is fixed by
     PINNING palette.reviewGold in theme.json, never by hard-coding here. */
  --gold: #B0700A;

  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  --sans: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --wrap: 1240px;
  --gutter: clamp(1.25rem, 4vw, 3.5rem);
  --radius: 14px;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: clamp(1rem, 0.97rem + 0.16vw, 1.0625rem);
  line-height: 1.65;
  color: var(--ink-soft);
  background: var(--paper);
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }

h1, h2, h3, h4 {
  font-family: var(--serif);
  color: var(--ink);
  font-weight: 400;
  line-height: 1.12;
  margin: 0;
  letter-spacing: -0.01em;
}

p { margin: 0 0 1.05em; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: #fff; padding: 0.7rem 1.1rem; border-radius: 0 0 8px 0;
  font-size: 0.9rem;
}
.skip-link:focus { left: 0; }

:where(a, button, input, select, textarea):focus-visible {
  outline: 3px solid var(--wine-light);
  outline-offset: 2px;
}

/* ---------- shared bits -------------------------------------------------- */

.eyebrow {
  font-family: var(--sans);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin: 0 0 1.1rem;
}

.serif-italic { font-style: italic; }

.pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-family: var(--sans);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1;
  padding: 0.95rem 1.6rem;
  border-radius: 999px;
  border: 1.5px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .18s ease, color .18s ease, border-color .18s ease, transform .18s ease;
  white-space: nowrap;
}
.pill:hover { transform: translateY(-1px); }

.pill-teal { background: var(--teal); color: #fff; }
.pill-teal:hover { background: var(--teal-deep); }

.pill-wine { background: var(--wine); color: #fff; }
.pill-wine:hover { background: var(--wine-deep); }

.pill-white { background: #fff; color: var(--ink); }
.pill-white:hover { background: var(--tint); }

.pill-ghost-dark { background: transparent; color: var(--ink); border-color: color-mix(in srgb, var(--ink) 28%, transparent); }
.pill-ghost-dark:hover { background: var(--ink); color: #fff; border-color: var(--ink); }

.pill-ghost-light { background: transparent; color: #fff; border-color: rgba(255,255,255,.55); }
.pill-ghost-light:hover { background: #fff; color: var(--ink); border-color: #fff; }

.section { padding-block: clamp(3.5rem, 7vw, 6.5rem); }

/* ---------- 1. utility bar ----------------------------------------------- */

.utility {
  background: var(--ink);
  color: rgba(255,255,255,.82);
  font-size: 0.8125rem;
  letter-spacing: 0.01em;
}
.utility .wrap {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.6rem;
  min-height: 42px; padding-block: 0.5rem;
}
.utility a { text-decoration: none; }
.utility a:hover { color: #fff; text-decoration: underline; }
.utility-sep { margin-left: auto; }
.utility-strong { color: #fff; font-weight: 600; }

@media (max-width: 720px) {
  .utility { font-size: 0.75rem; }
  .utility .wrap { justify-content: center; gap: 0.35rem 1rem; }
  .utility-sep { margin-left: 0; }
  .utility-hide-sm { display: none; }
}

/* ---------- 2. nav -------------------------------------------------------
   The blueprint runs a solid brand-colored nav with a knocked-out wordmark.
   Many real prospect logotypes are multi-tone and collapse to a blob on a
   brand field, and most clinic sites run a white header, so this master keeps
   the nav white and carries the true logo. The brand color is spent on the
   mission band, the testimonial tint and the footer instead.
   ------------------------------------------------------------------------- */

.nav {
  position: sticky; top: 0; z-index: 60;
  background: rgba(255,255,255,.96);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--grey-line);
}
.nav .wrap {
  display: flex; align-items: center; gap: 1.25rem;
  min-height: 78px; padding-block: 0.75rem;
}
.nav-logo { display: block; flex: 0 0 auto; }
.nav-logo img { width: clamp(148px, 17vw, 200px); height: auto; }
/* Square badge / icon-mark logos (brandLogo.badge): size by HEIGHT so a 1:1
   mark never inflates the sticky bar. Additive + content-gated - wide-lockup
   prospects and the oracle build render byte-identically without the flag. */
.nav-logo img.logo-badge { width: auto; height: 52px; }
.nav-wordmark { font-family: var(--serif); font-size: clamp(1.15rem, 2vw, 1.45rem); color: var(--ink); letter-spacing: -0.01em; white-space: nowrap; }

.nav-links {
  display: flex; align-items: center; gap: clamp(1rem, 2vw, 1.9rem);
  margin-left: auto;
  list-style: none; padding: 0; margin-block: 0;
}
.nav-links a {
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ink-soft);
  padding-block: 0.4rem;
  border-bottom: 2px solid transparent;
}
.nav-links a:hover { color: var(--teal-deep); border-bottom-color: var(--teal); }

.nav-cta { flex: 0 0 auto; }

.nav-toggle {
  display: none;
  margin-left: auto;
  background: none; border: 1px solid var(--grey-line); border-radius: 10px;
  width: 44px; height: 44px; cursor: pointer;
  align-items: center; justify-content: center;
  color: var(--ink);
}
.nav-toggle span, .nav-toggle span::before, .nav-toggle span::after {
  content: ""; display: block; width: 20px; height: 2px; background: currentColor; border-radius: 2px;
}
.nav-toggle span { position: relative; }
.nav-toggle span::before { position: absolute; top: -6px; }
.nav-toggle span::after { position: absolute; top: 6px; }

@media (max-width: 960px) {
  .nav .wrap { min-height: 66px; }
  .nav-toggle { display: inline-flex; }
  .nav-links {
    position: absolute; inset: 100% 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: #fff; border-bottom: 1px solid var(--grey-line);
    padding: 0.5rem var(--gutter) 1.1rem;
    margin-left: 0;
    display: none;
  }
  .nav[data-open="true"] .nav-links { display: flex; }
  .nav-links a { padding-block: 0.85rem; border-bottom: 1px solid var(--grey-line); }
  .nav-links li:last-child a { border-bottom: 0; }
  .nav-cta { display: none; }
}

/* ---------- 3. hero -------------------------------------------------------
   Blueprint: a three-panel band of real clinic photography, edge to edge, with
   a centred white serif H1 over it and ONE conversion control. The blueprint's
   control is a multi-location zip finder; scaled to a single-location clinic
   that becomes a two-field callback request plus the real street address.
   Stage three real photos (panel A desktop-left, B centre + the mobile hero,
   C desktop-right); a lone panel spans the full band as a fallback.
   ------------------------------------------------------------------------- */

.hero { position: relative; isolation: isolate; background: var(--ink); }

.hero-panels {
  position: absolute; inset: 0; z-index: -2;
  display: grid; grid-template-columns: 1fr 1fr 1fr;
}
.hero-panels img { width: 100%; height: 100%; object-fit: cover; }
.hero-panels .panel-b { object-position: 50% 35%; }
.hero-panels img:only-child { grid-column: 1 / -1; }

.hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(180deg, rgba(20,26,25,.62) 0%, rgba(20,26,25,.48) 42%, rgba(20,26,25,.80) 100%),
    linear-gradient(90deg, rgba(31,37,35,.30) 0%, rgba(31,37,35,0) 30%, rgba(31,37,35,0) 70%, rgba(31,37,35,.30) 100%);
}

.hero-inner {
  padding-block: clamp(3.25rem, 8vw, 6.5rem);
  min-height: clamp(520px, 74vh, 720px);
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  text-align: center;
}

.hero h1 {
  color: #fff;
  font-size: clamp(1.95rem, 1.05rem + 3.7vw, 4rem);
  max-width: 22ch;
  text-wrap: balance;
  text-shadow: 0 2px 24px rgba(0,0,0,.42);
  margin-bottom: clamp(1.4rem, 3vw, 2.1rem);
}

/* ONE accent-colored hero word (TASTE_LEDGER #41: "punchy headlines... wisely use
   color for accent word"). Rendered only when content.json authors hero.h1Accent;
   with no accent authored the h1 is a plain string and this rule never applies.
   --teal-light is the LIGHT brand slot (palette.brandLight, "light brand on dark
   surfaces"): the h1 sits white-on-photo, so the darker --teal would sink into the
   scrim. #41's pixel-contrast duty is the TAILOR'S: after filling, verify this word
   against the prospect's own hero photo at 390 and 1440 (the gate's contrast walk
   reads it as hero text), and if brandLight is too pale on that ground, re-pin
   theme.json's brandLight rather than hard-coding a color here.
   font-style is forced NORMAL on purpose: <em> is semantic emphasis for the accent
   word, NOT the retired accent-ITALIC headline formula (#35 root cause 3). The
   operator asked for a COLOR accent; italic here would revive the look they cut. */
.h1-accent {
  color: var(--teal-light);
  font-style: normal;
}

.hero-form {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  background: rgba(255,255,255,.96);
  padding: 0.5rem;
  border-radius: 999px;
  width: min(620px, 100%);
  box-shadow: 0 18px 44px rgba(0,0,0,.28);
}
.hero-form input {
  flex: 1 1 150px; min-width: 0;
  border: 0; background: transparent;
  font: inherit; font-size: 0.9375rem;
  color: var(--ink);
  padding: 0.75rem 1.1rem;
  border-radius: 999px;
}
.hero-form input::placeholder { color: #8b9491; }
.hero-form input:focus { outline: 2px solid var(--teal); outline-offset: -1px; }
.hero-form .pill { flex: 0 0 auto; }

/* Phone-first hero (no published email): the one conversion control is the phone.
   Same shadow weight as the form bar so the hero keeps its anchor. */
.hero-call {
  font-size: clamp(1rem, 0.9rem + 0.5vw, 1.1875rem);
  padding: 1.05rem 2.4rem;
  box-shadow: 0 18px 44px rgba(0,0,0,.28);
}

.hero-objection {
  margin: 1.15rem 0 0;
  color: #fff;
  font-size: clamp(0.95rem, 0.9rem + 0.2vw, 1.0625rem);
  text-shadow: 0 1px 12px rgba(0,0,0,.5);
}

.hero-where {
  margin: 1.5rem 0 0;
  color: rgba(255,255,255,.86);
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 10px rgba(0,0,0,.5);
}
.hero-where a { color: #fff; font-weight: 600; text-decoration: none; white-space: nowrap; border-bottom: 1px solid rgba(255,255,255,.5); }
.hero-where a:hover { border-bottom-color: #fff; }

@media (max-width: 860px) {
  /* Blueprint's mobile hero collapses the three panels into one strip.
     Panels may ship bare (<img>) or wrapped in <picture> by the fill's webp
     wiring, so hide the hidden panels' WRAPPERS too and stack every panel on
     one explicit 1fr track — panel B sits flush at the top of the band with
     no reserved rows above it. */
  .hero-panels { grid-template-columns: 1fr; grid-template-rows: 1fr; grid-auto-rows: 0; }
  .hero-panels > * { grid-area: 1 / 1 / 2 / 2; }
  .hero-panels .panel-a, .hero-panels .panel-c,
  .hero-panels picture:has(> .panel-a), .hero-panels picture:has(> .panel-c) { display: none; }
  .hero-panels .panel-b { display: block; object-position: 50% 50%; }
  .hero-panels img:only-child { display: block; object-position: 50% 50%; }
  .hero-inner { min-height: 78vh; justify-content: flex-end; padding-bottom: 4rem; }
  .hero h1 {
    max-width: 100%;
    text-wrap: normal;
    font-size: clamp(1.6rem, 7.1vw, 2.4rem);
  }
  .hero-form { border-radius: 18px; flex-direction: column; padding: 0.75rem; gap: 0.6rem; }
  .hero-form input {
    flex: 0 0 auto; width: 100%;
    border-radius: 11px; background: #fff;
    box-shadow: inset 0 0 0 1px var(--grey-line);
    padding: 0.85rem 1rem;
  }
  .hero-form .pill { width: 100%; }
  .hero-call { width: 100%; }
  .hero-where { margin-top: 1.1rem; }
}

/* ---------- 4. mission band ---------------------------------------------- */

.mission {
  background: linear-gradient(115deg, var(--teal-darker) 0%, var(--teal) 55%, var(--teal-light) 130%);
  color: #fff;
}
.mission .wrap {
  display: grid; gap: clamp(1.6rem, 4vw, 3.5rem);
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  align-items: center;
}
.mission h2 {
  color: #fff;
  font-style: italic;
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 3rem);
  margin-bottom: 1.1rem;
  text-wrap: balance;
}
.mission p { color: rgba(255,255,255,.9); max-width: 60ch; }
.mission-cta { display: flex; justify-content: flex-start; }

@media (max-width: 860px) {
  .mission .wrap { grid-template-columns: 1fr; }
}

/* ---------- 5. services (two labelled groups, photo tiles) ---------------- */

.services { background: var(--paper); }

.split-intro {
  display: grid; gap: clamp(1rem, 3vw, 3rem);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.split-intro h2 {
  color: var(--teal-deep);
  font-style: italic;
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 2.9rem);
  text-wrap: balance;
}
.split-intro p { color: var(--grey); max-width: 52ch; }

@media (max-width: 800px) { .split-intro { grid-template-columns: 1fr; } }

.group-label {
  font-family: var(--sans);
  font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase;
  margin: 0 0 1.4rem;
}
.group-label.teal { color: var(--teal-deep); }
.group-label.wine { color: var(--wine); }

.tile-row {
  display: grid; gap: clamp(1.25rem, 2.6vw, 2rem);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-bottom: clamp(2.5rem, 5vw, 3.75rem);
}
.tile-row:last-of-type { margin-bottom: clamp(2rem, 4vw, 3rem); }

.tile { display: block; text-decoration: none; color: inherit; }
.tile-media {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: 10px;
  background: var(--tint);
}
.tile-media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .5s cubic-bezier(.2,.7,.3,1);
}
.tile:hover .tile-media img { transform: scale(1.045); }
.tile h3 {
  font-size: clamp(1.125rem, 1rem + 0.45vw, 1.4rem);
  border-top: 1px solid var(--grey-line);
  padding-top: 0.9rem;
  margin-top: 0.9rem;
}
.tile p { color: var(--grey); font-size: 0.9375rem; margin-top: 0.45rem; }

@media (max-width: 900px) { .tile-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .tile-row { grid-template-columns: 1fr; } }

.services-foot { text-align: center; }

/* ---------- 6. friction band (the ONE hot band) --------------------------- */

.friction {
  background: linear-gradient(118deg, var(--wine-deep) 0%, var(--wine) 48%, var(--wine-light) 100%);
  color: #fff;
}
.friction .eyebrow { color: rgba(255,255,255,.75); }
.friction h2 {
  color: #fff; font-style: italic;
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 3rem);
  margin-bottom: clamp(2.25rem, 5vw, 3.5rem);
  max-width: 20ch;
  text-wrap: balance;
}
.friction-grid {
  display: grid; gap: clamp(1.75rem, 3.2vw, 2.5rem);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.friction-item { display: flex; flex-direction: column; align-items: flex-start; }
.friction-item img.f-icon { width: 40px; height: 40px; margin-bottom: 1.1rem; }
.friction-item h3 {
  color: #fff; font-weight: 500;
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.3rem);
  margin-bottom: 0.5rem;
}
.friction-item p { color: rgba(255,255,255,.88); font-size: 0.9375rem; margin-bottom: 1.25rem; }
.friction-item .pill { margin-top: auto; }

@media (max-width: 980px) { .friction-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .friction-grid { grid-template-columns: 1fr; } }

/* ---------- 7. who-we-are mosaic ------------------------------------------
   The blueprint's careers mosaic alternates real staff portraits against solid
   color tiles. When a prospect publishes no staff photography, dressing stock
   models up as their team would be a fabrication, so the photo cell binds an
   unlabelled real treatment photograph (or drops) and the color tiles carry
   the prospect's real, published copy only.
   ------------------------------------------------------------------------- */

.about { background: var(--paper); }
.about .wrap {
  display: grid; gap: clamp(1.75rem, 4vw, 3.25rem);
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  align-items: start;
}
.about h2 {
  font-style: italic;
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 2.9rem);
  margin-bottom: 1.1rem;
  text-wrap: balance;
}
.about .eyebrow { color: var(--wine); }
.about-copy p { color: var(--grey); }
.about-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }

.mosaic {
  display: grid; gap: 12px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.mosaic-cell {
  border-radius: 10px; overflow: hidden;
  min-height: 210px;
  padding: 1.5rem;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.mosaic-photo { padding: 0; position: relative; }
.mosaic-photo img { width: 100%; height: 100%; min-height: 210px; object-fit: cover; }
.mosaic-teal { background: var(--teal); color: #fff; }
.mosaic-wine { background: var(--wine); color: #fff; }
.mosaic-ink  { background: var(--ink); color: #fff; }
/* Composure: with an odd cell count (e.g. no usable photo) the last tile spans
   the full row instead of leaving an empty grid slot. */
.mosaic > .mosaic-cell:nth-child(odd):last-child { grid-column: 1 / -1; }
.mosaic-cell h3 {
  color: #fff; font-style: italic;
  font-size: 1.1875rem; margin-bottom: 0.5rem;
}
.mosaic-cell .role {
  font-family: var(--sans); font-style: normal;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255,255,255,.72);
  display: block; margin-bottom: 0.75rem;
}
.mosaic-cell p { font-size: 0.875rem; line-height: 1.55; color: rgba(255,255,255,.9); }
/* Per-image credit chip: for imagery whose licence demands the attribution be
   shown ON the photo. Deliberately unobtrusive but never washed out — the plate
   is >0.85 opaque on purpose, so the gate's text-contrast walk reads THIS as the
   backdrop (a translucent plate makes it walk past to the section paper and
   score white-on-white) and overlay-legibility samples a dark crop. Wraps rather
   than ellipsing: a truncated attribution is not a displayed attribution. */
.photo-credit {
  position: absolute; right: 8px; bottom: 8px;
  max-width: calc(100% - 16px);
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
  background: rgba(17,22,21,.92);
  font-family: var(--sans); font-size: 0.6875rem; line-height: 1.45;
  color: #fff;
}
.photo-credit a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }

@media (max-width: 900px) {
  .about .wrap { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
  .mosaic { grid-template-columns: 1fr; }
}

/* ---------- 8. testimonials ---------------------------------------------- */

.reviews { background: var(--tint); position: relative; overflow: hidden; }
.reviews .eyebrow { color: var(--teal-deep); }
.reviews-head { text-align: center; margin-bottom: clamp(2.25rem, 5vw, 3.5rem); }
.reviews-head h2 {
  font-style: italic;
  font-size: clamp(1.6rem, 1.05rem + 2.1vw, 2.75rem);
  max-width: 22ch; margin-inline: auto;
  text-wrap: balance;
}
/* `> p`, not a descendant selector: the eyebrow now lives inside .reviews-hero
   and is also a <p> in this subtree, so a descendant rule would hand it the
   sub-line's colour and margin. */
.reviews-head > p {
  color: var(--grey); margin-top: 0.9rem;
  max-width: 58ch; margin-inline: auto;
  text-wrap: balance;
}

/* ---- section-top photograph (TASTE_LEDGER #66) ----------------------------
   The photo runs the FULL band width from the section's top edge and is masked
   to nothing just below the star strip, so the eyebrow, heading and stars sit
   ON imagery and every quote card below sits on clean tint. The mask dissolves
   the layer itself, so the tail is the band's own --tint showing through rather
   than a painted approximation of it.

   Two layers, both carrying the identical mask so they die together:
     img                    z-index -2  the photograph
     .reviews-hero::before  z-index -1  the legibility wash + a texture layer
   Text is in normal flow above both. A real <img>, eager and never lazy: a lazy
   band image stays undecoded while images-load still reports fine, and being an
   <img> child of the text's ancestor is what puts this text inside the gate's
   overlay-legibility sweep at all.

   EVERYTHING here is scoped to .has-photo, which the template only writes when
   reviews.headerPhoto.src is authored. With no photo the whole treatment
   collapses and the head reads exactly as it did before this existed - no
   padding, no wash, no empty slot. */
.reviews-hero { position: relative; z-index: 2; }
.reviews-hero.has-photo {
  /* The photo bleeds UP through the section's own top padding so it starts at
     the band edge, which is why this tracks .section's padding-block exactly.
     Moving one without the other leaves a strip of bare tint above the photo. */
  --reviews-bleed: clamp(3.5rem, 7vw, 6.5rem);
  /* Bottom padding and the fade share one value: the layer holds full opacity
     to the star strip's bottom edge and the ENTIRE fade happens in the padding
     underneath it (#66 asked for the fade to be harder, not longer). */
  --reviews-pad-b: clamp(1.3rem, 2.6vw, 2.1rem);
  padding-top: clamp(1.5rem, 3vw, 1.75rem);
  padding-bottom: var(--reviews-pad-b);
  --reviews-fade: linear-gradient(to bottom,
    #000 0%,
    #000 calc(100% - var(--reviews-pad-b)),
    rgba(0,0,0,.45) calc(100% - var(--reviews-pad-b) * 0.62),
    rgba(0,0,0,.08) calc(100% - var(--reviews-pad-b) * 0.22),
    rgba(0,0,0,0) 100%);
}
/* The photo ships EITHER bare (<img>) or wrapped in <picture> by the fill's webp
   wiring, and which one it is depends on whether the build had real image files
   to convert - the oracle refill is bare, every deployed build is wrapped. So the
   masked layer is whichever of the two is the direct child, and when it is the
   picture the img simply fills it. Scoping this to `> .reviews-hero-photo` alone
   was the bug this comment exists to prevent: on a real build the img is a
   GRANDCHILD, the rule missed it, and the photograph dropped into normal flow as
   a full-width banner above the eyebrow with no mask on it at all. */
.reviews-hero.has-photo > picture,
.reviews-hero.has-photo > .reviews-hero-photo,
.reviews-hero.has-photo::before {
  position: absolute;
  left: 50%; transform: translateX(-50%);
  width: 100vw; max-width: none;
  top: calc(-1 * var(--reviews-bleed));
  height: calc(100% + var(--reviews-bleed));
  pointer-events: none;
  -webkit-mask-image: var(--reviews-fade);
          mask-image: var(--reviews-fade);
}
.reviews-hero.has-photo > picture { display: block; z-index: -2; }
.reviews-hero.has-photo > picture > .reviews-hero-photo { width: 100%; height: 100%; }
/* 52% rather than the top of the frame: the band shows only a slice of the
   photo's height at desktop widths, and a crop through the top of a room photo
   lands on ceiling. */
.reviews-hero-photo { z-index: -2; object-fit: cover; object-position: center 52%; }
/* The wash is a HALO, not a sheet. A flat veil heavy enough to carry the small
   caps eyebrow washes the whole photograph to near-white, which is the opposite
   of the pop this was asked for; so the veil is thin at the edges where nothing
   sits on top and concentrates into an ellipse behind the centre column where
   every word of this band lives. The first layer is the build's diagonal
   texture (#48: gradients are never bare). */
.reviews-hero.has-photo::before {
  content: ''; z-index: -1;
  background-image:
    repeating-linear-gradient(135deg, color-mix(in srgb, var(--ink) 2%, transparent) 0 1px, transparent 1px 7px),
    radial-gradient(108% 86% at 50% 42%,
      rgba(255,255,255,.90) 0%,
      rgba(255,255,255,.62) 46%,
      rgba(255,255,255,0) 82%),
    linear-gradient(to bottom,
      rgba(255,255,255,.14) 0%,
      rgba(255,255,255,.12) 55%,
      color-mix(in srgb, var(--tint) 78%, transparent) 100%);
}
/* At phone widths the band's aspect approaches the photograph's own, so `cover`
   shows nearly the entire frame - including whatever high-key corner of it has
   no contrast left after the wash. Widening the box past the viewport zooms
   `cover` back into the middle of the frame, where the subject is. .reviews
   clips the overhang; the wash layer stays at 100vw. */
@media (max-width: 700px) {
  .reviews-hero.has-photo > picture,
  .reviews-hero.has-photo > .reviews-hero-photo { width: 175vw; }
  .reviews-hero-photo { object-position: 52% 56%; }
}
/* No mask support (older Safari/Firefox): the photo would otherwise end in a
   hard horizontal seam across the band. Drop both layers and keep the flat
   tint - the band then reads exactly as it did before the photo existed. */
@supports not ((-webkit-mask-image: linear-gradient(#000, transparent)) or (mask-image: linear-gradient(#000, transparent))) {
  .reviews-hero.has-photo > picture,
  .reviews-hero-photo, .reviews-hero.has-photo::before { display: none; }
}

/* ---- stars (TASTE_LEDGER #49 / #57) ---------------------------------------
   Two stacked copies of one glyph set: a track in faint ink and a gold fill
   clipped to the real rating. The fill fraction is DATA, never decoration -
   --stars-pct defaults to 0%, so a row whose rating did not come through draws
   an EMPTY bar rather than a flattering full one. Per card the integer rating
   drives it through data-stars; the head strip carries a decimal rating and
   sets --stars-pct inline (round(rating / 5 * 100)%). */
.star-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.stars-bar {
  position: relative; display: inline-block; line-height: 0;
  --stars-pct: 0%;
}
.stars-bar[data-stars="1"] { --stars-pct: 20%; }
.stars-bar[data-stars="2"] { --stars-pct: 40%; }
.stars-bar[data-stars="3"] { --stars-pct: 60%; }
.stars-bar[data-stars="4"] { --stars-pct: 80%; }
.stars-bar[data-stars="5"] { --stars-pct: 100%; }
.stars { display: block; height: 17px; width: auto; }
.stars-track { fill: color-mix(in srgb, var(--ink) 18%, transparent); }
.stars-fill {
  position: absolute; left: 0; top: 0; height: 100%;
  fill: var(--gold);
  clip-path: inset(0 calc(100% - var(--stars-pct)) 0 0);
}
.stars-bar-lg .stars { height: 27px; }

/* Source attribution: stars + the Google mark on a white chip, the standard
   review-widget register. No count, no badge, no claim beyond the source. */
.rating-strip {
  display: inline-flex; align-items: center; gap: 0.85rem;
  margin-top: 1.15rem;
  padding: 0.6rem 1.15rem;
  background: #fff;
  border-radius: 999px;
  box-shadow: 0 6px 20px color-mix(in srgb, var(--teal-darker) 10%, transparent);
}
.rating-sep { width: 1px; height: 20px; background: var(--grey-line); }
.g-mark { display: block; width: 20px; height: 20px; }

.review-stack {
  position: relative; z-index: 2;
  max-width: 760px; margin-inline: auto;
  /* The rhythm used to live in the separator rows' own padding. Now that every
     rating sits inside its card, the stack owns the spacing itself. */
  display: flex; flex-direction: column; align-items: stretch;
  gap: clamp(1.1rem, 2.4vw, 1.9rem);
}
.quote-card {
  position: relative; z-index: 2;
  background: #fff;
  border-radius: 12px;
  padding: clamp(1.6rem, 3.5vw, 2.6rem);
  box-shadow: 0 12px 34px color-mix(in srgb, var(--teal-darker) 9%, transparent);
  text-align: center;
}
/* Readability (#57: "change tesimonial font and color be easier to read"). The
   quote was a serif in --wine, which is legal contrast but tiring over a
   60-word patient story that people actually READ. Body sans in --ink takes
   over with a looser line-height; the serif keeps the headings. */
.quote-card blockquote {
  margin: 0;
  font-family: var(--sans);
  font-size: clamp(1rem, 0.95rem + 0.4vw, 1.1875rem);
  line-height: 1.62;
  color: var(--ink);
  max-width: 56ch; margin-inline: auto;
}
.quote-card cite {
  display: block; margin-top: 1.1rem;
  font-family: var(--sans); font-style: normal;
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--grey);
}
/* Card order is copy, stars, name, date: the words lead, the rating and the
   attribution sign off under them. When the stars render they own the gap, so
   the cite drops its own. */
.card-stars {
  display: flex; justify-content: center;
  margin-top: clamp(1.15rem, 2.2vw, 1.55rem);
  margin-bottom: clamp(0.7rem, 1.3vw, 0.95rem);
}
.card-stars + cite { margin-top: 0; }
/* When the review was written (#63), from its real publishedAt and never
   approximated. Numeric dd/mm/yyyy at footnote size, dropped out of the name's
   uppercase register so the name still leads the attribution, and in --ink-soft
   rather than --grey so the smaller type does not also get fainter. Tabular
   figures so a column of dates lines up on the slashes. */
.quote-date {
  display: block; margin-top: 0.35rem;
  text-transform: none; letter-spacing: 0.02em; font-weight: 500;
  font-variant-numeric: tabular-nums;
  font-size: 0.625rem; color: var(--ink-soft);
}

/* Photo collage bleeding around the quote stack, per the blueprint: the images
   tuck BEHIND the card corners and run off the page edge. Positioned against
   .review-stack (the 760px column) so the overlap is the same at every width. */
.reviews-bleed {
  position: absolute; z-index: 1;
  width: 300px; height: 210px;
  border-radius: 10px; overflow: hidden;
  /* The inset hairline keeps a pale photo from dissolving into the tint band -
     without it a blown-out frame reads as a missing image. */
  box-shadow: 0 14px 36px color-mix(in srgb, var(--teal-darker) 18%, transparent),
              inset 0 0 0 1px color-mix(in srgb, var(--teal-darker) 12%, transparent);
}
.reviews-bleed img { width: 100%; height: 100%; object-fit: cover; }
.bleed-left  { left: -170px; top: 22%; }
.bleed-right { right: -170px; top: 58%; }

@media (max-width: 1080px) {
  .reviews-bleed { width: 240px; height: 175px; }
  .bleed-left  { left: -130px; }
  .bleed-right { right: -130px; }
}
@media (max-width: 900px) {
  .reviews-bleed { display: none; }
}

.reviews-foot { text-align: center; margin-top: clamp(2rem, 4vw, 3rem); position: relative; z-index: 2; }

/* ---------- 9. first visit (dark band) ------------------------------------ */

.firstvisit { background: var(--ink); color: rgba(255,255,255,.86); }
.firstvisit .eyebrow { color: var(--teal-light); }
.firstvisit h2 {
  color: #fff; font-style: italic;
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 2.9rem);
  max-width: 20ch; margin-bottom: 1rem;
  text-wrap: balance;
}
.firstvisit-lede { max-width: 56ch; margin-bottom: clamp(2.25rem, 5vw, 3.25rem); }
.steps {
  display: grid; gap: clamp(1.5rem, 3vw, 2.5rem);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  list-style: none; padding: 0; margin: 0 0 clamp(2rem, 4vw, 3rem);
}
.steps li { border-top: 1px solid rgba(255,255,255,.22); padding-top: 1.25rem; }
.step-num {
  display: block;
  font-family: var(--serif); font-style: italic;
  font-size: 2.1rem; color: var(--teal-light); line-height: 1;
  margin-bottom: 0.75rem;
}
.steps h3 { color: #fff; font-size: 1.1875rem; margin-bottom: 0.5rem; }
.steps p { font-size: 0.9375rem; color: rgba(255,255,255,.8); }

@media (max-width: 800px) { .steps { grid-template-columns: 1fr; } }

/* ---------- 10. booking form ---------------------------------------------- */

.book { background: var(--paper); }
.book .wrap {
  display: grid; gap: clamp(1.75rem, 4vw, 3.5rem);
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  align-items: start;
}
.book .eyebrow { color: var(--teal-deep); }
.book h2 {
  font-style: italic;
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 2.9rem);
  margin-bottom: 1rem;
  text-wrap: balance;
}
.book-aside p { color: var(--grey); }
.book-call {
  display: inline-flex; align-items: baseline; gap: 0.6rem;
  margin-top: 1.4rem;
  font-family: var(--serif); font-size: 1.5rem;
  color: var(--teal-deep); text-decoration: none;
}
.book-call:hover { text-decoration: underline; }
.book-call .small {
  font-family: var(--sans); font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--grey);
}

.form-card {
  background: var(--tint-soft);
  border: 1px solid var(--grey-line);
  border-radius: var(--radius);
  padding: clamp(1.35rem, 3vw, 2.25rem);
}
.field-grid { display: grid; gap: 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.field { display: flex; flex-direction: column; gap: 0.4rem; }
.field.full { grid-column: 1 / -1; }
.field label {
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.04em; color: var(--ink);
}
.field input, .field select, .field textarea {
  font: inherit; font-size: 0.9375rem;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--grey-line);
  border-radius: 9px;
  padding: 0.75rem 0.9rem;
  width: 100%;
}
.field textarea { min-height: 120px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: 2px solid var(--teal); outline-offset: -1px; border-color: var(--teal);
}
.form-actions { margin-top: 1.35rem; display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.form-note { font-size: 0.8125rem; color: var(--grey); margin: 1rem 0 0; line-height: 1.55; }

/* Phone-first booking (no published email): the mailto form drops, the band
   centers, and the phone is the conversion control. */
.book-phone .wrap { grid-template-columns: minmax(0, 62ch); justify-content: center; text-align: center; }
.book-phone-cta { margin-top: 1.6rem; font-size: 1.0625rem; padding: 0.95rem 2.2rem; }

@media (max-width: 900px) { .book .wrap { grid-template-columns: 1fr; } }
@media (max-width: 560px) { .field-grid { grid-template-columns: 1fr; } }

/* ---------- 11. footer ----------------------------------------------------- */

.footer { background: var(--teal); color: rgba(255,255,255,.88); padding-block: clamp(3rem, 6vw, 4.5rem) 0; }
.footer-grid {
  display: grid; gap: clamp(1.75rem, 4vw, 3rem);
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.8fr) minmax(0, 0.8fr) minmax(0, 1.2fr);
  align-items: start;
}
.footer h3 {
  color: #fff; font-family: var(--sans);
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  margin-bottom: 1rem;
}
.footer a { color: #fff; text-decoration: none; }
.footer a:hover { text-decoration: underline; }
/* .footer a would otherwise paint the white pill's label white-on-white */
.footer a.pill-white { color: var(--ink); }
.footer a.pill-white:hover { text-decoration: none; }
.footer ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.55rem; font-size: 0.9375rem; }
.footer-logo-plate {
  display: inline-block; background: #fff; border-radius: 10px;
  padding: 0.85rem 1.1rem; margin-bottom: 1.25rem;
}
.footer-logo-plate img { width: 180px; height: auto; }
.footer-logo-plate img.logo-badge { width: auto; height: 96px; }
.footer-wordmark { display: inline-block; font-family: var(--serif); font-size: 1.45rem; color: #fff; margin-bottom: 1.25rem; }
.footer address { font-style: normal; font-size: 0.9375rem; line-height: 1.7; }
.hours-row { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.9375rem; }
.hours-row + .hours-row { margin-top: 0.5rem; }
.hours-row span:last-child { color: rgba(255,255,255,.78); white-space: nowrap; }
.footer-map {
  width: 100%; height: 210px; border: 0; border-radius: 10px; display: block;
  background: var(--teal-deep);
}
.footer-directions { margin: 0.75rem 0 0; font-size: 0.875rem; }
.footer-directions a { text-decoration: underline; text-underline-offset: 3px; }
.footer-cta { margin-top: 1rem; }
.footer-bottom {
  margin-top: clamp(2.25rem, 5vw, 3.25rem);
  border-top: 1px solid rgba(255,255,255,.24);
  padding-block: 1.4rem;
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; align-items: center;
  font-size: 0.8125rem; color: rgba(255,255,255,.78);
}
.footer-bottom .spacer { margin-left: auto; }
/* Page-level photo colophon: one deduped attribution line for every credited
   photo on the page (Google Place photos carry their contributor's name; Street
   View keeps its "© Google" notice). `flex-basis: 100%` drops it onto its own
   row under the copyright line. Renders ONLY when an image carries a `credit` —
   an uncredited build emits nothing at all, not an empty label. */
.photo-colophon {
  flex: 1 0 100%;
  color: rgba(255,255,255,.78);
  line-height: 1.6;
  /* The fixed PXLNorth preview badge parks over the last ~48px of the viewport,
     and this line is the last thing on the page — at 390 it sat directly under
     the badge. An attribution the chrome covers is not a displayed attribution,
     so the colophon reserves its own clearance. Costs nothing on an uncredited
     build: the element does not exist unless a photo carries a credit. */
  padding-bottom: 3.5rem;
}
.photo-colophon a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 980px) { .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr; } }

/* ---------- preview badge -------------------------------------------------- */

.preview-badge {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 14px; z-index: 120;
  display: flex; align-items: stretch; gap: 1px;
  background: rgba(31,37,35,.94);
  border-radius: 999px; overflow: hidden;
  box-shadow: 0 10px 30px rgba(0,0,0,.28);
  font-family: var(--sans); font-size: 0.75rem;
  max-width: calc(100vw - 24px);
}
.preview-badge a { text-decoration: none; padding: 0.6rem 1rem; white-space: nowrap; }
.preview-badge-info { color: rgba(255,255,255,.85); }
.preview-badge-cta { background: var(--teal-light); color: #14201d; font-weight: 700; }
.preview-badge-cta:hover { background: #fff; }

@media (max-width: 540px) {
  .preview-badge { font-size: 0.6875rem; }
  .preview-badge a { padding: 0.5rem 0.7rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
}

/* HEADING INK (TASTE_LEDGER #44 "font is weak and cheap" / #53 "hard to read font" /
   #66 "headings THICKER") — display headings shipped at Newsreader 400 italic, which the
   operator read as weak three times and which blueprint-gate's heading-ink check (floor 500)
   fails on every clinical tailor. Upright 600 across h1 + the eight section h2s; -0.02em
   keeps the wider weight from loosening the line. Last in the file so it beats the per-section
   blocks above (same specificity, later wins). The 600 face is REQUESTED in the master's
   default fonts.googleHref — a used weight must be a fetched weight, or the browser fakes it. */
.hero h1,
.mission h2,
.split-intro h2,
.friction h2,
.about h2,
.reviews-head h2,
.firstvisit h2,
.book h2 {
  font-style: normal;
  font-weight: 600;
  letter-spacing: -0.02em;
}
