/* =========================================================
   TOKENS
   ========================================================= */
:root {
  /* warm neutrals */
  --paper:      #F9F7F6;
  --cream:      #F3F0EC;
  --sand:       #E5E0DC;
  --stone:      #BFB7B0;
  --taupe:      #9A8A7E;
  --charcoal:   #342C27;
  --ink:        #1E1815;
  --near-black: #120E0C;

  /* navy — primary brand */
  --navy-t2: #769CC3;
  --navy-t1: #487EB4;
  --navy:    #2D557D;
  --navy-s1: #1B3652;
  --navy-s2: #0E1D2C;

  /* olive — success / nature cues */
  --olive-t2: #ACBF8B;
  --olive-t1: #91AE5F;
  --olive:    #67803B;
  --olive-s1: #465925;
  --olive-s2: #2A3515;

  /* orange — accent / CTA */
  --orange-t2: #E7BD9B;
  --orange-t1: #DF9C66;
  --orange:    #CF7125;
  --orange-s1: #9F5418;
  --orange-s2: #743D10;

  /* brown — grounding / dark chrome */
  --brown-t2: #9F715A;
  --brown-t1: #7C5440;
  --brown:    #482F22;
  --brown-s1: #1F140D;
  --brown-s2: #120B07;

  /* purple — secondary / links */
  --purple-t2: #A8A4C6;
  --purple-t1: #817AB2;
  --purple:    #544C8F;
  --purple-s1: #3A346A;
  --purple-s2: #272249;
 
  --font-display: "Archivo", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body: "Archivo", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;
  /* Weight applied to headings set in --font-display. */
  --font-heading-weight: 700;

  --wrap: 1180px;
  --gutter: clamp(1.25rem, 4vw, 3rem);

  --header-h: 84px;
}
@media (min-width: 860px) {
  :root { --header-h: 130px; }
}

*, *::before, *::after { box-sizing: border-box; }
html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--header-h);
  /* Safety net against horizontal scroll. The concrete case this was
     added for: .member-map's full-bleed breakout (see that rule,
     "min-width: 1300px") sizes itself with `100vw`, and on a desktop
     browser with a classic (non-overlay) scrollbar — Windows Chrome/
     Firefox/Edge by default — `100vw` measures the *full* viewport
     including the scrollbar's own reserved width, which is wider than
     the actual visible content area. That made the map a few pixels
     wider than the page on every page tall enough to need a vertical
     scrollbar (i.e. the homepage), producing a faint horizontal
     scrollbar and letting the page pan sideways. Chromium's headless/
     overlay-scrollbar rendering (used for local testing) doesn't
     reserve that space, so the gap between 100vw and the visible width
     is 0 there and the bug doesn't reproduce — it only shows up with a
     real, space-reserving OS scrollbar. This rule clips whatever small
     overflow slips through from that (or any future vw-based
     full-bleed element) rather than letting it become a scrollable
     page; at most a few pixels of a full-bleed section's edge, so
     nothing visible is lost. */
  overflow-x: hidden;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}
 
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
 
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
 
.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }

/* Full-bleed variant of .wrap — stretches to the browser's full width
   instead of capping at --wrap (1180px). Used together with .wrap (in
   place of the max-width it sets) on the rows that should keep
   growing on very wide screens: the main menu, the footer, and the
   services/icon-grid section. Below 1120px this matches .wrap's own
   --gutter padding almost exactly (both are the same clamp(1rem,
   2.5vw, 1.75rem) shape); from 1120px it ramps smoothly up to a fixed
   100px at 1400px and beyond, rather than jumping straight from the
   1.75rem cap to 100px at a single breakpoint. */
.wrap--edge {
  max-width: none;
  padding-inline: clamp(1rem, 2.5vw, 1.75rem);
}
@media (min-width: 1120px) {
  /* calc(25.7143vw - 260px) is the linear ramp between the two fixed
     points this needs to hand off smoothly with: 28px (1.75rem) at
     1120px, and 100px at 1400px. clamp()'s own min/max below hold it
     flat at those values outside that range. */
  .wrap--edge {
    padding-inline: clamp(1.75rem, calc(25.7143vw - 260px), 100px);
  }
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -999px; top: 0;
  background: var(--navy-s2); color: var(--paper);
  padding: 0.75rem 1.25rem; z-index: 100;
}
.skip-link:focus { left: var(--gutter); top: var(--gutter); }
:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }
 
/* =========================================================
   HERO — full bleed, overlaid nav
   ========================================================= */
.hero {
  position: relative;
  /* Capped by 65vw alongside the viewport-height value (not just
     100vh/100dvh alone) — full viewport height is fine on desktop,
     where the hero is wide relative to its height so object-fit:cover
     on the video below barely crops it, but on a narrow phone a full-
     height hero is much taller than it is wide, so cover crops in hard
     on the sides to fill that shape and blows the video's own
     embedded title text up past the edges (illegible/cut off). 65vw
     keeps the box close to the video's native 16:9 (56.25vw at full
     width would be the exact uncropped height; 65vw leaves a little
     extra presence/immersion beyond that without cropping much) — so
     as the browser narrows, the hero gets shorter right along with it
     instead of staying pinned to full-screen height. Two declarations
     for the same reason as before: the first is what non-dvh browsers
     use, the second (using the newer unit) overrides it everywhere
     that supports dvh. */
  min-height: min(100vh, 65vw);
  min-height: min(100dvh, 65vw);
  display: flex;
  flex-direction: column;
  color: var(--paper);
  /* Fallback color visible briefly before the hero video/gradient
     paints in — dark green (matching .site-header's own background)
     rather than navy, so the initial-load flash reads as on-brand
     instead of an off-palette color. */
  background: var(--olive-s2);
  overflow: hidden;
}

/* Below 640px, size the hero from a taller vw multiplier than the
   65vw used everywhere else. object-fit:cover always fills the full
   box width-to-width, so a taller box (relative to its own width)
   means the video is scaled up more to cover it — cropping in
   tighter on the left/right, which pushes the video's own embedded
   title text closer to the box's edges (less empty margin outside
   the text) and, on the vertical axis, opens up more clearance below
   the text for .hero__mark/__cta so the CTA button stops overlapping
   it. 100vw is a starting point (an on-brand phone at 393px wide
   works out to roughly a 400px-tall hero here) — since the actual
   video frame can't be checked visually in this environment, treat
   this multiplier as tunable if it still crops too little/much once
   seen on a real device. */
@media (max-width: 640px) {
  .hero {
    min-height: min(100vh, 100vw);
    min-height: min(100dvh, 100vw);
  }
}
 
.hero__video {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Starts at the very top of .hero (behind the fixed header, not
     pushed down below it) so the footage is visible through the
     header while it's transparent — see ".hero .site-header" below.
     The header sits above it (z-index: 100, its own stacking context
     from position: fixed) regardless of DOM order, so this never
     covers the header's own contents. */
}
 
/* Mobile used to get its own treatment here — a shorter, inset,
   letterboxed video "card" with the logo/CTA/scroll cue reserved in a
   flat-color band below it. Several rounds of hand-tuning that (fixed
   vw/px heights, a manually-offset crop) kept fighting against what
   was actually wanted: centered, as large as the hero allows, with the
   CTA sitting on top of it — which is exactly what the base rules
   above already do on desktop. So mobile no longer overrides the crop
   behavior at all: .hero__video stays inset:0 + object-fit:cover
   (always centered, always fills the full box — the largest it can be
   at any width), and .hero__mark/__cta/__scroll (below) overlay on
   top of it via the flex column, same as desktop. The one thing
   mobile does still override is .hero's own height multiplier (65vw
   base, bumped to 100vw under 640px — see the @media block just
   above) so narrow phones get a taller-relative-to-width box and
   therefore a tighter crop. One tradeoff worth knowing: on mobile
   Safari, 100dvh resizes as the toolbar shows/hides while scrolling,
   which can read as the video subtly "zooming" during that transition
   — dvh (rather than plain vh) already smooths most of that, and
   going back to a fixed height to eliminate it entirely would
   reintroduce the card/letterbox look this change is removing, so
   it's left as the simpler tradeoff unless it turns out to be a
   problem in practice. */

.hero__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Pre-video (and video-failed-to-load) treatment. The top-left glow
     and diagonal base wash were navy-toned (--navy-t1 / --navy-s1 /
     --navy-s2) — swapped for their olive equivalents here so the
     brief flash before the video's first frame reads as dark green
     rather than dark blue. The vignette layer above it and the brown
     glow are unrelated to that blue cast, so they're unchanged; the
     .hero.has-video override below (the permanent tint once the video
     is actually playing) is a separate rule and also untouched. */
  background:
    linear-gradient(180deg, rgba(14,29,44,0.55) 0%, rgba(14,29,44,0.25) 40%, rgba(14,29,44,0.7) 100%),
    radial-gradient(120% 90% at 15% 10%, #91AE5F 0%, transparent 55%),
    radial-gradient(120% 90% at 85% 90%, #482F22 0%, transparent 55%),
    linear-gradient(140deg, #465925 0%, #2A3515 45%, #120E0C 100%);
  background-blend-mode: normal, screen, screen, normal;
}
 
.hero.has-video .hero__overlay {
  background:
    linear-gradient(180deg, rgba(14,29,44,0.5) 0%, rgba(14,29,44,0.2) 40%, rgba(14,29,44,0.65) 100%);
}
 
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
  width: 100%;
  background: var(--olive-s2);
  transition: background-color 0.3s ease;
}

/* On the homepage, .site-header is nested inside .hero (see index.html)
   and sits directly on top of the hero video/gradient, so it can start
   fully transparent there and let the footage show through — fading
   in its usual solid green only once the page scrolls past the hero
   (see the "is-scrolled" toggle in js/script.js). Sub-pages render
   .site-header standalone, right above the light page background, so
   this selector simply never matches there and they keep the solid
   background unconditionally. ".is-scrolled" is one class more specific
   than ".hero .site-header" alone, so it always wins regardless of
   source order. */
.hero .site-header {
  background: transparent;
}
.hero .site-header.is-scrolled {
  background: var(--olive-s2);
}

/* Same transparent-until-scrolled treatment as ".hero .site-header"
   above, for sub-pages with a .page-banner (about, connect, contact,
   donate, join, resources). Those pages don't wrap .site-header in a
   .hero element, so this modifier class does the same job directly —
   see .page-banner above for the other half (the banner rendering at
   the very top of <main>, behind this fixed header, instead of being
   pushed down by .main--offset). */
.site-header--overlay {
  background: transparent;
}
.site-header--overlay.is-scrolled {
  background: var(--olive-s2);
}

.site-header__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  /* Sets the header's overall height — the logo is the tallest thing
     in this row, so this padding is effectively the logo's top/bottom
     breathing room. --header-h (used for the sub-page top offset,
     anchor-scroll offset, etc.) is kept in sync with the header's
     real rendered height automatically by js/script.js, so it never
     needs updating by hand when this changes. Transition lets the
     desktop scrolled-shrink state (see ".is-scrolled" below) animate
     smoothly instead of snapping. */
  padding-block: 0.65rem;
  transition: padding-block 0.3s ease;
  /* max-width and padding-inline come from the .wrap--edge class on
     this element (see the .wrap comment) — the main menu row is
     meant to stretch the full browser width, unlike the content
     sections below it that stay capped at --wrap. */
}

.site-header__logo { display: flex; align-items: center; flex: none; min-width: 0; }
.site-header__logo img {
  display: block;
  width: 160px;
  height: auto;
  object-fit: contain;
  /* Animates the desktop scrolled-shrink state (see ".is-scrolled"
     below) smoothly instead of snapping. */
  transition: width 0.3s ease;
}

.icon-btn {
  background: none;
  border: 0;
  color: var(--paper);
  padding: 0.4rem;
  display: inline-flex;
}

.nav-toggle {
  display: inline-flex;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: 0;
  padding: 0.5rem;
}
.nav-toggle__bar {
  width: 22px; height: 2px;
  background: var(--paper);
  display: block;
}

.primary-nav {
  position: absolute;
  inset-inline: 0;
  top: 100%;
  z-index: 99;
  display: flex;
  flex-direction: column;
  background: var(--olive-s2);
  max-height: 0;
  /* overflow-x stays hidden (this is also what clips the collapsed
     0-height state above); overflow-y is separate so an open menu
     that's taller than the viewport can scroll within itself instead
     of being cut off with no way to reach the last item. */
  overflow-x: hidden;
  overflow-y: auto;
  transition: max-height 0.3s ease;
}
/* A fixed rem value here used to clip the last item (e.g. "Stay
   Informed") once there were enough links to exceed it — sized off
   the actual viewport instead, minus the fixed header's own real
   height (--header-h, kept in sync by js/script.js), so the open
   menu always has exactly the remaining screen to grow into on any
   device, with no magic number to revisit as links are added. */
.primary-nav.is-open { max-height: calc(100vh - var(--header-h, 0px)); }
.primary-nav a {
  padding: 1.1rem var(--gutter);
  border-top: 1px solid rgba(249,247,246,0.12);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  color: var(--paper);
}
.primary-nav__join {
  font-weight: 700;
  color: var(--olive-t1) !important;
  background: transparent;
  border: 1px solid var(--olive-t1);
}
.primary-nav__join:hover { background: var(--olive-t1); color: var(--ink) !important; }

.primary-nav__stay {
  font-weight: 700;
  color: var(--ink) !important;
  background: var(--olive-t1);
  border: 1px solid var(--olive-t1);
}
.primary-nav__stay:hover { background: var(--olive-s1); border-color: var(--olive-s1); color: var(--paper) !important; }

/* .primary-nav a sets a top divider line for the stacked mobile menu —
   override its color here so it doesn't cut into these two buttons'
   own all-around border. */
.primary-nav a.primary-nav__join,
.primary-nav a.primary-nav__stay {
  border-top-color: var(--olive-t1);
}
.primary-nav a.primary-nav__stay:hover {
  border-top-color: var(--olive-s1);
}

@media (min-width: 860px) {
  .site-header__row { padding-block: 1rem; }
  .site-header__logo img { width: 320px; }
  /* Shrinks the header once the page has scrolled past 50px (the
     same ".is-scrolled" toggle — see js/script.js — that fades the
     header's background in on the homepage hero and .page-banner
     pages). Applied on .site-header directly so it takes effect on
     every page regardless of whether ".site-header--overlay" is also
     present. Desktop only — on mobile the logo is already smaller
     than this shrunk size (160px vs. 220px here), so shrinking would
     mean growing it, which doesn't make sense there. */
  .site-header.is-scrolled .site-header__row {
    padding-block: 14px;
  }
  .site-header.is-scrolled .site-header__logo img {
    width: 220px;
  }
  .primary-nav {
    position: static;
    flex: 1 1 auto;
    flex-direction: row;
    max-height: none;
    overflow: visible;
    background: none;
    gap: 2.25rem;
    align-items: center;
    justify-content: flex-end;
  }
  .primary-nav a { border-top: 0; padding: 0; }
  .primary-nav a.primary-nav__join,
  .primary-nav a.primary-nav__stay {
    border-top: 1px solid var(--olive-t1);
  }
  .primary-nav__join,
  .primary-nav__stay {
    padding: 0.9rem 1.7rem !important;
    border-radius: 999px;
    transition: background 0.2s ease;
  }
  .primary-nav__join { margin-left: 0.5rem; }
  .primary-nav__stay { margin-left: -0.75rem; }
  .nav-toggle { display: none; }
}

.main--offset { padding-top: var(--header-h); }
 
.hero__mark {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 1rem;
  position: relative;
  z-index: 2;
  padding: 3rem 1.5rem;
}
.hero__logo {
  width: min(90%, 420px);
  height: auto;
  margin-inline: auto;
}
.hero__leaf { color: var(--orange); }
 
.hero__cta {
  align-self: center;
  z-index: 2;
  margin-bottom: 1.25rem;
}

.hero__scroll {
  align-self: center;
  margin-bottom: 1.75rem;
  color: var(--paper);
  opacity: 0.8;
  z-index: 2;
  animation: bob 2.4s ease-in-out infinite;
}
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}
 
/* =========================================================
   MISSION
   ========================================================= */
.mission { padding-block: clamp(3.5rem, 8vw, 5.5rem); }
.mission__inner {
  max-width: 46rem;
  margin-inline: auto;
  text-align: center;
}
.mission__title {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(2.23756rem, 4.94618vw, 3.17969rem);
  line-height: 1.2;
  margin: 0 0 2.7rem;
}
.mission__lede {
  color: var(--charcoal);
  font-size: 1.05rem;
  margin: 0;
}
.mission__lede + .mission__lede {
  margin-top: 1rem;
}
.mission__inner .btn {
  margin-top: 2.7rem;
}
 
/* =========================================================
   SERVICES ICON GRID
   ========================================================= */
.services {
  background: var(--navy-s1);
  color: var(--paper);
  /* Deliberately more generous than the site's shared section-padding
     token (clamp(3.5rem, 8vw, 5.5rem), used by .mission/.partners/etc.
     above and below this section) — the same measured gap reads
     noticeably tighter against this section's dark, saturated
     background than it does against the light sections around it, so
     this is bumped up to restore an equal-feeling amount of breathing
     room top and bottom. */
  padding-block: clamp(4.5rem, 10vw, 6.5rem);
}
.services__grid {
  display: grid;
  /* All four core tiles (About, Our Community, Resources, Updates) in a
     single row on desktop; see the breakpoints below for how this steps
     down to a 2x2 grid on tablet and a single column on mobile. */
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem;
}
.service-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  text-align: left;
  padding: 1.4rem;
  border-radius: 10px;
  overflow: hidden;
  color: var(--paper);
  background-color: var(--navy-s2);
  background-image:
    repeating-linear-gradient(135deg, rgba(249,247,246,0.06) 0px, rgba(249,247,246,0.06) 1px, transparent 1px, transparent 14px);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.service-tile:hover,
