/* Philip Foust for Prosecutor — site styles.

   One committed look: courthouse blue-black, brand gold, and photography.
   Type is a pair of system faces — a tight uppercase grotesque for the
   headlines and structure, an old-style serif for everything anyone actually
   reads. No webfonts, so nothing on this site waits on a third party.

   The palette is deliberately dark end to end. A campaign has one look, and
   flipping half the visitors to a light theme on an OS setting is not a
   design decision anybody made. `color-scheme: dark` is set so form controls,
   scrollbars, and autofill follow.

   Components read the semantic tokens (--bg, --fg, --rule, --accent), never
   the raw palette. Change the raws and the whole site moves together. */

:root {
  /* ── raw palette ─────────────────────────────────────────────────────── */
  --gold:        #DAA51E;
  --gold-lift:   #F0C64E;
  --gold-deep:   #A87A0F;

  --ink:         #0C1420;   /* courthouse blue-black */
  --ink-2:       #131F2E;
  --ink-3:       #1C2A3B;

  --paper:       #FBFAF7;

  /* neutrals biased cool-blue toward the ink, never a flat grey */
  --slate:       #939DAC;
  --slate-dim:   #6B7686;

  --error:       #E4776A;
  --ok:          #7FD3A0;

  /* ── semantic tokens ─────────────────────────────────────────────────── */
  --bg:          var(--ink);
  --surface:     var(--ink-2);
  --surface-2:   var(--ink-3);
  --fg:          #F4F6F9;
  --fg-muted:    var(--slate);
  --rule:        rgba(218, 165, 30, 0.30);
  --rule-soft:   rgba(255, 255, 255, 0.10);
  --accent:      var(--gold);
  --accent-ink:  #0C1420;
  --ghost-brd:   rgba(244, 246, 249, 0.34);
  --photo-dim:   0.96;
  --watermark:   0.032;
  --veil-mid:    26%;
  --veil-reach:  58%;

  --shadow: 0 18px 44px -18px rgba(0, 0, 0, 0.65);

  --sans:  "Helvetica Neue", Helvetica, Arial, sans-serif;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;

  --shell: 1180px;
  --header-h: 74px;
  /* Height of the preview disclaimer bar. Zero unless the bar is on screen,
     which is the only state the production site is ever in. */
  --preview-h: 0px;
}

* { box-sizing: border-box; }

html {
  color-scheme: dark;
  scroll-behavior: smooth;
  /* Anchors land below the fixed header rather than under it. */
  scroll-padding-top: calc(var(--header-h) + var(--preview-h) + 1rem);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

/* height:auto matters: images carry width/height attributes for layout
   stability, and without it the attribute height is used literally once CSS
   narrows the width — a 1920-tall portrait in a 400px column. */
img { max-width: 100%; height: auto; display: block; }

a { color: var(--accent); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { color: var(--gold-lift); }

::selection { background: var(--accent); color: var(--accent-ink); }

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

.shell { max-width: var(--shell); margin: 0 auto; padding: 0 clamp(1.25rem, 4vw, 2.5rem); }
.shell.narrow { max-width: 780px; }

/* ── Type ───────────────────────────────────────────────────────────────── */

/* Headings are the grotesque, set tight and uppercase. Prose is the serif —
   it is the thing people read at length, and it carries the gravity. */
h1, h2 {
  margin: 0 0 0.9rem;
  font-family: var(--sans);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.028em;
  text-transform: uppercase;
  text-wrap: balance;
  color: var(--fg);
}
h1 { font-size: clamp(2.4rem, 5.4vw, 4.2rem); }
h2 { font-size: clamp(1.85rem, 3.6vw, 2.9rem); }
h3 {
  margin: 0 0 0.6rem;
  font-family: var(--sans);
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg);
}
.gold { color: var(--accent); }

p { margin: 0 0 1.1rem; }
main p, .card p, .split .body p, .prose p {
  font-family: var(--serif);
  font-size: 1.075rem;
  line-height: 1.68;
  color: var(--fg-muted);
}
main p strong, .prose strong { color: var(--fg); font-weight: 600; }

.lede {
  font-family: var(--serif);
  font-size: clamp(1.08rem, 1.5vw, 1.35rem);
  line-height: 1.55;
  color: var(--fg-muted);
  max-width: 42ch;
}
.muted { color: var(--fg-muted); }

/* ── Kicker: small caps trailed by a gold hairline ──────────────────────── */

.kicker {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  min-width: 0;
  margin: 0 0 1.1rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
}
.kicker::after {
  content: "";
  flex: 1;
  height: 1px;
  max-width: 10rem;
  background: var(--rule);
}
.kicker--center { justify-content: center; }
.kicker--center::before {
  content: "";
  flex: 1;
  height: 1px;
  max-width: 10rem;
  background: var(--rule);
}

/* ── Buttons ────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1.02em 2.3em;
  font-family: var(--sans);
  font-size: clamp(0.76rem, 0.88vw, 0.86rem);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: 0;
  background: var(--accent);
  color: var(--accent-ink);
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease,
              border-color 0.18s ease, translate 0.18s ease;
}
.btn:hover { background: var(--gold-lift); color: var(--accent-ink); translate: 0 -2px; }

.btn--ghost { background: transparent; border-color: var(--ghost-brd); color: var(--fg); }
.btn--ghost:hover { background: transparent; border-color: var(--accent); color: var(--accent); }

/* Sits on the gold band, where a gold button would vanish. */
.btn--dark { background: var(--ink); color: var(--fg); }
.btn--dark:hover { background: var(--ink-2); color: var(--fg); }

.btn--sm { padding: 0.72em 1.5em; font-size: 0.74rem; }

/* ── Header ─────────────────────────────────────────────────────────────── */

/* Fixed, so it can sit over the homepage hero as a transparent bar and only
   take on its background once you scroll. site.js toggles .is-stuck. */
.site-header {
  position: fixed;
  top: var(--preview-h);
  left: 0;
  right: 0;
  z-index: 60;
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--rule);
  transition: background-color 0.25s ease, border-color 0.25s ease;
}
/* ── Preview disclaimer ─────────────────────────────────────────────────
   Shown only while the concept is hosted on a Lasbury preview URL. It is a
   fixed bar above the fixed header; --preview-h carries its measured height
   into every offset that used to answer to --header-h alone. */
