// Home page + Find My Reps + the compact PoliticianMiniCard used in both.

// ─── Mini card (compact baseball card) ──────────────────────────────────
function PoliticianMiniCard({ id, onClick, compact }) {
  const p = window.CV_DATA.POLITICIANS[id] || window.CV_DATA.STATE_REPS.find(x => x.id === id);
  if (!p) return null;
  const party = partyOf(p.party);
  const dist = p.district ? `${p.state}-${p.district}` : p.state;
  return (
    <button type="button" className={`cv-mini ${compact ? "cv-mini-compact" : ""}`} onClick={() => onClick && onClick(p.id)}>
      <div className="cv-mini-photo">
        <Avatar photo={p.photo} name={p.name} size={compact ? 56 : 68} shape="rounded" party={p.party} />
        <span className="cv-mini-party" style={{ background: party.color }} aria-label={p.party} />
      </div>
      <div className="cv-mini-body">
        <div className="cv-mini-name">{p.name}</div>
        <div className="cv-mini-office">
          <PartyTag party={p.party} />
          <span>{p.office} · {dist}</span>
        </div>
        <div className="cv-mini-stats">
          <span className="cv-mini-stat"><b>{p.years}y</b><span>tenure</span></span>
          <span className="cv-mini-stat"><b>{p.loyalty}%</b><span>loyalty</span></span>
          <span className="cv-mini-stat"><b>${p.raised}</b><span>raised</span></span>
        </div>
        {p.conflicts > 0 && <div className="cv-mini-flag">{p.conflicts} conflict flag{p.conflicts > 1 ? "s" : ""}</div>}
      </div>
      <span className="cv-mini-arrow" aria-hidden="true">→</span>
    </button>
  );
}

