/* ===========================================================
   David Poll for LASD 2026 — shared design system
   Aligned to the official campaign Brand Board.
   Civic Navy foundation + Long-Run Green secondary + Warm Gold
   vote accent on Cool Paper neutrals. Geometric-humanist sans
   display (ballot-legible), quiet system body.
   Palette, type, and usage = brand board source of truth.
   =========================================================== */

/* Inter — self-hosted display face (SIL OFL 1.1; see assets/fonts/inter/OFL.txt).
   The brand board calls for Söhne, but that face is commercial and was never
   served, so every visitor fell back to system fonts. Inter is a free grotesque
   that ships the intended display voice to everyone. */
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/inter/inter-400.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/inter/inter-500.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/inter/inter-600.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/inter/inter-700.woff2') format('woff2'); }

:root {
  /* Neutrals — Cool Paper = transparency, breathing room */
  --bg:       #f6f8fb;   /* Cool Paper */
  --surface:  #ffffff;
  --surface-2:#eef2f8;   /* cool paper tint */
  --fg:       #15233b;   /* Brand ink — body text */
  --muted:    #5a6b82;   /* Slate Muted */
  --muted-on-ink: #c9d3e2; /* Secondary text on navy fields */
  --muted-on-green: #edf5f0; /* Secondary text on green fields */
  --border:   #dce3ee;

  /* Civic navy primary · Long-Run green secondary · Warm gold accent */
  --ink:        #13294a; /* Civic Navy — dark fields, headlines, footer */
  --navy:       #1c3a63; /* Navy 600 — supporting navy */
  --accent:     #1c3a63; /* interactive navy — links, focus, ink buttons */
  --accent-ink: #13294a; /* navy hover */
  --blue:       #2f5fa3; /* link blue */
  --green:      #3c7a59; /* Long-Run Green — growth, roots, the long run */
  --green-ink:  #2f6147; /* green-deep — green text/emphasis on light */
  --green-bright:#7fb069;/* brightened sprout green — DARK backgrounds only */
  --gold:       #e3a53c; /* Warm Gold — rising sun, calls-to-vote, highlights */
  --gold-deep:  #c2842a; /* gold for larger text/labels on light (>=3:1) */
  --eyebrow-on-light: #8a5e16; /* darker gold — small-cap eyebrows on light/paper; ~5:1 on --surface-2 (passes AA small) */
  --gold-ink:   #8a5e16; /* dark gold for label/body text on light — same value as the eyebrow gold; clears AA (>=4.5:1) where gold-deep (~3:1) doesn't */

  --font-display: 'Inter', 'Avenir Next', 'Segoe UI', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --maxw: 1120px;
  --radius: 14px;
  --radius-sm: 9px;
  --shadow: 0 1px 2px rgba(20,30,55,.05), 0 12px 32px -16px rgba(20,30,55,.22);
  --shadow-lg: 0 30px 70px -34px rgba(20,30,55,.45);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
img { max-width: 100%; display: block; }
a { color: inherit; }

/* ---------- keyboard focus ---------- */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { outline: none; }

/* ---------- skip link ---------- */
.skip-link {
  position: absolute; left: 12px; top: -60px; z-index: 200;
  background: var(--ink); color: #fff; text-decoration: none;
  padding: .7em 1.1em; border-radius: 999px; font-weight: 600; font-size: .92rem;
  box-shadow: var(--shadow-lg); transition: top .18s ease;
}
.skip-link:focus { top: 12px; }

/* Visually-hidden but available to assistive tech (used for the footer
   signup's email label, which is visually implied by the placeholder). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(20px, 5vw, 40px); }

h1, h2, h3 { font-family: var(--font-display); font-weight: 600; line-height: 1.06; letter-spacing: -0.02em; color: var(--ink); text-wrap: balance; }
h1 { font-size: clamp(2.6rem, 6vw, 4.5rem); }
h2 { font-size: clamp(1.9rem, 3.6vw, 2.8rem); letter-spacing: -0.018em; }
h3 { font-size: clamp(1.2rem, 2vw, 1.45rem); letter-spacing: -0.01em; }
p { text-wrap: pretty; }

/* Brand label / eyebrow — sans, 600, 0.16em caps. Small all-caps text needs
   the darker --eyebrow-on-light (~5:1 on paper) to pass AA; the brighter
   --gold-deep is reserved for larger labels. On dark fields the tone-* /
   page-hero--dark / signup-band rules override to a light green. */
.eyebrow {
  font-family: var(--font-body);
  font-size: .8rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--eyebrow-on-light);
  font-weight: 600;
}
.eyebrow.green { color: var(--green-ink); }
.lead { font-size: clamp(1.08rem, 1.6vw, 1.28rem); color: var(--muted); max-width: 60ch; }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: .55em;
  font-weight: 600; font-size: 1rem; letter-spacing: .005em;
  padding: .82em 1.5em; border-radius: 999px;
  border: 1.5px solid transparent; cursor: pointer;
  text-decoration: none; transition: transform .14s ease, background .16s, box-shadow .16s, color .16s;
  white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--green); color: #fff; box-shadow: 0 10px 24px -12px rgba(60,122,89,.6); }
