/* ==========================================================================
   IIRF — Components  ("Living Systems")
   Forest green, mint breathing bands, glass surfaces, leaf/droplet shapes.
   Class names are unchanged from v2 so all page markup and js/main.js
   continue to work.
   ========================================================================== */

/* ==========================================================================
   1. Buttons
   ========================================================================== */

.btn {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 0.82rem 1.65rem;
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.005em;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  border: 1.5px solid transparent;
  border-radius: var(--r-pill);
  cursor: pointer;
  isolation: isolate;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease-spring);
}
.btn:hover { transform: translateY(-2px) scale(1.018); }
.btn:active { transform: translateY(0) scale(0.99); }
.btn svg { width: 16px; height: 16px; flex: none; }

/* Waterdrop ripple — JS sets --rx/--ry to the click point and toggles .is-rippling */
.btn::after {
  content: "";
  position: absolute;
  left: var(--rx, 50%);
  top: var(--ry, 50%);
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0) 70%);
  opacity: 0;
  transform: scale(0);
  pointer-events: none;
  z-index: -1;
}
.btn.is-rippling::after { animation: btn-ripple 560ms var(--ease-out); }
@keyframes btn-ripple {
  0%   { opacity: 0.9; transform: scale(0); }
  100% { opacity: 0;   transform: scale(26); }
}
.btn--outline::after,
.btn--ghost-light::after {
  background: radial-gradient(circle, rgba(21, 128, 61, 0.35) 0%, rgba(21, 128, 61, 0) 70%);
}
.hero .btn--outline::after,
.section--dark .btn--outline::after,
.cta-band .btn--outline::after,
.btn--ghost-light::after {
  background: radial-gradient(circle, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0) 70%);
}

.btn--primary {
  background: var(--green-700);
  border-color: var(--green-700);
  color: #fff;
  box-shadow: 0 2px 10px rgba(21, 128, 61, 0.22);
}
.btn--primary:hover {
  background: var(--green-800);
  border-color: var(--green-800);
  color: #fff;
  box-shadow: 0 10px 28px rgba(21, 128, 61, 0.30);
}

/* Legacy modifier kept so existing markup keeps working */
.btn--navy {
  background: var(--green-900);
  border-color: var(--green-900);
  color: #fff;
}
.btn--navy:hover {
  background: var(--green-800);
  border-color: var(--green-800);
  color: #fff;
}

.btn--gold {
  background: var(--gold-400);
  border-color: var(--gold-400);
  color: var(--green-900);
}
.btn--gold:hover {
  background: var(--gold-500);
  border-color: var(--gold-500);
  color: var(--green-900);
}

.btn--outline {
  background: transparent;
  border-color: var(--mint-200);
  color: var(--green-800);
}
.btn--outline:hover {
  background: var(--green-700);
  border-color: var(--green-700);
  color: #fff;
}

.btn--ghost-light {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.42);
  color: #fff;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.btn--ghost-light:hover {
  background: #fff;
  border-color: #fff;
  color: var(--green-900);
}

/* On forest bands the primary button switches to gold — green on green is
   invisible. The hero was in this group until 2026-08-21; now that it is a
   light section, the site's ordinary green primary is correct there. */
.section--dark .btn--primary,
.cta-band .btn--primary {
  background: var(--gold-400);
  border-color: var(--gold-400);
  color: var(--green-900);
  box-shadow: 0 4px 18px rgba(214, 166, 60, 0.30);
}
.section--dark .btn--primary:hover,
.cta-band .btn--primary:hover {
  background: var(--gold-300);
  border-color: var(--gold-300);
  color: var(--green-900);
  box-shadow: 0 12px 34px rgba(214, 166, 60, 0.38);
}
/* …except inside the hero's glass card, which carries its own scheme */
.hero__card .btn--primary {
  background: var(--green-700);
  border-color: var(--green-700);
  color: #fff;
}
.hero__card .btn--primary:hover {
  background: var(--green-600);
  border-color: var(--green-600);
}

.section--dark .btn--outline,
.cta-band .btn--outline {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.42);
  color: #fff;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.section--dark .btn--outline:hover,
.cta-band .btn--outline:hover {
  background: #fff;
  border-color: #fff;
  color: var(--green-900);
}
/* The hero is a light section over a photograph: the outline button needs a
   pale fill of its own so the border and label hold up wherever the picture
   happens to be bright. */
.hero .btn--outline {
  background: rgba(255, 255, 255, 0.55);
  border-color: var(--green-700);
  color: var(--green-900);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.hero .btn--outline:hover {
  background: var(--green-800);
  border-color: var(--green-800);
  color: #fff;
}
.hero__card .btn--outline {
  border-color: rgba(255, 255, 255, 0.35);
  color: #fff;
}

.btn--lg { padding: 1rem 2.1rem; font-size: var(--fs-base); }
.btn--sm { padding: 0.52rem 1.2rem; font-size: var(--fs-xs); }

.btn-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

/* ==========================================================================
   2. Header + navigation
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(var(--glass-sat)) blur(var(--glass-blur));
  -webkit-backdrop-filter: saturate(var(--glass-sat)) blur(var(--glass-blur));
  border-bottom: 1px solid var(--border);
  transition: background-color 400ms var(--ease),
              box-shadow 400ms var(--ease),
              border-color 400ms var(--ease),
              backdrop-filter 400ms var(--ease);
}
.site-header.is-scrolled {
  box-shadow: var(--shadow-md);
  border-bottom-color: transparent;
}

/* Browsers without backdrop-filter get a solid bar instead of a see-through one */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header { background: var(--surface-000); }
}

/* The container is capped in px (--container: 1280px) but everything inside
   the header is sized in rem. A visitor whose browser default font is larger
   than 16px therefore overflows the cap and the Join Us button is clipped away
   — 1519px of content in a 1280px box at a 24px root. Letting the header (and
   only the header) grow to 80rem keeps it proportional: at a normal 16px root
   80rem is 1280px, so nothing changes; at a 24px root it becomes 1920px and
   the row fits. Pre-existing; found while enlarging the brand, which made the
   clipping worse. */
.site-header > .container { max-width: max(var(--container), 80rem); }

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  min-height: var(--header-h);
  transition: min-height 400ms var(--ease);
}
.site-header.is-scrolled .header__inner { min-height: var(--header-h-scrolled); }

/* ----- Homepage: the header floats over the hero at the top of the page,
   then settles into the site's normal light glass on scroll. `:has()` avoids
   a flash of the wrong scheme; browsers without it simply keep the light
   header, which is now the correct scheme anyway.

   Until 2026-08-21 this block also forced white brand text and white nav
   links, because the hero photograph was dark. The client's replacement
   photograph is bright (median luminance ~0.86 across the header strip), so
   white text no longer passes AA anywhere on it and those overrides were
   removed. The default dark-on-light header is what belongs here now. ----- */

