:root {
  /* Brand colours */
  --color-blue: #057bb6;
  --color-blue-deep: #04628f; /* link hover on light -- lime failed contrast here */
  --color-lime: #87c641;
  --color-text: #575757;
  --color-heading: #000;
  --color-white: #fff;
  --color-offwhite: #f7f7f7;

  /* Lime for text on mid-tone grounds: the hero scrims and the hero panel.
     The brand lime is reserved for fills and the near-black header, where it
     clears 4.5:1 on its own. Over the light hero photography it cannot reach
     4.5:1 at any overlay strength, so text uses this tint instead. */
  --color-lime-text: #c2e88f;

  /* Form status. The blue card is the constraint: pure white only reaches
     4.65:1 on it, so no tint of it can clear 4.5:1. Status colour therefore
     sits on its own near-white chip rather than directly on the card. */
  --surface-status: #fdfdfd;
  --color-status-error: #9a1c15;
  --color-status-success: #155c25;
  --color-status-warn: #7a4f01;

  /* Surfaces. Structure comes from tone, not heavy fills. */
  --surface-1: var(--color-white);
  --surface-2: var(--color-offwhite);
  --surface-3: var(--color-blue);
  /* Both bars are opaque. They were 94%/96% alpha, which let page content --
     photography, headings, the lime icons -- smear through the sticky bar and
     the open menu as you scrolled. Sitting on a near-black ground, the tint
     bought no depth and cost legibility on every page. */
  --surface-header: #161618;
  --surface-nav: #141416;
  --surface-hero-panel: rgba(59, 59, 59, 0.53);

  /* Backdrop for the open mobile menu. Not a WCAG-governed colour (it carries
     no text); 0.55 separates the panel from the page without flattening the
     page to black. */
  --surface-scrim: rgba(0, 0, 0, 0.55);

  /* Hairlines on dark and light surfaces */
  --border-on-dark: rgba(255, 255, 255, 0.45);
  --border-subtle: rgba(255, 255, 255, 0.15);
  --border-hairline: #e6e6e6;

  /* Boundary of a control sitting on the mid-tone blue card. WCAG 1.4.11 wants
     3:1 there and the field's own fill only reaches 1.37:1, so the outline has
     to carry the boundary. --border-on-dark is a hairline for the near-black
     header and is far too weak for this job. */
  --border-control: rgba(255, 255, 255, 0.75);

  /* Type */
  --font-sans: "Montserrat", system-ui, sans-serif;
  --font-display: "Philosopher", "Georgia", serif;

  /* Type scale, ~1.25. Upper steps stay restrained. */
  --text-xs: 0.8rem;
  --text-sm: 0.9rem;
  --text-base: 1rem;
  --text-md: 1.125rem;
  --text-lg: 1.25rem;
  --text-xl: 1.5rem;
  --text-2xl: 1.9rem;
  --text-3xl: 2.4rem;
  --text-4xl: 3rem;

  /* Spacing, 0.25rem grid */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.5rem;
  --space-8: 3rem;
  --space-9: 4rem;
  --space-10: 6rem;

  /* Layout */
  --max: 1200px;
  --section-pad: clamp(3rem, 6vw, 5rem);

  /* Prose measure. Montserrat 300 at 18px averages 9.32px per character, so
     40rem (640px) is ~69 characters per line, inside the 75-character ceiling.
     Deliberately NOT in ch: Montserrat's "0" is 0.658em (11.84px), 27% wider
     than an average character, so 68ch would render ~805px and ~86 characters.
     Also immune to ch's failure mode -- it silently changes meaning if a nested
     element alters font-size. */
  --measure: 40rem;
  --measure-narrow: 34rem;

  /* Interior rhythm. The homepage reaches --section-pad through .band; interior
     pages previously drew spacing from eight ad-hoc values across three
     sources. */
  --section-gap: clamp(2rem, 4vw, 3rem);
  --block-gap: var(--space-5);

  /* Media plate. --media-ratio is set by usage context, never here. */
  --media-ratio: 3 / 2;
  --media-focus: 50% 50%;

  /* Shape and depth */
  --radius-sm: 6px;
  --radius-md: 12px;
  --radius-pill: 999px;
  --radius-btn: var(--radius-pill);
  --shadow: 0 3px 11px rgba(0, 0, 0, 0.25); /* sticky header, dropdowns */
  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.05), 0 8px 24px rgba(0, 0, 0, 0.05);
  --transition: 180ms ease;
}

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

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: var(--text-md);
  line-height: 1.6;
  color: var(--color-text);
  background: var(--color-white);
  min-height: 100vh;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--color-blue);
}

a:hover {
  color: var(--color-blue-deep);
  text-decoration: underline;
}

/* One focus treatment site-wide. The white halo keeps the dark ring legible on
   the header, the blue panels and the lime legal bar alike. */
:focus-visible {
  outline: 2px solid var(--color-blue-deep);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.85);
}

h1,
h2,
h3 {
  font-weight: 700;
  line-height: 1.2;
  margin: 0 0 0.6em;
}

