/* =========================================================================
   ClinicPlus OS Design System
   Airy, Light, Organised Mobile Application Experience
   ========================================================================= */

:root {
  /* Surface System Level 1, 2, 3 */
  --bg: #F8FAFC;                /* Level 1 — Clean canvas */
  --surface: #FFFFFF;           /* Level 2 — Primary white surface */
  --surface-2: #F1F5F9;         /* Soft slate surface */
  --surface-soft: #E2E8F0;      /* Level 3 — Soft supporting surface */
  --surface-mint: #E8F1F9;      /* Level 3 — Soft blue surface tint (replacing mint) */
  --brand: #0B4F9C;             /* OmniMatter demo blue */
  --brand-dark: #063366;        /* Deep Blue */
  --brand-soft: #D4E4F5;        /* Soft Blue Tint */
  --brand-accent: #0066CC;      /* Vibrant Blue Accent */
  --ink: #0F172A;               /* Dark Slate Text */
  --ink-2: #334155;             /* Secondary Slate Text */
  --ink-3: #64748B;             /* Muted Text */
  --border: #CBD5E1;            /* Neutral Border */
  --border-strong: #94A3B8;     /* Strong Border */
  --rose: #C02600;              /* accessible alert accent */
  --clinic-red: #C02600;
  --clinic-red-soft: #FDE8E8;

  /* Typography Scale */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* Geometry & Radius */
  --r-sm: 8px;
  --r: 12px;
  --r-lg: 16px;
  --r-full: 9999px;

  /* Safe Area Inset */
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-top: env(safe-area-inset-top, 0px);

  /* The fixed patient bottom bar: 58px of items plus 4px padding top and
     bottom. Screens derive their bottom clearance from this rather than from a
     copied number, so the bar and the space reserved for it can never drift
     apart and leave the last card sitting under the navigation. */
  --bottom-nav-h: 66px;
  --bottom-nav-clearance: calc(var(--bottom-nav-h) + var(--safe-bottom) + 24px);
}

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  background-color: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

body {
  min-height: 100dvh;
  background-color: var(--bg);
}

a {
  color: var(--brand);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

/* -------------------------------------------------------- Viewport Utilities */
.desktop-only {
  display: flex !important;
}
.mobile-only {
  display: none !important;
}

@media (max-width: 768px) {
  .desktop-only {
    display: none !important;
  }
  .mobile-only {
    display: flex !important;
  }
  /* The brand shares a narrow header with the section title and menu button.
     At the desktop size the name was crushed to ~75px and wrapped onto four
     lines, overflowing the header. Smaller text plus word-boundary wrapping
     keeps the full clinic name readable on one or two lines. */
  .text-brand__name {
    font-size: 13px;
    max-width: none;
    overflow-wrap: break-word;
  }
  .appbar__inner {
    padding-top: 6px;
    padding-bottom: 6px;
  }
  /* Sub-screens (booking flow, confirmation, manage, privacy) drop the brand
     so the centered title always has the full row, however long the clinic
     name or the title is — see appbar()'s stackedTitleScreen. */
  .appbar--stacked-title .brand {
    display: none;
  }
}

/* -------------------------------------------------------- Typography System */
.h1 {
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--brand-dark);
}

.h2 {
  font-size: clamp(22px, 3vw, 28px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--brand-dark);
}

.h3 {
  font-size: 18px;
  font-weight: 650;
  line-height: 1.3;
  color: var(--ink);
}

.eyebrow {
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--brand-accent);
}

.muted { color: var(--ink-2); }
.muted-3 { color: var(--ink-3); }
.tnum { font-variant-numeric: tabular-nums; }
.center { text-align: center; }

/* ------------------------------------------------------------- Layout Grid */
.screen {
  max-width: 1140px;
  margin: 0 auto;
  padding: 24px 20px 60px;
}

.screen--mobile-app {
  max-width: 430px;
  margin: 0 auto;
  padding: 20px 20px var(--bottom-nav-clearance);
}

@media (max-width: 360px) {
  .screen--mobile-app {
    padding-left: 16px;
    padding-right: 16px;
  }
}

/* ------------------------------------------------------------------ Header */
.appbar {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.demo-banner {
  background: var(--brand-dark);
  color: #FFFFFF;
  padding: 6px 20px;
  font-size: 12px;
  justify-content: space-between;
  align-items: center;
}

.demo-banner__left {
  display: flex;
  align-items: center;
  gap: 10px;
}

.demo-banner__badge {
  background: rgba(255,255,255,0.18);
  padding: 2px 8px;
  border-radius: var(--r-full);
  font-weight: 600;
  font-size: 11px;
}

.demo-banner__right {
  display: flex;
  align-items: center;
  gap: 12px;
}

.demo-banner__right button {
  background: transparent;
  border: none;
  color: #FFFFFF;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  min-height: 32px;
  padding: 6px 4px;
}

.demo-dropdown {
  position: relative;
}

.demo-dropdown__btn {
  background: rgba(255,255,255,0.15) !important;
  padding: 3px 10px;
  border-radius: var(--r-sm);
}

.demo-dropdown__menu {
  display: none;
  position: absolute;
  right: 0;
  top: 100%;
  margin-top: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  min-width: 180px;
  overflow: hidden;
}
.demo-dropdown__menu.open {
  display: block;
}

.demo-dropdown__menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 14px;
  color: var(--ink) !important;
  font-size: 13px !important;
  border-bottom: 1px solid var(--border);
}
.demo-dropdown__menu button:hover {
  background: var(--surface-mint);
}

.appbar__inner {
  min-height: 56px;
  padding: 0 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
}

.brand {
  display: flex;
  align-items: center;
}
.brand__logo {
  height: 28px;
  width: auto;
}

.text-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  color: var(--brand);
  font-weight: 800;
}

.text-brand__mark {
  display: inline-grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--surface-mint);
  border: 1px solid var(--border);
  font-size: 13px;
  letter-spacing: 0;
  flex: 0 0 auto;
}

.text-brand__name {
  display: block;
  max-width: min(38vw, 360px);
  overflow-wrap: anywhere;
  line-height: 1.15;
}

.text-brand--compact .text-brand__mark {
  width: 34px;
  height: 34px;
}

@media (max-width: 768px) {
  /* An exceptionally long real clinic name (caught by the long-name stress
     fixture) wrapped onto six lines and inflated the mobile header far past
     its normal height. `.appbar .text-brand__name` outranks the unconditional
     rule above by specificity, so this reaches the header brand specifically
     without disturbing the footer's brand mark. The full name stays available
     in the page title, footer and every other mention.

     Two lines, not one: a clinic name is the patient's confirmation that they
     opened the right site, and clipping it to "V Smile Dental..." to protect a
     redundant "Home" label was the wrong trade. The centred screen title is no
     longer rendered on these sections, so the brand may use the width that
     label was occupying — enough that ordinary clinic names resolve on one or
     two lines at 360px without any destructive ellipsis. */
  .appbar .text-brand__name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    max-width: calc(100vw - 132px);
    font-size: 15.5px;
    line-height: 1.2;
  }

  /* On the sub-screens that do show a centred title, the brand is dropped
     entirely, so this constraint does not apply there. */
  .appbar--stacked-title .text-brand__name {
    max-width: min(38vw, 360px);
  }
}

.map-fallback {
  display: grid;
  place-content: center;
  min-height: 220px;
  padding: 22px;
  text-align: center;
  background: var(--surface-soft);
}

.site-nav {
  display: flex;
  gap: 24px;
}
.site-nav a {
  color: var(--ink-2);
  font-size: 14.5px;
  font-weight: 550;
  padding: 4px 0;
  /* WCAG 2.5.8 minimum target height. */
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}
.site-nav a:hover, .site-nav a.active {
  color: var(--brand);
  font-weight: 650;
}

/* Mobile Top Bar Controls */
.mobile-top-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.mobile-top-bar__title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-size: 17px;
  font-weight: 700;
  color: var(--brand-dark);
  letter-spacing: -0.01em;
  pointer-events: none;
}

.demo-overflow-wrap {
  position: relative;
  margin-left: auto;
}

.demo-overflow-btn {
  background: var(--surface-mint);
  border: 1px solid var(--border);
  color: var(--brand-dark);
  width: 40px;
  height: 40px;
  border-radius: var(--r-full);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  touch-action: manipulation;
}

.demo-overflow-menu {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  width: 270px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 12px 32px rgba(0,0,0,0.15);
  padding: 12px;
  z-index: 200;
  animation: slideDown 0.18s ease-out;
}

.demo-overflow-menu.open {
  display: block;
}

@keyframes slideDown {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

.demo-overflow-header {
  border-bottom: 1px solid var(--border);
  padding-bottom: 8px;
  margin-bottom: 8px;
}
.demo-overflow-sub {
  font-size: 12px;
  color: var(--ink-2);
  margin-top: 4px;
}

.demo-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 12px;
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 550;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  touch-action: manipulation;
}
.demo-menu-item:hover, .demo-menu-item:active {
  background: var(--surface-mint);
}

.demo-menu-info {
  margin-top: 8px;
  padding: 8px 10px;
  background: var(--bg);
  border-radius: var(--r-sm);
  font-size: 11.5px;
  color: var(--ink-2);
}

/* ---------------------------------------------------------- Mobile App Home */
.m-hero {
  text-align: left;
}

.m-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  background: var(--surface-mint);
  border-radius: var(--r-full);
  font-size: 13px;
  font-weight: 600;
  color: var(--brand-dark);
  margin-bottom: 12px;
  max-width: 100%;
  /* "Clinic · Area, City, District" is longer than a phone is wide. A fixed
     height with nowrap cut it off mid-word, so the chip grows and wraps. */
  min-height: 36px;
  height: auto;
  padding: 7px 12px;
  align-items: flex-start;
  line-height: 1.35;
}
.m-chip svg { flex: none; margin-top: 1px; }
.m-chip span { min-width: 0; }

/* The clinic's own name, above the headline. The phone header clamps the brand
   to two lines in a 40px bar, so without this the first screenful could carry a
   generic dental headline and no clinic identity at all. */
.m-hero__brand {
  display: block;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--brand-accent);
  margin-bottom: 6px;
}

.m-hero__title {
  font-size: clamp(30px, 7vw, 34px);
  line-height: 1.1;
  font-weight: 700;
  color: var(--brand-dark);
  letter-spacing: -0.02em;
}

.m-hero__sub {
  font-size: 15px;
  color: var(--ink-2);
  margin-top: 8px;
  line-height: 1.45;
}

.m-hero__doctor {
  font-size: 14px;
  font-weight: 600;
  color: var(--brand);
  margin-top: 6px;
  line-height: 1.4;
}

/* Rhythm between the clinic-site blocks the phone now carries. */
.m-home-section {
  margin-top: 32px;
}

.m-visit-actions,
.m-contact-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 14px;
}

.m-visit-actions .btn,
.m-contact-actions .btn {
  flex: 1 1 auto;
  min-width: 132px;
}

/* A phone number makes this the longest control in the set; letting it take a
   row of its own keeps every label readable instead of squeezing three buttons
   onto one 320px line. */
.m-contact-actions .btn:first-child {
  flex-basis: 100%;
}

/* The phone column is 430px at most, so the service cards and the hygiene strip
   stay in one column even at the 768px tablet width where the shared grid rules
   would otherwise ask for two. */
.screen--mobile-app .services-grid,
.screen--mobile-app .quality-strip {
  grid-template-columns: 1fr;
}

.screen--mobile-app .quality-strip__body {
  padding: 20px;
}

.m-hero__actions {
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.m-primary-cta {
  height: 52px;
  font-size: 16px;
  font-weight: 650;
}

.m-quick-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.m-quick-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  height: 72px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-dark);
  transition: background 0.15s ease, transform 0.1s ease;
  touch-action: manipulation;
}
.m-quick-btn:active {
  background: var(--surface-mint);
  transform: scale(0.96);
}

.m-hero-img-wrap {
  margin-top: 20px;
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
}

/* The clinic's photograph, at the shape the clinic supplied it in.

   The box does not decide the composition — the asset does. `height: auto`
   against a full-width column means the image renders at its own intrinsic
   ratio, so a portrait photograph stays portrait and a landscape one stays
   landscape, with no crop and no letterbox bars, for any clinic's asset.

   Two earlier attempts both cropped it. A fixed 200px band showed 48% of a
   portrait photo. Replacing that with `aspect-ratio: 4/5` was the right shape
   but was then clamped by a `44vh` cap, which re-cropped 15% off the bottom
   through `object-fit: cover` — a shallow cut across the picture, which is what
   still looked wrong on a real phone.

   `max-height` survives only as a guard against a pathologically tall asset
   taking over the screen. It is set well clear of a normal portrait crop
   (a 4:5 photo in a 390px column is ~438px, comfortably under it), so for
   ordinary clinic photography it never engages. When it does, `--hero-focus`
   decides what stays in frame, which a pack can set without touching CSS. */
.m-hero-img {
  width: 100%;
  height: auto;
  max-height: 78vh;
  object-fit: cover;
  object-position: var(--hero-focus, 50% 30%);
  display: block;
}

.m-hero-badge {
  position: absolute;
  bottom: 12px;
  left: 12px;
  background: rgba(24, 87, 79, 0.9);
  backdrop-filter: blur(8px);
  color: #FFFFFF;
  padding: 4px 12px;
  border-radius: var(--r-full);
  font-size: 12px;
  font-weight: 600;
  max-height: 32px;
}

/* Open Value Rows (Replacing heavy bordered cards) */
.m-open-value-list {
  background: var(--surface);
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  padding: 0 16px;
}

.m-value-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
}

.m-trust-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--r-full);
  background: var(--surface-mint);
  color: var(--brand);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.m-trust-title {
  font-size: 14.5px;
  font-weight: 650;
  color: var(--ink);
}

.m-trust-desc {
  font-size: 13px;
  color: var(--ink-2);
  margin-top: 1px;
}

.m-preview-card {
  background: var(--surface-soft);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 18px;
}

/* ---------------------------------------------------------- Mobile App Care */
.m-screen-head {
  text-align: left;
}

.m-doctor-card {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 18px;
  margin-top: 14px;
}

.m-doctor-avatar {
  width: 64px;
  height: 64px;
  border-radius: var(--r-full);
  background: var(--brand-dark);
  color: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.doctor-profile-avatar {
  width: 120px;
  height: 120px;
  margin: 0 auto;
}

.doctor-photo {
  width: 148px;
  aspect-ratio: 3 / 4;
  border-radius: var(--r-sm);
  object-fit: cover;
  object-position: center top;
  display: block;
  border: 1px solid var(--border);
  background: var(--surface-soft);
}

.doctor-photo--compact {
  width: 76px;
  flex-shrink: 0;
}

.m-doctor-initials {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.05em;
}

/* Council registration number. Quiet by design — it is a trust signal, not a
   headline, and must not compete with the doctor's name. */
.doctor-reg {
  margin-top: 4px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-2);
}

.m-doctor-body {
  flex: 1;
}

.m-services-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.m-service-row {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 96px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 12px 14px;
  text-align: left;
  width: 100%;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.1s ease;
  touch-action: manipulation;
}
.m-service-row:active {
  background: var(--surface-mint);
  transform: scale(0.98);
}

.m-service-thumb {
  width: 68px;
  height: 68px;
  border-radius: var(--r);
  object-fit: cover;
  flex-shrink: 0;
}

