/* ==========================================================================
   Greater Springfield VT Habitat for Humanity — styles.css

   Mobile-first, no build step. Component rules never carry a hex literal:
   every color routes through a semantic --color-* token so a rebrand stays a
   one-block edit in :root. See CLAUDE.md; habitat-site-check enforces it.
   ========================================================================== */

:root {
  /* Official HFHI chips. These hex values are the brand, not a design choice,
     and must never be tinted or alpha-faded. Need another stop? Derive a named
     variable below rather than fading one of these. */
  --hfh-bright-blue: #00AFD7;
  --hfh-bright-green: #C4D600;
  --hfh-white: #FFFFFF;
  --hfh-gray: #888B8D;
  --hfh-black: #000000;

  /* Secondary palette (official HFHI). --hfh-orange and --hfh-brick are
     currently unused; kept as the sanctioned values should copy ever need
     an alert or accent stop. */
  --hfh-habitat-blue: #002F6C;
  --hfh-habitat-green: #43B02A;
  --hfh-orange: #FF671F;
  --hfh-brick: #A4343A;
  --hfh-yellow: #FFD100;

  /* Derived stops, not official chips. Hand-picked rather than computed so
     they stay stable and pass contrast against white text. */
  --hfh-green-800: #27500A; /* nav background */
  --hfh-green-900: #173404; /* hero scrim, pressed nav state */
  --hfh-blue-700: #1D4F8F;  /* hover stop for Habitat-Blue buttons */

  /* Semantic layer. Component rules reference only these. */
  --color-page-bg: var(--hfh-white);
  --color-nav-bg: var(--hfh-green-800);
  --color-nav-text: var(--hfh-white);
  --color-nav-accent-rule: var(--hfh-bright-green);
  --color-nav-cta-bg: var(--hfh-bright-green);
  --color-nav-cta-text: var(--hfh-green-900);
  --color-heading: var(--hfh-habitat-blue);
  --color-body-text: #444441; /* warm near-black; --hfh-gray fails 4.5:1 on white */
  --color-cta-bg: var(--hfh-habitat-green);
  --color-cta-text: var(--hfh-white);
  --color-footer-bg: var(--hfh-habitat-blue);
  --color-footer-text: var(--hfh-white);
  --color-section-alt-bg: #F5F5F2; /* neutral wash, deliberately not a tinted brand chip */
  --color-rule: #E2E2DE;
  --color-todo-bg: var(--hfh-yellow);
  --color-todo-text: var(--hfh-black);

  /* System font stack only: no webfont request, so no FOUT and nothing on the
     critical path. --measure caps prose at a comfortable line length. */
  --font-stack: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --measure: 68ch;
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --space-5: 4rem;
  --radius: 6px;
  --radius-lg: 10px; /* softer corner for cards and photos */
  /* Shared height for form controls. Fixed rather than intrinsic because
     Safari and Blink disagree on how line-height feeds an <input>'s height,
     and <button> ignores inherited line-height entirely; pinning both to one
     value is what keeps them aligned across engines. In rem so it still
     scales with the user's font-size setting, and 3rem clears the 44px
     minimum touch target at default sizing. */
  --control-h: 3rem;
  --container: 72rem;
}

/* ---------- Reset / base ---------- */

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

body {
  margin: 0;
  font-family: var(--font-stack);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--color-body-text);
  background: var(--color-page-bg);
  -webkit-text-size-adjust: 100%;
}

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

h1, h2, h3, h4 {
  color: var(--color-heading);
  line-height: 1.15;
  margin: 0 0 var(--space-2);
  font-weight: 700;
}

h1 { font-size: clamp(1.9rem, 5vw, 2.8rem); }
h2 { font-size: clamp(1.5rem, 3.5vw, 2rem); }
h3 { font-size: 1.25rem; }

p { margin: 0 0 var(--space-2); max-width: var(--measure); }

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

a:hover { color: var(--hfh-habitat-green); }