body:has(.hero) .site-header {
  background: transparent;
  border-bottom-color: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* …and settles back into the site's normal light glass once it leaves the
   photograph. Without this the transparent rule above would win at every
   scroll position, and the header would be dark text floating over the dark
   stats band immediately below the hero. */
body:has(.hero) .site-header.is-scrolled {
  background: rgba(255, 255, 255, 0.86);
  border-bottom-color: var(--border);
  backdrop-filter: saturate(var(--glass-sat)) blur(var(--glass-blur));
  -webkit-backdrop-filter: saturate(var(--glass-sat)) blur(var(--glass-blur));
  box-shadow: 0 10px 40px rgba(7, 31, 22, 0.10);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body:has(.hero) .site-header.is-scrolled { background: var(--surface-000); }
}

/* ----- Phones and tablets: a solid bar, not glass -----

   Added 2026-08-22. The client opened the live site on Safari, Chrome and
   Brave on iOS and got three different-looking headers. `backdrop-filter` is
   the largest variable between them: each engine composites the blur over the
   photograph differently, and Chrome on iOS ignores `theme-color` where Safari
   paints the status bar from it. An opaque bar has no compositing for them to
   disagree about, and the organisation name reads identically in all three.

   All three homepage states are named because `body:has(.hero)
   .site-header.is-scrolled` carries higher specificity than a plain
   `.site-header` and would otherwise restore the glass on scroll. ----- */
@media (max-width: 959px) {
  .site-header,
  body:has(.hero) .site-header,
  body:has(.hero) .site-header.is-scrolled {
    background: var(--surface-000);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  body:has(.hero) .site-header { border-bottom-color: var(--border); }
}
/* The hero's matching half of this change — it must stop sliding under the
   now-opaque bar — lives beside the .hero rule itself further down, because a
   media query adds no specificity and the base rule is declared after this
   one. */

/* Brand */

/* Three bands, not two. Changed 2026-08-22 after the client reviewed the live
   site on four phones and browsers:

     < 1024px    logo + full organisation name, two lines, fluid size
     1024–1319   logo + "IIRF"          (no room beside the full nav)
     >= 82.5em   logo + full name + tagline

   Two things drove it. The client asked for the full name on the phone header,
   in place of the "INNOVATE · INSPIRE · REFORM · FUTURE" tagline — which was
   repeating, word for word, the eyebrow badge sitting directly beneath it in
   the hero. And that tagline was the cause of a real defect: at 11.5px with
   0.17em tracking it measures 288px, it is `nowrap`, and the media query that
   shrank it stopped at 420px — just below every Pro Max and Plus iPhone. At
   430px the header row wanted 483px in a 430px viewport, and `overflow-x: clip`
   on <html> sliced the hamburger off the right edge. It fitted at 393px by two
   pixels. A width sweep on 2026-08-22 checked 459px and saw nothing, because
   `clip` had already pulled the toggle's reported bounding box back inside the
   viewport; only a screenshot from a real device found it. */

.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  text-decoration: none;
  /* Below 1024px the brand is allowed to give. `flex-shrink: 0` plus a
     `nowrap` child is exactly what pushed the hamburger off-screen; with
     shrink enabled and min-width:0 on both boxes the name wraps instead, so
     no future wording can push anything off the row again. The desktop bands
     restore flex-shrink: 0 — there the row has slack and shrinking the brand
     would only squeeze it against the nav. */
  flex-shrink: 1;
  min-width: 0;
}
/* Enlarged 2026-08-21 at the client's request: the IIRF identity was reading
   smaller than the homepage headline beneath it. Applied to the shared header
   on every page — a brand that changes size between pages reads as a bug. */
.brand__logo {
  height: 62px;
  width: auto;
  flex-shrink: 0;
  transition: transform 400ms var(--ease-spring);
}
.brand:hover .brand__logo { transform: rotate(-6deg) scale(1.06); }
.site-header.is-scrolled .brand__logo { height: 52px; }

.brand__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding-left: var(--sp-3);
  border-left: 1px solid var(--border-strong);
}
/* Hidden by default now. "IIRF" exists for one band only — 1024px to 1319px,
   where the full nav and the Join Us button claim ~700px of a 944px row and
   the name would have to set at 12.5px to fit beside them. */
.brand__short {
  display: none;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.5rem;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--green-900);
}
/* The default wordmark everywhere below 1024px.

   Sized against measured type, not estimated. Bricolage Grotesque 700 at
   -0.022em tracking renders the longer of the two lines, "and Research
   Foundation", at 12.006em — measured in the browser with a Range over the
   live text. (An offline fontTools pass first put it at 10.721em, 13% low,
   because the variable font's optical-size axis was left at its default; the
   in-browser figure is the one these numbers use.)

   Below 1024px the only competition is a 44px toggle, so the row's fixed cost
   is 159px with the smallest mark, 173px with the 48px mark, and 195px with
   the full 62px one:

     320px  budget 161  ->  floor 13.0px  ->  156px   2 lines
     360px  budget 187  ->  4vw   14.4px  ->  173px   2 lines
     390px  budget 217  ->  4vw   15.6px  ->  187px   2 lines
     430px  budget 235  ->  4vw   17.2px  ->  206px   2 lines
     600px+ budget 405+ ->  cap   20.0px  ->  240px   2 lines

   The cap is 1.25rem — the same size the name uses on a 1440px desktop — so
   every phone from ~500px up shows it at full size. Two lines at 20px x 1.18
   is 47px inside an 80px header, 64px when scrolled.

   `white-space: normal` is the safety valve. The <br> in the markup forces the
   two-line break, so this changes nothing at any width in the table above; it
   means that when the row does run out of room — below ~300px, or if the name
   is ever reworded — the text takes a third line instead of pushing the
   hamburger off the edge, which is the failure this whole change is fixing. */
.brand__name {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(0.8125rem, 4vw, 1.25rem);
  line-height: 1.18;
  letter-spacing: -0.022em;
  color: var(--green-900);
  white-space: normal;
}
/* Retired from every width except the widest. It duplicated the hero eyebrow
   badge, and its 288px nowrap width was what pushed the hamburger off-screen
   on large iPhones. */
.brand__tag {
  display: none;
  font-family: var(--font-body);
  font-size: 0.7188rem;
  font-weight: 700;
  letter-spacing: 0.17em;
  /* green-800, not green-700: at 10.5px over the light hero photograph
     green-700 measured 4.3:1, just under AA. */
  color: var(--green-800);
  text-transform: uppercase;
  white-space: nowrap;
  margin-top: 5px;
}

/* 1024px is where the hamburger gives way to the full nav plus the Join Us
   button, and those two together claim about 700px. Measured at 1024 the
   logo + "IIRF" + tagline lockup ran the header row 154px past the viewport.
   Of a 944px inner row that leaves ~134px for a wordmark, which the full name
   could only meet at 12.5px — unreadable. So this one band, and only this
   band, keeps the compact mark. */
@media (min-width: 1024px) {
  .brand { flex-shrink: 0; }
  .brand__short { display: block; }
  .brand__name { display: none; }
}

/* The mark is narrow, so the full lockup can appear far earlier than in v2.
   Raised twice on 2026-08-21, from 1180px to 1240px and then to 82.5em, as the
   brand was enlarged in two rounds at the client's request. Each time the
   previous breakpoint had fallen to exactly the minimum flex gap — brand, nav
   and the Join Us button edge to edge with 0px of slack.

   In `em`, not `px`, deliberately. Everything in this header is sized in rem,
   so on a browser whose default font is larger than 16px the whole row grows
   while a px breakpoint stays put — at a 24px root the wordmark appeared at a
   width that could not hold it and squeezed the Join Us button. 82.5em is
   exactly 1320px at a normal 16px root, so nothing changes for most visitors,
   and it scales with the row for everyone else. */
@media (min-width: 82.5em) {
  .brand__short { display: none; }
  .brand__name {
    display: block;
    font-size: 1.25rem;
    white-space: nowrap;
  }
  .brand__tag { display: block; }
}

/* Small phones: the mark gives back 14px and the text column tightens, so the
   lockup clears the hamburger with room to spare. The tagline rules that used
   to live here went with the tagline itself. */
@media (max-width: 26.25em) {
  .brand__logo { height: 48px; }
  .brand { gap: var(--sp-2); }
  .brand__text { padding-left: var(--sp-2); }
}
/* Below ~333px the second line stops fitting at 4vw and wraps to a third.
   Buying the 20px back from the mark and the gaps is better than shrinking the
   type: at 320px it holds the two-line lockup at the clamp's 13px floor, where
   trimming the font instead would have meant about 11px — under the 12px this
   site keeps everywhere else. 21.25em is 340px, clear of the 360px class.
   Below about 300px the name takes a third and fourth line; the toggle still
   measures 44x44 inside the container there, which is what matters. */
@media (max-width: 21.25em) {
  .brand__logo { height: 42px; }
  .brand { gap: var(--sp-1); }
  .brand__text { padding-left: var(--sp-1); }
}

/* Primary nav */

.nav { display: none; }

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__list li + li { margin-top: 0; }

.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0.55rem 0.6rem;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-700);
  text-decoration: none;
  white-space: nowrap;
  border-radius: var(--r-pill);
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.nav__link:hover {
  color: var(--green-800);
  background: var(--mint-050);
}

.nav__item { position: relative; }
.nav__item--current > .nav__link,
.nav__link[aria-current="page"] {
  color: var(--green-800);
  background: var(--mint-100);
  font-weight: 600;
}

.nav__caret {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  opacity: 0.55;
  transition: transform var(--dur) var(--ease);
}
.nav__item.is-open .nav__caret { transform: rotate(180deg); }

/* Dropdown — glass panel */

.nav__dropdown {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  min-width: 278px;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg), var(--glass-highlight);
  padding: var(--sp-2);
  list-style: none;
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px) scale(0.985);
  transition: opacity var(--dur) var(--ease),
              transform var(--dur) var(--ease-out),
              visibility var(--dur);
}
.nav__item.is-open .nav__dropdown {
  opacity: 1;
  visibility: visible;
  transform: none;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .nav__dropdown { background: var(--surface-000); }
}

.nav__dropdown li + li { margin-top: 1px; }

