/* ==========================================================================
   Your Lifestyle Physician — Design System
   Petrol & Bronze · Source Serif 4 (display) / Inter (body) / DM Mono (accent)

   TYPE SCALE — the single source of truth for every text role on the site.
   Every heading/subtitle/label/body element on every page must use one of
   the role classes below (.hero-heading, .page-title, .section-subheading,
   .subtitle, .eyebrow, .lede) rather than a bare tag or a page-specific
   font-size/font-family rule. Two different roles landing on the same
   rendered size (page titles and section subheadings were once
   indistinguishable) is exactly the drift this file exists to prevent —
   if a new page needs a text style not covered here, add a new role class
   to this section rather than styling it in place on that page.
   ========================================================================== */

:root {
  --petrol: #153B3B;
  --bronze: #A9713D;
  --charcoal: #2B2B2B;
  --grey: #6B6B6B;
  --cream: #EDEAE1;
  --white: #FFFFFF;

  --font-display: 'Source Serif 4', serif;
  --font-body: 'Inter', sans-serif;
  --font-accent: 'DM Mono', monospace;

  --max-width: 1140px;
  --gutter: 1.5rem;

  --space-xs: 0.5rem;
  --space-sm: 1rem;
  --space-md: 2rem;
  --space-lg: 4rem;
  --space-xl: 6.5rem;
}

/* ---- Reset ---- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body, h1, h2, h3, h4, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

body {
  font-family: var(--font-body);
  color: var(--charcoal);
  background: var(--white);
  line-height: 1.6;
  font-size: 1.0625rem;
  -webkit-font-smoothing: antialiased;
}

/* ---- Typography ----
   h1–h4 carry only shared defaults (colour, weight, line-height, balance) —
   deliberately NO font-size here. A bare heading with no role class is a
   sign a class is missing, not something that should silently render at a
   plausible-looking fallback size — that silence is exactly how page
   titles and section subheadings drifted to the same size before. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--petrol);
  font-weight: 600;
  line-height: 1.2;
  text-wrap: balance;
}

p { max-width: 62ch; }
p + p { margin-top: 1em; }

/* Left-aligned always — never centred or justified (Visual Identity §11) */
h1, h2, h3, h4, p, li { text-align: left; }

/* Hero heading — homepage only ("One physician. Every phase of your
   health."). The single largest text on the site; nothing else uses it. */
.hero-heading {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--petrol);
  line-height: 1.15;
  font-size: clamp(2.25rem, 4vw + 1rem, 3.5rem); /* 36–56px */
}

/* Page title — every page's main <h1> (About Me, The Six Pillars, Frequently
   Asked Questions...). One size, identical everywhere, noticeably smaller
   than the hero heading and noticeably larger than a section subheading. */
.page-title {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--petrol);
  line-height: 1.2;
  font-size: clamp(1.875rem, 2.2vw + 1.2rem, 2.5rem); /* 30–40px */
}

/* Section subheading — Credentials, Why This Guide Exists, Pillar One,
   the homepage's teaser headings, etc. Deliberately non-overlapping with
   .page-title (max 28px here vs. min 30px there) so the two can never be
   mistaken for each other regardless of viewport. */
.section-subheading {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--petrol);
  line-height: 1.25;
  font-size: clamp(1.375rem, 1.4vw + 1rem, 1.75rem); /* 22–28px */
}

/* Subtitle / deck line — sits directly under a page title. */
.subtitle {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.25rem; /* 20px — matches .lede's body-copy size */
  color: var(--charcoal);
}

/* Eyebrow label — small caps-style label above a page title ("WRITING"). */
.eyebrow {
  font-family: var(--font-accent);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.8rem;
  color: var(--petrol);
}

/* Body copy — the one reading-content paragraph style, sitewide. One
   definition, no page-level overrides anywhere (verified) — every page
   using .lede picks up a change here automatically, including pages not
   yet built (Pricing, Contact). Apply this class directly to any paragraph
   of substantive body text — do not recreate its values with a
   page-specific rule. */