.service-tile:focus-visible {
  transform: translateY(-4px);
  box-shadow: 0 12px 28px rgba(0,0,0,0.28);
}
.service-tile::after {
  content: "";
  position: absolute;
  top: 1.4rem;
  right: 1.4rem;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: 1px solid rgba(249,247,246,0.4);
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='12' height='12' fill='none' stroke='%23F9F7F6' stroke-width='2'%3E%3Cpath d='M7 17L17 7M9 7h8v8'/%3E%3C/svg%3E") no-repeat center;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}
.service-tile:hover::after,
.service-tile:focus-visible::after {
  background-color: var(--orange);
  border-color: var(--orange);
}
/* Photo-background tiles (About, Our Community, Resources, Updates) —
   the image + a bottom-weighted gradient are set together via an
   inline background-image so each tile can use its own photo, while
   this class handles the shared sizing/legibility treatment. */
.service-tile--photo {
  justify-content: flex-end;
  background-color: var(--navy-s2);
  background-size: cover;
  background-repeat: no-repeat;
}
.service-tile--photo .service-tile__text {
  color: rgba(249,247,246,0.85);
}
.service-tile__title {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: 1.2rem;
  line-height: 1.2;
  margin: 0 0 0.25rem;
}
.service-tile__text {
  margin: 0;
  color: rgba(249,247,246,0.8);
  font-size: 0.85rem;
  line-height: 1.4;
}

/* Tablet: step the single row down to a 2x2 grid. Reuses the same
   860px breakpoint as the header's mobile/desktop nav switch. */
@media (max-width: 860px) {
  .services__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .service-tile { aspect-ratio: auto; min-height: 190px; }
}
@media (max-width: 420px) {
  .services__grid { grid-template-columns: 1fr; }
}
 
/* =========================================================
   QUOTE BLOCK
   ========================================================= */
.quote-block {
  background: var(--navy-s1);
  /* Slightly more than the shared clamp(3.5rem, 8vw, 5.5rem) token
     used elsewhere (dispatches, partners, etc.) — those sections'
     padding ends at a line of text, whose line-height leaves a bit of
     visible space above the letters themselves before the padding
     "runs out", so the same nominal value reads as a bit more
     generous there. Here the padding runs straight into the photo's
     and card's hard edges with no such buffer, so it's bumped up to
     look like an equal amount of breathing room top and bottom. */
  padding-block: clamp(4rem, 8.5vw, 6rem);
}
.quote-block__grid {
  display: grid;
  /* 3:4 column ratio — the text card gets the larger share (was
     1fr 1fr). Portrait (see ".quote-block__portrait" order below,
     which visually moves it in front of the card) takes the smaller
     3fr share, the text card the larger 4fr. */
  grid-template-columns: 3fr 4fr;
  gap: 2.5rem;
  align-items: stretch;
}
.quote-block__card {
  background: var(--paper);
  color: var(--charcoal);
  padding: clamp(2rem, 4vw, 3rem);
  border-radius: 12px;
}
.quote-block__heading {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  /* Fixed at a specific pixel size (was a responsive clamp that used
     to share the site's shared "major heading" scale). */
  font-size: 30px;
  line-height: 1.2;
  color: var(--olive-t1);
  margin: 0 0 1.25rem;
}
.quote-block__card blockquote {
  margin: 0 0 1.5rem;
  font-family: var(--font-display);
  /* Fixed at a specific pixel size (was 1.05rem, matching
     .mission__lede's size — the intro section's body text). */
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
}
/* Large decorative quotation mark hanging just above and to the left
   of the pull-quote's first word — a soft tint of the heading's
   olive-t1, styled after the testimonial quotes on
   lngandhealthcare.ca. Absolutely positioned (rather than sitting in
   normal flow above the text) so it can tuck in close to "Human"
   instead of pushing the paragraph down. */
.quote-block__card blockquote { position: relative; }
.quote-block__card blockquote::before {
  content: "\201C";
  position: absolute;
  left: -2.1rem;
  top: -1.6rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 4.5rem;
  line-height: 1;
  color: rgba(145,174,95,0.6);
  pointer-events: none;
}
/* Only the opening paragraph reads as the italicized "pull quote";
   the rest of the testimonial is set in the regular (non-italic)
   body style — in --font-body rather than the inherited
   --font-display, keeping heading and body copy on separate tokens
   even though both currently resolve to the same Archivo family. */
.quote-block__card blockquote p {
  margin: 0 0 1rem;
  font-style: normal;
  font-family: var(--font-body);
}
.quote-block__card blockquote p:first-of-type {
  font-style: italic;
  font-family: var(--font-display);
}
.quote-block__card blockquote strong { font-weight: 700; }
/* Same treatment as .update-card__text a / .contact-block__lede a
   elsewhere on the site — inherits the surrounding text color rather
   than switching to orange, with just the underline picking up the
   orange accent, so these two citation links read as links without
   standing out from the rest of the quote. */
.quote-block__card blockquote a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--orange-s1);
  /* Browser default underline thickness reads as a near-invisible
     hairline against the body copy's own weight — bumped up so it's
     clearly visible without turning heavy. */
  text-decoration-thickness: 1.5px;
  text-underline-offset: 2px;
}
.quote-block__name {
  /* Same font as the blockquote content above it — was var(--font-mono). */
  font-family: var(--font-display);
  /* Matches .quote-block__card blockquote's size (was 0.85rem). */
  font-size: 16px;
  margin: 0 0 1.75rem;
  color: var(--charcoal);
}
.quote-block__name span { color: var(--taupe); }
/* Scoped to .quote-block__card so this beats .btn's own `display:
   inline-block` (same specificity, but .btn is declared later in this
   file, so without the extra scoping it was winning the cascade and
   silently cancelling the centering below). .btn is inline-block by
   default, so centering it takes an explicit width + margin rather
   than just text-align on the card. */
.quote-block__card .quote-block__cta {
  display: block;
  width: max-content;
  margin-inline: auto;
}

.quote-block__portrait {
  /* Moves this ahead of .quote-block__card in visual/auto-placement
     order (it comes second in the HTML, after the card, which keeps
     the quote read first for screen readers) so the photo renders in
     the grid's first — now left-hand — column. */
  order: -1;
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  /* No aspect-ratio or align-self override here (used to opt out of
     the grid row's stretch, sizing itself as a squarer 1:1 box
     instead) — now stretches via .quote-block__grid's own
     align-items: stretch, so this matches .quote-block__card's height
     exactly regardless of how much text the card holds. The <img>
     below (object-fit: cover) fills whatever height that ends up
     being. */
  background:
    linear-gradient(200deg, rgba(30,24,21,0.5), rgba(30,24,21,0.15)),
    linear-gradient(120deg, #487EB4 0%, #2D557D 55%, #1B3652 100%);
}
.quote-block__portrait img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Crops out most of the empty canopy that used to fill the top of
     the frame, while showing her whole hand and roughly the top 4
     inches of the bag she's carrying, rather than running all the
     way down to her feet. */
  object-position: center 65%;
}
 
@media (max-width: 760px) {
  .quote-block__grid { grid-template-columns: 1fr; }
  .quote-block__portrait {
    min-height: 22rem;
    /* Matches the source photo's own proportions so the crop doesn't
       cut into her head — any object-fit cropping instead trims the
       bottom, kept in check by the top alignment below. */
    aspect-ratio: 520 / 640;
    /* Inset within .wrap's side padding, same as .quote-block__card
       below it (was full-bleed/edge-to-edge here, which put the photo
       flush with the screen edges while the quote card kept its usual
       side margin — inconsistent with each other). */
  }
  .quote-block__portrait img { object-position: center top; }
}
 
.btn {
  display: inline-block;
  /* Sized/typeset to match the "Join the Alliance" / "Stay Informed"
     pills in the header nav (see .primary-nav__join/.primary-nav__stay
     below) so every button on the site — in the hero, forms, footer,
     wherever — reads as the same button. */
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: normal;
  text-transform: none;
  padding: 0.9rem 1.7rem;
  border-radius: 999px;
  transition: transform 0.15s ease, background 0.2s ease, border-color 0.2s ease;
}
.btn--line {
  border: 1px solid rgba(30,24,21,0.25);
  color: inherit;
}
.btn--line:hover { border-color: var(--orange); transform: translateY(-1px); }
.btn--outline {
  border: 1px solid var(--olive-t1);
  background: var(--olive-t1);
  color: var(--ink);
  margin-top: 2.5rem;
  font-family: var(--font-body);
  font-style: normal;
  font-weight: 700;
  border-radius: 999px;
}
.btn--outline:hover {
  background: var(--olive-s1);
  border-color: var(--olive-s1);
  color: var(--paper);
}
 
/* =========================================================
   IMAGE BANDS
   ========================================================= */
