import { useState } from "react"; import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, Legend } from "recharts"; const keywords = [ { word: "renewable", runs: [88, 85, 78], cat: "persist" }, { word: "emissions", runs: [62, 95, 72], cat: "persist" }, { word: "energy", runs: [78, 70, 82], cat: "persist" }, { word: "technology", runs: [30, 50, 95], cat: "grow" }, { word: "greenhouse", runs: [40, 35, 55], cat: "persist" }, { word: "biofuels", runs: [95, 15, 5], cat: "shrink" }, { word: "algae", runs: [55, 5, 0], cat: "shrink" }, { word: "ethanol", runs: [50, 5, 0], cat: "shrink" }, { word: "cellulosic", runs: [38, 0, 0], cat: "shrink" }, { word: "biodiesel", runs: [42, 0, 0], cat: "shrink" }, { word: "carbon capture", runs: [65, 45, 20], cat: "shrink" }, { word: "digital", runs: [0, 55, 25], cat: "emerge15" }, { word: "AI", runs: [0, 58, 20], cat: "emerge15" }, { word: "economic", runs: [10, 52, 35], cat: "emerge15" }, { word: "natural gas", runs: [25, 55, 30], cat: "emerge15" }, { word: "plastics", runs: [0, 38, 15], cat: "emerge15" }, { word: "sustainability", runs: [20, 65, 50], cat: "emerge15" }, { word: "infrastructure", runs: [15, 42, 30], cat: "emerge15" }, { word: "offshore", runs: [0, 0, 62], cat: "emerge50" }, { word: "monitoring", runs: [0, 0, 70], cat: "emerge50" }, { word: "marine fuels", runs: [0, 0, 48], cat: "emerge50" }, { word: "exploration", runs: [0, 0, 55], cat: "emerge50" }, { word: "CO2", runs: [15, 20, 58], cat: "emerge50" }, { word: "storage", runs: [5, 10, 52], cat: "emerge50" }, { word: "exports", runs: [0, 0, 45], cat: "emerge50" }, { word: "power gen", runs: [0, 5, 50], cat: "emerge50" }, { word: "facility", runs: [0, 0, 42], cat: "emerge50" }, { word: "electronics", runs: [0, 0, 48], cat: "emerge50" }, ]; const runLabels = ["5-Article", "15-Article", "50-Article"]; const runColors = ["#0D9488", "#3B82F6", "#7C3AED"]; const catColors = { persist: "#16A34A", grow: "#0D9488", shrink: "#DC2626", emerge15: "#3B82F6", emerge50: "#7C3AED" }; const catLabels = { persist: "Persistent across all runs", grow: "Grows with scale", shrink: "Fades at scale", emerge15: "Emerges at 15 articles", emerge50: "Emerges at 50 articles" }; // ── Full SC descriptions ────────────────────────────────────────────────────── const scFullText = { "SC_1": "Natural gas as a climate transition fuel — claims positioning natural gas as a bridge or essential part of the transition to clean energy", "SC_2": "Oil operations and sustainability — claims that oil extraction and production are managed sustainably or responsibly", "SC_3": "Carbon capture and storage (CCUS) viability — claims that CCS technology is safe, available, scalable, and essential for decarbonisation", "SC_4": "Investment in renewable energy — claims that fossil fuel companies are actively investing in and enabling renewable energy", "SC_5": "Addressing climate change — claims that the company is taking meaningful action to address climate change and environmental impacts", "SC_6": "Digital technology and AI for climate — claims that digital innovation and AI are being deployed to solve climate and energy challenges", "SC_7": "Economic development and growth — claims that fossil fuel activity drives jobs, economic growth, and energy security", }; const scParagraphData = [ { sc: "SC_1 Nat gas", r5: 0, r15: 44, r50: 100, scKey: "SC_1" }, { sc: "SC_2 Oil", r5: 2, r15: 17, r50: 66, scKey: "SC_2" }, { sc: "SC_3 CCUS", r5: 26, r15: 14, r50: 53, scKey: "SC_3" }, { sc: "SC_4 Renewable", r5: 36, r15: 8, r50: 130, scKey: "SC_4" }, { sc: "SC_5 Climate", r5: 18, r15: 93, r50: 321, scKey: "SC_5" }, { sc: "SC_6 Digital/AI", r5: 2, r15: 24, r50: 20, scKey: "SC_6" }, { sc: "SC_7 Economic", r5: 9, r15: 13, r50: 49, scKey: "SC_7" }, ]; // ── Top NCs per run with full text from claim_history JSONs ─────────────────── const topNCs = [ // 5-article [ { nc: "NC_22", label: "Algae/bacteria grow in diverse environments", fullText: "Single-cell organisms like algae and bacteria can grow in diverse environments without competing with food production", count: 4, sc: "SC_4" }, { nc: "NC_18", label: "Algae biofuels power diesel engines", fullText: "Algae biofuels can power existing diesel engines, enabling cleaner fossil fuel alternatives", count: 3, sc: "SC_4" }, { nc: "NC_19", label: "Next-gen biofuels as sustainable energy", fullText: "Next generation biofuels as sustainable and environmentally friendly energy sources", count: 3, sc: "SC_4" }, { nc: "NC_23", label: "Saltwater algae can produce oil directly", fullText: "Saltwater algae can produce oil directly", count: 3, sc: "SC_4" }, { nc: "NC_24", label: "Bacteria unlock energy from plant waste", fullText: "Bacteria can unlock energy from plant waste materials like cornhusks and sawdust without competing with food production", count: 3, sc: "SC_5" }, { nc: "NC_46", label: "Building world-scale blue hydrogen facility", fullText: "We are building a world-scale blue hydrogen facility", count: 3, sc: "SC_3" }, { nc: "NC_6", label: "Carbon capture is safe", fullText: "Carbon capture is safe", count: 2, sc: "SC_3" }, { nc: "NC_7", label: "Carbon capture needed to fight climate", fullText: "Carbon capture is needed to fight climate change", count: 2, sc: "SC_3" }, { nc: "NC_10", label: "Carbon capture is widely available", fullText: "Carbon capture is widely available", count: 2, sc: "SC_3" }, { nc: "NC_17", label: "Algae biofuels: lower-carbon diesel alt.", fullText: "Biofuels from algae are lower-carbon alternatives to diesel", count: 2, sc: "SC_4" }, ], // 15-article [ { nc: "NC_6", label: "Carbon capture is safe", fullText: "Carbon capture is safe", count: 6, sc: "SC_3" }, { nc: "NC_153", label: "Reusing produced water reduces env. impact", fullText: "Reusing produced water reduces environmental impact by minimizing freshwater use and wastewater discharge", count: 4, sc: "SC_5" }, { nc: "NC_23", label: "CCS permanently stores CO2 underground", fullText: "CCS permanently stores CO2 underground to prevent atmospheric emissions", count: 3, sc: "SC_3" }, { nc: "NC_36", label: "LNG supply will rapidly expand", fullText: "Claim that LNG supply will rapidly expand in coming years", count: 3, sc: "SC_1" }, { nc: "NC_38", label: "Policies encourage switching coal to gas", fullText: "Environmental policies encourage switching from coal to gas-fired power generation", count: 3, sc: "SC_1" }, { nc: "NC_154", label: "Water use in oil ops contributes to sustain.", fullText: "Claim that water use in oil field operations contributes to sustainability management", count: 3, sc: "SC_2" }, { nc: "NC_159", label: "Oil development must avoid stressing water", fullText: "Claim that oil development and production must avoid stressing water supply", count: 3, sc: "SC_2" }, { nc: "NC_7", label: "Carbon capture needed to fight climate", fullText: "Carbon capture is needed to fight climate change", count: 2, sc: "SC_3" }, { nc: "NC_30", label: "Mobility-as-a-service challenges vehicle use", fullText: "Claim that mobility as a service challenges personal vehicle ownership primarily in urban centers", count: 2, sc: "SC_5" }, { nc: "NC_35", label: "LNG industry growth in global gas market", fullText: "LNG industry growth as a key part of global natural gas consumption", count: 2, sc: "SC_1" }, ], // 50-article [ { nc: "NC_87", label: "CCS is key tech to unlock low-carbon future", fullText: "The claim that carbon capture and storage (CCS) is a key technology to unlock a lower-emission future", count: 12, sc: "SC_3" }, { nc: "NC_1", label: "Natural gas reduces emissions", fullText: "Natural gas reduces emissions", count: 11, sc: "SC_1" }, { nc: "NC_56", label: "LNG bunker as essential transition solution", fullText: "Immediate availability of LNG bunker as an essential transition solution", count: 10, sc: "SC_1" }, { nc: "NC_57", label: "LNG propulsion reduces environmental harm", fullText: "LNG propulsion reduces emissions or environmental harm", count: 9, sc: "SC_5" }, { nc: "NC_7", label: "Carbon capture needed to fight climate", fullText: "Carbon capture is needed to fight climate change", count: 8, sc: "SC_3" }, { nc: "NC_2", label: "Natural gas integral to climate transition", fullText: "Natural gas is integral to the climate transition", count: 7, sc: "SC_1" }, { nc: "NC_6", label: "Carbon capture is safe", fullText: "Carbon capture is safe", count: 7, sc: "SC_3" }, { nc: "NC_120", label: "Commitment to limit env. impacts on water", fullText: "Claim of commitment to limit environmental impacts on water resources including reinjection of produced water into reservoirs", count: 7, sc: "SC_5" }, { nc: "NC_277", label: "Nat. gas essential across multiple sectors", fullText: "Natural gas is essential across multiple sectors including jobs, electricity, heating, and manufacturing", count: 7, sc: "SC_1" }, { nc: "NC_3", label: "Oil spills are part of the everyday", fullText: "Oil spills are part of the every day", count: 6, sc: "SC_2" }, ], ]; const scColorMap = { "SC_1": "#0D9488", "SC_2": "#6B7280", "SC_3": "#D97706", "SC_4": "#16A34A", "SC_5": "#3B82F6", "SC_6": "#7C3AED", "SC_7": "#EC4899", }; const scLabelMap = { "SC_1": "Nat gas", "SC_2": "Oil", "SC_3": "CCUS", "SC_4": "Renewable", "SC_5": "Climate", "SC_6": "Digital/AI", "SC_7": "Economic", }; const ttStyle = { background: "#1e293b", border: "1px solid #334155", borderRadius: 8, color: "white", fontSize: 12 }; const panelStyle = { background: "rgba(255,255,255,0.03)", border: "1px solid rgba(255,255,255,0.06)" }; // SC tooltip — shows full description on bar hover const SCTooltip = ({ active, payload, label }) => { if (!active || !payload || !payload.length) return null; const scKey = scParagraphData.find(d => d.sc === label)?.scKey; return (
{label}
{scKey && (
{scFullText[scKey]}
)}
{payload.map((p, i) => (
{p.name} {p.value} paragraphs
))}
); }; // Floating tooltip for NC rows const NCHoverTooltip = ({ item, color }) => (
{item.nc}
{item.fullText}
{item.sc} — {scLabelMap[item.sc]}
); export default function Dashboard() { const [activeRun, setActiveRun] = useState(0); const [hoveredNC, setHoveredNC] = useState(null); const visible = keywords.filter((k) => k.runs[activeRun] > 0); const sorted = [...visible].sort((a, b) => b.runs[activeRun] - a.runs[activeRun]); const cloudWords = sorted.map((k, i) => { const weight = k.runs[activeRun]; const angle = i * 1.3 + 0.7; const radius = 12 + i * 5.5; return { ...k, weight, x: Math.max(8, Math.min(92, 50 + Math.cos(angle) * radius * 0.75)), y: Math.max(8, Math.min(92, 50 + Math.sin(angle) * radius * 0.5)) }; }); const ncBarData = topNCs[activeRun]; const maxNcCount = Math.max(...ncBarData.map(d => d.count)); return (

BERTopic Analysis Dashboard

Topic word maps, seed superclaim usage, and taxonomy growth across 5, 15, and 50 article runs

{/* Run selector */}
{runLabels.map((label, i) => ( ))}
{/* Word cloud */}
{cloudWords.map((w, i) => { const fontSize = Math.max(12, 12 + (w.weight / 100) * 34); const opacity = 0.35 + (w.weight / 100) * 0.65; const color = catColors[w.cat]; return (
60 ? `0 0 30px ${color}30` : "none" }}>{w.word}
{w.word}
{catLabels[w.cat]}
{runLabels.map((l, ri) => (
{l}
{w.runs[ri]}
))}
); })}
{/* Keyword rankings + narrative shifts */}