.lede {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 1.125rem; /* 18px — one step down from the previous 20px */
  line-height: 1.6;
  color: var(--charcoal);
}

/* ---- Layout ---- */
.container {
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: var(--space-xl);
}

.section--cream {
  background: var(--cream);
}

.section--petrol {
  background: var(--petrol);
  color: var(--white);
}
.section--petrol h1,
.section--petrol h2,
.section--petrol h3 { color: var(--white); }

.divider {
  border: none;
  border-top: 1px solid var(--bronze);
  width: 4rem;
  margin: var(--space-sm) 0 var(--space-md);
}

/* ---- Buttons / Links ---- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  padding: 0.85rem 1.75rem;
  border: 1px solid var(--petrol);
  border-radius: 2px;
  background: var(--petrol);
  color: var(--white);
  transition: background 0.15s ease, color 0.15s ease;
}
.btn:hover, .btn:focus-visible { background: #0e2a2a; }

.btn--outline {
  background: transparent;
  color: var(--petrol);
}
.btn--outline:hover, .btn--outline:focus-visible { background: var(--petrol); color: var(--white); }

.btn--on-petrol {
  background: var(--white);
  color: var(--petrol);
  border-color: var(--white);
}
.btn--on-petrol:hover, .btn--on-petrol:focus-visible { background: var(--cream); }

.btn--sm {
  padding: 0.55rem 1.25rem;
  font-size: 0.9rem;
}

.text-link {
  display: inline-block;
  font-weight: 600;
  color: var(--petrol);
  border-bottom: 1px solid var(--bronze);
  padding-bottom: 0.1em;
}
.text-link:hover, .text-link:focus-visible { color: var(--bronze); }

.btn:disabled,
.btn[aria-disabled="true"] {
  background: var(--white);
  color: var(--grey);
  border-color: var(--grey);
  cursor: not-allowed;
}
.btn:disabled:hover, .btn[aria-disabled="true"]:hover { background: var(--white); }

.disabled-note {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.875rem;
  color: var(--grey);
  background: var(--white);
  padding: 0.1em 0;
}

/* ---- Header ----
   VERIFIED ROOT CAUSE of the logo clipping (screenshotted and bisected
   directly, not inferred from source): position:sticky combined with a
   flex row whose height is left to auto-size from its content clips that
   content at paint time, in real Chrome, independent of what
   getBoundingClientRect()/getComputedStyle() report (those reflect layout,
   not what's actually painted for a stuck sticky element — a genuine
   engine bug, reproduced with a plain colored div standing in for the
   logo, nothing image/font/aspect-ratio-specific about it). A previous
   attempt at this fix — increasing padding — didn't work for exactly this
   reason: padding was never the problem, auto height was.

   Fix: give the sticky element (.site-header) and its flex row
   (.container) an explicit, definite height instead of auto. Built from
   the same custom properties the logo and padding already use, via
   calc(), so it's structurally impossible for the pinned height to drift
   out of sync with the content driving it — this is the one thing that
   must stay true, or the clipping bug comes back. */
:root {
  --logo-height: clamp(44px, 27px + 5.35vw, 90px);
  --header-pad-top: 0.6rem;
  --header-pad-bottom: 0.6rem;
}

.site-header {
  height: calc(var(--logo-height) + var(--header-pad-top) + var(--header-pad-bottom) + 1px);
  border-bottom: 1px solid #E4E0D6;
  background: var(--white);
  position: sticky;
  top: 0;
  z-index: 50;
}

.site-header .container {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--header-pad-top) var(--header-pad-bottom);
  gap: var(--space-sm);
  flex-wrap: nowrap;
}

.site-logo {
  flex-shrink: 0;
}

