MediaWiki:Gadget-wix-interactive.js: Difference between revisions

Content deleted Content added
No edit summary
No edit summary
Line 15:
- "building-blocks" Stacked-bar initial recognition building blocks
- "impact-sort" Binary classification: CSM vs P&L scenario sorter
- "income-builder" Sort 9 items into IFRS 17 income statement sections
================================================================ */
 
Line 36 ⟶ 37:
'csm-rollforward': initCsmRollforward,
'building-blocks': initBuildingBlocks,
'impact-sort': initImpactSort,
'income-builder': initIncomeBuilder
};
 
Line 3,479 ⟶ 3,481:
 
renderCard();
}
 
 
/* ================================================================
INCOME BUILDER — data-wix-module="income-builder"
Sort 9 line items into the correct section of the IFRS 17
income statement: Revenue, Service Expenses, or Finance.
Items accumulate as chips in the statement skeleton.
================================================================ */
 
function initIncomeBuilder( container ) {
 
/* ── Item data ───────────────────────────────────────────── */
 
var ITEMS = [
{ id: 'csm-release', icon: '\uD83C\uDF81', label: 'CSM release',
desc: 'Share of unearned profit recognised as coverage is provided',
zone: 'revenue',
okHead: '\u2705 Correct \u2014 Insurance Revenue.',
okBody: 'The CSM release represents profit earned by delivering coverage during the period. It is one of the three components that build insurance revenue \u2014 alongside expected claims costs and the RA release.',
noHead: '\u2717 This belongs in Insurance Revenue.',
noBody: 'The CSM release is the share of unearned profit recognised as the insurer delivers coverage. Revenue under IFRS 17 is built from three releases: expected claims costs, the RA release, and the CSM release. All three go to revenue.' },
{ id: 'ra-release', icon: '\uD83D\uDEE1\uFE0F', label: 'Risk adjustment release',
desc: 'Reduction in RA as risk is borne over time',
zone: 'revenue',
okHead: '\u2705 Correct \u2014 Insurance Revenue.',
okBody: 'Bearing risk is part of the service the insurer provides. As each period passes and uncertainty diminishes, the released portion of the RA flows into insurance revenue \u2014 it represents compensation for risk the insurer has now borne.',
noHead: '\u2717 This belongs in Insurance Revenue.',
noBody: 'The RA release reflects risk the insurer has already borne \u2014 that\u2019s a service delivered. Under IFRS 17, it is one of the three components of insurance revenue, not an expense or a finance item.' },
{ id: 'claims-alloc', icon: '\uD83D\uDCCB', label: 'Expected claims & expenses allocated to period',
desc: 'Portion of estimated claims/expenses matching coverage provided (e.g.\u00A0the Lyon home contract at 6\u00A0months)',
zone: 'revenue',
okHead: '\u2705 Correct \u2014 Insurance Revenue.',
okBody: 'This is often the most surprising one. Under IFRS 17, the portion of expected claims cost allocated to the period is a component of revenue, not an expense. Think of the Lyon home contract: at six months, half the expected cost is released into revenue. The logic is that revenue measures the <em>value of service delivered</em>, and the expected claims cost is part of that value. Actual claims incurred go to expenses.',
noHead: '\u2717 This actually belongs in Insurance Revenue.',
noBody: 'Under IFRS 17, revenue is not premiums \u2014 it\u2019s the value of coverage delivered. That value includes the expected claims cost allocated to the period (like the Lyon home contract at 6\u00A0months), plus the RA release and CSM release. The <em>expected</em> cost is a revenue component; <em>actual</em> incurred claims go to expenses.' },
{ id: 'claims-incurred', icon: '\u26C8\uFE0F', label: 'Claims incurred',
desc: 'Actual claim costs from events that occurred (e.g.\u00A0hailstorm damaging 200 cars in Bavaria)',
zone: 'expenses',
okHead: '\u2705 Correct \u2014 Insurance Service Expenses.',
okBody: 'When the Bavaria hailstorm damages 200 cars, the cost of those actual claims is an insurance service expense. This is the cost of events that have happened \u2014 current or past service \u2014 hitting the income statement directly.',
noHead: '\u2717 This belongs in Insurance Service Expenses.',
noBody: 'Claims incurred, like the Bavaria hailstorm damaging 200 cars, are the actual costs of insured events. These go to insurance service expenses \u2014 they\u2019re the cost side of underwriting, paired against revenue to produce the insurance service result.' },
{ id: 'est-change-past', icon: '\uD83D\uDD27', label: 'Changes in estimates for current/past service',
desc: 'Re-estimates of claims already incurred or coverage already provided',
zone: 'expenses',
okHead: '\u2705 Correct \u2014 Insurance Service Expenses.',
okBody: 'When estimates change for service already delivered \u2014 like revised repair costs on claims already incurred \u2014 the adjustment bypasses the CSM and goes straight to insurance service expenses. Only changes about <em>future</em> service adjust the CSM.',
noHead: '\u2717 This belongs in Insurance Service Expenses.',
noBody: 'Changes in estimates for current or past service relate to coverage already provided. They cannot go through the CSM (which is about future profit) and they aren\u2019t a finance effect. They go directly to insurance service expenses in the income statement.' },
{ id: 'onerous-loss', icon: '\uD83D\uDEA8', label: 'Losses on onerous groups',
desc: 'Initial losses at recognition and subsequent deterioration of onerous groups',
zone: 'expenses',
okHead: '\u2705 Correct \u2014 Insurance Service Expenses.',
okBody: 'Losses on onerous contracts \u2014 whether recognised at day one or from subsequent deterioration \u2014 are part of insurance service expenses. They reflect the cost of underwriting contracts that are expected to be unprofitable, which is fundamentally a service-related cost.',
noHead: '\u2717 This belongs in Insurance Service Expenses.',
noBody: 'Onerous group losses are an underwriting cost, not a finance effect. Whether it\u2019s a day-one loss or subsequent deterioration, these sit within insurance service expenses \u2014 they tell you about pricing and claims management, not market conditions.' },
{ id: 'acq-costs', icon: '\uD83E\uDD1D', label: 'Acquisition costs amortised',
desc: 'Commissions to brokers (e.g.\u00A0in Belgium) and distribution costs (e.g.\u00A0in Spain), spread over the period',
zone: 'expenses',
okHead: '\u2705 Correct \u2014 Insurance Service Expenses.',
okBody: 'Under IFRS 17, acquisition costs \u2014 like commissions paid to Belgian brokers or Spanish distribution costs \u2014 are amortised and included within insurance service expenses. They\u2019re not shown as a separate deduction; this keeps the insurance service result self-contained.',
noHead: '\u2717 This belongs in Insurance Service Expenses.',
noBody: 'Acquisition costs (Belgian broker commissions, Spanish distribution costs) are amortised within insurance service expenses under IFRS 17 \u2014 not shown separately. The insurance service result captures the <em>full</em> cost of acquiring, servicing, and settling contracts.' },
{ id: 'discount-unwind', icon: '\u23F3', label: 'Unwinding of the discount',
desc: 'Liability grows as future payments get closer in time (accretion of interest)',
zone: 'finance',
okHead: '\u2705 Correct \u2014 Insurance Finance Income / Expense.',
okBody: 'The discount unwind is a financing effect, not an underwriting cost. As future claim payments draw closer, the present value of the liability rises \u2014 this accretion of interest belongs in insurance finance income or expense, keeping the service result clean.',
noHead: '\u2717 This belongs in Insurance Finance Income / Expense.',
noBody: 'A common mistake! The discount unwind feels like a cost of doing business, but IFRS 17 treats it as a financing effect. It goes to insurance finance income or expense \u2014 <em>not</em> to the service result. This separation is deliberate: it keeps underwriting profitability free from time-value-of-money effects.' },
{ id: 'rate-change', icon: '\uD83D\uDCC9', label: 'Effect of discount rate changes',
desc: 'Gains or losses when market interest rates move between reporting dates (e.g.\u00A0rates drop for AXA Germany long-tail liability contracts)',
zone: 'finance',
okHead: '\u2705 Correct \u2014 Insurance Finance Income / Expense.',
okBody: 'When interest rates drop \u2014 as in the AXA Germany long-tail liability example \u2014 the present value of future claims increases, creating a financial expense. This has nothing to do with underwriting quality, so it sits in insurance finance income or expense. The insurer can also choose the OCI option to keep rate volatility out of reported profit entirely.',
noHead: '\u2717 This belongs in Insurance Finance Income / Expense.',
noBody: 'Discount rate changes (like the AXA Germany long-tail scenario where rates drop sharply) are a market-driven effect, not underwriting performance. IFRS 17 places them in insurance finance income or expense to keep the service result undistorted. The insurer may also elect the OCI option to smooth this further.' }
];
 
/* ── State ───────────────────────────────────────────────── */
 
var order = shuffle( makeRange( ITEMS.length ) );
var cur = 0, ok = 0, no = 0, answered = false;
var placed = { revenue: [], expenses: [], finance: [] };
 
function makeRange( n ) { var a = []; for ( var i = 0; i < n; i++ ) a.push( i ); return a; }
 
function shuffle( arr ) {
for ( var i = arr.length - 1; i > 0; i-- ) {
var j = Math.floor( Math.random() * ( i + 1 ) );
var t = arr[i]; arr[i] = arr[j]; arr[j] = t;
}
return arr;
}
 
/* ── Build UI ────────────────────────────────────────────── */
 
wix.empty( container );
 
var wrapper = wix.el( 'div', { className: 'wix-eng-wrapper' } );
container.appendChild( wrapper );
 
/* Title */
wrapper.appendChild( wix.el( 'div', { className: 'wix-sim-title', textContent: 'Build the Income Statement' } ) );
 
/* Progress */
var progFill = wix.el( 'div', { className: 'wix-progress-fill', style: { width: '0%' } } );
var progText = wix.el( 'span', { style: { fontSize: '0.78em', fontWeight: '600', color: 'var(--wix-text-muted)', whiteSpace: 'nowrap' } } );
wrapper.appendChild( wix.el( 'div', { className: 'wix-progress-wrap', style: { marginBottom: '1rem' } }, [
wix.el( 'div', { className: 'wix-progress-track' }, [ progFill ] ),
progText
] ) );
 
/* Score badges */
var badgeOk = wix.el( 'span', { className: 'wix-is-badge wix-is-badge--correct' } );
var badgeNo = wix.el( 'span', { className: 'wix-is-badge wix-is-badge--wrong' } );
wrapper.appendChild( wix.el( 'div', { className: 'wix-is-score-row' }, [ badgeOk, badgeNo ] ) );
 
/* Tile stage */
var tileStage = wix.el( 'div', { style: { minHeight: '60px', marginBottom: '1rem' } } );
wrapper.appendChild( tileStage );
 
/* Choice buttons */
var btnRev = wix.el( 'button', { className: 'wix-ib-choice wix-ib-choice--rev', innerHTML: 'Insurance<br>Revenue' } );
var btnExp = wix.el( 'button', { className: 'wix-ib-choice wix-ib-choice--exp', innerHTML: 'Service<br>Expenses' } );
var btnFin = wix.el( 'button', { className: 'wix-ib-choice wix-ib-choice--fin', innerHTML: 'Finance<br>Inc/Exp' } );
var choicesEl = wix.el( 'div', { className: 'wix-ib-choices' }, [ btnRev, btnExp, btnFin ] );
wrapper.appendChild( choicesEl );
 
/* Income statement skeleton */
function makeSection( dotColor, titleColor, titleText, zoneKey ) {
var countEl = wix.el( 'span', { className: 'wix-ib-sec-count', textContent: '0 items' } );
var dropEl = wix.el( 'div', { className: 'wix-ib-drop' }, [
wix.el( 'span', { className: 'wix-ib-drop-ph', textContent: 'Items will appear here' } )
] );
var section = wix.el( 'div', { className: 'wix-ib-section' }, [
wix.el( 'div', { className: 'wix-ib-sec-head' }, [
wix.el( 'span', { className: 'wix-ib-sec-dot', style: { background: dotColor } } ),
wix.el( 'span', { className: 'wix-ib-sec-title', style: { color: titleColor }, textContent: titleText } ),
countEl
] ),
dropEl
] );
return { section: section, dropEl: dropEl, countEl: countEl };
}
 
var revSec = makeSection( 'var(--ib-rev)', 'var(--ib-rev)', 'Insurance Revenue', 'revenue' );
var expSec = makeSection( 'var(--ib-exp)', 'var(--ib-exp)', 'Insurance Service Expenses', 'expenses' );
var finSec = makeSection( 'var(--ib-fin)', 'var(--ib-fin)', 'Insurance Finance Income / Expense', 'finance' );
 
var resultRow = wix.el( 'div', { className: 'wix-ib-result' }, [
wix.el( 'span', { className: 'wix-ib-result-label', textContent: '= Insurance Service Result' } ),
wix.el( 'span', { className: 'wix-ib-result-eq', textContent: 'Revenue \u2212 Expenses' } )
] );
 
wrapper.appendChild( wix.el( 'div', { className: 'wix-ib-stmt' }, [
revSec.section, expSec.section, resultRow, finSec.section
] ) );
 
/* Feedback */
var fbHead = wix.el( 'div', { className: 'wix-is-fb-head' } );
var fbBody = wix.el( 'div' );
var btnNext = wix.el( 'button', { className: 'wix-btn', textContent: 'Next item \u25B8', style: { marginTop: '0.5rem' } } );
var feedbackEl = wix.el( 'div', { className: 'wix-is-feedback wix-hidden' }, [ fbHead, fbBody, btnNext ] );
wrapper.appendChild( feedbackEl );
 
/* Final screen */
var finalIcon = wix.el( 'div', { className: 'wix-is-final-icon' } );
var finalTitle = wix.el( 'div', { className: 'wix-is-final-title' } );
var finalBody = wix.el( 'div', { className: 'wix-is-final-body' } );
var btnRestart = wix.el( 'button', { className: 'wix-btn wix-btn--outline', textContent: '\u21BA Try again' } );
var finalEl = wix.el( 'div', { className: 'wix-is-final wix-hidden' }, [ finalIcon, finalTitle, finalBody, btnRestart ] );
wrapper.appendChild( finalEl );
 
/* ── Helpers ─────────────────────────────────────────────── */
 
var ZONE_MAP = {
revenue: { sec: revSec, chipCls: 'wix-ib-chip--rev' },
expenses: { sec: expSec, chipCls: 'wix-ib-chip--exp' },
finance: { sec: finSec, chipCls: 'wix-ib-chip--fin' }
};
 
function updateScores() {
badgeOk.textContent = '\u2713 ' + ok;
badgeNo.textContent = '\u2717 ' + no;
}
 
function updateCounts() {
revSec.countEl.textContent = placed.revenue.length + ' item' + ( placed.revenue.length !== 1 ? 's' : '' );
expSec.countEl.textContent = placed.expenses.length + ' item' + ( placed.expenses.length !== 1 ? 's' : '' );
finSec.countEl.textContent = placed.finance.length + ' item' + ( placed.finance.length !== 1 ? 's' : '' );
}
 
function toggleBtns( on ) {
btnRev.disabled = !on;
btnExp.disabled = !on;
btnFin.disabled = !on;
}
 
/* ── Render ──────────────────────────────────────────────── */
 
function renderTile() {
feedbackEl.className = 'wix-is-feedback wix-hidden';
answered = false;
 
if ( cur >= order.length ) { showFinal(); return; }
 
var it = ITEMS[order[cur]];
wix.empty( tileStage );
tileStage.appendChild( wix.el( 'div', { className: 'wix-ib-tile' }, [
wix.el( 'span', { textContent: it.icon } ),
wix.el( 'span', { textContent: it.label } )
] ) );
tileStage.appendChild( wix.el( 'div', { className: 'wix-ib-tile-desc', textContent: it.desc } ) );
 
progFill.style.width = ( cur / order.length * 100 ) + '%';
progText.textContent = cur + ' / ' + order.length;
 
toggleBtns( true );
choicesEl.className = 'wix-ib-choices';
updateScores();
}
 
function handleAnswer( chosen ) {
if ( answered ) return;
answered = true;
toggleBtns( false );
 
var it = ITEMS[order[cur]];
var isCorrect = chosen === it.zone;
 
if ( isCorrect ) ok++; else no++;
updateScores();
 
/* Place chip in correct zone (always into the right section) */
var target = ZONE_MAP[it.zone];
var ph = target.sec.dropEl.querySelector( '.wix-ib-drop-ph' );
if ( ph ) ph.parentNode.removeChild( ph );
 
var chip = wix.el( 'span', { className: 'wix-ib-chip ' + target.chipCls, textContent: it.icon + ' ' + it.label } );
target.sec.dropEl.appendChild( chip );
placed[it.zone].push( it.id );
updateCounts();
 
/* Hide tile */
wix.empty( tileStage );
 
/* Feedback */
if ( isCorrect ) {
feedbackEl.className = 'wix-is-feedback wix-is-feedback--correct';
fbHead.textContent = it.okHead;
fbBody.innerHTML = it.okBody;
} else {
feedbackEl.className = 'wix-is-feedback wix-is-feedback--wrong';
fbHead.textContent = it.noHead;
fbBody.innerHTML = it.noBody;
}
 
btnNext.textContent = cur >= order.length - 1 ? 'See results \u25B8' : 'Next item \u25B8';
}
 
function showFinal() {
wix.empty( tileStage );
choicesEl.className = 'wix-ib-choices wix-hidden';
feedbackEl.className = 'wix-is-feedback wix-hidden';
progFill.style.width = '100%';
progText.textContent = order.length + ' / ' + order.length;
 
var pct = Math.round( ok / order.length * 100 );
finalIcon.textContent = pct === 100 ? '\uD83C\uDFC6' : pct >= 67 ? '\uD83D\uDC4F' : '\uD83D\uDCDA';
finalTitle.textContent = pct === 100 ? 'Perfect \u2014 you built it!' : pct >= 67 ? 'Almost there!' : 'Keep practising!';
finalBody.innerHTML = 'You placed <strong>' + ok + ' of ' + order.length + '</strong> items correctly (' + pct + '%).<br><br>' +
'The completed income statement above is now your reference. Notice how the <strong>Insurance Service Result</strong> sits between revenue and expenses \u2014 purely underwriting \u2014 while finance effects live below, keeping the two stories separate.';
 
finalEl.className = 'wix-is-final';
}
 
function restart() {
order = shuffle( makeRange( ITEMS.length ) );
cur = 0; ok = 0; no = 0; answered = false;
placed.revenue = []; placed.expenses = []; placed.finance = [];
 
/* Clear chips, restore placeholders */
[ revSec, expSec, finSec ].forEach( function ( s ) {
wix.empty( s.dropEl );
s.dropEl.appendChild( wix.el( 'span', { className: 'wix-ib-drop-ph', textContent: 'Items will appear here' } ) );
} );
updateCounts();
 
finalEl.className = 'wix-is-final wix-hidden';
choicesEl.className = 'wix-ib-choices';
renderTile();
}
 
/* ── Events ──────────────────────────────────────────────── */
 
btnRev.addEventListener( 'click', function () { handleAnswer( 'revenue' ); } );
btnExp.addEventListener( 'click', function () { handleAnswer( 'expenses' ); } );
btnFin.addEventListener( 'click', function () { handleAnswer( 'finance' ); } );
btnNext.addEventListener( 'click', function () { cur++; renderTile(); } );
btnRestart.addEventListener( 'click', restart );
 
renderTile();
}