.band {
  height: clamp(12rem, 30vw, 20rem);
  background-size: cover;
  background-position: center;
}
.band--canopy {
  background:
    linear-gradient(180deg, rgba(14,29,44,0.35), rgba(14,29,44,0.6)),
    radial-gradient(90% 120% at 20% 0%, #487EB4 0%, transparent 60%),
    linear-gradient(135deg, #2D557D 0%, #0E1D2C 100%);
}
.band--river {
  background:
    linear-gradient(180deg, rgba(14,29,44,0.2), rgba(14,29,44,0.55)),
    radial-gradient(80% 100% at 80% 20%, #487EB4 0%, transparent 55%),
    linear-gradient(135deg, #2D557D 0%, #0E1D2C 100%);
}
.band--roots {
  background:
    linear-gradient(180deg, rgba(14,29,44,0.25), rgba(14,29,44,0.6)),
    radial-gradient(90% 110% at 60% 90%, #7C5440 0%, transparent 55%),
    linear-gradient(135deg, #482F22 0%, #0E1D2C 100%);
}
 
/* =========================================================
   PARTNERS / MEMBER MAP
   ========================================================= */
.partners { padding-block: clamp(3.5rem, 8vw, 5.5rem); text-align: center; }
.section-title {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  /* Matches .mission__title (the H1) exactly — size, line-height and
     bottom margin — so H1 and H2 share one consistent heading style. */
  font-size: clamp(2.23756rem, 4.94618vw, 3.17969rem);
  line-height: 1.2;
  margin: 0 0 2.7rem;
}
.partners__lede {
  max-width: 40rem;
  /* 2rem base gap + ~40px to open up more breathing room before the map */
  margin: 0 auto 4.5rem;
  color: var(--charcoal);
  font-size: 1.05rem;
}
.partners__row {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.25rem 2.25rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--charcoal);
  letter-spacing: 0.03em;
}

.member-map {
  display: grid;
  grid-template-columns: minmax(260px, 320px) 1fr;
  gap: 0;
  text-align: left;
  background: var(--paper);
  border-radius: 14px;
  margin-bottom: 1rem;
}

/* On large desktop monitors, break the map out of the .wrap max-width
   so it spans the full browser width — the heading/intro/button
   around it stay constrained to the normal content width. */
@media (min-width: 1300px) {
  .member-map {
    width: calc(100vw - (var(--gutter) * 2));
    margin-left: 50%;
    transform: translateX(-50%);
  }
}

.member-map__sidebar {
  /* Layout only — no background or radius here. Those used to live on
     this <aside> and, because it's a grid item that stretches to match
     .member-map__main's height (the map plus the "Join the Alliance"
     button — see that class below) by default, the color stretched
     with it: with every country collapsed the accordion's own content
     is shorter than that column, so the navy ran on past the last
     country into empty space at the bottom. Moving the visual box to
     .member-accordion below (which is sized to its own content, not
     stretched) stops the color exactly where the accordion ends. This
     element still stretches for layout purposes — that's what lets the
     row grow to fit a tall open panel — it just has nothing to paint. */
}

.member-accordion {
  /* Navy (matching the accordion triggers' own default background)
     rather than the old flat cream — this is what shows through behind
     each open panel's rounded corners below (see
     .member-accordion__panel's own comment). Lives here, on the actual
     list of countries, rather than on .member-map__sidebar (the <aside>
     wrapping it) — see that class's own comment for why: in short, this
     way the color only ever covers the accordion's own content height,
     never the full stretched column. */
  background: var(--navy-s2);
  /* Top-left and bottom-left are outer corners of the whole .member-map
     component (the accordion's own left edge is the component's left
     edge, top and bottom edges likewise), so both get the same 14px
     radius as .member-map's outer corners — otherwise they read as a
     sharp square notch against the rounded column/card around them.
     (A previous version of this rule only set bottom-left here, leaving
     the top-left corner square by mistake — every corner of this
     component should match.) */
  border-top-left-radius: 14px;
  border-bottom-left-radius: 14px;
  /* Rounds the inner corner where the accordion meets the map at the
     top (desktop two-column layout only — see the max-width: 860px
     override below, where this drops beneath the map instead). Matches
     .member-map's own outer radius so it reads as one continuous curve;
     .member-map__stage sits on the same --paper background as
     .member-map itself, so the cut-away notch blends in rather than
     exposing a mismatched color. */
  border-top-right-radius: 14px;
  /* Same idea as the top corner above, but for where this meets the map
     at the bottom — only reached when an open panel's content is tall
     enough to fill the column's full height. */
  border-bottom-right-radius: 14px;
  /* Clips the accordion triggers' own square navy backgrounds (they're
     opaque children, drawn on top of this element) to the rounded
     corners above — without this, a trigger just paints right over the
     corner and the radius above has no visible effect. */
  overflow: hidden;
}

.member-accordion__item {
  border-bottom: 1px solid rgba(249,247,246,0.12);
}
.member-accordion__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: var(--navy-s2);
  border: 0;
  padding: 1.1rem 1.4rem;
  text-align: left;
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: 1.02rem;
  color: var(--paper);
  transition: background 0.15s ease;
}
.member-accordion__trigger:hover { background: var(--navy-s1); }
/* Defensive: bake the outer corner radius directly into the first/last
   trigger's own background too, rather than relying solely on
   .member-accordion's overflow:hidden clip above. That clip is correct
   in principle, but a flush edge-to-edge child like a trigger button
   can leave a hairline square sliver unclipped at a rounded corner in
   some browsers/zoom levels — matching the radius on the painted
   element itself avoids that regardless of the clip. Mirrors
   .member-accordion's own corner radii above (all four corners, at
   every width — see that rule). */
.member-accordion__item:first-child .member-accordion__trigger {
  border-top-left-radius: 14px;
  border-top-right-radius: 14px;
}
.member-accordion__item:last-child:not(.is-open) .member-accordion__trigger {
  border-bottom-left-radius: 14px;
  border-bottom-right-radius: 14px;
}
.member-accordion__item.is-open .member-accordion__trigger {
  background: var(--navy-s1);
  color: var(--paper);
}
.member-accordion__icon {
  position: relative;
  width: 14px;
  height: 14px;
  flex: none;
}
.member-accordion__icon::before,
.member-accordion__icon::after {
  content: "";
  position: absolute;
  background: currentColor;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.member-accordion__icon::before { width: 14px; height: 2px; }
.member-accordion__icon::after {
  width: 2px;
  height: 14px;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.member-accordion__item.is-open .member-accordion__icon::after {
  transform: translate(-50%, -50%) rotate(90deg);
  opacity: 0;
}

.member-accordion__panel {
  /* A floating beige "card" inset from the (now navy) sidebar on all
     four sides, rather than a flush full-bleed panel — that inset is
     what reveals the navy sidebar background behind each of its
     rounded corners. */
  background: var(--cream);
  border-radius: 12px;
  margin: 0.7rem;
  padding: 1.2rem 1.4rem 1.4rem;
}
.member-accordion__label {
  margin: 0 0 0.5rem;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--orange-s1);
}
.member-accordion__orgs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.member-accordion__orgs li {
  font-size: 0.9rem;
  /* Combines with .member-accordion__orgs' own 1.1rem flex gap above
     each item (Netherlands is the only country where this is visible
     today, since it's the only one with two separate blurb entries) —
     was calc(1.8rem + 40px), reduced down to a flat 20px. */
  padding-bottom: 20px;
  border-bottom: 1px dashed rgba(30,24,21,0.15);
}
.member-accordion__orgs li:last-child { border-bottom: 0; padding-bottom: 0; }
.member-accordion__org-logo {
  display: block;
  max-width: 260px;
  max-height: 100px;
  width: auto;
  height: auto;
  /* Matches .member-accordion__panel's 1.2rem top padding, so the gap
     below the logo (before the blurb text) equals the gap above it. */
  margin: 0 0 1.2rem;
  object-fit: contain;
}
/* Wraps a primary + secondary logo pair side by side (see
   "secondaryLogo" in js/member-map.js / members-data.js) — used for an
   org that's an initiative of another org (e.g. PaRx / BC Parks
   Foundation) rather than a separate member. Carries the 1.2rem
   bottom margin that a lone .member-accordion__org-logo normally has
   itself, so spacing before the blurb text stays the same either way. */
.member-accordion__org-logos {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin: 0 0 1.2rem;
}
.member-accordion__org-logos .member-accordion__org-logo { margin: 0; }
/* Close to the primary logo's own height (PaRx's is dialed down to 72px
   via its own logoMaxHeight — see members-data.js) so the two read as
   comparable in weight, without quite matching it exactly. */
.member-accordion__org-logo--secondary {
  max-height: 70px;
  max-width: 170px;
}

.member-accordion__org-head {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
}
.member-accordion__org-name { font-weight: 600; font-size: 15.2031px; }
.member-accordion__org-city {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--charcoal);
  white-space: nowrap;
}
.member-accordion__org-blurb {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--charcoal);
}
.member-accordion__org-blurb + .member-accordion__org-blurb {
  margin-top: 0.7rem;
}
.member-accordion__org-blurb a {
  color: var(--orange-s1);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.member-accordion__org-blurb a:hover {
  color: var(--orange);
}
.member-accordion__org-figure {
  /* Equal top/bottom spacing (margins collapse with the surrounding
     paragraphs' own margins, so both sides land at exactly 12px). */
  margin: 12px 0;
}
.member-accordion__org-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
}
.member-accordion__org-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--orange-s1);
  border-bottom: 1px solid var(--orange-s1);
  padding-bottom: 2px;
}
.member-accordion__org-link svg { flex: none; }

.member-accordion__note {
  /* No margin/padding of its own — Italy (the only country using this
     today) already gets its full 1.2rem gap from .member-accordion__
     org-logo's own bottom margin just above, matching the panel's
     1.2rem top padding above the logo for equal spacing on both
     sides. */
}
.member-accordion__note-text {
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--charcoal);
}
.member-accordion__note-text a {
  color: var(--orange-s1);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.member-accordion__note-text a:hover {
  color: var(--orange);
}
.member-accordion__note-figure {
  margin: 0.7rem 0 0;
}
.member-accordion__note-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
}

