Files
zeekayandHanzo Dev 4d4fa23382 Lux branding on every screen, in the Lux faces
The demo tenant conceit is gone: mockups and the six animated
screens brand as Lux with the triangle mark. The site drops Zen for
the brand faces — Lux Sans for text, Lux Display for the hero, both
vendored until @luxfi/font is on the registry. Screens record on
pure black, and the showcase leads with a full-width frame.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-31 13:21:25 -07:00

107 lines
6.1 KiB
HTML

<!doctype html>
<html><head><meta charset="utf-8"><link rel="stylesheet" href="lib.css">
<style>
.grid2 { display: grid; grid-template-columns: 1.5fr 1fr; gap: 20px; flex: 1; min-height: 0; }
.chart-card { display: flex; flex-direction: column; }
.chart-card svg { flex: 1; width: 100%; margin-top: 12px; }
.draw { stroke-dasharray: 1600; stroke-dashoffset: 1600; }
.draw.go { animation: draw 2.2s cubic-bezier(0.4, 0, 0.2, 1) forwards; }
@keyframes draw { to { stroke-dashoffset: 0; } }
.fillfade { opacity: 0; transition: opacity 1.2s ease 1.4s; }
.fillfade.go { opacity: 0.9; }
.row { grid-template-columns: 1.6fr 1.2fr 1fr 1fr; }
.thead { grid-template-columns: 1.6fr 1.2fr 1fr 1fr; }
</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>Overview</div>
<div class="nav-item"><svg viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></svg>Customers</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"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>Compliance</div>
<div class="nav-item"><svg viewBox="0 0 24 24"><path d="M3 3v18h18"/><path d="M7 14l4-4 4 4 5-6"/></svg>Reports</div>
<div class="foot"><span class="env-dot"></span>Operations · Sandbox</div>
</aside>
<main class="main">
<div class="topbar fx"><h1>Operations</h1><div class="who"><span class="pill"><span class="env-dot"></span>All rails live</span><div class="avatar">OP</div>ops@lux</div></div>
<div class="stats">
<div class="card fx"><div class="label">Customers</div><div class="value" id="cust">12,847</div><div class="delta" id="custd">+156 this week</div></div>
<div class="card fx"><div class="label">30d volume</div><div class="value" id="vol">$48.2M</div><div class="delta">+12.5%</div></div>
<div class="card fx"><div class="label">Liquidity</div><div class="value" id="liq">$6.31M</div><div class="delta">across 5 currencies</div></div>
<div class="card fx"><div class="label">Pending KYC</div><div class="value" id="kyc">7</div><div class="delta down" id="kycd">reviewing…</div></div>
</div>
<div class="grid2">
<div class="card chart-card fx">
<div class="label">Payment volume · 30 days</div>
<svg viewBox="0 0 600 220" preserveAspectRatio="none">
<defs>
<linearGradient id="goldgrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#8a6d1f"/><stop offset="1" stop-color="#e8cd6a"/>
</linearGradient>
<linearGradient id="goldfill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="rgba(212,175,55,0.22)"/><stop offset="1" stop-color="rgba(212,175,55,0)"/>
</linearGradient>
</defs>
<path class="chart-fill fillfade" id="area" d=""/>
<path class="chart-line draw" id="line" d=""/>
</svg>
</div>
<div class="table fx" style="flex:none;">
<div class="thead"><span>Customer</span><span>Check</span><span>Risk</span><span>Status</span></div>
<div id="queue"></div>
</div>
</div>
</main>
</div>
<script>
const $ = (id) => document.getElementById(id);
// deterministic pseudo-random walk for the chart
const pts = []; let v = 120;
for (let i = 0; i <= 30; i++) { v += Math.sin(i * 1.7) * 14 + Math.cos(i * 0.9) * 9 + 1.8; pts.push([i * 20, 200 - v * 0.62]); }
const line = 'M' + pts.map(p => p.map(n => n.toFixed(1)).join(',')).join(' L');
document.getElementById('line').setAttribute('d', line);
document.getElementById('area').setAttribute('d', line + ' L600,220 L0,220 Z');
const queue = [
['Northwind Ltd', 'KYB · UBO graph', 'Low', 'wait', 'Review'],
['Adaeze Okafor', 'KYC · document + liveness', 'Low', 'wait', 'Review'],
['Vertex Trading', 'KYB · sanctions rescreen', 'Medium', 'wait', 'Review'],
['Sandbox Realty', 'KYC · PEP check', 'Low', 'wait', 'Review'],
];
function renderQueue() {
$('queue').innerHTML = '';
for (const [name, check, risk, cls, st] of queue) {
const d = document.createElement('div');
d.className = 'row';
d.style.gridTemplateColumns = '1.6fr 1.2fr 1fr 1fr';
d.innerHTML = `<span>${name}</span><span style="color:var(--ink2);font-size:12px;">${check}</span><span class="tag ${risk === 'Medium' ? 'wait' : 'info'}">${risk}</span><span class="tag ${cls}">${st}</span>`;
$('queue').append(d);
}
}
function approve(i) {
const el = $('queue').children[i];
el.classList.add('flash');
el.lastElementChild.outerHTML = '<span class="tag ok">Approved</span>';
}
function count(el, from, to, ms, fmt) {
const t0 = performance.now();
(function tick(t) {
const k = Math.min(1, (t - t0) / ms), val = from + (to - from) * (1 - Math.pow(1 - k, 3));
el.textContent = fmt(val);
if (k < 1) requestAnimationFrame(tick);
})(t0);
}
function cycle() {
document.querySelectorAll('.fx').forEach((el, i) => { el.classList.remove('in'); setTimeout(() => el.classList.add('in'), 80 + i * 90); });
const ln = $('line'), ar = $('area');
ln.classList.remove('go'); ar.classList.remove('go'); void ln.getBoundingClientRect();
setTimeout(() => { ln.classList.add('go'); ar.classList.add('go'); }, 500);
renderQueue();
count($('cust'), 12691, 12847, 1600, x => Math.round(x).toLocaleString());
count($('vol'), 42.9, 48.2, 1600, x => '$' + x.toFixed(1) + 'M');
count($('liq'), 5.87, 6.31, 1600, x => '$' + x.toFixed(2) + 'M');
$('kyc').textContent = '7';
[0, 1, 2].forEach(i => setTimeout(() => { approve(i); count($('kyc'), 7 - i, 6 - i, 300, x => Math.round(x)); }, 3200 + i * 1200));
}
cycle(); setInterval(cycle, 8000);
</script></body></html>