h1,
h2 {
  font-family: var(--font-display);
  color: var(--color-heading);
}

h1 {
  font-size: clamp(2rem, 5vw, var(--text-4xl));
}

h2 {
  font-size: clamp(1.5rem, 3vw, var(--text-2xl));
  line-height: 1.15;
}

/* Sub-headings are a different family, not just a smaller size. Rank reads as
   serif-large -> sans-small -> sans-light body, so it holds at every size.
   Size alone could not carry it: the h2:h3 ratio was 1.27, and h3 rendered at
   two different sizes with no rule deciding which. One size now, everywhere.
   Safe globally: no h3 exists outside the interior pages. */
h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--text-lg);
  line-height: 1.35;
  /* The deeper blue, not the brand blue: 20px semibold is NOT WCAG "large
     text", so this needs 4.5:1, and brand blue on the off-white content ground
     only reaches 4.34:1. Deep measures 6.23:1. #fafafa would still fall short
     at 4.46, so the ground cannot carry the brighter tone. */
  color: var(--color-blue-deep);
}

/* A heading belongs to what follows it, so it takes more space above than the
   0.6em below. Scoped to .content so the homepage's .value h2 -- which is the
   second child, after the icon -- never picks up a top margin. */
.content h2 {
  margin-top: var(--section-gap);
}

.content h2:first-child {
  margin-top: 0;
}

.content h3 {
  margin-top: var(--space-6);
}

.content h3:first-child {
  margin-top: 0;
}

p {
  margin: 0 0 1em;
}

ul {
  margin: 0 0 1em;
  padding-left: 1.2em;
}

li + li {
  margin-top: 0.35em;
}

/* This spacing is for vertical lists. In a flex row it becomes a defect: the
   margin pushes items 2..n down against item 1, so the breadcrumb read as a
   staircase and the primary nav sat ~3px out of line. Reset it in the two flex
   rows rather than weakening the global rule, which does real work in prose
   lists and in .submenu.

   The `>` in .nav-list > li + li is load-bearing: .nav-list CONTAINS the
   .submenu lists, and .submenu is vertical and depends on li + li for its item
   spacing. Without the direct-child combinator this would silently collapse the
   dropdown spacing. */
.crumbs li + li,
.nav-list > li + li {
  margin-top: 0;
}

.skip-link {
  position: absolute;
  left: 0.75rem;
  top: -3rem;
  z-index: 200;
  background: var(--color-blue);
  color: var(--color-white);
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius-sm);
}

.skip-link:focus {
  top: 0.75rem;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.wrap {
  width: min(100% - 2.5rem, var(--max));
  margin-inline: auto;
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--surface-header);
  box-shadow: var(--shadow);
  color: var(--color-white);
}

.header-inner {
  display: grid;
  grid-template-columns: minmax(160px, 220px) 1fr auto;
  align-items: start;
  /* 0.75rem, not 1rem: with the nav-item padding trim below this is what lets
     the seven desktop nav labels sit on one line at 1120px. Without it the
     header overflows horizontally on every width from 1025px to 1069px. */
  gap: 0.75rem;
  padding: 0.85rem 1.25rem;
}

.brand {
  color: var(--color-white);
  text-decoration: none;
  font-weight: 800;
  font-size: var(--text-base);
  line-height: 1.1;
  letter-spacing: 0.02em;
  min-width: 0; /* grid child: let the two-line lockup wrap rather than push */
}

.brand:hover {
  color: var(--color-lime);
}

.brand span {
  display: block;
}

.nav-toggle {
  display: none;
  background: rgba(255, 255, 255, 0.1);
  border: 0;
  color: var(--color-white);
  /* 48px to match the CTA pill it sits beside. */
  width: 48px;
  height: 48px;
  border-radius: var(--radius-md);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--transition);
}

.nav-toggle:hover {
  background: rgba(255, 255, 255, 0.18);
}

/* A dedicated class, not `.nav-toggle span`. The old descendant selector
   (0,1,1) outranked `.visually-hidden` (0,1,0) and so restyled the button's own
   screen-reader label into a second, phantom 18x2 bar. That extra in-flow child
   forced a second grid row, and the 44px box split into two 22px rows -- which
   pushed the real hamburger ~10px above the button's centre. Matching on a
   class the label does not carry removes the collision outright. */
.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  width: 22px;
  height: 3px;
  border-radius: 2px;
  background: var(--color-white);
  position: relative;
  margin: 0 auto;
  transition: transform var(--transition), background-color var(--transition);
}

.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
}

/* The three bar centres sit at -5.5, +1.5 and +8.5 on the element's 3px box, so
   the icon is symmetric about the box centre -- no optical nudge needed. The X
   collapses the outer two onto the middle one: they travel 7px. */
.nav-toggle__bars::before {
  top: -7px;
}

.nav-toggle__bars::after {
  top: 7px;
}

/* aria-expanded is the only state hook; the markup never changes on toggle. */
.nav-toggle[aria-expanded="true"] .nav-toggle__bars {
  background-color: transparent;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before {
  transform: translateY(7px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after {
  transform: translateY(-7px) rotate(-45deg);
}

/* Right-aligned so the links sit against the CTA rather than floating in the
   middle of the leftover column. The drawer overrides this to stretch. */
.site-nav {
  justify-self: end;
}

/* Inert at every width until the drawer opens (see the 1120px block). Declared
   here so a stale `body.nav-open` can never dim a desktop layout. */
.nav-scrim {
  display: none;
}

.nav-list {
  display: flex;
  align-items: start;
  gap: 0.15rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-list > li {
  position: relative;
}

.nav-list a,
.nav-list button.nav-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--color-white);
  text-decoration: none;
  font-weight: 700;
  font-size: var(--text-sm);
  letter-spacing: 0.03em;
  /* 0.6rem, not 0.85rem: 7 items x 8px saves the 56px that lets the desktop
     nav clear the viewport at 1120px instead of overflowing. The dropdown rows
     are unaffected -- `.submenu a` restates the full padding shorthand. */
  padding: 0.7rem 0.6rem;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: inherit;
}

.nav-list a:hover,
.nav-list button.nav-link:hover {
  color: var(--color-lime);
}

/* Current section / page. Marked at rest so you can tell where you are without
   pointing at it, and underlined so it reads differently from hover. */
.nav-list a[aria-current] {
  color: var(--color-lime);
  text-decoration: underline;
  text-underline-offset: 0.4rem;
  text-decoration-thickness: 2px;
}

.submenu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 17rem;
  background: #faf9f9;
  box-shadow: var(--shadow);
  list-style: none;
  margin: 0;
  padding: 0.35rem 0;
  z-index: 20;
}

/* Drawn, not the ▾ text glyph: Montserrat has no such character, so it fell
   back to a different font (and size) on every device. */
.nav-link__caret {
  display: inline-block;
  flex: none;
  width: 0.42em;
  height: 0.42em;
  border: solid currentColor;
  border-width: 0 2px 2px 0;
  transform: translateY(-0.15em) rotate(45deg);
}

.submenu a {
  color: #696868;
  font-weight: 500;
  font-size: var(--text-sm);
  display: block;
  padding: 0.7rem 1rem;
}

.submenu a:hover {
  background: #f0f6ea;
  color: var(--color-blue);
}

.btn {
  display: inline-flex;
  /* center, not start: below 720px the header CTA's content is 44.5px in a
     48px box, and start-alignment parked the label 3.5px above the pill's
     centre. Every other button's content already exceeds min-height, so it has
     no free space and renders identically either way. */
  align-items: center;
  justify-content: center;
  min-height: 48px; /* keeps the tap target above the 44px minimum */
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-btn);
  background: var(--color-blue);
  color: var(--color-white);
  font-weight: 700;
  font-size: var(--text-base);
  text-decoration: none;
  border: 0;
  cursor: pointer;
  font-family: inherit;
  text-align: center;
  transition: background-color var(--transition), color var(--transition);
}

.btn:hover {
  background: var(--color-lime);
  color: var(--color-heading);
  /* Stated rather than inherited. a:hover sets an underline at (0,1,1), which
     beats .btn's text-decoration:none at (0,1,0), so every button underlined
     its label on hover. Declaring it here is order-independent; lowering
     a:hover globally would instead tie .btn and win only by source order. */
  text-decoration: none;
}

/* The form disables the submit button while sending; without this the state
   was completely invisible. */
.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn:disabled:hover {
  background: var(--color-blue);
  color: var(--color-white);
}

.btn-header {
  min-width: 160px;
}

/* Outlined variant for buttons on the coloured panels (footer, contact card).
   Shares the pill radius with the filled buttons, so one control does not read
   as two different components. Scoped so the filled header CTA is unaffected. */
.site-footer .btn,
.contact-block .btn {
  background: transparent;
  border: 2px solid var(--color-white);
  border-radius: var(--radius-pill);
  color: var(--color-white);
}

.site-footer .btn:hover,
.contact-block .btn:hover {
  background: var(--color-white);
  color: var(--color-blue);
}

.hero {
  position: relative;
  min-height: 62vh;
  display: flex;
  align-items: stretch;
  color: var(--color-white);
  background:
    linear-gradient(rgba(39, 3, 118, 0.18), rgba(39, 3, 118, 0.18)),
    url("../images/hero-lymphedema-care.jpg") center / cover no-repeat;
  padding: 0;
}

.hero > .wrap {
  display: flex;
}

/* The shaded panel runs the full height of the hero section, with its
   content vertically centred -- matching the live site. */
.hero-panel {
  background: var(--surface-hero-panel);
  padding: 3.25rem 1.75rem 2.25rem;
  max-width: 28rem;
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.hero-title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: clamp(2.4rem, 6vw, 3.85rem);
  line-height: 0.88;
  color: var(--color-white);
  margin: 0 0 1rem;
}

.hero-title em {
  color: var(--color-lime-text);
  font-style: normal;
}

.hero-kicker {
  margin: 0;
  color: var(--color-white);
  font-weight: 500;
  font-size: var(--text-base);
}

/* Sections sit on the page's white ground; the contact block below carries the
   off-white tone that separates it from both the band above and the blue
   footer beneath. */
.band {
  background: var(--surface-1);
  padding-block: var(--section-pad);
}

.band-tight {
  padding-block: var(--space-7) var(--space-4);
}

.center {
  text-align: center;
}

.tagline {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 3vw, var(--text-3xl));
  /* Was inheriting body's 1.6 leading at display size. */
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--color-text);
  letter-spacing: 0.01em;
  margin: 0 auto;
  max-width: 75rem;
}

.values {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

.value {
  text-align: center;
  padding: 1.5rem 1.25rem;
}

.value svg,
.value img.icon {
  width: 66px;
  height: 66px;
  fill: var(--color-lime);
  color: var(--color-lime);
  margin: 0 auto 0.75rem;
}

.value h2 {
  font-family: var(--font-display);
  color: var(--color-blue);
  font-size: var(--text-xl);
  margin-bottom: 0.4rem;
}

.value p {
  margin: 0;
}

/* Section shares the light background of the band above it; the blue sits on
   the interior cards only. Inset to the same measure as the rest of the site
   (120px at 1440px), matching the live site's contact row. */
.contact-block {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr 1fr;
  min-height: 420px;
  background: var(--surface-2);
  color: var(--color-white);
  padding-inline: max(1.25rem, calc((100% - var(--max)) / 2));
  /* Without this the blue cards run flush into the band above and the blue
     footer below, reading as one continuous slab. */
  padding-block: var(--section-pad);
}

.contact-copy,
.contact-form-wrap {
  padding: 3rem 2rem;
  background: var(--color-blue);
}

/* The email addresses are long single tokens. Grid items default to
   min-width:auto, so without these the contact row overflows horizontally on
   narrow screens instead of shrinking. */
.contact-copy,
.contact-form-wrap,
.contact-photo {
  min-width: 0;
}

.contact-copy a,
.contact-meta a {
  overflow-wrap: anywhere;
}

.contact-copy h2 {
  font-size: var(--text-2xl);
  color: var(--color-white);
}

/* The contact page card carries no heading -- the page h1 already says
   "Contact Us" -- so the lead paragraph does the opening work instead. */
.contact-lead {
  font-size: var(--text-md);
  font-weight: 500;
}

.contact-copy p,
.contact-copy a {
  color: var(--color-white);
  font-weight: 500;
}

.contact-copy a:hover {
  color: var(--color-white);
  text-decoration-color: var(--color-lime);
  text-decoration-thickness: 2px;
}

.contact-meta {
  display: grid;
  gap: 0.65rem;
  margin: 1.5rem 0;
}

.contact-photo {
  background: #1a4a2a url("../images/leaf-texture.jpg") center / cover no-repeat;
  min-height: 280px;
}

.form-field {
  margin-bottom: var(--space-5);
}

/* Bot trap. A field no person can see or reach: moved off-screen rather than
   given display:none, because a naive bot skips what is explicitly hidden but
   will happily fill an input it believes is real. aria-hidden and tabindex="-1"
   (set in the markup) keep it out of the accessibility tree and the tab order,
   so it turns away bots without turning away the people the form exists for.
   A populated value means the submission is dropped -- see js/form.js. */
.form-field--hp {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-field label {
  display: block;
  color: var(--color-white);
  font-weight: 500;
  font-size: var(--text-sm);
  margin-bottom: var(--space-2);
}

/* Visible field surfaces rather than bare underlines -- clearer affordance
   against the blue card. The wash is dark, not light: a white wash over the
   blue card lifts the surface toward the white text and dropped the field's
   own text to 3.8:1 and its placeholder to 3.2:1. A dark wash recesses the
   field instead and clears both. */
.form-field input,
.form-field textarea {
  width: 100%;
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-sm);
  color: var(--color-white);
  font: inherit;
  padding: var(--space-3) var(--space-4);
  appearance: none;
  box-shadow: none;
}

.form-field input::placeholder,
.form-field textarea::placeholder {
  color: rgba(255, 255, 255, 0.85);
}

.form-field textarea {
  min-height: 110px;
  resize: vertical;
}

.form-field input:focus,
.form-field textarea:focus {
  border-color: var(--color-white);
  background: rgba(0, 0, 0, 0.28);
  outline: 2px solid var(--color-white);
  outline-offset: 2px;
}

.form-field input:-webkit-autofill,
.form-field input:-webkit-autofill:hover,
.form-field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--color-white);
  transition: background-color 9999s ease-out;
}

/* Status sits on its own chip. Inline on the card these were 3.4-3.9:1; the
   chip lets the semantic hues carry real saturation and gives the message a
   visible region instead of floating text. */
.field-error,
.form-status {
  font-size: var(--text-sm);
  margin: 0.5rem 0 0;
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius-sm);
  background: var(--surface-status);
  color: var(--color-status-error);
}

.form-status {
  font-weight: 500;
}

.form-status[data-form-success] {
  color: var(--color-status-success);
}

