koopa-admin-log/configs/bank-landing/index.html
Hernâni Marques 265a7fa3eb
bank landing: “How the shared account works” as expandable fold
The how-it-works list was always open and competed with the withdraw QR.

Make it a collapsed gold fold: title row with chevron, body expands on click.
Same wireFold helper style as the optional bank UI section.
2026-07-10 20:34:05 +02:00

1213 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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;
}
.balance-note {
margin: 0.75rem 0 0;
padding: 0.55rem 0.7rem;
border-radius: 10px;
background: rgba(232, 168, 56, 0.08);
border: 1px solid rgba(232, 168, 56, 0.32);
color: #f5e6c8;
font-size: 0.82rem;
line-height: 1.4;
text-align: left;
}
.balance-note strong { color: #f0c86a; font-weight: 750; }
/* How shared account works: collapsed until click */
.how-fold {
margin: 0 0 1.15rem;
border: 1px solid rgba(232, 168, 56, 0.4);
border-radius: 14px;
overflow: hidden;
background: rgba(232, 168, 56, 0.08);
color: #f5e6c8;
font-size: 0.88rem;
line-height: 1.45;
}
.how-fold-toggle {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
width: 100%;
margin: 0;
padding: 0.75rem 0.9rem;
border: 0;
background: transparent;
color: #f0c86a;
font: inherit;
font-size: 0.9rem;
font-weight: 750;
text-align: left;
cursor: pointer;
line-height: 1.3;
}
.how-fold-toggle:hover {
background: rgba(232, 168, 56, 0.12);
color: #f5d78a;
}
.how-fold-toggle-text { flex: 1; }
.how-fold-chevron {
flex-shrink: 0;
transition: transform 0.15s ease;
color: #e8a838;
font-size: 0.95rem;
}
.how-fold.open .how-fold-chevron { transform: rotate(90deg); }
.how-fold-body {
display: none;
padding: 0 0.95rem 0.9rem;
border-top: 1px solid rgba(232, 168, 56, 0.28);
}
.how-fold.open .how-fold-body { display: block; }
.how-fold-body ol {
margin: 0.55rem 0 0;
padding-left: 1.2rem;
}
.how-fold-body li { margin: 0.3rem 0; }
.how-fold-body strong { color: #f0c86a; font-weight: 750; }
.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:not(.browser-ico) path,
.app-ico:not(.browser-ico) circle,
.app-ico:not(.browser-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.4rem;
}
.hub-icons .app-ico { width: 1.25rem; height: 1.25rem; }
.hub-icons .browser-ico { border-radius: 3px; flex-shrink: 0; }
.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; }
/* Optional block: collapsed by default, expand like settlement / wallet fold */
.more-section {
margin-top: 1.5rem;
margin-bottom: 1.35rem;
border: 1px solid rgba(201, 184, 160, 0.4);
border-radius: 14px;
overflow: hidden;
background: rgba(18, 28, 28, 0.55);
}
.more-section > .more-label {
display: none; /* replaced by toggle */
}
.more-fold-toggle {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
width: 100%;
margin: 0;
padding: 0.8rem 0.95rem;
border: 0;
background: transparent;
color: #e8c878;
font: inherit;
font-size: 0.88rem;
font-weight: 750;
letter-spacing: 0.04em;
text-align: left;
cursor: pointer;
line-height: 1.3;
}
.more-fold-toggle:hover {
background: rgba(232, 168, 56, 0.1);
color: #f0d090;
}
.more-fold-toggle-text { flex: 1; }
.more-fold-chevron {
flex-shrink: 0;
transition: transform 0.15s ease;
color: #3ecfbf;
font-size: 0.95rem;
}
.more-section.open .more-fold-chevron { transform: rotate(90deg); }
.more-fold-body {
display: none;
padding: 0 0.95rem 1.1rem;
border-top: 1px solid rgba(201, 184, 160, 0.3);
}
.more-section.open .more-fold-body { display: block; }
.more-fold-body > .more-card { margin-top: 0.85rem; }
.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 · shared account</span>
<h1>GOA Exploration Bank</h1>
<p class="sub">
This bank now funds wallets from a <strong>single shared bank account</strong>
for everyone. Install the wallet, scan <strong>one QR</strong> — no personal
bank registration for the demo.
</p>
</header>
<!-- ========== 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="Web browsers (experimental): Chrome, Firefox, Edge, Brave">
<span class="hub-icons" aria-hidden="true" title="Chrome · Firefox · Edge · Brave">
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Chrome">
<circle cx="12" cy="12" r="10" fill="#DEE1E6"/>
<path fill="#EA4335" d="M12 2a10 10 0 0 1 8.66 5H12a4 4 0 0 0-3.46 2L5.1 4.1A10 10 0 0 1 12 2z"/>
<path fill="#FBBC04" d="M20.66 7A10 10 0 0 1 12 22l3.46-6A4 4 0 0 0 12 8h8.66z"/>
<path fill="#34A853" d="M5.1 4.1 8.54 9A4 4 0 0 0 12 16l-3.46 6A10 10 0 0 1 5.1 4.1z"/>
<circle cx="12" cy="12" r="4" fill="#fff"/>
<circle cx="12" cy="12" r="3.1" fill="#4285F4"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Firefox">
<circle cx="12" cy="12.2" r="8.2" fill="#9059FF"/>
<path fill="#FF7139" d="M20.2 9.2c-.2-1.4-1.1-2.6-2.2-3.4.5 1.1.6 2.3.3 3.4-.9-1.6-2.5-2.6-4.4-2.6-1.3 0-2.5.4-3.4 1.1C9.3 5.4 10.9 3.8 13 3.2c-2.5.1-4.7 1.4-6.1 3.4C5.3 8.2 4.6 10.2 4.8 12.3c.3 3.9 3.5 7 7.4 7.1 3.6.1 6.7-2.2 7.7-5.5.4-1.4.4-2.9.3-4.7z"/>
<path fill="#FFD567" d="M12.1 9.4c1.6 0 2.9.9 3.5 2.2-.7-.4-1.5-.6-2.4-.6-2.2 0-4 1.6-4.2 3.7-.5-.8-.7-1.8-.6-2.8.4-1.5 1.8-2.5 3.7-2.5z"/>
<circle cx="12.2" cy="13.4" r="2.35" fill="#20123A"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Edge">
<defs>
<linearGradient id="edge-g-bank" x1="4" y1="4" x2="20" y2="20" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C59A4"/>
<stop offset="0.55" stop-color="#1B9DE2"/>
<stop offset="1" stop-color="#36C5F0"/>
</linearGradient>
</defs>
<circle cx="12" cy="12" r="10" fill="url(#edge-g-bank)"/>
<path fill="#fff" d="M6.2 14.6c.6 3.1 3.3 5.4 6.6 5.4 3.7 0 6.7-2.7 7.1-6.2-1.4 1.7-3.6 2.7-6 2.7-1.5 0-2.9-.4-4-1.2.6.1 1.2.2 1.8.2 3.4 0 5.9-1.9 5.9-4.6 0-.7-.2-1.3-.5-1.9-1.4-2.5-4.3-3.6-7.2-2.7C7.1 7.2 5.3 9.6 5.3 12.3c0 .8.2 1.6.5 2.3h.4z"/>
<path fill="#9CE4FA" opacity="0.9" d="M12.8 8.1c1.9 0 3.5 1.1 3.5 2.8 0 1.9-1.8 3.2-4.4 3.2-1.4 0-2.6-.4-3.5-1 1.5 2.1 4.1 3.2 6.7 2.8 2.2-.3 3.9-1.9 4.4-4-1.1 1.1-2.7 1.7-4.4 1.7-1.6 0-2.9-.7-2.9-1.9 0-1.1 1-1.9 2.5-2.1.4-.1.7-.3.9-.5-.8-.6-1.7-.9-2.8-.9z"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Brave">
<path fill="#FB542B" d="M12 2.2 4.8 5.1v5.6c0 5.1 3.4 9.8 7.2 11.1 3.8-1.3 7.2-6 7.2-11.1V5.1L12 2.2z"/>
<path fill="#fff" d="M12 5.1 8.1 6.7l.7 1.5L7.2 10l1.5.9L7.6 13.2l2.2.4L12 18.2l2.2-4.6 2.2-.4-1.1-2.3 1.5-.9-1.6-1.8.7-1.5L12 5.1z"/>
<path fill="#FB542B" d="M12 7.2 10.3 8l.5 1.1h2.4l.5-1.1L12 7.2zm-1.6 3.1L9.2 11.6l.9.5.4-1.8h-.1zm3.2 0h-.1l.4 1.8.9-.5-1.2-1.3zM10.4 13l-.5 1.1L12 16.1l2.1-2-.5-1.1h-3.2z"/>
</svg>
</span>
Web browsers (experimental)
</a>
</div>
</div>
</section>
<!-- ========== STEP 2: one-click community pool withdraw ========== -->
<section class="step-card step-money" id="withdraw" aria-labelledby="step2-title">
<h2 id="step2-title"><span class="step-num">2</span> Withdraw from shared account</h2>
<p class="step-tagline">One QR · shared community bank account · no registration</p>
<div class="step-body">
<div class="qr-box">
<div id="qr-withdraw-demo"></div>
<div class="qr-label">Shared account · scan or open in wallet</div>
</div>
<a class="cta teal" id="open-withdraw-demo" href="#">
Open shared withdraw →
</a>
<p class="meta" id="withdraw-demo-meta">Preparing shared-account withdraw…</p>
<p class="meta" id="withdraw-demo-amount" hidden></p>
<button type="button" class="cta-sec" id="withdraw-demo-refresh" style="margin-top:0.5rem">
Get a fresh QR
</button>
<p class="balance-note" role="note">
<strong>Surprising balance?</strong>
Shared pool currently holds
<strong id="shared-balance"></strong>
— that is the communal demo account, not yours. You only receive the
small withdraw amount into your wallet.
</p>
</div>
</section>
<div class="how-fold" id="how-fold">
<button type="button" class="how-fold-toggle" id="how-fold-toggle"
aria-expanded="false" aria-controls="how-fold-body">
<span class="how-fold-toggle-text" id="how-title">How the shared account works</span>
<span class="how-fold-chevron" aria-hidden="true"></span>
</button>
<div class="how-fold-body" id="how-fold-body" hidden>
<ol>
<li><strong>Now live:</strong> one <strong>shared bank account</strong> (community pool) already holds GOA for everyone.</li>
<li>Each “Open shared withdraw” / QR is a <strong>one-shot withdraw from that shared account</strong> — you do <em>not</em> log into the bank for this step.</li>
<li>Your GNU Taler app receives coins after the bank confirms (automatic on this demo).</li>
<li>Optional below: register your <em>own</em> bank UI account only if you want a private balance separate from the pool.</li>
</ol>
</div>
</div>
<!-- ========== Optional bank UI + mini GOA shop (collapsed) ========== -->
<div class="more-section" id="more-fold">
<button type="button" class="more-fold-toggle" id="more-fold-toggle"
aria-expanded="false" aria-controls="more-fold-body">
<span class="more-fold-toggle-text">Optional · bank UI &amp; GOA shop</span>
<span class="more-fold-chevron" aria-hidden="true"></span>
</button>
<div class="more-fold-body" id="more-fold-body" hidden>
<div class="more-card">
<h2>Own bank account?</h2>
<p>Register in the bank UI for your own balance (separate from the shared pool).</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>
</div>
<!-- ========== LIVE STATS (before performance) ========== -->
<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>
<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/";
/* Secondary: own-account path (register first) */
var WITHDRAW_EXCHANGE = "taler://withdraw-exchange/exchange.hacktivism.ch/";
function makeQr(el, text) {
if (!el) return;
el.innerHTML = "";
if (!text) {
el.textContent = "…";
return;
}
if (typeof QRCode === "undefined") {
el.textContent = "QR lib missing";
return;
}
// Encode off-DOM so QR is visible (canvas/img not collapsed)
var size = 220;
var scratch = document.createElement("div");
scratch.style.cssText =
"position:fixed;left:-9999px;top:0;width:" + size + "px;height:" + size + "px;opacity:0";
document.body.appendChild(scratch);
try {
new QRCode(scratch, {
text: String(text),
width: size,
height: size,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.M
});
var canvas = scratch.querySelector("canvas");
var dataUrl = canvas ? canvas.toDataURL("image/png") : "";
if (dataUrl) {
var img = document.createElement("img");
img.alt = "QR code";
img.width = size;
img.height = size;
img.src = dataUrl;
img.style.display = "block";
img.style.margin = "0 auto";
el.appendChild(img);
} else {
el.appendChild(scratch.firstChild || document.createTextNode("QR failed"));
}
} catch (e) {
el.textContent = "QR encode failed";
}
if (scratch.parentNode) scratch.parentNode.removeChild(scratch);
}
function setDemoWithdraw(uri, amount) {
uri = String(uri || "").replace(/:443\//g, "/").replace(/:443\?/g, "?");
var open = document.getElementById("open-withdraw-demo");
var meta = document.getElementById("withdraw-demo-meta");
var amtEl = document.getElementById("withdraw-demo-amount");
if (open) {
open.href = uri;
open.classList.remove("disabled");
}
if (meta) meta.textContent = uri;
if (amtEl) {
if (amount) {
amtEl.hidden = false;
amtEl.textContent = "Amount: " + amount + " · from shared account";
} else {
amtEl.hidden = true;
}
}
makeQr(document.getElementById("qr-withdraw-demo"), uri);
}
function loadDemoWithdraw() {
var meta = document.getElementById("withdraw-demo-meta");
var open = document.getElementById("open-withdraw-demo");
if (meta) meta.textContent = "Preparing community withdraw…";
if (open) {
open.removeAttribute("href");
open.classList.add("disabled");
}
// Prefer live mint (fresh op each time); fall back to static withdraw.uri
fetch("/intro/demo-withdraw.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.taler_withdraw_uri) throw new Error(d && d.error ? d.error : "no uri");
setDemoWithdraw(d.taler_withdraw_uri, d.amount || "");
})
.catch(function () {
return fetch("/intro/withdraw.uri", { cache: "no-store" })
.then(function (r) {
if (!r.ok) throw new Error("no fallback");
return r.text();
})
.then(function (t) {
var uri = String(t || "").trim();
if (!uri) throw new Error("empty");
return fetch("/intro/withdraw.amount", { cache: "no-store" })
.then(function (r) {
return r.ok ? r.text() : Promise.resolve("");
})
.then(function (a) {
setDemoWithdraw(uri, String(a || "").trim());
});
});
})
.catch(function (err) {
if (meta) {
meta.textContent =
"Could not prepare withdraw — try again or use bank UI. " +
(err && err.message ? err.message : "");
}
});
}
loadDemoWithdraw();
var refreshBtn = document.getElementById("withdraw-demo-refresh");
if (refreshBtn) refreshBtn.onclick = function () { loadDemoWithdraw(); };
/* Collapsible folds (like settlement QR fold) */
function wireFold(foldId, btnId, bodyId, onOpen) {
var fold = document.getElementById(foldId);
var btn = document.getElementById(btnId);
var body = document.getElementById(bodyId);
if (!fold || !btn || !body) return;
btn.addEventListener("click", function () {
var open = !fold.classList.contains("open");
fold.classList.toggle("open", open);
btn.setAttribute("aria-expanded", open ? "true" : "false");
if (open) {
body.removeAttribute("hidden");
if (onOpen) onOpen();
} else {
body.setAttribute("hidden", "");
}
});
}
wireFold("how-fold", "how-fold-toggle", "how-fold-body");
(function () {
var webuiPainted = false;
wireFold("more-fold", "more-fold-toggle", "more-fold-body", function () {
if (webuiPainted) return;
makeQr(document.getElementById("qr-webui"), WEBUI);
webuiPainted = true;
});
})();
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");
set("shared-balance", d.balance_explorer || "—");
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>