.btn-primary:hover { background: var(--green-ink); }
.btn-ink { background: var(--accent); color: #fff; }
.btn-ink:hover { background: var(--accent-ink); }
/* Warm Gold — reserved for vote / call-to-action moments (brand: max two
   gold uses per surface). Navy ink text for AA contrast on gold. */
.btn-gold { background: var(--gold); color: var(--ink); }
.btn-gold:hover { background: var(--gold-deep); color: var(--ink); }
.btn-ghost { background: transparent; border-color: var(--border); color: var(--ink); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn-lg { font-size: 1.06rem; padding: .95em 1.8em; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .6; cursor: not-allowed; pointer-events: none; box-shadow: none; }
.btn.is-loading { color: transparent !important; position: relative; pointer-events: none; }
.btn.is-loading::after {
  content: ""; position: absolute; width: 18px; height: 18px; top: 50%; left: 50%; margin: -9px 0 0 -9px;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  color: #fff; animation: btn-spin .6s linear infinite;
}
.cta-band .btn.is-loading::after, .btn-primary.is-loading::after { color: #fff; }
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn.is-loading::after { animation-duration: 1.4s; } }

/* ---------- top nav ---------- */
.site-nav {
  position: sticky; top: 0; z-index: 100;
  background: rgba(246,248,251,.82);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--border);
}
.site-nav .wrap { display: flex; align-items: center; gap: 1.2rem; min-height: 70px; }
/* Brand sits hard-left; the primary links cluster right beside it. The
   right-aligned .nav-actions owns the single auto-margin split (see below),
   so the brand must NOT also claim margin-right:auto or the two fight. */
.brand { display: flex; align-items: center; gap: .7rem; text-decoration: none; }
.brand-mark {
  width: 40px; height: 40px; border-radius: 11px; flex: none;
  background: var(--accent);
  display: grid; place-items: center;
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.14);
}
.brand-emblem { display: block; width: 26px; height: 26px; }
.brand-name { font-family: var(--font-display); font-weight: 600; font-size: 1.06rem; color: var(--ink); line-height: 1.05; letter-spacing: -.01em; }
/* Wordmark sub-label = brand lockup "office" line: display sans, 600,
   uppercase. Uses --eyebrow-on-light (the AA-safe small-caps gold, ~5:1 on
   paper) because at .6rem the brighter --gold-deep only clears ~3:1 — below the
   4.5:1 small-text floor. Footer overrides to brighter --gold on the dark field. */
.brand-name small { display: block; font-family: var(--font-display); font-size: .6rem; letter-spacing: .09em; text-transform: uppercase; color: var(--eyebrow-on-light); font-weight: 600; margin-top: 3px; }
/* .nav-links fills the bar (flex:1) so the nested .nav-actions { margin-left:auto }
   has room to push the Donate/Get-Involved cluster to the far right edge. Without
   flex:1 the menu shrinks to its content width and the actions sit mid-bar, flush
   against the links. At <=1024px this rule is overridden (position:absolute
   dropdown), so flex:1 is desktop-only by construction. */