.preview-note {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 70;
  background: var(--ink-3);
  border-bottom: 1px solid var(--rule);
}
.preview-note .shell { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.preview-note p {
  margin: 0;
  text-align: center;
  font-family: var(--sans);
  font-size: 0.7rem;
  line-height: 1.5;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
}
.preview-note b { color: var(--accent); font-weight: 700; }

/* Over the hero, at rest: no bar at all. */
.has-hero .site-header:not(.is-stuck) {
  background: transparent;
  backdrop-filter: none;
  border-bottom-color: transparent;
}
.site-header .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: var(--header-h);
  padding-top: 0.6rem;
  padding-bottom: 0.6rem;
  position: relative;
}

/* Everything below the fixed header on pages with no hero to sit under it. */
body:not(.has-hero) main { padding-top: calc(var(--header-h) + var(--preview-h)); }

/* --- lockup: shield + serif wordmark --- */
/* The shield is taller than it is wide, so it sets the height of the whole
   bar. Sizing it off the header height keeps the two locked together instead
   of the mark drifting when one of them changes. */
.lockup {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  color: inherit;
  text-decoration: none;
}
.lockup img {
  width: auto;
  height: calc(var(--header-h) - 30px);
  display: block;
  flex: none;
}
/* The wordmark's optical centre sits above its box centre: "FOUST" is caps,
   and the tagline below it is small and dim. Nudge it down to match the
   shield rather than centring the box. */
.lockup-word {
  display: flex;
  flex-direction: column;
  justify-content: center;
  line-height: 1;
  padding-top: 0.1em;
}
.lockup-word b {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.2rem;
  letter-spacing: 0.14em;
  text-indent: 0.14em;
}
.lockup-word span {
  font-family: var(--serif);
  font-style: italic;
  font-size: 0.78rem;
  color: var(--fg-muted);
  margin-top: 0.32em;
  letter-spacing: 0.015em;
}
@media (max-width: 420px) {
  .lockup { gap: 0.6rem; }
  .lockup-word b { font-size: 1.05rem; }
  .lockup-word span { font-size: 0.7rem; }
}

.site-nav { display: flex; align-items: center; gap: 1.7rem; }
.site-nav a {
  font-family: var(--sans);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg);
  text-decoration: none;
  transition: color 0.18s ease;
}
/* :not(.btn) matters — the Donate button is already gold, and letting the
   current-page rule recolour its label paints gold on gold. */
.site-nav a:hover, .site-nav a:not(.btn)[aria-current="page"] { color: var(--accent); }
.site-nav .btn { padding: 0.78em 1.6em; color: var(--accent-ink); }
.site-nav .btn:hover { color: var(--accent-ink); }
.site-nav .btn[aria-current="page"] { background: var(--gold-lift); }

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--ghost-brd);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.55rem 0.9rem;
  cursor: pointer;
}

