Widget:Solvency2 BalanceSheet: Difference between revisions
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
<!-- Solvency II Balance Sheet — Interactive Module for MediaWiki --> |
|||
<!DOCTYPE html> |
|||
<div id="insurerbrain-module-solvency2"> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Solvency II Balance Sheet</title> |
|||
<style> |
|||
:root { |
|||
--bg: #ffffff; |
|||
--surface: #f8f9fa; |
|||
--surface2: #eaecf0; |
|||
--border: #a2a9b1; |
|||
--border-heavy: #202122; |
|||
--text: #202122; |
|||
--text-dim: #54595d; |
|||
--text-muted: #72777d; |
|||
--link: #000099; |
|||
<div class="s2-header"> |
|||
/* Monochrome block palette — dark-to-light for visual hierarchy */ |
|||
<h2 class="s2-title">Solvency II Balance Sheet</h2> |
|||
--assets-fill: #202122; |
|||
<p class="s2-subtitle">Adjust the sliders to explore how changes in assets and liabilities affect the solvency position.</p> |
|||
--bel-fill: #54595d; |
|||
</div> |
|||
--rm-fill: #72777d; |
|||
--scr-fill: #a2a9b1; |
|||
--mcr-fill: #c8ccd1; |
|||
--surplus-fill: #eaecf0; |
|||
} |
|||
<div class="s2-dashboard"> |
|||
* { margin: 0; padding: 0; box-sizing: border-box; } |
|||
<!-- Solvency Ratio Indicator --> |
|||
body { |
|||
<div class="s2-ratio-panel"> |
|||
background: var(--bg); |
|||
<div class="s2-ratio-ring-wrap"> |
|||
color: var(--text); |
|||
<svg class="s2-ratio-ring" viewBox="0 0 120 120"> |
|||
font-family: sans-serif; |
|||
<circle class="s2-ring-bg" cx="60" cy="60" r="52" /> |
|||
line-height: 1.6; |
|||
<circle class="s2-ring-fg" cx="60" cy="60" r="52" stroke-dasharray="326.73" stroke-dashoffset="326.73" /> |
|||
-webkit-font-smoothing: auto; |
|||
</svg> |
|||
} |
|||
<div class="s2-ratio-label"> |
|||
<span class="s2-ratio-value" id="s2-ratio-value">0%</span> |
|||
<span class="s2-ratio-caption">Solvency Ratio</span> |
|||
</div> |
|||
</div> |
|||
<div class="s2-ratio-badges"> |
|||
<span class="s2-badge" id="s2-badge-scr">SCR Coverage: —</span> |
|||
<span class="s2-badge" id="s2-badge-mcr">MCR Coverage: —</span> |
|||
</div> |
|||
<div class="s2-status-bar" id="s2-status-bar">Calculating…</div> |
|||
</div> |
|||
<!-- Balance Sheet Visual --> |
|||
.container { |
|||
<div class="s2-sheet-wrap"> |
|||
max-width: 900px; |
|||
<div class="s2-col s2-col-assets"> |
|||
margin: 0 auto; |
|||
<h3 class="s2-col-head">Assets</h3> |
|||
padding: 24px 16px 60px; |
|||
<div class="s2-bar-stack" id="s2-asset-bars"></div> |
|||
} |
|||
<div class="s2-col-total">Total: <strong id="s2-asset-total">0</strong> €m</div> |
|||
</div> |
|||
<div class="s2-col s2-col-liabilities"> |
|||
<h3 class="s2-col-head">Liabilities & Own Funds</h3> |
|||
<div class="s2-bar-stack" id="s2-liability-bars"></div> |
|||
<div class="s2-col-total">Total: <strong id="s2-liability-total">0</strong> €m</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- Sliders --> |
|||
<div class="s2-controls"> |
|||
header { |
|||
<fieldset class="s2-fieldset"> |
|||
border-bottom: 2px solid var(--border-heavy); |
|||
<legend>Assets</legend> |
|||
padding-bottom: 10px; |
|||
<div class="s2-slider-grid" id="s2-asset-sliders"></div> |
|||
margin-bottom: 20px; |
|||
</fieldset> |
|||
} |
|||
<fieldset class="s2-fieldset"> |
|||
<legend>Liabilities</legend> |
|||
<div class="s2-slider-grid" id="s2-liability-sliders"></div> |
|||
</fieldset> |
|||
</div> |
|||
<!-- Summary Table (uses wikitable class for native MediaWiki styling) --> |
|||
header h1 { |
|||
<table class="wikitable s2-summary-table" id="s2-summary-table"> |
|||
font-size: 1.8em; |
|||
<caption>Solvency II — Key Figures (€ millions)</caption> |
|||
font-weight: bold; |
|||
<thead> |
|||
color: var(--text); |
|||
<tr><th>Item</th><th>Value</th><th>Category</th></tr> |
|||
line-height: 1.25; |
|||
</thead> |
|||
<tbody id="s2-summary-tbody"></tbody> |
|||
} |
|||
</table> |
|||
<p class="s2-footnote">This module is for educational purposes only and uses simplified, illustrative figures. Actual Solvency II reporting follows EIOPA technical standards.</p> |
|||
header p { |
|||
</div> |
|||
font-size: 0.88em; |
|||
color: var(--text-dim); |
|||
margin-top: 4px; |
|||
line-height: 1.6; |
|||
} |
|||
<style> |
|||
/* ---- Two-column master layout ---- */ |
|||
/* ===== STRICT SCOPING — every rule prefixed ===== */ |
|||
.main-grid { |
|||
display: grid; |
|||
grid-template-columns: 1fr 300px; |
|||
gap: 24px; |
|||
align-items: start; |
|||
} |
|||
@media (max-width: 760px) { |
|||
.main-grid { grid-template-columns: 1fr; } |
|||
} |
|||
#insurerbrain-module-solvency2 { |
|||
/* ---- Balance-sheet visual ---- */ |
|||
--s2-accent: #1a6b4e; |
|||
.bs-visual { |
|||
--s2-accent-light: #e3f2ec; |
|||
border: 1px solid var(--border); |
|||
--s2-warn: #c0760a; |
|||
background: var(--bg); |
|||
--s2-warn-light: #fdf3e1; |
|||
padding: 20px 24px 16px; |
|||
--s2-danger: #b52a2a; |
|||
} |
|||
--s2-danger-light: #fce8e8; |
|||
--s2-border: #c8ccd1; |
|||
--s2-text: #202122; |
|||
--s2-text-muted: #54595d; |
|||
--s2-surface: #f8f9fa; |
|||
--s2-white: #ffffff; |
|||
--s2-radius: 0.35rem; |
|||
max-width: 64rem; |
|||
margin: 1.5rem auto; |
|||
padding: 0 0.75rem; |
|||
} |
|||
/* ---- Header ---- */ |
|||
.bs-title-row { |
|||
#insurerbrain-module-solvency2 .s2-header { |
|||
display: flex; |
|||
margin-bottom: 1.25rem; |
|||
justify-content: space-between; |
|||
} |
|||
margin-bottom: 14px; |
|||
#insurerbrain-module-solvency2 .s2-title { |
|||
} |
|||
margin: 0 0 0.25rem; |
|||
border-bottom: none; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-subtitle { |
|||
margin: 0; |
|||
color: var(--s2-text-muted); |
|||
} |
|||
/* ---- Dashboard ---- */ |
|||
.bs-col-label { |
|||
#insurerbrain-module-solvency2 .s2-dashboard { |
|||
font-size: 0.78em; |
|||
display: flex; |
|||
gap: 1.25rem; |
|||
flex-wrap: wrap; |
|||
text-transform: uppercase; |
|||
margin-bottom: 1.5rem; |
|||
color: var(--text-muted); |
|||
} |
|||
/* -- Ratio Panel -- */ |
|||
.bs-columns { |
|||
#insurerbrain-module-solvency2 .s2-ratio-panel { |
|||
display: grid; |
|||
flex: 0 0 auto; |
|||
grid-template-columns: 1fr 24px 1fr; |
|||
min-width: 11rem; |
|||
display: flex; |
|||
} |
|||
flex-direction: column; |
|||
align-items: center; |
|||
gap: 0.6rem; |
|||
padding: 1rem; |
|||
border: 1px solid var(--s2-border); |
|||
border-radius: var(--s2-radius); |
|||
background: var(--s2-surface); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ratio-ring-wrap { |
|||
position: relative; |
|||
width: 7.5rem; |
|||
height: 7.5rem; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ratio-ring { |
|||
width: 100%; |
|||
height: 100%; |
|||
transform: rotate(-90deg); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ring-bg { |
|||
fill: none; |
|||
stroke: var(--s2-border); |
|||
stroke-width: 8; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ring-fg { |
|||
fill: none; |
|||
stroke: var(--s2-accent); |
|||
stroke-width: 8; |
|||
stroke-linecap: round; |
|||
transition: stroke-dashoffset 0.5s ease, stroke 0.4s ease; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ratio-label { |
|||
position: absolute; |
|||
inset: 0; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ratio-value { |
|||
font-size: 1.5rem; |
|||
font-weight: 700; |
|||
line-height: 1.1; |
|||
color: var(--s2-text); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ratio-caption { |
|||
font-size: 0.7rem; |
|||
text-transform: uppercase; |
|||
letter-spacing: 0.04em; |
|||
color: var(--s2-text-muted); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-ratio-badges { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 0.35rem; |
|||
justify-content: center; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-badge { |
|||
font-size: 0.72rem; |
|||
padding: 0.15rem 0.45rem; |
|||
border-radius: 999px; |
|||
background: var(--s2-accent-light); |
|||
color: var(--s2-accent); |
|||
white-space: nowrap; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-status-bar { |
|||
font-size: 0.78rem; |
|||
font-weight: 600; |
|||
padding: 0.3rem 0.7rem; |
|||
border-radius: var(--s2-radius); |
|||
text-align: center; |
|||
transition: background 0.3s, color 0.3s; |
|||
} |
|||
/* -- Balance Sheet Columns -- */ |
|||
.bs-col { |
|||
#insurerbrain-module-solvency2 .s2-sheet-wrap { |
|||
display: flex; |
|||
flex: 1 1 20rem; |
|||
display: flex; |
|||
gap: 0.5rem; |
|||
} |
|||
min-height: 18rem; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-col { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
border: 1px solid var(--s2-border); |
|||
border-radius: var(--s2-radius); |
|||
overflow: hidden; |
|||
background: var(--s2-white); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-col-head { |
|||
margin: 0; |
|||
padding: 0.45rem 0.6rem; |
|||
font-size: 0.82rem; |
|||
text-transform: uppercase; |
|||
letter-spacing: 0.04em; |
|||
border-bottom: 1px solid var(--s2-border); |
|||
background: var(--s2-surface); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-bar-stack { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
padding: 0.25rem; |
|||
gap: 2px; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-bar-item { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 0.25rem 0.45rem; |
|||
border-radius: 3px; |
|||
font-size: 0.75rem; |
|||
color: var(--s2-white); |
|||
transition: flex-grow 0.45s ease; |
|||
overflow: hidden; |
|||
min-height: 1.4rem; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-bar-item span:last-child { |
|||
font-weight: 600; |
|||
margin-left: 0.3rem; |
|||
white-space: nowrap; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-col-total { |
|||
padding: 0.35rem 0.6rem; |
|||
font-size: 0.82rem; |
|||
border-top: 1px solid var(--s2-border); |
|||
background: var(--s2-surface); |
|||
text-align: right; |
|||
} |
|||
/* ---- Controls ---- */ |
|||
.bs-separator { |
|||
#insurerbrain-module-solvency2 .s2-controls { |
|||
display: flex; |
|||
display: flex; |
|||
align-items: stretch; |
|||
gap: 1rem; |
|||
justify-content: center; |
|||
flex-wrap: wrap; |
|||
} |
|||
margin-bottom: 1.25rem; |
|||
.bs-separator .line { |
|||
} |
|||
width: 1px; |
|||
#insurerbrain-module-solvency2 .s2-fieldset { |
|||
background: var(--border); |
|||
flex: 1 1 18rem; |
|||
} |
|||
border: 1px solid var(--s2-border); |
|||
border-radius: var(--s2-radius); |
|||
padding: 0.6rem 0.8rem 0.8rem; |
|||
margin: 0; |
|||
background: var(--s2-surface); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-fieldset legend { |
|||
font-weight: 600; |
|||
font-size: 0.85rem; |
|||
padding: 0 0.3rem; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-slider-grid { |
|||
display: grid; |
|||
grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); |
|||
gap: 0.55rem; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-slider-group { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 0.15rem; |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-slider-label { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
font-size: 0.78rem; |
|||
color: var(--s2-text-muted); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-slider-label strong { |
|||
color: var(--s2-text); |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-slider-group input[type="range"] { |
|||
width: 100%; |
|||
cursor: pointer; |
|||
accent-color: var(--s2-accent); |
|||
} |
|||
/* ---- Summary Table ---- */ |
|||
#insurerbrain-module-solvency2 .s2-summary-table { |
|||
.bs-block { |
|||
width: 100%; |
|||
margin-top: 0; |
|||
} |
|||
cursor: pointer; |
|||
transition: background 0.2s ease, box-shadow 0.2s ease; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
border: 1px solid transparent; |
|||
color: #fff; |
|||
} |
|||
/* ---- Footnote ---- */ |
|||
.bs-block .label { |
|||
#insurerbrain-module-solvency2 .s2-footnote { |
|||
font-size: 0.82em; |
|||
font-size: 0.78rem; |
|||
color: var(--s2-text-muted); |
|||
line-height: 1.3; |
|||
margin-top: 0.5rem; |
|||
} |
|||
} |
|||
.bs-block .sublabel { |
|||
font-size: 0.72em; |
|||
opacity: 0.75; |
|||
margin-top: 1px; |
|||
} |
|||
.bs-block .value { |
|||
font-size: 0.88em; |
|||
font-weight: bold; |
|||
margin-top: 4px; |
|||
font-variant-numeric: tabular-nums; |
|||
} |
|||
/* ---- Status colours ---- */ |
|||
.bs-block:hover { |
|||
#insurerbrain-module-solvency2 .s2-status-good { |
|||
box-shadow: 0 0 0 2px var(--border-heavy); |
|||
background: var(--s2-accent-light); |
|||
z-index: 2; |
|||
color: var(--s2-accent); |
|||
} |
|||
} |
|||
.bs-block.active { |
|||
#insurerbrain-module-solvency2 .s2-status-warn { |
|||
box-shadow: 0 0 0 2.5px var(--border-heavy); |
|||
background: var(--s2-warn-light); |
|||
z-index: 3; |
|||
color: var(--s2-warn); |
|||
} |
|||
} |
|||
#insurerbrain-module-solvency2 .s2-status-danger { |
|||
background: var(--s2-danger-light); |
|||
color: var(--s2-danger); |
|||
} |
|||
/* ---- Responsive ---- */ |
|||
@media (max-width: 48rem) { |
|||
.block-assets { background: var(--assets-fill); } |
|||
#insurerbrain-module-solvency2 .s2-dashboard { |
|||
.block-bel { background: var(--bel-fill); } |
|||
flex-direction: column; |
|||
.block-rm { background: var(--rm-fill); } |
|||
.block-scr { background: var(--scr-fill); color: var(--text); } |
|||
.block-mcr { background: var(--mcr-fill); color: var(--text); } |
|||
.block-surplus { background: var(--surplus-fill); color: var(--text); border: 1px solid var(--border); } |
|||
/* ---- Legend ---- */ |
|||
.legend-strip { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 4px; |
|||
margin-top: 16px; |
|||
border-top: 1px solid var(--border); |
|||
padding-top: 12px; |
|||
} |
} |
||
#insurerbrain-module-solvency2 .s2-ratio-panel { |
|||
.legend-item { |
|||
flex-direction: row; |
|||
align-items: center; |
|||
gap: 5px; |
|||
padding: 3px 8px; |
|||
font-size: 0.75em; |
|||
color: var(--text-dim); |
|||
cursor: pointer; |
|||
border: 1px solid transparent; |
|||
border-radius: 2px; |
|||
transition: border-color 0.15s ease; |
|||
} |
|||
.legend-item:hover { border-color: var(--border); } |
|||
.legend-dot { |
|||
width: 10px; height: 10px; |
|||
border-radius: 1px; |
|||
flex-shrink: 0; |
|||
} |
|||
/* ---- Status bar ---- */ |
|||
.status-bar { |
|||
display: flex; |
|||
gap: 16px; |
|||
flex-wrap: wrap; |
flex-wrap: wrap; |
||
justify-content: center; |
|||
font-size: 0.8em; |
|||
color: var(--text-dim); |
|||
} |
} |
||
#insurerbrain-module-solvency2 .s2-sheet-wrap { |
|||
.status-item { |
|||
min-height: 14rem; |
|||
align-items: center; |
|||
gap: 6px; |
|||
} |
} |
||
} |
|||
.status-dot { |
|||
</style> |
|||
width: 7px; height: 7px; |
|||
border-radius: 50%; |
|||
flex-shrink: 0; |
|||
} |
|||
.status-dot.ok { background: var(--text); } |
|||
.status-dot.warn { background: var(--text-muted); } |
|||
.status-dot.fail { border: 2px solid var(--text); background: transparent; } |
|||
<script> |
|||
/* ---- Side panel ---- */ |
|||
(function () { |
|||
.side-panel { |
|||
'use strict'; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 16px; |
|||
} |
|||
/* ============ DATA MODEL ============ */ |
|||
.info-card { |
|||
var assetItems = [ |
|||
border: 1px solid var(--border); |
|||
{ key: 'gov_bonds', label: 'Government Bonds', value: 350, min: 0, max: 800, color: '#1a6b4e' }, |
|||
background: var(--bg); |
|||
{ key: 'corp_bonds', label: 'Corporate Bonds', value: 220, min: 0, max: 600, color: '#2a8c68' }, |
|||
padding: 16px 18px; |
|||
{ key: 'equities', label: 'Equities', value: 120, min: 0, max: 400, color: '#3bae85' }, |
|||
} |
|||
{ key: 'property', label: 'Property', value: 80, min: 0, max: 300, color: '#5bc4a0' }, |
|||
{ key: 'reins_recv', label: 'Reinsurance Recoverables',value: 60, min: 0, max: 200, color: '#85d4b8' }, |
|||
{ key: 'cash', label: 'Cash & Equivalents', value: 40, min: 0, max: 200, color: '#abd9c9' } |
|||
]; |
|||
var liabilityItems = [ |
|||
.card-label { |
|||
{ key: 'best_est', label: 'Best Estimate Liabilities', value: 480, min: 100, max: 900, color: '#8b3a3a' }, |
|||
font-size: 0.7em; |
|||
{ key: 'risk_margin', label: 'Risk Margin', value: 45, min: 5, max: 150, color: '#b35656' }, |
|||
font-weight: bold; |
|||
{ key: 'other_liab', label: 'Other Liabilities', value: 55, min: 0, max: 200, color: '#cc8080' }, |
|||
letter-spacing: 0.08em; |
|||
{ key: 'scr', label: 'SCR (Own Funds Buffer)', value: 180, min: 20, max: 400, color: '#1b5e90' }, |
|||
text-transform: uppercase; |
|||
{ key: 'mcr', label: 'MCR (Minimum Capital)', value: 70, min: 10, max: 200, color: '#3a8bc2' } |
|||
color: var(--text-muted); |
|||
]; |
|||
margin-bottom: 10px; |
|||
} |
|||
/* |
/* ============ REFERENCES ============ */ |
||
var root = document.getElementById('insurerbrain-module-solvency2'); |
|||
.gauge-card { text-align: center; padding: 18px; } |
|||
var assetSliders = root.querySelector('#s2-asset-sliders'); |
|||
var liabSliders = root.querySelector('#s2-liability-sliders'); |
|||
var assetBars = root.querySelector('#s2-asset-bars'); |
|||
var liabBars = root.querySelector('#s2-liability-bars'); |
|||
var assetTotalEl = root.querySelector('#s2-asset-total'); |
|||
var liabTotalEl = root.querySelector('#s2-liability-total'); |
|||
var ratioValueEl = root.querySelector('#s2-ratio-value'); |
|||
var ringFg = root.querySelector('.s2-ring-fg'); |
|||
var statusBar = root.querySelector('#s2-status-bar'); |
|||
var badgeSCR = root.querySelector('#s2-badge-scr'); |
|||
var badgeMCR = root.querySelector('#s2-badge-mcr'); |
|||
var tbody = root.querySelector('#s2-summary-tbody'); |
|||
var CIRCUMFERENCE = 2 * Math.PI * 52; // matches r=52 |
|||
.gauge-value { |
|||
font-size: 2em; |
|||
font-weight: bold; |
|||
color: var(--text); |
|||
line-height: 1.1; |
|||
} |
|||
.gauge-label { |
|||
font-size: 0.78em; |
|||
color: var(--text-muted); |
|||
margin-top: 2px; |
|||
} |
|||
.gauge-bar-track { |
|||
margin-top: 14px; |
|||
height: 6px; |
|||
background: var(--surface2); |
|||
border-radius: 3px; |
|||
overflow: hidden; |
|||
} |
|||
.gauge-bar-fill { |
|||
height: 100%; |
|||
border-radius: 3px; |
|||
background: var(--text); |
|||
transition: width 0.6s ease; |
|||
} |
|||
.gauge-thresholds { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
margin-top: 6px; |
|||
font-size: 0.68em; |
|||
color: var(--text-muted); |
|||
} |
|||
/* |
/* ============ BUILD SLIDERS ============ */ |
||
function buildSliders(items, container) { |
|||
.slider-group { margin-bottom: 14px; } |
|||
items.forEach(function (item) { |
|||
.slider-group:last-child { margin-bottom: 0; } |
|||
var group = document.createElement('div'); |
|||
.slider-header { |
|||
group.className = 's2-slider-group'; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: baseline; |
|||
margin-bottom: 5px; |
|||
} |
|||
.slider-header label { |
|||
font-size: 0.8em; |
|||
color: var(--text-dim); |
|||
} |
|||
.slider-val { |
|||
font-size: 0.8em; |
|||
font-weight: bold; |
|||
color: var(--text); |
|||
font-variant-numeric: tabular-nums; |
|||
} |
|||
var lbl = document.createElement('div'); |
|||
input[type="range"] { |
|||
lbl.className = 's2-slider-label'; |
|||
-webkit-appearance: none; |
|||
lbl.innerHTML = '<span>' + item.label + '</span><strong id="s2-val-' + item.key + '">' + item.value + '</strong>'; |
|||
width: 100%; |
|||
height: 3px; |
|||
background: var(--surface2); |
|||
border-radius: 2px; |
|||
outline: none; |
|||
cursor: pointer; |
|||
} |
|||
input[type="range"]::-webkit-slider-thumb { |
|||
-webkit-appearance: none; |
|||
width: 14px; height: 14px; |
|||
border-radius: 50%; |
|||
background: var(--text); |
|||
cursor: pointer; |
|||
border: 2px solid #fff; |
|||
box-shadow: 0 0 0 1px var(--border); |
|||
} |
|||
input[type="range"]::-moz-range-thumb { |
|||
width: 14px; height: 14px; |
|||
border-radius: 50%; |
|||
background: var(--text); |
|||
cursor: pointer; |
|||
border: 2px solid #fff; |
|||
box-shadow: 0 0 0 1px var(--border); |
|||
} |
|||
var inp = document.createElement('input'); |
|||
/* ---- Detail card ---- */ |
|||
inp.type = 'range'; |
|||
.info-card h3 { |
|||
inp.min = item.min; |
|||
font-size: 1.05em; |
|||
inp.max = item.max; |
|||
font-weight: bold; |
|||
inp.value = item.value; |
|||
color: var(--text); |
|||
inp.id = 's2-range-' + item.key; |
|||
margin-bottom: 6px; |
|||
inp.setAttribute('aria-label', item.label + ' (€m)'); |
|||
line-height: 1.3; |
|||
} |
|||
.info-card p { |
|||
font-size: 0.84em; |
|||
color: var(--text-dim); |
|||
line-height: 1.6; |
|||
} |
|||
.formula { |
|||
margin-top: 10px; |
|||
padding: 8px 12px; |
|||
background: var(--surface); |
|||
border: 1px solid var(--surface2); |
|||
font-family: "Nimbus Roman No9 L", "Times New Roman", Times, serif; |
|||
font-size: 0.88em; |
|||
color: var(--text); |
|||
letter-spacing: 0.01em; |
|||
} |
|||
inp.addEventListener('input', function () { |
|||
/* ---- MCR badge inside SCR ---- */ |
|||
item.value = Number(this.value); |
|||
.tier-badge { |
|||
root.querySelector('#s2-val-' + item.key).textContent = item.value; |
|||
display: inline-block; |
|||
update(); |
|||
}); |
|||
font-size: 0.72em; |
|||
font-weight: bold; |
|||
margin-top: 3px; |
|||
background: rgba(255,255,255,0.18); |
|||
color: inherit; |
|||
cursor: pointer; |
|||
} |
|||
.block-scr .tier-badge { background: rgba(0,0,0,0.08); } |
|||
group.appendChild(lbl); |
|||
/* ---- Deficit block ---- */ |
|||
group.appendChild(inp); |
|||
.block-deficit { |
|||
container.appendChild(group); |
|||
background: #fff; |
|||
}); |
|||
border: 2px dashed var(--text); |
|||
color: var(--text); |
|||
} |
} |
||
/* ============ BUILD BAR ITEMS (once) ============ */ |
|||
/* ---- Print ---- */ |
|||
function buildBars(items, container) { |
|||
@media print { |
|||
items.forEach(function (item) { |
|||
.side-panel { display: none; } |
|||
var bar = document.createElement('div'); |
|||
.main-grid { grid-template-columns: 1fr; } |
|||
bar.className = 's2-bar-item'; |
|||
.container { padding: 0; } |
|||
bar.id = 's2-bar-' + item.key; |
|||
.bs-visual { border: 1px solid #000; } |
|||
bar.style.background = item.color; |
|||
bar.innerHTML = '<span>' + item.label + '</span><span></span>'; |
|||
container.appendChild(bar); |
|||
}); |
|||
} |
} |
||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="container"> |
|||
<header> |
|||
<h1>Solvency II Balance Sheet</h1> |
|||
<p>Interactive breakdown of the market-value balance sheet under the EU Solvency II Directive. Adjust the sliders and click any block to explore how assets, technical provisions, and capital requirements interconnect.</p> |
|||
</header> |
|||
/* ============ UPDATE ============ */ |
|||
<div class="main-grid"> |
|||
function update() { |
|||
<!-- Left column: balance-sheet diagram --> |
|||
var totalAssets = assetItems.reduce(function (s, i) { return s + i.value; }, 0); |
|||
<div> |
|||
var techProv = liabilityItems[0].value + liabilityItems[1].value; // BE + RM |
|||
<div class="bs-visual" id="bsVisual"> |
|||
var otherLiab = liabilityItems[2].value; |
|||
<div class="bs-title-row"> |
|||
var scrReq = liabilityItems[3].value; |
|||
var mcrReq = liabilityItems[4].value; |
|||
<span class="bs-col-label">Liabilities & Own Funds</span> |
|||
</div> |
|||
<div class="bs-columns" id="bsColumns"> |
|||
<div class="bs-col" id="assetsCol"></div> |
|||
<div class="bs-separator"><div class="line"></div></div> |
|||
<div class="bs-col" id="liabCol"></div> |
|||
</div> |
|||
<div class="legend-strip" id="legend"></div> |
|||
</div> |
|||
<div class="status-bar" id="statusBar"></div> |
|||
</div> |
|||
var totalLiabSide = techProv + otherLiab + scrReq + mcrReq; |
|||
<!-- Right column: controls & info --> |
|||
<div class="side-panel"> |
|||
<!-- Solvency ratio --> |
|||
<div class="info-card gauge-card"> |
|||
<div class="card-label">Solvency Ratio</div> |
|||
<div class="gauge-value" id="ratioValue">—</div> |
|||
<div class="gauge-label">Eligible Own Funds / SCR</div> |
|||
<div class="gauge-bar-track"><div class="gauge-bar-fill" id="ratioBar"></div></div> |
|||
<div class="gauge-thresholds"> |
|||
<span>0%</span> |
|||
<span>100% min</span> |
|||
<span>200%</span> |
|||
<span>300%</span> |
|||
</div> |
|||
</div> |
|||
// Eligible Own Funds = Assets - Technical Provisions - Other Liabilities |
|||
<!-- Sliders --> |
|||
var eof = totalAssets - techProv - otherLiab; |
|||
<div class="info-card"> |
|||
if (eof < 0) eof = 0; |
|||
<div class="card-label">Adjust Values (€ bn)</div> |
|||
var solvencyRatio = scrReq > 0 ? (eof / scrReq) * 100 : 0; |
|||
<div class="slider-group"> |
|||
var mcrRatio = mcrReq > 0 ? (eof / mcrReq) * 100 : 0; |
|||
<div class="slider-header"> |
|||
<label>Total Assets</label> |
|||
<span class="slider-val" id="valAssets">150</span> |
|||
</div> |
|||
<input type="range" id="sliderAssets" min="80" max="250" value="150"> |
|||
</div> |
|||
// Ratio ring |
|||
<div class="slider-group"> |
|||
var capped = Math.min(solvencyRatio, 250); |
|||
<div class="slider-header"> |
|||
var offset = CIRCUMFERENCE - (CIRCUMFERENCE * capped / 250); |
|||
<label>Best Estimate Liabilities</label> |
|||
ringFg.style.strokeDashoffset = offset; |
|||
<span class="slider-val" id="valBEL">85</span> |
|||
</div> |
|||
<input type="range" id="sliderBEL" min="30" max="180" value="85"> |
|||
</div> |
|||
var ringColor; |
|||
<div class="slider-group"> |
|||
if (solvencyRatio >= 150) ringColor = '#1a6b4e'; |
|||
<div class="slider-header"> |
|||
else if (solvencyRatio >= 100) ringColor = '#c0760a'; |
|||
<label>Risk Margin</label> |
|||
else ringColor = '#b52a2a'; |
|||
<span class="slider-val" id="valRM">8</span> |
|||
ringFg.style.stroke = ringColor; |
|||
</div> |
|||
<input type="range" id="sliderRM" min="1" max="30" value="8"> |
|||
</div> |
|||
ratioValueEl.textContent = Math.round(solvencyRatio) + '%'; |
|||
<div class="slider-group"> |
|||
<div class="slider-header"> |
|||
<label>SCR</label> |
|||
<span class="slider-val" id="valSCR">35</span> |
|||
</div> |
|||
<input type="range" id="sliderSCR" min="10" max="80" value="35"> |
|||
</div> |
|||
badgeSCR.textContent = 'SCR Coverage: ' + Math.round(solvencyRatio) + '%'; |
|||
<div class="slider-group"> |
|||
badgeMCR.textContent = 'MCR Coverage: ' + Math.round(mcrRatio) + '%'; |
|||
<div class="slider-header"> |
|||
<label>MCR (% of SCR)</label> |
|||
<span class="slider-val" id="valMCR">35%</span> |
|||
</div> |
|||
<input type="range" id="sliderMCR" min="20" max="50" value="35"> |
|||
</div> |
|||
</div> |
|||
// Status |
|||
<!-- Detail panel --> |
|||
statusBar.classList.remove('s2-status-good', 's2-status-warn', 's2-status-danger'); |
|||
<div class="info-card" id="detailCard"> |
|||
if (solvencyRatio >= 150) { |
|||
<div class="card-label">Component Detail</div> |
|||
statusBar.textContent = 'Comfortable — well above SCR'; |
|||
<h3 id="detailTitle">Select a component</h3> |
|||
statusBar.classList.add('s2-status-good'); |
|||
<p id="detailDesc">Click or hover on any block in the balance sheet to see its definition, purpose, and relationship to other elements.</p> |
|||
} else if (solvencyRatio >= 100) { |
|||
<div class="formula" id="detailFormula" style="display:none;"></div> |
|||
statusBar.textContent = 'Caution — approaching SCR threshold'; |
|||
</div> |
|||
statusBar.classList.add('s2-status-warn'); |
|||
</div> |
|||
} else { |
|||
</div> |
|||
statusBar.textContent = 'Breach — own funds below SCR'; |
|||
</div> |
|||
statusBar.classList.add('s2-status-danger'); |
|||
} |
|||
// Asset bars |
|||
<script> |
|||
assetItems.forEach(function (item) { |
|||
const data = { assets: 150, bel: 85, rm: 8, scr: 35, mcrPct: 35 }; |
|||
var bar = root.querySelector('#s2-bar-' + item.key); |
|||
bar.style.flexGrow = item.value || 0.01; |
|||
bar.querySelector('span:last-child').textContent = item.value + ' €m'; |
|||
}); |
|||
assetTotalEl.textContent = totalAssets; |
|||
// Liability bars |
|||
const descriptions = { |
|||
liabilityItems.forEach(function (item) { |
|||
assets: { |
|||
var bar = root.querySelector('#s2-bar-' + item.key); |
|||
title: 'Market Value of Assets', |
|||
bar.style.flexGrow = item.value || 0.01; |
|||
desc: 'All assets held by the insurer valued at fair (market) value under Solvency II. This includes bonds, equities, property, cash, reinsurance recoverables, and other investments. Market-consistent valuation is a core SII principle.', |
|||
bar.querySelector('span:last-child').textContent = item.value + ' €m'; |
|||
formula: null |
|||
} |
}); |
||
liabTotalEl.textContent = totalLiabSide; |
|||
bel: { |
|||
title: 'Best Estimate Liabilities (BEL)', |
|||
desc: 'The probability-weighted average of future cash flows for insurance obligations, discounted using the relevant risk-free interest rate term structure prescribed by EIOPA. It represents the expected cost of meeting policyholder obligations.', |
|||
formula: 'BEL = \u03A3 PV(expected future cash flows)' |
|||
}, |
|||
rm: { |
|||
title: 'Risk Margin', |
|||
desc: 'An additional amount over the BEL to ensure technical provisions are sufficient. Calculated as the cost of holding eligible own funds equal to the SCR over the lifetime of the obligations, using a prescribed cost-of-capital rate (currently 6%).', |
|||
formula: 'RM = CoC \u00D7 \u03A3 SCR(t) / (1 + r(t))^t' |
|||
}, |
|||
tp: { |
|||
title: 'Technical Provisions (TP)', |
|||
desc: 'The total value of insurance liabilities \u2014 the sum of the Best Estimate Liabilities and the Risk Margin. This represents the full amount the insurer must reserve to meet policyholder obligations.', |
|||
formula: 'TP = BEL + Risk Margin' |
|||
}, |
|||
scr: { |
|||
title: 'Solvency Capital Requirement', |
|||
desc: 'The capital buffer required to absorb significant unforeseen losses over a 1-year period with a 99.5% confidence level (i.e. a 1-in-200 year event). Can be calculated via the Standard Formula or an approved Internal Model.', |
|||
formula: 'SCR = VaR\u2089\u2089.\u2085%(Basic Own Funds) over 1 year' |
|||
}, |
|||
mcr: { |
|||
title: 'Minimum Capital Requirement', |
|||
desc: 'The minimum level of security below which financial resources should not fall. Breach triggers ultimate supervisory intervention \u2014 the insurer\u2019s licence may be withdrawn. Bounded between 25\u201345% of SCR.', |
|||
formula: 'MCR = max(25% \u00D7 SCR, min(45% \u00D7 SCR, linear MCR))' |
|||
}, |
|||
surplus: { |
|||
title: 'Free Surplus', |
|||
desc: 'Own funds in excess of the SCR \u2014 the capital cushion above regulatory requirements. A healthy surplus signals strong financial resilience and may support dividend distributions or growth strategies.', |
|||
formula: 'Surplus = Own Funds \u2212 SCR' |
|||
}, |
|||
ownfunds: { |
|||
title: 'Eligible Own Funds', |
|||
desc: 'Total own funds available to cover the SCR and MCR. Classified into Tier 1 (highest quality \u2014 equity, retained earnings), Tier 2 (subordinated debt), and Tier 3 (limited eligibility). Tiering limits apply when covering SCR/MCR.', |
|||
formula: 'Own Funds = Assets \u2212 Technical Provisions' |
|||
} |
|||
}; |
|||
// Summary table |
|||
const legendItems = [ |
|||
var rows = []; |
|||
{ id: 'assets', label: 'Assets', color: 'var(--assets-fill)' }, |
|||
assetItems.forEach(function (i) { rows.push([i.label, i.value, 'Asset']); }); |
|||
{ id: 'bel', label: 'BEL', color: 'var(--bel-fill)' }, |
|||
rows.push(['<strong>Total Assets</strong>', '<strong>' + totalAssets + '</strong>', '']); |
|||
{ id: 'rm', label: 'Risk Margin', color: 'var(--rm-fill)' }, |
|||
liabilityItems.forEach(function (i) { rows.push([i.label, i.value, 'Liability / Capital']); }); |
|||
{ id: 'scr', label: 'SCR', color: 'var(--scr-fill)' }, |
|||
rows.push(['<strong>Technical Provisions</strong>', '<strong>' + techProv + '</strong>', '']); |
|||
{ id: 'surplus', label: 'Surplus', color: 'var(--surplus-fill)' }, |
|||
rows.push(['<strong>Eligible Own Funds</strong>', '<strong>' + Math.round(eof) + '</strong>', '']); |
|||
{ id: 'tp', label: 'Tech. Prov.', color: '#72777d' }, |
|||
rows.push(['<strong>Solvency Ratio (EOF / SCR)</strong>', '<strong>' + Math.round(solvencyRatio) + '%</strong>', '']); |
|||
{ id: 'ownfunds', label: 'Own Funds', color: '#c8ccd1' }, |
|||
]; |
|||
function |
tbody.innerHTML = rows.map(function (r) { |
||
return '<tr><td>' + r[0] + '</td><td style="text-align:right">' + r[1] + '</td><td>' + r[2] + '</td></tr>'; |
|||
const tp = data.bel + data.rm; |
|||
}).join(''); |
|||
const ownFunds = Math.max(0, data.assets - tp); |
|||
const mcr = Math.round(data.scr * data.mcrPct / 100); |
|||
const surplus = Math.max(0, ownFunds - data.scr); |
|||
const ratio = data.scr > 0 ? (ownFunds / data.scr * 100) : 0; |
|||
return { tp, ownFunds, mcr, surplus, ratio }; |
|||
} |
|||
function render() { |
|||
const { tp, ownFunds, mcr, surplus, ratio } = compute(); |
|||
const total = data.assets; |
|||
const totalLiab = tp + data.scr + Math.max(0, surplus); |
|||
const ref = Math.max(total, totalLiab, 1); |
|||
const totalH = 480; |
|||
const pxPer = totalH / ref; |
|||
// Assets |
|||
const ac = document.getElementById('assetsCol'); |
|||
ac.innerHTML = ''; |
|||
ac.appendChild(makeBlock('assets', 'block-assets', 'Total Assets', 'Market value', '\u20AC' + data.assets + 'bn', totalH)); |
|||
// Liabilities |
|||
const lc = document.getElementById('liabCol'); |
|||
lc.innerHTML = ''; |
|||
const belH = Math.max(32, data.bel * pxPer); |
|||
lc.appendChild(makeBlock('bel', 'block-bel', 'Best Estimate Liabilities', 'Discounted expected cash flows', '\u20AC' + data.bel + 'bn', belH)); |
|||
const rmH = Math.max(26, data.rm * pxPer); |
|||
lc.appendChild(makeBlock('rm', 'block-rm', 'Risk Margin', 'Cost-of-capital provision', '\u20AC' + data.rm + 'bn', rmH)); |
|||
const scrH = Math.max(40, data.scr * pxPer); |
|||
const scrBlock = makeBlock('scr', 'block-scr', 'SCR', 'Solvency Capital Req.', '\u20AC' + data.scr + 'bn', scrH); |
|||
const mcrBadge = document.createElement('div'); |
|||
mcrBadge.className = 'tier-badge'; |
|||
mcrBadge.textContent = 'MCR \u20AC' + mcr + 'bn'; |
|||
mcrBadge.style.cursor = 'pointer'; |
|||
mcrBadge.dataset.block = 'mcr'; |
|||
mcrBadge.addEventListener('click', function(e) { e.stopPropagation(); showDetail('mcr'); }); |
|||
scrBlock.appendChild(mcrBadge); |
|||
lc.appendChild(scrBlock); |
|||
if (surplus > 0) { |
|||
const surpH = Math.max(26, surplus * pxPer); |
|||
lc.appendChild(makeBlock('surplus', 'block-surplus', 'Free Surplus', 'Excess own funds', '\u20AC' + surplus + 'bn', surpH)); |
|||
} else if (ownFunds < data.scr) { |
|||
const defBlock = makeBlock('surplus', 'block-deficit', 'Capital Shortfall', 'Own funds < SCR', '\u20AC' + Math.round(data.scr - ownFunds) + 'bn deficit', 32); |
|||
lc.appendChild(defBlock); |
|||
} |
} |
||
/* ============ INIT ============ */ |
|||
// Ratio |
|||
buildSliders(assetItems, assetSliders); |
|||
var rVal = document.getElementById('ratioValue'); |
|||
buildSliders(liabilityItems, liabSliders); |
|||
var rBar = document.getElementById('ratioBar'); |
|||
buildBars(assetItems, assetBars); |
|||
rVal.textContent = Math.round(ratio) + '%'; |
|||
buildBars(liabilityItems, liabBars); |
|||
rBar.style.width = Math.min(100, ratio / 3) + '%'; |
|||
update(); |
|||
rBar.style.opacity = ratio < 100 ? '0.35' : '1'; |
|||
})(); |
|||
// Status |
|||
var sb = document.getElementById('statusBar'); |
|||
var scrOk = ownFunds >= data.scr; |
|||
var mcrOk = ownFunds >= mcr; |
|||
sb.innerHTML = |
|||
'<div class="status-item"><div class="status-dot ' + (scrOk ? 'ok' : 'fail') + '"></div><span>SCR ' + (scrOk ? 'covered' : 'breached') + '</span></div>' + |
|||
'<div class="status-item"><div class="status-dot ' + (mcrOk ? 'ok' : 'fail') + '"></div><span>MCR ' + (mcrOk ? 'covered' : 'breached') + '</span></div>' + |
|||
'<div class="status-item"><div class="status-dot ' + (ratio >= 150 ? 'ok' : ratio >= 100 ? 'warn' : 'fail') + '"></div><span>Ratio: ' + Math.round(ratio) + '%</span></div>' + |
|||
'<div class="status-item" style="cursor:pointer" onclick="showDetail(\'ownfunds\')"><span style="font-weight:bold">Own Funds: \u20AC' + Math.round(ownFunds) + 'bn</span></div>'; |
|||
// Legend |
|||
var lg = document.getElementById('legend'); |
|||
lg.innerHTML = ''; |
|||
legendItems.forEach(function(l) { |
|||
var el = document.createElement('div'); |
|||
el.className = 'legend-item'; |
|||
el.innerHTML = '<span class="legend-dot" style="background:' + l.color + '"></span>' + l.label; |
|||
el.onclick = function() { showDetail(l.id); }; |
|||
lg.appendChild(el); |
|||
}); |
|||
} |
|||
function makeBlock(id, cls, label, sublabel, value, height) { |
|||
var el = document.createElement('div'); |
|||
el.className = 'bs-block ' + cls; |
|||
el.style.height = height + 'px'; |
|||
el.dataset.block = id; |
|||
el.innerHTML = |
|||
'<div class="label">' + label + '</div>' + |
|||
(height > 38 ? '<div class="sublabel">' + sublabel + '</div>' : '') + |
|||
'<div class="value">' + value + '</div>'; |
|||
el.addEventListener('mouseenter', function() { showDetail(id); }); |
|||
el.addEventListener('click', function() { showDetail(id); }); |
|||
return el; |
|||
} |
|||
function showDetail(id) { |
|||
var info = descriptions[id]; |
|||
if (!info) return; |
|||
document.getElementById('detailTitle').textContent = info.title; |
|||
document.getElementById('detailDesc').textContent = info.desc; |
|||
var fEl = document.getElementById('detailFormula'); |
|||
if (info.formula) { fEl.style.display = 'block'; fEl.textContent = info.formula; } |
|||
else { fEl.style.display = 'none'; } |
|||
document.querySelectorAll('.bs-block').forEach(function(b) { b.classList.remove('active'); }); |
|||
document.querySelectorAll('.bs-block[data-block="' + id + '"]').forEach(function(b) { b.classList.add('active'); }); |
|||
} |
|||
function bindSlider(sid, vid, key, suffix) { |
|||
var s = document.getElementById(sid); |
|||
var v = document.getElementById(vid); |
|||
s.addEventListener('input', function() { |
|||
data[key] = parseInt(s.value); |
|||
v.textContent = suffix ? s.value + suffix : s.value; |
|||
render(); |
|||
}); |
|||
} |
|||
bindSlider('sliderAssets', 'valAssets', 'assets', ''); |
|||
bindSlider('sliderBEL', 'valBEL', 'bel', ''); |
|||
bindSlider('sliderRM', 'valRM', 'rm', ''); |
|||
bindSlider('sliderSCR', 'valSCR', 'scr', ''); |
|||
bindSlider('sliderMCR', 'valMCR', 'mcrPct', '%'); |
|||
render(); |
|||
</script> |
</script> |
||
</body> |
|||
</html> |
|||
Revision as of 10:05, 31 March 2026
Solvency II Balance Sheet
Adjust the sliders to explore how changes in assets and liabilities affect the solvency position.
<svg class="s2-ratio-ring" viewBox="0 0 120 120">
<circle class="s2-ring-bg" cx="60" cy="60" r="52" />
<circle class="s2-ring-fg" cx="60" cy="60" r="52" stroke-dasharray="326.73" stroke-dashoffset="326.73" />
</svg>
0% Solvency Ratio
SCR Coverage: — MCR Coverage: —
Assets
Liabilities & Own Funds
<fieldset class="s2-fieldset">
<legend>Assets</legend>
</fieldset>
<fieldset class="s2-fieldset">
<legend>Liabilities</legend>
</fieldset>
</thead> <tbody id="s2-summary-tbody"></tbody>
| Item | Value | Category |
|---|
This module is for educational purposes only and uses simplified, illustrative figures. Actual Solvency II reporting follows EIOPA technical standards.
<style> /* ===== STRICT SCOPING — every rule prefixed ===== */
- insurerbrain-module-solvency2 {
--s2-accent: #1a6b4e; --s2-accent-light: #e3f2ec; --s2-warn: #c0760a; --s2-warn-light: #fdf3e1; --s2-danger: #b52a2a; --s2-danger-light: #fce8e8; --s2-border: #c8ccd1; --s2-text: #202122; --s2-text-muted: #54595d; --s2-surface: #f8f9fa; --s2-white: #ffffff; --s2-radius: 0.35rem; max-width: 64rem; margin: 1.5rem auto; padding: 0 0.75rem;
}
/* ---- Header ---- */
- insurerbrain-module-solvency2 .s2-header {
margin-bottom: 1.25rem;
}
- insurerbrain-module-solvency2 .s2-title {
margin: 0 0 0.25rem; border-bottom: none;
}
- insurerbrain-module-solvency2 .s2-subtitle {
margin: 0; color: var(--s2-text-muted);
}
/* ---- Dashboard ---- */
- insurerbrain-module-solvency2 .s2-dashboard {
display: flex; gap: 1.25rem; flex-wrap: wrap; margin-bottom: 1.5rem;
}
/* -- Ratio Panel -- */
- insurerbrain-module-solvency2 .s2-ratio-panel {
flex: 0 0 auto; min-width: 11rem; display: flex; flex-direction: column; align-items: center; gap: 0.6rem; padding: 1rem; border: 1px solid var(--s2-border); border-radius: var(--s2-radius); background: var(--s2-surface);
}
- insurerbrain-module-solvency2 .s2-ratio-ring-wrap {
position: relative; width: 7.5rem; height: 7.5rem;
}
- insurerbrain-module-solvency2 .s2-ratio-ring {
width: 100%; height: 100%; transform: rotate(-90deg);
}
- insurerbrain-module-solvency2 .s2-ring-bg {
fill: none; stroke: var(--s2-border); stroke-width: 8;
}
- insurerbrain-module-solvency2 .s2-ring-fg {
fill: none; stroke: var(--s2-accent); stroke-width: 8; stroke-linecap: round; transition: stroke-dashoffset 0.5s ease, stroke 0.4s ease;
}
- insurerbrain-module-solvency2 .s2-ratio-label {
position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
}
- insurerbrain-module-solvency2 .s2-ratio-value {
font-size: 1.5rem; font-weight: 700; line-height: 1.1; color: var(--s2-text);
}
- insurerbrain-module-solvency2 .s2-ratio-caption {
font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--s2-text-muted);
}
- insurerbrain-module-solvency2 .s2-ratio-badges {
display: flex; flex-wrap: wrap; gap: 0.35rem; justify-content: center;
}
- insurerbrain-module-solvency2 .s2-badge {
font-size: 0.72rem; padding: 0.15rem 0.45rem; border-radius: 999px; background: var(--s2-accent-light); color: var(--s2-accent); white-space: nowrap;
}
- insurerbrain-module-solvency2 .s2-status-bar {
font-size: 0.78rem; font-weight: 600; padding: 0.3rem 0.7rem; border-radius: var(--s2-radius); text-align: center; transition: background 0.3s, color 0.3s;
}
/* -- Balance Sheet Columns -- */
- insurerbrain-module-solvency2 .s2-sheet-wrap {
flex: 1 1 20rem; display: flex; gap: 0.5rem; min-height: 18rem;
}
- insurerbrain-module-solvency2 .s2-col {
flex: 1; display: flex; flex-direction: column; border: 1px solid var(--s2-border); border-radius: var(--s2-radius); overflow: hidden; background: var(--s2-white);
}
- insurerbrain-module-solvency2 .s2-col-head {
margin: 0; padding: 0.45rem 0.6rem; font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.04em; border-bottom: 1px solid var(--s2-border); background: var(--s2-surface);
}
- insurerbrain-module-solvency2 .s2-bar-stack {
flex: 1; display: flex; flex-direction: column; padding: 0.25rem; gap: 2px;
}
- insurerbrain-module-solvency2 .s2-bar-item {
display: flex; align-items: center; justify-content: space-between; padding: 0.25rem 0.45rem; border-radius: 3px; font-size: 0.75rem; color: var(--s2-white); transition: flex-grow 0.45s ease; overflow: hidden; min-height: 1.4rem;
}
- insurerbrain-module-solvency2 .s2-bar-item span:last-child {
font-weight: 600; margin-left: 0.3rem; white-space: nowrap;
}
- insurerbrain-module-solvency2 .s2-col-total {
padding: 0.35rem 0.6rem; font-size: 0.82rem; border-top: 1px solid var(--s2-border); background: var(--s2-surface); text-align: right;
}
/* ---- Controls ---- */
- insurerbrain-module-solvency2 .s2-controls {
display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
- insurerbrain-module-solvency2 .s2-fieldset {
flex: 1 1 18rem; border: 1px solid var(--s2-border); border-radius: var(--s2-radius); padding: 0.6rem 0.8rem 0.8rem; margin: 0; background: var(--s2-surface);
}
- insurerbrain-module-solvency2 .s2-fieldset legend {
font-weight: 600; font-size: 0.85rem; padding: 0 0.3rem;
}
- insurerbrain-module-solvency2 .s2-slider-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); gap: 0.55rem;
}
- insurerbrain-module-solvency2 .s2-slider-group {
display: flex; flex-direction: column; gap: 0.15rem;
}
- insurerbrain-module-solvency2 .s2-slider-label {
display: flex; justify-content: space-between; font-size: 0.78rem; color: var(--s2-text-muted);
}
- insurerbrain-module-solvency2 .s2-slider-label strong {
color: var(--s2-text);
}
- insurerbrain-module-solvency2 .s2-slider-group input[type="range"] {
width: 100%; cursor: pointer; accent-color: var(--s2-accent);
}
/* ---- Summary Table ---- */
- insurerbrain-module-solvency2 .s2-summary-table {
width: 100%; margin-top: 0;
}
/* ---- Footnote ---- */
- insurerbrain-module-solvency2 .s2-footnote {
font-size: 0.78rem; color: var(--s2-text-muted); margin-top: 0.5rem;
}
/* ---- Status colours ---- */
- insurerbrain-module-solvency2 .s2-status-good {
background: var(--s2-accent-light); color: var(--s2-accent);
}
- insurerbrain-module-solvency2 .s2-status-warn {
background: var(--s2-warn-light); color: var(--s2-warn);
}
- insurerbrain-module-solvency2 .s2-status-danger {
background: var(--s2-danger-light); color: var(--s2-danger);
}
/* ---- Responsive ---- */ @media (max-width: 48rem) {
#insurerbrain-module-solvency2 .s2-dashboard {
flex-direction: column;
}
#insurerbrain-module-solvency2 .s2-ratio-panel {
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
#insurerbrain-module-solvency2 .s2-sheet-wrap {
min-height: 14rem;
}
} </style>
<script> (function () {
'use strict';
/* ============ DATA MODEL ============ */
var assetItems = [
{ key: 'gov_bonds', label: 'Government Bonds', value: 350, min: 0, max: 800, color: '#1a6b4e' },
{ key: 'corp_bonds', label: 'Corporate Bonds', value: 220, min: 0, max: 600, color: '#2a8c68' },
{ key: 'equities', label: 'Equities', value: 120, min: 0, max: 400, color: '#3bae85' },
{ key: 'property', label: 'Property', value: 80, min: 0, max: 300, color: '#5bc4a0' },
{ key: 'reins_recv', label: 'Reinsurance Recoverables',value: 60, min: 0, max: 200, color: '#85d4b8' },
{ key: 'cash', label: 'Cash & Equivalents', value: 40, min: 0, max: 200, color: '#abd9c9' }
];
var liabilityItems = [
{ key: 'best_est', label: 'Best Estimate Liabilities', value: 480, min: 100, max: 900, color: '#8b3a3a' },
{ key: 'risk_margin', label: 'Risk Margin', value: 45, min: 5, max: 150, color: '#b35656' },
{ key: 'other_liab', label: 'Other Liabilities', value: 55, min: 0, max: 200, color: '#cc8080' },
{ key: 'scr', label: 'SCR (Own Funds Buffer)', value: 180, min: 20, max: 400, color: '#1b5e90' },
{ key: 'mcr', label: 'MCR (Minimum Capital)', value: 70, min: 10, max: 200, color: '#3a8bc2' }
];
/* ============ REFERENCES ============ */
var root = document.getElementById('insurerbrain-module-solvency2');
var assetSliders = root.querySelector('#s2-asset-sliders');
var liabSliders = root.querySelector('#s2-liability-sliders');
var assetBars = root.querySelector('#s2-asset-bars');
var liabBars = root.querySelector('#s2-liability-bars');
var assetTotalEl = root.querySelector('#s2-asset-total');
var liabTotalEl = root.querySelector('#s2-liability-total');
var ratioValueEl = root.querySelector('#s2-ratio-value');
var ringFg = root.querySelector('.s2-ring-fg');
var statusBar = root.querySelector('#s2-status-bar');
var badgeSCR = root.querySelector('#s2-badge-scr');
var badgeMCR = root.querySelector('#s2-badge-mcr');
var tbody = root.querySelector('#s2-summary-tbody');
var CIRCUMFERENCE = 2 * Math.PI * 52; // matches r=52
/* ============ BUILD SLIDERS ============ */
function buildSliders(items, container) {
items.forEach(function (item) {
var group = document.createElement('div');
group.className = 's2-slider-group';
var lbl = document.createElement('div');
lbl.className = 's2-slider-label';
lbl.innerHTML = '' + item.label + '' + item.value + '';
var inp = document.createElement('input');
inp.type = 'range';
inp.min = item.min;
inp.max = item.max;
inp.value = item.value;
inp.id = 's2-range-' + item.key;
inp.setAttribute('aria-label', item.label + ' (€m)');
inp.addEventListener('input', function () {
item.value = Number(this.value);
root.querySelector('#s2-val-' + item.key).textContent = item.value;
update();
});
group.appendChild(lbl);
group.appendChild(inp);
container.appendChild(group);
});
}
/* ============ BUILD BAR ITEMS (once) ============ */
function buildBars(items, container) {
items.forEach(function (item) {
var bar = document.createElement('div');
bar.className = 's2-bar-item';
bar.id = 's2-bar-' + item.key;
bar.style.background = item.color;
bar.innerHTML = '' + item.label + '';
container.appendChild(bar);
});
}
/* ============ UPDATE ============ */
function update() {
var totalAssets = assetItems.reduce(function (s, i) { return s + i.value; }, 0);
var techProv = liabilityItems[0].value + liabilityItems[1].value; // BE + RM
var otherLiab = liabilityItems[2].value;
var scrReq = liabilityItems[3].value;
var mcrReq = liabilityItems[4].value;
var totalLiabSide = techProv + otherLiab + scrReq + mcrReq;
// Eligible Own Funds = Assets - Technical Provisions - Other Liabilities var eof = totalAssets - techProv - otherLiab; if (eof < 0) eof = 0;
var solvencyRatio = scrReq > 0 ? (eof / scrReq) * 100 : 0; var mcrRatio = mcrReq > 0 ? (eof / mcrReq) * 100 : 0;
// Ratio ring var capped = Math.min(solvencyRatio, 250); var offset = CIRCUMFERENCE - (CIRCUMFERENCE * capped / 250); ringFg.style.strokeDashoffset = offset;
var ringColor; if (solvencyRatio >= 150) ringColor = '#1a6b4e'; else if (solvencyRatio >= 100) ringColor = '#c0760a'; else ringColor = '#b52a2a'; ringFg.style.stroke = ringColor;
ratioValueEl.textContent = Math.round(solvencyRatio) + '%';
badgeSCR.textContent = 'SCR Coverage: ' + Math.round(solvencyRatio) + '%'; badgeMCR.textContent = 'MCR Coverage: ' + Math.round(mcrRatio) + '%';
// Status
statusBar.classList.remove('s2-status-good', 's2-status-warn', 's2-status-danger');
if (solvencyRatio >= 150) {
statusBar.textContent = 'Comfortable — well above SCR';
statusBar.classList.add('s2-status-good');
} else if (solvencyRatio >= 100) {
statusBar.textContent = 'Caution — approaching SCR threshold';
statusBar.classList.add('s2-status-warn');
} else {
statusBar.textContent = 'Breach — own funds below SCR';
statusBar.classList.add('s2-status-danger');
}
// Asset bars
assetItems.forEach(function (item) {
var bar = root.querySelector('#s2-bar-' + item.key);
bar.style.flexGrow = item.value || 0.01;
bar.querySelector('span:last-child').textContent = item.value + ' €m';
});
assetTotalEl.textContent = totalAssets;
// Liability bars
liabilityItems.forEach(function (item) {
var bar = root.querySelector('#s2-bar-' + item.key);
bar.style.flexGrow = item.value || 0.01;
bar.querySelector('span:last-child').textContent = item.value + ' €m';
});
liabTotalEl.textContent = totalLiabSide;
// Summary table
var rows = [];
assetItems.forEach(function (i) { rows.push([i.label, i.value, 'Asset']); });
rows.push(['Total Assets', '' + totalAssets + '', ]);
liabilityItems.forEach(function (i) { rows.push([i.label, i.value, 'Liability / Capital']); });
rows.push(['Technical Provisions', '' + techProv + '', ]);
rows.push(['Eligible Own Funds', '' + Math.round(eof) + '', ]);
rows.push(['Solvency Ratio (EOF / SCR)', '' + Math.round(solvencyRatio) + '%', ]);
tbody.innerHTML = rows.map(function (r) {
return '' + r[0] + '' + r[1] + '' + r[2] + '';
}).join(); }
/* ============ INIT ============ */ buildSliders(assetItems, assetSliders); buildSliders(liabilityItems, liabSliders); buildBars(assetItems, assetBars); buildBars(liabilityItems, liabBars); update();
})(); </script>