.nav__sublink {
  display: block;
  padding: 0.65rem 0.85rem;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-800);
  text-decoration: none;
  border-radius: var(--r-sm);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav__sublink:hover {
  background: var(--mint-050);
  color: var(--green-800);
}
.nav__sublink span {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 400;
  color: var(--text-muted);
  margin-top: 3px;
}

/* flex: none — the row is a flex container, so without it the button is the
   thing that gets compressed when the header is tight, and "Join Us" spills
   out of its own pill. Everything else in the row has a natural width it will
   defend; this one did not. */
.header__cta { display: none; flex: none; white-space: nowrap; }

/* Mobile toggle — declared before the desktop media query so the query wins */

/* flex: none for the same reason .header__cta needs it — the header row is a
   flex container, and without it the toggle is what gets compressed when the
   brand beside it is wide. Measured at 360px on 2026-08-22 it had been squeezed
   from 44px to 22px, i.e. below the minimum tap target, which is a failure you
   cannot see in a screenshot. */
.nav-toggle {
  display: inline-flex;
  flex: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: 0 10px;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  height: 2px;
  width: 100%;
  border-radius: 2px;
  background: var(--green-800);
  transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Desktop breakpoint: show the full nav, hide the hamburger */

@media (min-width: 1024px) {
  .nav { display: block; }
  .header__cta { display: inline-flex; }
  .nav-toggle { display: none; }
}

/* Mobile drawer — dark glass */

.mobile-nav {
  display: block;
  position: fixed;
  inset: var(--header-h) 0 0 0;
  background: rgba(7, 31, 22, 0.88);
  backdrop-filter: saturate(160%) blur(26px);
  -webkit-backdrop-filter: saturate(160%) blur(26px);
  border-top: 1px solid var(--glass-dark-border);
  padding: var(--sp-5) 0 var(--sp-9);
  overflow-y: auto;
  z-index: 99;
  transform: translateX(100%);
  visibility: hidden;
  transition: transform 380ms var(--ease-out), visibility 380ms;
}
.mobile-nav.is-open {
  transform: none;
  visibility: visible;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .mobile-nav { background: var(--green-950); }
}
@media (min-width: 1024px) {
  .mobile-nav { display: none; }
}

.mobile-nav__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.mobile-nav__list li + li {
  margin-top: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.10);
}

.mobile-nav__link {
  display: block;
  padding: var(--sp-4) 0;
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.028em;
  color: #fff;
  text-decoration: none;
}

.mobile-nav__group > button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--sp-4) 0;
  background: none;
  border: 0;
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.028em;
  color: #fff;
  text-align: left;
  cursor: pointer;
}
.mobile-nav__group svg {
  width: 14px;
  height: 14px;
  color: var(--sage-300);
  transition: transform var(--dur) var(--ease);
}
.mobile-nav__group.is-open svg { transform: rotate(180deg); }

.mobile-nav__sub {
  list-style: none;
  margin: 0 0 var(--sp-4);
  padding: 0 0 0 var(--sp-4);
  border-left: 2px solid rgba(143, 207, 168, 0.4);
  display: none;
}
.mobile-nav__group.is-open .mobile-nav__sub { display: block; }
.mobile-nav__sub li + li { border-top: 0; margin-top: 0; }
.mobile-nav__sub a {
  display: block;
  padding: var(--sp-3) 0;
  font-size: var(--fs-sm);
  color: rgba(255, 255, 255, 0.74);
  text-decoration: none;
}
.mobile-nav__sub a:hover { color: var(--sage-300); }

.mobile-nav .btn { width: 100%; margin-top: var(--sp-6); }

body.nav-open { overflow: hidden; }

/* ==========================================================================
   3. Hero — layered composition
   The photo is a real element inside a clipped layer, not a background image,
   so it can parallax and so wide viewports never show a floating strip.
   ========================================================================== */

.hero {
  position: relative;
  isolation: isolate;
  /* Pale, not --green-950. These are the colours that show if the photograph
     404s or is still in flight — since the 2026-08-21 inversion every rule
     inside the hero paints dark ink, so a dark fallback meant dark-on-dark. */
  background-color: var(--mint-050);
  color: var(--ink-900);
  padding-top: var(--header-h);
  margin-top: calc(-1 * var(--header-h));   /* header floats over it */
}
/* …but not below 960px, where the header is an opaque white bar (see the
   header section). Sliding under it would hide the top 80px of the photograph
   behind white while still paying for it in height. The hero gives back
   exactly the padding the negative margin was cancelling, so the page is
   neither shorter nor longer than before. */
@media (max-width: 959px) {
  .hero {
    padding-top: 0;
    margin-top: 0;
  }
}

/* Clipped backdrop stack */
.hero__bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: -1;
  pointer-events: none;
}

/* Over-tall so the parallax translate never exposes an edge. This costs
   image: a taller media box means `cover` scales the photograph up to match
   the height, pushing more of its width outside the frame. It is worth it at
   desktop widths, where the box is already wider than the image is tall.
   Below 960px parallax is off (js/main.js), so the inset only cropped. */
.hero__media {
  position: absolute;
  inset: -14% 0 -14% 0;
  will-change: transform;
}
@media (max-width: 959px) {
  .hero__media { inset: 0; will-change: auto; }
}
/* display: contents on the <picture> — without it the picture element is an
   inline box with no height, `height: 100%` on the img resolves against *that*
   rather than against .hero__media, and the photograph collapses to its natural
   aspect at the top of the hero with bare background colour underneath. Caught
   at 390px on 2026-08-22, the day the <picture> was introduced. */
.hero__media picture { display: contents; }
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
}

/* Scrim: a pale wash, heavy on the left so the headline always has a bright
   ground under it, clearing almost completely across the right third so the
   globe and the five icon discs in the photograph read at full strength.

   This was a dark scrim until 2026-08-21. It was inverted with the new
   photograph, which is bright everywhere (median luminance 0.84-0.93). The
   left-hand values are set by the darkest pixels the text can land on — the
   leaf cluster (L 0.02) and the solar panel (L 0.015): a 0.62 white wash
   lifts those to ~0.63, which keeps --green-950 text above 10:1. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg, rgba(247, 251, 248, 0.74) 0%, rgba(247, 251, 248, 0.62) 38%, rgba(247, 251, 248, 0.14) 72%, rgba(247, 251, 248, 0.05) 100%);
}
/* Below the hero's two-column breakpoint the text runs the full width, so
   there is no clear right-hand side left to protect — and the sideways
   gradient would leave the lead paragraph sitting on the globe at 2.3:1.
   A gentle top-to-bottom wash instead: measured worst case 8.9:1 for the
   lead and 3.6:1 for the outline button border at 390px. */
/* Shaped to the text block, not a flat ramp. Revised 2026-08-22 after the
   client reported the copy was hard to read on a phone.

   The contrast ratios were never the problem — they measured 6.4:1 to 9.0:1
   against the old wash, comfortably past AA. What made it hard to read was
   VARIANCE: behind the lead paragraph the background swung across a luminance
   range of 0.533, from near-white sky to the dark side of the globe, inside a
   single paragraph. A contrast-ratio check cannot see that, because it only
   ever reports the single worst pixel.

   So the wash is heavy exactly where the small text sits (12-58%) and releases
   hard below it, where the buttons and trust badges carry their own fills and
   do not need help. Measured against the mobile crop:

                       lead sd    lead range   image strength, lower 38%
     flat 0.72->0.58     0.111       0.533              39%
     shaped (this)       0.043       0.208              59%

   Both better at once — the copy is calmer AND more of the photograph shows,
   because the opacity was being spent in the wrong place. */
@media (max-width: 959px) {
  .hero__scrim {
    background: linear-gradient(180deg,
      rgba(247, 251, 248, 0.60) 0%,
      rgba(247, 251, 248, 0.80) 12%,
      rgba(247, 251, 248, 0.90) 34%,
      rgba(247, 251, 248, 0.87) 58%,
      rgba(247, 251, 248, 0.50) 70%,
      rgba(247, 251, 248, 0.20) 100%);
  }
}
/* Soft fade into the dark stats band below. With a light hero this is now a
   transition between two schemes rather than a blend within one, so it is
   shorter and weighted late — a long fade would read as a dark bar eating the
   bottom of the photograph. */
.hero__bg::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 150px;
  background: linear-gradient(to bottom, rgba(7, 31, 22, 0) 0%, rgba(7, 31, 22, 0.18) 55%, var(--green-950) 100%);
}

