SaaS — Usage / Quota Meter
A compact, scannable usage and quota meter set for SaaS dashboards. Each metered resource (API calls, object storage, team seats, CI builds) shows used versus limit with a progress bar that shifts from brand to warn to danger as it nears the cap, plus a projected-overage note and a billing period reset countdown. A simulate-usage control ticks values upward, a smart upgrade prompt surfaces when thresholds are crossed, and a monthly/yearly toggle reframes every quota.
MCP
Code
:root {
--bg: #f7f8fb;
--surface: #ffffff;
--surface-2: #f1f3f9;
--ink: #0f1222;
--muted: #646b85;
--brand: #6366f1;
--brand-d: #4f46e5;
--ok: #16a34a;
--ok-bg: #e8f6ec;
--warn: #d97706;
--warn-bg: #fdf2e3;
--danger: #dc2626;
--danger-bg: #fdeaea;
--line: rgba(15, 18, 34, .1);
--shadow: 0 1px 2px rgba(15, 18, 34, .06), 0 8px 24px rgba(15, 18, 34, .06);
--radius: 14px;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2 { margin: 0; letter-spacing: -.01em; }
p { margin: 0; }
.page {
max-width: 880px;
margin: 0 auto;
padding: 22px 20px 64px;
}
/* ---------- Topbar ---------- */
.topbar {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
justify-content: space-between;
padding-bottom: 22px;
}
.brand { display: flex; align-items: center; gap: 12px; }
.logo {
width: 40px; height: 40px;
display: grid; place-items: center;
border-radius: 11px;
color: #fff;
background: linear-gradient(135deg, var(--brand), var(--brand-d));
box-shadow: 0 4px 12px rgba(99, 102, 241, .35);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.25; }
.brand-text strong { font-size: 16px; font-weight: 700; }
.brand-text span { font-size: 12.5px; color: var(--muted); }
.topbar-right { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.period {
display: inline-flex;
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: 10px;
padding: 3px;
}
.period-btn {
border: 0;
background: transparent;
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--muted);
padding: 7px 14px;
border-radius: 7px;
cursor: pointer;
transition: background .15s, color .15s, box-shadow .15s;
}
.period-btn.is-active {
background: var(--surface);
color: var(--ink);
box-shadow: 0 1px 2px rgba(15, 18, 34, .1);
}
.period-btn:not(.is-active):hover { color: var(--ink); }
/* ---------- Buttons ---------- */
.btn {
font: inherit;
font-size: 13.5px;
font-weight: 600;
border-radius: 10px;
padding: 9px 15px;
cursor: pointer;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
gap: 8px;
transition: transform .08s, background .15s, box-shadow .15s, border-color .15s;
}
.btn:active { transform: translateY(1px); }
.btn-primary {
background: linear-gradient(180deg, var(--brand), var(--brand-d));
color: #fff;
box-shadow: 0 1px 2px rgba(15, 18, 34, .12), 0 6px 16px rgba(99, 102, 241, .28);
}
.btn-primary:hover { box-shadow: 0 1px 2px rgba(15, 18, 34, .12), 0 8px 22px rgba(99, 102, 241, .4); }
.btn-ghost {
background: var(--surface);
color: var(--ink);
border-color: var(--line);
}
.btn-ghost:hover { background: var(--surface-2); }
.dot-pulse {
width: 8px; height: 8px;
border-radius: 50%;
background: #fff;
box-shadow: 0 0 0 0 rgba(255, 255, 255, .7);
animation: pulse 1.8s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 255, 255, .6); }
70% { box-shadow: 0 0 0 7px rgba(255, 255, 255, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
}
:focus-visible {
outline: 2px solid var(--brand);
outline-offset: 2px;
border-radius: 8px;
}
/* ---------- Summary ---------- */
.summary {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 20px 22px;
margin-bottom: 16px;
}
.summary-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 14px;
margin-bottom: 16px;
}
.summary h1 { font-size: 19px; font-weight: 700; }
.reset { font-size: 13px; color: var(--muted); margin-top: 4px; }
.reset strong { color: var(--ink); font-weight: 600; }
.health {
display: inline-flex;
align-items: center;
gap: 7px;
font-size: 12.5px;
font-weight: 600;
padding: 6px 12px;
border-radius: 999px;
white-space: nowrap;
}
.health-dot { width: 8px; height: 8px; border-radius: 50%; }
.health[data-state="ok"] { background: var(--ok-bg); color: var(--ok); }
.health[data-state="ok"] .health-dot { background: var(--ok); }
.health[data-state="warn"] { background: var(--warn-bg); color: var(--warn); }
.health[data-state="warn"] .health-dot { background: var(--warn); }
.health[data-state="danger"] { background: var(--danger-bg); color: var(--danger); }
.health[data-state="danger"] .health-dot { background: var(--danger); }
.summary-bar {
height: 10px;
background: var(--surface-2);
border-radius: 999px;
overflow: hidden;
}
.summary-bar-fill {
height: 100%;
width: 0;
border-radius: 999px;
background: linear-gradient(90deg, var(--brand), var(--brand-d));
transition: width .6s cubic-bezier(.4, 0, .2, 1), background .3s;
}
.summary-meta { font-size: 13px; color: var(--muted); margin-top: 11px; }
.summary-meta span { color: var(--ink); font-weight: 600; }
/* ---------- Meters grid ---------- */
.meters {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14px;
}
.meter {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 18px 18px 16px;
display: flex;
flex-direction: column;
gap: 12px;
transition: border-color .2s, box-shadow .2s;
}
.meter:hover { border-color: rgba(99, 102, 241, .35); }
.meter.is-danger { border-color: rgba(220, 38, 38, .4); }
.meter-head {
display: flex;
align-items: center;
gap: 10px;
}
.meter-ico {
width: 34px; height: 34px;
border-radius: 9px;
display: grid; place-items: center;
color: var(--brand-d);
background: rgba(99, 102, 241, .12);
flex: 0 0 auto;
}
.meter-title { font-size: 14px; font-weight: 600; }
.meter-sub { font-size: 11.5px; color: var(--muted); }
.meter-state {
margin-left: auto;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .04em;
padding: 3px 8px;
border-radius: 6px;
}
.meter-state[data-state="ok"] { background: var(--ok-bg); color: var(--ok); }
.meter-state[data-state="warn"] { background: var(--warn-bg); color: var(--warn); }
.meter-state[data-state="danger"] { background: var(--danger-bg); color: var(--danger); }
.meter-values {
display: flex;
align-items: baseline;
gap: 6px;
}
.meter-used { font-size: 22px; font-weight: 800; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.meter-limit { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.bar {
height: 9px;
background: var(--surface-2);
border-radius: 999px;
overflow: hidden;
position: relative;
}
.bar-fill {
height: 100%;
width: 0;
border-radius: 999px;
background: var(--brand);
transition: width .6s cubic-bezier(.4, 0, .2, 1), background .3s;
}
.bar-fill[data-state="ok"] { background: linear-gradient(90deg, var(--brand), var(--brand-d)); }
.bar-fill[data-state="warn"] { background: linear-gradient(90deg, #f59e0b, var(--warn)); }
.bar-fill[data-state="danger"] { background: linear-gradient(90deg, #ef4444, var(--danger)); }
.meter-foot {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
font-size: 12px;
}
.meter-pct { color: var(--muted); font-weight: 600; font-variant-numeric: tabular-nums; }
.meter-note {
text-align: right;
color: var(--muted);
}
.meter-note.is-over { color: var(--danger); font-weight: 600; }
.meter-note.is-near { color: var(--warn); font-weight: 600; }
/* ---------- Upsell ---------- */
.upsell {
margin-top: 16px;
background: linear-gradient(135deg, #fff, #f4f5ff);
border: 1px solid rgba(99, 102, 241, .3);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 18px 20px;
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
.upsell[hidden] { display: none; }
.upsell-icon {
width: 44px; height: 44px;
border-radius: 11px;
display: grid; place-items: center;
color: #fff;
background: linear-gradient(135deg, var(--brand), var(--brand-d));
flex: 0 0 auto;
}
.upsell-body { flex: 1 1 220px; }
.upsell-body h2 { font-size: 15px; font-weight: 700; }
.upsell-body p { font-size: 13px; color: var(--muted); margin-top: 3px; }
.upsell-actions { display: flex; gap: 9px; margin-left: auto; }
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 20px);
background: var(--ink);
color: #fff;
font-size: 13.5px;
font-weight: 500;
padding: 11px 18px;
border-radius: 11px;
box-shadow: 0 10px 30px rgba(15, 18, 34, .3);
opacity: 0;
pointer-events: none;
transition: opacity .25s, transform .25s;
z-index: 50;
}
.toast.is-show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- Responsive ---------- */
@media (max-width: 640px) {
.meters { grid-template-columns: 1fr; }
.topbar { gap: 12px; }
.topbar-right { width: 100%; justify-content: space-between; }
.summary-head { flex-direction: column; }
.health { align-self: flex-start; }
.upsell-actions { margin-left: 0; width: 100%; }
.upsell-actions .btn { flex: 1; justify-content: center; }
}
@media (max-width: 380px) {
.page { padding: 16px 14px 48px; }
.summary { padding: 16px; }
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 2600);
}
/* ---------- Icons (inline SVG markup) ---------- */
var ICONS = {
api: '<path d="M16 18l6-6-6-6M8 6l-6 6 6 6"/>',
storage: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v6c0 1.7 4 3 9 3s9-1.3 9-3V5M3 11v6c0 1.7 4 3 9 3s9-1.3 9-3v-6"/>',
seats: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>',
builds: '<path d="M14.7 6.3a4 4 0 0 0-5.6 5.6l-6.4 6.4a2 2 0 1 0 2.8 2.8l6.4-6.4a4 4 0 0 0 5.6-5.6l-2.5 2.5-2-2 2.5-2.5z"/>'
};
/* ---------- Data model ----------
Limits differ per billing period (yearly plans grant more headroom). */
var RESOURCES = [
{
id: "api", icon: "api", title: "API calls", unit: "",
sub: "REST + GraphQL requests",
used: 742000,
limit: { month: 1000000, year: 14000000 },
growth: 1.18,
fmt: "compact"
},
{
id: "storage", icon: "storage", title: "Object storage", unit: "GB",
sub: "Uploads & backups",
used: 84,
limit: { month: 100, year: 250 },
growth: 1.07,
fmt: "gb"
},
{
id: "seats", icon: "seats", title: "Team seats", unit: "",
sub: "Active members",
used: 18,
limit: { month: 20, year: 20 },
growth: 1.04,
fmt: "int"
},
{
id: "builds", icon: "builds", title: "CI builds", unit: "",
sub: "Pipeline minutes used",
used: 5200,
limit: { month: 6000, year: 80000 },
growth: 1.22,
fmt: "int"
}
];
var WARN_AT = 0.8;
var DANGER_AT = 0.95;
var state = { period: "month", dismissedUpsell: false };
/* ---------- Formatting ---------- */
function fmtValue(v, fmt) {
if (fmt === "gb") return Math.round(v) + " GB";
if (fmt === "compact") return compact(v);
return Math.round(v).toLocaleString("en-US");
}
function compact(v) {
if (v >= 1e6) return (v / 1e6).toFixed(v >= 1e7 ? 0 : 1).replace(/\.0$/, "") + "M";
if (v >= 1e3) return (v / 1e3).toFixed(v >= 1e4 ? 0 : 1).replace(/\.0$/, "") + "K";
return String(Math.round(v));
}
function stateFor(ratio) {
if (ratio >= DANGER_AT) return "danger";
if (ratio >= WARN_AT) return "warn";
return "ok";
}
/* ---------- Period reset countdown ---------- */
function nextReset() {
var now = new Date();
if (state.period === "year") {
return new Date(now.getFullYear() + 1, 0, 1);
}
return new Date(now.getFullYear(), now.getMonth() + 1, 1);
}
function updateCountdown() {
var ms = nextReset() - new Date();
var days = Math.floor(ms / 86400000);
var hrs = Math.floor((ms % 86400000) / 3600000);
var label;
if (days > 0) label = days + (days === 1 ? " day" : " days");
else label = hrs + (hrs === 1 ? " hour" : " hours");
document.getElementById("reset-countdown").textContent = label;
}
/* ---------- Render meters ---------- */
var metersEl = document.getElementById("meters");
function buildMeters() {
metersEl.innerHTML = "";
RESOURCES.forEach(function (r) {
var el = document.createElement("article");
el.className = "meter";
el.dataset.id = r.id;
el.innerHTML =
'<div class="meter-head">' +
'<span class="meter-ico" aria-hidden="true"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + ICONS[r.icon] + '</svg></span>' +
'<div><div class="meter-title">' + r.title + '</div><div class="meter-sub">' + r.sub + '</div></div>' +
'<span class="meter-state" data-state="ok">OK</span>' +
'</div>' +
'<div class="meter-values"><span class="meter-used">0</span><span class="meter-limit"></span></div>' +
'<div class="bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" aria-label="' + r.title + ' usage"><span class="bar-fill" data-state="ok"></span></div>' +
'<div class="meter-foot"><span class="meter-pct">0%</span><span class="meter-note"></span></div>';
metersEl.appendChild(el);
});
}
function renderMeter(r) {
var el = metersEl.querySelector('[data-id="' + r.id + '"]');
var limit = r.limit[state.period];
var ratio = r.used / limit;
var pct = Math.min(ratio * 100, 100);
var st = stateFor(ratio);
el.classList.toggle("is-danger", st === "danger");
el.querySelector(".meter-used").textContent = fmtValue(r.used, r.fmt);
el.querySelector(".meter-limit").textContent = "/ " + fmtValue(limit, r.fmt) + (r.unit && r.fmt !== "gb" ? " " + r.unit : "");
var fill = el.querySelector(".bar-fill");
fill.style.width = pct + "%";
fill.dataset.state = st;
var bar = el.querySelector(".bar");
bar.setAttribute("aria-valuenow", Math.round(pct));
var badge = el.querySelector(".meter-state");
badge.dataset.state = st;
badge.textContent = st === "danger" ? (ratio >= 1 ? "OVER" : "CRITICAL") : st === "warn" ? "NEAR CAP" : "OK";
el.querySelector(".meter-pct").textContent = Math.round(ratio * 100) + "% used";
/* Projected overage: extrapolate to end of period using growth factor */
var note = el.querySelector(".meter-note");
note.className = "meter-note";
var projected = r.used * r.growth;
if (r.used > limit) {
note.classList.add("is-over");
note.textContent = "Over by " + fmtValue(r.used - limit, r.fmt);
} else if (projected > limit) {
note.classList.add("is-near");
note.textContent = "Projected " + fmtValue(projected, r.fmt) + " — overage likely";
} else {
note.textContent = fmtValue(limit - r.used, r.fmt) + " left";
}
return { ratio: ratio, st: st };
}
/* ---------- Summary + health ---------- */
function renderAll() {
var totalUsed = 0, totalLimit = 0, worst = "ok", overCount = 0, nearCount = 0;
RESOURCES.forEach(function (r) {
var res = renderMeter(r);
var limit = r.limit[state.period];
totalUsed += Math.min(r.used, limit);
totalLimit += limit;
if (res.st === "danger") { worst = "danger"; overCount++; }
else if (res.st === "warn") { if (worst !== "danger") worst = "warn"; nearCount++; }
});
var aggPct = Math.round((totalUsed / totalLimit) * 100);
document.getElementById("summary-fill").style.width = aggPct + "%";
document.getElementById("summary-pct").textContent = aggPct + "%";
document.getElementById("meter-count").textContent = RESOURCES.length;
var pill = document.getElementById("health-pill");
var label = document.getElementById("health-label");
pill.dataset.state = worst;
if (worst === "danger") label.textContent = overCount + (overCount === 1 ? " resource over limit" : " resources over limit");
else if (worst === "warn") label.textContent = nearCount + (nearCount === 1 ? " resource near cap" : " resources near cap");
else label.textContent = "All within limits";
updateUpsell(worst, overCount, nearCount);
updateCountdown();
}
/* ---------- Upsell prompt ---------- */
var upsell = document.getElementById("upsell");
function updateUpsell(worst, overCount, nearCount) {
if (state.dismissedUpsell || worst === "ok") {
upsell.hidden = true;
return;
}
upsell.hidden = false;
var title = document.getElementById("upsell-title");
var text = document.getElementById("upsell-text");
if (worst === "danger") {
title.textContent = "Limit reached on " + overCount + (overCount === 1 ? " resource" : " resources");
text.textContent = "Overage rates apply once you exceed your plan. Upgrade now to keep usage uninterrupted.";
} else {
title.textContent = "You're approaching a limit";
text.textContent = nearCount + (nearCount === 1 ? " resource is" : " resources are") + " near the cap. Upgrade to add headroom before the cycle resets.";
}
}
/* ---------- Simulate usage ---------- */
var simBtn = document.getElementById("simulate");
var simRunning = false;
function simulate() {
if (simRunning) return;
simRunning = true;
simBtn.disabled = true;
state.dismissedUpsell = false;
var ticks = 0;
var iv = setInterval(function () {
ticks++;
RESOURCES.forEach(function (r) {
var limit = r.limit[state.period];
/* bump by a small random fraction of the limit; seats grow slower */
var step = (r.id === "seats" ? 0.02 : 0.04 + Math.random() * 0.06) * limit * Math.random();
if (r.id === "seats") step = Math.random() < 0.4 ? 1 : 0;
r.used = Math.min(r.used + step, limit * 1.12);
if (r.id === "seats") r.used = Math.round(r.used);
});
renderAll();
if (ticks >= 6) {
clearInterval(iv);
simRunning = false;
simBtn.disabled = false;
toast("Simulated 6 usage events");
}
}, 320);
}
simBtn.addEventListener("click", simulate);
/* ---------- Period toggle ---------- */
var periodBtns = document.querySelectorAll(".period-btn");
periodBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
if (btn.classList.contains("is-active")) return;
periodBtns.forEach(function (b) {
b.classList.remove("is-active");
b.setAttribute("aria-pressed", "false");
});
btn.classList.add("is-active");
btn.setAttribute("aria-pressed", "true");
state.period = btn.dataset.period;
renderAll();
toast(state.period === "year" ? "Showing yearly quotas" : "Showing monthly quotas");
});
});
/* ---------- Upsell actions ---------- */
document.getElementById("upsell-dismiss").addEventListener("click", function () {
state.dismissedUpsell = true;
upsell.hidden = true;
toast("Reminder dismissed for this session");
});
document.getElementById("upsell-cta").addEventListener("click", function () {
toast("Opening upgrade flow… (demo)");
});
/* ---------- Init ---------- */
buildMeters();
renderAll();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Usage / Quota Meter</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>
<div class="page">
<header class="topbar" role="banner">
<div class="brand">
<span class="logo" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 12h4l3 8 4-16 3 8h4" />
</svg>
</span>
<div class="brand-text">
<strong>Meterly</strong>
<span>Workspace · Growth plan</span>
</div>
</div>
<div class="topbar-right">
<div class="period" role="group" aria-label="Billing period">
<button type="button" class="period-btn is-active" data-period="month" aria-pressed="true">Monthly</button>
<button type="button" class="period-btn" data-period="year" aria-pressed="false">Yearly</button>
</div>
<button type="button" id="simulate" class="btn btn-primary">
<span class="dot-pulse" aria-hidden="true"></span> Simulate usage
</button>
</div>
</header>
<main class="content" role="main">
<section class="summary" aria-label="Usage overview">
<div class="summary-head">
<div>
<h1>Usage this period</h1>
<p class="reset" id="reset-note">Resets in <strong id="reset-countdown">—</strong> · billing cycle</p>
</div>
<div class="health" id="health-pill" data-state="ok">
<span class="health-dot" aria-hidden="true"></span>
<span id="health-label">All within limits</span>
</div>
</div>
<div class="summary-bar" aria-hidden="true">
<div class="summary-bar-fill" id="summary-fill"></div>
</div>
<p class="summary-meta">
<span id="summary-pct">0%</span> of total plan capacity in use across
<span id="meter-count">4</span> metered resources.
</p>
</section>
<section class="meters" id="meters" aria-label="Per-resource meters"></section>
<aside class="upsell" id="upsell" hidden aria-live="polite">
<div class="upsell-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2 4 7v6c0 5 3.5 7.7 8 9 4.5-1.3 8-4 8-9V7l-8-5Z" />
<path d="m9 12 2 2 4-4" />
</svg>
</div>
<div class="upsell-body">
<h2 id="upsell-title">You're close to a limit</h2>
<p id="upsell-text">One or more resources are near their cap. Upgrade to avoid overage charges next cycle.</p>
</div>
<div class="upsell-actions">
<button type="button" class="btn btn-ghost" id="upsell-dismiss">Not now</button>
<button type="button" class="btn btn-primary" id="upsell-cta">Upgrade plan</button>
</div>
</aside>
</main>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Usage / Quota Meter
A trustworthy, at-a-glance panel for showing how much of a plan a workspace has consumed. Four metered resources — API calls, object storage, team seats, and CI builds — each render a value, a used/limit pair, and a colored progress bar that turns amber near the cap and red once usage is critical or over. A per-meter note does the honest math: how much headroom is left, a projected-overage warning when growth would blow past the limit, or how far over the cap you already are.
The header rolls everything up into an aggregate capacity bar, a health pill (all clear / near cap / over limit), and a reset countdown that recalculates against the next billing boundary. Click Simulate usage to watch values tick upward over a few events; as a resource crosses its warn or danger threshold, the bars recolor, the health pill updates, and a contextual upgrade prompt slides in. Toggle Monthly / Yearly to reframe every quota against the larger annual allowance.
Everything is vanilla HTML, CSS, and JavaScript — no chart libraries, no images — with keyboard-focusable controls, progressbar roles with live aria-valuenow, a polite toast, and a layout that collapses from a two-up grid to a single column on small screens.
Illustrative SaaS UI only — fictional product, metrics, and billing. No real backend.