Clear old index.html
This commit is contained in:
parent
726b294efd
commit
38cfa587a5
372
index.html
372
index.html
|
|
@ -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 — Executive Summary</h1>
|
|
||||||
<p class="subtitle">As of March 27, 2026 · FY 2023–2025 · 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">↓ 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">↑ 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">↑ 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">↑ 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">▾</span>
|
|
||||||
</div>
|
|
||||||
<div class="section-body">
|
|
||||||
<div class="insight"><strong>Key trend:</strong> Worldline has declined 30% since 2022 ($9.3M → $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">↓ 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">↓ 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">↓ 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">↓ 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">↓ 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% — cost cuts are outrunning the decline.</span></div>
|
|
||||||
<span class="chevron">▾</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 → $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 & transport eats 54% of every revenue dollar. Regulated wholesale rates — limited room to move.</span></div>
|
|
||||||
<span class="chevron">▾</span>
|
|
||||||
</div>
|
|
||||||
<div class="section-body">
|
|
||||||
<div class="insight"><strong>Access & transport — 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 — 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 — 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 — 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 — 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">▾</span>
|
|
||||||
</div>
|
|
||||||
<div class="section-body">
|
|
||||||
<div class="insight"><strong>People costs (call centre + salaries) dropped from $1.26M to $564K</strong> — 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">▾</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 — ↓7%)<br>
|
|
||||||
Expenses: <strong>$6.4M</strong> (vs $7.9M in 2025 — ↓19%)<br>
|
|
||||||
Net Income: <strong>$3.0M</strong> (vs $2.3M in 2025 — ↑33%)<br>
|
|
||||||
Net Margin: <strong>~32%</strong> (vs 22.8% in 2025) — 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) · GL10111 / GL10110 · 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>
|
|
||||||
Loading…
Reference in New Issue