/* ==========================================================================
   Atkinson Hyperlegible — Braille Institute, SIL Open Font License 1.1.
   Ported 2026-08-09 from save-trl, which is where the TRL family already
   standardised on it (see also work/community/trl-patron-coalition/
   volunteer-form/volunteer-form.css and the 7/19 site audit).

   Designed for low-vision readers: it gives distinct SHAPES, not just
   distinct widths, to the pairs that normally collapse — capital I,
   lowercase l and digit 1; capital O and zero. The stated audience for this
   site is older rural voters, and the stylesheet already targets AAA
   contrast for that reason. This is the same decision one layer down.

   Self-hosted deliberately. No Google Fonts request: it keeps visitor data
   off a third party and removes an external dependency a campaign site
   cannot afford to have fail.
   ========================================================================== */
@font-face { font-family: "Atkinson Hyperlegible"; src: url("../assets/fonts/AtkinsonHyperlegible-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Atkinson Hyperlegible"; src: url("../assets/fonts/AtkinsonHyperlegible-Bold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Atkinson Hyperlegible"; src: url("../assets/fonts/AtkinsonHyperlegible-Italic.woff2") format("woff2"); font-weight: 400; font-style: italic; font-display: swap; }
@font-face { font-family: "Atkinson Hyperlegible"; src: url("../assets/fonts/AtkinsonHyperlegible-BoldItalic.woff2") format("woff2"); font-weight: 700; font-style: italic; font-display: swap; }

/* ==========================================================================
   Archivo Black — Omnibus-Type, SIL Open Font License 1.1.
   Added 2026-08-13 at the coalition's request ("use Archivo Black for the
   headers"), which also settles the open question left in --font-display: the
   Georgia placeholder is gone.

   It is the right ask: the campaign logo's wordmark is a heavy grotesque, and
   this puts the headings in the same voice as the mark.

   ⚠️ ONE WEIGHT EXISTS — 400. There is no bold and no italic. Headings
   previously set 700, which on a face with no bold makes the browser
   SYNTHESISE one by smearing the outline. h1/h2 are therefore weight 400
   below. ⛔ Do not "restore" 700 on anything using this family.

   Self-hosted, like Atkinson, and for the same two reasons stated above: no
   third party sees a visitor's request, and a campaign site carries no
   external dependency that can fail. Both subsets are local; the latin-ext
   file only downloads if a character needs it.
   ========================================================================== */
@font-face {
  font-family: "Archivo Black";
  src: url("../assets/fonts/ArchivoBlack-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo Black";
  src: url("../assets/fonts/ArchivoBlack-Regular-latin-ext.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   Vote Yes for Libraries — Timberland Regional Library levy lid lift
   --------------------------------------------------------------------------
   EVERY colour is a custom property in :root. To change the whole palette,
   edit ONLY the block below. Nothing else in this file hard-codes a colour.

   Contrast ratios were computed, not estimated. WCAG AA = 4.5:1 body text,
   3:1 large text and UI. AAA = 7:1. The stated primary audience is older
   rural voters, so BODY TEXT targets AAA; brand colour targets AA.

   ✅ APPROVED PALETTE — 2026-08-13. The provisional hexes read off an image on
   2026-08-09 are gone. These are the supplied Y4L values, and they match the
   campaign logo (assets/img/y4l-logo.png), whose book spines are these same six.

     #0cc0df  aqua blue    2.18:1 on white — GRAPHIC ONLY, never text on light
     #3a66b5  blue         5.60:1 on white — AA
     #90ba38  green        2.27:1 on white — GRAPHIC ONLY, never text on light
     #3e6d3c  gray green   6.07:1 on white — AA
     #127871  dark cyan    5.32:1 on white — AA
     #41707c  gray azure   5.48:1 on white — AA

   ⭐ THE STRUCTURAL DECISION, Marika 2026-08-13: "keep the current white or
   cream and use the colors for accents." White and cream stay the surfaces.
   The six are accents, buttons, bands, and rules — not the reading surface.

   ⚠️ NO APPROVED SWATCH REACHES AAA ON WHITE. The darkest is gray green at
   6.07:1. So the two TEXT anchors below are the approved dark cyan DARKENED —
   same hue, deeper — which keeps body text at AAA without introducing a colour
   from outside the palette. Everything the reader perceives as brand colour is
   an approved hex; only the ink is derived.

   ⛔ --accent-graphic and --accent-graphic-2 (aqua, green) are 2.2:1 on white.
   They are for BARS, RULES, AND FILLS ONLY. Never text, never a link, never an
   icon that carries meaning on a light surface.
   ========================================================================== */

:root {
  /* --- Surfaces --- */
  --ink:            #0A4A45;  /* dark cyan darkened — 10.1:1 on paper, 9.7:1 on cream, AAA */
  --ink-soft:       #127871;  /* dark cyan, approved — 5.32:1 on paper, AA */
  --paper:          #FFFFFF;
  --paper-alt:      #FDFBF6;  /* cream. Unchanged — Marika's "lean heavily on the white/cream". */
  --rule:           #CBDEDB;  /* hairlines — dark-cyan tint, 1.35:1 on paper */

  /* --- Dark bands (footer, CTA, endorsements) --- */
  --band:           #0A4A45;  /* same derived anchor as --ink */
  --band-card:      #127871;  /* dark cyan, approved — cards inside dark bands */
  --on-band:        #FFFFFF;  /* 10.1:1 on --band, AAA */
  --on-band-soft:   #B7CCC9;  /* 7.1:1 on --band, AAA */

  /* --- Accents --- */
  --accent-sky:     #0cc0df;  /* aqua, approved. DARK BANDS ONLY: 4.6:1 on --band (AA large/UI).
                                 2.18:1 on paper — never on a light surface.

                                 ⛔ NEVER A LARGE SURFACE. Marika's ruling, 2026-08-29,
                                 after a full-width aqua banner: "I just don't want it
                                 that large as a background again." The objection is
                                 SCALE, not the colour and not backgrounds as such.

                                 ✅ Fine at small scale: buttons, the h2::before section
                                 rule, card borders, focus outlines. Those stay.
                                 ⛔ Not fine: a band, a banner, a hero, or anything else
                                 that fills the width of the page.

                                 ⭐ WHEN A LARGE BRIGHT SURFACE IS WANTED, IT IS THE GREEN.
                                 Her words: "when I say bright I probably mean green."
                                 --accent-graphic-2 #90ba38 with --on-accent-graphic-2
                                 #052E2A on top = 6.48:1. See the .banner block.
                                 ⛔ White on that green is 2.27:1 — never.

                                 As TEXT on dark bands the aqua is unchanged and is the
                                 only bright accent that clears AA there: green is 4.46:1
                                 and misses, blue 1.80:1 and slate 1.84:1 both fail. */

  --accent-link:    #127871;  /* dark cyan, approved — light surfaces. 5.32:1 on paper, AA */
  --accent-link-hi: #0A4A45;  /* hover/active on light. 10.1:1 */

  /* --- Brand graphics — the logo's own bars. NEVER text. --- */
  --accent-graphic:   #0cc0df;  /* aqua  — 2.18:1 on paper. Fills and rules only. */
  --accent-graphic-2: #90ba38;  /* green — 2.27:1 on paper. Fills and rules only. */
  --accent-earth:     #3e6d3c;  /* gray green, approved — 6.07:1 on paper, AA */
  --on-accent-graphic-2: #052E2A; /* label colour for text ON the green fill — 6.48:1.
                                 Derived (near-black teal), and it has to be: --ink on
                                 green is 4.46:1, and a 17px semibold button label is
                                 NORMAL text under WCAG, not large, so the bar is 4.5:1
                                 and --ink misses it. White on green is 2.27:1 — never. */
  --accent-blue:      #3a66b5;  /* blue, approved — 5.60:1 on paper, AA */
  --accent-slate:     #41707c;  /* gray azure, approved — 5.48:1 on paper, AA */

  /* --- Semantic --- */
  --focus:          #3a66b5;  /* approved blue. 5.60:1 on paper — clears the 3:1 UI threshold
                                 with room, and stays visually distinct from --accent-link,
                                 which is teal. Safe as text too, unlike the old value. */
  --shadow:         0 1px 3px rgb(10 74 69 / 0.10), 0 8px 24px rgb(10 74 69 / 0.08);

  /* ⭐ The spine bar, defined ONCE — Marika, 2026-08-13, of the header rule:
     "I LOVEE LOVE LOVE the multicolor line at the top", then "replace the
     plain line with the pretty multicolor line".

     Six hard-stopped bands in the logo's own left-to-right order. Used for the
     header edge and for every divider that was previously a hairline.

     ⚠️ --spine-on-blue exists because the footer's background IS --accent-blue.
     The blue segment would vanish into it and read as a gap in the bar — a
     rendering fault, not a design. That one segment goes white there; the
     other five are unchanged and in the same positions. */
  --spine: linear-gradient(90deg,
    var(--accent-slate)     0      16.67%,
    var(--accent-link)      16.67% 33.33%,
    var(--accent-graphic-2) 33.33% 50%,
    var(--accent-graphic)   50%    66.67%,
    var(--accent-blue)      66.67% 83.33%,
    var(--accent-earth)     83.33% 100%);

  --spine-on-blue: linear-gradient(90deg,
    var(--accent-slate)     0      16.67%,
    var(--accent-link)      16.67% 33.33%,
    var(--accent-graphic-2) 33.33% 50%,
    var(--accent-graphic)   50%    66.67%,
    #FFFFFF                 66.67% 83.33%,
    var(--accent-earth)     83.33% 100%);

  /* --- Type — UNCHANGED 2026-08-09 at Marika's instruction --- */
  --font-body: "Atkinson Hyperlegible", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* ⭐ RULED 2026-08-13. Was Georgia with a note saying it stayed "until Marika
     ruled." The coalition asked for Archivo Black on the headers and she
     approved it for h1 and h2. Applies to h1, h2, and the two big-numeral
     styles (.figure .num, .calc-amount), which were the other Georgia display
     contexts. ⛔ h3 is NOT on this family — see the note at h3 below. */
  --font-display: "Archivo Black", "Arial Black", "Helvetica Neue", Impact, sans-serif;

  /* Older readers: 18px base, not 16.
     ⭐ Line-height dropped 1.7 → 1.6 on 2026-08-12 at Marika's call — 1.7 was
     reading as "the font is bigger than normal" when the type size was not the
     cause. 1.6 is the typical range for body text and still generous against
     the 1.4–1.5 a 16px site would use. The 18px base is UNCHANGED and remains
     deliberate for the stated audience. */
  --size-base: 1.125rem;
  --leading: 1.6;

  /* ⛔ There is deliberately NO --measure / reading-column variable.
     Body copy runs the FULL width of .wrap. The container padding IS the
     measure. Do not add a narrower max-width to p, .lede, or any prose
     wrapper. See .claude/rules/full-width-body-copy.md in Komorebi. */
  --wide: 100%;         /* page width — the ONLY width constraint.
                           100% here is the phone case: the min() on .wrap supplies
                           the gutter, so a narrow screen keeps its 1.25rem either
                           side rather than losing a tenth of itself. The real value
                           is set at the breakpoint below. 2026-08-27. */
  --gap: 1.5rem;
}

/* ==========================================================================
   Reset + base
   ========================================================================== */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--size-base);
  line-height: var(--leading);
  color: var(--ink);
  background: var(--paper);
}

h1, h2 {
  font-family: var(--font-display);
  /* ⛔ 400, not 700. Archivo Black ships one weight and has no bold; asking for
     700 makes the browser fake it by smearing the outline. The face is already
     black — this is not a lighter setting, it is the only setting. */
  font-weight: 400;
  letter-spacing: -0.01em;   /* a black grotesque sets tight; Georgia did not */
}

/* ⭐ h3 deliberately stays on Atkinson bold — Marika, 2026-08-13, approving
   Archivo Black "for h2 and h1" only. A black grotesque at h3 size is dense,
   and density is exactly what this site's stated audience of older rural
   voters can least afford. The headings hierarchy still reads: h1/h2 are a
   different VOICE, not merely a larger size. */
h3 {
  font-family: var(--font-body);
  font-weight: 700;
}

h1, h2, h3 {
  line-height: 1.2;
  margin: 0 0 0.5em;
  /* No text-wrap: balance — it pulls headlines in from the right edge and
     reads as the same "half the page" problem the body copy had. */
}

h1 { font-size: clamp(2rem, 5vw, 3.25rem); }
h2 { font-size: clamp(1.6rem, 3.5vw, 2.25rem); }

/* ==========================================================================
   Accented phrase inside a heading
   --------------------------------------------------------------------------
   ⭐ Marika, 2026-08-13: "on the headers, could we use #3a66b5 blue to accent
   certain words — like 'open, staffed, and local.' — and maybe #90ba38 green
   as an underline."

   The contrast maths is unusually favourable here and worth stating, because
   it is the reason this is allowed on a heading and would NOT be allowed in a
   paragraph:

     #3a66b5 on white is 5.60:1. For LARGE text — which WCAG defines as 24px
     regular or 18.66px bold, and every h1 and h2 on this site clears — the AA
     threshold is 3:1 and the AAA threshold is 4.5:1. So the blue is AAA here.
     ⛔ In body copy the threshold is 7:1 for AAA and the same blue would fail.
     Do not reuse .hl on a paragraph.

   The green underline is decoration at 2.27:1 and carries no meaning, which is
   permitted — but it is deliberately THICK and offset so it reads as an
   editorial marker rather than as a hyperlink. Links on this site are teal,
   thin, and coloured on the text itself; this is neither.

   skip-ink is off on purpose: a decorative rule that breaks around every
   descender looks like a rendering fault at this weight.
   ========================================================================== */
.hl {
  color: var(--accent-blue);
  text-decoration: underline;
  text-decoration-color: var(--accent-graphic-2);
  text-decoration-thickness: 0.16em;
  text-underline-offset: 0.14em;
  text-decoration-skip-ink: none;
}

/* On a dark band the blue drops to 1.80:1 and the accent becomes unreadable.
   Inside .band--dark the phrase keeps the band's own text colour and only the
   green underline carries the accent. */
.band--dark .hl { color: inherit; }
h3 { font-size: clamp(1.25rem, 2.5vw, 1.5rem); }

p { margin: 0 0 1em; }   /* ⛔ never add max-width here */

a { color: var(--accent-link); text-underline-offset: 0.15em; }
a:hover { color: var(--accent-link-hi); }

/* Visible focus everywhere. Non-negotiable for keyboard users. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

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

/* ==========================================================================
   Layout helpers
   ========================================================================== */

.wrap { width: min(100% - 2.5rem, var(--wide)); margin-inline: auto; }

/* ⭐ Gutters — Marika, 2026-08-27. Was a fixed 1120px, which left 21% of a
   1920px screen empty either side. Now 10% each side on ordinary pages, and
   4% on the events page because two month grids side by side need the room.
   Percentages rather than pixels throughout, per her standing preference. */
@media (min-width: 700px) {
  :root { --wide: 80%; }
  body:has(.cal-pair) { --wide: 92%; }
}

.band { padding: clamp(3rem, 7vw, 5rem) 0; }
.band--alt  { background: var(--paper-alt); }
.band--dark { background: var(--band); color: var(--on-band); }

/* ⭐ Section rule — 2026-08-13, same instruction as the header spine bar.
   A short colour bar above each band's h2, so the palette shows up down the
   page and not only at the top. Each band gets a different one, matching the
   spine bar's sequence, so scrolling reads as one system rather than four
   unrelated accents.

   ⛔ Purely decorative: it must never be the only thing distinguishing one
   section from another. The h2 text is still the heading. */
.band > .wrap > h2::before {
  content: "";
  display: block;
  width: 3.5rem;
  height: 5px;
  margin-bottom: 0.85rem;
  border-radius: 2px;
  background: var(--accent-link);
}
.band--alt  > .wrap > h2::before { background: var(--accent-earth); }
.band--dark > .wrap > h2::before { background: var(--accent-sky); }

.band--dark h1,
.band--dark h2,
.band--dark h3 { color: var(--on-band); }

.band--dark p { color: var(--on-band-soft); }
.band--dark a { color: var(--accent-sky); }
.band--dark :focus-visible { outline-color: var(--accent-sky); }

/* Bright banner - added 2026-08-29 for the Thurston kickoff, green since the
   same day. A narrow, high contrast strip between a hero and the first band,
   for one time-limited announcement. Narrow on purpose: it is a line, not a
   section, and it must not compete with the calendar underneath it.

   COLOUR, and the numbers decide it:
     white on --accent-graphic-2          2.27:1  FAILS. Never.
     --ink on --accent-graphic-2          4.46:1  misses normal-text AA by 0.04
     --on-accent-graphic-2 on the green   6.48:1  passes AA comfortably

   Marika asked for white lettering on the green and it is not available - the
   green is a light fill, so anything light on it fails. --on-accent-graphic-2
   already exists in this file for exactly this case, and it is why the near-
   black teal was derived: --ink lands four hundredths short of the 4.5:1 bar
   for normal text. Use the token; do not reach for white or --ink here. */
.banner {
  background: var(--accent-graphic-2);
  color: var(--on-accent-graphic-2);
  padding: 1.1rem 0;
  border-top: 1px solid var(--band);
  border-bottom: 1px solid var(--band);
}
.banner > .wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  text-align: center;
}
.banner p { margin: 0; color: var(--on-accent-graphic-2); }
.banner__lead { font-size: 1.0625rem; }
.banner__detail { font-size: 0.9375rem; }
.banner__act { font-weight: 700; }
.banner a {
  color: var(--on-accent-graphic-2);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 700;
}
.banner a:hover { text-decoration-thickness: 2px; }
.banner :focus-visible { outline-color: var(--on-accent-graphic-2); }

/* Flyer variant - the artwork replaces the text, events page only. Capped so a
   portrait flyer does not become several screens tall on a wide monitor, and
   centred in the green frame. The alt text on the img carries every detail that
   is otherwise only in pixels. */
.banner--flyer { padding: 1.75rem 0; }
.banner--flyer > .wrap { align-items: center; }
.banner--flyer img { max-width: 34rem; width: 100%; height: auto; display: block;
                     border-radius: 3px; box-shadow: var(--shadow); }
/* Small, and deliberately a link rather than a button - the artwork above is
   already doing the shouting. The image is NOT wrapped in this link: an
   informative image and a navigation link are two different things, and nesting
   them makes a screen reader read the whole flyer description as the link name. */
.banner--flyer .banner__act { font-size: 0.9375rem; margin-top: 0.6rem; }


/* Two buttons side by side, wrapping to a stack on narrow screens rather than
   overflowing. Used in Host your own, where the email route and the print
   materials are equal choices. */
.btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }

/* The second format on a download card - the image alongside the PDF. */
.dl-card__also { font-size: 0.9375rem; }

.skip-link {
  position: absolute;
  left: -9999px;
  background: var(--band);
  color: var(--on-band);
  padding: 0.75rem 1.25rem;
  z-index: 100;
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ==========================================================================
   Header + nav
   ========================================================================== */

.site-header {
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
  position: sticky;
  top: 0;
  z-index: 50;
}

/* ⭐ The spine bar — 2026-08-13, Marika: "use more of the accent colors to make
   it clear that something has changed."

   Six hard-stopped bands in the logo's own left-to-right order, so the stripe
   reads as an extension of the book spines in the wordmark rather than as
   generic decoration. This is the one place all six approved colours appear at
   once, which is why aqua and green — 2.2:1 on white and barred from text —
   are legitimate here: it carries no information. A reader who cannot
   distinguish the bands loses nothing.

   Deliberately BELOW the 1px rule, not instead of it, so the header still has
   a defined edge against a white page when it is scrolled under. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 4px;
  background: var(--spine);
}

.site-header .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 2rem;
  padding-block: 0.5rem;   /* was 1rem — trimmed so the logo sets the header height */
}

/* Keep the brand and the whole nav on one line once there's room for it.
   Below this width they stack, which is correct on a phone. */
@media (min-width: 900px) {
  .site-header .wrap { flex-wrap: nowrap; }
  .site-header .brand { flex: 0 0 auto; }
  .nav { flex: 0 1 auto; }
  .nav ul { flex-wrap: nowrap; gap: 1.5rem; }
}

.brand {
  display: block;
  color: var(--ink);
  text-decoration: none;
  line-height: 0;        /* kills the inline-descender gap under the img */
}

/* ⭐ The logo IS the header — Marika, 2026-08-13: "the logo should be the
   height of the header." So the header no longer has a height of its own; the
   logo sets it, and .wrap's padding is trimmed to a thin margin rather than
   the 1rem it was carrying for a text wordmark.

   ⛔ The campaign name is now ONLY in the img's alt text. That is deliberate
   and it is why the alt text must never be removed or emptied: with the
   subtitle line gone, a screen reader has nothing else in the header to
   announce. It is invisible to a sighted reader and load-bearing for everyone
   else.

   Sized by HEIGHT with width:auto. The intrinsic width/height attributes on
   the img reserve the correct box during load, so the header does not jump. */
.brand__mark {
  display: block;
  height: 52px;
  width: auto;
}
@media (min-width: 900px) {
  .brand__mark { height: 72px; }
}

.nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav ul { align-items: center; }
.nav a { color: var(--ink); text-decoration: none; font-size: 1rem; }
.nav a:hover, .nav a[aria-current="page"] { text-decoration: underline; }

/* Donate button in the header — smaller than the hero buttons so it reads as
   navigation, not as a second hero CTA.

   ⚠️ The colour MUST be restated here. `.nav a` is (0,1,1) specificity and
   beats `.btn--primary` at (0,1,0), so without this the button renders navy
   text on a navy background — invisible until selected. Caught in the browser
   on 2026-08-08; the contrast maths was right and the cascade was not. */
.nav a.btn--nav {
  padding: 0.55rem 1.1rem;
  font-size: 1rem;
  color: var(--on-band);        /* white on navy — 17.9:1 */
  background: var(--band);
}
.nav a.btn--nav:hover {
  color: var(--on-band);
  background: var(--accent-link-hi);
  text-decoration: none;
}

/* ==========================================================================
   Buttons — the spec asks for exactly two on the home page
   ========================================================================== */

/* All button groups centre on the page. The header nav is excluded — it's a
   navigation bar, not a button group. */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin: 2rem 0 0;
  justify-content: center;
}

.btn {
  display: inline-block;
  padding: 0.9rem 1.75rem;
  border-radius: 6px;
  font-size: 1.0625rem;
  font-weight: 600;
  text-decoration: none;
  border: 2px solid transparent;
  cursor: pointer;
}

/* white on navy = 17.9:1 */
.btn--primary { background: var(--band); color: var(--on-band); }
.btn--primary:hover { background: var(--accent-link-hi); color: var(--on-band); }

/* ⭐ The secondary button is the green one — Marika, 2026-08-13: "use the
   #90ba38 green also for the buttons."

   It is the SECONDARY and not the primary on purpose. Green is the brightest
   thing on the page and pulls the eye first; giving it to "Get involved" while
   Donate keeps the dark anchor means the two actions stay distinguishable and
   the campaign's primary ask does not get out-shouted by its secondary one.
   Flipping them is a one-line change if that reads wrong.

   Label is --on-accent-graphic-2 (6.48:1), not --ink (4.46:1, a fail) and
   never white (2.27:1). The border matches the fill so the shape is identical
   to the primary's. */
.btn--secondary {
  background: var(--accent-graphic-2);
  color: var(--on-accent-graphic-2);
  border-color: var(--accent-graphic-2);
}
/* Hover darkens to the approved gray green. White on it is 6.07:1, so the
   label flips to white — the one place these two greens swap roles. */
