/* Easterseals Arkansas - Brand Tokens */

/* Brand typeface: Atkinson Hyperlegible Next (2025 Branding Playbook, digital
   mandate). Self-hosted variable woff2 (wght 200-800), SIL OFL, no CDN. */
@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url('/fonts/AtkinsonHyperlegibleNext-var.woff2') format('woff2') tech('variations'),
       url('/fonts/AtkinsonHyperlegibleNext-var.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

/* =============================================================================
   BRAND TOKEN AUTHORITY

   This :root block is the single source of truth for Easterseals brand tokens.
   Two other places declare overlapping tokens and both are subordinate:

     css/base.css      the shared iProv vanilla foundation (owned by Jen
                       McArdle, "shared, do not modify per site"). It has
                       drifted by carrying EAS brand hexes. We do not edit it;
                       scripts/check-tokens.mjs reports the divergence as a
                       warning so it stays visible and can be taken to Jen.
     CRITICAL_CSS      the inlined above-fold string in src/worker.js. It is a
                       DELIBERATE SUBSET and must stay small, because it ships
                       on every page. It may declare only the tokens its own
                       rules consume, and every value must match this block.
                       scripts/check-tokens.mjs enforces both halves.

   Load order is base.css < CRITICAL_CSS < site.css < page CSS, so this block
   wins every conflict.

   Values are measured, not transcribed: see .planning/80-NATIONAL-BRAND-MEASURED.md
   for the method and the per-swatch figures. Do not add a color or a tint that
   is not in that document. The 2025 playbook states, verbatim, "Color tints
   included here are approved for use. Do not create any additional tints not
   reflected here."

   Naming carries the usage rule so it travels with the token:
     --es-<tier>-<name>   a national color, tier is primary/secondary/tertiary/accent
     --es-<name>-<NN>     an approved tint, NN is the percentage
     --color-*, --eas-*   the pre-existing names, kept working as aliases

   CONTRAST ROLE GROUPS. The @palette-group markers below are machine readable;
   scripts/check-contrast.mjs parses them and proves each classification by
   measurement, so a mis-grouped token fails the build rather than shipping.

     role=text-on-light    measures at or above 4.5 on white, so it may set
                           color: on white and on our light grounds.
     role=needs-dark-ground measures BELOW 4.5 on white. These may not set
                           color: on a light ground at any size. On a dark
                           ground they are fine, and national approves several
                           (Navy on Sky 5.67, Yellow on Navy 5.06), so the
                           restriction is about the ground, not the color.

   A needs-dark-ground token used with color: must name its ground in a trailing
   comment on the same line, in the form   ground: --es-secondary-navy
   check-contrast.mjs reads that annotation, computes that exact pair, and fails
   if it is short. An unannotated one fails immediately. The example cannot be
   written here with real comment delimiters: CSS comments do not nest, so a
   second closing delimiter inside this block would end it early, turn the rest
   of it into a bogus selector and drop this whole :root rule. That failure is
   half masked, because css/base.css still supplies every --color-* but no
   --eas-*, so only the EAS-only colors go missing. scripts/check-tokens.mjs now
   fails on an unbalanced comment for exactly this reason.

   THE HOUSE RULE for orange text. Orange clears 4.5 on white (4.51) and
   nothing else: 4.20 on Yellow 10%, 4.06 on our light gray, 3.81 on Sky 30%,
   3.62 on Yellow 30%, 3.56 on Grey. National licenses those at 3.0 for "18x
   and above", which is wrong as printed, because WCAG's large-text line is
   24px regular or 18.66px bold. So orange text on any non-white ground stays
   at or above 1.1875rem (19px) AND font-weight 700, which is 14.25pt bold and
   clears the real threshold. See the CONTRAST notes in css/base.css around
   .text-sub, where this was first settled, and css/pages.css for the .prose
   link case. Do not drop below that while the text stays orange.
   ============================================================================= */
:root {
  /* ---- National palette: 13 colors, measured (doc 80, playbook p6-p10) ----
     Proportion guidance from p5: Orange 31.5% dominant, each Secondary 12.5%,
     each Tertiary 6.25%, each Accent 1.5%. */

  /* @palette-group name=text-safe role=text-on-light
     Measured on white: Midnight 15.60, Navy 10.12, Ruby 9.54, Purple 9.27,
     Blue 4.65, Orange 4.51. Blue and Orange pass with almost no margin; see
     the house rule above for orange, and css/programs.css for why every token
     on a Blue panel is pure white. */
  --es-primary-orange: #cf4b04;    /* PMS 1665 C */
  --es-secondary-navy: #004473;    /* PMS 541 C */
  --es-tertiary-purple: #653664;   /* PMS 519 C */
  --es-tertiary-ruby: #8b0641;     /* PMS 221 C */
  --es-tertiary-blue: #007cb1;     /* PMS 7640 C */
  --es-accent-midnight: #002448;   /* PMS 289 C */
  /* @palette-group-end name=text-safe */

  /* @palette-group name=fills role=needs-dark-ground
     Measured on white: Coral 3.03, Gold 2.28, Teal 2.15, Yellow 2.00,
     Lime 1.91, Sky 1.78, Grey 1.26. Not one reaches 4.5 and only Coral even
     ties the 3.0 large-text floor, so on a light ground these are fills and
     graphics, never text. */
  --es-secondary-yellow: #ffa300;  /* PMS 7549 C */
  --es-tertiary-lime: #99cc00;     /* PMS 376 C */
  --es-tertiary-teal: #58c58d;     /* PMS 346 C */
  --es-tertiary-sky: #82cde1;      /* PMS 630 C */
  --es-accent-coral: #f76365;      /* PMS 178 C */
  --es-accent-gold: #f7941e;       /* PMS 151 C */
  --es-accent-grey: #e7e4e4;       /* PMS 427 C */
  /* @palette-group-end name=fills */

  /* ---- The approved tints: six, and only six (doc 80, playbook p6) ----
     No other color has an approved tint, Navy included. The ivory from the
     previous guidelines was removed and a yellow tint replaces it, which is
     what retired the non-spec #ffe9c2. */

  /* @palette-group name=tints role=needs-dark-ground
     Measured on white: Yellow 50% 1.43, Yellow 30% 1.24, Sky 30% 1.18,
     Orange 10% 1.15, Yellow 10% 1.07, Sky 10% 1.06. Grounds, not text,
     on anything light. On navy they carry text well: Yellow 50% is 7.08. */
  --es-orange-10: #faede6;
  --es-yellow-50: #ffd180;
  --es-yellow-30: #ffe3b2;
  --es-yellow-10: #fff6e5;
  --es-sky-30: #daf0f6;
  --es-sky-10: #f3fafc;
  /* @palette-group-end name=tints */

  /* ---- Interface neutrals (not national colors, no playbook entry) ----
     These predate the palette and stay. The three text values were set by the
     2026-09-15 contrast audit; the CONTRAST comment in css/base.css records
     why --color-text-light moved off #6b7280. */
  --color-light: #F3F3F3;
  --color-white: #fff;
  --color-text: #1a2733;
  --color-text-body: #4b5563;
  --color-text-light: #5f6773;
  --color-border: #e2e6ec;

  /* ---- Pre-existing names, kept working ------------------------------------
     Every one of these is consumed by shipped stylesheets, so none is renamed
     and none changes value. Each points at its canonical token and repeats the
     hex as a var() fallback. The fallback is deliberate: a bare
     var(--es-missing) computes to the guaranteed-invalid value and takes the
     whole declaration down, where the fallback degrades to the right color.
     scripts/check-tokens.mjs fails if a fallback and its canonical token ever
     disagree, so the pair cannot drift. */
  --color-primary: var(--es-primary-orange, #cf4b04);
  --color-secondary: var(--es-secondary-navy, #004473);
  --color-accent: var(--es-secondary-yellow, #ffa300);
  --color-dark: var(--es-secondary-navy, #004473);
  --color-primary-dark: var(--es-secondary-navy, #004473); /* A2: hover/press, was non-spec #a63c03 */
  --color-footer-bg: var(--es-accent-midnight, #002448);   /* A2: was non-spec #003054 */
  --eas-orange: var(--es-primary-orange, #cf4b04);
  --eas-yellow: var(--es-secondary-yellow, #ffa300);
  --eas-navy: var(--es-secondary-navy, #004473);
  --eas-citrus: var(--es-accent-gold, #f7941e);
  --eas-green: var(--es-tertiary-lime, #99cc00);
  --eas-blue: var(--es-tertiary-blue, #007cb1);
  --color-gold: var(--es-yellow-50, #ffd180); /* Yellow 50%: newsletter band (A2) */

  /* KNOWN DIVERGENCE, one unit of blue. The measured Yellow 30% swatch is
     #ffe3b2; these two have shipped as #ffe3b3 across 20 call sites since the
     A2 brand pass. Aliasing them to --es-yellow-30 would change a computed
     color on 20 rules, and this spec is a zero-visual-change refactor, so they
     keep their literal. Contrast is unaffected: navy measures 8.14 and orange
     3.62 on both. check-tokens.mjs prints this divergence on every run so it
     cannot go quiet, and reconciling it is a separate change with a visual
     sign-off. */
  --eas-pale-yellow: #ffe3b3;
  --color-cream: #ffe3b3;

  /* ---- Layout and type ---- */
  --font-base: "Atkinson Hyperlegible Next", -apple-system, system-ui, sans-serif;
  --max-width: 1200px;
  --radius: 12px;
  --section-spacing: 80px;
}

/* =============================================
   Easterseals Arkansas - Site Shell
   Header, footer, and homepage section styles.
   Builds on css/base.css. Uses brand tokens only.
   ============================================= */

/* ---------- Utilities ---------- */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.section-head { margin-bottom: 48px; }
.section-head h2 { color: var(--color-secondary); }
h2, h3 { color: var(--color-secondary); }
/* Beacon #138: balance h2 line lengths so a headline never ends on one
   stranded word (the homepage "More Than a Service Provider" left
   "Provider" alone at 1920 and 1440). It keeps the line count, so no box
   grows. Chromium balances up to 6 lines and wraps longer h2s as before;
   browsers without support ignore it. h2 only: h1 and h3 are unchanged.
   It lives here and not on the base.css h2 rule because css/base.css is
   the shared iProv foundation and is not edited here. */
h2 { text-wrap: balance; }
/* Paragraph orphans (Jen, design owner, reviewing the CTA batch: "some of
   the paragraphs have orphans"): pretty wrapping keeps a block of text from
   ending on one stranded word. On the pages measured for this change,
   Chromium re-broke at most the last four lines of a block and kept every
   line count; browsers without support ignore it and wrap as before.
   text-wrap is inherited, so every text block takes it from body unless a
   rule sets its own value: the h2 balance rule above, the .people-hub card
   title and .people-intro-tagline in css/pages.css and the
   .housing-options-page rules in css/housing-options.css still win. It
   lives here and not on the base.css body rule because css/base.css is the
   shared iProv foundation and is not edited here. */
body { text-wrap: pretty; }
.text-sub { color: var(--color-primary); }
/* Shared interior-page eyebrow (FIX 2 page-intro / program-intro heroes). The
   homepage full-bleed hero no longer carries an eyebrow, but interior heroes
   still rely on this label style, so it lives here as a general utility. */
.hero-eyebrow {
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 2.5px;
  color: var(--color-primary);
  margin-bottom: 14px;
}
.link-arrow {
  display: inline-block;
  color: var(--color-primary);
  transition: color 0.2s;
}
.link-arrow:hover { color: var(--color-primary-dark); }

/* ---------- Header ---------- */
/* The sticky pin lives on the #site-header WRAPPER (a direct child of <body>),
   not on .site-header. A sticky element can only stick within its parent's box,
   and .site-header's wrapper shrink-wraps to header height, so pinning .site-header
   alone let it scroll away after ~140px. Sticking the wrapper (parent = tall body)
   keeps the header pinned sitewide; the program subnav's top offset then sits
   correctly beneath it. */
#site-header {
  position: sticky;
  top: 0;
  z-index: 1000;
}
.site-header {
  position: sticky;
  top: 0;
  z-index: 1000;
  background: var(--color-white);
  box-shadow: 0 1px 0 var(--color-border);
}
.topbar { background: var(--color-secondary); color: #fff; }
.topbar-inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 7px 24px;
  display: flex;
  justify-content: flex-end;
  gap: 24px;
  font-size: 1rem;
}
/* BEACON #355, filed at 500x963 against
   `#site-header > header > div:nth-of-type(1) > div`. Below the hamburger
   breakpoint the navy utility strip does not render, and its contents live in
   the hamburger panel instead. That is what easterseals.com does: there
   `.header__institutional` is `display: none` under 1100px, a SECOND copy of it
   renders inside the open `.offcanvas`, and its Search is a plain link to a
   /search page.

   This site keeps the live search WIDGET rather than a link, because
   `js/search-index.json` already indexes every URL in the sitemap client-side
   (measured 2026-10-23 on 9c2c2d6: `grep -c '<loc>' sitemap.xml` and the index
   length agree at 454, across all 10 type labels), so a results page would be a
   slower route to the same answer. ONE instance of #siteSearch is MOVED between
   the strip and the panel by `initHeaderFold()` in js/main.js, never copied:
   `initHeaderSearch()` binds by id, so a second copy would give all five of its
   ids two owners.

   This REPLACES a `@media (max-width: 560px)` block that set
   `.topbar-inner { flex-wrap: wrap }` and `#site-header .topbar { max-height:
   140px }`, and that block's own 23-line rationale went with it. Keeping the
   part of it that is still worth knowing: the strip's four items overflow the
   24px-padded content box on a phone, and `justify-content: flex-end` then
   pushes "Contact Us" off the LEFT edge (to -54px at 320 and -14px at 360) with
   no scrollbar to hint that it is there, which is why it wrapped rather than
   scrolled; the wrapped strip measured 71.19px tall at 375px against 39.59px
   from 400 up, which is what the 140px raise of the 52px condense clamp was
   for. Both rules are now unreachable, because the strip does not render at any
   width where either could fire. Do not restore them without first moving the
   bound below past 560px.

   900px, not the 500px the beacon was filed at. `.nav` and `.header-cta.desktop`
   go `display: none` and `.hamburger` goes `display: flex` at `max-width: 900px`
   (the Responsive block further down in this file), and js/main.js carries the
   same literal twice: initHeaderFold and initMobileMenu's close-on-widen
   listener. Measured through the worker, 900 is the last width with a hamburger
   and 901 the first without. Four places share that number and must stay in
   step.

   Reclaims sticky header height on a phone: #site-header measured 171.19px at
   375px and 139.59px at 500px before, 130.00px at both after, where it sits
   exactly on the `min-height: 130px` CLS reservation in src/worker.js. */
@media (max-width: 900px) {
  .topbar { display: none; }

  /* The relocated widget is a full-width block in the panel, not a dropdown. In
     the strip it is an inline-flex toggle with an absolutely positioned panel
     pinned to its right edge; inside a 24px-padded menu that hangs off the
     viewport. Static and auto-width instead, so it grows the menu, which
     already scrolls under a JS-computed cap (initMobileMenu). */
  .mobile-menu .topbar-search { display: block; width: 100%; }

  /* Match .mobile-link. `.topbar-link` (0,1,0) sets `color: #fff`, invisible on
     the panel's white ground, and `.topbar-link:hover` (0,2,0) sets the gold
     accent. `.mobile-menu .topbar-search-toggle` is (0,2,0) and outranks the
     first on specificity; the hover needs (0,3,0) because `.topbar-link:hover`
     is declared LATER in this file and a media query adds no specificity. Navy
     on white measures 10.12:1 (scripts/check-contrast.mjs). */
  .mobile-menu .topbar-search-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 14px 0;
    font-weight: 700;
    letter-spacing: 0.4px;
    color: var(--color-secondary);
    border-bottom: 1px solid var(--color-border);
  }
  .mobile-menu .topbar-search-toggle:hover { color: var(--color-secondary); }
  .mobile-menu .topbar-search-toggle svg { width: 17px; height: 17px; }
  .mobile-menu .topbar-search-panel {
    position: static;
    width: auto;
    padding: 10px 0 2px;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  /* One scroll container, not two. .mobile-menu is already overflow-y:auto
     under a computed cap, so a 60vh inner scroller strands the lower results
     behind a second gesture on a phone. */
  .mobile-menu .site-search-results.has-results {
    max-height: none;
    overflow: visible;
  }
}
.topbar-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: #fff;
  font-weight: 600;
  white-space: nowrap;
  transition: color 0.2s;
}
.topbar-link:hover { color: var(--color-accent); }
.topbar-link svg { width: 15px; height: 15px; fill: currentColor; }

/* Header top-bar search */
.topbar-search { position: relative; display: inline-flex; }
.topbar-search-toggle {
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  font-size: 1rem;
  padding: 0;
}
.topbar-search-panel {
  position: absolute;
  top: calc(100% + 9px);
  right: 0;
  width: min(420px, calc(100vw - 32px));
  background: var(--color-white);
  border-radius: var(--radius);
  box-shadow: 0 12px 40px rgba(0, 68, 115, 0.18);
  border: 1px solid var(--color-border);
  padding: 12px;
  z-index: 1200;
}
.site-search-input {
  width: 100%;
  padding: 11px 14px;
  font: inherit;
  font-size: 1rem;
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-light);
}
.site-search-input:focus {
  border-color: var(--color-primary);
  background: var(--color-white);
}
/* A11Y (2026-10-01): `outline: none` above left the search field with a
   border tint as its only focus affordance. The ring lands on the white
   .topbar-search-panel (12px of padding, so it is not clipped): 10.12:1. */
.site-search-input:focus-visible {
  outline: 3px solid var(--focus-ring-color);
  outline-offset: 2px;
}
.site-search-results { list-style: none; margin: 0; padding: 0; }
.site-search-results.has-results { margin-top: 8px; max-height: 60vh; overflow-y: auto; }
.site-search-results li { margin: 0; }
.site-search-results li a {
  display: block;
  padding: 9px 12px;
  border-radius: 8px;
  color: var(--color-text);
}
.site-search-results li a:hover,
.site-search-results li a:focus { background: var(--color-light); }
.site-search-type {
  display: inline-block;
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.5px;
  color: var(--color-primary);
  margin-bottom: 2px;
}
.site-search-title { display: block; font-weight: 700; font-size: 1rem; line-height: 1.3; }
.site-search-excerpt {
  display: block;
  font-size: 1rem;
  color: var(--color-text-light);
  line-height: 1.4;
  margin-top: 2px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.site-search-results mark { background: var(--color-accent); color: inherit; padding: 0 1px; border-radius: 2px; }
.site-search-empty { padding: 10px 12px; color: var(--color-text-light); font-size: 1rem; }

/* Full two-row header at the top; condenses to a thin sticky band on scroll
   (see #site-header.is-condensed below). Logo height + header padding transition. */
.header-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 14px 24px;
  transition: padding 0.28s ease;
}
.header-main .logo img { height: 72px; width: auto; max-width: none; transition: height 0.28s ease; }
.nav { display: flex; align-items: center; gap: 26px; }
.nav-parent, .nav-link {
  font-size: 1.125rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.3px;
  color: var(--color-secondary);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  transition: color 0.2s;
}
.nav-parent:hover, .nav-link:hover { color: var(--color-primary); }
.nav-arrow { font-size: 1rem; transition: transform 0.2s ease; }

/* An open menu has to show which nav item it belongs to.
   The only state used to be .nav-parent:hover, which is lost the instant the
   pointer leaves the item and travels down into the panel, so an open mega menu
   looked attached to nothing. Same flaw on the dropdowns: moving into one
   un-highlighted its own parent.

   Modelled on the WordPress dev site's plugin, where the open item keeps a solid
   background block for as long as the panel is up (measured: its link holds
   rgb(0,124,177) while the pointer is inside the panel). Here the block uses the
   mega panel's own rail tint, so the item reads as part of the panel below it.

   Painted with a pseudo-element rather than padding on the item: the nav has
   no room to spare at the narrow end of the desktop range (at 901px the items
   only fit because of the narrow desktop nav block below, #164), and padding
   four nav items would overflow it. ::before costs no layout at all. */
.nav-parent { position: relative; }
.nav-parent::before {
  content: '';
  position: absolute;
  inset: -9px -14px;
  border-radius: 9px;
  background: #f3fafc;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: -1;
}
.has-dropdown:hover > .nav-parent,
.has-dropdown:focus-within > .nav-parent,
.has-mega.is-open > .nav-mega-toggle,
.has-mega:not(.js):hover > .nav-mega-toggle { color: var(--color-primary); }
.has-dropdown:hover > .nav-parent::before,
.has-dropdown:focus-within > .nav-parent::before,
.has-mega.is-open > .nav-mega-toggle::before,
/* Without JS the panel opens on :hover and .is-open never applies, so the
   indicator needs the same fallback the panel itself has. */
.has-mega:not(.js):hover > .nav-mega-toggle::before { opacity: 1; }
/* Matches .mobile-group.open .mobile-arrow, which already does this. */
.has-dropdown:hover > .nav-parent .nav-arrow,
.has-dropdown:focus-within > .nav-parent .nav-arrow,
.has-mega.is-open > .nav-mega-toggle .nav-arrow,
.has-mega:not(.js):hover > .nav-mega-toggle .nav-arrow { transform: rotate(180deg); }

.has-dropdown { position: relative; }
.dropdown {
  display: none;
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  min-width: 240px;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 68, 115, 0.14);
  padding: 10px;
  z-index: 1100;
}
.has-dropdown:hover .dropdown,
.has-dropdown:focus-within .dropdown { display: block; }
.dropdown a {
  display: block;
  padding: 10px 14px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  border-radius: 8px;
  transition: background 0.15s, color 0.15s;
}
.dropdown a:hover { background: var(--color-light); color: var(--color-primary); }

.header-cta {
  background: var(--color-primary);
  color: #fff;
  padding: 12px 26px;
  border-radius: 50px;
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.5px;
  transition: background 0.2s;
  white-space: nowrap;
}
.header-cta:hover { background: var(--color-primary-dark); }

/* ---------- Condense-on-scroll ----------
   At the top the header shows both rows (navy utility strip + tall logo row).
   Once scrolled, main.js adds .is-condensed to #site-header: the strip collapses,
   the logo shrinks, and padding tightens into a thin sticky band. main.js also
   syncs --eas-header-h to the live header height so the program subnav docks
   flush beneath the condensed band. */
.topbar {
  overflow: hidden;
  max-height: 52px;
  transition: max-height 0.28s ease, opacity 0.2s ease;
}
/* CD-230: the search panel is position:absolute below the topbar, so the clip
   above cuts it off, and input.focus() in main.js scrolls the topbar's own
   content up by 60px to chase it. Lift the clip only while the panel is open.
   Scoped to the non-condensed header so the condense animation is untouched.
   main.js initHeaderSearch() toggles .is-search-open in open()/close(). */
#site-header:not(.is-condensed) .topbar.is-search-open { overflow: visible; }
#site-header.is-condensed { min-height: 0; box-shadow: 0 2px 14px rgba(0, 68, 115, 0.10); }
#site-header.is-condensed .topbar { max-height: 0; opacity: 0; }
#site-header.is-condensed .header-main { padding-top: 9px; padding-bottom: 9px; }
#site-header.is-condensed .header-main .logo img { height: 38px; }

/* CD-242 ("logo seems small in this space when you scroll"). The condensed row
   has three items: the logo image, the nav, and the Donate button. Measured at
   1437px through a running worker, the condensed .header-main is 67.59px tall
   with `padding: 9px 24px`, so its content box is 49.59px, and the Donate
   button is exactly 49.59px. The logo was the only item in the row not using
   the row it already pays for: 38px of 49.59px.

   48px, not 50px. The CTA is 49.59px, not 50px, so a 50px logo becomes the
   tallest flex item and the header grows to 68px. 48px leaves 1.59px of
   headroom against font-metric variation (the row height is a function of the
   brand font's line box) and the header height stays byte-identical at every
   desktop width. Measured 97.2x38 before, 122.81x48 after; the natural asset
   is 240x94.

   Scoped to 901px and up, and that bound is load-bearing. Below 901px
   `.header-cta.desktop` and `.nav` are display:none (the 900px block further
   down), so the LOGO is the tallest item in the condensed row and sets its
   height: the condensed header measures 56px there, which is 38 + 18. A 48px
   logo on a phone would push the sticky band to 66px, a 10px reflow on every
   page, for a row that has no nav or Donate button beside it to look small
   against. Mobile keeps 38px. */
@media (min-width: 901px) {
  #site-header.is-condensed .header-main .logo img { height: 48px; }
}

/* ---------- Narrow desktop nav (#164) ----------
   The desktop nav takes over from the mobile menu at 901px, but at full size
   (18px items, 26px gaps) the logo, the four items and Donate need a layout
   about 911px wide. Narrower than that, flex squeezed the nav and "About Us",
   "Get Support" and "Get Involved" broke onto two lines. A classic scrollbar
   (Chrome and Edge on Windows, 15px) narrows the layout but not the media
   query, so there the wrap ran up to a 925px window.
   From 901px to 959px the item size and the gap grow with the window, from
   16.5px and 18px at 901px to the full 18px and 26px at 960px, so nothing
   steps. Measured with the brand font loaded, that leaves at least 24px spare
   at every width with a 15px scrollbar. From 960px up this block does not
   apply and the header is exactly as before; 900px and below is the mobile
   menu and is untouched.
   `.nav .nav-parent`, not `.nav-parent`: css/base.css `.nav a` (0,1,1) sets
   the size of every nav link and outranks a single class. The arrow keeps its
   16px (.nav-arrow), as it does at full size. */
@media (min-width: 901px) and (max-width: 959.98px) {
  .nav { gap: clamp(18px, calc(18px + (100vw - 901px) * 8 / 59), 26px); }
  .nav .nav-parent { font-size: clamp(1.03125rem, calc(1.03125rem + (100vw - 901px) * 1.5 / 59), 1.125rem); }
}

/* ---------- Mobile menu ---------- */
.mobile-menu {
  display: none;
  background: var(--color-white);
  border-top: 1px solid var(--color-border);
  padding: 12px 24px 24px;
}
.mobile-menu.active {
  display: block;
  /* max-height is set in JS (initMobileMenu) from the live bar height, because
     the header condenses on scroll. Without a cap the menu overflows a sticky
     header and its lower half cannot be reached on a phone. */
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.mobile-link, .mobile-group-btn {
  display: block;
  width: 100%;
  text-align: left;
  padding: 14px 0;
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.4px;
  color: var(--color-secondary);
  border-bottom: 1px solid var(--color-border);
  background: none;
  border-left: none;
  border-right: none;
  border-top: none;
  cursor: pointer;
}
.mobile-group-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.mobile-arrow { font-size: 1rem; transition: transform 0.2s; }
.mobile-group.open .mobile-arrow { transform: rotate(180deg); }
.mobile-submenu { display: none; padding: 4px 0 10px 12px; }
.mobile-group.open .mobile-submenu { display: block; }
.mobile-submenu a {
  display: block;
  padding: 9px 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text-body);
}
.mobile-submenu a:hover { color: var(--color-primary); }
.mobile-donate {
  display: block;
  margin-top: 18px;
  text-align: center;
  background: var(--color-primary);
  color: #fff;
  padding: 14px;
  border-radius: 50px;
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.5px;
}
.mobile-donate:hover { background: var(--color-primary-dark); }

/* ---------- Hero ----------
   Full-bleed photo hero matching the WP homepage: the image covers the section,
   a dark navy-tinted overlay carries the centered white headline, subline, and
   CTAs. The <img> (not a CSS background) is preloaded with fetchpriority=high so
   it is the LCP element; the section reserves height so nothing shifts on load. */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  min-height: clamp(460px, 60vh, 620px);
  overflow: hidden;
  background: var(--color-secondary);
}
.hero-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}
.hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(0, 34, 58, 0.42) 0%, rgba(0, 34, 58, 0.58) 100%);
  pointer-events: none;
}
.hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  text-align: center;
}
.hero-copy { max-width: 780px; margin: 0 auto; }
.hero h1 {
  font-size: clamp(2rem, 4.5vw, 3.4rem);
  font-weight: 800;
  line-height: 1.12;
  color: #fff;
  margin-bottom: 16px;
}
.hero-subtitle {
  font-size: 1.625rem;
  color: rgba(255, 255, 255, 0.94);
  margin-bottom: 26px;
  line-height: 1.55;
}
.hero-ctas { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
/* Self-sufficient: .btn sets border:none, so declaring only a border-COLOUR
   left this with border-width 0 and no outline at all. Staff pages happened to
   work because they also carry .btn-outline (which supplies the 2px); program
   pages used .btn-outline-light alone and rendered as bare white text.
   Declaring the full border here fixes it wherever the class is used alone. */
.btn-outline-light {
  border: 2px solid #fff;
  color: #fff;
}
.btn-outline-light:hover {
  background: #fff;
  color: var(--color-secondary);
}

/* ---------- Circular media crops ---------- */
.circle-media { display: flex; justify-content: center; }
.circle-media img {
  width: 100%;
  max-width: 420px;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 50%;
  border: 8px solid var(--color-white);
  box-shadow: 0 16px 40px rgba(0, 68, 115, 0.14);
}

/* ---------- Value cards (non-interactive, no hover) ---------- */
.value-card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 36px 28px;
  text-align: center;
}
.value-icon {
  width: 88px;
  height: 88px;
  margin: 0 auto 18px;
  background: var(--eas-pale-yellow);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.value-icon img { width: 48px; height: auto; }
.value-card h3 { margin-bottom: 10px; }

/* ---------- Mission ----------
   Two WP beats: a ringed circle photo beside the mission copy, then a rounded
   navy statement panel carrying the mission statement in white over the arc
   texture (audit signature 5: large-radius navy statement panels). */
.mission-lead { align-items: center; }
.mission-statement-wrap { padding-top: 0; }
/* Reusable rounded navy statement panel (audit signature 5). The homepage
   mission band and the program-page statement panel share the shell; the
   program variant adds a white heading above the statement text. */
.mission-statement,
.statement-panel {
  background: var(--color-secondary);
  border-radius: 28px;
  padding: 60px 40px;
  text-align: center;
}
.mission-statement-inner,
.statement-panel-inner {
  position: relative;
  z-index: 1;
  max-width: 900px;
  margin: 0 auto;
}
.mission-statement p {
  color: #fff;
  font-size: clamp(1.15rem, 2.1vw, 1.55rem);
  font-weight: 700;
  line-height: 1.42;
  margin: 0;
}
/* A link inside the panel (the download line on /empowered-by-you/; the
   homepage and Our History panels carry none). It keeps the white text, so the
   underline is what marks it as a link; gold #ffa300 on navy is 5.06:1. The
   long share URL must wrap, or it overflows the panel on a phone. */
.mission-statement a {
  text-decoration: underline;
  text-underline-offset: 0.15em;
  overflow-wrap: anywhere;
}
.mission-statement a:hover { color: var(--color-accent); /* ground: --es-secondary-navy */ }
.mission-statement a:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* Light yellow variant (the keep-a-copy box on /empowered-by-you/):
   --color-cream ground, navy text (8.14:1) and navy links. Gold hover would
   fail on cream, so a link marks hover with a thicker underline. Same
   specificity as the navy rules above and later in the file, so it wins. */
.mission-statement--cream { background: var(--color-cream); }
.mission-statement--cream p { color: var(--color-secondary); }
.mission-statement--cream a:hover { color: var(--color-secondary); text-decoration-thickness: 3px; }
.mission-statement--cream a:focus-visible { outline-color: var(--color-secondary); }
/* A button inside the panel (the PDF download on /empowered-by-you/,
   eas-easterseals#341) is not a text link: no underline, and white text in
   every state on the orange fill and its navy hover. The cream and navy hover
   rules above would otherwise turn its label navy on the navy hover fill. */
.mission-statement a.btn { text-decoration: none; }
.mission-statement a.btn,
.mission-statement a.btn:hover { color: #fff; }
/* Body-copy variant (the roadmap box on /about-us/our-future/): a heading and
   regular paragraphs in the statement shell. Used WITH .mission-statement--cream,
   which supplies the --color-cream ground and the navy paragraph colour
   (8.14:1), so no colour is restated for the paragraphs. Type is .prose's:
   1.125rem, weight 400, line-height 1.8, 18px under each paragraph, and the
   heading is .prose h2's size and navy. The 820px measure is .prose's (the
   shell's 900px is for one large line). Declared after .mission-statement p
   and .mission-statement--cream p at equal specificity, so it wins on source
   order. Alignment stays the shell's centre. */
.mission-statement--prose .mission-statement-inner { max-width: 820px; }
.mission-statement--prose h2 {
  color: var(--color-secondary);
  font-size: clamp(1.5rem, 3vw, 2rem);
  line-height: 1.2;
  margin: 0 0 16px;
}
.mission-statement--prose p {
  font-size: 1.125rem;
  font-weight: 400;
  line-height: 1.8;
  margin: 0 0 18px;
}
.mission-statement--prose p:last-child { margin-bottom: 0; }
/* A button inside the statement box (the Join Our Advocates box on /advocacy/).
   `.mission-statement a` underlines every link in the box, and
   `.mission-statement--cream a:hover` (0,2,1) turns the text navy, which on a
   .btn-primary hover (navy ground) would be navy on navy. A button is neither:
   no underline, and white text on hover, as `.btn-primary` has everywhere else.
   (0,2,0) beats the underline at (0,1,1); (0,3,0) beats the navy hover. */
.mission-statement .btn { text-decoration: none; }
.mission-statement--cream .btn:hover { color: var(--color-white); }
.statement-panel h2 {
  color: #fff;
  font-size: clamp(1.4rem, 2.6vw, 2rem);
  line-height: 1.2;
  margin: 0 0 16px;
}
.statement-panel p {
  color: rgba(255, 255, 255, 0.92);
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  line-height: 1.6;
  margin: 0;
}

/* ---------- What We Offer ---------- */
.offer { position: relative; overflow: hidden; }
.offer-block { margin-top: 56px; position: relative; }
.offer-block:first-of-type { margin-top: 40px; }
/* Real content and the circle images ride within the block. */
.offer-block > .eas-img,
.offer-block > div { position: relative; z-index: 1; }
.offer-title { font-size: 1.6rem; margin-bottom: 14px; }
.program-list {
  list-style: none;
  columns: 2;
  column-gap: 28px;
  margin: 0 0 20px;
}
/* CD-301 ("I didn't realize I could click any of these, I just thought they
   were bolded"). Measured through a running worker at 1437px: all 18 links in
   the two .program-list columns compute to navy rgb(0,68,115), weight 600,
   16px and text-decoration-line: none, which is exactly how the bold runs in
   the copy beside them render. The 2025 Branding Playbook's type rules say do
   not underline EXCEPT for links, so the underline is the on-brand answer
   here, not a workaround. Navy on the --color-light band measures 9.12:1.

   The rule is drawn under the TEXT only, not under the amber ::before dot or
   the 8px gap, even though the anchor is the flex container and the dot is one
   of its items. That is not a Chrome quirk being leaned on: verified by
   screenshot in Chromium, Firefox and WebKit at 1437px, all three identical.
   It is also why the dot stays where it is rather than moving up to the li.
   Moving it was the first attempt: it works, but a flex li has no inline
   strut, so every row loses the 3.2px the li's inherited 18px line-height was
   contributing and the two columns shorten by 25px for nothing. Measured
   28.8px per row before, 25.59px after, and the whole .offer section 1583.39px
   against 1558.17px. Adding three declarations to the existing anchor changes
   no box on the page.

   The hover COLOUR is pre-existing and deliberately not touched: orange
   #cf4b04 at 16px/600 on the light-gray band measures 4.06:1, under AA's 4.5
   for normal text. That is a standing defect, it predates this change, and
   fixing it means choosing a new link-hover colour, which is a design call.
   The 2px rule is what is added, so the hover still reports itself even where
   the colour shift does not carry it. */
.program-list li { margin-bottom: 10px; break-inside: avoid; }
.program-list a {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-secondary);
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color 0.2s, text-decoration-thickness 0.2s;
}
.program-list a::before {
  content: '';
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-accent);
  transform: translateY(-2px);
}
.program-list a:hover { color: var(--color-primary); text-decoration-thickness: 2px; }
.program-list a:focus-visible {
  outline: 3px solid var(--focus-ring-color);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ---------- Blog cards ---------- */
.blog .blog-card-body h3 { color: var(--color-secondary); }

/* ---------- Donate banner ----------
   WP recolors this band from orange to navy (sampled #004473 = --color-secondary)
   with white heading/copy and the primary orange "Give Today" button (the donate
   button pixel-samples #cf4b04 on production, not amber). Recolored here so it no
   longer stacks loud against the adjacent gold newsletter band. */
.donate-banner {
  padding: 64px 0;
  background: var(--color-secondary);
  color: #fff;
  text-align: center;
}
.donate-banner h2 { color: #fff; margin-bottom: 12px; }
.donate-banner p {
  font-size: 1.25rem;
  max-width: 620px;
  margin: 0 auto 26px;
  opacity: 0.95;
  line-height: 1.6;
}

/* ---------- Involvement band (CD-230, Casey direct) ----------
   Casey: "I don't like how it is a CTA band for the open positions, then a CTA
   band for donate now, and then another band for the newsletter." Three stacked
   centre-aligned bands read as one long run of the same shape. This one goes
   two-column and left-aligned so the rhythm breaks, and offers three real
   destinations instead of a single dead-end button. Every path here is an
   existing page; none were invented for the layout. */
.involve-band { text-align: left; }
.involve-inner {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 56px;
  align-items: center;
}
.involve-lead h2 { margin-bottom: 14px; }
.involve-lead .text-sub { color: var(--color-accent); margin-bottom: 8px; }
.involve-band .involve-lead p {
  font-size: 1.0625rem;
  max-width: 46ch;
  margin: 0 0 26px;
}
.involve-paths { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.involve-paths a {
  display: block;
  padding: 16px 20px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--radius);
  color: #fff;
  text-decoration: none;
  transition: background 0.18s, border-color 0.18s;
}
.involve-paths a:hover,
.involve-paths a:focus-visible {
  background: rgba(255, 255, 255, 0.09);
  border-color: var(--color-accent);
}
.involve-paths a:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.involve-path-name {
  display: block;
  font-weight: 700;
  font-size: 1.0625rem;
  margin-bottom: 2px;
}
.involve-path-name::after { content: " \2192"; color: var(--color-accent); }
.involve-path-desc { display: block; font-size: 0.9375rem; opacity: 0.88; }
/* The "In this issue" topics line on a volume card (/empowered-by-you/,
   eas-easterseals#342): the same size and softness as the description, set off
   by a 1px white rule at 40% with 10px above and below it. */
.involve-path-topics {
  display: block;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid rgba(255, 255, 255, 0.4);
  font-size: 0.9375rem;
  opacity: 0.88;
}

@media (max-width: 900px) {
  .involve-inner { grid-template-columns: 1fr; gap: 32px; }
  .involve-band .involve-lead p { max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
  .involve-paths a { transition: none; }
}

/* ---------- Primary button hover on navy containers (CD-230) ----------
   .btn-primary:hover in base.css sets background: var(--color-primary-dark),
   which is Navy #004473 by the A2 ruling and is the SAME value as
   --color-secondary. On a navy container the hover fill therefore matches the
   band behind it exactly (1.00:1) and the button disappears. The token stays
   navy on purpose: 17 text link-hover rules depend on its documented AA
   behaviour. So the fix is scoped to the three navy containers instead.
   Gold #ffa300 on navy #004473 is 5.06:1, and the navy label on gold is 5.06:1
   (AA). The color declaration is load-bearing, not cosmetic: the inherited
   white label would be 2.0:1 on gold and would fail AA badly.
   .hero (site.css:348) and .donate-banner (site.css:542) carry the navy
   directly; .program-cta puts it on the child, css/programs.css:505
   .program-cta > .container. All three live here rather than beside their
   containers because index.html does not load programs.css, so .hero and
   .donate-banner could not move there, and one rule keeps the three from
   drifting apart. */
.hero .btn-primary:hover,
.donate-banner .btn-primary:hover,
.program-cta .btn-primary:hover,
.event-feature .btn-primary:hover,
.footer .btn-primary:hover {
  background: var(--color-accent);
  color: #004473;
}

/* ---------- Involvement band on cream (/about-us/our-future/) ----------
   The same .donate-banner.involve-band markup as the homepage, on
   --color-cream (#ffe3b3) instead of navy. This one modifier re-points every
   colour the navy ground decided; layout, type sizes, spacing and the grid
   are NOT restated, they come from .donate-banner and .involve-band above.
   Declared after both so it wins on source order at equal specificity.
   Measured on #ffe3b3: navy #004473 8.14:1 (heading, copy, arrow, focus ring),
   navy at 0.88 opacity 6.24:1 (path description), white on the orange button
   4.51:1. Gold #ffa300 is 1.61:1 on cream, so it appears nowhere here: the
   arrow and the focus ring go navy, and the button hover returns to
   base.css's navy fill instead of the gold one the navy band uses. The
   eyebrow also goes navy rather than base.css's orange: .involve-band
   .involve-lead p sets it to 17px, under the 18.66px bold large-text
   threshold, so orange (3.62:1) would need 4.5 and fail.
   The navy band carries no texture or overlay (it is not .has-texture), so
   there is none to handle. */
.involve-band--cream {
  background: var(--color-cream);
  color: var(--color-secondary);
}
.involve-band--cream h2 { color: var(--color-secondary); }
.involve-band--cream p { opacity: 1; }
.involve-band--cream .involve-lead .text-sub { color: var(--color-secondary); }
.involve-band--cream .btn-primary:hover {
  background: var(--color-primary-dark);
  color: #fff;
}
.involve-band--cream .involve-paths a {
  border-color: rgba(0, 68, 115, 0.6);
  color: var(--color-secondary);
}
.involve-band--cream .involve-paths a:hover,
.involve-band--cream .involve-paths a:focus-visible {
  background: rgba(0, 68, 115, 0.07);
  border-color: var(--color-secondary);
}
.involve-band--cream .involve-paths a:focus-visible { outline-color: var(--color-secondary); }
.involve-band--cream .involve-path-name::after { color: var(--color-secondary); }

/* ---------- Footer ---------- */
.footer { background: var(--color-footer-bg); color: #fff; padding: 60px 0 28px; }
.footer-top {
  display: grid;
  grid-template-columns: 1.1fr 1.6fr;
  gap: 48px;
  padding-bottom: 40px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}
.footer-newsletter h3 { color: #fff; margin-bottom: 12px; }
.footer-newsletter p {
  font-size: 1rem;
  color: rgba(255, 255, 255, 0.75);
  line-height: 1.6;
  margin-bottom: 18px;
  max-width: 360px;
}
.newsletter-form { display: flex; gap: 10px; max-width: 400px; flex-wrap: wrap; }
.newsletter-input {
  flex: 1;
  min-width: 180px;
  padding: 12px 16px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(255, 255, 255, 0.08);
  border-radius: 50px;
  color: #fff;
  font-size: 1rem;
  font-family: var(--font-base);
}
.newsletter-input::placeholder { color: rgba(255, 255, 255, 0.55); }
.newsletter-input:focus { border-color: var(--color-accent); }
/* A11Y (2026-10-01): `outline: none` above suppressed the ring on the one
   form that appears on EVERY page. The field and the Subscribe button both
   sit in the gold band (#ffd180), where navy measures 7.08:1. */
.newsletter-input:focus-visible,
.newsletter-btn:focus-visible {
  outline: 3px solid var(--focus-ring-color);
  outline-offset: 2px;
}
.newsletter-btn {
  background: var(--color-primary);
  color: #fff;
  border: none;
  padding: 12px 26px;
  border-radius: 50px;
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.5px;
  cursor: pointer;
  transition: background 0.2s;
}
.newsletter-btn:hover { background: var(--color-primary-dark); }

.footer-menus {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 32px;
}
.footer-col h3 {
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 1px;
  color: #fff;
  margin-bottom: 14px;
}
.footer-col ul { list-style: none; }
.footer-col li { margin-bottom: 9px; }
.footer-col a {
  font-size: 1rem;
  color: rgba(255, 255, 255, 0.75);
  transition: color 0.2s;
}
.footer-col a:hover { color: #fff; }
.footer-contact p {
  font-size: 1rem;
  color: rgba(255, 255, 255, 0.75);
  line-height: 1.6;
  margin-bottom: 10px;
}
.footer-contact a { color: rgba(255, 255, 255, 0.75); }
.footer-contact a:hover { color: #fff; }
.footer-social { display: flex; gap: 10px; margin-top: 14px; }
.footer-social a {
  width: 40px;
  height: 40px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s;
}
.footer-social a:hover { background: var(--color-primary); }
.footer-social svg { width: 20px; height: 20px; fill: #fff; }

/* iprov-agency/eas-easterseals#309: equal space above and below the logo. The
   image is display:block so the inline baseline gap no longer adds to the
   bottom, the padding is symmetric, and auto margins centre it. */
.footer-brand-row { padding: 24px 0; }
.footer-logo { display: block; height: 56px; width: auto; margin: 0 auto; }
.footer-bottom { padding-top: 6px; }
.footer-bottom p { font-size: 1rem; color: rgba(255, 255, 255, 0.6); line-height: 1.6; }
.footer-credit { margin-top: 6px; font-style: italic; color: rgba(255, 255, 255, 0.5); }
.footer-credit a { color: rgba(255, 255, 255, 0.75); text-decoration: underline; }
.footer-credit a:hover { color: var(--color-accent); }

/* CD-303 ("There's a lot of empty space in this column, and no Donate button
   in the footer. Could we balance it more by added one that stand out?").
   Measured at 1437px: the three footer columns are each 362.66 x 379.77, the
   row height set by "Site Links" and its 9 links, so "Get Involved" and its 4
   carried about 250px of dead space, and no Donate control existed anywhere
   below the fold. The button goes in the first column, under its four links.

   The href is /donate/, matching components/header.html's own Donate button
   exactly. It is NOT /ways-to-give/: that is the giving-methods hub, the first
   column already links it as "Ways to Give" one line above, and /donate/ is
   the page carrying the secure GiveSmart form. Both the header and this button
   therefore read "Donate" and land on the same page.

   Measured on the footer ground, which is --color-footer-bg #002448 (midnight,
   PMS 289), NOT the #004473 navy of the band above it:
     white label on the orange fill   4.51:1  (AA normal; 17px/700 is under the
                                               18.66px bold large-text line, so
                                               4.5 is the bar it has to clear)
     orange fill against #002448      3.46:1  (SC 1.4.11 non-text, needs 3.0)
     gold hover fill against #002448  7.79:1
     navy label on the gold hover     5.06:1  (AA normal)
     white focus ring on #002448     15.60:1
   The gold hover is why `.footer` joined the three navy containers in the
   .btn-primary:hover rule above: base.css hovers to --color-primary-dark,
   which resolves to navy #004473, and navy against #002448 is 1.54:1, so the
   default hover would make the button all but vanish into the footer.

   Both overrides below are cascade work, not styling: `.footer-col a` (0,1,1)
   sets 16px and rgba(255,255,255,0.75) on every anchor in the column and
   outranks `.btn-primary` (0,1,0), so without the (0,2,0) selector the label
   renders translucent at 16px and the 4.51:1 measurement above does not hold.
   The ring is spelled out rather than taken from --focus-ring-color because
   that token is navy, 1.54:1 here. */
.footer-col .footer-donate {
  color: var(--color-white);
  font-size: 1.0625rem;
  margin-top: 20px;
}
.footer-col .footer-donate:focus-visible {
  outline: 3px solid var(--color-white);
  outline-offset: 3px;
}

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  .nav, .header-cta.desktop { display: none; }
  .hamburger { display: flex; }
  .footer-top { grid-template-columns: 1fr; gap: 32px; }
}

@media (max-width: 768px) {
  .offer-block,
  .offer-block-reverse { grid-template-columns: 1fr; gap: 28px; }
  .offer-block-reverse .eas-img { order: -1; }
  .program-list { columns: 1; }
  .footer-menus { grid-template-columns: 1fr 1fr; }
  .mission-statement,
  .statement-panel { padding: 44px 26px; }
}

@media (max-width: 520px) {
  .footer-menus { grid-template-columns: 1fr; }
  .hero { min-height: 420px; }
}

/* =============================================
   Get Support mega menu (desktop)
   Tabbed full-width panel below the header. The
   toggle is a button; hover, click, and keyboard
   all open it. Lives inside .nav, so it inherits
   the < 900px hide with the rest of the desktop nav.
   ============================================= */
.nav-mega-toggle {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  /* Match the <a> nav items exactly: font:inherit above resets weight to 400 and
     .nav-parent supplies a navy color, so the button read as a different font.
     Re-assert the .nav a treatment (18px is already inherited). */
  font-weight: 600;
  color: var(--color-text);
  letter-spacing: 0.3px;
}
.has-mega { position: static; }
.mega-panel {
  display: block;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 1100;
  background: var(--color-white);
  border-top: 1px solid var(--color-border);
  box-shadow: 0 18px 40px rgba(0, 68, 115, 0.14);
  visibility: hidden;
  opacity: 0;
  transform: translateY(6px);
  /* Delay only the hide so the fade-out is visible; show flips immediately so
     the panel is focusable the moment it opens (keyboard path). */
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0.18s;
}
/* Invisible hover bridge across the ~42px gap between the toggle and the panel.
   It is part of .mega-panel (a child of .has-mega), so hovering it keeps
   .has-mega:hover true while the pointer travels from the button down into the
   panel, instead of crossing a non-hovered dead zone that closes the menu.
   Inherits the panel's visibility, so it only catches hover while the panel is
   open (opened by hovering the button). */
.mega-panel::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -46px;
  height: 46px;
}
/* With JS on, initMegaMenu drives .is-open through a hover-intent delay, so a
   bare :hover must NOT open the panel (it opened instantly on every pass of the
   pointer). The :hover and :focus-within paths stay as the no-JS fallback. */
/* Closing because the pointer moved to another nav item: drop the fade. The
   sibling dropdown opens instantly, so dissolving over 180ms leaves both menus
   on screen together, which is the thing this is meant to stop. */
.has-mega.is-closing-fast > .mega-panel { transition: none; }

.has-mega:not(.js):hover > .mega-panel,
.has-mega:not(.js):focus-within > .mega-panel,
.has-mega.is-open > .mega-panel {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s;
}
.mega-inner {
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr) 300px;
  max-width: 1320px;
  margin: 0 auto;
  /* Constant height across tabs, sized to the tallest (Programs). Without this
     the box was content-driven: 273px / 362px / 625px, and the "View All" link
     moved 382px and the CTA button 176px as the pointer crossed the rail. The
     WordPress dev site's Max Mega Menu pins its panel the same way
     (min-height: 782px on every tab). */
  /* Never taller than the window below the header (issue #105). On a short
     desktop window (1280x600) the 624px pin pushed the bottom of the panel off
     screen with no way to reach it. --eas-header-h is the live header height
     main.js keeps in sync (initHeaderCondense); the 1px is .mega-panel's
     border-top, so the panel's bottom edge lands on the window's. min-height
     must come down too, or it would beat max-height. On a tall window the cap
     sits above both 624px and the tallest tab, so nothing scrolls there.
     The scroll lives HERE, not on .mega-panel: overflow on the panel would clip
     .mega-panel::before, the hover bridge above it, and reopen the dead zone
     between the toggle and the panel. Functional fix; the look of the
     scrolling panel is pending Jen's visual check, then Casey's. */
  --mega-avail-h: calc(100vh - var(--eas-header-h, 96px) - 1px);
  min-height: min(624px, var(--mega-avail-h));
  max-height: var(--mega-avail-h);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.mega-rail { background: #f3fafc; padding: 30px 24px; }
.mega-eyebrow {
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: 1.5px;
  text-transform: none;
  color: var(--color-secondary);
  margin-bottom: 18px;
}
.mega-tabs { display: flex; flex-direction: column; gap: 6px; }
.mega-tab {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  border: none;
  background: none;
  border-radius: 10px;
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-secondary);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, box-shadow 0.15s;
}
.mega-tab:hover { background: rgba(255, 255, 255, 0.6); }
.mega-tab.is-active {
  background: var(--color-white);
  box-shadow: 0 4px 14px rgba(0, 68, 115, 0.12);
}
.mega-tab-icon { width: 20px; height: 20px; flex-shrink: 0; }
.mega-tab-icon circle { fill: var(--color-accent); }
.mega-tab-icon path { stroke: var(--color-accent); fill: none; }

.mega-content { padding: 30px 34px; display: flex; flex-direction: column; }
/* Fill the pinned row so the divider + "View All" can be pushed to a constant
   position instead of floating up under short tabs (it moved 382px between
   Who We Serve and Programs). The dev site's plugin holds it steady the same
   way: measured 0px movement across its tabs.
   [hidden] must still beat this display:flex, or a closed tab panel would show. */
.mega-tabpanel { flex: 1; display: flex; flex-direction: column; }
.mega-tabpanel[hidden] { display: none; }
/* Park the divider + "View All" at the foot of the pinned row so they sit at the
   same y on every tab (they moved 382px before). Done with margin-top:auto
   rather than a reserved min-height on the link region: both render identically,
   but a min-height would hardcode the tallest tab's height and silently drift
   the moment a program is added or removed. */
.mega-tabpanel .mega-divider { margin-top: auto; }
.mega-heading {
  font-size: 1rem;
  font-weight: 800;
  text-transform: none;
  letter-spacing: 0.5px;
  color: var(--color-secondary);
  margin-bottom: 16px;
}
.mega-group-label { margin: 0 0 10px; }
/* base.css sets `.nav a { font-size: 1.125rem; text-transform: none }` and the
   mega panel sits inside .nav, so these must be set on the anchor itself.
   Inheriting them from the <p> does not survive that rule. base.css is the
   shared do-not-modify-per-site file, so the override lives here. */
.mega-group-label a {
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--color-primary);
}
.mega-group-label a:hover { text-decoration: underline; }
.mega-groups { display: grid; grid-template-columns: 1fr 1fr; column-gap: 28px; }
.mega-groups .mega-links-single { grid-template-columns: 1fr; }
.mega-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 28px;
}
.mega-links li { margin: 0; }
.mega-links a {
  display: block;
  padding: 7px 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  transition: color 0.15s;
}
.mega-links a:hover { color: var(--color-primary); }

/* Who We Serve cards. Selectors are written as `.mega-links a.mega-card` (0,2,1)
   because `.mega-links a` (0,1,1) already sets display/padding/size and would
   otherwise win on source order. Type is set on the inner spans, not inherited:
   base.css `.nav a { font-size: 1.125rem }` applies to anything in the nav. */
.mega-links.mega-cards { row-gap: 10px; }
.mega-links a.mega-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px;
  border-radius: 12px;
  transition: background 0.15s;
}
.mega-links a.mega-card:hover { background: #f3fafc; }
.mega-card-media { flex: 0 0 auto; line-height: 0; }
.mega-card-media img {
  width: 112px;
  height: 112px;
  border-radius: 50%;
  object-fit: cover;
  border: 4px solid var(--color-white);
  box-shadow: 0 6px 20px rgba(0, 68, 115, 0.12);
}
.mega-card-body { display: block; min-width: 0; }
.mega-card-title {
  display: block;
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-secondary);
  margin-bottom: 4px;
}
.mega-card-desc {
  display: block;
  font-size: 0.86rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--color-text-body);
}

/* Areas rows: EAS's own one-line subheading under each link. No photo, per the
   same split the homepage band uses (9 photo tiles measured far taller). */
.mega-links.mega-links-desc { row-gap: 6px; }
.mega-links.mega-links-desc a { padding: 6px 0; }
.mega-link-desc {
  display: block;
  font-size: 0.86rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--color-text-body);
  margin-top: 2px;
}
.mega-divider { height: 1px; background: var(--color-border); margin: 18px 0 14px; }
.mega-viewall {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.5px;
  color: var(--color-primary);
  transition: color 0.15s, gap 0.15s;
}
.mega-viewall:hover { color: var(--color-primary-dark); gap: 12px; }
.mega-viewall-arrow { font-size: 1rem; }

.mega-cta-card {
  background: var(--color-light);
  margin: 24px 24px 24px 0;
  border-radius: var(--radius);
  padding: 32px 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.mega-cta-card { align-self: start; }
.mega-cta-title { font-size: 1.125rem; font-weight: 800; color: var(--color-primary); margin-bottom: 10px; }
.mega-cta-copy { font-size: 1rem; color: var(--color-text-body); line-height: 1.55; margin-bottom: 20px; }
/* Scoped to .mega-cta-card so these win over the generic `.nav a` /
   `.nav a:hover` header link rules (which otherwise force navy text at rest
   and primary-on-primary text on hover, hiding the label). */
.mega-cta-card .mega-cta-btn {
  display: inline-block;
  padding: 11px 26px;
  border: 2px solid var(--color-primary);
  border-radius: 50px;
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.5px;
  color: var(--color-primary);
  transition: background 0.18s, color 0.18s;
}
.mega-cta-card .mega-cta-btn:hover { background: var(--color-primary); color: #fff; }

/* Get Support nested accordions inside the mobile menu */
.mobile-subgroup { border-top: 1px solid var(--color-border); }
.mobile-subgroup-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  text-align: left;
  padding: 12px 0;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.2px;
  color: var(--color-secondary);
  background: none;
  border: none;
  cursor: pointer;
}
.mobile-subarrow { font-size: 1rem; transition: transform 0.2s; }
.mobile-subgroup.open .mobile-subarrow { transform: rotate(180deg); }
.mobile-subsubmenu { display: none; padding: 2px 0 10px 12px; }
.mobile-subgroup.open .mobile-subsubmenu { display: block; }
.mobile-subsubmenu .mobile-grouplabel {
  padding: 12px 0 4px;
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--color-primary);
}
.mobile-subsubmenu .mobile-grouplabel:first-child { padding-top: 4px; }
.mobile-submenu .mobile-viewall { font-weight: 700; color: var(--color-primary); }
.mobile-submenu .mobile-mega-cta {
  display: block;
  margin: 14px 0 6px;
  padding: 12px 14px;
  background: var(--color-light);
  border-radius: 10px;
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-primary);
  text-align: center;
}

@media (max-width: 1040px) {
  .mega-inner { grid-template-columns: 240px minmax(0, 1fr) 260px; }
  .mega-content { padding: 26px 24px; }
}

/* Any content sitting in a textured band rides above the decoration. */
.has-texture { position: relative; overflow: hidden; }
.has-texture > .container { position: relative; z-index: 1; }

/* ---------- Circle photo brand treatment ----------
   Thick brand-color ring + soft offset accent circle behind, opt-in via
   .circle-media--ring plus a color modifier. Applies to the existing
   .circle-media markup without changing base circle crops. */
.circle-media--ring {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin-inline: auto;
}
.circle-media--ring img {
  position: relative;
  z-index: 1;
  border: 10px solid var(--ring-color, var(--eas-blue));
  box-shadow: 0 16px 40px rgba(0, 68, 115, 0.14);
}
.circle-media--ring::before {
  content: '';
  position: absolute;
  z-index: 0;
  width: 46%;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  /* Offset accent disc: a soft brand-color circle behind the ringed photo.
     Decorative, sits behind the photo, adds no layout. */
  background-color: var(--accent-color, var(--color-cream));
  background-image: none;
  background-size: cover;
  background-position: center;
  left: -3%;
  bottom: -3%;
}
.circle-media--blue { --ring-color: var(--eas-blue); }
.circle-media--navy { --ring-color: var(--color-secondary); }
.circle-media--orange { --ring-color: var(--color-primary); --accent-color: var(--eas-pale-yellow); }
/* Art-restraint (52-SLICE): the museum rule demotes the homepage's SECONDARY
   ringed discs (mission, children's services, adult services) back to a flat
   cream offset. Only the who-we-are disc and each interior page's single ringed
   photo keep the day's accent artwork, honoring the one-accent-per-room budget.
   Higher specificity than the base ::before so it wins regardless of order. */
.circle-media--ring.circle-media--flat::before { background-image: none; }

/* ==========================================================================
   WordPress style packages (ported from altitude-pro/style.css)
   Faithful port of the Gutenberg block styles the WP site exposes through the
   editor: image circle treatments + container background decorations.
   Spec + per-page usage: .planning/STYLE-PACKAGE-INVENTORY.md
   Pale-yellow uses the brand-compliant --eas-pale-yellow (#ffe3b3), a
   deliberate 1-shade change from WP's ivory #ffe9c2 (brand A2 compliance).
   ========================================================================== */

/* ---- Image styles: circle packages (feature circles) ----
   Ported from WP's altitude-pro rules, then reduced to ONE treatment
   (iprov-agency/eas-easterseals#300, Casey 2026-10-29): every large feature
   circle is an 8px Blue (--eas-blue, #007cb1) ring with no offset disc,
   whatever colour its class name says. The orange and green names stay as
   aliases so no page markup and no generator had to change. What each name
   still contributes:
     - blue / green -left and the base X-circle = square shell + ringed circle
     - orange -left = ringed circle only;  orange -right = square shell only
       (WP's children's image carries BOTH orange classes, so it gets both)
   WP's offset discs (190px pale yellow or grey, placed by grid column or by
   variant) are deleted, not hidden: no ::after is set on any of these names.
   Markup: <figure class="eas-img is-style-blue-circle-left"><img ...></figure> */
.eas-img { position: relative; margin: 0; }

/* Square shell (WP: base, blue/green -left, orange -right carry padding-top) */
.eas-img.is-style-blue-circle,
.eas-img.is-style-blue-circle-left,
.eas-img.is-style-orange-circle,
.eas-img.is-style-orange-circle-right,
.eas-img.is-style-green-circle,
.eas-img.is-style-green-circle-left {
  display: block;
  padding-top: 100%;
}
/* Bordered circular image (WP: base + each color's -left). One ring for all
   six names, see the header above. */
.eas-img.is-style-blue-circle img,
.eas-img.is-style-blue-circle-left img,
.eas-img.is-style-orange-circle img,
.eas-img.is-style-orange-circle-left img,
.eas-img.is-style-green-circle img,
.eas-img.is-style-green-circle-left img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 999px;
  border: 8px solid var(--eas-blue);
  z-index: 2;
}

/* Small blue circle: inline size, small 60px disc (used on the-guardians grid) */
.eas-img.is-style-small-blue-circle img {
  border-radius: 999px;
  border: 5px solid var(--eas-blue);
  position: relative;
  z-index: 2;
  display: block;
}
.eas-img.is-style-small-blue-circle::after {
  content: '';
  position: absolute;
  width: 60px;
  height: 60px;
  border-radius: 999px;
  background: var(--eas-pale-yellow);
  bottom: 0;
  left: -25px;
}

/* Rounded corners: the 12px card radius (--radius), so the homepage Who We Are
   photo matches the story and blog cards (iprov-agency/eas-easterseals#300).
   WP's package was 20px. */
.eas-img.is-style-rounded-corners img { border-radius: var(--radius); display: block; }

/* ---- Container background decorations ----
   Corner swirl: a faint orange spiral tucked into a bottom corner, behind
   content. Content is lifted above via a direct-child z-index (WP lifts
   .gb-container-inside; vanilla lifts the container's direct child). */
.is-style-corner-swirl,
.is-style-corner-swirl-right { position: relative; overflow: hidden; }
.is-style-corner-swirl > *,
.is-style-corner-swirl-right > * { position: relative; z-index: 1; }
.is-style-corner-swirl::after {
  content: url('/img/decor/background-swirl-orange.png');
  position: absolute;
  left: -50px;
  bottom: -200px;
  z-index: 0;
  opacity: 0.4;
  pointer-events: none;
}
.is-style-corner-swirl-right::after {
  content: url('/img/decor/background-swirl-orange.png');
  position: absolute;
  right: -50px;
  bottom: -200px;
  z-index: 0;
  opacity: 0.4;
  transform: scale(-1, 1);
  pointer-events: none;
}

/* Pattern top-left: the brand's faint swirl field (already ~10% alpha) pinned
   to the top-left corner of a container, behind content. */
.pattern-top-left-white,
.pattern-top-left-yellow { position: relative; overflow: hidden; }
.pattern-top-left-white > *,
.pattern-top-left-yellow > * { position: relative; z-index: 1; }
.pattern-top-left-white::before,
.pattern-top-left-yellow::before {
  content: url('/img/decor/background-pattern-white-v3.png');
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
  pointer-events: none;
}

/* ---- National design elements: art is geometry, colour is a token ----------
   The 2025 national brand folder ships an Orange Shapes set: 11 SVGs, every one
   a single path. They are vendored under /img/decor/elements/ as GEOMETRY ONLY,
   a viewBox and one path, with no fill attribute, no defs, no style block and
   no hex anywhere in the file. Asset 4 and Asset 8 are byte-identical geometry
   in two different fills, so the 11 supplied files reduce to 10 shapes and
   4,593 bytes of source to 1,637 vendored.

   The art is therefore a CSS mask and the paint is the background-color under
   it, so every shape takes its colour from a brand token and nothing else.
   That is not a preference. Three of the 11 supplied files carry a fill that is
   not on the approved palette (Asset 8 #ce4b04 against Orange #cf4b04, Asset 11
   #ffa200 against Yellow #ffa300, and Asset 10 #faa21b, whose nearest approved
   colour measures as Gold #f7941e rather than Yellow). A file with no colour in
   it cannot carry a wrong one, so the defect is closed structurally.
   scripts/generate_styleguide.py FAILS if any vendored file gains a hex, if a
   shape class and its file disagree about the viewBox, or if the ink classes
   and the palette stop matching. The whole inventory is published, measured, at
   /styleguide/elements/.

   This sits BESIDE the raster decorations above, it does not replace them. The
   corner swirl is an organic brush-stroke drawing with no counterpart anywhere
   in the national set, so no shape here is a substitute for it and nothing
   above is retired.

   APPLIED TO ZERO PRODUCTION PAGES AT THIS COMMIT, deliberately. Decoration on
   a real page is a visible change that needs a sign-off, and bundling it here
   would make the zero-visual-change proof impossible. The guard measures and
   publishes the production count, so the day that stops being zero is visible.

   Placement is the consuming page's job: give the host position and overflow,
   then position .es-el inside it. No pin or corner helper ships here, because
   this repo's own audit has just finished counting 11 implemented classes with
   zero consumers and speculative placement rules would be the twelfth. Decide
   placement with a real page in front of you.
   -------------------------------------------------------------------------- */
.es-el {
  --es-el-art: none;
  --es-el-ink: var(--es-secondary-yellow);
  --es-el-size: contain;
  --es-el-pos: center;
  --es-el-opacity: 1;
  display: block;
  background-color: transparent;
  opacity: var(--es-el-opacity);
  -webkit-mask-image: var(--es-el-art);
          mask-image: var(--es-el-art);
  -webkit-mask-size: var(--es-el-size);
          mask-size: var(--es-el-size);
  -webkit-mask-position: var(--es-el-pos);
          mask-position: var(--es-el-pos);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  pointer-events: none;
}
/* With no mask support the paint would be a solid rectangle, which is a worse
   failure than no decoration at all, so the paint is gated on the mask. */
@supports (-webkit-mask-image: none) or (mask-image: none) {
  .es-el {
    background-color: var(--es-el-ink);
  }
}

/* The 10 shapes. aspect-ratio carries the file's own viewBox, so an .es-el with
   a width and no height is exactly the shape's proportions, and the generator
   fails if a value here and the file it points at ever disagree. */
.es-el--lens-tlbr-01 { --es-el-art: url("/img/decor/elements/lens-tlbr-01.svg"); aspect-ratio: 240.13 / 240.13; }
.es-el--lens-tlbr-02 { --es-el-art: url("/img/decor/elements/lens-tlbr-02.svg"); aspect-ratio: 240.08 / 240.13; }
.es-el--lens-bltr-01 { --es-el-art: url("/img/decor/elements/lens-bltr-01.svg"); aspect-ratio: 240.13 / 240.13; }
.es-el--scoop-br     { --es-el-art: url("/img/decor/elements/scoop-br.svg");     aspect-ratio: 239.8 / 240.13; }
.es-el--scoop-tr     { --es-el-art: url("/img/decor/elements/scoop-tr.svg");     aspect-ratio: 239.8 / 240.13; }
.es-el--scoop-bl     { --es-el-art: url("/img/decor/elements/scoop-bl.svg");     aspect-ratio: 222.46 / 222.46; }
.es-el--drop-tr      { --es-el-art: url("/img/decor/elements/drop-tr.svg");      aspect-ratio: 241.26 / 241.26; }
.es-el--disc         { --es-el-art: url("/img/decor/elements/disc.svg");         aspect-ratio: 167.05 / 167.05; }
.es-el--sweep-wide   { --es-el-art: url("/img/decor/elements/sweep-wide.svg");   aspect-ratio: 411.34 / 202.14; }
.es-el--quarter-bl   { --es-el-art: url("/img/decor/elements/quarter-bl.svg");   aspect-ratio: 205.17 / 206.02; }

/* The ink set IS the palette: 13 national colours and the 6 approved tints, one
   class each, no more and no fewer. The generator proves that both ways, so a
   token added to the palette without an ink class fails, and an ink class
   naming a token the palette does not carry fails too. These set a fill under a
   mask and never set color:, which is why a needs-dark-ground token is safe
   here and would not be on text. */
.es-el--ink-orange    { --es-el-ink: var(--es-primary-orange); }
.es-el--ink-yellow    { --es-el-ink: var(--es-secondary-yellow); }
.es-el--ink-navy      { --es-el-ink: var(--es-secondary-navy); }
.es-el--ink-purple    { --es-el-ink: var(--es-tertiary-purple); }
.es-el--ink-ruby      { --es-el-ink: var(--es-tertiary-ruby); }
.es-el--ink-lime      { --es-el-ink: var(--es-tertiary-lime); }
.es-el--ink-teal      { --es-el-ink: var(--es-tertiary-teal); }
.es-el--ink-blue      { --es-el-ink: var(--es-tertiary-blue); }
.es-el--ink-sky       { --es-el-ink: var(--es-tertiary-sky); }
.es-el--ink-coral     { --es-el-ink: var(--es-accent-coral); }
.es-el--ink-gold      { --es-el-ink: var(--es-accent-gold); }
.es-el--ink-midnight  { --es-el-ink: var(--es-accent-midnight); }
.es-el--ink-grey      { --es-el-ink: var(--es-accent-grey); }
.es-el--ink-orange-10 { --es-el-ink: var(--es-orange-10); }
.es-el--ink-yellow-50 { --es-el-ink: var(--es-yellow-50); }
.es-el--ink-yellow-30 { --es-el-ink: var(--es-yellow-30); }
.es-el--ink-yellow-10 { --es-el-ink: var(--es-yellow-10); }
.es-el--ink-sky-30    { --es-el-ink: var(--es-sky-30); }
.es-el--ink-sky-10    { --es-el-ink: var(--es-sky-10); }

/* ---------- Cream section band ---------- */
.bg-cream { background: var(--color-cream); }

/* Footer menus now span full width (newsletter moved to the gold band). */
.footer-top--menus { grid-template-columns: 1fr; }

/* ---------- Gold newsletter band (above the navy footer) ----------
   Restructured "Let's Keep in Touch": gold band with a corner swirl, white
   copy, white inputs, and a yellow submit button. Matches WP footer stack. */
.footer-newsletter-band {
  position: relative;
  overflow: hidden;
  background: var(--color-gold);
  padding: 46px 0;
}
.footer-newsletter-inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 28px 40px;
  align-items: center;
}
.footer-newsletter-copy h2 { color: var(--color-secondary); margin-bottom: 8px; font-size: 1.55rem; }
.footer-newsletter-copy p {
  color: rgba(0, 68, 115, 0.9);
  font-size: 1rem;
  line-height: 1.55;
  max-width: 460px;
}
.footer-newsletter-band .newsletter-form { max-width: 520px; }
.footer-newsletter-band .newsletter-input {
  background: #fff;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--color-secondary);
}
.footer-newsletter-band .newsletter-input::placeholder { color: rgba(0, 68, 115, 0.65); }
.footer-newsletter-band .newsletter-input:focus { border-color: var(--color-secondary); }
.footer-newsletter-band .newsletter-btn {
  background: var(--color-accent);
  color: var(--color-secondary);
  border-radius: 4px;
}
.footer-newsletter-band .newsletter-btn:hover { background: var(--color-secondary); color: #fff; }
/* Newsletter signup result (js/main.js, issue #140). The live region ships
   EMPTY in components/footer.html so screen readers register it before any
   message arrives, and while empty it takes no space, so the band is the
   same height as before on every page. It wraps the form in
   .footer-newsletter-action rather than sitting in the band grid directly,
   because a third grid child would add a 28px row gap to every page.
   width: 0 plus min-width: 100% stops a long message from widening the
   band's auto grid column. The text sits straight on the gold band
   (--es-yellow-50, #ffd180): navy measures 7.08:1 and ruby 6.67:1 in the
   scripts/check-contrast.mjs matrix, both clear 4.5:1 for normal text. The
   words carry the outcome; colour only reinforces it. */
.newsletter-status {
  width: 0;
  min-width: 100%;
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--color-secondary);
}
.newsletter-status:not(:empty) { margin-top: 12px; }
.newsletter-status.is-error { color: var(--es-tertiary-ruby); }

@media (max-width: 760px) {
  .footer-newsletter-inner { grid-template-columns: 1fr; }
}

/* =============================================
   Interior page-title hero band
   The WP altitude-pro page header: a full-bleed band with a centered navy
   title, opening every interior page and archive (single Stories of Hope
   posts are the one exception). Band color and title are sampled from
   production, not guessed: band #ffa300 (the brand amber, --color-accent);
   title navy #004473 (A2 contrast fix, 5.06:1). Supporting copy (eyebrow,
   lead, intro) lives below the band on white, per the WP rhythm.

   GEOMETRY IS SHARED, THE FILL IS NOT, since the 2026-10-21 blog repaint
   (Casey: the amber opener is retired for blog posts). The three selectors
   still share one box, so a padding or alignment change still lands on all
   three and cannot drift between them; only `background` is split out below.

   THE AMBER OPENER IS RETIRED FROM PRODUCTION, 2026-10-21, and these rules are
   kept anyway. That is a decision, not an oversight: Casey's call on the
   interior-opener slice 1 Q7, option (a). Read the two halves separately.

     .post-header   LIVE on 247 pages, the whole of /blog/<slug>/. It shares
                    the box above and has its own fill below. Nothing here is
                    dead for it.
     .hero-band     ZERO production pages. .page-head, ZERO. Both are rendered
     .page-head     only by styleguide/components/index.html and
                    styleguide/archive/layouts/program.html, which document
                    them as specimens and always will, so the markup count can
                    never reach zero and the rules cannot be deleted on a
                    "nothing uses it" argument.

   scripts/generate_styleguide.py reports both as `unused` rather than `live`,
   and that is correct rather than a defect to fix: its SKIP_DIRS excludes
   styleguide/ precisely so the guide cannot count its own specimens as usage.
   `inert` is the opposite case (markup with no rule) and does not apply.

   The count history, since four separate hand counts of this subject have been
   wrong: 8 and 57 were the styleguide-inclusive totals; 7 and 55 excluding
   styleguide/. .page-head went 55 to 25 with the 30 annual-report pages, 25 to
   4 with the 21 hand-authored pages, 4 to 2 when the Q5 duplicate pairs
   collapsed, and 2 to 0 here. .hero-band went 7 to 0 here, in one step: the 5
   generated hubs, /careers/ and the /careers/job/ template.
   styleguide/components/index.html is the generated authority for these
   counts, not this comment.
   ============================================= */
.hero-band,
.page-head,
.post-header {
  position: relative;
  overflow: hidden;
  padding: 58px 0 54px;
  text-align: center;
}
.hero-band,
.page-head {
  background: var(--color-accent);
}
/* Blog posts (247 generated pages, the whole of /blog/<slug>/) open on the
   light Sky tint instead of the amber. Values are the shipped .breadcrumb-bar
   treatment in css/template-kit.css, a tint of Sky #82cde1, reused rather than
   invented: the playbook approves six tints and forbids a seventh, so a new
   hex here would be a brand defect as well as a second source of truth.
   The navy h1 reads 9.00:1 on this ground against 5.06:1 on the amber.
   The 1px border is the only geometry difference from the amber band; padding,
   text-align and the h1 clamp are untouched.
   This band carries NO pattern overlay: .post-header came out of the overlay
   block below on 2026-10-21, before this repaint landed, because the white
   pattern measures 1.12:1 on this tint. See that rule's scope note. */
.post-header {
  background: #e6f4f9;                   /* light Sky #82cde1 tint */
  border-bottom: 1px solid #cfeaf3;
}
.hero-band > .container,
.page-head > .container,
.post-header > .container {
  position: relative;
  z-index: 1;
}

/* THE RULE, so the next template does not have to guess (Casey, 2026-10-15,
   issue #88): the amber opener bands, .hero-band and .page-head, carry the
   pattern overlay. It is declared HERE, on the archetype, rather than as a
   `pattern-top-left-white` class on each page, for one reason: a class has to
   be remembered. The rule said "every amber page-head band" until 2026-10-21,
   when .post-header came out of the block below; it is named rather than
   implied now, so the next reader does not have to infer the scope from a
   selector list. Five of the nine page-heads audited on 2026-10-13 were
   missing it, including the two generated People pages, and nobody noticed
   because each page looked fine on its own.
   Declaring it on the selector means a new page that uses .page-head gets the
   treatment by existing, and no generator has to know about it.

   SCOPE, and what is deliberately NOT in it:
     .hero-band  .page-head                yes, these two share this block
                                           and are one visual family
     .post-header                          NO, dropped 2026-10-21. It still
                                           shares the box above, so a padding
                                           change still lands on all three,
                                           but not the overlay. The asset is
                                           142,094 bytes of white shapes, and
                                           the blog opener WAS repainted off
                                           the amber onto the light Sky tint
                                           #e6f4f9 (see the .post-header rule
                                           above), where white measures
                                           1.12:1 and reads as nothing at
                                           all. 142 KB on 247 pages for an
                                           invisible texture is the whole
                                           reason. Do not put it back without
                                           re-measuring the band it sits on.
     .program-hero--accent                 NO. A different archetype: it
                                           pairs copy with a photo, and a
                                           background pattern competes with
                                           the image rather than supporting
                                           it. /start-here/ and /get-support/
                                           use it.
   Both parents already set position:relative and overflow:hidden above, and
   the > .container rule above already lifts content to z-index 1, so the
   overlay cannot cover anything. Removing a selector from this block cannot
   move one either: the pseudo element is position:absolute at z-index 0, so
   it takes part in no layout its parent can see. */
.hero-band::before,
.page-head::before {
  content: url('/img/decor/background-pattern-white-v3.png');
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
  pointer-events: none;
}
.hero-band h1,
.page-head h1,
.post-header h1 {
  /* Navy on amber = 5.06:1 (A2 contrast fix; was #fff 2.00:1); navy on the
     .post-header Sky tint = 9.00:1. Both clear 4.5 at any size. */
  color: var(--color-secondary);
  margin: 0;
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  font-weight: 800;
  line-height: 1.14;
}
/* Breadcrumb, when a banded type carries one, rides on the amber in white.
   NO CONSUMER as of 2026-10-21. /careers/job/ was the only page in the tree
   carrying a `.breadcrumb` (not `.breadcrumb-bar`), and it moved to the shared
   trail bar below; css/careers.css's three rules written to beat these alpha
   values went with it. Reported rather than deleted, the same way slice 2
   reported its dead CSS: this is inside the block option (a) preserves whole,
   and no specimen renders `.breadcrumb`, so it is the one deletion candidate
   in here that a CSS-hygiene pass could take without touching the specimens.
   Not taken in this change, which is a markup change. */
.hero-band .breadcrumb,
.hero-band .breadcrumb a {
  color: rgba(0, 68, 115, 0.9);
}
.hero-band .breadcrumb a:hover { color: var(--color-secondary); }
.hero-band .breadcrumb span { color: rgba(0, 68, 115, 0.6); }

/* Slim variant: staff and location detail pages open with a shorter amber title
   band above their functional (photo/map) hero, for sitewide band consistency.
   The band owns the page-title h1; the hero below keeps its functional content. */
.hero-band--slim { padding: 34px 0 30px; }
.hero-band--slim h1 { font-size: clamp(1.5rem, 3vw, 2.15rem); }

/* Intro block that follows a title band on white (eyebrow + lead). It was the
   WP layout's intro-under-the-band on the static pages and the generated hubs.
   ZERO production pages as of 2026-10-21, specimen only, measured `unused`,
   kept for the same reason the band above is kept. Every selector in this
   block had a live consumer until that date, which is why the lead list names
   seven classes: .col-hero-intro, .archive-hero-intro, .stories-hero-intro,
   .location-archive-lead and .careers-hero-intro were the five hub lead
   classes it overrode to 19px/720px, and the migrated pages carry a plain <p>
   under .people-intro--wide instead, which is already 720px. Do not add a
   selector here; the live opener is the Option B block below. */
.page-intro { padding: 44px 0 8px; text-align: center; }
.page-intro .container { max-width: 860px; }
/* Section heading under the band (CD-230: the /about/ tagline moved out of the
   h1). Sized at 32px at 1440 against the 46.4px .page-head h1 so the band keeps
   the page-title role; matches the .prose h2 scale. Additive: before CD-230 no
   .page-intro section on any of the 75 pages carried an h2 or h3. */
.page-intro h2 {
  font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 800;
  line-height: 1.25;
  color: var(--color-secondary);
  margin: 0 0 14px;
}
.page-intro .eyebrow,
.page-intro .hero-eyebrow,
.page-intro .text-sub {
  font-size: 1.1875rem; /* large-text threshold; see base.css .text-sub */
  font-weight: 700;
  text-transform: none;
  letter-spacing: 2.5px;
  color: var(--color-primary);
  margin-bottom: 14px;
}
.page-intro .lead,
.page-intro .col-hero-intro,
.page-intro .archive-hero-intro,
.page-intro .stories-hero-intro,
.page-intro .location-archive-lead,
.page-intro .staff-intro,
.page-intro .careers-hero-intro {
  font-size: 1.1875rem;
  color: var(--color-text-body);
  line-height: 1.6;
  max-width: 720px;
  margin: 0 auto;
  opacity: 1;
}
.page-intro > .container > :last-child { margin-bottom: 0; }
/* Archive/hub intros carry a button row under the blurb. */
.archive-intro .archive-hero-nav { margin-top: 24px; }

/* =============================================
   Interior opener, Option B: breadcrumb bar + title block on white
   The replacement for the amber .page-head band above. Shipped and approved on
   5 pages (the People hub, Leadership Team, Board of Directors, Empowered by
   You, FAQs), with the trail bar alone on 116. A 12px light-Sky bar carries the
   ancestor trail, then the page title sits on white.

   Both components were declared in sheets only some pages load
   (.breadcrumb-bar in css/template-kit.css, .people-intro in css/pages.css).
   They live here now so a page gets them by loading site.css, which every page
   already does, instead of pulling a 14.5 KB or 41 KB sheet onto a page for one
   component.

   THE MOVE IS CASCADE SAFE BY CONSTRUCTION, the same argument the
   .audience-badges move below made on 2026-10-01 and verified the same way.
   Every page rendering either component loads site.css BEFORE template-kit.css
   and pages.css with no stylesheet between, so these declarations moved only
   EARLIER against sheets that could override them, never later. Nothing in
   either sheet competes: the one h1 selector in template-kit.css is
   .area-hero-copy h1, and in pages.css they are .error-page h1 and
   .thankyou-page h1, none of which can match inside .people-intro.

   src/worker.js carries a SUBSET of this section in CRITICAL_CSS and the two
   move together. The paragraph colour is deliberately not in that subset: it
   reads var(--color-text-body), one of the five tokens dropped from the subset
   on purpose, and scripts/check-tokens.mjs fails a rule reading a token the
   subset does not declare.
   ============================================= */

/* ---------- Breadcrumb bar (light-blue, under the white header) ---------- */
.breadcrumb-bar {
  background: #e6f4f9; /* light Sky #82cde1 tint */
  border-bottom: 1px solid #cfeaf3;
}
.breadcrumb-bar .container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-top: 12px;
  padding-bottom: 12px;
  font-size: 0.9rem;
}
.breadcrumb-bar a {
  color: #004473; /* navy links */
  font-weight: 600;
  text-decoration: none;
}
/* CONTRAST (2026-10-21, live on 116 pages): this hover was
   var(--color-primary), the brand orange #cf4b04, which measures 4.01:1 on the
   #e6f4f9 bar above. The links render at 0.9rem/600, 14.4px, which is NOT WCAG
   large text (that needs 24px regular or 18.66px bold), so the requirement is
   4.5 and the orange failed it. Three options, all measured with
   contrastRatio() from scripts/check-contrast.mjs:

     a darker orange           rejected before measuring. css/base.css carries
                               the house rule that orange text is 19px/700 or
                               nothing, so NO orange passes here at 14.4px
                               whatever its ratio, and a seventh approved hex
                               would be a brand defect as well.
     navy + underline          #004473 on #e6f4f9 = 9.00:1. Chosen.
     darker navy + underline   #003255 = 11.76:1, but a navy-to-navy shift at
                               14.4px is not perceptible in practice, so it
                               buys ratio nobody asked for and mints a hex the
                               palette does not carry.

   So the hover keeps the resting navy and carries its whole signal on the
   underline, which the resting rule above does not have. That is the one
   affordance here that does not depend on color perception at all (SC 1.4.1).
   The color is restated rather than left to inherit, so a later change to the
   resting rule cannot silently drag the hover with it. */
.breadcrumb-bar a:hover {
  color: #004473;
  text-decoration: underline;
}
.breadcrumb-sep { color: rgba(0, 68, 115, 0.5); }
.breadcrumb-current {
  color: rgba(0, 68, 115, 0.75);
  font-weight: 600;
}

/* ---------- Interior title block (.people-intro) ---------- */
/* The class name is the one it shipped under on the People hub and is kept as
   is: it is live on 5 pages and claimed in generate_styleguide.py's
   CLASS_INVENTORY, and renaming a live class is its own change, not a rider on
   a move. Read it as "intro block", not "People". */
/* iprov-agency/eas-easterseals#161, #162 and #163. A centred title and intro
   under the breadcrumb bar replace the amber page-head band and the eyebrowed
   page-intro. The h1 is the About Us hero title: the colour and the clamp are
   the ones on `.area-hero-copy h1` in css/template-kit.css (and the clamp on
   .about-splits-heading above), so they scale together; change all or none.
   The top padding is .area-hero's (56px, 36px at 600px and under). No bottom
   padding: the card section's own 50px top padding opens the gap under the
   paragraph. */
.people-intro {
  padding: 56px 0 0;
  text-align: center;
}
.people-intro h1 {
  color: #007cb1; /* 4.66:1 on white, as on the About Us hero */
  font-size: clamp(2rem, 4vw, 3rem);
  line-height: 1.1;
  margin: 0 0 16px;
}
.people-intro p {
  max-width: 500px;
  margin: 0 auto;
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--color-text-body);
}
@media (max-width: 600px) {
  .people-intro { padding-top: 36px; }
}
/* The Leadership Team page (/about-us/people/staff/) reuses this header
   (iprov-agency/eas-easterseals#166, #167) with a wider measure for its
   paragraph. Same specificity as `.people-intro p` and later in the file, so
   it wins on that page only; the hub keeps 500px. .people-roster is that
   page's card section, given the hub's 50px top padding for the same gap
   under the paragraph. */
.people-intro--wide p { max-width: 720px; }
/* The Guardians page hero gets 60px of space under its paragraph, before the
   gray card band (Jen). A modifier rather than a change to .people-intro,
   which has no bottom padding on purpose everywhere else. */
.people-intro--spaced { padding-bottom: 60px; }
/* The Board of Directors page (/about-us/people/board-of-directors/,
   iprov-agency/eas-easterseals#174 and #175) adds an orange tagline between
   the h1 and the paragraph. Its type is `.area-hero-tagline`'s in
   css/template-kit.css (20px, 700, line-height 1.4, --color-primary #cf4b04:
   4.51:1 on white, large text), the tagline on the About Us hero. Two
   classes, so it beats `.people-intro p` and `.people-intro--wide p` and
   keeps their measure and auto side margins; the 16px under it repeats the
   gap under the h1. Balanced wrapping: at 390 the line breaks as "Meet the
   leaders of / Easterseals Arkansas." instead of leaving "Arkansas." alone.
   The hub and the Leadership Team page carry no tagline, so they do not
   change. */
.people-intro .people-intro-tagline {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--color-primary);
  margin-bottom: 16px;
  text-wrap: balance;
}

/* The category eyebrow, kept as a visible line ABOVE the h1 on the 9 interior
   pages whose eyebrow names a category with NO page behind it (Internal Form,
   Legal, Accessibility, PASSE), so its text cannot become a linked crumb and
   has nowhere else to go. Casey's call on slice 1's Q3. Every other migrated
   page's eyebrow either became the parent crumb or restated the page itself,
   and was deleted rather than duplicated. Eyebrow above the title is the
   shipped shape: all 20 program pages render
   `.program-hero-eyebrow` + h1 + intro in that order.

   The class stays `.eyebrow`, the one the markup already carried, because here
   it is LOAD BEARING. Nothing under css/ declares a bare `.eyebrow`: the only
   rule that ever styled it is `.page-intro .eyebrow` above, so inside
   .people-intro it would fall through to `.people-intro p` and render as an
   18px body paragraph. That is the mirror image of the `.lead` class slice 2
   DROPPED from the 30 annual-report pages, where `.people-intro p` was already
   the correct rule and the class was dead weight. One principle, a class
   survives only if a rule needs it, landing on opposite answers.

   Values are copied from `.page-intro .eyebrow` so the line does not change
   appearance in the migration. --color-primary #cf4b04 is 4.51:1 on white and
   this is 19px/700, the large-text threshold and the css/base.css house rule
   for orange text; it cannot be shrunk without failing contrast.

   Specificity: this is (0,2,0) and `.people-intro p` is (0,1,1), so font-size
   and color win regardless of source order, and `margin-bottom` replaces only
   that one longhand of the `margin: 0 auto` on `.people-intro p`, leaving the
   auto side margins. `.people-intro--wide p` is also (0,2,0) and earlier, so
   the eyebrow keeps the 720px measure alongside the paragraph.

   Declared HERE, in the Option B section, and deliberately NOT bolted onto the
   `.page-intro .eyebrow` selector list above: slice 4 retires that whole
   `.page-intro` block with the amber band, and this rule must not leave with
   it. */
.people-intro .eyebrow {
  font-size: 1.1875rem; /* large-text threshold; see base.css .text-sub */
  font-weight: 700;
  text-transform: none;
  letter-spacing: 2.5px;
  color: var(--color-primary);
  margin-bottom: 14px;
}

/* ---------- Audience badge tiles (shared) ----------
   Circular photo tiles with a navy label and an orange Learn More. Three page
   families render this component: the 20 program pages ("Who This Program
   Serves"), /get-support/, and the homepage's "Who We Serve" and "Areas of
   Support" bands.

   It lived in css/programs.css until 2026-10-01. index.html loads only
   base.css + site.css, and pulling 21 KB of program-page CSS onto the site's
   most-visited page for one component was the wrong trade, especially since
   src/worker.js defers site.css (media=print + onload) but NOT programs.css,
   so programs.css on the homepage would be render blocking.

   The move is cascade safe by construction: all 21 pages that render the
   component load site.css BEFORE programs.css and no stylesheet loads between
   the two, so the relative order of these declarations against anything that
   could compete with them is unchanged. Verified with a computed-style and
   bounding-rect diff over 3 pages x 7 widths: 0 differences.

   .program-audiences, the program page's section wrapper, stays in
   css/programs.css. Only the component itself moved. */
.audience-badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 32px;
}
.audience-badge {
  width: 220px;
  text-align: center;
}
.audience-badge-media {
  display: flex;
  justify-content: center;
  margin-bottom: 16px;
}
.audience-badge-media img {
  width: 128px;
  height: 128px;
  border-radius: 50%;
  object-fit: cover;
  border: 5px solid var(--color-white);
  box-shadow: 0 6px 20px rgba(0, 68, 115, 0.12);
}
/* A trailing qualifier moves to the next line as a unit. Without this the
   break lands mid-qualifier and the tile reads "Developmental Support (Ages 0-"
   / "5)". Emitted by title_markup() in scripts/generate_areas.py. */
.tile-title-qual { white-space: nowrap; }

.audience-badge h3 {
  color: var(--color-secondary);
  font-size: 1.15rem;
  margin-bottom: 4px;
}
/* The size-up used elsewhere for orange-on-tint does not work here: 19px would
   make this sub-label larger than the .audience-badge h3 above it (18.4px).
   Navy instead, per Casey's 2026-09-15 ruling. 9.12:1 on light-gray. */
.audience-badge-sub {
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 1px;
  color: var(--color-secondary);
}

/* ---------- Wide tiles with a description (homepage bands) ----------
   The homepage renders the same tile with a body paragraph, which the 220px
   badge cannot hold. --wide switches the container from a centred flex wrap to
   an explicit grid, so the column count is STATED rather than inferred from a
   fixed tile width: the two bands hold 4 and 9 records and a single shared
   width orphans one or the other.

   Everything here is behind --wide, --text or --cols-N, none of which any
   existing page carries, so the 21 pages already rendering .audience-badges
   are untouched. Verified: 3 pages x 7 widths, 0 computed-style differences. */
.audience-badges--wide {
  display: grid;
  gap: 48px 32px;
}
.audience-badges--wide .audience-badge {
  width: auto;
  display: flex;
  flex-direction: column;
}
/* Figma node 107:422 draws this photo at 167.8px. Rounded to 168. This is the
   only number taken from Figma; every string comes from data/*.json. */
.audience-badges--wide .audience-badge-media img {
  width: 168px;
  height: 168px;
}
.audience-badges--wide .audience-badge h3 {
  font-size: 1.35rem;
  margin-bottom: 10px;
}
/* The link sits on a shared baseline per row: descriptions run 2 to 5 lines and
   without this they stagger across a row. margin-top:auto rather than
   flex-grow on the description (which is what .area-card-desc uses) so it still
   bottom-aligns for a record that has no description at all. */
.audience-badges--wide .audience-badge .link-arrow {
  margin-top: auto;
  padding-top: 14px;
}
/* 6.81:1 on the light-gray band, 7.56:1 on white. Must NOT become orange: at
   16px regular, #cf4b04 needs 4.5 and only reaches 4.06 on light-gray. The
   orange tokens on this tile pass only because base.css pins them at 19px/700,
   which makes them large text. */
.audience-badge-desc {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--color-text-body);
}

/* --text is the photo-less variant used by the Areas of Support band. Nine
   tiles with a 168px photo measured 1547px at desktop and 3983px on a phone,
   the tallest band on the page after the hero; without the photo the same nine
   cost 1076px and 2449px. A centred composition needs the photo to anchor it,
   so without one the text reads better left-aligned against an amber rule. */
.audience-badges--text .audience-badge {
  text-align: left;
  border-left: 4px solid var(--color-accent);
  padding: 2px 0 2px 20px;
}