/* Dew drops — refracting glass ornaments */
.hero__drops {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* Green-tinted rim, not white: on the bright photograph a white rim over a
   near-white sky is invisible. The tint and the drop shadow are what make the
   ornament read at all now. */
.drop {
  position: absolute;
  border-radius: var(--r-drop);
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(21, 128, 61, 0.24);
  backdrop-filter: blur(4px) brightness(1.03) saturate(125%);
  -webkit-backdrop-filter: blur(4px) brightness(1.03) saturate(125%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), 0 10px 30px rgba(11, 59, 42, 0.14);
  animation: drop-float 14s var(--ease) infinite alternate;
}
/* The specular glint that makes it read as a droplet rather than a blob */
.drop::before {
  content: "";
  position: absolute;
  left: 18%;
  top: 14%;
  width: 32%;
  height: 24%;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0) 70%);
}
/* All four sit in the right-hand third, clear of the headline column */
.drop--1 { width: 96px;  height: 96px;  right: 4%;  top: 10%;  animation-duration: 16s; opacity: 0.5; }
.drop--2 { width: 52px;  height: 52px;  right: 21%; top: 72%;  animation-duration: 12s; animation-delay: -3s; opacity: 0.45; }
.drop--3 { width: 168px; height: 168px; right: -60px; bottom: 10%; animation-duration: 21s; animation-delay: -7s; opacity: 0.4; }
.drop--4 { width: 34px;  height: 34px;  right: 30%; top: 88%;  animation-duration: 10s; animation-delay: -5s; opacity: 0.5; }

@keyframes drop-float {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(-14px, -26px, 0) rotate(12deg); }
}

@media (max-width: 959px) {
  /* On small screens the drops would sit under the text — keep only the big one */
  .drop--1, .drop--2, .drop--4 { display: none; }
  .drop--3 { opacity: 0.4; }
}

.hero__inner {
  position: relative;
  z-index: 2;
  padding-block: clamp(2.5rem, 4vw, 3.5rem) clamp(2.5rem, 4vw, 4rem);
  min-height: min(76svh, 700px);
  display: flex;
  align-items: center;
}

.hero__grid {
  display: grid;
  gap: var(--sp-8);
  align-items: start;
  width: 100%;
}
@media (min-width: 960px) {
  /* Asymmetric split — the text column dominates, the card sits off-axis */
  .hero__grid { grid-template-columns: 1.35fr 0.85fr; gap: clamp(2.5rem, 5vw, 5.5rem); }
}


.hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--green-800);
  background: rgba(255, 255, 255, 0.62);
  border: 1px solid rgba(21, 128, 61, 0.28);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 0.45rem 1rem;
  border-radius: var(--r-pill);
  margin-bottom: var(--sp-5);
}
.hero__eyebrow::before {
  content: "";
  width: 9px;
  height: 9px;
  background: currentColor;
  border-radius: var(--r-leaf);
  flex: none;
}

/* Stepped down three times on 2026-08-21, from --fs-4xl to --fs-3xl to
   --fs-2xl and finally to this bespoke clamp, as the client asked repeatedly
   for a wider gap between the IIRF name in the header and this headline. The
   header can only grow so far inside a fixed-height bar, so most of the
   difference had to come from here.

   Not a token: --fs-xl is the size used for card headings, and reusing it for
   the homepage's h1 would make the type scale say something untrue about the
   document. 22 -> 30px sits deliberately between --fs-xl and --fs-2xl. */
.hero h1 {
  color: var(--green-950);
  font-size: clamp(1.375rem, 0.9vw + 1.16rem, 1.875rem);
  font-weight: 800;
  letter-spacing: -0.038em;
  line-height: 0.98;
  max-width: 15ch;
  margin-bottom: var(--sp-5);
  text-wrap: balance;
}
/* gold-400 is the accent on dark surfaces and only reaches ~2:1 on the pale
   hero. gold-700 is the same accent carried onto a light ground. */
.hero h1 em {
  font-style: normal;
  color: var(--gold-700);
}

.hero__lead {
  font-size: var(--fs-md);
  line-height: var(--lh-relaxed);
  /* ink-900, not ink-700: the lead runs 54ch wide, past the strong part of the
     scrim and over the brighter-but-busier middle of the photograph, where
     ink-700 measured 4.1:1 against the darkest pixels underneath it. */
  color: var(--ink-900);
  max-width: 54ch;
  margin-bottom: var(--sp-7);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-bottom: var(--sp-8);
}

/* Trust badge row — small glass chips */

.trust-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

/* Light glass, since 2026-08-21 — see the note on .hero__scrim. These sit in
   the lower-left of the photograph, over the solar panel, which is the darkest
   thing in the picture; the fill is what guarantees the label contrast there
   rather than the scrim alone. */
.trust-badge {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: rgba(255, 255, 255, 0.66);
  border: 1px solid rgba(11, 59, 42, 0.16);
  border-radius: var(--r-md);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  box-shadow: var(--glass-highlight);
}
.trust-badge svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--gold-700);
}
.trust-badge__label {
  display: block;
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-700);
  line-height: 1.3;
}
.trust-badge__value {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--green-950);
  line-height: 1.35;
}

/* ----- The hero on a phone -----
   A 3:2 landscape photograph inside a tall narrow box is a losing fight: the
   taller the hero grows, the more of the image `cover` pushes off the sides.
   At 390px the hero measured ~840px tall and showed roughly a quarter of the
   photograph's width. Everything here exists to make the box shorter, which is
   what puts the image back on screen; the portrait crop served by <picture> in
   index.html does the rest. Numbers set from measurement, not taste. */
@media (max-width: 959px) {
  .hero__inner {
    min-height: min(62svh, 520px);
    padding-block: clamp(1.75rem, 5vw, 2.5rem) clamp(2rem, 6vw, 3rem);
  }
  .hero__grid { gap: var(--sp-6); }
  .hero__eyebrow { margin-bottom: var(--sp-4); }
  /* No cap at all below 960px. 15ch was set for --fs-4xl and shrank with the
     font through three reductions; measured at 390px it left the headline in a
     219px column inside a 350px one, wrapping to five lines for no reason. The
     container is already the constraint here. */
  .hero h1 { max-width: none; margin-bottom: var(--sp-4); }
  /* The lead is the single biggest contributor to hero height on a phone —
     256px of a 873px stack when measured. --fs-base and a tighter leading take
     ~40px off it without making it hard to read at arm's length. */
  .hero__lead {
    font-size: var(--fs-base);
    line-height: 1.55;
    max-width: 42ch;
    margin-bottom: var(--sp-5);
  }
  .hero__actions { margin-bottom: var(--sp-5); }
  .trust-row { gap: var(--sp-2); }
  .trust-badge { padding: var(--sp-2) var(--sp-3); }
}

/* Below ~420px two buttons side by side wrap into two ragged rows with a
   stranded second button. One column reads as a deliberate stack instead. */
@media (max-width: 26.25em) {
  .hero__actions { flex-direction: column; align-items: stretch; }
  .hero__actions .btn { width: 100%; }
}

/* ----- Hero side card — frosted glass over the photograph -----
   Dark glass rather than light: the photo behind it is busy foliage, and a
   dark tint is the only way the body text keeps AA contrast.

   PARKED, NOT DEAD (2026-08-21). The newsletter card that used these styles
   was removed from the homepage hero at the client's request so the new
   background photograph reads across the full width; the client has asked for
   it back in a later update. Nothing on the site uses .hero__card today. See
   the comment in _build/pages/index.html for how to restore it.

   Note for whoever restores it: these values were tuned against the old DARK
   hero. The hero is light now, so the card will need re-checking against the
   new photograph before it goes back. */

.hero__card {
  position: relative;
  background: rgba(7, 31, 22, 0.58);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--r-xl);
  padding: var(--sp-6);
  backdrop-filter: blur(26px) saturate(150%);
  -webkit-backdrop-filter: blur(26px) saturate(150%);
  box-shadow: var(--shadow-glass), var(--glass-highlight);
  color: #fff;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .hero__card { background: rgba(7, 31, 22, 0.92); }
}

/* Break the grid: the card sits lower than its column and runs slightly past
   the text column's right edge. A negative bottom margin would do nothing
   here — the taller left column sets the row height. */
@media (min-width: 960px) {
  .hero__card { margin-top: clamp(1.5rem, 4vw, 4rem); margin-right: clamp(-40px, -2vw, 0px); }
}

.hero__card h2 {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -0.025em;
  color: #fff;
  max-width: none;
  margin-bottom: var(--sp-5);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
}
.hero__card ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.hero__card li {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  font-size: var(--fs-sm);
  color: rgba(255, 255, 255, 0.88);
}
.hero__card li + li { margin-top: var(--sp-4); }
.hero__card li svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--sage-300);
}