.form-status[data-form-error] {
  color: var(--color-status-warn);
}

/* The overlay has to carry the type on its own. Every hero photograph is a
   light warm image, so the previous 0.45 start left the white h1 at 2.5-3.1:1
   and the eyebrow near 1.5:1 -- lime cannot reach 4.5:1 over this gradient at
   any alpha. At 0.78 the white h1 measures 6.4-8.5:1 and the lime tint
   4.6-6.2:1 across all nine photographs. */
.page-hero {
  padding-block: var(--space-8) var(--space-6);
  color: var(--color-white);
  background:
    linear-gradient(rgba(10, 70, 104, 0.78), rgba(14, 42, 60, 0.88)),
    var(--page-hero-image) center / cover no-repeat;
}

.page-hero h1 {
  color: var(--color-white);
  margin-bottom: 0.35rem;
}

.eyebrow {
  text-transform: lowercase;
  letter-spacing: 0.08em;
  font-weight: 500;
  margin: 0 0 0.4rem;
  color: var(--color-lime-text);
}

.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
  padding: 0;
  margin: 0 0 1rem;
  font-size: var(--text-sm);
}

.crumbs a {
  color: var(--color-white);
}

.crumbs li:not(:last-child)::after {
  content: "/";
  margin-left: 0.4rem;
  opacity: 0.7;
}

/* The content band carries the prose. Its ground is the off-white tone and the
   white cards and media plates float on it -- #f7f7f7 against white is only
   1.09:1, so the tone by itself is invisible; the relationship is what reads. */
.content {
  background: var(--surface-2);
  padding-block: var(--section-pad);
}

/* Links on the tinted ground take the deeper blue for the same reason h3 does:
   brand blue measures 4.34:1 here, under the 4.5:1 normal-text floor. Scoped to
   .content, so the white-on-blue contact card and the dark hero are unaffected.

   The ancestor sits in :where() so this rule contributes ZERO specificity and
   can never outrank a component. It previously read `.content a` at (0,1,1),
   which beat `.btn` at (0,1,0) and painted every button label on the site
   #04628f on the #057bb6 fill -- 1.43:1. It also shadowed a:hover, killing the
   hover colour on interior links.

   CONVENTION: any rule whose job is "set a default across a region" goes in
   :where(). A contextual override must always lose to the component it wraps. */
:where(.content) a {
  color: var(--color-blue-deep);
}

/* Prose measure. Deliberately on .wrap's prose children rather than on .wrap
   itself: .wrap is also the hero's flex container and every 3-up grid, so a
   max-width there would reflow them. :where() keeps specificity at 0 so any
   component can still override. */
.content > .wrap > :where(p, ul, ol, h2, h3, blockquote, .media-frame, .related) {
  max-width: var(--measure);
}

/* A .split's text column. This does bind at desktop -- the track is 667px at a
   1440px viewport, so the measure trims 27px off it -- and it is what stops the
   text running to 984px once .split stacks. */
.content > .wrap.split > div > :where(p, ul, ol, h2, h3) {
  max-width: var(--measure);
}

/* The two bio text columns, which ran ~102 characters per line. */
.bio > div > :where(p, h2) {
  max-width: var(--measure);
}

.split {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: 2.5rem;
  align-items: start;
}

/* Media plate. A fixed ratio stops sibling pages rendering the same component
   as a 1600x2397 tower on one and a 1400x671 strip on another. --media-ratio
   comes from the usage context below, never from here. */
.media-frame {
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  aspect-ratio: var(--media-ratio);
}

.media-frame img {
  width: 100%;
  /* Required. aspect-ratio sizes the box, but an img left at height:auto
     overflows it and gets clipped from the top -- an arbitrary crop instead of
     a centred cover. This was the missing half of the inert object-fit rule. */
  height: 100%;
  object-fit: cover;
  object-position: var(--media-focus);
}

/* An image whose own content must not be cropped -- clinical diagrams, charts.
   Keeps the intrinsic ratio and its own white ground. */
.media-frame--fit {
  aspect-ratio: auto;
  background: var(--color-white);
}

.media-frame--fit img {
  height: auto;
  object-fit: contain;
}

/* Side column of a .split: portrait plate, matching the two sources that are
   already portrait and cropping the 492x740 one to fit. */
.split .media-frame {
  --media-ratio: 4 / 5;
}

/* Inline figure inside the long-form FAQ column. */
.faq .media-frame {
  --media-ratio: 16 / 9;
}

/* Wide figure band. A landscape image cannot sit in the measured prose column
   without either trailing the article or shrinking below a readable size, so it
   breaks out into a full-width band with the copy beside it. The three measure
   rules cap only .wrap's listed prose children, so this element is not capped
   and escapes the measure without needing an override.

   The copy track is deliberately narrow. The band's copy is one short sentence
   plus two buttons, so 26rem (~416px, ~47 characters) stays comfortable and
   leaves the image column ~760px. Splitting the width using the measured 640px
   copy track instead would leave the image only ~536px -- narrower than the
   640px it rendered at before, i.e. harder to read, not easier. */
