Created
April 15, 2026 09:56
-
-
Save sinkers/35324b66b5bd1c6e8f3631c2bf675f51 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>AI Inference Data Center — Cost per MW Calculator</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&family=DM+Sans:wght@400;500&display=swap" rel="stylesheet"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --bg: #0f1117; | |
| --surface: #181c27; | |
| --surface2: #1f2436; | |
| --border: rgba(255,255,255,0.08); | |
| --border2: rgba(255,255,255,0.14); | |
| --text: #e8eaf0; | |
| --muted: #8b90a0; | |
| --accent: #f5a623; | |
| --accent-dim: rgba(245,166,35,0.12); | |
| --blue: #4a90d9; | |
| --green: #3ecf8e; | |
| --red: #f56565; | |
| --mono: 'IBM Plex Mono', monospace; | |
| --sans: 'DM Sans', sans-serif; | |
| --display: 'Syne', sans-serif; | |
| --radius: 8px; | |
| --radius-lg: 12px; | |
| } | |
| html { font-size: 15px; } | |
| body { background: var(--bg); color: var(--text); font-family: var(--sans); min-height: 100vh; padding: 0; } | |
| /* ── Header ── */ | |
| header { | |
| padding: 48px 48px 32px; | |
| border-bottom: 1px solid var(--border); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| header::before { | |
| content: ''; | |
| position: absolute; | |
| top: -60px; right: -60px; | |
| width: 320px; height: 320px; | |
| border-radius: 50%; | |
| background: radial-gradient(circle, rgba(245,166,35,0.06) 0%, transparent 70%); | |
| pointer-events: none; | |
| } | |
| .header-tag { | |
| font-family: var(--mono); | |
| font-size: 11px; | |
| color: var(--accent); | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| margin-bottom: 12px; | |
| } | |
| h1 { | |
| font-family: var(--display); | |
| font-size: 32px; | |
| font-weight: 700; | |
| color: #fff; | |
| line-height: 1.15; | |
| max-width: 680px; | |
| margin-bottom: 12px; | |
| } | |
| .header-desc { | |
| font-size: 14px; | |
| color: var(--muted); | |
| max-width: 640px; | |
| line-height: 1.6; | |
| } | |
| .header-meta { | |
| margin-top: 16px; | |
| display: flex; | |
| gap: 20px; | |
| flex-wrap: wrap; | |
| } | |
| .meta-item { | |
| font-family: var(--mono); | |
| font-size: 11px; | |
| color: var(--muted); | |
| padding: 4px 10px; | |
| border: 1px solid var(--border2); | |
| border-radius: 20px; | |
| } | |
| /* ── Main layout ── */ | |
| main { padding: 40px 48px; max-width: 1200px; } | |
| /* ── Slider ── */ | |
| .controls { margin-bottom: 32px; } | |
| .control-label { | |
| font-family: var(--display); | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: var(--muted); | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| margin-bottom: 12px; | |
| } | |
| .slider-row { display: flex; align-items: center; gap: 20px; } | |
| input[type=range] { | |
| flex: 1; | |
| -webkit-appearance: none; | |
| height: 3px; | |
| border-radius: 2px; | |
| background: var(--border2); | |
| outline: none; | |
| cursor: pointer; | |
| } | |
| input[type=range]::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| width: 20px; height: 20px; | |
| border-radius: 50%; | |
| background: var(--accent); | |
| cursor: pointer; | |
| box-shadow: 0 0 0 4px rgba(245,166,35,0.2); | |
| transition: box-shadow 0.15s; | |
| } | |
| input[type=range]::-webkit-slider-thumb:hover { box-shadow: 0 0 0 6px rgba(245,166,35,0.25); } | |
| .slider-val { | |
| font-family: var(--mono); | |
| font-size: 22px; | |
| font-weight: 500; | |
| color: var(--accent); | |
| min-width: 80px; | |
| text-align: right; | |
| } | |
| /* ── Metrics ── */ | |
| .metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 36px; } | |
| .metric { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-lg); | |
| padding: 20px 22px; | |
| } | |
| .metric-label { font-size: 12px; color: var(--muted); margin-bottom: 6px; } | |
| .metric-val { | |
| font-family: var(--mono); | |
| font-size: 26px; | |
| font-weight: 500; | |
| color: #fff; | |
| line-height: 1; | |
| margin-bottom: 4px; | |
| } | |
| .metric-sub { font-size: 12px; color: var(--muted); } | |
| .metric-sub.positive { color: var(--green); } | |
| /* ── Table ── */ | |
| .table-wrap { width: 100%; overflow-x: auto; } | |
| table { width: 100%; border-collapse: collapse; } | |
| thead th { | |
| font-family: var(--mono); | |
| font-size: 10px; | |
| font-weight: 500; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| color: var(--muted); | |
| padding: 0 12px 10px; | |
| border-bottom: 1px solid var(--border2); | |
| text-align: right; | |
| white-space: nowrap; | |
| } | |
| thead th:first-child { text-align: left; } | |
| .group-row td { | |
| font-family: var(--display); | |
| font-size: 11px; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| color: var(--muted); | |
| background: var(--surface2); | |
| padding: 10px 12px; | |
| border-top: 1px solid var(--border); | |
| } | |
| tbody tr.data-row { | |
| border-bottom: 1px solid var(--border); | |
| cursor: pointer; | |
| position: relative; | |
| transition: background 0.12s; | |
| } | |
| tbody tr.data-row:hover { background: rgba(255,255,255,0.03); } | |
| tbody tr.data-row.active { background: var(--accent-dim); } | |
| td { | |
| padding: 13px 12px; | |
| font-size: 13px; | |
| vertical-align: middle; | |
| text-align: right; | |
| color: var(--text); | |
| white-space: nowrap; | |
| } | |
| td:first-child { text-align: left; white-space: normal; } | |
| .cat-name { font-size: 13px; color: var(--text); display: flex; align-items: center; gap: 8px; } | |
| .cat-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } | |
| .mono { font-family: var(--mono); font-size: 12px; } | |
| .saving-val { font-family: var(--mono); font-size: 12px; color: var(--green); } | |
| .saving-none { font-family: var(--mono); font-size: 12px; color: var(--muted); } | |
| .scale-badge { | |
| display: inline-block; | |
| font-size: 10px; | |
| font-family: var(--mono); | |
| padding: 3px 8px; | |
| border-radius: 20px; | |
| white-space: nowrap; | |
| } | |
| .hint-icon { | |
| display: inline-flex; align-items: center; justify-content: center; | |
| width: 16px; height: 16px; | |
| border: 1px solid var(--border2); | |
| border-radius: 50%; | |
| font-size: 10px; | |
| color: var(--muted); | |
| flex-shrink: 0; | |
| margin-left: 4px; | |
| font-family: var(--sans); | |
| user-select: none; | |
| } | |
| .total-row td { | |
| font-family: var(--mono); | |
| font-weight: 500; | |
| font-size: 13px; | |
| border-top: 1px solid var(--border2); | |
| padding-top: 14px; | |
| } | |
| .total-row td:first-child { font-family: var(--display); font-weight: 600; font-size: 13px; color: #fff; } | |
| /* ── Popover ── */ | |
| .popover-overlay { | |
| display: none; | |
| position: fixed; | |
| inset: 0; | |
| z-index: 99; | |
| } | |
| .popover { | |
| display: none; | |
| position: fixed; | |
| z-index: 100; | |
| width: 420px; | |
| background: #1a1f2e; | |
| border: 1px solid var(--border2); | |
| border-radius: var(--radius-lg); | |
| box-shadow: 0 20px 60px rgba(0,0,0,0.6); | |
| overflow: hidden; | |
| pointer-events: all; | |
| } | |
| .popover.visible { display: block; } | |
| .popover-overlay.visible { display: block; } | |
| .pop-header { | |
| padding: 16px 18px 14px; | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 10px; | |
| } | |
| .pop-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; margin-top: 4px; } | |
| .pop-title { font-family: var(--display); font-weight: 600; font-size: 14px; color: #fff; margin-bottom: 3px; } | |
| .pop-desc { font-size: 12px; color: var(--muted); line-height: 1.5; } | |
| .pop-close { | |
| margin-left: auto; | |
| background: none; | |
| border: none; | |
| color: var(--muted); | |
| font-size: 18px; | |
| cursor: pointer; | |
| line-height: 1; | |
| padding: 2px 4px; | |
| flex-shrink: 0; | |
| } | |
| .pop-close:hover { color: var(--text); } | |
| .pop-body { padding: 14px 18px; } | |
| .pop-section { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); margin-bottom: 8px; } | |
| .pop-items { margin-bottom: 14px; } | |
| .pop-item { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 10px; | |
| padding: 8px 0; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .pop-item:last-child { border-bottom: none; } | |
| .pop-item-left { flex: 1; } | |
| .pop-item-name { font-size: 12px; color: var(--text); margin-bottom: 2px; } | |
| .pop-item-note { font-size: 11px; color: var(--muted); line-height: 1.4; } | |
| .pop-item-right { text-align: right; flex-shrink: 0; } | |
| .pop-item-cost { font-family: var(--mono); font-size: 12px; color: var(--accent); } | |
| .pop-item-pct { font-family: var(--mono); font-size: 10px; color: var(--muted); } | |
| .pop-refs { background: rgba(255,255,255,0.03); border-radius: var(--radius); padding: 10px 12px; margin-top: 4px; } | |
| .pop-ref-title { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin-bottom: 6px; } | |
| .pop-ref-item { font-size: 11px; color: var(--muted); margin-bottom: 4px; padding-left: 10px; position: relative; line-height: 1.4; } | |
| .pop-ref-item::before { content: '›'; position: absolute; left: 0; color: var(--accent); } | |
| .pop-bar-wrap { height: 3px; background: var(--border); border-radius: 2px; margin-top: 4px; overflow: hidden; } | |
| .pop-bar { height: 100%; border-radius: 2px; } | |
| .pop-cost-at { font-size: 11px; color: var(--muted); padding: 10px 18px 4px; border-top: 1px solid var(--border); font-family: var(--mono); } | |
| /* ── Chart ── */ | |
| .chart-section { margin-top: 48px; } | |
| .section-title { | |
| font-family: var(--display); | |
| font-size: 16px; | |
| font-weight: 600; | |
| color: #fff; | |
| margin-bottom: 6px; | |
| } | |
| .section-sub { font-size: 13px; color: var(--muted); margin-bottom: 22px; } | |
| .chart-box { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-lg); | |
| padding: 24px; | |
| position: relative; | |
| height: 280px; | |
| } | |
| canvas { display: block; } | |
| /* ── References ── */ | |
| .refs-section { | |
| margin-top: 40px; | |
| padding-top: 32px; | |
| border-top: 1px solid var(--border); | |
| } | |
| .refs-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; } | |
| .ref-group { } | |
| .ref-group-title { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent); margin-bottom: 10px; } | |
| .ref-entry { font-size: 12px; color: var(--muted); margin-bottom: 8px; line-height: 1.5; padding-left: 12px; position: relative; } | |
| .ref-entry::before { content: '—'; position: absolute; left: 0; color: var(--border2); } | |
| /* ── Footer ── */ | |
| footer { | |
| margin-top: 48px; | |
| padding: 24px 48px; | |
| border-top: 1px solid var(--border); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| gap: 12px; | |
| } | |
| .footer-note { font-size: 12px; color: var(--muted); } | |
| .footer-tag { font-family: var(--mono); font-size: 11px; color: var(--border2); } | |
| @media (max-width: 760px) { | |
| header, main, footer { padding-left: 20px; padding-right: 20px; } | |
| h1 { font-size: 24px; } | |
| .metrics { grid-template-columns: 1fr; } | |
| .popover { width: calc(100vw - 32px); left: 16px !important; right: 16px !important; } | |
| .refs-grid { grid-template-columns: 1fr; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="header-tag">Capital Expenditure Model · 2026</div> | |
| <h1>AI Inference Data Center<br>Cost per Megawatt Calculator</h1> | |
| <p class="header-desc"> | |
| All-in construction and equipment costs for a purpose-built AI inference facility, including | |
| GPU hardware, liquid cooling, networking, and civil infrastructure. Hover or click any row | |
| for a detailed sub-component breakdown and source references. | |
| </p> | |
| <div class="header-meta"> | |
| <span class="meta-item">Baseline: $35–45M / MW</span> | |
| <span class="meta-item">Midpoint: $40M / MW at 1 MW</span> | |
| <span class="meta-item">Scale: 1 – 200 MW</span> | |
| <span class="meta-item">Updated: April 2026</span> | |
| </div> | |
| </header> | |
| <main> | |
| <div class="controls"> | |
| <div class="control-label">Facility capacity</div> | |
| <div class="slider-row"> | |
| <input type="range" min="1" max="200" value="1" step="1" id="mw-slider"> | |
| <div class="slider-val"><span id="mw-display">1</span> MW</div> | |
| </div> | |
| </div> | |
| <div class="metrics"> | |
| <div class="metric"> | |
| <div class="metric-label">Total capital expenditure</div> | |
| <div class="metric-val" id="total-cost">$40.0M</div> | |
| <div class="metric-sub" id="total-sub">at 1 MW</div> | |
| </div> | |
| <div class="metric"> | |
| <div class="metric-label">Blended cost per MW</div> | |
| <div class="metric-val" id="per-mw">$40.0M</div> | |
| <div class="metric-sub" id="per-mw-sub">1 MW baseline</div> | |
| </div> | |
| <div class="metric"> | |
| <div class="metric-label">Infrastructure savings vs linear</div> | |
| <div class="metric-val" id="saving-val">—</div> | |
| <div class="metric-sub positive" id="saving-sub">economies of scale</div> | |
| </div> | |
| </div> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th style="text-align:left; min-width:240px;">Category</th> | |
| <th>Base (1 MW)</th> | |
| <th>Total at N MW</th> | |
| <th>Cost / MW</th> | |
| <th>Saving vs linear</th> | |
| <th style="text-align:left; min-width:150px;">Scaling behaviour</th> | |
| </tr> | |
| </thead> | |
| <tbody id="table-body"></tbody> | |
| </table> | |
| </div> | |
| <div class="chart-section"> | |
| <div class="section-title">Blended cost/MW as facility scales</div> | |
| <div class="section-sub">Infrastructure economies of scale reduce the all-in cost per MW — but GPU hardware prevents it falling below ~$22M at any realistic scale.</div> | |
| <div class="chart-box"> | |
| <canvas id="curve-chart"></canvas> | |
| </div> | |
| </div> | |
| <div class="refs-section"> | |
| <div class="section-title" style="margin-bottom:20px;">Primary sources & methodology</div> | |
| <div class="refs-grid"> | |
| <div class="ref-group"> | |
| <div class="ref-group-title">Construction costs</div> | |
| <div class="ref-entry">JLL Global Data Center Outlook 2026 — shell & core average global cost forecast $11.3M/MW; AI fit-out up to $25M/MW (March 2026)</div> | |
| <div class="ref-entry">Turner & Townsend Data Centre Construction Cost Index 2025–2026 — campus scale $45–55B/GW fully built-out</div> | |
| <div class="ref-entry">ConstructConnect 2025 — average cost per sq ft surpassed $1,033 by year-end 2025, up from $535 in 2023</div> | |
| <div class="ref-entry">ConstructElements — AI-optimised build cost modelling, February 2026</div> | |
| </div> | |
| <div class="ref-group"> | |
| <div class="ref-group-title">IT equipment & hardware</div> | |
| <div class="ref-entry">Bernstein Research — GB200/NVL72 rack at $5.9M ($3.4M compute + $2.5M infra); GPUs = 39% of total AI DC capex, November 2025</div> | |
| <div class="ref-entry">NVIDIA FY Q2 2026 earnings call — $50–60B per GW campus cost estimate</div> | |
| <div class="ref-entry">Thunder Said Energy — AI data center economics model; total capex ~$40M/kW ($40M/MW) for GPU-heavy builds, November 2025</div> | |
| <div class="ref-entry">Avid Solutions — 2026 global data center growth projections, January 2026</div> | |
| </div> | |
| <div class="ref-group"> | |
| <div class="ref-group-title">Cooling & power infrastructure</div> | |
| <div class="ref-entry">GPUnex Blog — liquid cooling adds $500K–$2M/MW capex; AI racks exceed 50 kW/rack by 2026, February 2026</div> | |
| <div class="ref-entry">mlq.ai — CDU market growing at 33% CAGR; integrated solutions reach $2–3M/MW, December 2025</div> | |
| <div class="ref-entry">IoT Analytics — Data Centre Equipment & Infrastructure Market Report 2025–2030; modern AI racks exceeding 100 kW/rack, November 2025</div> | |
| <div class="ref-entry">Schneider Electric Galaxy VXL UPS launch (April 2025); ABB MegaFlex AI-ready architecture (July 2025)</div> | |
| </div> | |
| <div class="ref-group"> | |
| <div class="ref-group-title">Market context</div> | |
| <div class="ref-entry">McKinsey — The cost of compute: $7 trillion race to scale data centers; $3.1T allocated to chip/hardware vendors, April 2025</div> | |
| <div class="ref-entry">Alpha Matica — Deconstructing the Data Center: total investment $3.4B–$5.5B+ for 100 MW AI facility, January 2026</div> | |
| <div class="ref-entry">Shield Operations — AI data center power consumption; B200 at 1,000W, 100,000 GPU cluster = 87.5 MW compute load, February 2026</div> | |
| <div class="ref-entry"><em>Scaling exponents are modelled estimates based on industry benchmarks. Actual curves vary significantly by region, redundancy class, and procurement strategy.</em></div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <footer> | |
| <div class="footer-note">Costs reflect US market mid-range. Europe and Japan typically 30–40% higher. Middle East 10–15% lower. All figures in USD millions unless stated.</div> | |
| <div class="footer-tag">AI DC COST MODEL · 2026</div> | |
| </footer> | |
| <!-- Popover --> | |
| <div class="popover-overlay" id="pop-overlay"></div> | |
| <div class="popover" id="popover"> | |
| <div class="pop-header"> | |
| <div class="pop-dot" id="pop-dot"></div> | |
| <div style="flex:1;"> | |
| <div class="pop-title" id="pop-title"></div> | |
| <div class="pop-desc" id="pop-desc"></div> | |
| </div> | |
| <button class="pop-close" id="pop-close">×</button> | |
| </div> | |
| <div class="pop-cost-at" id="pop-cost-at"></div> | |
| <div class="pop-body"> | |
| <div class="pop-section">Cost components</div> | |
| <div class="pop-items" id="pop-items"></div> | |
| <div class="pop-refs" id="pop-refs"></div> | |
| </div> | |
| </div> | |
| <script> | |
| const cats = [ | |
| { | |
| name: 'AI accelerators (GPU / Blackwell)', | |
| group: 'IT equipment & fit-out', | |
| base: 16.0, exp: 1.00, color: '#4a90d9', | |
| scaleName: 'Linear (1:1)', scaleClass: 'badge-linear', | |
| desc: 'The dominant cost driver. Blackwell-generation GPU accelerators account for ~40% of all-in capex. There is no meaningful bulk discount at data center scale — Nvidia sets the price.', | |
| items: [ | |
| { name: 'GPU accelerators (NVIDIA Blackwell B200 / GB200)', pct: 62, note: '~$30–40K per GPU; ~250–320 GPUs per MW of IT load at 70–100 kW rack densities' }, | |
| { name: 'DGX / HGX server chassis & host CPUs', pct: 13, note: 'AMD EPYC or Intel Xeon host CPUs; chassis, baseboard, PSU — sold as part of the DGX SuperPod system' }, | |
| { name: 'NVLink interconnect & HGX baseboard', pct: 12, note: 'Proprietary high-bandwidth GPU-to-GPU interconnect within each node; not substitutable with commodity hardware' }, | |
| { name: 'Spare GPU inventory (hot-swap buffer)', pct: 9, note: '5–10% spare pool held on-site; GPU failure in a training or inference cluster causes full-rack downtime without spares' }, | |
| { name: 'Acceptance testing & firmware qualification', pct: 4, note: 'Multi-week burn-in, performance validation, and driver/firmware sign-off before production deployment' }, | |
| ], | |
| refs: [ | |
| 'Bernstein Research: GB200/NVL72 rack at ~$5.9M (~$3.4M compute + $2.5M infra), Nov 2025', | |
| 'NVIDIA FY Q2 2026 earnings: $50–60B per GW campus cost stated on call', | |
| 'Investing.com / Bernstein: GPUs = 39% of total AI data center capex, Nov 2025', | |
| 'McKinsey: $3.1T of $5.2T total AI DC investment allocated to chip/hardware vendors', | |
| ] | |
| }, | |
| { | |
| name: 'Host servers & node hardware', | |
| group: 'IT equipment & fit-out', | |
| base: 2.50, exp: 1.00, color: '#5ba0e0', | |
| scaleName: 'Linear (1:1)', scaleClass: 'badge-linear', | |
| desc: 'CPU-based host infrastructure that manages the GPU clusters, runs orchestration, handles ingress/egress, and supports auxiliary workloads. Scales directly with GPU count.', | |
| items: [ | |
| { name: 'Server chassis, motherboards & PSUs', pct: 30, note: 'Standard 2U rackmount servers; density ~20 per rack for management/orchestration nodes' }, | |
| { name: 'Host CPUs (AMD EPYC / Intel Xeon)', pct: 24, note: 'High core-count EPYC or Sapphire Rapids CPUs for parallel workload dispatch and I/O management' }, | |
| { name: 'DRAM — DDR5 system memory', pct: 18, note: '512 GB–2 TB per management node; lower HBM dependency than GPU nodes but still memory-intensive' }, | |
| { name: 'Boot/OS NVMe SSDs', pct: 10, note: 'Fast local NVMe for OS, container images, and model cache; typically 2–4 TB per node' }, | |
| { name: 'Rack hardware, rails & cable management', pct: 10, note: 'Physical rack infrastructure, blanking panels, cable trays, and tooling' }, | |
| { name: 'BMC / server management hardware', pct: 8, note: 'Out-of-band management (iDRAC, iLO) for remote power cycling and diagnostics without OS dependency' }, | |
| ], | |
| refs: [ | |
| 'Standard server OEM pricing 2025–2026 (Dell PowerEdge, HPE ProLiant, Supermicro)', | |
| 'Alpha Matica: Deconstructing the Data Center cost structure, Jan 2026', | |
| ] | |
| }, | |
| { | |
| name: 'High-speed networking (InfiniBand)', | |
| group: 'IT equipment & fit-out', | |
| base: 3.50, exp: 0.95, color: '#3b82f6', | |
| scaleName: 'Near-linear', scaleClass: 'badge-near', | |
| desc: 'AI inference at scale requires ultra-low-latency GPU-to-GPU networking. InfiniBand NDR dominates; slight economies emerge from fewer redundant spine switches per rack at large scale.', | |
| items: [ | |
| { name: 'NDR InfiniBand spine & leaf switches (NVIDIA Quantum-3)', pct: 42, note: '$1,200–$1,500 per port at 400 Gb/s; thousands of ports needed per MW; no viable commodity substitute' }, | |
| { name: 'InfiniBand host channel adapters (HCAs) per server', pct: 22, note: '~$2,000–$3,500 per dual-port HCA; one per GPU node required for full fabric connectivity' }, | |
| { name: 'Optical transceivers & DAC cables', pct: 18, note: '400G–800G optical modules at ~$300–$600 each; a 1 MW cluster may require 2,000+ transceivers' }, | |
| { name: 'Top-of-rack Ethernet (management plane)', pct: 10, note: 'Standard 25G/100G Ethernet for out-of-band management, storage access, and internet egress' }, | |
| { name: 'Patch panels, fibre trays & structured cabling', pct: 8, note: 'Pre-terminated MPO fibre trunks; high-density patch panels; cable management for dense GPU racks' }, | |
| ], | |
| refs: [ | |
| 'NVIDIA Quantum-3 NDR InfiniBand switch pricing 2025–2026', | |
| 'Shield Operations: InfiniBand switches drawing 1,200–1,500W each, thousands needed per cluster, Feb 2026', | |
| 'Alpha Matica: networking as significant AI data center sub-cost, Jan 2026', | |
| ] | |
| }, | |
| { | |
| name: 'Storage arrays & HBM memory', | |
| group: 'IT equipment & fit-out', | |
| base: 0.50, exp: 0.95, color: '#6ea8e8', | |
| scaleName: 'Near-linear', scaleClass: 'badge-near', | |
| desc: 'Shared storage for model weights, training checkpoints, and inference datasets. Smaller at inference vs training workloads; slight economies from larger array configurations.', | |
| items: [ | |
| { name: 'All-NVMe shared storage arrays (model weights)', pct: 48, note: 'High-throughput NVMe arrays for low-latency model weight serving; ~500 TB–2 PB per MW of inference capacity' }, | |
| { name: 'Object/bulk storage (checkpoint & dataset tier)', pct: 30, note: 'Lower-cost high-capacity storage for cold checkpoints and training datasets; often hybrid NVMe + HDD' }, | |
| { name: 'Storage networking (NVMe-oF / RDMA fabric)', pct: 22, note: 'Dedicated storage fabric using NVMe over Fabrics or Fibre Channel; essential for sub-millisecond model load times' }, | |
| ], | |
| refs: [ | |
| 'Enterprise NVMe array pricing 2025–2026 (Pure Storage, NetApp, VAST Data)', | |
| 'Seagate Mozaic 3+/4 HAMR platform for data center storage, sampled H1 2026', | |
| 'IoT Analytics: Data Centre Equipment & Infrastructure Market Report 2025–2030, Nov 2025', | |
| ] | |
| }, | |
| { | |
| name: 'Liquid cooling (CDUs, plumbing)', | |
| group: 'IT equipment & fit-out', | |
| base: 3.00, exp: 0.85, color: '#3ecf8e', | |
| scaleName: 'Moderate economies', scaleClass: 'badge-moderate', | |
| desc: 'Mandatory for AI inference. At 50–100 kW per rack, air cooling is physically impossible. Liquid cooling adds significant capex but reduces operating costs substantially via better PUE.', | |
| items: [ | |
| { name: 'Coolant Distribution Units (CDUs)', pct: 38, note: 'CDUs are the intelligent hubs connecting facility chilled water to IT cold plates; capacity 300 kW–10 MW each; 12–18 month lead times as of 2026' }, | |
| { name: 'Direct-to-chip cold plates & manifolds', pct: 27, note: 'Individual cold plates per GPU; aluminium or copper; includes quick-disconnect manifolds for hot-swap servicing' }, | |
| { name: 'Facility-side piping & distribution headers', pct: 17, note: 'Insulated stainless-steel or CPVC piping runs from mechanical room to each row; must handle 40°C return temperatures' }, | |
| { name: 'Pumps, heat exchangers & dry coolers', pct: 12, note: 'Redundant pump sets; plate heat exchangers to isolate facility and IT water loops; dry coolers for ambient rejection' }, | |
| { name: 'Leak detection & real-time monitoring', pct: 6, note: 'Sensing cables under raised floor and along pipe runs; integrated with BMS for automated shutoff on detection' }, | |
| ], | |
| refs: [ | |
| 'GPUnex Blog: liquid cooling adds $500K–$2M/MW capex; AI racks exceed 50 kW/rack by early 2026, Feb 2026', | |
| 'mlq.ai: CDU market growing at 33% CAGR from ~$1B to $7.7B; integrated solutions $2–3M/MW, Dec 2025', | |
| 'IoT Analytics: Johnson Controls Silent-Aire CDU platform 500 kW–10 MW, Sep 2025', | |
| 'Shield Operations: liquid cooling achieves PUE 1.10–1.15 vs air-cooled 1.4–1.6, Feb 2026', | |
| ] | |
| }, | |
| { | |
| name: 'In-room power distribution (PDUs)', | |
| group: 'IT equipment & fit-out', | |
| base: 1.50, exp: 0.90, color: '#10b981', | |
| scaleName: 'Moderate economies', scaleClass: 'badge-moderate', | |
| desc: 'Power distribution at rack level. AI racks require intelligent PDUs rated for 30–60A circuits at 208–415V — substantially more expensive than standard enterprise PDUs.', | |
| items: [ | |
| { name: 'Intelligent rack PDUs (metered & switched)', pct: 32, note: '$2,000–$8,000 per PDU; 2 per rack standard for redundancy; per-outlet monitoring for capacity planning' }, | |
| { name: 'Overhead busway / raised-floor busbar system', pct: 28, note: 'Plug-in busway runs replace individual cable runs; critical for dense GPU rows requiring frequent reconfigurations' }, | |
| { name: 'Branch circuit monitoring panels', pct: 20, note: 'Per-circuit current monitoring upstream of PDUs; integrates with DCIM for real-time capacity management' }, | |
| { name: 'Cabinet & rack hardware', pct: 20, note: '42–52U open-frame or enclosed racks; must be rated for 1,500–3,000 kg point load for dense GPU nodes' }, | |
| ], | |
| refs: [ | |
| 'Vertiv, Schneider Electric, and Raritan intelligent PDU pricing 2025–2026', | |
| 'IoT Analytics: AI-ready electrical architectures; AI racks exceeding 100 kW/rack requiring full distribution redesign, Nov 2025', | |
| ] | |
| }, | |
| { | |
| name: 'Electrical (switchgear, UPS, generators)', | |
| group: 'Building & infrastructure', | |
| base: 5.50, exp: 0.70, color: '#f5a623', | |
| scaleName: 'Strong economies', scaleClass: 'badge-strong', | |
| desc: 'The largest infrastructure sub-cost and where the strongest economies appear. One large substation and transformer installation serves 10× more capacity than ten small ones, at far less than 10× the price.', | |
| items: [ | |
| { name: 'Medium-voltage switchgear & transformers', pct: 36, note: 'Custom MV/LV transformers (33kV→480V); lead times now 12–18 months for large units; biggest capex with strongest economies' }, | |
| { name: 'UPS systems (2N redundancy, online double-conversion)', pct: 27, note: 'Schneider Galaxy VXL or ABB MegaFlex class; 500 kW–1,250 kW per module; 2N means full duplicate system' }, | |
| { name: 'Backup diesel generators (N+1)', pct: 18, note: '1–3 MW diesel gensets; N+1 minimum; 48–96 hour fuel storage on-site; auto-start within 10 seconds of utility loss' }, | |
| { name: 'Automatic Transfer Switches (ATS)', pct: 10, note: 'Static or mechanical ATS between utility, UPS, and generator feeds; must transfer in <100 ms for continuous GPU operation' }, | |
| { name: 'Power monitoring, metering & SCADA', pct: 5, note: 'Real-time power quality monitoring; PUE calculation; utility billing reconciliation; demand-response programme integration' }, | |
| { name: 'Electrical installation labour', pct: 4, note: 'Licensed electricians; strongly regulated; specialist shortage driving labour cost inflation in 2025–2026' }, | |
| ], | |
| refs: [ | |
| 'JLL Global Data Center Outlook 2026: electrical infrastructure dominant building-side cost; shell & core $11.3M/MW average', | |
| 'IoT Analytics: Schneider Electric Galaxy VXL UPS (Apr 2025); ABB MegaFlex AI-ready (Jul 2025)', | |
| 'ConstructElements: power infrastructure "limiting factor rather than building size" in most 2026 markets, Feb 2026', | |
| 'GPUnex: power availability replaced chip supply as #1 AI infrastructure constraint by 2026, Feb 2026', | |
| ] | |
| }, | |
| { | |
| name: 'Civil & structural (shell, core)', | |
| group: 'Building & infrastructure', | |
| base: 3.00, exp: 0.75, color: '#f59e0b', | |
| scaleName: 'Strong economies', scaleClass: 'badge-strong', | |
| desc: 'Building construction has genuine volume economies. Fixed design, permitting, and mobilisation costs are amortised over more floor space. Larger builds also attract better contractor pricing.', | |
| items: [ | |
| { name: 'Structural steel frame & connections', pct: 33, note: 'Long-span steel frames; must support 1,500–3,000 kg/m² point loads for dense GPU rack rows and overhead cooling infrastructure' }, | |
| { name: 'Concrete foundations & ground slab', pct: 27, note: 'Piled foundations where needed; reinforced slab rated for heavy equipment; specialist vibration isolation for sensitive workloads' }, | |
| { name: 'Roofing, cladding & weatherproofing envelope', pct: 18, note: 'Insulated metal cladding; EPDM or TPO roofing; designed for 50-year operational life with minimal maintenance' }, | |
| { name: 'Site preparation & earthworks', pct: 12, note: 'Grading, excavation, compaction; varies enormously by terrain; brownfield sites typically cheaper than greenfield' }, | |
| { name: 'External civil works (roads, drainage, fencing)', pct: 10, note: 'Heavy vehicle access routes; stormwater management; perimeter fencing and gatehouse infrastructure' }, | |
| ], | |
| refs: [ | |
| 'Turner & Townsend Data Centre Construction Cost Index 2025–2026: average $10.7M/MW shell & core (2025); ~$11.3M forecast 2026', | |
| 'ConstructConnect: average cost per sq ft reached $1,033 in 2025, up from $535 in 2023', | |
| 'ConstructElements: $10–12M/MW typical shell-and-core range for standard hyperscale, Feb 2026', | |
| ] | |
| }, | |
| { | |
| name: 'Mechanical / base HVAC', | |
| group: 'Building & infrastructure', | |
| base: 2.00, exp: 0.75, color: '#fbbf24', | |
| scaleName: 'Strong economies', scaleClass: 'badge-strong', | |
| desc: 'Base building HVAC handles office areas, battery rooms, electrical switchrooms, and supplemental cooling. Liquid cooling handles the GPU heat load; HVAC handles everything else.', | |
| items: [ | |
| { name: 'Chillers & cooling towers (supplemental)', pct: 35, note: 'Even with liquid cooling, chillers serve battery rooms, UPS rooms, and offices; larger plants are more efficient per ton' }, | |
| { name: 'Computer Room Air Handlers (CRAHs)', pct: 30, note: 'Supplemental in-row or overhead CRAHs for hot-spot management and air-side economisation in cooler climates' }, | |
| { name: 'General building HVAC (offices, NOC, comms)', pct: 20, note: 'Standard commercial HVAC for occupied spaces; a relatively small fraction of total mechanical spend in AI facilities' }, | |
| { name: 'Mechanical plumbing & installation labour', pct: 15, note: 'Pipe fitting, insulation, valve installation; includes commissioning and pressure testing of all mechanical systems' }, | |
| ], | |
| refs: [ | |
| 'mlq.ai: air-cooled facilities achieve PUE 1.4–1.6; well-designed liquid cooling achieves 1.10–1.20, Dec 2025', | |
| 'Shield Operations: liquid cooling mandatory at 50+ kW/rack; HVAC handles residual facility load, Feb 2026', | |
| ] | |
| }, | |
| { | |
| name: 'Land, permits & project management', | |
| group: 'Building & infrastructure', | |
| base: 1.50, exp: 0.50, color: '#d97706', | |
| scaleName: 'Very strong economies', scaleClass: 'badge-vstrong', | |
| desc: 'Near-fixed costs that barely move with scale. You buy one plot, run one permitting process, and hire one PM team — whether the building is 10 MW or 200 MW. The highest-economy line item.', | |
| items: [ | |
| { name: 'Land acquisition', pct: 33, note: 'Highly variable: $1M/acre in rural Texas vs $10M+/acre in Northern Virginia or Dublin. Power proximity typically trumps land cost.' }, | |
| { name: 'Planning & environmental permits', pct: 20, note: 'Environmental impact assessments, grid connection applications, building warrants; one process regardless of site size' }, | |
| { name: 'Construction project management', pct: 27, note: 'PM and QS fees typically 3–5% of construction cost; the percentage actually decreases at larger project sizes' }, | |
| { name: 'Legal, professional & advisory fees', pct: 13, note: 'Site acquisition legal costs, planning solicitors, structural and M&E consultant fees at concept and design stages' }, | |
| { name: 'Security perimeter (initial fencing & gatehouse)', pct: 7, note: 'Site hoarding during construction; permanent perimeter fencing and gatehouse spec\'d at design stage' }, | |
| ], | |
| refs: [ | |
| 'JLL 2026 Global Data Center Outlook: "speed to power is the primary criteria driving site selection, followed by community support, latency and proximity to customers"', | |
| 'ConstructElements: land selection impacts cost significantly via off-site power, road access, and grading requirements, Feb 2026', | |
| 'GPUnex: Northern Virginia connection wait times exceed 3–5 years for new large-scale deployments, Feb 2026', | |
| ] | |
| }, | |
| { | |
| name: 'Fire suppression, security & misc.', | |
| group: 'Building & infrastructure', | |
| base: 1.00, exp: 0.50, color: '#b45309', | |
| scaleName: 'Very strong economies', scaleClass: 'badge-vstrong', | |
| desc: 'Largely fixed overhead. A 100 MW facility does not need 100× the security staff of a 1 MW facility. Clean-agent fire suppression scales by zone count, not linearly by power capacity.', | |
| items: [ | |
| { name: 'Clean-agent fire suppression (FM-200 / Novec 1230)', pct: 30, note: 'Gaseous suppression in whitespace and electrical rooms; zoned by area not by MW; one zone protects many racks' }, | |
| { name: 'CCTV surveillance system', pct: 20, note: 'Pan-tilt-zoom cameras, NVR infrastructure, 90-day retention; camera count grows slowly with facility area' }, | |
| { name: 'Access control & biometric systems', pct: 20, note: 'Mantraps, biometric readers, card access throughout; Tier 3 mandates multi-factor physical access at whitespace entry' }, | |
| { name: 'Building Management System (BMS)', pct: 15, note: 'Integrated DCIM and BMS platform; one system manages the whole facility; major fixed-cost component' }, | |
| { name: 'Signage, misc. fit-out & commissioning snagging', pct: 15, note: 'Flooring, painting, signage, wayfinding, snagging lists; relatively fixed costs per building regardless of size' }, | |
| ], | |
| refs: [ | |
| 'Uptime Institute Tier Standards: Tier 3 mandates N+1 fire suppression zoning and multi-factor physical access control', | |
| 'Alpha Matica: Deconstructing the Data Center — fire, security and misc as infrastructure overhead, Jan 2026', | |
| ] | |
| }, | |
| ]; | |
| const scaleBadgeStyle = { | |
| 'badge-linear': 'background:#3d1515; color:#f09595;', | |
| 'badge-near': 'background:#3d2a0a; color:#EF9F27;', | |
| 'badge-moderate': 'background:#162909; color:#97C459;', | |
| 'badge-strong': 'background:#04201a; color:#5DCAA5;', | |
| 'badge-vstrong': 'background:#051b30; color:#85B7EB;', | |
| }; | |
| function fmt(v) { | |
| if (v >= 1000) return '$' + (Math.round(v / 10) / 100).toFixed(2) + 'B'; | |
| if (v >= 100) return '$' + Math.round(v) + 'M'; | |
| if (v >= 10) return '$' + (Math.round(v * 10) / 10).toFixed(1) + 'M'; | |
| if (v >= 1) return '$' + (Math.round(v * 100) / 100).toFixed(2) + 'M'; | |
| return '$' + (Math.round(v * 1000) / 1000).toFixed(3) + 'M'; | |
| } | |
| function totalAt(mw) { | |
| return cats.reduce((s, c) => s + c.base * Math.pow(mw, c.exp), 0); | |
| } | |
| let currentMW = 1; | |
| let activeRow = null; | |
| function render(mw) { | |
| currentMW = mw; | |
| document.getElementById('mw-display').textContent = mw; | |
| const total = totalAt(mw); | |
| const perMW = total / mw; | |
| const linearTotal = cats.reduce((s, c) => s + c.base * mw, 0); | |
| const saved = linearTotal - total; | |
| const savedPct = mw > 1 ? Math.round(saved / linearTotal * 100) : 0; | |
| document.getElementById('total-cost').textContent = fmt(total); | |
| document.getElementById('total-sub').textContent = 'at ' + mw + ' MW'; | |
| document.getElementById('per-mw').textContent = fmt(perMW); | |
| document.getElementById('per-mw-sub').textContent = mw > 1 | |
| ? (Math.round((perMW / 40 - 1) * 100) + '% vs 1 MW baseline') | |
| : '1 MW baseline'; | |
| document.getElementById('saving-val').textContent = mw > 1 ? fmt(saved) : '—'; | |
| document.getElementById('saving-sub').textContent = mw > 1 | |
| ? (savedPct + '% cheaper than if all costs scaled linearly') | |
| : 'economies of scale'; | |
| let html = ''; | |
| let lastGroup = ''; | |
| cats.forEach((c, i) => { | |
| if (c.group !== lastGroup) { | |
| html += `<tr class="group-row"><td colspan="6">${c.group}</td></tr>`; | |
| lastGroup = c.group; | |
| } | |
| const totalC = c.base * Math.pow(mw, c.exp); | |
| const perMWc = totalC / mw; | |
| const savingPct = mw > 1 ? Math.round((c.base - perMWc) / c.base * 100) : 0; | |
| html += `<tr class="data-row" data-idx="${i}"> | |
| <td> | |
| <div class="cat-name"> | |
| <div class="cat-dot" style="background:${c.color}"></div> | |
| ${c.name} | |
| <span class="hint-icon" title="Click for detail breakdown">i</span> | |
| </div> | |
| </td> | |
| <td class="mono">$${c.base}M</td> | |
| <td class="mono">${fmt(totalC)}</td> | |
| <td class="mono">${fmt(perMWc)}</td> | |
| <td>${savingPct > 1 ? `<span class="saving-val">−${savingPct}%</span>` : `<span class="saving-none">—</span>`}</td> | |
| <td><span class="scale-badge" style="${scaleBadgeStyle[c.scaleClass]}">${c.scaleName}</span></td> | |
| </tr>`; | |
| }); | |
| const blendedPerMW = total / mw; | |
| const totalLinear = cats.reduce((s, c) => s + c.base * mw, 0); | |
| const overallSaving = mw > 1 ? Math.round((40 - blendedPerMW) / 40 * 100) : 0; | |
| html += `<tr class="total-row"> | |
| <td>Total (all categories)</td> | |
| <td class="mono">$40.0M</td> | |
| <td class="mono">${fmt(total)}</td> | |
| <td class="mono">${fmt(blendedPerMW)}</td> | |
| <td>${overallSaving > 0 ? `<span class="saving-val">−${overallSaving}%</span>` : `<span class="saving-none">—</span>`}</td> | |
| <td></td> | |
| </tr>`; | |
| document.getElementById('table-body').innerHTML = html; | |
| document.querySelectorAll('tr.data-row').forEach(row => { | |
| row.addEventListener('click', function(e) { | |
| const idx = parseInt(this.dataset.idx); | |
| showPopover(idx, this); | |
| }); | |
| }); | |
| } | |
| function showPopover(idx, rowEl) { | |
| const c = cats[idx]; | |
| const mw = currentMW; | |
| const totalC = c.base * Math.pow(mw, c.exp); | |
| const perMWc = totalC / mw; | |
| document.getElementById('pop-dot').style.background = c.color; | |
| document.getElementById('pop-title').textContent = c.name; | |
| document.getElementById('pop-desc').textContent = c.desc; | |
| document.getElementById('pop-cost-at').textContent = | |
| `At ${mw} MW: ${fmt(totalC)} total · ${fmt(perMWc)}/MW`; | |
| let itemsHtml = ''; | |
| c.items.forEach(item => { | |
| const itemCost = totalC * item.pct / 100; | |
| itemsHtml += `<div class="pop-item"> | |
| <div class="pop-item-left"> | |
| <div class="pop-item-name">${item.name}</div> | |
| <div class="pop-item-note">${item.note}</div> | |
| <div class="pop-bar-wrap"><div class="pop-bar" style="width:${item.pct}%; background:${c.color}; opacity:0.6;"></div></div> | |
| </div> | |
| <div class="pop-item-right"> | |
| <div class="pop-item-cost">${fmt(itemCost)}</div> | |
| <div class="pop-item-pct">${item.pct}%</div> | |
| </div> | |
| </div>`; | |
| }); | |
| document.getElementById('pop-items').innerHTML = itemsHtml; | |
| let refsHtml = `<div class="pop-ref-title">Sources</div>`; | |
| c.refs.forEach(r => { refsHtml += `<div class="pop-ref-item">${r}</div>`; }); | |
| document.getElementById('pop-refs').innerHTML = refsHtml; | |
| const pop = document.getElementById('popover'); | |
| pop.style.display = 'block'; | |
| document.getElementById('pop-overlay').style.display = 'block'; | |
| const rect = rowEl.getBoundingClientRect(); | |
| const scrollY = window.scrollY; | |
| const popH = 560; | |
| const popW = 420; | |
| let top = rect.bottom + scrollY + 4; | |
| let left = rect.left + 200; | |
| if (left + popW > window.innerWidth - 16) left = window.innerWidth - popW - 16; | |
| if (left < 8) left = 8; | |
| if (top + popH > scrollY + window.innerHeight - 8) top = rect.top + scrollY - popH - 4; | |
| if (top < scrollY + 8) top = scrollY + 8; | |
| pop.style.top = top + 'px'; | |
| pop.style.left = left + 'px'; | |
| pop.classList.add('visible'); | |
| document.getElementById('pop-overlay').classList.add('visible'); | |
| } | |
| function closePopover() { | |
| document.getElementById('popover').classList.remove('visible'); | |
| document.getElementById('pop-overlay').classList.remove('visible'); | |
| document.getElementById('popover').style.display = 'none'; | |
| document.getElementById('pop-overlay').style.display = 'none'; | |
| } | |
| document.getElementById('pop-close').addEventListener('click', closePopover); | |
| document.getElementById('pop-overlay').addEventListener('click', closePopover); | |
| document.getElementById('mw-slider').addEventListener('input', e => { | |
| render(+e.target.value); | |
| closePopover(); | |
| }); | |
| // Chart | |
| const labels = [], data = []; | |
| for (let mw = 1; mw <= 200; mw++) { | |
| labels.push(mw); | |
| data.push(Math.round(totalAt(mw) / mw * 10) / 10); | |
| } | |
| new Chart(document.getElementById('curve-chart'), { | |
| type: 'line', | |
| data: { | |
| labels, | |
| datasets: [{ | |
| label: 'Cost/MW ($M)', | |
| data, | |
| borderColor: '#f5a623', | |
| backgroundColor: 'rgba(245,166,35,0.07)', | |
| fill: true, | |
| tension: 0.4, | |
| pointRadius: 0, | |
| borderWidth: 2.5, | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { | |
| backgroundColor: '#1a1f2e', | |
| borderColor: 'rgba(255,255,255,0.1)', | |
| borderWidth: 1, | |
| titleColor: '#8b90a0', | |
| bodyColor: '#f5a623', | |
| titleFont: { family: 'IBM Plex Mono', size: 11 }, | |
| bodyFont: { family: 'IBM Plex Mono', size: 13 }, | |
| callbacks: { | |
| title: ctx => ctx[0].label + ' MW', | |
| label: ctx => 'Cost/MW: $' + ctx.raw.toFixed(1) + 'M', | |
| } | |
| } | |
| }, | |
| scales: { | |
| x: { | |
| ticks: { | |
| color: '#555b70', | |
| maxTicksLimit: 10, | |
| font: { family: 'IBM Plex Mono', size: 11 }, | |
| callback: v => v + ' MW' | |
| }, | |
| grid: { color: 'rgba(255,255,255,0.04)' } | |
| }, | |
| y: { | |
| ticks: { | |
| color: '#555b70', | |
| font: { family: 'IBM Plex Mono', size: 11 }, | |
| callback: v => '$' + Math.round(v) + 'M' | |
| }, | |
| grid: { color: 'rgba(255,255,255,0.04)' } | |
| } | |
| } | |
| } | |
| }); | |
| render(1); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment