sirius/tld.html
Local Dev 105a245b2f site-sirius-x: only paint .X acid; keep Sirius white
Brand button was wrapping the whole "Sirius.X" label in <span class="g">,
so the entire name rendered acid green. Consistency across the ecosystem
is Sirius (ink) + .X (acid), like Theseus.X and Hephaestus.X. Split the
span across every page (index, tld, portal, admin, brand, docs, market,
storage, theseus/) so only the .X tail carries the acid tint.
2026-09-22 01:28:25 +02:00

387 lines
20 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>Register a TLD — Sirius.X</title>
<meta name="description" content="Mint a top-level domain on Bitcoin Cash. Any wallet can register a TLD; second-level names can then be sold under it.">
<link rel="icon" href="./assets/favicon.svg" type="image/svg+xml">
<link rel="mask-icon" href="./assets/favicon.svg" color="#d6ff3d">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Sirius.X">
<meta property="og:title" content="Register a TLD on Bitcoin Cash">
<meta property="og:description" content="Mint a TLD certificate. One BCH transaction, no registrar. Renewals are per-TLD policy.">
<meta property="og:url" content="https://silentmode.st/sirius-x/tld.html">
<meta property="og:image" content="https://silentmode.st/sirius-x/brand/banner.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500;9..144,600;9..144,700&family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Ubuntu:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#050810; --panel:#141a24; --panel2:#0a0f1c; --line:rgba(255,255,255,.06);
--sky-mid:#0a0f1c; --sky-rim:#131a2a;
--ink:#f1f4fa; --mut:#b8c2d4; --dim:#6a7488; --acid:#d6ff3d;
--chain-native:#0AC18E;
--ok:#4fd1a5; --taken:#f6768a; --warn:#ffc75f;
--serif:'Fraunces',Georgia,serif;
--sans:'DM Sans','Inter',system-ui,sans-serif;
--mono:'JetBrains Mono',ui-monospace,monospace;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;
background:radial-gradient(ellipse 1200px 600px at 50% -5%,var(--sky-rim) 0%,transparent 60%),
radial-gradient(ellipse 900px 500px at 100% 100%,var(--sky-mid) 0%,transparent 55%),
radial-gradient(ellipse 900px 500px at 0% 100%,#0c1424 0%,transparent 55%),
var(--bg);
background-attachment:fixed;
color:var(--ink);font:16px/1.6 var(--sans);min-height:100vh;font-feature-settings:"ss01","ss02"}
h1,h2,h3{font-family:var(--serif);font-feature-settings:"ss01","ss02"}
code,.mono{font-family:var(--mono)}
html[data-font-cmp="ubuntu"] body,
html[data-font-cmp="ubuntu"] h1, html[data-font-cmp="ubuntu"] h2, html[data-font-cmp="ubuntu"] h3,
html[data-font-cmp="ubuntu"] p, html[data-font-cmp="ubuntu"] a, html[data-font-cmp="ubuntu"] button,
html[data-font-cmp="ubuntu"] span, html[data-font-cmp="ubuntu"] div,
html[data-font-cmp="ubuntu"] label, html[data-font-cmp="ubuntu"] input, html[data-font-cmp="ubuntu"] li{
font-family:'Ubuntu',system-ui,sans-serif !important
}
html[data-font-cmp="ubuntu"] code, html[data-font-cmp="ubuntu"] .mono{font-family:var(--mono) !important}
.font-picker{display:inline-flex;align-items:center;gap:2px;padding:2px;margin:0 10px;
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:999px;
font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase}
.font-picker button{background:transparent;border:none;color:var(--dim);
padding:4px 10px;border-radius:999px;cursor:pointer;font-family:inherit;font-size:inherit;
letter-spacing:inherit;text-transform:inherit;transition:color .12s}
.font-picker button:hover{color:var(--ink)}
.font-picker button.active{background:var(--acid);color:var(--bg);font-weight:600}
a{color:var(--acid)}
.wrap{max-width:820px;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)}
.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)}
/* Adaptive side nav — same shape as index.html and Theseus.X */
:root{--sn-w:200px}
.sidenav{position:fixed;top:56px;left:0;bottom:0;width:var(--sn-w);z-index:15;
background:transparent;padding:32px 12px 22px 18px;
overflow-y:auto;pointer-events:none;transition:width .15s ease}
.sidenav a{display:flex;align-items:center;gap:14px;color:var(--mut);text-decoration:none;
padding:9px 12px;border-radius:10px;
font-size:14.5px;font-weight:500;opacity:.35;pointer-events:auto;
transition:opacity .12s,color .12s,background .12s,backdrop-filter .12s}
.sidenav a .ic{flex:none;width:22px;font-size:18px;line-height:1;text-align:center;
display:none;justify-content:center;align-items:center}
.sidenav a .lb{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sidenav a:hover{opacity:1;color:var(--ink);background:rgba(11,14,20,.72);backdrop-filter:blur(8px)}
.sidenav a.active{opacity:.85;color:var(--acid)}
.sidenav a.active:hover{opacity:1}
section{scroll-margin-top:70px}
@media (max-width:1180px){
:root{--sn-w:64px}
.sidenav{padding:22px 6px}
.sidenav a{justify-content:center;padding:12px 4px;gap:0}
.sidenav a .ic{display:inline-flex}
.sidenav a .lb{display:none}
}
@media (max-width:720px){
:root{--sn-w:0}
body{padding-bottom:64px}
.sidenav{top:auto;bottom:0;left:0;right:0;width:100%;height:auto;
padding:4px;flex-direction:row;background:rgba(11,14,20,.52);
display:flex;justify-content:space-around;overflow-x:auto;
/* Hide horizontal scrollbar chrome; icons still swipe/scroll when they overflow. */
scrollbar-width:none;-ms-overflow-style:none}
.sidenav::-webkit-scrollbar{display:none;width:0;height:0}
.sidenav a{flex:1;min-width:44px;padding:8px 4px;justify-content:center;gap:0;opacity:.5;font-size:15px}
.sidenav a .ic{display:inline-flex}
.sidenav a .lb{display:none}
}
header.hero{text-align:center;padding:3.4rem 1.2rem 1rem}
.mark{font-size:44px;line-height:1}
h1{font-size:clamp(1.7rem,4.5vw,2.3rem);margin:.3rem 0 .3rem}
h1 .g{color:var(--acid)}
.tag{color:var(--mut);font-size:1rem;max-width:600px;margin:0 auto}
h2{font-size:1.25rem;margin:0 0 .4rem}
section{padding:2rem 0;border-top:1px solid var(--line);margin-top:2rem}
section:first-of-type{border-top:none;margin-top:1rem}
.btn{display:inline-block;text-decoration:none;padding:10px 18px;border-radius:10px;background:#4b7bec;color:#fff;font-size:14px;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:hover{filter:brightness(1.12)}
.btn:disabled{opacity:.45;cursor:not-allowed;filter:none}
.muted{color:var(--mut);font-size:13px}
.dim{color:var(--dim);font-size:12.5px}
code,.mono{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px;word-break:break-all}
.badge{display:inline-block;font-size:11px;padding:2px 10px;border-radius:999px;font-weight:600}
.b-ok{background:rgba(79,209,165,.15);color:var(--ok)}
.b-taken{background:rgba(246,118,138,.15);color:var(--taken)}
.b-check{background:rgba(139,152,169,.14);color:var(--mut)}
.status{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:10px 14px;font-size:13px;color:var(--mut);margin-top:12px}
.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)}
/* TLD search cell */
.search{max-width:600px;margin:1.5rem auto 0}
.searchrow{display:flex;gap:8px}
.searchrow input{flex:1;padding:14px 18px;font-size:17px;border-radius:12px;border:1px solid #ffffff22;background:#141a24;color:var(--ink);font-family:inherit;outline:none}
.searchrow input:focus{border-color:#4b7bec}
.searchhint{font-size:12.5px;color:var(--mut);margin-top:8px;text-align:center}
.result-card{display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;
background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 18px;margin-top:12px}
.result-card .lbl{font-family:ui-monospace,monospace;font-size:18px;color:var(--ink)}
.result-card .lbl .dot{color:var(--acid)}
/* Registered-TLDs table */
table.tlds{width:100%;border-collapse:collapse;margin-top:1rem;font-size:13px}
table.tlds th,table.tlds td{padding:8px 12px;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;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.price, table.tlds .price{
color:var(--acid);font-family:ui-monospace,monospace;font-size:13px;font-weight:600;
padding:2px 8px;border-radius:6px;background:rgba(214,255,61,.10);white-space:nowrap;display:inline-block
}
.result-card{grid-template-columns:1fr auto auto auto !important}
footer{border-top:1px solid var(--line);padding:2rem 0 3rem;color:var(--dim);font-size:13px;text-align:center;margin-top:2.5rem}
</style>
<script type="importmap">
{ "imports": { "@bitauth/libauth": "https://silentmode.st/js/libauth.js?v=20260920usd" } }
</script>
<script src="./js/i18n.js?v=20260921c"></script>
</head>
<body>
<nav class="topnav">
<a class="brand" href="./"><svg width="18" height="18" viewBox="0 0 64 64" style="vertical-align:-4px;margin-right:6px" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 4 L36.4 27.6 L60 32 L36.4 36.4 L32 60 L27.6 36.4 L4 32 L27.6 27.6 Z" fill="#d6ff3d"/></svg>Sirius<span class="g">.X</span></a>
<a href="./tld.html" class="here">🌐 TLD</a>
<!-- Trailing slash is required — the URL points at the market/ subdirectory,
not a bare `market` page. Stripping the slash forces Angie to serve the
market.html redirect stub (English, cached 2min) before it can bounce the
user to market/, which reads as an English flash on other-language sessions. -->
<a href="./market/">🛒 Market</a>
<a href="./theseus"><svg width="14" height="14" viewBox="0 0 64 64" style="vertical-align:-2px;margin-right:4px" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/><path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/><path d="M6 32 L32 27 L58 32 L32 37 Z" fill="#d6ff3d" opacity=".55"/><circle cx="32" cy="32" r="3" fill="#0b0e14"/></svg>Theseus<span class="g">.X</span></a>
<a href="./portal.html" class="portal">🔑 Sign in</a>
</nav>
<aside class="sidenav" aria-label="On this page">
<a href="#search-section" title="Search a TLD" ><span class="ic" aria-hidden="true">🔎</span><span class="lb">Search</span></a>
<a href="#listing" title="Registered TLDs" ><span class="ic" aria-hidden="true">📋</span><span class="lb">Registered</span></a>
</aside>
<header class="hero">
<div class="mark">🌐</div>
<h1>Register a <span class="g">TLD</span></h1>
<p class="tag">A TLD is a certificate on the Bitcoin Cash chain. You mint it, you own it — any
second-level name registered under it lives in your TLD's registry. One BCH transaction,
no ICANN. Renewals? Your TLD, your policy.</p>
</header>
<div class="wrap">
<section id="search-section">
<h2>Search a TLD</h2>
<p class="muted">Type a label (az, 09, hyphen; up to 16 chars). Available TLDs get a Register
button that opens the shared wallet-choice modal — the same flow name registration uses.
As TLD owner you earn <b style="color:var(--ok)">90 %</b> of every name registered under
your TLD. Renewals are your call — set them per TLD, or don't.</p>
<div class="search">
<div class="searchrow">
<input id="q" placeholder="your-tld" spellcheck="false" autocomplete="off" autofocus maxlength="16">
</div>
<div class="searchhint">Register minting to your token address; you become the registry operator.</div>
</div>
<div id="result"></div>
</section>
<section id="listing">
<h2>Registered TLDs</h2>
<p class="muted">Fetched from the on-chain TLD registry via the public gateway. Refresh the page for the latest.</p>
<div id="tlds-status" class="status">Loading…</div>
<table class="tlds" id="tlds-table" style="display:none">
<thead><tr><th>TLD</th><th>Mint price</th><th>Category</th></tr></thead>
<tbody></tbody>
</table>
</section>
</div>
<footer id="site-footer"></footer>
<script>
// Simple oracle stub: static chipnet rate for testing. Same number the
// portal uses. Mainnet swaps for a real price feed.
const CHIPNET_SATS_PER_USD = 400_000; // first-paint fallback only; pricing.js serves the live rate
const DEFAULT_USD = 5;
const $ = (id) => document.getElementById(id);
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));
const validLabel = (s) => /^[a-z0-9-]{1,16}$/.test(s);
const normLabel = (s) => String(s).toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 16);
// ---------- registered-TLD list ----------
let registeredTlds = null;
async function loadTldList() {
const status = $("tlds-status"), tbody = $("tlds-table").querySelector("tbody");
try {
const r = await fetch("https://navigate.st/api/tlds");
if (!r.ok) throw new Error("api " + r.status);
const j = await r.json();
const tlds = (j.tlds || []).map((t) => (typeof t === "string" ? { tld: t } : t));
tlds.sort((a, b) => a.tld.localeCompare(b.tld));
tbody.innerHTML = tlds.map((t) => {
const p = window.siriusPricing?.priceForTld(t.tld);
const priceCell = p ? `<span class="price" title="${esc(p.tier)}">${esc(window.siriusPricing.formatUsd(p.usd))}</span>` : "—";
return `<tr>
<td class="tld">.${esc(t.tld)}</td>
<td class="price-cell">${priceCell}</td>
<td class="cat">${esc(t.certificate || t.category || "")}</td>
</tr>`;
}).join("");
$("tlds-table").style.display = "";
status.className = "status ok";
status.textContent = `${tlds.length} TLDs registered on chipnet.`;
registeredTlds = tlds.map((t) => t.tld);
refreshSearch();
} catch (e) {
status.className = "status err";
status.textContent = "Could not load TLD list: " + (e.message || e);
}
}
loadTldList();
// ---------- search result renderer ----------
function renderResult(label, state) {
const res = $("result");
if (!label) { res.innerHTML = ""; return; }
if (state === "checking") {
res.innerHTML = `<div class="result-card">
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
<span class="badge b-check">checking…</span><span></span></div>`;
return;
}
if (state === "taken") {
res.innerHTML = `<div class="result-card">
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
<span class="badge b-taken">taken</span><span></span></div>
<p class="dim" style="margin-top:8px">Someone already minted <code>.${esc(label)}</code>. Try a
different label — TLDs are first-come, first-served.</p>`;
return;
}
const p = window.siriusPricing?.priceForTld(label);
const priceBadge = p
? `<span class="price" title="${esc(p.tier)}">${esc(window.siriusPricing.formatUsd(p.usd))}</span>`
: "";
res.innerHTML = `<div class="result-card">
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
<span class="badge b-ok">available</span>
${priceBadge}
<button class="btn acid" id="go-register">Register .${esc(label)} →</button>
</div>`;
$("go-register").onclick = () => beginRegister(label);
}
// ---------- search wiring ----------
let currentLabel = "";
function refreshSearch() {
const raw = normLabel($("q").value);
currentLabel = raw;
if (!raw) { renderResult("", null); return; }
if (!validLabel(raw)) { renderResult(raw, "invalid"); return; }
if (registeredTlds !== null) {
renderResult(raw, registeredTlds.includes(raw) ? "taken" : "ok");
return;
}
renderResult(raw, "checking");
}
$("q").addEventListener("input", () => {
$("q").value = normLabel($("q").value);
refreshSearch();
});
$("q").addEventListener("keydown", (e) => {
if (e.key === "Enter" && currentLabel && registeredTlds && !registeredTlds.includes(currentLabel)) {
beginRegister(currentLabel);
}
});
// ---------- mint delegation ----------
// Register button hands the label off to the shared modal (register-flow.js
// — window.siriusRegisterTld). That modal owns the wallet-choice, fund,
// confirm, and mint steps — same UX as name registration.
function beginRegister(label) {
// Label-based tier pricing lives in siriusPricing (js/pricing.js) so
// landing / tld / mint agree. Fall back to the flat DEFAULT_USD if the
// pricing module hasn't loaded for some reason.
const p = window.siriusPricing?.priceForTld(label);
const fee = p ? p.sats : BigInt(DEFAULT_USD * (window.siriusPricing?.SATS_PER_USD || CHIPNET_SATS_PER_USD));
const call = () => window.siriusRegisterTld(label, { serviceFeeSats: fee });
if (typeof window.siriusRegisterTld === "function") { call(); return; }
// Module still loading — retry until it lands.
const btn = $("go-register");
const orig = btn?.textContent;
if (btn) { btn.disabled = true; btn.textContent = "loading…"; }
const t = setInterval(() => {
if (typeof window.siriusRegisterTld === "function") {
clearInterval(t);
if (btn) { btn.disabled = false; btn.textContent = orig; }
call();
}
}, 100);
}
// ---------- ?q= deep-link (from landing links etc.) ----------
(() => {
const q = normLabel(new URLSearchParams(location.search).get("q") || "");
if (!q) return;
$("q").value = q;
const wait = setInterval(() => {
if (registeredTlds === null) return;
clearInterval(wait);
refreshSearch();
setTimeout(() => $("q").scrollIntoView({ behavior: "smooth", block: "start" }), 100);
}, 150);
})();
</script>
<script type="module" src="./js/register-flow.js?v=20260920usd"></script>
<script src="./js/pricing.js?v=20260920bits"></script>
<script src="./js/session.js?v=20260917dash"></script>
<script src="./js/pin-escrow.js?v=20260909pin"></script>
<script defer src="./js/theme.js?v=20260921mob"></script>
<script defer src="./js/site-footer.js?v=20260917market"></script>
<script defer src="./js/profile-menu.js?v=20260921mob"></script>
<script>
// Sidenav scroll-spy: highlight the section currently in view.
// Uses scroll-position tracking so short sections at the end of the page
// still activate their link when the reader scrolls to them.
(() => {
const links = document.querySelectorAll(".sidenav a[href^='#']");
if (!links.length) return;
const entries = [];
links.forEach((a) => { const el = document.querySelector(a.getAttribute("href")); if (el) entries.push({ el, link: a }); });
if (!entries.length) return;
const setActive = (link) => { links.forEach((l) => l.classList.remove("active")); link.classList.add("active"); };
const update = () => {
const doc = document.documentElement;
const scrollBottom = window.scrollY + window.innerHeight;
if (scrollBottom >= (doc.scrollHeight - 4)) { setActive(entries[entries.length - 1].link); return; }
const line = window.scrollY + window.innerHeight * 0.35;
let active = entries[0].link;
for (const e of entries) {
if (e.el.getBoundingClientRect().top + window.scrollY <= line) active = e.link;
else break;
}
setActive(active);
};
window.addEventListener("scroll", update, { passive: true });
window.addEventListener("resize", update);
update();
})();
</script>
</body>
</html>