/*
  The wordmark is a stacked 3-line lockup (script "Your" / serif "LIFESTYLE
  PHYSICIAN" / "BERLIN"), so the visible cap-height of its dominant middle
  line is only ~24% of the image's total rendered height — it needs a taller
  render than a single-line logo would, to read as dominant against the nav.

  aspect-ratio + flex-shrink:0 stop the browser from squashing it out of
  proportion when the row is tight (which is what was clipping "BERLIN" —
  the image was keeping its explicit height while flex shrank its width,
  distorting the lockup rather than scaling it). The clamp() scales the
  logo down fluidly across the whole range instead of relying on flex.

  The growth rate (5.35vw) is deliberately gentle: the full nav list measures
  ~417px, so right at the 768px mobile/desktop boundary there's only ~300px
  of spare row width for the logo. A steeper curve (as used previously) hits
  ~90px well before 768px and overflows the row right at the breakpoint.
  This curve stays comfortably inside budget at 768px and only reaches its
  90px ceiling around 1180px, once there's genuine room for it.
*/
.site-logo img {
  height: var(--logo-height);
  width: auto;
  aspect-ratio: 4 / 1;
  flex-shrink: 0;
}

.site-nav {
  display: flex;
  align-items: center;
}

.site-nav ul {
  display: flex;
  gap: 1.75rem;
}

.site-nav a {
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--charcoal);
  padding-block: 0.25rem;
  border-bottom: 2px solid transparent;
}

.site-nav a:hover,
.site-nav a:focus-visible,
.site-nav a[aria-current="page"] {
  color: var(--petrol);
  border-bottom-color: var(--bronze);
}

/*
  Disabled/placeholder CTA — shared visual treatment for any "not live yet"
  link styled to look like a button (currently: nav's "Schedule a
  Consultation", and the matching one closing the Six Pillars article).
  Sized like .btn by default, so it pairs correctly next to a real .btn in
  a CTA row. Deliberately reads as disabled — grey border/text, no fill,
  not-allowed cursor — rather than matching .btn or a live link, so it's
  clear at a glance it isn't active yet.
*/
.btn-disabled {
  display: inline-flex;
  align-items: center;
  padding: 0.85rem 1.75rem;
  border: 1px solid var(--grey);
  border-radius: 2px;
  color: var(--grey);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  cursor: not-allowed;
}
.btn-disabled:hover,
.btn-disabled:focus-visible {
  color: var(--grey);
  background: var(--cream);
}

/* Nav-specific sizing/position — the shared look above, shrunk to sit
   compactly inline with the other nav links, in the same <ul> so it
   naturally follows them into the mobile dropdown too.

   color and border-bottom are re-asserted here deliberately, not just
   inherited from .btn-disabled above: .site-nav a (1 class + 1 element)
   ties .btn-disabled (1 class + 0 elements) on the class tier, then wins
   outright on the element tier — so without this, two properties from
   .site-nav a silently beat .btn-disabled's: its charcoal text color, and
   its border-bottom:2px solid transparent (the underline indicator every
   plain nav link uses on hover) clobbering just the bottom edge of this
   button's border, leaving it visibly unclosed while the other three
   sides render correctly. .site-nav .btn-disabled.nav-cta (3 classes)
   safely outranks .site-nav a regardless. Caught both by inspecting
   computed styles directly, not assumed from the source. */
.site-nav .btn-disabled.nav-cta {
  margin-left: var(--space-xs);
  padding: 0.45rem 1rem;
  font-size: 0.85rem;
  color: var(--grey);
  border-bottom: 1px solid var(--grey);
}
.site-nav .btn-disabled.nav-cta:hover,
.site-nav .btn-disabled.nav-cta:focus-visible {
  color: var(--grey);
  border-bottom-color: var(--grey);
}

/* Hamburger — three Petrol bars that morph into an X when the menu opens,
   driven by the button's own aria-expanded state (see js/main.js). */
.nav-toggle {
  display: none;
  position: relative;
  background: none;
  border: none;
  padding: 0.5rem;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  flex-shrink: 0;
  z-index: 60;
}
.nav-toggle span {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--petrol);
  transition: transform 0.2s ease, opacity 0.15s ease;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}
.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Dimmed backdrop behind the open mobile menu — also the "tap outside to
   close" target (see js/main.js click handler). */
.nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(21, 59, 59, 0.35);
  z-index: 40;
  opacity: 0;
  transition: opacity 0.2s ease;
}