.figure-band {
  display: grid;
  grid-template-columns: minmax(0, 26rem) minmax(0, 1fr);
  gap: var(--section-gap);
  align-items: start;
  margin-top: var(--section-gap);
}

.figure-band .media-frame {
  /* The band sets its own width; the FAQ column ratio would crop a wide source
     harder than it needs to. */
  --media-ratio: 3 / 2;
}

.bio {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 1.75rem;
  margin-bottom: var(--section-gap);
  align-items: start;
}

.bio img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  object-position: top;
}

.creds {
  display: inline-block;
  background: var(--color-blue);
  color: var(--color-white);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.35rem 0.7rem;
  margin: 0 0 0.8rem;
}

.hands {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 0;
  margin-top: var(--section-gap);
  background: var(--color-blue);
  color: var(--color-white);
}

.hands img {
  width: 100%;
  height: 100%;
  min-height: 280px;
  object-fit: cover;
}

.hands-copy {
  padding: 2.5rem 2rem;
}

.hands-copy h2,
.hands-copy p {
  color: var(--color-white);
}

/* The grid carries its own top gap so all three hub pages space identically.
   Previously only /services/ had one, via a one-off .card-section wrapper.
   Two columns, not three: the vascular card was withdrawn from all three hub
   pages, leaving two each. A third column would sit empty above 1024px. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.25rem;
  margin-top: var(--section-gap);
}

.card {
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  box-shadow: var(--shadow-card);
}

/* A card title and the aside title are h2 for outline reasons -- on a hub page
   the cards ARE the page's sections -- not because they are page sections. Both
   keep the previous 20px treatment, so the element swap is a pure semantic
   upgrade. Blue is reserved for h3 sub-headings, which is what makes the colour
   carry meaning rather than the wrapper. */
.card h2,
.related h2 {
  font-size: var(--text-lg);
}

.card a {
  font-weight: 700;
  text-decoration: none;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: var(--block-gap);
}

.coming-soon {
  text-align: center;
}

/* Capped and centred as a container, rather than left-aligned at the full
   measure with centred text sitting inside it. */
.wrap.coming-soon {
  max-width: var(--measure-narrow);
}

.coming-soon .cta-row {
  justify-content: center;
}

/* <ol> keeps the UA's 40px inset and .refs li added a second 4px on top, while
   the grid gap and the global li margin stacked to ~22px between items. One
   indent now, one gap. */
.refs {
  display: grid;
  gap: var(--space-4);
  padding-inline-start: 1.75rem;
}

.refs li + li {
  margin-top: 0;
}

.related {
  margin-top: var(--section-gap);
  padding-top: var(--block-gap);
  border-top: 1px solid var(--border-hairline);
}

.site-footer {
  background: var(--surface-3);
  color: var(--color-white);
  padding: var(--space-7) 0 0;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 2rem;
}

/* Same long-token problem as the contact row: the footer email links set the
   grid's min-content width and push the page into horizontal scroll. */
.footer-grid > * {
  min-width: 0;
}

.footer-grid a {
  overflow-wrap: anywhere;
}

/* The quick links under the Online Scheduling button. */
.footer-links {
  margin-top: var(--space-4);
}

.site-footer h2,
.site-footer p,
.site-footer a {
  color: var(--color-white);
}

.site-footer h2 {
  font-size: var(--text-base);
  margin-bottom: 0.5rem;
}

.site-footer a:hover {
  color: var(--color-white);
  text-decoration-color: var(--color-lime);
  text-decoration-thickness: 2px;
}

.brand-footer {
  font-weight: 800;
  font-size: var(--text-lg);
  line-height: 1.1;
}

.social {
  display: flex;
  gap: 0.75rem;
  margin-top: 1rem;
}

.social a {
  width: 40px;
  height: 40px;
  border: 1px solid var(--border-control);
  border-radius: 50%;
  display: grid;
  place-items: center;
  text-decoration: none;
}

.legal {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 2rem;
  padding: 1rem 0;
  border-top: 1px solid var(--border-subtle);
  font-size: var(--text-xs);
  color: var(--color-heading);
  background: var(--color-lime);
}

