Banking — Statements
A trust-first fintech statements and documents page where customers browse monthly account statements with period, closing balance and status, alongside a dedicated tax documents section. Filter every list by year, search across titles, download any file with a simulated progress toast, and open a polished preview modal that renders a realistic statement or tax certificate. Built with semantic HTML, Inter typography, tabular money figures and a fully responsive layout.
MCP
Code
:root {
--navy: #0e1b3a;
--navy-2: #16264d;
--ink: #0e1726;
--ink-2: #3a4660;
--muted: #697089;
--accent: #3b6ef6;
--accent-d: #2a55cc;
--accent-50: #eaf0ff;
--teal: #0fb5a6;
--violet: #7c5cff;
--bg: #f5f7fb;
--surface: #ffffff;
--line: rgba(14, 27, 58, 0.10);
--line-2: rgba(14, 27, 58, 0.18);
--ok: #1f9d62;
--warn: #d9982b;
--danger: #d4493e;
--credit: #1f9d62;
--debit: #0e1726;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--sh-sm: 0 1px 2px rgba(14, 27, 58, 0.06);
--sh-md: 0 6px 18px rgba(14, 27, 58, 0.08), 0 1px 3px rgba(14, 27, 58, 0.06);
--sh-lg: 0 24px 60px rgba(14, 27, 58, 0.22);
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
}
body {
font-family: "Inter", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.tnum { font-variant-numeric: tabular-nums; }
/* ---------- Top bar ---------- */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 22px;
background: linear-gradient(120deg, var(--navy), var(--navy-2));
color: #fff;
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
width: 38px; height: 38px;
display: grid; place-items: center;
border-radius: 11px;
background: linear-gradient(135deg, var(--accent), var(--violet));
font-weight: 800; font-size: 18px;
box-shadow: var(--sh-sm);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.2; }
.brand-text strong { font-size: 15px; font-weight: 700; }
.brand-text span { font-size: 12px; color: rgba(255, 255, 255, 0.66); }
.topbar-right { display: flex; align-items: center; gap: 14px; }
.secure-pill {
display: inline-flex; align-items: center; gap: 6px;
font-size: 12px; font-weight: 600;
padding: 6px 11px;
border-radius: 999px;
background: rgba(15, 181, 166, 0.18);
color: #8ff0e6;
border: 1px solid rgba(15, 181, 166, 0.35);
}
.avatar {
width: 36px; height: 36px;
display: grid; place-items: center;
border-radius: 50%;
background: rgba(255, 255, 255, 0.14);
font-size: 13px; font-weight: 700;
border: 1px solid rgba(255, 255, 255, 0.2);
}
/* ---------- Layout ---------- */
.layout {
max-width: 940px;
margin: 0 auto;
padding: 28px 22px 56px;
display: flex;
flex-direction: column;
gap: 20px;
}
.hero {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
flex-wrap: wrap;
}
.hero h1 { margin: 0 0 6px; font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }
.hero-sub { margin: 0; color: var(--muted); font-size: 14px; max-width: 460px; }
.account-chip {
display: flex; align-items: center; gap: 11px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 11px 15px;
box-shadow: var(--sh-sm);
}
.account-chip .dot {
width: 10px; height: 10px; border-radius: 50%;
background: var(--ok);
box-shadow: 0 0 0 4px rgba(31, 157, 98, 0.14);
}
.acc-label { display: block; font-size: 13px; font-weight: 700; }
.acc-num { display: block; font-size: 12px; color: var(--muted); }
/* ---------- Toolbar ---------- */
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
}
.years { display: inline-flex; gap: 6px; background: var(--surface); padding: 5px; border-radius: 999px; border: 1px solid var(--line); box-shadow: var(--sh-sm); }
.year-tab {
border: none; background: transparent;
font: inherit; font-size: 13px; font-weight: 600;
color: var(--ink-2);
padding: 7px 15px;
border-radius: 999px;
cursor: pointer;
transition: background .16s, color .16s;
}
.year-tab:hover { background: var(--accent-50); color: var(--accent-d); }
.year-tab.is-active { background: var(--accent); color: #fff; box-shadow: var(--sh-sm); }
.year-tab:focus-visible { outline: 2px solid var(--accent-d); outline-offset: 2px; }
.search-wrap {
display: flex; align-items: center; gap: 8px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 999px;
padding: 0 14px;
color: var(--muted);
min-width: 230px;
flex: 1;
max-width: 320px;
box-shadow: var(--sh-sm);
}
.search-wrap input {
border: none; background: transparent; outline: none;
font: inherit; font-size: 14px; color: var(--ink);
padding: 10px 0; width: 100%;
}
/* ---------- Cards ---------- */
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-md);
overflow: hidden;
}
.card-head {
display: flex; align-items: center; justify-content: space-between;
padding: 18px 20px 14px;
}
.card-head h2 { margin: 0; font-size: 16px; font-weight: 700; }
.count { font-size: 12px; color: var(--muted); font-weight: 600; }
/* ---------- Table ---------- */
.table { display: flex; flex-direction: column; }
.thead, .row {
display: grid;
grid-template-columns: 1.6fr 1fr 0.9fr 1fr;
align-items: center;
gap: 12px;
padding: 12px 20px;
}
.thead {
font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
color: var(--muted); font-weight: 700;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
background: #fafbfe;
}
.ta-r { text-align: right; }
.row {
border-bottom: 1px solid var(--line);
transition: background .14s;
}
.row:last-child { border-bottom: none; }
.row:hover { background: var(--accent-50); }
.period { display: flex; align-items: center; gap: 12px; }
.doc-ico {
width: 36px; height: 36px; flex-shrink: 0;
display: grid; place-items: center;
border-radius: 10px;
background: var(--accent-50);
color: var(--accent-d);
}
.period-text strong { display: block; font-size: 14px; font-weight: 600; }
.period-text span { font-size: 12px; color: var(--muted); }
.balance { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--debit); }
.pill {
display: inline-flex; align-items: center; gap: 5px;
font-size: 11.5px; 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.ready { background: rgba(31, 157, 98, 0.12); color: var(--ok); }
.pill.pending { background: rgba(217, 152, 43, 0.14); color: var(--warn); }
.row-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* ---------- Buttons ---------- */
.btn {
font: inherit; font-size: 13px; font-weight: 600;
border-radius: var(--r-sm);
padding: 8px 13px;
cursor: pointer;
border: 1px solid transparent;
transition: background .15s, transform .08s, border-color .15s, box-shadow .15s;
display: inline-flex; align-items: center; gap: 6px;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--accent-d); outline-offset: 2px; }
.btn-ghost {
background: var(--surface); color: var(--ink-2);
border-color: var(--line-2);
}
.btn-ghost:hover { background: #fff; border-color: var(--accent); color: var(--accent-d); box-shadow: var(--sh-sm); }
.btn-primary {
background: var(--accent); color: #fff;
}
.btn-primary:hover { background: var(--accent-d); box-shadow: var(--sh-sm); }
.btn-primary:disabled { opacity: .6; cursor: progress; }
.icon-btn {
border: none; background: transparent;
font-size: 16px; color: var(--muted);
width: 32px; height: 32px; border-radius: 8px;
cursor: pointer; transition: background .14s, color .14s;
}
.icon-btn:hover { background: var(--line); color: var(--ink); }
/* ---------- Tax grid ---------- */
.tax-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 14px;
padding: 4px 20px 22px;
}
.tax-card {
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
transition: border-color .15s, box-shadow .15s, transform .12s;
background: #fff;
}
.tax-card:hover { border-color: var(--accent); box-shadow: var(--sh-md); transform: translateY(-2px); }
.tax-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.tax-badge {
font-size: 10.5px; font-weight: 800; letter-spacing: .04em;
padding: 4px 8px; border-radius: 6px;
}
.tax-badge.tax { background: rgba(124, 92, 255, 0.12); color: var(--violet); }
.tax-badge.interest { background: rgba(15, 181, 166, 0.14); color: var(--teal); }
.tax-year { font-size: 12px; color: var(--muted); font-weight: 700; }
.tax-card h4 { margin: 0; font-size: 14px; font-weight: 700; }
.tax-card p { margin: 0; font-size: 12px; color: var(--muted); }
.tax-foot { display: flex; gap: 8px; margin-top: auto; }
.tax-foot .btn { flex: 1; justify-content: center; }
/* ---------- Empty ---------- */
.empty { padding: 34px 20px; text-align: center; color: var(--muted); font-size: 14px; }
/* ---------- Modal ---------- */
.modal-overlay {
position: fixed; inset: 0; z-index: 50;
background: rgba(14, 23, 38, 0.55);
backdrop-filter: blur(4px);
display: grid; place-items: center;
padding: 20px;
animation: fade .18s ease;
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.modal {
background: var(--surface);
width: 100%; max-width: 520px;
max-height: 90vh;
border-radius: var(--r-lg);
box-shadow: var(--sh-lg);
display: flex; flex-direction: column;
overflow: hidden;
animation: pop .2s cubic-bezier(.2, .9, .3, 1.2);
}
@keyframes pop { from { transform: translateY(12px) scale(.98); opacity: 0; } to { transform: none; opacity: 1; } }
.modal-head {
display: flex; align-items: flex-start; justify-content: space-between;
padding: 18px 20px; border-bottom: 1px solid var(--line);
}
.modal-head h3 { margin: 0; font-size: 16px; font-weight: 700; }
.modal-meta { margin: 3px 0 0; font-size: 12px; color: var(--muted); }
.doc-paper {
margin: 18px; padding: 22px;
border: 1px solid var(--line);
border-radius: var(--r-md);
background: #fff;
overflow: auto;
font-size: 13px;
box-shadow: inset 0 0 0 1px rgba(14, 27, 58, 0.02);
}
.doc-paper .dp-head { display: flex; justify-content: space-between; align-items: flex-start; padding-bottom: 14px; border-bottom: 2px solid var(--navy); margin-bottom: 14px; }
.doc-paper .dp-bank { font-weight: 800; font-size: 17px; color: var(--navy); }
.doc-paper .dp-bank span { display: block; font-size: 11px; color: var(--muted); font-weight: 600; }
.doc-paper .dp-meta { text-align: right; font-size: 11px; color: var(--muted); }
.doc-paper .dp-row { display: flex; justify-content: space-between; padding: 7px 0; border-bottom: 1px dashed var(--line); font-variant-numeric: tabular-nums; }
.doc-paper .dp-row:last-child { border-bottom: none; }
.doc-paper .dp-row .credit { color: var(--credit); font-weight: 700; }
.doc-paper .dp-row .debit { color: var(--debit); font-weight: 600; }
.doc-paper .dp-total { display: flex; justify-content: space-between; margin-top: 14px; padding-top: 12px; border-top: 2px solid var(--navy); font-weight: 800; font-variant-numeric: tabular-nums; }
.modal-foot {
display: flex; align-items: center; justify-content: space-between;
padding: 14px 20px; border-top: 1px solid var(--line);
gap: 12px;
}
.secure-note { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--ok); }
/* ---------- Toast ---------- */
.toast-host {
position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%);
z-index: 80;
display: flex; flex-direction: column; gap: 10px; align-items: center;
}
.toast {
background: var(--navy); color: #fff;
font-size: 13px; font-weight: 600;
padding: 11px 16px; border-radius: 999px;
box-shadow: var(--sh-lg);
display: flex; align-items: center; gap: 9px;
animation: toastIn .25s ease;
}
.toast .tk { color: var(--teal); display: inline-flex; }
@keyframes toastIn { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }
.toast.out { animation: toastOut .25s ease forwards; }
@keyframes toastOut { to { transform: translateY(10px); opacity: 0; } }
/* ---------- Responsive ---------- */
@media (max-width: 520px) {
.layout { padding: 18px 14px 44px; }
.hero h1 { font-size: 22px; }
.account-chip { width: 100%; }
.toolbar { flex-direction: column; align-items: stretch; }
.years { width: 100%; justify-content: space-between; overflow-x: auto; }
.search-wrap { max-width: none; }
.thead { display: none; }
.row {
grid-template-columns: 1fr auto;
grid-template-areas: "period bal" "status actions";
row-gap: 10px;
}
.period { grid-area: period; }
.balance { grid-area: bal; text-align: right; align-self: center; }
.row .pill { grid-area: status; justify-self: start; }
.row-actions { grid-area: actions; }
.tax-grid { grid-template-columns: 1fr; }
.modal-foot { flex-direction: column-reverse; align-items: stretch; }
.modal-foot .btn { justify-content: center; }
}(function () {
"use strict";
var GBP = new Intl.NumberFormat("en-GB", { style: "currency", currency: "GBP" });
var statements = [
{ id: "st-2026-05", year: 2026, period: "May 2026", range: "1 May – 31 May 2026", balance: 8421.55, status: "ready", opening: 7290.12, credits: 4310.00, debits: 3178.57 },
{ id: "st-2026-04", year: 2026, period: "Apr 2026", range: "1 Apr – 30 Apr 2026", balance: 7290.12, status: "ready", opening: 6655.88, credits: 3980.50, debits: 3346.26 },
{ id: "st-2026-03", year: 2026, period: "Mar 2026", range: "1 Mar – 31 Mar 2026", balance: 6655.88, status: "ready", opening: 7102.40, credits: 3540.00, debits: 3986.52 },
{ id: "st-2026-06", year: 2026, period: "Jun 2026", range: "1 Jun – 16 Jun 2026", balance: 9012.34, status: "pending", opening: 8421.55, credits: 2100.00, debits: 1509.21 },
{ id: "st-2025-12", year: 2025, period: "Dec 2025", range: "1 Dec – 31 Dec 2025", balance: 7102.40, status: "ready", opening: 5980.10, credits: 5120.00, debits: 3997.70 },
{ id: "st-2025-11", year: 2025, period: "Nov 2025", range: "1 Nov – 30 Nov 2025", balance: 5980.10, status: "ready", opening: 6240.33, credits: 3410.00, debits: 3670.23 },
{ id: "st-2025-10", year: 2025, period: "Oct 2025", range: "1 Oct – 31 Oct 2025", balance: 6240.33, status: "ready", opening: 5710.00, credits: 3890.00, debits: 3359.67 },
{ id: "st-2025-09", year: 2025, period: "Sep 2025", range: "1 Sep – 30 Sep 2025", balance: 5710.00, status: "ready", opening: 5455.20, credits: 3600.00, debits: 3345.20 },
{ id: "st-2024-12", year: 2024, period: "Dec 2024", range: "1 Dec – 31 Dec 2024", balance: 5455.20, status: "ready", opening: 4980.00, credits: 4720.00, debits: 4244.80 },
{ id: "st-2024-11", year: 2024, period: "Nov 2024", range: "1 Nov – 30 Nov 2024", balance: 4980.00, status: "ready", opening: 5012.45, credits: 3210.00, debits: 3242.45 },
{ id: "st-2024-10", year: 2024, period: "Oct 2024", range: "1 Oct – 31 Oct 2024", balance: 5012.45, status: "ready", opening: 4630.10, credits: 3700.00, debits: 3317.65 },
{ id: "st-2024-09", year: 2024, period: "Sep 2024", range: "1 Sep – 30 Sep 2024", balance: 4630.10, status: "ready", opening: 4401.00, credits: 3120.00, debits: 2890.90 }
];
var taxDocs = [
{ id: "tx-2026-int", year: 2026, kind: "interest", title: "Interest summary 2025/26", note: "Gross interest earned on savings", file: "interest-summary-2026.pdf" },
{ id: "tx-2025-cert", year: 2025, kind: "tax", title: "Tax certificate 2024/25", note: "HMRC self-assessment ready", file: "tax-certificate-2025.pdf" },
{ id: "tx-2025-int", year: 2025, kind: "interest", title: "Interest summary 2024/25", note: "Gross interest earned on savings", file: "interest-summary-2025.pdf" },
{ id: "tx-2024-cert", year: 2024, kind: "tax", title: "Tax certificate 2023/24", note: "HMRC self-assessment ready", file: "tax-certificate-2024.pdf" }
];
var DOC_ICON = '<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6Zm0 2 4 4h-4V4ZM8 13h8v2H8v-2Zm0 4h8v2H8v-2Zm0-8h4v2H8V9Z"/></svg>';
var DL_ICON = '<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path fill="currentColor" d="M12 3v10.6l3.3-3.3 1.4 1.4-5.7 5.7-5.7-5.7 1.4-1.4 3.3 3.3V3h2ZM5 19h14v2H5v-2Z"/></svg>';
var state = { year: "all", query: "" };
var stmtList = document.getElementById("stmtList");
var stmtEmpty = document.getElementById("stmtEmpty");
var stmtCount = document.getElementById("stmtCount");
var taxGrid = document.getElementById("taxGrid");
var taxEmpty = document.getElementById("taxEmpty");
var taxCount = document.getElementById("taxCount");
// ---------- Toast ----------
var toastHost = document.getElementById("toastHost");
function toast(msg) {
var el = document.createElement("div");
el.className = "toast";
el.innerHTML = '<span class="tk"><svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path fill="currentColor" d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2Z"/></svg></span>' +
'<span></span>';
el.querySelector("span:last-child").textContent = msg;
toastHost.appendChild(el);
setTimeout(function () {
el.classList.add("out");
setTimeout(function () { el.remove(); }, 260);
}, 2600);
}
// ---------- Filtering ----------
function matches(d, hay) {
if (state.year !== "all" && d.year !== Number(state.year)) return false;
if (state.query && hay.toLowerCase().indexOf(state.query) === -1) return false;
return true;
}
function renderStatements() {
var rows = statements.filter(function (s) { return matches(s, s.period + " " + s.range); });
rows.sort(function (a, b) { return a.id < b.id ? 1 : -1; });
stmtList.innerHTML = "";
rows.forEach(function (s) {
var row = document.createElement("div");
row.className = "row";
row.setAttribute("role", "row");
var pillCls = s.status === "ready" ? "ready" : "pending";
var pillTxt = s.status === "ready" ? "Available" : "Processing";
row.innerHTML =
'<div class="period">' +
'<span class="doc-ico">' + DOC_ICON + '</span>' +
'<span class="period-text"><strong>' + s.period + '</strong><span>' + s.range + '</span></span>' +
'</div>' +
'<div class="balance tnum">' + GBP.format(s.balance) + '</div>' +
'<div><span class="pill ' + pillCls + '">' + pillTxt + '</span></div>' +
'<div class="row-actions">' +
'<button class="btn btn-ghost js-preview">Preview</button>' +
'<button class="btn btn-primary js-dl">Download</button>' +
'</div>';
row.querySelector(".js-preview").addEventListener("click", function () { openModal(s); });
row.querySelector(".js-dl").addEventListener("click", function (e) { download(e.currentTarget, "Statement " + s.period); });
stmtList.appendChild(row);
});
stmtCount.textContent = rows.length + (rows.length === 1 ? " document" : " documents");
stmtEmpty.hidden = rows.length !== 0;
}
function renderTax() {
var rows = taxDocs.filter(function (d) { return matches(d, d.title + " " + d.note); });
taxGrid.innerHTML = "";
rows.forEach(function (d) {
var card = document.createElement("div");
card.className = "tax-card";
var badge = d.kind === "tax" ? "tax" : "interest";
var badgeTxt = d.kind === "tax" ? "TAX FORM" : "INTEREST";
card.innerHTML =
'<div class="tax-top">' +
'<span class="tax-badge ' + badge + '">' + badgeTxt + '</span>' +
'<span class="tax-year tnum">' + d.year + '</span>' +
'</div>' +
'<h4>' + d.title + '</h4>' +
'<p>' + d.note + '</p>' +
'<div class="tax-foot">' +
'<button class="btn btn-ghost js-preview">Preview</button>' +
'<button class="btn btn-primary js-dl">' + DL_ICON + 'Download</button>' +
'</div>';
card.querySelector(".js-preview").addEventListener("click", function () { openTaxModal(d); });
card.querySelector(".js-dl").addEventListener("click", function (e) { download(e.currentTarget, d.title); });
taxGrid.appendChild(card);
});
taxCount.textContent = rows.length + (rows.length === 1 ? " document" : " documents");
taxEmpty.hidden = rows.length !== 0;
}
function renderAll() { renderStatements(); renderTax(); }
// ---------- Download (simulated) ----------
function download(btn, label) {
if (btn.dataset.busy) return;
btn.dataset.busy = "1";
var original = btn.innerHTML;
btn.disabled = true;
btn.textContent = "Preparing…";
setTimeout(function () {
btn.disabled = false;
btn.innerHTML = original;
delete btn.dataset.busy;
toast(label + " downloaded (PDF)");
}, 850);
}
// ---------- Modal ----------
var overlay = document.getElementById("overlay");
var modalTitle = document.getElementById("modalTitle");
var modalMeta = document.getElementById("modalMeta");
var docPaper = document.getElementById("docPaper");
var modalDownload = document.getElementById("modalDownload");
var modalClose = document.getElementById("modalClose");
var activeLabel = "";
function openModal(s) {
activeLabel = "Statement " + s.period;
modalTitle.textContent = "Statement preview";
modalMeta.textContent = "Everyday Current · " + s.range;
var net = s.credits - s.debits;
docPaper.innerHTML =
'<div class="dp-head">' +
'<div class="dp-bank">Northbank<span>Everyday Current · •••• 4242</span></div>' +
'<div class="dp-meta">Statement period<br><strong>' + s.range + '</strong><br>IBAN GB29 NWBK 0000 0011</div>' +
'</div>' +
'<div class="dp-row"><span>Opening balance</span><span class="debit tnum">' + GBP.format(s.opening) + '</span></div>' +
'<div class="dp-row"><span>Total credits</span><span class="credit tnum">+' + GBP.format(s.credits) + '</span></div>' +
'<div class="dp-row"><span>Total debits</span><span class="debit tnum">−' + GBP.format(s.debits) + '</span></div>' +
'<div class="dp-row"><span>Net movement</span><span class="' + (net >= 0 ? "credit" : "debit") + ' tnum">' + (net >= 0 ? "+" : "−") + GBP.format(Math.abs(net)) + '</span></div>' +
'<div class="dp-total"><span>Closing balance</span><span class="tnum">' + GBP.format(s.balance) + '</span></div>';
showOverlay();
}
function openTaxModal(d) {
activeLabel = d.title;
modalTitle.textContent = "Document preview";
modalMeta.textContent = (d.kind === "tax" ? "Tax certificate" : "Interest summary") + " · " + d.year;
docPaper.innerHTML =
'<div class="dp-head">' +
'<div class="dp-bank">Northbank<span>' + (d.kind === "tax" ? "Annual tax certificate" : "Gross interest summary") + '</span></div>' +
'<div class="dp-meta">Tax year<br><strong>' + d.title.replace(/^[^0-9]+/, "") + '</strong><br>Ref ' + d.id.toUpperCase() + '</div>' +
'</div>' +
'<div class="dp-row"><span>Account holder</span><span>A. Mercer</span></div>' +
'<div class="dp-row"><span>Gross interest paid</span><span class="credit tnum">' + GBP.format(d.kind === "tax" ? 312.48 : 184.20) + '</span></div>' +
'<div class="dp-row"><span>Tax deducted at source</span><span class="debit tnum">' + GBP.format(0.00) + '</span></div>' +
'<div class="dp-total"><span>Net interest</span><span class="tnum">' + GBP.format(d.kind === "tax" ? 312.48 : 184.20) + '</span></div>';
showOverlay();
}
function showOverlay() {
overlay.hidden = false;
document.body.style.overflow = "hidden";
modalClose.focus();
}
function closeModal() {
overlay.hidden = true;
document.body.style.overflow = "";
}
modalClose.addEventListener("click", closeModal);
overlay.addEventListener("click", function (e) { if (e.target === overlay) closeModal(); });
document.addEventListener("keydown", function (e) { if (e.key === "Escape" && !overlay.hidden) closeModal(); });
modalDownload.addEventListener("click", function () { download(modalDownload, activeLabel); });
// ---------- Filters wiring ----------
var yearTabs = document.getElementById("yearTabs");
yearTabs.addEventListener("click", function (e) {
var btn = e.target.closest(".year-tab");
if (!btn) return;
state.year = btn.dataset.year;
Array.prototype.forEach.call(yearTabs.children, function (b) {
var on = b === btn;
b.classList.toggle("is-active", on);
b.setAttribute("aria-selected", on ? "true" : "false");
});
renderAll();
});
var search = document.getElementById("search");
search.addEventListener("input", function () {
state.query = search.value.trim().toLowerCase();
renderAll();
});
renderAll();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Banking — Statements & Documents</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="app">
<header class="topbar">
<div class="brand">
<div class="brand-mark" aria-hidden="true">N</div>
<div class="brand-text">
<strong>Northbank</strong>
<span>Documents & Statements</span>
</div>
</div>
<div class="topbar-right">
<span class="secure-pill" title="Your documents are encrypted">
<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path fill="currentColor" d="M12 1a5 5 0 0 0-5 5v3H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-1V6a5 5 0 0 0-5-5Zm3 8H9V6a3 3 0 0 1 6 0v3Z"/></svg>
Encrypted
</span>
<div class="avatar" aria-label="Account holder">AM</div>
</div>
</header>
<main class="layout">
<section class="hero">
<div>
<h1>Statements & documents</h1>
<p class="hero-sub">Monthly statements, tax forms and account letters for your accounts. Download a copy or preview before you save.</p>
</div>
<div class="account-chip">
<span class="dot"></span>
<div>
<span class="acc-label">Everyday Current</span>
<span class="acc-num">•••• 4242 · GB29 NWBK 0000 0011</span>
</div>
</div>
</section>
<section class="toolbar" aria-label="Filters">
<div class="years" role="tablist" aria-label="Filter by year" id="yearTabs">
<button class="year-tab is-active" role="tab" aria-selected="true" data-year="all">All years</button>
<button class="year-tab" role="tab" aria-selected="false" data-year="2026">2026</button>
<button class="year-tab" role="tab" aria-selected="false" data-year="2025">2025</button>
<button class="year-tab" role="tab" aria-selected="false" data-year="2024">2024</button>
</div>
<div class="search-wrap">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true"><path fill="currentColor" d="M21 20.3 16.6 16a7.5 7.5 0 1 0-1.4 1.4l4.3 4.3 1.5-1.4ZM5 10.5a5.5 5.5 0 1 1 5.5 5.5A5.5 5.5 0 0 1 5 10.5Z"/></svg>
<input id="search" type="search" placeholder="Search statements & docs" aria-label="Search documents" />
</div>
</section>
<section class="card">
<div class="card-head">
<h2>Monthly statements</h2>
<span class="count" id="stmtCount">12 documents</span>
</div>
<div class="table" role="table" aria-label="Monthly statements">
<div class="thead" role="row">
<span role="columnheader">Period</span>
<span role="columnheader">Closing balance</span>
<span role="columnheader">Status</span>
<span role="columnheader" class="ta-r">Actions</span>
</div>
<div id="stmtList" class="tbody"></div>
<div id="stmtEmpty" class="empty" hidden>No statements for this year.</div>
</div>
</section>
<section class="card">
<div class="card-head">
<h2>Tax documents</h2>
<span class="count" id="taxCount">4 documents</span>
</div>
<div id="taxGrid" class="tax-grid"></div>
<div id="taxEmpty" class="empty" hidden>No tax documents for this year.</div>
</section>
</main>
</div>
<!-- Preview modal -->
<div class="modal-overlay" id="overlay" hidden>
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" id="modal">
<header class="modal-head">
<div>
<h3 id="modalTitle">Statement preview</h3>
<p id="modalMeta" class="modal-meta"></p>
</div>
<button class="icon-btn" id="modalClose" aria-label="Close preview">✕</button>
</header>
<div class="doc-paper" id="docPaper"></div>
<footer class="modal-foot">
<span class="secure-note">
<svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path fill="currentColor" d="M12 1a5 5 0 0 0-5 5v3H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-1V6a5 5 0 0 0-5-5Zm3 8H9V6a3 3 0 0 1 6 0v3Z"/></svg>
Verified document · PDF/A
</span>
<button class="btn btn-primary" id="modalDownload">Download PDF</button>
</footer>
</div>
</div>
<div class="toast-host" id="toastHost" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Statements
A calm, trust-forward documents screen for a fictional retail bank. The page pairs a list of monthly account statements — each showing its period, closing balance and an availability status pill — with a separate grid of tax documents like annual tax certificates and gross interest summaries. Money is right-aligned with tabular figures, account numbers are masked, and an encrypted badge reinforces the security cues customers expect.
Every list responds to the year tabs (All years, 2026, 2025, 2024) and to a single search box that filters across both sections at once. Downloading any document shows a brief “Preparing…” state on the button before a confirmation toast, mimicking a real PDF export. The Preview action opens an accessible modal that renders the chosen document on a paper-style surface — a statement summary with opening balance, credits, debits and closing balance, or a tax certificate with gross interest and tax deducted.
The whole interface is self-contained vanilla JavaScript with no dependencies: a small toast() helper, keyboard-dismissable modal (Escape and backdrop click), focus management, and a layout that collapses cleanly down to 360px-wide mobile screens.
Illustrative UI only — not real banking software or financial advice.