/*
 * Samraksha Chest & Allergy Precision Clinics
 *
 * Hand-authored rather than generated: shared hosting has no build step, and
 * one reviewable stylesheet beats a toolchain the clinic's next developer
 * would have to install before changing a colour.
 *
 * Colours come from the clinic's own mark - the blue of the lungs and the
 * green of the leaf - with a teal between them. The teal is not decoration:
 * a gradient straight from blue to green passes through grey and goes muddy,
 * and a stop in the middle is what keeps the large colour areas clean.
 */

/* ------------------------------------------------------------- tokens -- */

:root {
  --brand-50:  #eff6ff;
  --brand-100: #dbeafe;
  --brand-200: #bfdbfe;
  --brand-300: #93c5fd;
  --brand-400: #60a5fa;
  --brand-500: #2b7fee;
  --brand-600: #1565d8;
  --brand-700: #1250ae;
  --brand-800: #123f85;
  --brand-900: #0f2f5c;
  --brand-950: #08203f;

  --teal-200: #a5f3fc;
  --teal-300: #67e8f9;
  --teal-400: #22d3ee;
  --teal-500: #0eb8c4;
  --teal-600: #0b93a6;

  --leaf-50:  #ecfdf3;
  --leaf-100: #d1fae5;
  --leaf-200: #a7f3d0;
  --leaf-300: #6ee7a7;
  --leaf-400: #34d576;
  --leaf-500: #18b85c;
  --leaf-600: #12994c;
  --leaf-700: #0f7a3e;
  --leaf-800: #115e34;
  --leaf-900: #0d4a2b;

  --ink-900: #0b1524;
  --ink-700: #33415c;
  --ink-500: #64748b;
  --ink-300: #cbd5e1;
  --ink-200: #e2e8f0;
  --ink-100: #f1f5f9;
  --ink-50:  #f8fafc;

  --amber-50:  #fffbeb;
  --amber-200: #fde68a;
  --amber-800: #92400e;
  --rose-50:   #fff1f2;
  --rose-200:  #fecdd3;
  --rose-600:  #e11d48;
  --rose-700:  #be123c;

  --surface: #ffffff;
  --surface-muted: #f3f8ff;
  --border: #e4ecf6;
  --text: var(--ink-900);
  --text-soft: var(--ink-500);

  /* One gradient, for the navy bands. The rest of the page is flat colour
     and hairlines: three of these running at once was the whole problem. */
  --grad-deep:    linear-gradient(135deg, #08203f 0%, #0f2f5c 42%, #123f85 100%);

  /* Four steps, none of them large. The 24 and 32px corners were what
     made every panel read as the same soft rectangle. */
  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;
  --radius-xl: 18px;

  --shadow-sm: 0 1px 2px rgba(11, 21, 36, .05), 0 1px 3px rgba(11, 21, 36, .04);
  --shadow:    0 4px 12px rgba(11, 21, 36, .06), 0 12px 28px rgba(11, 21, 36, .07);
  --shadow-lg: 0 14px 38px rgba(11, 21, 36, .12);

  --wrap: 1180px;
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--text);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* Inter is served with its optical size axis, so the drawing tightens by
     itself at display sizes instead of being faked with tracking. */
  font-optical-sizing: auto;
}

/*
 * One family, used properly.
 *
 * A second display face was doing work that size, weight and tracking do
 * better, and the pairing it made - a geometric display over Inter - is the
 * house style of every template on the internet. The hierarchy here comes
 * from the scale: each step down loses weight and gains letter-spacing.
 */
h1, h2, h3, h4 {
  margin: 0 0 .6rem;
  line-height: 1.12;
  font-weight: 700;
  letter-spacing: -.022em;
  /* Keeps a two-line heading from leaving one word stranded. */
  text-wrap: balance;
}
h1 { font-size: clamp(2.1rem, 1.45rem + 2.9vw, 3.3rem); letter-spacing: -.032em; }
h2 { font-size: clamp(1.55rem, 1.15rem + 1.6vw, 2.2rem); letter-spacing: -.026em; }
h3 { font-size: 1.12rem; letter-spacing: -.014em; }
p { margin: 0 0 1rem; text-wrap: pretty; }
a { color: var(--brand-700); text-decoration: none; }

/* Skip link: keyboard users should not have to tab the whole menu. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--brand-700); color: #fff;
  padding: .75rem 1.25rem; border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 1.25rem; }
.muted { color: var(--text-soft); }
.small { font-size: .9rem; }
.tiny  { font-size: .79rem; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.tabular { font-variant-numeric: tabular-nums; }
.stack > * + * { margin-top: 1rem; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }

/* ------------------------------------------------------------- header -- */

/* Thin utility strip above the main bar. It is what gives the header its
   layered, built look; it is also the first place a patient looks for a
   phone number. Hidden on phones, where the space is worth more. */
.header-strip {
  background: var(--brand-950);
  color: rgba(255, 255, 255, .72);
  font-size: .82rem;
}
.header-strip .bar {
  display: flex; align-items: center; gap: 1.5rem;
  min-height: 40px; flex-wrap: wrap;
}
.header-strip a { color: rgba(255, 255, 255, .82); }
.header-strip a:hover { color: #fff; text-decoration: none; }
.strip-item { display: inline-flex; align-items: center; gap: .45rem; }
.strip-item svg { width: 15px; height: 15px; flex: none; color: var(--teal-400); }
.strip-right { margin-left: auto; display: inline-flex; align-items: center; gap: 1.25rem; }
/* A branch and its hours are one unit: never break "9 AM - 4 PM" across
   two lines, or the strip reads as four separate times. */
.strip-branch { white-space: nowrap; }
.strip-hours { align-items: flex-start; }
.strip-hours svg { margin-top: .22rem; }

.site-header { position: sticky; top: 0; z-index: 60; }
/*
 * The dot grid is a background layer rather than a pseudo-element on
 * purpose. An overlay would need overflow:hidden to stay inside the rounded
 * corners, and that would clip the mobile navigation, which drops out of
 * the bottom of this element.
 */
.header-main {
  position: relative;
  background-color: var(--brand-950);
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, .09) 1px, transparent 1.4px),
    var(--grad-deep);
  background-size: 22px 22px, auto;
  border-bottom: 1px solid rgba(255, 255, 255, .1);
  box-shadow: 0 6px 24px rgba(8, 32, 63, .22);
}
.header-main .bar {
  display: flex; align-items: center; gap: 1rem;
  min-height: 74px;
}

.logo { display: inline-flex; align-items: center; gap: .65rem; text-decoration: none; }
.logo:hover { text-decoration: none; }
.logo svg { width: 42px; height: 42px; flex: none; }
.logo-text { display: flex; flex-direction: column; line-height: 1.15; }
/*
 * The mark is used on light backgrounds too - the staff sign-in page and the
 * admin sidebar - so the default has to be readable there. Only the navy
 * header and footer flip it to white.
 */
