/* ==========================================================================
   FAMILY TREE DENTAL — THE DESIGN SYSTEM
   ---------------------------------------------------------------------------
   Loaded on EVERY page, immediately after style.css and before any page's own
   sheet. It holds three things and nothing else:

     1. the shared chrome — the utility bar and the services mega-menu, which
        are additions to the navigation pill that style.css already styles
     2. the shared furniture — eyebrows, headings, prose, links, chips,
        floating cards, the reveal entry states, the action dock
     3. the shared SECTIONS — the appointment band, the patient experience,
        the testimonials, the clinic panel, the closing call, the service
        cards, the treatment rail and the pager

   Anything that appears on more than one page lives here. Anything that
   appears on exactly one page lives in that page's own sheet
   (services-hub.css, service-detail.css). If you find yourself copying a rule
   from one page sheet to the other, it belonged here.

   Every token it reads — colour, family, shadow, easing, gutter, nav height,
   hero inset — is declared in style.css and is not repeated.

   Structure
   ---------------------------------------------------------------------------
   00. Page tokens
   01. Chrome: the utility bar
   02. Chrome: the services mega-menu
   03. Furniture: eyebrow, headings, prose, links, chips, floats
   04. Reveal entry states
   05. Hero (shared by the hub and every service page)
   06. The sticky section rail
   07. Service cards
   08. The treatment rail and the pager
   09. Appointment band
   10. Patient experience
   11. Testimonials
   12. Clinic location
   13. The closing call
   14. Footer additions
   15. The action dock
   16. Responsive
   17. Reduced motion / print
   ========================================================================== */


/* ==========================================================================
   00. PAGE TOKENS
   Scoped to .sv-body so nothing here can leak into a page that does not want
   it. The homepage carries the class too — it uses the same chrome.
   ========================================================================== */
:root {
  /* THE UTILITY BAR'S HEIGHT, and it is a root token rather than a local one
     because three separate things measure themselves against it: the pill's
     own top offset, the hero's top padding, and the mobile menu's anchor.
     Set to 0 and the bar disappears cleanly, taking its space with it. */
  --util-h: 40px;
}

.sv-body,
body {
  --sv-ivory:   #faf7f2;
  --sv-cream:   #f5efe6;
  --sv-ice:     #eaf3fd;
  --sv-white:   #ffffff;

  --sv-navy:    #06213a;
  --sv-navy-2:  #0b3b56;
  --sv-teal:    #0e4f5e;

  --sv-on-dark:      rgba(255, 255, 255, 0.94);
  --sv-on-dark-soft: rgba(214, 232, 251, 0.78);
  --sv-on-dark-hair: rgba(174, 207, 246, 0.24);

  /* ONE RADIUS LADDER, four rungs, sitting either side of the hero frame's
     signature 28px so a chip, a card and a photograph are visibly the same
     family at three different sizes. */
  --sv-r-xl: 34px;
  --sv-r-lg: 24px;
  --sv-r-md: 16px;
  --sv-r-sm: 12px;

  /* Narrower than style.css's 1440 on purpose: these pages are READ, and a
     1440px column of clinical copy is a column nobody finishes. */
  --sv-shell: min(1300px, 100% - 2 * var(--gutter));
  --sv-pad:   clamp(4rem, 10vh, 8rem);
  --sv-gap:   clamp(2.5rem, 5vw, 5rem);

  --sv-fs-h1: clamp(1.6rem, 1.1rem + 5vw, 5rem);
  --sv-fs-h2: clamp(1.95rem, 1.1rem + 2.9vw, 3.35rem);
  --sv-fs-h3: clamp(1.15rem, 0.95rem + 0.7vw, 1.5rem);

  --sv-card:      rgba(255, 255, 255, 0.86);
  --sv-card-brd:  rgba(120, 170, 225, 0.26);
  --sv-hair:      rgba(11, 45, 82, 0.09);
}

.sv-body { background: var(--sv-ivory); }


/* ==========================================================================
   01. CHROME — THE UTILITY BAR
   ---------------------------------------------------------------------------
   The strip above the pill. It carries the things you want ONCE, on arrival:
   the number, the forms link, the booking button.

   IT COLLAPSES THE MOMENT YOU SCROLL. `.is-scrolled` is already written to
   .nav by every page controller, so the bar rides that class rather than
   introducing a second scroll listener. A strip you have already read is a
   strip costing you 40px of a phone for the rest of the page — and once it
   is gone the pill sits at the top, exactly where it does on the homepage
   today.

   .nav BECOMES A COLUMN HERE. style.css lays it out as a centred row, which
   was right when the pill was the only thing in it. The override is confined
   to these four declarations so nothing else about the header changes.
   ========================================================================== */
.nav {
  display: block;
  padding-top: 0;
}

.util {
  height: var(--util-h);
  overflow: hidden;
  color: rgba(255, 255, 255, .92);
  background: linear-gradient(100deg, var(--c-blue-700) 0%, #1580b4 52%, var(--c-teal, #0c4a5c) 100%);
  transition: height var(--t-med) var(--ease-out),
              opacity var(--t-fast) var(--ease-out);
}
.nav.is-scrolled .util { height: 0; opacity: 0; }

.util__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: var(--util-h);
  width: min(1600px, 100% - 2 * clamp(1rem, 3vw, 2.5rem));
  margin-inline: auto;
  font-family: var(--f-ui);
  font-size: 0.79rem;
  font-weight: 500;
  letter-spacing: .02em;
}

.util__note {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: .9;
}
.util__dot {
  flex: none;
  width: 6px; height: 6px;
  background: var(--c-mint);
  border-radius: 50%;
  box-shadow: 0 0 0 3px rgba(127, 227, 212, .25);
}

.util__actions { display: flex; align-items: center; gap: clamp(.75rem, 1.8vw, 1.6rem); }

.util__link {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  white-space: nowrap;
  color: inherit;
  opacity: .92;
  transition: opacity var(--t-fast) var(--ease-out);
}
.util__link:hover { opacity: 1; }
.util__link svg { width: 14px; height: 14px; }

/* THE ONE WARM ACCENT IN THE CHROME. Small, and the only orange above the
   fold — the pill's own button is blue, so the two do not compete. */
.util__cta {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding-inline: .9rem;
  font-size: 0.73rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
  color: #fff;
  background: var(--c-accent);
  border-radius: 100px;
  transition: background var(--t-fast) var(--ease-out),
              transform var(--t-fast) var(--ease-out);
}
.util__cta:hover { background: #ff7d45; transform: translateY(-1px); }

/* The pill floats below the bar, at the hero frame's own inset. */
.nav__inner { margin-top: calc(var(--hero-inset) + var(--hero-pad)); }

/* A link that IS the page you are on. Marked, not just hoverable. */
.nav__link.is-active { color: var(--c-blue-900); }
.nav__link.is-active::after { transform: scaleX(1); }


/* ==========================================================================
   02. CHROME — THE SERVICES MEGA-MENU
   ---------------------------------------------------------------------------
   ONE PIECE OF MARKUP, TWO LAYOUTS. On a desktop it is a panel that drops
   from the pill: a feature column on the left, the two treatment families in
   columns on the right. Below 900px the same markup is an accordion inside
   the mobile menu — the category headings are real buttons in both cases, and
   chrome.js takes their tabindex away where they have nothing to toggle.

   IT IS AUTHORED OPEN. Every panel is visible in the markup and every
   aria-expanded starts "true", so with no JavaScript the mobile menu is a
   long but complete list of all fifteen treatments. chrome.js collapses it on
   arrival. Authored closed and opened by script is a menu that a script
   failure turns into a dead end.

   `visibility`, not `display`: a display change cannot transition, and
   without a transition the panel appears in one frame while the caret above
   it is still turning.
   ========================================================================== */
.nav__item--mega { position: static; }

.mega {
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  z-index: 5;
  width: min(1080px, calc(100vw - 2 * (var(--hero-inset) + var(--hero-pad) + 12px)));
  padding: clamp(1.1rem, 1.8vw, 1.6rem);
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid var(--glass-brd);
  border-radius: var(--sv-r-lg);
  box-shadow: 0 8px 24px rgba(11, 45, 82, .1), 0 40px 90px rgba(11, 45, 82, .18);
  -webkit-backdrop-filter: blur(24px) saturate(170%);
  backdrop-filter: blur(24px) saturate(170%);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -10px);
  transition: opacity var(--t-fast) var(--ease-out),
              transform var(--t-med) var(--ease-out),
              visibility var(--t-med);
}
/* Bridges the gap between the trigger and the panel so crossing it with the
   pointer does not drop the hover. */
.mega::before {
  content: "";
  position: absolute;
  inset: -14px 0 auto 0;
  height: 14px;
}
.nav__item--mega:hover .mega,
.nav__item--mega:focus-within .mega,
.nav__trigger[aria-expanded="true"] + .mega {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.mega__inner {
  display: grid;
  grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr);
  gap: clamp(1rem, 2vw, 1.75rem);
}

/* --- The feature column --------------------------------------------------- */
.mega__aside {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(1.1rem, 1.8vw, 1.5rem);
  overflow: hidden;
  color: #fff;
  border-radius: var(--sv-r-md);
  background:
    radial-gradient(80% 90% at 84% 8%, rgba(127, 227, 212, .22) 0%, rgba(127, 227, 212, 0) 60%),
    linear-gradient(150deg, var(--c-blue-700) 0%, var(--c-blue-900) 60%, #06213a 100%);
}
.mega__kicker {
  font-family: var(--f-ui);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--c-mint);
}
.mega__title {
  margin-top: .5rem;
  font-family: var(--f-display);
  font-size: 1.5rem;
  line-height: 1.14;
  letter-spacing: -0.012em;
}
.mega__text {
  margin-top: .5rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--sv-on-dark-soft);
}
.mega__all {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  align-self: flex-start;
  margin-top: 1rem;
  padding: .55rem 1rem;
  font-family: var(--f-ui);
  font-size: 0.83rem;
  font-weight: 500;
  color: #fff;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: 100px;
  transition: background var(--t-fast) var(--ease-out);
}
.mega__all svg { width: 16px; height: 16px; transition: transform var(--t-med) var(--ease-out); }
.mega__all:hover { background: rgba(255, 255, 255, .2); }
.mega__all:hover svg { transform: translateX(3px); }

.mega__media {
  margin-top: auto;
  padding-top: 1rem;
  overflow: hidden;
  border-radius: var(--sv-r-sm);
      height: 300px;
}
.mega__media img {
  width: 100%;
  aspect-ratio: 16 / 7;
  object-fit: cover;
  border-radius: var(--sv-r-sm);
  opacity: .82;
}

/* --- The two family columns ----------------------------------------------- */
.mega__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(.75rem, 1.6vw, 1.5rem);
}

.mega__head { margin: 0 0 .4rem; }
.mega__toggle {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  padding: .45rem .6rem;
  font-family: var(--f-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  text-align: left;
  color: var(--c-blue-700);
  border-bottom: 1px solid rgba(120, 170, 225, .35);
  border-radius: 0;
}
.mega__head-label { flex: 1 1 auto; min-width: 0; }
/* .mega__count is gone — the walker no longer prints a tally beside the two
   category headings. Nothing else styled it, so the rule went with it. */
/* The accordion's plus. Hidden on a desktop, where the panel never closes. */
.mega__sign { display: none; }

.mega__list { list-style: none; }

.mega__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .48rem .6rem;
  font-family: var(--f-ui);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--c-ink-soft);
  border-radius: 10px;
  transition: background var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out);
}
.mega__i {
  flex: none;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--c-blue-300);
  font-variant-numeric: var(--nums-tab);
  transition: color var(--t-fast) var(--ease-out);
}
.mega__label { flex: 1 1 auto; min-width: 0; }
.mega__item svg {
  flex: none;
  width: 15px; height: 15px;
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--t-fast) var(--ease-out),
              transform var(--t-med) var(--ease-out);
}
.mega__item:hover,
.mega__item:focus-visible {
  color: var(--c-blue-900);
  background: rgba(26, 114, 214, .08);
}
.mega__item:hover .mega__i { color: var(--c-accent); }
.mega__item:hover svg { opacity: 1; transform: translateX(0); }

/* The page you are on, marked in the menu that got you here. */
.mega__item.is-current {
  color: var(--c-blue-900);
  background: rgba(26, 114, 214, .1);
}
.mega__item.is-current .mega__i { color: var(--c-accent); }
.mega__item.is-current::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 3px; height: 55%;
  background: var(--c-accent);
  border-radius: 2px;
  transform: translateY(-50%);
}


/* ==========================================================================
   02b. CHROME — THE SMALL DROPDOWNS
   ---------------------------------------------------------------------------
   About Us, and any other nav entry given a `menu` in config.php. The mega
   menu is the bespoke panel for fifteen services; this is the general case for
   two or three, and it is deliberately a different size rather than a smaller
   copy of the same composition.

   It opens on hover and on focus-within like the mega menu, and on
   aria-expanded, which is what chrome.js sets on a touch screen's first tap.
   Same three ways in, same `visibility` transition, same hover bridge.
   ========================================================================== */
.nav__item--drop { position: relative; }

.drop {
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  z-index: 5;
  width: max(19rem, 100%);
  padding: .5rem;
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid var(--glass-brd);
  border-radius: var(--sv-r-md);
  box-shadow: 0 8px 24px rgba(11, 45, 82, .1), 0 30px 70px rgba(11, 45, 82, .16);
  -webkit-backdrop-filter: blur(24px) saturate(170%);
  backdrop-filter: blur(24px) saturate(170%);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -10px);
  transition: opacity var(--t-fast) var(--ease-out),
              transform var(--t-med) var(--ease-out),
              visibility var(--t-med);
}
/* Bridges the gap to the trigger so crossing it does not drop the hover. */
.drop::before {
  content: "";
  position: absolute;
  inset: -14px 0 auto 0;
  height: 14px;
}
.nav__item--drop:hover .drop,
.nav__item--drop:focus-within .drop,
[data-drop-trigger][aria-expanded="true"] + .drop {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.drop__list { margin: 0; padding: 0; list-style: none; }

.drop__item {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 .75rem;
  padding: .7rem .85rem;
  text-decoration: none;
  border-radius: var(--sv-r-sm);
  transition: background var(--t-fast) var(--ease-out);
}
.drop__item:hover,
.drop__item:focus-visible { background: rgba(26, 114, 214, .07); }

.drop__label {
  font-weight: 500;
  font-size: .98rem;
  color: var(--sv-navy-2);
  transition: color var(--t-fast) var(--ease-out);
}
.drop__item:hover .drop__label { color: var(--c-blue-700); }

/* The note is the reason the row is two lines tall. It says where the link
   goes, which is what a menu of two similar-sounding pages needs most. */
.drop__note {
  grid-column: 1;
  margin-top: .12rem;
  font-size: .78rem;
  line-height: 1.35;
  color: rgba(11, 45, 82, .58);
}

.drop__arrow {
  grid-row: 1 / -1;
  grid-column: 2;
  width: 18px; height: 18px;
  color: var(--c-blue-700);
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--t-fast) var(--ease-out),
              transform var(--t-med) var(--ease-out);
}
.drop__item:hover .drop__arrow,
.drop__item:focus-visible .drop__arrow { opacity: 1; transform: translateX(0); }

.drop__item.is-current { background: rgba(26, 114, 214, .09); }
.drop__item.is-current .drop__label { color: var(--c-blue-700); }


/* ==========================================================================
   03. FURNITURE
   The handful of pieces that appear in five sections each, written once.
   ========================================================================== */

/* --- The eyebrow: a drawn rule, then the label ---------------------------- */
.sv-eyebrow {
  display: flex;
  align-items: center;
  gap: .75rem;
  font-family: var(--f-ui);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--c-blue-700);
}
.sv-eyebrow__rule {
  display: block;
  flex: none;
  width: clamp(28px, 4vw, 54px);
  height: 1.5px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--c-blue-500), var(--c-mint));
  transform: scaleX(0);
  transform-origin: 0 50%;
}
.sv-eyebrow--light { color: var(--c-mint); }
.sv-eyebrow--light .sv-eyebrow__rule {
  background: linear-gradient(90deg, var(--c-mint), rgba(127, 227, 212, 0));
}

/* --- Headings -------------------------------------------------------------
   The masks are the homepage's, so the reveal reads as the same gesture at a
   different size. `.line` clips; `.line__inner` rises through it.

   A LINE THAT WRAPS IS A LINE HALF SEEN — the mask is built for one visual
   row. Every heading is authored to its breaks in the component that emits
   it; re-check them at 1440, 1100 and 480 if copy changes.
   -------------------------------------------------------------------------- */
.sv-h1,
.sv-h2 {
  position: relative;
  font-family: var(--f-display);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.016em;
  color: var(--c-ink);
}
.sv-h1 { font-size: var(--sv-fs-h1); }
.sv-h2 { font-size: var(--sv-fs-h2); }