/* CD-302, the clickable half only. The client: "It's the most useful section
   but it's text only, while the less useful age cards get the photos. Adding
   icons and making each whole card clickable would fix the imbalance." Casey
   split that: the whole-card link is here, the visual-weight half (photos or
   icons) is a design call held with #300 and is NOT in this slice.

   Do not flip HOME_REGIONS' photo flag to turn the photos on "while you are in
   here". It is one character and it costs 471px of desktop height and 1534px
   on a phone: nine tiles with a 168px photo measured 1547px desktop and 3983px
   mobile against 1076px and 2449px without. All nine records in
   data/areas-of-support.json already carry a hero_image that exists in the
   repo, so nothing has to be built when the call is made.

   The tile is now an <a> rather than a <div>, emitted by render_home_badge()
   in scripts/generate_areas.py. That is the same shape .program-audiences
   already ships on the 20 program pages (css/programs.css), and the same shape
   .blog-card, .staff-card and .loc-card use: ONE anchor wrapping the whole
   tile, with "Learn More" demoted to a <span>. A nested anchor would be
   invalid inside the tile anchor, would put two links per card in the
   accessibility tree, and would shrink the hit target back to one word.
   aria-labelledby points at the tile's own <h3>, so the accessible name is
   "Developmental Support (Ages 0-5)" rather than the title plus the whole
   description plus "Learn More"; .blog-card on this same page already does
   exactly this.

   NO hover fill, deliberately. A surface tint needs right and bottom padding
   to breathe, the padding above is the resting composition, and that
   composition is what the held design call owns. The three cues here are all
   free of reflow: the amber rule turns orange, the Learn More darkens from
   orange to navy, and it gains a 2px underline. The focus ring is the site's
   3px --focus-ring-color at a 4px offset, the same indicator
   .program-audiences a.audience-badge:focus-visible uses.

   Every selector below is scoped to `.audience-badges--text a.audience-badge`,
   never to a bare `a.audience-badge`. 20 program pages already render
   `<a class="audience-badge">` (scripts/generate_programs.py
   render_audience_tile) and css/programs.css owns their hover and ring; the
   two other `--text` bands, /contact/ and /empowered-by-you/, render <div>
   tiles and so match nothing here. Measured: 9 elements site wide. */