.m-service-info {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.m-service-title {
  font-size: 15px;
  font-weight: 650;
  color: var(--ink);
  line-height: 1.25;
}

.m-service-sub {
  font-size: 13px;
  color: var(--ink-2);
  margin-top: 4px;
  line-height: 1.35;
}

.m-service-chev {
  color: var(--ink-3);
  flex-shrink: 0;
}

.m-feature-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 16px;
}

/* -------------------------------------------------- Service Detail Bottom Sheet */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: fadeIn 0.2s ease-out;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.sheet-modal {
  width: 100%;
  max-width: 440px;
  background: var(--surface);
  border-radius: 20px 20px 0 0;
  padding: 16px 20px calc(20px + var(--safe-bottom));
  position: relative;
  box-shadow: 0 -8px 32px rgba(0,0,0,0.18);
  max-height: 88dvh;
  overflow-y: auto;
  animation: slideUp 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes slideUp {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

.sheet-handle {
  width: 36px;
  height: 4px;
  background: var(--border-strong);
  border-radius: var(--r-full);
  margin: 0 auto 14px;
}

.sheet-close-btn {
  position: absolute;
  top: 14px;
  right: 16px;
  width: 36px;
  height: 36px;
  border-radius: var(--r-full);
  background: var(--bg);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--ink-2);
}

.sheet-img {
  width: 100%;
  height: 180px;
  object-fit: cover;
  border-radius: var(--r);
  display: block;
}

.sheet-body {
  margin-top: 14px;
}

.sheet-safety-note {
  background: var(--surface-mint);
  border: 1px solid var(--border-strong);
  padding: 10px 14px;
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--brand-dark);
  font-weight: 550;
}

/* ------------------------------------------------------- Contact & Map Screen */
.m-contact-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 18px;
}

.m-map-wrapper {
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--border);
}

/* --------------------------------------------------- Bottom Navigation Bar */
.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 150;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 4px 10px calc(4px + var(--safe-bottom));
}

.bottom-nav__inner {
  /* .bottom-nav is itself a flex container, so without an explicit width this
     shrink-to-fits to its content (174px inside a 320px bar). The items use
     flex:1, so they then divided 174px instead of the full width and each tap
     target collapsed to ~28-57px wide. */
  width: 100%;
  max-width: 430px;
  margin: 0 auto;
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: 58px;
}

.bottom-nav__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  flex: 1;
  color: var(--ink-3);
  font-size: 12px;
  font-weight: 550;
  text-decoration: none;
  padding: 4px 0;
  border-radius: var(--r-sm);
  transition: color 0.15s ease, background 0.15s ease;
  touch-action: manipulation;
}

.bottom-nav__item svg {
  width: 22px;
  height: 22px;
}

.bottom-nav__item:hover {
  text-decoration: none;
  color: var(--brand);
}

.bottom-nav__item.active {
  color: var(--brand);
  font-weight: 700;
  background: var(--surface-mint);
}

.mobile-footer {
  height: 48px;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: var(--ink-3);
  border-top: 1px solid var(--border);
  padding-bottom: var(--safe-bottom);
}

/* ---------------------------------------------------- Buttons & UI Controls */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  height: 48px;
  padding: 0 20px;
  border-radius: var(--r-lg);
  font-size: 15px;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  transition: all 0.15s ease;
  text-decoration: none;
  touch-action: manipulation;
}
.btn:hover {
  text-decoration: none;
}
.btn:active {
  transform: scale(0.98);
}

.btn--primary {
  background: var(--brand);
  color: #FFFFFF;
}
.btn--primary:hover {
  background: var(--brand-dark);
}

.btn--ghost {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--brand-dark);
}
.btn--ghost:hover {
  background: var(--surface-mint);
}

.btn--whatsapp {
  background: #075E54;
  color: #FFFFFF;
}
.btn--whatsapp:hover {
  background: #064B43;
}

.btn--danger {
  background: #FFF0F0;
  border-color: #FCA5A5;
  color: var(--rose);
}

.btn--sm {
  height: 40px;
  padding: 0 14px;
  font-size: 13.5px;
  border-radius: var(--r);
}

/* See the touch-target block near the end of this file: the compact controls
   are raised to 44px there, after every rule that sets their height. */

.btn-row {
  display: flex;
  gap: 10px;
}

/* Form Inputs (16px font-size to prevent iOS Safari auto-zoom) */
.input {
  width: 100%;
  height: 48px;
  padding: 0 16px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  font-size: 16px; /* Prevents auto-zoom on iOS */
  color: var(--ink);
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.input:focus {
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px rgba(42, 126, 115, 0.15);
}

.field {
  margin-bottom: 16px;
  text-align: left;
}
.field label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 6px;
}

/* ---------------------------------------------------------- Appointments Hub */
.appointments-hub {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 16px;
}

.appt-tabs {
  display: flex;
  background: var(--bg);
  padding: 4px;
  border-radius: var(--r);
  margin-bottom: 16px;
}

.appt-tab {
  flex: 1;
  height: 40px;
  border: none;
  background: transparent;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  transition: all 0.15s ease;
  touch-action: manipulation;
}
.appt-tab[aria-selected="true"] {
  background: var(--surface);
  color: var(--brand-dark);
  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}

.choice {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  margin-bottom: 10px;
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
  touch-action: manipulation;
}
.choice[aria-pressed="true"], .choice:hover {
  background: var(--surface-mint);
  border-color: var(--brand);
}

.choice__body {
  flex: 1;
  display: flex;
  flex-direction: column;
}
.choice__title {
  font-size: 15px;
  font-weight: 650;
  color: var(--ink);
}
.choice__sub {
  font-size: 13px;
  color: var(--ink-2);
  margin-top: 2px;
}
.choice__chev {
  color: var(--ink-3);
}

.avatar {
  width: 44px;
  height: 44px;
  border-radius: var(--r-full);
  background: var(--brand-dark);
  color: #FFFFFF;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 15px;
}
.avatar--lg {
  width: 52px;
  height: 52px;
  font-size: 18px;
}

.steps {
  display: flex;
  gap: 6px;
  margin-top: 10px;
}
.steps span {
  flex: 1;
  height: 4px;
  background: var(--border);
  border-radius: var(--r-full);
}
.steps span.done, .steps span.active {
  background: var(--brand);
}

.flowhead {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
  text-align: left;
}

/* The title has to be able to shrink, or a long step title pushes the back
   button off a 320px screen instead of wrapping. */
.flowhead__text {
  min-width: 0;
}

/* The step's context line (the chosen day and practitioner). It sits after the
   progress bar with real space of its own — the steps used to pull it up by
   6px with a negative margin, straight through the progress indicator. */
.flowmeta {
  margin: 14px 0 16px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-2);
}

/* Demo-mode reminder. The protection stays; it just no longer shouts over the
   top of the form it is introducing. */
.demo-hint {
  margin: 0 0 16px;
  padding: 10px 12px;
  background: var(--surface-mint);
  border-radius: var(--r-sm);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--brand-dark);
  text-align: left;
}

/* A question that invites a sentence needs a control that can hold one. */
.input--multiline {
  height: auto;
  min-height: 92px;
  padding: 12px 16px;
  line-height: 1.45;
  resize: vertical;
  font-family: inherit;
}

.backbtn {
  width: 40px;
  height: 40px;
  border-radius: var(--r-full);
  background: var(--bg);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--ink);
  touch-action: manipulation;
}

/* A horizontal scroller cut its last visible chip in half — on a 293px card the
   fourth date was sliced down the middle, which reads as a rendering fault
   rather than as "scroll for more". A wrapping grid removes the whole class of
   problem: every chip is whole at every width, nothing overflows sideways, and
   all fourteen dates are reachable without a gesture that has to be discovered. */
.datestrip {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 8px;
  padding-bottom: 4px;
}

.datechip {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  cursor: pointer;
  transition: all 0.15s ease;
  touch-action: manipulation;
}
.datechip[aria-pressed="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: #FFFFFF;
}
.datechip__dow { font-size: 11.5px; font-weight: 600; text-transform: uppercase; }
.datechip__day { font-size: 20px; font-weight: 700; margin: 2px 0; }
.datechip__mon { font-size: 11px; }

.slotgroup { margin-top: 16px; text-align: left; }
.slotgroup__label { font-size: 13px; font-weight: 700; color: var(--brand-accent); margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }

.slots {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.slot {
  height: 42px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  transition: all 0.15s ease;
  touch-action: manipulation;
}
.slot[aria-pressed="true"] {
  background: var(--brand);
  color: #FFFFFF;
  border-color: var(--brand);
}
.slot:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.segrow { display: flex; gap: 8px; }
.seg {
  flex: 1;
  height: 42px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  touch-action: manipulation;
}
.seg[aria-pressed="true"] {
  background: var(--brand);
  color: #FFFFFF;
  border-color: var(--brand);
}

.chiprow { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.chip {
  padding: 6px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  font-size: 13px;
  font-weight: 550;
  color: var(--ink);
  cursor: pointer;
  touch-action: manipulation;
}
.chip[aria-pressed="true"] {
  background: var(--surface-mint);
  border-color: var(--brand);
  color: var(--brand-dark);
  font-weight: 650;
}

.summary {
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: left;
}
.summary__row {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 8px;
}
.summary__k { color: var(--ink-2); }
.summary__v { font-weight: 600; color: var(--ink); text-align: right; }

.actionbar {
  margin-top: 20px;
}

/* ------------------------------------------- Booking confirmation + ticket
   These classes were rendered by the confirmation screen but never styled, so
   the doctor header and the detail rows collapsed on top of each other and the
   whole screen read as a different, cheaper product than the landing page. */
.confirm { text-align: center; }
.confirm__tick {
  width: 68px;
  height: 68px;
  margin: 0 auto 14px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-mint);
  color: var(--brand-dark);
}
.confirm__title {
  font-family: var(--font-display, inherit);
  font-size: 22px;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.3;
}
.confirm__ref {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--brand);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

.ticket {
  margin-top: 20px;
  margin-bottom: 16px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
  text-align: left;
}
.ticket__top {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  background: var(--surface-soft);
  border-bottom: 1px solid var(--border);
}
.ticket__body { padding: 16px 18px; }
.ticket__body .summary__row:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

/* ------------------------------------------------------------- Desktop Sections */
.hero-section {
  display: grid;
  grid-template-columns: 55% 45%;
  gap: 40px;
  align-items: center;
  padding: 40px 0 60px;
}

.hero__badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  background: var(--surface-mint);
  border-radius: var(--r-full);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--brand-dark);
  margin-bottom: 16px;
  /* A long "Clinic · Area, City, District" line ran off the narrow viewport and
     was clipped mid-word. Let it wrap inside the pill instead. */
  max-width: 100%;
  text-align: left;
}
.hero__badge span {
  min-width: 0;
  overflow-wrap: anywhere;
}

.hero__title {
  margin-bottom: 16px;
}

.hero__sub {
  font-size: 17px;
  color: var(--ink-2);
  line-height: 1.55;
  margin-bottom: 28px;
}

.hero__ctas {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.hero-img-card {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: 0 16px 40px rgba(0,0,0,0.08);
}
.hero-img-card img {
  width: 100%;
  height: 380px;
  object-fit: cover;
  display: block;
}

/* Desktop honours the pack's `displayMode` for the same reason mobile does:
   the asset decides the composition, not the box. A fixed 380px cover crop was
   applied to every clinic's photography here, so a portrait of a doctor or a
   signboard arrived on a laptop cropped through the middle. */
.hero-img-card img.hero-img--natural {
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: 520px;
  margin: 0 auto;
  object-fit: contain;
}
.hero-img-card img.hero-img--cover {
  width: 100%;
  height: 380px;
  object-fit: cover;
  object-position: var(--hero-focus, 50% 30%);
}

.hero-img-card__badge {
  position: absolute;
  bottom: 16px;
  left: 16px;
  right: 16px;
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(8px);
  padding: 14px 18px;
  border-radius: var(--r);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.trust-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  margin-bottom: 60px;
}

.trust-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 20px;
  display: flex;
  gap: 14px;
}
.trust-card__icon {
  width: 44px;
  height: 44px;
  border-radius: var(--r-full);
  background: var(--surface-mint);
  color: var(--brand);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.trust-card__title { font-size: 15px; font-weight: 700; color: var(--ink); }
.trust-card__sub { font-size: 13px; color: var(--ink-2); margin-top: 2px; }

.section {
  padding: 40px 0;
}

.section-title {
  margin-bottom: 24px;
  text-align: left;
}

.about-card-grid {
  display: grid;
  grid-template-columns: 45% 55%;
  gap: 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.about-card-img {
  width: 100%;
  height: 100%;
  min-height: 280px;
  object-fit: cover;
}
.about-card-content {
  padding: 32px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: left;
}

.doctor-profile-card {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 32px;
  align-items: center;
  text-align: left;
}

.consent-row {
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 10px;
  align-items: start;
  margin-top: 14px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  font-size: 13.5px;
  color: var(--ink-2);
  text-align: left;
}

.consent-row input {
  width: 18px;
  height: 18px;
  margin-top: 1px;
  accent-color: var(--brand);
}

.services-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}

.service-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  text-align: left;
}
.service-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(0,0,0,0.08);
}

.service-card__img {
  width: 100%;
  height: 160px;
  object-fit: cover;
}

/* aspect-ratio reserves the box before the image decodes, so lazily loaded
   service photography cannot shift the grid under the reader. */
.service-card__media {
  aspect-ratio: 3 / 2;
  background: var(--surface-soft);
  overflow: hidden;
}
.service-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.service-card__body {
  padding: 20px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  flex: 1;
}

.service-card__badge {
  display: inline-block;
  padding: 3px 10px;
  background: var(--surface-mint);
  color: var(--brand-dark);
  font-size: 11.5px;
  font-weight: 700;
  border-radius: var(--r-full);
  margin-bottom: 8px;
}

.feature-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  align-items: center;
  text-align: left;
}
.feature-split__img {
  width: 100%;
  height: 300px;
  object-fit: cover;
}
.feature-split__content {
  padding: 32px;
}

.location-card {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 32px;
  text-align: left;
}