@media (max-width: 900px) {
  .nav-toggle { display: block; }
  .site-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0.75rem clamp(1.25rem, 4vw, 2.5rem) 1.5rem;
    background: var(--ink);
    border-bottom: 1px solid var(--rule);
  }
  .site-nav.is-open { display: flex; }
  .site-nav a { padding: 0.85rem 0; border-bottom: 1px solid var(--rule-soft); }
  .site-nav .btn { margin-top: 1rem; border-bottom: 0; }
}

/* ── Hero ───────────────────────────────────────────────────────────────── */

/* Full-bleed photograph of Monument Circle with the pitch set over its left
   third. The photo is a warm sunset and the type is white and gold, so the
   veil below is doing real work — it is not decoration. */
.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: center;
  overflow: hidden;
  isolation: isolate;
}

/* Wider than the frame and anchored left, which slides the monument off the
   headline and into the right half. object-position alone cannot do this: at
   desktop aspect ratios the frame is wider than the photograph, so cover
   already shows the full width and there is nothing left to pan.

   122% is a deliberately light touch — enough to clear the headline, not so
   much that the plaza and the circle itself crop away and the shot stops
   reading as Monument Circle. The trade is that Victory's headroom is 12% of
   the rendered height, which follows viewport width, so on a short window she
   sits closer to the header than on a tall one. */
.hero-photo {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  width: 122%;
  /* The global img rule caps every image at max-width:100%, which would clamp
     that 122% straight back to the frame and silently undo the pan. */
  max-width: none;
  height: 100%;
  object-fit: cover;
  object-position: center 40%;
  filter: brightness(0.74) saturate(0.9);
}

/* Three gradients, each with a job: darken the left so the headline holds,
   sink the bottom into the section below, and shade the top strip the
   transparent header sits on. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(100deg,
      var(--ink) 0%,
      color-mix(in srgb, var(--ink) 74%, transparent) 34%,
      color-mix(in srgb, var(--ink) 26%, transparent) 62%,
      transparent 88%),
    linear-gradient(to top,
      var(--ink) 0%,
      color-mix(in srgb, var(--ink) 45%, transparent) 20%,
      transparent 46%),
    linear-gradient(to bottom,
      color-mix(in srgb, var(--ink) 72%, transparent) 0%,
      transparent 20%);
}

/* Shares the shell's gutter and max-width so the headline sits on the same
   left edge as every section heading below it. The extra top padding is the
   fixed header — without it "centred" would mean centred behind the nav. */
.stage {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: calc(var(--header-h) + var(--preview-h) + clamp(2rem, 5vh, 3.5rem))
           clamp(1.25rem, 4vw, 2.5rem)
           clamp(2rem, 5vh, 3.5rem);
}
.stage .pitch { max-width: min(46rem, 66%); }

.pitch { display: flex; flex-direction: column; gap: clamp(1.35rem, 2.4vh, 1.9rem); }
/* The CTA row sits closer to the promise than the promise does to the
   headline — it is the answer to that sentence, not a separate thought. */
.pitch .cta { margin-top: clamp(0.4rem, 1.4vh, 1rem); }

.pitch .eyebrow {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  min-width: 0;
  margin: 0;
  font-size: clamp(0.66rem, 0.78vw, 0.76rem);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
}
.pitch .eyebrow::after {
  content: "";
  flex: 1;
  height: 1px;
  max-width: 10rem;
  background: var(--rule);
}

.hero h1 {
  margin: 0;
  font-size: clamp(3rem, 6.9vw, 6.6rem);
  line-height: 0.87;
  letter-spacing: -0.032em;
}
.hero h1 .line { display: block; }

.promise {
  margin: 0;
  max-width: 30ch;
  font-family: var(--serif);
  font-size: clamp(1.06rem, 1.55vw, 1.42rem);
  line-height: 1.5;
  color: var(--fg-muted);
}
.promise strong { color: var(--fg); font-weight: 600; }

.cta { display: flex; flex-wrap: wrap; gap: 0.8rem; }

/* Load sequence. */
@keyframes rise { from { opacity: 0; translate: 0 1.1em; } to { opacity: 1; translate: 0 0; } }
@keyframes settle { from { opacity: 0; scale: 1.045; } to { opacity: 1; scale: 1; } }

.hero .eyebrow, .hero h1 .line, .promise, .cta {
  animation: rise 0.78s cubic-bezier(0.16, 0.84, 0.3, 1) backwards;
}
.hero .eyebrow { animation-delay: 0.16s; }
.hero h1 .l1   { animation-delay: 0.26s; }
.hero h1 .l2   { animation-delay: 0.34s; }
.hero h1 .l3   { animation-delay: 0.42s; }
.promise       { animation-delay: 0.56s; }
.cta           { animation-delay: 0.66s; }
.hero-photo    { animation: settle 1.5s cubic-bezier(0.16, 0.84, 0.3, 1) backwards 0.1s; }

