Test Post

Singapore Integrated Shield Plans: An Interactive Guide

body {
font-family: ‘Inter’, sans-serif;
background-color: #f0f4f8;
}
.bg-brand-dark-blue { background-color: #003f5c; }
.bg-brand-purple { background-color: #955196; }
.bg-brand-pink { background-color: #dd5182; }
.bg-brand-orange { background-color: #ff6e54; }
.bg-brand-yellow { background-color: #ffa600; }
.text-brand-dark-blue { color: #003f5c; }
.text-brand-purple { color: #955196; }
.text-brand-pink { color: #dd5182; }
.text-brand-orange { color: #ff6e54; }
.text-brand-yellow { color: #ffa600; }
.border-brand-purple { border-color: #955196; }
.chart-container {
position: relative;
width: 100%;
max-width: 800px;
margin-left: auto;
margin-right: auto;
height: 400px;
max-height: 50vh;
}
.lifetime-chart-container {
position: relative;
width: 100%;
height: 450px;
}
.value-chart-container {
position: relative;
width: 100%;
height: 400px;
margin-bottom: 2rem;
}
.card {
background-color: white;
border-radius: 0.75rem;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
padding: 1.5rem;
margin-bottom: 2rem;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.1);
}
.tab-button {
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s, color 0.3s;
border: 2px solid transparent;
}
.tab-button.active {
background-color: #003f5c;
color: white;
border-color: #003f5c;
}
.tab-button:not(.active) {
background-color: #e2e8f0;
color: #4a5568;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.flowchart-box {
border: 2px solid;
padding: 1rem;
border-radius: 0.5rem;
text-align: center;
font-weight: 600;
}
.flowchart-arrow {
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
color: #4a5568;
margin: 0.5rem 0;
}
.rider-card {
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
padding: 1rem;
background-color: #f8fafc;
}
/* Tooltip styles */
.tooltip {
position: relative;
display: inline-block;
cursor: pointer;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 300px;
background-color: #555;
color: #fff;
text-align: left;
border-radius: 6px;
padding: 10px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -150px;
opacity: 0;
transition: opacity 0.3s;
font-size: 0.875rem;
font-weight: normal;
line-height: 1.5;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
.bill-bar-container {
width: 100%;
background-color: #e5e7eb;
border-radius: 0.5rem;
overflow: hidden;
display: flex;
height: 40px;
margin-bottom: 1rem;
font-weight: 600;
color: white;
}
.bill-bar-segment {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 0 0.5rem;
font-size: 0.75rem;
}
@media (min-width: 640px) {
.bill-bar-segment {
font-size: 0.875rem;
padding: 0 1rem;
}
}

Navigating Singapore’s Health Insurance Maze

An Interactive Deep-Dive into Integrated Shield Plans, Riders, and Cancer Coverage

The 3 Layers of Your Health Coverage

Singapore’s healthcare system is built on three interconnected layers. The limitations within the national schemes are intentional, creating a “coverage gap” for those who prefer higher standards of care. This gap is what Integrated Shield Plans and their Riders are designed to fill.

Your Total Hospital Bill is Covered By…

Layer 1: MediShield Life (MediSave)

Universal basic coverage pegged to B2/C ward bills in public hospitals.

Layer 2: Integrated Shield Plan (MediSave & Cash)

Enhances coverage for A/B1 wards or private hospitals.

Layer 3: Riders (Cash)

Covers the deductible and co-insurance from the main plan.

💰
MediSave

Compulsory Savings

+
💵
Cash

Out-of-Pocket

These are the funding sources for your premiums and out-of-pocket costs.

Why Basic Coverage Isn’t Always Enough: MediShield Life Explained

MediShield Life is your basic health insurance, covering costs for B2/C wards in public hospitals. It has claim limits and requires you to pay a portion (deductible & co-insurance).

Hypothetical Scenario: A S$100,000 Private Hospital Bill

Let’s see what happens if you rely solely on MediShield Life for a major surgery at a private hospital.

MSHL Pays: S$16,000
You Pay: S$84,000

Because MediShield Life’s payouts are based on public hospital rates (pro-ration factor of 16% for private hospitals), a large portion of a private hospital bill remains uncovered. This significant out-of-pocket expense is the “coverage gap” that Integrated Shield Plans address.

Bridging the Gap: The Role of Integrated Shield Plans

Integrated Shield Plans (ISPs) boost your coverage for higher-class wards or private hospitals. They cover a much larger part of the bill, but you still pay the deductible and co-insurance.

Same Scenario with an Integrated Shield Plan:

Now, let’s add a Private Hospital ISP to the S$100,000 bill.

MSHL Pays: S$16,000
ISP Pays: S$74,000
You Pay: S$10,000

The ISP covers the majority of the remaining bill. However, you are still responsible for the plan’s Deductible (the initial amount you pay) and Co-insurance (a percentage of the remaining bill). This is where riders come in.

Closing the Final Gap: Why Riders Are Essential

Riders are cash-paid add-ons that cover most of your remaining deductible and co-insurance, reducing your out-of-pocket costs to a small, manageable co-payment.

The Final Picture with a Rider:

Completing our S$100,000 bill scenario with a rider.

MSHL Pays: S$16,000
ISP Pays: S$74,000
Rider Pays: S$5,000
You Pay: S$5,000

With a rider, your out-of-pocket cost is reduced to a fixed 5% co-payment, often capped at S$3,000 per year if you use the insurer’s panel of doctors. This combination provides the most comprehensive coverage against large medical bills.

Seeking Care at a Restructured Hospital

When you seek treatment at a restructured (public) hospital, you will need to provide a referral letter from a polyclinic or A&E to be eligible for subsidized care. Without a referral, you will be treated as a private patient and will not receive subsidies.

Insurer Deep-Dive: Rider Comparison


The rider has become the key determinant of your actual out-of-pocket costs and access to the latest medical treatments. Here’s a detailed, side-by-side comparison of the riders offered by each insurer.

The Critical Divide: Cancer Coverage Explained

In response to cancer drug costs growing 20% annually, the MOH introduced the Cancer Drug List (CDL). This ended broad “as-charged” coverage in main plans, shifting the responsibility for covering new, unlisted (Non-CDL) drugs to riders.

On the Cancer Drug List (CDL)

Clinically-proven, cost-effective treatments covered by your main ISP, up to a multiple of the MediShield Life limit. Riders enhance this coverage.

Coverage = MSHL Limit x Insurer’s Multiplier

NOT on the Cancer Drug List (Non-CDL)

Newer, innovative drugs. Your main ISP provides **ZERO** coverage. Protection comes **entirely** from your rider, based on the LIA’s A-F classification for experimental drugs.

Coverage = Rider’s Specific Non-CDL Benefit Limit

What is the LIA Framework?

The Life Insurance Association (LIA) established a framework to classify Non-CDL drugs from Class A (strongest clinical evidence) to Class F (weakest evidence). Most riders cover drugs in Classes A through E, but the total amount they will pay per year varies dramatically, as the chart below illustrates.

This chart compares the maximum annual coverage for Non-CDL cancer drugs provided by each insurer’s top-tier rider. The difference is stark and represents your biggest potential financial exposure.

Lifetime Premiums & Value Analysis

Choosing a plan is a long-term commitment. This section analyzes the total estimated premiums from age 31 to 85. Use the dropdown to compare different tiers of riders and see the full financial picture.

Estimated Lifetime Premiums

Value Analysis: Benefits vs. Cost

The ideal position is the bottom-left corner (lower cost, higher benefits).

Benefits vs. Cost Ratio Table

Insurer Rider Used for Analysis Lifetime Premium Benefit Score* Value Ratio

*Benefit Score is a relative ranking based on Non-CDL Coverage Limit (50% weight), Main Plan Annual Limit (30% weight), and CDL Multiplier (20% weight). A lower score is better. Value Ratio = Lifetime Premium / (1/Benefit Score). It is an indicative metric.

Premium Calculator

Estimate the total annual premium for your family. Select an insurer, plan, and rider combination. Premiums are based on age next birthday and include 9% GST. Note that these are for standard lives and are non-guaranteed.



This infographic is for informational purposes only and does not constitute financial advice. All data is synthesized from publicly available product summaries as of April 2025 and is subject to change. Please consult a qualified financial advisor before making any decisions.

// — DATA STRUCTURES —
// This section contains all the plan, rider, and premium data.
// It has been restructured to support the new categorization and logic.

const insurerData = {
“AIA”: {
mainPlan: “HealthShield Gold Max A”,
mainPlanKey: “aia_hsg_a”,
annualLimit: 2000000,
cdlMultiplier: 16,
premiumModel: ‘Community’,
premiumModelDescription: “Your premium is based on your age band, not your personal claims. This provides predictable costs, even if you fall ill.”,
riders: [
{ name: “AIA Max VitalHealth A”, riderKey: “aia_mvh_a”, category: 1, type: “Standard 5% Co-payment Rider”, nonCdlBenefit: “Not covered”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + full deductible”, keyFeatures: [“Covers deductible and co-insurance”, “Standard option for private hospital coverage”] },
{ name: “AIA Max VitalHealth A with Cancer Care Booster”, riderKey: “aia_mvh_a_booster”, category: 1, type: “Enhanced 5% Co-payment Rider”, nonCdlBenefit: “S$200,000 per year”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + full deductible”, keyFeatures: [“All benefits of the standard rider, PLUS:“, “High Non-CDL cancer drug coverage (Classes A-E)”, “S$10,000 Critical Illness payout benefit”] },
{ name: “AIA Max VitalHealth A Value”, riderKey: “aia_mvh_a_value”, category: 2, type: “Value 10% Co-payment Rider”, nonCdlBenefit: “Not covered”, copayPanel: “10% co-payment, capped at S$6,000/year”, copayNonPanel: “10% co-payment (uncapped) + full deductible”, keyFeatures: [“Lower premium option”, “Higher co-payment percentage and cap”] }
]
},
“Income”: {
mainPlan: “Enhanced IncomeShield Preferred”,
mainPlanKey: “income_eis_pref”,
annualLimit: 1500000,
cdlMultiplier: 18,
premiumModel: ‘Community’,
premiumModelDescription: “Your premium is based on your age band, not your personal claims. This provides predictable costs, even if you fall ill.”,
riders: [
{ name: “Deluxe Care Rider”, riderKey: “income_dcr”, category: 1, type: “Comprehensive 5% Co-payment Rider”, nonCdlBenefit: “S$15,000 per month”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + S$2,000 + 5% of remaining deductible”, keyFeatures: [“High 18x CDL multiplier from main plan”, “Covers deductible and co-insurance”, “Additional S$100/day hospital cash benefit”] },
{ name: “Classic Care Rider”, riderKey: “income_ccr”, category: 2, type: “Standard 10% Co-payment Rider”, nonCdlBenefit: “S$15,000 per month”, copayPanel: “10% co-payment, capped at S$3,000/year”, copayNonPanel: “10% co-payment (uncapped) + S$2,000 + 10% of remaining deductible”, keyFeatures: [“Higher co-payment percentage”, “Covers deductible and co-insurance”] }
]
},
“Great Eastern”: {
mainPlan: “GREAT SupremeHealth P PLUS”,
mainPlanKey: “ge_gsh_p”,
annualLimit: 1500000,
cdlMultiplier: 15,
premiumModel: ‘Claims-Adjusted’,
premiumModelDescription: “Applies to GREAT TotalCare P Signature. Your premium is adjusted based on claims. Using a Panel Provider keeps your premium level stable. Using a Non-Panel provider can increase your premium based on the claim amount. Staying claim-free can lead to discounts.”,
riders: [
{ name: “GREAT TotalCare P Signature”, riderKey: “ge_gtc_p”, category: 1, type: “Comprehensive 5% Co-payment Rider (Claims-Adjusted)”, nonCdlBenefit: “S$200,000 per year”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + deductible”, keyFeatures: [“High S$200k annual Non-CDL limit”, “Post-hospitalisation TCM benefit”, “Home care and palliative care benefits”] },
{ name: “GREAT TotalCare P Optimum”, riderKey: “ge_gtc_p_optimum”, category: 2, type: “Value 5% Co-payment Rider”, nonCdlBenefit: “S$200,000 per year”, copayPanel: “5% co-payment, capped at S$6,500/year”, copayNonPanel: “5% co-payment (uncapped) + deductible”, keyFeatures: [“Higher co-payment cap”, “High Non-CDL coverage”, “Community-rated premium”] },
{ name: “GREAT TotalCare Plus (Essential)”, riderKey: “ge_gtc_plus”, category: 3, type: “Add-on for Overseas Coverage”, nonCdlBenefit: “N/A”, copayPanel: “N/A”, copayNonPanel: “N/A”, keyFeatures: [“Extends medical coverage worldwide for emergencies and planned treatments”, “Adds an additional annual benefit limit for overseas care”] }
]
},
“HSBC Life”: {
mainPlan: “HSBC Life Shield Plan A”,
mainPlanKey: “hsbc_sa”,
annualLimit: 2500000,
cdlMultiplier: 18,
premiumModel: ‘Community’,
premiumModelDescription: “Your premium is based on your age band, not your personal claims. This provides predictable costs, even if you fall ill.”,
riders: [
{ name: “HSBC Life Enhanced Care Plan A”, riderKey: “hsbc_eca”, category: 1, type: “Comprehensive 5% Co-payment Rider”, nonCdlBenefit: “S$30,000 per month”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + deductible”, keyFeatures: [“Market-leading S$2.5M panel annual limit from main plan”, “High 15x multiplier for cancer drug *services*”, “Planned overseas treatment benefit”] }
]
},
“Raffles Health”: {
mainPlan: “Raffles Shield Private”,
mainPlanKey: “rhi_sp”,
annualLimit: 1500000,
cdlMultiplier: 18,
premiumModel: ‘Community’,
premiumModelDescription: “Your premium is based on your age band, not your personal claims. This provides predictable costs, even if you fall ill.”,
riders: [
{ name: “Raffles Key Rider”, riderKey: “rhi_key”, category: 1, stackable: true, type: “Co-payment Rider”, nonCdlBenefit: “S$5,000 per year”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + deductible”, keyFeatures: [“Covers deductible and co-insurance”, “Very limited Non-CDL coverage”] },
{ name: “Raffles Premier Rider”, riderKey: “rhi_premier”, category: 3, stackable: true, type: “Add-on Rider”, nonCdlBenefit: “S$50,000 per year”, copayPanel: “N/A”, copayNonPanel: “N/A”, keyFeatures: [“Moderate Non-CDL coverage”, “Includes accommodation for family member”, “Emergency outpatient accident treatment”] },
{ name: “Raffles Cancer Guard Rider”, riderKey: “rhi_cancer_guard”, category: 3, stackable: true, type: “Specialist Cancer Add-on”, nonCdlBenefit: “S$250,000 per year”, copayPanel: “N/A”, copayNonPanel: “N/A”, keyFeatures: [“Market-leading S$250k annual Non-CDL limit“, “High 15x multiplier for cancer drug services”] }
]
},
“Singlife”: {
mainPlan: “Singlife Shield Plan 1”,
mainPlanKey: “sl_s1”,
annualLimit: 2000000,
cdlMultiplier: 15,
premiumModel: ‘No-Claim Discount’,
premiumModelDescription: “You will get a 20% discount on your Health Plus rider premium if no claims are paid from your Singlife Shield and Health Plus policies during the assessment period (past 10-24 months).”,
riders: [
{ name: “Singlife Health Plus Private Prime”, riderKey: “sl_hppp”, category: 1, type: “Comprehensive 5% Co-payment Rider”, nonCdlBenefit: “S$15,000 per month”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + deductible”, keyFeatures: [“High S$2M panel annual limit from main plan”, “S$10,000 lump sum critical illness payout”] },
{ name: “Singlife Health Plus Private Lite”, riderKey: “sl_hppl”, category: 2, type: “Deductible Rider”, nonCdlBenefit: “S$15,000 per month”, copayPanel: “5% co-payment (uncapped)”, copayNonPanel: “5% co-payment (uncapped)”, keyFeatures: [“Covers deductible only, no co-payment cap, resulting in higher potential out-of-pocket costs”] },
{ name: “Singlife Cancer Cover Plus II”, riderKey: “sl_ccp2”, category: 3, type: “Standalone Cancer Add-on”, nonCdlBenefit: “Up to S$1.5 million”, copayPanel: “High deductibles apply”, copayNonPanel: “High deductibles apply”, keyFeatures: [“Massive S$1.5M annual coverage for cancer treatments”, “Can be added to any ISP”, “Comes with significant deductibles for advanced treatments”] }
]
},
“Prudential”: {
mainPlan: “PRUShield Premier”,
mainPlanKey: “pru_prem”,
annualLimit: 2000000,
cdlMultiplier: 15,
premiumModel: ‘Claims-Based’,
premiumModelDescription: “Your premium is influenced by your personal claims history. If you don’t claim, you may enjoy PRUWell Reward premium discounts. However, a claim, especially with a non-panel doctor, can lead to significantly higher premiums (up to 3x the standard level) at renewal.”,
riders: [
{ name: “PRUExtra Premier CoPay”, riderKey: “pru_prem_copay”, category: 1, type: “Standard 5% Co-payment Rider”, nonCdlBenefit: “S$150,000 per year”, copayPanel: “5% co-payment, capped at S$3,000/year”, copayNonPanel: “5% co-payment (uncapped) + deductible”, keyFeatures: [“Full co-payment coverage with cap”, “Claims-based pricing applies”] },
{ name: “PRUExtra Premier Lite CoPay”, riderKey: “pru_prem_lite”, category: 2, type: “Budget Co-payment Rider”, nonCdlBenefit: “S$150,000 per year”, copayPanel: “Covers 50% of deductible & 50% of co-insurance”, copayNonPanel: “No additional coverage for non-panel”, keyFeatures: [“Lower premium option”, “Claims-based pricing applies”, “Partial coverage for deductible and co-insurance”] }
]
}
};

const insurerDataAWard = {
“AIA”: {
mainPlan: “HealthShield Gold Max B”, mainPlanKey: “aia_hsg_b”, annualLimit: 1000000, cdlMultiplier: 15, premiumModel: ‘Community’, premiumModelDescription: “Your premium is based on your age band, not your personal claims.”,
riders: [ { name: “AIA Max VitalHealth B”, riderKey: “aia_mvh_b”, category: 1, type: “5% Co-payment Rider”, nonCdlBenefit: “Not Covered”, copayPanel: “5% co-payment, capped at S$3,000/year”, keyFeatures: [“Covers deductible and co-insurance”] } ]
},
“Income”: {
mainPlan: “Enhanced IncomeShield Advantage”, mainPlanKey: “income_advantage”, annualLimit: 600000, cdlMultiplier: 15, premiumModel: ‘Community’, premiumModelDescription: “Your premium is based on your age band, not your personal claims.”,
riders: [ { name: “Advantage Care Rider”, riderKey: “income_acr”, category: 1, type: “5% Co-payment Rider”, nonCdlBenefit: “S$15,000 per month”, copayPanel: “5% co-payment, capped at S$3,000/year”, keyFeatures: [“Covers deductible and co-insurance”] } ]
},
“Great Eastern”: {
mainPlan: “GREAT SupremeHealth A PLUS”, mainPlanKey: “ge_gsh_a”, annualLimit: 600000, cdlMultiplier: 15, premiumModel: ‘Community’, premiumModelDescription: “Your premium is based on your age band, not your personal claims.”,
riders: [
{ name: “GREAT TotalCare A Signature”, riderKey: “ge_gtc_a”, category: 1, type: “5% Co-payment Rider”, nonCdlBenefit: “S$150,000 per year”, copayPanel: “5% co-payment, capped at S$3,000/year”, keyFeatures: [“High Non-CDL coverage for an A-ward plan”] },
{ name: “GREAT TotalCare Plus (Essential)”, riderKey: “ge_gtc_plus”, category: 3, type: “Add-on for Overseas Coverage”, nonCdlBenefit: “N/A”, copayPanel: “N/A”, copayNonPanel: “N/A”, keyFeatures: [“Extends medical coverage worldwide for emergencies and planned treatments”, “Adds an additional annual benefit limit for overseas care”] }
]
},
“HSBC Life”: {
mainPlan: “HSBC Life Shield Plan B”, mainPlanKey: “hsbc_sb”, annualLimit: 550000, cdlMultiplier: 15, premiumModel: ‘Community’, premiumModelDescription: “Your premium is based on your age band, not your personal claims.”,
riders: [ { name: “HSBC Life Enhanced Care Plan B”, riderKey: “hsbc_ecb”, category: 1, type: “5% Co-payment Rider”, nonCdlBenefit: “S$25,000 per month”, copayPanel: “5% co-payment, capped at S$3,000/year”, keyFeatures: [“High monthly Non-CDL benefit”] } ]
},
“Raffles Health”: {
mainPlan: “Raffles Shield A with Raffles Hospital Option”, mainPlanKey: “rhi_sa_rho”, annualLimit: 600000, cdlMultiplier: 15, premiumModel: ‘Community’, premiumModelDescription: “Your premium is based on your age band, not your personal claims.”,
riders: [
{ name: “Raffles Key Rider A”, riderKey: “rhi_key_a”, category: 1, stackable: true, type: “Co-payment Rider”, nonCdlBenefit: “S$5,000 per year”, copayPanel: “5% co-payment, capped at S$3,000/year”, keyFeatures: [“Low cost option with minimal Non-CDL cover”] },
{ name: “Raffles Premier Rider A”, riderKey: “rhi_premier_a”, category: 3, stackable: true, type: “Add-on Rider”, nonCdlBenefit: “S$50,000 per year”, copayPanel: “N/A”, keyFeatures: [“Balanced cost and benefits”] }
]
},
“Singlife”: {
mainPlan: “Singlife Shield Plan 2”, mainPlanKey: “sl_s2”, annualLimit: 1000000, cdlMultiplier: 15, premiumModel: ‘No-Claim Discount’, premiumModelDescription: “You will get a 20% discount on your Health Plus rider premium if no claims are paid from your Singlife Shield and Health Plus policies during the assessment period (past 10-24 months).”,
riders: [
{ name: “Singlife Health Plus Public Prime”, riderKey: “sl_hppubp”, category: 1, type: “5% Co-payment Rider”, nonCdlBenefit: “S$15,000 per month”, copayPanel: “5% co-payment, capped at S$3,000/year”, keyFeatures: [“High S$1M annual limit for an A-ward plan”] },
{ name: “Singlife Health Plus Public Lite”, riderKey: “sl_hppubl”, category: 2, type: “Deductible Rider”, nonCdlBenefit: “S$15,000 per month”, copayPanel: “5% co-payment (uncapped)”, keyFeatures: [“Covers deductible only, higher potential out-of-pocket costs”] }
]
},
“Prudential”: {
mainPlan: “PRUShield Plus”, mainPlanKey: “pru_plus”, annualLimit: 600000, cdlMultiplier: 15, premiumModel: ‘Community’, premiumModelDescription: “Your premium is based on your age band, not your personal claims.”,
riders: [
{ name: “PRUExtra Plus CoPay”, riderKey: “pru_plus_copay”, category: 1, type: “5% Co-payment Rider”, nonCdlBenefit: “S$100,000 per year”, copayPanel: “5% co-payment, capped at S$3,000/year”, keyFeatures: [“Claims-based pricing does not apply to this rider”] },
{ name: “PRUExtra Plus Lite CoPay”, riderKey: “pru_plus_lite_copay”, category: 2, type: “Budget Rider”, nonCdlBenefit: “S$100,000 per year”, copayPanel: “Covers 50% of deductible & 50% of co-insurance”, keyFeatures: [“Partial coverage for deductible/co-insurance”] }
]
}
};

const premiumTables = {
// Main Plans – Private
aia_hsg_a: [[35, 440.00], [40, 490.00], [45, 1105.00], [50, 1199.00], [55, 1708.00], [60, 2170.00], [65, 2931.00], [70, 4308.00], [75, 6338.00], [80, 8837.00], [85, 9488.00], [90, 9679.00], [95, 10672.00], [100, 11533.00]],
income_eis_pref: [[35, 360.00], [40, 373.00], [50, 1009.00], [60, 1687.00], [70, 3148.00], [80, 5990.00], [90, 8588.00], [100, 11063.00]],
ge_gsh_p: [[35, 409.28], [40, 440.93], [50, 879.83], [60, 1948.09], [70, 3890.74], [80, 8184.01], [90, 9345.01], [100, 10411.84]],
hsbc_sa: [[35, 408.00], [40, 408.00], [50, 900.00], [60, 1925.40], [70, 3158.40], [80, 5837.80], [90, 8381.10], [100, 10775.90]],
rhi_sp: [[35, 345.33], [40, 348.40], [50, 643.81], [60, 1076.76], [70, 2274.74], [80, 3940.82], [90, 7669.73], [100, 9695.91]],
sl_s1: [[35, 381.00], [40, 432.00], [50, 1237.00], [60, 1930.00], [70, 3629.00], [80, 7058.00], [90, 9231.00], [100, 9976.00]],
pru_prem: [[35, 460.00], [40, 472.00], [50, 943.00], [60, 2064.00], [70, 4116.00], [80, 7567.00], [90, 11175.00], [100, 12335.00]],

// Riders – Private
aia_mvh_a: [[35, 1018.14], [40, 1018.14], [45, 1243.14], [50, 1332.00], [55, 1895.00], [60, 2440.00], [65, 3286.00], [70, 4482.00], [75, 6014.00], [80, 6948.00], [85, 7814.00], [90, 8787.00], [95, 9382.00], [100, 9698.00]],
aia_mvh_a_value: [[35, 351.44], [40, 351.44], [45, 434.98], [50, 546.00], [55, 699.84], [60, 974.88], [65, 1310.02], [70, 1793.88], [75, 2285.94], [80, 2703.58], [85, 2970.50], [90, 3149.78], [95, 3340.28], [100, 3808.88]],
aia_mvh_a_booster: [[35, 1018.14], [40, 1018.14], [45, 1243.14], [50, 1395.14], [55, 1958.14], [60, 2503.14], [65, 3349.14], [70, 4545.14], [75, 6077.14], [80, 7011.14], [85, 7877.14], [90, 8850.14], [95, 9445.14], [100, 10083.14]],
income_dcr: [[35, 976.00], [40, 1028.00], [50, 1626.00], [60, 3572.00], [70, 6994.00], [80, 11346.00], [90, 13057.00], [100, 14159.00]],
income_ccr: [[35, 394.00], [40, 446.00], [50, 874.00], [60, 1643.00], [70, 3188.00], [80, 6738.00], [90, 7995.00], [100, 8836.00]],
ge_gtc_p: [[35, 1225.78], [40, 1358.73], [50, 2170.81], [60, 4528.82], [70, 8388.35], [80, 12098.53], [90, 15048.56], [100, 16924.40]],
ge_gtc_p_optimum: [[35, 409.92], [40, 494.35], [50, 768.84], [60, 1420.00], [70, 2431.90], [80, 3773.85], [90, 5022.45], [100, 5642.86]],
hsbc_eca: [[35, 1100.10], [40, 1116.00], [50, 1833.80], [60, 3281.10], [70, 6613.00], [80, 8414.90], [90, 11016.40], [100, 15273.30]],
rhi_key: [[35, 347.94], [40, 353.79], [50, 467.42], [60, 1001.63], [70, 1636.58], [80, 2337.13], [90, 2931.09], [100, 4038.14]],
rhi_premier: [[35, 104.26], [40, 112.47], [50, 176.90], [60, 230.79], [70, 378.39], [80, 864.57], [90, 1444.46], [100, 1971.62]],
rhi_cancer_guard: [[35, 99.19], [40, 99.19], [50, 421.83], [60, 855.65], [70, 1205.54], [80, 2182.18], [90, 3361.56], [100, 3361.56]],
sl_hppp: [[35, 970.00], [40, 1009.00], [50, 1709.00], [60, 3522.00], [70, 6764.00], [80, 8481.00], [90, 11582.00], [100, 14076.00]],
sl_hppl: [[35, 451.00], [40, 460.00], [50, 574.00], [60, 1021.00], [70, 2606.00], [80, 3572.00], [90, 4538.00], [100, 5503.00]],
pru_prem_copay: [[35, 927.20], [40, 988.00], [50, 1534.40], [60, 3116.00], [70, 6068.00], [80, 8845.60], [90, 10797.60], [100, 11699.20]],
pru_prem_lite: [[35, 473.00], [40, 505.00], [50, 778.00], [60, 1515.00], [70, 2945.00], [80, 4472.00], [90, 5662.00], [100, 6207.00]],

// Main Plans – A Ward
aia_hsg_b: [[35, 171.14], [40, 171.14], [50, 334.12], [60, 510.36], [70, 1389.48], [80, 2968.46], [90, 3379.00], [100, 4760.34]],
income_advantage: [[35, 100.00], [40, 125.00], [50, 251.00], [60, 436.00], [70, 1075.00], [80, 2595.00], [90, 3708.00], [100, 5425.00]],
ge_gsh_a: [[35, 125.30], [40, 137.53], [50, 246.53], [60, 527.68], [70, 1337.54], [80, 2601.74], [90, 3665.26], [100, 5164.77]],
hsbc_sb: [[35, 158.90], [40, 158.90], [50, 317.80], [60, 476.70], [70, 964.70], [80, 1993.60], [90, 3135.50], [100, 4933.50]],
rhi_sa: [[35, 114.09], [40, 128.36], [50, 225.13], [60, 382.01], [70, 963.68], [80, 2395.96], [90, 3515.50], [100, 4707.37]],
rhi_rho: [[35, 145.67], [40, 145.67], [50, 227.17], [60, 232.26], [70, 342.28], [80, 430.91], [90, 654.00], [100, 731.42]], // Raffles Hospital Option Rider
rhi_sa_rho: [[35, 259.76], [40, 274.03], [50, 452.3], [60, 614.27], [70, 1305.96], [80, 2826.87], [90, 4169.5], [100, 5438.79]], // Combined Main Plan + Rider
sl_s2: [[35, 178.00], [40, 184.00], [50, 352.00], [60, 510.00], [70, 1249.00], [80, 2919.00], [90, 3659.00], [100, 4699.00]],
pru_plus: [[35, 87.61], [40, 114.09], [50, 184.38], [60, 260.79], [70, 788.47], [80, 2053.68], [90, 2823.81], [100, 3826.21]],

// Riders – A Ward
aia_mvh_b: [[35, 351.44], [40, 351.44], [50, 609.14], [60, 974.88], [70, 1793.88], [80, 2703.58], [90, 3403.42], [100, 3872.02]],
income_acr: [[35, 120.00], [40, 131.00], [50, 216.00], [60, 278.00], [70, 563.00], [80, 1152.00], [90, 1732.00], [100, 2183.00]],
ge_gtc_a: [[35, 214.95], [40, 238.37], [50, 405.44], [60, 787.45], [70, 1414.96], [80, 2177.96], [90, 2856.41], [100, 3134.51]],
hsbc_ecb: [[35, 248.60], [40, 254.70], [50, 360.40], [60, 559.30], [70, 1367.10], [80, 2237.00], [90, 2610.00], [100, 3728.40]],
rhi_key_a: [[35, 161.67], [40, 161.67], [50, 240.16], [60, 388.93], [70, 636.12], [80, 1205.46], [90, 1433.91], [100, 1699.84]],
rhi_premier_a: [[35, 92.55], [40, 101.92], [50, 145.26], [60, 200.33], [70, 311.62], [80, 767.33], [90, 1183.21], [100, 1521.77]],
sl_hppubp: [[35, 280.00], [40, 293.00], [50, 449.00], [60, 707.00], [70, 1519.00], [80, 2429.00], [90, 2868.00], [100, 4110.00]],
sl_hppubl: [[35, 88.00], [40, 93.00], [50, 158.00], [60, 304.00], [70, 865.00], [80, 1204.00], [90, 1299.00], [100, 2179.00]],
pru_plus_copay: [[35, 289.00], [40, 289.00], [50, 360.00], [60, 693.00], [70, 1250.00], [80, 2226.00], [90, 2790.00], [100, 3140.00]],
pru_plus_lite_copay: [[35, 173.00], [40, 173.00], [50, 217.00], [60, 415.00], [70, 749.00], [80, 1402.00], [90, 1842.00], [100, 2072.00]],

// Specialist / Other
sl_ccp2: [[35, 155], [40, 170], [50, 355], [60, 900], [70, 1830], [80, 2560], [85, 2780], [100, 3410]],
ge_gtc_plus: [[5, 129.37], [6, 111.04], [7, 95.76], [8, 82.51], [9, 71.31], [10, 65.19], [11, 67.23], [12, 68.26], [13, 70.29], [14, 74.36], [15, 75.38], [16, 77.42], [17, 79.46], [18, 81.50], [19, 84.55], [20, 86.59], [21, 89.64], [22, 91.68], [23, 94.74], [24, 98.81], [25, 100.85], [26, 103.91], [27, 106.96], [28, 110.01], [29, 111.04], [30, 112.05], [31, 112.05], [32, 112.05], [33, 113.08], [34, 113.08], [35, 114.09], [36, 114.09], [37, 114.09], [38, 115.11], [39, 115.11], [40, 116.13], [41, 117.15], [42, 118.17], [43, 125.30], [44, 126.32], [45, 134.46], [46, 141.60], [47, 142.62], [48, 144.65], [49, 151.78], [50, 153.82], [51, 154.85], [52, 165.03], [53, 175.22], [54, 185.40], [55, 196.60], [56, 208.83], [57, 222.08], [58, 235.32], [59, 257.73], [60, 269.95], [61, 294.40], [62, 308.67], [63, 323.95], [64, 348.40], [65, 375.90], [66, 403.40], [67, 432.95], [68, 464.53], [69, 535.83], [70, 571.49], [71, 611.22], [72, 652.99], [73, 697.81], [74, 749.76], [75, 776.24], [76, 814.95], [77, 853.67], [78, 896.45], [79, 939.23], [80, 985.08], [81, 1033.97], [82, 1082.87], [83, 1135.85], [84, 1192.89], [85, 1249.94], [100, 1586.10]],
medisave_limits: [[40, 300], [70, 600], [90, 900], [100, 900]],
medishield_life: [[35, 503], [40, 503], [50, 637], [60, 903], [70, 1326], [80, 2187], [90, 2785], [100, 2826]]
};

// — UTILITY FUNCTIONS —
function getPremium(tableKey, age) {
const table = premiumTables[tableKey];
if (!table) return 0;
for (let i = 0; i < table.length; i++) { if (age { setupEventListeners(); updateAllContent(currentPlanType); }); function setupEventListeners() { document.getElementById('plan-type-tabs').addEventListener('click', (e) => {
if (e.target.matches(‘.tab-button’) && !e.target.classList.contains(‘active’)) {
document.querySelector(‘#plan-type-tabs .active’).classList.remove(‘active’);
e.target.classList.add(‘active’);
currentPlanType = e.target.dataset.planType;
updateAllContent(currentPlanType);
}
});

document.getElementById(‘insurer-tabs’).addEventListener(‘click’, (e) => {
if (e.target.matches(‘.tab-button’)) {
document.querySelector(‘#insurer-tabs .active’).classList.remove(‘active’);
e.target.classList.add(‘active’);
renderInsurerContent(e.target.dataset.insurer, currentPlanType);
}
});

document.getElementById(‘rider-scenario-select’).addEventListener(‘change’, (e) => {
updateAnalysisForScenario(e.target.value);
});

document.getElementById(‘calc-insurer’).addEventListener(‘change’, () => populateCalcPlansAndRiders(currentPlanType));
document.getElementById(‘calc-plan’).addEventListener(‘change’, () => populateCalcPlansAndRiders(currentPlanType));
document.getElementById(‘add-person-btn’).addEventListener(‘click’, addFamilyMemberRow);
document.getElementById(‘calculate-btn’).addEventListener(‘click’, calculateFamilyPremiums);
}

// — CORE LOGIC —
function updateAllContent(planType) {
if (lifetimePremiumChart) lifetimePremiumChart.destroy();
if (valueRatioChart) valueRatioChart.destroy();
if (nonCdlChart) nonCdlChart.destroy();
if (lifetimeFamilyChart) lifetimeFamilyChart.destroy();

const dataSet = planType === ‘private’ ? insurerData : insurerDataAWard;
const insurers = Object.keys(dataSet);

updateInsurerTabs(insurers);
renderInsurerContent(insurers[0], planType);
renderNonCdlChart(planType);
generateScenariosAndPopulateDropdown(planType);
updateAnalysisForScenario(document.getElementById(‘rider-scenario-select’).value);
populateCalculator(planType);
}

function updateInsurerTabs(insurers) {
const insurerTabsContainer = document.getElementById(‘insurer-tabs’);
insurerTabsContainer.innerHTML = ”;
insurers.forEach((insurer, index) => {
const button = document.createElement(‘button’);
button.className = `tab-button ${index === 0 ? ‘active’ : ”}`;
button.textContent = insurer;
button.dataset.insurer = insurer;
insurerTabsContainer.appendChild(button);
});
}

function renderInsurerContent(insurer, planType) {
const dataSet = planType === ‘private’ ? insurerData : insurerDataAWard;
const data = dataSet[insurer];
const insurerContentContainer = document.getElementById(‘insurer-content’);

let premiumModelHtml = `

Premium Model: ${data.premiumModel}

${data.premiumModelDescription}

`;

let ridersHtml = ‘

‘;
const categories = { 1: [], 2: [], 3: [] };
data.riders.forEach(r => { if(categories[r.category]) categories[r.category].push(r) });

Object.keys(categories).forEach(cat => {
if (categories[cat].length > 0) {
let catTitle = ”;
if (cat === ‘1’) catTitle = ‘Category 1: Primary Riders (Lowest Co-payment)’;
if (cat === ‘2’) catTitle = ‘Category 2: Alternative Riders (Higher Co-payment/Deductible)’;
if (cat === ‘3’) catTitle = ‘Category 3: Add-on Riders’;
ridersHtml += `

${catTitle}

`;

categories[cat].forEach(rider => {
let featuresHtml = (rider.keyFeatures || []).map(feature => `

  • ${feature}
  • `).join(”);
    ridersHtml += `

    ${rider.name}

    ${rider.type}

    Non-CDL Benefit

    ${rider.nonCdlBenefit || ‘N/A’}

    Panel Co-payment

    ${rider.copayPanel || ‘N/A’}

    ${rider.copayNonPanel ? `

    Non-Panel Co-payment

    ${rider.copayNonPanel}

    ` : ”}
    ${featuresHtml ? `

    Key Features

      ${featuresHtml}

    ` : ”}

    `;
    });
    }
    });

    ridersHtml += ‘

    ‘;
    insurerContentContainer.innerHTML = `

    ${premiumModelHtml}${ridersHtml}

    `;
    }

    function renderNonCdlChart(planType) {
    if (nonCdlChart) nonCdlChart.destroy();
    const dataSet = planType === ‘private’ ? insurerData : insurerDataAWard;
    const nonCdlCtx = document.getElementById(‘nonCdlChart’).getContext(‘2d’);
    const topRiderNonCdlData = Object.entries(dataSet).map(([name, data]) => {
    // Find the best rider for Non-CDL coverage
    const topRider = data.riders.reduce((max, rider) => {
    let benefitValue = 0;
    if (typeof rider.nonCdlBenefit === ‘string’) {
    const match = rider.nonCdlBenefit.match(/S\$([\d,]+)/);
    if (match) {
    benefitValue = parseInt(match[1].replace(/,/g, ”), 10);
    if (rider.nonCdlBenefit.includes(‘month’)) benefitValue *= 12;
    }
    }
    return benefitValue > (max.benefitValue || 0) ? { …rider, benefitValue } : max;
    }, { benefitValue: 0 });

    return { name, benefit: topRider.benefitValue };
    }).sort((a,b) => b.benefit – a.benefit);

    nonCdlChart = new Chart(nonCdlCtx, {
    type: ‘bar’,
    data: {
    labels: topRiderNonCdlData.map(d => d.name),
    datasets: [{
    label: ‘Max Annual Non-CDL Coverage (S$)’,
    data: topRiderNonCdlData.map(d => d.benefit),
    backgroundColor: [‘#003f5c’, ‘#444e86’, ‘#955196’, ‘#dd5182’, ‘#ff6e54’, ‘#ffa600’, ‘#7a5195’],
    }]
    },
    options: {
    responsive: true, maintainAspectRatio: false, indexAxis: ‘y’,
    scales: { x: { beginAtZero: true, ticks: { callback: value => ‘S$’ + (value / 1000) + ‘k’ } } },
    plugins: {
    legend: { display: false },
    title: { display: true, text: ‘Non-CDL Cancer Drug Coverage by Insurer (Best Rider)’, font: { size: 16 } },
    tooltip: { callbacks: { label: context => `S$${new Intl.NumberFormat(‘en-SG’).format(context.parsed.x)}` } }
    }
    }
    });
    }

    // — ANALYSIS & CHARTING —
    function generateScenariosAndPopulateDropdown(planType) {
    // This function is kept for the lifetime analysis section, but the logic might need adjustments
    // based on the new rider categories if more complex scenarios are needed in the future.
    // For now, it will focus on finding the best Cat 1 and Cat 2 riders for comparison.
    const dataSet = planType === ‘private’ ? insurerData : insurerDataAWard;
    scenarios = {
    main_plan_only: { name: ‘Main Plan Only’, riders: {} }
    };

    // Top-Tier (Best Cat 1)
    scenarios.top_tier = { name: ‘Top-Tier Riders (Cat 1)’, riders: {} };
    Object.keys(dataSet).forEach(insurer => {
    const cat1Riders = dataSet[insurer].riders.filter(r => r.category === 1);
    if (cat1Riders.length > 0) {
    // Simple logic: pick the first Cat 1 rider as the “top tier” for this comparison
    scenarios.top_tier.riders[insurer] = cat1Riders[0].riderKey;
    }
    });

    // Budget-Tier (Best Cat 2)
    scenarios.budget_tier = { name: ‘Budget Riders (Cat 2)’, riders: {} };
    Object.keys(dataSet).forEach(insurer => {
    const cat2Riders = dataSet[insurer].riders.filter(r => r.category === 2);
    if (cat2Riders.length > 0) {
    scenarios.budget_tier.riders[insurer] = cat2Riders[0].riderKey;
    } else {
    // Fallback to Cat 1 if no Cat 2 exists
    const cat1Riders = dataSet[insurer].riders.filter(r => r.category === 1);
    if (cat1Riders.length > 0) {
    scenarios.budget_tier.riders[insurer] = cat1Riders[0].riderKey;
    }
    }
    });

    const selectEl = document.getElementById(‘rider-scenario-select’);
    selectEl.innerHTML = ”;
    for (const key in scenarios) {
    if(Object.keys(scenarios[key].riders).length > 0 || key === ‘main_plan_only’) {
    const option = document.createElement(‘option’);
    option.value = key;
    option.textContent = scenarios[key].name;
    selectEl.appendChild(option);
    }
    }
    selectEl.value = ‘top_tier’;
    }

    function updateAnalysisForScenario(scenarioKey) {
    // This function remains largely the same, driving the lifetime premium and value charts.
    const dataSet = currentPlanType === ‘private’ ? insurerData : insurerDataAWard;
    const scenario = scenarios[scenarioKey];
    const selectedRiders = scenario.riders;

    currentAnalysisData = Object.keys(dataSet).map(insurer => {
    const data = dataSet[insurer];
    const mainPlanKey = data.mainPlanKey;
    const riderKey = selectedRiders[insurer];

    let lifetimeMsl = 0, lifetimeMain = 0, lifetimeRider = 0;
    for (let age = 31; age (getNcdValue(r) > getNcdValue(max) ? r : max), data.riders[0] || {});

    return {
    insurer,
    lifetimeMsl,
    lifetimeMain,
    lifetimeRider,
    lifetimePremium: lifetimeMsl + lifetimeMain + lifetimeRider,
    nonCdlBenefit: getNcdValue(topRiderForNcd),
    annualLimit: data.annualLimit,
    cdlMultiplier: data.cdlMultiplier,
    riderKey: riderKey
    };
    });

    renderLifetimeChart(scenarioKey !== ‘main_plan_only’);
    renderValueAnalysis();
    }

    function getNcdValue(rider) {
    if (!rider || !rider.nonCdlBenefit) return 0;
    let benefitValue = 0;
    if (typeof rider.nonCdlBenefit === ‘string’) {
    const match = rider.nonCdlBenefit.match(/S\$([\d,]+)/);
    if (match) {
    benefitValue = parseInt(match[1].replace(/,/g, ”), 10);
    if (rider.nonCdlBenefit.includes(‘month’)) benefitValue *= 12;
    }
    }
    return benefitValue;
    }

    function renderLifetimeChart(includeRider) {
    if (lifetimePremiumChart) lifetimePremiumChart.destroy();
    const lifetimeCtx = document.getElementById(‘lifetimePremiumChart’).getContext(‘2d’);
    const titleEl = document.getElementById(‘lifetime-chart-title’);
    const scenarioName = scenarios[document.getElementById(‘rider-scenario-select’).value].name;

    let datasets;
    if (includeRider) {
    titleEl.textContent = `Estimated Lifetime Premiums (${scenarioName})`;
    datasets = [
    { label: ‘MediShield Life’, data: currentAnalysisData.map(d => d.lifetimeMsl), backgroundColor: ‘#955196’, stack: ‘A’ },
    { label: ‘Main Plan’, data: currentAnalysisData.map(d => d.lifetimeMain), backgroundColor: ‘#dd5182’, stack: ‘A’ },
    { label: ‘Rider (Cash)’, data: currentAnalysisData.map(d => d.lifetimeRider), backgroundColor: ‘#ff6e54’, stack: ‘A’ }
    ];
    } else {
    titleEl.textContent = “Estimated Lifetime Premiums (Main Plan + MSHL)”;
    datasets = [
    { label: ‘MediShield Life’, data: currentAnalysisData.map(d => d.lifetimeMsl), backgroundColor: ‘#955196’, stack: ‘A’ },
    { label: ‘Main Plan’, data: currentAnalysisData.map(d => d.lifetimeMain), backgroundColor: ‘#dd5182’, stack: ‘A’ }
    ];
    }

    lifetimePremiumChart = new Chart(lifetimeCtx, {
    type: ‘bar’,
    data: {
    labels: currentAnalysisData.map(d => d.insurer),
    datasets: datasets
    },
    options: {
    responsive: true, maintainAspectRatio: false, indexAxis: ‘y’,
    scales: { x: { stacked: true, ticks: { callback: value => ‘S$’ + (value / 1000) + ‘k’ } }, y: { stacked: true } },
    plugins: { legend: { position: ‘bottom’ }, title: { display: false }, tooltip: { callbacks: { label: function(context) { return `${context.dataset.label}: S$${context.raw.toLocaleString()}`; } } } }
    }
    });
    }

    function renderValueAnalysis() {
    const dataSet = currentPlanType === ‘private’ ? insurerData : insurerDataAWard;

    const rank = (data, key, ascending = false) => {
    const sorted = […data].sort((a, b) => ascending ? a[key] – b[key] : b[key] – a[key]);
    return data.map(item => sorted.findIndex(sortedItem => sortedItem.insurer === item.insurer) + 1);
    };
    const nonCdlRanks = rank(currentAnalysisData, ‘nonCdlBenefit’);
    const annualLimitRanks = rank(currentAnalysisData, ‘annualLimit’);
    const cdlMultiplierRanks = rank(currentAnalysisData, ‘cdlMultiplier’);

    currentAnalysisData.forEach((item, i) => {
    item.benefitScore = (nonCdlRanks[i] * 0.5) + (annualLimitRanks[i] * 0.3) + (cdlMultiplierRanks[i] * 0.2);
    item.valueRatio = item.lifetimePremium * item.benefitScore;
    });

    if (valueRatioChart) valueRatioChart.destroy();
    const valueRatioCtx = document.getElementById(‘valueRatioChart’).getContext(‘2d’);
    const scatterData = { datasets: currentAnalysisData.map((item, index) => ({ label: item.insurer, data: [{ x: item.lifetimePremium, y: item.benefitScore }], backgroundColor: [‘#003f5c’, ‘#444e86’, ‘#955196’, ‘#dd5182’, ‘#ff6e54’, ‘#ffa600’, ‘#7a5195’][index], pointRadius: 8, pointHoverRadius: 10 })) };
    valueRatioChart = new Chart(valueRatioCtx, {
    type: ‘scatter’, data: scatterData,
    options: { responsive: true, maintainAspectRatio: false, scales: { x: { title: { display: true, text: ‘Estimated Lifetime Premium (S$)’ }, ticks: { callback: value => ‘S$’ + (value / 1000) + ‘k’ } }, y: { title: { display: true, text: ‘Benefit Score (Lower is Better)’ } } }, plugins: { legend: { display: false }, title: { display: false }, tooltip: { callbacks: { title: function (context) { return context[0].dataset.label; }, label: function (context) { const item = context.dataset.data[0]; return [ `Lifetime Premium: S$${item.x.toLocaleString()}`, `Benefit Score: ${item.y.toFixed(2)}` ]; } } } } }
    });

    const valueTableBody = document.getElementById(‘value-ratio-table’);
    valueTableBody.innerHTML = ”;
    currentAnalysisData.sort((a,b) => a.valueRatio – b.valueRatio).forEach(item => {
    const insurerInfo = dataSet[item.insurer];
    const riderInfo = item.riderKey ? insurerInfo.riders.find(r => r.riderKey === item.riderKey) : { name: ‘N/A’ };
    const riderName = riderInfo ? riderInfo.name : ‘N/A’;

    const row = `

    ${item.insurer} ${riderName} S$${item.lifetimePremium.toLocaleString()} ${item.benefitScore.toFixed(2)} ${(item.valueRatio / 1e8).toFixed(2)}

    `;
    valueTableBody.innerHTML += row;
    });
    }

    // — CALCULATOR LOGIC —
    function populateCalculator(planType) {
    const dataSet = planType === ‘private’ ? insurerData : insurerDataAWard;
    const insurers = Object.keys(dataSet);
    const calcInsurer = document.getElementById(‘calc-insurer’);
    calcInsurer.innerHTML = ”;
    insurers.forEach(insurer => {
    const option = document.createElement(‘option’);
    option.value = insurer;
    option.textContent = insurer;
    calcInsurer.appendChild(option);
    });
    populateCalcPlansAndRiders(planType);
    }

    function populateCalcPlansAndRiders(planType) {
    const dataSet = planType === ‘private’ ? insurerData : insurerDataAWard;
    const selectedInsurer = document.getElementById(‘calc-insurer’).value;
    const calcPlan = document.getElementById(‘calc-plan’);

    calcPlan.innerHTML = ”;
    const planOption = document.createElement(‘option’);
    planOption.value = dataSet[selectedInsurer].mainPlanKey;
    planOption.textContent = dataSet[selectedInsurer].mainPlan;
    calcPlan.appendChild(planOption);

    // Populate Riders
    const riderContainer = document.getElementById(‘calc-rider-options’);
    riderContainer.innerHTML = ”;
    const data = dataSet[selectedInsurer];
    const categories = { 1: [], 2: [], 3: [] };
    data.riders.forEach(r => { if(categories[r.category]) categories[r.category].push(r) });

    // Handle Cat 1 & 2 (Radio buttons, mutually exclusive)
    const primaryRiders = […categories[1], …categories[2]];
    if (primaryRiders.length > 0 && !primaryRiders[0].stackable) {
    riderContainer.innerHTML += `

    Primary Rider (Optional)

    `;
    // Add “None” option
    riderContainer.innerHTML += `

    None

    `;
    primaryRiders.forEach((rider) => {
    riderContainer.innerHTML += `

    ${rider.name} (${rider.type})

    `;
    });
    }

    // Handle Raffles Stackable & Cat 3 (Checkboxes)
    const addonRiders = […categories[3]];
    if (primaryRiders.length > 0 && primaryRiders[0].stackable) {
    addonRiders.unshift(…primaryRiders); // Add Raffles’ Cat 1 to the checkbox list
    }

    if (addonRiders.length > 0) {
    riderContainer.innerHTML += `

    Add-on Options

    `;
    addonRiders.forEach(rider => {
    riderContainer.innerHTML += `

    ${rider.name} (${rider.type})

    `;
    });
    }
    }

    function addFamilyMemberRow() {
    const container = document.getElementById(‘family-members-container’);
    const count = container.getElementsByClassName(‘family-member-row’).length;
    const newRow = document.createElement(‘div’);
    newRow.className = ‘flex items-center gap-4 family-member-row’;
    newRow.innerHTML = `


    `;
    container.appendChild(newRow);
    newRow.querySelector(‘.remove-person-btn’).addEventListener(‘click’, (e) => {
    e.target.parentElement.remove();
    const rows = container.getElementsByClassName(‘family-member-row’);
    for(let i = 1; i {
    const age = parseInt(input.value);
    if (isNaN(age) || age {
    riderPremium += getPremium(addonInput.value, age);
    });

    totalMainPlanPremium += (mslPremium + mainPlanPrivatePremium);
    totalRiderPremium += riderPremium;

    const medisaveLimit = getPremium(‘medisave_limits’, age);
    const medisaveForMainPlan = Math.min(mainPlanPrivatePremium, medisaveLimit);
    totalMedisavePayable += (mslPremium + medisaveForMainPlan);

    const cashForMain = mainPlanPrivatePremium – medisaveForMainPlan;
    totalCashForMainPlan += (cashForMain > 0 ? cashForMain : 0);

    // Lifetime Projection
    for (let projAge = age; projAge {
    projRiderPremium += getPremium(addonInput.value, projAge);
    });

    const projMedisaveLimit = getPremium(‘medisave_limits’, projAge);
    const medisavePortionForMain = Math.min(projMainPlanPremium, projMedisaveLimit);
    const cashPortionForMain = projMainPlanPremium > medisavePortionForMain ? projMainPlanPremium – medisavePortionForMain : 0;

    totalLifetimeMedisave += projMslPremium + medisavePortionForMain;
    totalLifetimeCash += cashPortionForMain + projRiderPremium;
    }
    });

    const totalCash = totalCashForMainPlan + totalRiderPremium;
    const totalPremium = totalMainPlanPremium + totalRiderPremium;

    document.getElementById(‘result-main-plan’).textContent = `S$${totalMainPlanPremium.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}`;
    document.getElementById(‘result-medisave’).textContent = `S$${totalMedisavePayable.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})} payable by MediSave`;
    document.getElementById(‘result-rider’).textContent = `S$${totalRiderPremium.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}`;
    document.getElementById(‘result-total-cash’).textContent = `S$${totalCash.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}`;
    document.getElementById(‘result-total’).textContent = `Total Annual Premium: S$${totalPremium.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}`;
    document.getElementById(‘results-container’).classList.remove(‘hidden’);
    document.getElementById(‘lifetime-family-results’).classList.remove(‘hidden’);

    if (lifetimeFamilyChart) lifetimeFamilyChart.destroy();
    const lifetimeCtx = document.getElementById(‘lifetimeFamilyChart’).getContext(‘2d’);
    lifetimeFamilyChart = new Chart(lifetimeCtx, {
    type: ‘bar’,
    data: {
    labels: [‘Total Lifetime Premium’],
    datasets: [
    { label: ‘MediSave Portion’, data: [totalLifetimeMedisave], backgroundColor: ‘#955196’ },
    { label: ‘Cash Portion’, data: [totalLifetimeCash], backgroundColor: ‘#dd5182’ }
    ]
    },
    options: {
    indexAxis: ‘y’,
    scales: { x: { stacked: true, ticks: { callback: value => ‘S$’ + (value / 1000).toFixed(0) + ‘k’ } }, y: { stacked: true } },
    responsive: true, maintainAspectRatio: false,
    plugins: { legend: { position: ‘bottom’ }, tooltip: { callbacks: { label: function(context) { return `${context.dataset.label}: S$${context.raw.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 0})}`; } } } }
    }
    });
    const totalLifetime = totalLifetimeMedisave + totalLifetimeCash;
    document.getElementById(‘lifetime-summary-text’).textContent = `Total: S$${totalLifetime.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 0})} (MediSave: S$${totalLifetimeMedisave.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 0})}, Cash: S$${totalLifetimeCash.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 0})})`;
    }

    Important: The information and opinions in this article are for general information purposes only. They should not be relied on as professional financial advice. Readers should seek unbiased financial advice that is customised to their specific financial objectives, situations & needs. This advertisement or publication has not been reviewed by the Monetary Authority of Singapore.

    Published By:

    Marshall Wong

    By day, I’m the Digital Marketing Manager at Financial Alliance here in Singapore, where I get to blend my passion for tech and marketing. I build tools (like this ‘Engage’ platform), conduct trainings, and manage our internal ‘Content Creator Alliance’ – all aimed at helping financial consultants succeed online.

    But when I’m off the clock, you’ll often find me underwater. I’m a certified SSI Freediving Instructor and love sharing the peace and challenge of freediving with students. It’s a world away from screens and strategy, yet teaches immense focus.

    I’m constantly exploring new tech, especially AI and automation, and thinking about how it can shape the future. If you share interests in digital marketing, financial advisory innovation, or the transformative power of freediving, I’d love to connect!

    🌏 Website: https://marshallwong.com

    CONTACT US

    Contact Me

    What type of services are you looking for? 请问您需要咨询哪些方面的服务?(Please select all that applies)

    ACKNOWLEDGEMENT

    By submitting this form, I confirm that

    提交此表格,即表示

    • I have read and understood FAPL’s Personal Data Policy, and hereby give my acknowledgement and consent to FAPL to use my personal data in accordance with FAPL’s Personal Data Policy.
    • 我已阅读并理解鑫盟理财私人有限公司的个人数据保护政策,并同意鑫盟理财私人有限公司根据个人数据保护政策所阐述的用途使用我的个人资料。
    • I have read and understood the disclaimers above and hereby affirm my acceptance of these terms.
    • 我已阅读并理解了上述免责声明,特此声明接受这些条款。
    • I have not been directly contacted or approached by any representative or employee of FAPL with an offer or solicitation to apply for any financial products not offered in my home country.
    • 鑫盟理财私人有限公司的代表或员工并未直接与我联系或提出要约或招揽购买我原居住国境内所不提供的任何金融产品.
    • Financial Alliance Pte Ltd ( “FAPL”) is licensed by the Monetary Authority of Singapore (“MAS”) and allowed to conduct financial advisory activities in accordance with the Financial Advisers Act (Cap. 110) (“FAA”) and the Securities & Futures Act (Cap. 289) (“SFA”) within the jurisdiction of the Republic of Singapore and in accordance with the licenses granted by MAS
    • 鑫盟理财私人有限公司( “鑫盟理财”)持有由新加坡金融管理局(“MAS”)所颁发的牌照,并许可在新加坡共和国境内依据财务顾问法(第110章)和证券与期货交易法(第289章)进行财务咨询等相关业务
    • Any services provided by FAPL to persons not ordinarily resident in Singapore are provided solely on an offshore basis from Singapore, resulting from direct enquiry on the part of the foreign residents.
    • 鑫盟理财为非新加坡居民所提供的任何服务仅限于直接向鑫盟理财发出咨询请求的国外居民。
    • As an integral part of the provision of such services, FAPL may from time to time make available to such residents, documents and information making reference to capital markets products (for example, in connection with the provision of fund management or investment advisory services outside of the foreign jurisdiction).
    • 作为提供此类服务的一部分,鑫盟理财会不时向此类咨询者提供有关产品的参考文件和信息(例如,关于海外司法管辖区以外的资金管理或投资咨询服务等信息)。
    • Such documents and information are provided by Financial Alliance Pte Ltd (“FAPL”) is for general information only and is not intended for anyone other than the recipient.
    • 此类由鑫盟理财所提供的文件和信息仅供收件人做一般信息参考。
    • It does not take into account the specific investment objectives, financial situation or particular needs of any particular person.
    • 此类文件和信息不会把-个人投资目标,财务状况或其特定需求等考虑在内。
    • It does not constitute the making available of, or an offer or solicitation by FAPL to buy or sell or subscribe for any such capital markets product or to enter into a transaction or to participate in any particular trading or investment strategy nor an advice or a recommendation with respect to such financial products.
    • 鑫盟理财向该人士提供此类文件和信息将不构成要约或招揽购买或出售或认购任何此类资本市场产品,或达成交易或参与任何特定的投资策略,也不构成有关此类金融产品的建议或推荐。
    • These documents may not be published, circulated, reproduced or distributed in whole or in part to any other person without FAPL’s prior written consent.
    • 未经鑫盟理财的事先书面同意,不得将这些文件全文或部分发布,传播,复制或分发给其他人。
    • This document is not intended for distribution to, publication or use by any person in any jurisdiction, where such distribution, publication or use would be contrary to applicable law or would subject FAPL and its related corporations, connected persons, associated persons and/or affiliates to any registration, licensing or other requirements within such jurisdiction.
    • 鑫盟理财无意在分发,出版或使用此类文件可能违反当地法律,或本公司及关联公司或关联人士需要注册,获得许可或符合规定的司法权区向当地人士分发、出版此类文件或供其使用。
    • You shall ensure that you have and will continue to be fully compliant with all applicable laws in your home country when entering into discussion or contracts with FAPL.
    • 在与鑫盟理财进行讨论或签订合约时,您应确保您已完全遵守并将继续遵守您所在原居住国的所有适用法律及条规。

    In compliance with the Personal Data Protection Act, Financial Alliance Pte Ltd (“FAPL”) seek your consent to collect and use your personal data (e.g. name, NRIC, contact numbers, mailing addresses, email addresses and photograph) for the purposes of and in accordance with FAPL’s Data Protection Policy, which can be found on FAPL’s website at https://fa.com.sg/data-protection-policy/.

    根据《个人数据保护法》,鑫盟理财私人有限公司征求您的同意向您收集并使用您的个人信息。鑫盟理财将根据公司的个人数据保护政策所阐述的用途使用您的个人资料(例如姓名,证件号码,联系电话,邮寄地址,电邮地址和照片)。 该政策可在本公司网站上查寻,网址为 https://fa.com.sg/data-protection-policy/.

    By submitting this form, you are deemed to have read and understood FAPL’s Personal Data Policy.

    提交此表格,即表示您已阅读并理解鑫盟理财私人有限公司的个人数据政策

    Scan for Contact Details

    Join our newsletter

    Subscribe to our newsletter to receive updates on our latest content!

    By providing the info below, I confirm that I am the user and/or subscriber of the telephone number(s) and email address provided by me and I consent to receive from Financial Alliance and/or its financial adviser representatives, any marketing, advertising and promotional information organise by Financial Alliance via voice calls, SMS/MMS (text messages) or faxes to my telephone number(s) provided below. I understand I may withdraw any consent I have given at any time by writing in to Financial Alliance Pte Ltd.

    Scan for WeChat QR

    Estate Planning

    Who Gets What?
    If You Don’t Have a Will.

    Retirement Planning

    Will I be able to retire comfortably?