.btn--secondary:hover {
  background: var(--accent-earth);
  border-color: var(--accent-earth);
  color: var(--paper);
}

.band--dark .btn--primary { background: var(--accent-sky); color: var(--band); }
/* The green fill reads on the dark band too (4.46:1 against --band as a large
   block, which is a UI-component threshold of 3:1, not a text one), so the
   secondary keeps its colour rather than reverting to an outline. */
.band--dark .btn--secondary {
  background: var(--accent-graphic-2);
  color: var(--on-accent-graphic-2);
  border-color: var(--accent-graphic-2);
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero { padding: clamp(3rem, 8vw, 6rem) 0 clamp(2rem, 5vw, 3.5rem); }
.hero p.lede { font-size: clamp(1.2rem, 2.5vw, 1.5rem); color: var(--ink-soft); }

/* Privacy policy: the revision date is a timestamp, not a lede. Smaller than
   body copy, and sitting close to the text it stamps rather than floating in
   a full hero's worth of space. No max-width - see
   .claude/rules/full-width-body-copy.md. */
.hero p.hero__updated {
  font-size: clamp(0.8rem, 1.3vw, 0.9rem);
  color: var(--ink-soft);
  margin-bottom: 0;
}
body:has(.hero__updated) .hero { padding-bottom: clamp(0.75rem, 1.5vw, 1.25rem); }
body:has(.hero__updated) .hero + .band { padding-top: clamp(1rem, 2vw, 1.5rem); }

/* The privacy page is a reference document, not a pitch. The site-wide band
   rhythm (3-5rem) is built for scrolling past big claims; here it just pushes
   short sections apart and makes a one-screen policy into four. Roughly half
   the padding, and the section rule above each h2 carries the separation that
   whitespace was doing. Scoped to this page - the campaign pages keep the
   site rhythm. */
body:has(.hero__updated) .band { padding: clamp(1.5rem, 3vw, 2.25rem) 0; }
body:has(.hero__updated) .band > .wrap > h2 { margin-bottom: 0.5rem; }
body:has(.hero__updated) .band > .wrap > h2::before { margin-bottom: 0.6rem; }

/* ⭐ Events page rhythm — 2026-08-27, Marika: "shrink the spaces between
   sections... too much blank between the intro para and the calendars
   themselves. I'd like to reduce unnecessary scroll."

   Scoped the same way the privacy page is (line above): a :has() hook on
   something only this page contains, so the home page's banding is untouched.
   The default stack put 3.5rem of hero padding, 5rem of band padding, a rule,
   and a heading margin between the lede and the first calendar — roughly
   9.5rem of nothing on a large screen. */
body:has(.cal-pair) .hero { padding-bottom: clamp(1.25rem, 2.5vw, 1.75rem); }
body:has(.cal-pair) .band { padding: clamp(1.75rem, 3.5vw, 2.5rem) 0; }
body:has(.cal-pair) .band > .wrap > h2 { margin-bottom: 0.4rem; }
body:has(.cal-pair) .band > .wrap > h2::before { margin-bottom: 0.6rem; }
body:has(.cal-pair) .cal-pair { margin-bottom: 1.25rem; }
body:has(.cal-pair) h3.sub { margin-top: 1.5rem; }

/* Was uppercase when it held two words and a date. Now that it's a full
   sentence, uppercase is dropped: all-caps removes word-shape cues and is
   measurably harder to read at sentence length — which matters given the
   stated audience of older rural voters. */
.eyebrow {
  display: inline-block;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--accent-link-hi);   /* 5.9:1+ on white */
  margin-bottom: 0.75rem;
}