/* ----- Newsletter list -----
   Used twice: inside the dark hero card, and on the light archive section of
   publications.html. The base rules target the light surface; the dark
   treatment is layered on underneath .hero__card. */

.hero__card-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-200);
  margin-bottom: var(--sp-3);
}
.hero__card-eyebrow::before {
  content: "";
  width: 9px;
  height: 9px;
  background: currentColor;
  border-radius: var(--r-leaf);
  flex: none;
}
.hero__card--newsletter h2 { margin-bottom: var(--sp-4); }

.nl-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--sp-3);
}

/* .hero__card li is display:flex for the old checklist card; these need to
   out-specify it or the <a> shrinks to its content instead of filling. */
.nl-list > .nl-item { display: block; }
.nl-item + .nl-item { margin-top: 0; }

.nl-item > a {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-card);
  color: var(--text-primary);
  text-decoration: none;
  transition: border-color var(--dur) var(--ease),
              background-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.nl-item > a:hover {
  border-color: var(--green-700);
  background: var(--mint-050);
  transform: translateY(-2px);
}

.nl-item__body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}
.nl-item__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-base);
  letter-spacing: -0.02em;
  line-height: 1.25;
}
.nl-item__meta {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.nl-item__icon {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--green-700);
}

/* Dark variant — inside the frosted hero card.
   The inner panel is tinted *darker* than the card, not lighter. A white wash
   would sit at only 3.3–4.2:1 over the brightest part of the photograph; going
   darker puts every label above 6:1 in that worst case. */
.hero__card .nl-item > a {
  background: rgba(7, 31, 22, 0.45);
  border-color: rgba(255, 255, 255, 0.18);
  color: #fff;
}
.hero__card .nl-item > a:hover {
  background: rgba(7, 31, 22, 0.30);
  border-color: rgba(255, 255, 255, 0.42);
}
.hero__card .nl-item__meta { color: rgba(255, 255, 255, 0.82); }
.hero__card .nl-item__icon { color: var(--sage-300); }

.nl-note {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-xs);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
}
.nl-note strong { color: var(--gold-200); }

/* Page hero (inner pages) — mint band with mesh */

.page-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background:
    radial-gradient(760px 420px at 88% -30%, rgba(107, 184, 148, 0.30), transparent 64%),
    radial-gradient(560px 340px at 6% 120%, rgba(214, 166, 60, 0.14), transparent 62%),
    var(--mint-050);
  padding-block: clamp(1.75rem, 3vw, 2.75rem);
}
/* The band's own bottom padding and the next section's top padding used to
   stack into ~128px of empty space between the page title and the first
   paragraph (Changes.txt item 9, SS 9). Collapsing the join fixes it in one
   place for every inner page. */
.page-hero + .section,
.page-hero + .section--subtle { padding-top: clamp(1.75rem, 2.6vw, 2.5rem); }
.page-hero::after {
  content: "";
  position: absolute;
  right: -70px;
  top: -70px;
  width: 260px;
  height: 260px;
  background: rgba(21, 128, 61, 0.05);
  border-radius: var(--r-leaf);
  pointer-events: none;
  z-index: -1;
}
.page-hero h1 {
  color: var(--green-900);
  margin-bottom: var(--sp-4);
  max-width: 20ch;
}
.page-hero p {
  font-size: var(--fs-md);
  line-height: var(--lh-relaxed);
  color: var(--text-secondary);
  max-width: 62ch;
}

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  padding: 0;
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.breadcrumb li + li { margin-top: 0; }
.breadcrumb li + li::before {
  content: "/";
  margin-right: var(--sp-2);
  color: var(--border-strong);
}
.breadcrumb a {
  color: var(--text-secondary);
  text-decoration: none;
}
.breadcrumb a:hover { color: var(--green-700); }

/* ==========================================================================
   4. Stats band — continues the hero's dark surface
   ========================================================================== */

.stats {
  position: relative;
  z-index: 1;
  overflow: hidden;
  isolation: isolate;
  background: var(--green-950);
  color: #fff;
  padding-block: clamp(2.5rem, 4vw, 3.5rem);
}
.stats::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--mesh-band);
  opacity: 0.7;
  pointer-events: none;
  z-index: -1;
}
/* The section below rises over this band with a curved edge — leave room so
   the sub-labels are never tucked under it. */
.stats { padding-bottom: clamp(3rem, 5vw, 4.5rem); }

.stats__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: var(--sp-6);
}
.stat {
  position: relative;
  text-align: center;
  padding-inline: var(--sp-4);
}
@media (min-width: 720px) {
  .stat + .stat { border-left: 1px solid var(--border-on-dark); }
}
.stat__num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(3rem, 4.6vw, 4.25rem);
  font-weight: 800;
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 1;
  letter-spacing: -0.05em;
  background: linear-gradient(160deg, #ffffff 8%, var(--sage-300) 60%, var(--gold-300) 130%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Fallback if background-clip:text is unavailable */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .stat__num { color: var(--sage-300); background: none; }
}
.stat__label {
  display: block;
  margin-top: var(--sp-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: rgba(255, 255, 255, 0.92);
}
.stat__sub {
  display: block;
  font-size: var(--fs-xs);
  color: rgba(255, 255, 255, 0.58);
  margin-top: 3px;
}

/* ==========================================================================
   5. Cards
   ========================================================================== */

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface-000);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  isolation: isolate;
  transition: border-color var(--dur) var(--ease),
              box-shadow 380ms var(--ease),
              transform 380ms var(--ease-out);
}
.card:hover {
  border-color: var(--mint-200);
  box-shadow: var(--shadow-lg);
  transform: translateY(-6px);
}
.card:hover .card__icon { background: var(--green-700); color: #fff; transform: rotate(-8deg); }

/* Sheen — a band of light sweeping across the card on hover */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg,
    rgba(255, 255, 255, 0) 38%,
    rgba(255, 255, 255, 0.55) 50%,
    rgba(255, 255, 255, 0) 62%);
  transform: translateX(-120%);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
  transition: transform 900ms var(--ease-out), opacity 300ms var(--ease);
}
.card:hover::before { transform: translateX(120%); opacity: 1; }
.section--dark .card::before {
  background: linear-gradient(105deg,
    rgba(255, 255, 255, 0) 38%,
    rgba(255, 255, 255, 0.14) 50%,
    rgba(255, 255, 255, 0) 62%);
}

.card--flat:hover { transform: none; box-shadow: var(--shadow-sm); }
.card--accent { border-color: var(--mint-200); background: var(--mint-050); }

.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 54px;
  height: 54px;
  margin-bottom: var(--sp-5);
  background: var(--mint-100);
  border-radius: var(--r-leaf);
  color: var(--green-700);
  flex-shrink: 0;
  transition: background-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              transform 420ms var(--ease-spring);
}
.card__icon svg { width: 24px; height: 24px; }

/* Legacy modifier — secondary (teal) icon tile */
.card__icon--navy {
  background: rgba(28, 107, 98, 0.1);
  color: var(--teal-700);
}

.card h2 { margin-bottom: var(--sp-3); max-width: none; }
.card h3 { margin-bottom: var(--sp-3); max-width: none; }
.card h4 { margin-bottom: var(--sp-3); max-width: none; }
/* Heading level and heading size are separate concerns. A card's heading has
   to sit at whatever level keeps the document outline gap-free (WCAG 1.3.1 —
   no skipped levels), which is not always the level whose default size the
   card was designed around. These two classes restore the intended size
   without disturbing the outline. */
.card .card__title--md { font-size: var(--fs-xl); letter-spacing: -0.022em; }  /* h3 size */
.card .card__title--sm { font-size: var(--fs-lg); letter-spacing: -0.018em; }  /* h4 size */
.card p {
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--text-secondary);
  max-width: none;
}

.card > .btn { align-self: flex-start; margin-top: auto; }

