koopa-admin-log/configs/bank-landing/index.html
Hernâni Marques 53ce943d87
bank landing: blue stack bar (libeufin versions only)
Taler-specific packages at the top in small monospace blue text.
2026-07-10 22:48:00 +02:00

1978 lines
70 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>
<meta name="description" content="Get your GOA in one step from the shared bank account, try GOA shop samples, pay with GNU Taler — Intergalactic · GOA · no IBAN." />
<link rel="canonical" href="https://bank.hacktivism.ch/intro/" />
<!-- Link previews (chats / social): absolute image + no-IBAN copy matching the page badge -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="hacktivism.ch · GOA" />
<meta property="og:locale" content="en_US" />
<meta property="og:title" content="GOA Exploration Bank · no IBAN" />
<meta property="og:description" content="Intergalactic · GOA · no IBAN. Get your GOA in one step from the shared bank account, browse GOA shop samples, pay with GNU Taler." />
<meta property="og:url" content="https://bank.hacktivism.ch/intro/" />
<meta property="og:image" content="https://bank.hacktivism.ch/intro/og-goa-shop.png?v=android1" />
<meta property="og:image:secure_url" content="https://bank.hacktivism.ch/intro/og-goa-shop.png?v=android1" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="GOA Shop — Intergalactic · GOA · no IBAN — pay with GNU Taler" />
<link rel="image_src" href="https://bank.hacktivism.ch/intro/og-goa-shop.png?v=android1" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="GOA Exploration Bank · no IBAN" />
<meta name="twitter:description" content="Intergalactic · GOA · no IBAN. Get your GOA in one step from the shared bank account, browse GOA shop samples, pay with GNU Taler." />
<meta name="twitter:image" content="https://bank.hacktivism.ch/intro/og-goa-shop.png?v=android1" />
<meta name="twitter:image:alt" content="GOA Shop — Intergalactic · GOA · no IBAN — pay with GNU Taler" />
<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;
}
/* Technical stack strip (software + versions) — top of page */
.stack-bar {
margin: 0;
padding: 0.35rem 0.75rem;
background: #0042b3;
color: #d6e4ff;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.62rem;
font-weight: 500;
letter-spacing: 0.02em;
line-height: 1.35;
text-align: center;
border-bottom: 1px solid rgba(164, 201, 255, 0.35);
}
.stack-bar strong {
color: #fff;
font-weight: 650;
}
.stack-bar .sep { color: rgba(164, 201, 255, 0.55); margin: 0 0.2rem; }
.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);
padding: 1rem 1.05rem 1.05rem;
margin-bottom: 1rem;
}
.step-card.step-money h2 {
font-size: 1.15rem;
margin: 0 0 0.15rem;
}
.step-card.step-money .step-num {
width: 1.75rem;
height: 1.75rem;
font-size: 1rem;
margin-right: 0.4rem;
}
.step-card.step-money .step-tagline {
margin: 0 0 0.65rem;
font-size: 0.88rem;
}
.step-card.step-money .step-body {
gap: 0.5rem;
}
.step-card.step-money .qr-taler-wrap {
max-width: 200px;
}
.step-card.step-money .qr-taler-wrap .goa-pay-taler-qr {
margin-bottom: 0.3rem;
max-width: 200px;
padding: 7px;
border-radius: 16px;
}
.step-card.step-money .goa-pay-taler-qr__inner {
padding: 7px;
border-radius: 14px;
min-height: 0;
}
.step-card.step-money .qr-label {
margin-top: 0;
font-size: 0.78rem;
}
.step-card.step-money .cta {
font-size: 0.92rem;
padding: 0.55rem 1.05rem;
border-radius: 10px;
}
.step-card.step-money a.meta-uri {
margin: 0.1rem 0 0;
font-size: 0.68rem;
max-width: 20rem;
}
.step-card.step-money .balance-note {
margin: 0.35rem 0 0;
padding: 0.4rem 0.55rem;
font-size: 0.76rem;
line-height: 1.35;
}
.step-card.step-money button.gen-own-acct {
margin-top: 0.05rem;
font-size: 0.76rem;
}
.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; }
/* Page QRs: QR_Taler blue ring (same classes as shop-pay / merchant webui) */
.qr-taler-wrap {
text-align: center;
width: 100%;
max-width: 280px;
}
.qr-taler-wrap .goa-pay-taler-qr {
margin-bottom: 0.55rem;
}
.qr-label {
margin-top: 0.15rem;
font-size: 0.85rem;
color: var(--muted);
font-weight: 700;
max-width: 260px;
margin-left: auto;
margin-right: auto;
}
.more-card .qr-label { color: #c9b8a0; }
.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%;
}
a.meta-uri {
display: block;
font-size: 0.72rem;
color: #5eead4;
word-break: break-all;
margin: 0.35rem 0 0;
text-align: center;
max-width: 22rem;
text-decoration: none;
border-bottom: 1px solid rgba(94, 234, 212, 0.35);
line-height: 1.35;
cursor: pointer;
}
a.meta-uri:hover {
color: #99f6e4;
border-bottom-color: #5eead4;
}
a.meta-uri.disabled,
a.meta-uri[href="#"] {
color: var(--muted);
border-bottom-color: transparent;
cursor: default;
pointer-events: none;
}
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(232, 168, 56, 0.4);
color: #f0d090;
}
.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; }
/* hub browser marks use explicit gold/blue fills in SVG */
.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;
}
/* git branch: solid nodes, stroked edges */
.app-ico.branch-ico circle {
fill: currentColor;
stroke: none;
}
.app-ico.branch-ico path {
fill: none;
stroke: currentColor;
stroke-width: 1.7;
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: #f0c86a; }
.app-btn: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; }
/* command-line marks: gold penguin (deb) · gold terminal (posix) */
button.app-btn.cli-toggle > .app-ico.penguin-ico,
button.app-btn.cli-toggle > .app-ico.console-ico {
width: 1.2rem;
height: 1.2rem;
flex-shrink: 0;
border-radius: 0;
}
button.app-btn.cli-toggle > .app-ico.console-ico rect,
button.app-btn.cli-toggle > .app-ico.console-ico path {
fill: none;
}
.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: #f0c86a; }
.step-tagline .app-ico.android { color: #f0c86a; }
/* Two tech options side-by-side; both open the same panel below */
.cli-tech-row {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
width: 100%;
flex: 1 1 100%;
}
button.app-btn.cli-toggle {
flex: 1 1 calc(50% - 0.25rem);
min-width: 9.5rem;
max-width: 100%;
border-color: rgba(232, 168, 56, 0.4);
color: #f0d090;
font-size: 0.72rem;
cursor: pointer;
font-family: inherit;
font-weight: 650;
width: auto;
justify-content: flex-start;
align-items: center;
gap: 0.4rem;
padding: 0.55rem 0.65rem;
line-height: 1.25;
}
button.app-btn.cli-toggle .cli-label {
flex: 1 1 auto;
text-align: left;
min-width: 0;
}
button.app-btn.cli-toggle .cli-chevron {
margin-left: auto;
flex-shrink: 0;
color: #3ecfbf;
font-size: 1rem;
font-weight: 700;
line-height: 1;
transition: transform 0.15s ease;
}
button.app-btn.cli-toggle[aria-expanded="true"] .cli-chevron {
transform: rotate(90deg);
}
button.app-btn.cli-toggle .app-ico { color: #f0c86a; }
button.app-btn.cli-toggle[aria-expanded="true"] {
border-color: rgba(240, 200, 106, 0.75);
background: rgba(232, 168, 56, 0.12);
}
/* Shared expand area under both tech buttons (does not cover withdraw) */
.cli-panel {
display: none;
width: 100%;
max-width: 100%;
margin: 0.55rem 0 0.25rem;
text-align: left;
border: 1px solid rgba(232, 168, 56, 0.32);
border-radius: 12px;
background: rgba(0, 0, 0, 0.28);
padding: 0.85rem 0.9rem 1rem;
box-sizing: border-box;
position: relative;
z-index: 0;
clear: both;
}
.cli-panel.open { display: block; }
.cli-pane { display: none; }
.cli-pane.active { display: block; }
.step-card[aria-labelledby="step1-title"] .step-body {
align-items: stretch;
}
.step-card[aria-labelledby="step1-title"] .app-links {
width: 100%;
}
#withdraw {
position: relative;
z-index: 1;
}
.cli-panel .cli-lead {
margin: 0 0 0.75rem;
color: var(--muted);
font-size: 0.86rem;
line-height: 1.4;
}
.cli-panel a {
color: #e8c878;
text-decoration: none;
border-bottom: 1px solid rgba(232, 168, 56, 0.35);
}
.cli-panel a:hover {
color: #f0d090;
border-bottom-color: #e8c878;
}
.cli-panel .cli-h {
margin: 0.85rem 0 0.35rem;
font-size: 0.78rem;
font-weight: 750;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #f0c86a;
}
.cli-panel .cli-h:first-of-type { margin-top: 0.15rem; }
.cli-panel .cli-note {
margin: 0 0 0.45rem;
color: var(--muted);
font-size: 0.8rem;
line-height: 1.4;
}
/* Allow selecting/copying individual lines (not whole block at once) */
.cli-block {
margin: 0 0 0.35rem;
padding: 0.65rem 0.7rem;
border-radius: 10px;
background: rgba(0, 0, 0, 0.45);
border: 1px solid rgba(240, 200, 106, 0.22);
color: #e8e0d0;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.7rem;
line-height: 1.45;
overflow-x: auto;
white-space: pre;
-webkit-overflow-scrolling: touch;
-webkit-user-select: text;
user-select: text;
cursor: text;
}
.cli-block .cmt { color: #8a9a90; }
.cli-block .hi { color: #5eead4; }
/* 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;
background: transparent;
font: inherit;
cursor: pointer;
width: 100%;
max-width: 22rem;
box-sizing: border-box;
}
button.cta-sec:hover {
background: rgba(62, 207, 191, 0.1);
}
.cta-sec.merchant {
color: #f0d090;
border-color: rgba(232, 168, 56, 0.35);
}
/* Small distinct control under shared withdraw (not a second full CTA) */
button.gen-own-acct {
display: inline-block;
margin: 0.15rem auto 0;
padding: 0.2rem 0.35rem;
border: 0;
border-bottom: 1px solid rgba(240, 200, 106, 0.55);
border-radius: 0;
background: transparent;
color: #f0c86a;
font: inherit;
font-size: 0.8rem;
font-weight: 650;
letter-spacing: 0.01em;
cursor: pointer;
width: auto;
max-width: none;
line-height: 1.35;
text-decoration: none;
}
button.gen-own-acct:hover,
button.gen-own-acct:focus-visible {
color: #ffe6a8;
border-bottom-color: #f0c86a;
background: transparent;
outline: none;
}
button.gen-own-acct:active {
opacity: 0.85;
}
.more-qr {
display: flex;
justify-content: center;
margin: 1rem 0 0.25rem;
}
.more-qr .qr-taler-wrap {
transform: scale(0.92);
transform-origin: center top;
}
.own-acct-lead {
margin: 0 0 0.85rem;
color: var(--muted);
font-size: 0.95rem;
line-height: 1.45;
}
.own-acct-lead strong { color: #e8c878; }
.own-acct-creds {
margin: 0 0 0.85rem;
padding: 0.75rem 0.85rem;
border-radius: 12px;
background: rgba(94, 234, 212, 0.08);
border: 1px solid rgba(94, 234, 212, 0.35);
text-align: left;
}
.own-acct-warn {
margin: 0 0 0.65rem;
font-size: 0.86rem;
color: #ccfbf1;
line-height: 1.4;
}
.own-acct-warn strong { color: #5eead4; }
.own-acct-dl {
margin: 0;
display: grid;
gap: 0.45rem;
}
.own-acct-dl div {
display: grid;
grid-template-columns: 5.5rem 1fr;
gap: 0.35rem;
align-items: baseline;
}
.own-acct-dl dt {
margin: 0;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
}
.own-acct-dl dd {
margin: 0;
}
.own-acct-dl code {
font-size: 0.95rem;
font-weight: 700;
color: #f0d090;
word-break: break-all;
user-select: all;
}
#auto-account-btn {
border: 0;
cursor: pointer;
width: 100%;
max-width: 22rem;
}
#auto-account-btn:disabled {
opacity: 0.55;
cursor: wait;
}
.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">
<p class="stack-bar" role="note" title="Taler software on this bank service">
<strong>stack</strong><span class="sep">·</span>
libeufin-bank&nbsp;1.6.6
<span class="sep">·</span>
libeufin-common&nbsp;1.6.6
</p>
<main>
<header class="hero">
<span class="badge">Intergalactic · GOA · no IBAN</span>
<h1>GOA Exploration Bank</h1>
<p class="sub">
Get your <strong>GOA in one step</strong> from the
<strong>shared bank account</strong> into your wallet — no registration.
</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, Opera, Ubuntu Touch">
<span class="hub-icons" aria-hidden="true" title="Chrome · Firefox · Opera · Ubuntu Touch">
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Chrome">
<circle cx="12" cy="12" r="10" fill="#3ecfbf" opacity="0.22"/>
<path fill="#3ecfbf" 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="#3ecfbf" opacity="0.8" d="M20.66 7A10 10 0 0 1 12 22l3.46-6A4 4 0 0 0 12 8h8.66z"/>
<path fill="#3ecfbf" opacity="0.55" 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="3.2" fill="#3ecfbf"/>
</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="#3ecfbf" opacity="0.28"/>
<path fill="#3ecfbf" 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"/>
<circle cx="12.2" cy="13.2" r="2.4" fill="#3ecfbf"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Opera">
<circle cx="12" cy="12" r="9.2" fill="none" stroke="#3ecfbf" stroke-width="2"/>
<ellipse cx="12" cy="12" rx="4.15" ry="7.1" fill="none" stroke="#3ecfbf" stroke-width="1.85"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Ubuntu Touch">
<circle cx="12" cy="12" r="2.25" fill="#3ecfbf"/>
<circle cx="12" cy="4.85" r="1.7" fill="#3ecfbf"/>
<circle cx="18.15" cy="15.55" r="1.7" fill="#3ecfbf"/>
<circle cx="5.85" cy="15.55" r="1.7" fill="#3ecfbf"/>
</svg>
</span>
Web browsers (experimental)
</a>
<!-- Linux (left) · POSIX (right) — same expand area below -->
<div class="cli-tech-row" role="group" aria-label="Command-line wallet paths">
<button type="button" class="app-btn hub cli-toggle" id="deb-cli-toggle"
aria-expanded="false" aria-controls="cli-shared-body"
data-cli-pane="cli-pane-deb"
aria-label="Debian/Ubuntu · Command line">
<svg class="app-ico browser-ico penguin-ico" viewBox="0 0 24 24" width="20" height="20" role="img" aria-label="Linux penguin">
<path fill="#f0c86a" d="M12 2.8c-2.2 0-3.8 1.7-3.8 3.6 0 .5.1 1 .3 1.4C6.4 8.8 5 11 5 13.6c0 2.6 1.3 4.5 3.2 5.6-.2.5-.3 1-.3 1.5 0 1.2.9 2.2 2.2 2.2.7 0 1.3-.3 1.7-.8.4.1.8.2 1.2.2s.8-.1 1.2-.2c.4.5 1 .8 1.7.8 1.3 0 2.2-1 2.2-2.2 0-.5-.1-1-.3-1.5 1.9-1.1 3.2-3 3.2-5.6 0-2.6-1.4-4.8-3.5-5.8.2-.4.3-.9.3-1.4C15.8 4.5 14.2 2.8 12 2.8z"/>
<ellipse cx="12" cy="14.5" rx="3.2" ry="4.2" fill="#1a1410" opacity="0.28"/>
<circle cx="10.4" cy="6.6" r="0.75" fill="#1a1410"/>
<circle cx="13.6" cy="6.6" r="0.75" fill="#1a1410"/>
<path fill="#e8a838" d="M11.2 8 12 9.4 12.8 8z"/>
<ellipse cx="9.2" cy="21.2" rx="1.8" ry="0.9" fill="#e8a838"/>
<ellipse cx="14.8" cy="21.2" rx="1.8" ry="0.9" fill="#e8a838"/>
</svg>
<span class="cli-label">Debian/Ubuntu · Command line</span>
<span class="cli-chevron" aria-hidden="true"></span>
</button>
<button type="button" class="app-btn hub cli-toggle" id="unix-cli-toggle"
aria-expanded="false" aria-controls="cli-shared-body"
data-cli-pane="cli-pane-unix"
aria-label="Unix / POSIX · from source">
<svg class="app-ico browser-ico console-ico" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
<rect x="2.5" y="4" width="19" height="15" rx="2.2" fill="none" stroke="#f0c86a" stroke-width="1.7"/>
<path d="M6.5 9.2 9.8 12 6.5 14.8" fill="none" stroke="#f0c86a" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5 14.8h5.2" fill="none" stroke="#f0c86a" stroke-width="1.7" stroke-linecap="round"/>
</svg>
<span class="cli-label">Unix / POSIX · from source</span>
<span class="cli-chevron" aria-hidden="true"></span>
</button>
</div>
</div>
<!-- Shared expand area under both tech buttons (sibling of app-links; above withdraw) -->
<div class="cli-panel" id="cli-shared-body" hidden>
<div class="cli-pane" id="cli-pane-deb">
<p class="cli-lead">
<strong>Usual path on Debian &amp; Ubuntu:</strong> install the prebuilt
<a href="https://docs.taler.net/manpages/taler-wallet-cli.1.html" target="_blank" rel="noopener noreferrer"><strong>taler-wallet-cli</strong></a>
package from
<a href="https://deb.taler.net/" target="_blank" rel="noopener noreferrer">deb.taler.net</a>
— no compile. Then withdraw &amp; pay with the shell recipes below.
</p>
<p class="cli-h">1 · Install</p>
<p class="cli-note">
Add the GNU Taler apt source for your release from
<a href="https://deb.taler.net/" target="_blank" rel="noopener noreferrer">deb.taler.net</a>
(key + <code>sources.list</code> line), then:
</p>
<pre class="cli-block"><span class="cmt"># after the Taler apt source is configured</span>
sudo apt-get update
sudo apt-get install -y taler-wallet-cli
taler-wallet-cli --version</pre>
<p class="cli-h">2 · Withdraw &amp; pay (shared pool)</p>
<pre class="cli-block"><span class="cmt"># after package install — taler-wallet-cli is on PATH</span>
export EX=https://exchange.hacktivism.ch/
export WDB="$HOME/.local/share/taler-wallet-goa.sqlite3"
mkdir -p "$(dirname "$WDB")"
alias tw='taler-wallet-cli --wallet-db="$WDB" --no-throttle'
tw exchanges add "$EX" || true
tw exchanges update "$EX" || true
tw exchanges accept-tos "$EX"
WURI=$(curl -fsS https://bank.hacktivism.ch/intro/demo-withdraw.json \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["taler_withdraw_uri"])')
tw withdraw accept-uri --exchange "$EX" "$WURI"
tw run-until-done
tw balance
PAY=taler://pay-template/taler.hacktivism.ch/instances/goa-shop/orbit-sticker
tw handle-uri --yes --non-interactive "$PAY"
tw run-until-done
tw balance</pre>
</div>
<div class="cli-pane" id="cli-pane-unix">
<p class="cli-lead">
<strong>Other Unix / POSIX systems</strong> (and anyone who prefers source):
there is usually <em>no</em> distro package — build
<a href="https://docs.taler.net/manpages/taler-wallet-cli.1.html" target="_blank" rel="noopener noreferrer"><strong>taler-wallet-cli</strong></a>
from the monorepo on
<a href="https://git.taler.net/" target="_blank" rel="noopener noreferrer">git.taler.net</a>
(<code>taler-typescript-core</code><code>packages/taler-wallet-cli</code>),
or run the compiled <code>.mjs</code> with Node without a system install.
</p>
<p class="cli-h">1 · Packages you usually need (build)</p>
<p class="cli-note">
<strong>Typical:</strong> <code>git</code>, <code>curl</code>,
<code>python3</code> (≥&nbsp;3.8), <code>nodejs</code> (≥&nbsp;20),
<code>npm</code>, <code>pnpm</code> (≥&nbsp;11 — often
<code>npm install -g pnpm</code>), <code>jq</code>, <code>zip</code>.
<strong>At runtime</strong> for the GOA recipes: <code>curl</code>,
<code>python3</code>, and either <code>taler-wallet-cli</code> on
<code>PATH</code> or <code>node</code> + the <code>.mjs</code> file.
</p>
<pre class="cli-block"><span class="cmt"># example on a Debian-like host used only as a build box</span>
sudo apt-get update
sudo apt-get install -y git curl python3 nodejs npm jq zip
sudo npm install -g pnpm
<span class="cmt"># Fedora/RHEL-ish: dnf install git curl python3 nodejs npm jq zip (then npm i -g pnpm)</span>
<span class="cmt"># macOS: brew install git python node jq (then npm i -g pnpm)</span></pre>
<p class="cli-h">2 · Clone, compile, install <em>or</em> just run</p>
<pre class="cli-block"><span class="cmt">#!/bin/sh</span>
set -eu
git clone https://git.taler.net/taler-typescript-core.git
<span class="cmt"># or: git clone git://git.taler.net/taler-typescript-core.git</span>
cd taler-typescript-core
./bootstrap
./configure <span class="cmt"># optional: --prefix="$HOME/.local"</span>
make
<span class="cmt"># optional system install: make install</span>
pnpm install
pnpm run compile
<span class="cmt"># just use without install (usual for day-to-day dev):</span>
WCLI="$PWD/packages/taler-wallet-cli/bin/taler-wallet-cli.mjs"
node "$WCLI" --version</pre>
<p class="cli-h">3 · Withdraw &amp; pay (POSIX <code>sh</code>)</p>
<pre class="cli-block"><span class="cmt">#!/bin/sh</span>
set -eu
: "${HOME?HOME is unset}"
EX=https://exchange.hacktivism.ch/
BANK=https://bank.hacktivism.ch
MER=taler.hacktivism.ch
WDB=${WDB:-$HOME/.local/share/taler-wallet-goa.sqlite3}
mkdir -p "$(dirname "$WDB")"
<span class="cmt"># package/install path:</span>
<span class="cmt"># tw() { taler-wallet-cli --wallet-db="$WDB" --no-throttle "$@"; }</span>
<span class="cmt"># monorepo path (no make install):</span>
WCLI=${WCLI:-$HOME/src/taler-typescript-core/packages/taler-wallet-cli/bin/taler-wallet-cli.mjs}
tw() { node "$WCLI" --wallet-db="$WDB" --no-throttle "$@"; }
tw exchanges add "$EX" || true
tw exchanges update "$EX" || true
tw exchanges accept-tos "$EX"
WURI=$(curl -fsS "$BANK/intro/demo-withdraw.json" \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["taler_withdraw_uri"])')
tw withdraw accept-uri --exchange "$EX" "$WURI"
tw run-until-done
tw balance
PAY="taler://pay-template/$MER/instances/goa-shop/orbit-sticker"
tw handle-uri --yes --non-interactive "$PAY"
tw run-until-done
tw balance</pre>
</div>
</div>
</div>
</section>
<!-- ========== STEP 2: one-click community pool withdraw (compact) ========== -->
<section class="step-card step-money" id="withdraw" aria-labelledby="step2-title">
<h2 id="step2-title"><span class="step-num">2</span> Withdraw GOA</h2>
<p class="step-tagline">One step · shared pool · no registration</p>
<div class="step-body">
<div class="qr-taler-wrap">
<div class="goa-pay-taler-qr" id="qr-withdraw-demo-wrap">
<div class="goa-pay-taler-qr__inner" id="qr-withdraw-demo"></div>
<img class="goa-pay-taler-qr__logo" id="qr-withdraw-demo-logo" alt="" />
</div>
<div class="qr-label">Scan in wallet</div>
</div>
<a class="cta teal" id="open-withdraw-demo" href="#">
Open in wallet →
</a>
<a class="meta-uri" id="withdraw-demo-meta" href="#">Preparing withdraw…</a>
<button type="button" class="gen-own-acct" id="generate-own-account">
Generate own account
</button>
<p class="balance-note" role="note">
<strong>Surprising balance?</strong>
Pool holds <strong id="shared-balance"></strong> (shared, but also yours).
You only get the small withdraw amount.
</p>
</div>
</section>
<!-- ========== GOA shop (after withdraw, always visible) ========== -->
<aside class="shop-box" aria-labelledby="shop-title">
<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 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: own bank UI account (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 · own bank account</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 class="own-acct-lead">
We can <strong>create a personal bank account for you</strong> automatically
(you do not pick a username or password) — separate from the shared pool.
</p>
<div class="more-actions" style="margin-bottom:0.75rem">
<button type="button" class="cta teal" id="auto-account-btn">
Create my bank account →
</button>
</div>
<div class="own-acct-creds" id="auto-account-box" hidden>
<p class="own-acct-warn" role="status">
<strong>Created for you.</strong>
Copy these credentials now — they are not stored for recovery.
Login at the bank UI.
</p>
<dl class="own-acct-dl">
<div><dt>Username</dt><dd><code id="auto-account-user"></code></dd></div>
<div><dt>Password</dt><dd><code id="auto-account-pass"></code></dd></div>
</dl>
<p class="meta" id="auto-account-status" style="margin:0.5rem 0 0"></p>
<div class="more-qr" id="auto-account-qr-block" style="margin-top:0.85rem" hidden>
<div class="qr-taler-wrap">
<div class="goa-pay-taler-qr" id="qr-auto-acct-wrap">
<div class="goa-pay-taler-qr__inner" id="qr-auto-acct"></div>
<img class="goa-pay-taler-qr__logo" id="qr-auto-acct-logo" alt="" />
</div>
<div class="qr-label" id="qr-auto-acct-label">Your account · bank UI login</div>
</div>
</div>
</div>
<div class="more-qr">
<div class="qr-taler-wrap">
<div class="goa-pay-taler-qr" id="qr-webui-wrap">
<div class="goa-pay-taler-qr__inner" id="qr-webui"></div>
<img class="goa-pay-taler-qr__logo" id="qr-webui-logo" alt="" />
</div>
<div class="qr-label">Bank UI · open site</div>
</div>
</div>
<div class="more-actions">
<a class="cta-sec" href="/webui/" id="auto-account-webui">Open bank UI →</a>
<button type="button" class="cta-sec" id="auto-account-again" hidden style="margin-top:0.45rem">
Generate another account
</button>
</div>
</div>
</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/";
/* QR_Taler: off-DOM encode → PNG img + center logo (same as shop-pay / merchant) */
/* Prefer PNG: Android WebView often fails the official Penpot SVG
(root fill="none" + wordmark paths inherit black from a parent <g>
→ blue T only, missing "taler"). PNG matches taler-assets qr-logo.png. */
function logoSrc() {
try {
var b = document.querySelector("base");
if (b && b.href) return new URL("qr-logo.png", b.href).href;
} catch (e) {}
return "/intro/qr-logo.png";
}
function setQrLogo(imgEl) {
if (!imgEl) return;
imgEl.alt = "Taler";
imgEl.width = 100;
imgEl.height = 50;
imgEl.decoding = "async";
imgEl.src = logoSrc();
imgEl.onerror = function () {
/* fallback to fixed SVG if PNG missing */
try {
var b = document.querySelector("base");
imgEl.src = b && b.href
? new URL("qr-logo.svg", b.href).href
: "/intro/qr-logo.svg";
} catch (e2) {
imgEl.style.display = "none";
}
};
imgEl.style.display = "";
}
function makeQr(el, text, logoEl, sizeOpt) {
if (!el) return;
el.innerHTML = "";
var size = sizeOpt || 220;
if (!text) {
el.innerHTML = '<p style="padding:1rem;color:#666;font-size:0.85rem">…</p>';
return;
}
if (typeof QRCode === "undefined") {
el.innerHTML = '<p style="padding:1rem;color:#b91c1c;font-size:0.85rem">QR lib missing</p>';
return;
}
var level =
QRCode.CorrectLevel && QRCode.CorrectLevel.M != null
? QRCode.CorrectLevel.M
: 1;
var scratch = document.createElement("div");
scratch.setAttribute("aria-hidden", "true");
scratch.style.cssText =
"position:fixed;left:-9999px;top:0;width:" +
size +
"px;height:" +
size +
"px;overflow:hidden;opacity:0;pointer-events:none";
document.body.appendChild(scratch);
var dataUrl = "";
try {
new QRCode(scratch, {
text: String(text),
width: size,
height: size,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: level
});
var canvas = scratch.querySelector("canvas");
var libImg = scratch.querySelector("img");
if (canvas && canvas.width > 0) {
try {
dataUrl = canvas.toDataURL("image/png");
} catch (e1) {}
}
if (!dataUrl && libImg && libImg.src && libImg.src.indexOf("data:") === 0) {
dataUrl = libImg.src;
}
} catch (err) {
dataUrl = "";
}
if (scratch.parentNode) scratch.parentNode.removeChild(scratch);
if (!dataUrl) {
el.innerHTML = '<p style="padding:1rem;color:#b91c1c;font-size:0.85rem">QR encode failed</p>';
return;
}
var img = document.createElement("img");
img.alt = "QR code";
img.width = size;
img.height = size;
img.src = dataUrl;
img.style.display = "block";
img.style.width = size + "px";
img.style.height = size + "px";
img.style.maxWidth = "100%";
img.style.margin = "0 auto";
img.style.background = "#fff";
el.appendChild(img);
setQrLogo(logoEl);
}
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");
if (open) {
open.href = uri;
open.classList.remove("disabled");
}
if (meta) {
meta.href = uri;
meta.textContent = uri;
meta.classList.remove("disabled");
meta.setAttribute("title", "Open in wallet");
}
makeQr(
document.getElementById("qr-withdraw-demo"),
uri,
document.getElementById("qr-withdraw-demo-logo"),
176
);
}
function loadDemoWithdraw() {
var meta = document.getElementById("withdraw-demo-meta");
var open = document.getElementById("open-withdraw-demo");
if (meta) {
meta.textContent = "Preparing withdraw…";
meta.href = "#";
meta.classList.add("disabled");
}
if (open) {
open.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();
/* Collapsible folds (like settlement QR fold) */
function openFold(foldId, btnId, bodyId, onOpen) {
var fold = document.getElementById(foldId);
var btn = document.getElementById(btnId);
var body = document.getElementById(bodyId);
if (!fold || !body) return;
fold.classList.add("open");
if (btn) btn.setAttribute("aria-expanded", "true");
body.removeAttribute("hidden");
if (onOpen) onOpen();
}
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 wireCliFolds() {
var panel = document.getElementById("cli-shared-body");
var btns = [
document.getElementById("deb-cli-toggle"),
document.getElementById("unix-cli-toggle")
].filter(Boolean);
if (!panel || !btns.length) return;
var panes = panel.querySelectorAll(".cli-pane");
function showPane(paneId) {
for (var i = 0; i < panes.length; i++) {
panes[i].classList.toggle("active", panes[i].id === paneId);
}
}
function setExpanded(activeBtn) {
for (var i = 0; i < btns.length; i++) {
btns[i].setAttribute(
"aria-expanded",
btns[i] === activeBtn ? "true" : "false"
);
}
}
function closeAll() {
panel.classList.remove("open");
panel.setAttribute("hidden", "");
for (var i = 0; i < panes.length; i++) panes[i].classList.remove("active");
setExpanded(null);
}
btns.forEach(function (btn) {
btn.addEventListener("click", function () {
var paneId = btn.getAttribute("data-cli-pane");
var already =
panel.classList.contains("open") &&
btn.getAttribute("aria-expanded") === "true";
if (already) {
closeAll();
return;
}
showPane(paneId);
panel.classList.add("open");
panel.removeAttribute("hidden");
setExpanded(btn);
});
});
})();
var paintWebuiQrOnce = (function () {
var webuiPainted = false;
return function () {
if (webuiPainted) return;
makeQr(
document.getElementById("qr-webui"),
WEBUI,
document.getElementById("qr-webui-logo")
);
webuiPainted = true;
};
})();
wireFold("more-fold", "more-fold-toggle", "more-fold-body", paintWebuiQrOnce);
(function () {
function createAutoAccount() {
var btn = document.getElementById("auto-account-btn");
var box = document.getElementById("auto-account-box");
var st = document.getElementById("auto-account-status");
var again = document.getElementById("auto-account-again");
if (btn) {
btn.disabled = true;
btn.textContent = "Creating account…";
}
if (st) st.textContent = "";
fetch("/intro/auto-account.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) || "create failed");
if (box) box.hidden = false;
var u = document.getElementById("auto-account-user");
var p = document.getElementById("auto-account-pass");
if (u) u.textContent = d.username || d.name || d.display_name || "—";
if (p) p.textContent = d.password || "—";
if (st) {
st.textContent =
"Account created for you · " +
(d.username || d.name || "") +
" · save username & password";
}
/* Fancy QR_Taler: encode bank UI URL (login with credentials above) */
var qrBlock = document.getElementById("auto-account-qr-block");
var qrLabel = document.getElementById("qr-auto-acct-label");
var webui = d.webui || WEBUI;
if (qrBlock) qrBlock.hidden = false;
if (qrLabel) {
qrLabel.textContent =
"QR_Taler · open bank UI · login as " + (d.username || "you");
}
makeQr(
document.getElementById("qr-auto-acct"),
webui,
document.getElementById("qr-auto-acct-logo")
);
if (btn) {
btn.hidden = true;
btn.disabled = false;
btn.textContent = "Create my bank account →";
}
if (again) again.hidden = false;
})
.catch(function (err) {
if (box) box.hidden = false;
if (st) {
st.textContent =
"Could not create account — try again. " +
(err && err.message ? err.message : "");
}
if (btn) {
btn.disabled = false;
btn.hidden = false;
btn.textContent = "Create my bank account →";
}
});
}
var btn = document.getElementById("auto-account-btn");
var again = document.getElementById("auto-account-again");
if (btn) btn.addEventListener("click", createAutoAccount);
if (again) {
again.addEventListener("click", function () {
var b = document.getElementById("auto-account-btn");
if (b) {
b.hidden = false;
b.disabled = false;
}
again.hidden = true;
createAutoAccount();
});
}
/* Withdraw step: open optional fold + create personal account */
var genOwn = document.getElementById("generate-own-account");
if (genOwn) {
genOwn.addEventListener("click", function () {
openFold("more-fold", "more-fold-toggle", "more-fold-body", paintWebuiQrOnce);
var fold = document.getElementById("more-fold");
if (fold && fold.scrollIntoView) {
fold.scrollIntoView({ behavior: "smooth", block: "start" });
}
createAutoAccount();
});
}
})();
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>