.logo-name {
  font-weight: 800; font-size: 1.28rem; color: var(--brand-900); letter-spacing: -.025em;
}
.logo-sub {
  font-size: .63rem; text-transform: uppercase; letter-spacing: .16em;
  color: var(--brand-600); font-weight: 600;
}
.site-header .logo-name, .site-footer .logo-name { color: #fff; }
.site-header .logo-sub, .site-footer .logo-sub { color: var(--teal-300); }

.site-nav { margin-left: auto; display: flex; align-items: center; gap: .1rem; }
.site-nav a {
  padding: .55rem .85rem; border-radius: 999px;
  color: rgba(255, 255, 255, .82); font-size: .93rem; font-weight: 500;
}
.site-nav a:hover { background: rgba(255, 255, 255, .12); color: #fff; text-decoration: none; }
.site-nav a[aria-current="page"] { color: #fff; font-weight: 600; background: rgba(255, 255, 255, .14); }

.header-actions { display: flex; align-items: center; gap: .6rem; }
/* The booking label is two flex items ("Book" and a span that disappears on
   small screens), so the button's own .5rem gap would read as a double
   space between the words. */
.header-actions .btn { gap: .32rem; }
.header-phone {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .5rem .85rem; border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .22);
  font-size: .88rem; font-weight: 600; color: #fff;
}
.header-phone:hover { border-color: var(--teal-400); color: var(--teal-300); text-decoration: none; }
.header-phone svg { width: 17px; height: 17px; flex: none; }

.nav-toggle {
  display: none; width: 44px; height: 44px; flex: none;
  align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, .25); border-radius: 12px;
  background: none; cursor: pointer; color: #fff;
}
/* Bars while it is shut, a cross while it is open: the label alone said so,
   and a label is not on the screen. */
.nav-toggle .nav-toggle-cross { display: none; }
.site-header.open .nav-toggle .nav-toggle-bars { display: none; }
.site-header.open .nav-toggle .nav-toggle-cross { display: block; }

/*
 * The screen behind the open drawer.
 *
 * The drawer reaches about two thirds of the way down a phone and the page
 * carried on underneath it, live: tapping 60px below the last item - a thumb
 * aiming at Contact and missing - closed the menu and opened /book. The
 * backdrop takes that tap, dims what it covers, and sits just under the
 * header so the drawer stays on top of it.
 */
.nav-scrim {
  position: fixed; inset: 0; z-index: 59;
  background: rgba(6, 23, 45, .55);
  opacity: 0; pointer-events: none;
  transition: opacity .22s ease;
}
body.nav-open .nav-scrim { opacity: 1; pointer-events: auto; }
/* Both of these float above the header, so the backdrop cannot cover them.
   They go quiet instead - and they are the two controls a missed tap was
   most likely to hit. */
body.nav-open .action-bar,
body.nav-open .wa-fab { opacity: 0; pointer-events: none; }
.action-bar { transition: opacity .22s ease; }

@media (max-width: 1040px) {
  .site-nav { display: none; }
  .header-phone { display: none; }
  .nav-toggle { display: inline-flex; margin-left: auto; }
  /*
    * The drawer scrolls on its own and never runs off the bottom of the
    * screen.
    *
    * With the services submenu printed inside it, this was 1121px tall on an
    * 844px phone: seven links below the fold, and because the panel hangs off
    * a sticky header, scrolling the page moved the page behind it and left
    * those links exactly where they were. It read as a frozen menu, which is
    * what it was.
    */
  .site-header.open .site-nav {
    display: flex; flex-direction: column; align-items: stretch;
    position: absolute; left: 0; right: 0; top: 100%;
    background: var(--brand-900);
    border-top: 1px solid rgba(255, 255, 255, .12);
    padding: .75rem 1.25rem 1.25rem; gap: .2rem;
    box-shadow: var(--shadow-lg);
    max-height: calc(100vh - 100px);
    max-height: calc(100dvh - 100px);
    overflow-y: auto;
    /* Reaching the end of the drawer must not start scrolling the page
       underneath it. */
    overscroll-behavior: contain;
  }
  .site-header.open .site-nav a { padding: .85rem 1rem; border-radius: 12px; }

  /*
   * Inside the drawer a long submenu is its two headings, not its twenty
   * links. A phone menu is for getting to a section; the services and the
   * conditions are listed in full on the pages those headings lead to.
   *
   * Only the long ones. The Branches menu is five places under two
   * headings, and dropping its links would have left a phone with nothing
   * to tap - the whole point of that menu is the pages underneath it. The
   * drawer scrolls, so a short menu costs nothing to print in full.
   */
  /* Scoped to the open drawer, which also gets these past the desktop
     submenu rules further down the file: those match at the same strength
     and would otherwise win simply by coming later. */
  .site-header.open .nav-menu-long .nav-menu-inner ul { display: none; }
  /* A column pointing where its own parent points repeats it either way:
     with its list hidden it is nothing but the repeat, and with its list
     shown the links simply sit under the parent, which reads better than
     naming the same place twice. Gone in both. */
  .site-header.open .nav-menu-echo > .nav-menu-head { display: none; }
  .site-header.open .nav-menu-long .nav-menu-echo { display: none; }

  /* Submenu links are a size down from the sections they sit under. Worth
     the rows: the drawer runs to thirteen items now, and Contact being the
     one that falls off the bottom would be the worst possible choice. Kept
     at a 44px target - .75rem top and bottom around a 20px line. */
  .site-header.open .nav-menu li a { padding: .75rem 1rem; }
  .site-header.open .nav-menu .nav-menu-head {
    font-size: .93rem; text-transform: none; letter-spacing: normal;
    font-weight: 500; margin-bottom: 0; color: rgba(255, 255, 255, .82);
  }
  .site-header.open .nav-menu-inner { padding: 0 0 0 1rem; gap: 0; }

  /* While the drawer is open the page behind it holds still. */
  body.nav-open { overflow: hidden; }
}

@media (max-width: 860px) {
  /*
   * Kept on a phone, with the hours alone.
   *
   * It used to be dropped entirely below this width, which took the opening
   * times off the screen of nearly every visitor - most of them arrive on a
   * phone, and "when are they open" is the commonest question a clinic
   * homepage has to answer. The email and the number go, because the button
   * and the floating WhatsApp control already cover those; the hours stay.
   */
  .header-strip .bar { justify-content: center; gap: .5rem; padding-block: .45rem; }
  .strip-right { display: none; }
  .header-strip { font-size: .78rem; text-align: center; }
  /*
   * The clock goes on a phone. The line of text is as wide as the screen, so
   * the icon could not sit beside it and wrapped onto a line of its own -
   * a small circle floating above the words, and a strip half again as tall
   * as it needed to be. "Mon-Sat" already says these are opening hours.
   */
  .strip-hours svg { display: none; }
  .strip-hours { justify-content: center; flex-wrap: wrap; }
}

/*
 * Phones. Three things have to share one line - the logo, the booking
 * button and the menu toggle - and at 360px they do not fit while the
 * strapline is still there. Dropping it is what keeps the other two on a
 * single line; a wrapped logo and a two-line button made the header over
 * 100px tall and pushed the page content off the first screen.
 */
@media (max-width: 600px) {
  .header-main .bar { gap: .55rem; min-height: 62px; }
  .logo svg { width: 34px; height: 34px; }
  .logo-name { font-size: 1.08rem; }
  /* Header only. The footer logo has a whole column to itself and keeps
     the strapline. */
  .site-header .logo-sub { display: none; }
  .header-actions { gap: .4rem; }
  .header-actions .btn { white-space: nowrap; padding: .5rem .8rem; }
  .nav-toggle { width: 40px; height: 40px; }
}
/* Narrower still: the button keeps the verb and drops the noun. */
@media (max-width: 420px) {
  .btn-label-long { display: none; }
}

/* ------------------------------------------------------------ buttons -- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .78rem 1.4rem; border-radius: 10px;
  border: 1px solid transparent;
  font: inherit; font-size: .95rem; font-weight: 600;
  cursor: pointer; text-decoration: none;
  transition: transform .16s, box-shadow .16s, background-color .16s, border-color .16s, color .16s;
}
.btn:hover { text-decoration: none; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; transform: none; }
/*
 * Inline SVG has no intrinsic size, so an icon inside a button is sized by
 * whatever the flex line happens to be. That is fine until the label wraps
 * to two lines on a narrow screen, at which point the icon grows to match
 * and swamps the button. Pin it.
 */
.btn svg { width: 18px; height: 18px; flex: none; }
.btn-sm svg { width: 16px; height: 16px; }
.btn-lg svg { width: 20px; height: 20px; }

/* Flat fills that darken on press. A gradient with a matching coloured
   glow under it is decoration pretending to be affordance. */
.btn-primary { background: var(--brand-700); color: #fff; }
.btn-primary:hover { background: var(--brand-800); }
.btn-accent { background: var(--leaf-600); color: #fff; }
.btn-accent:hover { background: var(--leaf-700); }
.btn-secondary { background: var(--surface); color: var(--ink-900); border-color: var(--border); }
.btn-secondary:hover { border-color: var(--brand-300); color: var(--brand-700); background: var(--brand-50); }
.btn-ghost { background: none; color: var(--ink-500); }
.btn-ghost:hover { background: var(--ink-100); color: var(--ink-900); }
.btn-outline { background: none; border-color: var(--ink-300); color: var(--ink-700); }
.btn-outline:hover { border-color: var(--brand-400); color: var(--brand-700); }
.btn-success { background: var(--leaf-600); color: #fff; }
.btn-success:hover { background: var(--leaf-700); }
.btn-danger { background: var(--rose-600); color: #fff; }
.btn-danger:hover { background: var(--rose-700); }
.btn-sm { padding: .5rem 1rem; font-size: .86rem; }
.btn-lg { padding: 1rem 2rem; font-size: 1.03rem; }
.btn-block { width: 100%; }
.btn-row { display: flex; flex-wrap: wrap; gap: .7rem; }

/* On a dark panel the plain white button is the primary one. */
.on-dark .btn-secondary { background: #fff; border-color: #fff; color: var(--brand-900); }
.on-dark .btn-secondary:hover { background: var(--brand-50); color: var(--brand-800); }
.on-dark .btn-outline { border-color: rgba(255, 255, 255, .42); color: #fff; }
.on-dark .btn-outline:hover { background: rgba(255, 255, 255, .14); border-color: #fff; color: #fff; }

/* -------------------------------------------------------------- cards -- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.6rem;
}
.card-tight { padding: 1.15rem; }
/* A column, so the "learn more" line sits on the floor of every card in the
   row rather than wherever its own paragraph happened to end. */
.card-link {
  display: flex; flex-direction: column; color: inherit;
  position: relative; overflow: hidden;
  transition: transform .18s, box-shadow .18s, border-color .18s;
}
.card-link .link-more { margin-top: auto; padding-top: 1rem; }
/* A hairline that wipes across the top on hover: enough to say "this is a
   link" without animating the whole card. */
.card-link::before {
  content: ""; position: absolute; inset: 0 auto auto 0; height: 2px; width: 100%;
  background: var(--brand-600);
  transform: scaleX(0); transform-origin: left;
  transition: transform .25s ease;
}
.card-link:hover {
  transform: translateY(-2px); box-shadow: var(--shadow-sm);
  border-color: var(--brand-200); text-decoration: none;
}
.card-link:hover::before { transform: scaleX(1); }

.grid { display: grid; gap: 1.4rem; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------ sections -- */

.section { padding: 4.5rem 0; position: relative; }
.section-lg { padding: 5.5rem 0; }
/* A flat, almost-white ground with a hairline. The gradient version read
   as a coloured band and there were three of them down the page. */
.section-muted { background: var(--ink-50); border-block: 1px solid var(--border); }

/* A full-width band in brand colour. Used once or twice a page to break the
   run of white and give the eye somewhere to land. */
.section-dark {
  background: var(--grad-deep); color: #fff;
  position: relative; isolation: isolate; overflow: hidden;
}
.section-dark h1, .section-dark h2, .section-dark h3 { color: #fff; }
.section-dark p { color: rgba(255, 255, 255, .76); }
.section-dark .muted { color: rgba(255, 255, 255, .62); }

.eyebrow {
  display: inline-flex; align-items: center; gap: .55rem;
  text-transform: uppercase; letter-spacing: .15em;
  font-size: .76rem; font-weight: 700; color: var(--brand-600);
  margin-bottom: .9rem;
}
.eyebrow::before {
  content: ""; width: 22px; height: 2px;
  background: currentColor; opacity: .55;
}
.section-dark .eyebrow, .hero .eyebrow { color: var(--teal-300); }

.lead { font-size: 1.1rem; color: var(--text-soft); max-width: 46rem; }
.section-dark .lead, .hero .lead { color: rgba(255, 255, 255, .78); }

.section-head { max-width: 44rem; margin-bottom: 2.75rem; }
.section-head.center { margin-inline: auto; text-align: center; }
.section-head.center .eyebrow { justify-content: center; }
.section-head .lead { margin-bottom: 0; }

/* --------------------------------------------------------------- hero -- */

.hero {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--grad-deep); color: #fff;
}
/* One wash, not three. Blue into teal into green across a single band is
   three accents fighting; this is depth in the brand colour and nothing
   more. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -2;
  background: radial-gradient(56rem 38rem at 76% -14%, rgba(43, 127, 238, .34), transparent 70%);
}
.hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1; opacity: .3;
  background-image:
    linear-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
  background-size: 62px 62px;
  mask-image: radial-gradient(70% 70% at 50% 40%, #000 30%, transparent 100%);
}
.hero h1 { color: #fff; }
.hero > .wrap:not(.hero-inner) { padding-block: 4.5rem; }
.hero-inner {
  padding-block: 4.75rem;
  display: grid; gap: 3.5rem;
  grid-template-columns: 1.06fr .94fr; align-items: center;
}
@media (max-width: 1040px) { .hero-inner { grid-template-columns: 1fr; padding-block: 3rem; gap: 2.5rem; } }
@media (max-width: 600px) { .hero > .wrap:not(.hero-inner) { padding-block: 3rem; } }

.pill {
  display: inline-flex; align-items: center; gap: .55rem;
  background: rgba(255, 255, 255, .08);
  border: 1px solid rgba(255, 255, 255, .18);
  color: #fff; border-radius: 999px;
  padding: .4rem .9rem; font-size: .73rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .1em;
}
.pill svg { width: 18px; height: 18px; flex: none; }

.hero-media { position: relative; justify-self: center; width: 100%; max-width: 420px; }
.hero-photo {
  border-radius: var(--radius-xl); overflow: hidden;
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 28px 70px rgba(8, 32, 63, .5);
  aspect-ratio: 4 / 5; background: var(--brand-900);
}
.hero-photo picture { display: block; height: 100%; }
.hero-photo img { display: block; width: 100%; height: 100%; object-fit: cover; }

/*
 * Floating panel over the photograph. Tinted dark rather than white-
 * translucent: a light frosted panel over a light photograph left the
 * qualification lines with almost no contrast against the face behind them.
 */
.hero-card {
  position: absolute; left: -1.25rem; right: -1.25rem; bottom: -2.6rem;
  background: rgba(8, 32, 63, .92);
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: var(--radius-lg);
  /* No backdrop-filter. At this opacity the blur is invisible anyway, and
     the property still misrenders the photograph underneath it on older
     Android WebViews. */
  padding: 1.1rem 1.35rem;
  box-shadow: var(--shadow-lg);
}
.hero-card { display: flex; align-items: center; gap: .95rem; }
.hc-avatar {
  display: block; flex: none; width: 74px; height: 74px;
  border-radius: 50%; overflow: hidden; background: var(--brand-900);
  border: 2px solid rgba(255, 255, 255, .3);
  box-shadow: 0 10px 24px rgba(8, 32, 63, .55);
}
.hc-avatar picture { display: block; height: 100%; }
.hc-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; }
.hc-text { display: flex; flex-direction: column; min-width: 0; }
.hero-card .hc-name { font-weight: 800; font-size: 1.05rem; color: #fff; }
.hero-card .hc-meta { font-size: .79rem; color: rgba(255, 255, 255, .88); }
@media (max-width: 520px) {
  .hero-card { left: .75rem; right: .75rem; bottom: -1.25rem; }
  .hero-media { margin-bottom: 2rem; }
  .hc-avatar { width: 62px; height: 62px; }
}

.stat-row {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.25rem; margin-top: 2.5rem;
}
@media (max-width: 620px) { .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.stat-value {
  /* nowrap and a fluid size: "Mon-Sat" is far wider than "2" and broke
     across two lines, which knocked its label out of line with the rest. */
  font-size: clamp(1.45rem, 1.05rem + .95vw, 2rem);
  white-space: nowrap;
  font-weight: 800; color: #fff; line-height: 1.05;
  letter-spacing: -.03em;
}
.stat-label { font-size: .78rem; color: rgba(255, 255, 255, .62); text-transform: uppercase; letter-spacing: .1em; }
/* Outside a dark band the same block has to work on white. */
.section .stat-value { color: var(--brand-700); }
.section .stat-label { color: var(--text-soft); }

/* --------------------------------------------------------- icon badges -- */

/*
 * A tinted square with a hairline, not a saturated gradient tile with its
 * own coloured shadow. Forty-four pixels rather than fifty-four: the icon
 * is a label for the heading under it, not a competitor to it.
 */
.icon-badge {
  display: inline-grid; place-items: center;
  width: 44px; height: 44px; border-radius: 10px;
  background: var(--brand-50); color: var(--brand-700);
  border: 1px solid var(--brand-100);
  margin-bottom: 1rem;
}
.icon-badge svg { width: 21px; height: 21px; }
.icon-badge.leaf { background: var(--leaf-50); color: var(--leaf-700); border-color: var(--leaf-100); }
.icon-badge.soft { background: var(--brand-50); color: var(--brand-700); }
.icon-badge.solid { background: var(--brand-900); color: #fff; border-color: transparent; }
.icon-badge.lg { width: 54px; height: 54px; border-radius: 12px; }
.icon-badge.lg svg { width: 25px; height: 25px; }
.section-dark .icon-badge, .hero .icon-badge {
  background: rgba(255, 255, 255, .07); color: #fff;
  border-color: rgba(255, 255, 255, .16);
}

/* --------------------------------------------------------------- chips -- */

.chip-row { display: flex; flex-wrap: wrap; gap: .55rem; padding: 0; list-style: none; }
.chip {
  display: inline-block;
  border: 1px solid var(--border); background: var(--surface);
  border-radius: 999px; padding: .42rem 1rem;
  font-size: .88rem; font-weight: 500; color: var(--ink-700);
  transition: border-color .15s, color .15s, background-color .15s, transform .15s;
}
a.chip:hover, .chip-row a:hover .chip {
  border-color: var(--brand-300); color: var(--brand-700);
  background: var(--brand-50); transform: translateY(-1px); text-decoration: none;
}
.hero .chip, .section-dark .chip {
  background: rgba(255, 255, 255, .1); border-color: rgba(255, 255, 255, .2); color: #fff;
}
/*
 * In the hero they are a size down. There are nine of them and the longest
 * runs to thirty-six characters, which beside the lungs - a column of about
 * 575px - filled five rows and pushed the booking button off the bottom of
 * a laptop screen. A list of what the clinic treats is worth having there;
 * it is not worth the thing the page is for.
 */
.hero .chip-row {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: .4rem;
}
.hero .chip-row li { display: flex; }
.hero .chip {
  display: flex; align-items: center; justify-content: center;
  width: 100%; text-align: center; line-height: 1.25;
  font-size: .76rem; padding: .4rem .7rem;
}
.hero a.chip:hover, .section-dark a.chip:hover {
  background: rgba(255, 255, 255, .2); border-color: rgba(255, 255, 255, .4); color: #fff;
}

/* ----------------------------------------------------------- cta panel -- */

.cta-panel {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--grad-deep); color: #fff;
  border-radius: var(--radius-xl); padding: 3.75rem 1.75rem; text-align: center;
}
.cta-panel::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(30rem 20rem at 20% -10%, rgba(43, 127, 238, .34), transparent 70%);
}
.cta-panel h2 { color: #fff; }
.cta-panel p { color: rgba(255, 255, 255, .8); max-width: 36rem; margin-inline: auto; }

/* --------------------------------------------------------- feature row -- */

.feature { display: flex; gap: .8rem; align-items: flex-start; }
.feature-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--leaf-500); flex: none; margin-top: .6rem;
}
.tick-list { list-style: none; padding: 0; margin: 0; display: grid; gap: .6rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 700px) { .tick-list { grid-template-columns: 1fr; } }
.tick-list li { display: flex; gap: .6rem; font-size: .94rem; color: var(--ink-700); }
.tick-list svg { width: 17px; height: 17px; flex: none; margin-top: .3rem; color: var(--leaf-600); }

.info-line { display: flex; gap: .75rem; align-items: flex-start; margin-bottom: .9rem; }
.info-line svg { width: 18px; height: 18px; flex: none; margin-top: .3rem; color: var(--leaf-600); }

/* ------------------------------------------------------ home page bits -- */

/* One word of the headline in the accent. Clipping a three-stop gradient
   into type is the single clearest sign that nobody drew the page. */
/* The paler ice blue, not the electric one: at 3.3rem the brighter teal
   buzzed against the navy. */
.accent-text { color: var(--teal-200); }

.link-more {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .9rem; font-weight: 700; color: var(--brand-700);
}
.inline-arrow { width: 16px; height: 16px; transition: transform .18s; }
.card-link:hover .inline-arrow { transform: translateX(4px); }

/* The two branch cards ride up over the bottom of the hero, so the first
   thing below the fold is where to go and when, not more prose. */
.branch-strip-wrap { padding-top: 0; }
.branch-strip {
  position: relative; z-index: 2; margin-top: -3.25rem;
  display: grid; gap: 1.25rem; grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 820px) { .branch-strip { grid-template-columns: 1fr; margin-top: -2.25rem; } }
.branch-tile {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.35rem 1.5rem;
  box-shadow: var(--shadow);
}
.branch-tile .icon-badge { margin-bottom: 0; }
/* On a phone the three-across row squeezes the address into a column a few
   words wide. Icon and text keep the top row; the buttons take their own. */
@media (max-width: 620px) {
  .branch-tile {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    gap: .35rem 1rem; align-items: center; padding: 1.25rem;
  }
  .branch-tile .btn-row { grid-column: 1 / -1; margin-top: .6rem; }
  .branch-tile .btn-row .btn { flex: 1; }
}

/* Text beside a picture, either order. */
.split { display: grid; gap: 3.5rem; grid-template-columns: 1fr 1fr; align-items: center; }
.split-narrow { grid-template-columns: 1fr 1.25fr; align-items: start; }
@media (max-width: 900px) { .split, .split-narrow { grid-template-columns: 1fr; gap: 2.25rem; } }
.split-media { position: relative; }
.split-media .photo-frame { border-radius: var(--radius-xl); box-shadow: var(--shadow-lg); }

/* A single figure lifted out of the copy and dropped on the photograph.
   White with a hairline: a coloured sticker with a coloured glow is what an
   advert looks like. */
.badge-float {
  position: absolute; right: -1rem; bottom: -1.5rem;
  display: flex; align-items: center; gap: .7rem;
  background: var(--surface); color: var(--ink-900);
  border: 1px solid var(--border);
  border-radius: var(--radius); padding: .85rem 1.15rem;
  box-shadow: var(--shadow);
}
.badge-float .bf-value {
  font-size: 1.75rem; font-weight: 700; line-height: 1; color: var(--brand-700);
  font-variant-numeric: tabular-nums;
}
.badge-float .bf-label { font-size: .72rem; line-height: 1.25; color: var(--text-soft); }
@media (max-width: 900px) { .badge-float { right: 1rem; bottom: -1.25rem; } }

.cta-logo { width: 58px; height: 58px; }

/* -------------------------------------------------------------- forms -- */

.field { margin-bottom: 1.15rem; }
.field > label, .field-label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: .4rem; }
.req { color: var(--rose-600); }
.input, .select, .textarea {
  width: 100%; padding: .7rem .85rem;
  border: 1px solid var(--ink-300); border-radius: 12px;
  background: var(--surface); color: var(--text);
  font: inherit; font-size: .95rem;
  transition: border-color .15s, box-shadow .15s;
}
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px rgba(49, 120, 214, .16);
  outline: none;
}
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--rose-600);
}
.textarea { resize: vertical; min-height: 5rem; }
.hint { font-size: .8rem; color: var(--text-soft); margin-top: .3rem; }
.error-text { font-size: .82rem; color: var(--rose-700); font-weight: 600; margin-top: .3rem; }
.checkline { display: flex; align-items: center; gap: .6rem; font-size: .93rem; }
.checkline input { width: 1.05rem; height: 1.05rem; accent-color: var(--brand-600); }
.field-row { display: grid; gap: 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.field-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.field-row-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 760px) { .field-row, .field-row-3, .field-row-4 { grid-template-columns: 1fr; } }

fieldset { border: 0; padding: 0; margin: 0 0 1.5rem; }
legend { font-weight: 700; font-size: 1.05rem; padding: 0; margin-bottom: .85rem; }

/* Branch chooser on the booking form */
.branch-choice { display: grid; gap: .85rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 620px) { .branch-choice { grid-template-columns: 1fr; } }
.branch-option { position: relative; }
.branch-option input { position: absolute; opacity: 0; pointer-events: none; }
.branch-option label {
  display: block; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1rem 1.1rem; background: var(--surface);
  transition: border-color .15s, background-color .15s, box-shadow .15s;
}
.branch-option label:hover { border-color: var(--brand-300); }
.branch-option input:checked + label {
  border-color: var(--brand-600); background: var(--brand-50);
  box-shadow: 0 0 0 1px var(--brand-600);
}
.branch-option input:focus-visible + label { outline: 3px solid var(--brand-500); outline-offset: 2px; }
.branch-option .b-name { font-weight: 700; }
.branch-option .b-meta { font-size: .82rem; color: var(--text-soft); display: block; margin-top: .2rem; }
.branch-option .b-hours { font-size: .82rem; font-weight: 600; color: var(--brand-700); display: block; margin-top: .25rem; }

/* Slot picker */
.slot-session + .slot-session { margin-top: 1.5rem; }
.slot-session h3 { font-size: .95rem; margin-bottom: .7rem; }
.slot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: .5rem; }
.slot { position: relative; }
.slot input { position: absolute; opacity: 0; pointer-events: none; }
.slot label {
  display: block; text-align: center; cursor: pointer;
  border: 1px solid var(--border); border-radius: 11px;
  padding: .55rem .35rem; font-size: .9rem; font-weight: 600;
  background: var(--surface);
  transition: border-color .15s, background-color .15s, color .15s;
}
.slot label:hover { border-color: var(--brand-400); background: var(--brand-50); }
.slot input:checked + label { background: var(--brand-600); border-color: var(--brand-600); color: #fff; }
.slot input:focus-visible + label { outline: 3px solid var(--brand-500); outline-offset: 2px; }
.slot input:disabled + label {
  cursor: not-allowed; opacity: .5; border-style: dashed;
  background: var(--ink-50); color: var(--text-soft);
}
.slot .slot-note { display: block; font-size: .68rem; font-weight: 500; opacity: .85; }

/* -------------------------------------------------------------- alerts -- */

.alert { border-radius: var(--radius); padding: .9rem 1.1rem; margin-bottom: 1.25rem; border: 1px solid; font-size: .93rem; }
.alert-success { background: var(--leaf-50); border-color: var(--leaf-200); color: var(--leaf-900); }
.alert-error { background: var(--rose-50); border-color: var(--rose-200); color: var(--rose-700); }
.alert-info { background: var(--brand-50); border-color: var(--brand-200); color: var(--brand-900); }
.alert-warn { background: var(--amber-50); border-color: var(--amber-200); color: var(--amber-800); }
.alert strong { display: block; }

/* -------------------------------------------------------------- tables -- */

.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
table.data { width: 100%; border-collapse: collapse; font-size: .9rem; min-width: 46rem; }
table.data th, table.data td { padding: .75rem .9rem; text-align: left; border-bottom: 1px solid var(--border); }
table.data thead th { background: var(--surface-muted); font-weight: 600; white-space: nowrap; }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--ink-50); }

.badge {
  display: inline-block; border-radius: 999px;
  padding: .2rem .6rem; font-size: .74rem; font-weight: 700; white-space: nowrap;
}
.badge-booked     { background: #e0f2fe; color: #075985; }
.badge-arrived    { background: #fef3c7; color: #92400e; }
.badge-in_consult { background: var(--brand-100); color: var(--brand-900); }
.badge-completed  { background: var(--leaf-100); color: var(--leaf-800); }
.badge-cancelled  { background: var(--ink-200); color: var(--ink-700); }
.badge-no_show    { background: #ffe4e6; color: #9f1239; }
.badge-outline    { background: none; border: 1px solid var(--border); color: var(--text-soft); font-weight: 600; }

/* ------------------------------------------------------------- footer -- */

/*
 * Dark, matching the header, so the page is bracketed by brand colour
 * rather than fading out into grey.
 *
 * No top margin: the last section's own bottom padding is the gap, and a
 * margin here would paint a white band above the footer.
 */
.site-footer {
  background: var(--grad-deep); color: rgba(255, 255, 255, .72);
  position: relative; isolation: isolate; overflow: hidden;
}
/* Two layers in one pseudo-element: the dot grid over the corner glow.
   Slightly wider spacing and lower contrast than the header, because the
   footer is a much larger area and the same setting reads as noise. */
.site-footer::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, .075) 1px, transparent 1.4px),
    radial-gradient(34rem 20rem at 6% 0%, rgba(43, 127, 238, .2), transparent 62%);
  background-size: 26px 26px, auto;
}
/*
 * Five columns of the same width - brand, services, contact and the two
 * centres - so the rows line up instead of one wide column setting the
 * height. minmax(0, 1fr) rather than 1fr: a long word (the email address)
 * would otherwise push its column past its share and unbalance the row.
 */
.footer-grid {
  display: grid; gap: 2.25rem 1.75rem; padding-block: 4rem;
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
@media (max-width: 1100px) { .footer-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px)  { .footer-grid { grid-template-columns: 1fr; } }
/*
 * The email is one long token in a 200px column. The list items are grid
 * items, whose automatic minimum is their content, so without this they
 * simply grow past the column instead of wrapping - and the links are
 * inline-block for the hover nudge, which does the same.
 */
.footer-grid ul { grid-template-columns: minmax(0, 1fr); }
.footer-grid a { overflow-wrap: break-word; max-width: 100%; }
.footer-grid li .tiny { opacity: .75; margin-left: .3rem; }
/* Site links below the contact details, separated by a rule so the two
   groups do not read as one list. */
.footer-grid .footer-quick {
  margin-top: 1.1rem; padding-top: 1.1rem;
  border-top: 1px solid rgba(255, 255, 255, .13);
}
.footer-grid h3 {
  font-size: .76rem; text-transform: uppercase; letter-spacing: .15em;
  margin-bottom: 1rem; color: var(--teal-300); font-weight: 700;
}
.footer-grid ul { list-style: none; padding: 0; margin: 0; display: grid; gap: .5rem; font-size: .92rem; }
.footer-grid a, .site-footer a { color: rgba(255, 255, 255, .74); }
.footer-grid a:hover, .site-footer a:hover { color: #fff; }
.site-footer .muted { color: rgba(255, 255, 255, .55); }
.site-footer strong { color: #fff; }
.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, .13);
  padding-block: 1.75rem 2.75rem; font-size: .84rem;
  color: rgba(255, 255, 255, .55);
}
/* Copyright to the left, the build credit to the right; they stack on a
   phone rather than being squeezed onto one line. */
.footer-legal {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: .45rem 1.5rem;
  /*
   * Keep clear of the WhatsApp button. It is fixed to the bottom right of
   * the viewport, so at the end of the page it sits exactly where this row
   * ends - and below about 1330px wide it covered the credit link, which
   * made it unclickable rather than merely untidy.
   */
  padding-right: 76px;
}
.footer-legal .credit a {
  color: #fff; font-weight: 600;
  border-bottom: 1px solid rgba(255, 255, 255, .32);
  transition: color .2s ease, border-color .2s ease;
}
.footer-legal .credit a:hover { color: var(--teal-300); border-bottom-color: var(--teal-300); }

/* WhatsApp floating button */
.wa-fab {
  position: fixed; right: 18px; bottom: 18px; z-index: 70;
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center;
  background: #25d366; color: #fff; box-shadow: var(--shadow-lg);
  transition: transform .15s, opacity .22s ease;
}
.wa-fab:hover { transform: scale(1.06); text-decoration: none; }
.wa-fab svg { width: 30px; height: 30px; }

/* ---------------------------------------------------------- admin shell */

.admin { display: flex; min-height: 100vh; background: var(--surface-muted); }
.admin-side {
  width: 236px; flex: none; background: var(--surface);
  border-right: 1px solid var(--border); padding: 1.25rem 1rem;
  display: flex; flex-direction: column; gap: 1.25rem;
}
.admin-side nav { display: grid; gap: .2rem; }
.admin-side nav a {
  display: flex; align-items: center; gap: .65rem;
  padding: .6rem .75rem; border-radius: 10px;
  color: var(--ink-700); font-size: .92rem; font-weight: 500;
}
.admin-side nav a:hover { background: var(--brand-50); color: var(--brand-700); text-decoration: none; }
.admin-side nav a.active { background: var(--brand-600); color: #fff; }
.admin-side svg { width: 18px; height: 18px; flex: none; }
.admin-main { flex: 1; min-width: 0; padding: 1.75rem; }
.admin-user { margin-top: auto; border-top: 1px solid var(--border); padding-top: 1rem; font-size: .85rem; }

.admin-topbar { display: none; }
@media (max-width: 900px) {
  .admin { flex-direction: column; }
  .admin-side {
    width: 100%; border-right: 0; border-bottom: 1px solid var(--border);
    flex-direction: row; align-items: center; gap: .5rem; overflow-x: auto;
    padding: .75rem 1rem;
  }
  .admin-side nav { display: flex; gap: .3rem; }
  .admin-side nav a { white-space: nowrap; }
  .admin-side .admin-user { display: none; }
  .admin-main { padding: 1.1rem; }
}

.page-head { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end; justify-content: space-between; margin-bottom: 1.5rem; }
.page-head h1 { font-size: 1.6rem; margin: 0; }

.tile { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.1rem; }
.tile .t-value { font-size: 1.75rem; font-weight: 800; line-height: 1.1; }
.tile .t-label { font-size: .8rem; color: var(--text-soft); }
.tile.warn .t-value { color: #b45309; }
.tile.good .t-value { color: var(--leaf-700); }
.tile.brand .t-value { color: var(--brand-700); }

.token-chip {
  display: grid; place-items: center; flex: none;
  width: 54px; height: 54px; border-radius: 13px;
  background: var(--brand-50); color: var(--brand-700);
  font-weight: 800; font-size: 1.2rem;
}
.queue-item { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }
.queue-body { flex: 1; min-width: 210px; }
.queue-actions { display: flex; flex-wrap: wrap; gap: .4rem; }
.queue-actions form { display: contents; }

.filter-bar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-bottom: 1.25rem; }
.filter-bar .input, .filter-bar .select { width: auto; }

/* --------------------------------------------------------- print sheet -- */

.sheet { background: #fff; max-width: 820px; margin: 1.5rem auto; padding: 2.25rem; border: 1px solid var(--border); border-radius: var(--radius-lg); }
.sheet-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; border-bottom: 2px solid var(--brand-700); padding-bottom: .9rem; }
.sheet dl.meta { display: grid; grid-template-columns: repeat(4, 1fr); gap: .6rem 1rem; margin: 1.1rem 0; padding: .9rem 0; border-bottom: 1px solid var(--border); }
@media (max-width: 620px) { .sheet dl.meta { grid-template-columns: repeat(2, 1fr); } }
.sheet dt { font-size: .74rem; color: var(--text-soft); text-transform: uppercase; letter-spacing: .05em; }
.sheet dd { margin: .15rem 0 0; font-weight: 600; font-size: .93rem; }
.sheet section { margin-top: 1.1rem; }
.sheet section h4 { font-size: .82rem; text-transform: uppercase; letter-spacing: .06em; color: var(--brand-800); margin-bottom: .3rem; }
.sheet section p { margin: 0; white-space: pre-line; font-size: .95rem; }
.rx-table { width: 100%; border-collapse: collapse; font-size: .92rem; margin-top: .5rem; }
.rx-table th, .rx-table td { text-align: left; padding: .45rem .3rem; border-bottom: 1px solid var(--border); }
.rx-table th { font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-soft); }
.sign { margin-top: 3rem; display: flex; justify-content: space-between; align-items: flex-end; font-size: .9rem; }
.sign .line { border-top: 1px solid var(--ink-700); padding-top: .3rem; min-width: 210px; text-align: center; }

/* ------------------------------------------------------------- photos -- */

/*
 * The frame owns the shape, not the photograph. Each frame declares its own
 * aspect-ratio and the picture inside is cropped to fill it, so a portrait
 * phone snapshot and a landscape studio shot sit in the same grid without
 * either being squashed - and the row is already the right height before a
 * single image byte arrives.
 */
.photo-frame {
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--brand-50);
  box-shadow: var(--shadow-sm);
}
.photo-frame picture { display: block; height: 100%; }
.photo-frame img { display: block; width: 100%; height: 100%; object-fit: cover; }

/*
 * The frame while it waits for a photograph the clinic has still to send.
 * Deliberately a drawing and deliberately plain: it should read as a gap
 * somebody is going to fill, not as a picture of the building.
 */
.photo-pending {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .6rem; width: 100%; height: 100%; padding: 1rem;
  color: var(--brand-600);
  background:
    repeating-linear-gradient(135deg,
      transparent 0 10px,
      rgba(255, 255, 255, .5) 10px 20px);
}
.photo-pending svg { width: 46px; height: 46px; flex: none; opacity: .8; }
.photo-pending span {
  font-size: .82rem; font-weight: 600; text-align: center;
  color: var(--text-soft); max-width: 22ch; line-height: 1.35;
}

.photo-4x3 { aspect-ratio: 4 / 3; }
.photo-3x2 { aspect-ratio: 3 / 2; }
.photo-1x1 { aspect-ratio: 1 / 1; }

/* The doctor's portrait, beside their name. Explicitly a block: as a flex
   item it would size itself, but in ordinary flow an inline <span> ignores
   width and height and the picture spills across the card. */
.portrait {
  display: block;
  width: 92px; height: 92px; flex: none;
  border-radius: 50%; overflow: hidden;
  background: var(--brand-50);
  box-shadow: 0 0 0 3px var(--surface), 0 0 0 5px var(--brand-100);
}
.portrait picture { display: block; height: 100%; }
.portrait img { display: block; width: 100%; height: 100%; object-fit: cover; }
.portrait-lg { width: 124px; height: 124px; }

.figure { margin: 0; }
.figure figcaption {
  margin-top: .65rem;
  font-size: .84rem;
  line-height: 1.45;
  color: var(--text-soft);
}

/* A service card with a photograph: text first, picture alongside. Below
   820px the picture drops under the text rather than being squeezed. */
.service-split { display: grid; gap: 1.75rem; grid-template-columns: minmax(0, 1fr) 320px; align-items: start; }
@media (max-width: 820px) { .service-split { grid-template-columns: minmax(0, 1fr); } }

/* Conference and award photographs: square tiles, uniform whatever the
   shape of the original. */
/*
 * A photograph that opens full size.
 *
 * It is a <button>, so the first job is to undo everything a button brings
 * with it and leave the frame looking exactly like the ones that are not
 * clickable. The magnifier in the corner is the only thing that says it is.
 */
.photo-zoom {
  position: relative; display: block; width: 100%; padding: 0;
  font: inherit; color: inherit; cursor: zoom-in; appearance: none;
}
.photo-zoom-hint {
  position: absolute; right: .5rem; bottom: .5rem;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: rgba(6, 23, 45, .62); color: #fff;
  opacity: 0; transform: translateY(4px);
  transition: opacity .18s ease, transform .18s ease;
}
.photo-zoom-hint svg { width: 15px; height: 15px; }
.photo-zoom:hover .photo-zoom-hint,
.photo-zoom:focus-visible .photo-zoom-hint { opacity: 1; transform: none; }
/* Touch has no hover to reveal it with, so it is simply always there. */
@media (hover: none) {
  .photo-zoom-hint { opacity: 1; transform: none; }
}

.lightbox {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
}
.lightbox[hidden] { display: none; }
.lightbox-shade { position: absolute; inset: 0; background: rgba(4, 14, 30, .93); }
.lightbox-panel {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: min(100%, 1100px); max-height: 100%;
}
.lightbox-figure { margin: 0; display: flex; flex-direction: column; gap: .75rem; }
.lightbox-figure picture { display: block; }
.lightbox-figure img {
  display: block; max-width: 100%;
  /* Room for the caption and the close button, whatever the screen. */
  max-height: calc(100vh - 8rem); max-height: calc(100dvh - 8rem);
  width: auto; height: auto; margin-inline: auto;
  border-radius: var(--radius); background: #0b1b33;
}
.lightbox-figure figcaption {
  display: flex; flex-wrap: wrap; gap: .25rem 1rem; justify-content: center;
  text-align: center; color: rgba(255, 255, 255, .86);
  font-size: .88rem; line-height: 1.45;
}
.lightbox-count { color: rgba(255, 255, 255, .55); }

.lightbox-btn {
  position: absolute; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .28); background: rgba(10, 26, 48, .72);
  color: #fff; font-size: 1.25rem; line-height: 1; cursor: pointer;
  transition: background-color .15s, border-color .15s;
}
.lightbox-btn:hover { background: rgba(255, 255, 255, .2); border-color: #fff; }
.lightbox-btn[hidden] { display: none; }
.lightbox-close { top: -.25rem; right: -.25rem; }
.lightbox-prev { left: -.25rem; }
.lightbox-next { right: -.25rem; }

/* The page behind it holds still, the same way it does under the drawer. */
body.lightbox-open { overflow: hidden; }
/*
 * And the two floating controls go quiet, as they do under the drawer. The
 * overlay does cover them - a tap lands on the shade and closes it - but a
 * saturated green button showing through at twelve per cent still reads as
 * something you are meant to press.
 */
body.lightbox-open .wa-fab,
body.lightbox-open .action-bar { opacity: 0; pointer-events: none; }

@media (max-width: 640px) {
  /* The arrows come off the picture and sit under it: at this width they
     covered the faces at both edges of every photograph. */
  .lightbox-panel { flex-direction: column; }
  .lightbox-prev { left: calc(50% - 60px); }
  .lightbox-next { right: calc(50% - 60px); }
  .lightbox-prev, .lightbox-next { top: auto; bottom: -3.5rem; }
  .lightbox-figure img { max-height: calc(100dvh - 12rem); }
}

.gallery { display: grid; gap: 1.1rem; grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 960px) { .gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 560px) { .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ------------------------------------------------------------- motion -- */

/*
 * Motion is opt in from JavaScript. A one line inline script in <head> sets
 * .js-motion before first paint, and only then does anything start hidden.
 * With scripting off the site simply has no animation, which is the right
 * fallback for decoration - the alternative, hiding content and waiting for
 * a script that never arrives, is a blank page.
 *
 * Everything below animates opacity and transform and nothing else. Those
 * are the two properties the browser can hand straight to the compositor;
 * animating height, top or margin instead forces a layout on every frame,
 * which is what makes a phone stutter.
 */

/*
 * The reveal set. Scoped to .section so the hero is left out - it has its
 * own entrance below - and to .site-main so the admin and print layouts are
 * untouched.
 *
 * :where(), not :is(). :is() takes the specificity of its most specific
 * argument, and `.chip-row > li` carries a type selector, so the hiding rule
 * would outweigh the .is-in rule that reveals it and nothing would ever
 * appear. :where() contributes nothing, which leaves .is-in free to win on
 * the strength of one class.
 *
 * KEEP IN SYNC with REVEAL in app.js, which observes the same elements.
 */
.js-motion .site-main .section :where(
  .section-head, .card, .grid > *, .split > *, .figure,
  .branch-tile, .cta-panel, .stat-card, .journey-step,
  .viz-panel
) {
  opacity: 0;
  /* Twelve pixels, not twenty-two. Long travel on every block in the page
     is what makes a site feel like a slide deck arriving. */
  transform: translateY(12px);
  transition:
    opacity .45s cubic-bezier(.22, .9, .3, 1),
    transform .45s cubic-bezier(.22, .9, .3, 1);
  transition-delay: var(--reveal-delay, 0ms);
}
.js-motion .site-main .section .is-in {
  opacity: 1;
  transform: none;
}

/* Hero entrance. Pure CSS: it is above the fold, so there is nothing to
   wait for and no observer worth the bytes. `backwards` makes the start
   state apply during the delay rather than after it. */
@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@keyframes riseMedia {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.js-motion .hero .pill,
.js-motion .hero h1,
.js-motion .hero .lead,
.js-motion .hero .chip-row,
.js-motion .hero .btn-row,
.js-motion .hero .stat-row {
  animation: rise .55s cubic-bezier(.22, .9, .3, 1) backwards;
}
.js-motion .hero .pill      { animation-delay: .02s; }
.js-motion .hero h1         { animation-delay: .07s; }
.js-motion .hero .lead      { animation-delay: .13s; }
.js-motion .hero .chip-row  { animation-delay: .19s; }
.js-motion .hero .btn-row   { animation-delay: .25s; }
.js-motion .hero .stat-row  { animation-delay: .31s; }
.js-motion .hero-media {
  animation: riseMedia .7s cubic-bezier(.22, .9, .3, 1) .1s backwards;
}

/* Header: settles onto the page once you leave the top of it. */
.header-main { transition: box-shadow .28s ease, background-color .28s ease; }
.header-main .bar { transition: min-height .28s ease; }
.js-motion .site-header.scrolled .header-main {
  box-shadow: 0 12px 34px rgba(8, 32, 63, .42);
}
.js-motion .site-header.scrolled .header-main .bar { min-height: 62px; }

/* Hover detail. Each of these is a transform on one element, so they cost
   nothing and they are what makes the page feel answerable. */
.icon-badge { transition: border-color .25s ease, background-color .25s ease; }
.card-link:hover .icon-badge { border-color: var(--brand-300); background: var(--brand-100); }

.figure .photo-frame img,
.split-media .photo-frame img {
  transition: transform .7s cubic-bezier(.22, .9, .3, 1);
}
.figure:hover .photo-frame img,
.split-media:hover .photo-frame img { transform: scale(1.03); }

.branch-tile { transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease; }
.branch-tile:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: var(--brand-200); }

.footer-grid ul a { display: inline-block; transition: transform .2s ease, color .2s ease; }
.footer-grid ul a:hover { transform: translateX(4px); }

.site-nav a { transition: background-color .2s ease, color .2s ease; }
.strip-item, .header-phone { transition: color .2s ease, border-color .2s ease; }

/* Keyboard users get a ring that does not depend on any of the above. */
:focus-visible { outline: 3px solid var(--teal-400); outline-offset: 2px; }

/* ------------------------------------------------------------ detail -- */

::selection { background: var(--brand-100); color: var(--brand-900); }

/*
 * Figures line up in a column when they are tabular, and a phone number or
 * a time never re-flows as its digits change.
 */
.stat-value, .journey-stat, .rg-centre, .ch-tick,
.tl-when, .queue-token, td, th { font-variant-numeric: tabular-nums; }

/*
 * Underlines in running text sit clear of the descenders instead of
 * striking through them, which is the difference between a link a browser
 * drew and one somebody set.
 */
main p a:not(.btn):not(.chip), .prose a, .faq-item a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
}
main p a:not(.btn):not(.chip):hover { text-decoration-color: currentColor; }

/* Off screen, not display:none, and out of the tab order: the field exists
   for automated submissions to fill in, and for nobody else to meet. */
.hp-field {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}

/* Present for a screen reader and for the heading outline, absent to the
   eye. Not display:none, which takes it out of both. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ===================================================== paired actions === */

/*
 * "Book an appointment" and the phone number, side by side on one line at
 * every width including a 360px phone, where they used to stack.
 *
 * A grid rather than a flex row that wraps: the booking button gets half as
 * much width again as the number, both are told not to wrap, and the type
 * and padding step down together so the pair still fits rather than
 * overflowing. Applied by class, so the three-button rows elsewhere are left
 * alone.
 */
/*
 * The hero's three calls to action: book, then the number for each clinic.
 *
 * Each dial button carries two lines - the number, and which clinic it
 * reaches - because the hero used to show one unlabelled number, and the one
 * it showed was Tirupati's.
 */
/*
 * Booking takes the row and the two numbers share the one beneath it.
 *
 * All three abreast was tried first and does not fit: beside the lungs the
 * hero's text column is about 575px and the three need 762px, so one number
 * dropped onto a line by itself and the row read as a mistake. Shrinking
 * them to fit meant losing the line that says which clinic each number is,
 * which is the reason there are two of them.
 *
 * Capped, because the hero goes to one column on a tablet and a booking
 * button the better part of a metre wide is not a better booking button.
 */
.hero-actions {
  display: grid; grid-template-columns: 1fr 1fr; gap: .6rem;
  align-items: stretch; max-width: 34rem;
}
.hero-actions .btn-accent { grid-column: 1 / -1; }
.btn-go { width: 18px; height: 18px; opacity: .8; }
.btn-dial { text-align: left; gap: .65rem; padding-block: .7rem; }
.dial-text { display: flex; flex-direction: column; line-height: 1.2; }
.dial-number { font-size: 1.03rem; font-weight: 700; white-space: nowrap; }
.dial-label { font-size: .76rem; font-weight: 500; opacity: .72; }

@media (max-width: 760px) {
  .hero-actions { gap: .55rem; }
  .hero-actions .btn { padding: .7rem .75rem; font-size: .9rem; }
  .hero-actions .dial-number { font-size: .95rem; }
  .hero-actions .dial-label { font-size: .7rem; }
}

@media (max-width: 430px) {
  /* Narrower than this, two numbers side by side leave no room for the line
     saying which clinic each one is, and that line is the whole point. */
  .hero-actions { grid-template-columns: 1fr; }
}

.btn-row-pair { display: flex; flex-wrap: wrap; gap: .7rem; }

@media (max-width: 620px) {
  .btn-row-pair {
    display: grid; grid-template-columns: 1.5fr 1fr; gap: .5rem;
    align-items: stretch;
  }
  .btn-row-pair .btn {
    /*
     * The row stays one row; a long label wraps inside its own button
     * instead. Held to one line, "Book an appointment" next to "Find your
     * branch" simply ran out of the button at 320px.
     */
    white-space: normal; min-width: 0; text-wrap: balance;
    padding: .72rem .6rem; font-size: .86rem; gap: .35rem;
    line-height: 1.25;
  }
  /* A phone number is the exception: never broken across two lines. */
  .btn-row-pair .btn[href^="tel:"] { white-space: nowrap; }
  .btn-row-pair .btn svg { width: 16px; height: 16px; }
}

/* Narrower still: the icons go before the words do. */
@media (max-width: 400px) {
  .btn-row-pair .btn { font-size: .8rem; padding: .7rem .45rem; }
  .btn-row-pair .btn svg { display: none; }
}

/* ================================================= navigation submenu === */

/*
 * A submenu under Services that needs no script.
 *
 * It opens on hover and on keyboard focus anywhere inside it, so tabbing
 * through the bar reveals it exactly as pointing does. The parent is still a
 * plain link to /services, which is what makes it work on a touch screen,
 * where hovering does not exist: tapping goes to the page.
 */
.nav-item { position: relative; display: inline-flex; }
.nav-caret { width: 14px; height: 14px; margin-left: .2rem; opacity: .7; transition: transform .2s ease; }
.nav-item > a { display: inline-flex; align-items: center; }

.nav-menu {
  position: absolute; top: 100%; left: 50%; transform: translate(-50%, 6px);
  padding-top: .6rem; z-index: 5;
  opacity: 0; visibility: hidden;
  /*
   * visibility is deliberately not in the transition list. It keeps the
   * closed menu out of the tab order, which opacity alone would not, and
   * transitioning it makes the value descendants inherit arrive late - long
   * enough on a slow frame clock that the links were painted and still not
   * clickable. It flips at once; only the fade and the slide are animated.
   */
  transition: opacity .18s ease, transform .18s ease;
}
.nav-menu-inner {
  display: grid; grid-template-columns: repeat(2, minmax(190px, 1fr)); gap: 1.75rem;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); padding: 1.35rem 1.5rem;
}
.nav-item:hover .nav-menu,
.nav-item:focus-within .nav-menu {
  opacity: 1; visibility: visible; transform: translate(-50%, 0);
}
.nav-item:hover .nav-caret, .nav-item:focus-within .nav-caret { transform: rotate(180deg); }

/* Qualified with .nav-menu: .site-nav a is a class plus a type and beats a
   lone class, so the heading was inheriting the navy bar's white and
   printing white on a white panel. */
.nav-menu .nav-menu-head {
  display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .12em;
  font-weight: 700; color: var(--brand-600); margin-bottom: .7rem;
  padding: 0; border-radius: 0;
}
.nav-menu .nav-menu-head:hover { color: var(--brand-800); background: none; }
.nav-menu ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.nav-menu li a {
  display: block; padding: .38rem .5rem; margin-left: -.5rem;
  border-radius: 8px; font-size: .9rem; color: var(--ink-700);
}
.nav-menu li a:hover { background: var(--brand-50); color: var(--brand-800); text-decoration: none; }

/* On a phone the menu is a list inside the open drawer: no hover, no
   floating panel, no second tap to discover anything. */
@media (max-width: 1040px) {
  .nav-item { display: block; }
  .nav-caret { display: none; }
  .nav-menu {
    position: static; opacity: 1; visibility: visible; transform: none;
    padding-top: .35rem;
  }
  .nav-menu-inner {
    grid-template-columns: 1fr; gap: 1rem; background: none; border: 0;
    box-shadow: none; padding: 0 0 .6rem 1rem;
  }
  .nav-menu .nav-menu-head { color: var(--teal-300); margin-bottom: .35rem; }
  .nav-menu li a { color: rgba(255, 255, 255, .78); padding: .45rem .5rem; }
  .nav-menu li a:hover { background: rgba(255, 255, 255, .1); color: #fff; }
}

/* ====================================================== interior pages == */

/* Shorter than the home band: there is no photograph to balance here, and a
   reader who followed a link about their own symptom wants the page. */
.hero.hero-compact > .wrap:not(.hero-inner) { padding-block: 3.25rem 3.75rem; }

/* A landmark is a phrase, not a figure: it has to be allowed to wrap, which
   the nowrap on .stat-value exists to prevent. */
.stat-value-sm {
  font-size: clamp(1.05rem, .95rem + .35vw, 1.3rem);
  white-space: normal; line-height: 1.25; display: inline-block;
}

.crumbs { margin-bottom: 1.1rem; font-size: .82rem; }
.crumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: .45rem; margin: 0; padding: 0; }
.crumbs li + li::before { content: "/"; margin-right: .45rem; opacity: .4; }
.crumbs li[aria-current] { color: rgba(255, 255, 255, .65); }
.hero .crumbs a { color: rgba(255, 255, 255, .85); }
.hero .crumbs a:hover { color: #fff; }

.step-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.15rem; max-width: 54rem; }
.step-list li { display: flex; gap: 1rem; align-items: flex-start; }
.step-num {
  flex: none; width: 30px; height: 30px; border-radius: 8px;
  display: grid; place-items: center;
  background: var(--brand-50); border: 1px solid var(--brand-100); color: var(--brand-700);
  font-weight: 700; font-size: .85rem; font-variant-numeric: tabular-nums;
}
.step-list p { color: var(--text-soft); }

/* Said once, quietly, on a page about a disease: it is information, not an
   assessment of the person reading it. */
.scope-note {
  font-size: .84rem; color: var(--text-soft);
  border-left: 2px solid var(--border); padding-left: .9rem; margin-top: 1.75rem;
}

/* ======================================================== next step ===== */

.next-step {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; grid-template-columns: 1.45fr 1fr; gap: 2.25rem;
  background: var(--grad-deep); color: #fff;
  border-radius: var(--radius-lg); padding: 2.6rem 2.35rem;
}
.next-step::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background-image: radial-gradient(circle, rgba(255, 255, 255, .07) 1px, transparent 1.4px);
  background-size: 24px 24px;
}
.next-step h2 { color: #fff; margin-bottom: .6rem; }
.next-step p { color: rgba(255, 255, 255, .78); max-width: 34rem; }
.ns-branches { display: grid; gap: .7rem; align-content: center; }
.ns-branch {
  display: grid; gap: .1rem;
  border: 1px solid rgba(255, 255, 255, .16); border-radius: var(--radius);
  padding: .8rem 1rem;
}
.ns-branch-name { font-weight: 700; font-size: .95rem; }
.ns-branch-hours { font-size: .8rem; color: rgba(255, 255, 255, .7); }
.ns-branch-phone { font-weight: 600; color: var(--teal-200); font-variant-numeric: tabular-nums; }
.ns-branch-phone:hover { color: #fff; }
@media (max-width: 860px) {
  .next-step { grid-template-columns: 1fr; padding: 2rem 1.5rem; gap: 1.75rem; }
}

/* ===================================================== action bar ======= */

/*
 * The two things a phone visitor wants, where a thumb is.
 *
 * Only on small screens, never on the booking page - it would sit on top of
 * the form it points at - and the body gains matching bottom padding so it
 * cannot cover the last line of the footer.
 */
.action-bar { display: none; }

@media (max-width: 720px) {
  .action-bar {
    display: grid; grid-template-columns: 1.6fr 1fr; gap: .6rem;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 65;
    padding: .7rem .9rem calc(.7rem + env(safe-area-inset-bottom));
    background: rgba(255, 255, 255, .94);
    border-top: 1px solid var(--border);
    box-shadow: 0 -6px 24px rgba(11, 21, 36, .1);
  }
  .action-bar .btn { padding: .72rem 1rem; font-size: .92rem; }
  .page-book .action-bar, .page-booking .action-bar { display: none; }

  /* Lift the floating button clear of the bar, and let the footer run under
     it rather than padding the body - padding the body would put a white
     strip below a navy footer. */
  body:not(.page-book):not(.page-booking) .site-footer { padding-bottom: 68px; }
  body:not(.page-book):not(.page-booking) .wa-fab { bottom: 88px; }
}

/* ================================================== advanced motion ===== */

/*
 * Scroll-driven animation, where the browser supports it.
 *
 * These run on the compositor against the scroll position itself rather than
 * on a scroll listener, so they cost nothing per frame and cannot judder.
 * Everything is inside @supports: a browser without the timeline simply
 * shows the finished state, which is the page as it is.
 */
@supports (animation-timeline: view()) {
  .js-motion .hero-viz .lungviz {
    animation: heroDrift linear both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
  @keyframes heroDrift {
    from { transform: translateY(0); opacity: 1; }
    to   { transform: translateY(-38px); opacity: .35; }
  }

  /* A photograph settles to its true size as it comes up the screen. */
  .js-motion .split-media .photo-frame img,
  .js-motion .figure .photo-frame img {
    animation: photoSettle linear both;
    animation-timeline: view();
    animation-range: entry 10% cover 40%;
  }
  @keyframes photoSettle {
    from { transform: scale(1.07); }
    to   { transform: scale(1); }
  }
}

@supports (animation-timeline: scroll()) {
  /*
   * How far through the page you are, drawn on the bottom edge of the
   * header so it reads as part of it rather than as a stripe stuck on top
   * of the site.
   */
  .js-motion .read-progress {
    position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
    transform-origin: 0 50%; transform: scaleX(0);
    background: linear-gradient(90deg, var(--teal-400), var(--leaf-400));
    animation: readProgress linear;
    animation-timeline: scroll(root block);
  }
  @keyframes readProgress { to { transform: scaleX(1); } }
}

/*
 * Cross-document view transitions: one page cross-fades into the next
 * instead of blinking white. Chrome and Edge honour it today and everything
 * else ignores the rule entirely.
 */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
  .js-motion .read-progress,
  .js-motion .hero-viz .lungviz,
  .js-motion .split-media .photo-frame img,
  .js-motion .figure .photo-frame img { animation: none !important; }
}

/* ============================================== maps ==================== */

/*
 * A map in a fixed frame with the address beneath it. An aspect ratio rather
 * than a height, so the two sit level with each other whatever the column
 * width, and a border because a Google frame has none of its own and floats
 * otherwise.
 */
.map-frame {
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; background: var(--ink-100);
  aspect-ratio: 16 / 10;
}
.map-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
@media (max-width: 620px) { .map-frame { aspect-ratio: 4 / 3; } }

.map-caption {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-top: .9rem;
}
.map-caption h3 { font-size: 1.05rem; margin-bottom: .1rem; }
.map-caption .btn { flex: none; }
@media (max-width: 480px) {
  .map-caption { flex-direction: column; align-items: flex-start; gap: .7rem; }
}

/* ============================================ visiting consultants ====== */

/*
 * A timetable, so it is set as rows with rules between them rather than as
 * another grid of cards. Hospital and town on the left, the pattern of dates
 * on the right, the way a printed schedule reads.
 */
.visit-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.visit-row {
  display: grid; grid-template-columns: 1.5fr 1fr; gap: .5rem 1.5rem;
  align-items: center; padding: 1.15rem 0;
  border-bottom: 1px solid var(--border);
}
.visit-row h3 { margin-bottom: .2rem; font-size: 1.05rem; }
.visit-place { display: inline-flex; align-items: center; gap: .4rem; font-size: .9rem; color: var(--text-soft); }
.visit-place svg { width: 15px; height: 15px; flex: none; }
.visit-when { text-align: right; }
.visit-day { display: block; font-weight: 700; color: var(--brand-800); }
.visit-note { display: block; font-size: .84rem; color: var(--text-soft); }
@media (max-width: 620px) {
  .visit-row { grid-template-columns: 1fr; }
  .visit-when { text-align: left; }
}

/* The one-line version under the branch strip on the home page. */
.visit-line {
  display: flex; gap: .7rem; align-items: flex-start;
  margin: 1.5rem 0 0; font-size: .92rem; color: var(--text-soft);
}
.visit-line svg { width: 18px; height: 18px; flex: none; margin-top: .25rem; color: var(--brand-600); }
.visit-line strong { color: var(--ink-900); font-weight: 600; }

/* ==================================================== lung network ===== */

/*
 * The breathing lungs.
 *
 * Every loop below animates opacity or transform, which the compositor can
 * do without repainting, and none of them run until JavaScript has put
 * .viz-run on the wrapper - so a graphic that is three screens down costs
 * nothing until it is looked at. The static drawing is the fallback: with no
 * script, no motion preference or an old browser, it is simply a diagram.
 */
/*
 * The hero graphic has the right-hand column to itself.
 *
 * It was first drawn behind the doctor's photograph, which turned out to
 * hide all of it: the lungs occupy the middle half of their own frame, so
 * a picture over the middle covers the lot however far the drawing is
 * scaled up. Given the column, it is legible at a glance and the
 * photograph keeps a full-size showing further down the page.
 */
.hero-viz { position: relative; width: 100%; pointer-events: none; }
.hero-viz .lungviz { width: 100%; }
@media (max-width: 1040px) { .hero-viz .lungviz { width: min(420px, 82vw); margin: 0 auto; } }

/*
 * Interior pages get the same mark, quieter: enough to tie the pages
 * together, never enough to compete with the heading in front of it.
 *
 * Out of the flow and sized from the band's height rather than its width,
 * because these headers are short and wide - scaled by width the drawing
 * would be twice the height of the band it sits in.
 */
.hero-viz-edge {
  position: absolute; top: 0; bottom: 0; right: 3%; left: auto;
  width: auto; z-index: -1; display: flex; align-items: center;
}
.hero-viz-edge .lungviz { height: 116%; width: auto; opacity: .3; }
@media (max-width: 860px) { .hero-viz-edge { display: none; } }

/* Clipped, not visible: the halo is drawn wider than the frame, and an SVG
   that paints outside its box still counts towards the width of the page -
   it was pushing a 320px screen two pixels sideways. */
.lungviz { display: block; height: auto; overflow: hidden; }
.lv-shell {
  fill: rgba(125, 211, 252, .07);
  stroke: rgba(186, 246, 255, .72); stroke-width: 1.6;
}
/* A wide, faint stroke under the outline. Cheaper than a blur filter and it
   is what makes the pair read as lungs rather than as loose curves. */
.lv-shell-glow { fill: none; stroke: rgba(103, 232, 249, .16); stroke-width: 9; }
.lv-air {
  fill: none; stroke: url(#lv-air); stroke-width: 3.2;
  stroke-linecap: round; opacity: .5;
}
.lv-pulse {
  fill: none; stroke: #ecfeff; stroke-width: 3.2; stroke-linecap: round;
  stroke-dasharray: 16 84; stroke-dashoffset: 100; opacity: 0;
}
.lv-edge { stroke: url(#lv-edge); stroke-width: .9; opacity: .2; }
.lv-node { fill: #ecfeff; opacity: .5; }
.lv-signal { fill: #bbf7e0; opacity: 0; }
.lv-mote { fill: var(--teal-200); opacity: 0; }
.lv-halo { opacity: .45; transform-box: fill-box; transform-origin: center; }
.lv-body { transform-box: fill-box; transform-origin: center; }

@keyframes lvBreathe {
  0%, 100% { transform: scale(1); }
  45%      { transform: scale(1.038); }
}
@keyframes lvHalo {
  0%, 100% { transform: scale(1);    opacity: .38; }
  45%      { transform: scale(1.11); opacity: .66; }
}
/* A bright dash running down each airway, the way air actually arrives. */
@keyframes lvFlow {
  0%        { stroke-dashoffset: 100; opacity: 0; }
  12%       { opacity: .95; }
  85%       { opacity: .85; }
  100%      { stroke-dashoffset: 0; opacity: 0; }
}
@keyframes lvFire {
  0%, 100% { opacity: .34; }
  50%      { opacity: .88; }
}
@keyframes lvEdgeGlow {
  0%, 100% { opacity: .12; }
  50%      { opacity: .38; }
}
/* The signal crosses from one branch point to the next; --dx/--dy carry the
   distance, set on the element from the same node list that drew the mesh. */
@keyframes lvSignal {
  0%   { transform: translate(0, 0); opacity: 0; }
  12%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)); opacity: 0; }
}
@keyframes lvRise {
  0%   { transform: translateY(0); opacity: 0; }
  18%  { opacity: .7; }
  100% { transform: translateY(-210px); opacity: 0; }
}

/* Nearer a resting adult's rate than a cartoon's: twelve or so a minute. */
.js-motion .viz-run .lv-body  { animation: lvBreathe 6.4s ease-in-out infinite; }
.js-motion .viz-run .lv-halo  { animation: lvHalo 6.4s ease-in-out infinite; }
.js-motion .viz-run .lv-pulse {
  animation: lvFlow 3.6s cubic-bezier(.4, 0, .5, 1) infinite;
  animation-delay: calc(var(--i, 0) * 240ms);
}
.js-motion .viz-run .lv-node {
  animation: lvFire 3.4s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 210ms);
}
.js-motion .viz-run .lv-edge {
  animation: lvEdgeGlow 4.8s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 260ms);
}
.js-motion .viz-run .lv-signal {
  animation: lvSignal 2.6s cubic-bezier(.5, 0, .5, 1) infinite;
  animation-delay: calc(var(--i, 0) * 430ms);
}
.js-motion .viz-run .lv-mote {
  animation: lvRise 9s linear infinite;
  animation-delay: calc(var(--i, 0) * 1.4s);
}

/* Phones do the breathing and the airflow and leave the rest out. Same
   picture, a third of the animated elements, which is what keeps it off a
   mid-range phone's battery. */
@media (max-width: 720px) {
  .js-motion .viz-run .lv-signal,
  .js-motion .viz-run .lv-mote,
  .js-motion .viz-run .lv-edge { animation: none; }
}

/* ================================================ awareness sections ==== */

.counter { font-variant-numeric: tabular-nums; }

/* The four numbers. On the dark band, so they read as a statement rather
   than another row of cards. */
.stat-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.25rem; }
@media (max-width: 980px) { .stat-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .stat-cards { grid-template-columns: 1fr; } }

.stat-card {
  position: relative; overflow: hidden;
  padding: 1.6rem 1.45rem 1.35rem;
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .15);
  transition: transform .3s cubic-bezier(.22, .9, .3, 1), border-color .3s ease;
}
.stat-card:hover { transform: translateY(-4px); border-color: rgba(103, 232, 249, .5); }
.stat-card .icon-badge { margin-bottom: .9rem; }
/* Qualified with the card: .stat-card p below is a class plus a type, which
   would otherwise win and print the headline figure at body size. */