.card__list {
  list-style: none;
  padding: 0;
  margin: var(--sp-4) 0 0;
}
.card__list li {
  position: relative;
  padding-left: 1.5rem;
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.card__list li + li { margin-top: var(--sp-3); }
.card__list li::before {
  content: "";
  position: absolute;
  left: 1px;
  top: 0.55em;
  width: 9px;
  height: 9px;
  background: var(--sage-400);
  border-radius: var(--r-leaf);
}

/* Numbered card */

.card__num {
  font-family: var(--font-display);
  font-size: 2.75rem;
  font-weight: 800;
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 1;
  letter-spacing: -0.05em;
  color: var(--mint-200);
  margin-bottom: var(--sp-4);
}

/* Dark card variant — glass on forest bands */

.section--dark .card {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--border-on-dark);
  box-shadow: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.section--dark .card:hover {
  border-color: rgba(255, 255, 255, 0.30);
  background: rgba(255, 255, 255, 0.09);
}
.section--dark .card p,
.section--dark .card__list li { color: rgba(255, 255, 255, 0.78); }
.section--dark .card__list li::before { background: var(--sage-400); }
.section--dark .card__icon,
.section--dark .card__icon--navy {
  background: rgba(255, 255, 255, 0.1);
  color: var(--sage-300);
}
.section--dark .card:hover .card__icon { background: var(--sage-400); color: var(--green-900); }
.section--dark .card__num { color: rgba(143, 207, 168, 0.5); }
.section--dark .card--accent { background: rgba(255, 255, 255, 0.06); }

/* ==========================================================================
   6. Focus-area tiles
   ========================================================================== */

.focus-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: var(--sp-4);
}
.focus-tile {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--surface-000);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease),
              box-shadow 380ms var(--ease),
              transform 380ms var(--ease-out);
}
/* A leaf of colour that grows from the corner on hover */
.focus-tile::after {
  content: "";
  position: absolute;
  right: -40px;
  bottom: -40px;
  width: 110px;
  height: 110px;
  background: var(--mint-100);
  border-radius: var(--r-leaf);
  transform: scale(0.4);
  opacity: 0;
  transition: transform 520ms var(--ease-out), opacity 320ms var(--ease);
  z-index: -1;
}
.focus-tile:hover {
  border-color: var(--mint-200);
  box-shadow: var(--shadow-md);
  transform: translateY(-4px);
}
.focus-tile:hover::after { transform: scale(1); opacity: 1; }
.focus-tile svg {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--green-700);
  transition: transform 420ms var(--ease-spring);
}
.focus-tile:hover svg { transform: scale(1.14) rotate(-6deg); }
.focus-tile h3 {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: -0.022em;
  color: var(--green-900);
  line-height: 1.3;
  max-width: none;
}

/* ==========================================================================
   7. People
   ========================================================================== */

.people-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 210px), 1fr));
  gap: var(--sp-6) var(--sp-5);
}

.person { text-align: center; }
.person__photo {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  margin-bottom: var(--sp-4);
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--mint-100);
}
.person__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(7, 31, 22, 0.42), transparent 52%);
  opacity: 0;
  transition: opacity 380ms var(--ease);
}
.person:hover .person__photo::after { opacity: 1; }
.person__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  transition: transform 620ms var(--ease-out);
}
.person:hover .person__photo img { transform: scale(1.06); }

.person__name {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -0.026em;
  color: var(--green-900);
  line-height: 1.2;
  margin-bottom: var(--sp-2);
  max-width: none;
}
.person__role {
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--text-muted);
  max-width: none;
}
/* .person__creds — the small uppercase qualifications line that sat between the
   photograph and the name ("PH.D. (IIT/K), FIAPS", "LL.M.") — was removed at
   the client's request (Changes.txt item 14, SS 12 / 12.1 / 13). It read as a
   floating label above the wrong person on a wrapped grid row. The rule is
   deleted rather than left orphaned; restore from git history if it is ever
   wanted back. */

/* Profile link under each person (Changes.txt item 13). Sized to the 44px tap
   target the rest of the site keeps, so it stays usable on a phone. */
.person__link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: var(--sp-3);
  padding: 0.35rem 0.7rem;
  font-size: var(--fs-xs);
  font-weight: 600;
  /* green-800, not green-700: on the mint-100 chip green-700 measures 4.41:1,
     which fails AA for 13px text. green-800 measures 7.3:1. This is the same
     pairing .eyebrow already uses, for the same reason. */
  color: var(--green-800);
  background: var(--mint-100);
  border-radius: var(--r-pill);
  text-decoration: none;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.person__link svg { width: 14px; height: 14px; flex: none; }
.person__link:hover {
  background: var(--green-700);
  color: #fff;
}

/* Founder feature */

.founder {
  display: grid;
  gap: var(--sp-7);
  align-items: start;
}
@media (min-width: 860px) {
  .founder { grid-template-columns: 300px 1fr; gap: var(--sp-9); }
}
.founder__photo {
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--mint-100);
  box-shadow: var(--shadow-lg);
}
.founder__photo img { width: 100%; }

.founder blockquote {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: var(--lh-relaxed);
  letter-spacing: normal;
  color: var(--text-secondary);
  padding-left: var(--sp-5);
  border-left: 3px solid var(--sage-400);
}
.founder blockquote p + p { margin-top: var(--sp-4); }

.signature {
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--border);
}
.signature__name {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -0.026em;
  color: var(--green-900);
}
.signature__role {
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

/* ==========================================================================
   8. Tables
   ========================================================================== */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface-000);
  -webkit-overflow-scrolling: touch;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
  min-width: 520px;
}
caption {
  text-align: left;
  padding: var(--sp-4) var(--sp-5);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
th, td {
  padding: var(--sp-4) var(--sp-5);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}
thead th {
  background: var(--green-900);
  color: #fff;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:nth-child(even) { background: var(--mint-050); }
td strong { color: var(--green-800); }

/* ==========================================================================
   9. Programme catalogue (accordion)
   ========================================================================== */

.programme {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface-000);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.programme + .programme { margin-top: var(--sp-3); }
.programme.is-open { border-color: var(--mint-200); box-shadow: var(--shadow-md); }

.programme__toggle {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  width: 100%;
  padding: var(--sp-5);
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}
.programme__toggle:hover { background: var(--mint-050); }

.programme__toggle > svg:first-child {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  color: var(--green-700);
}

.programme__title {
  flex: 1;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -0.026em;
  color: var(--green-900);
  line-height: 1.25;
}

.programme__chev {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--ink-300);
  transition: transform var(--dur) var(--ease);
}
.programme.is-open .programme__chev { transform: rotate(180deg); }

.programme__panel {
  display: none;
  padding: 0 var(--sp-5) var(--sp-5);
  border-top: 1px solid var(--border);
}
.programme.is-open .programme__panel { display: block; }

.programme__cols {
  display: grid;
  gap: var(--sp-6);
  padding-top: var(--sp-5);
}
@media (min-width: 640px) {
  .programme__cols { grid-template-columns: 1fr 1fr; }
}

.programme__cols h4 {
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--green-700);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
  max-width: none;
}
.programme__cols ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.programme__cols li {
  position: relative;
  padding-left: 1.15rem;
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.programme__cols li + li { margin-top: var(--sp-2); }
.programme__cols li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 7px;
  height: 7px;
  background: var(--sage-400);
  border-radius: var(--r-leaf);
}

/* ==========================================================================
   10. Split feature + sticky scene
   ========================================================================== */

.split {
  display: grid;
  gap: var(--sp-7);
  align-items: center;
}
/* See the .grid > * note in base.css — same reason. */
.split > *,
.hero__grid > *,
.founder > *,
.programme__cols > *,
.event > *,
.form-grid > *,
.footer__grid > *,
.stats__grid > * { min-width: 0; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; gap: var(--sp-9); }
  .split--wide-left { grid-template-columns: 1.2fr 0.8fr; }
}

/* Heading in one column, body prose in the other. Both must start at the same
   baseline or the heading floats in the middle of the paragraph beside it —
   .split centres by default, which is right for cards and wrong for text. */
.split--top { align-items: start; }

/* Sticky storytelling scene: the first column pins while the second scrolls
   past it. Only above the breakpoint — stacked columns must never stick. */
@media (min-width: 900px) {
  .split--sticky { align-items: start; }
  .split--sticky > *:first-child {
    position: sticky;
    top: calc(var(--header-h) + var(--sp-7));
    align-self: start;
  }
}

.split__media {
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.split__media img { width: 100%; }

/* ==========================================================================
   11. Events
   ========================================================================== */

.event {
  display: grid;
  gap: var(--sp-6);
  padding: var(--sp-6);
  background: var(--surface-000);
  border: 1px solid var(--border);
  border-left: 4px solid var(--green-700);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
@media (min-width: 780px) {
  .event { grid-template-columns: 160px 1fr; align-items: start; }
}

.event__date {
  padding: var(--sp-4);
  background: var(--mint-050);
  border-radius: var(--r-md);
  text-align: center;
}
.event__date-label {
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.event__date-value {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 800;
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: -0.03em;
  color: var(--green-800);
  line-height: 1.2;
  margin-top: 5px;
}

.event__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin: var(--sp-4) 0;
  padding: var(--sp-4) 0;
  border-block: 1px solid var(--border);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.event__meta div {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.event__meta svg {
  width: 16px;
  height: 16px;
  color: var(--green-700);
  flex-shrink: 0;
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
}
.tag {
  padding: 6px 13px;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--green-800);
  background: var(--mint-100);
  border-radius: var(--r-pill);
}

/* Simple event list */

.event-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.event-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--border);
}
.event-list li + li { margin-top: 0; }
.event-list svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  margin-top: 3px;
  color: var(--green-700);
}
.event-list h3 {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: -0.022em;
  color: var(--green-900);
  max-width: none;
}

