koopa-admin-log/configs/bank-landing/index.html

938 lines
32 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GOA Exploration Bank</title>
<style>
:root {
--bg: #1a1410;
--card: rgba(42, 32, 24, 0.82);
--text: #fff6e8;
--muted: #c9b8a0;
--accent: #e8a838;
--accent2: #3ecfbf;
--border: rgba(232, 168, 56, 0.28);
--glow-gold: rgba(232, 168, 56, 0.35);
--glow-teal: rgba(62, 207, 191, 0.22);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
color: var(--text);
line-height: 1.55;
background-color: var(--bg);
background-image:
radial-gradient(ellipse 90% 55% at 50% 108%, rgba(26, 107, 110, 0.55) 0%, transparent 55%),
radial-gradient(circle 420px at 12% 18%, var(--glow-gold) 0%, transparent 62%),
radial-gradient(circle 380px at 88% 12%, var(--glow-teal) 0%, transparent 58%),
radial-gradient(circle 280px at 70% 70%, rgba(196, 122, 24, 0.18) 0%, transparent 60%),
linear-gradient(165deg, #2c1e14 0%, #1a1410 38%, #12181a 72%, #0e1c1e 100%);
background-attachment: fixed;
}
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
opacity: 0.07;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
main {
position: relative;
z-index: 1;
max-width: 38rem;
margin: 0 auto;
padding: 2.25rem 1.25rem 3.5rem;
}
.hero {
text-align: center;
margin: 0 0 1.75rem;
}
h1 {
font-size: 1.85rem;
font-weight: 750;
margin: 0 0 0.45rem;
letter-spacing: -0.02em;
line-height: 1.2;
background: linear-gradient(120deg, #fff6e8 0%, #f0c86a 45%, #3ecfbf 120%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.sub {
color: var(--muted);
margin: 0 auto;
max-width: 30rem;
font-size: 1.05rem;
line-height: 1.45;
}
.badge {
display: inline-block;
background: linear-gradient(135deg, rgba(232, 168, 56, 0.2), rgba(62, 207, 191, 0.15));
color: #f0d090;
font-size: 0.75rem;
font-weight: 600;
padding: 0.2rem 0.7rem;
border-radius: 999px;
margin: 0 0 0.85rem;
border: 1px solid rgba(232, 168, 56, 0.4);
letter-spacing: 0.04em;
}
.step-card {
background: var(--card);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--border);
border-radius: 18px;
padding: 1.4rem 1.35rem 1.5rem;
margin-bottom: 1.35rem;
box-shadow:
0 14px 42px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 246, 232, 0.06);
}
.step-card.step-money {
border-color: rgba(62, 207, 191, 0.4);
}
.step-num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.1rem;
height: 2.1rem;
border-radius: 50%;
font-weight: 800;
font-size: 1.15rem;
margin-right: 0.55rem;
vertical-align: middle;
color: #1a1208;
background: linear-gradient(135deg, #f0c86a, #c47a18);
box-shadow: 0 4px 14px var(--glow-gold);
}
.step-card.step-money .step-num {
background: linear-gradient(135deg, #5eead4, #0d9488);
color: #042f2e;
box-shadow: 0 4px 14px var(--glow-teal);
}
.step-card h2 {
font-size: 1.35rem;
font-weight: 800;
margin: 0 0 0.4rem;
letter-spacing: -0.01em;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 0.15rem 0;
text-align: center;
}
.step-tagline {
margin: 0 0 1.15rem;
color: var(--muted);
font-size: 0.98rem;
text-align: center;
}
.step-body {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.1rem;
}
.step-body ol {
margin: 0;
padding-left: 1.25rem;
width: 100%;
align-self: stretch;
}
.step-body li { margin: 0.4rem 0; }
.qr-box {
background: #fff;
padding: 0.85rem;
border-radius: 14px;
text-align: center;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}
.qr-box img, .qr-box canvas, .qr-box svg {
display: block;
width: 220px;
height: 220px;
margin: 0 auto;
}
.qr-label {
margin-top: 0.6rem;
font-size: 0.85rem;
color: #3a342c;
font-weight: 700;
max-width: 240px;
margin-left: auto;
margin-right: auto;
}
.cta {
display: inline-block;
text-align: center;
text-decoration: none;
background: linear-gradient(135deg, #f0c86a 0%, #e8a838 40%, #c47a18 100%);
color: #1a1208;
font-weight: 750;
font-size: 1rem;
padding: 0.75rem 1.25rem;
border-radius: 12px;
box-shadow: 0 8px 22px var(--glow-gold);
}
.cta:hover { filter: brightness(1.06); }
.cta.teal {
background: linear-gradient(135deg, #5eead4 0%, #14b8a6 50%, #0d9488 100%);
color: #042f2e;
box-shadow: 0 8px 22px var(--glow-teal);
}
.meta {
font-size: 0.78rem;
color: var(--muted);
word-break: break-all;
margin: 0.5rem 0 0;
text-align: center;
max-width: 100%;
}
code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.86em;
background: rgba(0, 0, 0, 0.35);
padding: 0.1em 0.35em;
border-radius: 4px;
color: #f0d090;
}
/* App store buttons */
.app-links {
display: flex;
flex-wrap: wrap;
gap: 0.55rem;
justify-content: center;
width: 100%;
}
.app-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.45rem;
min-width: 9.5rem;
text-decoration: none;
font-weight: 700;
font-size: 0.92rem;
padding: 0.65rem 0.95rem;
border-radius: 11px;
border: 1px solid rgba(232, 168, 56, 0.4);
background: rgba(0, 0, 0, 0.28);
color: #f0d090;
}
.app-btn:hover { filter: brightness(1.08); border-color: rgba(240, 200, 106, 0.7); }
.app-btn.ios {
border-color: rgba(62, 207, 191, 0.4);
color: #b8f5ec;
}
.app-btn.hub {
flex: 1 1 100%;
border-color: rgba(201, 184, 160, 0.35);
color: var(--muted);
font-weight: 600;
font-size: 0.85rem;
gap: 0.5rem;
}
.app-ico {
width: 1.15rem;
height: 1.15rem;
flex-shrink: 0;
display: block;
}
.app-ico path, .app-ico circle, .app-ico rect { fill: currentColor; }
.app-ico.stroke-ico path, .app-ico.stroke-ico circle, .app-ico.stroke-ico rect {
fill: none;
stroke: currentColor;
stroke-width: 1.6;
stroke-linecap: round;
stroke-linejoin: round;
}
.app-ico.gold { color: #f0c86a; }
.app-ico.blue { color: #3ecfbf; }
.app-btn .app-ico.gold { color: #f0c86a; }
.app-btn .app-ico.blue { color: #5eead4; }
.app-btn.ios .app-ico { color: #5eead4; }
.app-btn:not(.ios):not(.hub) .app-ico { color: #f0c86a; }
.hub-icons {
display: inline-flex;
align-items: center;
gap: 0.35rem;
}
.hub-icons .app-ico { width: 1.05rem; height: 1.05rem; }
.step-tagline .app-ico {
width: 1rem;
height: 1rem;
display: inline-block;
vertical-align: -0.15em;
margin: 0 0.12em;
}
.step-tagline .app-ico.ios { color: #5eead4; }
.step-tagline .app-ico.android { color: #f0c86a; }
.more-section {
margin-top: 2.75rem;
padding-top: 1.75rem;
border-top: 1px dashed rgba(201, 184, 160, 0.35);
}
.more-section > .more-label {
display: block;
text-align: center;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 1rem;
}
.more-card {
background: rgba(18, 28, 28, 0.65);
border: 1px dashed rgba(62, 207, 191, 0.35);
border-radius: 16px;
padding: 1.25rem 1.2rem 1.4rem;
}
.more-card h2 {
font-size: 1.05rem;
font-weight: 700;
margin: 0 0 0.5rem;
color: #d8ebe8;
}
.more-card p {
margin: 0 0 0.85rem;
color: var(--muted);
font-size: 0.95rem;
}
.more-card ol {
margin: 0 0 1rem;
padding-left: 1.2rem;
color: var(--muted);
font-size: 0.92rem;
}
.more-card li { margin: 0.35rem 0; }
.more-actions {
display: flex;
flex-direction: column;
gap: 0.55rem;
align-items: stretch;
}
.cta-sec {
display: block;
text-align: center;
text-decoration: none;
color: var(--accent2);
border: 1px solid rgba(62, 207, 191, 0.4);
padding: 0.6rem 0.9rem;
border-radius: 10px;
font-weight: 600;
font-size: 0.92rem;
}
.cta-sec.merchant {
color: #f0d090;
border-color: rgba(232, 168, 56, 0.35);
}
.more-qr {
display: flex;
justify-content: center;
margin: 1rem 0 0.25rem;
}
.more-qr .qr-box {
transform: scale(0.92);
transform-origin: center top;
opacity: 0.95;
}
.stats {
background: rgba(18, 28, 28, 0.55);
border: 1px solid rgba(62, 207, 191, 0.28);
border-radius: 16px;
padding: 1.1rem 1.15rem 1.2rem;
margin: 0 0 1.5rem;
}
.stats h2 {
margin: 0 0 0.85rem;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
text-align: center;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.65rem 0.85rem;
}
@media (min-width: 420px) {
.stats-grid { grid-template-columns: 1fr 1fr 1fr; }
}
.stat {
background: rgba(0, 0, 0, 0.22);
border-radius: 10px;
padding: 0.55rem 0.65rem;
min-width: 0;
}
.stat .k {
display: block;
font-size: 0.68rem;
color: var(--muted);
letter-spacing: 0.04em;
text-transform: uppercase;
margin-bottom: 0.2rem;
text-align: center;
}
.stat .v {
display: block;
font-size: 1.05rem;
font-weight: 750;
color: #f0d090;
word-break: break-word;
font-variant-numeric: tabular-nums;
text-align: center;
}
.stat .v.teal { color: #5eead4; }
.stat.wide { grid-column: 1 / -1; }
.wd-list {
list-style: none;
margin: 0.15rem 0 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.wd-list li {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 0.25rem 0.75rem;
font-size: 0.88rem;
font-weight: 650;
color: #f0d090;
font-variant-numeric: tabular-nums;
border-bottom: 1px solid rgba(201, 184, 160, 0.12);
padding-bottom: 0.3rem;
}
.wd-list li:last-child { border-bottom: none; padding-bottom: 0; }
.wd-list .amt { color: #5eead4; font-weight: 750; }
.wd-list .meta {
color: var(--muted);
font-size: 0.78rem;
font-weight: 600;
}
.stats-foot {
margin: 0.75rem 0 0;
font-size: 0.72rem;
color: var(--muted);
text-align: center;
}
.stats-foot.err { color: #f0a090; }
.stats-run-note {
margin: 0.35rem 0 0;
font-size: 0.7rem;
color: #f0a090;
text-align: center;
}
footer {
margin-top: 2rem;
color: var(--muted);
font-size: 0.85rem;
text-align: center;
}
footer a { color: #e8c878; }
/* Compact GOA shop teaser */
.shop-box {
background: rgba(18, 28, 28, 0.55);
border: 1px solid rgba(232, 168, 56, 0.32);
border-radius: 16px;
padding: 1rem 1.05rem 1.1rem;
margin: 0 0 1.35rem;
}
.shop-box h2 {
margin: 0 0 0.25rem;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
text-align: center;
}
.shop-box .shop-lead {
margin: 0 0 0.75rem;
text-align: center;
font-size: 0.88rem;
color: var(--muted);
}
.shop-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
max-width: 22rem;
margin: 0 auto;
}
.shop-box.shop-mini {
margin: 0 0 1rem;
padding: 0.85rem 0.9rem 0.95rem;
}
.shop-item {
background: rgba(0, 0, 0, 0.28);
border: 1px solid rgba(232, 168, 56, 0.18);
border-radius: 12px;
padding: 0.55rem 0.5rem 0.6rem;
text-align: center;
min-width: 0;
}
.shop-item .emoji {
display: block;
font-size: 1.35rem;
line-height: 1.2;
margin-bottom: 0.2rem;
}
.shop-item .name {
display: block;
font-size: 0.78rem;
font-weight: 700;
color: #fff6e8;
line-height: 1.25;
}
.shop-item .price {
display: block;
margin-top: 0.2rem;
font-size: 0.8rem;
font-weight: 750;
color: #5eead4;
font-variant-numeric: tabular-nums;
}
.shop-foot {
margin: 0.75rem 0 0;
text-align: center;
}
.shop-foot a {
display: inline-block;
text-decoration: none;
font-size: 0.85rem;
font-weight: 700;
color: #1a1208;
background: linear-gradient(135deg, #f0c86a, #c47a18);
padding: 0.45rem 0.9rem;
border-radius: 10px;
}
.shop-foot a:hover { filter: brightness(1.06); }
</style>
<base href="/intro/" />
<link rel="stylesheet" href="/intro/shop-pay.css" />
<script src="/intro/qrcode.min.js"></script>
</head>
<body data-shop-theme="bank">
<main>
<header class="hero">
<span class="badge">Exploration · GOA</span>
<h1>GOA Exploration Bank</h1>
<p class="sub">
Install the wallet, then <strong>one QR</strong> adds the exchange and starts a withdraw.
</p>
</header>
<!-- ========== LIVE STATS ========== -->
<section class="stats" id="stats" aria-labelledby="stats-title" hidden>
<h2 id="stats-title">GOA flow</h2>
<div class="stats-grid">
<div class="stat">
<span class="k">Accounts</span>
<span class="v teal" id="st-accounts"></span>
</div>
<div class="stat">
<span class="k">Bank credits</span>
<span class="v teal" id="st-incoming"></span>
</div>
<div class="stat">
<span class="k">Withdraws</span>
<span class="v" id="st-withdraw"></span>
</div>
<div class="stat">
<span class="k">Unique wallets</span>
<span class="v teal" id="st-wallets"></span>
</div>
<div class="stat">
<span class="k">24h</span>
<span class="v" id="st-24h"></span>
</div>
<div class="stat">
<span class="k">7d</span>
<span class="v" id="st-7d"></span>
</div>
<div class="stat wide">
<span class="k">Recent Withdraws</span>
<ul class="wd-list" id="st-recent" aria-live="polite">
<li class="meta"></li>
</ul>
</div>
</div>
<p class="stats-foot" id="st-foot" hidden></p>
<p class="stats-run-note" id="st-run-note" hidden></p>
</section>
<section class="stats" id="stats-perf" aria-labelledby="perf-title">
<h2 id="perf-title">Performance</h2>
<p class="note" style="text-align:center;font-size:0.82rem;color:var(--muted);margin:0 0 0.85rem">
Live HTTP probes + container memory (RSS)
</p>
<div class="stats-grid">
<div class="stat">
<span class="k">/config</span>
<span class="v teal" id="st-config-ms"></span>
</div>
<div class="stat">
<span class="k">/taler-integration/config</span>
<span class="v" id="st-int-ms"></span>
</div>
<div class="stat">
<span class="k">/webui/</span>
<span class="v" id="st-webui-ms"></span>
</div>
<div class="stat">
<span class="k">Loadavg</span>
<span class="v" id="st-load"></span>
</div>
<div class="stat">
<span class="k">Container</span>
<span class="v teal" id="st-mem-ctr"></span>
</div>
<div class="stat">
<span class="k">PostgreSQL</span>
<span class="v" id="st-mem-pg"></span>
</div>
<div class="stat">
<span class="k">Java / libeufin</span>
<span class="v" id="st-mem-java"></span>
</div>
<div class="stat">
<span class="k">Nginx</span>
<span class="v" id="st-mem-nginx"></span>
</div>
</div>
<div style="margin-top:0.85rem;overflow-x:auto">
<p class="note" style="text-align:center;margin-bottom:0.4rem">Top processes (RSS)</p>
<div id="st-mem-top"></div>
</div>
<p class="stats-foot" id="st-perf-foot">Memory from /proc + cgroup inside podman container.</p>
</section>
<!-- ========== STEP 1: install apps ========== -->
<section class="step-card" aria-labelledby="step1-title">
<h2 id="step1-title"><span class="step-num">1</span> Get the wallet</h2>
<div class="step-body">
<div class="app-links">
<a class="app-btn"
href="https://play.google.com/store/apps/details?id=net.taler.wallet"
target="_blank" rel="noopener noreferrer"
aria-label="Android Play Store">
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.2 2.4l-.9-1.6c-.1-.2 0-.4.2-.5.2-.1.4 0 .5.2l.9 1.6c.8-.3 1.7-.5 2.6-.5s1.8.2 2.6.5l.9-1.6c.1-.2.3-.3.5-.2.2.1.3.3.2.5l-.9 1.6c1.8.8 3.1 2.5 3.3 4.6H4.9c.2-2.1 1.5-3.8 3.3-4.6zM7 8.5c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zm10 0c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zM5 10.5h14c.6 0 1 .4 1 1v6.2c0 1.3-1 2.3-2.3 2.3h-.5v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-4.8v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-.5C5.9 20 5 19 5 17.7v-6.2c0-.6.4-1 1-1z"/></svg>
Play Store
</a>
<a class="app-btn"
href="https://f-droid.org/en/packages/net.taler.wallet.fdroid/"
target="_blank" rel="noopener noreferrer"
aria-label="Android F-Droid">
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.2 2.4l-.9-1.6c-.1-.2 0-.4.2-.5.2-.1.4 0 .5.2l.9 1.6c.8-.3 1.7-.5 2.6-.5s1.8.2 2.6.5l.9-1.6c.1-.2.3-.3.5-.2.2.1.3.3.2.5l-.9 1.6c1.8.8 3.1 2.5 3.3 4.6H4.9c.2-2.1 1.5-3.8 3.3-4.6zM7 8.5c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zm10 0c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zM5 10.5h14c.6 0 1 .4 1 1v6.2c0 1.3-1 2.3-2.3 2.3h-.5v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-4.8v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-.5C5.9 20 5 19 5 17.7v-6.2c0-.6.4-1 1-1z"/></svg>
F-Droid
</a>
<a class="app-btn ios"
href="https://apps.apple.com/app/taler-wallet/id6463440117"
target="_blank" rel="noopener noreferrer"
aria-label="iOS App Store">
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M16.7 12.6c0-2.1 1.7-3.1 1.8-3.2-1-1.4-2.5-1.6-3-1.7-1.3-.1-2.5.8-3.1.8s-1.6-.7-2.7-.7c-1.4 0-2.7.8-3.4 2.1-1.5 2.5-.4 6.3 1 8.3.7 1 1.5 2.1 2.6 2 1-.1 1.5-.7 2.8-.7s1.7.7 2.8.7 1.9-1 2.6-2c.8-1.1 1.1-2.2 1.1-2.3-.1 0-2.2-.8-2.2-3.3zm-2-6.2c.6-.7 1-1.7.9-2.7-1 .1-2.1.6-2.8 1.4-.6.7-1.1 1.7-.9 2.6 1 .1 2-.5 2.8-1.3z"/></svg>
App Store
</a>
<a class="app-btn hub"
href="https://wallet.taler.net/"
target="_blank" rel="noopener noreferrer"
aria-label="Browser platforms (experimental)">
<span class="hub-icons" aria-hidden="true">
<!-- simplified original marks · gold / blue -->
<!-- Chrome: tri-segment disc + center -->
<svg class="app-ico gold" viewBox="0 0 24 24" role="img" aria-label="Chrome">
<circle cx="12" cy="12" r="10" opacity="0.22"/>
<path d="M12 2a10 10 0 0 1 8.66 5H12V2z" opacity="0.95"/>
<path d="M20.66 7A10 10 0 0 1 12 22l5-8.66H12L20.66 7z" opacity="0.55"/>
<path d="M12 22A10 10 0 0 1 3.34 7H12l-5 8.66L12 22z" opacity="0.75"/>
<circle cx="12" cy="12" r="3.4"/>
</svg>
<!-- Firefox: globe + fox wrap (simple) -->
<svg class="app-ico blue" viewBox="0 0 24 24" role="img" aria-label="Firefox">
<circle cx="12" cy="13" r="6.2" opacity="0.35"/>
<path d="M12 4.2c1.6 0 2.9.7 3.7 1.6.6-.9 1.6-1.4 2.7-1.3-.2 1.6-.9 2.8-1.9 3.6 1.4 1.3 2.3 3.1 2.3 5.1 0 4.1-3.4 7.3-7.8 7.3S3.2 17.3 3.2 13.2c0-2.3 1-4.3 2.7-5.6C5 6.5 4.4 5.1 4.5 3.5c1.1-.1 2.1.5 2.7 1.4C8.1 4.8 9.6 4.2 12 4.2z"/>
<path d="M9.2 11.2c.9-.6 1.8-.9 2.8-.9 1.2 0 2.3.4 3.1 1.1" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" opacity="0.5"/>
</svg>
<!-- Safari: compass -->
<svg class="app-ico gold" viewBox="0 0 24 24" role="img" aria-label="Safari">
<circle cx="12" cy="12" r="9.2" fill="none" stroke="currentColor" stroke-width="1.7"/>
<path d="M14.8 9.2 13 13l-3.8 1.8L11 11l3.8-1.8z"/>
<path d="M12 5.2v1.4M12 17.4v1.4M5.2 12h1.4M17.4 12h1.4" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" opacity="0.55"/>
</svg>
</span>
Browser platforms (experimental)
</a>
</div>
</div>
</section>
<!-- ========== STEP 2: one-step withdraw-exchange ========== -->
<section class="step-card step-money" aria-labelledby="step2-title">
<h2 id="step2-title"><span class="step-num">2</span> Withdraw GOA</h2>
<p class="step-tagline">One-step: add exchange + withdraw</p>
<div class="step-body">
<div class="qr-box">
<div id="qr-withdraw-exchange"></div>
<div class="qr-label">Scan or open in wallet</div>
</div>
<a class="cta teal" id="open-withdraw-exchange"
href="taler://withdraw-exchange/exchange.hacktivism.ch/">
Open in wallet →
</a>
<p class="meta">taler://withdraw-exchange/exchange.hacktivism.ch/</p>
</div>
</section>
<!-- ========== Optional bank UI + mini GOA shop teaser ========== -->
<div class="more-section">
<span class="more-label">Optional · bank UI &amp; GOA shop</span>
<div class="more-card">
<h2>Own bank account?</h2>
<p>Register in the bank UI for your own balance.</p>
<div class="more-qr">
<div class="qr-box">
<div id="qr-webui"></div>
<div class="qr-label">Bank UI · register / login</div>
</div>
</div>
<div class="more-actions">
<a class="cta-sec" href="/webui/">Open bank UI →</a>
</div>
</div>
<aside class="shop-box shop-mini" aria-labelledby="shop-title" style="margin-top:1rem">
<h2 id="shop-title">GOA shop · samples</h2>
<p class="shop-lead">Tap → pay link · more at merchant</p>
<div class="shop-grid">
<button type="button" class="shop-item" data-product="orbit-sticker">
<span class="emoji" aria-hidden="true">🪐</span>
<span class="name">Orbit sticker pack</span>
<span class="price">GOA:2</span>
</button>
<button type="button" class="shop-item" data-product="nebula-coffee">
<span class="emoji" aria-hidden="true"></span>
<span class="name">Nebula coffee</span>
<span class="price">GOA:5</span>
</button>
</div>
<p class="shop-foot">
<a href="https://taler.hacktivism.ch/intro/">For more → taler.hacktivism.ch</a>
</p>
</aside>
</div>
<footer>
hacktivism.ch ·
<a href="https://bank.hacktivism.ch/intro/">Bank</a> ·
<a href="https://exchange.hacktivism.ch/intro/">Exchange</a> ·
<a href="https://taler.hacktivism.ch/intro/">Merchant</a> ·
<a href="https://bank.hacktivism.ch/terms">Terms</a> ·
<a href="https://bank.hacktivism.ch/privacy">Privacy</a> ·
<a href="/config">/config</a> ·
<a href="https://git.hacktivism.ch/hernani/koopa-admin-log/src/branch/main/configs/bank-landing">Source</a>
<p class="privacy-note" style="margin:0.65rem 0 0;font-size:0.78rem;color:var(--muted);line-height:1.4">
Privacy: processing under Swiss FADP (revDSG). What is retained is listed on
<a href="https://bank.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
</p>
</footer>
</main>
<script>
(function () {
var WEBUI = "https://bank.hacktivism.ch/webui/";
/* taler-ops one-step: add exchange + start withdraw */
var WITHDRAW_EXCHANGE = "taler://withdraw-exchange/exchange.hacktivism.ch/";
function makeQr(el, text) {
if (!el) return;
el.innerHTML = "";
if (typeof QRCode === "undefined") {
el.textContent = "QR lib missing";
return;
}
new QRCode(el, {
text: text,
width: 220,
height: 220,
correctLevel: QRCode.CorrectLevel.M
});
}
makeQr(document.getElementById("qr-withdraw-exchange"), WITHDRAW_EXCHANGE);
makeQr(document.getElementById("qr-webui"), WEBUI);
function fmtCest(unix, fallback) {
var base = "";
if (fallback) {
base = String(fallback).replace(/\s*(CEST|CET|UTC|Z)\s*$/i, "").trim();
} else if (unix != null && unix !== "") {
try {
base = new Intl.DateTimeFormat("de-CH", {
timeZone: "Europe/Zurich",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false
}).format(new Date(Number(unix) * 1000));
} catch (e) {
base = "";
}
}
return base ? base + " Europe/Zurich" : "";
}
function loadStats() {
var box = document.getElementById("stats");
var foot = document.getElementById("st-foot");
if (!box) return;
box.hidden = false;
fetch("stats.json", { cache: "no-store" })
.then(function (r) {
if (!r.ok) throw new Error("HTTP " + r.status);
return r.json();
})
.then(function (d) {
if (!d || !d.ok) throw new Error((d && d.error) || "stats not ok");
var w = d.withdraws || {};
var h24 = w.last_24h || {};
var h7 = w.last_7d || {};
var ba = d.bank_accounts || {};
var wl = d.wallets || {};
function set(id, v) {
var el = document.getElementById(id);
if (el) el.textContent = v == null || v === "" ? "—" : String(v);
}
var flow = d.flow || {};
var fin = flow.incoming || {};
var fwd = flow.withdraw || {};
set("st-accounts", ba.total != null ? ba.total : "—");
set("st-wallets", wl.unique_reserves != null ? wl.unique_reserves : "—");
set("st-incoming", fin.amount || flow.total_in || "—");
set("st-withdraw", fwd.amount || w.total_amount || "—");
set("st-24h", h24.amount || "GOA:0");
set("st-7d", h7.amount || "GOA:0");
var list = document.getElementById("st-recent");
if (list) {
list.innerHTML = "";
var rows = (d.recent_withdraws || []).slice(0, 3);
if (!rows.length) {
var empty = document.createElement("li");
empty.className = "meta";
empty.textContent = "—";
list.appendChild(empty);
} else {
rows.forEach(function (row) {
var li = document.createElement("li");
var amt = document.createElement("span");
amt.className = "amt";
amt.textContent = row.amount || "?";
var meta = document.createElement("span");
meta.className = "meta";
meta.textContent = fmtCest(row.at_unix, row.at || row.at_iso) || "";
li.appendChild(amt);
li.appendChild(meta);
list.appendChild(li);
});
}
}
if (foot) {
foot.hidden = false;
foot.className = "stats-foot";
foot.textContent = d.generated_at_human || d.generated_at || "";
}
function msLabel(ms, http) {
if (ms == null || ms === "") return "—";
var s = ms + " ms";
if (http && http !== "200") s += " (" + http + ")";
return s;
}
var p = d.performance || {};
set("st-config-ms", msLabel(p.config_ms, p.config_http));
set("st-int-ms", msLabel(p.integration_ms, p.integration_http));
set("st-webui-ms", msLabel(p.webui_ms, p.webui_http));
set("st-load", p.loadavg || "—");
var mem = p.memory || {};
set("st-mem-ctr", mem.container_rss_human || "—");
set("st-mem-pg", mem.postgres_rss_human
? mem.postgres_rss_human + (mem.postgres_n ? " · " + mem.postgres_n + "p" : "")
: "—");
set("st-mem-java", mem.java_rss_human
? mem.java_rss_human + (mem.java_n ? " · " + mem.java_n + "p" : "")
: "—");
set("st-mem-nginx", mem.nginx_rss_human || "—");
var topBox = document.getElementById("st-mem-top");
if (topBox) {
var tops = mem.top || [];
if (!tops.length) {
topBox.innerHTML = '<p class="meta">—</p>';
} else {
var th = '<table style="width:100%;font-size:0.78rem;border-collapse:collapse"><thead><tr>' +
'<th style="text-align:left;padding:0.25rem;border-bottom:1px solid var(--border)">RSS</th>' +
'<th style="text-align:left;padding:0.25rem;border-bottom:1px solid var(--border)">Comm</th>' +
'<th style="text-align:left;padding:0.25rem;border-bottom:1px solid var(--border)">Command</th></tr></thead><tbody>';
tops.forEach(function (t) {
th += '<tr><td style="padding:0.25rem;color:#5eead4">' + (t.rss_human || "?") +
'</td><td style="padding:0.25rem">' + (t.comm || "") +
'</td><td style="padding:0.25rem;word-break:break-all;color:var(--muted)">' +
(t.cmd || "") + "</td></tr>";
});
th += "</tbody></table>";
topBox.innerHTML = th;
}
}
var pf = document.getElementById("st-perf-foot");
if (pf) {
pf.textContent = "Container RSS + process groups · " +
(d.generated_at_human || d.generated_at || "");
}
return fetch("stats-run.json", { cache: "no-store" })
.then(function (r) { return r.ok ? r.json() : null; })
.catch(function () { return null; });
})
.then(function (run) {
var note = document.getElementById("st-run-note");
if (!note) return;
if (run && run.ok === false) {
note.hidden = false;
note.textContent = "Last refresh failed" +
(run.at_human ? " · " + run.at_human : "") +
" · showing last good data";
} else {
note.hidden = true;
note.textContent = "";
}
})
.catch(function () {
if (foot) {
foot.hidden = false;
foot.className = "stats-foot err";
foot.textContent = "Stats offline";
}
});
}
loadStats();
})();
</script>
<script src="/intro/shop-pay.js"></script>
</body>
</html>