.member-accordion__org-row {
  display: flex;
  gap: 1.5rem;
}
.member-accordion__org-row-item {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.member-accordion__org-row-item .member-accordion__org-logo {
  max-width: 140px;
  max-height: 80px;
  min-width: 0;
  min-height: 0;
  margin: 0 0 1.2rem;
}

/* Wraps .member-map__stage + the "Join the Alliance" button as one
   grid item, so the button sits immediately below the map and moves
   with it — never with .member-map__sidebar, which can grow taller
   (an opened accordion panel) without shifting the button. */
.member-map__main {
  display: flex;
  flex-direction: column;
  /* align-items defaults to "stretch", so .member-map__stage (the
     only other stretch-eligible child) still fills the column's full
     width like before; the button overrides this itself below. */
  /* align-self: start (rather than the grid default of "stretch") is
     what makes this column size itself from .member-map__stage's own
     aspect-ratio plus the button's height, independent of the row's
     height — if it stretched instead, a taller sidebar would stretch
     this column too and drag the button down with it. */
  align-self: start;
}
.member-map__main > .btn {
  /* Un-stretch the button back to its own intrinsic (pill) width and
     center it under the map, rather than the full column width that
     align-items: stretch would otherwise give it. */
  align-self: center;
}

.member-map__stage {
  position: relative;
  background: var(--paper);
  display: flex;
  align-items: flex-start;
  /* Matches the SVG's own cropped viewBox proportions (CROP_WIDTH x
     HEIGHT in js/member-map.js: 850 x 501, now that HEIGHT comes from
     the Miller-projected latitude range rather than a plain linear
     one) so this has a fixed, predictable height instead of one
     implied by the rendered SVG — that's what lets .member-map__sidebar
     stretch to match .member-map__main's height exactly. Keep this in
     sync if those constants ever change. */
  aspect-ratio: 850 / 501;
}
.member-map__svg {
  display: block;
  width: 100%;
  height: auto;
}

.member-map__country {
  fill: var(--sand);
  stroke: var(--cream);
  stroke-width: 0.6;
  opacity: 0.55;
}
.member-map__country--member {
  fill: var(--olive);
  opacity: 1;
  cursor: pointer;
  transition: fill 0.2s ease;
}
.member-map__country--member:hover {
  fill: var(--orange-s1);
}
.member-map__country--member.is-selected,
.member-map__country--member.is-selected:hover {
  fill: var(--orange);
}
.member-map__country--member:focus-visible {
  outline: 2px solid var(--orange-s1);
  outline-offset: -2px;
}

.member-map__marker { cursor: pointer; }
.member-map__marker-hit { fill: transparent; }
.member-map__marker-glyph {
  fill: var(--orange);
  /* Hairline stroke around the pin's own outline — literal white
     (not var(--paper), which is slightly off-white) so it reads as a
     crisp, consistent edge over any of the map's own fill colors. */
  stroke: #fff;
  stroke-width: 1.2;
}
/* The white dot in the pin's head stays plain white always — it's
   not part of the hover color change below. */
.member-map__marker-dot { fill: #fff; }
.member-map__marker:hover .member-map__marker-glyph,
.member-map__marker:focus .member-map__marker-glyph {
  fill: var(--orange-s1);
}
.member-map__marker:focus { outline: none; }
.member-map__marker:focus-visible .member-map__marker-glyph {
  fill: var(--orange-s1);
}

/* Marker drop-in — pins start faded out and shifted above their
   final position, then ease down with a slight overshoot/bounce the
   first time the map scrolls into view (see the IntersectionObserver
   in js/member-map.js, right after the markers are built). This
   lives on the separate inner <g> the JS wraps around each pin's
   visuals, not on .member-map__marker itself — that outer group's
   own translate(x,y) is a presentation ATTRIBUTE (the marker's real
   map position), and a CSS transform on that same element would
   replace it outright rather than combine with it. */
.member-map__marker-inner {
  opacity: 0;
  transform: translateY(-26px);
  transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.4s ease;
}
.member-map__marker-inner.is-visible {
  opacity: 1;
  transform: translateY(0);
}
/* The JS already skips setting up the observer under reduced motion,
   so in practice markers never start hidden for these users — this
   is just a backstop so an inner group still renders fully visible
   immediately even if ".is-visible" is never added. */
@media (prefers-reduced-motion: reduce) {
  .member-map__marker-inner {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

.member-map__tooltip {
  position: absolute;
  z-index: 5;
  /* --tip-shift: a horizontal nudge (px) set from JS (positionTooltip()
     in js/member-map.js) when the tooltip would otherwise overflow past
     the map stage's left/right edge (e.g. Australia's marker sits close
     to the stage's right edge). Defaults to 0 = perfectly centered on
     the marker, same as before. The ::after arrow below compensates so
     it still points at the actual marker even when the box is shifted. */
  --tip-shift: 0px;
  transform: translate(calc(-50% + var(--tip-shift)), calc(-100% - 12px));
  /* Light card rather than the site's usual dark navy — this now shows
     an org's logo image (see .member-map__tooltip img below), and most
     logos are drawn for a light background the way they already
     appear elsewhere on the site (the accordion sidebar, the
     Supporters strip). The name/country that used to be visible text
     here now live only in that image's alt text (see showTip() in
     js/member-map.js) — strong/span below are a fallback for the rare
     org with no logo, so they're recolored for this light background
     too. */
  background: var(--paper);
  color: var(--ink);
  padding: 0.5rem 0.7rem;
  border-radius: 3px;
  font-size: 0.82rem;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 6px 18px rgba(0,0,0,0.25);
}
.member-map__tooltip img {
  display: block;
  max-width: 208px;
  max-height: 64px;
  width: auto;
  height: auto;
  object-fit: contain;
}
.member-map__tooltip strong {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
}
.member-map__tooltip span {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--charcoal);
  margin-top: 0.15rem;
}
.member-map__tooltip::after {
  content: "";
  position: absolute;
  top: 100%;
  /* Offsets by --tip-shift in the opposite direction of the box's own
     shift above, so the arrow stays pinned to the actual marker
     position even when the tooltip box has been nudged sideways to
     stay on-stage. */
  left: calc(50% - var(--tip-shift));
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: var(--paper);
}

/* Side-by-side layout for a country with two markers close enough
   together to share one accordion panel (Italy, Netherlands — see
   layoutPairedTooltips() in js/member-map.js). Each box sits flush
   against a shared center point (--tip-shift is unused here, always
   0) instead of centering on its own marker, and each arrow points
   in at that same shared point instead of centering under its own
   box — the two arrows end up converging where the boxes almost
   touch. */
.member-map__tooltip--pair-left {
  transform: translate(-100%, calc(-100% - 12px));
}
/* The arrow (border-triangle trick — see the base ::after above) has no
   width of its own; its "left" position is the triangle's own left
   edge, and the visible tip sits 6px in from that (half of the 6px
   border used on all four sides). So "left" needs to land 6px short of
   wherever the tip should actually point. Left box's right edge already
   sits (almost) exactly on the shared center point above (see
   layoutPairedTooltips() in js/member-map.js) — was a flat "16px",
   which only happened to look right for two similarly-sized boxes and
   drifted the tip noticeably off-marker whenever one box was much
   narrower than the other (e.g. Italy's two very different logo
   widths). */
.member-map__tooltip--pair-left::after {
  left: calc(100% - 6px);
  transform: none;
}
.member-map__tooltip--pair-right {
  transform: translate(0, calc(-100% - 12px));
}
/* Same reasoning as pair-left::after above, mirrored: the right box's
   left edge already sits on the shared center point, so the tip only
   needs to pull back 6px from "left: 0". */
.member-map__tooltip--pair-right::after {
  left: -6px;
  transform: none;
}

@media (max-width: 860px) {
  .member-map {
    grid-template-columns: 1fr;
  }
  .member-map__sidebar {
    border-right: 0;
    order: 2;
  }
  .member-accordion {
    /* Below 860px the accordion drops beneath the map into its own
       stacked row (see .member-map__main/.member-map__sidebar order
       swap above), so its top corners are no longer an inner seam
       against the map — they're already rounded unconditionally in the
       base rule above (all four corners match .member-map's own 14px
       radius), so nothing extra is needed here for that. This border is
       what visually separates the accordion "card" from the map above
       it now that they've swapped from a side-by-side to a stacked
       layout. */
    border-bottom: 1px solid rgba(30,24,21,0.12);
  }
  .member-map__main { order: 1; }
  .member-map__tooltip { white-space: normal; max-width: 12rem; }
  /* Keep the logo from pushing past the tooltip's own mobile max-width
     above (12rem) — .member-map__tooltip img's base max-width (208px)
     is wider than that container once its 0.7rem-each-side padding is
     subtracted, which let a wide logo poke past its own box's edge on
     mobile. A fixed calc() here (not a percentage) is deliberate: the
     tooltip has no explicit width of its own — it shrinks to fit its
     content — so a percentage max-width on the image would depend on
     the very size that depends on it, which collapses the image to
     0×0 in Chromium instead of constraining it. */
  .member-map__tooltip img { max-width: calc(12rem - 1.4rem); }
}

/* =========================================================
   SUPPORTERS — logo strip, sourced from js/supporters-data.js
   (kept separate from js/members-data.js so this list can be
   updated independently of the map accordion)
   ========================================================= */
.supporters {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
  text-align: center;
  /* Matches the map's non-member "land" fill exactly — that fill is
     --sand rendered at 55% opacity over the page background (see
     .member-map__country), not raw --sand, so it's mixed the same way
     here rather than just reusing the token directly. */
  background: color-mix(in srgb, var(--sand) 55%, var(--paper));
}
.supporters__grid {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 2.5rem 3rem;
}
.supporters__logo {
  max-width: 300px;
  max-height: 80px;
  width: auto;
  height: auto;
  min-width: 0;
  min-height: 0;
  object-fit: contain;
}
.supporters__logo-link {
  display: inline-flex;
  transition: opacity 0.2s ease;
}
.supporters__logo-link:hover,
.supporters__logo-link:focus-visible {
  opacity: 0.7;
}
/* .section-title's shared margin-bottom (2.7rem — see that class above)
   wants 10px more breathing room here specifically. Added on top of the
   shared value (rather than replacing it) so it stays scoped to just
   this 10px, without locking in a fixed rem value that would drift out
   of sync if .section-title's own margin ever changes. Scoped to
   .supporters only so "Our Community"'s heading is untouched. */
.supporters .section-title { margin-bottom: calc(2.7rem + 10px); }

/* =========================================================
   DISPATCHES
   ========================================================= */
.dispatches { padding-block: clamp(3.5rem, 8vw, 5.5rem); text-align: center; }
/* .section-title's shared margin-bottom (2.7rem, used by "Our Community"
   and "Supporters" too) reads noticeably tighter here than the section's
   own top/bottom padding above and below it — bumped up to the same
   clamp() so the heading-to-grid gap matches. Scoped to .dispatches only
   so the other two sections' headings are untouched. */
.dispatches .section-title { margin-bottom: clamp(3.5rem, 8vw, 5.5rem); }
.update-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2.5rem;
  text-align: left;
  /* Same reasoning as .section-title above — was a much smaller 2.5rem,
     now matches the section's own padding-block gap on either side of
     it. */
  margin-bottom: clamp(3.5rem, 8vw, 5.5rem);
}
.update-card__tile {
  display: block;
  color: inherit;
}
.update-card__media {
  display: block;
  aspect-ratio: 3 / 2;
  border-radius: 8px;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  margin-bottom: 1rem;
  transition: opacity 0.2s ease;
}
.update-card__tile:hover .update-card__media,
.update-card__tile:focus-visible .update-card__media {
  opacity: 0.88;
}
.update-card__title {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: 1.3rem;
  margin: 0 0 0.6rem;
  line-height: 1.3;
}
.update-card__tile:hover .update-card__title,
.update-card__tile:focus-visible .update-card__title {
  text-decoration: underline;
  text-decoration-color: var(--orange-s1);
  text-underline-offset: 3px;
}
.update-card__text {
  margin: 0;
  color: var(--charcoal);
  font-size: 0.98rem;
  line-height: 1.55;
}
.update-card__text a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--orange-s1);
  text-underline-offset: 2px;
}