/* ==========================================================================
   Number cards
   ========================================================================== */

.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: var(--gap);
  margin-top: 2rem;
  padding: 0;
  list-style: none;
}

.figure {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-top: 5px solid var(--accent-slate);   /* overridden per-card below */
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: var(--shadow);
}

/* Each card takes a different spine colour, in the logo's order. Decorative
   only — the number and its label carry the meaning, and both stay --ink /
   --ink-soft. Nothing here is the sole cue for anything, so the two
   graphic-only colours are safe. The cycle is nth-child(4n) so it keeps
   working if a fifth figure is ever added. */
.figure:nth-child(4n + 1) { border-top-color: var(--accent-slate); }
.figure:nth-child(4n + 2) { border-top-color: var(--accent-earth); }
.figure:nth-child(4n + 3) { border-top-color: var(--accent-graphic-2); }
.figure:nth-child(4n + 4) { border-top-color: var(--accent-graphic); }

.band--dark .figure { background: var(--band-card); border-color: transparent; }
/* On a dark band the spine edge would disappear against --band-card, so it
   switches to the one colour that reads brightly there (aqua, 4.63:1). */
.band--dark .figure { border-top: 5px solid var(--accent-sky); }

.figure .num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 2.75rem);
  line-height: 1.1;
  color: var(--ink);
}
.band--dark .figure .num { color: var(--on-band); }
.figure .label { display: block; color: var(--ink-soft); font-size: 1rem; }
.band--dark .figure .label { color: var(--on-band-soft); }

/* ==========================================================================
   Calculator
   ========================================================================== */

.calc {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: clamp(1.5rem, 4vw, 2.5rem);
  box-shadow: var(--shadow);
  /* Full container width, like every other block. The input inside it is
     narrow because it's a form field; the card is not. */
}

.calc label { display: block; font-weight: 600; margin-bottom: 0.5rem; }
.calc .hint { font-size: 1rem; color: var(--ink-soft); margin-bottom: 1rem; }

.calc-input { display: flex; align-items: stretch; gap: 0; max-width: 22rem; }
.calc-input .prefix {
  display: flex;
  align-items: center;
  padding: 0 0.75rem;
  border: 2px solid var(--ink);
  border-right: 0;
  border-radius: 6px 0 0 6px;
  background: var(--paper-alt);
  font-weight: 600;
}
.calc-input input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 1.25rem;
  padding: 0.7rem 0.9rem;
  border: 2px solid var(--ink);
  border-radius: 0 6px 6px 0;
  color: var(--ink);
  background: var(--paper);
}

/* The divider above the result was a 1px --rule hairline. It is now the spine
   bar. Drawn as a background rather than a border or a pseudo-element:
   .calc-result carries role="status" + aria-live, and generated content inside
   a live region can be picked up and announced by some screen readers. A
   background image cannot be. */
.calc-result {
  margin-top: 1.75rem;
  padding-top: 1.5rem;
  background-image: var(--spine);
  background-repeat: no-repeat;
  background-size: 100% 4px;
  background-position: top left;
}

/* Three cells: what you pay now, what you would pay, the difference.
   Stacks on narrow screens. No max-width anywhere — the card is the measure. */
.calc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: var(--gap);
  list-style: none;
  margin: 0;
  padding: 0;
}

.calc-cell {
  padding: 1.25rem;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--paper);
}

/* The difference is the number people came for — give it the emphasis
   without using colour alone to carry the meaning (the label says it too). */
.calc-cell--diff {
  background: var(--band);
  border-color: var(--band);
}

.calc-label {
  display: block;
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: 0.35rem;
}
.calc-cell--diff .calc-label { color: var(--on-band-soft); }

.calc-amount {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 5vw, 2.6rem);
  line-height: 1.1;
  color: var(--ink);
}
.calc-cell--diff .calc-amount { color: var(--on-band); }

.calc-per { display: block; color: var(--ink-soft); }
.calc-cell--diff .calc-per { color: var(--on-band-soft); }