/* WCAG 2.4.7. Bright Blue is used here precisely because it appears nowhere
   else as a background, so the ring never disappears into what it outlines. */
:focus-visible {
  outline: 3px solid var(--hfh-bright-blue);
  outline-offset: 2px;
}

.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-2);
}

.section { padding-block: var(--space-4); }
.section--alt { background: var(--color-section-alt-bg); }

/* ---------- Skip link ---------- */

/* Parked off-canvas rather than display:none, which would drop it from the tab
   order and defeat the point. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-heading);
  color: var(--hfh-white);
  padding: var(--space-1) var(--space-2);
  z-index: 100;
}
.skip-link:focus {
  left: 0;
  color: var(--hfh-white);
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  min-height: 44px;
  padding: 0.7rem 1.6rem;
  border: 0;
  border-radius: var(--radius);
  font-size: 1rem;
  font-weight: 700;
  text-decoration: none;
  text-align: center;
  cursor: pointer;
  background: var(--color-cta-bg);
  color: var(--color-cta-text);
  transition: filter 0.15s ease, transform 0.15s ease;
}
.btn:hover { filter: brightness(0.9); color: var(--color-cta-text); }
.btn:active { transform: translateY(1px); }

/* Secondary CTA. Navy is already dark enough that the base rule's
   brightness(0.9) reads as no change, so hover swaps to a lighter stop and
   cancels the filter instead. */
.btn--blue {
  background: var(--hfh-habitat-blue);
  color: var(--hfh-white);
}
.btn--blue:hover {
  background: var(--hfh-blue-700);
  color: var(--hfh-white);
  filter: none;
}

.btn--nav {
  background: var(--color-nav-cta-bg);
  color: var(--color-nav-cta-text);
}
.btn--nav:hover { color: var(--color-nav-cta-text); }

/* ---------- Header / nav (mobile-first) ---------- */

.site-header {
  background: var(--color-nav-bg);
  border-bottom: 4px solid var(--color-nav-accent-rule);
  position: relative;
  z-index: 50;
}

.site-header__inner {
  max-width: var(--container);
  margin-inline: auto;
  padding: var(--space-1) var(--space-2);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* White plate under the mark. The HFHI brand guide forbids recoloring or
   inverting the logo, so the background yields to it rather than the reverse.
   Same pattern applies to any third-party mark (see .site-footer__equal-housing). */
.site-logo {
  display: inline-flex;
  align-items: center;
  background: var(--hfh-white);
  border-radius: var(--radius);
  padding: 8px 14px;
}
.site-logo img { height: 58px; width: auto; }

.nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 44px;
  min-height: 44px;
  padding: 0.4rem 0.8rem;
  background: transparent;
  border: 2px solid var(--color-nav-text);
  border-radius: var(--radius);
  color: var(--color-nav-text);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.nav-toggle__bars {
  display: inline-block;
  width: 20px;
  height: 2px;
  background: currentColor;
  position: relative;
}
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 20px;
  height: 2px;
  background: currentColor;
}
.nav-toggle__bars::before { top: -6px; }
.nav-toggle__bars::after { top: 6px; }

/* Open state keys off aria-expanded rather than a separate class, so the
   visual and the accessibility contract can't drift apart. */
.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  transition: top 0.15s ease, transform 0.15s ease, background 0.15s ease;
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after { top: 0; transform: rotate(-45deg); }

.site-nav {
  display: none; /* .is-open toggles it on mobile; forced visible at desktop */
  flex-basis: 100%;
}
.site-nav.is-open { display: block; }

.site-nav > ul {
  list-style: none;
  margin: 0;
  padding: var(--space-1) 0 var(--space-2);
  display: flex;
  flex-direction: column;
}

/* :not(.btn) throughout this block: the Donate CTA lives in the same list but
   must keep its button styling, not inherit the nav-link treatment. */
