Insurance — Quote Calculator
An interactive insurance quote estimator where you pick a coverage type, enter age, ZIP and coverage amount, then drag a deductible slider to watch the estimated monthly and annual premium recalculate live alongside a transparent line-item breakdown.
MCP
Code
:root {
--blue: #2563eb;
--blue-d: #1d4ed8;
--blue-50: #eef4ff;
--green: #15a06b;
--green-50: #e6f6ef;
--ink: #0f1a2b;
--muted: #5a6b82;
--bg: #f3f7fc;
--white: #ffffff;
--line: #e2eaf4;
--radius: 18px;
--shadow: 0 10px 30px rgba(15, 26, 43, 0.08);
--shadow-sm: 0 2px 8px rgba(15, 26, 43, 0.06);
--font: "Inter", system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font);
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
display: flex;
justify-content: center;
padding: 32px 16px;
}
.quote {
width: 100%;
max-width: 880px;
background: var(--white);
border: 1px solid var(--line);
border-radius: 26px;
box-shadow: var(--shadow);
overflow: hidden;
}
/* ---------- header ---------- */
.quote__head {
padding: 26px 28px 22px;
background: linear-gradient(135deg, var(--blue-50), #fbfdff);
border-bottom: 1px solid var(--line);
}
.quote__brand { display: flex; align-items: center; gap: 14px; }
.quote__shield {
font-size: 26px;
width: 48px; height: 48px;
display: grid; place-items: center;
background: var(--white);
border: 1px solid var(--line);
border-radius: 14px;
box-shadow: var(--shadow-sm);
}
.quote__eyebrow {
font-size: 12px; font-weight: 600; letter-spacing: .06em;
text-transform: uppercase; color: var(--blue-d);
}
.quote__head h1 { font-size: 22px; font-weight: 800; letter-spacing: -.01em; }
.quote__sub { margin-top: 10px; color: var(--muted); font-size: 14px; }
/* ---------- grid ---------- */
.quote__grid {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 0;
}
/* ---------- form ---------- */
.form { padding: 26px 28px; display: flex; flex-direction: column; gap: 22px; }
.field { display: flex; flex-direction: column; gap: 8px; border: 0; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
legend, label, .field > label {
font-size: 13px; font-weight: 600; color: var(--ink);
}
input[type="number"], input[type="text"] {
font: inherit; font-weight: 500;
width: 100%;
padding: 12px 14px;
border: 1.5px solid var(--line);
border-radius: 12px;
background: var(--white);
color: var(--ink);
transition: border-color .15s, box-shadow .15s;
}
input[type="number"]:focus, input[type="text"]:focus {
outline: none;
border-color: var(--blue);
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.14);
}
.amount { position: relative; }
.amount__cur {
position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
color: var(--muted); font-weight: 600;
}
.amount input { padding-left: 28px; }
/* segmented coverage */
.seg {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
}
.seg__opt { position: relative; cursor: pointer; }
.seg__opt input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.seg__face {
display: flex; flex-direction: column; align-items: center; gap: 6px;
padding: 12px 4px;
border: 1.5px solid var(--line);
border-radius: 14px;
font-size: 13px; font-weight: 600; color: var(--muted);
transition: all .15s;
}
.seg__ico { font-size: 20px; }
.seg__opt input:hover + .seg__face { border-color: #c8d8f0; }
.seg__opt input:checked + .seg__face {
border-color: var(--blue);
background: var(--blue-50);
color: var(--blue-d);
}
.seg__opt input:focus-visible + .seg__face {
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.18);
}
/* quick amount chips */
.quick { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.quick__btn {
font: inherit; font-size: 12px; font-weight: 600;
padding: 6px 12px; border-radius: 999px;
border: 1.5px solid var(--line); background: var(--white);
color: var(--muted); cursor: pointer; transition: all .15s;
}
.quick__btn:hover { border-color: #c8d8f0; color: var(--blue-d); }
.quick__btn.is-active { background: var(--blue); border-color: var(--blue); color: #fff; }
.quick__btn:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.18); }
/* slider */
.slider__top { display: flex; justify-content: space-between; align-items: baseline; }
.slider__val { font-size: 16px; font-weight: 800; color: var(--blue-d); font-variant-numeric: tabular-nums; }
input[type="range"] {
-webkit-appearance: none; appearance: none;
width: 100%; height: 8px; border-radius: 999px;
background: linear-gradient(90deg, var(--blue) var(--fill, 30%), var(--line) var(--fill, 30%));
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 22px; height: 22px; border-radius: 50%;
background: var(--white); border: 3px solid var(--blue);
box-shadow: var(--shadow-sm); cursor: grab;
}
input[type="range"]::-moz-range-thumb {
width: 22px; height: 22px; border-radius: 50%;
background: var(--white); border: 3px solid var(--blue);
box-shadow: var(--shadow-sm); cursor: grab;
}
input[type="range"]:focus-visible { outline: none; }
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.22); }
.slider__scale { display: flex; justify-content: space-between; font-size: 11px; color: var(--muted); margin-top: 4px; }
.slider__hint { font-size: 12px; color: var(--muted); }
/* toggle */
.switch { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch__track {
width: 44px; height: 26px; border-radius: 999px;
background: var(--line); position: relative; transition: background .18s; flex: 0 0 auto;
}
.switch__dot {
position: absolute; top: 3px; left: 3px;
width: 20px; height: 20px; border-radius: 50%;
background: #fff; box-shadow: var(--shadow-sm); transition: transform .18s;
}
.switch input:checked + .switch__track { background: var(--green); }
.switch input:checked + .switch__track .switch__dot { transform: translateX(18px); }
.switch input:focus-visible + .switch__track { box-shadow: 0 0 0 4px rgba(21, 160, 107, 0.22); }
.switch__label { font-size: 13px; font-weight: 500; color: var(--ink); }
.switch__label em { color: var(--green); font-style: normal; font-weight: 700; }
/* ---------- estimate panel ---------- */
.estimate {
padding: 28px 26px;
background: linear-gradient(165deg, #0f1a2b, #16335f);
color: #fff;
display: flex; flex-direction: column; gap: 8px;
}
.estimate__label { font-size: 13px; color: #aebfd9; font-weight: 500; }
.estimate__price {
font-size: 52px; font-weight: 800; letter-spacing: -.02em; line-height: 1;
font-variant-numeric: tabular-nums; margin: 2px 0;
}
.estimate__per { font-size: 13px; color: #c4d2ec; }
.estimate__per span { color: #fff; font-weight: 600; }
.estimate__pills { display: flex; gap: 8px; flex-wrap: wrap; margin: 14px 0 4px; }
.pill {
font-size: 12px; font-weight: 600; padding: 5px 11px;
border-radius: 999px; background: rgba(255,255,255,0.12); color: #eaf1ff;
}
.pill--save { background: rgba(21, 160, 107, 0.22); color: #8ef0c5; }
.break__title { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: #92a6ca; margin-top: 14px; }
.break { list-style: none; display: flex; flex-direction: column; }
.break__row {
display: flex; justify-content: space-between; align-items: center;
padding: 9px 0; font-size: 13.5px; color: #d6e0f3;
border-bottom: 1px solid rgba(255,255,255,0.08);
}
.break__num { font-weight: 600; color: #fff; font-variant-numeric: tabular-nums; }
.break__num--neg { color: #8ef0c5; }
.break__row--total {
border-bottom: 0; margin-top: 4px; padding-top: 12px;
border-top: 1.5px solid rgba(255,255,255,0.2);
font-size: 15px; font-weight: 700; color: #fff;
}
.break__row--total .break__num { font-size: 18px; }
.cta {
margin-top: 16px;
font: inherit; font-weight: 700; font-size: 15px;
padding: 14px; border: 0; border-radius: 14px;
background: var(--green); color: #fff; cursor: pointer;
transition: background .15s, transform .08s;
}
.cta:hover { background: #128a5c; }
.cta:active { transform: translateY(1px); }
.cta:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(21, 160, 107, 0.4); }
.cta.is-locked { background: rgba(255,255,255,0.18); cursor: default; }
.estimate__fine { font-size: 11px; color: #8ea4c8; margin-top: 8px; }
.estimate__fine span { color: #c4d2ec; font-weight: 600; }
/* ---------- responsive ---------- */
@media (max-width: 720px) {
.quote__grid { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
body { padding: 16px 10px; }
.quote { border-radius: 20px; }
.quote__head { padding: 20px 18px 18px; }
.form, .estimate { padding: 20px 18px; }
.field-row { grid-template-columns: 1fr; }
.seg { grid-template-columns: repeat(2, 1fr); }
.estimate__price { font-size: 44px; }
}(function () {
"use strict";
var form = document.getElementById("quoteForm");
if (!form) return;
// ---- elements ----
var ageEl = document.getElementById("age");
var zipEl = document.getElementById("zip");
var amountEl = document.getElementById("amount");
var dedEl = document.getElementById("deductible");
var bundleEl = document.getElementById("bundle");
var quickBtns = Array.prototype.slice.call(document.querySelectorAll(".quick__btn"));
var monthlyEl = document.getElementById("monthly");
var annualEl = document.getElementById("annual");
var dedOut = document.getElementById("deductibleOut");
var dedTier = document.getElementById("deductibleTier");
var zipEcho = document.getElementById("zipEcho");
var approvalPill = document.getElementById("approvalPill");
var savePill = document.getElementById("savePill");
var quoteIdEl = document.getElementById("quoteId");
var lockBtn = document.getElementById("lockBtn");
// base monthly rate per coverage type (USD)
var BASE = { auto: 96, home: 84, health: 168, life: 42 };
// sensitivity of premium to coverage amount, per type
var COV_RATE = { auto: 0.00018, home: 0.00009, health: 0.00012, life: 0.00006 };
var fmt = function (n) {
return "$" + Math.round(n).toLocaleString("en-US");
};
// deterministic location factor derived from ZIP digits
function locationFactor(zip) {
var digits = (zip || "").replace(/\D/g, "");
if (digits.length < 5) return { mult: 1, score: 0 };
var sum = 0;
for (var i = 0; i < digits.length; i++) sum += parseInt(digits[i], 10);
// map 0..45 -> roughly 0.88..1.18
var mult = 0.88 + (sum / 45) * 0.3;
return { mult: mult, score: sum };
}
function ageFactor(age) {
age = parseInt(age, 10) || 34;
if (age < 25) return 1.34;
if (age < 35) return 1.0;
if (age < 50) return 0.94;
if (age < 65) return 1.08;
return 1.42;
}
function getCoverageType() {
var checked = form.querySelector('input[name="coverage"]:checked');
return checked ? checked.value : "auto";
}
function setBreakdown(k, val) {
var el = document.querySelector('.break__num[data-k="' + k + '"]');
if (el) el.textContent = val;
}
function recalc() {
var type = getCoverageType();
var age = parseInt(ageEl.value, 10) || 34;
var amount = Math.max(0, parseInt(amountEl.value, 10) || 0);
var ded = parseInt(dedEl.value, 10) || 1000;
var bundle = bundleEl.checked;
var base = BASE[type];
var aFactor = ageFactor(age);
var loc = locationFactor(zipEl.value);
// component contributions (all monthly)
var ageAdj = base * (aFactor - 1);
var locAdj = base * (loc.mult - 1);
var covAdj = amount * COV_RATE[type];
var subtotal = base + ageAdj + locAdj + covAdj;
// deductible discount: higher deductible -> bigger discount (up to ~28%)
var dedPct = ((ded - 250) / (5000 - 250)) * 0.28;
var dedDiscount = subtotal * dedPct;
var afterDed = subtotal - dedDiscount;
var bundleDiscount = bundle ? afterDed * 0.12 : 0;
var monthly = Math.max(8, afterDed - bundleDiscount);
var annual = monthly * 12;
// ---- render headline ----
monthlyEl.textContent = fmt(monthly);
annualEl.textContent = fmt(annual);
// ---- breakdown ----
setBreakdown("base", fmt(base));
setBreakdown("age", (ageAdj >= 0 ? "+" : "−") + fmt(Math.abs(ageAdj)));
setBreakdown("loc", (locAdj >= 0 ? "+" : "−") + fmt(Math.abs(locAdj)));
setBreakdown("cov", "+" + fmt(covAdj));
setBreakdown("ded", "−" + fmt(dedDiscount));
setBreakdown("total", fmt(monthly) + "/mo");
zipEcho.textContent = (zipEl.value || "").replace(/\D/g, "") || "—";
var bundleRow = document.querySelector("[data-bundle]");
if (bundle) {
bundleRow.hidden = false;
setBreakdown("bundle", "−" + fmt(bundleDiscount));
} else {
bundleRow.hidden = true;
}
// ---- deductible UI ----
dedOut.textContent = fmt(ded);
var fillPct = ((ded - dedEl.min) / (dedEl.max - dedEl.min)) * 100;
dedEl.style.setProperty("--fill", fillPct + "%");
if (ded <= 500) {
dedTier.textContent = "Low tier — higher premium, less out-of-pocket if you claim.";
} else if (ded <= 2000) {
dedTier.textContent = "Standard tier — balanced premium and out-of-pocket.";
} else {
dedTier.textContent = "High tier — lowest premium, more out-of-pocket if you claim.";
}
// ---- approval likelihood (heuristic) ----
var approvalScore = 100 - Math.abs(age - 38) * 0.6 - (covAdj * 0.4) + (ded > 1500 ? 6 : 0);
var label, cls;
if (approvalScore >= 78) { label = "Very likely approval"; cls = "pill--save"; }
else if (approvalScore >= 55) { label = "Likely approval"; cls = ""; }
else { label = "Manual review likely"; cls = ""; }
approvalPill.textContent = label;
approvalPill.className = "pill " + cls;
// ---- savings pill (vs lowest-deductible, no-bundle baseline) ----
var baselineMonthly = Math.max(8, subtotal); // ded=250-ish, no bundle
var saving = (baselineMonthly - monthly) * 12;
savePill.textContent = saving > 0 ? "Saving " + fmt(saving) + "/yr" : "Best coverage";
// reset any locked state on change
if (lockBtn.classList.contains("is-locked")) {
lockBtn.classList.remove("is-locked");
lockBtn.textContent = "Lock this rate for 30 days";
}
}
// ---- quick amount chips ----
function syncQuick() {
quickBtns.forEach(function (b) {
b.classList.toggle("is-active", parseInt(b.dataset.amount, 10) === parseInt(amountEl.value, 10));
});
}
quickBtns.forEach(function (b) {
b.addEventListener("click", function () {
amountEl.value = b.dataset.amount;
syncQuick();
recalc();
});
});
// ---- random but stable-looking quote id ----
function makeQuoteId() {
var n = Math.floor(100000 + Math.random() * 899999);
quoteIdEl.textContent = "NB-" + n;
}
// ---- lock CTA ----
lockBtn.addEventListener("click", function () {
if (lockBtn.classList.contains("is-locked")) return;
lockBtn.classList.add("is-locked");
lockBtn.textContent = "✓ Rate locked · " + quoteIdEl.textContent;
});
// ---- wire up ----
form.addEventListener("input", function () { syncQuick(); recalc(); });
form.addEventListener("change", recalc);
// init
makeQuoteId();
syncQuick();
recalc();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Insurance — Quote Calculator</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="quote" aria-labelledby="quote-title">
<header class="quote__head">
<div class="quote__brand">
<span class="quote__shield" aria-hidden="true">🛡️</span>
<div>
<p class="quote__eyebrow">Northbridge Insurance</p>
<h1 id="quote-title">Get your instant quote</h1>
</div>
</div>
<p class="quote__sub">Estimate in under a minute. No account, no obligation.</p>
</header>
<div class="quote__grid">
<!-- ---------- FORM ---------- -->
<form class="form" id="quoteForm" novalidate>
<fieldset class="field">
<legend>Coverage type</legend>
<div class="seg" role="radiogroup" aria-label="Coverage type">
<label class="seg__opt">
<input type="radio" name="coverage" value="auto" checked />
<span class="seg__face"><span class="seg__ico">🚗</span>Auto</span>
</label>
<label class="seg__opt">
<input type="radio" name="coverage" value="home" />
<span class="seg__face"><span class="seg__ico">🏠</span>Home</span>
</label>
<label class="seg__opt">
<input type="radio" name="coverage" value="health" />
<span class="seg__face"><span class="seg__ico">➕</span>Health</span>
</label>
<label class="seg__opt">
<input type="radio" name="coverage" value="life" />
<span class="seg__face"><span class="seg__ico">🌳</span>Life</span>
</label>
</div>
</fieldset>
<div class="field-row">
<div class="field">
<label for="age">Your age</label>
<input type="number" id="age" name="age" min="18" max="85" value="34" inputmode="numeric" />
</div>
<div class="field">
<label for="zip">ZIP code</label>
<input type="text" id="zip" name="zip" maxlength="5" value="80203" inputmode="numeric" autocomplete="postal-code" />
</div>
</div>
<div class="field">
<label for="amount">Coverage amount</label>
<div class="amount">
<span class="amount__cur">$</span>
<input type="number" id="amount" name="amount" min="10000" max="2000000" step="5000" value="250000" inputmode="numeric" />
</div>
<div class="quick" role="group" aria-label="Quick coverage amounts">
<button type="button" class="quick__btn" data-amount="100000">$100k</button>
<button type="button" class="quick__btn" data-amount="250000">$250k</button>
<button type="button" class="quick__btn" data-amount="500000">$500k</button>
<button type="button" class="quick__btn" data-amount="1000000">$1M</button>
</div>
</div>
<div class="field">
<div class="slider__top">
<label for="deductible">Annual deductible</label>
<output class="slider__val" id="deductibleOut" for="deductible">$1,000</output>
</div>
<input type="range" id="deductible" name="deductible" min="250" max="5000" step="250" value="1000" />
<div class="slider__scale" aria-hidden="true">
<span>$250</span><span>Lower premium →</span><span>$5,000</span>
</div>
<p class="slider__hint" id="deductibleTier">Standard tier — balanced premium and out-of-pocket.</p>
</div>
<div class="field field--toggle">
<label class="switch">
<input type="checkbox" id="bundle" name="bundle" />
<span class="switch__track" aria-hidden="true"><span class="switch__dot"></span></span>
<span class="switch__label">Bundle with an existing policy <em>save 12%</em></span>
</label>
</div>
</form>
<!-- ---------- ESTIMATE ---------- -->
<aside class="estimate" aria-label="Your estimate">
<p class="estimate__label">Estimated monthly premium</p>
<p class="estimate__price" id="monthly" aria-live="polite">$0</p>
<p class="estimate__per">per month · <span id="annual">$0</span> / year</p>
<div class="estimate__pills">
<span class="pill" id="approvalPill">Likely approval</span>
<span class="pill pill--save" id="savePill">Saving $0/yr</span>
</div>
<h2 class="break__title">How we got there</h2>
<ul class="break" id="breakdown" aria-live="polite">
<li class="break__row"><span>Base rate</span><span class="break__num" data-k="base">$0</span></li>
<li class="break__row"><span>Age factor</span><span class="break__num" data-k="age">$0</span></li>
<li class="break__row"><span>Location (<span id="zipEcho">80203</span>)</span><span class="break__num" data-k="loc">$0</span></li>
<li class="break__row"><span>Coverage adjustment</span><span class="break__num" data-k="cov">$0</span></li>
<li class="break__row"><span>Deductible discount</span><span class="break__num break__num--neg" data-k="ded">$0</span></li>
<li class="break__row break__row--bundle" data-bundle hidden><span>Bundle discount</span><span class="break__num break__num--neg" data-k="bundle">$0</span></li>
<li class="break__row break__row--total"><span>Monthly total</span><span class="break__num" data-k="total">$0</span></li>
</ul>
<button type="button" class="cta" id="lockBtn">Lock this rate for 30 days</button>
<p class="estimate__fine">Quote ID <span id="quoteId">NB-000000</span> · valid 30 days. Not a binding offer.</p>
</aside>
</div>
</main>
<script src="script.js"></script>
</body>
</html>Quote Calculator
A reassuring, conversion-focused insurance quote widget. Choose a coverage type (Auto, Home, Health or Life), enter the policyholder’s age, ZIP code and desired coverage amount, then drag the deductible slider to explore the classic premium-vs-deductible tradeoff. A big confident monthly number anchors the panel, with the equivalent annual figure and projected savings shown beside it.
Every input recalculates instantly. A clear breakdown lists the base rate, age and location factors, the coverage adjustment and the deductible discount, so the final number never feels like a black box. Status pills surface the deductible tier and an estimated approval likelihood.
The pricing model is deterministic, dependency-free vanilla JS — easy to swap for a real rating
API. Markup uses real labels, focus styles and aria-live so the live total is announced to
assistive tech as it changes.
Illustrative UI only — not a real insurance quote or binding offer.