/* Keyed off shape as well as width: a portrait viewport wants the type at the
   bottom whether it is a phone or an iPad held upright. Left-veiling a frame
   that is taller than it is wide just leaves a hole under the buttons. */
@media (max-width: 860px), (max-aspect-ratio: 1 / 1) {
  .pitch .eyebrow { font-size: 0.64rem; letter-spacing: 0.16em; }
  .pitch .eyebrow::after { display: none; }
  /* Portrait crop: the type takes the lower half, so the veil runs bottom-up
     instead of left-to-right and the monument stays visible above it.
     The stage has to stretch for its flex-end to mean anything — centred grid
     items are only as tall as their content. */
  .hero { align-items: stretch; }
  .stage { justify-content: flex-end; }
  .stage .pitch { max-width: none; }
  /* No overhang here — the type sits below the monument, not beside it. */
  .hero-photo { width: 100%; object-position: center 32%; }
  .hero::after {
    background:
      linear-gradient(to top,
        var(--ink) 18%,
        color-mix(in srgb, var(--ink) 88%, transparent) 44%,
        color-mix(in srgb, var(--ink) 46%, transparent) 72%,
        color-mix(in srgb, var(--ink) 24%, transparent) 100%),
      linear-gradient(to bottom,
        color-mix(in srgb, var(--ink) 70%, transparent) 0%,
        transparent 16%);
  }
  .cta .btn { flex: 1 1 12rem; }
}

/* ── Page head: the hero stand-in on interior pages ─────────────────────── */

.page-head {
  position: relative;
  padding: clamp(3.5rem, 8vh, 6rem) 0 clamp(2.5rem, 5vh, 4rem);
  border-bottom: 1px solid var(--rule);
  overflow: hidden;
}
.page-head::before {
  content: "";
  position: absolute;
  right: -10vmin;
  top: 50%;
  translate: 0 -50%;
  width: 44vmin;
  aspect-ratio: 236 / 289;
  background: url("../img/foust-shield.png") center / contain no-repeat;
  opacity: var(--watermark);
  pointer-events: none;
}
.page-head .shell { position: relative; }
.page-head p:last-child { margin-bottom: 0; }

/* ── Sections ───────────────────────────────────────────────────────────── */

.section { padding: clamp(3.5rem, 8vh, 6rem) 0; }
.section--tint { background: var(--surface); border-top: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); }
.section--rule { border-top: 1px solid var(--rule); }

/* Every plate on this site — cards, donate chips, form fields — is painted
   --surface, which is exactly what --tint paints the band behind them. On a
   tinted section the plate would vanish into its own background, so it climbs
   one step. Anything plate-shaped added later belongs in this list. */
.section--tint .card,
.section--tint .issue-cards > li,
.section--tint .amount,
.section--tint .amount-field,
.section--tint .form input,
.section--tint .form textarea { background: var(--surface-2); }
/* --lead marked itself with a step up in colour, which is no longer available
   here. It marks itself with the full-strength gold edge instead. */
.section--tint .amount--lead { border-color: var(--accent); }
.section-head { max-width: 42rem; margin-bottom: clamp(2rem, 4vh, 3rem); }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.section-head p:last-child { margin-bottom: 0; }

/* ── Cards: issues, news, events all share one plate ────────────────────── */

.card, .issue-cards > li {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--rule-soft);
  border-top: 2px solid var(--accent);
  padding: clamp(1.6rem, 2.6vw, 2.1rem);
}
.card p:last-child, .issue-cards > li p:last-child { margin-bottom: 0; }

.issue-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem;
  counter-reset: issue;
}
.issue-cards > li { counter-increment: issue; }
.issue-cards > li::before {
  content: "0" counter(issue);
  display: block;
  margin-bottom: 1rem;
  font-family: var(--serif);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--accent);
}
.issue-cards h3 { font-size: 1.1rem; margin-bottom: 0.8rem; }
@media (max-width: 860px) { .issue-cards { grid-template-columns: 1fr; } }

/* Each issue card is the doorway to that issue's full plan, so it behaves
   like a news teaser: the heading is the link and the whole plate is its
   target, which keeps the accessible name the issue rather than "read the
   plan" three times over. */
.issue-cards > li { transition: background-color 0.18s ease, border-color 0.18s ease; }
.issue-cards h3 a { color: var(--fg); text-decoration: none; }
.issue-cards h3 a::after { content: ""; position: absolute; inset: 0; }
.issue-cards > li:hover { background: var(--surface-2); border-color: var(--rule); }
.issue-cards > li:hover h3 { color: var(--accent); }
.issue-cards > li:focus-within { outline: 2px solid var(--accent); outline-offset: 3px; }
.issue-cards h3 a:focus-visible { outline: none; }
.issue-cards > li:hover .readmore { color: var(--gold-lift); }
.issue-cards > li:hover .readmore::after { translate: 3px 0; }
/* At the foot of an issue page only two issues are left to read. The numerals
   are dropped there: they would restart at 01 and disagree with the numbering
   the same three cards carry on the home page. */
