// State Detail page — human-first reframe.
// Hero: state name (moderate scale) + ONE editorial finding.
// Summary card: visual at-a-glance — top lenses + party split + count.
// Lens spectrum: ALL lenses with data, no overflow.
// Featured bills: cards with evidence.
{
const { LENSES, STATES, scoreFor, lensMetaFor, billsFor, billCountFor } = window.PL_DATA;
function StatePage({ go, setLensId }) {
const code = window.__plSelectedState || "TX";
const st = STATES.find(s => s[0] === code);
if (!st) return
Missing state.
;
const [, name] = st;
// Lenses where this state has actually-scored data
const lensRows = LENSES
.map(l => {
const meta = lensMetaFor(code, l.id);
const passed = scoreFor(code, l.id, "passed");
const pressure = scoreFor(code, l.id, "pressure");
return { ...l, passed, pressure, meta, billCount: meta?.bill_count || 0, nPassed: meta?.n_passed || 0 };
})
.filter(l => l.billCount > 0)
.sort((a, b) => b.billCount - a.billCount);
const totalBills = lensRows.reduce((s, l) => s + l.billCount, 0);
const totalPassed = lensRows.reduce((s, l) => s + l.nPassed, 0);
// Top 3 most-active lenses, used to drive the headline finding
const topLenses = lensRows.slice(0, 3);
// Default active lens = the most-scored one
const [activeLens, setActiveLens] = React.useState(topLenses[0]?.id || "reproductive");
const activeRow = lensRows.find(l => l.id === activeLens) || topLenses[0];
// Editorial finding driven by the data
const finding = buildFinding(name, lensRows);
return (
{/* HERO — state name + editorial finding */}
{name}
{code} · 2025–26 SESSION
{finding}
{/* Summary card — compact visual at-a-glance */}
{/* LENS SPECTRUM — every lens with data, sorted by activity */}
The {name} lens spectrum
Each row is one policy lens with at least one scored bill in this state.
Filled bar = bills that became law.
Dashed outline = all bills introduced (the pressure).
Click a row to see its bills below.
{lensRows.length === 0 ? (
No bills scored for this state yet.
) : (
{lensRows.map(l => (
setActiveLens(l.id)}
/>
))}
)}
{/* BILLS for the active lens */}
{activeRow && (
)}
);
}
// --- Editorial finding builder (data-driven, simple) ---------------------
function buildFinding(stateName, lensRows) {
if (lensRows.length === 0) {
return `${stateName} has no scored bills yet for the lenses we track. Pipeline is filling in — check back as more states are added.`;
}
const top = lensRows[0];
const score = top.passed;
const direction = score >= 60 ? "expanding" : score < 40 ? "restricting" : "evenly split on";
const verb = top.nPassed === 0 ? "introduced" : "passed";
return `${stateName}'s most-active lens this session is ${top.label.toLowerCase()} — ${top.billCount} bills tracked, ${top.nPassed} became law. The lawmaking is ${direction} ${top.label.toLowerCase()}.`;
}
window.PL_StatePage = StatePage;
}