/* ---- Opening-soon banner + Notify me panel ----
   Homepage only, sits between the header and the hero section — the site's
   existing Petrol CTA
   treatment (see .section--petrol / .btn--on-petrol) reused here rather
   than reaching for Bronze: white-on-Petrol is already proven AAA-safe
   sitewide, where white/cream-on-Bronze fails contrast at this text size
   (Visual Identity §12). Static in normal flow — no position:sticky/fixed
   — deliberately, given the paint bug documented on .site-header above. */
.opening-banner {
  background: var(--petrol);
  color: var(--white);
}
.opening-banner__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-sm);
  padding-block: 0.75rem;
}
.opening-banner__text {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--white);
}

/*
  True modal overlay — fixed to the viewport, not in normal document flow,
  so it centers over the page (and the dimmed background covers everything,
  including the header/banner) rather than pushing page content down.
  [hidden] is handled explicitly below: an author .notify-panel { display:
  flex } rule would otherwise beat the UA stylesheet's [hidden] → display:
  none at equal specificity, since author styles win ties.

  This is a separate DOM subtree from .site-header, not a descendant of
  it, so it isn't affected by the position:sticky/fixed paint bug
  documented above — that was specific to elements inside the header's own
  tree. Confirmed clean at mobile widths after building this (see below).
*/
.notify-panel[hidden] {
  display: none;
}
.notify-panel:not([hidden]) {
  display: flex;
  align-items: center;
  justify-content: center;
}
.notify-panel {
  position: fixed;
  inset: 0;
  z-index: 100;
  padding: var(--space-md);
  background: rgba(21, 59, 59, 0.6);
}

/* The dialog card itself — close button lives inside it (top-right of the
   card), so the two always read as one self-contained unit, never as a
   floating X separate from the panel. */
.notify-panel__dialog {
  position: relative;
  width: 100%;
  max-width: 600px;
  max-height: 90vh;
  overflow-y: auto;
  background: var(--cream);
  border-radius: 2px;
  padding: 3rem var(--space-md) var(--space-md);
}
.notify-panel__close {
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--grey);
  cursor: pointer;
  padding: 0.25rem;
}
.notify-panel__close:hover,
.notify-panel__close:focus-visible {
  color: var(--petrol);
}

/* The embedded form's own frame: white card, contained shadow, generous
   padding so the foreign iframe reads as set into the page rather than
   dropped on it. Can't restyle the form's own fonts/colours (cross-origin
   Jotform content) — this is the full extent of what's controllable. */
.notify-panel__form-frame {
  background: var(--white);
  border: 1px solid #DFD8C8;
  border-radius: 2px;
  padding: var(--space-sm);
}
.notify-panel__form-frame iframe {
  display: block;
}

@media (max-width: 480px) {
  .notify-panel { padding: var(--space-sm); }
  .notify-panel__dialog { padding: 2.75rem var(--space-sm) var(--space-sm); }
}

/* ---- Footer ---- */
.site-footer {
  background: var(--petrol);
  color: var(--cream);
  padding-block: var(--space-lg) var(--space-md);
}

.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-lg);
}

.site-footer h4 {
  color: var(--white);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 1rem;
}