.issue-cards--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.issue-cards--pair > li::before { content: none; }
@media (prefers-reduced-motion: reduce) {
  .issue-cards > li { transition: none; }
  .issue-cards > li:hover .readmore::after { translate: none; }
}

.cards { display: grid; gap: 1.5rem; }
.card h2, .card h3 {
  font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  letter-spacing: -0.01em;
  text-transform: none;
  line-height: 1.2;
  margin-bottom: 0.5rem;
}
.card h2 a, .card h3 a { color: var(--fg); text-decoration: none; }
.card h2 a:hover, .card h3 a:hover { color: var(--accent); }
.card .meta {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 0.65rem;
}
.card .btn { margin-top: 1rem; }
.card.is-cancelled { border-top-color: var(--slate-dim); }
.card.is-cancelled h2 { text-decoration: line-through; color: var(--fg-muted); }
.card .badge {
  display: inline-block;
  margin: 0 0 0.75rem;
  padding: 0.25rem 0.7rem;
  font-family: var(--sans);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--slate);
}
/* iOS hands U+2197 an emoji glyph, so the off-site mark is drawn rather than
   typed. Masked, so it still takes its colour from the accent. */
.card--link::after {
  content: "";
  position: absolute;
  top: 1.5rem;
  right: 1.4rem;
  width: 0.72rem;
  height: 0.72rem;
  background: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7'/%3E%3Cpath d='M10 7h7v7'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7'/%3E%3Cpath d='M10 7h7v7'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* News teasers: the headline is the link, but the whole plate is the target.
   The stretched pseudo-element covers the card, so the accessible name stays
   the headline rather than becoming "read the full release" five times. */
.card--post { transition: background-color 0.18s ease, border-color 0.18s ease; }
.card--post h2 a::after,
.card--post h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
}
.card--post:hover { background: var(--surface-2); border-color: var(--rule); }
.card--post:hover h2,
.card--post:hover h3 { color: var(--accent); }
.card--post:focus-within { outline: 2px solid var(--accent); outline-offset: 3px; }
/* The card already carries the outline for a keyboard user. */
.card--post h2 a:focus-visible,
.card--post h3 a:focus-visible { outline: none; }

/* Sits under the teaser as the call to read on. Decorative — it is
   aria-hidden in the markup, because the headline above is the real link. */
.card .readmore, .issue-cards .readmore {
  display: block;
  margin: 1.1rem 0 0;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}
.card .readmore::after, .issue-cards .readmore::after {
  content: "→";
  display: inline-block;
  /* The label is tracked out at 0.16em, so a literal space in the content
     string lands nowhere near where the eye wants the arrow. */
  margin-left: 0.5em;
  transition: translate 0.18s ease;
}
.card--post:hover .readmore { color: var(--gold-lift); }
.card--post:hover .readmore::after { translate: 3px 0; }
/* The arrow in the corner already says "off-site" — don't run two. */
.card--link .readmore::after { content: ""; }

@media (prefers-reduced-motion: reduce) {
  .card--post, .card .readmore::after { transition: none; }
  .card--post:hover .readmore::after { translate: none; }
}

.addcal { margin-top: 1.25rem; font-size: 0.92rem; }
.addcal summary {
  cursor: pointer;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg);
}
.addcal summary:hover { color: var(--accent); }
.addcal ul { margin: 0.75rem 0 0; padding-left: 1.2rem; color: var(--fg-muted); }
.addcal li { margin-bottom: 0.25rem; }

.news-teasers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.5rem; }
@media (max-width: 860px) { .news-teasers { grid-template-columns: 1fr; } }

/* ── One post: the newsroom's reading page ──────────────────────────────── */

/* A press release runs long, and the whole job here is that somebody finishes
   it. Measure comes from .shell.narrow; the rest is rhythm — headings that
   separate without shouting, and a quote rule in gold because a candidate's
   own words are the point of most of these. */