.map-card {
  display: flex;
  flex-direction: column;
  border-radius: var(--r);
  overflow: hidden;
  border: 1px solid var(--border);
}
.map-container {
  height: 250px;
  width: 100%;
  position: relative;
}
.map-iframe {
  width: 100%;
  height: 100%;
  border: 0;
}
.map-actions-bar {
  background: var(--surface-soft);
  padding: 10px 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* ------------------------------------------------ Real map panel + fallback */
.map-card__surface {
  position: relative;
  overflow: hidden;
}
.map-card__surface .map-card__fallback {
  position: absolute;
  inset: 0;
  height: 100% !important;
}
.map-card__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  /* transparent so the photographic card shows through a blocked embed */
  background: transparent;
}
.map-card__fallback {
  position: relative;
  overflow: hidden;
}
.map-card__fallback img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.map-card__fallback-body {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  color: #fff;
  background: linear-gradient(to top, rgba(11,31,59,0.88), rgba(11,31,59,0));
  font-size: 13.5px;
}
.map-card__bar {
  background: var(--surface-soft);
  padding: 12px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
.map-card__meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 13px;
  color: var(--ink-2);
  min-width: 0;
}
.map-card__meta b { color: var(--ink); font-size: 14px; }
.map-card__actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ------------------------------------------------------- WhatsApp panel */
.wa-panel {
  margin-top: 20px;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 20px 22px;
  border-radius: var(--r-lg);
  /* #075E54 on white is 7.3:1; white text on it is 6.1:1 — both clear AA. */
  background: linear-gradient(135deg, #075E54 0%, #0B7A6C 100%);
  color: #fff;
  flex-wrap: wrap;
}
.wa-panel__icon {
  width: 48px; height: 48px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(255,255,255,0.16);
  flex: none;
}
.wa-panel__body { flex: 1 1 300px; min-width: 0; }
.wa-panel__body h3 { font-size: 18px; font-weight: 700; margin: 0; }
.wa-panel__body p { margin-top: 4px; font-size: 14px; line-height: 1.55; color: rgba(255,255,255,0.92); }
.wa-panel .btn--whatsapp {
  background: #fff;
  color: #075E54;
  border: 0;
  flex: none;
}
.wa-panel .btn--whatsapp:hover { background: #EAF7F4; }

/* --------------------------------------------- WhatsApp preview modal */
.wa-preview {
  position: relative;
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: 26px;
  max-width: 460px;
  width: calc(100% - 32px);
  margin: auto;
  text-align: left;
  box-shadow: 0 24px 60px rgba(0,0,0,0.24);
}
.wa-preview__tag {
  display: inline-block;
  padding: 4px 10px;
  border-radius: var(--r-full);
  background: var(--surface-mint);
  color: var(--brand-dark);
  font-size: 11.5px;
  font-weight: 700;
}
.wa-preview__thread {
  margin-top: 16px;
  padding: 16px;
  border-radius: var(--r);
  background: #ECE5DD;
}
.wa-preview__bubble {
  background: #fff;
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 14px;
  line-height: 1.5;
  box-shadow: 0 1px 2px rgba(0,0,0,0.12);
  position: relative;
}
.wa-preview__bubble b { color: var(--ink); }
.wa-preview__bubble p { margin-top: 6px; color: var(--ink-2); }
.wa-preview__time {
  display: block;
  margin-top: 8px;
  font-size: 11px;
  color: var(--ink-3);
  text-align: right;
}

/* --------------------------------------------- Hygiene / quality strip */
.quality-strip {
  margin-top: 48px;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  align-items: stretch;
  text-align: left;
}
.quality-strip__media { aspect-ratio: 16 / 10; background: var(--surface-soft); }
.quality-strip__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.quality-strip__body {
  padding: 32px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.faq-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  text-align: left;
}

.faq-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
}

.faq-question {
  width: 100%;
  padding: 16px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: transparent;
  border: none;
  font-size: 15.5px;
  font-weight: 650;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  touch-action: manipulation;
}

.faq-answer {
  padding: 0 20px 16px;
  font-size: 14.5px;
  color: var(--ink-2);
  line-height: 1.6;
  border-top: 1px solid var(--bg);
}
.faq-answer.emergency {
  background: #FFF0F0;
  color: #991B1B;
}

.site-footer {
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 48px 20px 24px;
  margin-top: 60px;
  text-align: left;
}

/* WCAG 2.5.8 requires a 24x24 minimum target. These are standalone navigation
   and contact links, not links inline in a sentence, so the inline exception
   does not apply. inline-flex keeps them on their existing baseline while
   giving the hit area real height. */
.site-footer a,
.site-footer__bottom a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

.site-footer__inner {
  max-width: 1140px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 40px;
  border-bottom: 1px solid var(--border);
}

.site-footer__bottom {
  max-width: 1140px;
  margin: 24px auto 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  color: var(--ink-3);
}

/* The software credit: who built the experience, and where to find them. One
   line on a wide screen; on a phone the two halves stack rather than compete for
   a row that is already too narrow for either of them. */
.site-credit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
}

.site-credit__link {
  color: var(--ink-2);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

.site-credit__link:hover,
.site-credit__link:focus-visible {
  color: var(--brand);
}

/* -------------------------------------------------------- Staff Ops Styles */
.screen--ops {
  padding-top: 20px;
  text-align: left;
}
.ops-grid {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 24px;
}
.tabbar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 12px;
}
.tabbar__inner {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.tab {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  padding: 0 14px;
  border-radius: var(--r);
  border: none;
  background: transparent;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  text-align: left;
  touch-action: manipulation;
}
.tab[aria-current="true"] {
  background: var(--brand);
  color: #FFFFFF;
}

.opshead {
  margin-bottom: 20px;
}

.statrow {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: 24px;
}

.stat {
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.stat--accent {
  background: var(--surface-mint);
  border-color: var(--brand);
}
.stat__n { font-size: 32px; font-weight: 800; color: var(--brand-dark); }
.stat__l { font-size: 13px; font-weight: 600; color: var(--ink-2); margin-top: 4px; }

.board {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.appt {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.appt__time {
  text-align: center;
  background: var(--bg);
  padding: 8px 12px;
  border-radius: var(--r);
  min-width: 72px;
}
.appt__time .t { font-size: 15px; font-weight: 700; color: var(--brand-dark); }
.appt__time .p { font-size: 11px; color: var(--ink-3); }

.appt__main { flex: 1; }
.appt__name { font-size: 16px; font-weight: 700; color: var(--ink); }
.appt__meta { font-size: 13.5px; color: var(--ink-2); margin-top: 4px; }
.appt__actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--r-full);
  font-size: 11.5px;
  font-weight: 700;
}
.pill--booked { background: #E0F2FE; color: #0369A1; }
.pill--waiting { background: #FEF3C7; color: #92400E; }
.pill--with-doctor { background: #DCFCE7; color: #15803D; }
.pill--completed { background: #F3F4F6; color: #4B5563; }
.pill--cancelled { background: #FEE2E2; color: #B91C1C; }
.pill--no-show { background: #F3E8FF; color: #6B21A8; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.card--pad { padding: 20px; }

/* A bottom toast sat at a fixed viewport offset while the page scrolled, so on
   any screen where real content (confirmation actions, the mobile tab bar)
   also lives near the bottom of the viewport, the toast landed on top of it —
   observed covering Call/WhatsApp on the confirmation screen. Anchoring below
   the sticky header instead keeps it clear of every bottom-row action and the
   mobile bottom nav on every screen, not just the ones audited by hand. */
.toast {
  position: fixed;
  /* Sits above the sticky header itself rather than below it: content on the
     confirmation screen starts immediately under the header on narrow phones,
     so "below the header" still landed on the doctor card there. A few
     seconds over the header title is a smaller cost than covering the Call /
     WhatsApp / Reschedule row underneath, on every screen and every viewport. */
  top: max(10px, env(safe-area-inset-top, 0px));
  left: 50%;
  transform: translateX(-50%) translateY(-16px);
  background: var(--brand-dark);
  color: #FFFFFF;
  padding: 12px 24px;
  border-radius: var(--r-full);
  font-size: 14px;
  font-weight: 600;
  box-shadow: 0 10px 30px rgba(0,0,0,0.2);
  z-index: 400;
  opacity: 0;
  max-width: calc(100vw - 32px);
  text-align: center;
  transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease;
  pointer-events: none;
}
.toast.show {
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}
.toast.err {
  background: var(--rose);
}

.spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--border);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  margin: 20px auto;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

.empty {
  text-align: center;
  padding: 40px 20px;
  color: var(--ink-3);
}
.empty__title {
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  margin-top: 12px;
}

/* Touch targets.
   40px is comfortable under a mouse and too small under a thumb. On a touch
   viewport the compact controls grow to the 44px minimum — the Contact hub
   alone puts six of them in a column, and Call, WhatsApp and Directions are the
   controls a patient reaches for while standing outside the clinic; the time
   slots and segment controls are tapped repeatedly during booking.

   This block sits at the end deliberately. An earlier copy of it was written
   directly under `.btn--sm`, which meant `.slot { height: 42px }` — declared
   several hundred lines further down at the same specificity — simply won on
   source order, and the slots stayed at 42px while the tests and the stylesheet
   both looked correct. */
@media (max-width: 768px) {
  .btn--sm,
  .slot,
  .seg {
    height: 44px;
  }
}

/* Responsive adjustments */
@media (max-width: 900px) {
  .hero-section { grid-template-columns: 1fr; gap: 24px; }
  .trust-strip { grid-template-columns: 1fr 1fr; }
  .services-grid { grid-template-columns: 1fr 1fr; }
  .about-card-grid { grid-template-columns: 1fr; }
  .doctor-profile-card { grid-template-columns: 1fr; text-align: center; }
  .feature-split { grid-template-columns: 1fr; }
  .location-card { grid-template-columns: 1fr; }
  .site-footer__inner { grid-template-columns: 1fr; gap: 24px; }
  .ops-grid { grid-template-columns: 1fr; }
  .quality-strip { grid-template-columns: 1fr; }
  .quality-strip__body { padding: 24px; }
}

@media (max-width: 600px) {
  .trust-strip { grid-template-columns: 1fr; }
  .services-grid { grid-template-columns: 1fr; }
  .statrow { grid-template-columns: 1fr; }
  .wa-panel { padding: 18px; gap: 14px; }
  .wa-panel .btn--whatsapp { width: 100%; }
  .map-card__bar { flex-direction: column; align-items: flex-start; }
  .map-card__actions { width: 100%; }
  .map-card__actions .btn { flex: 1; }
}

/* Availability failed to load. The recovery controls sit on one row, both at
   the 44px touch minimum, so Retry is reachable without hunting on a phone. */
.slot-error-actions {
  margin-top: 16px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}
.slot-error-actions .btn {
  min-height: 44px;
}

/* Hero media display modes.

   `natural` renders the clinic's photograph at its own intrinsic ratio in a
   full-width column: portrait stays portrait, landscape stays landscape, with
   no crop and no letterbox bars. This is the default because showing a clinic
   its own picture whole is the behaviour that cannot be wrong.

   `cover` is opted into per clinic for lifestyle imagery where a crop reads
   better, and only then does `--hero-focus` decide what stays in frame. Neither
   mode is expressed as clinic-specific CSS: the pack sets displayMode and
   focalPoint, and these two rules serve every clinic. */
.m-hero-img--natural {
  width: 100%;
  height: auto;
  object-fit: contain;
  max-height: 78vh;
}

.m-hero-img--cover {
  width: 100%;
  height: auto;
  max-height: 78vh;
  object-fit: cover;
  object-position: var(--hero-focus, 50% 30%);
}

/* The conversion row is one unit: Book dominates, and Directions / WhatsApp /
   Call carry equal weight on a shared baseline. The grid tracks are sized from
   the number of available actions, so a clinic without verified WhatsApp gets
   two evenly balanced buttons rather than two-thirds of a three-column row. */
.m-quick-row {
  align-items: stretch;
  grid-auto-rows: 1fr;
}
.m-quick-row:has(> :nth-child(3)) { grid-template-columns: repeat(3, 1fr); }
.m-quick-row:has(> :nth-child(2):last-child) { grid-template-columns: repeat(2, 1fr); }
.m-quick-row:has(> :only-child) { grid-template-columns: 1fr; }

.m-quick-btn {
  min-height: 72px;
  padding: 8px 6px;
  text-align: center;
}
.m-quick-btn svg { flex: 0 0 auto; }

/* The mobile header row.

   `.mobile-top-bar` is `width: 100%` because it used to hold an absolutely
   centred screen title that needed the full row to centre against. On the main
   sections that title is no longer rendered, so the bar holds only the overflow
   button — and a 100%-wide container for one 40px button left the clinic's own
   name with 87px and a two-line ellipsis.

   Where there is no centred title, the bar sizes to its content and the brand
   takes the remaining width. The stacked-title screens keep the old behaviour,
   which is correct there because they hide the brand entirely. */
@media (max-width: 768px) {
  .appbar:not(.appbar--stacked-title) .mobile-top-bar {
    width: auto;
    flex: 0 0 auto;
    margin-left: auto;
  }

  .appbar:not(.appbar--stacked-title) .brand {
    flex: 1 1 auto;
    min-width: 0;
    margin-right: 12px;
  }

  .appbar:not(.appbar--stacked-title) .text-brand {
    min-width: 0;
    width: 100%;
  }
}

/* Long clinic names step the header type down instead of losing characters.
   Paired with brandNameSizeClass() in app.js; the two thresholds are product
   behaviour, identical for every clinic. */
@media (max-width: 768px) {
  .appbar .text-brand__name--long {
    font-size: 14px;
    line-height: 1.18;
  }
  .appbar .text-brand__name--xlong {
    font-size: 12.5px;
    line-height: 1.16;
    /* At this size three lines still fit the header's own min-height, so an
       exceptionally long name resolves whole rather than being cut. */
    -webkit-line-clamp: 3;
  }
}

/* ------------------------------------- Pre-boot server-rendered clinic frame */
/* The served document states the clinic's identity as real text inside #app, so
   a crawler, a link preview and a reader without JavaScript get the clinic
   rather than an empty div. It is also what a visitor looks at on a cold load
   until app.js has arrived and rendered, so it is styled as the site's opening
   frame rather than left as unstyled text on the default background. render()
   replaces the whole subtree on boot. Nothing here is hidden: the same content
   is visible to everyone, and the palette is the clinic's own from first paint. */
.boot-identity {
  max-width: 620px;
  margin: 0 auto;
  padding: 56px 24px 40px;
  text-align: center;
  font-family: var(--font-sans);
  color: var(--ink);
}
.boot-identity__logo {
  display: block;
  width: 72px;
  height: 72px;
  margin: 0 auto 20px;
  object-fit: contain;
}
.boot-identity__name {
  margin: 0;
  font-size: clamp(1.6rem, 6vw, 2.1rem);
  line-height: 1.15;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--brand-dark);
}
.boot-identity__lead {
  margin: 10px 0 0;
  font-size: 1.05rem;
  color: var(--brand-accent);
}
.boot-identity__line {
  margin: 14px 0 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--ink-2);
}
.boot-identity__line a {
  color: var(--brand);
}

/* --------------------------------------------- Premium Public Clinic Site */
body.public-premium {
  --surface: #FFFCF6;
  --surface-2: #F5F0E8;
  --surface-soft: #F8F4EC;
  --surface-mint: #E6F0EC;
  --brand: #0F766E;
  --brand-dark: #102A32;
  --brand-soft: #DDEBE7;
  --brand-accent: #9A6A3A;
  --ink: #172326;
  --ink-2: #54615E;
  --ink-3: #77827F;
  --border: #E4DDD0;
  background: var(--surface);
}

.appbar--public-premium {
  /* The premium surface, translucent. Written against the token rather than as
     a literal so a clinic that supplies its own colours gets its own header
     instead of the standard cream one. The literal stays as the fallback for
     anything without color-mix. */
  background: rgba(255, 252, 246, 0.92);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: blur(18px);
}

.appbar--public-premium .appbar__inner { min-height: 74px; }
.appbar--public-premium .site-nav a { font-size: 14px; letter-spacing: 0; }

.mdc-site {
  max-width: none;
  padding: 0;
  color: var(--ink);
}

.mdc-site h1,
.mdc-site h2,
.mdc-site h3 {
  letter-spacing: 0;
  color: var(--brand-dark);
}

.mdc-site {
  --mdc-header-h: 75px;
}

.mdc-site section[id] {
  scroll-margin-top: calc(var(--mdc-header-h) + 17px);
}

/* A stack of composed sections, not one long document. Proximity snapping nudges
   a section into place when the visitor stops near a boundary and otherwise
   stays entirely out of the way — the wheel, the trackpad, a finger and the
   keyboard all scroll normally, and a section taller than the viewport is never
   trapped. */
@media (min-width: 769px) and (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
    scroll-snap-type: y proximity;
  }

  .mdc-site > .mdc-hero,
  .mdc-site > .mdc-band {
    scroll-snap-align: start;
    scroll-snap-stop: normal;
  }
}

.mdc-hero {
  min-height: min(720px, calc(100svh - var(--mdc-header-h)));
  display: grid;
  /* The image track used to be sized independently of the photo it holds
     (1.08fr — wider than the copy column), so a portrait photo stretched to
     fill it forced heavy cropping no matter where object-position pointed.
     Sizing the track itself to the photo's actual height-driven target width
     — the same formula the item uses below — means the box needs almost no
     crop, and the space that opens up lands inside the flexible copy track
     as normal breathing room instead of as a visible gap between the two. */
  --mdc-hero-img-h: min(calc(100svh - var(--mdc-header-h) - 190px), 680px);
  grid-template-columns: minmax(360px, 1fr) minmax(340px, calc(var(--mdc-hero-img-h) * 0.82));
  grid-template-areas:
    "copy image"
    "actions image";
  align-items: center;
  gap: clamp(36px, 6vw, 88px);
  padding: clamp(48px, 8vw, 96px) clamp(28px, 6vw, 88px) clamp(42px, 7vw, 80px);
}

.mdc-hero__copy { grid-area: copy; max-width: 620px; align-self: end; }

.mdc-kicker,
.mdc-section-kicker {
  font-size: 12px;
  font-weight: 760;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand);
}

.mdc-hero h1 {
  margin-top: 12px;
  font-size: clamp(48px, 6.2vw, 86px);
  line-height: 0.96;
  font-weight: 620;
}

.mdc-tagline {
  margin-top: 18px;
  font-size: clamp(21px, 2.1vw, 30px);
  line-height: 1.22;
  color: var(--brand-accent);
}

.mdc-doctor-line {
  margin-top: 18px;
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink-2);
}

.mdc-hero-summary {
  margin-top: 18px;
  color: var(--ink-2);
  font-size: 16.5px;
  line-height: 1.65;
  max-width: 560px;
}

.mdc-actions {
  grid-area: actions;
  margin-top: -28px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  max-width: 620px;
  align-self: start;
}

.mdc-quick-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  width: 100%;
}

.mdc-quick-btn {
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  color: var(--brand-dark);
  background: rgba(255, 252, 246, 0.7);
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  font-weight: 650;
  font-size: 14px;
}

.mdc-hero__image,
.mdc-doctor figure,
.mdc-clinic-grid figure,
.mdc-location-photo {
  margin: 0;
  overflow: hidden;
  border-radius: 8px;
  background: var(--surface-soft);
}

.mdc-hero__image {
  grid-area: image;
  box-shadow: 0 24px 70px rgba(16, 42, 50, 0.14);
  /* The photograph is portrait, so at its natural aspect it grew taller than the
     browser window and the doctor was cut off by the fold on any laptop shorter
     than about 900px. Bounding it by the viewport — minus the header and the
     hero's own vertical padding — keeps the whole composition on one screen. The
     680px ceiling only matters on tall displays (it used to be 620px, which was
     shrinking the image on a 900px-tall screen even though the viewport had
     room). --mdc-hero-img-h is inherited from .mdc-hero, which now sizes the
     image's own grid track to match this box — see the comment there for why. */
  block-size: var(--mdc-hero-img-h);
  min-block-size: 320px;
}

/* On a phone the hero is a stacked column, not a two-column grid, and the height
   that has to be protected is the one holding the photograph, Book, and the
   Call / WhatsApp / Directions row together on the first screen. The bound is
   expressed in svh so it is the *small* viewport — the one with Safari's chrome
   fully expanded — that has to fit, not the tall one that appears after a
   scroll. */
@media (max-width: 900px) {
  .mdc-hero__image {
    block-size: min(46svh, 420px);
    min-block-size: 260px;
  }
}

.mdc-hero__image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 16%;
  transform: scale(1.015);
  animation: mdcImageSettle 900ms cubic-bezier(.2,.7,.2,1) forwards;
}

