/* public/style.css — MyTX15, mobile-first
   Palette matched to the Bobby Pulido for Congress brand sheet so this page
   reads as a native subpage of the official site rather than a separate
   branded app: navy #00205B, crimson #AD0B32 (used sparingly, as an accent
   only — never as a full-card background), light gray #E9E9E9.

   TYPEFACE. Museo Slab 500 is the brand sheet's heading face and is now
   actually served, self-hosted from public/assets/fonts (25 KB woff2, 29 KB
   woff fallback). It was declared in --font-heading for months but never
   loaded, so every heading silently rendered in Georgia. font-display:swap
   paints text immediately in the fallback and reflows once, rather than
   holding a headline blank on a slow connection.

   2026-08 redesign: hybrid type system. Headings keep the brand serif
   (Museo Slab/Georgia); ALL other UI text — body, labels, buttons, inputs —
   uses the platform system sans (SF on iOS, Segoe on Windows, Roboto on
   Android) for legibility and a cleaner, more premium feel on phones. */
@font-face{
  font-family:"Museo Slab";
  src:url("assets/fonts/MuseoSlab-500.woff2") format("woff2"),
      url("assets/fonts/MuseoSlab-500.woff") format("woff");
  font-weight:400 700;
  font-style:normal;
  font-display:swap;
}

:root{
  /* The three brand-sheet colors, exact. Everything else on this page is a
     tint or a semantic state color derived from them. */
  --ink:#00205B; --ink-soft:#3B4A73; --muted:#5E6785;
  --page:#E9E9E9; --surface:#FFFFFF;
  --blue:#00205B; --blue-dark:#001640;
  --red:#AD0B32; --red-soft:#F7E6EA; --green:#1E9E6B; --green-soft:#E8F8F1;
  --amber-soft:#FEF6E6; --blue-soft:#EDEFF5;
  --line:rgba(0,32,91,.12);
  --campaign-red:#AD0B32; --campaign-red-soft:#F7E6EA;

  /* Map palette. District blue is kept (the one color the client asked to
     preserve); counties moved from orange to a violet analogous to it, the
     precinct highlight from green to teal on its other side. Keep in sync
     with scripts/build-static-map.js COLORS. */
  --map-district:#086CFF;
  --map-county:#6E56CF;
  --map-county-label:#4A4370;
  --map-precinct:#0E8E8E;

  /* Pill palette. Each pill type gets its own hue so a card reads as several
     distinct facts rather than one grey wash — every one is a soft tint at
     roughly the same weight, so none of them shouts. */
  --pill-election:#8A6100; --pill-election-soft:#FDF3E0;
  --pill-count:#5B4BC4;   --pill-count-soft:#EEEBFA;

  --radius-lg:18px; --radius-md:14px; --radius-sm:10px;
  --shadow-sm:0 1px 2px rgba(0,32,91,.05), 0 1px 1px rgba(0,32,91,.04);
  --shadow-md:0 2px 12px rgba(0,32,91,.06), 0 1px 3px rgba(0,32,91,.05);
  --shadow-lg:0 12px 32px rgba(0,32,91,.12), 0 4px 10px rgba(0,32,91,.05);
  --font-heading: "Museo Slab", "Georgia", "Times New Roman", serif;
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  /* Legacy alias — a few older rules still reference --font. */
  --font: var(--font-heading);
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
/* The hidden attribute must always win: display:flex/grid on a class (e.g.
   .ios-row on the collapsible ZIP row) otherwise out-specifies the UA's
   [hidden]{display:none} and the "hidden" element renders anyway. */
[hidden]{display:none!important}
body{
  margin:0;background:var(--page);color:var(--ink);font-family:var(--font-ui);
  -webkit-font-smoothing:antialiased;line-height:1.5;font-size:16px;
}
h1,h2,h3{font-family:var(--font-heading)}
img{max-width:100%;display:block}
button,input,textarea{font-family:inherit}
:focus-visible{outline:3px solid rgba(8,108,255,.55);outline-offset:2px;border-radius:4px}
.skip-link{position:fixed;left:12px;top:-80px;z-index:9999;background:#fff;padding:12px 16px;border-radius:8px;box-shadow:var(--shadow-md)}
.skip-link:focus{top:12px}

/* ---------- Header ---------- */
/* Deliberately minimal: no site nav — this page is meant to sit inline on the
   official site's own page shell, so this just needs the brand mark, the
   2026-cycle note, and the language toggle. */
.topbar{
  display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  padding:14px 4px;border-bottom:0;position:relative;
}
/* A single hairline. The three-band flag stripe sat directly under the logo
   and competed with it, and hard color bands under a header read as dated
   political-website chrome. With Museo Slab now actually loading, the wordmark
   and the type carry the identity; this rule only terminates the header. */
.topbar::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:var(--line);
}
.cycle-badge-wrap{display:flex;align-items:center;gap:10px;min-width:0}
.brand-logo{height:30px;width:auto;display:block}
.brand-logo-link,.footer-logo-link{display:block;text-decoration:none;border-radius:6px}
.brand-logo-link:active,.footer-logo-link:active{opacity:.8}
@media(max-width:400px){.brand-logo{height:26px}}
.language-toggle{display:flex;padding:3px;background:var(--blue-soft);border-radius:10px}
.language-toggle button{border:0;background:transparent;min-height:38px;padding:7px 13px;border-radius:8px;font-weight:600;font-size:13px;color:var(--muted);cursor:pointer;transition:background .2s,color .2s;white-space:nowrap}
.language-toggle button.is-active{background:#fff;color:var(--ink);box-shadow:var(--shadow-sm)}

/* ---------- Layout ---------- */
/* 16px, not 40px: the footer supplies its own margin and padding, so the
   extra 40 here just opened a gap under whatever each screen ends with. */
main{max-width:640px;margin:0 auto;padding:0 16px 16px}
.view{display:none;animation:view-in .35s cubic-bezier(.22,.9,.28,1)}
.view.is-active{display:block}
@keyframes view-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
@media(prefers-reduced-motion: reduce){.view{animation:none}}

/* ---------- Hero ---------- */
.hero-copy{padding:30px 0 4px;text-align:center}
.hero-copy .eyebrow{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--red);margin:0 0 10px}
.hero-copy h1{font-size:clamp(28px,7vw,40px);font-weight:600;letter-spacing:-.01em;line-height:1.12;margin:0 0 14px;color:var(--ink)}
.hero-subtitle{font-size:16px;color:var(--ink-soft);margin:0 auto 18px;max-width:46ch}

.new-for-2026-card{
  padding:16px 18px;border-radius:var(--radius-md);
  background:var(--surface);
  border:1px solid var(--line);border-left:3px solid var(--campaign-red);
  margin-bottom:8px;text-align:left;
}
.new-for-2026-card .tag{
  display:inline-block;font-size:10px;font-weight:700;letter-spacing:.07em;
  color:#fff;background:var(--ink);
  padding:4px 9px;border-radius:5px;margin-bottom:9px;
}
.new-for-2026-card p{margin:0;font-size:14.5px;color:var(--ink-soft);line-height:1.55}

/* ---------- How it works (page 1 explainer) ---------- */
.how-steps{display:grid;grid-template-columns:1fr;gap:10px;margin-top:16px;text-align:left}
.how-step{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md)}
.how-step-number{
  flex:none;width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:var(--blue-soft);color:var(--ink);font-weight:700;font-size:14px;
}
.how-step strong{display:block;font-size:15px;font-weight:600;color:var(--ink)}
.how-step span{display:block;font-size:13.5px;color:var(--muted);margin-top:1px;line-height:1.45}
@media(min-width:760px){.how-steps{grid-template-columns:1fr 1fr 1fr}}

/* ---------- Static district map (page 1) ---------- */
.home-map-panel{position:relative;margin-top:20px;padding:0}
.map-caption{padding:16px 18px 10px}
.map-caption strong{display:block;font-size:16px;font-weight:600;color:var(--ink);font-family:var(--font-heading)}
.map-caption span{display:block;font-size:13px;color:var(--muted);margin-top:2px}
.static-map{padding:6px 18px 4px;display:flex;justify-content:center;background:#FFFFFF}
/* Bounded by HEIGHT, not width. The district is a genuinely long, narrow
   shape (Gonzales down to Hidalgo) and the artwork is 760x1517 — a 1:2 ratio.
   Sizing it to width:100% made it ~800px tall on a 375px phone, taller than
   the viewport, so the map alone pushed the form two screens down. */
.static-map img{max-width:100%;max-height:52vh;width:auto;height:auto}
@media(min-width:760px){.static-map img{max-height:520px}}

/* ---------- Home v2: merged landing + intake (2026-08-12) ---------- */
/* Block 1 reuses .hero-copy above. The trust line closes the hero: the three
   facts (free, bilingual, a minute) a visitor wants before typing anything. */
.trust-line{font-size:13px;font-weight:600;color:var(--muted);text-align:center;margin:0 0 10px}

/* Block 2 — the intake card. Stripe-checkout register: one white card, fields
   with visible borders and a soft focus ring, labels above inputs. Everything
   here uses .field/.pair classes, NOT .ios-row, so the older grouped-row form
   styling (still used by the result screen's help card) is untouched. */
.info-form{margin-top:10px;max-width:560px;margin-left:auto;margin-right:auto}
.intake-card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-md);padding:22px 18px 20px;
}
.intake-head{margin-bottom:18px}
.intake-head h2{margin:0;font-size:22px;font-weight:600;color:var(--ink);letter-spacing:-.015em;line-height:1.2}
.intake-head small{display:block;font-size:13.5px;color:var(--muted);margin-top:4px;line-height:1.5}