.post-back {
  margin: 0 0 1.6rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.post-back a { text-decoration: none; }
.post-back a::before {
  content: "←";
  display: inline-block;
  margin-right: 0.5em;
  transition: translate 0.18s ease;
}
.post-back a:hover::before { translate: -3px 0; }
.post-back--end { margin: 3rem 0 0; padding-top: 2rem; border-top: 1px solid var(--rule-soft); }

/* A release headline is a sentence, not a two-word page title — the interior
   h1 size is tuned for "News & Press" and swallows the screen here. */
.page-head--post h1 { font-size: clamp(1.9rem, 3.4vw, 2.9rem); line-height: 1.02; }

.post-meta {
  margin: 0 0 1.1rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

.post-image { margin-bottom: 2.25rem; }

.post-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-top: 2.25rem;
}

/* Long-form body, rendered from markdown server-side — so these have to cover
   whatever a staffer reasonably types, not just what today's posts use. */
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose h2 { font-size: clamp(1.25rem, 2.2vw, 1.7rem); margin: 2.6rem 0 0.9rem; }
.prose h3 { margin: 2.2rem 0 0.7rem; }
.prose ul, .prose ol {
  margin: 0 0 1.1rem;
  padding-left: 1.35rem;
  font-family: var(--serif);
  font-size: 1.075rem;
  line-height: 1.68;
  color: var(--fg-muted);
}
.prose li { margin-bottom: 0.45rem; }
.prose li::marker { color: var(--accent); }
/* A plan reads as "step, then explanation" — the step carries the page's full
   text colour so the list scans before it is read. */
.prose li b, .prose li strong { color: var(--fg); font-weight: 600; }
.prose blockquote {
  margin: 1.9rem 0;
  padding-left: 1.4rem;
  border-left: 2px solid var(--accent);
}
.prose blockquote p { color: var(--fg); }
.prose hr { margin: 2.6rem 0; border: 0; border-top: 1px solid var(--rule-soft); }
.prose img { margin: 1.9rem 0; }
.prose table { width: 100%; border-collapse: collapse; margin: 1.9rem 0; font-size: 0.95rem; }
.prose th, .prose td { padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--rule-soft); text-align: left; }
.prose th { font-family: var(--sans); font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg); }

/* "###" — the wire-copy end marker the campaign closes every release with.
   Set as a body paragraph it reads like a typo; centred, it reads as a mark. */
.end-mark {
  margin: 2.6rem 0 0;
  text-align: center;
  font-family: var(--sans);
  font-size: 0.8rem;
  letter-spacing: 0.25em;
  color: var(--slate-dim);
}

@media (prefers-reduced-motion: reduce) {
  .post-back a::before { transition: none; }
  .post-back a:hover::before { translate: none; }
}

/* ── Donate: WinRed amount tiles ────────────────────────────────────────── */

/* Every tile is a plain link to WinRed's hosted page with ?amount= preset —
   nothing about a card ever touches this site. The "other amount" control is
   a GET form pointed at the same URL, so it composes the same query string
   with no JavaScript at all. Never collect payment details here. */
.amounts {
  list-style: none;
  margin: 0 0 1.75rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: 1rem;
}
.amount {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 5.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--rule-soft);
  color: var(--fg);
  text-decoration: none;
  font-family: var(--sans);
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  transition: background-color 0.18s ease, border-color 0.18s ease,
              color 0.18s ease, translate 0.18s ease;
}
.amount:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  translate: 0 -2px;
}
/* One tile carries the emphasis so the grid has a centre of gravity. */
.amount--lead { background: var(--surface-2); border-color: var(--rule); }

.amount-custom { display: grid; gap: 0.7rem; max-width: 30rem; }
.amount-custom > label {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.amount-custom-row { display: flex; gap: 0.75rem; }
.amount-field {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--rule-soft);
}
.amount-field:focus-within { border-color: var(--accent); }
.amount-field .dollar {
  padding-left: 1rem;
  font-family: var(--sans);
  font-size: 1.25rem;
  font-weight: 800;
  color: var(--accent);
}
.amount-field input {
  width: 100%;
  padding: 0.9rem 1rem 0.9rem 0.4rem;
  font-family: var(--sans);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--fg);
  background: transparent;
  border: 0;
}
.amount-field input:focus { outline: none; }
/* The spinner arrows fight a 2rem-tall numeral for space and win. */
.amount-field input::-webkit-outer-spin-button,
.amount-field input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.amount-field input[type="number"] { -moz-appearance: textfield; }

.donate-note {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule-soft);
  font-family: var(--serif);
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--fg-muted);
  /* Runs the full width of the donate band rather than a reading measure —
     the disclosure is two sentences and reads better on as few lines as the
     column allows. */
  max-width: none;
}
.donate-note strong { color: var(--fg); font-weight: 600; }

@media (max-width: 520px) {
  .amounts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .amount-custom-row { flex-direction: column; }
}

/* ── On the trail: the two-up community band ────────────────────────────── */

/* Same gold hairline the cards carry, so the photographs read as part of the
   same system rather than as a slideshow bolted on. The 3:2 frame matches the
   source files exactly — no crop, no faces lost. */