.stat-card .stat-figure {
  font-size: clamp(2rem, 1.45rem + 1.9vw, 2.7rem);
  font-weight: 700; line-height: 1; letter-spacing: -.035em;
  color: #fff; font-variant-numeric: tabular-nums;
}
.stat-card h3 {
  font-size: .98rem; color: #fff; margin: .55rem 0 .45rem; font-weight: 700;
}
.stat-card p { font-size: .87rem; color: rgba(255, 255, 255, .72); margin-bottom: .9rem; }
/* Small uppercase type needs more contrast than body text, not less: at
   .45 alpha this was under 3:1 on the navy. */
.src-note {
  display: block; font-size: .68rem; letter-spacing: .08em; text-transform: uppercase;
  color: rgba(255, 255, 255, .66); font-weight: 600;
}
.section:not(.section-dark) .src-note { color: var(--ink-500); }

/* --- the journey of one breath ----------------------------------------- */

.journey { position: relative; display: grid; gap: 1.5rem 1.1rem; grid-template-columns: repeat(5, minmax(0, 1fr)); }
/*
 * The rail the steps hang from, with a light running along it.
 *
 * The light is a second background layer rather than a moving element: a dot
 * would have to be translated by a distance only the browser knows, and
 * sliding a gradient across a two-pixel strip costs nothing to paint.
 */
