1445 lines
50 KiB
HTML
1445 lines
50 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>GOA Exploration Bank</title>
|
||
<style>
|
||
:root {
|
||
--bg: #1a1410;
|
||
--card: rgba(42, 32, 24, 0.82);
|
||
--text: #fff6e8;
|
||
--muted: #c9b8a0;
|
||
--accent: #e8a838;
|
||
--accent2: #3ecfbf;
|
||
--border: rgba(232, 168, 56, 0.28);
|
||
--glow-gold: rgba(232, 168, 56, 0.35);
|
||
--glow-teal: rgba(62, 207, 191, 0.22);
|
||
}
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0;
|
||
min-height: 100vh;
|
||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||
color: var(--text);
|
||
line-height: 1.55;
|
||
background-color: var(--bg);
|
||
background-image:
|
||
radial-gradient(ellipse 90% 55% at 50% 108%, rgba(26, 107, 110, 0.55) 0%, transparent 55%),
|
||
radial-gradient(circle 420px at 12% 18%, var(--glow-gold) 0%, transparent 62%),
|
||
radial-gradient(circle 380px at 88% 12%, var(--glow-teal) 0%, transparent 58%),
|
||
radial-gradient(circle 280px at 70% 70%, rgba(196, 122, 24, 0.18) 0%, transparent 60%),
|
||
linear-gradient(165deg, #2c1e14 0%, #1a1410 38%, #12181a 72%, #0e1c1e 100%);
|
||
background-attachment: fixed;
|
||
}
|
||
body::before {
|
||
content: "";
|
||
position: fixed;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
opacity: 0.07;
|
||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
||
}
|
||
main {
|
||
position: relative;
|
||
z-index: 1;
|
||
max-width: 38rem;
|
||
margin: 0 auto;
|
||
padding: 2.25rem 1.25rem 3.5rem;
|
||
}
|
||
.hero {
|
||
text-align: center;
|
||
margin: 0 0 1.75rem;
|
||
}
|
||
h1 {
|
||
font-size: 1.85rem;
|
||
font-weight: 750;
|
||
margin: 0 0 0.45rem;
|
||
letter-spacing: -0.02em;
|
||
line-height: 1.2;
|
||
background: linear-gradient(120deg, #fff6e8 0%, #f0c86a 45%, #3ecfbf 120%);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
}
|
||
.sub {
|
||
color: var(--muted);
|
||
margin: 0 auto;
|
||
max-width: 30rem;
|
||
font-size: 1.05rem;
|
||
line-height: 1.45;
|
||
}
|
||
.badge {
|
||
display: inline-block;
|
||
background: linear-gradient(135deg, rgba(232, 168, 56, 0.2), rgba(62, 207, 191, 0.15));
|
||
color: #f0d090;
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
padding: 0.2rem 0.7rem;
|
||
border-radius: 999px;
|
||
margin: 0 0 0.85rem;
|
||
border: 1px solid rgba(232, 168, 56, 0.4);
|
||
letter-spacing: 0.04em;
|
||
}
|
||
.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; }
|
||
/* 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%;
|
||
}
|
||
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; }
|
||
/* 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;
|
||
}
|
||
.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;
|
||
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);
|
||
}
|
||
.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">
|
||
<main>
|
||
<header class="hero">
|
||
<span class="badge">Intergalactic · GOA · no IBAN</span>
|
||
<h1>GOA Exploration Bank</h1>
|
||
<p class="sub">
|
||
Install the wallet, then <strong>one QR</strong> withdraws GOA from a
|
||
<strong>shared bank account</strong> — 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>
|
||
</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 GOA</h2>
|
||
<p class="step-tagline">One QR · shared account · 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>
|
||
<p class="meta" id="withdraw-demo-meta">Preparing withdraw…</p>
|
||
<button type="button" class="cta-sec" id="generate-own-account" style="margin-top:0.5rem">
|
||
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>
|
||
|
||
<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 & 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 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>Name</dt><dd><code id="auto-account-name">—</code></dd></div>
|
||
<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>
|
||
|
||
<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/";
|
||
|
||
/* QR_Taler: off-DOM encode → PNG img + center logo (same as shop-pay / merchant) */
|
||
function logoSrc() {
|
||
try {
|
||
var b = document.querySelector("base");
|
||
if (b && b.href) return new URL("qr-logo.svg", b.href).href;
|
||
} catch (e) {}
|
||
return "/intro/qr-logo.svg";
|
||
}
|
||
function setQrLogo(imgEl) {
|
||
if (!imgEl) return;
|
||
imgEl.src = logoSrc();
|
||
imgEl.onerror = function () {
|
||
imgEl.style.display = "none";
|
||
};
|
||
imgEl.style.display = "";
|
||
}
|
||
function makeQr(el, text, logoEl) {
|
||
if (!el) return;
|
||
el.innerHTML = "";
|
||
var size = 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.textContent = uri;
|
||
makeQr(
|
||
document.getElementById("qr-withdraw-demo"),
|
||
uri,
|
||
document.getElementById("qr-withdraw-demo-logo")
|
||
);
|
||
}
|
||
|
||
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();
|
||
|
||
/* 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");
|
||
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 n = document.getElementById("auto-account-name");
|
||
var u = document.getElementById("auto-account-user");
|
||
var p = document.getElementById("auto-account-pass");
|
||
if (n) n.textContent = d.name || d.display_name || "—";
|
||
if (u) u.textContent = d.username || "—";
|
||
if (p) p.textContent = d.password || "—";
|
||
if (st) {
|
||
st.textContent =
|
||
"Account created for you · " +
|
||
(d.name || d.username || "") +
|
||
" · 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>
|