Files
zeekay 7c5d9aa269 the face is Zen, and it ships from @hanzo/font
One family. Font stacks name Zen and fall back to the native faces, tokens
name the role rather than a face, and the packages that used to supply a
face now point at @hanzo/font.

Self-hosted @font-face blocks are removed rather than renamed: rewriting the
family in one would leave a src pointing at a file this repo no longer ships.
@hanzo/font carries the binaries and declares the faces, so the local copies
go with the declaration that used them.

Comments naming a face we no longer use are deleted, not reworded.
2026-09-01 08:16:50 -07:00

116 lines
7.3 KiB
HTML

<!doctype html>
<html><head><meta charset="utf-8"><link rel="stylesheet" href="lib.css">
<style>
.bank-grid { display: grid; grid-template-columns: 1fr 380px; gap: 20px; flex: 1; min-height: 0; }
.left { display: flex; flex-direction: column; gap: 20px; min-height: 0; }
.balance-card .value { font-size: 40px; }
.accounts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.vcard {
aspect-ratio: 1.586; border-radius: 16px; padding: 22px;
background: linear-gradient(135deg, #1a1a1e 0%, #000 60%), radial-gradient(400px 200px at 80% 0%, rgba(212,175,55,0.3), transparent);
background-blend-mode: screen; border: 1px solid var(--line2);
display: flex; flex-direction: column; justify-content: space-between;
position: relative; overflow: hidden;
}
.vcard::after { content: ''; position: absolute; inset: 0; background: linear-gradient(110deg, transparent 35%, rgba(255,255,255,0.06) 50%, transparent 65%); background-size: 220% 100%; animation: shimmer 3.2s linear infinite; }
.vcard .top { display: flex; justify-content: space-between; align-items: center; }
.vcard .net { font-weight: 800; font-style: italic; letter-spacing: 0.04em; color: var(--ink2); }
.vcard .num { font-family: Zen Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; font-size: 17px; letter-spacing: 0.14em; color: var(--ink); }
.vcard .meta { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink2); }
.toast {
position: absolute; top: 26px; right: 32px; z-index: 5;
background: var(--panel2); border: 1px solid var(--line2); border-left: 3px solid var(--green);
border-radius: 10px; padding: 12px 16px; font-size: 13px; font-weight: 600;
box-shadow: 0 12px 40px rgba(0,0,0,0.5);
opacity: 0; transform: translateY(-8px); transition: all 0.4s cubic-bezier(0.22,1,0.36,1);
}
.toast.show { opacity: 1; transform: none; }
.toast .sub { font-weight: 400; color: var(--ink2); font-size: 11.5px; margin-top: 2px; }
.row { grid-template-columns: 46px 1.8fr 1fr 1fr; }
.thead { grid-template-columns: 46px 1.8fr 1fr 1fr; }
.txicon { width: 32px; height: 32px; border-radius: 50%; background: var(--panel2); border: 1px solid var(--line); display: flex; align-items: center; justify-content: center; font-size: 13px; }
</style></head>
<body><div class="app">
<aside class="side">
<div class="brand"><div class="brand-mark">▼</div><div class="brand-name">Lux</div></div>
<div class="nav-item active"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>Home</div>
<div class="nav-item"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18"/></svg>Payments</div>
<div class="nav-item"><svg viewBox="0 0 24 24"><rect x="2" y="6" width="20" height="13" rx="2"/><path d="M2 10h20"/></svg>Cards</div>
<div class="nav-item"><svg viewBox="0 0 24 24"><path d="M12 2v20M2 12h20"/></svg>Earn</div>
<div class="foot"><span class="env-dot"></span>Member · Lux Demo</div>
</aside>
<main class="main">
<div class="toast" id="toast"><div>Payment received — $4,200.00</div><div class="sub">From Northwind Ltd · just now</div></div>
<div class="topbar fx"><h1>Good morning, Demo</h1><div class="who"><span class="pill"><span class="env-dot"></span>All systems normal</span><div class="avatar">LX</div></div></div>
<div class="bank-grid">
<div class="left">
<div class="card balance-card fx"><div class="label">Total balance</div><div class="value" id="bal">$24,850.00</div><div class="delta" id="delta">+$4,200.00 today</div></div>
<div class="accounts">
<div class="card fx"><div class="label">USD</div><div class="value" id="usd">$15,000</div><div class="delta">checking</div></div>
<div class="card fx"><div class="label">EUR</div><div class="value">€5,240</div><div class="delta">IBAN · DE89…3000</div></div>
<div class="card fx"><div class="label">USDC</div><div class="value">4,610</div><div class="delta">multi-chain</div></div>
</div>
<div class="table fx">
<div class="thead"><span></span><span>Activity</span><span>Amount</span><span>Status</span></div>
<div id="feed"></div>
</div>
</div>
<div class="fx-pop" id="cardwrap" style="display:flex;flex-direction:column;gap:14px;">
<div class="vcard">
<div class="top"><span style="font-size:15px;">▼ <b>Lux</b></span><span class="net">VISA</span></div>
<div class="num" id="pan">•••• •••• •••• 4127</div>
<div class="meta"><span>LUX DEMO</span><span>EXP 09/29</span><span>VIRTUAL</span></div>
</div>
<div class="card">
<div class="label">Card controls</div>
<div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap;">
<span class="tag ok">Active</span><span class="tag info">Online payments</span><span class="tag info">Contactless</span><span class="tag wait" id="freeze">Freeze</span>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
const $ = (id) => document.getElementById(id);
const txs = [
['⬇','Payment received · Northwind Ltd','+$4,200.00','ok','Settled'],
['🛒','Apple Store','-$129.00','ok','Card'],
['⬆','Rent · Sandbox Realty','-$2,400.00','ok','ACH'],
['↔','USDC → USD conversion','$1,000.00','ok','Instant'],
['⬇','Salary · Acme Corp','+$6,500.00','ok','Wire'],
['🛒','Whole Foods','-$86.20','ok','Card'],
['⬆','Wire to EUR IBAN','-€2,000.00','ok','SEPA'],
['🛒','Netflix','-$15.49','ok','Card'],
['⬇','Refund · Delta Air','+$340.00','ok','Card'],
];
function addRow([ic, label, amt, cls, st], flash) {
const d = document.createElement('div');
d.className = 'row' + (flash ? ' flash' : '');
d.style.gridTemplateColumns = '46px 1.8fr 1fr 1fr';
const col = amt.startsWith('+') ? 'var(--green)' : 'var(--ink)';
d.innerHTML = `<span class="txicon">${ic}</span><span>${label}</span><span style="color:${col};font-variant-numeric:tabular-nums;">${amt}</span><span class="tag ${cls}">${st}</span>`;
$('feed').prepend(d);
while ($('feed').children.length > 9) $('feed').lastChild.remove();
}
function count(el, from, to, ms, fmt) {
const t0 = performance.now();
(function tick(t) {
const k = Math.min(1, (t - t0) / ms), v = from + (to - from) * (1 - Math.pow(1 - k, 3));
el.textContent = fmt(v);
if (k < 1) requestAnimationFrame(tick);
})(t0);
}
const money = (v) => '$' + v.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
function cycle() {
document.querySelectorAll('.fx,.fx-pop').forEach((el, i) => { el.classList.remove('in'); setTimeout(() => el.classList.add('in'), 80 + i * 90); });
$('toast').classList.remove('show');
$('feed').innerHTML = '';
txs.slice(1).reverse().forEach(r => addRow(r, false));
count($('bal'), 20650, 20650, 300, money);
setTimeout(() => { $('toast').classList.add('show'); addRow(txs[0], true); count($('bal'), 20650, 24850, 1200, money); count($('usd'), 10800, 15000, 1200, v => '$' + Math.round(v).toLocaleString()); }, 3200);
setTimeout(() => $('toast').classList.remove('show'), 6800);
}
cycle(); setInterval(cycle, 8000);
</script></body></html>