.site-footer nav ul {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.site-footer a {
  color: var(--cream);
}
.site-footer a:hover, .site-footer a:focus-visible { color: var(--white); text-decoration: underline; }

.site-footer .footer-meta {
  font-size: 0.875rem;
  color: #B9C4C0;
}

/* Quiet social icons next to the contact details — thin single-weight
   outline glyphs (the rounded-square frame is part of each mark's own
   recognizable shape, not a separate badge/background layer — the icons
   read as bare abstract lines without it, per the previous round's
   feedback). Muted by default (matching .footer-meta), brightening to
   white on hover like every other footer link. Explicit flex-direction/
   flex-wrap/flex-shrink rather than relying on defaults, so this can't
   collapse into a stacked column under any ancestor's flex context. */
.footer-social {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.5rem;
}
/* .site-footer a (1 class + 1 element) would otherwise beat a bare
   .footer-social__link (1 class) on specificity — same trap documented on
   .site-nav .btn-disabled.nav-cta — so this is scoped under .site-footer
   to safely outrank it. text-decoration:none is deliberate, not a leftover
   reset: .site-footer a:hover sets text-decoration:underline sitewide, and
   without overriding it here that underline was being computed against an
   anchor containing only a block-level SVG (no text/baseline), rendering
   as a stray dash near the icon on hover. */
.site-footer .footer-social__link {
  display: inline-flex;
  flex-shrink: 0;
  color: #B9C4C0;
  text-decoration: none;
  transition: color 0.15s ease;
}
.site-footer .footer-social__link:hover,
.site-footer .footer-social__link:focus-visible {
  color: var(--white);
  text-decoration: none;
}
.footer-social__link svg {
  display: block;
  width: 30px;
  height: 30px;
}

.footer-bottom {
  max-width: var(--max-width);
  margin: var(--space-md) auto 0;
  padding: var(--space-md) var(--gutter) 0;
  border-top: 1px solid #2B5252;
  font-size: 0.8rem;
  color: #9AABA6;
}

/* ---- Utility ---- */
.text-grey { color: var(--grey); }
.mt-lg { margin-top: var(--space-lg); }
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---- About page ----
   Grid, not flex: an explicit column track + gap reads as a deliberate
   relationship between the text column and the portrait, rather than the
   portrait just occupying whatever space flex happened to leave over.

   Fixed pixel widths made the portrait/text height ratio a coincidence of
   whatever the viewport happened to be (text re-wraps as the 1fr column
   resizes, so its height isn't fixed, while a fixed-width portrait's
   height never changed) — that's why a size tuned at one width looked
   right there and undersized elsewhere. Grid's default row stretch fixes
   this properly: the portrait's grid cell now stretches to match the text
   column's actual height at any width, and object-fit:contain lets the
   image fill that height exactly — scaled uniformly, never cropped or
   distorted — rather than approximating it with a static width. */
.about-hero {
  display: grid;
  grid-template-columns: 1fr 400px;
  gap: var(--space-sm);
}
.about-hero .portrait {
  background: var(--cream);
  height: 100%;
}
.about-hero .portrait img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: top center;
}

/* ---- What to Expect page ----
   Steps: the four-step process is inherently sequential, so a numbered
   list is accurate here, not decorative — unlike a generic "01/02/03"
   treatment applied for visual interest. Numeral reuses the existing
   display face/weight and Bronze (large, decorative, non-critical text —
   compliant with the Bronze-at-body-size restriction) rather than
   introducing a new type role for a four-item list. */
.steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  max-width: 68ch;
}
.step {
  display: flex;
  gap: var(--space-md);
  align-items: baseline;
}
.step__number {
  flex-shrink: 0;
  width: 2.25rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.75rem;
  color: var(--bronze);
}

/* Generic content list, sitewide — a small Bronze dash marker instead of a
   default bullet, matching the dash/underline language .divider and
   .text-link already use elsewhere, rather than introducing a new marker
   style per list (six pillars on What to Expect, package inclusions on
   Pricing, and whatever needs a plain list next). */
.dash-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-top: var(--space-sm);
}
.dash-list li {
  position: relative;
  padding-left: 1.5rem;
  font-family: var(--font-body);
  font-size: 1.125rem;
  color: var(--charcoal);
}
.dash-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 10px;
  height: 1px;
  background: var(--bronze);
}

/* ---- Pricing page ---- */

/* Small back-reference note, not a body paragraph — deliberately smaller
   than .lede and grey (secondary-text treatment, matching .footer-meta's
   sizing logic) so it reads as a pointer to another page, not page content
   in its own right. */
.page-note {
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--grey);
}
.page-note .text-link {
  font-size: inherit;
}

/* Price line — bold and Petrol, set apart from the plain-weight bullet
   list above it. Same treatment whether it holds a real figure or the
   "Price coming soon" placeholder, so the layout doesn't shift once real
   prices go in. */
