860 lines
39 KiB
HTML
860 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>GOA Merchant · dual currency</title>
|
||
<base href="/intro/" />
|
||
<link rel="stylesheet" href="/intro/shop-pay.css" />
|
||
<script src="/intro/qrcode.min.js"></script>
|
||
<style>
|
||
:root {
|
||
--bg: #120f18;
|
||
--card: rgba(36, 28, 48, 0.88);
|
||
--text: #f4eefc;
|
||
--muted: #b8a8c9;
|
||
--accent: #c084fc;
|
||
--accent2: #a78bfa;
|
||
--border: rgba(167, 139, 250, 0.32);
|
||
--glow-violet: rgba(168, 85, 247, 0.38);
|
||
--glow-indigo: rgba(99, 102, 241, 0.28);
|
||
--chf: #93c5fd;
|
||
--goa: #c4b5fd;
|
||
--val: #e9d5ff;
|
||
--pay: #86efac;
|
||
--ref: #fca5a5;
|
||
}
|
||
* { 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(76, 29, 149, 0.45) 0%, transparent 55%),
|
||
radial-gradient(circle 420px at 12% 18%, var(--glow-violet) 0%, transparent 62%),
|
||
radial-gradient(circle 380px at 88% 14%, var(--glow-indigo) 0%, transparent 58%),
|
||
linear-gradient(165deg, #1e1530 0%, #120f18 40%, #0f1220 72%, #0c0e18 100%);
|
||
background-attachment: fixed;
|
||
}
|
||
main { position: relative; z-index: 1; max-width: 42rem; margin: 0 auto; padding: 2.25rem 1.25rem 3.5rem; }
|
||
.hero { text-align: center; margin: 0 0 1.25rem; }
|
||
h1 {
|
||
font-size: 1.85rem; font-weight: 750; margin: 0 0 0.45rem; letter-spacing: -0.02em;
|
||
background: linear-gradient(120deg, #faf5ff 0%, #d8b4fe 40%, #818cf8 100%);
|
||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||
}
|
||
.sub { color: var(--muted); margin: 0 auto; max-width: 30rem; font-size: 1.02rem; }
|
||
.sub a {
|
||
color: #5eead4; text-decoration: none; border-bottom: 1px solid rgba(94, 234, 212, 0.35);
|
||
font-weight: 650;
|
||
}
|
||
.sub a:hover { border-bottom-color: #5eead4; }
|
||
.badge {
|
||
display: inline-block;
|
||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.22), rgba(99, 102, 241, 0.18));
|
||
color: #e9d5ff; font-size: 0.75rem; font-weight: 700;
|
||
padding: 0.2rem 0.7rem; border-radius: 999px; margin: 0 0 0.75rem;
|
||
border: 1px solid rgba(167, 139, 250, 0.45); letter-spacing: 0.04em;
|
||
}
|
||
.money-hint {
|
||
margin: 0 0 1.15rem;
|
||
padding: 0.85rem 0.95rem 0.95rem;
|
||
border-radius: 12px;
|
||
background: rgba(94, 234, 212, 0.1);
|
||
border: 1px solid rgba(94, 234, 212, 0.4);
|
||
color: #ccfbf1;
|
||
font-size: 0.9rem;
|
||
line-height: 1.4;
|
||
text-align: center;
|
||
}
|
||
.money-hint strong { color: #5eead4; font-weight: 750; }
|
||
/* p(l)ay: plain text; only (l) in site accent (violet), not a pill/button */
|
||
.money-hint .play-joke {
|
||
font-weight: 700;
|
||
white-space: nowrap;
|
||
letter-spacing: 0.01em;
|
||
}
|
||
.money-hint .play-joke .play-l {
|
||
color: var(--accent);
|
||
font-weight: 800;
|
||
}
|
||
.money-hint a {
|
||
color: #5eead4; font-weight: 700; text-decoration: none;
|
||
border-bottom: 1px solid rgba(94, 234, 212, 0.4);
|
||
}
|
||
.money-hint a:hover { border-bottom-color: #5eead4; }
|
||
.money-hint .money-hint-detail {
|
||
display: inline-block;
|
||
margin-top: 0.35rem;
|
||
font-size: 0.84rem;
|
||
color: #99f6e4;
|
||
opacity: 0.92;
|
||
}
|
||
.money-hint .money-hint-cta {
|
||
display: inline-block;
|
||
margin-top: 0.55rem;
|
||
padding: 0.45rem 0.9rem;
|
||
border-radius: 10px;
|
||
border: 0;
|
||
border-bottom: 0;
|
||
background: linear-gradient(135deg, #5eead4, #0d9488);
|
||
color: #042f2e;
|
||
font-weight: 750;
|
||
font-size: 0.88rem;
|
||
text-decoration: none;
|
||
}
|
||
.money-hint .money-hint-cta:hover {
|
||
filter: brightness(1.06);
|
||
border-bottom: 0;
|
||
}
|
||
.stats {
|
||
background: rgba(24, 18, 40, 0.65);
|
||
border: 1px solid rgba(167, 139, 250, 0.32);
|
||
border-radius: 16px; padding: 1.1rem 1.15rem 1.2rem; margin: 0 0 1.15rem;
|
||
}
|
||
.stats h2 {
|
||
margin: 0 0 0.35rem; font-size: 0.78rem; font-weight: 700;
|
||
letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); text-align: center;
|
||
}
|
||
.stats .note {
|
||
text-align: center; font-size: 0.82rem; color: var(--muted); margin: 0 0 0.85rem;
|
||
}
|
||
.stats-grid {
|
||
display: grid; grid-template-columns: 1fr 1fr; gap: 0.55rem 0.7rem;
|
||
}
|
||
@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.5rem 0.6rem;
|
||
}
|
||
.stat .k {
|
||
display: block; font-size: 0.68rem; color: var(--muted);
|
||
letter-spacing: 0.04em; text-transform: uppercase; margin-bottom: 0.15rem; text-align: center;
|
||
}
|
||
.stat .v {
|
||
display: block; font-size: 1.05rem; font-weight: 750; color: var(--val);
|
||
text-align: center; font-variant-numeric: tabular-nums;
|
||
}
|
||
.stat .v.teal { color: var(--accent2); }
|
||
|
||
/* Side-by-side currencies — static panels, never links */
|
||
.cur-pair {
|
||
display: grid; grid-template-columns: 1fr 1fr; gap: 0.65rem; margin-top: 0.85rem;
|
||
}
|
||
@media (max-width: 380px) { .cur-pair { grid-template-columns: 1fr; } }
|
||
.cur-panel {
|
||
border-radius: 14px; padding: 0.85rem 0.75rem 0.9rem;
|
||
background: rgba(0, 0, 0, 0.28);
|
||
border: 1px solid rgba(167, 139, 250, 0.25);
|
||
pointer-events: none; user-select: text;
|
||
}
|
||
.cur-panel.chf { border-color: rgba(147, 197, 253, 0.45); }
|
||
.cur-panel.goa { border-color: rgba(196, 181, 253, 0.5); }
|
||
.cur-panel .cur-name {
|
||
text-align: center; font-size: 0.78rem; font-weight: 800; letter-spacing: 0.08em;
|
||
text-transform: uppercase; margin: 0 0 0.15rem;
|
||
}
|
||
.cur-panel.chf .cur-name { color: var(--chf); }
|
||
.cur-panel.goa .cur-name { color: var(--goa); }
|
||
.cur-panel .cur-sub {
|
||
text-align: center; font-size: 0.68rem; color: var(--muted); margin: 0 0 0.55rem;
|
||
}
|
||
.cur-panel .amt-big {
|
||
display: block; text-align: center; font-size: 1.28rem; font-weight: 800;
|
||
font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
|
||
color: var(--val); margin: 0 0 0.15rem; word-break: break-word;
|
||
}
|
||
.cur-panel.chf .amt-big { color: #bfdbfe; }
|
||
.cur-panel.goa .amt-big { color: #ddd6fe; }
|
||
.cur-panel .amt-label {
|
||
display: block; text-align: center; font-size: 0.68rem; color: var(--muted);
|
||
text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.55rem;
|
||
}
|
||
.cur-meta {
|
||
display: grid; grid-template-columns: 1fr 1fr; gap: 0.3rem 0.4rem;
|
||
font-size: 0.8rem; text-align: center;
|
||
}
|
||
.cur-meta .mk { display: block; font-size: 0.64rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
|
||
.cur-meta .mv { display: block; font-weight: 700; color: var(--val); font-variant-numeric: tabular-nums; }
|
||
.cur-panel .amt-secondary {
|
||
margin-top: 0.55rem; text-align: center; font-size: 0.78rem; color: var(--muted);
|
||
}
|
||
.cur-panel .amt-secondary strong { color: var(--val); font-weight: 700; }
|
||
|
||
/* Recent activity — split by currency */
|
||
.act-pair {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 0.75rem;
|
||
margin-top: 0.35rem;
|
||
}
|
||
@media (max-width: 480px) {
|
||
.act-pair { grid-template-columns: 1fr; }
|
||
}
|
||
.act-col {
|
||
background: rgba(24, 18, 40, 0.45);
|
||
border: 1px solid rgba(167, 139, 250, 0.22);
|
||
border-radius: 12px;
|
||
padding: 0.55rem 0.55rem 0.35rem;
|
||
min-width: 0;
|
||
}
|
||
.act-col-h {
|
||
margin: 0 0 0.35rem;
|
||
font-size: 0.78rem;
|
||
font-weight: 750;
|
||
letter-spacing: 0.06em;
|
||
text-transform: uppercase;
|
||
color: var(--goa);
|
||
text-align: center;
|
||
}
|
||
.act-col.chf .act-col-h { color: var(--chf); }
|
||
.act-list { list-style: none; margin: 0; padding: 0; }
|
||
.act-item {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
gap: 0.25rem 0.45rem;
|
||
align-items: center;
|
||
padding: 0.4rem 0.2rem;
|
||
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||
font-size: 0.82rem;
|
||
}
|
||
.act-item:last-child { border-bottom: 0; }
|
||
.act-kind {
|
||
font-size: 0.58rem; font-weight: 800; letter-spacing: 0.05em;
|
||
text-transform: uppercase; padding: 0.14rem 0.32rem; border-radius: 6px;
|
||
align-self: center;
|
||
}
|
||
.act-kind.payment { color: #14532d; background: rgba(134, 239, 172, 0.85); }
|
||
/* violet — refund is not an error */
|
||
.act-kind.refund { color: #f5e8ff; background: rgba(167, 139, 250, 0.92); }
|
||
.act-kind.other { color: #1e1b4b; background: rgba(196, 181, 253, 0.85); }
|
||
.act-main { min-width: 0; }
|
||
.act-amt {
|
||
font-weight: 800; font-variant-numeric: tabular-nums; color: var(--val);
|
||
font-size: 0.88rem;
|
||
}
|
||
.act-sum {
|
||
color: var(--muted); font-size: 0.72rem; white-space: nowrap;
|
||
overflow: hidden; text-overflow: ellipsis; max-width: 100%;
|
||
}
|
||
.act-meta {
|
||
text-align: right; font-size: 0.62rem; color: var(--muted);
|
||
font-variant-numeric: tabular-nums; white-space: nowrap;
|
||
}
|
||
.empty { text-align: center; color: var(--muted); font-size: 0.8rem; padding: 0.45rem 0.2rem; }
|
||
|
||
.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;
|
||
}
|
||
.step-card {
|
||
background: var(--card); border: 1px solid var(--border); border-radius: 18px;
|
||
padding: 1.3rem 1.25rem 1.4rem; margin-bottom: 1.2rem;
|
||
}
|
||
.step-card h2 {
|
||
font-size: 1.2rem; font-weight: 800; margin: 0 0 0.4rem; text-align: center;
|
||
}
|
||
.step-tagline { margin: 0 0 1rem; color: var(--muted); text-align: center; font-size: 0.95rem; }
|
||
.app-links {
|
||
display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; width: 100%;
|
||
margin-bottom: 0.85rem;
|
||
}
|
||
.app-btn {
|
||
display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
|
||
min-width: 9rem; text-decoration: none; font-weight: 700; font-size: 0.88rem;
|
||
padding: 0.55rem 0.85rem; border-radius: 10px;
|
||
border: 1px solid rgba(167, 139, 250, 0.45); background: rgba(0,0,0,0.28); color: #e9d5ff;
|
||
}
|
||
.app-btn.ios { border-color: rgba(94, 234, 212, 0.4); color: #b8f5ec; }
|
||
.app-btn.hub {
|
||
flex: 1 1 100%; border-color: rgba(184, 168, 201, 0.35); color: var(--muted);
|
||
font-weight: 600; font-size: 0.82rem; gap: 0.5rem;
|
||
}
|
||
.app-ico { width: 1.15rem; height: 1.15rem; flex-shrink: 0; display: block; }
|
||
.app-ico path, .app-ico circle, .app-ico rect { fill: currentColor; }
|
||
.app-ico.stroke-ico path, .app-ico.stroke-ico circle, .app-ico.stroke-ico rect {
|
||
fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
|
||
}
|
||
.app-btn:not(.ios):not(.hub) .app-ico { color: #c4b5fd; }
|
||
.app-btn.ios .app-ico { color: #5eead4; }
|
||
.hub-icons { display: inline-flex; align-items: center; gap: 0.35rem; }
|
||
.hub-icons .app-ico { width: 1.05rem; height: 1.05rem; }
|
||
.hub-icons .app-ico.gold { color: #e9d5ff; }
|
||
.hub-icons .app-ico.blue { color: #93c5fd; }
|
||
.step-tagline .app-ico {
|
||
width: 1rem; height: 1rem; display: inline-block; vertical-align: -0.15em; margin: 0 0.1em;
|
||
}
|
||
.step-tagline .app-ico.android { color: #c4b5fd; }
|
||
.step-tagline .app-ico.ios { color: #5eead4; }
|
||
.meta { font-size: 0.78rem; color: var(--muted); word-break: break-all; text-align: center; margin: 0.4rem 0 0.75rem; }
|
||
.links { display: flex; flex-direction: column; gap: 0.55rem; }
|
||
.cta {
|
||
display: block; text-align: center; text-decoration: none; font-weight: 750;
|
||
padding: 0.7rem 1.1rem; border-radius: 12px;
|
||
background: linear-gradient(135deg, #c084fc, #7c3aed); color: #1a0b2e;
|
||
box-shadow: 0 8px 22px rgba(124, 58, 237, 0.35);
|
||
}
|
||
.cta.teal {
|
||
background: linear-gradient(135deg, #a78bfa, #6366f1); color: #0f0a1a;
|
||
}
|
||
.cta-sec {
|
||
display: block; text-align: center; text-decoration: none; color: var(--accent2);
|
||
border: 1px solid rgba(167, 139, 250, 0.45); padding: 0.55rem 0.85rem;
|
||
border-radius: 10px; font-weight: 600; font-size: 0.92rem;
|
||
}
|
||
footer { margin-top: 1.75rem; color: var(--muted); font-size: 0.85rem; text-align: center; }
|
||
footer a { color: #d8b4fe; }
|
||
code {
|
||
font-family: ui-monospace, Menlo, monospace; font-size: 0.86em;
|
||
background: rgba(0,0,0,0.35); padding: 0.1em 0.35em; border-radius: 4px; color: #e9d5ff;
|
||
}
|
||
.shop-box {
|
||
background: rgba(24, 18, 40, 0.65);
|
||
border: 1px solid rgba(167, 139, 250, 0.38);
|
||
border-radius: 16px;
|
||
padding: 1.15rem 1.1rem 1.25rem;
|
||
margin: 0 0 1.2rem;
|
||
}
|
||
.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.85rem; text-align: center; font-size: 0.9rem; color: var(--muted);
|
||
}
|
||
.shop-grid {
|
||
display: grid; grid-template-columns: 1fr 1fr; gap: 0.55rem;
|
||
}
|
||
@media (min-width: 420px) { .shop-grid { grid-template-columns: 1fr 1fr 1fr; } }
|
||
.shop-item {
|
||
background: rgba(0, 0, 0, 0.28);
|
||
border: 1px solid rgba(167, 139, 250, 0.28);
|
||
border-radius: 12px;
|
||
padding: 0.65rem 0.55rem 0.7rem;
|
||
text-align: center; min-width: 0;
|
||
cursor: pointer;
|
||
transition: border-color 0.15s, transform 0.12s, background 0.15s;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
display: block;
|
||
}
|
||
.shop-item:hover, .shop-item:focus-visible {
|
||
border-color: rgba(196, 181, 253, 0.75);
|
||
background: rgba(0, 0, 0, 0.4);
|
||
transform: translateY(-1px);
|
||
outline: none;
|
||
}
|
||
.shop-item .emoji { display: block; font-size: 1.4rem; line-height: 1.2; margin-bottom: 0.25rem; }
|
||
.shop-item .emoji.pill-duo {
|
||
display: flex; justify-content: center; align-items: center;
|
||
min-height: 1.5rem; margin-bottom: 0.35rem;
|
||
}
|
||
.shop-item .pill-svg { display: block; }
|
||
.shop-item .name {
|
||
display: block; font-size: 0.8rem; font-weight: 700; color: #faf5ff; line-height: 1.25;
|
||
}
|
||
.shop-item .price {
|
||
display: block; margin-top: 0.25rem; font-size: 0.82rem; font-weight: 750;
|
||
color: #c4b5fd; font-variant-numeric: tabular-nums;
|
||
}
|
||
.shop-item .hint {
|
||
display: block; margin-top: 0.2rem; font-size: 0.65rem; font-weight: 600;
|
||
color: var(--muted); line-height: 1.25; letter-spacing: 0.01em;
|
||
}
|
||
|
||
/* Wallet: collapsed box, expand like settlement QR panel */
|
||
.wallet-fold {
|
||
margin: 0 0 1.15rem;
|
||
border: 1px solid rgba(167, 139, 250, 0.38);
|
||
border-radius: 14px;
|
||
overflow: hidden;
|
||
background: rgba(36, 28, 48, 0.72);
|
||
}
|
||
.wallet-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: #e9d5ff;
|
||
font: inherit;
|
||
font-size: 0.92rem;
|
||
font-weight: 700;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
line-height: 1.3;
|
||
}
|
||
.wallet-fold-toggle:hover {
|
||
background: rgba(167, 139, 250, 0.12);
|
||
color: #faf5ff;
|
||
}
|
||
.wallet-fold-toggle-text { flex: 1; }
|
||
.wallet-fold-chevron {
|
||
flex-shrink: 0;
|
||
transition: transform 0.15s ease;
|
||
color: var(--accent2);
|
||
font-size: 0.9rem;
|
||
}
|
||
.wallet-fold.open .wallet-fold-chevron { transform: rotate(90deg); }
|
||
.wallet-fold-body {
|
||
display: none;
|
||
padding: 0.15rem 0.9rem 1rem;
|
||
border-top: 1px solid rgba(167, 139, 250, 0.28);
|
||
}
|
||
.wallet-fold.open .wallet-fold-body { display: block; }
|
||
.wallet-fold-body .app-links { margin-top: 0.65rem; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main>
|
||
<header class="hero">
|
||
<span class="badge">Merchant · dual currency</span>
|
||
<h1>GOA Merchant</h1>
|
||
<p class="sub">
|
||
Payments in <strong>GOA</strong>
|
||
(<a href="https://taler.hacktivism.ch/terms" title="Terms: GOA explorational currency">explorational</a>)
|
||
and <strong>CHF</strong>
|
||
(<a href="https://taler.hacktivism.ch/terms" title="Terms: CHF via taler-ops">taler-ops</a>).
|
||
</p>
|
||
</header>
|
||
|
||
<p class="money-hint" role="note">
|
||
<strong>Get GOA first.</strong>
|
||
You need coins in your wallet before you can
|
||
<span class="play-joke" title="pay / play">p<span class="play-l">(l)</span>ay</span>
|
||
here.
|
||
<br />
|
||
<span class="money-hint-detail">Install the wallet → one-step withdraw at the bank, then return to this shop.</span>
|
||
<br />
|
||
<a class="money-hint-cta" href="https://bank.hacktivism.ch/intro/">Get money at the bank →</a>
|
||
</p>
|
||
|
||
<div class="wallet-fold" id="wallet-fold">
|
||
<button type="button" class="wallet-fold-toggle" id="wallet-fold-toggle"
|
||
aria-expanded="false" aria-controls="wallet-fold-body">
|
||
<span class="wallet-fold-toggle-text">Get the wallet · Android / iOS / web browsers</span>
|
||
<span class="wallet-fold-chevron" aria-hidden="true">›</span>
|
||
</button>
|
||
<div class="wallet-fold-body" id="wallet-fold-body" hidden>
|
||
<div class="app-links">
|
||
<a class="app-btn" href="https://play.google.com/store/apps/details?id=net.taler.wallet" target="_blank" rel="noopener noreferrer" aria-label="Android Play Store">
|
||
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.2 2.4l-.9-1.6c-.1-.2 0-.4.2-.5.2-.1.4 0 .5.2l.9 1.6c.8-.3 1.7-.5 2.6-.5s1.8.2 2.6.5l.9-1.6c.1-.2.3-.3.5-.2.2.1.3.3.2.5l-.9 1.6c1.8.8 3.1 2.5 3.3 4.6H4.9c.2-2.1 1.5-3.8 3.3-4.6zM7 8.5c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zm10 0c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zM5 10.5h14c.6 0 1 .4 1 1v6.2c0 1.3-1 2.3-2.3 2.3h-.5v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-4.8v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-.5C5.9 20 5 19 5 17.7v-6.2c0-.6.4-1 1-1z"/></svg>
|
||
Play Store
|
||
</a>
|
||
<a class="app-btn" href="https://f-droid.org/en/packages/net.taler.wallet.fdroid/" target="_blank" rel="noopener noreferrer" aria-label="Android F-Droid">
|
||
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.2 2.4l-.9-1.6c-.1-.2 0-.4.2-.5.2-.1.4 0 .5.2l.9 1.6c.8-.3 1.7-.5 2.6-.5s1.8.2 2.6.5l.9-1.6c.1-.2.3-.3.5-.2.2.1.3.3.2.5l-.9 1.6c1.8.8 3.1 2.5 3.3 4.6H4.9c.2-2.1 1.5-3.8 3.3-4.6zM7 8.5c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zm10 0c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zM5 10.5h14c.6 0 1 .4 1 1v6.2c0 1.3-1 2.3-2.3 2.3h-.5v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-4.8v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-.5C5.9 20 5 19 5 17.7v-6.2c0-.6.4-1 1-1z"/></svg>
|
||
F-Droid
|
||
</a>
|
||
<a class="app-btn ios" href="https://apps.apple.com/app/taler-wallet/id6463440117" target="_blank" rel="noopener noreferrer" aria-label="iOS App Store">
|
||
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M16.7 12.6c0-2.1 1.7-3.1 1.8-3.2-1-1.4-2.5-1.6-3-1.7-1.3-.1-2.5.8-3.1.8s-1.6-.7-2.7-.7c-1.4 0-2.7.8-3.4 2.1-1.5 2.5-.4 6.3 1 8.3.7 1 1.5 2.1 2.6 2 1-.1 1.5-.7 2.8-.7s1.7.7 2.8.7 1.9-1 2.6-2c.8-1.1 1.1-2.2 1.1-2.3-.1 0-2.2-.8-2.2-3.3zm-2-6.2c.6-.7 1-1.7.9-2.7-1 .1-2.1.6-2.8 1.4-.6.7-1.1 1.7-.9 2.6 1 .1 2-.5 2.8-1.3z"/></svg>
|
||
App Store
|
||
</a>
|
||
<a class="app-btn hub" href="https://wallet.taler.net/" target="_blank" rel="noopener noreferrer" aria-label="Browser platforms (experimental)">
|
||
<span class="hub-icons" aria-hidden="true">
|
||
<svg class="app-ico gold" viewBox="0 0 24 24" role="img" aria-label="Chrome">
|
||
<circle cx="12" cy="12" r="10" opacity="0.22"/>
|
||
<path d="M12 2a10 10 0 0 1 8.66 5H12V2z" opacity="0.95"/>
|
||
<path d="M20.66 7A10 10 0 0 1 12 22l5-8.66H12L20.66 7z" opacity="0.55"/>
|
||
<path d="M12 22A10 10 0 0 1 3.34 7H12l-5 8.66L12 22z" opacity="0.75"/>
|
||
<circle cx="12" cy="12" r="3.4"/>
|
||
</svg>
|
||
<svg class="app-ico blue" viewBox="0 0 24 24" role="img" aria-label="Firefox">
|
||
<circle cx="12" cy="13" r="6.2" opacity="0.35"/>
|
||
<path d="M12 4.2c1.6 0 2.9.7 3.7 1.6.6-.9 1.6-1.4 2.7-1.3-.2 1.6-.9 2.8-1.9 3.6 1.4 1.3 2.3 3.1 2.3 5.1 0 4.1-3.4 7.3-7.8 7.3S3.2 17.3 3.2 13.2c0-2.3 1-4.3 2.7-5.6C5 6.5 4.4 5.1 4.5 3.5c1.1-.1 2.1.5 2.7 1.4C8.1 4.8 9.6 4.2 12 4.2z"/>
|
||
<path d="M9.2 11.2c.9-.6 1.8-.9 2.8-.9 1.2 0 2.3.4 3.1 1.1" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" opacity="0.5"/>
|
||
</svg>
|
||
<svg class="app-ico gold" viewBox="0 0 24 24" role="img" aria-label="Safari">
|
||
<circle cx="12" cy="12" r="9.2" fill="none" stroke="currentColor" stroke-width="1.7"/>
|
||
<path d="M14.8 9.2 13 13l-3.8 1.8L11 11l3.8-1.8z"/>
|
||
<path d="M12 5.2v1.4M12 17.4v1.4M5.2 12h1.4M17.4 12h1.4" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" opacity="0.55"/>
|
||
</svg>
|
||
</span>
|
||
Browser platforms (experimental)
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<aside class="shop-box" aria-labelledby="shop-title">
|
||
<h2 id="shop-title">GOA shop · samples</h2>
|
||
<p class="shop-lead">Tap an item → open payment link for the wallet</p>
|
||
<div class="shop-grid" id="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>
|
||
<button type="button" class="shop-item" data-product="voidwave-playlist">
|
||
<span class="emoji" aria-hidden="true">🎧</span>
|
||
<span class="name">Voidwave playlist</span>
|
||
<span class="price">GOA:8</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="comet-cap">
|
||
<span class="emoji" aria-hidden="true">🧢</span>
|
||
<span class="name">Comet cap</span>
|
||
<span class="price">GOA:15</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="shuttle-pass">
|
||
<span class="emoji" aria-hidden="true">🛸</span>
|
||
<span class="name">Shuttle day pass</span>
|
||
<span class="price">GOA:25</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="beacon-badge">
|
||
<span class="emoji" aria-hidden="true">📡</span>
|
||
<span class="name">Beacon badge</span>
|
||
<span class="price">GOA:3</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="relay-pin">
|
||
<span class="emoji" aria-hidden="true">🛰️</span>
|
||
<span class="name">Relay pin</span>
|
||
<span class="price">GOA:4</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="eclipse-shades">
|
||
<span class="emoji" aria-hidden="true">🕶️</span>
|
||
<span class="name">Eclipse shades</span>
|
||
<span class="price">GOA:12</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="star-chart">
|
||
<span class="emoji" aria-hidden="true">📜</span>
|
||
<span class="name">Star chart print</span>
|
||
<span class="price">GOA:7</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="rainbow-pill">
|
||
<span class="emoji" aria-hidden="true">🌈</span>
|
||
<span class="name">Rainbow pill</span>
|
||
<span class="price">GOA:4.2</span>
|
||
<span class="hint">sample only · joke · not a drug / legal product</span>
|
||
</button>
|
||
<button type="button" class="shop-item" data-product="blue-or-red-pill">
|
||
<span class="emoji pill-duo" aria-hidden="true" title="blue or red">
|
||
<svg class="pill-svg" viewBox="0 0 40 20" width="40" height="20">
|
||
<defs>
|
||
<clipPath id="pill-shape">
|
||
<rect x="1" y="3" width="38" height="14" rx="7" ry="7"/>
|
||
</clipPath>
|
||
</defs>
|
||
<g clip-path="url(#pill-shape)">
|
||
<rect x="1" y="3" width="19" height="14" fill="#3b82f6"/>
|
||
<rect x="20" y="3" width="19" height="14" fill="#ef4444"/>
|
||
<line x1="20" y1="3" x2="20" y2="17" stroke="rgba(255,255,255,0.35)" stroke-width="1"/>
|
||
</g>
|
||
<rect x="1" y="3" width="38" height="14" rx="7" ry="7" fill="none" stroke="rgba(233,213,255,0.45)" stroke-width="1"/>
|
||
</svg>
|
||
</span>
|
||
<span class="name">Blue or red pill</span>
|
||
<span class="price">GOA:1.5</span>
|
||
<span class="hint">sample only · joke · not a drug / legal product</span>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
|
||
<section class="stats" id="stats" aria-labelledby="stats-title">
|
||
<h2 id="stats-title">Merchant stats</h2>
|
||
<p class="note">From merchant DB · dual currency side by side</p>
|
||
<div class="stats-grid">
|
||
<div class="stat"><span class="k">Instances</span><span class="v teal" id="st-instances">—</span></div>
|
||
<div class="stat"><span class="k">Orders</span><span class="v" id="st-orders">—</span></div>
|
||
<div class="stat"><span class="k">Paid</span><span class="v teal" id="st-paid">—</span></div>
|
||
<div class="stat"><span class="k">Unpaid</span><span class="v" id="st-unpaid">—</span></div>
|
||
<div class="stat"><span class="k">Wired</span><span class="v" id="st-wired">—</span></div>
|
||
<div class="stat"><span class="k">Refunds</span><span class="v" id="st-refunds">—</span></div>
|
||
</div>
|
||
|
||
<!-- Static dual panels (not links) -->
|
||
<div class="cur-pair" id="st-currencies" aria-label="Amounts by currency">
|
||
<div class="cur-panel goa" id="panel-goa">
|
||
<div class="cur-name">GOA</div>
|
||
<div class="cur-sub">explorational · hacktivism</div>
|
||
<span class="amt-big" id="goa-paid-amt">—</span>
|
||
<span class="amt-label">paid volume</span>
|
||
<div class="cur-meta">
|
||
<div><span class="mk">Orders</span><span class="mv" id="goa-orders">—</span></div>
|
||
<div><span class="mk">Paid</span><span class="mv" id="goa-paid">—</span></div>
|
||
<div><span class="mk">Wired</span><span class="mv" id="goa-wired">—</span></div>
|
||
<div><span class="mk">Unpaid</span><span class="mv" id="goa-unpaid">—</span></div>
|
||
</div>
|
||
<div class="amt-secondary">all orders <strong id="goa-all-amt">—</strong></div>
|
||
</div>
|
||
<div class="cur-panel chf" id="panel-chf">
|
||
<div class="cur-name">CHF</div>
|
||
<div class="cur-sub">taler-ops</div>
|
||
<span class="amt-big" id="chf-paid-amt">—</span>
|
||
<span class="amt-label">paid volume</span>
|
||
<div class="cur-meta">
|
||
<div><span class="mk">Orders</span><span class="mv" id="chf-orders">—</span></div>
|
||
<div><span class="mk">Paid</span><span class="mv" id="chf-paid">—</span></div>
|
||
<div><span class="mk">Wired</span><span class="mv" id="chf-wired">—</span></div>
|
||
<div><span class="mk">Unpaid</span><span class="mv" id="chf-unpaid">—</span></div>
|
||
</div>
|
||
<div class="amt-secondary">all orders <strong id="chf-all-amt">—</strong></div>
|
||
</div>
|
||
</div>
|
||
<p class="stats-foot" id="st-foot">Loading…</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">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">/terms</span><span class="v" id="st-terms-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 sm" 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">Taler procs</span><span class="v" id="st-mem-taler">—</span></div>
|
||
<div class="stat"><span class="k">Nginx</span><span class="v sm" id="st-mem-nginx">—</span></div>
|
||
</div>
|
||
<div style="margin-top:0.85rem;overflow-x:auto">
|
||
<p class="note" style="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>
|
||
|
||
<section class="stats" id="activity" aria-labelledby="act-title">
|
||
<h2 id="act-title">Recent activity</h2>
|
||
<p class="note">Latest 5 payments & refunds per currency</p>
|
||
<div class="act-pair" id="st-activity-pair">
|
||
<div class="act-col goa" aria-labelledby="act-goa-h">
|
||
<h3 class="act-col-h" id="act-goa-h">GOA</h3>
|
||
<ul class="act-list" id="st-activity-goa">
|
||
<li class="empty">Loading…</li>
|
||
</ul>
|
||
</div>
|
||
<div class="act-col chf" aria-labelledby="act-chf-h">
|
||
<h3 class="act-col-h" id="act-chf-h">CHF</h3>
|
||
<ul class="act-list" id="st-activity-chf">
|
||
<li class="empty">Loading…</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="step-card">
|
||
<h2>Merchant SPA</h2>
|
||
<p class="step-tagline"><code>taler.hacktivism.ch</code> · GOA + CHF</p>
|
||
<div class="links">
|
||
<a class="cta teal" href="https://taler.hacktivism.ch/webui/">Open /webui/ →</a>
|
||
<a class="cta-sec" href="https://taler.hacktivism.ch/config">/config</a>
|
||
<a class="cta-sec" href="https://taler.hacktivism.ch/terms">/terms</a>
|
||
<a class="cta-sec" href="https://taler.hacktivism.ch/privacy">/privacy</a>
|
||
</div>
|
||
</section>
|
||
|
||
<footer>
|
||
<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://taler.hacktivism.ch/terms">Terms</a> ·
|
||
<a href="https://taler.hacktivism.ch/privacy">Privacy</a> ·
|
||
<a href="https://git.hacktivism.ch/hernani/koopa-admin-log/src/branch/main/configs/merchant-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://taler.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
|
||
</p>
|
||
</footer>
|
||
</main>
|
||
<script>
|
||
(function () {
|
||
function set(id, v) {
|
||
var el = document.getElementById(id);
|
||
if (el) el.textContent = v == null || v === "" ? "—" : String(v);
|
||
}
|
||
function msLabel(ms, http) {
|
||
if (ms == null || ms === "") return "—";
|
||
var s = ms + " ms";
|
||
if (http && http !== "200") s += " (" + http + ")";
|
||
return s;
|
||
}
|
||
function fillCurrency(prefix, c) {
|
||
if (!c) {
|
||
set(prefix + "-paid-amt", "—");
|
||
set(prefix + "-all-amt", "—");
|
||
set(prefix + "-orders", "—");
|
||
set(prefix + "-paid", "—");
|
||
set(prefix + "-wired", "—");
|
||
set(prefix + "-unpaid", "—");
|
||
return;
|
||
}
|
||
set(prefix + "-paid-amt", c.amount_paid_sum || "—");
|
||
set(prefix + "-all-amt", c.amount_sum || "—");
|
||
set(prefix + "-orders", c.contracts);
|
||
set(prefix + "-paid", c.paid);
|
||
set(prefix + "-wired", c.wired);
|
||
set(prefix + "-unpaid", c.unpaid);
|
||
}
|
||
function esc(s) {
|
||
return String(s == null ? "" : s)
|
||
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
||
.replace(/"/g, """);
|
||
}
|
||
function load() {
|
||
var foot = document.getElementById("st-foot");
|
||
fetch("/intro/stats.json", { cache: "no-store" })
|
||
.then(function (r) { if (!r.ok) throw new Error("http"); return r.json(); })
|
||
.then(function (d) {
|
||
if (!d || !d.ok) throw new Error("bad");
|
||
set("st-instances", d.instances);
|
||
set("st-orders", d.orders);
|
||
set("st-paid", d.paid);
|
||
set("st-unpaid", d.unpaid);
|
||
set("st-wired", d.wired);
|
||
set("st-refunds", d.refunds);
|
||
|
||
var by = {};
|
||
(d.by_currency || []).forEach(function (c) {
|
||
by[(c.currency || "").toUpperCase()] = c;
|
||
});
|
||
fillCurrency("goa", by.GOA);
|
||
fillCurrency("chf", by.CHF);
|
||
|
||
function fillActivityList(el, acts) {
|
||
if (!el) return;
|
||
el.innerHTML = "";
|
||
acts = acts || [];
|
||
if (!acts.length) {
|
||
el.innerHTML = '<li class="empty">No recent payments or refunds</li>';
|
||
return;
|
||
}
|
||
acts.forEach(function (a) {
|
||
var kind = (a.kind || "other").toLowerCase();
|
||
var li = document.createElement("li");
|
||
li.className = "act-item";
|
||
// no merchant names / ids — amount, optional summary, time only
|
||
li.innerHTML =
|
||
'<span class="act-kind ' + esc(kind) + '">' + esc(kind) + "</span>" +
|
||
'<div class="act-main">' +
|
||
'<div class="act-amt">' + esc(a.amount || "—") + "</div>" +
|
||
(a.summary
|
||
? '<div class="act-sum">' + esc(a.summary) + "</div>"
|
||
: "") +
|
||
"</div>" +
|
||
'<div class="act-meta">' +
|
||
"<span>" + esc(a.ts_human || a.ts || "") + "</span>" +
|
||
"</div>";
|
||
el.appendChild(li);
|
||
});
|
||
}
|
||
// Prefer split-by-currency (≤5 each); fall back to flat list split by amount prefix
|
||
var byCur = {};
|
||
(d.recent_activity_by_currency || []).forEach(function (block) {
|
||
byCur[(block.currency || "").toUpperCase()] = block.items || [];
|
||
});
|
||
if (!Object.keys(byCur).length && (d.recent_activity || []).length) {
|
||
byCur.GOA = [];
|
||
byCur.CHF = [];
|
||
(d.recent_activity || []).forEach(function (a) {
|
||
var cur = String(a.amount || "").split(":")[0].toUpperCase();
|
||
if (cur === "GOA") byCur.GOA.push(a);
|
||
else if (cur === "CHF") byCur.CHF.push(a);
|
||
});
|
||
byCur.GOA = byCur.GOA.slice(0, 5);
|
||
byCur.CHF = byCur.CHF.slice(0, 5);
|
||
}
|
||
fillActivityList(document.getElementById("st-activity-goa"), byCur.GOA);
|
||
fillActivityList(document.getElementById("st-activity-chf"), byCur.CHF);
|
||
|
||
if (foot) {
|
||
foot.textContent = (d.dual_currency ? "Dual-currency · " : "") +
|
||
(d.generated_at_human || d.generated_at || "");
|
||
}
|
||
var p = d.performance || {};
|
||
set("st-config-ms", msLabel(p.config_ms, p.config_http));
|
||
set("st-terms-ms", msLabel(p.terms_ms, p.terms_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-taler", mem.taler_rss_human
|
||
? mem.taler_rss_human + (mem.taler_n ? " · " + mem.taler_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="note">—</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("/intro/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.innerHTML = 'Stats offline · <a href="https://bank.hacktivism.ch/intro/" style="color:#e8c878">bank.hacktivism.ch</a>';
|
||
["st-activity-goa", "st-activity-chf"].forEach(function (id) {
|
||
var list = document.getElementById(id);
|
||
if (list) list.innerHTML = '<li class="empty">Stats offline</li>';
|
||
});
|
||
});
|
||
}
|
||
load();
|
||
})();
|
||
</script>
|
||
<script>
|
||
(function () {
|
||
var fold = document.getElementById("wallet-fold");
|
||
var btn = document.getElementById("wallet-fold-toggle");
|
||
var body = document.getElementById("wallet-fold-body");
|
||
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");
|
||
else body.setAttribute("hidden", "");
|
||
});
|
||
})();
|
||
</script>
|
||
<script src="/intro/shop-pay.js"></script>
|
||
</body>
|
||
</html>
|