/* ---------- Tokens ---------- */
/* Colours are pixel-sampled from the clinic's logo and social posts.
   See docs/superpowers/specs/2026-09-05-squadlethics-brand-redesign-design.md
   before changing any value here. */
:root {
  /* Brand */
  --brand-deep:  #0d1f4a;  /* gradient start; flat nav + footer */
  --brand-mid:   #163a72;  /* gradient midpoint */
  --brand-steel: #195586;  /* gradient end — capped for contrast, deliberately
                              NOT the measured #1c5d95; see spec */
  --brand-ink:   #0a18b8;  /* logo ultramarine; eyebrow + specialty on light */
  --accent:      #71daed;  /* cyan; eyebrow + emphasis + buttons on dark */
  --accent-dark: #4dd1e8;  /* button hover on dark */
  --accent-ink:  #08183a;  /* label colour on cyan buttons */
  /* Hero scrim. 0.80 clears 4.5:1 for every foreground over a pure WHITE
     pixel — the worst any photo could contain — so the floor survives a photo
     swap. Muted text binds at 0.770. Do not lighten this; check-contrast.py
     will fail. */
  --scrim-alpha: 0.80;

  /* Matches .nav__inner's min-height. Static, not measured in JS — layout must
     not depend on a script the site has to survive without. Only used at
     >= 768px, where .nav__inner provably does not wrap. */
  --nav-h: 64px;

  /* Surfaces */
  --bg:   #f5f8fc;
  --card: #ffffff;
  --line: #dde5ef;
  --line-on-dark: rgba(255, 255, 255, 0.28);

  /* Text */
  --ink:           #0d1f4a;  /* headings on LIGHT bands — this token used to be
                                the dark SURFACE colour; the role changed */
  --text:          #4a5b71;
  --text-muted:    #5c6a7d;
  --on-dark:       #ffffff;
  --on-dark-muted: #b8c9e0;

  /* Composite */
  --band: linear-gradient(150deg,
            var(--brand-deep) 0%,
            var(--brand-mid) 60%,
            var(--brand-steel) 100%);

  --font-display: Archivo, "Helvetica Neue", Arial, sans-serif;
  --font-body: Inter, system-ui, sans-serif;
}