/* .btn--outline's own margin-top (2.5rem — meant for the form contexts
   it's normally paired with, e.g. right under a newsletter input) stacks
   on top of .update-grid's margin-bottom above, making this particular
   gap taller than the section's other, matching gaps (top padding to
   heading, heading to grid, bottom padding). Zeroed here so the grid's
   own margin is the only spacing, matching those. Scoped to .dispatches
   only — every other .btn--outline on the site keeps its usual
   margin-top. */
.dispatches .btn--outline { margin-top: 0; }

@media (max-width: 760px) {
  .update-grid { grid-template-columns: 1fr; }
}
 
/* =========================================================
   FOOTER
   ========================================================= */
.site-footer {
  background: var(--navy-s2);
  color: rgba(249,247,246,0.85);
}

/* Full-bleed, same as the header/main menu — see .wrap--edge on
   .footer-main and .footer-bottom in the HTML (added alongside
   .wrap) for the actual max-width/padding rules. */

.footer-main {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: clamp(2rem, 6vw, 4rem);
  padding-block: clamp(2.5rem, 6vw, 3.5rem);
}

.footer-col {
  display: flex;
  flex-direction: column;
}

.footer-col--about {
  justify-content: space-between;
  gap: 2.5rem;
}

.footer-nav {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  font-size: 0.95rem;
}
.footer-nav a { font-weight: 700; }
.footer-nav a:hover { color: var(--orange-t1); }
.footer-nav .btn {
  align-self: flex-start;
  margin-top: 0.4rem;
}

.footer-brand {
  display: flex;
  flex-wrap: wrap;
  /* More breathing room between the INPA mark and the CCGHC logo
     beside it than the surrounding nav's gap. */
  gap: 2.5rem;
  align-items: center;
}
.footer-brand__logo {
  width: min(100%, 190px);
  height: auto;
  flex: none;
}
.footer-brand__logo--ccghc {
  width: 100%;
  height: auto;
}
.footer-brand__logo-link {
  display: inline-flex;
  flex: none;
  width: min(100%, 190px);
  transition: opacity 0.2s ease;
}
.footer-brand__logo-link:hover {
  opacity: 0.75;
}
.footer-brand a {
  display: inline-flex;
  flex: none;
  /* Bumped up from 170px so the CCGHC logo reads a bit larger next to
     the INPA mark. */
  width: min(100%, 200px);
  transition: opacity 0.2s ease;
}
.footer-brand a:hover {
  opacity: 0.75;
}

.footer-newsletter__heading {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  /* Matches .update-card__title's size, so "Stay connected" reads at
     the same scale as the Updates section's card headings. */
  font-size: 1.3rem;
  margin: 0 0 0.6rem;
}

.footer-newsletter__lede {
  font-size: 0.9rem;
  color: rgba(249,247,246,0.75);
  line-height: 1.5;
  margin: 0 0 1.1rem;
}

.footer-newsletter__form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.footer-newsletter__fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.footer-newsletter__fields input[type="text"],
.footer-newsletter__form > input[type="email"] {
  background: rgba(249,247,246,0.08);
  border: 1px solid rgba(249,247,246,0.3);
  color: var(--paper);
  padding: 0.7rem 0.9rem;
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 0.9rem;
}
/* Inside the row-flex __fields wrapper, flex-basis sizes width as
   intended. The email input is a direct child of the column-flex
   form instead, so it just needs full width, not a flex-basis (which
   on the column's main axis would set its HEIGHT, not its width). */
.footer-newsletter__fields input[type="text"] {
  flex: 1 1 8rem;
}
.footer-newsletter__form > input[type="email"] {
  width: 100%;
}
.footer-newsletter__fields input[type="text"]::placeholder,
.footer-newsletter__form > input[type="email"]::placeholder { color: rgba(249,247,246,0.5); }
.footer-newsletter__form .btn {
  /* The form's flex gap (0.75rem) already adds space above every
     field; this tops it up to 1.1rem so the gap above the Sign Up
     button matches the space above the Name fields (the lede's
     margin-bottom, also 1.1rem). */
  margin-top: 0.35rem;
  align-self: flex-start;
}

.footer-newsletter__status {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  min-height: 1.1em;
}
.footer-newsletter__status[data-state="success"] { color: var(--olive-t1); }
.footer-newsletter__status[data-state="error"] { color: var(--orange-t1); }

.footer-newsletter__caption {
  font-size: 0.78rem;
  color: rgba(249,247,246,0.55);
  line-height: 1.5;
  margin: 0.85rem 0 0;
}
.footer-newsletter__caption a { text-decoration: underline; }

.footer-acknowledgment {
  /* Matches .footer-newsletter__lede's size and line-height, so this
     reads at the same scale as the "Share your details..." wording
     above it. */
  font-size: 0.9rem;
  color: rgba(249,247,246,0.65);
  line-height: 1.5;
  margin: 1.5rem 0 0;
}
.footer-registration {
  font-size: 0.82rem;
  color: rgba(249,247,246,0.65);
  line-height: 1.6;
  margin: 1.5rem 0 0;
}
/* Same treatment as .footer-registration just below it — matching
   small-print weight/color — but with a tighter gap from that line
   than from .footer-acknowledgment above, so the two read as a paired
   set of credit/registration lines rather than separate blocks. */
.footer-support {
  font-size: 0.82rem;
  color: rgba(249,247,246,0.65);
  line-height: 1.6;
  margin: 1.5rem 0 0;
}
/* Inherits the surrounding footer text color, with an underline in
   that same color (no orange accent here — the orange underline on the
   dark footer read too seasonal/Hallowe'eny). Hover dims it slightly,
   matching the footer logo links. */
.footer-support a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 2px;
  transition: opacity 0.2s ease;
}
.footer-support a:hover {
  opacity: 0.75;
}
.footer-support + .footer-registration {
  margin-top: 0.3rem;
}
.footer-acknowledgment em,
.footer-registration em { color: rgba(249,247,246,0.45); font-style: italic; }

@media (max-width: 760px) {
  .footer-main { grid-template-columns: 1fr; }
}
 
/* =========================================================
   CONTACT (beige block, sits above the footer)
   ========================================================= */
.contact-block {
  background: var(--cream);
  border-top: 1px solid rgba(30,24,21,0.1);
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
}
/* White-background variant — used on join.html, contact.html and
   connect.html. Plain white rather than the --cream default below,
   so this section reads as visually separate from .donate-cta (also
   --cream) immediately after it instead of blending into one block. */
.contact-block--white {
  background: #FFFFFF;
}
/* This element is both .wrap (1180px max-width, from css above) and
   .contact-block__inner — laid out as two columns modeled on the
   registration section at parkprescriptions.ca/en/prescribers#register:
   the intro paragraph sits fixed-width on the left, the form fills the
   remaining (much wider than the old single 30rem column) space on the
   right, and align-items: start keeps their tops flush with each
   other. Below 860px (.contact-form__fields' own breakpoint for
   stacking its paired inputs is narrower, at 480px, since that's about
   fitting two inputs side by side rather than this two-column split)
   it collapses back to a single column — see the media query below. */
.contact-block__inner {
  display: grid;
  grid-template-columns: minmax(240px, 340px) 1fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
  text-align: left;
}
@media (max-width: 860px) {
  .contact-block__inner {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
}
.contact-block__label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--orange-s1);
  margin: 0 0 1rem;
}
.contact-block__title {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(1.82738rem, 3.65476vw, 2.39844rem);
  line-height: 1.2;
  margin: 0 0 0.85rem;
}
.contact-block__lede {
  color: var(--charcoal);
  /* No bottom margin needed now that this sits beside the form as its
     own column rather than stacked above it — see .contact-block__inner. */
  margin: 0;
  text-align: left;
}
/* contact.html splits its intro across two of these (one question, one
   contact line) rather than the single paragraph join/connect use —
   this puts space between that pair without giving every lone
   .contact-block__lede an margin it doesn't need. */
.contact-block__lede + .contact-block__lede {
  margin-top: 1em;
}
/* contact.html's version of this lede includes a mailto link (the
   global "a" reset above makes links invisible — same color, no
   underline — unless a context like this opts back in). Same treatment
   as .update-card__text a elsewhere on the site. */
.contact-block__lede a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--orange-s1);
  text-underline-offset: 2px;
}

.contact-form {
  text-align: left;
  /* Tighter than the site-wide body line-height (1.55) — this form's
     long question labels and wrapping textarea placeholder text read
     as too loose at that setting. */
  line-height: 1.2;
  /* Nudges the form down so its first field doesn't sit higher than
     the heading beside it in .contact-block__inner's two-column,
     align-items: start layout (join.html, connect.html, contact.html). */
  margin-top: 16px;
}
.contact-form__honeypot {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}
.contact-form__fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
/* A lone full-width field (e.g. Country on connect.html) inside an
   otherwise-paired-inputs row — same spacing as .contact-form__fields,
   just one column instead of two. */
