EMP Infrastructure Cascade Simulator

EMP Infrastructure Cascade Simulator

Orbital Infrastructure

<div id="emp-sector-tool">

<div class="emp-header">

<h2>Critical Infrastructure EMP Protection Dashboard</h2>

<p>

Explore representative EMP readiness across the 16 critical infrastructure sectors.

Sectors are grouped by dependency layers. The estimator below generates a modeled EMP protection scope,

planning-level cost, modeled loss exposure, and a side-by-side ROI / cost-benefit view.

</p>

</div>

<div class="emp-toolbar">

<button class="emp-filter active" data-view="current">Current State</button>

<button class="emp-filter" data-view="target">Target State</button>

<button class="emp-filter" data-view="future">Hardened Future State</button>

</div>

<div class="emp-legend">

<div><span class="dot low"></span> Minimal</div>

<div><span class="dot mid"></span> Partial</div>

<div><span class="dot high"></span> Advanced</div>

<div><span class="case-chip conservative">Conservative</span></div>

<div><span class="case-chip base">Base</span></div>

<div><span class="case-chip severe">Severe</span></div>

</div>

<div id="empComposite"></div>

<div id="empLayerGroups"></div>

<div class="emp-detail hidden" id="empDetail">

<div class="emp-detail-top">

<div>

<h3 id="detailTitle">Sector</h3>

<p id="detailSubtitle">Representative facility types and modeled planning ranges</p>

</div>

<button id="closeDetail" class="emp-close">Close</button>

</div>

<div id="detailSummary" class="emp-summary"></div>

<div id="facilityList" class="facility-list"></div>

</div>

<div class="estimator-panel">

<div class="estimator-header">

<h3>Facility Protection Scope Estimator + ROI Workbench</h3>

<p>

Select a sector and facility type to generate a modeled EMP protection punch list, estimated implementation cost,

modeled loss exposure, and break-even / cost-benefit metrics.

</p>

</div>

<div class="estimator-controls">

<div class="control-group">

<label for="sectorSelect">Sector</label>

<select id="sectorSelect"></select>

</div>

<div class="control-group">

<label for="facilitySelect">Facility Type</label>

<select id="facilitySelect"></select>

</div>

<div class="control-group">

<label for="facilitySizeSelect">Facility Size</label>

<select id="facilitySizeSelect"></select>

</div>

<div class="control-group">

<label for="protectionLevelSelect">Protection Goal</label>

<select id="protectionLevelSelect">

<option value="target">Target State</option>

<option value="future">Hardened Future State</option>

</select>

</div>

<div class="control-group button-group">

<button id="generateEstimateBtn" class="estimate-btn">Generate Modeled Scope</button>

</div>

</div>

<div id="estimatorOutput" class="estimator-output hidden"></div>

<div id="roiOutput" class="roi-output hidden"></div>

</div>

</div>

<style>

#emp-sector-tool, #emp-sector-tool * { box-sizing: border-box; }

#emp-sector-tool {

font-family: Arial, sans-serif;

color: #f4f7fb;

background: linear-gradient(180deg, #0c1320 0%, #111a2b 100%);

padding: 28px;

border-radius: 18px;

box-shadow: 0 18px 40px rgba(0,0,0,0.25);

}

.emp-header {

background: linear-gradient(180deg, rgba(18,28,45,0.96), rgba(14,22,36,0.96));

border: 1px solid rgba(111,163,255,0.18);

border-top: 3px solid rgba(67,129,255,0.85);

border-radius: 16px;

padding: 20px 22px;

margin-bottom: 22px;

}