/* EVERY HEADING BUILT BY heading() NEEDS THIS GROUP, whatever its own class
   is. Without it the `.line` spans stay inline: the authored breaks collapse
   into one running line, and — worse — there is no overflow to clip, so the
   reveal slides the words in over whatever sits above instead of lifting them
   out of a hidden edge. The page-specific titles are listed here rather than
   in their own stylesheets because more than one page now needs them, which
   is the rule includes/README.md sets for this file. */
.sv-h1 .line,
.sv-h2 .line,
.sv-hero__title .line,
.tm-hero__title .line,
.of-hero__title .line,
.of-intro__title .line,
.ct-hero__title .line,
.ct-consult__title .line,
.np-hero__title .line,
.rv-hero__title .line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.11em;
  margin-bottom: -0.07em;
}
.sv-h1 .line__inner,
.sv-h2 .line__inner,
.sv-hero__title .line__inner,
.tm-hero__title .line__inner,
.of-hero__title .line__inner,
.of-intro__title .line__inner,
.ct-hero__title .line__inner,
.ct-consult__title .line__inner,
.np-hero__title .line__inner,
.rv-hero__title .line__inner { display: block; }

.sv-h1 .word,
.sv-h2 .word,
.sv-hero__title .word,
.tm-hero__title .word,
.of-hero__title .word,
.of-intro__title .word,
.ct-hero__title .word,
.ct-consult__title .word,
.np-hero__title .word,
.rv-hero__title .word { display: inline-block; }

/* The one warm accent, exactly as the homepage uses it. */
.sv-h1 em,
.sv-h2 em,
.sv-hero__title em {
  position: relative;
  font-style: italic;
  color: var(--c-accent);
}
/* The nested form, used when a word carries punctuation the emphasis must
   not swallow. inline-block so the drawn rule inside it has a real box. */
.word__em { display: inline-block; }

.sv-h2--light { color: #fff; }
.sv-h2--light em { color: var(--c-mint); }

.word__rule {
  position: absolute;
  left: 0; right: 0; bottom: 0.06em;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--c-accent), rgba(255, 107, 44, 0));
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* --- The split hero band --------------------------------------------------
   PROMOTED OUT OF contact.css, under the rule that file states twice: shared
   markup belongs here the day a third page needs it.

   template-parts/common/page-hero.php takes its prefix as an argument and
   defaults to `ct`. Five callers now take that default — the contact page, the
   services archive, the Services page template, page.php and single.php — but
   only the contact page loads contact.css, so on the other four the band was
   emitted with no rules against it at all: no panel, no padding, no grid. The
   hero rendered as 2411px of stacked static blocks.

   The Our Office, New Patients and Reviews pages keep their own `of-`, `np-`
   and `rv-` variants in their own stylesheets. Those are genuinely different
   compositions on the same skeleton, not copies of this one, so they stay
   where they are.

   .ct-play is NOT here. It is the video control, the contact page is the only
   caller that passes a video, and contact.css's own note asks for a third
   consumer before promoting it.
   -------------------------------------------------------------------------- */
.ct-hero {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-top: calc(var(--util-h) + var(--nav-h) + var(--hero-inset) + var(--hero-pad));
  background: var(--sv-ivory);
}

.ct-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
  align-items: stretch;
  /* THE BAND'S HEIGHT IS SET HERE AND NOWHERE ELSE. The <video> is taken out
     of flow below so it cannot contribute its intrinsic height — a portrait
     clip would otherwise stretch this band past the fold and push the title
     down with it. The row is as tall as this says; the film crops to fit. */
  min-height: clamp(22rem, 48vh, 31rem);
}

/* --- The green field ----------------------------------------------------- */
.ct-hero__panel {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  /* The right padding carries the film's overlap as well as its own gutter,
     so the copy never runs under the card that sits on top of it. */
  padding: clamp(2rem, 5vw, 4.5rem) clamp(2.5rem, 6vw, 6rem) clamp(2rem, 5vw, 4.5rem) clamp(1.25rem, 5vw, 5rem);
  /* The practice's green, deepened towards the outside edge so the type has
     a darker ground to sit on than the middle of the panel. */
  background:
    radial-gradient(120% 90% at 84% 12%, rgba(127, 227, 212, .2) 0%, rgba(127, 227, 212, 0) 62%),
    linear-gradient(152deg, #5aad38 0%, #4c9c2e 46%, #3d7f25 100%);
}

/* A single very soft wash. Flat colour across a panel this large reads as a
   swatch; this gives it a surface without giving it a pattern. */
.ct-hero__grain {
  position: absolute;
  inset: -20%;
  z-index: -1;
  background:
    radial-gradient(38% 34% at 22% 28%, rgba(255, 255, 255, .14) 0%, rgba(255, 255, 255, 0) 70%),
    radial-gradient(30% 40% at 78% 76%, rgba(6, 33, 58, .16) 0%, rgba(6, 33, 58, 0) 72%);
  pointer-events: none;
}

/* The practice's own mark, huge and nearly invisible, drifting on the
   pointer. The same device the footer uses, so the two ends of the page
   rhyme. */
.ct-hero__leaf {
  position: absolute;
  left: -5%;
  bottom: -36%;
  z-index: -1;
  width: min(28vw, 20rem);
  color: rgba(255, 255, 255, .085);
  pointer-events: none;
}
.ct-hero__leaf svg { width: 100%; height: auto; }

.ct-hero__copy {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 30rem;
  margin-left: auto;
}

/* On the green, the breadcrumb's ink has to invert. contact.css and office.css
   each stated these for themselves and each left a note asking for exactly
   this promotion once a third page needed them; page-hero.php emits the
   modifier for every caller, so that day has arrived. */
.sv-crumbs--light ol,
.sv-crumbs--light li { color: rgba(255, 255, 255, .72); }
.sv-crumbs--light a { color: rgba(255, 255, 255, .82); }
.sv-crumbs--light a:hover { color: #fff; }
.sv-crumbs--light [aria-current="page"] { color: #fff; }

.ct-hero__title {
  margin-top: clamp(.9rem, 2.5vh, 1.5rem);
  font-family: var(--f-display);
  font-size: clamp(2.6rem, 1rem + 5.6vw, 4.6rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.022em;
  color: #fff;
}
/* The accent word is white too — a second colour on a green field is one
   colour too many. It carries the italic and nothing else. */
.ct-hero__title em { font-style: italic; color: rgba(255, 255, 255, .93); }

.ct-hero__tag {
  margin-top: clamp(.6rem, 1.6vh, 1rem);
  font-family: var(--f-display);
  font-style: italic;
  font-size: clamp(1rem, .9rem + .45vw, 1.28rem);
  color: rgba(255, 255, 255, .88);
}

/* --- The film, or the photograph ----------------------------------------- */
.ct-hero__stage {
  position: relative;
  z-index: 1;
  display: flex;
  /* Pulled back over the green so the card overlaps the field it sits on.
     Negative margins are written as a positive clamp times -1 — a clamp with
     three negative values is legal but reads backwards. */
  margin-left: calc(clamp(1.25rem, 3vw, 3.25rem) * -1);
  /* THE THREE INSETS ARE ONE NUMBER. Top, right and bottom come off the same
     clamp, so the card floats by the same amount on every side it is not
     overlapping — an inset that differs by a few pixels per edge is the
     difference between a composed card and a misplaced one. */
  padding: clamp(.75rem, 1.6vw, 1.75rem);
  padding-left: 0;
}

.ct-hero__frame {
  position: relative;
  flex: 1;
  margin: 0;
  overflow: hidden;
  /* It floats on three sides and overlaps on the fourth, so it is drawn as a
     card: one radius, all four corners. */
  border-radius: var(--sv-r-xl);
  background: #06213a;
  box-shadow: 0 30px 70px rgba(11, 45, 82, .22);
}
/* ABSOLUTE, so the clip's own dimensions never drive the band's height — see
   the note on .ct-hero__inner. The frame is sized by the grid row and the
   film covers whatever that turns out to be. */
.ct-hero__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}
/* The still is the same bargain as the film: the frame is the grid's size and
   the picture covers it, so a portrait original cannot lengthen the band. */
.ct-hero__photo {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* A quarter-stop of depth in the corners, so the white play glyph has
   something to sit against whatever the footage is doing. */
.ct-hero__grade {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(120% 90% at 50% 50%, rgba(6, 33, 58, 0) 42%, rgba(6, 33, 58, .34) 100%);
  pointer-events: none;
}

@media (max-width: 1180px) {
  .ct-hero__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .ct-hero__copy  { max-width: 26rem; }
}

@media (max-width: 980px) {
  /* The hero becomes a stack: the green field, then the film under it. */
  .ct-hero__inner  { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .ct-hero__copy   { max-width: none; margin-left: 0; }
  .ct-hero__panel  { padding: clamp(2.25rem, 7vw, 3.5rem) clamp(1.25rem, 5vw, 3rem); }
  .ct-hero__leaf   { left: -14%; bottom: -42%; width: min(46vw, 18rem); }
  .ct-hero__stage  { margin-left: 0; padding: 0; }
  .ct-hero__frame  { aspect-ratio: 16 / 10; border-radius: var(--sv-r-lg) var(--sv-r-lg) 0 0; }
}

@media (max-width: 560px) {
  .ct-hero__title { font-size: clamp(2.4rem, 1rem + 9vw, 3.4rem); }
}

@media (prefers-reduced-motion: reduce) {
  .ct-hero__video { will-change: auto; }
}

/* --- Prose ---------------------------------------------------------------- */
.sv-lede {
  max-width: 46ch;
  margin-top: clamp(1.25rem, 2.4vw, 1.9rem);
  font-size: var(--fs-lede);
  line-height: var(--lh-body);
  color: var(--c-ink);
}
.sv-p {
  max-width: 58ch;
  margin-top: 1.1rem;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--c-ink-soft);
}

/* --- The quiet link: the third action level ------------------------------- */
.sv-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding-bottom: .3rem;
  font-family: var(--f-ui);
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--c-blue-700);
  transition: color var(--t-fast) var(--ease-out);
}
.sv-link svg { width: 18px; height: 18px; transition: transform var(--t-med) var(--ease-out); }
.sv-link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1.5px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--c-blue-500), var(--c-mint));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-med) var(--ease-out);
}
.sv-link:hover { color: var(--c-blue-900); }
.sv-link:hover::after { transform: scaleX(1); }
.sv-link:hover svg { transform: translateX(4px); }

/* --- Chips ---------------------------------------------------------------- */
.sv-chip {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .5rem .95rem;
  font-family: var(--f-ui);
  font-size: 0.79rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--c-blue-900);
  background: rgba(255, 255, 255, 0.82);
  border: 1px solid var(--glass-brd);
  border-radius: 100px;
  box-shadow: var(--sh-sm);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
}
.sv-chip svg { width: 15px; height: 15px; color: var(--c-accent); }
.sv-chip--solid {
  color: #fff;
  background: linear-gradient(135deg, var(--c-blue-500), var(--c-blue-700));
  border-color: transparent;
  box-shadow: 0 6px 18px rgba(26, 114, 214, .28);
}

/* --- The floating card: one component, many positions --------------------- */
.sv-float {
  position: absolute;
  z-index: 3;
  width: min(19rem, 82%);
  padding: clamp(1rem, 1.6vw, 1.35rem);
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid var(--glass-brd);
  border-radius: var(--sv-r-md);
  box-shadow: var(--sh-lg);
  -webkit-backdrop-filter: blur(20px) saturate(165%);
  backdrop-filter: blur(20px) saturate(165%);
}
.sv-float__mark {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  margin-bottom: .7rem;
  color: var(--c-blue-500);
  background: rgba(26, 114, 214, .09);
  border-radius: 11px;
}
.sv-float__mark svg { width: 22px; height: 22px; }
.sv-float__kicker {
  font-family: var(--f-ui);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-ink-faint);
}
.sv-float__title {
  margin-top: .1rem;
  font-family: var(--f-display);
  font-size: 1.16rem;
  line-height: 1.2;
  color: var(--c-blue-900);
}
.sv-float__title--lg { font-size: 1.6rem; }
.sv-float__text {
  margin-top: .4rem;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--c-ink-soft);
}
.sv-float__text--tight { font-size: 0.858rem; }
.sv-float__text a { color: var(--c-blue-700); text-decoration: underline; text-underline-offset: 2px; }
.sv-float__text a:hover { color: var(--c-accent); }

/* A short list of links inside a floating card. Rows, not a run-on
   sentence — see the note where it is emitted. */
.sv-float__links { margin-top: .5rem; list-style: none; }
.sv-float__links li + li { margin-top: .3rem; }
.sv-float__links a {
  display: flex;
  align-items: center;
  gap: .4rem;
  font-family: var(--f-ui);
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--c-blue-700);
  transition: color var(--t-fast) var(--ease-out), gap var(--t-fast) var(--ease-out);
}
.sv-float__links a::before {
  content: "";
  flex: none;
  width: 10px; height: 1.5px;
  background: currentColor;
  border-radius: 2px;
}
.sv-float__links a:hover { color: var(--c-accent); gap: .6rem; }

/* THE FLOAT IS WRITTEN ON `translate`, NOT `transform`, and that is the whole
   reason it can coexist with the reveal: site.js owns `transform` on these
   same elements. Two writers on one property trade it back and forth every
   frame; two writers on two composable properties simply stack. */
@keyframes sv-drift {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -9px; }
}
[data-float] { animation: sv-drift 7s ease-in-out infinite; }
[data-float]:nth-of-type(even) { animation-duration: 8.5s; animation-delay: -2.5s; }

/* --- The shade strip (decorative) ----------------------------------------- */
.sv-shade { display: flex; gap: 5px; }
.sv-shade i {
  display: block;
  width: 26px; height: 30px;
  background: var(--s);
  border: 1px solid rgba(11, 45, 82, .09);
  border-radius: 6px 6px 9px 9px;
  box-shadow: inset 0 -6px 10px rgba(11, 45, 82, .05);
}

/* --- Breadcrumb ----------------------------------------------------------- */
.sv-crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  list-style: none;
  font-family: var(--f-ui);
  font-size: 0.81rem;
  font-weight: 500;
  color: var(--c-ink-faint);
}
.sv-crumbs li + li::before {
  content: "";
  display: inline-block;
  width: 4px; height: 4px;
  margin-right: .5rem;
  vertical-align: 2px;
  background: currentColor;
  border-radius: 50%;
  opacity: .45;
}
.sv-crumbs a { transition: color var(--t-fast) var(--ease-out); }
.sv-crumbs a:hover { color: var(--c-blue-700); }
.sv-crumbs [aria-current] { color: var(--c-blue-900); }


/* Present for a reader, absent for a viewer. Used where a section needs a
   heading for the document outline but the design carries its own title —
   see the doctor showcase on the Our Team page. Not `display:none`, which
   takes it out of the accessibility tree along with the layout. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ==========================================================================
   04. REVEAL ENTRY STATES
   ---------------------------------------------------------------------------
   Set here, not in JS, so there is no flash of the finished layout before the
   script runs. site.js removes `.no-js` in the same frame it writes the GSAP
   start values; if the script or GSAP never arrives, the class stays and
   everything below renders in its final state.

   THREE CHANNELS, and they never overlap:
     [data-sv]        opacity + y      the copy
     [data-sv-mask]   clip-path        the photographs
     .line__inner     yPercent + blur  the headlines (style.css already hides
                                       these globally)
   ========================================================================== */
[data-sv] { opacity: 0; }
/* Written in the four-value percentage form GSAP interpolates cleanly. */
[data-sv-mask] { clip-path: inset(0% 0% 100% 0%); }

.no-js [data-sv] { opacity: 1; }
.no-js [data-sv-mask] { clip-path: none; }
.no-js .sv-eyebrow__rule,
.no-js .sv-step__link,
.no-js .word__rule { transform: none; }
.no-js [data-float] { animation: none; }


/* ==========================================================================
   05. HERO — shared by the hub and by every service page
   ---------------------------------------------------------------------------
   NOT the homepage hero. That one is a full-bleed photograph with the type
   washed over it; this is an editorial split — ivory ground carrying the
   type on the left, a tall photograph carrying the floating cards on the
   right.
   ========================================================================== */