.contact-form__fields--single {
  grid-template-columns: 1fr;
}
.contact-form input:not([type="checkbox"]),
.contact-form select,
.contact-form textarea {
  width: 100%;
  background: var(--paper);
  border: 1px solid rgba(30,24,21,0.25);
  color: var(--ink);
  padding: 0.7rem 0.9rem;
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 0.9rem;
  margin-bottom: 0.75rem;
  appearance: none;
}
.contact-form textarea {
  resize: vertical;
  min-height: 8rem;
}
.contact-form select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2314231A' stroke-width='1.6'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 1rem;
  padding-right: 2.4rem;
}
.contact-form input::placeholder { color: rgba(30,24,21,0.4); }

.contact-form__checkboxes {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin: 0.25rem 0 1rem;
}
.contact-form__checkbox {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  font-size: 0.88rem;
  color: var(--ink);
  cursor: pointer;
}
.contact-form__checkbox input[type="checkbox"] {
  appearance: auto;
  -webkit-appearance: checkbox;
  width: 1rem;
  height: 1rem;
  margin: 0.15rem 0 0;
  flex: none;
  accent-color: var(--navy);
}

.contact-form__fine {
  font-size: 0.78rem;
  color: rgba(30,24,21,0.55);
  margin: 0 0 1rem;
}
.contact-form__fine a { text-decoration: underline; }

.contact-form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.9rem;
  margin-top: 1.5rem;
}
.contact-form__actions .btn { margin-top: 0; }
.btn--solid {
  background: var(--olive-t1);
  color: var(--ink);
  border: 1px solid var(--olive-t1);
}
.btn--solid:hover { background: var(--olive-s1); border-color: var(--olive-s1); color: var(--paper); }

/* .btn--large used to bump a button up to header-nav size; now that
   every .btn is that size by default, this class is a no-op for sizing
   and only survives in markup for the extra top margin below (kept so
   existing "Donate"/"Join the Alliance" buttons don't need HTML edits). */
.partners .btn--large {
  margin-top: 30px;
}

.contact-form__caption {
  font-size: 0.78rem;
  color: rgba(30,24,21,0.55);
  margin: 1rem 0 0;
  line-height: 1.5;
}
.contact-form__caption a { text-decoration: underline; }

.contact-form__status {
  margin: 1rem 0 0;
  font-size: 0.9rem;
  min-height: 1.2em;
}
/* Before a submission, this live-status paragraph is empty markup
   (js/script.js only fills it in after a submit attempt) — collapse
   its reserved margin/height in that state so it doesn't read as
   extra blank padding under the Submit button. Once js/script.js sets
   a message, it's no longer :empty and gets its normal spacing back. */
.contact-form__status:empty {
  margin: 0;
  min-height: 0;
}
.contact-form__status[data-state="success"] { color: var(--olive); }
.contact-form__status[data-state="error"] { color: #a83e3e; }
 
@media (max-width: 480px) {
  .contact-form__fields { grid-template-columns: 1fr; }
}
 
.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid rgba(249,247,246,0.12);
  padding-block: 1.5rem;
  font-size: 0.82rem;
  color: rgba(249,247,246,0.55);
}
.footer-bottom__copyright { margin: 0; }

.footer-social {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.footer-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Matches the newsletter form's text-input height (~40px) above, so
     this row of buttons reads as the same scale as the rest of the
     footer rather than looking small next to it. The icons inside are
     sized in HTML to 32px, leaving exactly 4px of padding to this
     button's edge on every side. */
  width: 40px;
  height: 40px;
  border-radius: 11px;
  background: rgba(249,247,246,0.12);
  color: var(--paper);
  transition: background 0.2s ease, color 0.2s ease;
}
.footer-social a:hover { background: var(--orange); color: var(--navy-s2); }

/* =========================================================
   SIMPLE SUB-PAGES (donate, privacy policy) — nav bar without
   the full-bleed hero video/mark used on the homepage.
   ---------------------------------------------------------
   These pages render .site-header (with the .site-header--overlay
   modifier — see that class below) directly on top of .page-banner
   instead of being pushed below it by .main--offset, the same overlay
   relationship the homepage hero has with its header. min-height:
   590px is the combined header + banner height at the top of the
   page; the header doesn't add to that height itself (it's a
   position: fixed overlay with no document flow height of its own),
   so 590px is simply this element's own height. */
.page-banner {
  /* Base banner styling, shared by every page-banner regardless of
     whether .page-banner--photo is also applied. The flat navy
     background set here is what actually shows on a page that omits
     the --photo modifier; on a page that includes it, --photo's own
     background-image (set inline per page) paints over this, and
     this navy only briefly shows as a pre-load fallback. */
  background: var(--navy-s2);
  color: var(--paper);
  display: flex;
  flex-direction: column;
  /* Anchors the title near the bottom of the banner (rather than dead
     center) so it reads over the lower, less busy part of the photo —
     matching the editorial hero treatment on the reference sites (e.g.
     Health In Harmony). padding-bottom, not justify-content: flex-end
     alone, so the title's own gap to the banner's bottom edge stays
     fixed and predictable regardless of how many lines it wraps to. */
  justify-content: flex-end;
  padding-bottom: 9rem;
  min-height: 590px;
  text-align: left;
}
/* .wrap's own margin-inline: auto (see its definition near the top of
   this file) is normally harmless on a plain block element — it just
   centers the max-width-capped box. But as a flex child here, auto
   margins in the cross axis (horizontal, since .page-banner is
   flex-direction: column) take priority over the default "stretch"
   sizing and instead shrink the box to its content width and center
   THAT within the banner — which is what made a short title like
   "Join the Alliance" look centered even with text-align: left set
   above, since the box was only ever as wide as the text itself.
   Forcing width: 100% here restores the plain-block behavior (fill
   the available width, then let max-width + the auto margins cap and
   center it same as everywhere else on the site on very wide
   screens) instead of the flex content-sized one. */
.page-banner > .wrap {
  width: 100%;
}
.page-banner__title {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(1.8rem, 4vw, 2.4rem);
  margin: 0;
}

/* Photo variant — used by pages that want a real photograph behind the
   banner title instead of a flat navy field (e.g. join.html). The image
   + a dark gradient (for title legibility) are supplied together via an
   inline background-image, same pattern as the services photo tiles. */
.page-banner--photo {
  /* Fallback color visible briefly before the banner photo (set as an
     inline background-image per page) loads in — dark green rather
     than navy, matching .hero's own pre-load fallback. */
  background-color: var(--olive-s2);
  background-size: cover;
  background-position: center;
}

/* Donate page (v2) — donation form embedded in the banner itself,
   beside the "Support Us" copy, instead of further down the page.
   Reference: healthinharmony.org/donate — a floating white card
   overlapping the bottom edge of the hero photo. */
.page-banner--donate {
  /* .page-banner's own padding-bottom (9rem) existed to keep a
     single-line title clear of the banner's bottom edge — not needed
     here, since the form card now defines the banner's visual weight
     and pokes past that edge on purpose. */
  padding-bottom: 0;
  /* Full-height hero, so the banner fills the browser window on
     load (down to the fold) instead of stopping partway — matching
     the reference site. 100svh (small viewport height) avoids the
     iOS/Android address-bar jump that plain 100vh causes; browsers
     without svh support just keep the 100vh line above them. */
  min-height: 100vh;
  min-height: 100svh;
  /* .page-banner's own justify-content: flex-end anchored a single
     short title near the bottom — right for a ~590px banner, but on
     a full-height one it would glue the text/card group to the very
     bottom and leave a large empty gap of photo above. Centering
     keeps the group in the main viewing area regardless of exactly
     how tall the live (non-fallback) Zeffy form ends up rendering. */
  justify-content: center;
  /* Space below the banner box itself so the overlapping card (see
     .donate-hero__form's negative margin below) doesn't crowd the
     footer that follows it in the document. Not needed once the
     layout collapses to a single column on mobile — cleared there. */
  margin-bottom: 3rem;
}
.donate-hero__grid {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2.5rem;
}
.donate-hero__text {
  flex: 1 1 20rem;
  max-width: 30rem;
  /* Breathing room above the banner's bottom edge, now that
     .page-banner--donate's own padding-bottom is zeroed out. */
  padding-bottom: 3rem;
}
.donate-hero__lede {
  margin: 1rem 0 0;
  font-size: 1.05rem;
  line-height: 1.55;
  color: var(--paper);
  opacity: 0.92;
  max-width: 26rem;
}
.donate-hero__form {
  flex: 0 1 23rem;
  width: 100%;
  max-width: 23rem;
  /* The float: pulls the card's bottom edge past the banner's own
     bottom edge (see .page-banner--donate's padding-bottom: 0 above),
     so it visually straddles the photo and the section below it. */
  margin-bottom: -4.5rem;
}
.donate-hero__form-inner {
  background: var(--paper);
  border-radius: 14px;
  box-shadow: 0 24px 48px rgba(14,17,12,0.32);
  padding: 1.25rem;
  overflow: hidden;
}
@media (max-width: 860px) {
  .page-banner--donate {
    min-height: 0;
    /* Stacked layout no longer has .page-banner's own min-height/
       justify-content: flex-end keeping content clear of the fixed
       header overlaying the top of the banner — replaced with
       explicit top padding so "Support Us" doesn't render underneath
       the logo/nav. */
    padding-top: calc(var(--header-h) + 2rem);
    padding-bottom: 3rem;
    margin-bottom: 0;
  }
  .donate-hero__grid {
    flex-direction: column;
    align-items: stretch;
    /* Was 2.5rem (the desktop side-by-side row's gap) *plus* a
       separate margin-top: 2rem on .donate-hero__form below — in the
       stacked column layout those two stack on top of each other
       instead of overlapping, roughly doubling the visible gap
       between the lede text and the form card. This is now the only
       thing setting that gap. */
    gap: 1.25rem;
    /* Tightened to match the ~1.25rem rhythm used elsewhere here
       (the gap above, and .donate-hero__form-inner's own padding)
       instead of the wider clamp(1.25rem, 4vw, 3rem) .wrap normally
       uses — brings the card closer to the screen edges. */
    padding-inline: 1.25rem;
  }
  .donate-hero__text {
    max-width: none;
    padding-bottom: 0;
  }
  .donate-hero__lede {
    max-width: none;
  }
  .donate-hero__form {
    flex: none;
    max-width: none;
    margin-bottom: 0;
    margin-top: 0;
  }
}

/* =========================================================
   JOIN THE ALLIANCE — application form
   ========================================================= */
.form-field {
  display: block;
  margin-bottom: 1.1rem;
}
.form-field__label {
  display: block;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--ink);
  margin-bottom: 0.4rem;
}
.form-field__hint {
  display: block;
  margin: -0.5rem 0 0.9rem;
  text-align: right;
  font-size: 0.78rem;
  color: rgba(30,24,21,0.55);
}
.form-field__hint.is-over { color: #a83e3e; }
.form-field__hint--inline {
  text-align: left;
  margin: -0.25rem 0 0.7rem;
}

/* Repeatable social-media link rows (connect.html) */
.social-links {
  margin-bottom: 0.6rem;
}
.social-links__row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.social-links__row input {
  flex: 1 1 auto;
  margin-bottom: 0 !important;
}
.social-links__remove {
  flex: none;
  width: 2.6rem;
  height: 2.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  border: 1px solid rgba(30,24,21,0.25);
  border-radius: 8px;
  color: var(--charcoal);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.social-links__remove:hover {
  border-color: #a83e3e;
  color: #a83e3e;
}
.social-links__add {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: none;
  border: 1px dashed rgba(30,24,21,0.35);
  border-radius: 8px;
  padding: 0.55rem 1rem;
  margin-bottom: 1.1rem;
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.social-links__add:hover {
  border-color: var(--navy);
  color: var(--navy);
}

.form-choice {
  border: 0;
  padding: 0;
  margin: 0 0 1.1rem;
}
.form-choice__options {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.5rem;
}
.form-choice__option {
  position: relative;
  cursor: pointer;
}
.form-choice__option input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}
.form-choice__option span {
  display: inline-flex;
  align-items: center;
  padding: 0.55rem 1.35rem;
  border-radius: 999px;
  border: 1px solid rgba(30,24,21,0.25);
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--paper);
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.form-choice__option input:checked + span {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--paper);
}
.form-choice__option input:focus-visible + span {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
}

.join-form__subheading {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: 1.05rem;
  color: var(--ink);
  margin: 0 0 0.9rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(30,24,21,0.12);
}

.placeholder-block {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
}
.placeholder-block__inner {
  max-width: 34rem;
  text-align: left;
}
/* About and Resources pair this with .wrap--edge (see that class near
   the top of this file) to run full width, matching the header menu's
   own left/right margins, instead of this 34rem cap. Needs the extra
   specificity of the compound selector — .wrap--edge's own max-width:
   none is a single class too, so on source order alone this rule would
   keep winning regardless of which classes end up on the element. */
.placeholder-block__inner.wrap--edge {
  max-width: none;
}
.placeholder-block h1 {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(1.8rem, 3.6vw, 2.3rem);
  margin: 0 0 1rem;
}
.placeholder-block p {
  color: var(--charcoal);
  margin: 0;
}
/* Was written for a single paragraph (donate.html's old "coming soon"
   placeholder) — same pattern as .mission__lede + .mission__lede and
   .contact-block__lede + .contact-block__lede elsewhere, now that
   donate.html's version holds multiple paragraphs. */
.placeholder-block p + p {
  margin-top: 1em;
}

/* Donate page's live donation form (Zeffy embed) — replaced the old
   "coming soon" .placeholder-block copy once the form went live.
   Capped narrower than the site's usual 1180px .wrap, since a
   donation form reads better as a single centered column than
   stretched full width. */
.donate-form-block {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
}
.donate-form-block__inner {
  max-width: 40rem;
}
.donate-form-block__lede {
  color: var(--charcoal);
  font-size: 1.05rem;
  line-height: 1.55;
  margin: 0 0 2rem;
}
.donate-form-block__note {
  color: var(--charcoal);
  font-size: 0.95rem;
  line-height: 1.55;
  margin: 1.75rem 0 0;
}
.donate-form-block__note a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* =========================================================
   404 — NOT FOUND
   ========================================================= */
.not-found {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
  text-align: center;
}
.not-found__inner {
  max-width: 34rem;
}
.not-found__code {
  /* Large, quiet numeral above the heading — a kicker rather than the
     page's real title (the banner's <h1> already carries that), so
     it's sized big but kept light in color rather than competing with
     the heading below for attention. */
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(3rem, 8vw, 4.5rem);
  line-height: 1;
  color: var(--olive-t1);
  margin: 0 0 0.5rem;
}
.not-found h2 {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(1.8rem, 3.6vw, 2.3rem);
  margin: 0 0 1rem;
}
.not-found p {
  color: var(--charcoal);
  margin: 0 0 2rem;
}
.not-found .btn {
  display: inline-block;
  /* Extra breathing room below the button and the section's own
     bottom edge, on top of .not-found's shared top/bottom
     padding-block — without this the button sits right at that
     padding's edge, closer to the footer below than it is to the
     paragraph above. */
  margin-bottom: 1.5rem;
}

/* Long-form legal/policy copy (privacy-policy.html) — headings, body
   text, bulleted lists and dividing rules for a page transcribed from
   an actual policy document, as opposed to .placeholder-block's single
   "coming soon" line. */
.policy-content {
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
}
.policy-content__inner {
  max-width: 42rem;
  text-align: left;
}
.policy-content h1 {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(1.8rem, 3.6vw, 2.3rem);
  margin: 0 0 1rem;
}
.policy-content__meta {
  color: var(--taupe);
  font-size: 0.9rem;
  margin: 0 0 2rem;
}
.policy-content h2 {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(1.25rem, 2.2vw, 1.5rem);
  color: var(--ink);
  margin: 0 0 1rem;
}
.policy-content h3 {
  font-family: var(--font-display);
  font-weight: var(--font-heading-weight);
  font-size: clamp(1.02rem, 1.6vw, 1.15rem);
  color: var(--ink);
  margin: 1.8rem 0 0.75rem;
}
/* Tighten the gap where a heading follows immediately after another
   heading with no paragraph between (e.g. "Information we collect" ->
   "Email and signup forms"), rather than stacking both margins. */
.policy-content h2 + h3 {
  margin-top: 0.5rem;
}
.policy-content p {
  color: var(--charcoal);
  margin: 0 0 1rem;
}
.policy-content p:last-child {
  margin-bottom: 0;
}
/* Run-in bold labels that act like a heading but stay body-text size
   in the source document (e.g. "How we use this"). */
.policy-content__subhead {
  margin: 1.5rem 0 0.6rem;
}
.policy-content ul {
  margin: 0 0 1rem;
  padding-left: 1.3rem;
  color: var(--charcoal);
}
.policy-content li {
  margin-bottom: 0.5rem;
}
.policy-content li:last-child {
  margin-bottom: 0;
}
.policy-content strong {
  color: var(--ink);
  font-weight: 700;
}
.policy-content a {
  color: var(--purple);
  text-decoration: underline;
  text-decoration-color: rgba(84,76,143,0.4);
  text-underline-offset: 2px;
}
.policy-content a:hover,
.policy-content a:focus-visible {
  text-decoration-color: currentColor;
}
.policy-content hr {
  border: none;
  border-top: 1px solid rgba(30,24,21,0.12);
  margin: 2.5rem 0;
}

/* =========================================================
   DONATE CTA — closing band at the bottom of every page,
   sits above the footer.
   ========================================================= */
.donate-cta {
  background: var(--cream);
  border-top: 1px solid rgba(30,24,21,0.1);
  padding-block: clamp(3rem, 7vw, 4.5rem);
  text-align: center;
}
.donate-cta__inner {
  max-width: 32rem;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Matches .donate-cta's own padding-block above/below (minus 16px,
     pulled in tighter per feedback), so the button-to-text gap reads
     close to the section's top and bottom breathing room instead of
     noticeably tighter or looser. */
  gap: calc(clamp(3rem, 7vw, 4.5rem) - 16px);
}
.donate-cta__text {
  margin: 0;
  color: var(--charcoal);
}

/* =========================================================
   SCROLL REVEAL — content blocks fade in and slide up slightly as
   they enter the viewport (the same subtle effect used on sites like
   Health In Harmony and the Planetary Health Alliance).
   ---------------------------------------------------------
   Entirely opt-in from the JS side: js/script.js is what decides
   which elements get the ".reveal" class in the first place (see the
   "Scroll reveal" block there), picking a generic set of content
   blocks — grid/list items, section-level blocks — that works the
   same way on every page without needing this class hand-added to
   the markup. That also means there's no separate no-JS fallback to
   maintain here: if JS never runs, no element ever receives ".reveal"
   and everything just displays normally at full opacity. */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}
.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}
/* Belt and suspenders for the OS-level "reduce motion" setting: the
   JS side already checks this and skips adding ".reveal" to anything
   in the first place when it's on, so in practice content never
   starts hidden at all for these users. This is just a backstop so
   that if ".reveal" ever ends up on an element regardless, it renders
   fully visible immediately rather than depending on scroll position
   with no animation to reveal it. */
@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