.price-line {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.125rem;
  color: var(--petrol);
  margin-top: var(--space-md);
}

/* Continuity of Care packages — visually lighter than the four main
   packages above (which each get a full alternating-background section,
   the same treatment About/What to Expect use for their content blocks).
   These sit together in one section as compact bordered cards instead, so
   the page doesn't read as six equally-weighted options. */
.package-mini {
  background: var(--white);
  border: 1px solid #E4E0D6;
  border-radius: 2px;
  padding: var(--space-md);
}
.package-mini + .package-mini {
  margin-top: var(--space-md);
}
.package-mini .dash-list {
  margin-top: var(--space-sm);
}
.package-mini .dash-list li {
  font-size: 1rem;
}

/* ---- Writing index page ----
   Repeatable card pattern for listing pieces — one now, more later, so the
   whole entry (not just its title) is the tap target. Sizing reuses
   .section-subheading/.lede as-is (no new heading size), only the
   container is new. Read → is a styled span, not a real nested <a> — an
   anchor can't validly contain another anchor, and the whole card is
   already the real link. */
.writing-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  margin-top: var(--space-lg);
}
.writing-item {
  display: block;
  padding: var(--space-md);
  border: 1px solid #E4E0D6;
  border-radius: 2px;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.writing-item:hover,
.writing-item:focus-visible {
  border-color: var(--bronze);
  background: var(--cream);
}
.writing-item h2 {
  margin-bottom: 0.4rem;
}
.writing-item .lede {
  margin-bottom: var(--space-sm);
}
.writing-item .text-link {
  pointer-events: none;
}

/* ---- Article pages (Writing) ----
   Layout only — max-width and the rhythm between stacked section headings.
   Typography itself comes entirely from the shared role classes above
   (.eyebrow, .page-title, .subtitle, .section-subheading, .lede), applied
   directly in the HTML. No font-family/font-size/font-weight belongs in
   this section — if you're about to add one here, it belongs in the type
   scale above instead. */
.article-header,
.article-body {
  max-width: 68ch;
}

.article-body .section-subheading {
  margin-top: var(--space-lg);
}
.article-body .section-subheading:first-child { margin-top: 0; }

/* Closing CTA row — side by side where there's room, wrapping to stacked
   automatically on narrow viewports rather than needing an explicit
   mobile-only rule. */
.article-cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
}

/* ---- Responsive ----
   768px is the one mobile/desktop nav breakpoint, site-wide. Above it, the
   header is untouched: full inline nav, no hamburger. The logo's clamp()
   above is tuned specifically so it never outgrows its budget right at this
   boundary (~300px of spare row width at 768px, against the nav's measured
   ~417px). */