.legal-inner {
  width: min(100% - 2.5rem, var(--max));
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.legal,
.legal a {
  color: var(--color-heading);
}

/* .site-footer p / .site-footer a outrank bare .legal, which was rendering the
   legal text white on the lime bar. Match the footer scope to win. */
.site-footer .legal p,
.site-footer .legal a {
  color: var(--color-heading);
}

/* ---------------------------------------------------------------------------
   HEADER / NAV BREAKPOINT: 1120px

   Deliberately NOT the layout breakpoint below. The header goes to the drawer
   at 1120px because seven labels need ~670px and the brand and CTA need a
   further 380px between them; below 1120 they stop sharing a line and the nav
   wraps to two, growing the bar from 77px to 96px. The layout grid keeps its
   1024px step-downs because those were tuned for the content columns, not the
   nav. Two numbers on purpose -- keep in sync with NAV_QUERY in js/main.js.
--------------------------------------------------------------------------- */
@media (max-width: 1120px) {
  .header-inner {
    grid-template-columns: 1fr auto auto;
    align-items: center; /* 2-line brand, 44px toggle and 48px CTA share a centre */
  }

  .nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* The wordmark is a link home, and two lines of 14.4px type is only ~32px
     tall -- a cramped target on the device most visitors arrive from.
     min-height, not padding: padding is added to the content, so at 320px the
     three-line lockup plus padding became the tallest thing in the row and
     pushed the bar from 75px to 87px. A floor cannot exceed the content, so the
     48px CTA still sets the row. Column flex keeps the two-line lockup centred
     in that floor rather than parked at its top. Scoped here so the desktop
     alignment above is untouched. */
  .brand {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 44px;
  }

  .site-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    /* The desktop rule is `justify-self: end`, which on an absolutely
       positioned grid child shrink-wraps it: the tray was 148px wide and
       floating mid-screen at 390px. stretch + full width makes it a tray. */
    justify-self: stretch;
    width: 100%;
    background: var(--surface-nav);
    padding: 0.25rem 0 1rem;
    /* A scroll container, not a sheet that grows past the fold. The percentage
       resolves against this element's containing block -- the sticky header's
       padding box -- so it reads as "viewport minus header" with no one having
       to measure the header. Without it the panel ran off a short viewport
       (landscape phones) while the body scroll was locked, so the lower half of
       the menu could not be reached at all. */
    max-height: calc(100vh - 100%);
    max-height: calc(100dvh - 100%);
    overflow-y: auto;
    overscroll-behavior: contain; /* panel scroll must not chain to the page */
    z-index: 2;                   /* above the scrim */
    box-shadow: var(--shadow);    /* the header's own shadow sits behind it */
  }

  .site-nav.is-open {
    display: block;
  }

  /* The toggle sets body.nav-open, but nothing styled it, so the page scrolled
     behind the open panel. Scoped here since the panel only exists below 1120. */
  body.nav-open {
    overflow: hidden;
  }

  body.nav-open .nav-scrim {
    display: block;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: 100vh;
    height: 100dvh;
    z-index: 1; /* below the panel (2), above the page */
    background: var(--surface-scrim);
  }

  .nav-list {
    flex-direction: column;
    align-items: stretch;
  }

  /* The anchors are inline-flex, so in a column they shrank to their own text
     and the current-page wash stopped mid-row, reading as a rendering fault.
     The split rows are already full width via `flex: 1` on their link. */
  .nav-list > li:not(.has-submenu) > a {
    display: flex;
  }

  /* Full-bleed rows: the hairlines and the current-page wash run edge to edge
     and the label lines up with the header's own 1.25rem gutter. */
  .nav-list a {
    padding: 0.95rem 1.25rem;
    font-size: 1.05rem;
  }

  /* Seven items read as one flat wall without these. They also anchor the row's
     disclosure chevron, which otherwise floats at the right edge with nothing
     tying it to its label. Direct children only -- the submenu's rows must not
     inherit a hairline. */
  .nav-list > li + li {
    border-top: 1px solid var(--border-subtle);
  }

  /* Split row: the label navigates to the section hub, the chevron beside it
     expands the submenu. Both are real controls, so the row is a two-column
     flex -- with the submenu wrapping to its own full-width line. */
  .has-submenu {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch; /* the chevron button matches the label's row height */
  }

  .has-submenu > a.nav-link {
    flex: 1 1 auto;
  }

  /* A visible control, not a glyph floating at the edge: the divider marks it as
     a separate target from the label beside it. */
  .submenu-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    font-size: 1.05rem;
    background: none;
    border: 0;
    border-left: 1px solid var(--border-subtle);
    cursor: pointer;
    color: var(--color-white);
    font-family: inherit;
    -webkit-tap-highlight-color: transparent;
  }

  .submenu-toggle:active {
    background: rgba(255, 255, 255, 0.08);
  }

  .has-submenu > .submenu {
    flex-basis: 100%;
  }

  /* The caret moves out of the link and into the button, so each row carries
     exactly one. */
  .has-submenu .nav-link__caret {
    display: none;
  }

  .submenu-toggle .nav-link__caret {
    display: inline-block;
    transition: transform var(--transition);
  }

  .has-submenu.is-open .submenu-toggle .nav-link__caret {
    transform: translateY(0.15em) rotate(-135deg);
  }

  .submenu {
    position: static;
    display: none;
    background: transparent;
    box-shadow: none;
    padding: 0 0 0.75rem 1.25rem;
  }

  .has-submenu.is-open > .submenu {
    display: block;
  }

  /* li + li spaces the desktop dropdown; here it would break the rule below. */
  .submenu li + li {
    margin-top: 0;
  }

  .submenu a {
    color: var(--color-white);
    padding: 0.8rem 1rem;
    font-size: 1rem;
    border-left: 2px solid var(--border-subtle);
  }

  /* A vertical list wants a vertical "you are here". The desktop underline
     reads as a stray rule under one word in a column, so the current page takes
     a lime wash across the whole row instead, while its section link keeps lime
     text alone -- on the nine third-level pages both are marked, and identical
     treatment would make them indistinguishable. */
  .nav-list a[aria-current] {
    text-decoration: none;
  }

  .nav-list a[aria-current="page"] {
    background: rgba(135, 198, 65, 0.14);
  }
}