.field{display:flex;flex-direction:column;gap:6px;margin-bottom:14px;min-width:0}
.field>label,.field>.field-label{font-size:13px;font-weight:600;color:var(--ink)}
.field>label em,.field>.field-label em{font-style:normal;font-weight:500;color:var(--muted)}
.field input{
  width:100%;border:1px solid #D3D8E4;border-radius:10px;padding:11px 13px;
  font-size:16px;color:var(--ink);background:#fff;
  transition:border-color .15s,box-shadow .15s;
}
.field input::placeholder{color:#A3AAC4}
/* The ring replaces the global focus outline on these inputs — border darkens
   to campaign navy and a soft navy halo marks the field, Stripe-style. */
.field input:focus,.field input:focus-visible{outline:none;border-color:var(--ink);box-shadow:0 0 0 4px rgba(0,32,91,.10)}
@media(prefers-reduced-motion: reduce){.field input,.dob-shell{transition:none}}

/* Side-by-side pairs. .pair-always stays two-up even on phones (first/last
   name); plain .pair (phone/email) stacks below 560px. */
.pair{display:grid;grid-template-columns:1fr;gap:0 12px}
.pair-always{grid-template-columns:1fr 1fr}
@media(min-width:560px){.pair{grid-template-columns:1fr 1fr}}

/* DOB as one visual input: the shell carries the border and focus ring, the
   three inner boxes are borderless, the slashes are structural. */
.dob-shell{
  display:flex;align-items:center;gap:2px;border:1px solid #D3D8E4;border-radius:10px;
  background:#fff;padding:0 8px;transition:border-color .15s,box-shadow .15s;
}
.dob-shell:focus-within{border-color:var(--ink);box-shadow:0 0 0 4px rgba(0,32,91,.10)}
.dob-shell input{
  border:0;outline:none;background:transparent;text-align:center;
  font-size:16px;font-weight:600;letter-spacing:.03em;color:var(--ink);
  padding:11px 0;width:44px;flex:none;
}
.dob-shell input:last-of-type{width:64px}
.dob-shell input::placeholder{color:#A3AAC4;font-weight:500}
.dob-shell .dob-sep{color:#C4C9DA;font-weight:600}

/* Suggestions panel under the address field: same normal-flow placement as
   before (never floats over later fields), restyled as a dropdown sheet. */
.field .address-suggestions:not(:empty){border-radius:12px;box-shadow:var(--shadow-lg)}
.field .field-hint{padding:0 2px}

/* Error state matches the focus-ring language, in red: the field the message
   names gets a red border and halo instead of the old inset bar. */
.field.is-invalid input,.field.is-invalid .dob-shell{border-color:var(--red);box-shadow:0 0 0 4px rgba(173,11,50,.08)}
.field.is-invalid .dob-shell input{border:0;box-shadow:none}
.field.is-invalid>label,.field.is-invalid>.field-label{color:var(--red)}
.intake-card .form-error{margin:0 0 12px}
.intake-card .cta-button{margin-top:2px}

/* Block 3 — benefits, the page-1 v6 cards restated. */
.home-benefits{margin-top:30px}
.benefits-lead{font-family:var(--font-heading);font-size:19px;font-weight:600;text-align:center;margin:0 0 14px;color:var(--ink)}
.benefit-grid{display:grid;grid-template-columns:1fr;gap:9px}
@media(min-width:760px){.benefit-grid{grid-template-columns:1fr 1fr}}
.benefit-card{
  display:flex;gap:11px;align-items:flex-start;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md);
  padding:13px 15px;
}
.benefit-tick{
  flex:none;width:24px;height:24px;border-radius:50%;background:var(--green-soft);color:var(--green);
  text-align:center;line-height:24px;font-size:13px;font-weight:800;
}
.benefit-text{display:block;min-width:0}
.benefit-text b{display:block;font-size:15px;font-weight:700;color:var(--ink)}
.benefit-text>span{display:block;font-size:13.5px;color:var(--muted);line-height:1.5;margin-top:1px}

/* Block 4 — the map card, closing tease above the walk-back CTA. Shares the
   header-bar treatment page 1 used so the page still reads as one set. */
.home-map-card{
  margin:26px auto 0;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius-md);overflow:hidden;
}
.home-map-tag{
  font-size:10.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink);background:var(--blue-soft);padding:8px 15px;margin:0;text-align:center;
  border-bottom:1px solid var(--line);
}
.home-map-cap{padding:13px 16px 4px;text-align:center;font-size:13px;color:var(--muted);margin:0;line-height:1.55}
.home-map-cta-row{padding:6px 16px 18px}
.home-map-cta-row .cta-button{max-width:420px;margin:0 auto}

/* ---------- Form ---------- */
.form-heading{margin-bottom:12px}
.form-heading h1{margin:0;font-size:26px;font-weight:600;color:var(--ink);letter-spacing:-.015em;line-height:1.15;font-family:var(--font-heading)}
.form-heading small{display:block;font-size:13.5px;color:var(--muted);margin-top:2px}