@media (max-width: 768px) {
  .site-nav ul {
    /*
      fixed, not absolute: absolute would need .site-header (or an
      ancestor) to be a positioned element to anchor to, and giving the
      logo's own ancestor any non-static position is exactly what causes
      the mobile clipping bug above — verified directly, position:relative
      clips it identically to sticky/fixed. fixed positions against the
      viewport instead, so the header can stay position:static.
    */
    /*
      --dropdown-top is set by js/main.js from the header's actual measured
      bottom edge (getBoundingClientRect), not calculated in CSS — a fixed
      calc() here only ever knew about the header's own height, so it broke
      the moment the opening-soon banner added content above the header.
      Reading the real DOM position instead of recalculating a second
      formula in JS means this can never drift out of sync with whatever
      is actually stacked above the header, banner or no banner.
    */
    position: fixed;
    top: var(--dropdown-top, calc(var(--logo-height) + var(--header-pad-top) + var(--header-pad-bottom) + 1px));
    left: 0;
    right: 0;
    flex-direction: column;
    background: var(--white);
    padding: var(--space-sm) var(--gutter) var(--space-md);
    gap: 0.25rem;
    border-bottom: 1px solid #E4E0D6;
    /*
      VERIFIED (elementFromPoint at scrollY 0): a bare translateY(-110%) is a
      percentage of the list's OWN height, computed independent of `top`
      above. With 6 links + the CTA the list runs ~400px tall against a
      ~70-90px `top` (banner pushes it further on the homepage), so -110%
      only lifts it to roughly `top - 1.1*height` — its bottom edge lands
      back at a positive y (~30px), a persistent white sliver fixed over the
      logo on every load, not just after scrolling. Subtracting the same
      `top` value used above cancels it out of the equation entirely, so the
      closed list's top edge always lands at exactly -height regardless of
      list length or which page's `top` offset is in play; the extra -20px
      is just breathing room above 0.
    */
    transform: translateY(calc(-100% - var(--dropdown-top, calc(var(--logo-height) + var(--header-pad-top) + var(--header-pad-bottom) + 1px)) - 20px));
    transition: transform 0.2s ease;
    z-index: 50;
  }
  .site-nav[data-open="true"] ul {
    transform: translateY(0);
  }
  /* Comfortable tap targets: each stacked link gets a full-width row with
     real vertical padding, not the compact inline padding used desktop-side. */
  .site-nav ul a {
    display: block;
    padding-block: 0.9rem;
    font-size: 1.05rem;
    border-bottom: 1px solid #EFEFEF;
  }
  .site-nav ul li:last-child a { border-bottom: none; }

  /* Keep the disabled CTA reading as a compact button even in the stacked
     mobile list, rather than inheriting the full-width row treatment above.
     Specificity note: needs to be at least as specific as the desktop
     ".site-nav .btn-disabled.nav-cta" rule above (3 classes) or it silently
     loses to it even inside this media query — the extra "ul" here is what
     makes that true (3 classes + 1 element beats 3 classes + 0 elements). */
  .site-nav ul .btn-disabled.nav-cta {
    display: inline-block;
    margin-top: 0.5rem;
    padding: 0.6rem 1.1rem;
    font-size: 0.9rem;
  }

  .nav-toggle { display: flex; }

  .site-nav[data-open="true"] ~ .nav-backdrop {
    display: block;
    opacity: 1;
  }

  /* Redefine the custom properties, not padding-block directly — .site-header's
     height is calc()'d from --header-pad-top/-bottom (see Header section), so
     the pinned height has to move in lockstep with the padding or the
     clipping bug returns. A bit more top padding than desktop's, for
     clearance around the "Your" script line's ascender. */
  :root {
    --header-pad-top: 0.85rem;
    --header-pad-bottom: 0.6rem;
  }

  /*
    Bisected directly with screenshots (not inferred): at mobile viewport
    widths, ANY non-static position on .site-header — sticky, fixed, AND
    relative, all three tested individually — clips the logo's top.
    Ruled out separately: height, padding, aspect-ratio, clamp()/vw,
    GPU-layer promotion (transform:translateZ(0)/will-change), a non-zero
    top offset, and a forced reflow after paint — none of those changed
    it. position:static is the only value that rendered correctly, in
    every screenshot, at every mobile width tested. Real engine rendering
    bug, not a stylesheet bug — the exact same sticky header is clipped
    nowhere at desktop widths.

    Because relative is included in what's broken, .site-header can't be
    a positioned element at all here — the mobile dropdown below is
    position:fixed (anchored to the viewport, not to .site-header) so it
    doesn't need .site-header to be positioned.

    Trade-off, explicit: the header no longer stays pinned while scrolling
    on mobile (≤768px) — it scrolls away normally, like the rest of the
    page. Desktop keeps position:sticky from the Header section above,
    unchanged, since that's verified clean.
  */
  .site-header {
    position: static;
  }

  .site-footer .container { flex-direction: column; gap: var(--space-md); }

  /* Stacked single column: the stretch/contain height-matching above only
     makes sense when text and portrait share a grid row side by side.
     Stacked, the portrait reverts to sizing itself by its own aspect ratio. */
  .about-hero { grid-template-columns: 1fr; }
  .about-hero .portrait { height: auto; max-width: 260px; }
  .about-hero .portrait img { height: auto; object-fit: initial; object-position: initial; }
}