.sv-hero {
  position: relative;
  isolation: isolate;
  /* `clip`, not `hidden` — hidden on a section this tall would make it a
     scroll container and take the sticky rail below it with it. */
  overflow: clip;
  padding-top: calc(var(--util-h) + var(--nav-h) + var(--hero-inset) + var(--hero-pad) + clamp(1.5rem, 5vh, 3.5rem));
  padding-bottom: clamp(3.5rem, 9vh, 7rem);
  background:
    radial-gradient(90% 70% at 92% 4%, rgba(214, 232, 251, .8) 0%, rgba(214, 232, 251, 0) 58%),
    linear-gradient(168deg, var(--sv-ivory) 0%, #f6f5f2 38%, #eef5fd 100%);
}

.sv-hero__blob {
  position: absolute;
  top: -18%;
  right: -14%;
  z-index: -2;
  width: min(46vw, 640px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 38%,
              rgba(127, 227, 212, .34) 0%,
              rgba(110, 178, 244, .22) 42%,
              rgba(110, 178, 244, 0) 70%);
  filter: blur(18px);
  pointer-events: none;
}

/* THE OVERSIZED WORD. Set at a size nobody is meant to read it at and an
   opacity nobody is meant to notice — it is grain, not copy. Most of it is
   under the floor of the section: what shows is the top third of the
   letterforms, which reads as texture along the seam. */
.sv-hero__word {
  position: absolute;
  left: -0.06em;
  bottom: -0.46em;
  z-index: -1;
  font-family: var(--f-display);
  font-size: clamp(6rem, 19vw, 17rem);
  font-style: italic;
  line-height: .8;
  letter-spacing: -0.03em;
  color: rgba(11, 45, 82, .038);
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
}

.sv-hero__inner {
  position: relative;
  z-index: 1;
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1.04fr) minmax(0, 0.96fr);
  align-items: center;
  gap: clamp(2rem, 4.5vw, 4.5rem);
}

.sv-hero__copy .sv-eyebrow { margin-top: clamp(1rem, 2.5vh, 1.6rem); }

.sv-hero__title {
  position: relative;
  margin-top: clamp(.9rem, 2vh, 1.4rem);
  font-family: var(--f-display);
  font-size: var(--sv-fs-h1);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.022em;
  color: var(--c-ink);
}
/* The hub's title is ONE WORD. It can afford to be much larger than a
   treatment name, and at this size it becomes the page's own graphic — so
   the accent rule is a direct child of the H1 rather than of an <em>, and
   spans the whole word. */
.sv-hero__title--hub {
  /* `width: max-content`, NOT `display: inline-block`. The box has to shrink
     to the word so the rule under it stops where the word does — but it must
     stay BLOCK level, or the strapline that follows it is inline-level too
     and the two share a line box. `max-width` keeps it inside the column on
     a narrow screen. */
  width: max-content;
  max-width: 100%;
  font-size: clamp(3.5rem, 1rem + 10vw, 9rem);
  line-height: .94;
}
.sv-hero__title--hub > .word__rule { bottom: .02em; height: 4px; }

.sv-hero__tag {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  margin-top: clamp(.9rem, 2vh, 1.35rem);
  font-family: var(--f-display);
  font-style: italic;
  font-size: clamp(1.05rem, 0.95rem + 0.45vw, 1.3rem);
  color: var(--c-blue-700);
}
.sv-hero__tag::before {
  content: "";
  width: 22px; height: 1.5px;
  background: var(--c-blue-300);
  border-radius: 2px;
}

.sv-hero__lede {
  max-width: 46ch;
  margin-top: clamp(1.1rem, 2.4vh, 1.6rem);
  font-size: var(--fs-body);
  line-height: 1.68;
  color: var(--c-ink-soft);
}

.sv-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(.7rem, 1.4vw, 1rem);
  margin-top: clamp(1.5rem, 3.4vh, 2.25rem);
}
/* The homepage's breathing CTA, borrowed for the one primary action above
   the fold. The keyframes live in style.css. */
.sv-cta-breathe { animation: cta-breathe 5.5s ease-in-out infinite; }
.sv-cta-breathe:hover,
.sv-cta-breathe:focus-visible { animation: none; }

.sv-hero__facts {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.1rem, 2.6vw, 2.25rem);
  margin-top: clamp(1.9rem, 4vh, 2.75rem);
  padding-top: clamp(1.4rem, 3vh, 2rem);
  list-style: none;
  border-top: 1px solid var(--sv-hair);
}
.sv-hero__facts li { display: flex; flex-direction: column; gap: .3rem; }
.sv-hero__fact-n {
  font-family: var(--f-display);
  font-size: clamp(1.9rem, 1.2rem + 1.6vw, 2.7rem);
  line-height: 1;
  color: var(--c-blue-700);
  font-variant-numeric: var(--nums-tab);
}
.sv-hero__fact-n--word {
  font-style: italic;
  font-size: clamp(1.35rem, 0.9rem + 1.1vw, 1.9rem);
  color: var(--c-accent);
}
.sv-hero__fact-l {
  font-family: var(--f-ui);
  font-size: 0.79rem;
  font-weight: 500;
  letter-spacing: .04em;
  line-height: 1.45;
  color: var(--c-ink-faint);
}

.sv-hero__media { position: relative; }

.sv-hero__frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1 / 1.12;
  border-radius: var(--sv-r-xl);
  /* A finished gradient behind the photograph, so a slow decode — or a file
     dropped by the [data-photo] guard — leaves a composed panel, not a box. */
  background:
    radial-gradient(80% 70% at 70% 30%, rgba(174, 207, 246, .9) 0%, rgba(174, 207, 246, 0) 62%),
    linear-gradient(120deg, #f4fafd 0%, #dceaf8 46%, #bcd8f2 100%);
  box-shadow: 0 30px 70px rgba(11, 45, 82, .16), 0 6px 18px rgba(11, 45, 82, .07);
}
.sv-hero__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 42%;
  filter: contrast(1.04) saturate(1.05);
  transition: transform 1.4s var(--ease-soft);
}
.sv-hero__frame:hover .sv-hero__photo { transform: scale(1.03); }

.sv-hero__grade {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgba(6, 33, 58, .30) 0%, rgba(6, 33, 58, 0) 42%),
    linear-gradient(115deg, rgba(26, 114, 214, .12) 0%, rgba(26, 114, 214, 0) 45%);
  pointer-events: none;
}

.sv-hero .sv-chip--float {
  position: absolute;
  top: clamp(1rem, 3vw, 2rem);
  left: clamp(-1.5rem, -1.2vw, -0.5rem);
  z-index: 3;
}
.sv-float--benefit { left: clamp(-3.5rem, -4vw, -1rem); bottom: clamp(1.5rem, 4vw, 2.75rem); }
.sv-float--shade {
  right: clamp(-2.5rem, -3vw, -0.75rem);
  top: 26%;
  width: min(15rem, 68%);
}
.sv-float--shade .sv-float__text { margin-top: .65rem; }


/* ==========================================================================
   06. THE STICKY SECTION RAIL
   ---------------------------------------------------------------------------
   NAVIGATION, NOT DECORATION. It sticks UNDER the header pill: the offset is
   the pill's own height plus its inset, expressed in the pill's tokens so the
   two cannot drift. The utility bar is NOT in the sum — by the time the rail
   is stuck, the bar has collapsed.
   ========================================================================== */
.sv-jump {
  position: sticky;
  top: calc(var(--hero-inset) + var(--hero-pad) + 62px);
  z-index: 40;
  padding-block: clamp(1rem, 2vh, 1.5rem);
  background: linear-gradient(to bottom, rgba(238, 245, 253, .96) 62%, rgba(238, 245, 253, 0));
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.sv-jump__inner {
  width: var(--sv-shell);
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .45rem .45rem .45rem 1.1rem;
  background: rgba(255, 255, 255, .92);
  border: 1px solid rgba(186, 214, 243, .6);
  border-radius: 100px;
  box-shadow: 0 2px 8px rgba(11, 45, 82, .05), 0 14px 34px rgba(11, 45, 82, .07);
  -webkit-backdrop-filter: blur(18px) saturate(165%);
  backdrop-filter: blur(18px) saturate(165%);
}
.sv-jump__list {
  display: flex;
  align-items: center;
  gap: clamp(.35rem, 1.4vw, 1.1rem);
  list-style: none;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.sv-jump__list::-webkit-scrollbar { display: none; }
.sv-jump__list a {
  display: block;
  padding: .55rem .85rem;
  font-family: var(--f-ui);
  font-size: 0.88rem;
  font-weight: 500;
  white-space: nowrap;
  color: var(--c-ink-soft);
  border-radius: 100px;
  transition: color var(--t-fast) var(--ease-out),
              background var(--t-fast) var(--ease-out);
}
.sv-jump__list a:hover { color: var(--c-blue-700); background: rgba(26, 114, 214, .07); }
.sv-jump__list a.is-active {
  color: var(--c-blue-900);
  background: rgba(26, 114, 214, .11);
}
.sv-jump__cta { flex: none; }


/* ==========================================================================
   07. SERVICE CARDS
   ---------------------------------------------------------------------------
   ONE COMPONENT, FOUR VARIANTS, and the difference between them is size and
   direction rather than a different set of parts. Every one is a whole-card
   <a>: the target is the size of the card on a phone, and the hover state has
   one owner instead of three competing ones.

     .svc--feature  the large card at the head of a category
     .svc--tile     the standard grid card, with a photograph
     .svc--type     the same card WITHOUT one — a typographic panel. Not a
                    fallback: eleven of the fifteen treatments have no picture
                    yet, and a grid that mixes photographic and typographic
                    cards reads as art direction, where a grid of grey
                    rectangles reads as a broken page.
     .svc--index    the full-width editorial row
     .svc--row      the horizontal rail card
   ========================================================================== */
.svc { list-style: none; }

.svc__link {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  border-radius: var(--sv-r-lg);
  background: var(--sv-white);
  border: 1px solid var(--sv-card-brd);
  box-shadow: var(--sh-sm);
  transition: transform var(--t-med) var(--ease-out),
              box-shadow var(--t-med) var(--ease-out),
              border-color var(--t-med) var(--ease-out);
}
.svc__link:hover {
  transform: translateY(-4px);
  border-color: var(--c-blue-300);
  box-shadow: var(--sh-lg);
}

.svc__media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: linear-gradient(120deg, #dceaf8, #bcd8f2);
}
.svc__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.2s var(--ease-soft);
}
.svc__link:hover .svc__media img { transform: scale(1.06); }
.svc__tint {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(6, 33, 58, .28) 0%, rgba(6, 33, 58, 0) 55%);
  pointer-events: none;
}

.svc__body {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: clamp(1.15rem, 2vw, 1.6rem);
}

/* THE NUMBER SHIFTS ON HOVER — up and out of the way, as the arrow comes in
   from the other side. It is the smallest possible acknowledgement that the
   card is a control. */
.svc__num {
  font-family: var(--f-ui);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: .16em;
  color: var(--c-blue-300);
  font-variant-numeric: var(--nums-tab);
  transition: color var(--t-med) var(--ease-out),
              transform var(--t-med) var(--ease-out);
}
.svc__link:hover .svc__num { color: var(--c-accent); transform: translateY(-2px); }

.svc__name {
  margin-top: clamp(.9rem, 2vw, 1.5rem);
  font-family: var(--f-display);
  font-size: var(--sv-fs-h3);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.012em;
  color: var(--c-blue-900);
}
.svc__text {
  margin-top: .6rem;
  font-size: 0.925rem;
  line-height: 1.62;
  color: var(--c-ink-soft);
}

.svc__go {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  margin-top: auto;
  padding-top: clamp(1rem, 2vw, 1.4rem);
  font-family: var(--f-ui);
  font-size: 0.84rem;
  font-weight: 500;
  color: var(--c-blue-700);
}
.svc__go svg { width: 16px; height: 16px; transition: transform var(--t-med) var(--ease-out); }
.svc__link:hover .svc__go svg { transform: translateX(5px); }
/* The underline draws from the left rather than appearing. */
.svc__go::before {
  content: "";
  position: absolute;
  left: clamp(1.15rem, 2vw, 1.6rem);
  right: clamp(1.15rem, 2vw, 1.6rem);
  bottom: calc(clamp(1.15rem, 2vw, 1.6rem) - 6px);
  height: 1.5px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--c-blue-500), var(--c-mint));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-med) var(--ease-out);
}
.svc__link:hover .svc__go::before { transform: scaleX(1); }