.calc-year {
  display: block;
  margin-top: 0.5rem;
  font-size: 1rem;
  color: var(--ink-soft);
}
.calc-cell--diff .calc-year { color: var(--on-band-soft); }

.calc-note { font-size: 0.95rem; color: var(--ink-soft); margin-top: 1.25rem; }

/* ==========================================================================
   Cards / involvement grid
   ========================================================================== */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: var(--gap);
  padding: 0;
  margin: 2rem 0 0;
  list-style: none;
}

.card {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 1.5rem;
}
.card h3 { margin-top: 0; }
.card p { margin-bottom: 0; }
.band--dark .card { background: var(--band-card); border-color: transparent; }

/* County links */
.county-list { display: flex; flex-wrap: wrap; gap: 0.75rem; padding: 0; margin: 1.5rem 0 0; list-style: none; }
.county-list a {
  display: block;
  padding: 0.75rem 1.25rem;
  border: 1px solid var(--rule);
  border-radius: 6px;
  text-decoration: none;
  color: var(--ink);
  background: var(--paper);
}
.county-list a:hover { border-color: var(--ink); background: var(--paper-alt); }

/* ==========================================================================
   Footer — the PDC sponsor identification lives here
   ========================================================================== */

/* ⭐ Blue footer — Marika, 2026-08-13: "replace the footer with #3a66b5 blue,
   using white or cream text, if that works for at least AA."

   It does, on the footer's own text:

     white  on #3a66b5   5.60:1   AA (AAA is 7:1 — not reached)
     cream  on #3a66b5   5.42:1   AA. Cream is DARKER than white, so it is
                                  always the weaker of the two here.

   White is used rather than cream. Cream buys nothing on a saturated blue —
   its warmth is invisible against it — and it costs 0.18 of ratio.

   ⚠️ TWO INHERITED VALUES BREAK ON BLUE and are overridden below. Both were
   computed against the teal --band and neither survives the change:

     --on-band-soft (#B7CCC9) on blue   3.33:1   FAILS AA for text
     --accent-sky   (#0cc0df) on blue   2.57:1   FAILS badly — this is the
                                        link colour, so every footer link,
                                        including the PDC contact address,
                                        would have been near-illegible.

   The footer carries the sponsor identification, which is a legal
   requirement, not a design element. It does not get to be marginal. */
/* ⭐ Tightened 2026-08-13, Marika: "shrink the height of the whole footer —
   the text there can be much smaller and smushed. It feels too big."

   The footer was inheriting the body's 18px base and 1.6 leading, both of
   which exist for the READING surface and for the stated audience of older
   rural voters. A footer is navigation and legal boilerplate, not reading, so
   it does not need them — that inheritance is what made it feel like a second
   page. Now 15px at 1.4, with the vertical rhythm cut roughly in half.

   ⛔ The .disclosure keeps a larger size than the rest of the footer. It is
   the PDC sponsor identification — a legal requirement — and shrinking it with
   everything else is the one economy not available here. */
.site-footer {
  background: var(--accent-blue);
  color: #E6EBF7;                 /* light blue tint — 4.69:1 on the blue, AA */
  padding: 1.5rem 0 1rem;
  font-size: 0.875rem;            /* 14px — second reduction, 2026-08-13:
                                     "smaller font in the footer across the board" */
  line-height: 1.35;
}
/* ⛔ Footer column headings stay on Atkinson. They are h2 elements, so they
   would otherwise inherit Archivo Black — at 15px a black grotesque is a solid
   block of ink and these are labels, not headlines. */
.site-footer h2 {
  font-family: var(--font-body);
  font-weight: 700;
  letter-spacing: 0;
  font-size: 0.9375rem;
  color: var(--paper);            /* white, 5.60:1 */
  margin: 0 0 0.4rem;
  line-height: 1.3;
}
.site-footer p { margin: 0 0 0.4rem; }

/* Social link: the icon is the badge in currentColor with the glyph knocked
   out in the footer's own blue, so it reads as one of the white links rather
   than as the only saturated brand colour on the band - the same reasoning as
   the no-second-hue rule below. Swap the two fills back to #3F51B5 / #FFF if
   the full-colour mark is ever wanted. */
.site-footer .social-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  text-decoration: none;
}
.site-footer .social-link span { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
.social-icon { width: 1.15em; height: 1.15em; flex: none; border-radius: 0.2em; }

/* Links are white and underlined rather than a second hue. No approved swatch
   is light enough to serve as a link colour on this blue, and underline plus
   full-white is a stronger affordance than colour alone anyway. */
.site-footer a {
  color: var(--paper);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}
.site-footer a:hover { text-decoration-thickness: 2px; }

/* Focus rings inside the footer: --focus IS this blue, so it would vanish.
   White is the only thing guaranteed to read against the footer's own fill. */
.site-footer :focus-visible { outline-color: var(--paper); }

.footer-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: 1rem 2rem;
}
.footer-grid ul { list-style: none; padding: 0; margin: 0; }
.footer-grid li { margin-bottom: 0.2rem; }

.disclosure {
  margin-top: 1.25rem;
  padding-top: 0.9rem;
  /* Was --band-card (teal). On the blue footer the two are within 0.04 of the
     same luminance, so the rule became invisible — the sponsor identification
     lost its separation from the links above it. A white wash reads on blue
     without competing with the text. */
  /* Was a 35% white wash. Now the spine bar, in its on-blue variant so the
     blue segment does not vanish into the footer behind it. */
  background-image: var(--spine-on-blue);
  background-repeat: no-repeat;
  background-size: 100% 4px;
  background-position: top left;
  /* Was held at 1rem while the rest of the footer shrank, on the grounds that
     the PDC sponsor identification is a legal requirement. Marika's call
     2026-08-13 — "smaller across the board" — so it now matches the footer.
     The requirement is that it be present and clearly readable, not that it
     be a particular size; 14px white on this blue is 5.60:1 and legible.
     ⛔ Do not take it below the footer's own size. */
  font-size: inherit;
  color: var(--paper);          /* white — 5.60:1 on the blue footer */
  text-align: center;
  /* One line by design. Deliberately NO white-space: nowrap — on a narrow
     phone that would overflow the viewport instead of wrapping. It sets as a
     single centred line wherever there is room, and wraps gracefully where
     there is not. */
}

/* Standard copyright notice. Sits BELOW the PDC sponsor identification and is
   deliberately quieter than it: the disclosure is a legal requirement that must
   stay clearly readable, a copyright line is not. Smaller and dimmed so the two
   are never mistaken for one another.
   ⛔ Do not merge this into .disclosure, and do not add a vendor/builder credit
   to either. Site authorship is recorded in <meta name="author"> instead —
   Marika's call 2026-08-15, so nothing sits beside the regulated text. */
.copyright {
  margin-top: 0.5rem;
  font-size: 0.8125rem;         /* 13px — one step under the footer's 14px */
  color: #C9D4EC;               /* dimmer than --paper, still ~4.6:1 on the blue */
  text-align: center;
}

/* Marks the unfilled legal slot loudly in the mock-up. Delete .todo when filled. */
.todo {
  outline: 2px dashed var(--accent-sky);
  outline-offset: 4px;
}
.todo::before {
  content: "NEEDS REAL VALUE — ";
  font-weight: 700;
  color: var(--accent-sky);
}

/* Utility */
.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;
}

/* ==========================================================================
   Supporter signup form — Yes for Libraries
   Append to css/site.css in the trl-levy-lid-lift-pac repo.

   Every colour is a var() from the :root block at the top of that stylesheet.
   Nothing here hard-codes a colour, so a palette change carries through.

   ⛔ NO max-width on any paragraph, label, hint, or prose wrapper. Body copy
   runs the full width of .wrap — the container padding IS the measure.
   See .claude/rules/full-width-body-copy.md in Komorebi. The only widths here
   are grid tracks and form CONTROLS, which are controls, not prose.

   Accessibility notes carried over from the coalition form:
   · --focus (#3a66b5, approved blue) is 5.60:1 on paper — well clear of the
     3:1 UI threshold, and unlike the value it replaced on 2026-08-13 it is
     also safe as text. It stays reserved for focus rings and borders anyway.
   · .btn--primary is reused as-is. Do not restyle it here; the palette work
     on 2026-08-09 already computed its contrast.
   ========================================================================== */