/* Desktop only, and after the base rule so it wins on width — a same-specificity
   rule scoped only by @media does not out-rank one written later in the file,
   so this used to sit *before* the base rule and the mobile-tuned crop it named
   was actually never applied; the base rule's value was winning everywhere.
   On desktop the container is wide relative to the portrait photo, so a fixed
   crop keeps roughly a fifth of the image out of frame regardless of which end
   it comes from; shifting the window down trades headroom above the doctor for
   the dental chair and equipment that a fully top-anchored crop was cutting off
   the bottom. */
@media (min-width: 901px) {
  .mdc-hero__image img {
    object-position: 50% 30%;
  }
}

.mdc-band {
  padding: clamp(48px, 6.4vw, 88px) clamp(24px, 6vw, 88px);
  border-top: 1px solid var(--border);
}

/* Section activation. Applied only once a section has actually been scrolled to,
   and settling to `transform: none` so no element keeps a containing block that
   would force embedded content to re-composite. */
.mdc-site [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 280ms ease-out, transform 300ms cubic-bezier(.2, .7, .2, 1);
}

.mdc-site [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* The Visit section holds a cross-origin Google Maps iframe. Chrome re-rasterises
   such a frame while an ancestor transform animates, which reads as the map
   flashing. This section therefore arrives by opacity alone — on every
   viewport, including the stronger mobile motion below. */
.mdc-site #visit[data-reveal] {
  transform: none;
  filter: none;
  transition: opacity 280ms ease-out;
}

/* On a phone the same subtle desktop fade read as barely-there — real device QA
   asked for the "coming alive" feel to actually register. More vertical travel
   and a light softening make the arrival readable at a glance without becoming
   a gimmick: still one-shot, still no scroll-linked motion, still off entirely
   under reduced-motion (that media query overrides both rules below). */
@media (max-width: 900px) {
  .mdc-site [data-reveal] {
    opacity: 0;
    transform: translateY(28px);
    filter: blur(5px);
    transition: opacity 380ms ease-out, transform 420ms cubic-bezier(.2, .7, .2, 1), filter 380ms ease-out;
  }

  .mdc-site [data-reveal].is-revealed {
    filter: blur(0);
  }

  .mdc-site #visit[data-reveal] {
    transform: none;
    filter: none;
  }
}

/* Belt and braces: nothing in the map card may be promoted or filtered. */
.map-card,
.map-card__surface,
.map-card__frame {
  transform: none;
  filter: none;
  backdrop-filter: none;
}

.map-card__frame {
  border: 0;
}

.mdc-band > h1,
.mdc-band > h2,
.mdc-treatment-head h2,
.mdc-doctor h2,
.mdc-clinic-grid h2,
.mdc-location-copy h2,
.mdc-booking-split h2 {
  margin-top: 10px;
  font-size: clamp(32px, 4.2vw, 58px);
  line-height: 1.04;
  font-weight: 600;
  max-width: 860px;
}

.mdc-treatment-head,
.mdc-doctor,
.mdc-booking-split,
.mdc-location {
  display: grid;
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}

.mdc-treatment-head,
.mdc-booking-split {
  grid-template-columns: minmax(280px, 0.9fr) minmax(280px, 0.7fr);
}

.mdc-doctor,
.mdc-location {
  grid-template-columns: minmax(260px, 0.72fr) minmax(320px, 1fr);
}

.mdc-treatment-head p,
.mdc-clinic-grid p,
.mdc-doctor p,
.mdc-booking-split p,
.mdc-location-copy p {
  color: var(--ink-2);
  line-height: 1.7;
  font-size: 16px;
}

.mdc-treatment-index {
  margin-top: 34px;
  border-top: 1px solid var(--border);
}

.mdc-orientation {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 1px;
  background: var(--border);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.mdc-orientation__item {
  min-height: 112px;
  padding: 18px 16px;
  background: var(--surface);
}

.mdc-orientation__item b,
.mdc-orientation__item span {
  display: block;
}

.mdc-orientation__item b {
  color: var(--brand-dark);
  font-size: 13px;
  line-height: 1.3;
}

.mdc-orientation__item span {
  margin-top: 8px;
  color: var(--ink-2);
  font-size: 12.5px;
  line-height: 1.45;
}

.mdc-treatment-explorer {
  margin-top: 34px;
  border-top: 1px solid var(--border);
}

.mdc-treatment-row {
  padding: 24px 0;
  border-bottom: 1px solid var(--border);
}

.mdc-treatment-row summary {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) 32px;
  gap: 24px;
  align-items: center;
  cursor: pointer;
  list-style: none;
}

.mdc-treatment-row summary::-webkit-details-marker {
  display: none;
}

.mdc-treatment-number {
  font-variant-numeric: tabular-nums;
  color: var(--brand-accent);
  font-size: 14px;
  font-weight: 720;
}

.mdc-treatment-row h3 {
  font-size: clamp(20px, 2vw, 28px);
  font-weight: 610;
}

.mdc-treatment-toggle {
  color: var(--brand);
  font-size: 28px;
  font-weight: 400;
  line-height: 1;
  text-align: center;
  transition: transform 200ms ease;
}

.mdc-treatment-row[open] .mdc-treatment-toggle {
  transform: rotate(45deg);
}

.mdc-treatment-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 9px 28px;
  margin: 18px 40px 0 96px;
  padding: 18px 22px 20px;
  border-left: 2px solid var(--brand-soft);
  background: var(--surface-mint);
  color: var(--ink-2);
  list-style-position: inside;
  animation: mdcTreatmentOpen 200ms ease-out both;
}

.mdc-treatment-list li {
  line-height: 1.45;
}

.mdc-treatment-cta {
  margin-top: 28px;
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}

.mdc-treatment-question {
  color: var(--brand);
  font-weight: 650;
  text-decoration: underline;
  text-underline-offset: 4px;
}

.mdc-hours {
  margin: 22px 0;
  display: grid;
  gap: 8px;
}

.mdc-hours > div {
  display: grid;
  grid-template-columns: minmax(130px, 0.7fr) repeat(2, minmax(120px, 1fr));
  gap: 8px;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  padding: 10px 12px;
}

.mdc-hours b {
  color: var(--brand-dark);
}

.mdc-hours span {
  color: var(--ink-2);
}

.mdc-doctor figure { aspect-ratio: 3 / 4; }

.mdc-doctor img,
.mdc-clinic-gallery img,
.mdc-clinic-grid img,
.mdc-location-photo img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}

.mdc-doctor-title {
  margin-top: 14px;
  color: var(--brand) !important;
  font-weight: 650;
}

.mdc-band--clinic { background: var(--surface-soft); }

.mdc-clinic-editorial {
  display: grid;
  grid-template-columns: minmax(260px, 0.78fr) minmax(360px, 1.22fr);
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}

.mdc-copy-list {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

.mdc-copy-list li {
  color: var(--ink-2);
  line-height: 1.55;
  padding-left: 18px;
  position: relative;
}

.mdc-copy-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--brand-accent);
}

.mdc-clinic-gallery {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  grid-template-rows: repeat(2, minmax(160px, 1fr));
  gap: 12px;
  height: clamp(380px, 42vw, 560px);
}

.mdc-clinic-gallery figure {
  margin: 0;
  overflow: hidden;
  border-radius: 8px;
  background: var(--surface-soft);
}

.mdc-clinic-gallery__primary {
  grid-row: span 2;
}

.mdc-clinic-grid {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 24px;
  align-items: stretch;
}

.mdc-clinic-grid > div {
  align-self: center;
  padding-right: clamp(0px, 4vw, 48px);
}

.mdc-clinic-grid figure { aspect-ratio: 4 / 3; }
.mdc-clinic-grid figure:last-child { grid-column: 2; aspect-ratio: 16 / 9; }

.mdc-band--booking {
  background: var(--brand-dark);
  color: #FFFFFF;
}