// ─── Home ───────────────────────────────────────────────────────────────
function Home({ onNavigate }) {
  const [addr, setAddr] = React.useState("");
  const [showCompare, setShowCompare] = React.useState(false);

  // Random featured politicians — shuffled fresh every page load.
  // Pulled from the full pool of sitting members (no candidates).
  const [shuffleSeed, setShuffleSeed] = React.useState(0);
  const featured = React.useMemo(() => {
    const pool = Object.values(window.CV_DATA.POLITICIANS)
      .filter(p => !p.candidate)
      .map(p => p.id);
    const shuffled = [...pool];
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled.slice(0, 4);
  }, [shuffleSeed]);

  return (
    <div className="cv-home" data-screen-label="01 Home">
      <section className="cv-hero">
        <div className="cv-hero-stamp">
          <span className="cv-hero-stamp-l">Issue №</span>
          <span className="cv-hero-stamp-v">001</span>
          <span className="cv-hero-stamp-d">— Updated daily —</span>
        </div>
        <h1 className="cv-hero-title">
          Every politician,<br/>same file.
        </h1>
        <p className="cv-hero-sub">
          A free, nonpartisan reference for who represents you, how they vote, who funds them,
          and what the public record says. <strong>No opinion. Just facts.</strong>
        </p>
        <form className="cv-hero-form" onSubmit={(e) => { e.preventDefault(); onNavigate("reps", { addr }); }}>
          <div className="cv-hero-input">
            <PinIcon2 />
            <input
              type="text"
              placeholder="Enter your address — find your reps"
              value={addr}
              onChange={(e) => setAddr(e.target.value)}
            />
            {addr === "" && (
              <button type="button" className="cv-hero-try" onClick={() => setAddr("160 Cooper St, Bronx, NY 10464")}>
                try a sample address
              </button>
            )}
          </div>
          <button type="submit" className="cv-btn cv-btn-primary cv-btn-lg">
            Find my reps →
          </button>
        </form>
        <div className="cv-hero-meta">
          <span><CheckIcon2/> WCAG-AA</span>
          <span><CheckIcon2/> Mobile-first</span>
          <span><CheckIcon2/> Every fact links to its source</span>
          <span><CheckIcon2/> No ads, no tracking, no agenda</span>
        </div>
      </section>

      <section className="cv-home-section cv-now">
        <div className="cv-home-section-hd">
          <div>
            <h2 className="cv-home-section-title">Happening now</h2>
            <div className="cv-home-section-sub">A living dashboard of what's on the floor, in the hearing room, and in court — not just the archive.</div>
          </div>
        </div>
        <div className="cv-now-grid">
          <div className="cv-now-col">
            <div className="cv-now-col-hd">
              <h3 className="cv-now-col-title">Bills up this week</h3>
              <SourceLink label="House &amp; Senate schedules"/>
            </div>
            <div className="cv-now-list">
              {(window.CV_DATA.UPCOMING_BILLS || []).map((b, i) => (
                <button key={i} type="button" className="cv-now-bill cv-link-bill" onClick={() => onNavigate("bill", { id: "hr7024" })}>
                  <span className="cv-now-bill-day">
                    <span className="cv-now-bill-mon">{fmtMonth(b.date)}</span>
                    <span className="cv-now-bill-dnum">{fmtDay(b.date)}</span>
                  </span>
                  <span className="cv-now-bill-main">
                    <span className="cv-now-bill-no">{b.bill} · {b.chamber}</span>
                    <span className="cv-now-bill-t">{b.title}</span>
                    <span className="cv-now-bill-meta">
                      <span className="cv-now-bill-status">{b.status}</span>
                      {(b.issues || []).map(iss => <span key={iss} className="cv-prom-issue">{iss}</span>)}
                    </span>
                  </span>
                  <span className="cv-now-bill-arrow" aria-hidden="true">→</span>
                </button>
              ))}
            </div>
          </div>
          <div className="cv-now-col">
            <div className="cv-now-col-hd">
              <h3 className="cv-now-col-title">Active legal & enforcement</h3>
              <SourceLink label="FEC · House Ethics · PACER"/>
            </div>
            <div className="cv-now-list">
              {(window.CV_DATA.ACTIVE_LEGAL || []).map((l, i) => (
                <div key={i} className={`cv-now-legal cv-now-sev-${l.severity}`}>
                  <div className="cv-now-legal-type">{l.type}</div>
                  <div className="cv-now-legal-who">vs. {l.who}</div>
                  <div className="cv-now-legal-sum">{l.summary}</div>
                  <div className="cv-now-legal-meta">
                    <span>Filed {fmtDate(l.filed)}</span>
                    <span className="cv-ballot-hd-dot">·</span>
                    <span className="cv-now-legal-status">{l.status}</span>
                    <SourceLink label="docket"/>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </section>

      <section className="cv-home-section">
        <div className="cv-home-section-hd">
          <div>
            <h2 className="cv-home-section-title">Random spotlight</h2>
            <div className="cv-home-section-sub">Shuffled fresh on every load · no editorial bias, no popularity sort.</div>
          </div>
          <div className="cv-home-section-r">
            <button type="button" className="cv-link" onClick={() => setShuffleSeed(s => s + 1)}>↻ Reshuffle</button>
            <a className="cv-link" href="#" onClick={(e) => e.preventDefault()}>Browse all 535 →</a>
          </div>
        </div>
        <div className="cv-home-grid">
          {featured.map((id) => (
            <PoliticianMiniCard key={id} id={id} onClick={(pid) => onNavigate("politician", { id: pid })} />
          ))}
        </div>
      </section>

      <section className="cv-home-section cv-vs-promo">
        <div className="cv-vs-promo-l">
          <div className="cv-vs-promo-eyebrow">Head-to-head</div>
          <h2 className="cv-home-section-title">Compare any two politicians.</h2>
          <p className="cv-vs-promo-sub">
            See agreement rates, side-by-side donor lists, and the donors that funded
            <em> both</em> of them. The shared-donor file is the part you screenshot.
          </p>
          <div className="cv-vs-promo-actions">
            <CVButton variant="primary" onClick={() => onNavigate("vs", { a: "fetterman", b: "cruz" })}>
              Open Fetterman vs Cruz →
            </CVButton>
            <CVButton variant="ghost" onClick={() => onNavigate("vs", { a: "aoc", b: "cruz" })}>
              AOC vs Cruz
            </CVButton>
          </div>
        </div>
        <div className="cv-vs-promo-r">
          <MiniVSPreview/>
        </div>
      </section>

      <section className="cv-home-section">
        <div className="cv-home-section-hd">
          <h2 className="cv-home-section-title">What's in a card</h2>
        </div>
        <div className="cv-pillars">
          {PILLARS.map((p, i) => (
            <div key={i} className="cv-pillar">
              <div className="cv-pillar-num">{String(i + 1).padStart(2, "0")}</div>
              <div className="cv-pillar-name">{p.name}</div>
              <p className="cv-pillar-desc">{p.desc}</p>
            </div>
          ))}
        </div>
      </section>
    </div>
  );
}

const PILLARS = [
  { name: "Votes",         desc: "Every roll call, position, and outcome. Linked to the official record." },
  { name: "Money",         desc: "Donors, industries, and the small-dollar / PAC split — straight from the FEC." },
  { name: "Trades",        desc: "STOCK Act trades, late filings, and committee-overlap flags." },
  { name: "Lobbying",      desc: "Firms targeting their committees, with revolving-door tracking." },
  { name: "Accountability",desc: "Conflict flags, group ratings, ethics history. Evidence linked, never inferred." },
  { name: "Contact",       desc: "Office address, phone, and the form that actually goes to a staffer." },
];

function MiniVSPreview() {
  const A = window.CV_DATA.POLITICIANS.fetterman;
  const B = window.CV_DATA.POLITICIANS.cruz;
  return (
    <div className="cv-mini-vs">
      <div className="cv-mini-vs-card">
        <div className="cv-mini-vs-row">
          <Avatar photo={A.photo} name={A.name} size={44} shape="circle" party={A.party}/>
          <div className="cv-mini-vs-name">{A.short}</div>
          <PartyTag party={A.party} />
        </div>
        <div className="cv-mini-vs-divider"><span>VS</span></div>
        <div className="cv-mini-vs-row">
          <Avatar photo={B.photo} name={B.name} size={44} shape="circle" party={B.party}/>
          <div className="cv-mini-vs-name">{B.short}</div>
          <PartyTag party={B.party} />
        </div>
        <div className="cv-mini-vs-stats">
          <div><b>36.8%</b><span>agreement</span></div>
          <div><b>47</b><span>shared donors</span></div>
          <div><b>847</b><span>shared votes</span></div>
        </div>
      </div>
    </div>
  );
}

// ─── Ballot view (formerly Find My Reps) ────────────────────────────────
function FindMyReps({ initialAddr = "", onNavigate }) {
  const data = window.CV_DATA;
  const [addr, setAddr] = React.useState(initialAddr || data.BALLOT.address);
  const [submitted, setSubmitted] = React.useState(true);
  const [saved, setSaved] = React.useState(false);

  const ballot = data.BALLOT;

  return (
    <div className="cv-ballot" data-screen-label="04 Ballot">
      <header className="cv-ballot-hd">
        <div className="cv-ballot-hd-l">
          <div className="cv-ballot-hd-eyebrow">Your ballot</div>
          <h1 className="cv-ballot-hd-title">
            {ballot.election.name}<br/>
            <span className="cv-ballot-hd-date">{fmtBallotDate(ballot.election.date)}</span>
          </h1>
          <div className="cv-ballot-hd-meta">
            <span>{ballot.district}</span>
            <span className="cv-ballot-hd-dot">·</span>
            <span>{ballot.precinct}</span>
          </div>
        </div>
        <div className="cv-ballot-hd-r">
          <CVButton
            variant={saved ? "ghost" : "primary"}
            onClick={() => setSaved(true)}
          >
            <BookmarkIcon/>
            {saved ? "Saved for Election Day" : "Save for Election Day"}
          </CVButton>
          {saved && <div className="cv-ballot-saved-note">Available offline · pull up at the booth</div>}
        </div>
      </header>

      <form className="cv-ballot-form" onSubmit={(e) => { e.preventDefault(); setSubmitted(true); }}>
        <div className="cv-hero-input cv-reps-input">
          <PinIcon2 />
          <input
            type="text"
            value={addr}
            onChange={(e) => setAddr(e.target.value)}
            placeholder="Address OR just a ZIP code (e.g. 10464)"
            inputMode="text"
          />
          {isZipOnly(addr) && (
            <span className="cv-ballot-zip-hint">
              ZIP {addr} · {zipDistricts(addr).length} district{zipDistricts(addr).length === 1 ? "" : "s"} match
            </span>
          )}
        </div>
        <button type="submit" className="cv-btn cv-btn-ghost">Update ballot</button>
      </form>
      {isZipOnly(addr) && zipDistricts(addr).length > 1 && (
        <div className="cv-ballot-zip-note">
          <PinIcon2/>
          This ZIP code covers <strong>{zipDistricts(addr).length} congressional districts</strong> ({zipDistricts(addr).join(", ")}).
          Enter your full address for an exact match.
        </div>
      )}
      <p className="cv-reps-disclaimer">
        Address used only to look up districts and races. Not stored, not logged, not sent anywhere.
      </p>

      <VotingLogistics logistics={ballot.logistics} election={ballot.election} />

      <div className="cv-ballot-races">
        {ballot.races.map((race) => (
          race.kind === "proposition"
            ? <BallotProposition key={race.id} race={race} />
            : <BallotRace key={race.id} race={race} onNavigate={onNavigate} />
        ))}
      </div>

      <footer className="cv-ballot-bottom">
        <div className="cv-ballot-bottom-l">
          <div className="cv-ballot-bottom-eyebrow">Every candidate, same file.</div>
          <p>
            Every name on your ballot gets a card. Incumbents and challengers, frontrunners and long-shots.
            If a section has no data — first-time candidate, no voting record — it isn't padded with filler.
            What's missing is the data, not the candidate.
          </p>
        </div>
        <CVButton variant="ghost" onClick={() => window.print()}>
          <PrinterIcon/> Print sample ballot
        </CVButton>
      </footer>

      <TransparencyScorecard id="ny-state-assembly" onNavigate={onNavigate}/>
    </div>
  );
}

// ─── A race with 2+ candidates ──────────────────────────────────────────
function BallotRace({ race, onNavigate }) {
  const cands = race.candidates.map((c) => ({
    ...c,
    data: window.CV_DATA.POLITICIANS[c.id] || window.CV_DATA.STATE_REPS.find(x => x.id === c.id),
  }));
  const valid = cands.filter(c => c.data);
  // Two-candidate race: simple direct compare
  // 3+ candidates: multi-select checkboxes
  const isPrimary = valid.length >= 3;
  const [selected, setSelected] = React.useState(() => new Set(valid.map(c => c.id))); // default: all selected
  const toggle = (id) => setSelected(prev => {
    const next = new Set(prev);
    if (next.has(id)) next.delete(id); else next.add(id);
    return next;
  });
  const goVS = (ids) => onNavigate("vs", { ids });
  const selectedIds = Array.from(selected);

  return (
    <section className="cv-race" data-screen-label={`Race · ${race.title}`}>
      <header className="cv-race-hd">
        <div className="cv-race-hd-l">
          <div className="cv-race-hd-eyebrow">
            {race.kind === "federal"  ? "Federal"
            : race.kind === "state"   ? "State"
            : race.kind === "primary" ? "Primary election"
            : "Local"}
          </div>
          <h2 className="cv-race-hd-title">{race.title}</h2>
          <div className="cv-race-hd-meta">
            <span>{race.jurisdiction}</span>
            {race.term && <><span className="cv-ballot-hd-dot">·</span><span>{race.term} term</span></>}
            {race.electionDate && <><span className="cv-ballot-hd-dot">·</span><span>{fmtDate(race.electionDate)}</span></>}
          </div>
        </div>
        <div className="cv-race-hd-r">
          <span className="cv-race-count">{valid.length} candidate{valid.length === 1 ? "" : "s"}</span>
        </div>
      </header>

      {isPrimary ? (
        <PrimaryRace
          cands={valid}
          selected={selected}
          toggle={toggle}
          onOpen={(id) => onNavigate("politician", { id })}
        />
      ) : (
        <div className={`cv-race-cands cv-race-cands-${Math.min(valid.length, 3)}`}>
          {valid.map((c, i) => (
            <React.Fragment key={c.id}>
              <BallotCandidate cand={c} onOpen={() => onNavigate("politician", { id: c.id })} />
              {i === 0 && valid.length === 2 && (
                <div className="cv-race-vs" aria-hidden="true">
                  <span>VS</span>
                </div>
              )}
            </React.Fragment>
          ))}
        </div>
      )}

      <div className="cv-race-foot">
        {isPrimary ? (
          <>
            <div className="cv-race-multi-tally">
              <span><strong>{selectedIds.length}</strong> of {valid.length} selected.</span>
              {selectedIds.length < 2 && <span className="cv-race-multi-hint"> Pick at least 2.</span>}
            </div>
            <CVButton variant="primary" onClick={() => selectedIds.length >= 2 && goVS(selectedIds)}>
              <CompareIcon2/> Compare selected ({selectedIds.length}) →
            </CVButton>
          </>
        ) : (
          <>
            <span className="cv-race-foot-hint">Side-by-side comparison · issue-filterable.</span>
            <CVButton variant="primary" onClick={() => goVS(valid.map(c => c.id))}>
              <CompareIcon2/> Compare these candidates →
            </CVButton>
          </>
        )}
      </div>
    </section>
  );
}

function PrimaryRace({ cands, selected, toggle, onOpen }) {
  return (
    <div className="cv-race-primary-cands">
      {cands.map((c) => (
        <PrimaryCandidate
          key={c.id}
          cand={c}
          checked={selected.has(c.id)}
          onToggle={() => toggle(c.id)}
          onOpen={() => onOpen(c.id)}
        />
      ))}
    </div>
  );
}

function PrimaryCandidate({ cand, checked, onToggle, onOpen }) {
  const p = cand.data;
  return (
    <div className={`cv-pcand ${checked ? "is-on" : ""}`}>
      <label className="cv-pcand-check">
        <input type="checkbox" checked={checked} onChange={onToggle}/>
        <span className="cv-pcand-cbox" aria-hidden="true"/>
      </label>
      <button type="button" className="cv-pcand-body" onClick={onOpen}>
        <Avatar photo={p.photo} name={p.name} size={56} shape="rounded" party={p.party}/>
        <div className="cv-pcand-meta">
          <div className="cv-pcand-name">{p.name}</div>
          <div className="cv-pcand-sub">
            <PartyTag party={p.party}/>
            <span>{cand.meta}</span>
          </div>
          <div className="cv-pcand-stats">
            {p.raised && <span><b>${p.raised}</b><span>raised</span></span>}
            {p.smallDollar != null && <span><b>{p.smallDollar}%</b><span>small-$</span></span>}
            {p.pacShare != null && <span><b>{p.pacShare}%</b><span>PAC</span></span>}
          </div>
        </div>
        <span className="cv-pcand-open" aria-hidden="true">→</span>
      </button>
    </div>
  );
}

function BallotCandidate({ cand, onOpen }) {
  const p = cand.data;
  const party = partyOf(p.party);
  return (
    <button type="button" className="cv-bcand" onClick={onOpen}>
      <div className="cv-bcand-photo">
        <Avatar photo={p.photo} name={p.name} size={72} shape="rounded" party={p.party}/>
      </div>
      <div className="cv-bcand-body">
        <div className="cv-bcand-role">
          {cand.role}
          {p.candidate && <span className="cv-bcand-first">first-time candidate</span>}
        </div>
        <div className="cv-bcand-name">{p.name}</div>
        <div className="cv-bcand-party">
          <PartyTag party={p.party}/>
          <span>{cand.meta}</span>
        </div>
        <div className="cv-bcand-stats">
          {p.years != null && <span className="cv-bcand-stat"><b>{p.years}y</b><span>tenure</span></span>}
          {p.loyalty != null && <span className="cv-bcand-stat"><b>{p.loyalty}%</b><span>loyalty</span></span>}
          {p.raised && <span className="cv-bcand-stat"><b>${p.raised}</b><span>raised</span></span>}
          {p.candidate && p.smallDollar != null && (
            <span className="cv-bcand-stat"><b>{p.smallDollar}%</b><span>small-dollar</span></span>
          )}
        </div>
        <div className="cv-bcand-open">
          Open full card <span aria-hidden="true">→</span>
        </div>
      </div>
    </button>
  );
}

// ─── A ballot proposition (no candidates, just summary + Y/N implications)
function BallotProposition({ race }) {
  return (
    <section className="cv-race cv-race-prop" data-screen-label={`Race · ${race.title}`}>
      <header className="cv-race-hd">
        <div className="cv-race-hd-l">
          <div className="cv-race-hd-eyebrow">Statewide proposition</div>
          <h2 className="cv-race-hd-title">{race.title}</h2>
          <div className="cv-race-hd-meta">
            <span>{race.jurisdiction}</span>
          </div>
        </div>
      </header>
      <div className="cv-prop-body">
        <p className="cv-prop-sum">{race.summary}</p>
        <div className="cv-prop-implications">
          <div className="cv-prop-side cv-prop-yes">
            <div className="cv-prop-side-l">A YES vote means</div>
            <div className="cv-prop-side-v">{race.yesMeans}</div>
          </div>
          <div className="cv-prop-side cv-prop-no">
            <div className="cv-prop-side-l">A NO vote means</div>
            <div className="cv-prop-side-v">{race.noMeans}</div>
          </div>
        </div>
        <div className="cv-prop-foot">
          <SourceLink label={race.sourceLabel}/>
        </div>
      </div>
    </section>
  );
}

function fmtBallotDate(s) {
  const d = new Date(s);
  return d.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" });
}
function fmtDate(s) {
  if (!s) return "";
  const d = new Date(s);
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
}

function BookmarkIcon() { return <svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M3 1.5H11V12.5L7 9.5L3 12.5V1.5Z" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round" fill={"currentColor"} fillOpacity="0.18"/></svg>; }
function CompareIcon2() { return <svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true"><rect x="1.5" y="2.5" width="4" height="9" stroke="currentColor" strokeWidth="1.4"/><rect x="8.5" y="2.5" width="4" height="9" stroke="currentColor" strokeWidth="1.4"/></svg>; }
function PrinterIcon() { return <svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true"><rect x="3" y="1.5" width="8" height="3.5" stroke="currentColor" strokeWidth="1.3"/><rect x="2" y="5" width="10" height="5" stroke="currentColor" strokeWidth="1.3"/><rect x="3.5" y="8.5" width="7" height="4" stroke="currentColor" strokeWidth="1.3"/></svg>; }

// ─── ZIP helpers ────────────────────────────────────────────────────────
function isZipOnly(s) {
  return /^\s*\d{5}(\s*-\s*\d{4})?\s*$/.test(s || "");
}
// Stub: in production this is a real geo lookup. For the demo a couple
// of ZIPs span multiple districts so we can demo the disambiguation note.
function zipDistricts(zip) {
  const z = (zip || "").trim().slice(0, 5);
  const TABLE = {
    "10464": ["NY-14"],
    "10001": ["NY-12", "NY-10"],
    "10075": ["NY-12", "NY-13"],
    "94102": ["CA-11", "CA-12"],
  };
  return TABLE[z] || ["NY-14"];
}

// ─── Voting Logistics Hub ───────────────────────────────────────────────
function VotingLogistics({ logistics, election }) {
  const [alerts, setAlerts] = React.useState({ reg30: false, ev7: false, eve1: false });
  const [calMenu, setCalMenu] = React.useState(null);
  if (!logistics) return null;

  const day = election.date;
  const daysToVote = Math.max(0, Math.floor((new Date(day) - Date.now()) / 86400000));

  const events = [
    { id: "reg",   label: "Registration deadline", date: logistics.registrationDeadline, when: `Last day to register · ${fmtDate(logistics.registrationDeadline)}`, key: "reg30",  alertHint: "30 days before" },
    { id: "evs",   label: "Early voting starts",   date: logistics.earlyVotingStart,     when: `Through ${fmtDate(logistics.earlyVotingEnd)}`,                 key: "ev7",   alertHint: "7 days before" },
    { id: "mail",  label: "Mail-in request",       date: logistics.mailRequestDeadline,  when: `Ballot must be received by ${fmtDate(logistics.mailReceivedBy)}` },
    { id: "eday",  label: "ELECTION DAY",          date: day,                            when: `${logistics.polling?.hours || "Polls open"} · ${daysToVote} day${daysToVote === 1 ? "" : "s"} away`, key: "eve1", alertHint: "1 day before" },
  ];

  return (
    <section className="cv-logistics">
      <header className="cv-logistics-hd">
        <div>
          <div className="cv-logistics-eyebrow">Election logistics</div>
          <h2 className="cv-logistics-title">Your pre-flight checklist</h2>
          <div className="cv-logistics-sub">Everything you need to vote. Read top to bottom — no step is optional.</div>
        </div>
        <div className="cv-logistics-countdown">
          <div className="cv-logistics-countdown-n">{daysToVote}</div>
          <div className="cv-logistics-countdown-l">days to Election Day</div>
        </div>
      </header>

      {logistics.polling && (
        <div className="cv-logistics-block cv-logistics-polling">
          <div className="cv-logistics-block-l">Your polling place</div>
          <div className="cv-logistics-poll">
            <div className="cv-logistics-poll-meta">
              <div className="cv-logistics-poll-name">{logistics.polling.name}</div>
              <div className="cv-logistics-poll-addr">{logistics.polling.address}</div>
              <div className="cv-logistics-poll-sub">
                <span>{logistics.polling.hours}</span>
                <span className="cv-ballot-hd-dot">·</span>
                <span>{logistics.polling.mapHint}</span>
              </div>
            </div>
            <a className="cv-btn cv-btn-ghost" href={`https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(logistics.polling.address)}`} target="_blank" rel="noopener" onClick={(e) => e.preventDefault()}>
              <PinIcon2/> Open in maps
            </a>
          </div>
        </div>
      )}

      <div className="cv-logistics-block">
        <div className="cv-logistics-block-l">Key dates</div>
        <div className="cv-logistics-dates">
          {events.map((ev) => (
            <div key={ev.id} className={`cv-logistics-date ${ev.id === "eday" ? "cv-logistics-date-eday" : ""}`}>
              <div className="cv-logistics-date-c">
                <div className="cv-logistics-date-m">{fmtMonth(ev.date)}</div>
                <div className="cv-logistics-date-d">{fmtDay(ev.date)}</div>
              </div>
              <div className="cv-logistics-date-meta">
                <div className="cv-logistics-date-l">{ev.label}</div>
                <div className="cv-logistics-date-w">{ev.when}</div>
              </div>
              <button
                type="button"
                className="cv-logistics-cal"
                onClick={(e) => { e.stopPropagation(); downloadICS(ev, election); }}
              >
                <CalIcon/> Add to calendar
              </button>
            </div>
          ))}
        </div>
      </div>

      {logistics.dropOffLocations && logistics.dropOffLocations.length > 0 && (
        <div className="cv-logistics-block">
          <div className="cv-logistics-block-l">Mail-in drop-off</div>
          <div className="cv-logistics-drops">
            {logistics.dropOffLocations.map((d, i) => (
              <div key={i} className="cv-logistics-drop">
                <PinIcon2/>
                <div>
                  <div className="cv-logistics-drop-name">{d.name}</div>
                  <div className="cv-logistics-drop-addr">{d.address}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      <div className="cv-logistics-block">
        <div className="cv-logistics-block-l">Remind me</div>
        <div className="cv-logistics-alerts">
          {[
            { k: "reg30", label: "30 days before registration deadline", hint: `~${fmtDate(addDays(logistics.registrationDeadline, -30))}` },
            { k: "ev7",   label: "7 days before early voting starts",     hint: `~${fmtDate(addDays(logistics.earlyVotingStart, -7))}` },
            { k: "eve1",  label: "1 day before Election Day",             hint: `~${fmtDate(addDays(day, -1))}` },
          ].map((row) => (
            <label key={row.k} className="cv-logistics-alert">
              <input
                type="checkbox"
                checked={!!alerts[row.k]}
                onChange={(e) => setAlerts(a => ({ ...a, [row.k]: e.target.checked }))}
              />
              <span className="cv-pcand-cbox" aria-hidden="true"/>
              <span>
                <div className="cv-logistics-alert-l">{row.label}</div>
                <div className="cv-logistics-alert-h">Notify me on {row.hint} · email or push</div>
              </span>
            </label>
          ))}
        </div>
        {Object.values(alerts).some(Boolean) && (
          <div className="cv-logistics-alerts-confirm">
            <CheckIcon2/> {Object.values(alerts).filter(Boolean).length} reminder{Object.values(alerts).filter(Boolean).length === 1 ? "" : "s"} set. You'll be notified — no signups, no spam.
          </div>
        )}
      </div>
    </section>
  );
}

// ─── ICS / calendar helpers ─────────────────────────────────────────────
function downloadICS(ev, election) {
  const date = ev.date.replace(/-/g, "");
  const uid = `cv-${ev.id}-${date}@clarionvote.com`;
  const ics = [
    "BEGIN:VCALENDAR",
    "VERSION:2.0",
    "PRODID:-//ClarionVote//Voting Logistics//EN",
    "BEGIN:VEVENT",
    `UID:${uid}`,
    `DTSTAMP:${date}T000000Z`,
    `DTSTART;VALUE=DATE:${date}`,
    `SUMMARY:${ev.label} — ${election.name}`,
    `DESCRIPTION:${ev.when}\\nAdded from ClarionVote — clarionvote.com`,
    "END:VEVENT",
    "END:VCALENDAR",
  ].join("\r\n");
  const blob = new Blob([ics], { type: "text/calendar" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `clarionvote-${ev.id}.ics`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

function fmtMonth(s) {
  const d = new Date(s);
  return d.toLocaleDateString("en-US", { month: "short" });
}
function fmtDay(s) {
  return new Date(s).getDate();
}
function addDays(s, n) {
  const d = new Date(s);
  d.setDate(d.getDate() + n);
  return d.toISOString().slice(0, 10);
}

function CalIcon() { return <svg width="12" height="12" viewBox="0 0 14 14" fill="none" aria-hidden="true"><rect x="1.5" y="3" width="11" height="9.5" stroke="currentColor" strokeWidth="1.3" rx="1"/><line x1="1.5" y1="6" x2="12.5" y2="6" stroke="currentColor" strokeWidth="1.3"/><line x1="4" y1="1.5" x2="4" y2="4.5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="square"/><line x1="10" y1="1.5" x2="10" y2="4.5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="square"/></svg>; }

// Inline icons (separate set so file is self-contained)
function PinIcon2()   { return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8 14C8 14 13 9.5 13 6C13 3.2 10.8 1 8 1C5.2 1 3 3.2 3 6C3 9.5 8 14 8 14Z" stroke="currentColor" strokeWidth="1.4"/><circle cx="8" cy="6" r="1.8" stroke="currentColor" strokeWidth="1.4"/></svg>; }
function CheckIcon2() { return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M2 6L5 9L10 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="square"/></svg>; }

Object.assign(window, { Home, FindMyReps, PoliticianMiniCard });
