refactor: restyle index.html con design Slate (CSS/JS/SVG inline)

This commit is contained in:
2026-06-29 16:21:33 +02:00
parent a0490e4d8e
commit 54cbf385c7

View File

@@ -3,308 +3,491 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bal Server List</title>
<title>BAL Server List</title>
<link rel="icon" href="Logo_nero.ico" type="image/x-icon">
<link rel="stylesheet" href="w3.css">
<script src="qrcode.min.js"></script>
<script src="purify.min.js"></script>
<script src="bal.js"></script>
<style>
<style>
@font-face { font-family: 'Optima'; src: url('OPTIMA.TTF'); }
h1, h2, p {font-family: 'Optima', sans-serif;}
table {
border-collapse: collapse;
width: 100%;
margin-top: 20px;
/* =========================================================
BAL Server List — minimal redesign ("Slate")
Same backend fields as the original:
url, info, base_fee, version, balance, status,
last_block, count_win, points (+ chain, id)
========================================================= */
:root{
--maxw:1120px;
--radius:10px;
--bg:#eef1f4;
--panel:#ffffff;
--ink:#1a2027;
--muted:#5b6672;
--line:#dfe4ea;
--line-strong:#cbd2da;
--accent:#2d6cdf;
--accent-soft:#e6eefc;
--ok:#1f9d57;
--ok-soft:#e3f4ea;
--off:#9aa4b0;
--off-soft:#eceff3;
--rank:#94a0ad;
--display:"Inter","Segoe UI",system-ui,-apple-system,sans-serif;
--body:"Inter","Segoe UI",system-ui,-apple-system,sans-serif;
--mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
--shadow:0 1px 2px rgba(20,30,50,.05);
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
*{box-sizing:border-box}
html,body{margin:0}
body{
background:var(--bg);
color:var(--ink);
font-family:var(--body);
line-height:1.5;
-webkit-font-smoothing:antialiased;
}
th {
background-color: #f2f2f2;
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 20px}
/* ---------- Header / hero ---------- */
header.hero{padding:48px 0 28px;border-bottom:1px solid var(--line)}
.hero .wrap{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:16px}
.brand .mark{
/* Brand mark: light, rounded container that holds the scalable SVG logo.
Background is white so the (dark) logo is clearly visible; the SVG uses
fill:currentColor, which inherits this color. */
width:46px;height:46px;border-radius:12px;flex:none;
display:grid;place-items:center;
background:#fff;color:var(--ink);
border:1px solid var(--line-strong);box-shadow:var(--shadow);overflow:hidden;
}
tr:nth-child(even) {
background-color: #f9f9f9;
/* The inline logo is a vector (SVG) so it stays crisp at ANY size; we only
set a display size here and can change it freely without quality loss. */
.brand .mark .brand-logo{width:34px;height:34px;display:block}
.brand h1{
font-family:var(--display);font-weight:600;
font-size:30px;line-height:1.05;margin:0;letter-spacing:-.01em;
}
body>div:first-of-type{
text-align:center;
.brand .sub{color:var(--muted);font-size:14px;margin-top:2px}
.hero-stats{display:flex;gap:28px}
.stat .n{font-family:var(--display);font-size:26px;font-weight:600;line-height:1}
.stat .l{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-top:6px}
/* ---------- Controls ---------- */
.controls{display:flex;gap:12px;align-items:center;flex-wrap:wrap;padding:22px 0 14px}
.seg{display:inline-flex;border:1px solid var(--line-strong);border-radius:8px;overflow:hidden}
.seg button{
font:inherit;font-size:13px;padding:8px 13px;border:0;cursor:pointer;
background:var(--panel);color:var(--muted);border-right:1px solid var(--line);
}
.seg button:last-child{border-right:0}
.seg button[aria-pressed="true"]{background:var(--accent-soft);color:var(--ink);font-weight:600}
.field{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted)}
.toggle{position:relative;width:38px;height:22px;flex:none}
.toggle input{opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.toggle .track{position:absolute;inset:0;border-radius:999px;background:var(--line-strong);transition:.18s}
.toggle .knob{position:absolute;top:3px;left:3px;width:16px;height:16px;border-radius:50%;background:#fff;transition:.18s;box-shadow:0 1px 2px rgba(0,0,0,.25)}
.toggle input:checked ~ .track{background:var(--ok)}
.toggle input:checked ~ .knob{transform:translateX(16px)}
.search{flex:1 1 200px;min-width:160px}
.search input{
width:100%;font:inherit;font-size:14px;padding:9px 12px;
border:1px solid var(--line-strong);border-radius:8px;
background:var(--panel);color:var(--ink);
}
.search input::placeholder{color:var(--muted)}
.btn-ghost{
font:inherit;font-size:13px;cursor:pointer;text-decoration:none;
padding:9px 14px;border-radius:8px;border:1px solid var(--line-strong);
background:var(--panel);color:var(--ink);display:inline-flex;gap:7px;align-items:center;
}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent)}
/* ---------- Table ---------- */
.table-card{
background:var(--panel);border:1px solid var(--line);
border-radius:var(--radius);box-shadow:var(--shadow);
/* Show roughly 10 rows; once the list is longer the card scrolls
vertically instead of growing the page. The header stays pinned. */
max-height:600px;overflow:auto;
}
table{width:100%;border-collapse:collapse;font-size:14px}
thead th{
text-align:left;font-weight:600;color:var(--muted);
font-size:11px;letter-spacing:.08em;text-transform:uppercase;
padding:14px 14px;border-bottom:1px solid var(--line-strong);
white-space:nowrap;
/* Keep the column headers visible while the table body scrolls. */
position:sticky;top:0;background:var(--panel);z-index:2;
}
thead th.num,tbody td.num{text-align:right}
tbody td{padding:14px 14px;border-bottom:1px solid var(--line);vertical-align:middle}
tbody tr:last-child td{border-bottom:0}
tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
.rank{font-family:var(--display);font-weight:600;color:var(--rank);font-size:15px;width:34px}
.rank.top{color:var(--accent)}
.url a{color:var(--ink);text-decoration:none;font-weight:600}
.url a:hover{color:var(--accent);text-decoration:underline}
.url .tag{
display:inline-block;margin-left:8px;font-size:10px;letter-spacing:.08em;
text-transform:uppercase;color:var(--muted);border:1px solid var(--line-strong);
border-radius:5px;padding:1px 6px;vertical-align:middle;
}
.info{color:var(--muted);font-size:13px;max-width:200px}
.mono{font-family:var(--mono);font-size:13px}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
.score{font-family:var(--display);font-weight:600;font-size:15px}
.status{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:600;white-space:nowrap}
.dot{width:8px;height:8px;border-radius:50%;flex:none}
.status.online{color:var(--ok)}
.status.online .dot{background:var(--ok);box-shadow:0 0 0 3px var(--ok-soft)}
.status.offline{color:var(--off)}
.status.offline .dot{background:var(--off);box-shadow:0 0 0 3px var(--off-soft)}
/* ---------- Add server ---------- */
.add-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:24px;margin:40px 0;align-items:start}
.panel{
background:var(--panel);border:1px solid var(--line);
border-radius:var(--radius);padding:24px;box-shadow:var(--shadow);
}
.panel h2{font-family:var(--display);font-size:19px;margin:0 0 6px}
.panel p.lead{color:var(--muted);font-size:14px;margin:0 0 18px}
.form-row{display:flex;gap:10px;flex-wrap:wrap}
.form-row input{
flex:1 1 220px;font:inherit;font-size:14px;padding:11px 13px;
border:1px solid var(--line-strong);border-radius:8px;
background:var(--bg);color:var(--ink);font-family:var(--mono);
}
.btn{
font:inherit;font-size:14px;font-weight:600;cursor:pointer;
padding:11px 20px;border-radius:8px;border:1px solid var(--accent);
background:var(--accent);color:#fff;
}
.btn:hover{filter:brightness(1.06)}
.note{font-size:13px;color:var(--muted);margin-top:16px;line-height:1.55}
.note strong{color:var(--ink)}
/* invoice preview */
.invoice{display:none}
.invoice.show{display:block}
.invoice .row{display:flex;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid var(--line);font-size:13px}
.invoice .row:last-child{border-bottom:0}
.invoice .row span:first-child{color:var(--muted)}
.invoice .row span:last-child{font-family:var(--mono);text-align:right;word-break:break-all}
.qr{margin:18px auto 4px;width:160px;height:160px;display:grid;
grid-template-columns:repeat(21,1fr);gap:0;border:1px solid var(--line-strong);
border-radius:8px;overflow:hidden;background:#fff}
.qr i{background:#000}
.ok-line{color:var(--ok);font-weight:600;font-size:14px;margin-bottom:14px}
/* ---------- Info section ---------- */
/* ---------- Intro highlight card ----------
Prominent white box that introduces the page, placed above the
"Add your Will Executor" section. The accent stripe + larger title
give it more visual weight than the surrounding panels. */
.intro-card{
background:var(--panel);border:1px solid var(--line);
border-left:4px solid var(--accent);
border-radius:var(--radius);box-shadow:var(--shadow);
padding:28px 32px;margin:28px 0 8px;
}
.intro-card h2{font-family:var(--display);font-size:24px;margin:0 0 10px;letter-spacing:-.01em}
.intro-card p{margin:0;color:var(--muted);font-size:15px;line-height:1.65;max-width:820px}
.info-section{margin:48px 0}
.info-section h2{font-family:var(--display);font-size:22px;margin:0 0 8px}
.info-section .sub{color:var(--muted);font-size:15px;line-height:1.6;margin:0 0 28px;max-width:760px}
.info-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.info-block{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:22px 24px;box-shadow:var(--shadow)}
.info-block .ic{font-family:var(--mono);font-size:12px;color:var(--accent);letter-spacing:.1em;display:block;margin-bottom:10px}
.info-block h3{font-size:15px;margin:0 0 8px;letter-spacing:.01em}
.info-block p{font-size:14px;color:var(--muted);margin:0;line-height:1.6}
/* ---------- Footer ---------- */
footer{border-top:1px solid var(--line);margin-top:48px;padding:28px 0;color:var(--muted);font-size:13px}
footer .wrap{display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px}
footer a{color:var(--accent);text-decoration:none}
/* ---------- Mobile: table → cards ---------- */
.mobile-list{display:none}
@media (max-width:860px){
.table-card{display:none}
.mobile-list{display:grid;gap:12px;max-height:640px;overflow:auto;padding-right:2px}
.add-grid{grid-template-columns:1fr}
.info-grid{grid-template-columns:1fr}
.hero-stats{gap:20px}
.brand h1{font-size:24px}
.srv{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:16px;box-shadow:var(--shadow)}
.srv .top{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;margin-bottom:12px}
.srv .rk{font-family:var(--display);font-weight:600;color:var(--rank);font-size:18px}
.srv .grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;font-size:13px}
.srv .grid div span{display:block;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.srv .grid div b{font-family:var(--mono);font-weight:600}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
</style>
</head>
<body>
<header class="hero">
<div class="wrap">
<div class="brand">
<div class="mark"><svg class="brand-logo" viewBox="0 0 1036 1036" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Bitcoin After Life logo"><g transform="translate(0.000000,1036.000000) scale(0.100000,-0.100000)" fill="currentColor" stroke="none"><path d="M6130 10203 c-153 -318 -283 -665 -344 -916 -61 -250 -71 -335 -71 -602 1 -223 3 -249 28 -370 52 -244 140 -460 260 -633 72 -106 225 -257 320 -316 179 -111 335 -154 777 -211 232 -30 367 -61 442 -102 76 -41 195 -164 248 -257 170 -302 311 -774 362 -1211 19 -167 16 -591 -6 -745 -68 -484 -201 -871 -436 -1270 -541 -917 -1492 -1525 -2549 -1630 -132 -13 -453 -13 -596 0 -268 24 -530 77 -757 151 -1063 350 -1871 1192 -2187 2280 -63 217 -111 495 -127 742 -7 104 -7 107 -30 107 l-24 0 0 -167 c0 -286 27 -508 95 -773 148 -587 427 -1083 854 -1523 231 -237 423 -391 703 -563 685 -420 1543 -589 2343 -462 1346 213 2458 1211 2814 2524 89 329 116 543 115 919 0 349 -23 544 -100 847 -68 273 10 632 176 799 91 92 165 104 491 77 320 -26 499 36 699 242 171 175 285 422 320 690 15 116 12 401 -6 535 -13 100 -32 200 -91 478 -3 15 -8 27 -12 27 -3 0 -21 -30 -40 -66 -20 -39 -63 -96 -105 -139 -127 -130 -287 -216 -616 -330 -297 -104 -420 -161 -551 -256 -159 -117 -268 -285 -325 -504 -36 -135 -37 -312 -4 -500 12 -71 24 -132 26 -134 2 -2 41 33 87 78 115 115 199 177 369 270 267 146 442 260 590 384 38 32 72 57 74 54 7 -6 -107 -108 -193 -173 -88 -67 -259 -175 -402 -256 -58 -34 -146 -88 -196 -122 -107 -73 -276 -237 -332 -323 -50 -76 -100 -193 -116 -274 l-12 -60 -87 173 c-194 389 -411 680 -731 980 -375 353 -563 553 -677 723 -54 79 -170 297 -170 318 0 7 21 -22 46 -63 179 -288 481 -616 885 -962 265 -227 363 -338 496 -560 32 -54 60 -97 63 -98 9 0 100 229 141 352 99 302 114 589 44 869 -105 419 -325 659 -955 1039 -333 201 -500 322 -642 465 -148 150 -254 318 -297 475 -11 40 -22 75 -25 77 -2 2 -27 -44 -56 -104z"/><path d="M4436 9470 c-1069 -83 -2060 -527 -2825 -1265 -567 -548 -975 -1198 -1195 -1909 -248 -796 -290 -1575 -130 -2365 213 -1049 795 -2006 1636 -2688 727 -590 1604 -940 2558 -1023 189 -16 648 -14 825 5 601 63 1114 212 1645 481 834 421 1518 1083 1967 1904 296 540 464 1079 540 1725 23 203 23 683 -1 908 -39 374 -122 744 -237 1062 l-31 84 -151 -53 c-141 -49 -152 -54 -151 -77 1 -13 20 -94 42 -179 303 -1165 101 -2402 -553 -3395 -410 -622 -943 -1098 -1600 -1431 -469 -237 -913 -369 -1450 -430 -202 -24 -622 -24 -830 -1 -739 82 -1409 331 -1990 741 -293 206 -640 542 -869 840 -474 616 -756 1323 -843 2111 -20 185 -22 593 -4 785 112 1186 708 2250 1662 2965 619 465 1347 745 2139 826 127 13 546 18 745 9 l110 -5 21 160 c12 88 21 161 20 162 -4 4 -149 25 -275 39 -161 18 -620 26 -775 14z"/><path d="M3870 6492 c-52 -26 -73 -71 -78 -169 l-5 -83 -98 0 -99 0 0 -225 0 -225 100 0 100 0 0 -355 0 -355 -100 0 -100 0 0 -230 0 -230 100 0 100 0 0 -355 0 -355 -100 0 -100 0 0 -225 0 -225 100 0 100 0 0 -80 c0 -97 17 -142 62 -170 29 -17 50 -20 169 -20 125 0 139 2 170 23 43 29 58 71 58 170 l1 77 64 0 64 0 4 -111 c3 -107 4 -112 31 -135 27 -23 34 -24 182 -24 221 0 235 10 243 172 l5 98 261 0 c227 0 272 3 345 20 264 64 482 254 576 505 119 313 36 650 -216 878 l-72 64 33 47 c43 63 94 176 116 256 25 91 25 299 0 395 -59 230 -220 428 -431 530 -141 69 -182 77 -412 82 l-203 5 0 77 c0 104 -14 147 -53 171 -28 18 -51 20 -185 20 l-154 0 -34 -34 -34 -34 0 -101 0 -101 -65 0 -65 0 0 85 c0 162 -28 185 -222 185 -97 0 -130 -4 -158 -18z m1307 -732 c74 -45 103 -184 85 -416 -12 -151 -31 -208 -85 -248 -28 -21 -39 -21 -478 -24 l-449 -3 0 356 0 355 448 0 c426 0 448 -1 479 -20z m150 -1172 c46 -30 93 -97 125 -177 18 -45 22 -77 22 -161 1 -98 -1 -110 -30 -171 -35 -73 -92 -142 -135 -165 -23 -12 -115 -14 -544 -14 l-515 0 0 360 0 360 518 -3 518 -2 41 -27z"/></g></svg></div>
<div>
<a href="/"><img alt="BAL LOGO" src="/Logo_nero.png"></a>
<h1>Bal Server List</h1>
<div>
<select id="chain" name="chain">
<option selected value="bitcoin">Bitcoin</option>
<option value="testnet">Testnet</option>
<option value="testnet4">Testnet4</option>
<option>Regtest</option>
</select>
<h1>Will Executor — Server List</h1>
<div class="sub">Bitcoin After Life · Will-executor node leaderboard</div>
</div>
<div>
<label for="onion">Include tor: </label><input type="checkbox" name="onion" id="onion" value="yes" checked>
</div>
<div>
<a href="" id="download">Download</a>
<div class="hero-stats">
<div class="stat"><div class="n" id="s-online">5</div><div class="l">Online</div></div>
<div class="stat"><div class="n" id="s-total">6</div><div class="l">Servers</div></div>
<div class="stat"><div class="n">884,201</div><div class="l">Block</div></div>
</div>
<table id="willexecutors">
</div>
</header>
<main class="wrap">
<div class="controls">
<div class="seg" id="chainSeg" role="group" aria-label="Chain">
<button data-c="bitcoin" aria-pressed="true">Bitcoin</button>
<button data-c="testnet" aria-pressed="false">Testnet</button>
<button data-c="testnet4" aria-pressed="false">Testnet4</button>
<button data-c="regtest" aria-pressed="false">Regtest</button>
</div>
<label class="field">
<span class="toggle">
<input type="checkbox" id="onion" checked>
<span class="track"></span><span class="knob"></span>
</span>
Include Tor
</label>
<div class="search">
<input type="search" id="filter" placeholder="Filter by URL or info…" aria-label="Filter">
</div>
<a class="btn-ghost" id="download" href="#" download="willexecutors.json">↓ JSON</a>
</div>
<!-- Desktop table -->
<div class="table-card">
<table id="tbl">
<thead>
<tr>
<th>Url</th>
<th class="num">#</th>
<th>URL</th>
<th>Info</th>
<th>Fees</th>
<th class="num">Fee</th>
<th>Version</th>
<th>Balance</th>
<th class="num">Balance</th>
<th>Status</th>
<th>Height</th>
<th>Wins</th>
<th>Score</th>
<th class="num">Height</th>
<th class="num">Wins</th>
<th class="num">Score</th>
</tr>
</thead>
<tbody><!-- --></tbody>
<tbody id="tbody"></tbody>
</table>
</div>
<hr>
<form id="myServer">
<label for="url">Add your Will-Executor url:<br><input type="text" id="url" name="url" required placeholder="https://bitcoin-after.life:9137">
<button type="submit">Submit</button>
</form>
<div id="result_text"></div>
<div>
<a id="aqr">
<div id="qrcode"></div>
</a>
<!-- Mobile list -->
<div class="mobile-list" id="mlist"></div>
<!-- Intro highlight: moved here from the bottom of the page and given a
prominent white box so it reads as the page's primary description. -->
<section class="intro-card">
<h2>Will Executor Server List</h2>
<p>This page displays all Will Executor servers that manage the inheritance processing for the Bitcoin After Life plugin on Electrum. Will Executors operate in a competitive environment, racing to broadcast inheritance transactions to network nodes and earn the associated transaction fees.</p>
</section>
<!-- Add server -->
<div class="add-grid">
<div class="panel">
<h2>Add your Will Executor</h2>
<p class="lead">Enter your server URL. It will be checked online before it appears in the list.</p>
<div class="form-row">
<input type="text" id="url" placeholder="https://bitcoin-after.life:9137">
<button class="btn" id="submit">Add server</button>
</div>
<strong>
Please make sure your server is online before you attempt to add it.<br>
Servers that are offline will not appear in this list.<br>
They will be checked regularly until they come back online.
</strong>
<hr>
<div>
<b>Will Executor Server List</b>
<div>This page displays all Will Executor servers that manage the inheritance processing for the Bitcoin After Life plugin on Electrum. Will Executors operate in a competitive environment, racing to
broadcast inheritance transactions to network nodes and earn the associated transaction fees.</div>
<b>Listing Requirements</b>
<div>To be included in this list, a minimum contribution of 5,000 Satoshi is required.</div>
<b>Benefits of Listing</b>
<div>Listed Will Executors are automatically integrated into the plugin when inheritance arrangements are made, providing direct access to potential clients.</div>
<b>Ranking System</b>
<div>This leaderboard operates on a bid-based ranking system where higher contributions result in better positioning.</div>
<b>Early Adopter Incentives</b>
<div>To support the project's initial launch phase, we plan to maintain the first Will Executor servers for a minimum of 12 months, rewarding early participants. Subsequently, premium positions will be
available through an auction system based on protocol revenue generation.</div>
<b>Revenue Expectations</b>
<div>Operating as a Will Executor is not a get-rich-quick opportunity. This service requires patience and long-term commitment, but can provide substantial returns over time for dedicated operators.</div>
<div class="invoice" id="invoice">
<div class="ok-line">✓ Server added. Pay the invoice to complete.</div>
<div class="row"><span>Minimum amount</span><span>50,000 sats</span></div>
<div class="row"><span>Chain</span><span id="iv-chain">bitcoin</span></div>
<div class="row"><span>Address</span><span id="iv-addr">bc1q…demo…address</span></div>
<div class="row"><span>Status</span><span>online</span></div>
<div class="qr" id="qr" aria-label="Invoice QR (demo)"></div>
</div>
<hr>
<p>mail:<a href="mailto:info@bitcoin-after.life">welist@bitcoin-after.life</a></p>
<p class="note">
<strong>Please make sure your server is online before you attempt to add it.</strong>
Servers that are offline will not appear in this list. They will be checked regularly
until they come back online.
</p>
</div>
<div class="panel">
<h2>How ranking works</h2>
<p class="lead">Bid-based ranking: higher contributions rank higher.</p>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Listing threshold</span><b class="mono">50,000 sats</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Plugin integration</span><b>automatic</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;font-size:13px"><span style="color:var(--muted)">Early-adopter guarantee</span><b>12 months</b></div>
</div>
</div>
<!-- Info section -->
<section class="info-section">
<div class="info-grid">
<div class="info-block">
<span class="ic">01</span>
<h3>Benefits of Listing</h3>
<p>Listed Will Executors are automatically integrated into the plugin when inheritance arrangements are made, providing direct access to potential clients.</p>
</div>
<div class="info-block">
<span class="ic">02</span>
<h3>Ranking System</h3>
<p>This leaderboard operates on a bid-based ranking system where higher contributions result in better positioning.</p>
</div>
<div class="info-block">
<span class="ic">03</span>
<h3>Early Adopter Incentives</h3>
<p>To support the project's initial launch phase, we plan to maintain the first Will Executor servers for a minimum of 12 months, rewarding early participants. Subsequently, premium positions will be available through an auction system based on protocol revenue generation.</p>
</div>
<div class="info-block">
<span class="ic">04</span>
<h3>Revenue Expectations</h3>
<p>Operating as a Will Executor is not a get-rich-quick opportunity. This service requires patience and long-term commitment, but can provide substantial returns over time for dedicated operators.</p>
</div>
</div>
</section>
</main>
<footer>
<div class="wrap">
<span>Bitcoin After Life — Will Executor List</span>
<a href="mailto:welist@bitcoin-after.life">welist@bitcoin-after.life</a>
</div>
</footer>
<script>
class BalServerList {
constructor() {
this.chain = 'bitcoin';
this.page = 0;
this.limit = 100;
this.init();
/* ---- Sample data (same shape as the real backend) ----
12 example servers so the list demonstrates the scroll behaviour:
the table/cards show ~10 entries, the rest are reachable by scrolling.
Sorted by `points` (the bid score) at render time. */
const DATA = [
{url:"executor.bitcoin-after.life:9137", onion:false, info:"Primary node · EU", base_fee:1000, version:"1.2.0", balance:520000, status:"online", last_block:884201, count_win:142, points:120000},
{url:"exec.satoshi.eu:9137", onion:false, info:"Low latency · DE", base_fee:800, version:"1.2.0", balance:410000, status:"online", last_block:884201, count_win:118, points:98000},
{url:"node.lastwill.io:9137", onion:false, info:"High availability", base_fee:1500, version:"1.2.0", balance:360000, status:"online", last_block:884201, count_win:96, points:84000},
{url:"exec7g3k…q.onion:9137", onion:true, info:"Privacy-first relay", base_fee:1200, version:"1.2.0", balance:300000, status:"online", last_block:884200, count_win:88, points:76000},
{url:"heir.btcrelay.net:9137", onion:false, info:"US-East", base_fee:900, version:"1.2.0", balance:240000, status:"online", last_block:884201, count_win:71, points:65000},
{url:"will.nodeforge.org:9137", onion:false, info:"Community operated", base_fee:1100, version:"1.1.9", balance:190000, status:"online", last_block:884199, count_win:60, points:58000},
{url:"onx4f…z.onion:9137", onion:true, info:"Tor only", base_fee:1100, version:"1.2.0", balance:150000, status:"online", last_block:884201, count_win:52, points:52000},
{url:"exec.coldkeep.io:9137", onion:false, info:"Backup executor · APAC", base_fee:1300, version:"1.1.9", balance:120000, status:"online", last_block:884198, count_win:44, points:47000},
{url:"relay.inheritbtc.net:9137", onion:false, info:"", base_fee:1000, version:"1.2.0", balance:95000, status:"offline", last_block:884150, count_win:38, points:39000},
{url:"kx9d…w.onion:9137", onion:true, info:"Privacy relay · backup", base_fee:1400, version:"1.1.9", balance:70000, status:"online", last_block:884197, count_win:29, points:31000},
{url:"node.testator.eu:9137", onion:false, info:"Low fee", base_fee:600, version:"1.2.0", balance:48000, status:"online", last_block:884201, count_win:21, points:22000},
{url:"exec.legacychain.io:9137", onion:false, info:"New operator", base_fee:1200, version:"1.1.9", balance:25000, status:"offline", last_block:884120, count_win:9, points:11000},
];
const fmt = n => n.toLocaleString('en-US');
let withTor = true, q = "";
function rows(){
return DATA
.filter(d => withTor ? true : !d.onion)
.filter(d => (d.url+" "+d.info).toLowerCase().includes(q.toLowerCase()))
.sort((a,b)=>b.points-a.points);
}
init() {
this.parseUrlParams();
this.bindEvents();
this.fetchDataAndPopulateTable();
function render(){
const list = rows();
const tb = document.getElementById('tbody');
const ml = document.getElementById('mlist');
tb.innerHTML = ""; ml.innerHTML = "";
if(!list.length){
tb.innerHTML = '<tr><td colspan="10" style="padding:28px;text-align:center;color:var(--muted)">No servers found.</td></tr>';
}
parseUrlParams() {
const urlParams = new URLSearchParams(window.location.search);
this.page = parseInt(urlParams.get('page')) || 0;
this.limit = parseInt(urlParams.get('limit')) || 100;
this.chain = urlParams.get('chain') || 'bitcoin';
list.forEach((d,i)=>{
const rank = i+1;
const st = d.status==="online"
? '<span class="status online"><span class="dot"></span>Online</span>'
: '<span class="status offline"><span class="dot"></span>Offline</span>';
const tag = d.onion ? '<span class="tag">tor</span>' : '';
const chainSelect = document.getElementById('chain');
chainSelect.value = this.chain;
}
tb.insertAdjacentHTML('beforeend', `
<tr>
<td class="num"><span class="rank ${rank<=3?'top':''}">${rank}</span></td>
<td class="url"><a href="#">${d.url}</a>${tag}</td>
<td class="info">${d.info||'—'}</td>
<td class="num">${fmt(d.base_fee)}</td>
<td class="mono">${d.version}</td>
<td class="num">${fmt(d.balance)}</td>
<td>${st}</td>
<td class="num">${fmt(d.last_block)}</td>
<td class="num">${d.count_win}</td>
<td class="num"><span class="score">${fmt(d.points)}</span></td>
</tr>`);
bindEvents() {
document.getElementById('onion').addEventListener('change', () => {
this.fetchDataAndPopulateTable();
});
document.getElementById('chain').addEventListener('change', (e) => {
this.chain = this.getChain();
this.fetchDataAndPopulateTable();
});
document.getElementById('myServer').addEventListener('submit', (e) => {
this.handleServerSubmission(e);
});
}
async handleServerSubmission(e) {
e.preventDefault();
const urlInput = document.getElementById('url');
let url = urlInput.value.trim();
if (!url) return;
url = this.formatUrl(url);
urlInput.value = url;
try {
const response = await fetch('/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url, chain: this.chain })
});
const result = await response.json();
if (!response.ok) {
this.showError(result);
} else {
this.showSuccess(result, url);
}
} catch (error) {
console.error('Submission error:', error);
this.showError('Network error. Please try again.');
}
}
formatUrl(url) {
if (!url.startsWith('http://') && !url.startsWith('https://')) {
return 'https://' + url;
}
return url;
}
showError(message) {
const resultDiv = document.getElementById('result_text');
resultDiv.innerHTML = `<p style="color:red">${DOMPurify.sanitize(message)}</p>`;
}
showSuccess(result, url) {
const invoiceuri = `bitcoin:${result.address}?label=BAL&message=Order+For+url+%26+${encodeURIComponent(result.url)}`;
const cleanHtml = DOMPurify.sanitize(`
<p style="color:green">${url} successfully added</p>
<div>
Please Pay this invoice to complete the process:<br/>
<b><u>Less than ${result.min_amount} sats are considered donations! if you want your server to be listed you have to send at least ${result.min_amount} sats</u></b><br/>
<b>Chain:</b> ${result.chain}<br/>
<b>Address:</b> ${result.address}<br/>
<b>Balance:</b> ${result.balance}<br/>
<b>Status:</b> ${result.status}<br/>
ml.insertAdjacentHTML('beforeend', `
<div class="srv">
<div class="top">
<div><div class="rk">#${rank} ${tag}</div><a href="#" style="color:var(--ink);font-weight:600;text-decoration:none;font-size:14px">${d.url}</a></div>
${st}
</div>
`);
document.getElementById("result_text").innerHTML = cleanHtml;
// Generate QR code
const qrElement = document.getElementById("qrcode");
qrElement.innerHTML = "";
new QRCode(qrElement, invoiceuri);
document.getElementById("aqr").href = invoiceuri;
}
getChain() {
const select = document.getElementById('chain');
return select.value;
}
async fetchDataAndPopulateTable() {
try {
const params = new URLSearchParams({
page: this.page,
limit: this.limit
<div class="grid">
<div><span>Score</span><b>${fmt(d.points)}</b></div>
<div><span>Wins</span><b>${d.count_win}</b></div>
<div><span>Fee</span><b>${fmt(d.base_fee)} sat</b></div>
<div><span>Balance</span><b>${fmt(d.balance)}</b></div>
<div><span>Version</span><b>${d.version}</b></div>
<div><span>Height</span><b>${fmt(d.last_block)}</b></div>
</div>
</div>`);
});
if (!document.getElementById("onion").checked) {
params.append("onion", "no");
const online = DATA.filter(d=>d.status==="online").length;
document.getElementById('s-online').textContent = online;
document.getElementById('s-total').textContent = DATA.length;
}
const response = await fetch(`/data/${this.chain}?${params.toString()}`);
/* demo JSON download */
(function(){
const blob = new Blob([JSON.stringify(DATA,null,2)],{type:'application/json'});
document.getElementById('download').href = URL.createObjectURL(blob);
})();
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
/* controls */
document.getElementById('onion').addEventListener('change',e=>{withTor=e.target.checked;render();});
document.getElementById('filter').addEventListener('input',e=>{q=e.target.value;render();});
const data = await response.json();
this.downloadLink(data);
this.populateTable(data);
} catch (error) {
console.error('Error fetching data:', error);
this.showTableError();
}
}
showTableError() {
const tableBody = document.querySelector('#willexecutors tbody');
tableBody.innerHTML = '<tr><td colspan="6">Error loading data. Please try again later.</td></tr>';
}
downloadLink(data) {
const jsonString = JSON.stringify(data);
const blob = new Blob([jsonString], { type: 'application/json;charset=utf-8' });
const url = URL.createObjectURL(blob);
const downloadLink = document.getElementById('download');
downloadLink.href = url;
downloadLink.setAttribute('download', 'willexecutors.json');
}
get_url(id,url){
}
populateTable(willexecutors) {
const tableBody = document.querySelector('#willexecutors tbody');
tableBody.innerHTML = '';
if (Object.keys(willexecutors).length === 0) {
tableBody.innerHTML = '<tr><td colspan="9">No servers found</td></tr>';
return;
}
Object.entries(willexecutors).forEach(([key, we]) => {
const row = document.createElement('tr');
this.appendRawTd(`<a href='/weinfo/${we.chain}/${we.id}'>${we.url}</a>`, row);
this.appendTd(we.info || ' ', row);
this.appendTd(we.base_fee, row);
this.appendTd(we.version, row);
this.appendTd(we.balance, row);
this.appendTd(we.status, row);
this.appendTd(we.last_block, row);
this.appendTd(we.count_win, row);
this.appendTd(we.points, row);
tableBody.appendChild(row);
document.getElementById('chainSeg').addEventListener('click',e=>{
const b=e.target.closest('button'); if(!b)return;
[...e.currentTarget.children].forEach(x=>x.setAttribute('aria-pressed','false'));
b.setAttribute('aria-pressed','true');
render();
});
}
appendTd(content, tr) {
const td = document.createElement('td');
td.textContent = content;
tr.appendChild(td);
}
appendRawTd(content,tr){
const td = document.createElement('td');
td.innerHTML = DOMPurify.sanitize(content);
tr.appendChild(td);
}
}
document.addEventListener('DOMContentLoaded', () => {
new BalServerList();
/* invoice demo + faux QR */
document.getElementById('submit').addEventListener('click',()=>{
document.getElementById('iv-addr').textContent='bc1q'+Math.random().toString(36).slice(2,10)+'demo'+Math.random().toString(36).slice(2,8);
const inv=document.getElementById('invoice'); inv.classList.add('show');
const qr=document.getElementById('qr'); qr.innerHTML="";
for(let i=0;i<441;i++){ if(Math.random()>.52){qr.appendChild(document.createElement('i'));} else {qr.appendChild(document.createElement('em'));} }
inv.scrollIntoView({behavior:'smooth',block:'nearest'});
});
render();
</script>
</body>
</html>