/* ---------- Reset & base ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { scroll-behavior: smooth; scroll-padding-top: 80px; }
body {
  font-family: var(--font-body);
  color: var(--text);
  background: var(--bg);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}
.card :focus-visible, .section :focus-visible { outline-color: var(--brand-ink); }

/* ---------- Scroll reveals ---------- */
/* Gated on .js: with JavaScript unavailable nothing is ever hidden, because
   the class that hides it is never added. Asserted by tools/check-contrast.py. */
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: var(--reveal-delay, 0ms);
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}
.js .cards-grid > .reveal:nth-child(2) { --reveal-delay: 90ms; }
.js .cards-grid > .reveal:nth-child(3) { --reveal-delay: 180ms; }
.js .band__row > *:nth-child(2) { --reveal-delay: 90ms; }
.js .band__row > *:nth-child(3) { --reveal-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---------- Motion ---------- */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@keyframes hero-settle {
  from { scale: 1.06; }
  to   { scale: 1; }
}
@keyframes sweep {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes draw {
  to { stroke-dashoffset: 0; }
}

/* The hero photo uses `scale`, not `transform`, so the settle and the
   parallax translate compose instead of overwriting each other. */
.hero__photo { animation: hero-settle 1.8s cubic-bezier(0.16, 1, 0.3, 1) both; }

.hero .eyebrow,
.hero__title,
.hero__sub,
.hero__chips,
.hero .btn {
  animation: rise-in 0.55s cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.hero__title   { animation-delay:  80ms; }
.hero__sub     { animation-delay: 160ms; }
.hero__chips   { animation-delay: 240ms; }
.hero .btn     { animation-delay: 320ms; }

/* The emphasis word is the brand's own device; the sweep promotes it from a
   colour change to a gesture. Default is drawn, so it is present without
   JavaScript; only the animated-from-zero states are gated. */
.hl { position: relative; }
.hl::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.06em;
  height: 0.1em;
  background: currentColor;
  opacity: 0.35;
  transform: scaleX(1);
  transform-origin: left center;
}
.hero__title .hl::after {
  animation: sweep 0.5s cubic-bezier(0.16, 1, 0.3, 1) 700ms both;
}
.js .reveal .hl::after { transform: scaleX(0); }
.js .reveal.is-visible .hl::after {
  animation: sweep 0.5s cubic-bezier(0.16, 1, 0.3, 1) 200ms both;
}

/* The service icons are already stroked SVGs, so this costs no new assets.
   200 is comfortably longer than any of the three paths; a shorter path
   simply finishes drawing early. */
.js .reveal .service__icon svg :is(path, ellipse, circle) {
  stroke-dasharray: 200;
  stroke-dashoffset: 200;
}
.js .reveal.is-visible .service__icon svg :is(path, ellipse, circle) {
  animation: draw 0.9s ease-out 150ms both;
}

/* The lift animates `translate`; the shadow fades a pseudo-element's opacity
   rather than transitioning box-shadow itself, which would animate blur
   geometry and repaint on every frame. */
.card { transition: translate 0.2s cubic-bezier(0.16, 1, 0.3, 1); }
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 12px 28px rgba(13, 31, 74, 0.12);
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}
.card:hover { translate: 0 -4px; }
.card:hover::before { opacity: 1; }
.btn:hover { transform: translateY(-1px); }
.btn__arrow {
  display: inline-block;
  transition: transform 0.15s ease;
}
.btn:hover .btn__arrow { transform: translateX(3px); }

/* The reveal's own transition (opacity + transform, both 0.6s with the
   card's stagger delay) would otherwise apply to `translate` too, since
   `.js .reveal`'s transition shorthand (0,2,0) outranks `.card`'s (0,1,0).
   That would give the hover lift the reveal's 600ms duration and its
   per-card stagger delay instead of its own quick, undelayed timing. Give
   `translate` its own entry, with 0ms delay, positionally matched. */
.js .card.reveal {
  transition:
    opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.6s cubic-bezier(0.16, 1, 0.3, 1),
    translate 0.2s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: var(--reveal-delay, 0ms), var(--reveal-delay, 0ms), 0ms;
}

/* ---------- Layout helpers ---------- */
.container {
  max-width: 1100px;
  margin: 0 auto;
  padding-left: 20px;
  padding-right: 20px;
}
.section { padding: 56px 0; }
@media (min-width: 768px) {
  .section { padding: 88px 0; }
}

/* ---------- Typography helpers ---------- */
.eyebrow {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--brand-ink);
  margin-bottom: 12px;
}
.hero .eyebrow, .visit .eyebrow { color: var(--accent); }
.section__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.75rem, 4vw, 2.25rem);
  line-height: 1.1;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin-bottom: 32px;
  overflow-wrap: break-word;
}

/* Emphasis word — cyan on the dark bands, steel on the light ones.
   Taken from the "BREAKDOWN scar tissue" post. */
.hl { color: var(--brand-steel); }
.hero .hl, .visit .hl { color: var(--accent); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  text-decoration: none;
  border-radius: 6px;
  padding: 12px 22px;
  transition: background 0.15s ease, transform 0.15s ease;
}
.btn:hover { background: var(--accent-dark); }
.btn--sm { padding: 8px 16px; font-size: 0.875rem; }
.btn--lg { padding: 15px 28px; font-size: 1.0625rem; }

/* ---------- Cards ---------- */
.cards-grid { display: grid; gap: 16px; }
@media (min-width: 768px) {
  .cards-grid--3 { grid-template-columns: repeat(3, 1fr); gap: 24px; }
  .cards-grid--2 { grid-template-columns: repeat(2, 1fr); gap: 24px; }
}
.card {
  position: relative;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 28px 24px;
}
.card h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-bottom: 8px;
}