.nav-links { display: flex; align-items: center; gap: .35rem; flex: 1; }
.nav-links a {
  text-decoration: none; color: var(--muted); font-weight: 500; font-size: .95rem;
  padding: .5em .78em; border-radius: 8px; transition: color .15s, background .15s;
}
.nav-links a:hover { color: var(--ink); background: var(--surface-2); }
.nav-links a.active { color: var(--accent); font-weight: 600; }
/* Right-aligned action cluster (Donate ghost + Get Involved primary). The
   auto-left-margin pushes it to the far edge so the primary links stay a
   tight left group. */
.nav-actions { display: flex; align-items: center; gap: .5rem; margin-left: auto; }
.nav-actions .btn { padding: .6em 1.15em; font-size: .92rem; }
.nav-cta { margin-left: .25rem; }
/* The nav CTA is a button, not a plain nav link. The .nav-links a rules
   above (muted default, ink hover, navy .active) otherwise win on
   specificity and tint the label, making it illegible on the green fill.
   Restore the button's own white text in every state. */
.nav-links a.nav-cta,
.nav-links a.nav-cta:hover,
.nav-links a.nav-cta.active { color: #fff; background: var(--green); }
.nav-links a.nav-cta:hover { background: var(--green-ink); }
/* Donate is a filled navy button in the nav; the muted .nav-links a color
   would otherwise override its white label and tank contrast on navy. */
.nav-links a.btn-ink,
.nav-links a.btn-ink:hover,
.nav-links a.btn-ink.active { color: #fff; background: var(--accent); }
.nav-links a.btn-ink:hover { background: var(--accent-ink); }
.nav-toggle {
  display: none; width: 44px; height: 44px; border: 1px solid var(--border);
  background: var(--surface); border-radius: 10px; cursor: pointer;
  flex-direction: column; gap: 5px; align-items: center; justify-content: center;
  margin-left: auto; /* brand no longer claims the auto-margin; toggle owns the
                        right edge on mobile (no effect on desktop: display:none) */
}
.nav-toggle span { width: 20px; height: 2px; background: var(--ink); border-radius: 2px; transition: .2s; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------- sections ---------- */
.section { padding-block: clamp(56px, 9vw, 104px); }
.section-tight { padding-block: clamp(40px, 6vw, 68px); }
.section-head { max-width: 64ch; margin-bottom: clamp(28px, 4vw, 48px); }
.section-head .eyebrow { margin-bottom: 14px; display: inline-block; }
.section-head h2 + p { margin-top: 16px; }

/* ---------- section background rhythm (S5: canonical tonal cadence) ----------
   One stated rule so content pages don't improvise into two same-background
   sections stacking flat. Tone modifiers set the surface; alternate light
   bands so no two adjacent sections share a background. The interior-page
   template is: page-hero (tint) -> body (white) -> [accent band: ink] ->
   close (tint). Apply a tone class to <section>; .section already owns the
   vertical rhythm.
     .tone-paper  -> cool-paper tint (default page-hero / closing bands)
     .tone-white  -> white (primary reading surface)
     .tone-ink    -> navy field (quote / CTA / stat accent — use once/page)
     .tone-green  -> green-deep field (rare alternate accent)
   Use .band-edge to add the hairline separator a tint band needs against
   an adjacent white section. */
.tone-paper { background: var(--surface-2); }
.tone-white { background: var(--surface); }
.tone-ink   { background: var(--ink); color: #e7ecf4; }
.tone-green { background: var(--green-ink); color: #eaf3ee; }
.tone-ink :where(h1, h2, h3) { color: #fff; }
.tone-green :where(h1, h2, h3) { color: #fff; }
.tone-ink .eyebrow { color: var(--green-bright); }
.tone-green .eyebrow { color: #cfe8d6; }
.tone-ink .lead { color: var(--muted-on-ink); }
.tone-green .lead { color: var(--muted-on-green); }
.band-edge { border-block: 1px solid var(--border); }
.tone-ink.band-edge, .tone-green.band-edge { border-color: rgba(255,255,255,.12); }

/* ---------- page hero (S3: promoted default; pages keep only overrides) ----------
   Every interior page opens with <section class="page-hero" id="main"
   tabindex="-1">. Default = cool-paper tint band. Pages may override the
   .wrap max-width per page, and volunteer.html opts into the dark variant
   via .page-hero--dark. Do not re-declare these defaults inline. */
.page-hero { background: var(--surface-2); border-bottom: 1px solid var(--border); }
.page-hero .wrap { padding-block: clamp(46px, 7vw, 78px); max-width: 58ch; }
.page-hero h1 { font-size: clamp(2.3rem, 5vw, 3.6rem); margin-top: 16px; }
.page-hero .lead { margin-top: 18px; }
.page-hero--dark {
  background: var(--ink); color: #e7ecf4; position: relative; overflow: hidden;
  border-bottom: 0;
}
.page-hero--dark::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(50% 80% at 90% 0%, rgba(60,122,89,.22), transparent 60%);
}
.page-hero--dark .wrap { position: relative; }
.page-hero--dark h1 { color: #fff; }
.page-hero--dark .lead { color: #cfd8e4; }
.page-hero--dark .eyebrow { color: var(--green-bright); }

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: clamp(22px, 3vw, 32px);
  box-shadow: var(--shadow);
}

/* ---------- footer ---------- */
.site-footer { background: var(--ink); color: #e7ecf4; }
.site-footer a { color: #e7ecf4; }
.footer-grid {
  display: grid; grid-template-columns: 1.5fr 1fr 1fr;
  gap: clamp(28px, 5vw, 56px); padding-block: clamp(48px, 7vw, 76px);
}
.footer-brand .brand-mark { background: rgba(255,255,255,.12); }
/* On the navy footer the office line uses the brighter brand gold (gold-deep
   muddies on dark — matches the masthead reversed lockup). */
.site-footer .brand-name { color: #fff; }
.site-footer .brand-name small { color: var(--gold); }
.footer-brand p { color: #c3ccda; margin-top: 16px; max-width: 38ch; font-size: .95rem; }
.footer-col h4 { font-family: var(--font-body); font-size: .74rem; letter-spacing: .14em; text-transform: uppercase; color: var(--green-bright); margin-bottom: 16px; font-weight: 600; }
.footer-col a { display: block; text-decoration: none; color: #cbd3e0; padding: 5px 0; font-size: .95rem; transition: color .15s; }
.footer-col a:hover { color: #fff; }
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,.1); padding-block: 26px;
  display: flex; flex-wrap: wrap; gap: 14px 28px; align-items: center; justify-content: space-between;
  font-size: .82rem; color: #9aa6ba;
}
.footer-bottom .disclaimer { font-family: var(--font-body); letter-spacing: .01em; font-size: .76rem; max-width: 62ch; }
/* Privacy Policy link sits alongside the required paid-for-by disclaimer. */
.footer-legal { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; }
.footer-legal a { color: #cbd3e0; text-decoration: none; transition: color .15s; }
.footer-legal a:hover { color: #fff; }

/* ---------- forms ---------- */
.field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 18px; }
.field label { font-weight: 600; font-size: .9rem; color: var(--ink); }
.field label .req { color: var(--green-ink); }
.field input, .field select, .field textarea {
  font: inherit; font-size: 1rem; padding: .72em .85em;
  border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--fg); width: 100%;
  transition: border-color .15s, box-shadow .15s;
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(28,58,99,.16);
}
.field textarea { resize: vertical; min-height: 110px; }
/* Validation cue uses brand gold-deep, not red (brand: nonpartisan, no red).
   Message text uses --gold-ink so the small error copy clears AA on light. */
.field .err { color: var(--gold-ink); font-size: .82rem; display: none; }
.field.invalid input, .field.invalid textarea, .field.invalid select { border-color: #b8791f; }
.field.invalid .err { display: block; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }
.form-note { font-size: .84rem; color: var(--muted); margin-top: 6px; }
.form-success {
  display: none; align-items: flex-start; gap: 12px;
  background: rgba(60,122,89,.1); border: 1px solid rgba(60,122,89,.32);
  color: var(--green-ink); border-radius: var(--radius); padding: 18px 20px; margin-top: 8px;
  font-weight: 500;
}
.form-success.show { display: flex; }
.form-success svg { flex: none; margin-top: 2px; }

/* ---------- email capture (S4: the campaign list — #1 asset) ----------
   Two placements share the .signup-form markup, structured once:
     1. .signup-band  — full-width homepage band. LIGHT tint surface, so the
        homepage run reads navy quote -> light signup -> gradient CTA instead
        of three dark bands in a row, and the capture pops on light.
     2. .footer-signup — compact form in the footer, on the NAVY footer field.
   The base .signup-input / .signup-note / .form-success styles below assume a
   DARK field (the footer). The .signup-band overrides re-skin those for light.
   Both post to the campaign's EmailOctopus list: js initForms() reads
   data-eo-endpoint and subscribes via fetch, revealing .form-success. */
.signup-band { background: var(--surface-2); border-block: 1px solid var(--border); }
.signup-band .wrap {
  padding-block: clamp(46px, 7vw, 80px);
  display: grid; grid-template-columns: 1.1fr .9fr; gap: clamp(28px, 5vw, 60px); align-items: center;
}
.signup-band h2 { color: var(--ink); max-width: 18ch; }
.signup-band .eyebrow { color: var(--eyebrow-on-light); }
.signup-band p { color: var(--muted); margin-top: 14px; max-width: 46ch; }
/* Re-skin the shared (dark-by-default) signup form for LIGHT surfaces.
   `.signup-light` is the reusable variant (hero capture); the mid-page
   `.signup-band` opts into the same treatment. Input/placeholder/focus/note
   win on specificity; the success + error overrides live after the dark
   `.signup-form` base below so they win on source order too. */
.signup-band .signup-input,
.signup-light .signup-input {
  border: 1.5px solid var(--border); background: var(--surface); color: var(--ink);
}
.signup-band .signup-input::placeholder,
.signup-light .signup-input::placeholder { color: var(--muted); }
.signup-band .signup-input:focus,
.signup-light .signup-input:focus {
  border-color: var(--green); background: var(--surface);
  box-shadow: 0 0 0 3px rgba(60,122,89,.18);
}
/* Light-surface note override. Raised to .signup-form specificity so it beats
   the dark-default `.signup-form .signup-note` that follows in source order.
   --muted clears AA on paper; --muted-on-ink clears it on navy. */
.signup-band .signup-form .signup-note,
.signup-light .signup-form .signup-note { color: var(--muted); }
.signup-form { display: flex; flex-direction: column; gap: 12px; }
.signup-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: start; }
/* Guard: the signup button sizes to its own content, never the flex line. */
.signup-row > .btn { flex: none; align-self: start; height: auto; }
/* The growable row child is .field-inline (line below). Do NOT put a
   `flex` shorthand on .signup-input here: the input is a child of the
   column-flex .field-inline, so a flex-basis would be read as a HEIGHT
   and balloon the field to ~200px tall. */
.signup-input {
  font: inherit; font-size: 1rem; padding: .82em 1em;
  border: 1.5px solid rgba(255,255,255,.18); border-radius: 999px;
  background: rgba(255,255,255,.06); color: #fff; width: 100%;
  /* Guard: even if a flex-basis leaks in, the input can't balloon vertically. */
  flex: none; height: auto; align-self: stretch;
  transition: border-color .15s, box-shadow .15s, background .15s;
}
.signup-input::placeholder { color: #9aa6ba; }
.signup-input:focus { outline: none; border-color: var(--green-bright); background: rgba(255,255,255,.1); box-shadow: 0 0 0 3px rgba(127,176,105,.25); }
.signup-form .signup-note { font-size: .8rem; color: var(--muted-on-ink); margin-top: 2px; }
.signup-form .form-success { background: rgba(127,176,105,.14); border-color: rgba(127,176,105,.4); color: #eaf3ee; }
.signup-form .form-success strong, .signup-form .form-success b { color: #fff; }
/* Validation: invalid pill border on the dark field uses the warm-gold edge. */
.signup-form .field-inline.invalid .signup-input { border-color: var(--gold); }
.signup-form .field-inline .err { color: #f0c674; font-size: .8rem; display: none; }
.signup-form .field-inline.invalid .err { display: block; }
.field-inline { display: flex; flex-direction: column; gap: 6px; flex: 1 1 200px; }
/* Light-surface success + error overrides. These sit AFTER the dark
   `.signup-form` defaults on purpose: equal specificity, so source order wins,
   keeping the hero capture and mid-page band readable on paper. */
.signup-band .form-success,
.signup-light .form-success { background: rgba(60,122,89,.1); border-color: rgba(60,122,89,.35); color: var(--green-ink); }
.signup-band .form-success strong, .signup-band .form-success b,
.signup-light .form-success strong, .signup-light .form-success b { color: var(--ink); }
.signup-band .field-inline .err,
.signup-light .field-inline .err { color: var(--fg); }
/* Submit-level failure (e.g. the EmailOctopus subscribe request errored). Same
   warm gold as field validation — never red (brand: nonpartisan). Hidden until
   js unsets its [hidden] attribute. */
.signup-error { color: #f0c674; font-size: .82rem; margin-top: 2px; }
.signup-error a { color: inherit; }
.signup-band .signup-error,
.signup-light .signup-error { color: var(--gold-ink); }
/* Off-screen spam trap (EmailOctopus honeypot). Not display:none, so bots that
   skip hidden fields still fill it; kept out of the tab order and the layout. */
.signup-hp { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }

/* Footer placement: above the columns, full footer width. */
.footer-signup {
  padding-block: clamp(28px, 4vw, 40px);
  border-bottom: 1px solid rgba(255,255,255,.1);
  display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 4vw, 48px); align-items: center;
}
.footer-signup h3 { font-family: var(--font-display); font-size: 1.25rem; color: #fff; letter-spacing: -.01em; }
.footer-signup p { color: var(--muted-on-ink); font-size: .9rem; margin-top: 6px; max-width: 40ch; }
.footer-signup .signup-row { margin: 0; }

/* ---------- misc helpers ---------- */
.pill {
  display: inline-flex; align-items: center; gap: .4em;
  font-family: var(--font-body); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
  padding: .4em .8em; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--muted); font-weight: 500;
}
.pill.green { color: var(--green-ink); border-color: rgba(60,122,89,.4); background: rgba(60,122,89,.08); }
.pill.gold { color: var(--gold-deep); border-color: rgba(227,165,60,.5); background: rgba(227,165,60,.12); }
.divider { height: 1px; background: var(--border); border: 0; }
.grid { display: grid; gap: clamp(18px, 2.4vw, 26px); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.center { text-align: center; }
.mt-s { margin-top: 12px; } .mt-m { margin-top: 24px; } .mt-l { margin-top: 40px; }
.ph {
  background: repeating-linear-gradient(45deg, var(--surface-2), var(--surface-2) 10px, #e3e9f1 10px, #e3e9f1 20px);
  border: 1px dashed var(--border); border-radius: var(--radius);
  display: grid; place-items: center; color: var(--muted);
  font-family: var(--font-mono); font-size: .78rem; letter-spacing: .04em; text-align: center; padding: 16px;
}

/* ---------- image placeholder (intentional "photo goes here" state) ----------
   A designed empty state for photo slots awaiting real campaign imagery — a
   framed icon + label, so an empty box reads as deliberate, not unfinished.
   Pair with a sizing class (e.g. .portrait / .about-portrait / .bio-portrait)
   that owns aspect-ratio and placement. .img-ph--dark for navy fields. */
.img-ph {
  display: grid; place-content: center; justify-items: center; gap: 14px;
  text-align: center; padding: clamp(18px, 3vw, 28px);
  background:
    radial-gradient(130% 120% at 50% 0%, rgba(28,58,99,.05), transparent 68%),
    var(--surface-2);
  border: 1px solid var(--border); border-radius: 18px;
}
.img-ph-ico {
  width: 48px; height: 48px; border-radius: 13px; flex: none;
  display: grid; place-items: center;
  background: var(--surface); border: 1px solid var(--border);
  color: var(--green-ink); box-shadow: var(--shadow);
}
.img-ph-ico svg { width: 24px; height: 24px; }
.img-ph-label {
  font-family: var(--font-mono); font-size: .72rem; letter-spacing: .04em;
  line-height: 1.55; color: var(--muted);
}
.img-ph-label strong {
  display: block; color: var(--fg); font-weight: 600;
  font-size: .82rem; letter-spacing: .02em; margin-bottom: 2px;
}
.img-ph--dark {
  background:
    radial-gradient(130% 120% at 50% 0%, rgba(127,176,105,.16), transparent 70%),
    rgba(255,255,255,.04);
  border-color: rgba(255,255,255,.14);
}
.img-ph--dark .img-ph-ico { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.16); color: var(--green-bright); box-shadow: none; }
.img-ph--dark .img-ph-label { color: #aeb9cc; }
.img-ph--dark .img-ph-label strong { color: #fff; }

/* A real photo dropped into a sizing slot (.portrait / .about-portrait /
   .bio-portrait). The sizing class still owns aspect-ratio, placement, and any
   shadow; the image just fills and crops to it. */
.photo { display: block; width: 100%; object-fit: cover; border-radius: 18px; }

/* ---------- responsive ---------- */
/* Tablet landscape and below: collapse the expanded nav before it crowds and
   ease dense 4-up grids. */
@media (max-width: 1024px) {
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .site-nav { position: static; }
  .site-nav .wrap { flex-wrap: wrap; }
  .nav-links {
    flex-basis: 100%;
    flex-direction: column; align-items: stretch; gap: 2px;
    padding: 8px 0 18px;
  }
  .nav-links a { padding: .8em .6em; font-size: 1.02rem; }
  .nav-actions { margin-left: 0; margin-top: 8px; flex-direction: column; gap: 8px; }
  .nav-cta, .nav-actions .btn { margin-left: 0; justify-content: center; width: 100%; }
  .js .site-nav { position: sticky; }
  .js .site-nav .wrap { flex-wrap: nowrap; }
  .js .nav-toggle { display: flex; }
  .js .nav-links {
    position: absolute; top: 70px; left: 0; right: 0;
    background: var(--surface); border-bottom: 1px solid var(--border);
    padding: 14px clamp(20px, 5vw, 40px) 22px;
    box-shadow: var(--shadow); display: none;
  }
  .js .nav-links.open { display: flex; }
}
/* Tablet portrait and below: step footer and 3-up grids down a level. */
@media (max-width: 900px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
  .footer-signup { grid-template-columns: 1fr; gap: 14px; }
  .signup-band .wrap { grid-template-columns: 1fr; }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 760px) {
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
  .form-row { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .grid-4 { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }
  .footer-brand { grid-column: auto; }
}

/* ---------- scroll reveal (JS-only) ----------
   Reveal is opt-IN for JS users: the `js` class is set on <html> before paint
   (see head.html). Without that class, [data-reveal] content is fully visible —
   so the page stays usable with JS off, in print, and in non-scrolling/headless
   renders where the IntersectionObserver never fires. */
[data-reveal] { transition: opacity .6s ease, transform .6s ease; }
.js [data-reveal] { opacity: 0; transform: translateY(16px); }
.js [data-reveal].in { opacity: 1; transform: none; }
@media print { .js [data-reveal] { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal], .js [data-reveal].in { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
  /* Vestibular safety: strip motion-on-an-axis. Keep color/border feedback
     (hover affordances stay legible via border-color, not movement). */
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-property: color, background-color, border-color, box-shadow, opacity, outline-color !important;
    transition-duration: .12s !important;
  }
  /* Remove the hover/active lift entirely — the shadow + border still signal
     interactivity without any positional shift. */
  .plank:hover, .event:hover, .btn:active { transform: none !important; }
}
