koopa-admin-log/configs/merchant-landing/index.html
Hernâni Marques 0ad46aa27f
merchant landing: wallet fold with gold penguin packages row
Blue › expand on Get the wallet; packages use a single gold penguin.
2026-07-10 20:47:00 +02:00

1002 lines
46 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 Merchant · dual currency</title>
<meta name="description" content="GOA merchant dual currency · sample shop products, pay with GNU Taler (GOA / CHF demo)." />
<link rel="canonical" href="https://taler.hacktivism.ch/intro/" />
<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 Merchant · dual currency" />
<meta property="og:description" content="Sample GOA shop products and dual-currency merchant flow — pay with GNU Taler." />
<meta property="og:url" content="https://taler.hacktivism.ch/intro/" />
<meta property="og:image" content="https://taler.hacktivism.ch/intro/og-goa-shop.png" />
<meta property="og:image:secure_url" content="https://taler.hacktivism.ch/intro/og-goa-shop.png" />
<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 product samples — pay with GNU Taler" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="GOA Merchant · dual currency" />
<meta name="twitter:description" content="Sample GOA shop products and dual-currency merchant flow — pay with GNU Taler." />
<meta name="twitter:image" content="https://taler.hacktivism.ch/intro/og-goa-shop.png" />
<meta name="twitter:image:alt" content="GOA shop product samples — pay with GNU Taler" />
<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(232, 168, 56, 0.45); color: #f0d090; }
.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; }
/* monochrome app icons only — browser-ico uses multi-colour fill attrs */
.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.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-btn.hub .app-ico.branch-ico { color: #f0c86a; }
.app-btn:not(.ios):not(.hub) .app-ico { color: #c4b5fd; }
.app-btn.ios .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.1em;
}
.step-tagline .app-ico.android { color: #c4b5fd; }
.step-tagline .app-ico.ios { color: #f0c86a; }
.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: #3ecfbf;
font-size: 1.1rem;
font-weight: 700;
}
.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; }
/* Expandable package / source rows (blue like bank) */
button.app-btn.cli-toggle {
flex: 1 1 100%;
width: 100%;
justify-content: flex-start;
gap: 0.5rem;
font: inherit;
font-weight: 700;
font-size: 0.88rem;
cursor: pointer;
color: #e9d5ff;
border-color: rgba(167, 139, 250, 0.4);
background: rgba(0,0,0,0.28);
}
button.app-btn.cli-toggle .cli-chevron {
margin-left: auto;
flex-shrink: 0;
color: #3ecfbf;
font-size: 1.15rem;
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[aria-expanded="true"] {
border-color: rgba(62, 207, 191, 0.5);
background: rgba(62, 207, 191, 0.08);
}
.cli-panel {
display: none;
width: 100%;
margin: 0.35rem 0 0.55rem;
text-align: left;
border: 1px solid rgba(167, 139, 250, 0.32);
border-radius: 12px;
background: rgba(0, 0, 0, 0.28);
padding: 0.75rem 0.85rem 0.9rem;
box-sizing: border-box;
}
.cli-panel.open { display: block; }
.cli-panel .cli-lead {
margin: 0 0 0.55rem;
color: var(--muted);
font-size: 0.84rem;
line-height: 1.4;
}
.cli-panel .cli-lead a { color: #c4b5fd; }
.cli-block {
margin: 0;
padding: 0.55rem 0.65rem;
border-radius: 10px;
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(167, 139, 250, 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;
user-select: all;
}
.cli-block .cmt { color: #8a9a90; }
.sym-gold path, .sym-gold circle { fill: #f0c86a; stroke: #f0c86a; }
.sym-gold path[data-ring], .sym-gold circle[data-ring] { fill: none; stroke: #f0c86a; }
.sym-blue path, .sym-blue circle { fill: #3ecfbf; stroke: #3ecfbf; }
.sym-blue path[data-ring], .sym-blue circle[data-ring] { fill: none; stroke: #3ecfbf; }
</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="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>
<button type="button" class="app-btn hub cli-toggle" id="pkg-fold-toggle"
aria-expanded="false" aria-controls="pkg-fold-body"
aria-label="Linux packages: Debian and Ubuntu (taler-wallet-cli)">
<svg class="app-ico browser-ico penguin-ico" viewBox="0 0 24 24" role="img" aria-label="Linux">
<ellipse cx="12" cy="14.2" rx="6.4" ry="7.2" fill="#f0c86a"/>
<ellipse cx="12" cy="15.2" rx="3.6" ry="4.8" fill="#1a1410" opacity="0.35"/>
<circle cx="12" cy="7" r="4.1" fill="#f0c86a"/>
<circle cx="10.35" cy="6.6" r="0.85" fill="#1a1410"/>
<circle cx="13.65" cy="6.6" r="0.85" fill="#1a1410"/>
<path fill="#e8a838" d="M11.15 8.1 12 9.55 12.85 8.1z"/>
<ellipse cx="9.4" cy="21" rx="2.1" ry="1.05" fill="#e8a838"/>
<ellipse cx="14.6" cy="21" rx="2.1" ry="1.05" fill="#e8a838"/>
<ellipse cx="6.6" cy="13.5" rx="1.4" ry="2.6" fill="#f0c86a" transform="rotate(-18 6.6 13.5)"/>
<ellipse cx="17.4" cy="13.5" rx="1.4" ry="2.6" fill="#f0c86a" transform="rotate(18 17.4 13.5)"/>
</svg>
Debian · Ubuntu packages
<span class="cli-chevron" aria-hidden="true"></span>
</button>
<div class="cli-panel" id="pkg-fold-body" hidden>
<p class="cli-lead">
Official <strong>taler-wallet-cli</strong> from
<a href="https://deb.taler.net/" target="_blank" rel="noopener noreferrer">deb.taler.net</a>.
Full recipes also on the
<a href="https://bank.hacktivism.ch/intro/">bank landing</a>.
</p>
<pre class="cli-block"><span class="cmt"># Debian example</span>
sudo apt-get update
sudo apt-get install -y taler-wallet-cli</pre>
</div>
</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 &amp; 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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
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) {
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", "");
});
}
function wireCli(btnId, bodyId) {
var b = document.getElementById(btnId);
var p = document.getElementById(bodyId);
if (!b || !p) return;
b.addEventListener("click", function () {
var open = !p.classList.contains("open");
p.classList.toggle("open", open);
b.setAttribute("aria-expanded", open ? "true" : "false");
if (open) p.removeAttribute("hidden");
else p.setAttribute("hidden", "");
});
}
wireCli("pkg-fold-toggle", "pkg-fold-body");
})();
</script>
<script src="/intro/shop-pay.js"></script>
</body>
</html>