/* Above the drawer the disclosure button must disappear, or every dropdown
   parent renders two carets -- the link keeps its own for desktop. */
@media (min-width: 1121px) {
  .submenu-toggle {
    display: none;
  }

  /* Hover reveal lives here only. Unscoped, a touch tap applied :hover in the
     drawer, the panel reflowed, and the click landed on the label link instead
     of the chevron. */
  .has-submenu:hover > .submenu,
  .has-submenu:focus-within > .submenu {
    display: block;
  }

  /* The nav stretches the full bar height so each item's `top: 100%` is the
     bar's bottom edge. Before, the items were top-aligned and 41px tall in a
     77px bar, so the dropdown opened 18px inside the bar. The vertical padding
     moves from the container to the brand and CTA so the bar height is
     unchanged. */
  .header-inner {
    align-items: stretch;
    padding-block: 0;
  }

  .brand {
    align-self: center;
    padding-block: 0.85rem;
  }

  .btn-header {
    align-self: center;
    margin-block: 0.85rem;
  }

  .site-nav {
    align-self: stretch;
    display: flex;
  }

  .nav-list {
    align-items: stretch;
  }

  .nav-list > li {
    display: flex;
    align-items: center;
  }
}

@media (max-width: 1024px) {

  /* 3-up grids step down to 2-up here rather than dropping straight to a
     single full-width column, which made the jump from desktop abrupt. */
  .values,
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  /* .bio is deliberately absent: it steps down to 160px + 1fr rather than
     stacking, and only collapses at 720px. Listing it here was dead code -- the
     later .bio rule shadowed it at equal specificity. */
  .contact-block,
  .split,
  .footer-grid,
  .hands {
    grid-template-columns: 1fr;
  }

  /* .split has stacked to one column. Without this the plate goes full width and
     services-care.jpg -- 492px wide -- is upscaled 2x. 544px caps it at 1.11x
     while the 1600px sources stay sharp. */
  .split .media-frame {
    max-width: var(--measure-narrow);
  }

  /* The figure band stacks: copy and CTA first, diagram below, so the action
     stays reachable without scrolling past a tall chart. Source order already
     produces that, so no `order` override is needed -- the desktop grid places
     the same order into two columns (copy left, image right) and the narrow
     layout stacks it. */
  .figure-band {
    grid-template-columns: 1fr;
  }

  /* Stacked, the blue cards would touch and recreate the bleed vertically.
     The decorative texture band has nothing to decorate at this size. */
  .contact-block {
    gap: 1.5rem;
  }

  .contact-photo {
    display: none;
  }

  .bio {
    grid-template-columns: 160px 1fr;
  }
}

@media (max-width: 720px) {
  /* Completes the 3-up -> 2-up -> 1-up progression from the 1024px breakpoint. */
  .values,
  .card-grid {
    grid-template-columns: 1fr;
  }

  /* The portrait column and the text column can no longer both fit at this
     width, so the grid was pushing the page into horizontal scroll. */
  .bio {
    grid-template-columns: 1fr;
    gap: 1.25rem;
  }

  .bio img {
    max-width: 220px;
  }

  .hero {
    min-height: 52vh;
    padding-top: 1.25rem;
  }

  .hero-panel {
    margin: 2rem 0 0;
    padding: 2.25rem 1.25rem;
    max-width: none;
  }

  .page-hero {
    padding: 2rem 0 2.25rem;
  }

  .contact-copy,
  .contact-form-wrap,
  .hands-copy {
    padding: 2rem 1.25rem;
  }

  /* padding-inline only: the block padding stays 0.85rem from the 1120px rule,
     which is what keeps the bar at one height across every phone width. Setting
     the shorthand here is what made the bar 70px below 720 and 75px above. */
  .header-inner {
    padding-inline: 0.7rem;
    column-gap: 0.5rem;
  }

  /* 14.4px, not 16px. "LYMPHATIC HEALTH" needs ~172px and the brand column is
     only ~155px at 375, so the lockup broke across three ragged lines on every
     current iPhone. At this size it holds two. It also drops the column's
     min-content width enough that the CTA stops wrapping at 320px. Do NOT add
     white-space: nowrap to compensate at 320 -- the wrap is the graceful
     failure; nowrap converts it into horizontal page scroll. */
  .brand {
    font-size: var(--text-sm);
  }

  .btn-header {
    min-width: 0;
    padding-inline: 0.6rem;
    font-size: var(--text-xs);
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  /* The hamburger's X and the caret's flip are state, not decoration, so they
     still happen -- only the 180ms interpolations are removed. */
  .nav-toggle__bars,
  .nav-toggle__bars::before,
  .nav-toggle__bars::after,
  .submenu-toggle .nav-link__caret {
    transition: none;
  }
}