.mdc-band--booking .mdc-section-kicker,
.mdc-band--booking h2,
.mdc-band--booking p { color: #FFFFFF; }

.mdc-booking-panel .card { box-shadow: none; }
.mdc-booking-panel .card p,
.mdc-booking-panel .card .muted,
.mdc-booking-panel .card .muted-3 {
  color: var(--ink-2);
}
.mdc-location-photo { aspect-ratio: 4 / 5; }
.mdc-location-copy { max-width: 560px; }

.mdc-contact-lines {
  margin: 22px 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.mdc-contact-lines a {
  color: var(--brand-dark);
  display: grid;
  gap: 2px;
}

.mdc-contact-lines a span {
  color: var(--ink-2);
  font-size: 13px;
}

.mdc-contact-lines a b {
  font-weight: 680;
}

.mdc-map { margin-top: 34px; }
.mdc-footer .site-footer__bottom { color: var(--ink-3); }

.bottom-nav--actions {
  background: rgba(255, 252, 246, 0.88);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(20px);
}

@media (max-width: 768px) {
  body.public-premium .bottom-nav--actions {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(105%);
    transition: opacity 180ms ease, transform 180ms ease;
  }

  body.public-premium.premium-dock-visible .bottom-nav--actions {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateY(0);
  }
}

.bottom-nav--actions .bottom-nav__inner {
  max-width: 520px;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.bottom-nav__button {
  border: 0;
  background: transparent;
  font: inherit;
}

.public-premium .btn,
.public-premium .mdc-quick-btn,
.public-premium .bottom-nav__item {
  transition: transform 160ms ease, box-shadow 160ms ease, background-color 160ms ease;
}

.public-premium .btn:active,
.public-premium .mdc-quick-btn:active,
.public-premium .bottom-nav__item:active {
  transform: translateY(1px) scale(0.985);
}

.booking-privacy-note {
  margin: 14px 0 0;
  color: var(--ink-2);
  font-size: 13px;
  line-height: 1.5;
}

.mdc-review {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 18px;
  background: var(--surface);
}

.mdc-review h3 {
  margin-bottom: 8px;
  font-size: 20px;
}

.mdc-review__doctor {
  margin-top: 12px;
  color: var(--ink-2);
  font-size: 13px;
}

.mdc-confirm-screen {
  min-height: calc(100vh - 74px);
  display: grid;
  place-items: center;
  padding: 28px 20px var(--bottom-nav-clearance);
}

.mdc-confirm-ticket {
  width: min(100%, 460px);
  padding: 28px;
  border: 1px solid var(--border);
  border-radius: 18px;
  background: var(--surface);
  box-shadow: 0 18px 54px rgba(16, 42, 50, 0.1);
  text-align: center;
}

.mdc-confirm-ticket h1 {
  margin: 10px 0 24px;
  font-size: clamp(26px, 6vw, 34px);
  line-height: 1.08;
}

.mdc-confirm-ticket__label {
  display: block;
  color: var(--ink-3);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

.mdc-confirm-ticket .confirm__ref { margin: 6px 0 18px; }
.mdc-confirm-ticket__visit { color: var(--brand-dark); font-weight: 700; }
.mdc-confirm-ticket__name { margin-top: 5px; color: var(--ink-2); }
.mdc-confirm-ticket__note { margin: 20px 0; color: var(--ink-2); line-height: 1.5; }
.mdc-confirm-ticket .btn { width: 100%; margin-top: 10px; }
.mdc-confirm-ticket__call { display: inline-block; margin-top: 18px; color: var(--brand); font-weight: 650; }

@keyframes mdcImageSettle {
  to { transform: scale(1); }
}

@keyframes mdcReveal {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes mdcTreatmentOpen {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .mdc-hero__image img {
    animation: none;
    transform: none;
  }
  .mdc-band,
  .mdc-treatment-list {
    animation: none;
  }
  .mdc-site [data-reveal] {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }
  .public-premium .btn,
  .public-premium .mdc-quick-btn,
  .public-premium .bottom-nav__item,
  .mdc-treatment-toggle {
    transition: none;
  }
}

@media (max-width: 900px) {
  .mdc-hero,
  .mdc-treatment-head,
  .mdc-doctor,
  .mdc-clinic-editorial,
  .mdc-clinic-grid,
  .mdc-booking-split,
  .mdc-location {
    grid-template-columns: 1fr;
  }

  .mdc-orientation {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .mdc-hero {
    grid-template-areas:
      "copy"
      "image"
      "actions";
  }

  .mdc-actions { margin-top: 0; }

  .mdc-clinic-grid figure:last-child {
    grid-column: auto;
  }
}

@media (max-width: 768px) {
  .mdc-site--mobile {
    padding: 0 0 var(--bottom-nav-clearance);
  }

  .mdc-hero {
    min-height: auto;
    padding: 28px 20px 38px;
    gap: 24px;
    /* Clinic identity first, then the photograph, then the actions — the order
       real device QA asked for. The image itself is unchanged: same source,
       same crop, same size. Only its position in the stack moved. */
    grid-template-areas:
      "copy"
      "image"
      "actions";
  }

  .mdc-hero h1 { font-size: 42px; }
  .mdc-tagline { font-size: 22px; }
  .mdc-actions { margin-top: 0; }
  .mdc-actions .btn { flex: 1 1 100%; }
  .mdc-band { padding: 44px 20px; }
  .mdc-orientation {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .mdc-band > h1,
  .mdc-band > h2,
  .mdc-treatment-head h2,
  .mdc-doctor h2,
  .mdc-clinic-grid h2,
  .mdc-location-copy h2,
  .mdc-booking-split h2 {
    font-size: 32px;
  }

  .mdc-treatment-row {
    padding: 20px 0;
  }

  .mdc-treatment-row summary {
    grid-template-columns: 34px minmax(0, 1fr) 26px;
    gap: 14px;
  }

  .mdc-treatment-list {
    grid-template-columns: 1fr;
    margin: 14px 0 0 48px;
    padding: 14px 16px 16px;
  }

  .mdc-treatment-cta { align-items: stretch; flex-direction: column; }
  .mdc-treatment-question { text-align: center; }

  .mdc-hours > div {
    grid-template-columns: 1fr;
  }

  .mdc-clinic-gallery {
    display: flex;
    height: auto;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: 12px;
    margin: 0 -20px;
    padding: 0 20px 8px;
  }

  .mdc-clinic-gallery figure {
    flex: 0 0 min(84vw, 360px);
    aspect-ratio: 4 / 3;
    scroll-snap-align: start;
  }
}

/* ------------------------------------------- Premium public mobile menu.

   The premium header showed its four sections on desktop and nothing at all on a
   phone, so the section navigation simply did not exist on the device most
   patients use. A single compact control on the right of the header opens them.
   Privacy is deliberately absent: it lives in the footer. */
.mdc-menu {
  position: relative;
  margin-left: auto;
}

/* A pale near-white square on a cream header was easy to miss entirely — real
   phone QA flagged it as barely visible. A solid brand fill reads at a glance
   the way the header's own primary button does, without growing the tap target
   past what a header control should be. */
.mdc-menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 48px;
  block-size: 48px;
  border: none;
  border-radius: 13px;
  background: var(--brand);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(15, 118, 110, 0.28);
  box-shadow: 0 6px 16px color-mix(in srgb, var(--brand) 28%, transparent);
  transition: background 140ms ease-out, transform 120ms ease-out;
}

.mdc-menu-btn:active {
  background: var(--brand-dark);
  transform: scale(0.96);
}

.mdc-menu-btn[aria-expanded="true"] {
  background: var(--brand-dark);
}

.mdc-menu-btn svg {
  inline-size: 22px;
  block-size: 22px;
}

.mdc-menu__panel {
  position: absolute;
  inset-inline-end: 0;
  inset-block-start: calc(100% + 8px);
  z-index: 60;
  min-inline-size: 208px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface, #fffcf6);
  box-shadow: 0 18px 44px rgba(16, 42, 50, 0.18);
}

.mdc-menu__panel[hidden] {
  display: none;
}

.mdc-menu__list {
  display: flex;
  flex-direction: column;
}

.mdc-menu__list a {
  display: block;
  padding: 12px 12px;
  min-block-size: 44px;
  border-radius: 10px;
  color: var(--brand-dark);
  font-weight: 620;
  font-size: 15px;
  text-decoration: none;
}

.mdc-menu__list a.active {
  background: var(--surface-soft);
  color: var(--brand);
}

.mdc-menu__cta {
  inline-size: 100%;
  margin-block-start: 8px;
}

/* The dock and the menu must never overlap each other's tap targets. */
body.premium-menu-open .bottom-nav--actions {
  pointer-events: none;
}

/* The clinic's scope of care, stated once, in the clinic's own section. */
.mdc-clinic-scope {
  margin-block-start: 14px;
  color: var(--ink-2);
  font-size: 15.5px;
  line-height: 1.65;
  max-inline-size: 56ch;
}

/* The premium footer now renders on phones too, so it has to stack and clear the
   sticky action dock and the home indicator rather than sitting under them. */
@media (max-width: 768px) {
  .public-premium .mdc-footer .site-footer__inner {
    grid-template-columns: 1fr;
    gap: 26px;
  }

  .public-premium .mdc-footer {
    padding-block-end: calc(84px + env(safe-area-inset-bottom, 0px));
  }

  /* Side by side, the copyright and the credit each wrapped mid-phrase on a
     phone — half the clinic's name in one column and half the credit in the
     other, reading as one broken sentence. Stacked, each keeps its own lines. */
  .public-premium .mdc-footer .site-footer__bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .public-premium .mdc-footer .site-credit {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  .public-premium .mdc-footer .site-credit__sep {
    display: none;
  }
}

/* Printing does not scroll, so a section still waiting to be revealed would come
   out of the printer blank. Visibility never depends on the reveal. */
@media print {
  .mdc-site [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
}

/* =========================================================================
   Editorial presentation — opt-in composition
   =========================================================================

   Everything below is scoped to `body.presentation-editorial`, which only a
   clinic whose pack asks for `publicExperience.presentation: "editorial"`
   receives. No selector here can reach a clinic that did not opt in.

   The direction comes from the clinic's own photographs: pale-oak joinery and
   white marble against clinical white equipment. So the page is white paper and
   the warmth is entirely photographic. Blue is structural — type, rules,
   buttons. Red appears sparingly: the tagline, the rule above a treatment
   group, and focus. Nothing else.

   Two composition rules do the work:

     1. Photographs go to the edge of the viewport, or to the edge of a frame
        that was sized for the photograph. A photograph inset in a rounded card
        inside a padded section is what made the first hero read as a template
        with dead space around it.

     2. A frame's aspect ratio is chosen to match the photograph that goes in
        it. The portrait shots of the reception and the operatory were being
        poured into wide bands, which cut the desk and the chair in half — the
        clinic's single strongest asset, destroyed by the container. Portraits
        now get portrait frames.
   ========================================================================= */

body.presentation-editorial {
  --ed-gutter: clamp(20px, 5.2vw, 96px);
  --ed-rule: 1px solid var(--border);
  --ed-ease: cubic-bezier(.22, .68, 0, 1);
  --ed-max: 1320px;
}

.mdc-site--editorial {
  --ed-measure: 62ch;
}

.mdc-site--editorial .mdc-band {
  padding: clamp(72px, 9vw, 140px) var(--ed-gutter);
}

.presentation-editorial .mdc-section-kicker {
  font-size: 11px;
  letter-spacing: 0.18em;
}

/* One shared heading scale for every section, so the page reads as one
   document rather than seven differently-sized ones. */
.presentation-editorial .ed-story h2,
.presentation-editorial .ed-inside__intro h2,
.presentation-editorial .ed-doctor__copy h2,
.presentation-editorial .ed-treatments__head h2,
.presentation-editorial .ed-book__copy h2,
.presentation-editorial .ed-visit__copy h2 {
  font-size: clamp(28px, 3.1vw, 46px);
  line-height: 1.06;
  letter-spacing: -0.028em;
  font-weight: 700;
  color: var(--brand-dark);
  margin: 14px 0 0;
  text-wrap: balance;
}

/* ------------------------------------------------------------------ Hero

   The opening screen is a two-column composition on a tinted ground, not a
   photograph pasted against the right edge of the viewport.

   The previous version ran the consultant's portrait full-bleed across the
   right 54% of the screen and down past the fold — 770x960 of one person,
   touching the top and bottom corners of the page with nothing holding it.
   It made the hero about the photograph's size rather than about the clinic,
   and the copy column, stretched to that same height, opened a band of empty
   white above the eyebrow that no amount of vertical centring could fill.

   So the photograph is framed: a portrait card sized from the picture itself,
   sitting on a stage that keeps it clear of every page edge, with the copy
   column optically centred against it. The ground behind both is a pale
   clinical wash, which is what lets a white-mounted photograph read as a
   mounted photograph instead of as a hole in the page. */

.mdc-hero--editorial {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  align-items: center;
  gap: clamp(28px, 4vw, 72px);
  padding: clamp(96px, 9vw, 132px) var(--ed-gutter) clamp(72px, 8vw, 116px);
  min-height: min(100svh, 860px);
  border-top: 0;
  position: relative;
  isolation: isolate;
  background:
    radial-gradient(120% 90% at 82% 18%, color-mix(in srgb, var(--brand) 9%, transparent) 0%, transparent 62%),
    linear-gradient(178deg, var(--surface-2) 0%, var(--surface) 78%);
}

/* The stage exists so the frame can be centred inside its column and still
   carry the offset accent panel behind it. Sizing the figure alone would have
   made the accent a sibling of the grid, which is what previously dragged the
   photograph back onto the page edge at wide viewports. */
.ed-hero__stage {
  grid-column: 2;
  grid-row: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}

/* The stage carries no decoration of its own.
 *
 * It used to hold an offset panel in the clinic's blue behind the frame. On
 * the finished page that panel did not read as a deliberate accent: it read as
 * a shadow the photograph had failed to clip, because it emerged from under
 * two adjacent corners on a radius of its own while the frame above it was
 * being clipped square by the entry animation. Both halves of that are fixed
 * here — the animation now rounds, and the slab is gone. The frame's own
 * elevation is the only thing behind the picture. */
.ed-hero__stage::before { content: none; }

.ed-hero__media {
  position: relative;
  margin: 0;
  overflow: hidden;
  background: var(--surface-2);
  /* Sized from the picture, capped by the viewport. 4:5 is the shape the
     photograph was taken in, so `cover` removes nothing from it, and the
     height cap is what stops a tall window turning the frame back into the
     full-bleed slab this replaced. */
  inline-size: 100%;
  max-inline-size: clamp(320px, 38vw, 540px);
  aspect-ratio: 4 / 5;
  block-size: auto;
  max-block-size: min(68svh, 660px);
  min-block-size: 0;
  /* Published as a custom property because the entry animation has to clip on
     the same radius. A rounded box whose clip-path is square has square
     corners: `clip-path` replaces the border-radius clip rather than combining
     with it, which is precisely why the desktop hero's corners looked unfinished
     while the mobile hero — radius 0 — always looked correct. */
  --hero-radius: clamp(16px, 1.6vw, 26px);
  border-radius: var(--hero-radius);
  /* One elevation, close to the frame. The old pairing put a 64px-blur cast
     under a photograph that also had a coloured slab behind it, and the two
     together were the smudge. */
  box-shadow:
    0 1px 2px rgba(6, 20, 38, 0.06),
    0 18px 44px rgba(6, 20, 38, 0.13);
}

.ed-hero__media img {
  display: block;
  width: 100%;
  height: 100%;
  /* `cover` is declared, not implied: the mobile hero contract only skips its
     aspect-ratio assertion for a box that has opted into cropping. */
  object-fit: cover;
  /* The consultant stands in the left third of this frame and the chair fills
     the right two-thirds. In a 4:5 frame cut from a 4:5 photograph there is
     almost nothing to choose, so the bias is small and only guards the crop
     the height cap can introduce on a short window. */
  object-position: 42% 38%;
  /* Rounded on the image as well as on the frame. The image is promoted to its
     own compositor layer by the parallax transform, and a promoted child is the
     case where a parent's overflow clip is most likely to be applied a frame
     late or not at all. Rounding both means the corner is correct no matter
     which clip wins. The token is named rather than inherited: the image's
     parent is the <picture> element, which has no radius of its own. */
  border-radius: var(--hero-radius, 0px);
  will-change: transform;
  transform: translate3d(0, calc(var(--ed-parallax, 0) * 1px), 0) scale(1.04);
}

.ed-hero__copy {
  grid-column: 1;
  grid-row: 1;
  max-width: 620px;
  justify-self: start;
  align-self: center;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 0;
  /* The section now carries the gutter and the header clearance, so the column
     carries none of either. Padding here is what previously pushed the block
     down inside an already-centred cell and opened the band of nothing above
     the eyebrow. */
  padding: 0;
}

.mdc-hero--editorial h1 {
  font-size: clamp(38px, 4.6vw, 66px);
  line-height: 0.98;
  letter-spacing: -0.036em;
  font-weight: 700;
  color: var(--brand-dark);
  margin: 16px 0 0;
  max-width: 12ch;
  text-wrap: balance;
}

.mdc-hero--editorial .mdc-tagline {
  margin: 20px 0 0;
  font-size: clamp(15px, 1.35vw, 19px);
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--brand-accent);
}

.ed-hero__support {
  margin: 18px 0 0;
  max-width: 40ch;
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.6;
  color: var(--ink-2);
}

.ed-hero__actions { width: 100%; max-width: 520px; margin-top: clamp(28px, 3vw, 40px); }

/* ------------------------------------------------- Shared credential list */

/* A registered surgeon's public record, set as a ruled list rather than a
   paragraph: they are discrete facts, and a paragraph would make them look
   like prose the clinic wrote about itself. */
.ed-cred {
  margin: 26px 0 0;
  border-top: var(--ed-rule);
  max-width: 460px;
  width: 100%;
}

.ed-cred__row {
  display: grid;
  grid-template-columns: 116px minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: 13px 0;
  border-bottom: var(--ed-rule);
}

.ed-cred dt {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.ed-cred dd {
  margin: 0;
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--brand-dark);
}

.ed-cred--compact { max-width: 520px; }

/* ------------------------------------------------------- Buttons and links */

.presentation-editorial .btn--primary {
  border-radius: 4px;
  letter-spacing: 0.005em;
}

.presentation-editorial .mdc-quick-btn {
  border-radius: 4px;
  border: var(--ed-rule);
  background: var(--surface);
  transition: border-color 220ms var(--ed-ease), transform 220ms var(--ed-ease), background 220ms var(--ed-ease);
}

.presentation-editorial .mdc-quick-btn:hover {
  border-color: var(--brand);
  background: var(--surface);
  transform: translateY(-1px);
}

.presentation-editorial .btn:active,
.presentation-editorial .mdc-quick-btn:active { transform: scale(0.985); }

.ed-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 15px;
  font-weight: 600;
  color: var(--brand);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--brand) 34%, transparent);
  padding-bottom: 2px;
  transition: border-color 200ms var(--ed-ease), color 200ms var(--ed-ease);
}

.ed-link:hover { border-bottom-color: var(--brand); }

/* ---------------------------------------------------------- Hours block

   Stated once, in the visit section, and nowhere else on the page.

   The previous treatment set the label and both consultation windows as three
   items on one justified flex line inside a bordered box. At full width it was
   a cramped strip with the windows jammed against the right edge; at any
   narrower column the second window wrapped underneath and the box's own
   padding no longer matched its content, which is the spill that was reported.

   A caption over a two-cell grid cannot do that: the label owns its own line,
   and the two windows are equal cells that fall from two columns to one
   without anything moving out of the frame.

   What it never says: the clinic is closed on Sunday, and there is a break
   between the two windows. Both are facts about the rota rather than facts a
   patient benefits from reading, and the booking calendar already enforces
   both — it offers no Sunday and no slot between the sessions. */

.ed-hours {
  width: 100%;
  max-width: 560px;
  margin-top: clamp(26px, 2.6vw, 34px);
  padding: clamp(18px, 1.8vw, 24px) clamp(18px, 2vw, 26px);
  border: 1px solid color-mix(in srgb, var(--brand-dark) 13%, transparent);
  border-radius: 12px;
  background: var(--surface);
}

.ed-hours .mdc-hours { margin: 0; gap: 0; display: block; }

/* One window per line, at every width.
 *
 * Side by side they fit on a desktop and collide everywhere else, and the two
 * sittings are not a range to be read across — they are two separate times to
 * come in. Stacked, they are exactly what the clinic asked to publish, and the
 * block cannot spill because nothing in it is ever on the same line as
 * anything else. */
.ed-hours .mdc-hours > div {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 0;
  border: 0;
}

/* The label is the caption over the whole block, so it spans every column
   rather than competing with the first window for the same row. */
.ed-hours .mdc-hours > div > b {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 4px;
}

.ed-hours .mdc-hours > div > span {
  position: relative;
  font-size: clamp(16px, 1.35vw, 19px);
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--brand-dark);
  font-variant-numeric: tabular-nums;
  padding-left: 14px;
  white-space: nowrap;
}

/* A short accent rule at the head of each window, which is what tells the eye
   these are two separate sittings rather than one wrapped string. */
.ed-hours .mdc-hours > div > span::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.42em;
  block-size: 0.72em;
  inline-size: 3px;
  border-radius: 2px;
  background: var(--brand-accent);
}

/* ---------------------------------------------------------- Value story */

/* This section is the band, not a child of one, so it carries the gutter as
   padding and the cap has to include it — capping at the content width here
   inset the lede one gutter further than every other heading on the page. */
.ed-story {
  display: grid;
  gap: clamp(40px, 5vw, 72px);
  max-width: calc(var(--ed-max) + 2 * var(--ed-gutter));
  margin-inline: auto;
}

.ed-story__lede { max-width: 44ch; }

.ed-story__lede h2 { margin-top: 0; }

.ed-story__lede p {
  margin: 20px 0 0;
  font-size: clamp(16px, 1.25vw, 18px);
  line-height: 1.68;
  color: var(--ink-2);
  max-width: 46ch;
}

.ed-story__points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 2.2vw, 34px);
}