.site-nav a:not(.btn) {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.9rem;
  color: var(--color-nav-text);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  text-decoration: none;
  background: none;
  border: 0;
  width: 100%;
  font-family: inherit;
  cursor: pointer;
  /* Underline is a background-size transition on a solid gradient, not a
     border: it animates on the compositor and reserves no layout space, so
     hovering never shifts the row. */
  background-image: linear-gradient(var(--color-nav-accent-rule), var(--color-nav-accent-rule));
  background-repeat: no-repeat;
  background-size: 0% 3px;
  background-position: 0.9rem calc(100% - 4px);
  transition: background-size 0.2s ease;
}

.site-nav a:not(.btn):hover,
.site-nav a:not(.btn):focus-visible {
  color: var(--color-nav-text);
  background-size: calc(100% - 1.8rem) 3px;
}

.site-nav a:not(.btn)[aria-current="page"] {
  background-size: calc(100% - 1.8rem) 3px;
}

/* Mobile-only affordances, undone in the desktop query below. In a stacked
   menu the thin underline is easy to miss, so the current page gets a left
   accent bar on a darker panel instead (background-image:none suppresses the
   inherited underline so only one marker shows), plus a pressed state that
   substitutes for the hover cue phones don't have. */
.site-nav a:not(.btn):active { background-color: var(--hfh-green-900); }
.site-nav a:not(.btn)[aria-current="page"] {
  background-color: var(--hfh-green-900);
  border-left: 4px solid var(--color-nav-accent-rule);
  padding-left: calc(0.9rem - 4px); /* absorb the border so text stays aligned */
  background-image: none;
}

.nav-cta { margin-top: var(--space-1); padding-inline: 0.9rem; }
.nav-cta .btn { width: auto; display: inline-block; }

/* Desktop nav. 56em is the width at which the flat nav plus the Donate button
   stop wrapping; main.js watches the same breakpoint. */
@media (min-width: 56em) {
  .nav-toggle { display: none; }

  /* Unconditionally visible: .is-open may linger from a mobile-width
     interaction, but it also resolves to display:block, so this needs no
     !important to win. */
  .site-nav {
    display: block;
    flex-basis: auto;
  }
  .site-nav > ul {
    flex-direction: row;
    align-items: center;
    gap: 0.2rem;
    padding: 0;
  }
  .site-nav a:not(.btn) { width: auto; }

  .site-nav a:not(.btn):active { background-color: transparent; }
  .site-nav a:not(.btn)[aria-current="page"] {
    background-color: transparent;
    border-left: 0;
    padding-left: 0.9rem;
  }

  .nav-cta { margin: 0 0 0 var(--space-1); }
  .nav-cta .btn { width: auto; }
}

/* ---------- Hero ---------- */

/* The photo is dimmed against a dark green plate rather than overlaid with a
   gradient, so the headline's contrast ratio holds no matter which photo is
   swapped in. Text-shadow on the content below is belt-and-braces for the
   brightest crops. */
.hero {
  position: relative;
  background: var(--hfh-green-900);
}

/* Fixed height (not aspect-ratio) keeps the overlaid text block from
   reflowing across viewports; object-fit crops rather than distorts. */
.hero img.hero__img {
  width: 100%;
  height: min(62vh, 34rem);
  min-height: 20rem;
  object-fit: cover;
  opacity: 0.55;
}

.hero__content {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-2);
  width: 100%;
}

.hero__content h1 {
  color: var(--hfh-white);
  max-width: 22ch;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

.hero__content p {
  color: var(--hfh-white);
  font-size: 1.15rem;
  max-width: 46ch;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.45);
}

.hero__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-1); }

/* Interior pages get a flat color band instead of a photo hero: no second
   large image to download, and no per-page art direction to maintain. */
.page-hero {
  background: var(--color-heading);
  padding-block: var(--space-4);
}
.page-hero h1 { color: var(--hfh-white); margin: 0; }
.page-hero p { color: var(--hfh-white); margin: var(--space-1) 0 0; }

/* ---------- Feature / story card grid ---------- */

.card-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
  padding: 0;
  margin: var(--space-3) 0 0;
  list-style: none;
}
@media (min-width: 40em) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 60em) { .card-grid--3 { grid-template-columns: repeat(3, 1fr); } }

