mirror of
https://github.com/luxfi/financial.git
synced 2026-10-30 16:55:56 +00:00
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.
129 lines
8.0 KiB
HTML
129 lines
8.0 KiB
HTML
<!doctype html>
|
|
<html><head><meta charset="utf-8"><link rel="stylesheet" href="lib.css">
|
|
<style>
|
|
.pay-grid { display: grid; grid-template-columns: 400px 1fr; gap: 20px; flex: 1; min-height: 0; }
|
|
.form-card { display: flex; flex-direction: column; gap: 14px; }
|
|
.field label { font-size: 11px; font-weight: 650; color: var(--ink3); text-transform: uppercase; letter-spacing: 0.07em; }
|
|
.field .input {
|
|
margin-top: 6px; padding: 12px 14px; border-radius: 10px;
|
|
background: var(--panel2); border: 1px solid var(--line2);
|
|
font-size: 15px; font-weight: 600; display: flex; justify-content: space-between; align-items: center;
|
|
}
|
|
.field .input .cur { color: var(--ink2); font-size: 12px; font-weight: 700; }
|
|
.send-btn {
|
|
margin-top: 4px; padding: 13px; border-radius: 10px; border: none;
|
|
background: linear-gradient(135deg, #ffffff, #d8c37a); color: #000;
|
|
font-size: 14px; font-weight: 700; font-family: Zen, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; cursor: pointer;
|
|
transition: transform 0.15s ease, box-shadow 0.3s ease;
|
|
}
|
|
.send-btn.pressed { transform: scale(0.97); box-shadow: 0 0 26px rgba(212, 175, 55, 0.45); }
|
|
.steps { display: flex; flex-direction: column; gap: 0; margin-top: 6px; }
|
|
.step { display: flex; align-items: center; gap: 12px; padding: 9px 2px; color: var(--ink3); font-size: 13px; font-weight: 550; transition: color 0.4s ease; }
|
|
.step .dot {
|
|
width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--line2);
|
|
display: flex; align-items: center; justify-content: center; font-size: 11px;
|
|
transition: all 0.4s ease; flex-shrink: 0; color: transparent;
|
|
}
|
|
.step.done { color: var(--ink); }
|
|
.step.done .dot { background: rgba(52, 211, 153, 0.15); border-color: var(--green); color: var(--green); }
|
|
.step.live .dot { border-color: var(--gold); animation: pulse-glow 1s ease infinite; }
|
|
.rate { font-size: 12px; color: var(--ink3); display: flex; justify-content: space-between; }
|
|
.rate b { color: var(--ink2); font-variant-numeric: tabular-nums; }
|
|
.right-col { display: flex; flex-direction: column; gap: 20px; min-height: 0; }
|
|
.row { grid-template-columns: 2fr 1.4fr 1fr 1fr 1fr; }
|
|
.thead { grid-template-columns: 2fr 1.4fr 1fr 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"><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>Dashboard</div>
|
|
<div class="nav-item active"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3.5 3 14 0 18M12 3c-3 3.5-3 14 0 18"/></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="M21 8v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8m18 0V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2m18 0H3"/></svg>Wallets</div>
|
|
<div class="nav-item"><svg viewBox="0 0 24 24"><path d="M3 17l6-6 4 4 8-8"/><path d="M14 7h7v7"/></svg>Exchange</div>
|
|
<div class="foot"><span class="env-dot"></span>Sandbox · demo tenant</div>
|
|
</aside>
|
|
<main class="main">
|
|
<div class="topbar fx">
|
|
<h1>Cross-border payment</h1>
|
|
<div class="who"><span class="pill"><span class="env-dot"></span>Live rails</span><div class="avatar">LX</div>Lux Demo</div>
|
|
</div>
|
|
<div class="pay-grid">
|
|
<div class="card form-card fx">
|
|
<div class="field"><label>You send</label><div class="input"><span id="amt">$10,000.00</span><span class="cur">USDC · Polygon</span></div></div>
|
|
<div class="field"><label>Recipient gets</label><div class="input"><span id="recv">₦15,482,000</span><span class="cur">NGN · Lagos</span></div></div>
|
|
<div class="rate"><span>Rate</span><b>1 USDC = ₦1,548.20</b></div>
|
|
<div class="rate"><span>Arrives</span><b>in seconds</b></div>
|
|
<button class="send-btn" id="send">Send payment</button>
|
|
<div class="steps">
|
|
<div class="step" id="s1"><span class="dot">✓</span>Compliance check — sanctions, travel rule</div>
|
|
<div class="step" id="s2"><span class="dot">✓</span>MPC signing ceremony — 2 of 3 shares</div>
|
|
<div class="step" id="s3"><span class="dot">✓</span>Teleport — Polygon → settlement</div>
|
|
<div class="step" id="s4"><span class="dot">✓</span>Paid out — NGN local rails</div>
|
|
</div>
|
|
</div>
|
|
<div class="right-col">
|
|
<div class="stats" style="grid-template-columns: repeat(3, 1fr);">
|
|
<div class="card fx"><div class="label">Balance</div><div class="value" id="bal">$2,847,293</div><div class="delta">USDC · multi-chain</div></div>
|
|
<div class="card fx"><div class="label">Today</div><div class="value" id="cnt">1,247</div><div class="delta">payments sent</div></div>
|
|
<div class="card fx"><div class="label">Corridors</div><div class="value">40+</div><div class="delta">countries live</div></div>
|
|
</div>
|
|
<div class="table fx">
|
|
<div class="thead"><span>Recipient</span><span>Corridor</span><span>Amount</span><span>Rail</span><span>Status</span></div>
|
|
<div id="feed"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
<script>
|
|
const $ = (id) => document.getElementById(id);
|
|
const rows = [
|
|
['Adaeze O.', 'US → Nigeria', '$10,000.00', 'Teleport', 'ok', 'Settled'],
|
|
['Marisol G.', 'US → Mexico', '$3,420.00', 'SPEI', 'ok', 'Settled'],
|
|
['Rafael S.', 'US → Brazil', '$8,150.00', 'PIX', 'ok', 'Settled'],
|
|
['Priya N.', 'UK → India', '£2,600.00', 'UPI', 'ok', 'Settled'],
|
|
['Kwame A.', 'EU → Ghana', '€1,975.00', 'MoMo', 'ok', 'Settled'],
|
|
['Northwind Ltd', 'US → UK', '$42,000.00', 'SWIFT', 'ok', 'Settled'],
|
|
['Sandbox Realty', 'US → US', '$12,500.00', 'ACH', 'ok', 'Settled'],
|
|
['Linh T.', 'SG → Vietnam', 'S$4,800.00', 'Teleport', 'ok', 'Settled'],
|
|
['Acme Corp', 'EU → US', '€28,300.00', 'Fedwire', 'ok', 'Settled'],
|
|
['Fatima B.', 'UK → Pakistan', '£950.00', 'Teleport', 'ok', 'Settled'],
|
|
];
|
|
function addRow([name, corr, amt, rail, cls, st], flash) {
|
|
const d = document.createElement('div');
|
|
d.className = 'row' + (flash ? ' flash' : '');
|
|
d.style.gridTemplateColumns = '2fr 1.4fr 1fr 1fr 1fr';
|
|
d.innerHTML = `<span>${name}</span><span class="mono" style="color:var(--ink2)">${corr}</span><span>${amt}</span><span class="mono" style="color:var(--ink2)">${rail}</span><span class="tag ${cls}">${st}</span>`;
|
|
$('feed').prepend(d);
|
|
while ($('feed').children.length > 11) $('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 usd = (v) => '$' + Math.round(v).toLocaleString('en-US');
|
|
function cycle() {
|
|
document.querySelectorAll('.fx').forEach((el, i) => { el.classList.remove('in'); setTimeout(() => el.classList.add('in'), 80 + i * 90); });
|
|
['s1','s2','s3','s4'].forEach(id => $(id).className = 'step');
|
|
$('feed').innerHTML = '';
|
|
rows.slice(1).reverse().forEach(r => addRow(r, false));
|
|
count($('bal'), 2214000, 2847293, 1400, usd);
|
|
count($('cnt'), 1195, 1246, 1400, v => Math.round(v).toLocaleString());
|
|
setTimeout(() => { $('send').classList.add('pressed'); setTimeout(() => $('send').classList.remove('pressed'), 250); }, 1600);
|
|
const steps = ['s1','s2','s3','s4'];
|
|
steps.forEach((id, i) => {
|
|
setTimeout(() => {
|
|
if (i > 0) $(steps[i-1]).className = 'step done';
|
|
$(id).className = 'step live';
|
|
}, 2000 + i * 900);
|
|
});
|
|
setTimeout(() => { $(steps[3]).className = 'step done'; addRow(rows[0], true); count($('cnt'), 1246, 1247, 400, v => Math.round(v).toLocaleString()); }, 2000 + 4 * 900);
|
|
}
|
|
cycle(); setInterval(cycle, 8000);
|
|
</script></body></html>
|