.ed-story__points li {
  padding: clamp(22px, 2.2vw, 32px);
  border-radius: clamp(14px, 1.2vw, 18px);
  background: var(--surface-2);
  border-top: 2px solid var(--brand);
  display: flex;
  flex-direction: column;
}

.ed-story__num {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--brand-accent);
}

.ed-story__points h3 {
  margin: 14px 0 0;
  font-size: clamp(17px, 1.5vw, 21px);
  line-height: 1.24;
  letter-spacing: -0.018em;
  font-weight: 700;
  color: var(--brand-dark);
}

.ed-story__points p {
  margin: 10px 0 0;
  font-size: 15px;
  line-height: 1.62;
  color: var(--ink-2);
}

/* The lede sits above its three supports, not beside them.
 *
 * Side by side, the three points had to share whatever the 44ch lede left —
 * about 180px of column each at 1440px, which is four or five words a line in
 * a card whose own padding was wider than its text. And because the lede ran
 * taller than the cards, the row left a block of nothing under them.
 *
 * Stacked, each point gets a third of the full measure, the row is level, and
 * the section ends where its content ends. The lede keeps its own narrow
 * measure, so nothing here becomes a band of full-width centred text. */
@media (min-width: 1080px) {
  .ed-story { gap: clamp(34px, 3.6vw, 52px); }
  .ed-story__lede { max-width: 60ch; }
  .ed-story__lede p { max-width: 58ch; }
}

/* ------------------------------------------------------ Inside the clinic */

/* Scoped to out-specify `.mdc-site--editorial .mdc-band`, which sets the
   gutter as horizontal padding. Without the extra class this rule lost, the
   band kept its gutter, and the photographs that are supposed to run to the
   edge of the viewport sat inset like cards — the exact composition this
   presentation exists to replace. */
.mdc-site--editorial .ed-inside {
  padding-inline: 0;
  display: grid;
  gap: clamp(28px, 3.4vw, 52px);
}

/* The cap is on the band, and the gutter is inside it. A 780px cap with a
   96px gutter on each side left 588px of text column — which is why a 672px
   heading kept breaking after "you" on a 1440px screen while every
   measurement of the viewport said it had room. */
/* Two columns, the same proportions the treatments head uses: the heading
   carries the left, the two short supporting lines sit to its right on the
   same baseline. As one column the copy took a 56ch cap inside a 1320px box,
   so two thirds of a desktop screen was empty beside it and the photographs
   started a screen lower than they needed to. */
.ed-inside__intro {
  width: 100%;
  max-width: calc(var(--ed-max) + 2 * var(--ed-gutter));
  margin-inline: auto;
  padding-inline: var(--ed-gutter);
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(20px, 3vw, 56px);
  align-items: end;
}

.ed-inside__title, .ed-inside__copy { min-width: 0; }

.presentation-editorial .ed-inside__intro h2 {
  margin-top: 12px;
  /* No width cap and no balancing. Both were what broke this heading after
     "you": `balance` optimises for even line lengths, which on a heading that
     nearly fits is an instruction to split it. Let it run, and let it wrap
     naturally only when the viewport genuinely cannot hold it. */
  max-width: none;
  text-wrap: pretty;
}

/* 46ch, not 56ch: this column is now 0.85fr of the content width, and a cap
   wider than the column it sits in is a cap that never applies. */
.ed-inside__intro p {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.65;
  color: var(--ink-2);
}

/* The line that hands the reader over to the photographs. Set a step quieter
   than the line above it so the intro reads as one thought that narrows,
   rather than as two paragraphs of equal weight. */
.ed-inside__lead {
  font-size: clamp(14px, 1.05vw, 16px);
  color: var(--ink-3, var(--ink-2));
}

.ed-inside__copy p + p { margin-top: 14px; }

@media (max-width: 860px) {
  .ed-inside__intro { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .ed-inside__copy { margin-top: 16px; }
}

/* The value points close the walkthrough. `.ed-inside` carries no inline
   padding — the rail is meant to reach the viewport edge — so anything textual
   inside it takes the same capped, guttered box the intro takes. */
.ed-inside__points {
  width: 100%;
  max-width: calc(var(--ed-max) + 2 * var(--ed-gutter));
  margin-inline: auto;
  padding-inline: var(--ed-gutter);
}


/* The shared composition nudges section boundaries into place with `y
   proximity` snapping on desktop. This presentation opts out of it: the brief
   for this site is that the page scroll belongs to the visitor and nothing
   ever takes it, and the horizontal rails already provide the only snapping
   the page has — scoped to their own tracks.

   Opting out is done twice, on purpose. Clearing `scroll-snap-align` on the
   sections removes every snap target, which disables snapping on its own in
   any browser; clearing `scroll-snap-type` via `:has()` also removes the
   container. Neither reaches a clinic that did not opt into this
   presentation, so every other clinic keeps the behaviour it has today. */
.mdc-site--editorial > .mdc-hero,
.mdc-site--editorial > .mdc-band {
  scroll-snap-align: none;
  scroll-snap-stop: normal;
}

html:has(body.presentation-editorial) {
  scroll-snap-type: none;
}

/* ========================================================== Media rail

   A horizontal set of large rounded cards that the visitor moves through, with
   the next card deliberately left peeking into the viewport — the peek is the
   whole affordance, and a rail whose next card is fully off screen reads as a
   cropped grid instead of something you can swipe.

   Sizing is per photograph, not per card. Two shapes exist because the clinic
   supplied two: landscape room shots and portrait equipment shots. Each card
   is as wide as its own picture wants to be at the rail's shared height, so
   `cover` crops nothing worth keeping — the reception desk and the dental
   chair are whole, and so are both side walls of the treatment room. That is
   the defect this component was built to end: the previous gallery poured
   portraits into landscape bands and cut every one of them in half.

   Snapping is scoped to the track. The page itself never snaps and nothing
   here touches document scroll, so a thumb flicking down the page is never
   caught by the rail it passes over. */

/* A grid item's automatic minimum size is its content, so an overflowing
   scroller inside one stretches its own track and pushes the document sideways
   instead of scrolling within itself. Both ends of that need `min-width: 0`. */
.mdc-site--editorial .ed-inside { grid-template-columns: minmax(0, 1fr); }

.ed-rail {
  position: relative;
  min-width: 0;
  --rail-h: clamp(300px, 42vw, 520px);
  --rail-gap: clamp(12px, 1.4vw, 20px);
  --rail-radius: clamp(14px, 1.4vw, 22px);
}

.ed-rail__track {
  list-style: none;
  min-width: 0;
  margin: clamp(26px, 3vw, 40px) 0 0;
  display: flex;
  gap: var(--rail-gap);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* The gutter is padding rather than margin so the first card starts on the
     page's own left edge and the last card can still reach the right one. */
  padding: 4px var(--ed-gutter) 8px;
  scroll-padding-inline: var(--ed-gutter);
}

.ed-rail__track::-webkit-scrollbar { display: none; }

.ed-rail__track:focus-visible {
  outline: 2px solid var(--brand-accent);
  outline-offset: 4px;
}

.ed-rail__card {
  flex: 0 0 auto;
  scroll-snap-align: start;
  /* Width follows the picture's own shape at the shared rail height. The shape
     class is the pre-decode fallback; the script replaces `--card-ar` with the
     photograph's exact ratio as soon as it has one, so nothing is cropped. */
  inline-size: calc(var(--rail-h) * var(--card-ar));
  max-inline-size: 88vw;
  /* The card is the picture, so it states both of its own dimensions. With the
     caption lifted onto the photograph there is no in-flow child left to give
     the card a height, and a flex item with nothing in flow inside it collapses
     to nothing — which is exactly what emptied the rail once. */
  block-size: var(--rail-h);
}

.ed-rail__card--wide { --card-ar: 1.36; }
.ed-rail__card--tall { --card-ar: 0.76; }

/* The transform lives on an inner element, never on the snap target itself:
   scaling the flex item would move the snap point it defines and make the
   rail settle a few pixels off on every flick. */
/* The card's inner element is a <button>, so it resets the control styling a
   button brings with it. Everything visual below is unchanged from when this
   was a div; only the element changed, to make the expanded view reachable by
   keyboard and announced to a screen reader. */
.ed-rail__inner {
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  border: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: zoom-in;
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: var(--rail-radius);
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(6, 20, 38, 0.06), 0 12px 30px rgba(6, 20, 38, 0.07);
  transform: scale(0.965);
  opacity: 0.72;
  transition: transform 620ms var(--ed-ease), opacity 620ms var(--ed-ease), box-shadow 620ms var(--ed-ease);
  will-change: transform;
}

/* A card the script has not classified stays fully present. Focus only ever
   adds emphasis; it never withholds a card. */
.ed-rail__card.is-focused .ed-rail__inner,
.ed-rail:not(:has(.is-focused)) .ed-rail__inner {
  transform: scale(1);
  opacity: 1;
  box-shadow: 0 2px 4px rgba(6, 20, 38, 0.07), 0 22px 48px rgba(6, 20, 38, 0.11);
}

.ed-rail__media {
  margin: 0;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  background: var(--surface-2);
  border-radius: inherit;
}

.ed-rail__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  transform: scale(1.04);
  transition: transform 1100ms var(--ed-ease);
}

.ed-rail__card.is-focused .ed-rail__media img { transform: scale(1); }

.ed-rail__inner:focus-visible {
  outline: 2px solid var(--brand-accent);
  outline-offset: 3px;
}

/* The affordance that says a card opens. Small, top-right, over the
   photograph's quietest corner, and only fully present on the focused card so
   the rail does not become four identical badges. */
.ed-rail__expand {
  position: absolute;
  z-index: 3;
  top: clamp(10px, 1vw, 14px);
  right: clamp(10px, 1vw, 14px);
  inline-size: 34px;
  block-size: 34px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 22, 40, 0.42);
  color: #fff;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 420ms var(--ed-ease), transform 420ms var(--ed-ease), background 200ms var(--ed-ease);
  pointer-events: none;
}

.ed-rail__expand svg { inline-size: 15px; block-size: 15px; }

.ed-rail__card.is-focused .ed-rail__expand,
.ed-rail:not(:has(.is-focused)) .ed-rail__expand {
  opacity: 1;
  transform: none;
}

.ed-rail__inner:hover .ed-rail__expand { background: rgba(8, 22, 40, 0.62); }

@media (prefers-reduced-motion: reduce) {
  .ed-rail__expand { transition: none; }
}

/* ------------------------------------------------------ Expanded view

   A native <dialog>. The element is the backdrop, the panel inside it is the
   thing being read, so a click that lands on the dialog itself is a click
   outside the panel — which is what the script closes on. */
/* Two columns on a pointer screen: the photograph on the left, what the room
   is on the right.

   A single stacked column looked wrong for this set. Three of the four
   photographs are portrait, and a portrait picture `contain`-fitted into a
   1080px-wide panel is a tall image with two broad bands of empty white beside
   it — the panel reads as a picture that failed to fill its frame. Beside a
   column of text the same picture is simply the left half of a spread, and the
   empty space becomes the gutter between the two. */
.ed-lightbox {
  border: 0;
  padding: 0;
  margin: auto;
  max-inline-size: min(1120px, 94vw);
  max-block-size: 92svh;
  inline-size: min(1120px, 94vw);
  background: var(--surface);
  color: var(--ink);
  border-radius: clamp(14px, 1.4vw, 20px);
  overflow: hidden;
  box-shadow: 0 24px 70px rgba(6, 20, 38, 0.34);
  /* Pinned to the viewport, not to the rail.
   *
   * A modal dialog is `position: fixed` in the UA stylesheet, and this rule
   * used to say `relative` — which put the panel back in normal flow at the
   * element'''s place in the document. The backdrop still covered the screen, so
   * the page dimmed correctly and the panel opened seventeen hundred pixels
   * above the viewport: the visitor saw a dimmed page with nothing on it.
   * `inset: 0` plus `margin: auto` is what centres it. */
  position: fixed;
  inset: 0;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  align-items: stretch;
}

/* The display switch is stated for the open state only.
 *
 * The UA stylesheet closes a dialog with `dialog:not([open]) { display: none }`.
 * Declaring `display: grid` on the element itself beat that rule, so the
 * closed lightbox stayed laid out — and being fixed to `inset: 0`, it covered
 * the whole viewport with an empty panel from the moment the page loaded. The
 * page underneath was still there and still interactive to the keyboard; there
 * was simply nothing to see. */
.ed-lightbox[open] { display: grid; }

.ed-lightbox::backdrop {
  background: rgba(6, 16, 30, 0.74);
  backdrop-filter: blur(3px);
}

.ed-lightbox[open] {
  animation: ed-lightbox-in 320ms var(--ed-ease) both;
}

@keyframes ed-lightbox-in {
  from { opacity: 0; transform: translate3d(0, 10px, 0) scale(0.985); }
  to { opacity: 1; transform: none; }
}

/* A dark matte rather than the page's own off-white. Whatever the picture's
   shape leaves over now reads as a deliberate mount, and the photographs —
   all of which are bright interiors — sit forward off it. */