Top Keywords — {runLabels[activeRun]} Run

{sorted.slice(0, 10).map((k, i) => (
{i + 1} {k.word}
{[0, 1, 2].map((ri) => (
))}
))}
{runLabels.map((l, i) => (
{l.split("-")[0]}
))}

Biggest Narrative Shifts

{[ { word: "biofuels", dir: "down", note: "95 → 15 → 5 Dominant in small sample, irrelevant at scale" }, { word: "technology", dir: "up", note: "30 → 50 → 95 Becomes the dominant narrative at scale" }, { word: "monitoring", dir: "up", note: "0 → 0 → 70 Only visible with 50 articles" }, { word: "offshore", dir: "up", note: "0 → 0 → 62 Oil infrastructure framing emerges at scale" }, { word: "AI", dir: "up", note: "0 → 58 → 20 Peaked at 15 articles, prompted SC_6" }, { word: "emissions", dir: "stable", note: "62 → 95 → 72 Always present, peaks at 15 articles" }, { word: "algae", dir: "down", note: "55 → 5 → 0 Specific to the 5-article sample only" }, { word: "sustainability", dir: "up", note: "20 → 65 → 50 Corporate green signalling grows" }, ].map((item, i) => (
{item.dir === "up" ? "↑" : item.dir === "down" ? "↓" : "→"} {item.word} {item.note}
))}
{/* SC paragraph counts + Top NC subclaims */}
{/* SC paragraph bar chart — tooltip shows full SC description */}

Superclaim Paragraph Counts

Paragraphs mapped per superclaim category across runs

Hover a bar to see the full superclaim

} />
{Object.entries(scLabelMap).map(([sc, label]) => (
{sc}: {label}
))}
{/* Top 10 NCs — hover NC name to see full claim text */}

Top 10 Subclaims — {runLabels[activeRun]} Run

Paragraph hits per subclaim (matched + created events)

Hover an NC label to see the full subclaim

{ncBarData.map((item, i) => { const pct = (item.count / maxNcCount) * 100; const barColor = scColorMap[item.sc]; const isHovered = hoveredNC === `${activeRun}-${i}`; return (
{i + 1} {/* NC label with hover tooltip */}
setHoveredNC(`${activeRun}-${i}`)} onMouseLeave={() => setHoveredNC(null)} > {item.nc} {isHovered && }
{item.count}
{item.label}
); })}
{Object.entries(scLabelMap).map(([sc]) => ( {sc} ))}
{/* Word map legend */}
{Object.entries(catLabels).map(([key, label]) => (
{label}
))}
); }