Created
January 13, 2026 18:13
-
-
Save hodgesmr/b5267f1e5f276f0ec9871afaaf5e5106 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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