/* --- The featured card ---------------------------------------------------- */
.svc--feature .svc__link { border-color: transparent; }
.svc--feature .svc__media { aspect-ratio: 16 / 9; }
.svc--feature .svc__body {
  color: #fff;
  background:
    radial-gradient(80% 90% at 84% 8%, rgba(127, 227, 212, .2) 0%, rgba(127, 227, 212, 0) 60%),
    linear-gradient(150deg, var(--c-blue-700) 0%, var(--c-blue-900) 62%, #06213a 100%);
}
.svc--feature .svc__name {
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.2rem);
  color: #fff;
}
.svc--feature .svc__text { color: var(--sv-on-dark-soft); max-width: 42ch; }
.svc--feature .svc__num { color: var(--c-mint); }
.svc--feature .svc__go { color: #fff; }
.svc--feature .svc__go::before { background: linear-gradient(90deg, var(--c-mint), rgba(127, 227, 212, 0)); }
.svc--feature .svc__link:hover .svc__num { color: var(--c-mint); }

/* --- The typographic card -------------------------------------------------
   No photograph, so the NAME becomes the artwork: larger, and given the room
   the picture would have taken. A quiet tint and an oversized ghost number
   keep it from reading as an empty slot.
   -------------------------------------------------------------------------- */
.svc--type .svc__link {
  background: linear-gradient(160deg, #f4f9ff 0%, var(--sv-white) 55%);
}
.svc--type .svc__body { padding-block: clamp(1.6rem, 3vw, 2.4rem); min-height: 13rem; }
.svc--type .svc__name {
  margin-top: clamp(1.6rem, 3.5vw, 3rem);
  font-size: clamp(1.35rem, 1rem + 1.1vw, 1.85rem);
}
.svc--type .svc__body::after {
  content: "";
  position: absolute;
  right: -8%;
  bottom: -22%;
  width: 58%;
  aspect-ratio: 1;
  border: 1px solid rgba(120, 170, 225, .28);
  border-radius: 50%;
  transition: transform var(--t-slow) var(--ease-out);
  pointer-events: none;
}
.svc--type .svc__link:hover .svc__body::after { transform: translate(-6px, -6px) scale(1.04); }

/* --- The editorial index row ----------------------------------------------
   A full-width row: number, name at display size, arrow. The photograph — if
   the treatment has one — is a preview that fades in on hover at the right,
   which is why it is absolutely placed and clipped to the row.
   -------------------------------------------------------------------------- */
.svc--index .svc__link {
  flex-direction: row;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
  padding: clamp(1.1rem, 2.4vw, 1.9rem) clamp(1rem, 2.4vw, 2rem);
  background: transparent;
  border: 0;
  border-top: 1px solid var(--sv-on-dark-hair);
  border-radius: 0;
  box-shadow: none;
  transition: background var(--t-med) var(--ease-out),
              padding-left var(--t-med) var(--ease-out);
}
.svc--index:last-child .svc__link { border-bottom: 1px solid var(--sv-on-dark-hair); }
.svc--index .svc__link:hover {
  transform: none;
  background: rgba(255, 255, 255, .06);
  padding-left: clamp(1.5rem, 3.4vw, 3rem);
}

.svc--index .svc__media {
  position: absolute;
  right: clamp(4rem, 9vw, 8rem);
  top: 50%;
  z-index: 1;
  width: clamp(7rem, 12vw, 11rem);
  aspect-ratio: 4 / 3;
  border-radius: var(--sv-r-sm);
  opacity: 0;
  transform: translateY(-50%) scale(.94) rotate(-2deg);
  transition: opacity var(--t-med) var(--ease-out),
              transform var(--t-med) var(--ease-out);
  pointer-events: none;
}
.svc--index .svc__link:hover .svc__media {
  opacity: 1;
  transform: translateY(-50%) scale(1) rotate(-2deg);
}
.svc--index .svc__tint { display: none; }

.svc--index .svc__body {
  flex-direction: row;
  align-items: baseline;
  gap: clamp(1rem, 3vw, 2.5rem);
  padding: 0;
}
.svc--index .svc__num { flex: none; color: rgba(174, 207, 246, .6); }
.svc--index .svc__link:hover .svc__num { color: var(--c-mint); transform: none; }
.svc--index .svc__name {
  flex: 1 1 auto;
  margin-top: 0;
  font-size: clamp(1.4rem, 0.9rem + 2vw, 2.6rem);
  line-height: 1.1;
  color: #fff;
  transition: transform var(--t-med) var(--ease-out);
}
.svc--index .svc__text { display: none; }
.svc--index .svc__go {
  flex: none;
  margin-top: 0;
  padding-top: 0;
  color: var(--sv-on-dark-soft);
}
.svc--index .svc__go span { display: none; }
.svc--index .svc__go svg { width: 22px; height: 22px; }
.svc--index .svc__go::before { display: none; }
.svc--index .svc__link:hover .svc__go { color: var(--c-mint); }

/* --- The rail card -------------------------------------------------------- */
.svc--row { flex: 0 0 clamp(14rem, 24vw, 19rem); scroll-snap-align: start; }
.svc--row .svc__media { aspect-ratio: 16 / 11; }
.svc--row .svc__body { padding: clamp(1rem, 1.6vw, 1.25rem); }
.svc--row .svc__name { margin-top: .9rem; font-size: 1.12rem; }
.svc--row .svc__go { padding-top: 1rem; font-size: 0.8rem; }
.svc--row.is-current .svc__link { border-color: var(--c-blue-300); }


/* ==========================================================================
   08. THE TREATMENT RAIL AND THE PAGER
   ---------------------------------------------------------------------------
   The rail SCROLLS SIDEWAYS rather than wrapping. Nine cards wrapped into
   three rows is a second services page at the bottom of a treatment page; one
   row you push is a suggestion.
   ========================================================================== */
.svc-rail {
  position: relative;
  z-index: 1;
  padding-block: clamp(3rem, 7vh, 5rem);
  background: var(--sv-white);
}
.svc-rail__inner { width: var(--sv-shell); margin-inline: auto; }
.svc-rail__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: clamp(1.5rem, 3vh, 2.25rem);
}
.svc-rail__title {
  font-family: var(--f-display);
  font-size: clamp(1.4rem, 1.1rem + 1vw, 2rem);
  color: var(--c-blue-900);
}
.svc-rail__hint {
  font-family: var(--f-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--c-ink-faint);
}
.svc-rail__track {
  display: flex;
  gap: clamp(.9rem, 1.8vw, 1.4rem);
  list-style: none;
  /* The gutter is added back as padding so the first and last card can sit
     flush with the column while the strip itself bleeds to the page edge. */
  padding-bottom: .5rem;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.svc-rail__track::-webkit-scrollbar { display: none; }

/* THE CELLS MUST NOT SHRINK. They are flex children of a scrolling track, so
   without a basis they compress to fit the container — eight treatments were
   squeezing into the width of five and the strip never overflowed, which meant
   it never scrolled and the arrows correctly stayed hidden because there was
   nothing to scroll. Fixing the basis is what makes it a rail. */
.svc-rail__cell {
  flex: 0 0 clamp(14.5rem, 21vw, 18.5rem);
  display: flex;
  scroll-snap-align: start;
}

/* --- The pager ------------------------------------------------------------ */
.svc-pager {
  position: relative;
  z-index: 1;
  background: var(--sv-white);
  border-top: 1px solid rgba(120, 170, 225, .3);
}
.svc-pager__inner {
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.svc-pager__side {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: clamp(1.5rem, 3vw, 2.5rem) clamp(1rem, 2vw, 1.75rem);
  transition: background var(--t-med) var(--ease-out);
}
.svc-pager__side:hover { background: #f5faff; }
.svc-pager__side--next { text-align: right; align-items: flex-end; border-left: 1px solid rgba(120, 170, 225, .3); }
.svc-pager__kicker {
  font-family: var(--f-ui);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--c-ink-faint);
}
.svc-pager__name {
  font-family: var(--f-display);
  font-size: clamp(1.1rem, 0.95rem + 0.6vw, 1.5rem);
  color: var(--c-blue-900);
}
.svc-pager__side svg { width: 20px; height: 20px; color: var(--c-blue-500); transition: transform var(--t-med) var(--ease-out); }
.svc-pager__side--prev svg { transform: rotate(180deg); }
.svc-pager__side--prev:hover svg { transform: rotate(180deg) translateX(5px); }
.svc-pager__side--next:hover svg { transform: translateX(5px); }


/* ==========================================================================
   09. APPOINTMENT BAND
   The visual pause between the clinical half of a page and the practice half.
   The photograph sits in an ORGANIC container — a four-value radius rather
   than one — so it reads as composed into the band, not dropped into a box.
   ========================================================================== */
.sv-break {
  position: relative;
  z-index: 1;
  padding-block: var(--sv-pad);
  background:
    radial-gradient(60% 80% at 8% 20%, rgba(127, 227, 212, .16) 0%, rgba(127, 227, 212, 0) 60%),
    linear-gradient(120deg, #e8f2fd 0%, #eef5fd 45%, #f7f4ee 100%);
}
.sv-break__inner {
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  align-items: center;
  gap: var(--sv-gap);
}
.sv-break__media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 5 / 4;
  border-radius: clamp(2.5rem, 7vw, 6rem) var(--sv-r-lg) clamp(2.5rem, 7vw, 6rem) var(--sv-r-lg);
  background: linear-gradient(120deg, #dceaf8, #bcd8f2);
  box-shadow: 0 30px 70px rgba(11, 45, 82, .16);
}
.sv-break__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  transition: transform 1.5s var(--ease-soft);
}
.sv-break__media:hover img { transform: scale(1.04); }
.sv-break__caption {
  position: absolute;
  left: clamp(1rem, 3vw, 2rem);
  bottom: clamp(1rem, 3vw, 2rem);
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .6rem 1rem;
  font-family: var(--f-ui);
  font-size: 0.79rem;
  font-weight: 500;
  color: var(--c-blue-900);
  background: rgba(255, 255, 255, .9);
  border-radius: 100px;
  box-shadow: var(--sh-sm);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}
.sv-break__caption-dot {
  width: 7px; height: 7px;
  background: var(--c-accent);
  border-radius: 50%;
  box-shadow: 0 0 0 4px rgba(255, 107, 44, .16);
}
.sv-break__copy .sv-h2 { margin-top: clamp(1rem, 2vh, 1.5rem); }
.sv-break__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(.7rem, 1.4vw, 1rem);
  margin-top: clamp(1.75rem, 3.4vh, 2.5rem);
}


/* ==========================================================================
   10. PATIENT EXPERIENCE
   Copy left, a three-photograph collage right. One tall pair over one wide
   cell rather than three equal tiles — three equal tiles is the template look
   this site is trying not to be.
   ========================================================================== */
.sv-exp {
  position: relative;
  z-index: 1;
  padding-block: var(--sv-pad);
  background: var(--sv-white);
}
.sv-exp__inner {
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  align-items: center;
  gap: var(--sv-gap);
}
.sv-exp__copy .sv-h2 { margin-top: clamp(1rem, 2vh, 1.5rem); }

.sv-exp__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.1rem, 2.4vw, 1.9rem);
  margin-top: clamp(2rem, 4vh, 2.75rem);
  list-style: none;
}
.sv-exp__list li {
  display: flex;
  flex-direction: column;
  gap: .25rem;
  padding-left: 1.1rem;
  border-left: 2px solid rgba(120, 170, 225, .4);
}
.sv-exp__n {
  font-family: var(--f-display);
  font-size: clamp(1.6rem, 1.2rem + 1.1vw, 2.2rem);
  line-height: 1.05;
  color: var(--c-blue-700);
  font-variant-numeric: var(--nums-tab);
}
.sv-exp__n--word { font-style: italic; color: var(--c-accent); }
.sv-exp__l {
  margin-top: .3rem;
  font-family: var(--f-ui);
  font-size: 0.79rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--c-blue-900);
}
.sv-exp__t { font-size: 0.9rem; line-height: 1.55; color: var(--c-ink-faint); }
.sv-exp__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1rem, 2vw, 1.75rem);
  margin-top: clamp(2rem, 4vh, 2.75rem);
}

.sv-exp__cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(.75rem, 1.6vw, 1.25rem);
}
.sv-exp__card {
  position: relative;
  overflow: hidden;
  border-radius: var(--sv-r-lg);
  background: linear-gradient(120deg, #dceaf8, #bcd8f2);
  box-shadow: var(--sh-md);
}
.sv-exp__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 22%;
  transition: transform 1.4s var(--ease-soft);
}
.sv-exp__card:hover img { transform: scale(1.05); }
.sv-exp__card--a,
.sv-exp__card--b { aspect-ratio: 1 / 1.1; }
.sv-exp__card--c { grid-column: 1 / -1; aspect-ratio: 16 / 8; }
.sv-exp__card--c img { object-position: 50% 28%; }

.sv-exp__card figcaption {
  position: absolute;
  left: .75rem; right: .75rem; bottom: .75rem;
  padding: .7rem .9rem;
  font-family: var(--f-display);
  font-size: 1rem;
  line-height: 1.2;
  color: var(--c-blue-900);
  background: rgba(255, 255, 255, .9);
  border-radius: var(--sv-r-sm);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
}
.sv-exp__card figcaption span {
  display: block;
  margin-bottom: .1rem;
  font-family: var(--f-ui);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--c-blue-700);
}


/* ==========================================================================
   11. TESTIMONIALS — the split
   ---------------------------------------------------------------------------
   Editorial column left, photograph right with the quote card straddling its
   corner. Not a centred carousel: a carousel puts the words and the proof in
   the same box and makes the section look like a widget.

   THE QUOTE CARD SITS BOTTOM-RIGHT because this photograph carries its own
   caption baked into the lower LEFT of the artwork. Move the card and you
   cover the words in the picture.
   ========================================================================== */
.sv-reviews {
  position: relative;
  z-index: 1;
  padding-block: var(--sv-pad);
  background: linear-gradient(180deg, #eef5fd 0%, #e6f0fb 100%);
}
.sv-reviews__inner {
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
  align-items: center;
  gap: var(--sv-gap);
}
.sv-reviews__copy .sv-h2 { margin-top: clamp(1rem, 2vh, 1.5rem); }
.sv-reviews__lede {
  max-width: 46ch;
  margin-top: clamp(1rem, 2.2vh, 1.5rem);
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--c-ink-soft);
}

.sv-reviews__stat {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  margin-top: clamp(1.6rem, 3vh, 2.25rem);
  padding-top: clamp(1.25rem, 2.6vh, 1.75rem);
  border-top: 1px solid rgba(120, 170, 225, .35);
}
.sv-reviews__stat-n {
  flex: none;
  font-family: var(--f-display);
  font-size: clamp(2.2rem, 1.4rem + 2vw, 3.2rem);
  line-height: 1;
  color: var(--c-blue-700);
  font-variant-numeric: var(--nums-tab);
}
.sv-reviews__stat-l {
  display: flex;
  flex-direction: column;
  font-family: var(--f-ui);
  font-size: 0.79rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--c-blue-900);
}
.sv-reviews__stat-l em {
  margin-top: .25rem;
  font-family: var(--f-sans);
  font-size: 0.86rem;
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-ink-faint);
}

.sv-reviews__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: clamp(1.5rem, 3vh, 2rem);
}
.sv-reviews__controls { display: flex; gap: .5rem; }
.sv-reviews__arrow {
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  color: var(--c-blue-700);
  background: rgba(255, 255, 255, .8);
  border: 1px solid var(--glass-brd);
  border-radius: 50%;
  transition: background var(--t-fast) var(--ease-out),
              transform var(--t-fast) var(--ease-out);
}
.sv-reviews__arrow svg { width: 18px; height: 18px; }
.sv-reviews__arrow[data-review-prev] svg { transform: rotate(180deg); }
.sv-reviews__arrow:hover { background: #fff; transform: translateY(-2px); }

.sv-reviews__stage { position: relative; }
.sv-reviews__media {
  overflow: hidden;
  aspect-ratio: 4 / 3.4;
  border-radius: var(--sv-r-xl);
  background: linear-gradient(120deg, #dceaf8, #bcd8f2);
  box-shadow: 0 26px 60px rgba(11, 45, 82, .16);
}
.sv-reviews__media img { width: 100%; height: 100%; object-fit: cover; }

.sv-reviews__track {
  position: absolute;
  right: clamp(-2rem, -2.5vw, -0.75rem);
  bottom: clamp(-2.5rem, -3vw, -1rem);
  z-index: 3;
  width: min(24rem, 88%);
}
.sv-review {
  padding: clamp(1.25rem, 2.2vw, 1.75rem);
  background: rgba(255, 255, 255, .95);
  border: 1px solid var(--glass-brd);
  border-radius: var(--sv-r-lg);
  box-shadow: var(--sh-lg);
  -webkit-backdrop-filter: blur(18px) saturate(165%);
  backdrop-filter: blur(18px) saturate(165%);
}
.sv-review[aria-hidden="true"] { display: none; }
.sv-review__top { display: flex; align-items: center; gap: .75rem; }
.sv-review__g svg { width: 22px; height: 22px; }
.sv-review__stars { display: inline-flex; gap: 2px; }
.sv-review__stars svg { width: 15px; height: 15px; fill: var(--c-star); }

.sv-review__quote p {
  margin-top: .9rem;
  font-family: var(--f-display);
  font-size: clamp(1rem, 0.92rem + 0.4vw, 1.16rem);
  font-style: italic;
  line-height: 1.5;
  color: var(--c-ink);
}
.sv-review__by { display: flex; flex-direction: column; gap: .15rem; margin-top: 1rem; }
.sv-review__name {
  font-family: var(--f-ui);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--c-blue-900);
}
.sv-review__src { font-family: var(--f-ui); font-size: 0.76rem; color: var(--c-ink-faint); }


/* ==========================================================================
   12. CLINIC LOCATION
   The information panel OVERLAPS the photograph on a desktop — the overlap is
   what makes it one composition rather than a card beside a card.
   ========================================================================== */
.sv-visit {
  position: relative;
  z-index: 1;
  padding-block: var(--sv-pad);
  background:
    radial-gradient(70% 60% at 84% 10%, rgba(127, 227, 212, .14) 0%, rgba(127, 227, 212, 0) 60%),
    linear-gradient(170deg, #e6f0fb 0%, #eaf3fd 55%, #f3f8fd 100%);
}
.sv-visit__inner {
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  align-items: center;
  gap: var(--sv-gap);
}
.sv-visit__copy .sv-h2 { margin-top: clamp(1rem, 2vh, 1.5rem); }

.sv-visit__panel {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.6vw, 2.25rem);
  margin-top: clamp(1.75rem, 3.6vh, 2.5rem);
  padding: clamp(1.4rem, 2.6vw, 2.1rem);
  background: rgba(255, 255, 255, .82);
  border: 1px solid var(--glass-brd);
  border-radius: var(--sv-r-lg);
  box-shadow: var(--sh-md);
  -webkit-backdrop-filter: blur(20px) saturate(165%);
  backdrop-filter: blur(20px) saturate(165%);
}
.sv-visit__label {
  font-family: var(--f-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--c-blue-700);
}
.sv-visit__label + .sv-visit__line { margin-top: .6rem; }
.sv-visit__line + .sv-visit__label { margin-top: 1.4rem; }
.sv-visit__line {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  font-size: 0.935rem;
  line-height: 1.6;
  color: var(--c-ink-soft);
  font-variant-numeric: var(--nums-ui);
}
.sv-visit__line a { transition: color var(--t-fast) var(--ease-out); }
.sv-visit__line a:hover { color: var(--c-blue-700); }
.sv-visit__icon { flex: none; width: 18px; height: 18px; color: var(--c-blue-500); }
.sv-visit__icon svg { width: 18px; height: 18px; }
.sv-visit__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-top: 1.6rem;
}

.sv-hours { list-style: none; font-variant-numeric: var(--nums-tab); }
.sv-hours__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: .5rem 0;
  font-family: var(--f-ui);
  font-size: 0.855rem;
  border-bottom: 1px dashed rgba(120, 170, 225, .35);
}
.sv-hours__row:last-child { border-bottom: 0; }
.sv-hours__row span:first-child { color: var(--c-ink-soft); }
.sv-hours__row span:last-child { font-weight: 500; color: var(--c-blue-900); white-space: nowrap; }
.sv-hours__row--closed span:last-child { color: var(--c-ink-faint); font-weight: 400; }
.sv-hours__row.is-today { position: relative; padding-left: .8rem; }
.sv-hours__row.is-today::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 4px; height: 60%;
  background: var(--c-accent);
  border-radius: 2px;
  transform: translateY(-50%);
}
.sv-hours__row.is-today span:first-child { color: var(--c-blue-900); font-weight: 500; }

.sv-visit__media { position: relative; }
.sv-visit__frame {
  overflow: hidden;
  aspect-ratio: 4 / 4.7;
  border-radius: var(--sv-r-xl);
  background: linear-gradient(120deg, #dceaf8, #bcd8f2);
  box-shadow: 0 30px 70px rgba(11, 45, 82, .16);
}
.sv-visit__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  transition: transform 1.6s var(--ease-soft);
}
.sv-visit__frame:hover img { transform: scale(1.04); }

.sv-visit__inset {
  position: absolute;
  left: clamp(-3rem, -3.5vw, -1rem);
  bottom: clamp(4.5rem, 9vw, 7rem);
  z-index: 3;
  width: min(13rem, 46%);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border: 5px solid #fff;
  border-radius: var(--sv-r-md);
  box-shadow: var(--sh-lg);
}
.sv-visit__inset img { width: 100%; height: 100%; object-fit: cover; }

.sv-float--map {
  right: clamp(-2rem, -2.5vw, -0.75rem);
  bottom: clamp(-1rem, 1vw, 1.25rem);
  width: min(16rem, 72%);
}


/* ==========================================================================
   13. THE CLOSING CALL
   ---------------------------------------------------------------------------
   TWO LAYERS, AND THE BOTTOM ONE IS THE FOOTER'S. The band's lower layer is
   the footer's exact gradient — same angle, same three stops — and the upper
   layer is a navy that fades to nothing before it reaches the floor. So the
   pixel row where this section ends is the same colour as the row where the
   footer begins, at every x, and there is no seam. Change the footer's
   gradient and this one has to change with it.
   ========================================================================== */