.community {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem;
}
.community li { border-top: 2px solid var(--accent); }
.community img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  filter: brightness(var(--photo-dim)) saturate(0.97);
  transition: filter 0.3s ease;
}
.community img:hover { filter: brightness(1.04) saturate(1); }
@media (max-width: 760px) {
  .community { grid-template-columns: 1fr; }
}

/* ── Split layouts ──────────────────────────────────────────────────────── */

.split {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
.split .frame { position: relative; max-width: 480px; }
/* A gold hairline square offset behind the photograph — the only decoration
   any photo on this site gets. */
.split .frame::before {
  content: "";
  position: absolute;
  inset: -0.9rem -0.9rem 0.9rem 0.9rem;
  border: 1px solid var(--rule);
  pointer-events: none;
}
.split .frame img { position: relative; width: 100%; filter: brightness(var(--photo-dim)); }
.split .body p:last-of-type { margin-bottom: 1.75rem; }
@media (max-width: 860px) {
  .split { grid-template-columns: 1fr; gap: 2.5rem; }
  .split .frame { max-width: none; }
}

/* Credential strip — the scannable version of the bio beside it. Each item is
   a gold-ruled column: the credential in the grotesque, the detail beneath. */
.creds {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.6rem;
  border-top: 1px solid var(--rule);
  padding-top: 1.6rem;
}
.creds li {
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--fg-muted);
}
.creds b {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.74rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
}
@media (max-width: 1000px) {
  .creds { grid-template-columns: 1fr 1fr; gap: 1.3rem 1.6rem; }
}
@media (max-width: 420px) {
  .creds { grid-template-columns: 1fr; }
}

/* Pull quote */
.pull {
  margin: 0 0 2.5rem;
  padding: 2rem 0;
  text-align: center;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.pull p {
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1.25rem, 2.6vw, 1.75rem);
  line-height: 1.4;
  color: var(--fg);
}
/* The issues lede — a wide photograph with the quote hung directly beneath
   it, so the pull quote's top rule doubles as the photo's baseline and the
   two read as one unit rather than two stacked blocks. */
/* A photograph that opens a page's content column — the newsroom uses one to
   put a face on the listing before the first headline. */
.page-photo { margin: 0 0 clamp(2rem, 4vh, 3rem); }
.page-photo img { display: block; width: 100%; height: auto; filter: brightness(var(--photo-dim)); }

.issue-lede { margin: 0 0 clamp(2rem, 4vh, 3rem); }
.issue-lede img {
  display: block;
  width: 100%;
  height: clamp(220px, 34vw, 420px);
  object-fit: cover;
  object-position: 50% 42%;
  filter: brightness(var(--photo-dim));
}
.issue-lede .pull { margin-bottom: 0; }

/* Gallery */
.gallery {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.25rem;
}
.gallery img {
  width: 100%;
  height: 260px;
  object-fit: cover;
  filter: brightness(var(--photo-dim)) saturate(0.95);
  transition: filter 0.3s ease;
}
.gallery img:hover { filter: brightness(1.05) saturate(1); }
@media (max-width: 760px) {
  .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .gallery img { height: 200px; }
}
@media (max-width: 480px) {
  .gallery { grid-template-columns: 1fr; }
  .gallery img { height: auto; }
}

/* ── Forms ──────────────────────────────────────────────────────────────── */

.form { display: grid; gap: 1.35rem; }
.form .row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.35rem; }
@media (max-width: 600px) { .form .row { grid-template-columns: 1fr; } }

.field label, .field legend > span {
  display: block;
  margin-bottom: 0.5rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.field input, .field textarea {
  width: 100%;
  padding: 0.85rem 1rem;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--rule-soft);
  border-radius: 0;
}
.field textarea { resize: vertical; }

/* A mailing address is a block of reading copy, not an aside in italics,
   which is the browser default for <address>. */
address {
  font-family: var(--serif);
  font-style: normal;
  font-size: 1.15rem;
  line-height: 1.7;
  margin: 0 0 1.1rem;
}
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.field .hint {
  margin: 0.45rem 0 0;
  font-family: var(--serif);
  font-size: 0.9rem;
  color: var(--fg-muted);
}
.fieldset { border: 0; padding: 0; margin: 0; }
.fieldset legend { padding: 0; }

.choices { display: grid; grid-template-columns: 1fr 1fr; gap: 0.7rem 1.5rem; }
.choices label {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin: 0;
  font-family: var(--serif);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--fg);
}
.choices input { width: auto; accent-color: var(--accent); }
@media (max-width: 600px) { .choices { grid-template-columns: 1fr; } }