.card {
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-page-bg);
  display: flex;
  flex-direction: column;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
/* Lift only on cards that are actually clickable, i.e. those containing the
   stretched .card__more link. Board cards have no link and must stay static,
   or the hover promises an interaction that isn't there. */
.card:has(.card__more):hover,
.card:has(.card__more):focus-within {
  transform: translateY(-3px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.card img { aspect-ratio: 3 / 2; object-fit: cover; width: 100%; }

/* Board headshots crop square instead of 3:2. Written as ".card .card__media--photo"
   purely to outrank ".card img" (0,1,1) above. */
.card .card__media--photo {
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

/* Board cards omit the title line for some members, and .card__body p's flex:1
   only fires when a <p> exists, so a name-only card collapses shorter than its
   row neighbors (Grid stretches the card, not the body inside it). min-height
   is the computed height of a full two-line body: 2.5rem padding + 1.44rem h3 +
   0.5rem gap + 1.7rem p, rounded up. Revisit if the type scale changes. */
.card--board .card__body {
  flex: 1;
  min-height: 6.15rem;
}
.card__body { padding: var(--space-2) var(--space-2) var(--space-3); display: flex; flex-direction: column; gap: var(--space-1); flex: 1; }
.card__body h3 { margin: 0; }
.card__body p { margin: 0; flex: 1; }

.card__more {
  font-weight: 700;
  text-decoration: none;
  align-self: flex-start;
  padding-block: 0.35rem; /* lifts the line box to a 44px touch target */
  background-image: linear-gradient(var(--hfh-habitat-green), var(--hfh-habitat-green));
  background-repeat: no-repeat;
  background-size: 0% 2px;
  background-position: 0 100%;
  transition: background-size 0.2s ease;
}
.card__more:hover, .card__more:focus-visible { background-size: 100% 2px; }
.card__more::after { content: " →"; }

/* Stretched-link trick: one pseudo-element makes the whole card clickable
   while the markup keeps a single real anchor, so screen readers announce one
   link with meaningful text rather than a wrapper around the whole card. */
.card { position: relative; }
.card__more::before { content: ""; position: absolute; inset: 0; }

/* ---------- Representative projects photo array ----------
   Uniform 4:3 tiles: the source photos vary wildly in native ratio, and a
   ragged grid reads as broken rather than editorial. */

.project-grid {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 40em) { .project-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 60em) { .project-grid { grid-template-columns: repeat(4, 1fr); } }

.project-tile img {
  border-radius: var(--radius-lg);
  aspect-ratio: 4 / 3;
  object-fit: cover;
  width: 100%;
}
.project-tile__caption {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-heading);
}

/* ---------- Photo gallery ---------- */

.photo-grid {
  display: grid;
  gap: var(--space-2);
  grid-template-columns: repeat(2, 1fr);
  margin-top: var(--space-3);
}
@media (min-width: 60em) { .photo-grid { grid-template-columns: repeat(3, 1fr); } }
.photo-grid img { border-radius: var(--radius-lg); aspect-ratio: 4 / 3; object-fit: cover; width: 100%; }
.photo-grid img.crop-upper { object-position: 50% 15%; }

/* ---------- Sponsor logo grid ---------- */

.sponsor-grid {
  list-style: none;
  margin: var(--space-3) auto 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-3) var(--space-4);
}
@media (min-width: 60em) {
  .sponsor-grid { max-width: 75%; }
}
/* Scale on hover/focus is the only affordance signalling these logos are
   links; underlines and color shifts are both off the table, since altering a
   sponsor's mark violates their usage terms as much as ours. */
.sponsor-grid a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  border-radius: var(--radius);
  transition: transform 0.15s ease;
}
.sponsor-grid a:hover,
.sponsor-grid a:focus-visible {
  transform: scale(1.05);
}
/* Bounded box + contain: sponsor logos arrive at arbitrary sizes and ratios,
   and this normalizes them without ever cropping or distorting a mark. */