.ios-card{background:var(--surface);border-radius:var(--radius-md);box-shadow:var(--shadow-md);overflow:hidden;border:1px solid var(--line)}
.field-pair{display:grid;grid-template-columns:1fr 1fr}
.field-pair .ios-row:first-child{border-right:1px solid var(--line)}
.ios-row{display:flex;flex-direction:column;gap:2px;padding:9px 16px;border-bottom:1px solid var(--line);min-height:48px;justify-content:center;position:relative}
.ios-card>.ios-row:last-child,.field-pair{border-bottom:none}
.ios-card>*:last-child{border-bottom:none}
.ios-row span{font-size:12px;color:var(--muted);font-weight:600}
.ios-row span em{font-style:normal;font-weight:600;color:var(--ink);text-decoration:underline;text-decoration-style:dotted;text-decoration-color:rgba(0,32,91,.4);text-underline-offset:2px}
.ios-row input{border:0;outline:0;font-size:16px;padding:2px 0;background:transparent;min-height:24px;color:var(--ink);width:100%}
.ios-row input::placeholder{color:#A3AAC4}
/* Focus shows on the row's border, not as a grey fill: the fill read as a
   disabled state on the help form (Marco). The input's own 4px halo inside
   it went with it — one navy outline, nothing else changes colour. */
.ios-row:focus-within{border-color:var(--ink)}
.ios-row input:focus,.ios-row textarea:focus{box-shadow:none}

/* Optional fields (Apt/Unit) collapse behind a native <details> so they stay
   out of the way until someone actually needs them — no JS, no ARIA to keep
   in sync, and it re-opens automatically when a value is already present. */
.optional-row{padding-top:0;padding-bottom:0}
.optional-row>summary{
  list-style:none;cursor:pointer;padding:20px 0;
  font-size:12.5px;color:var(--muted);font-weight:600;
  display:flex;align-items:center;gap:6px;
}
.optional-row>summary::-webkit-details-marker{display:none}
.optional-row>summary::after{
  content:"";width:7px;height:7px;margin-left:auto;
  border-right:1.6px solid var(--muted);border-bottom:1.6px solid var(--muted);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .18s ease;
}
.optional-row[open]>summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.optional-row>summary em{font-style:normal;font-weight:600;color:var(--ink);text-decoration:underline;text-decoration-style:dotted;text-decoration-color:rgba(0,32,91,.4);text-underline-offset:2px}
.optional-row>input{padding-bottom:14px}
@media(prefers-reduced-motion: reduce){.optional-row>summary::after{transition:none}}

/* Centered as a single MM / DD / YYYY group rather than left-flushed. */
.dob-row{padding-top:6px;padding-bottom:6px}
.dob-row .dob-inputs{display:flex;align-items:center;gap:8px;width:100%}
.dob-inputs input{
  width:46px;flex:none;text-align:center;letter-spacing:.03em;font-weight:600;
  border:1.5px solid var(--line)!important;border-radius:8px;padding:4px 0!important;
  background:#fff;font-size:16px;
}
.dob-inputs input:last-of-type{width:64px}
.dob-inputs input:focus{border-color:var(--ink)!important;background:#fff!important}
.dob-sep{color:#A3AAC4;font-weight:700}

.address-row .address-suggestions{position:relative}
.address-suggestions{margin-top:2px}
/* In normal flow, NOT floating over the fields below it. As an overlay this
   covered the phone and email rows while the visitor typed, which reads as
   those fields having vanished — the opposite of reassuring for someone
   already unsure whether they are filling the form in correctly. Pushing the
   rest of the form down instead keeps everything visible and makes the list
   obviously part of the address field. */
.address-suggestions:not(:empty){
  position:static;margin-top:6px;
  background:#fff;border-radius:var(--radius-sm);box-shadow:var(--shadow-md);
  border:1px solid var(--line);overflow:hidden;max-height:280px;overflow-y:auto;
}
.suggestion-item{display:flex;flex-direction:column;gap:2px;width:100%;min-height:48px;justify-content:center;text-align:left;padding:11px 14px;border:0;border-bottom:1px solid var(--line);background:#fff;cursor:pointer}
.suggestion-item:last-child{border-bottom:none}
.suggestion-item:hover,.suggestion-item:focus{background:var(--blue-soft)}
.suggestion-line1{font-size:15px;font-weight:600;color:var(--ink)}
.suggestion-line2{font-size:13px;color:var(--muted)}
.field-hint{font-size:13px;color:var(--muted);padding:6px 16px 10px}
.field-hint.is-confirmed{color:var(--green);font-weight:700}
/* Flashed on the hint when someone submits without picking a suggestion —
   draws the eye to the one instruction that unblocks them. */
.field-hint.is-nudge{color:var(--red);font-weight:700}

.form-error{color:var(--red);font-size:14px;font-weight:600;min-height:18px;margin:14px 2px 0}

/* Buttons. Primary = solid navy; secondary = quiet outline. 12px radius for
   the softer, more current feel of the 2026-08 redesign (was the campaign
   site's flat 4px). */
.sticky-cta{position:sticky;bottom:0;padding:14px 0 10px;margin-top:6px;background:linear-gradient(to top,var(--page) 55%,transparent)}
.cta-button{
  display:block;width:100%;min-height:52px;border:1.5px solid var(--ink);border-radius:12px;
  background:var(--ink);color:#fff;
  font-weight:600;font-size:16.5px;cursor:pointer;letter-spacing:0;
  transition:opacity .15s,transform .15s;
}
.cta-button:active{opacity:.85;transform:scale(.99)}
.cta-button:disabled{opacity:.5;cursor:default}
.cta-button.link-button{display:flex;align-items:center;justify-content:center;text-decoration:none}
.secondary-button{
  background:#fff;border:1.5px solid var(--line);color:var(--ink);border-radius:12px;
  min-height:48px;font-weight:600;font-size:15px;cursor:pointer;
}
.secondary-button.link-button{display:flex;align-items:center;justify-content:center;text-decoration:none}
.text-button{background:none;border:0;color:var(--ink);font-weight:600;font-size:13.5px;padding:8px 0;cursor:pointer;text-decoration:underline}

/* Required-field marker. Campaign red, the same accent the eyebrow uses, so
   it reads as "attention" rather than as an error that already happened. */
.req{color:var(--campaign-red);font-style:normal;font-weight:700;margin-left:1px}
/* The field a validation message is about. Red rule down the side plus a
   tinted ground, so it is findable when the message is at the bottom of the
   form and the field is halfway up it. */
.ios-row.is-invalid{background:var(--red-soft);box-shadow:inset 3px 0 0 var(--red)}
.ios-row.is-invalid input,.ios-row.is-invalid textarea{background:transparent}
input[aria-invalid="true"],textarea[aria-invalid="true"]{outline:none}
@media(prefers-reduced-motion: reduce){.cta-button{transition:opacity .15s}}

/* ---------- Result: checking state ---------- */
.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}
.checking-panel{padding:120px 0;display:flex;justify-content:center}
.spinner{width:36px;height:36px;border-radius:50%;border:3px solid rgba(0,32,91,.1);border-top-color:var(--ink);animation:spin .9s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion: reduce){.spinner{animation:none}}

/* ---------- Result body ---------- */
#result-body{padding-top:20px;display:flex;flex-direction:column;gap:18px}
.map-card{padding:0}
.map-card #result-map,.map-panel #map{height:300px;width:100%;background:#eef1fa}

/* Matched address sits at the bottom of the map card, under the legend. */
.map-card .matched-address{padding:12px 16px 14px;border-top:1px solid var(--line)}
.matched-address{display:flex;flex-direction:column;gap:4px}
/* Factual in/out verdict beside the ADDRESS CHECKED label — driven by the
   same point-in-polygon result as the status card, never inferred. */
.tx15-pill{
  display:inline-block;margin-left:8px;padding:2px 9px;border-radius:999px;
  font-size:10.5px;font-weight:800;letter-spacing:.04em;vertical-align:1px;
}
.tx15-pill.inside{background:var(--green-soft);color:var(--green)}
.tx15-pill.outside{background:var(--red-soft);color:var(--red)}
.matched-address span{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.matched-address strong{font-size:15px;color:var(--ink);font-weight:600;line-height:1.4}
.matched-address .address-warning{font-size:12.5px;font-weight:700;color:#B9770E;text-transform:none;letter-spacing:0}

.status-card{padding:24px 20px 6px;text-align:center}
.status-icon{
  width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:22px;font-weight:700;margin:0 auto 14px;
}
.status-card h2{font-size:21px;font-weight:600;margin:0 0 8px;letter-spacing:-.01em}
.status-card p{font-size:15px;color:var(--ink-soft);margin:0 0 16px;line-height:1.6}
.status-card .cta-button,.status-card .secondary-button{margin-top:4px;margin-bottom:18px}
/* The statewide-lookup link is the last thing in the card on a registered
   result, and .text-button's 8px padding left it almost touching the card's
   bottom edge (Marco, 2026-09-02). Give it the same breathing room the
   buttons above it get. */
.status-card .text-button{display:inline-block;margin-top:2px;margin-bottom:16px}
.status-success .status-icon{background:var(--green-soft);color:var(--green)}
.status-review .status-icon{background:var(--amber-soft);color:#B9770E}
.status-outside .status-icon{background:var(--red-soft);color:var(--red)}
.status-checking .status-icon{background:var(--blue-soft);color:var(--muted)}

.dash-card h4{margin:0 0 4px;padding:16px 18px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.dash-row{display:flex;justify-content:space-between;gap:12px;padding:12px 18px;border-top:1px solid var(--line);font-size:14px}
.dash-row span{color:var(--muted)}
.dash-row b,.dash-row a{color:var(--ink);font-weight:600;text-align:right;text-decoration:none;white-space:nowrap}
.dash-row a{text-decoration:underline}
/* The nearest polling place opens the phone's map app. Underlined so it is
   visibly a link on a card where every other value is inert text, but in the
   same ink and weight so it does not shout. */
.dash-map-link{color:var(--ink);font-weight:600;text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px;text-decoration-color:rgba(0,32,91,.35)}
/* This link must be allowed to wrap. .dash-row sets white-space:nowrap on
   both b and a; the rule below lifts it for b but not for a, so a long
   polling place name ("Edinburg — UTRGV Student Academic Center") could not
   break. It stayed one unbreakable line, overflowed its 60% column, and —
   because the value box is justify-content:flex-end — overflowed LEFTWARD,
   printing itself across the "Polling place" label. The row still looked
   deliberate, which is why it survived: it read as a heading with a caption
   under it rather than as two fields colliding. */
.dash-row a.dash-map-link{white-space:normal;overflow-wrap:anywhere}
/* The distance now sits inside the link, so the whole row is one tap target
   instead of a link with a number stranded beside it. It keeps its quiet
   weight and drops the underline, so the name still reads as the link and the
   mileage as an aside. */
.dash-map-link .dash-sub{text-decoration:none;white-space:nowrap}
.dash-map-link:active{color:var(--blue)}
/* "Previously District NN" note under the district pill — context, not a
   category, so it stays quiet muted text rather than joining the pill
   palette. flex-basis:100% drops it onto its own line in the flex row. */
.dash-row .dash-sub{font-size:11.5px;font-weight:600;color:var(--muted);letter-spacing:.01em}
/* The value row centres its items so pills sit level with their text, but a
   smaller parenthetical centred against a 14px value rides high and reads as
   a superscript. Baseline has to be set on the CONTAINER: the value itself is
   a bare text node, an anonymous flex item no selector can reach, and a lone
   align-self:baseline on the parenthetical has no sibling to align against.
   Scoped by :has() so rows that still carry a pill keep centring, and a
   browser without :has() simply keeps today's look. */
.dash-row b:has(> .dash-sub:not(.dash-sub-stacked)){align-items:baseline}
.dash-row b .dash-sub-stacked{flex-basis:100%;text-align:right}
/* Every value on this card is a PASS 4 pill; rows flex-wrap so a long pill
   drops to its own line instead of shrinking or overflowing. */
.dash-row{align-items:center}
.dash-row b{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px;white-space:normal}
/* Label left, value right — the side-by-side reading Marco prefers, kept on
   phones and fitted rather than stacked (he saw the stacked version and asked
   for this back, 2026-09-02). The label is capped at 40% and allowed to wrap,
   so a long value gets the remaining 60% instead of being squeezed to a
   ragged column; both drop a little in size to buy that room. */
@media(max-width:430px){
  .dash-row{gap:10px;padding:11px 16px}
  /* The label does not shrink; it takes its natural width up to 40% and the
     value wraps into whatever is left. Letting both shrink meant a long value
     ("Edinburg — Elections Annex Building") pushed a short label
     ("Polling place") onto two lines while staying on one itself. */
  .dash-row>span{flex:0 0 auto;max-width:40%;font-size:12.5px;line-height:1.35;overflow-wrap:break-word}
  .dash-row b{flex:1 1 auto;min-width:0;font-size:14px;line-height:1.35;overflow-wrap:anywhere}
  .dash-row .dash-sub{font-size:11px}
}

/* Page-2 bottom actions: the officials CTA is the prominent next step; the
   "check a different address" restart sits under it as a quiet secondary. */
.result-actions{display:flex;flex-direction:column;gap:10px;padding:2px 0 4px}
.result-actions .secondary-button{align-self:center;min-width:220px;padding-left:20px;padding-right:20px}
/* The restart action as a quiet centered text link, pushed clear of the
   button above it so the two can't be confused or mis-tapped. */
.result-actions .restart-link{align-self:center;margin-top:10px;min-height:44px;color:var(--muted);font-size:13.5px}
/* Page 4's two quiet escape hatches (restart + report an error) share one
   compact section under a single divider. */
/* Both links sit symmetrically between the divider above and the footer
   rule below — same 10px breathing room top and bottom, nothing dead in
   between, and both in the same darker ink-soft so neither looks disabled. */
.officials-bottom-links{
  border-top:1px solid var(--line);
  margin-top:14px;padding:4px 0;display:flex;flex-direction:column;align-items:center;
}
.officials-bottom-links .restart-link,
.officials-bottom-links .officials-feedback-trigger{
  margin:0;min-height:0;padding:10px 6px;color:var(--ink-soft);font-size:13.5px;font-weight:600;
}
.officials-bottom-links .officials-feedback-panel{width:100%}

/* ---------- Officials page (page 3) ---------- */
.officials-page-header{padding:26px 0 14px;text-align:center}
.officials-page-header h2{font-size:clamp(24px,6vw,32px);font-weight:600;margin:0 0 6px;color:var(--ink);letter-spacing:-.01em}
.officials-page-header p{margin:0;font-size:14.5px;color:var(--muted)}
#officials-page-body{display:flex;flex-direction:column;gap:16px}
.tier-card{padding:0;scroll-margin-top:10px}
/* Title and count read as one unit on the left, chevron pinned right.
   space-between used to spread all three children apart, which left the count
   pill floating at a different x on every card (it tracked the title's width)
   and pushed the title itself hard into the card's 14px corner radius. */
.tier-summary{
  list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:flex-start;
  gap:10px;padding:15px 20px;min-height:56px;
}
.tier-summary::-webkit-details-marker{display:none}
.tier-summary h3{margin:0;font-size:19px;font-weight:600;color:var(--ink);letter-spacing:-.01em}
.tier-summary .tier-meta{display:flex;align-items:center;gap:10px}
/* Verified-count chip styling comes from the PASS 4 pill system. */
.tier-summary::after{
  content:"";width:8px;height:8px;flex:none;
  border-right:1.8px solid var(--muted);border-bottom:1.8px solid var(--muted);
  transform:rotate(45deg);transition:transform .18s ease;margin-left:auto;
}
.tier-card[open]>.tier-summary::after{transform:rotate(-135deg)}
@media(prefers-reduced-motion: reduce){.tier-summary::after{transition:none}}
.tier-card .official-list{padding:0 18px 16px;border-top:1px solid var(--line)}
.officials-pending{padding:40px 0;display:flex;justify-content:center}

.official-row{display:flex;flex-direction:column;gap:2px;padding:12px 0;border-bottom:1px solid var(--line)}
.official-row:last-child{border-bottom:none}
.official-row strong{font-size:15.5px;color:var(--ink);font-weight:600}
.official-row strong a{color:inherit;text-decoration:none}
.official-row strong a:hover{text-decoration:underline}
.official-title{font-size:13px;color:var(--muted);font-weight:600}
/* Term line — "In office since 2023 · Next election 2026". Deliberately
   quieter than the title: it is context, not the answer the visitor came for,
   and it is absent on ~59% of entries, so it must not leave an obvious hole
   in the rows that lack it. */
/* Colors/geometry come from the pill system; this positions the chip on
   its own line under the title. align-self stops the row's column-flex
   from stretching any pill to the card's full width. */
.official-term{margin-top:5px}
.official-row .pill{align-self:flex-start}
.official-contact{font-size:13.5px;color:var(--ink-soft)}
.official-contact a{color:inherit;text-decoration:underline}
.official-note{font-size:12px;color:var(--muted);font-style:italic}
.party-badge{display:inline-block;font-size:10px;font-weight:700;padding:1px 5px;border-radius:5px;vertical-align:middle;letter-spacing:.02em}
/* Personalized "Your commissioner" badge: pill system (pill--yours). */
.party-badge.party-d{background:#E5EEFF;color:#1E4FBF}
.party-badge.party-r{background:var(--campaign-red-soft);color:var(--campaign-red)}
.party-badge.party-other{background:var(--line);color:var(--muted)}
.official-row-empty{padding:12px 0}
.official-row-empty .muted-note{color:var(--muted);font-weight:600;font-size:13.5px}

.official-subgroup-label{
  padding:12px 0 2px;margin-top:4px;font-size:11px;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;color:var(--muted);
}
.official-subgroup-label:first-child{margin-top:0}

/* Population sits centered at the top of each open tier card; its look is
   the PASS 4 population pill — informational, never a solid CTA-blue. */
.population-pill-row{display:flex;justify-content:center;padding:12px 0 4px}

.official-more{border-bottom:1px solid var(--line);padding:6px 0}
.official-more:last-child{border-bottom:none}
.official-more .official-row{border-bottom:none;padding:10px 0 0}
.official-more-toggle{cursor:pointer;font-size:13.5px;font-weight:600;color:var(--campaign-red);list-style:none;padding:10px 0;min-height:44px;display:flex;align-items:center}
.official-more-toggle::-webkit-details-marker{display:none}
.official-more-toggle::before{content:'+ ';font-weight:700;margin-right:2px}
.official-more[open]>.official-more-toggle::before{content:'– '}

/* "About this role" — a quiet per-office disclosure with a 1-2 sentence
   plain-language description of what the office actually does. */
.role-about{margin-top:2px}
.role-about>summary{
  list-style:none;cursor:pointer;display:inline-flex;align-items:center;min-height:32px;
  font-size:12.5px;font-weight:600;color:var(--map-district);
}
.role-about>summary::-webkit-details-marker{display:none}
.role-about>summary::after{content:"▸";margin-left:4px;font-size:10px;transition:transform .15s}
.role-about[open]>summary::after{transform:rotate(90deg)}
.role-about>p{
  margin:6px 0 4px;padding:10px 12px;border-radius:10px;
  background:var(--blue-soft);font-size:13.5px;color:var(--ink-soft);line-height:1.55;
}
@media(prefers-reduced-motion: reduce){.role-about>summary::after{transition:none}}

.officials-feedback{border-top:1px solid var(--line);padding:12px 16px 4px;text-align:center}
.officials-feedback-trigger{background:none;border:none;padding:10px 6px;min-height:44px;font-size:13.5px;font-weight:600;color:var(--muted);text-decoration:underline;cursor:pointer}
.officials-feedback-panel[hidden]{display:none}
.officials-feedback-panel .help-card-inline{margin:0;border-top:none;padding-top:10px}
.officials-feedback-panel .help-card-inline h4{padding:0 0 4px}
.officials-feedback-panel .help-card-inline p{padding:0 0 12px}
.officials-feedback-panel .help-card-inline form{padding:0 0 6px}
.officials-feedback-panel .help-sent-message{padding:0}
.officials-feedback-panel textarea{border:1px solid var(--line);border-radius:8px;margin-bottom:10px;padding:11px 14px;font:inherit;resize:vertical;min-height:64px}
.officials-feedback-cancel{background:none;border:none;padding:10px 0;min-height:44px;font-size:13.5px;font-weight:600;color:var(--muted);text-decoration:underline;cursor:pointer;align-self:flex-start}

.help-card-inline{margin:0 -20px;padding-top:18px;text-align:left}
.help-card-inline h4{padding:0 20px 4px;margin:0;font-size:16px;font-weight:600;color:var(--ink)}
.help-card-inline p{margin:0;padding:0 20px 12px;font-size:13.5px;color:var(--muted);line-height:1.5}
.help-card-inline form{padding:0 20px 18px;display:flex;flex-direction:column;gap:2px}
.help-card-inline .ios-row{border:1px solid var(--line);border-radius:10px;margin-bottom:10px;padding:11px 14px}
/* (0,3,0): the line above sets the border shorthand at (0,2,0) later than the
   generic .ios-row:focus-within, so without this the help form's focus state
   was invisible — fill removed, border never turned navy. */
.help-card-inline .ios-row:focus-within{border-color:var(--ink)}
/* "Add Apt / Unit": a centred link in the form's column; pressing it swaps
   itself for a real field styled like the rows above. Both carry `hidden`
   on the side that is off, and nothing here sets display on them, so the
   attribute keeps doing its job. */
/* The button that opens the help form, directly under the state-lookup one
   it shares a column with. */
.help-open-button{display:flex;align-items:center;justify-content:center;max-width:420px;margin:0 auto 14px}
.help-open-button[hidden]{display:none}
.help-unit-add{align-self:center;margin:-2px 0 10px}
.help-unit-add[hidden],.help-card-inline .ios-row[hidden]{display:none}
.help-card-done{padding-bottom:18px}
.help-sent-message{margin:0;padding:0 20px;font-size:14px;font-weight:600;color:var(--green)}
.consent-row{display:flex;align-items:flex-start;gap:10px;font-size:13.5px;color:var(--ink-soft);margin:6px 0 14px;line-height:1.4}
.consent-row input{margin-top:3px;width:18px;height:18px}
#help-status{font-size:13.5px;font-weight:600;margin:10px 0 0;min-height:16px}
#help-status.is-success{color:var(--green)}
#help-status.is-error{color:var(--red)}

.actions-row{display:grid;grid-template-columns:1fr;gap:10px;margin-top:4px;padding-bottom:8px;text-align:center}
.actions-row .secondary-button{width:100%;font-size:13.5px;padding-left:8px;padding-right:8px}

.check-another-row{padding:2px 4px 4px;text-align:center}
.check-another-row .secondary-button{display:inline-flex;align-items:center;justify-content:center;min-width:220px;padding-left:20px;padding-right:20px}

.details-officials-row{display:grid;grid-template-columns:1fr;gap:14px;align-items:start}

/* ---------- Map ---------- */
.campaign-pin{width:42px;height:42px;border-radius:50% 50% 50% 8px;transform:rotate(-45deg);background:var(--ink);border:4px solid #fff;box-shadow:0 8px 20px rgba(0,32,91,.35);display:grid;place-items:center}
.campaign-pin span{transform:rotate(45deg);color:#fff;font-weight:700;font-size:13px}
.county-label{width:auto!important;height:auto!important;margin:0!important;overflow:visible!important;background:transparent!important;border:0!important;pointer-events:none!important}
/* font-size deliberately NOT !important: map.js's updateLabelScale() sets the
   real size (10-14px by zoom) as an inline style, which an !important rule
   here silently overrode — that was the "labels stuck tiny" bug. This is only
   the pre-first-zoom fallback. */
.county-label span{
  display:block!important;width:max-content!important;max-width:none!important;padding:0!important;overflow:visible!important;
  white-space:nowrap!important;line-height:1!important;letter-spacing:.02em!important;
  color:var(--map-county-label)!important;font-size:11px;font-weight:600!important;
  font-family:var(--font-ui)!important;
  background:transparent!important;border:0!important;box-shadow:none!important;
  transform:translate(-50%,-50%);
  text-shadow:-1px -1px 0 #fff,1px -1px 0 #fff,-1px 1px 0 #fff,1px 1px 0 #fff!important;
  transition:font-size .15s;
}
.leaflet-container{font-family:inherit}

/* Two-finger-pan hint: shown briefly when someone tries to drag the map with
   one finger, since one finger scrolls the page past it instead. */
.map-touch-hint{
  position:absolute;inset:0;display:grid;place-items:center;pointer-events:none;
  z-index:500;opacity:0;transition:opacity .18s ease}
.map-touch-hint.is-visible{opacity:1}
.map-touch-hint span{
  background:rgba(0,32,91,.86);color:#fff;font-size:13px;font-weight:600;
  padding:8px 14px;border-radius:999px;font-family:var(--font-ui)}
@media(prefers-reduced-motion: reduce){.map-touch-hint{transition:none}}

/* OpenStreetMap's credit is a licence condition of using its data, so it
   stays — but as a quiet corner mark rather than Leaflet's default bar.
   Leaflet's own prefix is dropped in map.js; this is the OSM line only. */
.leaflet-control-attribution{
  background:rgba(255,255,255,.72);padding:1px 6px;border-radius:6px 0 0 0;
  font-size:9.5px;line-height:1.5;color:#8A93AB;font-family:var(--font-ui)}
.leaflet-control-attribution a{color:#8A93AB;text-decoration:none}

/* ---------- Map legend ---------- */
/* One row at 375px, never wraps: nowrap on the container plus swatches and
   gaps sized so "TX-15 boundary / Counties / Voting District" fits. */
.map-legend{display:flex;flex-wrap:nowrap;justify-content:center;align-items:center;gap:0 10px;padding:6px 8px}
.map-legend-item{display:inline-flex;align-items:center;gap:4px;color:var(--muted);white-space:nowrap;min-width:0}
.map-legend-swatch{width:13px;height:0;flex:none}
.map-legend-swatch.solid{border-top:3px solid var(--map-district)}
.map-legend-swatch.dashed{border-top:2px dashed var(--map-county)}
.map-legend-swatch.precinct{border-top:2px dashed var(--map-precinct)}
.map-legend-item span{font-size:10.5px;color:var(--ink-soft);font-weight:600}
@media(max-width:360px){.map-legend-item span{font-size:9.5px}.map-legend{gap:0 7px}}
.map-status{position:absolute;left:12px;right:12px;bottom:12px;z-index:500;margin:0;padding:10px 12px;border-radius:8px;background:rgba(255,255,255,.94);color:var(--red);font-size:12.5px;line-height:1.4;box-shadow:var(--shadow-md)}
.map-status:empty{display:none}

/* ---------- Desktop ---------- */
@media(min-width:760px){
  main{max-width:900px;padding:0 24px 16px}
  .hero-copy{padding-top:40px}
  #result-body{display:grid;grid-template-columns:1fr 1fr;align-items:start}
  .map-card,.status-card,#registration-help-card,.details-officials-row,.result-actions,.check-another-row{grid-column:1/-1}
  .map-card #result-map{height:360px}
  .status-card{padding:28px 32px 6px}
  .help-card-inline{margin:0 -32px}
  .help-card-inline h4{padding:0 32px 4px}
  .help-card-inline p{padding:0 32px 12px}
  .help-card-inline form{padding:0 32px 18px}
  .help-sent-message{padding:0 32px}
  #officials-page-body{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}
  .officials-page-actions{grid-column:1/-1}
}
/* ---------- Footer ---------- */
.site-footer{
  /* 14px, not 36px: the page's last section already ends on its own rule,
     so a large margin here read as dead space under "Report an error". */
  max-width:560px;margin:14px auto 0;padding:24px 20px 32px; /* top overridden below */
  border-top:1px solid var(--line);
  text-align:center;
}
.footer-brand-row{margin-bottom:14px}
/* Campaign logo and MyTX15 wordmark as one side-by-side lockup. */
.footer-brand-lockup{display:flex;align-items:center;justify-content:center;gap:10px}
.footer-logo{height:34px;width:auto;display:block;margin:0}
.footer-brand-name{font-size:17px;font-weight:800;letter-spacing:-.01em;color:var(--ink);font-family:var(--font-heading);text-decoration:none}
.footer-brand-name:active{opacity:.8}
.footer-brand-name span{color:var(--blue)}
.footer-tagline{margin:3px 0 0;font-size:12.5px;color:var(--muted)}
.paid-for-box{
  display:block;margin:12px auto 0;padding:9px 16px;max-width:420px;
  border:2px solid var(--ink);border-radius:8px;
  background:transparent;color:var(--ink);
  font-size:12px;font-weight:800;letter-spacing:.03em;text-transform:uppercase;
}

/* No separators between the legal links, tighter rows. */
.footer-nav{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:0 6px;margin-bottom:14px}
.footer-nav button{
  background:none;border:0;padding:4px 10px;min-height:40px;font-size:13px;font-weight:600;
  color:var(--ink-soft);cursor:pointer;text-decoration:none;line-height:1.4;
}
.footer-nav button:hover,.footer-nav button:focus-visible{color:var(--blue)}

.footer-note{margin:0 auto;max-width:440px;padding-top:16px;border-top:1px solid var(--line);font-size:12px;color:var(--muted);line-height:1.6}

.legal-dialog{
  border:0;border-radius:var(--radius-lg);padding:0;max-width:520px;width:92vw;max-height:82vh;
  box-shadow:var(--shadow-lg);
}
.legal-dialog::backdrop{background:rgba(11,18,48,.5);backdrop-filter:blur(2px)}
/* Freeze the page behind an open legal dialog. overflow:hidden alone is
   ignored by iOS Safari once a scroll is already in flight, so the height and
   touch-action pin it too. */
html.has-open-dialog,html.has-open-dialog body{overflow:hidden;height:100%;touch-action:none}
.legal-dialog>div{padding:26px 22px 30px;overflow-y:auto;max-height:82vh}
.legal-dialog h2{font-size:19px;font-weight:800;margin:0 0 14px;color:var(--ink)}
.legal-dialog h4{font-size:14.5px;font-weight:800;margin:18px 0 6px;color:var(--ink)}
.legal-dialog p{font-size:13.5px;color:var(--ink-soft);line-height:1.65;margin:0 0 12px}
.legal-dialog a{color:var(--blue)}
.dialog-close{
  position:sticky;float:right;top:14px;right:14px;margin:14px 14px 0 0;
  width:36px;height:36px;border-radius:50%;border:0;background:#EEF1F7;color:var(--ink-soft);
  font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;
}

@media(min-width:1000px){
  .site-footer,main{max-width:960px}
}

/* =====================================================================
   2026-08 CONTINUOUS-SITE RESTYLE (Marco's direction, reaffirmed)
   Pages 1-4 read as one website: page 1 on pulidoevents.com is a
   look-alike of this app's form, so this block makes the app match that
   design language exactly — white page, Lora serif display (700 roman +
   700 italic, self-hosted; Google Fonts is blocked by our CSP), 2px navy
   cards on 10px radii, quiet #D0D5E0 input borders with a soft navy focus
   ring, solid navy CTA set in the display face. Appended last so it wins
   the cascade over the token system above without rewriting it.
   ===================================================================== */
@font-face{font-family:"Lora";src:url("assets/fonts/Lora-700.woff2") format("woff2");
  font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:"Lora";src:url("assets/fonts/Lora-700-Italic.woff2") format("woff2");
  font-weight:700;font-style:italic;font-display:swap}

body{background:#FFFFFF}
:root{--page:#FFFFFF;--line:#EDEFF5;--radius-md:10px}
h1,h2,h3,.form-heading h1,.map-caption strong,.tier-summary h3{
  font-family:"Lora",Georgia,"Times New Roman",serif;font-weight:700}
.hero-copy h1{font-style:italic;letter-spacing:-.8px}

.ios-card,.intake-card,.dash-card,.tier-card,.status-card,.address-card,.map-card,
.officials-feedback-card,.help-card-inline{
  border:2px solid var(--ink);border-radius:10px;
  box-shadow:0 2px 8px rgba(0,32,91,.06)}

input,textarea,select{border-color:#D0D5E0}
input:focus,textarea:focus{
  outline:none;border-color:var(--ink);box-shadow:0 0 0 4px rgba(0,32,91,.06)}

.cta-button{
  font-family:"Lora",Georgia,serif;font-weight:700;letter-spacing:.3px;
  border:2px solid var(--ink);border-radius:6px;background:var(--ink)}
.cta-button:active{opacity:.92;transform:translateY(1px)}
.secondary-button{border-radius:6px}

.topbar::after{background:#EDEFF5}

/* --- Continuous-site restyle, pass 2: the chrome. Pass 1 above carried the
   tokens (white page, Lora, navy cards); Marco's review said the app still
   read as the old design, and he is right - the header pills, the blue-tint
   chips on the result and officials screens, and the footer were untouched.
   This finishes the job: every piece of shared chrome now matches page 1's
   language exactly - hairline dividers, bordered white chips instead of
   tinted fills, square EN/ES buttons, the bordered uppercase paid-for box. */

/* Header: logo left, square language chips right, hairline below. */
.topbar{border-bottom:1px solid #EDEFF5;padding:18px 16px}
.topbar::after{display:none}
.cycle-badge{display:none!important}
.language-toggle{background:transparent;padding:0;gap:6px}
.language-toggle button{
  border:1.5px solid #D0D5E0;background:#fff;border-radius:5px;
  min-height:34px;padding:6px 11px;font-size:11px;letter-spacing:.5px;color:#777}
.language-toggle button.is-active{
  background:var(--ink);color:#fff;border-color:var(--ink);box-shadow:none}

/* Hero text matches the draft's rhythm. */
.hero-copy h1{letter-spacing:-.8px}
.hero-subtitle,.hero-copy p{color:#555}

/* Chips now come from the PASS 4 pill system at the end of this file —
   one geometry, per-category tints. (A previous pass flattened every chip
   to bordered white, which made district, election, and verified-count
   information all read as the same thing.) */
.party-badge{background:#fff;border:1px solid #D0D5E0}

/* Quiet buttons: white with navy border, matching the CTA family. */
.secondary-button{
  background:#fff;border:1.5px solid var(--ink);color:var(--ink);
  border-radius:6px;font-weight:600}

/* Footer: hairline top, Lora lockup, bordered uppercase paid-for box,
   tiny gray legal row - the draft's footer, verbatim. */
.site-footer{border-top:1px solid #EDEFF5;background:#fff;margin-top:40px;
  padding:40px 20px 20px;text-align:center}
.footer-brand-name{font-family:"Lora",Georgia,serif;font-weight:800;
  font-size:20px;color:var(--ink);letter-spacing:-.3px}
.paid-for-box{
  margin:16px auto 0;padding:12px 20px;max-width:420px;
  border:2px solid var(--ink);border-radius:6px;background:transparent;
  color:var(--ink);font-size:11px;font-weight:800;letter-spacing:.8px;
  text-transform:uppercase;font-family:"Lora",Georgia,serif}
.footer-nav{gap:4px 8px;font-size:10px}
.footer-nav button{color:#777;font-weight:500;font-size:10px;background:none;border:0}
.footer-nav button:hover{color:var(--ink)}
.footer-note{max-width:480px;margin:12px auto 0;padding-top:20px;
  border-top:1px solid #EDEFF5;font-size:12px;color:#777;line-height:1.8}

/* Home map card, matched to the comp: bordered navy card, Lora tag, gray
   caption, the live OSM embed filling the card's width. */
.home-map-card{background:#fff;border:2px solid var(--ink);border-radius:10px;
  box-shadow:0 2px 8px rgba(0,32,91,.06);overflow:hidden;margin:0 auto 64px;padding:0}
.home-map-tag{font-family:"Lora",Georgia,serif;font-size:clamp(22px,3vw,28px);
  font-weight:700;color:var(--ink);letter-spacing:-.3px;padding:24px 28px 0;margin:0;text-align:left}
.home-map-cap{padding:12px 28px 20px;font-size:14px;color:#666;margin:0;line-height:1.6;text-align:left}
.home-map-frame{width:100%;height:500px;border:0;display:block;background:#F7F8F9}
.home-map-cta-row{display:none}
@media(max-width:560px){.home-map-card{margin:0 0 48px;border-radius:8px}
  .home-map-tag{font-size:20px;padding:18px 18px 0}.home-map-cap{padding:10px 18px 16px}}
/* The comp's tag is sentence-case Lora on white; an earlier rule was
   uppercasing it onto a tinted band. */
.home-map-tag{text-transform:none;background:transparent;letter-spacing:-.3px}
.home-map-cap{background:transparent}

/* =====================================================================
   PASS 3 — pages 1 and 2 matched to Marco's mockup (2026-08-14)
   Three things change from pass 2: card headings become large left-aligned
   Lora rather than small centred labels, the benefits block becomes ONE
   card of numbered rows instead of four tiles with green ticks, and the
   district map is a real Leaflet canvas instead of an iframe.
   ===================================================================== */

/* --- Intake card ------------------------------------------------------
   The comp leads each card with a large left-aligned Lora line. The old
   intake-head centred a small heading over a subtitle, which read as a
   form label rather than a section opening. */
.intake-card{padding:28px;border-radius:12px}
.intake-head{margin:0 0 22px;text-align:left}
.intake-head h2{
  font-family:"Lora",Georgia,serif;font-weight:700;
  font-size:clamp(26px,5.2vw,34px);line-height:1.15;letter-spacing:-.5px;
  color:var(--ink);margin:0;text-align:left}
.intake-head small{
  display:block;margin-top:8px;font-size:15px;color:#666;line-height:1.55}

.intake-card .field{margin-bottom:18px}
.intake-card label,.intake-card .field-label{
  display:block;margin-bottom:7px;
  font-size:15px;font-weight:700;color:var(--ink)}
.intake-card label em,.intake-card .field-label em{
  font-style:normal;font-weight:500;color:#666}
.req{color:#AD0B32;font-style:normal;font-weight:700}

.intake-card input{
  width:100%;padding:14px 16px;font-size:16px;
  border:1.5px solid #D0D5E0;border-radius:8px;background:#fff;color:var(--ink)}
.intake-card input::placeholder{color:#9AA3B5}
.dob-shell{border:1.5px solid #D0D5E0;border-radius:8px;padding:0 10px}
/* Two classes deep on purpose: `.intake-card input` above sets width:100%,
   which the three DOB boxes must NOT inherit — they are flex children of a
   shared shell, and at 100% each they push the slashes off the card edge. */
.intake-card .dob-shell input{
  width:44px;flex:none;border:0;padding:14px 0;text-align:center}
.intake-card .dob-shell input:last-of-type{width:64px}
.dob-sep{color:#C2C9D8}

/* Phone and email each get their own row at every width (Marco, 2026-08-14 —
   he saw the two-column version and chose stacked). */
.pair{display:grid;grid-template-columns:1fr;gap:0}

.intake-card .cta-button{
  width:100%;margin-top:6px;padding:17px 20px;font-size:17px;border-radius:8px}

/* --- Benefits: one card, numbered rows --------------------------------
   Was four bordered tiles each with a green check. The comp is a single
   card whose rows are separated by hairlines and led by an outlined red
   numeral, so the list reads as a sequence rather than four claims. */
.home-benefits{
  background:#fff;border:2px solid var(--ink);border-radius:12px;
  box-shadow:0 2px 8px rgba(0,32,91,.06);
  padding:28px 28px 4px;margin:0 auto 28px}
.benefits-lead{
  font-family:"Lora",Georgia,serif;font-weight:700;
  font-size:clamp(24px,4.6vw,30px);line-height:1.2;letter-spacing:-.4px;
  color:var(--ink);margin:0 0 4px;text-align:left}
.benefit-grid{display:block;margin:0;padding:0;gap:0}
.benefit-card{
  display:flex;align-items:flex-start;gap:18px;
  background:transparent;border:0;border-radius:0;box-shadow:none;
  padding:22px 0;margin:0;border-bottom:1px solid #EDEFF5}
.benefit-card:last-child{border-bottom:0}
.benefit-num{
  flex:none;width:42px;height:42px;border-radius:50%;
  border:2px solid #AD0B32;color:#AD0B32;background:#fff;
  display:flex;align-items:center;justify-content:center;
  font-size:14px;font-weight:800;letter-spacing:.02em}
.benefit-text{display:block}
.benefit-text b{
  display:block;font-size:16px;font-weight:700;color:var(--ink);margin-bottom:5px}
.benefit-text span{display:block;font-size:15px;color:#666;line-height:1.6}

/* --- District map (the GitHub Pages embed, same as page 1) ------------ */
/* An earlier pass gave the tag a bottom rule, which the comp does not have —
   there the heading and its caption read as one block above the map. */
.home-map-tag{border-bottom:0}
.home-map-frame{border-top:1px solid #EDEFF5}

/* --- One width for all three cards ------------------------------------
   Marco's spec: the form, map and benefits cards read as the same component
   at every viewport. 560px cap, centred. Without this the desktop media
   query widens main to 900px and the map/benefits cards grow past the form
   (which .info-form already caps at 560). */
.info-form,.home-map-card,.home-benefits{
  max-width:560px;margin-left:auto;margin-right:auto}
.home-map-card{margin-bottom:28px}

/* Helper line pinned to the top of the address-suggestions dropdown. */
.suggest-helper{
  padding:10px 14px;font-size:12px;color:#666;
  border-bottom:1px solid #EDEFF5;line-height:1.5}

/* --- Condensed legal row, all five, every page ------------------------ */
.footer-nav{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:2px 6px;font-size:10px}
.footer-nav button{
  padding:4px 2px;font-size:10px;color:#777;font-weight:500;line-height:1.4}
/* The separator hangs off the END of each item rather than the START of the
   next one. As a ::before it belonged to the following button, so when the
   row wrapped it came down with it and sat as a stray dot at the head of line
   two, in front of "Contact / Support" (Marco, 2026-09-02). As an ::after it
   stays behind on line one, where it reads as "continues", and the fix holds
   at any width instead of only at the one that wraps today. */
.footer-nav button:not(:last-child)::after{
  content:"·";margin-left:8px;color:#C2C9D8}

@media(max-width:560px){
  .intake-card{padding:20px}
  .home-benefits{padding:20px 20px 2px}
  .benefit-card{gap:14px;padding:18px 0}
  .benefit-num{width:36px;height:36px;font-size:13px}
  .home-map-frame{height:380px}
}

/* ======================================================================
   PASS 4 (Marco, 2026-08-14): one pill system, everywhere.
   ONE base class owns geometry; variants change ONLY color. Importance is
   placement and label, never size: no pill grows taller or louder because
   its text matters more. Gray is reserved for unavailable/pending — the
   Coming Soon pill is the only neutral gray on either page.
   Size classes: --sm gives short numerics (40, 0106, Pct 4) a floor so
   they don't shrivel next to text pills; --md for one-word values; long
   values just grow wider at the same height. --wrap is for the one pill
   holding a sentence (CWPP).
   ====================================================================== */
.pill{
  display:inline-flex;align-items:center;justify-content:center;
  box-sizing:border-box;min-height:28px;padding:4px 12px;border-radius:999px;
  font-size:11.5px;font-weight:700;line-height:1.15;letter-spacing:.01em;
  border:1px solid transparent;white-space:nowrap;vertical-align:middle;
  max-width:100%;
}
.pill--sm{min-width:64px}
.pill--md{min-width:92px}
.pill--wrap{white-space:normal;height:auto;line-height:1.35;text-align:center}

/* Category tints: soft background, dark readable text. One category, one
   color — every Next-election pill matches every other, on every official,
   in both languages. */
/* ================= PILL COLORS — EDIT HERE ==========================
   Marco: change any pill's colors by editing ONLY these variables.
   Each pill has two: *-bg is the fill, *-ink is the text. Nothing else
   in the file needs touching — the rules below just read these.
   Rules of the palette: one category = one color everywhere, in both
   languages; gray belongs to pending/unavailable and nothing else.
   ==================================================================== */
:root{
  --pill-district-bg:#D8E7FF;        --pill-district-ink:#1859A9;
  --pill-representative-bg:#E7DAF8;  --pill-representative-ink:#65429B;
  --pill-county-bg:#CFEEE9;          --pill-county-ink:#127A6E;
  --pill-county-precinct-bg:#FFE3C9; --pill-county-precinct-ink:#A85518;
  --pill-voting-precinct-bg:#F9D7E1; --pill-voting-precinct-ink:#A83D5D;
  --pill-house-bg:#D9DDF6;           --pill-house-ink:#4F56A6;
  --pill-senate-bg:#FFEDB3;          --pill-senate-ink:#8A6500;
  --pill-cwpp-bg:#CFF0DE;            --pill-cwpp-ink:#148257;
  --pill-pending-bg:#EEF0F4;         --pill-pending-ink:#687187;
  --pill-verified-bg:#E0D9F8;        --pill-verified-ink:#5B4BC4;
  --pill-election-bg:#C7E9F6;        --pill-election-ink:#17708F;
  --pill-population-bg:#E9F0CF;      --pill-population-ink:#5F7018;
  --pill-yours-bg:#DCE6F9;           --pill-yours-ink:#00205B;
}
.pill--district{background:var(--pill-district-bg);color:var(--pill-district-ink)}
.pill--representative{background:var(--pill-representative-bg);color:var(--pill-representative-ink)}
.pill--county{background:var(--pill-county-bg);color:var(--pill-county-ink)}
.pill--county-precinct{background:var(--pill-county-precinct-bg);color:var(--pill-county-precinct-ink)}
.pill--voting-precinct{background:var(--pill-voting-precinct-bg);color:var(--pill-voting-precinct-ink)}
.pill--house{background:var(--pill-house-bg);color:var(--pill-house-ink)}
.pill--senate{background:var(--pill-senate-bg);color:var(--pill-senate-ink)}
.pill--cwpp{background:var(--pill-cwpp-bg);color:var(--pill-cwpp-ink)}
/* The ONLY gray pill: information that is pending/unavailable. */
.pill--pending{background:var(--pill-pending-bg);color:var(--pill-pending-ink)}
.pill--verified{background:var(--pill-verified-bg);color:var(--pill-verified-ink)}
.pill--election{background:var(--pill-election-bg);color:var(--pill-election-ink)}
.pill--population{background:var(--pill-population-bg);color:var(--pill-population-ink)}
/* Personalized: this official answers to YOUR address. Filled navy tint
   with a navy border — the white/outline-only version read as an empty
   pill (Marco, 2026-08-14). */
.pill--yours{background:var(--pill-yours-bg);border-color:var(--ink);color:var(--pill-yours-ink)}

/* The verified-count chip must never crush the tier title or chevron:
   title and pill both keep their natural size and the header wraps to a
   second line when the two can't share one; the chevron is pinned to the
   right edge out of the flow so wrapping never displaces it. */
.tier-summary{flex-wrap:wrap;position:relative;padding-right:44px}
.tier-summary .pill,.tier-summary h3{flex:none}
.tier-summary::after{position:absolute;right:22px;top:50%;margin:-6px 0 0}
.tier-card[open]>.tier-summary::after{margin-top:-3px}

/* Tier accent: a small colored dot before the level name — five cards stay
   one component, the dot alone carries the level's identity. */
.tier-dot{width:10px;height:10px;border-radius:50%;flex:none}
.tier-card[data-tier="federal"] .tier-dot{background:#1769AA}
.tier-card[data-tier="state"] .tier-dot{background:#7048A8}
.tier-card[data-tier="county"] .tier-dot{background:#168C8C}
.tier-card[data-tier="city"] .tier-dot{background:#C66A28}
.tier-card[data-tier="school"] .tier-dot{background:#B94A68}

/* Inline pills riding an official's name line sit a notch smaller so the
   name stays the loudest thing in its row. Same geometry family. */
.official-row strong .pill{min-height:24px;padding:2px 9px;font-size:10.5px;margin-left:6px}

/* ---- Page 1/page 2 parity: same gaps, same padding (page 1 uses 26px
   between cards and 26/22 card padding; these bring page 2 to match). ---- */
.info-form{margin-bottom:0}
.home-map-card{margin:26px auto}
.home-benefits{margin:26px auto 0}
.intake-card{padding:26px 22px}
.home-benefits{padding:26px 22px 4px}
.home-map-frame{height:440px}
@media(max-width:560px){
  .intake-card{padding:20px 16px}
  .home-benefits{padding:20px 16px 2px}
  .home-map-frame{height:340px}
}

/* Help card inside the status card: pass 2 gave it a full navy border
   (a nested card) but kept the full-bleed negative margins from when it
   was a borderless section — which parked its border 2px from the parent
   card's, reading as two overlapping cards (Marco's page-3 report,
   2026-08-14). Inset it inside the parent's padding instead. */
.help-card-inline{margin:0 0 14px}

/* Page 4's two escape links, per Marco's sketch (2026-08-14): a rule
   above, the two links, a rule below, minimal padding after. Centered,
   with +10% breathing room over the first cut (padding 6->7, 10->11). */
.officials-bottom-links{
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  margin-top:14px;padding:7px 0;align-items:center}
.officials-bottom-links .restart-link,
.officials-bottom-links .officials-feedback-trigger{
  text-decoration:underline;padding:11px 2px;text-align:center}

/* The help form's status line is an empty aria-live slot until something
   is worth saying — while empty it and its margins added ~60px of dead
   space under the Request-help button. */
#help-status:empty{display:none}

/* Footer hugs the page content — the old 40px top margin plus main's own
   16px bottom padding left a dead zone after page 4's bottom links (and
   every other page's last card). The footer's 40px top PADDING stays. */
/* Trimmed twice at Marco's eye: 84px of dead space between "Check a
   Different Address" and the logo, then 52px, now ~38px. The restart link
   keeps its 44px touch height; this is the air around it, not the target. */
.site-footer{margin-top:2px;padding-top:16px}

/* Breathing room between the "Official Texas voter lookup" link and the
   help card that follows it (Marco, 2026-08-14). */
#registration-help-card .help-card-inline{margin-top:28px}

/* ======================================================================
   PASS 5 (Marco, 2026-09-01, post-Vercel): quieter, wider, calmer.
   ====================================================================== */

/* No hairline dividers anywhere — the row spacing carries the separation
   and the cards' navy outlines carry the structure. Applies to the Your
   Details rows, officials rows, benefits rows, and the map card seam. */
.dash-row{border-top:0}
.official-row{border-bottom:0}
.official-more{border-bottom:0}
.benefit-card{border-bottom:0}
.tier-card .official-list{border-top:0}
.home-map-frame{border-top:0}

/* Elected officials read as one scannable column at every width — the
   desktop two-column grid split Federal/State/County into a zigzag the
   eye had to track. One column, the reading order IS the layout. */
#officials-page-body{display:flex;flex-direction:column;gap:16px}

/* The DOB shell already paints one focus ring for the whole group; the
   generic :focus-visible outline was ALSO ringing the individual MM/DD/
   YYYY box inside it — the "weird box highlight". One ring, the shell's. */
.dob-shell input:focus-visible{outline:none}

/* "Where you can vote" — county-published polling sites (data exported
   from the war-room county reference). Groups collapse so the card stays
   scannable; Hidalgo alone lists 23 early-voting and 57 Election-Day sites. */
.polling-card{padding:4px 0 8px}
.polling-card h4{margin:0;padding:16px 18px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
/* Title row with the collapse control, so the card the vote button opens can
   be closed again. The chevron is a rotated CSS square, matching the one the
   tier cards already use. */
.polling-card-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.polling-card-head h4{flex:1}
.polling-collapse{
  flex:none;background:none;border:0;cursor:pointer;
  width:44px;height:44px;margin-right:6px;display:grid;place-items:center}
.polling-collapse span{
  width:9px;height:9px;display:block;
  border-left:2px solid var(--muted);border-top:2px solid var(--muted);
  transform:rotate(45deg);margin-top:4px}
.polling-collapse:active span{border-color:var(--ink)}
.poll-group{padding:0}
.poll-group summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:8px;padding:12px 18px;font-weight:700;font-size:15px;color:var(--ink);min-height:44px}
.poll-group summary::-webkit-details-marker{display:none}
.poll-group summary::after{content:"";width:7px;height:7px;flex:none;margin-left:auto;border-right:1.8px solid var(--muted);border-bottom:1.8px solid var(--muted);transform:rotate(45deg);transition:transform .18s ease}
.poll-group[open]>summary::after{transform:rotate(-135deg)}
@media(prefers-reduced-motion: reduce){.poll-group summary::after{transition:none}}
.poll-group-pending{padding:12px 18px}
.poll-group-pending b{display:block;font-size:15px;color:var(--ink)}
.poll-pending{display:block;font-size:13.5px;color:var(--muted);margin-top:2px}
.poll-hours{margin:0;padding:0 18px 10px;font-size:13px;color:var(--muted);line-height:1.5}
.poll-site{padding:9px 18px}
.poll-site b{display:block;font-size:14.5px;color:var(--ink);font-weight:600}
.poll-site span{display:block;font-size:13px;color:var(--muted);line-height:1.5}
.poll-site-hours{font-size:12.5px}
.poll-source{margin:4px 0 6px;padding:0 18px;font-size:12px;color:var(--muted)}

/* Each site row: text left, one quiet outlined "Directions" action right —
   tappable (44px), never louder than the CTA family. Apple devices get an
   Apple Maps link, everything else Google Maps (result.js directionsUrl). */
.poll-site{display:flex;align-items:center;gap:12px}
.poll-site-text{flex:1;min-width:0}
.poll-site-text b{display:block;font-size:14.5px;color:var(--ink);font-weight:600}
.poll-site-text span{display:block;font-size:13px;color:var(--muted);line-height:1.5}
.poll-directions{
  flex:none;display:inline-flex;align-items:center;min-height:34px;white-space:nowrap;
  padding:5px 13px;border:1.5px solid var(--ink);border-radius:999px;
  font-size:12px;font-weight:700;color:var(--ink);text-decoration:none;
  white-space:nowrap}
.poll-directions:active{background:var(--blue-soft)}

/* Nearest-3 early voting: header row always visible, the rest behind one
   "Show N more" expander. Map markers: small navy ballot pins whose popup
   carries the same name/address/hours/Directions as the card rows. */
.poll-group-open{padding-bottom:4px}
/* Election Day in a precinct-assigned county: the voter's ONE place, named.
   Ruled off on the left so it reads as an answer rather than the first row of
   a list — the county's full list is still one tap below it. */
.poll-assigned{border-left:3px solid var(--campaign-red);background:var(--blue-soft)}
.poll-assigned .poll-group-head{padding-top:14px}
.poll-assigned-note{margin:0;padding:0 18px 6px;font-size:13px;color:var(--ink-soft)}
/* The precinct is on the county's map but not on the list it published. Amber
   rather than red: nothing is broken, the county simply has not said, and the
   voter needs to ring them rather than panic. */
.poll-assigned-unknown{margin:0 18px 8px;padding:9px 11px;border-radius:10px;
  background:var(--amber-soft);border:1px solid #B9770E;
  font-size:13px;line-height:1.45;color:var(--ink)}
.poll-group-head{display:flex;align-items:center;gap:8px;padding:12px 18px 2px;font-size:15px;color:var(--ink)}
.poll-group-head b{font-weight:700}
.poll-more-sites summary{list-style:none;cursor:pointer;min-height:44px;display:flex;align-items:center;justify-content:center;padding:10px 18px;font-size:13.5px;font-weight:700;color:var(--campaign-red)}
/* Centred, with the group chevron off: the + / − before the text already
   says open or closed, and a second indicator on the far right pulled the
   eye away from the words (Marco). */
.poll-group .poll-more-sites summary::after{display:none}
.poll-more-sites summary::-webkit-details-marker{display:none}
.poll-more-sites summary::before{content:"+";margin-right:6px;font-size:16px;line-height:1}
.poll-more-sites[open] summary::before{content:"−"}
.poll-marker{background:none;border:none}
/* Deliberately compact (Marco, 2026-09-02). The card must clear the +/-
   control at the top-left and leave map visible around it — with two-finger
   panning there has to be somewhere to put the fingers, and a popup that
   fills the card is what made an open popup feel like a stuck map. */
.poll-popup{display:flex;flex-direction:column;gap:3px;font-family:var(--font-ui)}
.poll-popup b{font-size:13px;line-height:1.3;color:var(--ink)}
.poll-popup span{font-size:11.5px;color:var(--muted);line-height:1.4}
/* The address, its distance and the Directions button as one centred block,
   ruled off from the name and hours above it. Previously the address was the
   head of a muted 12.5px run-on line — address, room, mileage, all one grey
   sentence — and the button hung off the left edge under it. */
.poll-popup-go{
  margin-top:7px;padding-top:0;border-top:0;
  display:flex;flex-direction:column;align-items:center;gap:2px;text-align:center}
.poll-popup-address{margin:0;font-size:12.5px;font-weight:600;color:var(--ink);line-height:1.35}
.poll-popup-room{margin:0;font-size:11px;color:var(--muted);line-height:1.35}
.poll-popup .poll-directions{margin-top:6px;align-self:center;min-height:30px;padding:4px 12px;font-size:12.5px}

/* ======================================================================
   PASS 5b (Marco, 2026-09-01, round 2)
   ====================================================================== */

/* Elected officials, desktop: the media query's align-items:start survived
   the flex override and shrank every card to its title's width. One
   centered 640px column of full-width cards, every viewport. */
#officials-page-body{align-items:stretch;max-width:640px;margin:0 auto;width:100%}
.officials-page-actions{max-width:640px;margin-left:auto;margin-right:auto;width:100%}

/* The LAST of the gray spacer lines: header hairline, footer seams, the
   bottom-links rules. Spacing does the work. */
.topbar::after{display:none}
.site-footer{border-top:0}
.footer-note{border-top:0}
.officials-bottom-links{border-top:0;border-bottom:0}
/* .help-card-inline's top rule was on this list until 2026-09-01. By then
   pass 2 had made the help card a real bordered card, so `border-top:0` was
   no longer deleting a gray hairline — it was deleting the card's own navy
   top edge, leaving a three-sided outline open at the top (Marco's report).
   The hairline it targeted is gone from the source rule instead. The
   .officials-feedback-panel variant above still suppresses its top border on
   purpose: there the card continues the panel rather than closing a box. */

/* DOB: ONE clean navy border on the shell when focused — no pale halo
   around the shell, no ring/shadow on the box inside it. */
.dob-shell:focus-within{border-color:var(--ink);box-shadow:none}
.dob-shell input:focus,
.dob-shell input:focus-visible{outline:none;box-shadow:none;border:0}

/* Polling row: the nearest site sits right in Your Details; the whole row
   is tappable and jumps to the full card. */
/* The "where you can vote" control, sitting under the polling row it belongs
   to. Every other line on this card is a readout; this is the one thing you
   can press, so it is shaped like a button rather than hiding as a red
   "More ›" inside a value slot. Replaces .dash-row-tap / .dash-more, which
   made the whole row the control while looking like text. */
.dash-vote-row{padding:12px 18px 16px}
/* Every box property stated here, because this button once lost its bottom
   edge to `.ios-card>*:last-child{border-bottom:none}` and, with the edge
   restored by an override, still showed ragged bottom corners in Safari.
   It now sits inside .dash-vote-row so it is never a card's last child, and
   nothing about its border is left for the cascade to reassemble. */
.dash-vote-button{
  display:block;width:100%;margin:0;padding:0 14px;box-sizing:border-box;
  appearance:none;-webkit-appearance:none;
  border:2px solid var(--ink);border-radius:6px}
.dash-vote-button:active{background:var(--blue-soft)}

/* Explicit CWPP verdict inside the polling card. */
.poll-cwpp{margin:2px 18px 10px;padding:10px 12px;border-radius:10px;font-size:13px;line-height:1.55;text-align:center}
.poll-cwpp b{display:block;font-size:13.5px}
.poll-cwpp-yes{background:var(--pill-cwpp-bg);color:var(--pill-cwpp-ink)}
.poll-cwpp-no{background:var(--pill-senate-bg);color:var(--pill-senate-ink)}
.poll-popup-tag{font-size:9px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--pill-cwpp-ink)}

/* Early-voting hours as a three-column table — weekdays, dates, hours — with
   hours right-aligned into one scannable edge. Each hours group is its own
   <tbody>, so the rule between groups is `tbody + tbody` and the day with
   different hours sits below a line by itself. Same markup in the map popup
   and the Where-you-can-vote card; the card wraps it in .poll-hours for its
   gutter. */
.hours-table{border-collapse:collapse;width:100%;font-size:12.5px;line-height:1.45;color:var(--muted);font-family:var(--font-ui)}
.hours-table td{padding:2px 0;vertical-align:baseline;white-space:nowrap}
.hours-table td:nth-child(1){color:var(--ink);font-weight:600;padding-right:12px}
.hours-table td:nth-child(2){padding-right:12px;width:100%}
.hours-table td:nth-child(3){text-align:right;color:var(--ink);font-variant-numeric:tabular-nums}
.hours-table tbody+tbody tr:first-child td{border-top:1px solid var(--line);padding-top:6px}
.hours-table tbody:not(:last-child) tr:last-child td{padding-bottom:5px}
.poll-popup .hours-table{margin:1px 0 0;font-size:11px}
/* Leaflet's default popup padding is generous; reclaim it for map. */
.leaflet-popup-content{margin:10px 14px 10px 12px}
.leaflet-popup-content-wrapper{border-radius:12px}
.poll-hours .hours-table{font-size:13px}

/* ======================================================================
   PASS 5c (Marco, 2026-09-01): officials use the horizontal space.
   Same components at every width — the card fills the page column, and
   inside an open tier the official rows flow into as many 320px columns
   as fit (one on phones, two on tablet, three on wide desktop). Headers,
   population, subgroup labels and the show-more section span the full
   card, so the reading structure stays identical to mobile.
   ====================================================================== */
#officials-page-body{max-width:none}
.officials-page-actions{max-width:none}
.tier-card .official-list{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));
  column-gap:36px}
.population-pill-row,.official-subgroup-label,.official-row-empty,
.official-more,.official-more-note{grid-column:1/-1}
@media(max-width:560px){
  .tier-card .official-list{grid-template-columns:1fr;column-gap:0}
}
/* The show-more section's own rows flow into the same columns. */
.official-more{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));column-gap:36px}
.official-more-toggle{grid-column:1/-1}
@media(max-width:560px){.official-more{grid-template-columns:1fr;column-gap:0}}

/* SOS handoff for unregistered visitors: the official-site button, then
   each already-collected value as a tap-to-copy chip — true prefill is
   impossible (their portal ignores URL params; browsers forbid cross-site
   form filling), so copy-paste is the honest maximum. */
.sos-handoff{margin:4px 0 14px;text-align:center}
/* An anchor styled as a .cta-button inherits display:block, which leaves its
   label sitting at the top of the 52px box instead of centred in it — the
   reason .cta-button.link-button exists. This rule came after that one and
   put display:block back, so the SOS button lost the centring. Flex here
   does both jobs at once. */
.sos-button{display:flex;align-items:center;justify-content:center;max-width:420px;margin:0 auto 10px}

/* Common questions. Collapsed by default so page 1 keeps the shape Marco
   signed off on; <details> opens without JavaScript, which is also what lets
   a crawler that never runs scripts read the answers. */
.home-faq{margin:22px 0 4px}
.home-faq-heading{font-size:15px;letter-spacing:.02em;color:var(--muted);
  text-transform:uppercase;font-family:var(--font-ui);font-weight:700;margin:0 0 10px}
.faq-item{border-top:1px solid var(--line);padding:2px 0}
.faq-item:last-child{border-bottom:1px solid var(--line)}
.faq-item summary{list-style:none;cursor:pointer;padding:13px 26px 13px 0;position:relative;
  font-weight:600;font-size:14.5px;color:var(--ink);min-height:44px;display:flex;align-items:center}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item summary::after{content:'';position:absolute;right:6px;top:50%;width:8px;height:8px;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:translateY(-70%) rotate(45deg);transition:transform .15s}
.faq-item[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
.faq-item p{margin:0 0 14px;font-size:14px;line-height:1.55;color:var(--ink-soft)}
