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;
}
}
An Interactive Deep-Dive into Integrated Shield Plans, Riders, and Cancer 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.
Universal basic coverage pegged to B2/C ward bills in public hospitals.
Enhances coverage for A/B1 wards or private hospitals.
Covers the deductible and co-insurance from the main plan.
Compulsory Savings
Out-of-Pocket
These are the funding sources for your premiums and out-of-pocket costs.
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).
Let’s see what happens if you rely solely on MediShield Life for a major surgery at a private hospital.
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.
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.
Now, let’s add a Private Hospital ISP to the S$100,000 bill.
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.
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.
Completing our S$100,000 bill scenario with a rider.
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.
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.
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.
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.
Clinically-proven, cost-effective treatments covered by your main ISP, up to a multiple of the MediShield Life limit. Riders enhance this coverage.
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.
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.
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.
The ideal position is the bottom-left corner (lower cost, higher benefits).
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.
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.
Total Main Plan Premium
S$0
S$0 payable by MediSave
Total Rider Premium (Cash)
S$0
Total Cost (Cash Portion)
S$0
Total Annual Premium: S$0
This chart shows the total estimated premiums for your family, from each member’s current age up to age 85.
// — 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 = `
${data.premiumModelDescription}
`;
let ridersHtml = ‘
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 += `
`;
categories[cat].forEach(rider => {
let featuresHtml = (rider.keyFeatures || []).map(feature => `
`).join(”);
ridersHtml += `
${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
` : ”}
`;
});
}
});
ridersHtml += ‘
‘;
insurerContentContainer.innerHTML = `
`;
}
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 = `
`;
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 += `
`;
// 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 += `
`;
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.
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
By submitting this form, I confirm that
提交此表格,即表示
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.
提交此表格,即表示您已阅读并理解鑫盟理财私人有限公司的个人数据政策
Subscribe to our newsletter to receive updates on our latest content!