Skip to content

Instantly share code, notes, and snippets.

@hodgesmr
Created January 13, 2026 18:13
Show Gist options
  • Select an option

  • Save hodgesmr/b5267f1e5f276f0ec9871afaaf5e5106 to your computer and use it in GitHub Desktop.

Select an option

Save hodgesmr/b5267f1e5f276f0ec9871afaaf5e5106 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HollywoodDemocrats.com FEC Spending Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
color: #fff;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
}
h1 {
font-size: 2.2rem;
margin-bottom: 8px;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
color: #888;
font-size: 1rem;
}
.filing-toggle {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 30px;
flex-wrap: wrap;
}
.toggle-btn {
padding: 12px 24px;
border: 2px solid #333;
background: transparent;
color: #888;
border-radius: 8px;
cursor: pointer;
font-size: 0.95rem;
transition: all 0.3s ease;
}
.toggle-btn:hover {
border-color: #00d4ff;
color: #00d4ff;
}
.toggle-btn.active {
background: linear-gradient(135deg, #00d4ff 0%, #7b2cbf 100%);
border-color: transparent;
color: #fff;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 24px;
text-align: center;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 40px rgba(0, 212, 255, 0.2);
}
.stat-value {
font-size: 2rem;
font-weight: 700;
margin-bottom: 8px;
}
.stat-value.green { color: #00d4ff; }
.stat-value.purple { color: #7b2cbf; }
.stat-value.orange { color: #ff6b35; }
.stat-value.red { color: #ef476f; }
.stat-label {
color: #888;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.charts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 30px;
margin-bottom: 30px;
}
.chart-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 24px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.chart-title {
font-size: 1.2rem;
margin-bottom: 20px;
color: #fff;
}
.chart-container {
position: relative;
height: 300px;
}
.highlight-box {
background: linear-gradient(135deg, rgba(239, 71, 111, 0.2) 0%, rgba(239, 71, 111, 0.1) 100%);
border: 2px solid #ef476f;
border-radius: 16px;
padding: 30px;
text-align: center;
margin-bottom: 30px;
}
.highlight-box h2 {
font-size: 1.5rem;
margin-bottom: 10px;
color: #ef476f;
}
.highlight-box p {
color: #ccc;
font-size: 1.1rem;
}
.table-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 24px;
border: 1px solid rgba(255, 255, 255, 0.1);
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 14px 16px;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
th {
color: #888;
font-weight: 600;
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 1px;
}
td {
font-size: 0.95rem;
}
tr:hover {
background: rgba(255, 255, 255, 0.03);
}
.amount {
font-weight: 600;
color: #00d4ff;
}
.percent {
color: #888;
}
.payee-tag {
display: inline-block;
padding: 4px 10px;
border-radius: 20px;
font-size: 0.8rem;
background: rgba(123, 44, 191, 0.3);
color: #c77dff;
}
.payee-tag.consulting { background: rgba(123, 44, 191, 0.3); color: #c77dff; }
.payee-tag.software { background: rgba(0, 212, 255, 0.3); color: #00d4ff; }
.payee-tag.processing { background: rgba(255, 107, 53, 0.3); color: #ff6b35; }
.payee-tag.legal { background: rgba(6, 214, 160, 0.3); color: #06d6a0; }
.payee-tag.other { background: rgba(136, 136, 136, 0.3); color: #aaa; }
footer {
text-align: center;
margin-top: 40px;
color: #555;
font-size: 0.85rem;
}
footer a {
color: #00d4ff;
text-decoration: none;
}
@media (max-width: 600px) {
.charts-grid {
grid-template-columns: 1fr;
}
h1 {
font-size: 1.6rem;
}
.stat-value {
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>HollywoodDemocrats.com</h1>
<p class="subtitle">FEC Spending Analysis | Committee ID: C00833129</p>
</header>
<div class="filing-toggle">
<button class="toggle-btn active" data-filing="combined">Combined</button>
<button class="toggle-btn" data-filing="1825053">Q3 2024 (Filing 1825053)</button>
<button class="toggle-btn" data-filing="1909115">H1 2025 (Filing 1909115)</button>
</div>
<div class="highlight-box">
<h2>$0 Spent on Candidates or Independent Expenditures</h2>
<p>100% of disbursements went to operational overhead—consultants, software, and processing fees</p>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value green" id="total-spending">$93,311</div>
<div class="stat-label">Total Spending</div>
</div>
<div class="stat-card">
<div class="stat-value purple" id="consultant-spending">$50,140</div>
<div class="stat-label">Consultants</div>
</div>
<div class="stat-card">
<div class="stat-value orange" id="software-spending">$37,115</div>
<div class="stat-label">Software</div>
</div>
<div class="stat-card">
<div class="stat-value red" id="candidate-spending">$0</div>
<div class="stat-label">Candidates/IEs</div>
</div>
</div>
<div class="charts-grid">
<div class="chart-card">
<h3 class="chart-title">Spending by Category</h3>
<div class="chart-container">
<canvas id="categoryChart"></canvas>
</div>
</div>
<div class="chart-card">
<h3 class="chart-title">Spending by Vendor</h3>
<div class="chart-container">
<canvas id="vendorChart"></canvas>
</div>
</div>
</div>
<div class="chart-card" style="margin-bottom: 30px;">
<h3 class="chart-title">Spending Comparison by Filing Period</h3>
<div class="chart-container">
<canvas id="comparisonChart"></canvas>
</div>
</div>
<div class="table-card">
<h3 class="chart-title">Detailed Disbursements</h3>
<table id="disbursements-table">
<thead>
<tr>
<th>Vendor</th>
<th>Category</th>
<th>Purpose</th>
<th>Amount</th>
<th>% of Total</th>
</tr>
</thead>
<tbody id="table-body">
</tbody>
</table>
</div>
<footer>
<p>Data sourced from <a href="https://www.fec.gov" target="_blank">FEC.gov</a> |
Filings: <a href="https://docquery.fec.gov/cgi-bin/forms/C00833129/1825053/" target="_blank">1825053</a>,
<a href="https://docquery.fec.gov/cgi-bin/forms/C00833129/1909115/" target="_blank">1909115</a></p>
</footer>
</div>
<script>
// Data for both filings
const filingData = {
"1825053": {
label: "Q3 2024",
period: "Jul-Sep 2024",
total: 21174.80,
vendors: {
"Action Network": { amount: 9870.30, category: "software", purpose: "Software" },
"Summit Campaign Strategies": { amount: 8000.00, category: "consulting", purpose: "General Campaign Consulting" },
"Utrecht Kleinfeld Fiori Clark": { amount: 1345.00, category: "legal", purpose: "Legal Consulting" },
"ActBlue": { amount: 1030.16, category: "processing", purpose: "Processing Fees" },
"Hawk": { amount: 500.00, category: "other", purpose: "Refund" },
"Bank of America": { amount: 275.00, category: "other", purpose: "Banking Fees" },
"Unfiltered Media Group": { amount: 92.70, category: "other", purpose: "Website" },
"Google GSuite": { amount: 61.64, category: "software", purpose: "Software" }
},
categories: {
"Campaign Consulting": 8000.00,
"Legal Consulting": 1345.00,
"Software": 9931.94,
"Processing Fees": 1030.16,
"Other": 867.70
},
candidateSpending: 0
},
"1909115": {
label: "H1 2025",
period: "Jan-Jun 2025",
total: 72135.65,
vendors: {
"Summit Campaign Strategies": { amount: 37000.00, category: "consulting", purpose: "General Campaign Consulting" },
"Action Network": { amount: 27244.57, category: "software", purpose: "Software" },
"Utrecht Kleinfeld Fiori Clark": { amount: 3795.00, category: "legal", purpose: "Legal Consulting" },
"ActBlue": { amount: 2718.58, category: "processing", purpose: "Processing Fees" },
"Bank of America": { amount: 554.00, category: "other", purpose: "Banking Fees" },
"Nellis": { amount: 482.50, category: "other", purpose: "Refund" },
"Stowe": { amount: 341.00, category: "other", purpose: "Refund" }
},
categories: {
"Campaign Consulting": 37000.00,
"Legal Consulting": 3795.00,
"Software": 27244.57,
"Processing Fees": 2718.58,
"Other": 1377.50
},
candidateSpending: 0
},
"combined": {
label: "Combined",
period: "Jul 2024 - Jun 2025",
total: 93310.45,
vendors: {
"Summit Campaign Strategies": { amount: 45000.00, category: "consulting", purpose: "General Campaign Consulting" },
"Action Network": { amount: 37114.87, category: "software", purpose: "Software" },
"Utrecht Kleinfeld Fiori Clark": { amount: 5140.00, category: "legal", purpose: "Legal Consulting" },
"ActBlue": { amount: 3748.74, category: "processing", purpose: "Processing Fees" },
"Bank of America": { amount: 829.00, category: "other", purpose: "Banking Fees" },
"Hawk": { amount: 500.00, category: "other", purpose: "Refund" },
"Nellis": { amount: 482.50, category: "other", purpose: "Refund" },
"Stowe": { amount: 341.00, category: "other", purpose: "Refund" },
"Unfiltered Media Group": { amount: 92.70, category: "other", purpose: "Website" },
"Google GSuite": { amount: 61.64, category: "software", purpose: "Software" }
},
categories: {
"Campaign Consulting": 45000.00,
"Legal Consulting": 5140.00,
"Software": 37176.51,
"Processing Fees": 3748.74,
"Other": 2245.20
},
candidateSpending: 0
}
};
const categoryColors = {
"Campaign Consulting": "#7b2cbf",
"Legal Consulting": "#06d6a0",
"Software": "#00d4ff",
"Processing Fees": "#ff6b35",
"Other": "#888888"
};
let currentFiling = "combined";
let categoryChart, vendorChart, comparisonChart;
function formatCurrency(amount) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(amount);
}
function updateStats(filing) {
const data = filingData[filing];
document.getElementById('total-spending').textContent = formatCurrency(data.total);
const consultantTotal = (data.categories["Campaign Consulting"] || 0) + (data.categories["Legal Consulting"] || 0);
document.getElementById('consultant-spending').textContent = formatCurrency(consultantTotal);
document.getElementById('software-spending').textContent = formatCurrency(data.categories["Software"] || 0);
document.getElementById('candidate-spending').textContent = formatCurrency(data.candidateSpending);
}
function updateTable(filing) {
const data = filingData[filing];
const tbody = document.getElementById('table-body');
tbody.innerHTML = '';
const sortedVendors = Object.entries(data.vendors).sort((a, b) => b[1].amount - a[1].amount);
sortedVendors.forEach(([vendor, info]) => {
const percent = ((info.amount / data.total) * 100).toFixed(1);
const row = document.createElement('tr');
row.innerHTML = `
<td>${vendor}</td>
<td><span class="payee-tag ${info.category}">${info.category}</span></td>
<td>${info.purpose}</td>
<td class="amount">${formatCurrency(info.amount)}</td>
<td class="percent">${percent}%</td>
`;
tbody.appendChild(row);
});
}
function createCategoryChart(filing) {
const data = filingData[filing];
const ctx = document.getElementById('categoryChart').getContext('2d');
if (categoryChart) categoryChart.destroy();
categoryChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: Object.keys(data.categories),
datasets: [{
data: Object.values(data.categories),
backgroundColor: Object.keys(data.categories).map(k => categoryColors[k]),
borderWidth: 0,
hoverOffset: 10
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
color: '#ccc',
padding: 15,
font: { size: 12 }
}
},
tooltip: {
callbacks: {
label: function(context) {
const value = context.raw;
const percent = ((value / data.total) * 100).toFixed(1);
return `${formatCurrency(value)} (${percent}%)`;
}
}
}
}
}
});
}
function createVendorChart(filing) {
const data = filingData[filing];
const ctx = document.getElementById('vendorChart').getContext('2d');
if (vendorChart) vendorChart.destroy();
const sortedVendors = Object.entries(data.vendors)
.sort((a, b) => b[1].amount - a[1].amount)
.slice(0, 6);
vendorChart = new Chart(ctx, {
type: 'bar',
data: {
labels: sortedVendors.map(([name]) => name.length > 20 ? name.substring(0, 20) + '...' : name),
datasets: [{
data: sortedVendors.map(([_, info]) => info.amount),
backgroundColor: sortedVendors.map(([_, info]) => {
const colorMap = {
consulting: '#7b2cbf',
legal: '#06d6a0',
software: '#00d4ff',
processing: '#ff6b35',
other: '#888888'
};
return colorMap[info.category];
}),
borderRadius: 8,
borderSkipped: false
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: function(context) {
return formatCurrency(context.raw);
}
}
}
},
scales: {
x: {
grid: { color: 'rgba(255,255,255,0.1)' },
ticks: {
color: '#888',
callback: value => formatCurrency(value)
}
},
y: {
grid: { display: false },
ticks: { color: '#ccc' }
}
}
}
});
}
function createComparisonChart() {
const ctx = document.getElementById('comparisonChart').getContext('2d');
if (comparisonChart) comparisonChart.destroy();
const categories = ["Campaign Consulting", "Legal Consulting", "Software", "Processing Fees", "Other"];
comparisonChart = new Chart(ctx, {
type: 'bar',
data: {
labels: categories,
datasets: [
{
label: 'Q3 2024',
data: categories.map(c => filingData["1825053"].categories[c] || 0),
backgroundColor: 'rgba(0, 212, 255, 0.7)',
borderRadius: 4
},
{
label: 'H1 2025',
data: categories.map(c => filingData["1909115"].categories[c] || 0),
backgroundColor: 'rgba(123, 44, 191, 0.7)',
borderRadius: 4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
labels: { color: '#ccc' }
},
tooltip: {
callbacks: {
label: function(context) {
return `${context.dataset.label}: ${formatCurrency(context.raw)}`;
}
}
}
},
scales: {
x: {
grid: { display: false },
ticks: { color: '#ccc' }
},
y: {
grid: { color: 'rgba(255,255,255,0.1)' },
ticks: {
color: '#888',
callback: value => formatCurrency(value)
}
}
}
}
});
}
function updateDashboard(filing) {
currentFiling = filing;
updateStats(filing);
updateTable(filing);
createCategoryChart(filing);
createVendorChart(filing);
}
// Toggle button handlers
document.querySelectorAll('.toggle-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.toggle-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
updateDashboard(this.dataset.filing);
});
});
// Initialize
updateDashboard('combined');
createComparisonChart();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment