Clinic — Billing & Claims Dashboard
A revenue-cycle dashboard for clinic administrators, leading with KPI cards for billed, collected, outstanding and denial rate plus small trend deltas, a pure-CSS bar chart of the last six months of collections, and an A/R aging bar split across 0-30, 31-60, 61-90 and 90-plus buckets. A claims table carries status pills, status filter tabs with live counts, a search box and a one-tap resubmit action on denied rows.
MCP
代码
:root {
--teal: #129c93;
--teal-d: #0c7a73;
--teal-700: #0a655f;
--teal-50: #e7f5f3;
--coral: #ff7a66;
--coral-soft: #ffe6df;
--ink: #16322f;
--ink-2: #3a534f;
--muted: #6b827e;
--bg: #f1f7f6;
--white: #ffffff;
--line: rgba(16, 50, 47, 0.1);
--line-2: rgba(16, 50, 47, 0.18);
--ok: #2f9e6f;
--warn: #d98a2b;
--pending: #6b7280;
--danger: #d4503e;
--font: "Inter", system-ui, -apple-system, sans-serif;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--shadow-1: 0 1px 2px rgba(16, 50, 47, 0.05), 0 4px 14px rgba(16, 50, 47, 0.06);
--shadow-2: 0 16px 40px rgba(12, 122, 115, 0.16);
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font);
background: var(--bg);
color: var(--ink);
-webkit-font-smoothing: antialiased;
line-height: 1.5;
}
:focus-visible {
outline: 2px solid var(--teal);
outline-offset: 2px;
border-radius: 6px;
}
.vh {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
/* ── Layout ── */
.billing {
max-width: 1040px;
margin: 0 auto;
padding: 36px 24px 64px;
display: flex;
flex-direction: column;
gap: 22px;
}
/* ── Header ── */
.bill-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 18px;
flex-wrap: wrap;
}
.eyebrow {
font-size: 0.76rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--teal-d);
}
.bill-head h1 {
font-size: 1.85rem;
font-weight: 800;
letter-spacing: -0.025em;
margin-top: 4px;
}
.bill-head .sub {
color: var(--muted);
font-size: 0.92rem;
margin-top: 4px;
}
.period {
display: inline-flex;
align-items: center;
gap: 8px;
background: var(--white);
border: 1px solid var(--line);
border-radius: 999px;
padding: 8px 14px;
font-size: 0.82rem;
font-weight: 600;
color: var(--ink-2);
box-shadow: var(--shadow-1);
}
.period .dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--ok);
box-shadow: 0 0 0 3px rgba(47, 158, 111, 0.18);
}
/* ── KPI cards ── */
.kpis {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.kpi {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 18px 18px 16px;
box-shadow: var(--shadow-1);
position: relative;
overflow: hidden;
transition: transform 0.15s, box-shadow 0.15s;
}
.kpi::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 3px;
background: var(--teal);
opacity: 0.85;
}
.kpi:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-2);
}
.kpi-label {
font-size: 0.78rem;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.kpi-value {
font-size: 1.6rem;
font-weight: 800;
letter-spacing: -0.03em;
margin-top: 8px;
color: var(--ink);
}
.kpi-delta {
display: inline-flex;
align-items: center;
gap: 5px;
margin-top: 10px;
font-size: 0.8rem;
font-weight: 700;
padding: 3px 9px;
border-radius: 999px;
}
.kpi-delta.up {
background: rgba(47, 158, 111, 0.12);
color: var(--ok);
}
.kpi-delta.down {
background: rgba(212, 80, 62, 0.1);
color: var(--danger);
}
.kpi-delta.up.bad {
background: rgba(217, 138, 43, 0.16);
color: var(--warn);
}
.kpi-delta .arrow {
font-size: 0.62rem;
}
.kpi-note {
font-weight: 500;
color: var(--muted);
margin-left: 2px;
}
/* ── Panels ── */
.panel {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-1);
padding: 22px;
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 18px;
}
.panel h2 {
font-size: 1.04rem;
font-weight: 700;
letter-spacing: -0.01em;
}
.panel-tag {
font-size: 0.76rem;
font-weight: 700;
color: var(--teal-d);
background: var(--teal-50);
padding: 4px 10px;
border-radius: 999px;
}
.grid {
display: grid;
grid-template-columns: 1.5fr 1fr;
gap: 16px;
}
/* ── Chart ── */
.chart {
display: flex;
align-items: flex-end;
gap: 14px;
height: 200px;
padding-top: 8px;
}
.bar {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
height: 100%;
position: relative;
}
.bar-fill {
width: 100%;
max-width: 46px;
height: var(--h);
border-radius: 8px 8px 4px 4px;
background: linear-gradient(180deg, var(--teal), var(--teal-d));
transition: filter 0.15s, transform 0.15s;
transform-origin: bottom;
animation: grow 0.6s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.bar.is-peak .bar-fill {
background: linear-gradient(180deg, #18b3a8, var(--teal-700));
box-shadow: 0 6px 16px rgba(12, 122, 115, 0.28);
}
.bar:hover .bar-fill {
filter: brightness(1.06);
transform: scaleY(1.015);
}
@keyframes grow {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
.bar-val {
font-size: 0.72rem;
font-weight: 700;
color: var(--ink-2);
margin-bottom: 7px;
opacity: 0;
transition: opacity 0.15s;
}
.bar:hover .bar-val,
.bar.is-peak .bar-val {
opacity: 1;
}
.bar.is-peak .bar-val {
color: var(--teal-700);
}
.bar-month {
font-size: 0.74rem;
font-weight: 600;
color: var(--muted);
margin-top: 9px;
}
.bar.is-peak .bar-month {
color: var(--teal-d);
font-weight: 700;
}
/* ── Aging ── */
.aging-bar {
display: flex;
height: 16px;
border-radius: 999px;
overflow: hidden;
background: var(--bg);
gap: 2px;
}
.seg {
width: var(--w);
height: 100%;
}
.seg.s0 {
background: var(--teal);
}
.seg.s1 {
background: #5cc2b9;
}
.seg.s2 {
background: var(--warn);
}
.seg.s3 {
background: var(--danger);
}
.aging-legend {
list-style: none;
margin-top: 18px;
display: flex;
flex-direction: column;
gap: 12px;
}
.aging-legend li {
display: flex;
align-items: center;
gap: 9px;
font-size: 0.85rem;
color: var(--ink-2);
}
.aging-legend strong {
margin-left: auto;
font-weight: 700;
color: var(--ink);
}
.swatch {
width: 11px;
height: 11px;
border-radius: 3px;
flex-shrink: 0;
}
.swatch.s0 {
background: var(--teal);
}
.swatch.s1 {
background: #5cc2b9;
}
.swatch.s2 {
background: var(--warn);
}
.swatch.s3 {
background: var(--danger);
}
/* ── Claims ── */
.claims-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.search {
display: inline-flex;
align-items: center;
gap: 8px;
background: var(--bg);
border: 1px solid var(--line);
border-radius: 999px;
padding: 9px 16px;
min-width: 280px;
transition: border-color 0.15s, background 0.15s;
}
.search:focus-within {
border-color: var(--teal);
background: var(--white);
}
.search-ico {
color: var(--muted);
font-size: 1.05rem;
}
.search input {
border: none;
background: transparent;
font: inherit;
font-size: 0.88rem;
color: var(--ink);
width: 100%;
outline: none;
}
.search input::placeholder {
color: var(--muted);
}
.filters {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.filter {
border: 1px solid var(--line);
background: var(--white);
border-radius: 999px;
padding: 7px 14px;
font: inherit;
font-weight: 600;
font-size: 0.84rem;
color: var(--ink-2);
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 7px;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.filter:hover {
border-color: var(--teal);
color: var(--teal-d);
}
.filter.is-active {
background: var(--teal-d);
border-color: var(--teal-d);
color: #fff;
}
.fcount {
display: inline-grid;
place-items: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 999px;
background: var(--teal-50);
color: var(--teal-d);
font-size: 0.7rem;
font-weight: 700;
}
.filter.is-active .fcount {
background: rgba(255, 255, 255, 0.22);
color: #fff;
}
.table-wrap {
overflow-x: auto;
border: 1px solid var(--line);
border-radius: var(--r-md);
}
table.claims {
width: 100%;
border-collapse: collapse;
font-size: 0.88rem;
min-width: 680px;
}
table.claims thead th {
text-align: left;
font-size: 0.73rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--muted);
padding: 12px 16px;
background: #f7fbfa;
border-bottom: 1px solid var(--line);
white-space: nowrap;
}
table.claims th.num,
table.claims td.num {
text-align: right;
}
table.claims tbody td {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
color: var(--ink-2);
vertical-align: middle;
}
table.claims tbody tr:last-child td {
border-bottom: none;
}
table.claims tbody tr {
transition: background 0.12s;
}
table.claims tbody tr:hover {
background: var(--teal-50);
}
table.claims tbody tr[data-status="denied"]:hover {
background: rgba(212, 80, 62, 0.06);
}
td.mono {
font-variant-numeric: tabular-nums;
font-weight: 600;
color: var(--ink);
letter-spacing: -0.01em;
}
td.num {
font-variant-numeric: tabular-nums;
font-weight: 600;
color: var(--ink);
}
td.date {
color: var(--muted);
white-space: nowrap;
}
/* ── Status pills ── */
.pill {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.74rem;
font-weight: 700;
padding: 4px 10px;
border-radius: 999px;
white-space: nowrap;
}
.pill::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.pill.paid {
background: rgba(47, 158, 111, 0.14);
color: var(--ok);
}
.pill.submitted {
background: var(--teal-50);
color: var(--teal-d);
}
.pill.pending {
background: rgba(217, 138, 43, 0.16);
color: var(--warn);
}
.pill.denied {
background: rgba(212, 80, 62, 0.14);
color: var(--danger);
}
/* ── Resubmit action ── */
td.act {
text-align: right;
white-space: nowrap;
}
.btn-resubmit {
border: 1px solid rgba(212, 80, 62, 0.4);
background: var(--white);
color: var(--danger);
border-radius: 8px;
padding: 6px 12px;
font: inherit;
font-weight: 600;
font-size: 0.8rem;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, transform 0.12s;
}
.btn-resubmit:hover {
background: rgba(212, 80, 62, 0.08);
border-color: var(--danger);
}
.btn-resubmit:active {
transform: translateY(1px);
}
.btn-resubmit:disabled {
opacity: 0.55;
cursor: default;
}
.empty {
padding: 32px 16px;
text-align: center;
color: var(--muted);
font-size: 0.9rem;
font-weight: 500;
}
.claims-foot {
margin-top: 14px;
font-size: 0.8rem;
color: var(--muted);
font-weight: 500;
}
/* ── Toast ── */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translateX(-50%);
background: var(--ink);
color: #fff;
padding: 13px 20px;
border-radius: 12px;
font-size: 0.9rem;
font-weight: 500;
box-shadow: var(--shadow-2);
z-index: 50;
max-width: 90vw;
}
/* ── Responsive ── */
@media (max-width: 860px) {
.kpis {
grid-template-columns: repeat(2, 1fr);
}
.grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 520px) {
.billing {
padding: 24px 14px 48px;
}
.bill-head h1 {
font-size: 1.5rem;
}
.kpis {
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.kpi-value {
font-size: 1.35rem;
}
.panel {
padding: 18px 16px;
}
.chart {
gap: 8px;
height: 170px;
}
.search {
min-width: 0;
width: 100%;
}
.claims-head {
flex-direction: column;
align-items: stretch;
}
}// ── Toast ──────────────────────────────────────────────────────────────────
const toast = document.getElementById("toast");
function showToast(msg) {
toast.textContent = msg;
toast.hidden = false;
clearTimeout(showToast._t);
showToast._t = setTimeout(() => (toast.hidden = true), 2600);
}
// ── Elements ─────────────────────────────────────────────────────────────────
const body = document.getElementById("claims-body");
const rows = Array.from(body.querySelectorAll("tr"));
const filters = Array.from(document.querySelectorAll(".filter"));
const searchInput = document.getElementById("search");
const emptyMsg = document.getElementById("empty");
const resultCount = document.getElementById("result-count");
let activeFilter = "all";
// ── Live counts per status ───────────────────────────────────────────────────
function refreshCounts() {
const counts = { all: rows.length, paid: 0, submitted: 0, pending: 0, denied: 0 };
rows.forEach((r) => {
counts[r.dataset.status] = (counts[r.dataset.status] || 0) + 1;
});
document.querySelectorAll(".fcount").forEach((el) => {
el.textContent = counts[el.dataset.count] ?? 0;
});
}
// ── Apply filter + search ────────────────────────────────────────────────────
function applyView() {
const q = searchInput.value.trim().toLowerCase();
let shown = 0;
rows.forEach((r) => {
const matchesFilter = activeFilter === "all" || r.dataset.status === activeFilter;
const matchesSearch = !q || r.textContent.toLowerCase().includes(q);
const visible = matchesFilter && matchesSearch;
r.hidden = !visible;
if (visible) shown++;
});
emptyMsg.hidden = shown !== 0;
resultCount.textContent = `Showing ${shown} of ${rows.length} claims`;
}
// ── Filter tabs ──────────────────────────────────────────────────────────────
filters.forEach((f) => {
f.addEventListener("click", () => {
filters.forEach((other) => {
const active = other === f;
other.classList.toggle("is-active", active);
other.setAttribute("aria-selected", String(active));
});
activeFilter = f.dataset.filter;
applyView();
});
});
// ── Search ───────────────────────────────────────────────────────────────────
searchInput.addEventListener("input", applyView);
// ── Resubmit denied claims ───────────────────────────────────────────────────
body.addEventListener("click", (e) => {
const btn = e.target.closest('[data-action="resubmit"]');
if (!btn) return;
const row = btn.closest("tr");
const claim = row.querySelector(".mono").textContent;
const patient = row.children[1].textContent;
// Move the claim back into the Submitted queue.
row.dataset.status = "submitted";
const pill = row.querySelector(".pill");
pill.className = "pill submitted";
pill.textContent = "Submitted";
btn.remove();
refreshCounts();
applyView();
showToast(`${claim} resubmitted to payer · ${patient}`);
});
// ── Init ─────────────────────────────────────────────────────────────────────
refreshCounts();
applyView();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
/>
<link rel="stylesheet" href="style.css" />
<title>Billing & Claims · Northpoint Clinic</title>
</head>
<body>
<main class="billing">
<header class="bill-head">
<div class="bill-head-left">
<p class="eyebrow">Northpoint Clinic · Revenue cycle</p>
<h1>Billing & claims</h1>
<p class="sub">A calm overview of what was billed, collected and what is still in flight.</p>
</div>
<div class="bill-head-right">
<span class="period">
<span class="dot" aria-hidden="true"></span>
May 2026 · updated 4 min ago
</span>
</div>
</header>
<!-- ── KPI cards ── -->
<section class="kpis" aria-label="Key billing metrics">
<article class="kpi">
<p class="kpi-label">Billed</p>
<p class="kpi-value">$248,930</p>
<p class="kpi-delta up">
<span class="arrow" aria-hidden="true">▲</span> 6.2%
<span class="kpi-note">vs. Apr</span>
</p>
</article>
<article class="kpi">
<p class="kpi-label">Collected</p>
<p class="kpi-value">$201,475</p>
<p class="kpi-delta up">
<span class="arrow" aria-hidden="true">▲</span> 4.8%
<span class="kpi-note">vs. Apr</span>
</p>
</article>
<article class="kpi">
<p class="kpi-label">Outstanding</p>
<p class="kpi-value">$47,455</p>
<p class="kpi-delta down">
<span class="arrow" aria-hidden="true">▼</span> 2.1%
<span class="kpi-note">vs. Apr</span>
</p>
</article>
<article class="kpi">
<p class="kpi-label">Denial rate</p>
<p class="kpi-value">5.4%</p>
<p class="kpi-delta up bad">
<span class="arrow" aria-hidden="true">▲</span> 0.7 pts
<span class="kpi-note">vs. Apr</span>
</p>
</article>
</section>
<div class="grid">
<!-- ── Monthly collections chart ── -->
<section class="panel chart-panel" aria-label="Monthly collections">
<div class="panel-head">
<h2>Monthly collections</h2>
<span class="panel-tag">Last 6 months</span>
</div>
<div class="chart" role="img" aria-label="Bar chart of monthly collections from December to May, peaking in May at 201 thousand dollars.">
<div class="bar" style="--h: 62%">
<span class="bar-val">$156k</span>
<span class="bar-fill"></span>
<span class="bar-month">Dec</span>
</div>
<div class="bar" style="--h: 70%">
<span class="bar-val">$172k</span>
<span class="bar-fill"></span>
<span class="bar-month">Jan</span>
</div>
<div class="bar" style="--h: 66%">
<span class="bar-val">$164k</span>
<span class="bar-fill"></span>
<span class="bar-month">Feb</span>
</div>
<div class="bar" style="--h: 79%">
<span class="bar-val">$188k</span>
<span class="bar-fill"></span>
<span class="bar-month">Mar</span>
</div>
<div class="bar" style="--h: 76%">
<span class="bar-val">$181k</span>
<span class="bar-fill"></span>
<span class="bar-month">Apr</span>
</div>
<div class="bar is-peak" style="--h: 88%">
<span class="bar-val">$201k</span>
<span class="bar-fill"></span>
<span class="bar-month">May</span>
</div>
</div>
</section>
<!-- ── Aging summary ── -->
<section class="panel aging-panel" aria-label="Receivables aging">
<div class="panel-head">
<h2>A/R aging</h2>
<span class="panel-tag">$47,455 open</span>
</div>
<div class="aging-bar" role="img" aria-label="Aging breakdown: 58 percent current, 24 percent 31 to 60 days, 12 percent 61 to 90 days, 6 percent over 90 days.">
<span class="seg s0" style="--w: 58%"></span>
<span class="seg s1" style="--w: 24%"></span>
<span class="seg s2" style="--w: 12%"></span>
<span class="seg s3" style="--w: 6%"></span>
</div>
<ul class="aging-legend">
<li><span class="swatch s0"></span> 0–30 days <strong>$27,524</strong></li>
<li><span class="swatch s1"></span> 31–60 days <strong>$11,389</strong></li>
<li><span class="swatch s2"></span> 61–90 days <strong>$5,695</strong></li>
<li><span class="swatch s3"></span> 90+ days <strong>$2,847</strong></li>
</ul>
</section>
</div>
<!-- ── Claims table ── -->
<section class="panel claims-panel" aria-label="Claims">
<div class="claims-head">
<h2>Claims</h2>
<label class="search">
<span class="search-ico" aria-hidden="true">⌕</span>
<input
type="search"
id="search"
placeholder="Search claim #, patient or payer…"
aria-label="Search claims"
/>
</label>
</div>
<div class="filters" role="tablist" aria-label="Filter by status">
<button class="filter is-active" role="tab" aria-selected="true" data-filter="all">
All <span class="fcount" data-count="all">0</span>
</button>
<button class="filter" role="tab" aria-selected="false" data-filter="paid">
Paid <span class="fcount" data-count="paid">0</span>
</button>
<button class="filter" role="tab" aria-selected="false" data-filter="submitted">
Submitted <span class="fcount" data-count="submitted">0</span>
</button>
<button class="filter" role="tab" aria-selected="false" data-filter="pending">
Pending <span class="fcount" data-count="pending">0</span>
</button>
<button class="filter" role="tab" aria-selected="false" data-filter="denied">
Denied <span class="fcount" data-count="denied">0</span>
</button>
</div>
<div class="table-wrap">
<table class="claims">
<thead>
<tr>
<th scope="col">Claim #</th>
<th scope="col">Patient</th>
<th scope="col">Payer</th>
<th scope="col" class="num">Amount</th>
<th scope="col">Date</th>
<th scope="col">Status</th>
<th scope="col"><span class="vh">Actions</span></th>
</tr>
</thead>
<tbody id="claims-body">
<tr data-status="paid">
<td class="mono">CLM-48201</td>
<td>Amara Whitfield</td>
<td>BlueShield PPO</td>
<td class="num">$1,240.00</td>
<td class="date">May 28</td>
<td><span class="pill paid">Paid</span></td>
<td class="act"></td>
</tr>
<tr data-status="denied">
<td class="mono">CLM-48197</td>
<td>Theo Marchetti</td>
<td>Aetna HMO</td>
<td class="num">$865.50</td>
<td class="date">May 27</td>
<td><span class="pill denied">Denied</span></td>
<td class="act">
<button class="btn-resubmit" data-action="resubmit">Resubmit</button>
</td>
</tr>
<tr data-status="submitted">
<td class="mono">CLM-48190</td>
<td>Priya Nandakumar</td>
<td>UnitedCare</td>
<td class="num">$2,310.75</td>
<td class="date">May 26</td>
<td><span class="pill submitted">Submitted</span></td>
<td class="act"></td>
</tr>
<tr data-status="pending">
<td class="mono">CLM-48186</td>
<td>Desmond Acheampong</td>
<td>Medicare</td>
<td class="num">$540.00</td>
<td class="date">May 26</td>
<td><span class="pill pending">Pending</span></td>
<td class="act"></td>
</tr>
<tr data-status="paid">
<td class="mono">CLM-48179</td>
<td>Helena Vargas</td>
<td>Cigna PPO</td>
<td class="num">$1,985.20</td>
<td class="date">May 25</td>
<td><span class="pill paid">Paid</span></td>
<td class="act"></td>
</tr>
<tr data-status="denied">
<td class="mono">CLM-48172</td>
<td>Jonas Eriksson</td>
<td>BlueShield PPO</td>
<td class="num">$3,120.00</td>
<td class="date">May 24</td>
<td><span class="pill denied">Denied</span></td>
<td class="act">
<button class="btn-resubmit" data-action="resubmit">Resubmit</button>
</td>
</tr>
<tr data-status="submitted">
<td class="mono">CLM-48168</td>
<td>Mei-Ling Cho</td>
<td>Aetna HMO</td>
<td class="num">$760.40</td>
<td class="date">May 24</td>
<td><span class="pill submitted">Submitted</span></td>
<td class="act"></td>
</tr>
<tr data-status="paid">
<td class="mono">CLM-48160</td>
<td>Rafael Ortega</td>
<td>Medicaid</td>
<td class="num">$415.00</td>
<td class="date">May 23</td>
<td><span class="pill paid">Paid</span></td>
<td class="act"></td>
</tr>
<tr data-status="pending">
<td class="mono">CLM-48155</td>
<td>Saoirse Donnelly</td>
<td>UnitedCare</td>
<td class="num">$1,090.65</td>
<td class="date">May 22</td>
<td><span class="pill pending">Pending</span></td>
<td class="act"></td>
</tr>
<tr data-status="submitted">
<td class="mono">CLM-48149</td>
<td>Idris Bello</td>
<td>Cigna PPO</td>
<td class="num">$2,675.00</td>
<td class="date">May 21</td>
<td><span class="pill submitted">Submitted</span></td>
<td class="act"></td>
</tr>
</tbody>
</table>
<p class="empty" id="empty" hidden>No claims match your filters.</p>
</div>
<footer class="claims-foot">
<span id="result-count">Showing 10 of 10 claims</span>
</footer>
</section>
</main>
<div class="toast" id="toast" hidden role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Billing & Claims Dashboard
A calm, single-screen view of a clinic’s revenue cycle. Four KPI cards open the page with the month’s Billed, Collected, Outstanding and Denial rate figures, each carrying a small trend delta against the prior month. Below them, a pure-CSS bar chart traces the last six months of collections — the most recent month rendered as a highlighted peak — sitting beside an accounts-receivable aging bar broken into 0–30, 31–60, 61–90 and 90+ day buckets with a labelled legend.
The claims table is the working surface. Status filter tabs (All, Paid, Submitted, Pending, Denied) each show a live count, and a search box narrows rows instantly by claim number, patient or payer. Every row carries a colour-coded status pill and a soft hover; denied claims expose a Resubmit action that moves the claim back into the submitted queue, updates the pill, refreshes the tab counts and confirms with a toast. A running result count keeps the footer honest as filters change.
Everything is vanilla JS and self-contained — no frameworks, no build step, and no external assets beyond the Inter font. Copy and figures are realistic but fictional, and the layout collapses cleanly down to narrow phones.
Illustrative UI only — not intended for real medical use.