.journey::before {
  content: ""; position: absolute; left: 6%; right: 6%; top: 27px; height: 2px;
  opacity: .6;
  background:
    linear-gradient(90deg, transparent, rgba(255, 255, 255, .95), transparent) -30% 0 / 24% 100% no-repeat,
    linear-gradient(90deg, var(--brand-200), var(--teal-400), var(--leaf-400));
}
@keyframes railSweep {
  0%   { background-position: -30% 0, 0 0; }
  100% { background-position: 130% 0, 0 0; }
}
.js-motion .journey.viz-run::before { animation: railSweep 4.5s cubic-bezier(.45, 0, .55, 1) infinite; }

.journey-step { position: relative; text-align: center; }
.journey-num {
  width: 56px; height: 56px; margin: 0 auto .85rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--surface);
  border: 2px solid var(--brand-200); color: var(--brand-700);
  box-shadow: var(--shadow-sm);
  transition: transform .3s cubic-bezier(.22, .9, .3, 1), border-color .3s ease, color .3s ease;
}
.journey-num svg { width: 24px; height: 24px; }
.journey-step:hover .journey-num { transform: translateY(-4px) scale(1.06); border-color: var(--teal-400); color: var(--teal-600); }
.journey-stat {
  font-weight: 800; font-size: 1.25rem; color: var(--brand-700); line-height: 1.1;
}
.journey-unit { display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .09em; color: var(--text-soft); margin-bottom: .5rem; }
.journey-step h3 { font-size: 1rem; margin-bottom: .35rem; }
.journey-step p { font-size: .85rem; color: var(--text-soft); margin-bottom: 0; }

