mirror of
https://github.com/luxfi/financial.git
synced 2026-10-30 16:55:56 +00:00
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]>
107 lines
6.1 KiB
HTML
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>
|