2026-09-06 18:46:45 +02:00
|
|
|
|
<!doctype html>
|
|
|
|
|
|
<html lang="en">
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
|
<title>Admin — sirius.x</title>
|
|
|
|
|
|
<meta name="description" content="Sirius.x operator admin — mint TLDs, manage the registry, verify chain health. Operator-key gated.">
|
|
|
|
|
|
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">
|
|
|
|
|
|
<link rel="mask-icon" href="../assets/favicon.svg" color="#d6ff3d">
|
|
|
|
|
|
<meta name="robots" content="noindex,nofollow">
|
|
|
|
|
|
<style>
|
|
|
|
|
|
:root{
|
|
|
|
|
|
--bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09);
|
|
|
|
|
|
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
|
|
|
|
|
|
--ok:#4fd1a5; --taken:#f6768a; --warn:#ffc75f;
|
|
|
|
|
|
}
|
|
|
|
|
|
*{box-sizing:border-box}
|
|
|
|
|
|
body{margin:0;background:radial-gradient(1100px 560px at 50% -12%,#16202e,var(--bg));
|
|
|
|
|
|
color:var(--ink);font:16px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;min-height:100vh}
|
|
|
|
|
|
a{color:var(--acid)}
|
|
|
|
|
|
.wrap{max-width:900px;margin:0 auto;padding:0 1.2rem}
|
|
|
|
|
|
.topnav{position:sticky;top:0;z-index:20;display:flex;gap:2px;align-items:center;flex-wrap:wrap;
|
|
|
|
|
|
padding:10px 1.2rem;background:rgba(11,14,20,.82);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
|
|
|
|
|
|
.topnav .brand{font-weight:600;color:var(--ink);text-decoration:none;margin-right:14px;font-size:15px;letter-spacing:.2px}
|
|
|
|
|
|
.topnav .brand .g{color:var(--acid)}
|
|
|
|
|
|
.topnav a{color:var(--mut);text-decoration:none;padding:6px 12px;border-radius:8px;font-size:14px}
|
|
|
|
|
|
.topnav a:hover{color:var(--ink);background:var(--panel)}
|
|
|
|
|
|
.topnav a.here{color:var(--acid);background:rgba(214,255,61,.08)}
|
|
|
|
|
|
.topnav a.portal{margin-left:auto;background:rgba(214,255,61,.10);color:var(--acid);font-weight:500}
|
|
|
|
|
|
.topnav a.portal:hover{background:rgba(214,255,61,.18)}
|
|
|
|
|
|
header.hero{text-align:center;padding:2.8rem 1.2rem 1rem}
|
|
|
|
|
|
.mark{font-size:40px;line-height:1}
|
|
|
|
|
|
h1{font-size:clamp(1.5rem,4vw,2rem);margin:.3rem 0 .3rem}
|
|
|
|
|
|
h1 .g{color:var(--acid)}
|
|
|
|
|
|
.tag{color:var(--mut);font-size:.98rem;max-width:600px;margin:0 auto}
|
|
|
|
|
|
h2{font-size:1.2rem;margin:0 0 .35rem}
|
|
|
|
|
|
h3{font-size:.98rem;margin:0 0 .3rem;font-family:ui-monospace,monospace}
|
|
|
|
|
|
section{padding:1.8rem 0;border-top:1px solid var(--line);margin-top:1.6rem}
|
|
|
|
|
|
section:first-of-type{border-top:none;margin-top:.8rem}
|
|
|
|
|
|
.card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:16px 18px;margin-top:.8rem}
|
|
|
|
|
|
label{display:block;font-size:11.5px;color:var(--mut);margin:12px 0 4px;text-transform:uppercase;letter-spacing:.4px}
|
|
|
|
|
|
.field-hint{color:var(--dim);font-size:11px;margin-left:8px;text-transform:none;letter-spacing:0}
|
|
|
|
|
|
input[type=text],textarea{
|
|
|
|
|
|
width:100%;padding:9px 12px;border-radius:8px;border:1px solid #ffffff22;background:#0e131b;
|
|
|
|
|
|
color:var(--ink);font-size:13px;font-family:ui-monospace,monospace;outline:none
|
|
|
|
|
|
}
|
|
|
|
|
|
textarea{min-height:68px;resize:vertical}
|
|
|
|
|
|
input:focus,textarea:focus{border-color:#4b7bec}
|
|
|
|
|
|
.btn{display:inline-block;text-decoration:none;padding:9px 16px;border-radius:9px;background:#4b7bec;color:#fff;font-size:13px;border:none;cursor:pointer;font-family:inherit}
|
|
|
|
|
|
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
|
|
|
|
|
|
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600}
|
|
|
|
|
|
.btn.small{padding:5px 10px;font-size:12px}
|
|
|
|
|
|
.btn:hover{filter:brightness(1.12)}
|
|
|
|
|
|
.btn:disabled{opacity:.45;cursor:not-allowed}
|
|
|
|
|
|
.row{display:flex;gap:9px;flex-wrap:wrap;margin-top:12px;align-items:center}
|
|
|
|
|
|
code{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:12.5px;word-break:break-all}
|
|
|
|
|
|
.status{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:10px 14px;font-size:13px;color:var(--mut);margin-top:10px}
|
|
|
|
|
|
.status.ok{border-left:3px solid var(--ok)}
|
|
|
|
|
|
.status.err{border-left:3px solid var(--taken);color:var(--taken)}
|
|
|
|
|
|
.status.warn{border-left:3px solid var(--warn);color:var(--warn)}
|
|
|
|
|
|
.hidden{display:none}
|
|
|
|
|
|
.badge{display:inline-block;font-size:10.5px;padding:2px 8px;border-radius:999px;font-weight:600;letter-spacing:.3px}
|
|
|
|
|
|
.b-op{background:rgba(214,255,61,.14);color:var(--acid)}
|
|
|
|
|
|
.b-other{background:rgba(139,152,169,.14);color:var(--mut)}
|
|
|
|
|
|
.b-mem{background:rgba(255,199,95,.14);color:var(--warn)}
|
|
|
|
|
|
table.tlds{width:100%;border-collapse:collapse;margin-top:1rem;font-size:13px}
|
|
|
|
|
|
table.tlds th,table.tlds td{padding:8px 10px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
|
|
|
|
|
|
table.tlds th{color:var(--mut);font-weight:500;font-size:11.5px;text-transform:uppercase;letter-spacing:.4px}
|
|
|
|
|
|
table.tlds .tld{font-family:ui-monospace,monospace;color:var(--acid);font-size:13.5px;font-weight:600}
|
|
|
|
|
|
table.tlds .cat{font-family:ui-monospace,monospace;color:var(--mut);font-size:11.5px}
|
|
|
|
|
|
table.tlds .records{font-family:ui-monospace,monospace;color:var(--dim);font-size:11.5px;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
|
|
|
|
.steps-log{margin-top:10px;font-family:ui-monospace,monospace;font-size:12px;color:var(--mut);background:#0e131b;border:1px solid var(--line);border-radius:8px;padding:10px 12px;max-height:140px;overflow-y:auto;display:none}
|
|
|
|
|
|
.steps-log.on{display:block}
|
|
|
|
|
|
.steps-log div::before{content:"› ";color:var(--acid)}
|
|
|
|
|
|
.msg{font-size:13px;margin-top:10px}
|
|
|
|
|
|
.msg.ok{color:var(--ok)} .msg.err{color:var(--taken)}
|
|
|
|
|
|
footer{border-top:1px solid var(--line);padding:1.8rem 0 3rem;color:var(--dim);font-size:12.5px;text-align:center;margin-top:2rem}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
|
|
|
|
|
|
|
|
<nav class="topnav">
|
|
|
|
|
|
<a class="brand" href="../">⭐ <span class="g">sirius.x</span></a>
|
|
|
|
|
|
<a href="../#try">Try it</a>
|
|
|
|
|
|
<a href="../docs/">📚 Docs</a>
|
|
|
|
|
|
<a href="../theseus/">🧭 Theseus</a>
|
2026-09-06 22:32:23 +02:00
|
|
|
|
<a href="../register.html">🪪 Register</a>
|
2026-09-06 18:46:45 +02:00
|
|
|
|
<a href="../#pantheon">Pantheon</a>
|
|
|
|
|
|
<a href="./" class="here">🛠 Admin</a>
|
|
|
|
|
|
<a href="../portal.html" class="portal">🔑 Sign in</a>
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
|
|
<header class="hero">
|
|
|
|
|
|
<div class="mark">🛠</div>
|
|
|
|
|
|
<h1><span class="g">Admin</span> panel</h1>
|
|
|
|
|
|
<p class="tag">Operator-side tools: mint new TLDs, browse the registry, verify chain health.
|
|
|
|
|
|
Sign in with the wallet the ecosystem knows as the operator; unfamiliar wallets get
|
|
|
|
|
|
read-only access to the registry.</p>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="wrap">
|
|
|
|
|
|
|
|
|
|
|
|
<section id="signin">
|
|
|
|
|
|
<h2>Sign in</h2>
|
|
|
|
|
|
<p class="status">Chipnet-only. Keys stay in this browser tab; a mint transaction is signed
|
|
|
|
|
|
locally and broadcast direct to the electrum. Operator gate is client-side — the covenant
|
|
|
|
|
|
that enforces it on-chain is a mainnet requirement (Decentralized.DNS/DESIGN-tld-registry.md §5.2).</p>
|
|
|
|
|
|
<div class="card">
|
|
|
|
|
|
<label for="seed">Recovery phrase</label>
|
|
|
|
|
|
<textarea id="seed" placeholder="word1 word2 … word12" spellcheck="false" autocomplete="off"></textarea>
|
|
|
|
|
|
<div class="row">
|
|
|
|
|
|
<button class="btn acid" id="signin-btn">Sign in →</button>
|
|
|
|
|
|
<span class="field-hint" id="signin-hint">seed stays in memory only</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div id="signin-err" class="msg err hidden"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section id="me" class="hidden">
|
|
|
|
|
|
<h2>Signed in <span class="badge b-op" id="me-role" style="margin-left:8px">operator</span></h2>
|
|
|
|
|
|
<div class="card">
|
|
|
|
|
|
<label>Address</label>
|
|
|
|
|
|
<div><code id="me-addr">—</code></div>
|
|
|
|
|
|
<label style="margin-top:12px">Token address (holds the TLD certificates)</label>
|
|
|
|
|
|
<div><code id="me-taddr">—</code></div>
|
|
|
|
|
|
<div class="row">
|
|
|
|
|
|
<button class="btn ghost small" id="signout-btn">Sign out</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section id="mint" class="hidden">
|
|
|
|
|
|
<h2>Mint a new TLD</h2>
|
|
|
|
|
|
<p class="tag" style="max-width:none">Adds a per-TLD certificate to the TLD beacon. The cert
|
|
|
|
|
|
lands in your token address. Cost is chain fees + beacon dust (~1,300 chipnet sats).</p>
|
|
|
|
|
|
<div class="card">
|
|
|
|
|
|
<label>TLD label <span class="field-hint">1–16 chars, [a-z0-9-]</span></label>
|
|
|
|
|
|
<input type="text" id="mint-tld" placeholder="e.g. shop, dao, wiki" maxlength="16">
|
|
|
|
|
|
|
|
|
|
|
|
<label>Records JSON <span class="field-hint">optional; leave empty for defaults</span></label>
|
|
|
|
|
|
<textarea id="mint-records" placeholder='e.g. {"policy":"open","fee_bps":500}'>{}</textarea>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="row">
|
|
|
|
|
|
<button class="btn acid" id="mint-btn">Mint TLD →</button>
|
|
|
|
|
|
<span class="field-hint" id="mint-hint"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="steps-log" id="mint-log"></div>
|
|
|
|
|
|
<div class="msg" id="mint-msg"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section id="registry">
|
|
|
|
|
|
<h2>TLD registry</h2>
|
|
|
|
|
|
<div class="status" id="registry-status">Loading current registry…</div>
|
|
|
|
|
|
<div id="registry-table"></div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<footer>
|
|
|
|
|
|
operator-only tools · <a href="../">back to sirius.x</a> · <a href="../docs/">docs</a>
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
|
|
|
|
|
|
<script type="importmap">
|
|
|
|
|
|
{ "imports": { "@bitauth/libauth": "https://silentmode.st/js/libauth.js" } }
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<script type="module">
|
|
|
|
|
|
// Cache-buster bump when the bundle changes (Chrome's ES-module map caches
|
|
|
|
|
|
// aggressively per plain URL). Portal.html has the same pattern.
|
2026-09-06 18:53:17 +02:00
|
|
|
|
import * as BNS from "https://silentmode.st/js/bns-register.js?v=20260906fixlistunspent";
|
2026-09-06 18:46:45 +02:00
|
|
|
|
|
|
|
|
|
|
const $ = (id) => document.getElementById(id);
|
|
|
|
|
|
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
|
|
|
|
|
|
|
|
|
|
|
// Operator gate: address that owns the chipnet TLD beacon + seeded TLDs.
|
|
|
|
|
|
// Client-side only — the covenant that enforces this on-chain lands with the
|
|
|
|
|
|
// mainnet TLD-registry work. For chipnet this is honour-system; a curious
|
|
|
|
|
|
// visitor can bypass by pasting any seed, but non-operator UPDs/TREGs won't
|
|
|
|
|
|
// carry the countersignature future clients will check.
|
|
|
|
|
|
const OPERATOR_ADDRESS = "bchtest:qzew3dqcsj0guwe5y3uxf9q2xs5ajgy9lca4z093cu";
|
|
|
|
|
|
|
|
|
|
|
|
let wallet = null;
|
|
|
|
|
|
let isOperator = false;
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- sign in ----------
|
|
|
|
|
|
$("signin-btn").addEventListener("click", async () => {
|
|
|
|
|
|
const btn = $("signin-btn");
|
|
|
|
|
|
const errBox = $("signin-err");
|
|
|
|
|
|
errBox.classList.add("hidden");
|
|
|
|
|
|
const raw = $("seed").value.trim();
|
|
|
|
|
|
if (!raw) { errBox.textContent = "Enter your seed phrase."; errBox.classList.remove("hidden"); return; }
|
|
|
|
|
|
btn.disabled = true; btn.textContent = "Unlocking…";
|
|
|
|
|
|
try {
|
|
|
|
|
|
wallet = await BNS.BuiltInWallet.fromMnemonic(raw);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
errBox.textContent = "Could not unlock: " + (e.message || e);
|
|
|
|
|
|
errBox.classList.remove("hidden");
|
|
|
|
|
|
btn.disabled = false; btn.textContent = "Sign in →";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
$("seed").value = "";
|
|
|
|
|
|
btn.disabled = false; btn.textContent = "Sign in →";
|
|
|
|
|
|
isOperator = wallet.address === OPERATOR_ADDRESS;
|
|
|
|
|
|
await enterAdmin();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$("signout-btn").addEventListener("click", () => {
|
|
|
|
|
|
wallet = null; isOperator = false;
|
|
|
|
|
|
$("me").classList.add("hidden");
|
|
|
|
|
|
$("mint").classList.add("hidden");
|
|
|
|
|
|
$("signin").classList.remove("hidden");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
async function enterAdmin() {
|
|
|
|
|
|
$("signin").classList.add("hidden");
|
|
|
|
|
|
$("me").classList.remove("hidden");
|
|
|
|
|
|
$("me-addr").textContent = wallet.address;
|
|
|
|
|
|
$("me-taddr").textContent = wallet.tokenAddress;
|
|
|
|
|
|
const role = $("me-role");
|
|
|
|
|
|
role.textContent = isOperator ? "operator" : "read-only";
|
|
|
|
|
|
role.className = "badge " + (isOperator ? "b-op" : "b-other");
|
|
|
|
|
|
if (isOperator) {
|
|
|
|
|
|
$("mint").classList.remove("hidden");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- TLD registry (always visible, signed-in or not) ----------
|
|
|
|
|
|
async function loadRegistry() {
|
|
|
|
|
|
const status = $("registry-status");
|
|
|
|
|
|
const table = $("registry-table");
|
|
|
|
|
|
status.className = "status";
|
|
|
|
|
|
status.textContent = "Loading current registry from the chain…";
|
|
|
|
|
|
table.innerHTML = "";
|
|
|
|
|
|
try {
|
|
|
|
|
|
const r = await fetch("https://navigate.st/api/tlds");
|
|
|
|
|
|
if (!r.ok) throw new Error("API " + r.status);
|
|
|
|
|
|
const body = await r.json();
|
|
|
|
|
|
// /api/tlds returns { source, root, count, legend, tlds: [{tld, certificate:{category, owner, regHeight,...}, records}] }
|
|
|
|
|
|
const tlds = body.tlds || [];
|
|
|
|
|
|
status.className = "status ok";
|
|
|
|
|
|
status.textContent = `${tlds.length} TLDs on the beacon` + (body.root ? ` · root ${body.root.slice(0, 16)}…` : "");
|
|
|
|
|
|
let html = '<table class="tlds"><thead><tr><th>TLD</th><th>Category</th><th>Records</th><th>Height</th></tr></thead><tbody>';
|
|
|
|
|
|
for (const t of tlds.sort((a, b) => a.tld.localeCompare(b.tld))) {
|
|
|
|
|
|
const cat = (t.certificate?.category || t.category || "").slice(0, 16);
|
|
|
|
|
|
const recs = t.records && Object.keys(t.records).length ? JSON.stringify(t.records) : "";
|
|
|
|
|
|
const h = t.certificate?.regHeight ?? t.regHeight ?? "";
|
|
|
|
|
|
html += `<tr><td class="tld">.${esc(t.tld)}</td><td class="cat">${esc(cat)}${cat ? "…" : ""}</td><td class="records" title="${esc(recs)}">${esc(recs)}</td><td class="cat">${esc(String(h))}</td></tr>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
html += "</tbody></table>";
|
|
|
|
|
|
table.innerHTML = html;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
status.className = "status err";
|
|
|
|
|
|
status.textContent = "Could not load registry: " + (e.message || e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
loadRegistry();
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- mint a new TLD ----------
|
|
|
|
|
|
$("mint-btn").addEventListener("click", async () => {
|
|
|
|
|
|
if (!wallet || !isOperator) return;
|
|
|
|
|
|
const tld = $("mint-tld").value.trim().toLowerCase();
|
|
|
|
|
|
if (!/^[a-z0-9-]{1,16}$/.test(tld)) {
|
|
|
|
|
|
$("mint-msg").className = "msg err";
|
|
|
|
|
|
$("mint-msg").textContent = "TLD label must be 1–16 chars [a-z0-9-]";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
let records = {};
|
|
|
|
|
|
const rawRecords = $("mint-records").value.trim();
|
|
|
|
|
|
if (rawRecords) {
|
|
|
|
|
|
try { records = JSON.parse(rawRecords); }
|
|
|
|
|
|
catch (e) { $("mint-msg").className = "msg err"; $("mint-msg").textContent = "Records JSON is invalid: " + e.message; return; }
|
|
|
|
|
|
}
|
|
|
|
|
|
const btn = $("mint-btn");
|
|
|
|
|
|
const msg = $("mint-msg");
|
|
|
|
|
|
const log = $("mint-log");
|
|
|
|
|
|
log.className = "steps-log on"; log.innerHTML = "";
|
|
|
|
|
|
msg.textContent = ""; msg.className = "msg";
|
|
|
|
|
|
const push = (t) => { const d = document.createElement("div"); d.textContent = t; log.appendChild(d); log.scrollTop = log.scrollHeight; };
|
|
|
|
|
|
btn.disabled = true; btn.textContent = "Signing…";
|
|
|
|
|
|
let el = null;
|
|
|
|
|
|
try {
|
|
|
|
|
|
el = await BNS.connect();
|
|
|
|
|
|
push("connected to chipnet");
|
|
|
|
|
|
const res = await BNS.registerTldWithBuiltInWallet(el, {
|
|
|
|
|
|
wallet, tld, records,
|
|
|
|
|
|
onProgress: (step) => push(step),
|
|
|
|
|
|
});
|
|
|
|
|
|
push("broadcast: " + (res.txid || res.txId || "(unknown)"));
|
|
|
|
|
|
msg.className = "msg ok";
|
|
|
|
|
|
msg.textContent = "Minted .${tld}. Category " + (res.category || "unknown") + ". Registry refresh below in ~30s.";
|
|
|
|
|
|
btn.textContent = "Mint TLD →"; btn.disabled = false;
|
|
|
|
|
|
$("mint-tld").value = "";
|
|
|
|
|
|
$("mint-records").value = "{}";
|
|
|
|
|
|
setTimeout(loadRegistry, 3000);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
msg.className = "msg err";
|
|
|
|
|
|
msg.textContent = "Mint failed: " + (e.message || e);
|
|
|
|
|
|
push("error: " + (e.message || e));
|
|
|
|
|
|
btn.disabled = false; btn.textContent = "Mint TLD →";
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
try { el?.close?.(); } catch {}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
2026-09-06 18:53:17 +02:00
|
|
|
|
<script defer src="../js/profile-menu.js"></script>
|
2026-09-06 18:46:45 +02:00
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|