.sponsor-grid img {
  max-height: 72px;
  width: auto;
  max-width: 180px;
  object-fit: contain;
}

/* ---------- Stats band ---------- */

.stats {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
  text-align: center;
  margin-top: var(--space-3);
}
@media (min-width: 40em) { .stats { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); } }
.stat__number {
  display: block;
  font-size: 2.6rem;
  font-weight: 700;
  color: var(--color-heading);
  line-height: 1;
}
.stat__label { display: block; margin-top: var(--space-1); }

/* ---------- Donate split ---------- */

.donate-band {
  background: var(--color-heading);
  color: var(--hfh-white);
  text-align: center;
}
.donate-band h2, .donate-band p { color: var(--hfh-white); margin-inline: auto; }
.donate-band p { max-width: 60ch; text-wrap: balance; }
.donate-band__actions {
  display: flex;
  gap: var(--space-2);
  justify-content: center;
  flex-wrap: wrap;
  margin-top: var(--space-3);
}
/* ---------- Newsletter inline form ---------- */

.newsletter { border-top: 4px solid var(--color-nav-accent-rule); }
.newsletter > .container > p { max-width: 60ch; text-wrap: balance; }

.newsletter form {
  display: grid;
  gap: var(--space-2);
  grid-template-columns: 1fr;
  max-width: 56rem;
  margin-top: var(--space-2);
}
@media (min-width: 48em) {
  .newsletter form { grid-template-columns: 1fr 1fr 1.3fr auto; align-items: end; }
  /* Turnstile renders at a fixed ~300px and won't compress into a grid column,
     so it and the status line span the full row. */
  .newsletter form .field--turnstile,
  .newsletter form .form-status { grid-column: 1 / -1; }
  /* The button is a bare grid item with no label above it. align-items:end
     sits it on the row's bottom edge, which is also the inputs' bottom edge,
     so giving it the inputs' exact height aligns the tops too. Earlier
     attempts derived the offset from the label's line box instead; that
     arithmetic rounded differently in Gecko and WebKit than in Blink and
     left the button a pixel out. Sharing one height means no engine has to
     agree with a calculation. */
  .newsletter form > .btn {
    height: var(--control-h);
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

/* Small print, but deliberately still at the body-text token: shrinking type
   and lightening it together is how privacy notices end up under 4.5:1. */
.form-privacy-note {
  margin-top: 0.75rem;
  font-size: 0.85rem;
  color: var(--color-body-text);
}

/* Honeypot. Positioned off-screen rather than display:none or
   visibility:hidden, which the better form-filling bots check for and skip.
   To a bot this is an ordinary input; to everyone else it is off-canvas, and
   tabindex="-1" plus aria-hidden in the markup keep it out of tab order and
   the accessibility tree. */
.field--honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- Forms ----------
   The newsletter signup is the only form on the site (contact is phone/email
   links by design), so these rules cover text and email inputs only. */

.field { display: flex; flex-direction: column; gap: 0.3rem; }
.field label { font-weight: 700; font-size: 0.95rem; color: var(--color-heading); }

input[type="text"], input[type="email"] {
  font: inherit;
  color: var(--color-body-text);
  padding: 0.65rem 0.8rem;
  height: var(--control-h);
  border: 1px solid var(--hfh-gray);
  border-radius: var(--radius);
  background: var(--hfh-white);
  width: 100%;
}

/* ---------- FAQ ---------- */

.faq { max-width: var(--measure); }
.faq details {
  border-bottom: 1px solid var(--color-rule);
}
/* Native <details> for the FAQ: keyboard, screen-reader, and find-in-page
   behavior come free, and there is no JS to load. Only the disclosure triangle
   is replaced, since it renders inconsistently across browsers. */
.faq summary {
  cursor: pointer;
  font-weight: 700;
  color: var(--color-heading);
  padding: var(--space-2) 0;
  padding-inline-end: var(--space-3);
  position: relative;
  list-style: none;
  min-height: 44px;
  display: flex;
  align-items: center;
}
/* Both selectors needed: Safari honors only the -webkit- form, and
   list-style:none above is what lets Chrome drop the marker at all. */
.faq summary::-webkit-details-marker { display: none; }
.faq summary::marker { content: ""; }
.faq summary::after {
  content: "";
  position: absolute;
  right: 0.25rem;
  top: 50%;
  width: 0.55rem;
  height: 0.55rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-70%) rotate(45deg);
  transform-origin: center;
  transition: transform 0.15s ease;
}
.faq details[open] > summary::after {
  transform: translateY(-30%) rotate(-135deg);
}
.faq summary:hover { color: var(--hfh-habitat-green); }
.faq details > *:not(summary) { padding-bottom: var(--space-2); }

/* ---------- News ---------- */

.post-meta { color: var(--hfh-gray); font-size: 0.95rem; margin-bottom: var(--space-2); }
/* Capped at the text measure: at full container width a 4:3 photo renders
   ~840px tall on desktop and swamps the paragraphs it sits between. */
.post-body img { border-radius: var(--radius-lg); margin-block: var(--space-2); max-width: var(--measure); }

/* ---------- Two-column split ---------- */

.split {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 48em) { .split { grid-template-columns: 1fr 1fr; } }
/* A fixed 3:2 window balances the photo column against the text column. The
   source photos range from 2.26:1 to 3:4 natively, which left white gaps beside
   short text on desktop and endless scroll on mobile. Cropped, never distorted. */
.split img {
  border-radius: var(--radius-lg);
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}
/* Per-photo focal-point overrides for subjects the centered crop would cut:
   crop-upper for faces in the upper third, crop-left for left-weighted wides. */
.split img.crop-upper { object-position: 50% 32%; }
.split img.crop-left { object-position: 28% 50%; }

/* ---------- Footer ---------- */

.site-footer {
  background: var(--color-footer-bg);
  color: var(--color-footer-text);
  padding-block: var(--space-4) var(--space-3);
  /* No margin-top by design: a gap here reads as a stray white band whenever
     the preceding section is dark. */
}
.site-footer a { color: var(--color-footer-text); }
.site-footer a:hover { color: var(--hfh-bright-green); }

.site-footer__grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
}
@media (min-width: 48em) { .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr; } }