.ed-lightbox__media {
  margin: 0;
  background: #0d1723;
  min-block-size: 0;
  max-block-size: 86svh;
  overflow: hidden;
  display: flex;
}

.ed-lightbox__media img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  min-block-size: 0;
  max-block-size: 86svh;
  /* `contain`, not `cover`: this is the view a visitor opened specifically to
     see the whole of. Cropping it here would defeat the control. */
  object-fit: contain;
}

.ed-lightbox__copy {
  align-self: center;
  padding: clamp(20px, 2.4vw, 38px) clamp(20px, 2.6vw, 40px);
  max-block-size: 86svh;
  overflow-y: auto;
}

.ed-lightbox__num {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--brand);
}

.ed-lightbox__copy h3 {
  margin: 8px 0 0;
  font-size: clamp(19px, 1.7vw, 25px);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.ed-lightbox__copy p {
  margin: 10px 0 0;
  max-width: 68ch;
  font-size: 15px;
  line-height: 1.62;
  color: var(--ink-soft, #4a5564);
}

.ed-lightbox__close {
  position: absolute;
  top: 12px;
  right: 12px;
  inline-size: 40px;
  block-size: 40px;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 22, 40, 0.52);
  color: #fff;
  transition: background 200ms var(--ed-ease);
}

.ed-lightbox__close:hover { background: rgba(8, 22, 40, 0.76); }
.ed-lightbox__close svg { inline-size: 20px; block-size: 20px; }

/* The arrows sit over the photograph, which is the thing they change. Over
   the whole panel the "next" arrow landed in the middle of the description,
   on top of the text it was about to replace. */
.ed-lightbox__nav {
  position: absolute;
  top: 50%;
  left: 0;
  width: 52.5%;
  transform: translateY(-50%);
  display: flex;
  justify-content: space-between;
  padding: 0 12px;
  pointer-events: none;
}

.ed-lightbox__btn {
  pointer-events: auto;
  inline-size: 44px;
  block-size: 44px;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 22, 40, 0.52);
  color: #fff;
  transition: background 200ms var(--ed-ease);
}

.ed-lightbox__btn:hover { background: rgba(8, 22, 40, 0.76); }
.ed-lightbox__btn svg { inline-size: 20px; block-size: 20px; }

.ed-lightbox__close:focus-visible,
.ed-lightbox__btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .ed-lightbox[open] { animation: none; }
}

/* The caption plate. A scrim rather than a panel: the photographs all have
   busy lower thirds — marble floor, chair bases, equipment trolleys — and flat
   white text on any of them is unreadable. The gradient is opaque enough at the
   baseline to guarantee contrast and gone entirely by the middle of the frame,
   so it never veils the subject. */
.ed-rail__cap {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: clamp(48px, 6vw, 76px) clamp(18px, 1.8vw, 26px) clamp(16px, 1.7vw, 24px);
  background: linear-gradient(
    to top,
    rgba(4, 16, 32, 0.88) 0%,
    rgba(4, 16, 32, 0.64) 34%,
    rgba(4, 16, 32, 0.18) 68%,
    rgba(4, 16, 32, 0) 100%
  );
  color: #fff;
}

.ed-rail__num {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: rgba(255, 255, 255, 0.72);
}

.ed-rail__cap h3 {
  margin: 7px 0 0;
  font-size: clamp(17px, 1.45vw, 21px);
  font-weight: 700;
  letter-spacing: -0.018em;
  color: #fff;
}

/* Progressive disclosure tied to the interaction rather than to a timer: a
   card's one-line note arrives as the card takes focus. The heading above it
   is always present, so nothing a visitor needs depends on the card being
   focused — or on the script running at all. */
.ed-rail__cap p {
  margin: 6px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.86);
  opacity: 0;
  transform: translate3d(0, 6px, 0);
  transition: opacity 520ms var(--ed-ease) 90ms, transform 520ms var(--ed-ease) 90ms;
}

.ed-rail__card.is-focused .ed-rail__cap p,
.ed-rail:not(:has(.is-focused)) .ed-rail__cap p {
  opacity: 1;
  transform: none;
}

/* The progress rule. Sits under the track, inset to the page gutter so it
   lines up with the first card's left edge. */
.ed-rail__progress {
  block-size: 2px;
  border-radius: 2px;
  margin: clamp(18px, 2vw, 26px) var(--ed-gutter) 0;
  background: color-mix(in srgb, var(--brand-dark) 12%, transparent);
  overflow: hidden;
}

.ed-rail__progress > i {
  display: block;
  block-size: 100%;
  inline-size: 100%;
  border-radius: inherit;
  background: var(--brand);
  transform-origin: left center;
  /* Scale rather than width: a transform composites, and this element is
     written to on every scroll frame of the rail. */
  transform: scaleX(calc(0.08 + 0.92 * var(--rail-progress, 0)));
  transition: transform 240ms var(--ed-ease);
}

/* Shown on touch as well as on a pointer. The arrows are a pointer affordance
   and are hidden on a phone, but how far through the walkthrough you are is
   not — and on a rail of unequal card widths the peek alone cannot say. */
.ed-rail__progress { display: block; }

@media (prefers-reduced-motion: reduce) {
  .ed-rail__progress > i { transition: none; }
}

/* Arrow pair, over the rail rather than under it.
 *
 * They used to sit in a row below the track, flush right — which on a 1440px
 * screen put them a full card-height away from the cards they scroll, at the
 * bottom-right corner of the section. They are now pinned to the vertical
 * midpoint of the track on its right-hand edge, where the next card is already
 * peeking: the control and the thing it moves are in the same place.
 *
 * Hidden where a thumb is the input device: on a touch screen the peeking card
 * is the affordance and an arrow pair is a second control for one gesture. */
.ed-rail__nav {
  display: none;
  position: absolute;
  z-index: 3;
  top: 50%;
  right: clamp(12px, 1.6vw, 26px);
  transform: translateY(-50%);
  flex-direction: column;
  gap: 10px;
  margin: 0;
  pointer-events: none;
}

/* The buttons themselves stay interactive; the column around them does not, so
   the strip of space between the two arrows never intercepts a click meant for
   the card underneath it. */
.ed-rail__nav > * { pointer-events: auto; }

.ed-rail__btn {
  inline-size: 46px;
  block-size: 46px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--brand-dark) 10%, transparent);
  /* Opaque, not translucent: these sit on top of a photograph, and a glassy
     control over a bright operatory wall is a control nobody can find. */
  background: var(--surface);
  box-shadow: 0 2px 6px rgba(6, 20, 38, 0.10), 0 10px 28px rgba(6, 20, 38, 0.16);
  color: var(--brand-dark);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 200ms var(--ed-ease), border-color 200ms var(--ed-ease),
              opacity 200ms var(--ed-ease), transform 200ms var(--ed-ease);
}

.ed-rail__btn svg { inline-size: 20px; block-size: 20px; }

.ed-rail__btn:hover:not(:disabled) { border-color: var(--brand); background: var(--surface); transform: scale(1.06); }

.ed-rail__btn:disabled { opacity: 0; pointer-events: none; cursor: default; }

@media (hover: hover) and (pointer: fine) {
  .ed-rail__nav { display: flex; }
}

/* The rail earns a bigger card on a wide screen; the peek is preserved by the
   viewport cap rather than by hiding anything. */
@media (min-width: 901px) {
  .ed-rail { --rail-h: clamp(380px, 38vw, 560px); }
  .ed-rail__card { max-inline-size: 62vw; }
}

@media (max-width: 900px) {
  /* A phone card is sized from the width a thumb swipes, not from the rail
     height: 84vw leaves the next card clearly peeking at the right edge. */
  .ed-rail { --rail-h: auto; }

  .ed-rail__card {
    inline-size: 84vw;
    max-inline-size: 84vw;
  }

  /* One frame for every card on a phone.
   *
   * Letting each card take its photograph's own ratio was honest about the
   * pictures and wrong about the set: at a fixed 84vw width it produced a
   * 110px-tall landscape card sitting next to a 440px-tall portrait one, and
   * the rail read as a layout accident rather than a walkthrough. Four cards
   * of one shape read as four views of one place, which is what they are.
   *
   * 4:5 is the compromise that costs the least: it is the portrait shots'
   * native shape, so they lose nothing, and the two landscape shots give up
   * their outer edges rather than their subject — each card carries its own
   * `object-position` from the pack, so the crop is chosen per photograph
   * instead of defaulting to the centre. */
  .ed-rail__card { aspect-ratio: 4 / 5; }
  .ed-rail__media { flex: 1 1 auto; }

  /* Every card is now the same height, so there is no difference left to
     distribute. Stretching keeps the tops and bottoms of the rail flush. */
  .ed-rail__track { align-items: stretch; }

  /* A swipe is the affordance here, and the peeking card is what announces it.
     An arrow pair on a touch screen is a second control for the same gesture. */
  .ed-rail__nav { display: none; }

  /* On a phone the expanded view is the screen, bottom-anchored so the close
     control and the description both sit within thumb reach. */
  /* One column on a phone: there is no room for a spread, and the photograph
     wants the width. */
  .ed-lightbox {
    inline-size: 100vw;
    max-inline-size: 100vw;
    max-block-size: 100svh;
    block-size: 100svh;
    margin: 0;
    border-radius: 0;
    grid-template-columns: minmax(0, 1fr);
    /* Both rows size to their content. With the media row at `1fr` it took
       every pixel the copy did not, so a landscape photograph sat in the
       middle of roughly five hundred pixels of dark matte — the mount stopped
       reading as a mount and started reading as a loading state. */
    grid-template-rows: auto auto;
    align-content: center;
  }

  .ed-lightbox__media {
    block-size: auto;
    max-block-size: 58svh;
  }

  .ed-lightbox__media img {
    block-size: auto;
    max-block-size: 58svh;
  }

  .ed-lightbox__copy {
    align-self: start;
    max-block-size: 42svh;
    /* Clears the arrow pair pinned to the bottom of the panel. Without it the
       last line of a four-line description ran underneath them. */
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 84px);
  }

  .ed-lightbox__nav {
    width: 100%;
    top: auto;
    transform: none;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
  }
}

/* ------------------------------------------------------------- Consultant */

.ed-doctor {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 1fr);
  gap: clamp(32px, 4.4vw, 80px);
  align-items: center;
  max-width: var(--ed-max);
  margin-inline: auto;
}

/* The same rounded surface the rail cards and the treatment cards use, so the
   consultant's portrait belongs to the page's card language instead of being
   the one square photograph left in it. */
.ed-doctor__photo {
  margin: 0;
  overflow: hidden;
  background: var(--surface-2);
  border-radius: clamp(14px, 1.4vw, 22px);
  box-shadow: 0 2px 4px rgba(6, 20, 38, 0.06), 0 20px 44px rgba(6, 20, 38, 0.09);
}

/* 4:5 is the ratio the portrait was taken in. A 5:6 frame is taller, and the
   difference came off the bottom of the picture — through the desk nameplate
   that names the consultant, in the section about the consultant. */
.ed-doctor__photo img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: 50% 40%;
}

.ed-doctor__copy h2 { margin-top: 12px; }

.ed-doctor__title {
  margin: 12px 0 0;
  font-size: clamp(15px, 1.25vw, 18px);
  font-weight: 600;
  color: var(--brand);
}

.ed-doctor__note {
  margin: 24px 0 0;
  max-width: 46ch;
  font-size: 15.5px;
  line-height: 1.64;
  color: var(--ink-2);
}

.ed-doctor__bio {
  margin: 22px 0 0;
  max-width: 52ch;
  font-size: 16px;
  line-height: 1.66;
  color: var(--ink-2);
}

/* -------------------------------------------------------------- Treatments */

.ed-treatments {
  background: var(--surface-2);
}

/* Heading left, lede right — the same two-part head the index below it uses,
   so the section reads as one block rather than as a title floating above an
   unrelated list. */
.ed-treatments__head {
  max-width: var(--ed-max);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(20px, 3vw, 56px);
  align-items: end;
}

.ed-treatments__head h2 { margin-top: 12px; }

.ed-treatments__head p {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.65;
  color: var(--ink-2);
}

@media (max-width: 860px) {
  .ed-treatments__head { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .ed-treatments__head p { margin-top: 16px; }
}

/* ------------------------------------------------------------ The index */

.ed-tx {
  max-width: var(--ed-max);
  margin: clamp(34px, 3.8vw, 56px) auto 0;
  border-top: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
}

.ed-tx__row {
  border-bottom: 1px solid color-mix(in srgb, var(--brand-dark) 14%, transparent);
}

/* One fixed four-track grid for every summary on the page. This is the whole
   point of the component: the number, the area name, the count and the toggle
   land on the same four vertical lines in every row, at every viewport, no
   matter how long any one name is. */
.ed-tx__row > summary {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) auto 28px;
  align-items: center;
  gap: clamp(12px, 1.6vw, 24px);
  padding: clamp(20px, 2.1vw, 30px) 4px;
  cursor: pointer;
  list-style: none;
  transition: background 240ms var(--ed-ease), padding-inline 240ms var(--ed-ease);
}

.ed-tx__row > summary::-webkit-details-marker { display: none; }
.ed-tx__row > summary::marker { content: ''; }

.ed-tx__row > summary:hover {
  background: color-mix(in srgb, var(--brand) 4%, transparent);
}

.ed-tx__row > summary:focus-visible {
  outline: 2px solid var(--brand-accent);
  outline-offset: -3px;
  border-radius: 4px;
}

.ed-tx__num {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--brand-accent);
  font-variant-numeric: tabular-nums;
}

.ed-tx__row > summary h3 {
  margin: 0;
  font-size: clamp(19px, 1.95vw, 28px);
  font-weight: 700;
  letter-spacing: -0.024em;
  line-height: 1.16;
  color: var(--brand-dark);
}

.ed-tx__count {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}

/* A drawn cross rather than a typed "+", so it can rotate into a minus on
   open. Two absolutely positioned bars on one square: nothing here depends on
   a font, and the rotation composites. */
.ed-tx__toggle {
  position: relative;
  inline-size: 22px;
  block-size: 22px;
  justify-self: end;
  color: var(--brand);
}

.ed-tx__toggle::before,
.ed-tx__toggle::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 50%;
  block-size: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 320ms var(--ed-ease), opacity 320ms var(--ed-ease);
}

.ed-tx__toggle::before { transform: translateY(-50%); }
.ed-tx__toggle::after { transform: translateY(-50%) rotate(90deg); }

.ed-tx__row[open] .ed-tx__toggle::after { transform: translateY(-50%) rotate(0deg); opacity: 0; }
.ed-tx__row[open] .ed-tx__num { color: var(--brand); }

.ed-tx__body {
  padding: 0 4px clamp(24px, 2.6vw, 38px);
}

/* Equal cells, equal gutters, one baseline per row. Auto-fit with a floor is
   what keeps the last row of an odd-numbered group aligned with the rows above
   it instead of stretching one cell across the leftover width. */
.ed-tx__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(18px, 2vw, 30px);
}

.ed-tx__item {
  padding-top: 18px;
  border-top: 2px solid color-mix(in srgb, var(--brand) 22%, transparent);
}

.ed-tx__item h4 {
  margin: 0;
  font-size: clamp(15.5px, 1.2vw, 17.5px);
  font-weight: 700;
  letter-spacing: -0.014em;
  color: var(--brand-dark);
}

.ed-tx__item p {
  margin: 8px 0 0;
  font-size: 14.5px;
  line-height: 1.62;
  color: var(--ink-2);
}

