/* global React */ /* ============================= MP PROFILE ============================= */ function MpPage({ id, nav }) { const D = window.PT_DATA; const mp = D.MPS.find(m => m.id === Number(id)) || D.TOP_MPS[0]; const speeches = D.SPEECHES.filter(s => s.mp.id === mp.id).slice(0, 12); // Rank among party and overall const sortedAll = D.TOP_MPS; const rankAll = sortedAll.findIndex(m => m.id === mp.id) + 1; const rankParty = D.MPS.filter(m => m.party.id === mp.party.id).sort((a,b)=>b.speechCount-a.speechCount).findIndex(m => m.id === mp.id) + 1; return (
{e.preventDefault();nav('mps');}}>← all members
Member of Parliament · {mp.isActive ? 'Active' : 'Inactive'}

{mp.name}

{mp.party.name} ({mp.party.abbr}) · {mp.district} District · 10th Parliament
{mp.speechCount}Speeches
#{rankAll}Overall rank
#{rankParty}In {mp.party.abbr}
{/* Activity + topic breakdown */}
Activity by sitting
Topic focus
({ label: tb.topic.name, value: tb.count, slug: tb.topic.slug }))} onClick={(it) => nav('topic', { slug: it.slug })} />

AI-assigned topic tags across {mp.speechCount} speeches. A speech can hold up to three topics.

{/* Speeches */}

Recent speeches

{speeches.length} of {mp.speechCount}
{speeches.map(s => ( ))}
{/* Related MPs */}

Others from {mp.party.abbr}

{e.preventDefault();nav('mps', { party: mp.party.abbr });}} className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>all {mp.party.abbr} members →
{D.MPS.filter(m => m.party.id === mp.party.id && m.id !== mp.id).slice(0, 4).map(m => ( {e.preventDefault();nav('mp',{id:m.id});}} style={{ padding: 16, borderTop: '1px solid var(--rule)', borderRight: '1px solid var(--rule)' }}>
{m.name}
{m.district}
{m.speechCount}
))}
); } function ActivityChart({ mp, sittings, nav }) { const max = Math.max(...mp.activity, 1); const W = 420, H = 140, PAD = 24; const step = (W - PAD * 2) / (sittings.length - 1 || 1); return ( {/* gridlines */} {[0, 0.5, 1].map(g => ( ))} {/* bars */} {mp.activity.map((v, i) => { const h = (v / max) * (H - PAD * 2); const x = PAD + i * step - 8; return ( nav('sitting', { date: sittings[i].date })}> {sittings[i].date.slice(5)} {v || ''} ); })} ); } /* ============================= TOPIC ============================= */ function TopicPage({ slug, nav }) { const D = window.PT_DATA; const topic = D.TOPICS.find(t => t.slug === slug) || D.TOPICS[0]; const speeches = D.SPEECHES.filter(s => s.topics.some(t => t.slug === topic.slug)).slice(0, 10); // top MPs for topic const mpCount = {}; D.SPEECHES.filter(s => s.topics.some(t => t.slug === topic.slug)).forEach(s => { mpCount[s.mp.id] = (mpCount[s.mp.id] || 0) + 1; }); const topMps = Object.entries(mpCount).sort((a,b) => b[1]-a[1]).slice(0, 6).map(([id, c]) => ({ mp: D.MPS.find(m => m.id === Number(id)), count: c })); // party breakdown from matrix const partyRow = D.TOPIC_PARTY_MATRIX.find(r => r.topic === topic.slug); const partyBreakdown = D.PARTIES.map(p => ({ label: p.abbr, value: Math.round((partyRow.values[p.abbr] || 0) * topic.count), color: p.color })).filter(x => x.value > 0).sort((a, b) => b.value - a.value); const idx = D.TOPICS.findIndex(t => t.slug === topic.slug); const prev = D.TOPICS[(idx - 1 + D.TOPICS.length) % D.TOPICS.length]; const next = D.TOPICS[(idx + 1) % D.TOPICS.length]; return (
{e.preventDefault();nav('topics');}}>← all topics
{e.preventDefault();nav('topic',{slug:prev.slug});}}>← {prev.short} · {e.preventDefault();nav('topic',{slug:next.slug});}}>{next.short} →
Topic · {String(idx + 1).padStart(2,'0')} of 18

{topic.name}

{topic.count} speeches tagged {topMps.length}+ members speaking {partyBreakdown.length} parties engaged
{/* Two-col: top MPs + party breakdown */}
Most vocal on this topic
{topMps.map((x, i) => ( {e.preventDefault();nav('mp',{id:x.mp.id});}} style={{ display: 'grid', gridTemplateColumns: '28px 1fr auto auto', gap: 14, padding: '10px 0', borderTop: '1px solid var(--rule)', alignItems: 'center' }}> {String(i+1).padStart(2,'0')}
{x.mp.name}
· {x.mp.district}
of {x.mp.speechCount} {x.count}
))}
Where it's coming from · by party

Share of speeches tagged {topic.short} by the MP's party. Includes multi-tagged speeches.

Recent speeches

{speeches.length} of {topic.count}
{speeches.map(s => )}
); } Object.assign(window, { MpPage, TopicPage, ActivityChart });