/* ==========================================================================
   12. Gallery
   ========================================================================== */

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
  gap: var(--sp-4);
}

.gallery-item {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--mint-100);
  cursor: zoom-in;
  padding: 0;
  border: 0;
}
.gallery-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 620ms var(--ease-out);
}
.gallery-item:hover img { transform: scale(1.07); }
.gallery-item::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(11, 59, 42, 0.5), transparent 55%);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.gallery-item:hover::after { opacity: 1; }

.gallery-item--tall { aspect-ratio: 3 / 4; }

/* ----- Poster and card walls -----
   Fixes the dead space the client circled (Changes.txt item 30, SS 24).

   THE BUG: .gallery-item sets its height from its own aspect-ratio, so a
   landscape 4/3 tile computed to 213px while a portrait 3/4 tile beside it
   computed to 379px. CSS grid makes the row as tall as its tallest item, so
   every landscape image sat above 166px of empty white.

   THE FIX: one aspect ratio for every tile in the grid, so the rows cannot
   disagree. Posters and testimonial cards are then fitted with `contain`
   rather than `cover`, because the wording designed into them is the content —
   cropping a poster to fill a tile cuts off its dates and speaker names. The
   letterbox sits on a mint field so it reads as a deliberate mount.

   Photographs keep `cover`: there is no text to lose, and edge-to-edge is what
   makes a photo wall look like a photo wall. */
.gallery-grid--posters .gallery-item,
.gallery-grid--posters .gallery-item--tall {
  aspect-ratio: 3 / 4;
  background: var(--mint-050);
  border: 1px solid var(--border);
}
.gallery-grid--posters .gallery-item img {
  object-fit: contain;
  padding: var(--sp-2);
}
/* The zoom-on-hover that suits a photograph makes poster text swim, so posters
   lift instead. */
.gallery-grid--posters .gallery-item:hover img { transform: none; }
.gallery-grid--posters .gallery-item {
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform 380ms var(--ease-out);
}
.gallery-grid--posters .gallery-item:hover {
  border-color: var(--mint-200);
  box-shadow: var(--shadow-md);
  transform: translateY(-4px);
}

/* Lightbox */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--sp-5);
  background: rgba(6, 26, 19, 0.92);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.lightbox.is-open { display: flex; }
.lightbox img {
  max-width: 100%;
  max-height: 88vh;
  border-radius: var(--r-md);
  object-fit: contain;
}
.lightbox__close {
  position: absolute;
  top: var(--sp-5);
  right: var(--sp-5);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 50%;
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}
.lightbox__close:hover { background: rgba(255, 255, 255, 0.22); }

.lightbox__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 50%;
  color: #fff;
  font-size: 1.5rem;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease);
}
.lightbox__nav--prev { left: var(--sp-5); }
.lightbox__nav--next { right: var(--sp-5); }
.lightbox__nav:hover { background: rgba(255, 255, 255, 0.22); }

/* ==========================================================================
   13. CTA band — gradient mesh + grain
   ========================================================================== */

.cta-band {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background-color: var(--green-950);
  background-image: var(--mesh-deep);
  color: #fff;
  padding-block: clamp(3rem, 5vw, 4.5rem);
}
.cta-band::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--noise);
  background-size: 160px 160px;
  opacity: 0.16;
  mix-blend-mode: overlay;
  pointer-events: none;
  z-index: -1;
}
.cta-band::after {
  content: "";
  position: absolute;
  right: -90px;
  bottom: -120px;
  width: 360px;
  height: 360px;
  background: rgba(255, 255, 255, 0.035);
  border-radius: var(--r-leaf);
  pointer-events: none;
  z-index: -1;
}
.cta-band > .container { position: relative; z-index: 1; }
.cta-band h2 {
  color: #fff;
  font-size: var(--fs-3xl);
  letter-spacing: -0.035em;
  line-height: 1.02;
  margin-bottom: var(--sp-5);
  margin-inline: auto;
  max-width: 18ch;
}
.cta-band h2 em { color: var(--gold-400); font-style: normal; }
.cta-band p {
  font-size: var(--fs-md);
  line-height: var(--lh-relaxed);
  color: rgba(255, 255, 255, 0.82);
  margin-inline: auto;
  margin-bottom: var(--sp-7);
}
.cta-band .btn-group { justify-content: center; }

/* ==========================================================================
   14. Marquee — continuous ambient motion
   ========================================================================== */

.marquee {
  position: relative;
  overflow: hidden;
  /* Fade the ends so items enter and leave rather than being cut off */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee__track {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  width: max-content;
  animation: marquee-scroll 42s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__track > * { flex: none; }
@keyframes marquee-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
  .marquee { overflow-x: auto; }
}

/* ==========================================================================
   15. Forms
   ========================================================================== */

.form-grid {
  display: grid;
  gap: var(--sp-5);
}
@media (min-width: 620px) {
  .form-grid--2 { grid-template-columns: 1fr 1fr; }
  .form-field--full { grid-column: 1 / -1; }
}

.form-field label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--green-800);
}
.form-field .req { color: var(--gold-600); }

.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  padding: 0.85rem 1rem;
  font-size: var(--fs-base);
  color: var(--ink-900);
  background: var(--surface-000);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.form-field textarea { resize: vertical; min-height: 150px; }

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--green-600);
  box-shadow: 0 0 0 3px rgba(30, 142, 71, 0.16);
}

.form-field input::placeholder,
.form-field textarea::placeholder { color: var(--ink-300); }

.form-note {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.form-status {
  display: none;
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  font-weight: 500;
}
.form-status.is-visible { display: block; }
.form-status--ok {
  background: var(--mint-100);
  border: 1px solid var(--sage-400);
  color: var(--green-800);
}
.form-status--err {
  background: #fdf2f2;
  border: 1px solid #f3b9b4;
  color: var(--red-600);
}
.form-status--busy {
  background: var(--surface-100);
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
}

/* ----- Long forms: numbered fieldsets -----
   The membership form runs to 28 fields. Grouping it into numbered fieldsets
   keeps it navigable without a multi-step wizard, which would need JavaScript
   to be usable at all. */

.form-fieldset {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-card);
  padding: var(--sp-6);
  margin: 0 0 var(--sp-5);
}
.form-fieldset > legend {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0 var(--sp-3);
  margin-left: calc(-1 * var(--sp-3));
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -0.025em;
  color: var(--green-900);
}
.form-fieldset > legend .step {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: #fff;
  background: var(--green-700);
  border-radius: var(--r-leaf);
}
.form-fieldset__intro {
  margin: var(--sp-3) 0 var(--sp-5);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}

@media (max-width: 619px) {
  .form-fieldset { padding: var(--sp-5) var(--sp-4); }
}

/* ----- Radio / checkbox groups ----- */

.choice-grid {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
}

.choice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--surface-000);
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--ink-800);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.choice:hover { border-color: var(--green-600); background: var(--mint-050); }
.choice input {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 1px;
  accent-color: var(--green-700);
}
.choice:has(input:checked) {
  border-color: var(--green-700);
  background: var(--mint-100);
  font-weight: 600;
}
.choice:has(input:focus-visible) {
  outline: 3px solid var(--green-600);
  outline-offset: 2px;
}

/* The declaration is a single full-width choice, so it reads as a statement */
.choice--wide { grid-column: 1 / -1; align-items: flex-start; }

/* Fee shown beneath a membership category, so the applicant sees the cost at
   the moment they choose rather than having to scroll back to the table. */
.choice small {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--fs-xs);
  font-weight: 400;
  color: var(--text-muted);
}
.choice:has(input:checked) small { color: var(--green-800); }

/* ----- File inputs ----- */

.file-field input[type="file"] {
  width: 100%;
  padding: 0.6rem 0.7rem;
  font-size: var(--fs-sm);
  color: var(--ink-700);
  background: var(--surface-000);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-md);
  cursor: pointer;
}
.file-field input[type="file"]::file-selector-button {
  margin-right: var(--sp-4);
  padding: 0.45rem 1rem;
  font: inherit;
  font-weight: 600;
  color: #fff;
  background: var(--green-700);
  border: 0;
  border-radius: var(--r-pill);
  cursor: pointer;
}
.file-field input[type="file"]:hover { border-color: var(--green-600); }
.file-field input[type="file"]:focus-visible {
  outline: none;
  border-color: var(--green-600);
  box-shadow: 0 0 0 3px rgba(30, 142, 71, 0.16);
}
.file-field .form-note { margin-top: var(--sp-2); }