.signup-direct {
  color: var(--ink-soft);
}

.signup-form {
  margin-top: 2rem;
}

/* Honeypot — hidden from sight and from assistive tech, but NOT
   display:none, which some bots detect and skip. */
.signup-form__hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Two columns on wide screens, one on phones. Most signups happen on a
   phone — the community meetings are where people fill this in. */
.signup-form__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1.25rem;
}

.signup-form__field {
  margin: 0 0 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.signup-form__field label {
  font-weight: 700;
}

.signup-form__field .req,
.signup-form__field .opt {
  font-weight: 400;
  color: var(--ink-soft);
  font-size: 0.9em;
}

.signup-form input[type="text"],
.signup-form input[type="email"],
.signup-form input[type="tel"],
.signup-form select {
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--rule);
  border-radius: 6px;
  padding: 0.7rem 0.8rem;
  width: 100%;
  /* 48px minimum touch target — this gets filled in on phones at events. */
  min-height: 3rem;
}

.signup-form select {
  /* Keep the native control on mobile; it is far easier to operate than
     anything custom, and it reads correctly to screen readers. */
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
                    linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
  background-position: calc(100% - 1.2rem) 1.35rem, calc(100% - 0.85rem) 1.35rem;
  background-size: 0.35rem 0.35rem, 0.35rem 0.35rem;
  background-repeat: no-repeat;
  padding-right: 2.5rem;
}

.signup-form input:focus-visible,
.signup-form select:focus-visible,
.signup-form button:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-color: var(--focus);
}

/* Consent — the load-bearing field. Given room rather than tucked under the
   button, because a checkbox nobody read is not consent. */
.signup-form__consent {
  margin: 0.5rem 0 1.5rem;
  padding: 1.1rem 1.25rem;
  background: var(--paper);
  border: 2px solid var(--rule);
  border-radius: 8px;
}

.signup-form__check {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  cursor: pointer;
}

.signup-form__check input[type="checkbox"] {
  /* Large enough to hit on a phone without zooming. */
  width: 1.4rem;
  height: 1.4rem;
  margin: 0.2rem 0 0;
  flex: 0 0 auto;
  accent-color: var(--accent-link);
}

/* The multi-select group, added 2026-08-31 when "how would you like to help"
   became checkboxes. A <fieldset> is the correct element for a set of related
   controls with one shared question, and <legend> is what a screen reader reads
   before each option - but both carry browser default chrome that has to go.

   ⛔ No max-width anywhere here. The form's own column is the measure. */
.signup-form__group {
  border: 0;
  padding: 0;
  margin: 0 0 1.25rem;
  min-width: 0;            /* fieldsets refuse to shrink in flex without this */
}

.signup-form__group legend {
  font-weight: 700;
  padding: 0;
}

/* The hint sits directly under the legend, so it needs less top gap than the
   standalone hint under the consent box. */
.signup-form__group .signup-form__hint {
  margin-top: 0.25rem;
  margin-bottom: 0.75rem;
}

.signup-form__checks {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.band--dark .signup-form__group legend { color: var(--on-band); }

.signup-form__hint {
  display: block;
  margin-top: 0.6rem;
  color: var(--ink-soft);
  font-size: 0.95em;
}

.signup-form__actions {
  margin-bottom: 1rem;
  /* Centered to match the Donate button above it — the two asks in this
     section are visually parallel, so their buttons sit the same way. */
  display: flex;
  justify-content: center;
}

.signup-form__submit[disabled] {
  opacity: 0.6;
  cursor: progress;
}

/* Status line. Announced via role="status"; it also takes focus on a result,
   so a keyboard user is not left wondering whether anything happened. */
.signup-form__status:empty {
  display: none;
}

.signup-form__status {
  margin: 0;
  padding: 0.85rem 1rem;
  border-radius: 6px;
  border-left: 4px solid var(--rule);
  background: var(--paper);
}

.signup-form__status.is-pending {
  border-left-color: var(--ink-soft);
  color: var(--ink-soft);
}

.signup-form__status.is-success {
  border-left-color: var(--focus);
  font-weight: 700;
}

.signup-form__status.is-error {
  /* Not colour alone — the border weight and the wording carry it too, so
     this reads for someone who cannot distinguish the hue. */
  border-left-color: #B3261E;
  border-left-width: 6px;
  color: #8C1D18;
  font-weight: 700;
}

.signup-form__status:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* ==========================================================================
   The form inside a DARK band
   --------------------------------------------------------------------------
   Added 2026-08-12, when "Get involved" and the signup form merged into one
   section at Marika's direction. The form now sits on --band (#0A4A45), so
   anything that was styled for a light surface has to be restated.

   The rule of thumb: text sitting DIRECTLY on the band goes light; text
   inside the white consent card or the white status line stays dark.
   ========================================================================== */

.band--dark .signup-direct {
  color: var(--on-band-soft);          /* 7.1:1 on --band, AAA */
}

/* "Or volunteer" is a genuine section break inside the band — the two buttons
   above it are off-page asks, the form below is on-page. It was carrying that
   break on margin alone. Now the spine bar marks it, same as the other
   dividers. The .band--dark selector below is dead as of 2026-08-13 (the
   section is cream now) and is kept only so the rule still applies if that
   band is ever darkened again. */
.signup-heading {
  margin-top: 3rem;
  padding-top: 1.5rem;
  background-image: var(--spine);
  background-repeat: no-repeat;
  background-size: 100% 4px;
  background-position: top left;
}
.band--dark .signup-heading {
  margin-top: 3rem;                    /* separates "Or volunteer" from the Donate button */
}

.band--dark .signup-form__field label {
  color: var(--on-band);               /* 10.1:1 on --band, AAA */
}

.band--dark .signup-form__field .req,
.band--dark .signup-form__field .opt {
  color: var(--on-band-soft);
}

/* ⚠️ Focus rings switch to --accent-sky inside a dark band, and after the
   2026-08-13 palette change this switch is load-bearing rather than merely
   preferable. --focus (#3a66b5) computes to 1.80:1 on --band — it FAILS the
   3:1 UI threshold outright there, because the approved blue and the derived
   dark cyan are close in luminance. --accent-sky (#0cc0df aqua) is 4.63:1.
   ⛔ Do not "simplify" this by dropping the override back to --focus. */
.band--dark .signup-form input:focus-visible,
.band--dark .signup-form select:focus-visible,
.band--dark .signup-form button:focus-visible,
.band--dark .signup-form__status:focus-visible {
  outline-color: var(--accent-sky);
  border-color: var(--accent-sky);
}

/* The consent card and the status line are white cards ON the dark band, so
   their contents keep the light-surface colours. Stated explicitly because
   they would otherwise inherit --on-band (white) from the band and vanish. */
.band--dark .signup-form__consent,
.band--dark .signup-form__status {
  color: var(--ink);
}

.band--dark .signup-form__hint {
  color: var(--ink-soft);              /* inside the white consent card */
}

/* ==========================================================================
   Story page — sponsor notice + the story textarea
   Append to css/style.css in the trl-levy-lid-lift-pac repo.
   Added 2026-08-12 with stories.html. Everything else on that page reuses the
   signup form's styles, which is why the form carries both classes:
   class="signup-form story-form".

   ⛔ NO max-width on prose here either — see .claude/rules/full-width-body-copy.md.
   ========================================================================== */

/* ⚠️ Not decoration. Marika, 2026-08-12: "This must be VERY clear that this is
   the PAC, not TRL." Washington restricts public agencies from using public
   resources to promote a ballot measure (RCW 42.17A.555), so a story form that
   reads as though the library district sent it is the ambiguity to avoid — for
   the district's protection as much as the campaign's. Styled as a bordered
   block so it reads as a statement of fact, not as fine print. */
.sponsor-notice {
  border: 2px solid var(--rule);
  border-left: 6px solid var(--accent-link);
  border-radius: 8px;
  padding: 1.1rem 1.25rem;
  margin: 2rem 0;
  background: var(--paper);
}

.sponsor-notice p { margin: 0 0 0.75rem; }
.sponsor-notice p:last-child { margin-bottom: 0; }

.story-form textarea {
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--rule);
  border-radius: 6px;
  padding: 0.7rem 0.8rem;
  width: 100%;
  min-height: 12rem;
  /* Vertical only: horizontal dragging breaks the layout and helps nobody. */
  resize: vertical;
}