/* ---------- Nav ---------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--brand-deep);
}
.nav--scrolled { box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35); }
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 64px;
  flex-wrap: wrap;
  padding-top: 8px;
  padding-bottom: 8px;
}
.nav__brand {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 85%;
  font-size: 1.125rem;
  letter-spacing: 0.1em;
  color: var(--on-dark);
  text-decoration: none;
}
.nav__links { display: none; }
@media (min-width: 768px) {
  .nav__links { display: flex; gap: 28px; }
  .nav__links a {
    color: var(--on-dark-muted);
    text-decoration: none;
    font-size: 0.9375rem;
    font-weight: 500;
    transition: color 0.15s ease;
  }
  .nav__links a:hover { color: var(--on-dark); }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* Fallback beneath the photo, and what prints. */
  background: var(--band);
  color: var(--on-dark);
  padding: 72px 0 88px;
}
.hero__photo {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 35%;
  /* Parallax is written to --parallax as an inline custom property, so the
     settle animation below can own `scale` without the two colliding. */
  transform: translate3d(0, var(--parallax, 0px), 0);
}
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Two layers: a directional gradient for depth, over a flat floor. The
     gradient only ever ADDS opacity, so total alpha is >= --scrim-alpha
     everywhere. Text reflows across the full width at narrow viewports, so a
     gradient alone cannot carry the guarantee. */
  background:
    linear-gradient(100deg,
      rgba(13, 31, 74, 0.55) 0%,
      rgba(13, 31, 74, 0.25) 45%,
      rgba(13, 31, 74, 0) 100%),
    rgba(13, 31, 74, var(--scrim-alpha));
}
@media (min-width: 768px) {
  /* Pull the hero up under the sticky nav so the photo runs behind it, and
     give the space back as padding so the content does not move. Below this
     breakpoint .nav__inner can wrap, so --nav-h is not reliable and the nav
     stays solid — which costs nothing against a navy-scrimmed hero. */
  .hero {
    margin-top: calc(-1 * var(--nav-h));
    padding: calc(120px + var(--nav-h)) 0 136px;
  }
  /* Gated on .js: .nav--scrolled is only ever added by js/main.js. Without
     that class present a plain `.nav { background: transparent }` would stay
     transparent at every scroll position, dropping nav text straight below
     the 4.5:1 floor over the light page background. With JavaScript
     unavailable the base .nav rule's solid --brand-deep applies instead —
     the same treatment already used below 768px. */
  .js .nav {
    background: transparent;
    transition: background 0.2s ease;
  }
  .js .nav--scrolled { background: var(--brand-deep); }
}
.hero__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2rem, 7.5vw, 4.25rem);
  line-height: 1;
  letter-spacing: -0.02em;
  margin-bottom: 20px;
  overflow-wrap: break-word;
}
.hero__sub {
  font-size: 1.0625rem;
  color: var(--on-dark-muted);
  max-width: 34em;
  margin-bottom: 24px;
}
.hero__sub em { color: var(--on-dark); font-style: normal; font-weight: 600; }
.hero__chips {
  list-style: none;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 36px;
}
.hero__chips li {
  border: 1px solid var(--line-on-dark);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--on-dark-muted);
}

/* ---------- Services ---------- */
.service__icon {
  color: var(--brand-steel);
  margin-bottom: 16px;
}
.service__icon svg { stroke-width: 1.3; }
.service p {
  font-size: 0.9375rem;
  color: var(--text-muted);
}

/* ---------- Photo band ---------- */
.band {
  background: var(--band);
  padding: 56px 0;
}
.band__row {
  display: grid;
  gap: 16px;
  margin-bottom: 28px;
}
.band__photo {
  width: 100%;
  height: 220px;
  object-fit: cover;
  border-radius: 10px;
  /* --stagger is the static layout offset, --parallax the scroll drift. They
     compose here so the scroll handler never clobbers the stagger. */
  transform: translate3d(0, calc(var(--stagger, 0px) + var(--parallax, 0px)), 0);
}
.band__caption {
  padding-top: 14px;
  border-top: 1px solid var(--line-on-dark);
  font-family: var(--font-body);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.3em;
  color: var(--on-dark-muted);
}
@media (min-width: 768px) {
  .band { padding: 88px 0; }
  .band__row {
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
    align-items: start;
  }
  .band__photo { height: 260px; }
  .band__photo--raised { --stagger: -24px; }
}

/* ---------- Doctors ---------- */
.doctor__specialty {
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand-ink);
  margin-bottom: 10px;
}
.doctor__sched {
  font-size: 0.9375rem;
  color: var(--text-muted);
}
.doctors__note {
  margin-top: 20px;
  font-size: 0.9375rem;
  color: var(--text-muted);
}

/* ---------- Visit + Contact ---------- */
.visit {
  background: var(--band);
  color: var(--on-dark);
  padding: 56px 0;
}
@media (min-width: 768px) {
  .visit { padding: 88px 0; }
  .visit__grid {
    display: grid;
    grid-template-columns: 1.2fr 1fr;
    gap: 48px;
    align-items: start;
  }
}
.visit__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.75rem, 4vw, 2.25rem);
  line-height: 1.1;
  letter-spacing: -0.015em;
  margin-bottom: 24px;
  overflow-wrap: break-word;
}
.visit__info {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 14px;
  margin-bottom: 20px;
}
.visit__info li { color: var(--on-dark-muted); font-size: 0.9375rem; }
.visit__info strong {
  display: block;
  color: var(--on-dark);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-bottom: 2px;
}
.visit__map {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
}
.visit__map:hover { text-decoration: underline; }
.visit__cta {
  margin-top: 40px;
  border: 1px solid var(--line-on-dark);
  border-radius: 12px;
  padding: 28px 24px;
}
@media (min-width: 768px) {
  .visit__cta { margin-top: 52px; }
}
.visit__cta p { color: var(--on-dark-muted); margin-bottom: 20px; }