.emp-header h2 { margin: 0 0 8px; color: #fff; font-size: 2rem; }

.emp-header p { margin: 0; color: #c7d4e6; line-height: 1.6; max-width: 980px; }

.emp-toolbar {

display: flex;

gap: 10px;

flex-wrap: wrap;

margin-bottom: 16px;

}

.emp-filter, .estimate-btn, .emp-close {

background: rgba(255,255,255,0.06);

color: #dce6f5;

border: 1px solid rgba(255,255,255,0.12);

padding: 10px 14px;

border-radius: 999px;

cursor: pointer;

font-weight: 600;

transition: all .25s ease;

}

.emp-filter:hover, .emp-filter.active, .estimate-btn:hover {

background: #1f6fff;

border-color: #1f6fff;

color: #fff;

}

.emp-legend {

display: flex;

gap: 12px 16px;

flex-wrap: wrap;

margin-bottom: 22px;

color: #b6c4d8;

font-size: .9rem;

}

.dot { display:inline-block; width:10px; height:10px; border-radius:50%; margin-right:8px; }

.dot.low { background:#ff7d7d; }

.dot.mid { background:#ffbe57; }

.dot.high { background:#6fe39a; }

.case-chip, .confidence-chip, .layer-chip, .punch-tag, .size-chip {

display: inline-block;

padding: 5px 10px;

border-radius: 999px;

font-size: .76rem;

font-weight: 700;

text-transform: uppercase;

letter-spacing: .03em;

}

.case-chip.conservative { background: rgba(86,210,125,.16); color:#98e9b2; }

.case-chip.base { background: rgba(255,179,71,.16); color:#ffc979; }

.case-chip.severe { background: rgba(255,82,82,.16); color:#ff9a9a; }

.confidence-chip.low { background: rgba(255,82,82,.18); color:#ff8d8d; }

.confidence-chip.med { background: rgba(255,179,71,.18); color:#ffc979; }

.confidence-chip.high { background: rgba(86,210,125,.18); color:#98e9b2; }

.size-chip { background: rgba(58,120,255,.18); color:#dcebff; border:1px solid rgba(111,163,255,.22); }

.layer-section { margin-bottom: 24px; }

.layer-header {

display:flex; justify-content:space-between; gap:16px; align-items:flex-start;

margin: 18px 0 14px; padding:14px 16px; border-radius:14px;

background: rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.07);

}

.layer-header h3 { margin:0 0 6px; color:#fff; font-size:1.1rem; }

.layer-header p { margin:0; color:#b5c3d7; font-size:.92rem; line-height:1.45; max-width:820px; }

.layer-chip { background: rgba(58,120,255,.18); color:#dcebff; border:1px solid rgba(111,163,255,.22); }

.emp-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:14px; }

.emp-card {

background: rgba(255,255,255,.05);

border:1px solid rgba(255,255,255,.08);

border-radius:16px;

padding:16px;

cursor:pointer;

transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;

}

.emp-card:hover { transform:translateY(-3px); box-shadow:0 14px 24px rgba(0,0,0,.22); border-color:rgba(255,255,255,.18); }

.aggregate-card {

background: radial-gradient(circle at top right, rgba(58,120,255,.24), transparent 35%), linear-gradient(180deg, rgba(19,31,51,.98), rgba(15,24,39,.98));

border:1px solid rgba(111,163,255,.22);

margin-bottom:22px;

}

.aggregate-tag {

display:inline-block; margin-bottom:10px; padding:5px 10px; border-radius:999px;

font-size:.72rem; font-weight:700; color:#dcebff; background:rgba(58,120,255,.18); border:1px solid rgba(111,163,255,.22);

}

.emp-card-top { display:flex; justify-content:space-between; gap:10px; margin-bottom:12px; }

.emp-card h4, .estimator-header h3, .panel-title, .roi-panel h4 { margin:0; color:#fff; }

.emp-score { font-size:1.35rem; font-weight:700; white-space:nowrap; color:#fff; }

.emp-status {

display:inline-block; margin-bottom:10px; padding:5px 10px; border-radius:999px;

font-size:.78rem; font-weight:700; text-transform:uppercase;

}

.status-low { background: rgba(255,82,82,.18); color:#ff8d8d; }

.status-mid { background: rgba(255,179,71,.18); color:#ffc979; }

.status-high { background: rgba(86,210,125,.18); color:#98e9b2; }

.emp-bar-wrap { height:10px; background:rgba(255,255,255,.08); border-radius:999px; overflow:hidden; margin-bottom:12px; }

.emp-bar { height:100%; border-radius:999px; transition:width .4s ease; }

.bar-low { background:linear-gradient(90deg,#d94b4b,#ff7d7d); }

.bar-mid { background:linear-gradient(90deg,#c98a28,#ffbe57); }

.bar-high { background:linear-gradient(90deg,#2d9a5b,#6fe39a); }

.metric-stack { display:grid; gap:8px; margin-bottom:10px; }

.metric-row {

display:flex; justify-content:space-between; gap:10px; font-size:.88rem; color:#d8e3f2;

padding:8px 10px; border-radius:10px; background:rgba(255,255,255,.04);

}

.metric-row .label { color:#9fb0c8; }

.emp-small, .estimate-note, .facility-note, .roi-note { color:#aebcd0; font-size:.9rem; line-height:1.45; }

.emp-detail, .estimator-panel {

margin-top:24px; background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.08);

border-radius:18px; padding:20px;

}

.hidden { display:none; }

.emp-detail-top { display:flex; justify-content:space-between; gap:14px; margin-bottom:18px; }

.emp-detail-top h3 { margin:0 0 4px; color:#fff; font-size:1.6rem; }

.emp-detail-top p, .estimator-header p { margin:0; color:#b7c4d8; }

.emp-summary, .estimate-grid, .facility-grid, .scope-grid, .estimator-controls {

display:grid; gap:12px;

}

.emp-summary { grid-template-columns:repeat(5,minmax(0,1fr)); margin-bottom:20px; }

.estimate-grid { grid-template-columns:repeat(5,minmax(0,1fr)); }

.scope-grid { grid-template-columns:repeat(4,minmax(0,1fr)); }

.facility-grid { grid-template-columns:repeat(5,minmax(0,1fr)); margin-top:10px; margin-bottom:10px; }

.estimator-controls { grid-template-columns:repeat(5,minmax(0,1fr)); margin-bottom:18px; }

.summary-box, .estimate-box, .facility-box, .punchlist-panel, .scope-panel, .cost-panel, .facility-row, .roi-panel, .roi-kpi {

background: rgba(255,255,255,.04);

border-radius:14px;

padding:14px;

}

.summary-label, .estimate-box-label, .facility-box-label, .roi-kpi-label {

color:#9eb0c9; font-size:.78rem; text-transform:uppercase; letter-spacing:.05em; margin-bottom:6px;

}

.summary-value, .estimate-box-value, .facility-box-value, .roi-kpi-value { color:#fff; font-weight:700; line-height:1.35; }

.facility-list, .estimator-output, .punchlist, .cost-lines, .roi-output, .roi-lines { display:grid; gap:12px; }

.facility-top { display:flex; justify-content:space-between; gap:12px; margin-bottom:8px; }

.facility-name, .facility-score { color:#fff; font-weight:700; }

.control-group { display:flex; flex-direction:column; gap:8px; }

.control-group label { color:#c7d4e6; font-size:.9rem; font-weight:600; }

.control-group select {

background: rgba(255,255,255,.05); color:#fff; border:1px solid rgba(255,255,255,.12);

border-radius:12px; padding:12px 14px; font-size:.95rem;

}

.punch-item {

display:grid; grid-template-columns:28px 1fr auto; gap:10px; align-items:start;

background:rgba(255,255,255,.03); border-radius:12px; padding:10px 12px;

}

.punch-num {

width:28px; height:28px; border-radius:50%; background:rgba(31,111,255,.18);

color:#dcebff; display:flex; align-items:center; justify-content:center; font-weight:700; font-size:.85rem;

}

.punch-text { color:#dbe5f4; line-height:1.45; font-size:.93rem; }

.punch-tag { color:#98e9b2; background:rgba(86,210,125,.14); }

.cost-line, .roi-line {

display:flex; justify-content:space-between; gap:12px; padding:10px 12px;

background:rgba(255,255,255,.03); border-radius:10px; color:#dbe5f4;

}

.cost-total {

margin-top:12px; padding:14px 16px; border-radius:12px;

background:linear-gradient(180deg, rgba(31,111,255,.18), rgba(31,111,255,.08));

border:1px solid rgba(111,163,255,.20);

display:flex; justify-content:space-between; gap:12px; align-items:center;

}

.cost-total .total-label { color:#cfe1ff; font-weight:700; text-transform:uppercase; font-size:.82rem; }

.cost-total .total-value { color:#fff; font-weight:800; font-size:1.2rem; }

.roi-grid {

display:grid;

grid-template-columns:repeat(3,minmax(0,1fr));

gap:14px;

}

.roi-kpi-grid {

display:grid;

grid-template-columns:repeat(4,minmax(0,1fr));

gap:12px;

}

.roi-bar-wrap {

height:12px;

background:rgba(255,255,255,.08);

border-radius:999px;

overflow:hidden;

margin-top:8px;

}

.roi-bar {

height:100%;

border-radius:999px;

background:linear-gradient(90deg,#2d9a5b,#6fe39a);

}

.roi-bar.loss {

background:linear-gradient(90deg,#d94b4b,#ff7d7d);

}

@media (max-width:1100px) {

.emp-grid, .emp-summary, .estimate-grid, .facility-grid, .scope-grid, .estimator-controls, .roi-grid, .roi-kpi-grid {

grid-template-columns:repeat(2,minmax(0,1fr));

}

}

@media (max-width:640px) {

#emp-sector-tool { padding:18px; }

.emp-grid, .emp-summary, .estimate-grid, .facility-grid, .scope-grid, .estimator-controls, .roi-grid, .roi-kpi-grid { grid-template-columns:1fr; }

.emp-detail-top, .facility-top, .emp-card-top, .layer-header, .cost-total { flex-direction:column; align-items:flex-start; }

.emp-header h2 { font-size:1.5rem; }

.punch-item { grid-template-columns:28px 1fr; }

.punch-tag { grid-column:2/3; width:fit-content; }

}

</style>

<script>

(function () {

const layerDefinitions = [

{ title: "Layer 1 — National Backbone", description: "Foundational sectors other sectors depend on first.", sectors: ["Energy","Communications","Information Technology","Water and Wastewater Systems","Transportation Systems"] },

{ title: "Layer 2 — Command, Finance, and High-Consequence Control", description: "Command, finance, government, nuclear, and defense continuity functions.", sectors: ["Financial Services","Emergency Services","Government Facilities","Nuclear Reactors, Materials, and Waste","Defense Industrial Base"] },

{ title: "Layer 3 — Public Health, Production, and Supply", description: "Healthcare, industrial processing, food, and manufacturing layers.", sectors: ["Healthcare and Public Health","Chemical","Food and Agriculture","Critical Manufacturing"] },

{ title: "Layer 4 — Built Environment and Site-Level Commercial Activity", description: "Important downstream sectors and supporting facilities.", sectors: ["Dams","Commercial Facilities"] }

];

const sectorData = [

{

name: "Energy", confidence: "medium", current: 36, target: 72, future: 90,

facilities: [

{ name: "Generation Plant", count: 3500, current: 40, target: 74, future: 91, costs: { selective: 20, deep: 60, rebuild: 150 }, mixes: { target:{c:[.35,.4,.25],b:[.2,.5,.3],s:[.1,.4,.5]}, future:{c:[.12,.43,.45],b:[.05,.4,.55],s:[.02,.3,.68]} }, note: "Balance-of-plant controls and continuity operations make this expensive." },

{ name: "Substation", count: 55000, current: 30, target: 64, future: 82, costs: { selective: .8, deep: 2.8, rebuild: 7.5 }, mixes: { target:{c:[.65,.25,.1],b:[.5,.35,.15],s:[.3,.4,.3]}, future:{c:[.3,.4,.3],b:[.15,.45,.4],s:[.08,.37,.55]} }, note: "Enormous national count drives very large aggregate cost." },

{ name: "Grid Control Center", count: 500, current: 43, target: 79, future: 94, costs: { selective: 8, deep: 24, rebuild: 60 }, mixes: { target:{c:[.25,.45,.3],b:[.15,.5,.35],s:[.08,.37,.55]}, future:{c:[.08,.42,.5],b:[.05,.35,.6],s:[.02,.28,.7]} }, note: "Among the most critical and electronics-dense utility assets." }

]

},

{

name: "Communications", confidence: "medium", current: 32, target: 67, future: 87,

facilities: [

{ name: "Switching Center", count: 1200, current: 35, target: 69, future: 88, costs: { selective: 5, deep: 14, rebuild: 30 }, mixes: { target:{c:[.35,.45,.2],b:[.25,.55,.2],s:[.15,.5,.35]}, future:{c:[.12,.5,.38],b:[.05,.55,.4],s:[.02,.43,.55]} }, note: "Dense electronics and network dependency push cost upward." },

{ name: "Tower Site", count: 130000, current: 26, target: 60, future: 80, costs: { selective: .1, deep: .3, rebuild: .8 }, mixes: { target:{c:[.75,.2,.05],b:[.6,.3,.1],s:[.4,.4,.2]}, future:{c:[.35,.45,.2],b:[.2,.5,.3],s:[.1,.45,.45]} }, note: "Very high count, lower per-site cost than switching hubs." },

{ name: "Carrier Hotel / Hub", count: 200, current: 38, target: 74, future: 92, costs: { selective: 10, deep: 30, rebuild: 70 }, mixes: { target:{c:[.25,.45,.3],b:[.15,.55,.3],s:[.08,.42,.5]}, future:{c:[.08,.42,.5],b:[.05,.45,.5],s:[.02,.33,.65]} }, note: "Core interconnection hubs behave like mission-critical digital infrastructure." }

]

},

{

name: "Information Technology", confidence: "medium", current: 46, target: 79, future: 94,

facilities: [

{ name: "Enterprise Data Center", count: 4500, current: 50, target: 82, future: 95, costs: { selective: 35, deep: 137.5, rebuild: 275 }, mixes: { target:{c:[.25,.5,.25],b:[.1,.55,.35],s:[.05,.45,.5]}, future:{c:[.08,.45,.47],b:[.02,.38,.6],s:[.01,.29,.7]} }, note: "Many sites likely require deep retrofit, hardened enclosure, or pod strategy." },

{ name: "Cloud Operations Hub", count: 800, current: 48, target: 78, future: 93, costs: { selective: 25, deep: 95, rebuild: 240 }, mixes: { target:{c:[.2,.5,.3],b:[.08,.55,.37],s:[.04,.41,.55]}, future:{c:[.06,.42,.52],b:[.02,.33,.65],s:[.01,.24,.75]} }, note: "Very high electronics density and uptime demands." },

{ name: "Network Operations Center", count: 1500, current: 44, target: 77, future: 92, costs: { selective: 4, deep: 12, rebuild: 30 }, mixes: { target:{c:[.35,.4,.25],b:[.2,.5,.3],s:[.1,.4,.5]}, future:{c:[.1,.4,.5],b:[.05,.4,.55],s:[.02,.3,.68]} }, note: "Smaller than data centers, but still heavily dependent on protected digital backbone." }

]

},

{

name: "Water and Wastewater Systems", confidence: "medium", current: 19, target: 55, future: 80,

facilities: [

{ name: "Treatment Plant", count: 16000, current: 20, target: 57, future: 82, costs: { selective: 2.5, deep: 8, rebuild: 20 }, mixes: { target:{c:[.5,.35,.15],b:[.35,.45,.2],s:[.2,.4,.4]}, future:{c:[.18,.45,.37],b:[.1,.45,.45],s:[.05,.35,.6]} }, note: "Controls, pumping, and process continuity matter more than shell cost alone." },

{ name: "Pumping Station", count: 80000, current: 17, target: 49, future: 75, costs: { selective: .12, deep: .4, rebuild: 1.1 }, mixes: { target:{c:[.72,.2,.08],b:[.55,.3,.15],s:[.35,.35,.3]}, future:{c:[.38,.4,.22],b:[.2,.4,.4],s:[.1,.35,.55]} }, note: "Very large national counts dominate totals." },

{ name: "SCADA / Control Center", count: 10000, current: 22, target: 60, future: 84, costs: { selective: .8, deep: 2.5, rebuild: 6.5 }, mixes: { target:{c:[.5,.35,.15],b:[.35,.45,.2],s:[.18,.42,.4]}, future:{c:[.15,.45,.4],b:[.08,.42,.5],s:[.03,.32,.65]} }, note: "Critical command continuity makes future-state assumptions much heavier." }

]

}

];

const estimatorTemplates = {

"Enterprise Data Center": {

target: {

assumptions: {

assetScale: "Medium-to-large enterprise data center with critical compute, network core, MEP controls, and uptime dependency.",

strategy: "Protect the most critical computing and control environments first, while addressing utility and telecom entry vulnerabilities.",

implementation: "Selective shielded enclosure strategy with filtered service entries and protected critical rooms."

},

items: [

["Critical asset inventory and dependency mapping", "Assessment"],

["EMP vulnerability survey of utility, telecom, and grounding interfaces", "Assessment"],

["Electrical service entrance EMP filters on critical feeds", "Electrical"],

["Shielded critical equipment room or hardened white-space enclave", "Shielding"],

["Waveguide-beyond-cutoff treatment for ventilation penetrations", "Shielding"],

["Shielded / filtered cable-entry treatment for fiber, copper, and controls", "Shielding"],

["Grounding and bonding remediation for racks, trays, cabinets, and support systems", "Electrical"],

["Protection of UPS controls, switchgear controls, ATS controls, and generator controls", "Electrical"],

["Protected network core, NOC support, and critical OT/BMS interfaces", "Controls"],

["Protected monitoring, logging, and alternate operations position", "Operations"],

["Factory/site testing, validation, and commissioning", "Validation"]

],

costs: {

"Assessment and design": 2.5,

"EMP filters and service entrance work": 8,

"Shielded enclave / room build-out": 14,

"Waveguides and penetration treatment": 4,

"Grounding / bonding remediation": 2.5,

"Controls / BMS / network protection": 6,

"Testing and commissioning": 2

}

},

future: {

assumptions: {

assetScale: "Mission-critical data center where continuity expectations justify deep retrofit or hardened modular strategy.",

strategy: "Harden critical compute, controls, and telecom interfaces using a protected enclosure or EMP-ready pod architecture.",

implementation: "Deep retrofit plus hardened rack-pod / modular protected environment for core loads."

},

items: [

["Full critical-load inventory and mission segmentation", "Assessment"],

["EMP design basis and zoning plan for white space, MEP, and support infrastructure", "Assessment"],

["Primary and redundant EMP filter systems on incoming electrical services", "Electrical"],

["Protected critical rack pod, shielded data hall enclave, or EMP-hardened containerized module", "Shielding"],

["Waveguide-beyond-cutoff assemblies at all protected ventilation penetrations", "Shielding"],

["Comprehensive cable-entry shielding and filtered penetrations for all protected zones", "Shielding"],

["Protected switchgear / UPS / generator controls and hardened MEP supervisory controls", "Electrical"],

["Protected network core, protected NOC node, and alternate control position", "Controls"],

["Protected telecom entry room and segmented communications architecture", "Controls"],

["Spare module / replacement strategy for essential electronics", "Operations"],

["Integrated testing, commissioning, and post-install validation", "Validation"]

],

costs: {

"Assessment and design": 5,

"EMP filters and utility entry reconstruction": 20,

"Shielded pod / hardened enclosure build-out": 55,

"Waveguides and penetration treatment": 12,

"Grounding / bonding remediation": 6,

"Controls / network / telecom hardening": 18,

"Testing, commissioning, and validation": 6

}

}

},

"Primary Data Center": {

target: {

assumptions: {

assetScale: "Financial-sector primary data center with critical transaction and continuity obligations.",

strategy: "Protect primary transaction backbone and protected continuity core.",

implementation: "Selective hardened compute / control enclave with filtered power and telecom entry."

},

items: [

["Critical transaction and settlement system mapping", "Assessment"],

["Filtered electrical entry for critical processing loads", "Electrical"],

["Shielded critical processing room / continuity enclave", "Shielding"],

["Waveguide-beyond-cutoff treatment at protected HVAC penetrations", "Shielding"],

["Protected cable-entry points and telecom segregation", "Shielding"],

["Protected network / time-sync / transaction backbone controls", "Controls"],

["Grounding and bonding remediation", "Electrical"],

["Protected backup operations interface", "Operations"],

["Testing and validation", "Validation"]

],

costs: {

"Assessment and design": 2,

"EMP filters and entry work": 7,

"Shielded critical enclave": 12,

"Waveguides and penetrations": 3,

"Grounding / bonding remediation": 2,

"Controls / network protection": 5,

"Testing and commissioning": 1.5

}

},

future: {

assumptions: {

assetScale: "High-consequence financial processing site requiring hardened continuity architecture.",

strategy: "Protect processing core, telecom core, continuity controls, and alternate operational backbone.",

implementation: "Deep retrofit or hardened modular core with protected continuity zone."

},

items: [

["Mission-segmented protection plan for transaction and settlement functions", "Assessment"],

["Redundant EMP-filtered utility and telecom entry architecture", "Electrical"],

["Shielded critical transaction core / hardened processing enclave", "Shielding"],

["Waveguides and all protected penetration treatment", "Shielding"],

["Protected communications core and alternate continuity position", "Controls"],

["Protected support systems, UPS controls, ATS controls, and supervisory monitoring", "Electrical"],

["Grounding and bonding reconstruction in protected zones", "Electrical"],

["Protected backup compute / recovery interface", "Operations"],

["Integrated validation and operational readiness testing", "Validation"]

],

costs: {

"Assessment and design": 4,

"EMP filters and utility / telecom entry": 14,

"Shielded processing enclave": 28,

"Waveguides and penetration treatment": 7,

"Grounding / bonding remediation": 4,

"Controls / network / continuity hardening": 10,

"Testing and validation": 3

}

}

}

};

const genericEstimatorBySector = {

"Information Technology": {

target: {

assumptions: {

assetScale: "Electronics-dense digital facility with critical compute, telecom, and control dependencies.",

strategy: "Protect the most critical digital loads, utility entry points, and control interfaces first.",

implementation: "Selective hardened core with filtered entries and protected controls."

},

items: [

["Asset inventory and critical-load dependency mapping", "Assessment"],

["EMP vulnerability survey of electrical and telecom entry points", "Assessment"],

["EMP filters on critical incoming services", "Electrical"],

["Protected control room, NOC node, or critical rack enclave", "Shielding"],

["Waveguide and cable-entry treatment for protected spaces", "Shielding"],

["Grounding and bonding remediation", "Electrical"],

["Protected network core and monitoring interfaces", "Controls"],

["Testing and commissioning", "Validation"]

],

costs: {

"Assessment and design": 1.5,

"EMP filters and entry work": 4,

"Protected enclosure / critical room": 6,

"Waveguides and penetrations": 1.5,

"Grounding / bonding remediation": 1,

"Controls / network protection": 2.5,

"Testing and validation": 0.75

}

},

future: {

assumptions: {

assetScale: "Mission-critical digital facility requiring hardened continuity architecture.",

strategy: "Harden compute / control core and provide protected continuity architecture.",

implementation: "Deep retrofit plus protected modular or enclosure strategy."

},

items: [

["Mission-segmented design basis and zoning plan", "Assessment"],

["Redundant EMP-filtered electrical and telecom entry", "Electrical"],

["Shielded critical compute / control enclave or hardened pod", "Shielding"],

["Waveguide-beyond-cutoff and full penetration treatment", "Shielding"],

["Protected network, controls, and alternate operations position", "Controls"],

["Grounding and bonding reconstruction", "Electrical"],

["Testing, validation, and post-install verification", "Validation"]

],

costs: {

"Assessment and design": 3,

"EMP filters and utility entry": 10,

"Shielded pod / hardened core": 18,

"Waveguides and penetrations": 4,

"Grounding / bonding remediation": 2.5,

"Controls / network hardening": 6,

"Testing and validation": 1.5

}

}

}

};

const genericFallback = {

target: {

assumptions: {

assetScale: "Representative critical facility with building systems, power distribution, controls, and communications dependencies.",

strategy: "Protect the most critical control, power, and communications systems first.",

implementation: "Selective hardened core with filtered electrical and communications entry."

},

items: [

["Critical asset inventory and dependency mapping", "Assessment"],

["EMP vulnerability survey of power and telecom pathways", "Assessment"],

["EMP filters on critical electrical services", "Electrical"],

["Protected control room / command node / critical equipment space", "Shielding"],

["Selective waveguide and cable-entry treatment", "Shielding"],

["Grounding and bonding remediation", "Electrical"],

["Protection of controls, OT, BAS, or communications interfaces", "Controls"],

["Testing and commissioning", "Validation"]

],

costs: {

"Assessment and design": 0.8,

"EMP filters and service entry work": 2,

"Protected room / enclosure": 3,

"Waveguides and penetration treatment": 0.8,

"Grounding / bonding remediation": 0.6,

"Controls / comms hardening": 1.5,

"Testing and validation": 0.4

}

},

future: {

assumptions: {

assetScale: "Representative critical facility requiring broader EMP continuity architecture.",

strategy: "Deeply harden command, controls, electrical interfaces, and continuity position.",

implementation: "Deep retrofit with hardened core and broader protected penetrations."

},

items: [

["Mission-segmented protection design basis", "Assessment"],

["Redundant EMP-filtered electrical and telecom interfaces", "Electrical"],

["Shielded hardened command / control / critical space", "Shielding"],

["Waveguide-beyond-cutoff and full penetration treatment", "Shielding"],

["Protected control systems, OT/BMS/SCADA, and communications", "Controls"],

["Grounding and bonding reconstruction", "Electrical"],

["Testing, validation, and readiness verification", "Validation"]

],

costs: {

"Assessment and design": 1.5,

"EMP filters and service entry work": 4,

"Shielded hardened core": 6,

"Waveguides and penetration treatment": 1.5,

"Grounding / bonding remediation": 1,

"Controls / comms hardening": 2.8,

"Testing and validation": 0.7

}

}

};

const facilitySizeProfiles = {

default: {

small: { label: "Small", multiplier: 0.55, description: "Smaller site footprint, fewer critical systems, lighter enclosure and penetration scope." },

medium: { label: "Medium", multiplier: 1.00, description: "Representative baseline configuration used for the planning model." },

large: { label: "Large", multiplier: 1.65, description: "Broader equipment footprint, more penetrations, more control interfaces, and larger protected area." },

xlarge: { label: "Extra Large", multiplier: 2.60, description: "High-complexity site with expanded utility, telecom, control, and enclosure scope." },

massive: { label: "Massive", multiplier: 4.25, description: "National-scale or exceptionally large mission-critical site with major hardening breadth and deeper implementation complexity." }

},

dataCenter: {

small: { label: "Edge / Small Enterprise", multiplier: 0.45, description: "Smaller compute footprint, fewer racks, limited telecom entry complexity, and a more compact protected zone." },

medium: { label: "Enterprise", multiplier: 1.00, description: "Baseline enterprise data center planning case used for the model." },

large: { label: "Regional Colocation", multiplier: 1.90, description: "Larger white-space footprint, more critical power paths, more telecom interfaces, and broader enclosure scope." },

xlarge: { label: "Hyperscale Hall", multiplier: 3.25, description: "Very large protected compute environment with expanded utility, cooling, telecom, and control hardening scope." },

massive: { label: "Campus / Multi-Hall", multiplier: 5.50, description: "Extremely large data-center campus or multi-hall facility with major enclosure, entry, and continuity architecture complexity." }

},

power: {

small: { label: "Local / Small", multiplier: 0.60, description: "Smaller utility or controls footprint with fewer bays, cabinets, relays, or support systems." },

medium: { label: "Regional / Standard", multiplier: 1.00, description: "Representative utility baseline used for the planning model." },

large: { label: "High-Capacity", multiplier: 1.75, description: "Broader command, relay, telecom, and restoration complexity." },

xlarge: { label: "Transmission-Scale", multiplier: 2.85, description: "Large utility environment with expanded controls, power interfaces, and protected restoration scope." },

massive: { label: "National Backbone", multiplier: 4.60, description: "Exceptionally critical utility command or power asset with extensive hardening breadth." }

},

water: {

small: { label: "Municipal / Small", multiplier: 0.60, description: "Smaller process, pumping, and SCADA scope with fewer hardened control interfaces." },

medium: { label: "Regional", multiplier: 1.00, description: "Representative treatment or water-control baseline used for the model." },

large: { label: "Metro Utility", multiplier: 1.80, description: "Larger process trains, pumping, instrumentation, and protected controls scope." },

xlarge: { label: "Multi-Plant System", multiplier: 2.90, description: "Broader plant command and process continuity architecture." },

massive: { label: "Major Utility Network", multiplier: 4.50, description: "Very large water or wastewater system with extensive command, process, and continuity hardening scope." }

}

};

const facilitySizeCategoryMap = {

"Enterprise Data Center": "dataCenter",

"Primary Data Center": "dataCenter",

"Cloud Operations Hub": "dataCenter",

"Network Operations Center": "dataCenter",

"Grid Control Center": "power",

"Substation": "power",

"Generation Plant": "power",

"Treatment Plant": "water",

"Pumping Station": "water",

"SCADA / Control Center": "water"

};

const lossModelProfiles = {

default: {

directDamageFactor: 0.55,

downtimeDays: { small: 14, medium: 30, large: 60, xlarge: 120, massive: 240 },

dailyOperationalLoss: { small: 0.08, medium: 0.18, large: 0.45, xlarge: 1.1, massive: 2.4 },

recoveryFactor: 0.30

},

dataCenter: {

directDamageFactor: 0.75,

downtimeDays: { small: 20, medium: 45, large: 90, xlarge: 180, massive: 365 },

dailyOperationalLoss: { small: 0.20, medium: 0.65, large: 2.2, xlarge: 6.5, massive: 15.0 },

recoveryFactor: 0.40

},

power: {

directDamageFactor: 0.68,

downtimeDays: { small: 20, medium: 50, large: 120, xlarge: 220, massive: 365 },

dailyOperationalLoss: { small: 0.12, medium: 0.45, large: 1.8, xlarge: 4.8, massive: 10.0 },

recoveryFactor: 0.38

},

water: {

directDamageFactor: 0.52,

downtimeDays: { small: 12, medium: 30, large: 60, xlarge: 120, massive: 210 },

dailyOperationalLoss: { small: 0.04, medium: 0.12, large: 0.35, xlarge: 0.9, massive: 2.0 },

recoveryFactor: 0.28

}

};

const compositeContainer = document.getElementById("empComposite");

const layerGroupsContainer = document.getElementById("empLayerGroups");

const detail = document.getElementById("empDetail");

const detailTitle = document.getElementById("detailTitle");

const detailSummary = document.getElementById("detailSummary");

const facilityList = document.getElementById("facilityList");

const closeDetail = document.getElementById("closeDetail");

const filters = document.querySelectorAll(".emp-filter");

const sectorSelect = document.getElementById("sectorSelect");

const facilitySelect = document.getElementById("facilitySelect");

const facilitySizeSelect = document.getElementById("facilitySizeSelect");

const protectionLevelSelect = document.getElementById("protectionLevelSelect");

const generateEstimateBtn = document.getElementById("generateEstimateBtn");

const estimatorOutput = document.getElementById("estimatorOutput");

const roiOutput = document.getElementById("roiOutput");

let activeView = "current";

function getStatus(score) {

if (score < 35) return { label: "Minimal", statusClass: "status-low", barClass: "bar-low" };

if (score < 70) return { label: "Partial", statusClass: "status-mid", barClass: "bar-mid" };

return { label: "Advanced", statusClass: "status-high", barClass: "bar-high" };

}

function confidenceClass(level) {

if (level === "high") return "high";

if (level === "medium") return "med";

return "low";

}

function confidenceLabel(level) {

if (level === "high") return "Higher Confidence";

if (level === "medium") return "Medium Confidence";

return "Low Confidence";

}

function formatMoney(value) {

function trim(num) { return num.toFixed(1).replace(/\.0$/, ""); }

if (value >= 1000000) return "$" + trim(value / 1000000) + "T";

if (value >= 1000) return "$" + trim(value / 1000) + "B";

return "$" + trim(value) + "M";

}

function formatPct(value) { return value.toFixed(1) + "%"; }

function formatX(value) { return value.toFixed(1) + "x"; }

function safeDivide(a, b) { return b === 0 ? 0 : a / b; }

function avg(arr) { return Math.round(arr.reduce((a,b) => a + b, 0) / arr.length); }

function weightedCost(mixArr, costObj) {

return mixArr[0] costObj.selective + mixArr[1] costObj.deep + mixArr[2] * costObj.rebuild;

}

function scenarioCost(facility, state, scenario) {

const mix = facility.mixes[state][scenario];

return facility.count * weightedCost(mix, facility.costs);

}

function sectorCost(sector, state, scenario) {

return sector.facilities.reduce((sum, f) => sum + scenarioCost(f, state, scenario), 0);

}

function sectorFacilityCount(sector) {

return sector.facilities.reduce((sum, f) => sum + f.count, 0);

}

function getAggregateData() {

return {

current: avg(sectorData.map(s => s.current)),

target: avg(sectorData.map(s => s.target)),

future: avg(sectorData.map(s => s.future)),

targetConservative: sectorData.reduce((s, x) => s + sectorCost(x, "target", "c"), 0),

targetBase: sectorData.reduce((s, x) => s + sectorCost(x, "target", "b"), 0),

targetSevere: sectorData.reduce((s, x) => s + sectorCost(x, "target", "s"), 0),

futureConservative: sectorData.reduce((s, x) => s + sectorCost(x, "future", "c"), 0),

futureBase: sectorData.reduce((s, x) => s + sectorCost(x, "future", "b"), 0),

futureSevere: sectorData.reduce((s, x) => s + sectorCost(x, "future", "s"), 0),

facilityCount: sectorData.reduce((s, x) => s + sectorFacilityCount(x), 0)

};

}

function renderAggregateCard() {

const a = getAggregateData();

const score = a[activeView];

const status = getStatus(score);

compositeContainer.innerHTML = `

<div class="emp-card aggregate-card">

<div class="aggregate-tag">Composite Portfolio View</div>

<div class="emp-card-top">

<h4>All Sectors</h4>

<div class="emp-score">${score}%</div>

</div>

<div class="emp-status ${status.statusClass}">${status.label}</div>

<div class="emp-bar-wrap"><div class="emp-bar ${status.barClass}" style="width:${score}%"></div></div>

<div class="metric-stack">

<div class="metric-row"><span class="label">Facility Count Modeled</span><strong>${a.facilityCount.toLocaleString()}</strong></div>

<div class="metric-row"><span class="label">Target C / B / S</span><strong>${formatMoney(a.targetConservative)} / ${formatMoney(a.targetBase)} / ${formatMoney(a.targetSevere)}</strong></div>

<div class="metric-row"><span class="label">Future C / B / S</span><strong>${formatMoney(a.futureConservative)} / ${formatMoney(a.futureBase)} / ${formatMoney(a.futureSevere)}</strong></div>

</div>

<div class="emp-small">Aggregated across the currently modeled sectors shown below.</div>

</div>

`;

}

function renderLayeredGrid() {

layerGroupsContainer.innerHTML = "";

layerDefinitions.forEach((layer, idx) => {

const section = document.createElement("div");

section.className = "layer-section";

const gridId = `layer-grid-${idx}`;

section.innerHTML = `

<div class="layer-header">

<div>

<h3>${layer.title}</h3>

<p>${layer.description}</p>

</div>

<div class="layer-chip">Dependency Layer ${idx + 1}</div>

</div>

<div class="emp-grid" id="${gridId}"></div>

`;

layerGroupsContainer.appendChild(section);

const gridEl = document.getElementById(gridId);

layer.sectors.forEach(name => {

const sector = sectorData.find(s => s.name === name);

if (!sector) return;

const score = sector[activeView];

const status = getStatus(score);

const count = sectorFacilityCount(sector);

const card = document.createElement("div");

card.className = "emp-card";

card.innerHTML = `

<div class="emp-card-top">

<h4>${sector.name}</h4>

<div class="emp-score">${score}%</div>

</div>

<div class="emp-status ${status.statusClass}">${status.label}</div>

<div class="emp-bar-wrap"><div class="emp-bar ${status.barClass}" style="width:${score}%"></div></div>

<div class="metric-stack">

<div class="metric-row"><span class="label">Confidence</span><strong><span class="confidence-chip ${confidenceClass(sector.confidence)}">${confidenceLabel(sector.confidence)}</span></strong></div>

<div class="metric-row"><span class="label">Facility Count</span><strong>${count.toLocaleString()}</strong></div>

<div class="metric-row"><span class="label">Target C / B / S</span><strong>${formatMoney(sectorCost(sector,"target","c"))} / ${formatMoney(sectorCost(sector,"target","b"))} / ${formatMoney(sectorCost(sector,"target","s"))}</strong></div>

<div class="metric-row"><span class="label">Future C / B / S</span><strong>${formatMoney(sectorCost(sector,"future","c"))} / ${formatMoney(sectorCost(sector,"future","b"))} / ${formatMoney(sectorCost(sector,"future","s"))}</strong></div>

</div>

<div class="emp-small">Click for facility detail and modeled planning ranges.</div>

`;

card.addEventListener("click", () => openSector(sector.name));

gridEl.appendChild(card);

});

});

}

function mixLabel(mixArr) {

return `${Math.round(mixArr[0]*100)} / ${Math.round(mixArr[1]*100)} / ${Math.round(mixArr[2]*100)}`;

}

function openSector(sectorName) {

const sector = sectorData.find(s => s.name === sectorName);

if (!sector) return;

detail.classList.remove("hidden");

detailTitle.textContent = sector.name;

const score = sector[activeView];

const status = getStatus(score);

detailSummary.innerHTML = `

<div class="summary-box"><div class="summary-label">Readiness</div><div class="summary-value">${score}%</div></div>

<div class="summary-box"><div class="summary-label">Protection State</div><div class="summary-value">${status.label}</div></div>

<div class="summary-box"><div class="summary-label">Confidence</div><div class="summary-value"><span class="confidence-chip ${confidenceClass(sector.confidence)}">${confidenceLabel(sector.confidence)}</span></div></div>

<div class="summary-box"><div class="summary-label">Facility Count Modeled</div><div class="summary-value">${sectorFacilityCount(sector).toLocaleString()}</div></div>

<div class="summary-box"><div class="summary-label">Target / Future Cases</div><div class="summary-value">${formatMoney(sectorCost(sector,"target","c"))} / ${formatMoney(sectorCost(sector,"target","b"))} / ${formatMoney(sectorCost(sector,"target","s"))}<br>${formatMoney(sectorCost(sector,"future","c"))} / ${formatMoney(sectorCost(sector,"future","b"))} / ${formatMoney(sectorCost(sector,"future","s"))}</div></div>

`;

facilityList.innerHTML = sector.facilities.map(f => {

const itemScore = f[activeView];

const fStatus = getStatus(itemScore);

return `

<div class="facility-row">

<div class="facility-top">

<div class="facility-name">${f.name}</div>

<div class="facility-score">${itemScore}%</div>

</div>

<div class="emp-bar-wrap"><div class="emp-bar ${fStatus.barClass}" style="width:${itemScore}%"></div></div>

<div class="facility-grid">

<div class="facility-box"><div class="facility-box-label">Facility Count</div><div class="facility-box-value">${f.count.toLocaleString()}</div></div>

<div class="facility-box"><div class="facility-box-label">Target C / B / S</div><div class="facility-box-value">${formatMoney(scenarioCost(f,"target","c"))} / ${formatMoney(scenarioCost(f,"target","b"))} / ${formatMoney(scenarioCost(f,"target","s"))}</div></div>

<div class="facility-box"><div class="facility-box-label">Future C / B / S</div><div class="facility-box-value">${formatMoney(scenarioCost(f,"future","c"))} / ${formatMoney(scenarioCost(f,"future","b"))} / ${formatMoney(scenarioCost(f,"future","s"))}</div></div>

<div class="facility-box"><div class="facility-box-label">Target Mix C / B / S</div><div class="facility-box-value">${mixLabel(f.mixes.target.c)}<br>${mixLabel(f.mixes.target.b)}<br>${mixLabel(f.mixes.target.s)}</div></div>

<div class="facility-box"><div class="facility-box-label">Future Mix C / B / S</div><div class="facility-box-value">${mixLabel(f.mixes.future.c)}<br>${mixLabel(f.mixes.future.b)}<br>${mixLabel(f.mixes.future.s)}</div></div>

</div>

<div class="facility-note">${f.note}</div>

</div>

`;

}).join("");

detail.scrollIntoView({ behavior: "smooth", block: "start" });

}

function getSizeProfileForFacility(facilityName) {

const category = facilitySizeCategoryMap[facilityName] || "default";

return facilitySizeProfiles[category] || facilitySizeProfiles.default;

}

function getLossProfileForFacility(facilityName) {

const category = facilitySizeCategoryMap[facilityName] || "default";

return lossModelProfiles[category] || lossModelProfiles.default;

}

function populateSizeSelector() {

const facilityName = facilitySelect.value;

const profile = getSizeProfileForFacility(facilityName);

const current = facilitySizeSelect.value || "medium";

facilitySizeSelect.innerHTML = Object.entries(profile).map(([key, value]) => {

const selected = key === current ? "selected" : "";

return `<option value="${key}" ${selected}>${value.label}</option>`;

}).join("");

if (!profile[current]) facilitySizeSelect.value = "medium";

}

function buildSelectors() {

sectorSelect.innerHTML = sectorData.map(s => `<option value="${s.name}">${s.name}</option>`).join("");

populateFacilitySelector();

}

function populateFacilitySelector() {

const sector = sectorData.find(s => s.name === sectorSelect.value);

facilitySelect.innerHTML = sector ? sector.facilities.map(f => `<option value="${f.name}">${f.name}</option>`).join("") : "";

populateSizeSelector();

}

function getEstimatorTemplate(sectorName, facilityName, level) {

if (estimatorTemplates[facilityName] && estimatorTemplates[facilityName][level]) return estimatorTemplates[facilityName][level];

if (genericEstimatorBySector[sectorName] && genericEstimatorBySector[sectorName][level]) return genericEstimatorBySector[sectorName][level];

return genericFallback[level];

}

function getProtectionEffectiveness(level, readinessScore) {

const normalized = Math.max(0, Math.min(1, readinessScore / 100));

const levelBoost = level === "future" ? 0.12 : 0.00;

return Math.max(0.25, Math.min(0.92, normalized * 0.72 + 0.18 + levelBoost));

}

function buildLossModel(facilityName, sizeKey, investmentTotal, protectionLevel, readinessScore) {

const lossProfile = getLossProfileForFacility(facilityName);

const directDamage = investmentTotal * lossProfile.directDamageFactor;

const downtimeDays = lossProfile.downtimeDays[sizeKey] || lossProfile.downtimeDays.medium;

const dailyLoss = lossProfile.dailyOperationalLoss[sizeKey] || lossProfile.dailyOperationalLoss.medium;

const downtimeLoss = downtimeDays * dailyLoss;

const recoveryLoss = (directDamage + downtimeLoss) * lossProfile.recoveryFactor;

const unprotectedLoss = directDamage + downtimeLoss + recoveryLoss;

const protectionEffectiveness = getProtectionEffectiveness(protectionLevel, readinessScore);

const residualLoss = unprotectedLoss * (1 - protectionEffectiveness);

const avoidedLoss = unprotectedLoss - residualLoss;

const roiMultiple = safeDivide(avoidedLoss, investmentTotal);

const benefitCostRatio = safeDivide(avoidedLoss, investmentTotal);

const netBenefit = avoidedLoss - investmentTotal;

const annualProbability = 0.005;

const annualAvoidedValue = avoidedLoss * annualProbability;

const fiveYearExpectedBenefit = annualAvoidedValue * 5;

const tenYearExpectedBenefit = annualAvoidedValue * 10;

const breakEvenProbability = safeDivide(investmentTotal, avoidedLoss || 1) * 100;

return {

directDamage,

downtimeDays,

dailyLoss,

downtimeLoss,

recoveryLoss,

unprotectedLoss,

residualLoss,

avoidedLoss,

protectionEffectiveness,

roiMultiple,

benefitCostRatio,

netBenefit,

annualAvoidedValue,

fiveYearExpectedBenefit,

tenYearExpectedBenefit,

breakEvenProbability

};

}

function renderROIComparison(sectorName, facilityName, sizeModel, levelLabel, investmentTotal, lossModel) {

const avoidedPct = safeDivide(lossModel.avoidedLoss, lossModel.unprotectedLoss) * 100;

const residualPct = safeDivide(lossModel.residualLoss, lossModel.unprotectedLoss) * 100;

roiOutput.classList.remove("hidden");

roiOutput.innerHTML = `

<div class="roi-grid">

<div class="roi-panel">

<h4>Protection Investment</h4>

<div class="roi-lines">

<div class="roi-line"><span>Sector</span><strong>${sectorName}</strong></div>

<div class="roi-line"><span>Facility</span><strong>${facilityName}</strong></div>

<div class="roi-line"><span>Size</span><strong>${sizeModel.label}</strong></div>

<div class="roi-line"><span>Protection Goal</span><strong>${levelLabel}</strong></div>

<div class="roi-line"><span>Investment</span><strong>${formatMoney(investmentTotal)}</strong></div>

</div>

</div>

<div class="roi-panel">

<h4>Modeled EMP Loss Exposure</h4>

<div class="roi-lines">

<div class="roi-line"><span>Direct Damage</span><strong>${formatMoney(lossModel.directDamage)}</strong></div>

<div class="roi-line"><span>Downtime Days</span><strong>${lossModel.downtimeDays.toLocaleString()}</strong></div>

<div class="roi-line"><span>Daily Operational Loss</span><strong>${formatMoney(lossModel.dailyLoss)}</strong></div>

<div class="roi-line"><span>Downtime Loss</span><strong>${formatMoney(lossModel.downtimeLoss)}</strong></div>

<div class="roi-line"><span>Recovery / Restoration Loss</span><strong>${formatMoney(lossModel.recoveryLoss)}</strong></div>

<div class="roi-line"><span>Unprotected Total Loss</span><strong>${formatMoney(lossModel.unprotectedLoss)}</strong></div>

</div>

</div>

<div class="roi-panel">

<h4>Break-Even / CBA View</h4>

<div class="roi-lines">

<div class="roi-line"><span>Residual Loss After Protection</span><strong>${formatMoney(lossModel.residualLoss)}</strong></div>

<div class="roi-line"><span>Avoided Loss</span><strong>${formatMoney(lossModel.avoidedLoss)}</strong></div>

<div class="roi-line"><span>Loss Reduction</span><strong>${formatPct(avoidedPct)}</strong></div>

<div class="roi-line"><span>Benefit / Cost Ratio</span><strong>${formatX(lossModel.benefitCostRatio)}</strong></div>

<div class="roi-line"><span>Net Benefit</span><strong>${formatMoney(lossModel.netBenefit)}</strong></div>

<div class="roi-line"><span>Break-Even Event Threshold</span><strong>${formatPct(lossModel.breakEvenProbability)}</strong></div>

</div>

</div>

</div>

<div class="roi-kpi-grid">

<div class="roi-kpi"><div class="roi-kpi-label">Investment</div><div class="roi-kpi-value">${formatMoney(investmentTotal)}</div></div>

<div class="roi-kpi"><div class="roi-kpi-label">Unprotected Loss</div><div class="roi-kpi-value">${formatMoney(lossModel.unprotectedLoss)}</div></div>

<div class="roi-kpi"><div class="roi-kpi-label">Avoided Loss</div><div class="roi-kpi-value">${formatMoney(lossModel.avoidedLoss)}</div></div>

<div class="roi-kpi"><div class="roi-kpi-label">ROI Multiple</div><div class="roi-kpi-value">${formatX(lossModel.roiMultiple)}</div></div>

<div class="roi-kpi"><div class="roi-kpi-label">Annual Expected Avoided Value</div><div class="roi-kpi-value">${formatMoney(lossModel.annualAvoidedValue)}</div></div>

<div class="roi-kpi"><div class="roi-kpi-label">5-Year Expected Benefit</div><div class="roi-kpi-value">${formatMoney(lossModel.fiveYearExpectedBenefit)}</div></div>

<div class="roi-kpi"><div class="roi-kpi-label">10-Year Expected Benefit</div><div class="roi-kpi-value">${formatMoney(lossModel.tenYearExpectedBenefit)}</div></div>

<div class="roi-kpi"><div class="roi-kpi-label">Loss Reduction</div><div class="roi-kpi-value">${formatPct(avoidedPct)}</div></div>

</div>

<div class="roi-panel">

<h4>Visual Comparison</h4>

<div class="roi-lines">

<div class="roi-line"><span>Investment</span><strong>${formatMoney(investmentTotal)}</strong></div>

<div class="roi-bar-wrap"><div class="roi-bar" style="width:${Math.max(6, Math.min(100, safeDivide(investmentTotal, Math.max(lossModel.unprotectedLoss, investmentTotal)) * 100))}%"></div></div>

<div class="roi-line"><span>Unprotected Loss</span><strong>${formatMoney(lossModel.unprotectedLoss)}</strong></div>

<div class="roi-bar-wrap"><div class="roi-bar loss" style="width:100%"></div></div>

<div class="roi-line"><span>Residual Loss</span><strong>${formatMoney(lossModel.residualLoss)} (${formatPct(residualPct)})</strong></div>

<div class="roi-bar-wrap"><div class="roi-bar loss" style="width:${Math.max(4, residualPct)}%"></div></div>

<div class="roi-line"><span>Avoided Loss</span><strong>${formatMoney(lossModel.avoidedLoss)}</strong></div>

<div class="roi-bar-wrap"><div class="roi-bar" style="width:${Math.max(6, avoidedPct)}%"></div></div>

</div>

</div>

`;

}

function renderEstimator() {

const sectorName = sectorSelect.value;

const facilityName = facilitySelect.value;

const level = protectionLevelSelect.value;

const sizeKey = facilitySizeSelect.value;

const sector = sectorData.find(s => s.name === sectorName);

const facility = sector ? sector.facilities.find(f => f.name === facilityName) : null;

if (!sector || !facility) return;

const template = getEstimatorTemplate(sectorName, facilityName, level);

const sizeProfile = getSizeProfileForFacility(facilityName);

const sizeModel = sizeProfile[sizeKey] || sizeProfile.medium;

const levelLabel = level === "target" ? "Target State" : "Hardened Future State";

const scaledCosts = {};

Object.entries(template.costs).forEach(([label, value]) => {

scaledCosts[label] = value * sizeModel.multiplier;

});

const total = Object.values(scaledCosts).reduce((sum, val) => sum + val, 0);

estimatorOutput.classList.remove("hidden");

estimatorOutput.innerHTML = `

<div class="estimate-grid">

<div class="estimate-box"><div class="estimate-box-label">Sector</div><div class="estimate-box-value">${sectorName}</div></div>

<div class="estimate-box"><div class="estimate-box-label">Facility Type</div><div class="estimate-box-value">${facilityName}</div></div>

<div class="estimate-box"><div class="estimate-box-label">Facility Size</div><div class="estimate-box-value"><span class="size-chip">${sizeModel.label}</span></div></div>

<div class="estimate-box"><div class="estimate-box-label">Protection Goal</div><div class="estimate-box-value">${levelLabel}</div></div>

<div class="estimate-box"><div class="estimate-box-label">Modeled Readiness</div><div class="estimate-box-value">${facility[level]}%</div></div>

</div>

<div class="scope-panel">

<h4 class="panel-title">Modeled Planning Assumptions</h4>

<div class="scope-grid">

<div class="estimate-box"><div class="estimate-box-label">Asset Scale</div><div class="estimate-note">${template.assumptions.assetScale}</div></div>

<div class="estimate-box"><div class="estimate-box-label">Strategy</div><div class="estimate-note">${template.assumptions.strategy}</div></div>

<div class="estimate-box"><div class="estimate-box-label">Implementation</div><div class="estimate-note">${template.assumptions.implementation}</div></div>

<div class="estimate-box"><div class="estimate-box-label">Size Assumption</div><div class="estimate-note">${sizeModel.description}</div></div>

</div>

</div>

<div class="punchlist-panel">

<h4 class="panel-title">Modeled EMP Protection Punch List</h4>

<div class="punchlist">

${template.items.map((item, idx) => `

<div class="punch-item">

<div class="punch-num">${idx + 1}</div>

<div class="punch-text">${item[0]}</div>

<div class="punch-tag">${item[1]}</div>

</div>

`).join("")}

</div>

</div>

<div class="cost-panel">

<h4 class="panel-title">Planning-Level Cost Breakdown</h4>

<div class="cost-lines">

${Object.entries(scaledCosts).map(([label, value]) => `

<div class="cost-line"><span>${label}</span><strong>${formatMoney(value)}</strong></div>

`).join("")}

</div>

<div class="cost-total">

<div class="total-label">Estimated Total</div>

<div class="total-value">${formatMoney(total)}</div>

</div>

<div class="estimate-note">

Size multiplier applied: <strong>${sizeModel.label}</strong> (${sizeModel.multiplier.toFixed(2)}x baseline). This is a planning-level modeled illustration, not a construction bid.

</div>

</div>

`;

const lossModel = buildLossModel(facilityName, sizeKey, total, level, facility[level]);

renderROIComparison(sectorName, facilityName, sizeModel, levelLabel, total, lossModel);

}

filters.forEach(btn => {

btn.addEventListener("click", () => {

filters.forEach(b => b.classList.remove("active"));

btn.classList.add("active");

activeView = btn.dataset.view;

renderAggregateCard();

renderLayeredGrid();

detail.classList.add("hidden");

});

});

closeDetail.addEventListener("click", () => detail.classList.add("hidden"));

sectorSelect.addEventListener("change", populateFacilitySelector);

facilitySelect.addEventListener("change", populateSizeSelector);

generateEstimateBtn.addEventListener("click", renderEstimator);

buildSelectors();

renderAggregateCard();

renderLayeredGrid();

})();

</script>

Power/Fiber Infrastructure

Internet Infrastructure

Space Weather Monitoring