.sv-final {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: clamp(4rem, 11vh, 8rem);
  color: var(--sv-on-dark);
  background:
    linear-gradient(180deg, #06213a 0%, rgba(6, 33, 58, .82) 38%, rgba(6, 33, 58, 0) 100%),
    linear-gradient(104deg, #324860 0%, #186d9f 46%, #0c4a5c 100%);
}
.sv-final__shape { position: absolute; z-index: -1; border-radius: 50%; pointer-events: none; }
.sv-final__shape--a {
  top: -30%; right: -8%;
  width: min(42vw, 560px);
  aspect-ratio: 1;
  background: radial-gradient(circle, rgba(127, 227, 212, .2) 0%, rgba(127, 227, 212, 0) 66%);
}
.sv-final__shape--b {
  bottom: -35%; left: -6%;
  width: min(38vw, 480px);
  aspect-ratio: 1;
  background: radial-gradient(circle, rgba(26, 114, 214, .3) 0%, rgba(26, 114, 214, 0) 68%);
}
.sv-final__inner {
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  align-items: center;
  gap: var(--sv-gap);
}
/* The artwork's own ratio — this card's caption is baked into the picture.
   The organic corners are on the diagonal the caption does NOT sit in. */
.sv-final__media {
  overflow: hidden;
  aspect-ratio: 428 / 362;
  border-radius: clamp(2rem, 6vw, 5rem) var(--sv-r-lg) clamp(2rem, 6vw, 5rem) var(--sv-r-lg);
  background: linear-gradient(120deg, #dceaf8, #bcd8f2);
  box-shadow: 0 30px 70px rgba(0, 0, 0, .32);
}
.sv-final__media img { width: 100%; height: 100%; object-fit: cover; }
.sv-final__copy .sv-h1 { margin-top: clamp(1rem, 2vh, 1.5rem); }
.sv-final__text {
  max-width: 46ch;
  margin-top: clamp(1.1rem, 2.4vh, 1.6rem);
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--sv-on-dark-soft);
}
.sv-final__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(.7rem, 1.4vw, 1rem);
  margin-top: clamp(1.75rem, 3.4vh, 2.5rem);
}
/* The glass button was designed against a near-white page. On this band it
   needs its own ink and hairline or it disappears. */
.sv-final .btn--glass {
  color: #fff;
  background: rgba(255, 255, 255, .1);
  border-color: rgba(255, 255, 255, .34);
  box-shadow: none;
}
.sv-final .btn--glass:hover { background: rgba(255, 255, 255, .18); border-color: rgba(255, 255, 255, .55); }


/* ==========================================================================
   14. FOOTER ADDITIONS
   ---------------------------------------------------------------------------
   style.css already styles the footer. These are the pieces the shared
   component added: a fourth column carrying every treatment, and a booking
   action in the brand column.
   ========================================================================== */
/* THREE COLUMNS. The treatments column has been removed from the markup, and a
   fourth track left behind would simply hold an empty gap on the right. The
   practice's column is the widest — it carries the logo, the blurb and the
   booking action; Quick Links is the narrowest, being five one-word labels;
   the contact column takes what is left, which is enough for the address to
   sit on two lines rather than four. */
.foot__grid {
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 0.85fr) minmax(0, 1.15fr);
  column-gap: clamp(1.5rem, 3.5vw, 4rem);
}

.foot__cta {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  margin-top: 1.25rem;
  padding: .7rem 1.2rem;
  font-family: var(--f-ui);
  font-size: 0.85rem;
  font-weight: 500;
  color: #fff;
  background: var(--c-accent);
  border-radius: 100px;
  transition: background var(--t-fast) var(--ease-out),
              transform var(--t-fast) var(--ease-out);
}
.foot__cta svg { width: 16px; height: 16px; transition: transform var(--t-med) var(--ease-out); }
.foot__cta:hover { background: #ff7d45; transform: translateY(-2px); }
.foot__cta:hover svg { transform: translateX(3px); }

.foot__treats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
.foot__treat-head {
  margin-bottom: .5rem;
  font-family: var(--f-ui);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(127, 227, 212, .9);
}
.foot__list--tight li + li { margin-top: .3rem; }
.foot__list--tight a { font-size: 0.85rem; }
.foot__more {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  margin-top: 1.1rem;
  font-family: var(--f-ui);
  font-size: 0.82rem;
  font-weight: 500;
  color: #fff;
  opacity: .9;
}
.foot__more svg { width: 15px; height: 15px; transition: transform var(--t-med) var(--ease-out); }
.foot__more:hover { opacity: 1; }
.foot__more:hover svg { transform: translateX(3px); }
.foot__list a[aria-current] { color: var(--c-mint); }


/* ==========================================================================
   15. THE ACTION DOCK
   Below 980px the section rail can no longer hold its labels and a button, so
   the appointment action moves here: thumb height, always reachable, and away
   while the hero — which has its own button — is still on screen.
   ========================================================================== */
.sv-dock {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 45;
  display: none;
  align-items: center;
  gap: .6rem;
  padding: .6rem max(.75rem, env(safe-area-inset-left))
           calc(.6rem + env(safe-area-inset-bottom))
           max(.75rem, env(safe-area-inset-right));
  background: rgba(255, 255, 255, .94);
  border-top: 1px solid rgba(186, 214, 243, .7);
  box-shadow: 0 -8px 30px rgba(11, 45, 82, .12);
  -webkit-backdrop-filter: blur(18px) saturate(165%);
  backdrop-filter: blur(18px) saturate(165%);
  transform: translateY(0);
  transition: transform var(--t-med) var(--ease-out);
}
.sv-dock.is-away { transform: translateY(130%); }
.sv-dock__call {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  flex: none;
  /* 48px minimum: it is a touch target before it is a link. */
  min-height: 48px;
  padding-inline: 1rem;
  font-family: var(--f-ui);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--c-blue-900);
  background: rgba(26, 114, 214, .08);
  border-radius: 100px;
}
.sv-dock__call svg { width: 18px; height: 18px; }
.sv-dock__cta { flex: 1 1 auto; }


/* ==========================================================================
   16. RESPONSIVE
   ---------------------------------------------------------------------------
   Three breakpoints, each with a reason rather than a device name:

     1180px  the two-column spreads still work but the floating cards no
             longer have gutters to hang in
     980px   two columns of body copy stop being readable. Everything stacks,
             the mega-menu becomes an accordion in the mobile menu, and the
             appointment button moves from the rail to the dock
     560px   the phone. Buttons go full width, grids go to one column, and the
             floating cards stop floating
   ========================================================================== */

@media (max-width: 1180px) {
  .sv-body { --sv-fs-h1: clamp(1.4rem, 1rem + 4.4vw, 4rem); }

  /* The floats come back INSIDE their frames: there is no page gutter left
     for them to hang in, and a card half off the edge of the viewport is a
     card with half a sentence. */
  .sv-hero .sv-chip--float { left: clamp(.75rem, 2vw, 1.25rem); }
  .sv-float--benefit { left: clamp(.75rem, 2vw, 1.25rem); width: min(17rem, 76%); }
  .sv-float--shade   { right: clamp(.75rem, 2vw, 1.25rem); }
  .sv-float--tl      { left: clamp(.75rem, 2vw, 1.25rem); }
  .sv-float--br      { right: clamp(.75rem, 2vw, 1.25rem); }
  .sv-visit__inset   { left: clamp(.75rem, 2vw, 1.25rem); }
  .sv-float--map     { right: clamp(.75rem, 2vw, 1.25rem); bottom: clamp(.75rem, 2vw, 1.25rem); }
  .sv-reviews__track { right: clamp(.75rem, 2vw, 1.25rem); bottom: clamp(.75rem, 2vw, 1.25rem); }

  .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- Tablet: the spreads stack, the menu becomes an accordion ------------- */
@media (max-width: 980px) {
  .sv-body { --sv-pad: clamp(3.25rem, 8vh, 5rem); }

  .sv-hero__inner,
  .sv-break__inner,
  .sv-exp__inner,
  .sv-reviews__inner,
  .sv-visit__inner,
  .sv-final__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(2.25rem, 6vw, 3.5rem);
  }

  /* Copy first in the DOM and first on screen everywhere — the heading tells
     you what you are looking at. */
  .sv-break__media,
  .sv-final__media { order: 2; }
  .sv-break__copy,
  .sv-final__copy { order: 1; }

  .sv-hero__frame   { aspect-ratio: 16 / 11; }
  .sv-hero__photo   { object-position: 62% 46%; }
  .sv-visit__frame  { aspect-ratio: 16 / 12; }
  /* Keeps 428:362 at every width — its caption is baked into the artwork. */
  .sv-final__media  { max-width: 30rem; }
  .sv-reviews__media { max-width: 34rem; }

  .sv-hero__lede,
  .sv-lede,
  .sv-p { max-width: 62ch; }

  .sv-reviews__track { position: static; width: 100%; margin-top: 1.25rem; }
  .sv-exp__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* --- The rail hands its button to the dock ---------------------------- */
  .sv-jump__cta { display: none; }
  .sv-jump__inner { padding-inline: .5rem; }
  .sv-dock { display: flex; }
  .sv-jump__list {
    flex: 1 1 auto;
    min-width: 0;
    -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
    mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
  }
  /* Room for the dock, so the footer's last line is never under it. */
  .foot { padding-bottom: calc(clamp(1.5rem, 3vh, 2.25rem) + 76px); }

  .sv-hero__word { display: none; }

  /* --- THE MEGA-MENU BECOMES AN ACCORDION -------------------------------
     Same markup. It drops out of its absolute box and into the flow of the
     mobile menu; the category headings become live toggles (chrome.js gives
     them back their tabindex here) and the panels animate their height. */
  .mega {
    position: static;
    width: auto;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    opacity: 1;
    visibility: visible;
    transform: none;
  }
  .mega::before { display: none; }
  .mega__inner { display: block; }
  .mega__aside { display: none; }
  .mega__cols { grid-template-columns: minmax(0, 1fr); gap: .25rem; }

  .mega__head { margin: 0; }
  .mega__toggle {
    padding: .8rem .25rem;
    border-bottom: 1px solid rgba(120, 170, 225, .28);
  }
  /* The plus, drawn from two bars so it becomes a minus by rotating one. */
  .mega__sign {
    position: relative;
    display: block;
    flex: none;
    width: 22px; height: 22px;
  }
  .mega__sign::before,
  .mega__sign::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 11px; height: 1.6px;
    background: currentColor;
    border-radius: 2px;
    transform: translate(-50%, -50%);
    transition: transform var(--t-med) var(--ease-out);
  }
  .mega__sign::after { transform: translate(-50%, -50%) rotate(90deg); }
  .mega__toggle[aria-expanded="true"] .mega__sign::after { transform: translate(-50%, -50%) rotate(0deg); }

  /* Height is animated by chrome.js; the panel only has to clip. */
  .mega__panel { overflow: hidden; }
  .mega__list { padding: .35rem 0 .75rem; }
  .mega__item { padding: .55rem .5rem; font-size: 0.95rem; }
  .mega__item svg { opacity: 1; transform: none; }

  /* --- THE SMALL DROPDOWNS BECOME PLAIN NESTED LISTS ---------------------
     No disclosure down here. Two links do not earn a tap to reveal them
     inside a menu that is already open, so they drop into the flow and are
     always visible — chrome.js stays out of the way below this breakpoint. */
  .drop {
    position: static;
    width: auto;
    padding: 0 0 .35rem .85rem;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    opacity: 1;
    visibility: visible;
    transform: none;
  }
  .drop::before { display: none; }
  .drop__item { padding: .5rem .5rem; }
  .drop__note { display: none; }
  .drop__arrow { opacity: 1; transform: none; }
  /* The caret points at a panel that is no longer a panel. */
  .nav__item--drop .nav__caret { display: none; }
}

/* --- The utility strip on a narrow screen ---------------------------------
   Three items and a strapline do not fit a phone. They go in order of what
   is least useful there:

     the strapline   it is on the page itself, twice
     the button      the pill above it and the dock below it both carry the
                     same action; three booking buttons on one 390px screen
                     is two too many

   What is left — the number and the forms link — is what somebody opening
   the site on a phone actually wants at the top.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .util__note { display: none; }
  .util__inner { justify-content: center; }
}
@media (max-width: 560px) {
  .util__cta { display: none; }
  :root { --util-h: 36px; }
}

/* --- The mobile menu's anchor --------------------------------------------
   style.css pins the drop-down panel to a FIXED offset of --nav-h, which was
   right when the pill was the only thing in the header. With the utility
   strip above it — and with that strip collapsing on scroll — a fixed number
   is wrong at least half the time.

   ANCHORED TO THE PILL, and `position: relative` on the pill is what makes
   that deterministic. It would happen anyway — .nav__inner carries a
   backdrop-filter, and a backdrop-filter makes an element the containing
   block for its absolutely positioned descendants — but relying on that is
   relying on a side effect that disappears the day the blur does. Stated
   outright, the panel measures the pill's edges, follows it when the utility
   strip collapses, and needs no number of its own.

   It also has to SCROLL now: fifteen treatments plus six navigation items is
   taller than a phone. `overscroll-behavior` stops that scroll continuing
   into the page behind it when it reaches the end.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .nav__inner { position: relative; }

  .nav__menu {
    position: absolute;
    inset: auto 0 auto 0;
    top: calc(100% + 10px);
    max-height: calc(100svh - 8rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* OPAQUE, and the blur goes with it. Glass was right for a six-item menu
       floating over a photograph; this one is twenty-one rows tall and every
       one of them is a destination. Even a 3% ghost of the hero headline
       behind a treatment name is noise on the one screen that has to be
       unambiguous — and with an opaque panel the backdrop-filter is a GPU
       layer rendering something nobody can see. */
    background: #ffffff;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* --- Phone ---------------------------------------------------------------- */
@media (max-width: 560px) {
  .sv-body {
    --sv-fs-h1: clamp(2.1rem, 8.6vw, 3rem);
    --sv-fs-h2: clamp(1.75rem, 7.4vw, 2.4rem);
    --sv-r-xl: 24px;
    --sv-r-lg: 18px;
  }

  .sv-hero__title--hub { font-size: clamp(3rem, 17vw, 4.5rem); }

  .sv-hero__actions .btn,
  .sv-break__actions .btn,
  .sv-final__actions .btn,
  .sv-exp__actions .btn { width: 100%; }
  .sv-hero__actions,
  .sv-final__actions { flex-direction: column; align-items: stretch; }
  .sv-dock .btn { width: auto; }

  .sv-hero__facts { gap: 1.25rem 1.75rem; }

  .sv-exp__list,
  .sv-exp__cards,
  .sv-visit__panel,
  .foot__grid,
  .foot__treats { grid-template-columns: minmax(0, 1fr); }
  .sv-exp__card--a,
  .sv-exp__card--b { aspect-ratio: 16 / 11; }
  .sv-exp__card--c { aspect-ratio: 16 / 10; }

  /* The floats stop floating: at this width there is nowhere for a card to
     overlap a picture without covering the subject of it. */
  .sv-hero__media,
  .sv-visit__media { display: flex; flex-direction: column; gap: .9rem; }
  .sv-float,
  .sv-hero .sv-chip--float { position: static; width: 100%; animation: none; }
  .sv-hero .sv-chip--float { width: max-content; }
  .sv-visit__inset { position: static; width: 100%; aspect-ratio: 16 / 9; border-width: 4px; }

  /* The index row loses its hover preview — there is no hover — and gives
     the space back to the name. */
  .svc--index .svc__media { display: none; }
  .svc--index .svc__link { padding-inline: .25rem; }
  .svc--index .svc__link:hover { padding-left: .25rem; }

  .svc-pager__inner { grid-template-columns: minmax(0, 1fr); }
  .svc-pager__side--next { border-left: 0; border-top: 1px solid rgba(120, 170, 225, .3); }
}


/* ==========================================================================
   17. REDUCED MOTION / PRINT
   Everything autonomous stops. The reveals are handled in site.js — it checks
   the same query and writes the finished state instead of animating to it —
   so all that is left here is the CSS-driven motion.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  [data-float],
  .sv-cta-breathe { animation: none !important; }

  .util,
  .sv-dock,
  .mega,
  .svc__link,
  .svc__media img,
  .sv-hero__photo,
  .sv-break__media img,
  .sv-exp__card img,
  .sv-visit__frame img { transition: none; }

  .sv-hero__frame:hover .sv-hero__photo,
  .svc__link:hover,
  .svc__link:hover .svc__media img,
  .sv-break__media:hover img,
  .sv-exp__card:hover img,
  .sv-visit__frame:hover img { transform: none; }
}

@media print {
  .sv-jump,
  .sv-dock,
  .util,
  .mega,
  .sv-hero__blob,
  .sv-hero__word,
  .sv-final__shape { display: none !important; }

  /* Nothing on a printed page should be invisible because a script did not
     get the chance to reveal it. */
  [data-sv],
  .line__inner { opacity: 1 !important; }
  [data-sv-mask] { clip-path: none !important; }

  .sv-final,
  .svc--feature .svc__body { color: #000 !important; background: none !important; }
  .sv-h2--light,
  .svc--index .svc__name { color: #000 !important; }
}

/* ==========================================================================
   THE CATALOGUE — svc-show, svc-grid, svc-card
   ---------------------------------------------------------------------------
   PROMOTED OUT OF services-hub.css. service-card.php renders every treatment
   card on the site — the services archive AND the related-services rail on
   each service page — but its rules lived in the archive stylesheet, which a
   service page does not load. The rail therefore drew unstyled cards with
   full-size "Read more" arrow SVGs, which is the tall washed-out block that
   appears under the rail.

   THE TEMPLATES AND THIS STYLESHEET WERE SPEAKING DIFFERENT LANGUAGES. Every
   rule above describes `hub-*` classes; archive-service.php and
   service-card.php emit `svc-*`, and nothing anywhere in the theme described
   those — so the catalogue rendered as raw markup and a card photograph came
   out at its intrinsic 1008px, which is what put a horizontal scrollbar on the
   page below 1024px.

   Four card variants share one skeleton: `feature` leads a category, `tile` is
   the standard photographic card, `type` is the same card without a
   photograph (a deliberate variant, not a fallback — see service-card.php),
   and `row` is the rail card on the detail page.
   ========================================================================== */
.svc-show {
  position: relative;
  padding-block: clamp(3rem, 8vh, 5.5rem);
}
.svc-show__inner {
  width: var(--sv-shell);
  margin-inline: auto;
}
.svc-show__head {
  max-width: 46rem;
  margin-bottom: clamp(1.75rem, 4vh, 2.75rem);
}

/* THE FEATURE CARD TAKES TWO COLUMNS AND TWO ROWS on a wide grid, which is
   what makes the first treatment in a family read as the headline of it. It
   falls back to one cell as soon as the grid is narrower than three. */
.svc-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
  gap: clamp(1rem, 2vw, 1.6rem);
}
.svc-grid__cell { display: flex; }
.svc-grid__cell--feature { grid-column: span 2; grid-row: span 2; }

/* --- The card ------------------------------------------------------------- */
.svc-card {
  position: relative;
  display: flex;
  width: 100%;
  border-radius: var(--sv-r-lg);
  background: #fff;
  box-shadow: 0 1px 2px rgba(11, 45, 82, .06), 0 12px 30px rgba(11, 45, 82, .07);
  transition: transform var(--t-med) var(--ease-out), box-shadow var(--t-med) var(--ease-out);
}
.svc-card:hover { transform: translateY(-4px); box-shadow: 0 20px 46px rgba(11, 45, 82, .14); }

/* The whole card is the link — see the note in service-card.php. */
.svc-card__link {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow: hidden;
  border-radius: inherit;
  color: inherit;
  text-decoration: none;
}
.svc-card__link:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 3px; }

/* --- The photograph ------------------------------------------------------- */
/* THE FIX FOR THE OVERFLOW IS HERE. The <img> is block, 100% wide and cropped
   by its figure; without this it laid itself out at the intrinsic width of the
   file. */
.svc-card__media {
  position: relative;
  margin: 0;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: #dfeaf5;
}
.svc-card__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow) var(--ease-out);
}
.svc-card:hover .svc-card__photo { transform: scale(1.05); }
.svc-card__veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6, 33, 58, 0) 46%, rgba(6, 33, 58, .42) 100%);
  pointer-events: none;
}

/* The index sits on the photograph's corner, or on the panel when there is
   no photograph. */
.svc-card__n {
  position: absolute;
  top: clamp(.75rem, 2vw, 1.1rem);
  right: clamp(.75rem, 2vw, 1.1rem);
  z-index: 2;
  font-family: var(--f-display);
  font-size: .92rem;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .82);
}
.svc-card--type .svc-card__n { color: rgba(11, 45, 82, .3); }

/* --- The words ------------------------------------------------------------ */
.svc-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: .5rem;
  padding: clamp(1.1rem, 2.4vw, 1.6rem);
}
.svc-card__cat {
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--c-accent);
}
.svc-card__title {
  font-family: var(--f-display);
  font-size: clamp(1.15rem, 1rem + .5vw, 1.5rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.012em;
  color: var(--c-ink);
}
.svc-card__text {
  font-size: .95rem;
  line-height: 1.6;
  color: rgba(11, 45, 82, .72);
}
/* Pushed to the bottom so every card in a row ends on the same line, whatever
   the length of the blurb above it. */
.svc-card__go {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin-top: auto;
  padding-top: .35rem;
  font-size: .92rem;
  font-weight: 600;
  color: var(--sv-navy-2);
  transition: gap var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.svc-card__go svg { width: 18px; height: 18px; }
.svc-card:hover .svc-card__go { gap: .7rem; color: var(--c-accent); }

/* --- Variants ------------------------------------------------------------- */
/* The feature card is the same card with more room, so the photograph gets a
   wider crop and the type steps up a size. */
.svc-card--feature .svc-card__media { aspect-ratio: 16 / 10; }
.svc-card--feature .svc-card__title { font-size: clamp(1.5rem, 1rem + 1.2vw, 2.1rem); }
.svc-card--feature .svc-card__text  { font-size: 1rem; }

/* No photograph: the panel carries the whole card, so it needs a ground of
   its own rather than a white rectangle with a title in it. */
.svc-card--type {
  background:
    radial-gradient(90% 70% at 88% 6%, rgba(127, 227, 212, .18) 0%, rgba(127, 227, 212, 0) 62%),
    linear-gradient(160deg, #f6f9fc 0%, #eaf2fa 100%);
}
.svc-card--type .svc-card__body { min-height: 11rem; padding-block: clamp(1.4rem, 3vw, 2rem); }

/* THE RAIL CARD STACKS. It used to lay its pieces on a row — media 40% of the
   card, stretched to the card's height — but a rail cell is only 232–296px
   wide at every breakpoint, so 40% is a 118px column and `align-items:stretch`
   pulled it to 200px tall. A 720x399 landscape photograph cropped into a
   118x200 portrait slot is the sliver the practice reported: "these services
   are shrinking".

   A 296px card has no room for two columns. Stacked, the photograph gets the
   full width of the card and a crop that matches its own shape. */
.svc-card--row .svc-card__link { flex-direction: column; }
.svc-card--row .svc-card__media { flex: none; aspect-ratio: 16 / 10; }

@media (max-width: 900px) {
  /* Two columns cannot hold a feature card spanning two of them and anything
     else beside it, so it goes back to being one cell among equals. */
  .svc-grid__cell--feature { grid-column: auto; grid-row: auto; }
  .svc-card--feature .svc-card__media { aspect-ratio: 4 / 3; }
}

@media (max-width: 560px) {
  .svc-grid { grid-template-columns: minmax(0, 1fr); }
  /* A little taller on a phone, where the card has the width to carry it. */
  .svc-card--row .svc-card__media { aspect-ratio: 4 / 3; }
}

/* --- The rail's controls -------------------------------------------------- */
/* The strip scrolls without them; these give a mouse-only visitor the same
   reach a trackpad already has. site.js unhides them and hides them again
   whenever the track is not actually overflowing. */
.svc-rail__tools {
  display: flex;
  align-items: center;
  gap: clamp(.5rem, 1.4vw, .9rem);
}

.svc-rail__nav {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  color: var(--c-blue-900);
  background: var(--sv-white);
  border: 1px solid rgba(120, 170, 225, .42);
  border-radius: 50%;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out),
              opacity var(--t-fast) var(--ease-out);
}
.svc-rail__nav svg { width: 19px; height: 19px; }
.svc-rail__nav--prev svg { transform: rotate(180deg); }
.svc-rail__nav:hover { color: #fff; background: var(--c-blue-700); border-color: transparent; }
.svc-rail__nav:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 3px; }
/* AT THE END OF THE TRACK IT IS DIMMED, NOT REMOVED — a control that vanishes
   moves the two beside it, and the row jumps under the pointer. */
.svc-rail__nav[disabled] { opacity: .34; cursor: default; }
.svc-rail__nav[disabled]:hover { color: var(--c-blue-900); background: var(--sv-white); border-color: rgba(120, 170, 225, .42); }

/* Dragging: the cursor says so, and the text stops being selectable mid-drag. */
.svc-rail__track { cursor: grab; }
.svc-rail__track.is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; }
.svc-rail__track.is-dragging a { pointer-events: none; }
.svc-rail__track:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 4px; }

@media (max-width: 700px) {
  /* Touch already drags. The arrows would be two more things to miss. */
  .svc-rail__nav { display: none; }
}

/* ==========================================================================
   THE CATALOGUE GRID, RE-PROPORTIONED
   ---------------------------------------------------------------------------
   THE ORPHAN ROW WAS ARITHMETIC, NOT TASTE. `auto-fill` resolved to FOUR
   columns while the feature card spans two of them and two rows. With six
   cells that leaves the fifth tile alone on a row of four with three empty
   cells beside it — the stranded "Bridges" card and the band of nothing next
   to it.

   Three columns divides both real categories exactly:
     6 cells  = feature(2x2) + tile,tile down column 3 + a row of 3   -> 5 tiles
     9 cells  = feature(2x2) + tile,tile down column 3 + two rows of 3 -> 8 tiles
   Neither leaves a hole, and the cards get wider rather than narrower.
   ========================================================================== */
.svc-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* THE FEATURE CARD IS A PICTURE, NOT A CAPTION WITH SPACE UNDER IT. Spanning
   two rows makes it twice a tile's height, but its body holds only a category,
   a name and a link — so a fixed-ratio image left roughly 300px of empty white
   beneath the title. The photograph absorbs the extra height instead. */
.svc-card--feature .svc-card__link { height: 100%; }
.svc-card--feature .svc-card__media {
  aspect-ratio: auto;
  flex: 1 1 auto;
  min-height: 14rem;
}
.svc-card--feature .svc-card__media img { height: 100%; }
.svc-card--feature .svc-card__body { flex: 0 0 auto; }

/* The showcase bands were paying full section padding twice over, above a grid
   that is already generously spaced. */
.svc-show { padding-block: clamp(2.5rem, 6vh, 4.25rem); }

/* WIDER COLUMNS MEAN SHORTER PICTURES. Three columns give each tile about
   415px, and a 4:3 crop on that is 311px of photograph under 144px of text —
   the card became mostly image and the page grew by a third. 16:10 keeps the
   crop generous and takes roughly 50px off every tile, which is 200px off a
   category. */
.svc-card__media { aspect-ratio: 16 / 10; }
.svc-card__body { padding: clamp(1rem, 2vw, 1.35rem); gap: .4rem; }
.svc-show__head { margin-bottom: clamp(1.25rem, 3vh, 2rem); }

@media (max-width: 1100px) {
  /* Two columns: the feature keeps its double width and the tiles pair off
     underneath, which still divides without a hole. */
  .svc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .svc-grid__cell--feature { grid-column: span 2; grid-row: auto; }
  .svc-card--feature .svc-card__media { aspect-ratio: 16 / 9; flex: 0 0 auto; }
}

@media (max-width: 640px) {
  .svc-grid { grid-template-columns: minmax(0, 1fr); }
  .svc-grid__cell--feature { grid-column: auto; }
  .svc-card--feature .svc-card__media { aspect-ratio: 4 / 3; }
}

/* ==========================================================================
   PHASE 1 — GLOBAL COMPONENT CORRECTIONS
   ---------------------------------------------------------------------------
   From the client change document. Each rule below answers a specific written
   note, and lives here — in the shared stylesheet — because every one of them
   is a global component the document asked to be fixed once rather than per
   page.
   ========================================================================== */

/* "The footer logo is too small" — noted three times, desktop and mobile.
   The mark was inheriting the header's 30–38px, which is right for a 54px
   navigation pill and far too quiet for the foot of the page. */
.logo--foot .logo__img { height: clamp(46px, 4.4vw, 62px); }

/* "The logo is very small, and the menu bar icon is very different." — the
   header mark on mobile. Raised at the small end only; the desktop pill keeps
   the height it was designed around. */
@media (max-width: 1100px) {
  /* 2.7vw resolves to under 30px from here down, which is where the mark
     started reading as an afterthought rather than a logo. */
  .logo__img { height: clamp(38px, 4vw, 46px); }
}

/* "On desktop, the footer content has too much empty space. Please adjust the
   spacing and alignment." The band was paying 5.5rem of top padding above a
   238px grid — roughly 200px of nothing. */
.foot { padding-block: clamp(2.25rem, 4.5vh, 3.25rem) clamp(1.25rem, 2.5vh, 1.75rem); }
.foot__grid { row-gap: clamp(1.5rem, 3vh, 2.25rem); }
.foot__bar { margin-top: clamp(1.5rem, 3vh, 2.25rem); padding-top: clamp(.9rem, 1.8vh, 1.25rem); }

/* "On mobile, footer is not properly aligned." Stacked, the columns were
   inheriting the desktop grid's asymmetric tracks and reading as ragged. */
@media (max-width: 700px) {
  .foot__grid { grid-template-columns: minmax(0, 1fr); row-gap: clamp(1.75rem, 5vw, 2.5rem); }
  .foot__brand,
  .foot__col { text-align: left; }
  .foot__bar { flex-direction: column; align-items: flex-start; gap: .75rem; }
  .foot__legal { gap: 1.1rem; }
}

/* "Please remove all image and image-container shadow effects so the images
   look clean and flat." Raised against the treatment pages, so it is applied
   to the image containers those pages use. Cards keep their own elevation —
   a card is a surface, not an image. */
.sv-overview__frame,
.sv-overview__extra-fig,
.sv-gallery__fig,
.sv-steps__media,
.sv-prose figure,
.sv-prose .wp-block-image img,
.tm-hero__frame,
.sv-hero__frame {
  box-shadow: none;
}

/* "The logo is very small, and the menu bar icon is very different." — the
   second half. The original site's trigger is Avada's collapse-to-button,
   which draws the conventional three-line mark; this one drew two, which is
   a different symbol, not a smaller one. The third bar is in the markup; it
   is the one that leaves when the other two cross.

   Three bars at 1.5px on a 6px gap stack 16.5px tall, so the outer two are
   7.5px off centre — twice the throw the two-bar version needed. */
/* The rows were auto-sized inside a 44px grid, and auto rows stretch: three
   1.5px bars were being spread across 42px on 16px centres. That is not a
   hamburger, it is three unrelated lines — and it is why the cross never
   closed either, since the old translate was written for the gap the bars
   were supposed to have rather than the one they had. Centring the content
   lets the rows be the height of a bar again: a 16.5px stack, 7.5px centres. */
.nav__toggle { align-content: center; }

.nav__toggle-bar { transition: transform var(--t-med) var(--ease-out),
                               opacity var(--t-fast) var(--ease-out); }
.nav.is-open .nav__toggle-bar:first-child  { transform: translateY(7.5px) rotate(45deg); }
.nav.is-open .nav__toggle-bar:nth-child(2) { opacity: 0; transform: scaleX(.35); }
.nav.is-open .nav__toggle-bar:last-child   { transform: translateY(-7.5px) rotate(-45deg); }

/* "Please fix the mobile menu alignment and spacing. When I click menu, the
   text wraps. This issue happens on both hover and click."

   Three separate defects, all inside the services accordion, all measured at
   430 / 390 / 360 with the drawer open:

     the wrap     the category headings are uppercase at .16em of tracking.
                  "Straight Teeth & Other Services" wanted 259px of a 255px
                  rail at 430 and of a 215px rail at 390, so it broke onto a
                  second line and left the count and the plus floating against
                  the middle of a two-line block. Tracking that wide is a
                  desktop mannerism; on a phone it is simply what breaks the
                  line, so it is what gives.

     the rail     the toggles carried .25rem of inline padding of their own,
                  which started the headings 4px right of every other row in
                  the drawer — 65 against 61.

     the numbers  .mega__i asks for tabular figures and the UI font does not
                  have them: "11" came out 6px narrower than "04", so the
                  fifteen treatment names began on fifteen slightly different
                  left edges (87.8 to 94.1). A fixed column is true whatever
                  the font does.
   ------------------------------------------------------------------------- */
@media (max-width: 980px) {
  /* One rail. The headings now start where Home, About Us and Services do. */
  .mega__toggle { padding-inline: 0; }

  .mega__head-label {
    /* Fits on one line at 360 with room to spare, and stops growing at the
       size it always had. */
    font-size: clamp(.6rem, 2.55vw, .68rem);
    letter-spacing: .06em;
    /* If a future category name is longer still, break it evenly rather
       than leaving one orphaned word. */
    text-wrap: balance;
  }

  /* A smaller plus buys the label back the last few pixels at 360, and reads
     better beside a 10px label than a 22px box did. */
  .mega__sign { width: 18px; height: 18px; }

  /* The digits get a column instead of a hope. */
  .mega__i {
    min-width: 1.55em;
    text-align: right;
  }

  /* A fixed number column costs the name a few pixels, and at 360 the longest
     three — Tooth Coloured Fillings, Crowns, Onlays & Inlays, Gum Disease
     Treatments — were already spending them. The row gives the width back out
     of its own padding, which no one was reading. */
  .mega__item {
    padding-inline: .25rem .35rem;
    gap: .5rem;
  }
}

/* At 360 there is no padding left to give back, and two names — Crowns,
   Onlays & Inlays and Gum Disease Treatments — still want about ten pixels
   more than the rail has. They are treatment names from the client's own
   site, so the name does not get shortened; the type does. */
@media (max-width: 380px) {
  /* The longest name, Gum Disease Treatments, wants 182px of a 181px rail at
     .89rem — one pixel. .87rem clears it by three and leaves every other name
     with room, which is a steadier place to sit than one pixel. */
  .mega__item { font-size: .87rem; }
}

/* ==========================================================================
   HOMEPAGE — THE SECTIONS RESTORED FROM THE ORIGINAL SITE           (H-05)
   --------------------------------------------------------------------------
   "Please go through the complete old website again and make sure that each
   section, its content, and the sequence match the old website exactly."

   Eight bands the new homepage had dropped. They share one skeleton — .hsec —
   because on the original they ARE one shape repeated: an eyebrow, a heading,
   a paragraph, sometimes a button. Giving them a single set of tokens is what
   keeps eight restored sections from reading as eight different pages.

   These are static by design. Every animated section on this page is driven
   by main.js keyed to its own id, and a section that depends on JS to become
   visible is a section that disappears when the JS does. Motion can be added
   later against these hooks; the content is not waiting for it.
   ========================================================================== */

.hsec {
  position: relative;
  padding-block: clamp(3.5rem, 8vh, 6.5rem);
}

.hsec__inner {
  /* --shell is the width every other band on this page uses. */
  width: var(--shell);
  margin-inline: auto;
}
.hsec__inner--narrow { max-width: 62ch; }
.hsec__inner--centre { text-align: center; margin-inline: auto; }

/* --- The shared type stack ------------------------------------------------ */
.hsec__eyebrow {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin: 0 0 1rem;
  font-family: var(--f-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-blue-700);
}
.hsec__eyebrow--centre { justify-content: center; }
.hsec__eyebrow-rule {
  width: clamp(24px, 4vw, 44px);
  height: 1px;
  background: currentColor;
  opacity: .45;
}

.hsec__title {
  margin: 0 0 .65rem;
  font-family: var(--f-display);
  font-size: clamp(1.7rem, 3.4vw, 2.9rem);
  line-height: 1.12;
  letter-spacing: -.01em;
  color: var(--c-ink);
  text-wrap: balance;
}

.hsec__sub {
  margin: 0 0 1rem;
  font-family: var(--f-ui);
  font-size: clamp(.95rem, 1.5vw, 1.08rem);
  font-weight: 600;
  color: var(--c-blue-700);
}

.hsec__lede {
  margin: 0;
  max-width: 68ch;
  font-size: clamp(1rem, 1.35vw, 1.075rem);
  line-height: 1.72;
  color: var(--c-ink-soft);
}
.hsec__inner--centre .hsec__lede { margin-inline: auto; }

.hsec__actions { margin-top: 1.75rem; }
.hsec__inner--centre .hsec__actions { display: flex; justify-content: center; }

/* --- WHY CHOOSE US: the six reasons --------------------------------------
   Six terse labels and nothing else. They are given a card each so the eye
   can count them, and a number rail so the order is legible — the original
   sets them in a row of six, which is unreadable below a laptop. */
.choose {
  position: relative;
  padding-block: clamp(1rem, 2vh, 2rem) clamp(3rem, 7vh, 5rem);
}
.choose__grid {
  display: grid;
  /* Six items, so the track count is stated rather than left to auto-fit:
     auto-fit put five across and stranded the sixth on a row of its own.
     3 / 2 / 1 all divide six exactly, so no width leaves an orphan. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(.75rem, 1.6vw, 1.15rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.choose__item {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: clamp(1rem, 2vw, 1.35rem);
  background: #fff;
  border: 1px solid rgba(120, 170, 225, 0.2);
  border-radius: 16px;
}
.choose__mark {
  flex: none;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--c-accent);
}
.choose__label {
  font-family: var(--f-ui);
  font-size: 0.83rem;
  font-weight: 600;
  letter-spacing: .09em;
  line-height: 1.4;
  text-transform: uppercase;
  color: var(--c-ink);
}

/* --- COMPREHENSIVE CARE: copy beside a photograph ------------------------- */
.care__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(1.75rem, 4vw, 3.5rem);
  align-items: center;
}
.care__list {
  margin: 1.4rem 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: .6rem;
}
.care__list li {
  position: relative;
  padding-left: 1.6rem;
  font-size: clamp(.95rem, 1.3vw, 1.02rem);
  color: var(--c-ink);
}
.care__list li::before {
  content: "";
  position: absolute;
  left: 0; top: .55em;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--c-accent);
}
.care__media { margin: 0; }
.care__media img,
.care__photo {
  display: block;
  width: 100%;
  /* The source is 1200x800 and ftd_image() emits no width/height for a theme
     asset, so without this the figure is 0px tall until the file arrives and
     the whole band jumps when it does. */
  aspect-ratio: 3 / 2;
  height: auto;
  object-fit: cover;
  border-radius: 20px;
  /* Flat, per "remove all image and image-container shadow effects". */
  box-shadow: none;
}

/* --- GENERAL / RESTORATIVE: the two doors --------------------------------- */
.explore { padding-block: clamp(3rem, 7vh, 5.5rem); }
.explore__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: clamp(1.25rem, 3vw, 2.25rem);
}
.explore__card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #fff;
  border: 1px solid rgba(120, 170, 225, 0.2);
  border-radius: 20px;
}
.explore__media { margin: 0; }
.explore__media img,
.explore__photo {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  box-shadow: none;
}
.explore__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: .75rem;
  padding: clamp(1.25rem, 2.5vw, 1.9rem);
}
.explore__title {
  margin: 0;
  font-family: var(--f-display);
  font-size: clamp(1.25rem, 2.1vw, 1.6rem);
  line-height: 1.2;
  color: var(--c-ink);
}
.explore__text {
  margin: 0;
  font-size: 0.98rem;
  line-height: 1.7;
  color: var(--c-ink-soft);
}
/* Pushed to the floor of the card so both buttons sit on one line. */
.explore__cta { align-self: flex-start; margin-top: auto; padding-top: .35rem; }

/* --- THE ROSTER: eight faces, not two ------------------------------------ */
.doctors--roster {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: clamp(1rem, 2.2vw, 1.75rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.doctors--roster .doctor__panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  border-radius: 20px;
  background: #fff;
  border: 1px solid rgba(120, 170, 225, 0.2);
  text-decoration: none;
  color: inherit;
  transition: transform var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out);
}
.doctors--roster .doctor__panel:hover { transform: translateY(-3px); border-color: var(--c-accent); }
.doctors--roster .doctor__media { position: relative; }
.doctors--roster .doctor__photo {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  box-shadow: none;
}
.doctors--roster .doctor__body {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: clamp(.9rem, 1.8vw, 1.2rem);
}
.doctors--roster .doctor__role {
  font-family: var(--f-ui);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--c-blue-300);
}
.doctors--roster .doctor__name {
  margin: 0;
  font-family: var(--f-display);
  font-size: clamp(1.02rem, 1.6vw, 1.2rem);
  line-height: 1.25;
  color: var(--c-ink);
}
.doctors--roster .doctor__cta {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin-top: .35rem;
  font-family: var(--f-ui);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--c-blue-700);
}
.doctors--roster .doctor__cta-icon { width: 16px; height: 16px; flex: none; }
.doctors--roster .doctor__panel:hover .doctor__cta { color: var(--c-accent); }

/* --- COME SAY HELLO: the sub-line the original carries -------------------- */
.office__sub {
  margin: 0 0 .75rem;
  font-family: var(--f-ui);
  font-size: clamp(.95rem, 1.5vw, 1.08rem);
  font-weight: 600;
  color: var(--c-blue-700);
}

/* --- Narrow screens ------------------------------------------------------- */
@media (max-width: 1100px) {
  .choose__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .care__grid { grid-template-columns: minmax(0, 1fr); }
  /* The photograph leads on a phone: it is the only image in this band. */
  .care__media { order: -1; }
}

@media (max-width: 560px) {
  .choose__grid { grid-template-columns: minmax(0, 1fr); }
  .hsec { padding-block: clamp(2.5rem, 6vh, 3.5rem); }
}

/* "Replace the current Why Choose Us section images with the client's images."
   The panel was built around a 16/11 placeholder; the client's team photograph
   is 1240x560. Left alone, `cover` would have taken about a third off each side
   of a group shot — which on a team photo means cropping people out of it. The
   frame takes the photograph's own proportions instead. */
.why__frame { aspect-ratio: 1240 / 560; }

/* "Mobile View — too much spacing. Maintain all the spacing properly for the
   mobile version."  (A-02 / G-12)

   Every shared band — the appointment CTA, the review strip, the clinic
   panel — pays --sv-pad top and bottom, so the variable is the one place to
   say this. Below 700px it was still spending 72px an edge: on an 844px
   screen that is a sixth of the viewport gone to nothing, twice per section.

   The floor moves, not the ratio: 10vh still governs on a tall phone, it is
   the minimum that was too generous. */
@media (max-width: 700px) {
  :root,
  /* THE VERTICAL RHYTHM, TIGHTENED FOR A PHONE. Every shared section pays
     --sv-pad top AND bottom, so the figure is doubled at every seam: at
     6.5vh it came to 55px a side — 110px of empty ground between one
     section and the next, on a screen 390px wide. Four seams on a doctor
     page spent roughly 400px, a whole viewport, on nothing.

     4.5vh is still a clear change of ground and reads as deliberate space
     rather than as a gap you scroll through. Desktop is untouched: this is
     the 700px block, and the 980px and root values above it still stand. */
  .sv-body { --sv-pad: clamp(2rem, 4.5vh, 2.9rem); }
}

/* ==========================================================================
   VIDEO — the YouTube facade                                        (OF-01)
   --------------------------------------------------------------------------
   A poster, a scrim and a play button until somebody asks for the film. The
   ratio is declared on the shell so the box is its final height before the
   poster decodes — an embed that grows on load pushes the rest of the page
   down, which is the one thing a video must not do.
   ========================================================================== */
.ftd-video {
  position: relative;
  width: 100%;
  aspect-ratio: var(--ftd-video-ratio, 16 / 9);
  overflow: hidden;
  border-radius: 20px;
  background: linear-gradient(150deg, #cfe6fb 0%, #b9d9f7 46%, #cdeae6 100%);
}

.ftd-video__btn {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: #fff;
}

.ftd-video__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Flat, per "remove all image and image-container shadow effects". */
  box-shadow: none;
}

.ftd-video__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 34, 57, .06) 0%, rgba(11, 34, 57, .38) 100%);
  transition: background var(--t-fast) var(--ease-out);
}