@media (max-width: 900px) {
  .journey { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .journey::before { display: none; }
}
@media (max-width: 520px) { .journey { grid-template-columns: 1fr; } }

/* --- charts ------------------------------------------------------------ */

.viz-panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.6rem; box-shadow: var(--shadow-sm);
}
.chart { display: block; width: 100%; height: auto; }
.ch-grid { stroke: var(--border); stroke-width: 1; }
.ch-tick { font-size: 11px; fill: var(--text-soft); text-anchor: end; }
.ch-tick-x { text-anchor: middle; }
.ch-axis { font-size: 11px; fill: var(--text-soft); text-anchor: end; font-weight: 600; }
.ch-line { fill: none; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.ch-never  { stroke: var(--leaf-500); }
.ch-quit   { stroke: var(--teal-500); }
.ch-smoker { stroke: var(--rose-600); }
.ch-dot { stroke: var(--surface); stroke-width: 2; }
.ch-dot.ch-never  { fill: var(--leaf-500); }
.ch-dot.ch-quit   { fill: var(--teal-500); }
.ch-dot.ch-smoker { fill: var(--rose-600); }

/* Drawn in, once, when the chart arrives on screen. */
.js-viz .chart-curve .ch-line { stroke-dasharray: 100; stroke-dashoffset: 100; }
.js-viz .chart-curve .ch-dot  { opacity: 0; }
@keyframes chDraw { to { stroke-dashoffset: 0; } }
@keyframes chDot  { to { opacity: 1; } }
.js-motion .viz-run .chart-curve .ch-line {
  animation: chDraw 1.5s cubic-bezier(.35, .1, .2, 1) forwards;
  animation-delay: calc(var(--i, 0) * 260ms);
}
.js-motion .viz-run .chart-curve .ch-dot {
  animation: chDot .4s ease forwards;
  animation-delay: calc(1.3s + var(--i, 0) * 260ms);
}

/* One column on anything narrow. This was an inline two-column grid, which
   kept both charts side by side at 320px and overflowed the screen. */
.chart-grid { display: grid; gap: 1.5rem; grid-template-columns: 1.5fr 1fr; align-items: start; }
@media (max-width: 900px) { .chart-grid { grid-template-columns: 1fr; } }

.chart-legend { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; margin-top: .75rem; }
.chart-legend span { display: inline-flex; align-items: center; gap: .45rem; font-size: .82rem; color: var(--text-soft); }
.chart-legend i { width: 14px; height: 4px; border-radius: 2px; flex: none; }
.legend-never  { background: var(--leaf-500); }
.legend-quit   { background: var(--teal-500); }
.legend-smoker { background: var(--rose-600); }

.chart-ring { max-width: 190px; margin: 0 auto; }
.rg-track { fill: none; stroke: var(--border); stroke-width: 13; }
.rg-value {
  fill: none; stroke: url(#rg-grad); stroke-width: 13; stroke-linecap: round;
  /* fill-box, so the quarter turn that puts zero at the top happens about
     the circle rather than the corner of the drawing. */
  transform: rotate(-90deg); transform-box: fill-box; transform-origin: center;
  stroke-dashoffset: var(--gap, 0);
}
.rg-centre {
  font-size: 34px; font-weight: 800; fill: var(--brand-800); text-anchor: middle;
}
.js-viz .chart-ring .rg-value { stroke-dashoffset: var(--full); }
@keyframes rgFill { to { stroke-dashoffset: var(--gap); } }
.js-motion .viz-run .chart-ring .rg-value { animation: rgFill 1.6s cubic-bezier(.35, .1, .2, 1) .2s forwards; }

/* --- what changes when you stop ---------------------------------------- */

.tl { position: relative; padding-left: 2.4rem; margin: 0; }
.tl::before {
  content: ""; position: absolute; left: 11px; top: 8px; bottom: 8px; width: 2px;
  background: linear-gradient(180deg, var(--teal-400), var(--leaf-500));
  transform-origin: top; border-radius: 2px;
}
.js-viz .tl::before { transform: scaleY(0); }
@keyframes tlGrow { to { transform: scaleY(1); } }
.js-motion .tl.viz-run::before { animation: tlGrow 1.4s cubic-bezier(.35, .1, .2, 1) forwards; }

.tl-item { position: relative; padding-bottom: 1.35rem; }
.tl-item:last-child { padding-bottom: 0; }
.tl-item::before {
  content: ""; position: absolute; left: -2.4rem; top: .35rem;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--teal-400);
  box-shadow: 0 0 0 4px rgba(34, 211, 238, .12);
}
.tl-when {
  display: block; font-weight: 800; color: var(--brand-800);
  font-size: .95rem; letter-spacing: -.01em;
}
.tl-item p { margin: .15rem 0 0; color: var(--text-soft); font-size: .92rem; }

/* --- symptoms worth measuring ------------------------------------------ */

.sign-card { position: relative; padding-left: 1.5rem; }
.sign-card::before {
  content: ""; position: absolute; left: 0; top: 1.6rem; bottom: 1.6rem; width: 3px;
  border-radius: 3px; background: var(--brand-600); opacity: .7;
  transform-origin: top; transition: transform .35s cubic-bezier(.22, .9, .3, 1);
}
.sign-card:hover::before { transform: scaleY(1.06); }

/* --- questions --------------------------------------------------------- */

.faq-list { display: grid; gap: .75rem; }
.faq-item {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 1.05rem 1.25rem;
  transition: border-color .25s ease, box-shadow .25s ease;
}
.faq-item[open] { border-color: var(--brand-200); box-shadow: var(--shadow-sm); }
.faq-item summary {
  display: flex; align-items: flex-start; gap: .75rem; cursor: pointer;
  font-weight: 650; color: var(--ink-900); list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: ""; flex: none; width: 10px; height: 10px; margin: .35rem 0 0 auto;
  border-right: 2px solid var(--brand-600); border-bottom: 2px solid var(--brand-600);
  transform: rotate(45deg); transition: transform .25s ease;
}
.faq-item[open] summary::after { transform: rotate(-135deg); }
.faq-item p { margin: .7rem 0 0; color: var(--text-soft); font-size: .93rem; }

@media print {
  .no-print, .site-header, .site-footer, .wa-fab, .admin-side { display: none !important; }
  body { background: #fff; }
  .admin-main { padding: 0; }
  .sheet { border: 0; margin: 0; padding: 0; max-width: none; box-shadow: none; }
  a[href]::after { content: ""; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }

  /*
   * Shortening the durations is not enough on its own. The reveal set starts
   * at opacity 0 and only becomes visible when the observer adds .is-in, so
   * a visitor who asks for no motion would otherwise still be waiting on
   * JavaScript to see the page. Everything starts visible and stays put.
   */
  .js-motion .site-main .section :where(
    .section-head, .card, .grid > *, .split > *, .figure,
    .branch-tile, .cta-panel, .stat-card, .journey-step,
    .viz-panel
  ),
  .js-motion .hero .pill,
  .js-motion .hero h1,
  .js-motion .hero .lead,
  .js-motion .hero .chip-row,
  .js-motion .hero .btn-row,
  .js-motion .hero .stat-row,
  .js-motion .hero-media {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }

  /* Ambient loops have no start state to restore; they just stop. */
  .js-motion .viz-run .lv-body,
  .js-motion .viz-run .lv-halo,
  .js-motion .viz-run .lv-node,
  .js-motion .viz-run .lv-edge,
  .js-motion .viz-run .lv-signal,
  .js-motion .viz-run .lv-mote,
  .js-motion .viz-run .lv-pulse,
  .js-motion .journey.viz-run::before { animation: none !important; }

  /*
   * The charts draw themselves in from a hidden state, so stopping the
   * animation is not enough - the finished state has to be the starting one,
   * or the graph would simply never appear.
   */
  .js-viz .chart-curve .ch-line { stroke-dashoffset: 0 !important; }
  .js-viz .chart-curve .ch-dot  { opacity: 1 !important; }
  .js-viz .chart-ring .rg-value { stroke-dashoffset: var(--gap) !important; }
  .js-viz .tl::before { transform: scaleY(1) !important; }
  .lv-pulse { opacity: 0 !important; }
}