/* ---------- Footer ---------- */
.footer {
  background: var(--brand-deep);
  border-top: 1px solid var(--line-on-dark);
  padding: 24px 0;
}
.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  font-size: 0.8125rem;
  color: var(--on-dark-muted);
}
.footer__social { display: flex; gap: 18px; }
.footer__social a {
  color: var(--on-dark-muted);
  text-decoration: none;
}
.footer__social a:hover { color: var(--on-dark); }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* Nothing moves. Content sits in its final state. The parallax handler
     checks this same query in JavaScript, because a media query cannot
     override an inline transform written by script. */
  .hero__photo,
  .hero .eyebrow,
  .hero__title,
  .hero__sub,
  .hero__chips,
  .hero .btn,
  .hl::after,
  .service__icon svg :is(path, ellipse, circle) {
    animation: none !important;
  }
  .hero__photo { scale: 1; transform: none; }
  .hl::after { transform: scaleX(1); }
  /* .js .reveal .hl::after (0,3,1) and .js .reveal ... service__icon svg
     :is(...) (0,3,2) outrank the plain selectors above, so each needs its own
     same-specificity restatement here or the sweep/draw states they set
     survive the !important animation kill and leave content invisible. */
  .js .reveal .hl::after { transform: scaleX(1); }
  .service__icon svg :is(path, ellipse, circle) { stroke-dashoffset: 0; }
  .js .reveal .service__icon svg :is(path, ellipse, circle) { stroke-dashoffset: 0; }
  /* Keep --stagger: the raised middle photo is layout, not motion. Only the
     scroll-driven --parallax term is dropped. The reveal now lives on the
     .picture wrapper, not on .band__photo itself, so this plain selector is
     no longer shadowed by a more specific `.reveal.is-visible` variant. */
  .band__photo { transform: translate3d(0, var(--stagger, 0px), 0); }
  .card, .btn, .btn__arrow, .card::before { transition: none; }
  /* `.js .card.reveal` (0,3,0), a few hundred lines up, sets its own
     `translate`-inclusive transition and outranks the plain `.card` reset
     above (0,1,0). Restate the reset at matching specificity so it actually
     wins — inert today because .card:hover's `translate` is pinned to 0 0
     below, but without this the reset silently loses to any future
     animatable property added to .card:hover. */
  .js .card.reveal { transition: none; }
  .card:hover { transform: none; }
  .card:hover::before { opacity: 0; }
  .card:hover, .js .card.reveal:hover { translate: 0 0; }
  .btn:hover { transform: none; }
  .btn:hover .btn__arrow { transform: none; }
}

/* ---------- Print ---------- */
@media print {
  .nav, .hero, .visit, .footer {
    background: #fff !important;
    color: #0d1f4a !important;
  }
  .hero__sub,
  .hero__chips li,
  .visit__info li,
  .visit__info strong,
  .visit__cta p,
  .footer__inner,
  .footer__social a,
  .nav__brand,
  .nav__links a,
  .hero__sub em {
    color: #0d1f4a !important;
  }
  .hero .hl, .visit .hl,
  .hero .eyebrow, .visit .eyebrow,
  .visit__map {
    color: #0d1f4a !important;
  }
  .hero__chips li, .visit__cta {
    border-color: #dde5ef !important;
  }
  .nav { position: static; }
  /* Photography and its scrim carry no information on paper. */
  .hero__photo, .hero__scrim, .band { display: none !important; }
  /* Undo the nav overlap so the hero does not print under the header. */
  .hero { margin-top: 0 !important; padding-top: 40px !important; }
  /* A page printed before scrolling has un-fired reveals; force them visible
     or those sections print blank. */
  .js .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .hero__photo,
  .hero .eyebrow,
  .hero__title,
  .hero__sub,
  .hero__chips,
  .hero .btn,
  .hl::after,
  .service__icon svg :is(path, ellipse, circle) {
    animation: none !important;
  }
  .service__icon svg :is(path, ellipse, circle) { stroke-dashoffset: 0 !important; }
  /* Hidden, not forced visible: this pseudo-element paints a translucent
     currentColor bar behind the text, which the print block above already
     forces to solid dark. Left alone it would print as a stray dark bar
     rather than the on-screen highlight sweep. */
  .hl::after { display: none !important; }
  .card::before { display: none !important; }
  .band__photo { transform: none !important; }
}