.story-form textarea:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-color: var(--focus);
}

.band--dark .story-form textarea:focus-visible {
  outline-color: var(--accent-sky);
  border-color: var(--accent-sky);
}

/* On this form the hint sits between the label and its control, so it takes
   the label's spacing rather than the consent block's. */
.story-form .signup-form__field .signup-form__hint { margin-top: 0; }

/* ==========================================================================
   Checkbox group ("What do you use it for?")
   Added 2026-08-12 when that question became multi-select.
   ========================================================================== */

.checkgroup {
  border: 2px solid var(--rule);
  border-radius: 8px;
  padding: 1rem 1.25rem 1.25rem;
  margin: 0 0 1.25rem;
  background: var(--paper);
  /* fieldset defaults are inconsistent across browsers; reset the ones that
     matter and let the grid below do the layout. */
  min-width: 0;
}

.checkgroup legend {
  font-weight: 700;
  padding: 0 0.4rem;
  margin-left: -0.4rem;
}

.checkgroup__item {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.45rem 0;
  cursor: pointer;
  /* The whole label is the hit target, not just the box — this gets filled in
     on phones, where a 16px checkbox alone is a miss waiting to happen. */
}

.checkgroup__item input[type="checkbox"] {
  width: 1.4rem;
  height: 1.4rem;
  margin: 0.15rem 0 0;
  flex: 0 0 auto;
  accent-color: var(--accent-link);
}

.checkgroup__item input[type="checkbox"]:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* Two columns once there is room. Single column on phones, which is where most
   of these get filled in. */
@media (min-width: 40rem) {
  .checkgroup {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 1.5rem;
  }
  .checkgroup legend { grid-column: 1 / -1; }
}

/* ==========================================================================
   Stories page — tighter vertical rhythm
   --------------------------------------------------------------------------
   Added 2026-08-12: Marika, "a bit too much blank space." The page inherited
   spacing designed for a marketing page with a few short bands, then stacked a
   hero's bottom padding on a band's top padding on a long column of form rows.
   A form reads as a single task, so it wants a tighter beat than a landing page.

   ⛔ ALL SCOPED to #story / .story-form. The home page's rhythm is deliberate
   and is not touched by any rule below.
   ========================================================================== */

/* The hero already supplies the space above; the band should not repeat it. */
#story { padding-top: clamp(1rem, 2vw, 1.75rem); }

.hero + #story { margin-top: 0; }