.audience-badges--text a.audience-badge {
  transition: border-color 0.18s;
}
.audience-badges--text a.audience-badge:hover { border-left-color: var(--color-primary); }
.audience-badges--text a.audience-badge:hover .link-arrow,
.audience-badges--text a.audience-badge:focus-visible .link-arrow {
  color: var(--color-primary-dark);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.audience-badges--text a.audience-badge:focus-visible {
  outline: 3px solid var(--focus-ring-color);
  outline-offset: 4px;
  border-radius: 6px;
}

.audience-badges--cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.audience-badges--cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* Two tiles (the "Read the Latest Issue" band on /empowered-by-you/). Two
   divide evenly by 2 and 1, so it collapses with --cols-3 at 700px. Each tile
   is 45% of the band, centred as a pair; the 700px rule below returns it to
   one full-width column. */
.audience-badges--cols-2 { grid-template-columns: repeat(2, 45%); justify-content: center; }

/* The collapse ladder is per column count, because an orphan tile is a function
   of the RECORD COUNT, not the viewport. 4 audiences divide evenly by 4, 2 and
   1, so that band can take an intermediate 2-up. 9 areas divide evenly only by
   3 and 1, so that band jumps straight from 3 to 1 or it strands a tile at
   every tablet width.

   These three rules carry the same specificity as the two above (0,1,0) and
   win on SOURCE ORDER alone, so they MUST stay below them. Measured: moving the
   1000px block above .audience-badges--cols-4 renders four 189px columns at
   900px instead of two 410px ones, and nothing errors. */
@media (max-width: 1000px) {
  .audience-badges--cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .audience-badges--cols-3,
  .audience-badges--cols-2 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 520px) {
  .audience-badges--cols-4 { grid-template-columns: minmax(0, 1fr); }
}

/* Everything from here to the end of the 768px block below was in
   css/pages.css until issue #139. It is the shared form kit, and the
   generated program pages load site.css but not pages.css, so it lives
   here now and every form on the site reads the same rules. Not in
   css/base.css: that is the shared iProv foundation, outside the token
   and contrast checks (scripts/check-contrast.mjs). */
/* ---------- Per-field form errors (the validation block in js/main.js) ----------
   The error paint is a border and a message, never an outline. css/base.css
   sets `outline: none` on .form-input and the missing focus ring is being
   fixed in its own site-wide PR; writing an outline rule here would collide
   with it. Ruby #8b0641 is the same error hue .form-message-error already
   uses below, so this adds no new color. */
.form-input[aria-invalid="true"] {
  border-color: #8b0641;
  box-shadow: 0 0 0 3px rgba(139, 6, 65, 0.12);
}
.field-error {
  font-size: 1rem;
  line-height: 1.5;
  color: #8b0641;
  margin-top: 6px;
}
/* An error slot is in the DOM from first paint and empty until something is
   wrong, so it must not reserve space or draw a gap while empty. */
.field-error:empty { display: none; }

/* ---------- Form kit (moved from css/pages.css, issue #139) ---------- */
.form-card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: 16px;
  padding: 36px;
  box-shadow: 0 12px 40px rgba(0, 68, 115, 0.08);
}
.form-intro { font-size: 1rem; color: var(--color-text-light); margin-bottom: 22px; }
.form-req { color: var(--color-primary); }
.form-section { border: none; margin: 0 0 8px; padding: 0; }
.form-section + .form-section { margin-top: 28px; padding-top: 26px; border-top: 1px solid var(--color-border); }
/* The legend is floated, which by the HTML rendering rules stops it being
   the fieldset's "rendered legend", the box the browser paints ON the top
   border. Floated it is an ordinary box inside the padding, so the divider
   .form-section + .form-section draws is a clean line 26px above the
   legend instead of a line through it (issue #139 review). The legend is
   still the fieldset's first child, so it still names the group for
   assistive technology. The element after it clears the float. */
.form-section legend {
  float: left;
  width: 100%;
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--color-secondary);
  margin-bottom: 16px;
  padding: 0;
}
.form-section legend + * { clear: both; }
.form-input,
select.form-input,
textarea.form-input { width: 100%; }
select.form-input { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23004473' d='M6 8 0 2 1.4.6 6 5.2 10.6.6 12 2z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 14px center; padding-right: 36px; }
.field-hint { font-size: 1rem; color: var(--color-text-light); margin-top: 4px; }
.choice-group { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.choice-group.inline { flex-direction: row; flex-wrap: wrap; gap: 10px 22px; }
.choice {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 1rem;
  color: var(--color-text-body);
  cursor: pointer;
}
.choice input { width: 16px; height: 16px; accent-color: var(--color-primary); }
/* Checkboxes and radios keep the browser default ring today, which is
   adequate but is not the ring every other control in this kit draws.
   Match them up. 10.12:1 navy on the white .form-card. */
.choice input:focus-visible {
  outline: 3px solid var(--focus-ring-color);
  outline-offset: 2px;
}
.days-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px 16px; margin-top: 6px; }

/* Honeypot: visually hidden, kept in tab order out */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Disabled photo upload note */
.upload-disabled {
  border: 1px dashed var(--color-border);
  border-radius: 10px;
  padding: 18px 20px;
  background: var(--color-light);
  margin-top: 6px;
}
.upload-disabled .upload-label { font-weight: 600; color: var(--color-text); font-size: 1rem; margin-bottom: 4px; }
.upload-disabled p { font-size: 1rem; color: var(--color-text-light); margin: 0; }
.upload-disabled input { display: none; }

.form-submit { margin-top: 26px; }
.form-submit:focus-visible {
  outline: 3px solid var(--focus-ring-color);
  outline-offset: 2px;
}
.form-footnote { font-size: 1rem; color: var(--color-text-light); margin-top: 14px; line-height: 1.6; }

/* Inline form status message (shown by the forms script on submit errors) */
.form-message {
  display: none;
  margin-top: 20px;
  padding: 12px 16px;
  border-radius: 8px;
  font-size: 1rem;
  line-height: 1.5;
}
/* Semantic states on brand palette (Ruby for error, Teal accent for success).
   Solid brand hues at low opacity for surfaces; readable text is Ruby / Navy. */
.form-message-error {
  display: block;
  background: rgba(139, 6, 65, 0.06); /* Ruby #8b0641 wash */
  color: #8b0641; /* Ruby, ~6.8:1 */
  border: 1px solid rgba(139, 6, 65, 0.28);
}
.form-message-success {
  display: block;
  background: rgba(88, 197, 141, 0.14); /* Teal #58c58d wash */
  color: var(--color-secondary); /* Navy, ~9:1 (no readable brand green exists) */
  border: 1px solid rgba(88, 197, 141, 0.55);
}
/* "(if applicable)" and similar asides inside a .form-label. */
.form-optional {
  font-weight: 500;
  color: var(--color-text-light);
  text-transform: none;
  letter-spacing: 0;
}
@media (max-width: 768px) {
  .days-grid { grid-template-columns: repeat(2, 1fr); }
  .form-card { padding: 26px 22px; }
}

/* ---------- Form rhythm (issue #139, normalised to /contact/) ----------
   One vertical gap between any two fields, whatever their type or wrapper.
   Before this, css/base.css gave a lone .form-group a 14px bottom margin
   but a .form-row pair 14px on the group PLUS 14px on the row, so fields
   sat 28px apart after a pair and 14px apart after a single field, and
   the two halves of a pair stacked 28px apart at 390. /contact/ is mostly
   pairs, so its rhythm is 28px; every field now gets that. The column gap
   inside a pair stays 14px. base.css is the shared iProv foundation and
   is not edited; these rules come later in the cascade and win at equal
   specificity. */
.form-group { margin-bottom: 28px; }
.form-row {
  row-gap: 28px;
  margin-bottom: 28px;
}
.form-row > .form-group { margin-bottom: 0; }
/* Single-line controls share the text input's height. Chromium draws a
   date input 2px taller than a text input (47px against 45px) and a
   select can come out 2px shorter, so both are pinned to 45px, written in
   em (2.8125em at the kit's 16px) so it still scales with text zoom. */
select.form-input,
input.form-input[type="date"] { height: 2.8125em; }
/* The body-copy statement box on the navy ground (the Supplemental Materials box on
   the Academy Curriculum page). The base `--prose` heading is navy, for the cream box;
   on navy it is white. Paragraphs already read white from `.mission-statement p`. */
.mission-statement--prose:not(.mission-statement--cream) h2 { color: var(--color-white); /* ground: --es-secondary-navy */ }
.about-splits .mission-statement { margin: 90px 0 30px; }

/* ---------- Resource list: a card list of links ----------
   Moved here from css/pages.css on 2026-10-29 unchanged, so /resources/ and
   the program pages' Resources sections (css/programs.css .pgs-resources)
   share one card instead of two copies. The .prose interactions stay in
   pages.css. Declarations are byte for byte what pages.css carried. */
.resource-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
  max-width: 900px;
  margin: 0 auto;
  list-style: none;
}
.resource-list li { margin: 0; }
/* Qualifier inside a resource card: part of the link, set quieter than the name. */
.resource-list .resource-note { font-weight: 400; color: var(--color-text-body); }
.resource-list a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 20px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-secondary);
  background: var(--color-white);
  transition: border-color 0.2s, color 0.2s;
}
.resource-list a::before {
  content: '';
  flex-shrink: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-accent);
}
.resource-list a:hover { border-color: var(--color-primary); color: var(--color-primary); }
@media (max-width: 768px) {
  .resource-list { grid-template-columns: 1fr; }
}