.ftd-video__play {
  position: absolute;
  top: 50%; left: 50%;
  display: grid;
  place-items: center;
  width: clamp(58px, 7vw, 82px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(255, 255, 255, .92);
  color: var(--c-blue-900);
  transition: transform var(--t-med) var(--ease-out),
              background var(--t-fast) var(--ease-out);
}
.ftd-video__play svg { width: 54%; height: 54%; }

.ftd-video__caption {
  position: absolute;
  left: clamp(1rem, 2.5vw, 1.75rem);
  bottom: clamp(1rem, 2.5vw, 1.75rem);
  font-family: var(--f-ui);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.ftd-video__btn:hover .ftd-video__play,
.ftd-video__btn:focus-visible .ftd-video__play { transform: translate(-50%, -50%) scale(1.07); background: #fff; }
.ftd-video__btn:hover .ftd-video__scrim { background: linear-gradient(180deg, rgba(11, 34, 57, .02) 0%, rgba(11, 34, 57, .3) 100%); }

/* The focus ring belongs on the frame, not on a 100%-sized transparent
   button whose outline would be clipped by the frame's own overflow. */
.ftd-video:has(.ftd-video__btn:focus-visible) { outline: 3px solid var(--c-accent); outline-offset: 3px; }

/* Once the film is in, the iframe simply is the component. */
.ftd-video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  .ftd-video__play,
  .ftd-video__scrim { transition: none; }
}

/* "Please remove all image and image-container shadow effects so the images
   look clean and flat."  The last three, found by auditing computed style
   across every page rather than by reading stylesheets: these are the figures
   inside the three SHARED bands — the appointment CTA, the review strip and
   the clinic panel — so they appear on nearly every page of the site and were
   the reason a shadow kept turning up wherever the sweep looked next.

   Cards still keep their elevation. A card is a surface; a figure holding a
   photograph is an image container. */
.sv-break__media,
.sv-reviews__media,
.sv-visit__frame {
  box-shadow: none;
}

/* ==========================================================================
   THE HOMEPAGE — THE VISUAL PASS
   --------------------------------------------------------------------------
   The content is the client's and stays exactly as it is. What changes is how
   it is presented, and it changes for three measured reasons:

     the hero      --fs-title capped at 3.6rem while every treatment page's H1
                   reaches 5rem. At 1440 the homepage headline measured 52px
                   against 80px on /dental-implants/ — the most important
                   sentence on the site was the smallest type on it.

     the ground    THIRTEEN of the fourteen bands were painted the same
                   #f0f8fb. Nothing marked where one thought ended and the
                   next began, which is what reads as flat, washed out, and
                   as a stack of separate components rather than one page.

     the rhythm    padding ran 72/72, 90/90, 72/72, 90/90 the whole way down,
                   with a single 115/115 outlier on a 630px section. Uniform
                   spacing has no emphasis in it: everything is equally
                   important, so nothing is.

   Contrast was measured before any of this and was never the problem — body
   copy sits at 7.5:1 and headings between 13:1 and 15.6:1. The page is not
   faint; it is undifferentiated. So none of what follows touches a colour
   value for text.
   ========================================================================== */

/* --- 1. THE HERO CARRIES THE PAGE ---------------------------------------- */
:root {
  /* Now the largest type on the site, as the homepage's opening line should
     be. Both tokens are used in exactly one place each — the hero headline
     and its column — so this reaches nothing else. */
  --fs-title: clamp(2.35rem, 1rem + 5.4vw, 5.5rem);
  --measure:  min(48rem, 92%);
}

/* The headline was set on a 620px column at 52px — a wide screen, a narrow
   sentence. Given room, three authored lines become a paragraph of display
   type instead of a caption. */
.hero__lede { max-width: 44ch; }

/* --- 2. MOVEMENTS, NOT PANES ---------------------------------------------
   Seven movements, each with its own ground, and each opening with a short
   gradient out of the one before it so the join is a transition rather than
   an edge. The order is the page's own narrative:

     the practice   why · choose            warm white
     the care       general · care · pros   tint
     the people     team                    white
     the treatments solutions · services · explore   tint
     the place      office                  white
     the voices     reviews                 deeper tint
     the invitation visit                   warm white, into the footer
   ------------------------------------------------------------------------- */
:root {
  --home-warm: #fdfcf9;
  --home-tint: #eef6fb;
  --home-deep: #e4eefa;
}

/* Movement 1 — the practice */
.why {
  background: linear-gradient(180deg, var(--c-page) 0, var(--home-warm) 220px);
}
.choose { background: var(--home-warm); }

/* Movement 2 — the care */
.hsec--general {
  background: linear-gradient(180deg, var(--home-warm) 0, var(--home-tint) 200px);
}
.hsec--care,
.hsec--pros { background: var(--home-tint); }

/* Movement 3 — the people */
.team {
  background: linear-gradient(180deg, var(--home-tint) 0, #fff 220px);
}

/* Movement 4 — the treatments */
.hsec--solutions {
  background: linear-gradient(180deg, #fff 0, var(--home-tint) 200px);
}
.services { background: var(--home-tint); }
.explore  { background: var(--home-tint); }

/* Movement 5 — the place. office is already white; it only needs the join. */
.office {
  background: linear-gradient(180deg, var(--home-tint) 0, #fff 200px;);padding-bottom: 0px;
}

/* Movement 6 — the voices. The one band that goes deeper: the reviews are the
   emotional centre of a dental practice's homepage and they were sitting on
   exactly the same paper as everything else. */
.reviews {
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, .85) 0%, rgba(255, 255, 255, 0) 55%),
    linear-gradient(180deg, #fff 0, var(--home-deep) 240px, var(--home-deep) 100%);
}

/* Movement 7 — the invitation, settling out of the deep band into the footer */
.visit {
  background: linear-gradient(180deg, var(--home-deep) 0, var(--home-warm) 260px);
}

/* --- 3. RHYTHM ------------------------------------------------------------
   Generous where a movement opens, tighter where a section continues the one
   above it. `why` was paying 115px top and bottom around 630px of content;
   `choose` belongs to it and should sit close, not float. */
.why      { padding-block: clamp(4.5rem, 9vh, 7rem) clamp(2.5rem, 5vh, 3.5rem); }
.choose   { padding-block: 0 clamp(4.5rem, 9vh, 7rem); }

.hsec--general   { padding-block: clamp(4.5rem, 9vh, 7rem) clamp(3rem, 6vh, 4.5rem); }
.hsec--care      { padding-block: clamp(3rem, 6vh, 4.5rem); }
.hsec--pros      { padding-block: clamp(3rem, 6vh, 4.5rem) clamp(4.5rem, 9vh, 7rem); }
.hsec--solutions { padding-block: clamp(4.5rem, 9vh, 7rem) clamp(2.5rem, 5vh, 3.5rem); }
.explore         { padding-block: clamp(2.5rem, 5vh, 3.5rem) clamp(4.5rem, 9vh, 7rem); }

/* --- 4. SECTION HEADINGS ------------------------------------------------
   A step up so the movements announce themselves, and a measure so a long
   heading breaks where it should rather than running the shell. */
.hsec__title {
  font-size: clamp(1.9rem, 1.05rem + 2.6vw, 3.4rem);
  max-width: 20ch;
}
.hsec__inner--centre .hsec__title { margin-inline: auto; }

/* --- 5. THE SIX REASONS --------------------------------------------------
   They were cards on the same ground as everything around them, which is what
   made this stretch read as card-on-card. On warm white they can be quieter:
   a hairline and a mark, no panel. */
.choose__item {
  background: none;
  border: 0;
  border-top: 1px solid rgba(11, 45, 82, .12);
  border-radius: 0;
  padding: clamp(1.1rem, 2.2vw, 1.5rem) 0;
  align-items: baseline;
}
.choose__label { letter-spacing: .1em; }

/* --- 6. THE ROSTER -------------------------------------------------------
   Portraits carry the section; the frame should not compete with them. */
.doctors--roster .doctor__panel {
  border-color: rgba(11, 45, 82, .1);
  border-radius: 22px;
}
.doctors--roster .doctor__photo { aspect-ratio: 4 / 5; }
.doctors--roster .doctor__panel:hover { transform: translateY(-5px); }

@media (prefers-reduced-motion: reduce) {
  .doctors--roster .doctor__panel:hover { transform: none; }
}

/* --- 7. WHY CHOOSE US: the composition ------------------------------------
   The badge is positioned for a TALL portrait frame — left: -1.5rem, pinned
   to the bottom — and the client's photograph is a wide 1240x560 collage of
   eight faces. Bottom-left on a wide frame is somebody's head, so the badge
   was covering a person. It sits under the frame now, where it reads as a
   caption to the picture rather than a sticker on it.

   The media column also gets the larger share: the photograph is the only
   image in this movement and it was losing to five lines of type. */
.why__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.18fr); }

.why__media { padding-bottom: 4.25rem; }

.why__badge {
  left: 0;
  top: calc(100% - 3.4rem);
  bottom: auto;
  background: rgba(255, 255, 255, .9);
}

@media (max-width: 900px) {
  .why__inner  { grid-template-columns: minmax(0, 1fr); }
  .why__media  { padding-bottom: 4rem; }
}

/* --- 8. THE FILM IN THE OFFICE MOVEMENT ----------------------------------
   Wide, so it reads as the centrepiece of that movement rather than a box
   dropped in — and spaced so the mosaic beneath it still feels related. */
.office__film {
  width: min(64rem, 100%);
  margin: clamp(2rem, 4.5vh, 3rem) auto clamp(2.5rem, 5.5vh, 3.75rem);
  border-radius: 24px;
}

/* --- 9. THE HOMEPAGE REVEAL — SUPERSEDED BY H-06 -------------------------
   This was one CSS transition — eighteen pixels and a fade — driven by an
   IntersectionObserver in main.js that marked every element of every restored
   band with [data-home-in]. It has been replaced by initRestoredBands(), which
   gives each band its own reading order in GSAP; see the H-06 block at the
   foot of this file and section 07 of main.js.

   THE RULES ARE GONE RATHER THAN LEFT INERT. Nothing sets [data-home-in] any
   more, so keeping an `opacity: 0` keyed to it would be a hidden trap for the
   next person who adds the attribute back expecting a fade and gets a
   permanently invisible section instead.

   The no-JS contract is unchanged and is now carried entirely by the gate in
   main.js: initRestoredBands() is called from inside the motion block, so a
   missing GSAP or a reduced-motion preference means it never runs and every
   band renders at the finished state its own rules already describe. */

/* --- 10. THE HERO WASH ON A PHONE ----------------------------------------
   The wash is a 101° diagonal, which is right on a wide screen: the copy sits
   left, the photograph opens out to the right, and the gradient follows that
   split. On a 390px screen there is no split — the copy runs the full width
   and lies directly over the middle of the photograph, where a diagonal
   whose coverage has already fallen to 8% leaves the lede sitting on a face.

   Below 700px the wash becomes vertical and holds through the copy band, so
   the type keeps its ground. The photograph is still fully visible beneath
   and below it. */
@media (max-width: 700px) {
  .hero__wash {
    background:
      linear-gradient(to bottom,
        rgba(255, 255, 255, .90) 0%,
        rgba(255, 255, 255, .88) 34%,
        rgba(255, 255, 255, .74) 52%,
        rgba(255, 255, 255, .30) 68%,
        rgba(255, 255, 255, .04) 82%),
      linear-gradient(to top,
        rgba(240, 248, 251, .95) 0%,
        rgba(240, 248, 251, .45) 18%,
        rgba(240, 248, 251, 0)   38%);
  }
}


/* ==========================================================================
   HOMEPAGE — THE SECOND VISUAL PASS                                  (H-06)
   --------------------------------------------------------------------------
   "Some sections look off on the home page — redesign them, and give the page
   modern scroll motion."

   Four things were wrong, and they were all the same kind of wrong: the bands
   restored in H-05 were built to be CORRECT — right content, right order —
   and were never given the composition the sections around them have. So the
   middle third of the page reads as a document dropped between two designed
   ends.

     1. THE ROSTER WAS WEARING THE WRONG MEASURE. `.doctors` caps itself at
        58rem because it was drawn for TWO portraits. `.doctors--roster`
        changed the track count for nine and inherited the cap, so nine people
        were squeezed into 53% of a 1440px shell while the services grid
        beside them ran the full width. That is the "section looks off".

     2. THE SIX REASONS had one orange dot each and nothing else. Six labels
        sharing one mark give the eye nothing to separate them with.

     3. GENERAL DENTISTRY was left-set inside a centred 62ch box — neither
        centred nor full width, so it read as a stray column.

     4. GENERAL / RESTORATIVE sat directly under the service cards in what
        looks like one 2x2 grid, but the two halves were different components
        with different surfaces: the top pair elevated and round, the bottom
        pair flat and square. One grid, two card languages.

   Everything here is composition. No copy was changed, nothing was added to
   the page that the client did not already say, and the section order is
   untouched.

   The motion that goes with it lives in main.js, section 07 — see
   initRestoredBands(). Nothing here depends on it: every rule below renders
   its finished state, and the script only ever animates INTO these values.
   ========================================================================== */

/* --- 1. THE ROSTER MEASURE ------------------------------------------------
   Flex rather than grid, for one reason: a roster is a COUNT THE PRACTICE
   OWNS. Nine today, ten when somebody joins. `justify-content: center` on a
   wrapping flex row centres a short final row on its own; a grid strands it
   against the left edge. So the layout stays right when the count changes,
   which is the only version of this worth writing.

   Three across at desktop is deliberate and not just what nine divides by:
   these are 4:5 portraits of people, and four-up takes them below the width
   where a face reads as a face. */
.doctors--roster {
  --roster-gap: clamp(1rem, 2.2vw, 1.75rem);

  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--roster-gap);
  /* Overrides the two-portrait cap inherited from `.doctors`. */
  max-width: min(72rem, 100%);
  /* AND PUTS THE CENTRING BACK. `.doctors` centres itself with
     `margin: <top> auto 0`, but the H-05 rule above resets `margin: 0` and
     lives in this file, so it beats style.css on source order. Without this
     the roster sits flush against the left edge of a 1440px shell with 290px
     of empty page beside it — which is the same "off" the cap was causing,
     just in the other direction. */
  margin-inline: auto;
}

.doctors--roster .doctor {
  flex: 0 1 calc((100% - 2 * var(--roster-gap)) / 3);
  max-width: calc((100% - 2 * var(--roster-gap)) / 3);
}

@media (max-width: 900px) {
  .doctors--roster .doctor {
    flex-basis: calc((100% - var(--roster-gap)) / 2);
    max-width:  calc((100% - var(--roster-gap)) / 2);
  }
}

@media (max-width: 520px) {
  .doctors--roster .doctor { flex-basis: 100%; max-width: 100%; }
}

/* --- 2. THE SIX REASONS ---------------------------------------------------
   A mark each, in a soft disc, on a hairline that the motion layer draws in
   from the left. The panel stays gone — this sits directly under the Why
   paragraph and a row of six cards there is card-on-card, which is what the
   first pass correctly removed. What it needed was never a box; it is a way
   to tell one reason from another at a glance. */
.choose__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(.8rem, 1.6vw, 1.05rem);
  /* The hairline is a child now, so the border goes. */
  border-top: 0;
  padding: clamp(1.15rem, 2.3vw, 1.55rem) 0 clamp(1.05rem, 2.1vw, 1.4rem);
}

/* The rule the motion layer draws. transform-origin is the left edge, so it
   arrives in the reading direction. */
.choose__rule {
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 1px;
  background: linear-gradient(90deg,
              rgba(11, 45, 82, .20) 0%,
              rgba(11, 45, 82, .10) 55%,
              rgba(11, 45, 82, 0) 100%);
  transform-origin: 0 50%;
}

.choose__disc {
  flex: none;
  display: grid;
  place-items: center;
  width: clamp(2.5rem, 4.4vw, 3rem);
  aspect-ratio: 1;
  border-radius: 50%;
  color: var(--c-blue-700);
  background:
    radial-gradient(120% 120% at 30% 20%, rgba(255, 255, 255, .95) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(150deg, #e6f2fd 0%, #dbecfb 55%, #e8f5f2 100%);
  box-shadow: inset 0 0 0 1px rgba(26, 114, 214, .12);
  transition: color var(--t-fast) var(--ease-out),
              transform var(--t-fast) var(--ease-out);
}

.choose__icon {
  width: 50%;
  height: 50%;
}

/* The accent is spent on the ONE mark under the pointer rather than on six
   dots at once — the strip stays quiet until you look at it. */
.choose__item:hover .choose__disc {
  color: var(--c-accent);
  transform: translateY(-2px);
}

.choose__label {
  /* Sentence case at a readable size. All-caps at .83rem across six labels is
     a row of texture rather than six things you can read, and three of these
     run to four words. */
  font-family: var(--f-ui);
  font-size: clamp(.9rem, .86rem + .18vw, 1rem);
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.35;
  text-transform: none;
  color: var(--c-ink);
}

@media (prefers-reduced-motion: reduce) {
  .choose__item:hover .choose__disc { transform: none; }
}

/* --- 3. GENERAL DENTISTRY: an asymmetric statement ------------------------
   The heading takes the left, the paragraph and the button take the right.
   Two columns give a short block a composition; a centred 62ch column gives
   it a shrug. The pair below it — Caring Professionals, Trusted Dental
   Solutions — stay centred, so this reads as a change of axis rather than a
   third variation on the same shape. */
.hsec--general { overflow: hidden; }

.gen__aura {
  position: absolute;
  z-index: 0;
  top: -18%;
  right: -6%;
  width: min(46rem, 74%);
  aspect-ratio: 1;
  pointer-events: none;
  border-radius: 50%;
  filter: blur(60px);
  background: radial-gradient(circle at 50% 50%,
              rgba(26, 114, 214, .13) 0%,
              rgba(111, 178, 244, .07) 42%,
              rgba(255, 255, 255, 0) 70%);
}

.gen__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: start;
  /* The band is a statement, not a full-width sweep: held in from the shell
     so the two columns stay a pair rather than drifting apart at 1440. */
  max-width: min(84rem, 100%);
  margin-inline: auto;
}

/* A hairline down the gutter, so the split is stated rather than implied. */
.gen__body {
  position: relative;
  padding-left: clamp(0px, 2vw, 2.25rem);
}
.gen__body::before {
  content: "";
  position: absolute;
  left: 0;
  top: .35rem;
  bottom: .35rem;
  width: 1px;
  background: linear-gradient(180deg,
              rgba(26, 114, 214, .28) 0%,
              rgba(26, 114, 214, .10) 60%,
              rgba(26, 114, 214, 0) 100%);
  transform-origin: 50% 0;
}

/* The heading may take the whole column here — the 20ch cap set for the
   centred bands makes a three-word rag of this one. */
.gen__lead .hsec__title { max-width: 16ch; }
.gen__body  .hsec__lede { max-width: 46ch; }

@media (max-width: 900px) {
  .gen__grid { grid-template-columns: minmax(0, 1fr); gap: clamp(1rem, 3vw, 1.5rem); }
  .gen__body { padding-left: 0; }
  .gen__body::before { display: none; }
  .gen__lead .hsec__title { max-width: 20ch; }
  .gen__aura { top: -10%; right: -30%; width: min(34rem, 120%); }
}

/* --- 4. THE CHAPTER MARK --------------------------------------------------
   Caring Professionals and Trusted Dental Solutions are both an eyebrow, a
   heading and a paragraph, centred, two movements apart. The identical shape
   twice reads as a template. The mark above the eyebrow makes each one an
   opening rather than a repeat, and it costs no copy. */
.hsec__ornament {
  display: grid;
  place-items: center;
  width: clamp(3.1rem, 5.2vw, 3.9rem);
  aspect-ratio: 1;
  margin: 0 auto clamp(1rem, 2vh, 1.4rem);
  border-radius: 50%;
  color: var(--c-blue-700);
  background:
    radial-gradient(120% 120% at 32% 18%, rgba(255, 255, 255, .95) 0%, rgba(255, 255, 255, 0) 62%),
    linear-gradient(150deg, #eaf4fe 0%, #ddedfb 55%, #e9f6f3 100%);
  box-shadow: inset 0 0 0 1px rgba(26, 114, 214, .14);
}

.hsec__ornament-mark {
  width: 56%;
  height: 56%;
}

/* --- 5. ONE CARD LANGUAGE FOR THE 2x2 -------------------------------------
   `.explore__card` now wears the same surface as `.card__panel`: the same
   radius, the same hairline, the same elevation and the same lift on hover.

   ON THE ELEVATION — G-13 removed shadows from IMAGE containers, and the
   tracker records the reason cards were exempt: "a card is a surface, not an
   image." `.card` kept its shadow under that ruling, so the way to make these
   four agree is to bring the flat pair UP to the card, not to strip the
   elevated pair down and reopen a decision the client has already made. The
   image inside stays flat, which is what G-13 actually asked for. */
.explore__card {
  border-radius: 22px;
  border-color: rgba(120, 170, 225, .22);
  box-shadow: var(--sh-md);
  transition: transform var(--t-med) var(--ease-out),
              box-shadow var(--t-med) var(--ease-out),
              border-color var(--t-med) var(--ease-out);
}

.explore__card:hover {
  transform: translateY(-6px);
  border-color: rgba(120, 170, 225, .4);
  box-shadow: var(--sh-lg);
}

/* Matches `.card__media` — 16/10, and the photo drifts slower than the card
   lifts, exactly as it does on the pair above. */
.explore__media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background:
    radial-gradient(90% 80% at 28% 18%, rgba(255, 255, 255, .8) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(150deg, #d3e8fb 0%, #bcdcf8 48%, #cfeae7 100%);
}

.explore__media img,
.explore__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
  transition: transform 1.1s var(--ease-out);
}

.explore__card:hover .explore__photo,
.explore__card:hover .explore__media img { transform: scale(1.05); }

/* The title steps up to `.card__title` size so the four read as one set. */
.explore__title {
  font-size: clamp(1.2rem, .9rem + .6vw, 1.6rem);
  font-weight: 500;
  letter-spacing: -.016em;
  color: var(--c-blue-900);
}

.explore__text { font-size: clamp(.84rem, .8rem + .16vw, .92rem); line-height: 1.72; }

/* The two grids are one composition, so they share a gap and the seam between
   them closes. `.cards` sets 2 up; `.explore__grid` was auto-fit and could
   land on a different track width at the same viewport. */
.explore__grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, 2.6vw, 2.25rem);
}

/* The service cards end the `.services` band and the two doors open
   `.explore`; between them sat both section paddings, which broke a 2x2 into
   two rows that had lost each other. */
.services { padding-bottom: 0; }
.explore  { padding-block: clamp(1.25rem, 2.6vw, 2.25rem) clamp(4.5rem, 9vh, 7rem); }

@media (max-width: 720px) {
  .explore__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  .explore__card:hover { transform: none; }
  .explore__card:hover .explore__photo,
  .explore__card:hover .explore__media img { transform: none; }
}