/* The API returns per-field errors; lasbury-forms.js sets these attributes. */
[aria-invalid="true"] { border-color: var(--error) !important; }
[data-error]::after {
  content: attr(data-error);
  display: block;
  margin-top: 0.35rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--error);
}

.status { min-height: 1.5em; margin: 0; font-family: var(--sans); font-size: 0.95rem; font-weight: 700; }
.status[data-state="success"] { color: var(--ok); }
.status[data-state="error"] { color: var(--error); }
.status[data-state="pending"] { color: var(--fg-muted); }
.status[data-state="paused"] { color: var(--fg-muted); }

/* Legal boilerplate, not reading copy — it sits with .when and .paid-for and
   takes the sans they take. The serif is for things people actually read. */
.fine {
  margin: 0;
  font-family: var(--sans);
  font-size: 0.8rem;
  line-height: 1.55;
  letter-spacing: 0.02em;
  color: var(--fg-muted);
}

/* Required by the honeypot. Must NOT be display:none — some bots skip those,
   and the honeypot is what catches most spam. */
.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;
}

/* ── The gold band: sign-up on the loudest surface the site owns ────────── */

/* One section wears the gold field, and everything inside it has to be
   restated for ink-on-gold — the defaults are all tuned for light type on
   the courthouse blue-black. */

.section--gold {
  background: var(--accent);
  color: var(--accent-ink);
  border-top: 1px solid var(--accent);
  border-bottom: 1px solid var(--accent);
}
.section--gold h2,
.section--gold p,
.section--gold .kicker,
.section--gold .field label,
.section--gold .field legend > span,
.section--gold .choices label { color: var(--accent-ink); }
/* The hairline that trails the kicker is a gold rule — invisible here. */
.section--gold .kicker::after,
.section--gold .kicker--center::before { background: color-mix(in srgb, var(--ink) 28%, transparent); }
.section--gold .field .hint { color: color-mix(in srgb, var(--ink) 72%, transparent); }

.section--gold .field input,
.section--gold .field textarea {
  color: var(--accent-ink);
  background: color-mix(in srgb, #fff 82%, var(--gold));
  border-color: transparent;
}
.section--gold .field input::placeholder,
.section--gold .field textarea::placeholder { color: color-mix(in srgb, var(--ink) 55%, transparent); }
.section--gold .field input:focus,
.section--gold .field textarea:focus { outline: none; border-color: var(--ink); background: #fff; }
.section--gold .choices input { accent-color: var(--ink); }

.section--gold .status { color: var(--accent-ink); }
.section--gold .status[data-state="success"] { color: var(--ink); }
.section--gold .status[data-state="error"] { color: #6E1A0F; }
.section--gold [data-error]::after { color: #6E1A0F; }
.section--gold [aria-invalid="true"] { border-color: #6E1A0F !important; }

/* ── Socials ────────────────────────────────────────────────────────────── */

.socials { display: flex; flex-wrap: wrap; gap: 0.75rem; padding: 0; margin: 1.25rem 0 0; list-style: none; }
.socials a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1px solid var(--rule);
  color: var(--accent);
  text-decoration: none;
  transition: background-color 0.18s ease, color 0.18s ease;
}
.socials a:hover { background: var(--accent); color: var(--accent-ink); }
.socials svg { width: 19px; height: 19px; fill: currentColor; }

/* ── Footer ─────────────────────────────────────────────────────────────── */

.site-footer { background: var(--bg); border-top: 1px solid var(--rule); }
.site-footer .shell {
  padding-top: clamp(2.75rem, 6vh, 4rem);
  padding-bottom: 2.5rem;
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 3rem);
}
.site-footer .logo img { width: 150px; }
.site-footer h3 {
  color: var(--accent);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  margin-bottom: 1rem;
}
.site-footer nav a {
  display: block;
  padding: 0.35rem 0;
  font-family: var(--sans);
  font-size: 0.88rem;
  color: var(--fg-muted);
  text-decoration: none;
}
.site-footer nav a:hover { color: var(--accent); }
.site-footer .disclaimer {
  grid-column: 1 / -1;
  padding-top: 1.75rem;
  border-top: 1px solid var(--rule-soft);
  text-align: center;
}
.site-footer .when {
  margin: 0 0 1.25rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}
/* The campaign-finance disclaimer: boxed, so it reads as the legal notice
   it is and not as another line of footer copy. */
.site-footer .disclaimer .paid-for {
  display: inline-block;
  margin: 0 0 0.9rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--rule);
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg);
}
@media (max-width: 860px) {
  .site-footer .shell { grid-template-columns: 1fr; gap: 2rem; }
}

/* ── Reveal on scroll ───────────────────────────────────────────────────── */

.reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
