Clear old index.html

This commit is contained in:
mosaic 2026-03-30 12:26:46 +00:00
parent 726b294efd
commit 38cfa587a5
1 changed files with 0 additions and 372 deletions

View File

@ -1,372 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FBN Residential — Executive Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<style>
:root {
--bg: #0f1117;
--card: #1a1d27;
--border: #2a2d3a;
--text: #e1e4ed;
--muted: #8b8fa3;
--green: #34d399;
--red: #f87171;
--blue: #60a5fa;
--purple: #a78bfa;
--amber: #fbbf24;
--cyan: #22d3ee;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'SF Pro', system-ui, sans-serif; background: var(--bg); color: var(--text); padding: 24px; max-width: 1280px; margin: 0 auto; }
h1 { font-size: 22px; font-weight: 600; margin-bottom: 4px; }
.subtitle { color: var(--muted); font-size: 13px; margin-bottom: 28px; }
/* KPI Cards */
.kpi-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
.kpi { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 20px; }
.kpi-label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
.kpi-value { font-size: 28px; font-weight: 700; }
.kpi-detail { font-size: 12px; color: var(--muted); margin-top: 4px; }
.kpi-change { font-size: 12px; margin-top: 4px; font-weight: 500; }
.kpi-change.up { color: var(--green); }
.kpi-change.down { color: var(--red); }
/* Sections */
.section { background: var(--card); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 16px; overflow: hidden; }
.section-header { padding: 16px 20px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; user-select: none; -webkit-tap-highlight-color: rgba(255,255,255,0.05); touch-action: manipulation; }
.section-header * { pointer-events: none; }
.section-header:hover { background: rgba(255,255,255,0.02); }
.section-header h2 { font-size: 20px; font-weight: 600; }
.section-header .chevron { color: var(--muted); font-size: 18px; transition: transform 0.2s; }
.section-header .chevron.open { transform: rotate(180deg); }
.section-body { padding: 0 20px 20px; display: none; }
.section-body.open { display: block; }
.section-hint { font-size: 15px; color: var(--muted); margin-top: 10px; font-weight: 400; line-height: 1.4; display: block; font-style: italic; }
/* Charts */
.chart-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 16px; }
.chart-box { position: relative; height: 280px; }
.chart-box.full { grid-column: 1 / -1; }
/* Tables */
table { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 12px; }
th { text-align: left; color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; padding: 8px 12px; border-bottom: 1px solid var(--border); }
th.num { text-align: right; }
td { padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,0.04); }
td.num { text-align: right; font-variant-numeric: tabular-nums; }
tr.total td { font-weight: 600; border-top: 1px solid var(--border); }
tr.subtotal td { font-weight: 600; color: var(--muted); }
tr.spacer td { border: none; padding: 4px; }
td.indent { padding-left: 28px; }
td.neg { color: var(--red); }
td.pos { color: var(--green); }
td.saving { color: var(--green); }
/* Pill */
.pill { display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 10px; font-weight: 500; }
.pill.green { background: rgba(52,211,153,0.15); color: var(--green); }
.pill.red { background: rgba(248,113,113,0.15); color: var(--red); }
.pill.amber { background: rgba(251,191,36,0.15); color: var(--amber); }
/* Insight callout */
.insight { background: rgba(96,165,250,0.1); border-left: 4px solid var(--blue); padding: 16px 20px; border-radius: 0 8px 8px 0; margin: 0 0 20px 0; font-size: 14px; line-height: 1.6; }
.insight strong { color: var(--blue); }
@media (max-width: 768px) {
body { padding: 12px; }
.kpi-row { grid-template-columns: repeat(2, 1fr); gap: 10px; }
.kpi { padding: 14px; }
.kpi-value { font-size: 22px; }
.chart-row { grid-template-columns: 1fr; }
.chart-box { height: 220px; }
.section-header { padding: 14px 16px; }
.section-header h2 { font-size: 17px; }
.section-hint { font-size: 13px; }
.section-body { padding: 0 14px 14px; }
table { font-size: 11px; display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap; }
th, td { padding: 6px 8px; }
.insight { font-size: 12px; padding: 12px 14px; }
}
</style>
</head>
<body>
<h1>FBN Residential &mdash; Executive Summary</h1>
<p class="subtitle">As of March 27, 2026 &middot; FY 2023&ndash;2025 &middot; Source: GP</p>
<!-- KPI Cards -->
<div class="kpi-row">
<div class="kpi">
<div class="kpi-label">2025 Revenue</div>
<div class="kpi-value">$10.2M</div>
<div class="kpi-change down">&darr; 12% vs 2024 ($11.5M)</div>
</div>
<div class="kpi">
<div class="kpi-label">Gross Margin</div>
<div class="kpi-value">39.6%</div>
<div class="kpi-change up">&uarr; from 38.4% in 2024</div>
</div>
<div class="kpi">
<div class="kpi-label">Net Income</div>
<div class="kpi-value">$2.3M</div>
<div class="kpi-change up">&uarr; 23% vs 2024 ($1.9M)</div>
</div>
<div class="kpi">
<div class="kpi-label">Net Margin</div>
<div class="kpi-value">22.8%</div>
<div class="kpi-change up">&uarr; from 16.4% in 2024</div>
</div>
</div>
<!-- Revenue Trend -->
<div class="section">
<div class="section-header" onclick="toggle(this)">
<div><h2>Revenue by Brand</h2><span class="section-hint">Every brand is shrinking. Worldline down 30% since 2022, Eyesurf now reversing too.</span></div>
<span class="chevron">&#9662;</span>
</div>
<div class="section-body">
<div class="insight"><strong>Key trend:</strong> Worldline has declined 30% since 2022 ($9.3M &rarr; $6.5M). Eyesurf was added in 2023 and offset the decline initially, but is now also softening. All brands are trending down.</div>
<div class="chart-row">
<div class="chart-box full"><canvas id="revChart"></canvas></div>
</div>
<table>
<tr><th>Brand</th><th class="num">2023</th><th class="num">2024</th><th class="num">2025</th><th class="num">YoY</th></tr>
<tr><td>Worldline</td><td class="num">$8,006,540</td><td class="num">$7,224,887</td><td class="num">$6,492,526</td><td class="num neg">&darr; 10%</td></tr>
<tr><td>Eyesurf</td><td class="num">$2,709,014</td><td class="num">$3,244,316</td><td class="num">$2,775,442</td><td class="num neg">&darr; 14%</td></tr>
<tr><td>Nucleus</td><td class="num">$778,311</td><td class="num">$696,009</td><td class="num">$620,463</td><td class="num neg">&darr; 11%</td></tr>
<tr><td>Voice Networks</td><td class="num">$490,295</td><td class="num">$377,414</td><td class="num">$309,717</td><td class="num neg">&darr; 18%</td></tr>
<tr class="total"><td>Total</td><td class="num">$11,984,160</td><td class="num">$11,542,626</td><td class="num">$10,198,148</td><td class="num neg">&darr; 12%</td></tr>
</table>
</div>
</div>
<!-- P&L Summary -->
<div class="section">
<div class="section-header" onclick="toggle(this)">
<div><h2>P&L Summary</h2><span class="section-hint">Revenue down 15% over 3 years, but net margin up from 19% to 23% &mdash; cost cuts are outrunning the decline.</span></div>
<span class="chevron">&#9662;</span>
</div>
<div class="section-body">
<div class="insight"><strong>Despite 15% revenue decline over 3 years, net margin improved from 19% to 23%.</strong> Opex was cut nearly in half ($3.0M &rarr; $1.7M), more than compensating for top-line erosion. The question is whether these cuts are sustainable.</div>
<div class="chart-row">
<div class="chart-box"><canvas id="plChart"></canvas></div>
<div class="chart-box"><canvas id="marginChart"></canvas></div>
</div>
<table>
<tr><th></th><th class="num">2023</th><th class="num">2024</th><th class="num">2025</th></tr>
<tr><td>Revenue</td><td class="num">$11,984,160</td><td class="num">$11,542,626</td><td class="num">$10,198,148</td></tr>
<tr><td>COGS (Network)</td><td class="num neg">($6,737,118)</td><td class="num neg">($7,104,778)</td><td class="num neg">($6,156,985)</td></tr>
<tr class="subtotal"><td>Gross Profit</td><td class="num">$5,247,042</td><td class="num">$4,437,848</td><td class="num">$4,041,163</td></tr>
<tr class="spacer"><td colspan="4"></td></tr>
<tr><td>Operating Expenses</td><td class="num neg">($2,956,408)</td><td class="num neg">($2,543,772)</td><td class="num neg">($1,718,719)</td></tr>
<tr class="total"><td>Net Income</td><td class="num">$2,290,634</td><td class="num">$1,894,076</td><td class="num">$2,322,444</td></tr>
<tr class="spacer"><td colspan="4"></td></tr>
<tr><td>Gross Margin</td><td class="num">43.8%</td><td class="num">38.4%</td><td class="num">39.6%</td></tr>
<tr><td>Net Margin</td><td class="num">19.1%</td><td class="num">16.4%</td><td class="num">22.8%</td></tr>
</table>
</div>
</div>
<!-- COGS Breakdown -->
<div class="section">
<div class="section-header" onclick="toggle(this)">
<div><h2>Cost of Sales (Network)</h2><span class="section-hint">Access &amp; transport eats 54% of every revenue dollar. Regulated wholesale rates &mdash; limited room to move.</span></div>
<span class="chevron">&#9662;</span>
</div>
<div class="section-body">
<div class="insight"><strong>Access & transport &mdash; internet ($5.5M) alone is 54% of revenue.</strong> This is the cost of delivering service (last-mile, backhaul, transit). It improved $768K from 2024, driving the margin recovery.</div>
<div class="chart-row">
<div class="chart-box full"><canvas id="cogsChart"></canvas></div>
</div>
<table>
<tr><th></th><th class="num">2023</th><th class="num">2024</th><th class="num">2025</th></tr>
<tr><td>Access & Transport &mdash; Internet</td><td class="num">($5,993,476)</td><td class="num">($6,297,431)</td><td class="num">($5,529,474)</td></tr>
<tr><td>Access & Transport &mdash; Residential</td><td class="num">($223,207)</td><td class="num">($198,523)</td><td class="num">($171,628)</td></tr>
<tr><td>Access & Transport &mdash; SIM</td><td class="num">($37,684)</td><td class="num">($53,026)</td><td class="num">($50,083)</td></tr>
<tr><td>Access & Transport &mdash; Local Loops</td><td class="num">($35,296)</td><td class="num">($10,520)</td><td class="num">($2,287)</td></tr>
<tr><td>Internet Transit</td><td class="num">($110,930)</td><td class="num">($113,539)</td><td class="num">($189,168)</td></tr>
<tr><td>Hardware Costs</td><td class="num">($213,858)</td><td class="num">($316,911)</td><td class="num">($145,120)</td></tr>
<tr><td>Co-Location</td><td class="num">($122,667)</td><td class="num">($114,828)</td><td class="num">($69,225)</td></tr>
<tr class="total"><td>Total COGS</td><td class="num">($6,737,118)</td><td class="num">($7,104,778)</td><td class="num">($6,156,985)</td></tr>
</table>
</div>
</div>
<!-- Opex Breakdown -->
<div class="section">
<div class="section-header" onclick="toggle(this)">
<div><h2>Operating Expenses</h2><span class="section-hint">Opex halved from $3.0M to $1.7M in two years. Call centre and salaries drove 56% of the savings.</span></div>
<span class="chevron">&#9662;</span>
</div>
<div class="section-body">
<div class="insight"><strong>People costs (call centre + salaries) dropped from $1.26M to $564K</strong> &mdash; the single biggest opex improvement. If this pace holds, opex alone adds ~2 points of net margin per year even without revenue growth.</div>
<div class="chart-row">
<div class="chart-box"><canvas id="opexChart"></canvas></div>
<div class="chart-box"><canvas id="savingsChart"></canvas></div>
</div>
<table>
<tr><th></th><th class="num">2023</th><th class="num">2024</th><th class="num">2025</th><th class="num">Savings (3yr)</th></tr>
<tr><td>Call Centre</td><td class="num">($734,126)</td><td class="num">($426,722)</td><td class="num">($244,016)</td><td class="num saving">$490,110</td></tr>
<tr><td>Advertising</td><td class="num">($227,031)</td><td class="num">($120,924)</td><td class="num">$0</td><td class="num saving">$227,031</td></tr>
<tr><td>Salaries</td><td class="num">($524,151)</td><td class="num">($382,523)</td><td class="num">($320,674)</td><td class="num saving">$203,477</td></tr>
<tr><td>Bad Debt & AR</td><td class="num">($795,582)</td><td class="num">($1,142,216)</td><td class="num">($703,968)</td><td class="num saving">$91,614</td></tr>
<tr><td>Freight & Shipping</td><td class="num">($92,435)</td><td class="num">($85,558)</td><td class="num">($27,048)</td><td class="num saving">$65,387</td></tr>
<tr><td>All Other</td><td class="num">($583,083)</td><td class="num">($385,829)</td><td class="num">($423,013)</td><td class="num saving">$160,070</td></tr>
<tr class="total"><td>Total Opex</td><td class="num">($2,956,408)</td><td class="num">($2,543,772)</td><td class="num">($1,718,719)</td><td class="num saving">$1,237,689</td></tr>
</table>
</div>
</div>
<!-- Q1 2026 -->
<div class="section">
<div class="section-header" onclick="toggle(this)">
<div><h2>Q1 2026 (Current)</h2><span class="section-hint">Revenue declining 7% but net margin expanding to ~32%. Cost discipline outpacing erosion.</span></div>
<span class="chevron">&#9662;</span>
</div>
<div class="section-body">
<div class="insight"><strong>Annualized run rates</strong> (based on Jan+Feb avg):<br>
Revenue: <strong>$9.5M</strong> (vs $10.2M in 2025 &mdash; &darr;7%)<br>
Expenses: <strong>$6.4M</strong> (vs $7.9M in 2025 &mdash; &darr;19%)<br>
Net Income: <strong>$3.0M</strong> (vs $2.3M in 2025 &mdash; &uarr;33%)<br>
Net Margin: <strong>~32%</strong> (vs 22.8% in 2025) &mdash; margin expansion continuing even as revenue declines.</div>
<div class="chart-row">
<div class="chart-box full"><canvas id="q1Chart"></canvas></div>
</div>
<table>
<tr><th>Brand</th><th class="num">January</th><th class="num">February</th><th class="num">March (MTD)</th></tr>
<tr><td>Worldline</td><td class="num">$520,952</td><td class="num">$501,511</td><td class="num">$353,558</td></tr>
<tr><td>Eyesurf</td><td class="num">$214,328</td><td class="num">$205,561</td><td class="num">$164,713</td></tr>
<tr><td>Nucleus</td><td class="num">$48,837</td><td class="num">$49,227</td><td class="num">$38,911</td></tr>
<tr><td>Voice Networks</td><td class="num">$23,360</td><td class="num">$22,968</td><td class="num">$22,676</td></tr>
<tr class="total"><td>Total</td><td class="num">$807,477</td><td class="num">$779,266</td><td class="num">$579,858</td></tr>
</table>
</div>
</div>
<p style="text-align:center; color:var(--muted); font-size:11px; margin-top:24px;">Source: GP (Dynamics GP) &middot; GL10111 / GL10110 &middot; Generated 2026-03-27</p>
<script>
const COLORS = { blue: '#60a5fa', purple: '#a78bfa', green: '#34d399', amber: '#fbbf24', red: '#f87171', cyan: '#22d3ee', muted: '#8b8fa3' };
Chart.defaults.color = '#8b8fa3';
Chart.defaults.borderColor = 'rgba(255,255,255,0.06)';
Chart.defaults.font.family = '-apple-system, system-ui, sans-serif';
Chart.defaults.font.size = 12;
const fmt = v => '$' + (v/1e6).toFixed(1) + 'M';
const fmtK = v => '$' + Math.round(v/1e3) + 'K';
function toggle(el) {
// Walk up to .section-header in case a child element triggered the event
while (el && !el.classList.contains('section-header')) el = el.parentElement;
if (!el) return;
const body = el.nextElementSibling;
const chev = el.querySelector('.chevron');
body.classList.toggle('open');
chev.classList.toggle('open');
}
// Revenue by Brand
new Chart(document.getElementById('revChart'), {
type: 'bar',
data: {
labels: ['2023', '2024', '2025'],
datasets: [
{ label: 'Worldline', data: [8006540, 7224887, 6492526], backgroundColor: COLORS.blue },
{ label: 'Eyesurf', data: [2709014, 3244316, 2775442], backgroundColor: COLORS.purple },
{ label: 'Nucleus', data: [778311, 696009, 620463], backgroundColor: COLORS.green },
{ label: 'Voice Networks', data: [490295, 377414, 309717], backgroundColor: COLORS.amber },
]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Revenue by Brand' } }, scales: { x: { stacked: true }, y: { stacked: true, ticks: { callback: fmt } } } }
});
// P&L bars
new Chart(document.getElementById('plChart'), {
type: 'bar',
data: {
labels: ['2023', '2024', '2025'],
datasets: [
{ label: 'Revenue', data: [11984160, 11542626, 10198148], backgroundColor: COLORS.blue },
{ label: 'COGS', data: [-6737118, -7104778, -6156985], backgroundColor: COLORS.red },
{ label: 'Opex', data: [-2956408, -2543772, -1718719], backgroundColor: COLORS.amber },
{ label: 'Net Income', data: [2290634, 1894076, 2322444], backgroundColor: COLORS.green },
]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'P&L Components' } }, scales: { y: { ticks: { callback: fmt } } } }
});
// Margin trend
new Chart(document.getElementById('marginChart'), {
type: 'line',
data: {
labels: ['2023', '2024', '2025'],
datasets: [
{ label: 'Gross Margin', data: [43.8, 38.4, 39.6], borderColor: COLORS.blue, tension: 0.3, pointRadius: 5 },
{ label: 'Net Margin', data: [19.1, 16.4, 22.8], borderColor: COLORS.green, tension: 0.3, pointRadius: 5 },
]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Margin Trend (%)' } }, scales: { y: { min: 0, max: 50, ticks: { callback: v => v + '%' } } } }
});
// COGS breakdown
new Chart(document.getElementById('cogsChart'), {
type: 'bar',
data: {
labels: ['2023', '2024', '2025'],
datasets: [
{ label: 'Access & Transport', data: [6289663, 6559500, 5753472], backgroundColor: COLORS.red },
{ label: 'Internet Transit', data: [110930, 113539, 189168], backgroundColor: COLORS.amber },
{ label: 'Hardware', data: [213858, 316911, 145120], backgroundColor: COLORS.purple },
{ label: 'Co-Location', data: [122667, 114828, 69225], backgroundColor: COLORS.muted },
]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'COGS Breakdown' } }, scales: { x: { stacked: true }, y: { stacked: true, ticks: { callback: fmt } } } }
});
// Opex trend
new Chart(document.getElementById('opexChart'), {
type: 'bar',
data: {
labels: ['2023', '2024', '2025'],
datasets: [
{ label: 'People', data: [1506269, 1005080, 787249], backgroundColor: COLORS.blue },
{ label: 'Bad Debt & AR', data: [795582, 1142216, 703968], backgroundColor: COLORS.red },
{ label: 'Telecom Fees', data: [134403, 141276, 147136], backgroundColor: COLORS.amber },
{ label: 'Other', data: [520154, 255200, 80366], backgroundColor: COLORS.muted },
]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Opex by Category' } }, scales: { x: { stacked: true }, y: { stacked: true, ticks: { callback: fmtK } } } }
});
// Savings waterfall
new Chart(document.getElementById('savingsChart'), {
type: 'bar',
data: {
labels: ['Call Centre', 'Advertising', 'Salaries', 'Bad Debt', 'Freight', 'Other'],
datasets: [
{ label: '2023', data: [734126, 227031, 524151, 795582, 92435, 583083], backgroundColor: COLORS.red, borderRadius: 3 },
{ label: '2025', data: [244016, 0, 320674, 703968, 27048, 423013], backgroundColor: COLORS.blue, borderRadius: 3 },
]
},
options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', plugins: { title: { display: true, text: 'Opex: 2023 vs 2025' } }, scales: { x: { ticks: { callback: fmtK } } } }
});
// Q1 2026
new Chart(document.getElementById('q1Chart'), {
type: 'bar',
data: {
labels: ['January', 'February', 'March (MTD)'],
datasets: [
{ label: 'Worldline', data: [520952, 501511, 353558], backgroundColor: COLORS.blue },
{ label: 'Eyesurf', data: [214328, 205561, 164713], backgroundColor: COLORS.purple },
{ label: 'Nucleus', data: [48837, 49227, 38911], backgroundColor: COLORS.green },
{ label: 'Voice Networks', data: [23360, 22968, 22676], backgroundColor: COLORS.amber },
]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Q1 2026 Monthly Revenue' } }, scales: { x: { stacked: true }, y: { stacked: true, ticks: { callback: fmtK } } } }
});
</script>
</body>
</html>