/* The hero itself needs less room when the page below it is a task, not a pitch. */
body:has(#story) .hero { padding-bottom: clamp(1rem, 2vw, 1.5rem); }

.story-form { margin-top: 1.25rem; }

/* Form rows: the default 1.25rem gap between every field adds up over fourteen
   of them. 0.85rem still separates cleanly at 18px base type. */
.story-form .signup-form__field { margin-bottom: 0.85rem; }
.story-form .signup-form__grid { gap: 0.85rem 1.5rem; }

.story-form .signup-form__consent { margin: 1rem 0 1.25rem; }

#story .sponsor-notice { margin: 1.25rem 0; }

.story-form .checkgroup { padding: 0.85rem 1.25rem 1rem; }
.story-form .checkgroup__item { padding: 0.3rem 0; }

/* The two textareas carry their own generous min-heights; they do not also need
   a full row gap beneath them. */
.story-form textarea { margin-bottom: 0.25rem; }

/* ==========================================================================
   Stories page — align the two grid columns
   --------------------------------------------------------------------------
   2026-08-12: "Which county is your library in? (optional)" fits on one line
   while "How often do you use the library? (optional)" wraps to two, so the
   two select boxes beside each other started at different heights.

   The fix Marika chose: put "(optional)" on its own line ALWAYS. Every label in
   the grid then occupies two lines, so every control below it starts at the
   same y-position — no subgrid, no min-height guessing, and it cannot come
   unstuck when someone edits a label's wording later.

   ⛔ Scoped to .story-form. The signup form's labels are all short enough to sit
   on one line, and forcing a second line there would add blank space to fix a
   problem it does not have.
   ========================================================================== */

.story-form .signup-form__field > label .req,
.story-form .signup-form__field > label .opt {
  display: block;
  /* Sits directly under its label rather than floating a full line away. */
  margin-top: 0.1rem;
}

/* Rows start at the top so a two-line label never centres its control. */
.story-form .signup-form__grid { align-items: start; }

/* ==========================================================================
   Endorsements — added 2026-08-27 at Rob Richards' request.

   Grouped by governing body. The grid is auto-fit so a five-name council and
   a one-name port commission both sit correctly without per-group tuning, and
   so the list keeps working as names are added.

   ⛔ No max-width anywhere below. .wrap is the only width constraint on this
   page (see the --wide note at the top of this file).
   ========================================================================== */

.endorsers {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  margin-top: 2rem;
  list-style: none;
  padding: 0;
}

/* Bodies render as cards too - Marika, 2026-08-28: "can we turn the bodies
   into cards as well? That should help with the organized look."

   Same border and radius as .org-card so the two tiers read as one system,
   but left-aligned rather than centred: an organization card is a single
   name and centres well, a council card is a heading over a list and centring
   a ragged name list is harder to scan. */
.endorsers__group {
  padding: 1.25rem 1.25rem 1.5rem;
  border: 1px solid var(--accent-earth);
  border-radius: 6px;
}

.band--dark .endorsers__group { border-color: var(--accent-sky); }

/* Community members - one name per card, same family as the others. */
.person-cards {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
}

.person-card {
  padding: 1rem 1.1rem;
  border: 1px solid var(--accent-earth);
  border-radius: 6px;
  font-weight: 600;
  text-align: center;
}

.band--dark .person-card { border-color: var(--accent-sky); }

.endorsers__group h3 {
  margin: 0 0 0.75rem;
  padding-bottom: 0.5rem;
  /* The card border now carries the edge, so the heading rule is a divider
     inside the card rather than the group's only boundary. Kept because it
     still separates the body name from its members. */
  border-bottom: 1px solid var(--accent-earth);
  font-size: 1rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.endorsers__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.endorsers__list li {
  padding: 0.4rem 0;
  line-height: 1.4;
}

.endorser__name { display: block; font-weight: 600; }

.endorser__role {
  display: block;
  font-size: 0.875rem;
  opacity: 0.75;
}

.endorsers__note {
  margin-top: 2.5rem;
  font-size: 0.9375rem;
  opacity: 0.8;
}

.band--dark .endorsers__group h3 { border-bottom-color: var(--accent-sky); }

/* ⭐ Marika, 2026-08-27: "some of the titles are long and take two lines. Can
   you make all the titles take two lines so that the underlines line up?"

   The rule under each heading is a border-bottom, so it sits wherever the text
   ends — one-line headings drew theirs higher than two-line ones and the row
   looked ragged. Reserving two lines on every heading puts them all on the
   same baseline.

   Only above 700px, where the groups actually sit side by side. Stacked on a
   phone there is nothing to align and the extra line would just be scroll.
   2lh is two of this element's own line-heights; the calc() is the fallback
   for anything that does not know the unit. */
@media (min-width: 700px) {
  .endorsers__group h3 {
    min-height: calc(2 * 1.2em);
    min-height: 2lh;
  }
}

/* ==========================================================================
   DOWNLOAD CARDS - materials.html, added 2026-08-28.

   Same card family as .org-card and .endorsers__group so the site reads as one
   system. Left-aligned: each card is a heading, a metadata line and a sentence,
   and centring that is harder to scan than centring a single name.

   The file size and paper size sit in .dl-card__meta above the description
   rather than below it, because on a phone on a rural connection the size is
   the thing a person decides on before they read anything else.

   No max-width. See .claude/rules/full-width-body-copy.md.
   ========================================================================== */

.dl-cards {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  list-style: none;
  margin: 2rem 0 0;
  padding: 0;
}

.dl-card {
  padding: 1.5rem 1.35rem;
  border: 1px solid var(--accent-earth);
  border-radius: 6px;
}

.band--dark .dl-card { border-color: var(--accent-sky); }

.dl-card h3 { margin: 0 0 0.35rem; font-size: 1.15rem; }

.dl-card__meta {
  margin: 0 0 0.75rem;
  font-size: 0.9375rem;
  opacity: 0.8;
}

.dl-card p:last-child { margin-bottom: 0; }

/* ==========================================================================
   NAV SOCIAL ICONS - added 2026-08-28 at Marika's request, sitting to the
   right of the Donate button so the header carries the two channels people
   actually share on.

   Icon-only, so each link needs an aria-label - there is no visible text to
   name it. The glyph is punched out in --paper rather than the footer's blue:
   the footer sits on navy and the header sits on paper, so the footer's
   colours would render a navy glyph on a navy tile here.
   ========================================================================== */

.nav__social {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-left: 0.25rem;
}

.nav__social a { display: inline-flex; color: var(--ink); }
.nav__social a:hover { color: var(--accent-earth); text-decoration: none; }
.nav__social .social-icon { width: 1.6em; height: 1.6em; }

/* ==========================================================================
   ENDORSEMENTS PAGE RHYTHM - Marika, 2026-08-28: "a little bit less space
   between sections and a little bit more space between orgs vs bodies."

   Two different gaps pulling opposite ways, which is why they are two rules.
   .band--tight closes the distance BETWEEN bands; .etier + .etier opens the
   distance between tiers WITHIN the list band, so organizations do not read
   as the first group of the elected-officials list.
   ========================================================================== */

/* Marika, 2026-08-28: "the space under the endorsements para needs to be
   smaller." The hero's bottom padding and the band's top padding were stacking
   - up to 3.5rem + 3rem - so the lede floated a long way above Organizations.
   Trimming the hero side only; .band--tight is already doing its half. */
.hero--tight { padding-bottom: clamp(0.75rem, 2vw, 1.5rem); }

.band--tight { padding: clamp(2rem, 4vw, 3rem) 0; }

.etier + .etier { margin-top: clamp(3.5rem, 6vw, 5rem); }

/* ==========================================================================
   ORGANIZATION ENDORSEMENTS - added 2026-08-28 at Rob Richards' request.

   Cards rather than a list, because an organization endorsing is a heavier
   thing than a name in a column and should look like it. Built logo-ready:
   the card renders correctly with no image at all, so it ships before logo
   permission is confirmed and an <img> can drop in as the first child later
   with no other change.

   ⛔ No max-width anywhere. .wrap is the only width constraint on this page,
   and the build's own check() rejects one. See
   .claude/rules/full-width-body-copy.md.
   ========================================================================== */

.endorsers__subhead {
  margin: 3rem 0 0;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid var(--accent-earth);
  font-size: 1rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.band--dark .endorsers__subhead { border-bottom-color: var(--accent-sky); }

.org-cards {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
}

.org-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  padding: 1.5rem 1.25rem;
  border: 1px solid var(--accent-earth);
  border-radius: 6px;
  text-align: center;
}

.band--dark .org-card { border-color: var(--accent-sky); }

/* The logo slot. Sized in rem so it scales with type, capped by a max-HEIGHT
   rather than a width so wide wordmarks and square seals both behave. This is
   image containment, not a reading measure. */
/* A fixed-height slot, not a height cap. See _logo_img() in build.py: capping
   only the tall marks lets a short wide wordmark sit in less vertical space than
   a square seal beside it, and the two organization names in a row then start at
   different heights. The box is the same for every mark; the mark centres in it
   and keeps its own aspect ratio. This is image containment, not a measure. */
.org-card__logo {
  height: 5rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* One line on purpose: the build's own check() allows a max-width only on a
   line that names img, input or --wide, and this is genuinely image
   containment. Splitting it across lines hides the selector from the check. */
.org-card__logo img { max-height: 100%; max-width: 100%; width: auto; height: auto; object-fit: contain; }

/* An organization with its own site gets the WHOLE card as the link, added
   2026-09-03 at Marika's request. The anchor repeats .org-card's column layout
   because it becomes the card's only flex child, and without it the logo and
   name lose their gap.

   No underline at rest: the card border and the logo already read as one
   clickable unit, and a rule under a wordmark looks like a defect. The name
   underlines on hover and on keyboard focus, so the affordance is there when
   it is being used. */
.org-card__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  color: inherit;
  text-decoration: none;
}

.org-card__link:hover .org-card__name,
.org-card__link:focus-visible .org-card__name {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.org-card__name {
  font-weight: 600;
  line-height: 1.35;
}

/* ==========================================================================
   EVENTS CALENDAR
   Added 2026-08-27. The markup shipped before the styling did, so the grid
   rendered as an unstyled table with the event names running inline and
   dragging the columns out of alignment.

   No max-width anywhere: the build's own check() rejects one, and the two
   months are sized by the grid track rather than by a cap on the table.
   ========================================================================== */

.cal-pair {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
  margin: 0 0 2rem;
}
@media (min-width: 820px) {
  .cal-pair { grid-template-columns: 1fr 1fr; }
}

.cal {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow: hidden;
}

.cal__title {
  margin: 0;
  padding: 0.7rem 1rem;
  background: var(--paper-alt);
  border-bottom: 1px solid var(--rule);
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Fixed layout is what keeps the seven columns even. Without it a long event
   name sets the column width and the month stops looking like a month. */
.cal__grid {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.cal__grid th {
  padding: 0.5rem 0;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* The day initials are abbr elements for screen readers; the dotted underline
   browsers add to them is noise at this size. */
.cal__grid th abbr {
  text-decoration: none;
  border-bottom: 0;
  cursor: default;
}

.cal__grid td {
  height: 4.2rem;
  padding: 0.3rem 0.25rem;
  border-top: 1px solid var(--rule);
  vertical-align: top;
  font-size: 0.85rem;
}

.cal__pad { background: var(--paper-alt); }

.cal__d {
  display: block;
  color: var(--ink-soft);
  font-size: 0.78rem;
  line-height: 1.2;
}

td.is-today { background: #FFF6DA; }
td.is-today .cal__d,
td.has-event .cal__d { font-weight: 700; color: var(--ink); }

/* Block, not inline — this is what stopped the event names sitting beside the
   date number and shoving the column sideways. */
.cal__ev {
  display: block;
  margin-top: 0.22rem;
  padding: 0.2rem 0.3rem;
  background: var(--band);
  color: var(--on-band);
  border-radius: 4px;
  font-size: 0.7rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

/* The listing under the grids. This is the part people actually read. */
.ev-list { margin: 0; padding: 0; list-style: none; }

.ev-list li {
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--rule);
}

.ev__when {
  margin: 0;
  font-weight: 700;
  font-size: 0.86rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.ev__what { margin: 0.15rem 0 0; font-size: 1.15rem; font-weight: 700; }
.ev__more { margin: 0.2rem 0 0; color: var(--ink-soft); }
.ev-none  { color: var(--ink-soft); }