.ed-treatments__foot {
  max-width: var(--ed-max);
  margin: clamp(30px, 3.4vw, 46px) auto 0;
}

/* The contents of an opened row arrive rather than appearing, which is what
   makes the index feel like a control instead of a toggle. `details` has no
   height to animate from, so the move is on the cells: the row's own box is
   still an instant layout change and nothing depends on the animation. */
@keyframes ed-tx-in {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to { opacity: 1; transform: none; }
}

.ed-tx__row[open] .ed-tx__item {
  animation: ed-tx-in 520ms var(--ed-ease) both;
  animation-delay: calc(var(--j, 0) * 55ms);
}

@media (max-width: 720px) {
  .ed-tx__row > summary {
    grid-template-columns: 34px minmax(0, 1fr) 22px;
    gap: 12px;
    padding-block: 18px;
  }

  /* The count is the first thing to go on a phone: the treatments themselves
     are one tap away and counting them is not what the row is for. */
  .ed-tx__count { display: none; }

  .ed-tx__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  .ed-tx__row[open] .ed-tx__item { animation: none; }
}

/* ------------------------------------------------------------ Appointment */

.ed-book {
  background: var(--brand-dark);
  color: #fff;
}

.presentation-editorial .ed-book.mdc-band {
  padding-block: clamp(56px, 6vw, 96px);
}

.ed-book__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 400px);
  gap: clamp(32px, 4vw, 64px);
  align-items: center;
  max-width: var(--ed-max);
  margin-inline: auto;
}

.presentation-editorial .ed-book .mdc-section-kicker { color: rgba(255, 255, 255, 0.62); }

.presentation-editorial .ed-book__copy h2 { color: #fff; max-width: 16ch; }

.ed-book__copy p {
  margin: 18px 0 0;
  max-width: 52ch;
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.64;
  color: rgba(255, 255, 255, 0.78);
}

.ed-book__note {
  margin-top: 22px !important;
  font-size: 15px !important;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.92) !important;
  font-style: italic;
}

.ed-book__panel { width: 100%; }

/* ----------------------------------------------------------------- Visit */

.mdc-site--editorial .ed-visit { padding-inline: 0; }

.ed-visit__photo { margin: 0; overflow: hidden; }

.ed-visit__photo img {
  display: block;
  width: 100%;
  /* The signage runs along the top of this frame and a cinematic band cut it
     in half. 16:9 crops less, and biasing the crop upward keeps the whole
     sign at the cost of pavement nobody is looking at. */
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: 50% 28%;
}

.ed-visit__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(32px, 4vw, 72px);
  align-items: start;
  max-width: calc(var(--ed-max) + 2 * var(--ed-gutter));
  /* No top margin: it was the gap under the full-bleed entrance photograph
     this section used to open with, and with the photograph gone it was
     stacking on the band's own padding and opening a screen of nothing above
     the heading. */
  margin: 0 auto;
  padding-inline: var(--ed-gutter);
}

.ed-visit__copy h2 { margin-top: 12px; }

.ed-visit__address {
  margin: 16px 0 0;
  max-width: 40ch;
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink-2);
}

.ed-visit__map { border-radius: 6px; overflow: hidden; }

.presentation-editorial .mdc-contact-lines { margin-top: 28px; }

/* =========================================================================
   Motion
   =========================================================================

   One motion language, four moves, and nothing else:

     rise      text and small blocks arrive 18px low and fade up
     media-in  a photograph uncovers from its lower edge and settles from 1.04
     stagger   children of a section follow their section, 70ms apart
     parallax  the hero photograph alone drifts against the page

   Everything animates `opacity`, `transform` or `clip-path` — properties the
   compositor owns — so no section reveal costs a layout. `will-change` is set
   only on the hero image, the one element under continuous scroll-linked
   transform; promoting every section would cost more memory than the motion
   is worth and is what makes a long page stutter on a mid-range phone.

   The safety rule, which survives every future edit here: a section's content
   is visible unless something actively hid it, and the only thing that hides
   it is `[data-reveal]:not(.is-revealed)`, an attribute JavaScript writes at
   mount. If the script never runs, never composites, or throws, no selector
   below matches and the page is simply a page.
   ========================================================================= */

@keyframes ed-rise {
  from { opacity: 0; transform: translate3d(0, 18px, 0); }
  to { opacity: 1; transform: none; }
}

/* The reveal wipes the frame up from its lower edge. Both ends state the
   frame's own radius: without `round`, the final `inset(0 0 0 0)` is a square
   clip that sits on the element for as long as the animation is applied —
   which, with `both`, is forever. That was the unrounded desktop hero. */
@keyframes ed-media-in {
  from { opacity: 0; clip-path: inset(0 0 18% 0 round var(--hero-radius, 0px)); }
  to { opacity: 1; clip-path: inset(0 0 0 0 round var(--hero-radius, 0px)); }
}

.presentation-editorial .ed-hero__copy > * {
  animation: ed-rise 760ms var(--ed-ease) both;
  animation-delay: calc(80ms + var(--i, 0) * 85ms);
}

.presentation-editorial .ed-hero__media {
  animation: ed-media-in 1000ms var(--ed-ease) both;
}

/* Section children stagger once their section is revealed by the existing
   IntersectionObserver. They inherit that observer's scroll fallback, so a
   section that is on screen is visible whether or not the observer fired. */
.presentation-editorial .mdc-site [data-reveal] .ed-tx__row,
.presentation-editorial .mdc-site [data-reveal] .ed-rail__card,
.presentation-editorial .mdc-site [data-reveal] .ed-story__points li,
.presentation-editorial .mdc-site [data-reveal] .ed-hours,
.presentation-editorial .mdc-site [data-reveal] .ed-visit__map,
.presentation-editorial .mdc-site [data-reveal] .mdc-contact-lines,
.presentation-editorial .mdc-site [data-reveal] .ed-cred__row {
  transition: opacity 620ms var(--ed-ease), transform 680ms var(--ed-ease),
              clip-path 900ms var(--ed-ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}

.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-tx__row,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-rail__card,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-hours,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-visit__map,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .mdc-contact-lines,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-story__points li {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
}

/* A card uncovers from its own lower edge as well as rising, so the rail
   arrives as a set of surfaces being drawn rather than a row of boxes fading
   on. `clip-path` composites, so this costs no layout. */
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-rail__card {
  clip-path: inset(0 0 14% 0);
}

.presentation-editorial .mdc-site [data-reveal] .ed-rail__card {
  clip-path: inset(0 0 0 0);
}

/* The photographic entrance. A frame settles from a slight scale as its
   section arrives, which is the one move that makes scrolling this page feel
   different from scrolling a document. */
.presentation-editorial .mdc-site [data-reveal] .ed-rail__media img,
.presentation-editorial .mdc-site [data-reveal] .ed-visit__photo img,
.presentation-editorial .mdc-site [data-reveal] .ed-doctor__photo img {
  transition: transform 1200ms var(--ed-ease), opacity 700ms var(--ed-ease);
}

.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-rail__media img,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-visit__photo img,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-doctor__photo img {
  transform: scale(1.06);
  opacity: 0.001;
}

/* The lede and heading of each section rise as the section arrives. Kept to
   the section's own first children so nothing deep in a card animates. */
.presentation-editorial .mdc-site [data-reveal] .ed-story__lede,
.presentation-editorial .mdc-site [data-reveal] .ed-inside__intro,
.presentation-editorial .mdc-site [data-reveal] .ed-treatments__head,
.presentation-editorial .mdc-site [data-reveal] .ed-doctor__copy,
.presentation-editorial .mdc-site [data-reveal] .ed-book__copy,
.presentation-editorial .mdc-site [data-reveal] .ed-visit__copy {
  transition: opacity 700ms var(--ed-ease), transform 760ms var(--ed-ease);
}

.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-story__lede,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-inside__intro,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-treatments__head,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-doctor__copy,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-book__copy,
.presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-visit__copy {
  opacity: 0;
  transform: translate3d(0, 16px, 0);
}

/* --------------------------------------------------- Tablet and phone */

@media (max-width: 1200px) {

  /* Between the phone breakpoint and a wide desktop the copy column is narrow
     enough that the eyebrow broke after the separator, leaving the locality
     stranded on a line of its own. Tightening the tracking and reclaiming the
     inner padding keeps it on one line rather than dropping the locality. */
  .mdc-hero--editorial .mdc-kicker { letter-spacing: 0.13em; font-size: 10.5px; }
  .ed-hero__copy { padding-right: clamp(20px, 2.4vw, 36px); }
}

@media (max-width: 1079px) {
  .ed-story__points { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 1100px) {
  .ed-doctor { grid-template-columns: minmax(0, 1fr); gap: 32px; align-items: start; }
  /* Still the photograph's own 4:5. Stacking the section does not change the
     shape of the picture, and the landscape frame this used to impose took the
     crop off the bottom — through the desk nameplate that names the consultant
     the section is about. */
  .ed-doctor__photo img { aspect-ratio: 4 / 5; object-position: 50% 40%; }
  .ed-doctor__photo { max-width: 460px; }
  .ed-visit__body { grid-template-columns: minmax(0, 1fr); }
  .ed-book__inner { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .ed-visit__photo img { aspect-ratio: 16 / 9; }
}

@media (max-width: 900px) {
  /* A phone keeps the edge-to-edge opening: the copy surface riding up over
     the photograph is what makes the first screen read as one composed panel
     rather than a picture with text under it, and there is no room on a phone
     for a frame plus the margin a frame needs to look deliberate. The desktop
     stage and frame are therefore released here rather than reproduced. */
  .mdc-hero--editorial {
    display: block;
    min-height: 0;
    padding: 0;
    gap: 0;
    background: var(--surface);
  }

  .ed-hero__stage { display: block; }
  .ed-hero__stage::before { display: none; }

  .ed-hero__media {
    grid-column: auto;
    grid-row: auto;
    inline-size: auto;
    max-inline-size: none;
    aspect-ratio: auto;
    block-size: clamp(360px, 54svh, 540px);
    max-block-size: none;
    min-block-size: 0;
    --hero-radius: 0px;
    border-radius: 0;
    box-shadow: none;
  }

  .ed-hero__media img {
    object-position: 42% 28%;
    transform: translate3d(0, calc(var(--ed-parallax, 0) * 1px), 0) scale(1.04);
  }

  .ed-hero__copy {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: none;
    justify-self: auto;
    margin-top: -28px;
    padding: 24px var(--ed-gutter) 32px;
    background: var(--surface);
    border-radius: 24px 24px 0 0;
    box-shadow: 0 -18px 40px rgba(6, 20, 38, 0.07);
  }

  .mdc-hero--editorial h1 { max-width: 16ch; margin-top: 10px; }

  .ed-cred, .ed-hero__actions, .ed-hours { max-width: none; }

  .ed-story__points { grid-template-columns: minmax(0, 1fr); gap: 22px; }

  /* The walkthrough becomes a swipe. Each slide is a tall frame carrying one
     whole room, which is the point of the section on a phone as much as on a
     desktop. */

  .ed-visit__photo img { aspect-ratio: 4 / 3; }

  .ed-treatments__grid { margin-top: 28px; }
}

@media (max-width: 900px) {
  .ed-kicker__where { display: none; }
}

@media (max-width: 400px) {
  .ed-cred__row {
    grid-template-columns: minmax(0, 1fr);
    gap: 3px;
    padding: 10px 0;
  }

  .ed-cred dd { font-size: 14.5px; }

}

/* ------------------------------------------------------- Reduced motion */

@media (prefers-reduced-motion: reduce) {
  .presentation-editorial .ed-hero__copy > *,
  .presentation-editorial .ed-hero__media {
    animation: none;
    opacity: 1;
    clip-path: none;
    transform: none;
  }

  .presentation-editorial .ed-rail__card:hover img { transform: none; }

  .presentation-editorial .mdc-site [data-reveal] .ed-hero__media img,
  .presentation-editorial .mdc-site [data-reveal] .ed-rail__media img,
  .presentation-editorial .mdc-site [data-reveal] .ed-visit__photo img,
  .presentation-editorial .mdc-site [data-reveal] .ed-doctor__photo img,
  .presentation-editorial .mdc-site [data-reveal] .ed-tx__row,
  .presentation-editorial .mdc-site [data-reveal] .ed-hours,
  .presentation-editorial .mdc-site [data-reveal] .ed-visit__map,
  .presentation-editorial .mdc-site [data-reveal] .mdc-contact-lines,
  .presentation-editorial .mdc-site [data-reveal] .ed-story__points li,
  .presentation-editorial .mdc-site [data-reveal] .ed-rail__card,
  .presentation-editorial .mdc-site [data-reveal] .ed-story__lede,
  .presentation-editorial .mdc-site [data-reveal] .ed-inside__intro,
  .presentation-editorial .mdc-site [data-reveal] .ed-treatments__head,
  .presentation-editorial .mdc-site [data-reveal] .ed-doctor__copy,
  .presentation-editorial .mdc-site [data-reveal] .ed-book__copy,
  .presentation-editorial .mdc-site [data-reveal] .ed-visit__copy {
    transition: none;
    transform: none;
    opacity: 1;
    clip-path: none;
  }

  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-rail__media img,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-visit__photo img,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-doctor__photo img,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-tx__row,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-hours,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-visit__map,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .mdc-contact-lines,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-story__points li,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-rail__card,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-story__lede,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-inside__intro,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-treatments__head,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-doctor__copy,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-book__copy,
  .presentation-editorial .mdc-site [data-reveal]:not(.is-revealed) .ed-visit__copy {
    opacity: 1;
    transform: none;
    clip-path: none;
  }

  .presentation-editorial .ed-hero__media img { transform: scale(1.04); }

  .presentation-editorial .ed-rail__inner,
  .presentation-editorial .ed-rail__media img,
  .presentation-editorial .ed-rail__cap p {
    transition: none;
    transform: none;
    opacity: 1;
  }
}

/* =========================================================================
   Scroll-linked polish, where the browser can do it on the compositor
   =========================================================================

   Native scroll-driven animations run entirely off the main thread, so the
   page stays responsive under them in a way a scroll listener writing styles
   never does. They are additive only: every rule below sits inside both a
   support query and a reduced-motion query, so a browser without the feature
   — and a visitor who asked for less movement — gets the page exactly as it is
   above, with nothing missing.
   ========================================================================= */

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {

    /* A reading-progress rule along the bottom edge of the fixed header. It is
       the smallest thing on the page that makes scrolling feel instrumented
       rather than inert, and it is decorative in the strict sense: it reports
       document position and nothing reads it. */
    body.presentation-editorial .appbar::after {
      content: '';
      position: absolute;
      inset: auto 0 -1px 0;
      block-size: 2px;
      background: linear-gradient(90deg, var(--brand), var(--brand-accent));
      transform-origin: left center;
      transform: scaleX(0);
      animation: ed-page-progress linear both;
      animation-timeline: scroll(root block);
    }

    /* The consultant's portrait drifts against the page as its section passes,
       which is the one still photograph on the page large enough for the move
       to register. The rail's photographs are deliberately excluded: they carry
       their own focus transform, and two transforms competing for one image is
       how a rail starts to judder. */
    body.presentation-editorial .ed-doctor__photo img {
      animation: ed-drift linear both;
      animation-timeline: view(block);
      animation-range: cover 0% cover 100%;
    }
  }
}

@keyframes ed-page-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@keyframes ed-drift {
  from { transform: translate3d(0, -2.6%, 0) scale(1.07); }
  to { transform: translate3d(0, 2.6%, 0) scale(1.07); }
}