/* Per-field validation message, shown by main.js before anything is sent */
.field-error {
  display: none;
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--red-600);
}
.field-error.is-visible { display: block; }
.form-field.has-error input,
.form-field.has-error select,
.form-field.has-error textarea { border-color: var(--red-600); }

/* ----- Submit row ----- */

.form-submit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  margin-top: var(--sp-5);
}
.form-submit .form-note { flex: 1 1 260px; margin: 0; }

/* ----- Direct contact channels (Changes.txt item 17) -----
   Sits on the dark CTA band, so every colour here is measured against
   --green-800/900 rather than white. */

.channel-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-3);
  margin-top: var(--sp-7);
}

.channel {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  /* 44px minimum tap target, the same floor the footer links use */
  min-height: 44px;
  padding: 0.6rem 1.05rem;
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.25;
  color: #fff;
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: var(--r-pill);
  text-decoration: none;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease-out);
}
.channel svg { width: 18px; height: 18px; flex: none; }

.cta-band .channel { color: #fff; }
.channel:hover,
.cta-band .channel:hover {
  color: var(--green-900);
  background: var(--gold-300);
  border-color: var(--gold-300);
  transform: translateY(-2px);
}

/* Awaiting an account handle from the client. Deliberately not a link and not
   focusable — a control that looks live but goes nowhere is worse than one
   that plainly says it is not ready yet. */
.channel--soon {
  cursor: default;
  opacity: 0.62;
  background: rgba(255, 255, 255, 0.05);
  border-style: dashed;
}
.channel--soon:hover,
.cta-band .channel--soon:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.22);
  transform: none;
}
.channel small {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 400;
  /* 0.62 opacity on the parent already dims this; keep it at full white so the
     composite stays legible rather than compounding two reductions. */
  color: #fff;
}

/* ==========================================================================
   16. Contact cards
   ========================================================================== */

.contact-card {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--surface-000);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              transform 380ms var(--ease-out);
}
.contact-card:hover {
  border-color: var(--mint-200);
  box-shadow: var(--shadow-md);
  transform: translateY(-3px);
}
.contact-card__icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  background: var(--mint-100);
  border-radius: var(--r-leaf);
  color: var(--green-700);
}
.contact-card__icon svg { width: 20px; height: 20px; }
/* h2 because these cards sit directly under the page h1 with no intervening
   section heading — the level keeps the outline gap-free; the styling below
   is what makes it read as a small label. */
.contact-card h2 {
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--sp-2);
  max-width: none;
}
.contact-card p,
.contact-card a {
  font-size: var(--fs-sm);
  color: var(--text-primary);
  max-width: none;
  line-height: 1.65;
}
.contact-card a { text-decoration: none; font-weight: 500; }
.contact-card a:hover { color: var(--link); text-decoration: underline; }

/* ==========================================================================
   17. Placeholder (flagged content gaps)
   ========================================================================== */

.placeholder {
  padding: var(--sp-6);
  background: repeating-linear-gradient(
    45deg,
    #fdf8ec,
    #fdf8ec 12px,
    var(--gold-100) 12px,
    var(--gold-100) 24px
  );
  border: 1px dashed var(--gold-500);
  border-radius: var(--r-lg);
  text-align: center;
}
.placeholder__tag {
  display: inline-block;
  margin-bottom: var(--sp-3);
  padding: 5px 13px;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fff;
  background: var(--gold-600);
  border-radius: var(--r-pill);
}
.placeholder p {
  max-width: 60ch;
  margin-inline: auto;
  font-size: var(--fs-sm);
  color: var(--gold-700);
}
.placeholder p + p { margin-top: var(--sp-3); }

/* ==========================================================================
   18. Footer
   ========================================================================== */

.site-footer {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--green-950);
  color: rgba(255, 255, 255, 0.76);
  padding-top: var(--sp-10);
}
.site-footer::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--mesh-band);
  opacity: 0.55;
  pointer-events: none;
  z-index: -1;
}
.site-footer::before {
  content: "";
  position: absolute;
  left: -100px;
  bottom: -140px;
  width: 420px;
  height: 420px;
  background: rgba(255, 255, 255, 0.03);
  border-radius: var(--r-leaf);
  pointer-events: none;
  z-index: -1;
}
.site-footer > .container { position: relative; z-index: 1; }
.site-footer h3 {
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sage-300);
  margin-bottom: var(--sp-5);
  max-width: none;
}

.footer__grid {
  display: grid;
  gap: var(--sp-7);
  padding-bottom: var(--sp-8);
}
@media (min-width: 760px) {
  .footer__grid { grid-template-columns: 1.6fr 1fr 1fr; }
}
@media (min-width: 1024px) {
  .footer__grid { grid-template-columns: 1.8fr 1fr 1fr 1.4fr; }
}

.footer__brand-logo {
  height: 54px;
  width: auto;
  margin-bottom: var(--sp-5);
  background: #fff;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
}
.footer__about {
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: rgba(255, 255, 255, 0.68);
  max-width: 42ch;
}
.footer__tagline {
  margin-top: var(--sp-5);
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: -0.022em;
  color: var(--gold-400);
}

.footer__list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.footer__list li + li { margin-top: var(--sp-3); }
.footer__list a {
  font-size: var(--fs-sm);
  color: rgba(255, 255, 255, 0.74);
  text-decoration: none;
  transition: color var(--dur) var(--ease);
/* These links were 19px tall — under the 24px minimum in WCAG 2.2 SC 2.5.8,
   which is easy to mis-tap on a phone. The padding grows the touch target;
   the matching negative margin cancels its effect on layout, so the footer
   keeps exactly the spacing it had. */
  display: inline-block;
  padding-block: 4px;
  margin-block: -4px;
}
.footer__list a:hover { color: var(--sage-300); }

.footer__contact {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: var(--fs-sm);
}
.footer__contact li {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  line-height: 1.65;
}
.footer__contact li + li { margin-top: var(--sp-4); }
.footer__contact svg {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  margin-top: 4px;
  color: var(--sage-300);
}
.footer__contact a {
  color: rgba(255, 255, 255, 0.84);
  text-decoration: none;
  word-break: break-word;
/* Same 24px touch-target correction as .footer__list a above. */
  display: inline-block;
  padding-block: 4px;
  margin-block: -4px;
}
.footer__contact a:hover { color: var(--sage-300); }

.footer__legal-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding-block: var(--sp-6);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.footer__legal-badge {
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-xs);
  color: rgba(255, 255, 255, 0.74);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--r-pill);
}
.footer__legal-badge strong { color: var(--gold-400); font-weight: 700; }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--sp-5);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  font-size: var(--fs-xs);
  color: rgba(255, 255, 255, 0.58);
}
.footer__bottom p { max-width: none; }
.footer__social {
  display: flex;
  gap: var(--sp-3);
  list-style: none;
  padding: 0;
  margin: 0;
}
.footer__social li + li { margin-top: 0; }
.footer__social a {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 50%;
  color: rgba(255, 255, 255, 0.78);
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              transform var(--dur) var(--ease-spring);
}
.footer__social a:hover {
  background: var(--sage-400);
  border-color: var(--sage-400);
  color: var(--green-900);
  transform: translateY(-3px);
}
.footer__social svg { width: 17px; height: 17px; }

/* ==========================================================================
   19. Back to top
   ========================================================================== */

.backtotop {
  position: fixed;
  right: var(--sp-5);
  bottom: var(--sp-5);
  z-index: 90;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  background: rgba(21, 128, 61, 0.88);
  border: 1px solid rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  box-shadow: var(--shadow-lg);
  transition: opacity var(--dur) var(--ease),
              transform var(--dur) var(--ease-spring),
              visibility var(--dur),
              background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.backtotop.is-visible { opacity: 1; visibility: visible; transform: none; }
.backtotop:hover {
  background: var(--green-900);
  border-color: var(--green-900);
  color: #fff;
  transform: translateY(-3px);
}
.backtotop svg { width: 18px; height: 18px; }

/* ==========================================================================
   20. Anchor offset helper
   ========================================================================== */

.anchor {
  display: block;
  position: relative;
  top: calc(-1 * var(--header-h) - 12px);
  visibility: hidden;
}