.site-footer h2 {
  color: var(--color-footer-text);
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.site-footer address { font-style: normal; }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: 0.4rem; }
.site-footer li a { display: inline-block; padding-block: 0.3rem; }

.site-footer__legal {
  border-top: 1px solid rgba(255, 255, 255, 0.25);
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  font-size: 0.9rem;
}
.site-footer__legal p { max-width: none; }

.site-footer__equal-housing {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
/* White plate, same rationale as .site-logo: a third-party mark is never
   recolored, inverted, or dropped straight onto a dark fill. */
.site-footer__equal-housing img {
  background: var(--hfh-white);
  border-radius: var(--radius);
  padding: 4px 6px;
  height: 44px;
  width: auto;
}
.site-footer__equal-housing p { margin: 0; }

/* ---------- Utilities ---------- */

.center { text-align: center; }
.mt-lg { margin-top: var(--space-4); }
.center p { margin-inline: auto; max-width: 60ch; text-wrap: balance; }
.mt-0 { margin-top: 0; }
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Highlights unconfirmed copy in the rendered page, so a stakeholder reviewing
   the live site sees the gap without grepping. Pairs 1:1 with a TODO HTML
   comment (see CLAUDE.md); remove both together. Currently no markup uses it,
   which is the intended end state, not dead code. */
.todo-highlight {
  background: var(--color-todo-bg);
  color: var(--color-todo-text);
  padding: 0 0.15em;
  border-radius: 2px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.todo-highlight--small { font-size: 0.7em; }

/* Every animation on the site is a transition, so killing transitions wholesale
   is sufficient. Add animation:none here if that ever stops being true. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; }
}
