Site UI 3: one design system, the live DAG module, the home page as one screen, the litepaper as a paper, every page on the shared tokens

site/site.css: the tokens of the miner app's third redesign in light and dark (a darker light-mode ember and molten for text
so every pair passes 4.5:1, checked by tools/ci/site-contrast-check.mjs), the type scale on Unbounded, Plex Sans and Plex
Mono, the nav with one primary, the footer with a System/Light/Dark control, cards, tiles, tables that scroll on a phone,
buttons, pills, notes, callouts, the state words, focus rings, reduced motion, print. Partials rebuilt; the head loads the
stylesheet and applies the stored theme before paint. Every page's own style block is reduced to its page rules.

site/live-dag.js: the shared DAG view for / and /live (docs/plans/site-ui-3.md section 6): blocks by header time and
miner lane, parent edges with the selected chain as one heavier path, blue lit, red dim, pending grey, proven filled,
checkpoint bands with their lock weight (locked solid, pending dashed), new blocks arriving with a glow, hover and tap
details in the ledger's words, wheel and pinch zoom of the window (30 to 300 s), a pause button, device-pixel sharpness,
a still frame under reduced motion; opts.mine marks the user's own blocks (the miner app embeds the same file),
opts.poll:false with dag.push(reply) lets a host feed it. Every pixel is a block the observer stored.

site/index.html: the one-screen story (what it is, the live scene with chain block, shards proven, last lock, vote keys and
hash rate as state words, three things a sceptic checks, the download, how it works, the wallet, the journey, economics),
every tick-list row kept, the chip model's numbers moved to the sceptic card, the testnet terms behind a chevron, the
scroll-reveal gone, the Open Graph set on the 1200 by 630 image, no horizontal overflow at 390.

site/litepaper.html: dark like the site with light on request, whole paper by default (the section mode kept, deep links
intact, print prints the paper), repository paths off the surface, the cover dated 6 October, the wallet at 0.1.4, the
roadmap's verifier figure aligned with the Mining section, the proof lag stated as measured (about 380 s against the 60 s
gate), the 0.3.6 plan dated with 0.3.14 stated, the two "tonight" placeholders said as not yet measured. Every ledger
sentence verbatim (tools/ci/ledger-text-check.mjs, 43 sentences).

site/live.html: the lane chart replaced by the module; "proven A/B in 10 min", "finality paused, last #N", "pending" for
"n/a"; the fee sentence true on both sides of DAA 210,000. site/metamask.html: its own canonical, the explorer linked, the
wallet's state said true. site/404.html: the page count and section count said true. site/build.mjs: the generated pages
on the system, each with its own eyebrow, the miners page's source notes as sentences. downloads.json refreshed from the
host (0.3.14). tools/ci/site-orphan-check.mjs: no site heading leaves one word alone at 390 px (log titles reported).

Checks: identity grep 0 hits, link check 854 links 0 broken, ledger text 43 of 43, contrast 32 pairs 0 under 4.5:1,
no horizontal overflow at 390 on 14 pages (the wallet page's timed table overflows by 5 px, for the wallet-ui-3 owner),
orphan check 0 site headings (the miner h1 at 96 characters is the site-miner agent's copy). After captures beside the
audit's: docs/plans/site-ui-3-shots/after/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-06 19:14:27 +00:00
parent 4e727402ce
commit b63be56016
90 changed files with 1663 additions and 2188 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View file

@ -13,9 +13,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -27,73 +28,21 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--fs-h1:clamp(32px,5.5vw,56px)}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;display:flex;flex-direction:column;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter);width:100%}
/* page block; tokens, type and components are in /site.css */
main{flex:1}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1{font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;margin:12px 0 16px}
p{margin:0;color:var(--ink-2);max-width:52ch}
.lost{padding-block:var(--sec)}
.places{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));margin-top:32px;list-style:none;padding:0}
.places a{display:flex;flex-direction:column;gap:6px;padding:18px 20px;border:1px solid var(--line);border-radius:14px;background:var(--graphite);color:var(--bone);text-decoration:none;transition:border-color .2s ease,transform .2s ease}
.places a:hover{border-color:var(--ember);transform:translateY(-2px)}
.places b{font-weight:600}
.places span{font-size:14px;color:var(--ash)}
.foot{margin-top:0}
</style>
</head>
<body>
@ -102,10 +51,9 @@ p{margin:0;color:var(--ink-2);max-width:52ch}
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -114,20 +62,32 @@ p{margin:0;color:var(--ink-2);max-width:52ch}
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -135,9 +95,9 @@ p{margin:0;color:var(--ink-2);max-width:52ch}
<section class="lost">
<div class="eyebrow">404</div>
<h1>Nothing is mined here.</h1>
<p>That address is not on igneum.network. It may have moved, or the link had a typo. These four pages are everything the site has.</p>
<p>That address is not on igneum.network. It may have moved, or the link had a typo. The pages below are where most readers want to go; the footer lists every page.</p>
<ul class="places">
<li><a href="/litepaper"><b>Litepaper</b><span>How the chain works, in 17 sections</span></a></li>
<li><a href="/litepaper"><b>Litepaper</b><span>How the chain works, in 19 sections</span></a></li>
<li><a href="/live"><b>Live devnet</b><span>Blocks, miners and finality as they happen</span></a></li>
<li><a href="/bench"><b>Engineering log</b><span>Every measurement, with the commands</span></a></li>
<li><a href="/evidence"><b>Evidence</b><span>Every claim and how far it is tested</span></a></li>
@ -149,7 +109,7 @@ p{margin:0;color:var(--ink-2);max-width:52ch}
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -175,9 +135,9 @@ p{margin:0;color:var(--ink-2);max-width:52ch}
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -185,10 +145,23 @@ p{margin:0;color:var(--ink-2);max-width:52ch}
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
</body>
</html>

View file

@ -29,9 +29,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -43,81 +44,25 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
/* page block; tokens, type and components are in /site.css */
/* the explorer pages share this block with block.html and address.html; same tokens as live.html */
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;--blue:#5CB8FF;--green:#3FD39A;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}a:hover{color:var(--molten)}
a:focus-visible,button:focus-visible,input:focus-visible{outline:2px solid var(--ember);outline-offset:3px;border-radius:6px}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.mono{font-family:var(--f-mono)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1,h2{font-family:var(--f-display);margin:0;line-height:1.08;text-wrap:balance}
h1{font-weight:900;font-size:var(--fs-h1);letter-spacing:-.01em;overflow-wrap:anywhere}
h2{font-weight:700;font-size:var(--fs-h3)}
p{margin:0}
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0;margin-top:var(--gap)}
.head{padding-block:var(--head) var(--gap);display:flex;flex-direction:column;gap:14px}
.head p{color:var(--ink-2);max-width:64ch;font-size:16px}
.search{display:flex;gap:10px;max-width:760px;margin-top:4px}
.search input{flex:1;min-width:0;min-height:48px;padding:10px 14px;border-radius:10px;border:1px solid var(--line-2);background:var(--graphite);color:var(--bone);font:500 15px/1.3 var(--f-mono)}
.search input::placeholder{color:var(--ash)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer;font-family:var(--f-sans)}
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
.msg{font-family:var(--f-mono);font-size:13px;color:var(--molten);min-height:18px}
.strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gap-tile)}
@ -129,28 +74,15 @@ p{margin:0}
@media (max-width:700px){.cell .v{font-size:19px}}
.cell .s{font-family:var(--f-mono);font-size:12px;color:var(--ash);text-wrap:balance}
.cell.status .v{display:flex;align-items:center;gap:10px}
.dot{width:10px;height:10px;border-radius:50%;background:var(--molten);display:inline-block;flex:0 0 10px;animation:pulse 2s ease-in-out infinite}
.off .dot{background:var(--ash);animation:none}.off .cell.status .v{color:var(--ash)}.on .cell.status .v{color:var(--molten)}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(255,179,92,.5)}50%{box-shadow:0 0 0 8px rgba(255,179,92,0)}}
@media (prefers-reduced-motion:reduce){.dot{animation:none}}
.viz-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 20px;margin-bottom:12px}
.tbl{overflow-x:auto;margin-top:12px;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;font-size:14px}
th,td{padding:10px 10px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap;vertical-align:top}
th{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500}
td.n,th.n{text-align:right;font-variant-numeric:tabular-nums}
tr:last-child td{border-bottom:0}
tbody tr:hover td{background:rgba(255,255,255,.025)}
.id{font-family:var(--f-mono);color:var(--molten)}
.hash{font-family:var(--f-mono)}
.sw{width:10px;height:10px;border-radius:3px;display:inline-block;margin-right:7px;vertical-align:middle;border:1.5px solid var(--ash)}
.sw.chain{background:var(--ember);border-color:var(--blue)}.sw.blue{background:rgba(92,184,255,.7);border-color:var(--blue)}.sw.red{background:linear-gradient(45deg,transparent 44%,var(--ash) 44%,var(--ash) 56%,transparent 56%) center/8px 8px no-repeat}
.chip{display:inline-block;font-family:var(--f-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:999px;border:1px solid var(--line-2);color:var(--ink-2);margin-right:6px}
.chip.hot{border-color:var(--molten);color:var(--molten)}.chip.ok{border-color:var(--green);color:var(--green)}
.legend{display:flex;flex-wrap:wrap;gap:6px 16px;margin-top:14px;font-size:13px;color:var(--ink-2)}
.legend span{display:inline-flex;align-items:center;white-space:nowrap}
.empty{color:var(--ash);font-size:14px;padding:12px 0}
.note{font-size:13px;color:var(--ash);margin-top:14px;max-width:80ch}
dl{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px 20px;margin:12px 0 0;font-size:14px}
dt{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding-top:3px}
dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
@ -159,7 +91,6 @@ dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
@media (min-width:900px){.two{grid-template-columns:1fr 1fr}.two .card{margin-top:0}}
.two-wrap{margin-top:var(--gap)}
main{padding-bottom:var(--sec)}
.foot{margin-top:0}
</style>
</head>
<body>
@ -168,10 +99,9 @@ main{padding-bottom:var(--sec)}
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
@ -180,20 +110,32 @@ main{padding-bottom:var(--sec)}
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -222,7 +164,7 @@ main{padding-bottom:var(--sec)}
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -248,9 +190,9 @@ main{padding-bottom:var(--sec)}
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -258,10 +200,23 @@ main{padding-bottom:var(--sec)}
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script type="module">
import { ign, ignFromWei, shortHash, rel, int, utc } from '/lib/explorer.mjs';

View file

@ -31,9 +31,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -45,93 +46,38 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;
/* one system for every page: container, gutter, section rhythm, card and tile boxes, heading scale (same tokens as the homepage) */
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:16px;line-height:1.6;padding:0;-webkit-font-smoothing:antialiased}
a{color:var(--ember);text-decoration:none}a:hover{text-decoration:underline}
/* generated-page block (the engineering log, the bench table, the evidence table); tokens, type and components are in /site.css */
[id]{scroll-margin-top:calc(var(--nav-h) + 16px)}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1{font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;margin:12px 0 12px}
.head{padding-top:var(--head)}
.note{color:var(--ash);font-size:15px;max-width:72ch;margin:0 0 32px}
.layout{display:grid;grid-template-columns:minmax(0,1fr);gap:32px}
.head{padding-top:calc(var(--sec) * .6)}
.head h1{margin:var(--s-3) 0}
.head .note{max-width:72ch;margin:0 0 var(--s-6)}
.layout{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-6)}
@media(min-width:960px){.layout{grid-template-columns:260px minmax(0,1fr);gap:56px}}
.toc{align-self:start;font-size:14px;border:1px solid var(--line);border-radius:var(--tile-r);background:var(--graphite);padding:8px}
.toc .lbl{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding:6px 10px 8px}
.toc{align-self:start;font-size:14px;border:1px solid var(--line);border-radius:var(--r-row);background:var(--graphite);padding:8px}
.toc .lbl{font:500 var(--t-xs)/1.4 var(--f-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding:6px 10px 8px}
.toc ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:1px;counter-reset:s;max-height:40vh;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin}
@media(min-width:960px){.toc{position:sticky;top:calc(var(--nav-h) + 20px)}.toc ol{max-height:calc(100vh - var(--nav-h) - 96px)}}
.toc a{display:flex;gap:10px;padding:7px 10px;border-radius:8px;color:var(--ink-2);counter-increment:s;line-height:1.35}
.toc a::before{content:counter(s,decimal-leading-zero);font-family:var(--f-mono);font-size:11px;color:var(--ash);min-width:20px;padding-top:2px}
.toc a:hover{background:var(--obsidian);text-decoration:none;color:var(--bone)}
article{max-width:78ch;min-width:0}
.toc a:hover{background:var(--row);text-decoration:none;color:var(--bone)}
article{max-width:78ch;min-width:0;color:var(--ink-2)}
article h1{display:none}
article h2{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);line-height:1.3;margin:44px 0 12px;padding-top:24px;border-top:1px solid var(--line)}
article h2{font-size:var(--t-h3);margin:44px 0 12px;padding-top:24px;border-top:1px solid var(--line)}
article h2:first-of-type{margin-top:0;padding-top:0;border-top:0}
article h3{font-family:var(--f-display);font-weight:500;font-size:16px;margin:26px 0 8px}
article h4{font-size:15px;margin:20px 0 6px;color:var(--molten)}
article h3{font-size:16px;margin:26px 0 8px}
article h4{font:600 15px/1.4 var(--f-sans);margin:20px 0 6px;color:var(--bone)}
article p{margin:0 0 14px}article ul,article ol{margin:0 0 14px;padding-left:22px}article li{margin-bottom:6px}
article p,article li,article h2,article h3{overflow-wrap:anywhere}
code{font-family:var(--f-mono);font-size:.92em;background:var(--graphite);padding:1px 5px;border-radius:4px;overflow-wrap:anywhere}
pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:var(--card-r);font-family:var(--f-mono);font-size:13px;line-height:1.55;overflow-x:auto;-webkit-overflow-scrolling:touch}
pre code{background:none;padding:0;font-size:inherit}
.tbl{overflow-x:auto;margin:18px 0 24px;border:1px solid var(--line);border-radius:var(--card-r);background:var(--graphite);-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:560px;font-size:15px}th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500;background:var(--obsidian)}tr:last-child td{border-bottom:0}
article pre{margin:0 0 var(--s-4)}
.tbl{margin:var(--s-4) 0 var(--s-5)}
table{min-width:560px}
.gen{color:var(--ash);font-size:13px;margin:40px 0 0;max-width:72ch}
</style>
</head>
@ -141,10 +87,9 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -153,20 +98,32 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -800,7 +757,7 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -826,9 +783,9 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -836,10 +793,23 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
</body>
</html>

View file

@ -29,9 +29,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -43,81 +44,25 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
/* page block; tokens, type and components are in /site.css */
/* the explorer pages share this block with block.html and address.html; same tokens as live.html */
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;--blue:#5CB8FF;--green:#3FD39A;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}a:hover{color:var(--molten)}
a:focus-visible,button:focus-visible,input:focus-visible{outline:2px solid var(--ember);outline-offset:3px;border-radius:6px}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.mono{font-family:var(--f-mono)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1,h2{font-family:var(--f-display);margin:0;line-height:1.08;text-wrap:balance}
h1{font-weight:900;font-size:var(--fs-h1);letter-spacing:-.01em;overflow-wrap:anywhere}
h2{font-weight:700;font-size:var(--fs-h3)}
p{margin:0}
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0;margin-top:var(--gap)}
.head{padding-block:var(--head) var(--gap);display:flex;flex-direction:column;gap:14px}
.head p{color:var(--ink-2);max-width:64ch;font-size:16px}
.search{display:flex;gap:10px;max-width:760px;margin-top:4px}
.search input{flex:1;min-width:0;min-height:48px;padding:10px 14px;border-radius:10px;border:1px solid var(--line-2);background:var(--graphite);color:var(--bone);font:500 15px/1.3 var(--f-mono)}
.search input::placeholder{color:var(--ash)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer;font-family:var(--f-sans)}
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
.msg{font-family:var(--f-mono);font-size:13px;color:var(--molten);min-height:18px}
.strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gap-tile)}
@ -129,28 +74,15 @@ p{margin:0}
@media (max-width:700px){.cell .v{font-size:19px}}
.cell .s{font-family:var(--f-mono);font-size:12px;color:var(--ash);text-wrap:balance}
.cell.status .v{display:flex;align-items:center;gap:10px}
.dot{width:10px;height:10px;border-radius:50%;background:var(--molten);display:inline-block;flex:0 0 10px;animation:pulse 2s ease-in-out infinite}
.off .dot{background:var(--ash);animation:none}.off .cell.status .v{color:var(--ash)}.on .cell.status .v{color:var(--molten)}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(255,179,92,.5)}50%{box-shadow:0 0 0 8px rgba(255,179,92,0)}}
@media (prefers-reduced-motion:reduce){.dot{animation:none}}
.viz-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 20px;margin-bottom:12px}
.tbl{overflow-x:auto;margin-top:12px;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;font-size:14px}
th,td{padding:10px 10px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap;vertical-align:top}
th{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500}
td.n,th.n{text-align:right;font-variant-numeric:tabular-nums}
tr:last-child td{border-bottom:0}
tbody tr:hover td{background:rgba(255,255,255,.025)}
.id{font-family:var(--f-mono);color:var(--molten)}
.hash{font-family:var(--f-mono)}
.sw{width:10px;height:10px;border-radius:3px;display:inline-block;margin-right:7px;vertical-align:middle;border:1.5px solid var(--ash)}
.sw.chain{background:var(--ember);border-color:var(--blue)}.sw.blue{background:rgba(92,184,255,.7);border-color:var(--blue)}.sw.red{background:linear-gradient(45deg,transparent 44%,var(--ash) 44%,var(--ash) 56%,transparent 56%) center/8px 8px no-repeat}
.chip{display:inline-block;font-family:var(--f-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:999px;border:1px solid var(--line-2);color:var(--ink-2);margin-right:6px}
.chip.hot{border-color:var(--molten);color:var(--molten)}.chip.ok{border-color:var(--green);color:var(--green)}
.legend{display:flex;flex-wrap:wrap;gap:6px 16px;margin-top:14px;font-size:13px;color:var(--ink-2)}
.legend span{display:inline-flex;align-items:center;white-space:nowrap}
.empty{color:var(--ash);font-size:14px;padding:12px 0}
.note{font-size:13px;color:var(--ash);margin-top:14px;max-width:80ch}
dl{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px 20px;margin:12px 0 0;font-size:14px}
dt{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding-top:3px}
dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
@ -159,7 +91,6 @@ dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
@media (min-width:900px){.two{grid-template-columns:1fr 1fr}.two .card{margin-top:0}}
.two-wrap{margin-top:var(--gap)}
main{padding-bottom:var(--sec)}
.foot{margin-top:0}
</style>
</head>
<body>
@ -168,10 +99,9 @@ main{padding-bottom:var(--sec)}
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
@ -180,20 +110,32 @@ main{padding-bottom:var(--sec)}
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -223,7 +165,7 @@ main{padding-bottom:var(--sec)}
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -249,9 +191,9 @@ main{padding-bottom:var(--sec)}
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -259,10 +201,23 @@ main{padding-bottom:var(--sec)}
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script type="module">
import { ign, ignFromWei, shortHash, rel, int, utc } from '/lib/explorer.mjs';

View file

@ -90,7 +90,7 @@ function meta(title, desc, path) {
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
<link rel="manifest" href="/site.webmanifest">`;
}
function page(title, desc, bodyHtml, toc, note, { path = '/bench', heading = title, active = 'bench' } = {}) {
function page(title, desc, bodyHtml, toc, note, { path = '/bench', heading = title, active = 'bench', eyebrow } = {}) {
const h2s = toc.filter(t => t.lvl === 2);
const nav = h2s.map(t => `<li><a href="#${t.id}">${esc(t.t)}</a></li>`).join('');
return `<!doctype html>
@ -103,42 +103,31 @@ ${meta(title, desc, path)}
${HEAD}
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;
/* one system for every page: container, gutter, section rhythm, card and tile boxes, heading scale (same tokens as the homepage) */
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:16px;line-height:1.6;padding:0;-webkit-font-smoothing:antialiased}
a{color:var(--ember);text-decoration:none}a:hover{text-decoration:underline}
/* generated-page block (the engineering log, the bench table, the evidence table); tokens, type and components are in /site.css */
[id]{scroll-margin-top:calc(var(--nav-h) + 16px)}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1{font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;margin:12px 0 12px}
.head{padding-top:var(--head)}
.note{color:var(--ash);font-size:15px;max-width:72ch;margin:0 0 32px}
.layout{display:grid;grid-template-columns:minmax(0,1fr);gap:32px}
.head{padding-top:calc(var(--sec) * .6)}
.head h1{margin:var(--s-3) 0}
.head .note{max-width:72ch;margin:0 0 var(--s-6)}
.layout{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-6)}
@media(min-width:960px){.layout{grid-template-columns:260px minmax(0,1fr);gap:56px}}
.toc{align-self:start;font-size:14px;border:1px solid var(--line);border-radius:var(--tile-r);background:var(--graphite);padding:8px}
.toc .lbl{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding:6px 10px 8px}
.toc{align-self:start;font-size:14px;border:1px solid var(--line);border-radius:var(--r-row);background:var(--graphite);padding:8px}
.toc .lbl{font:500 var(--t-xs)/1.4 var(--f-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding:6px 10px 8px}
.toc ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:1px;counter-reset:s;max-height:40vh;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin}
@media(min-width:960px){.toc{position:sticky;top:calc(var(--nav-h) + 20px)}.toc ol{max-height:calc(100vh - var(--nav-h) - 96px)}}
.toc a{display:flex;gap:10px;padding:7px 10px;border-radius:8px;color:var(--ink-2);counter-increment:s;line-height:1.35}
.toc a::before{content:counter(s,decimal-leading-zero);font-family:var(--f-mono);font-size:11px;color:var(--ash);min-width:20px;padding-top:2px}
.toc a:hover{background:var(--obsidian);text-decoration:none;color:var(--bone)}
article{max-width:78ch;min-width:0}
.toc a:hover{background:var(--row);text-decoration:none;color:var(--bone)}
article{max-width:78ch;min-width:0;color:var(--ink-2)}
article h1{display:none}
article h2{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);line-height:1.3;margin:44px 0 12px;padding-top:24px;border-top:1px solid var(--line)}
article h2{font-size:var(--t-h3);margin:44px 0 12px;padding-top:24px;border-top:1px solid var(--line)}
article h2:first-of-type{margin-top:0;padding-top:0;border-top:0}
article h3{font-family:var(--f-display);font-weight:500;font-size:16px;margin:26px 0 8px}
article h4{font-size:15px;margin:20px 0 6px;color:var(--molten)}
article h3{font-size:16px;margin:26px 0 8px}
article h4{font:600 15px/1.4 var(--f-sans);margin:20px 0 6px;color:var(--bone)}
article p{margin:0 0 14px}article ul,article ol{margin:0 0 14px;padding-left:22px}article li{margin-bottom:6px}
article p,article li,article h2,article h3{overflow-wrap:anywhere}
code{font-family:var(--f-mono);font-size:.92em;background:var(--graphite);padding:1px 5px;border-radius:4px;overflow-wrap:anywhere}
pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:var(--card-r);font-family:var(--f-mono);font-size:13px;line-height:1.55;overflow-x:auto;-webkit-overflow-scrolling:touch}
pre code{background:none;padding:0;font-size:inherit}
.tbl{overflow-x:auto;margin:18px 0 24px;border:1px solid var(--line);border-radius:var(--card-r);background:var(--graphite);-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:560px;font-size:15px}th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500;background:var(--obsidian)}tr:last-child td{border-bottom:0}
article pre{margin:0 0 var(--s-4)}
.tbl{margin:var(--s-4) 0 var(--s-5)}
table{min-width:560px}
.gen{color:var(--ash);font-size:13px;margin:40px 0 0;max-width:72ch}
</style>
</head>
@ -148,7 +137,7 @@ ${navFor(active)}
<!-- nav:end -->
<main id="main" class="wrap">
<div class="head">
<div class="eyebrow">${h2s.length} entries, newest at the bottom</div>
<div class="eyebrow">${eyebrow || `${h2s.length} entries, newest at the bottom`}</div>
<h1>${esc(heading)}</h1>
<p class="note">${note}</p>
</div>
@ -406,18 +395,18 @@ for (const [file, active] of PAGES) {
'<p>Every row names the engineering log entry or the job it came from. "Measured by the team" means our own hardware and our own log. "Reported by the fleet" means a machine we do not own, read from the status lines its miner uploads.</p>',
'<p>MH per watt needs the card\'s power draw during the run. The app reads it on NVIDIA cards through the driver. Rows get the figure when a run records it.</p>',
'<p>There is no other Igneum miner to compare with yet, so this table compares cards, not miners. The app that produces these rows: <a href="/miner">the miner page</a>.</p>',
`<p>Rows: ${rows.length}. Source file: <code>site/miner-bench.json</code> in the repository.</p>`,
`<p>Rows: ${rows.length}. Each row names the engineering log entry it came from.</p>`,
'<h2 id="priors">Fleet tuning priors</h2>',
'<p>Ember Tune runs on every card the app mines with: the power limit and the core clock are stepped on the live program and the card keeps the point with the best MH per watt within 1% of its top rate. Every finished tune is reported back without anything that identifies the owner, and the fleet\'s median point per card model, driver major and program class comes back down inside the signed update manifest as the starting point for the next card of that model. A model needs ' + pj.min_samples + ' reports before its prior is used.</p>',
ptable,
// the team's own measured rows (site/miner-priors.json "team"): the per-user sentence per card, with its job
...((pj.team || []).length ? ['<h3 id="team">Measured by the team</h3>', '<ul>' + pj.team.map(t => `<li><b>${esc(t.card)}</b> (driver ${esc(t.driver_major)}, class ${esc(t.class)}, ${esc(t.date)}, job <code>${esc(t.job)}</code>): ${esc(t.point)}. ${esc(t.sentence)}${t.note ? ' ' + esc(t.note) : ''}</li>`).join('') + '</ul>'] : []),
`<p>Rows: ${prows.length}${pj.generated ? ', generated ' + pj.generated : ''}. Source file: <code>site/miner-priors.json</code> in the repository, written from the fleet records by <code>tools/tuning.mjs --priors --site</code>.</p>`,
`<p>Rows: ${prows.length}${pj.generated ? ', generated ' + pj.generated : ''}. Written from the fleet's tune records at build time.</p>`,
].join('\n'));
const toc = [{ lvl: 2, t: 'The table', id: 'table' }, { lvl: 2, t: 'How a row gets here', id: 'how' }, { lvl: 2, t: 'Fleet tuning priors', id: 'priors' }];
writeFileSync(join(here, 'miners.html'), page('Igneum GPU bench table', 'Measured Igneum hash rates per GPU: card, generator version, best MH/s, MH per watt where measured, miner version, date and the log entry each number came from.', body, toc,
'Measured hash rates per card on the Igneum lottery hash, with the generator version, the miner version, the date and the log entry behind each number.',
{ path: '/miners', heading: 'GPU bench table', active: 'miner' }));
{ path: '/miners', heading: 'GPU bench table', active: 'miner', eyebrow: `${rows.length} measured rows, ${prows.length} fleet tuning models` }));
built.push('miners.html (' + rows.length + ' rows)');
}
console.log('built: ' + built.join(', '));

View file

@ -31,9 +31,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -45,72 +46,20 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;
/* one system for every page: container, gutter, section rhythm, card and tile boxes, heading scale (same tokens as the homepage) */
/* page block; tokens, type and components are in /site.css */
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:16px;line-height:1.6;padding:0;-webkit-font-smoothing:antialiased}
a{color:var(--ember);text-decoration:none}a:hover{text-decoration:underline}
[id]{scroll-margin-top:calc(var(--nav-h) + 16px)}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
/* nav and footer: shared, see site/partials */
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
.head{padding-top:var(--head)}
h1{font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;margin:12px 0 12px}
h2{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);line-height:1.3;margin:44px 0 12px;padding-top:24px;border-top:1px solid var(--line)}
.note{color:var(--ash);font-size:15px;max-width:72ch;margin-bottom:20px}
.note b{color:var(--bone);font-weight:500}
.labels{display:grid;gap:var(--gap-tile);grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));margin:20px 0 28px}
.label{background:var(--graphite);border:1px solid var(--line);border-radius:var(--tile-r);padding:var(--tile-pad);min-width:0}
@ -120,20 +69,15 @@ h2{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);line-heig
.chips{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 16px}
.chip{font:inherit;font-size:13px;color:var(--ink-2);background:var(--graphite);border:1px solid var(--line);border-radius:999px;padding:6px 12px;cursor:pointer;min-height:36px;transition:border-color .15s ease,color .15s ease}
.chip b{color:var(--bone);font-weight:600}.chip:hover{border-color:var(--ink-2)}.chip.on{border-color:var(--ember);color:var(--bone)}
.tbl{overflow-x:auto;margin:0 0 24px;border:1px solid var(--line);border-radius:var(--card-r);background:var(--graphite);-webkit-overflow-scrolling:touch}
.hint{font-family:var(--f-mono);font-size:12px;color:var(--ash);margin:0 0 10px;display:none}
@media (max-width:1140px){.hint{display:block}}
table{border-collapse:collapse;width:100%;min-width:1100px;font-size:14px}th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500;background:var(--obsidian);white-space:nowrap;padding:0}
th button{all:unset;display:block;box-sizing:border-box;width:100%;padding:12px 14px;cursor:pointer;color:inherit;font:inherit;letter-spacing:inherit;text-transform:inherit}
th button:hover{color:var(--bone)}th button:focus-visible{outline:2px solid var(--ember);outline-offset:-3px;border-radius:6px}
th[aria-sort] button::after{content:' \2191';color:var(--ember)}th[aria-sort="descending"] button::after{content:' \2193'}
tr:last-child td{border-bottom:0}tr[hidden]{display:none}
td.n{font-family:var(--f-mono);color:var(--ash)}td.claim{min-width:220px;font-weight:500}td .where{font-weight:400;color:var(--ash);font-size:13px;margin-top:4px}
td.mono{font-family:var(--f-mono);font-size:12.5px;min-width:180px}td.iv{color:var(--ash);white-space:nowrap}
.st{display:inline-block;font-family:var(--f-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:999px;border:1px solid var(--line);white-space:nowrap}
.st-0{color:var(--ash)}.st-1{color:var(--ink-2)}.st-2{color:var(--molten);border-color:var(--molten)}.st-3,.st-4{color:var(--ember);border-color:var(--ember)}
code{font-family:var(--f-mono);font-size:.92em;background:var(--obsidian);padding:1px 5px;border-radius:4px;overflow-wrap:anywhere}
.small table{min-width:560px}
.asof{color:var(--ash);font-size:13px;margin:40px 0 0;max-width:72ch}
</style>
@ -144,10 +88,9 @@ code{font-family:var(--f-mono);font-size:.92em;background:var(--obsidian);paddin
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -156,20 +99,32 @@ code{font-family:var(--f-mono);font-size:.92em;background:var(--obsidian);paddin
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence" aria-current="page">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -239,7 +194,7 @@ code{font-family:var(--f-mono);font-size:.92em;background:var(--obsidian);paddin
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -265,9 +220,9 @@ code{font-family:var(--f-mono);font-size:.92em;background:var(--obsidian);paddin
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -275,10 +230,23 @@ code{font-family:var(--f-mono);font-size:.92em;background:var(--obsidian);paddin
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script>
(function(){

View file

@ -28,9 +28,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -42,81 +43,25 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
/* page block; tokens, type and components are in /site.css */
/* the explorer pages share this block with block.html and address.html; same tokens as live.html */
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;--blue:#5CB8FF;--green:#3FD39A;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}a:hover{color:var(--molten)}
a:focus-visible,button:focus-visible,input:focus-visible{outline:2px solid var(--ember);outline-offset:3px;border-radius:6px}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.mono{font-family:var(--f-mono)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1,h2{font-family:var(--f-display);margin:0;line-height:1.08;text-wrap:balance}
h1{font-weight:900;font-size:var(--fs-h1);letter-spacing:-.01em;overflow-wrap:anywhere}
h2{font-weight:700;font-size:var(--fs-h3)}
p{margin:0}
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0;margin-top:var(--gap)}
.head{padding-block:var(--head) var(--gap);display:flex;flex-direction:column;gap:14px}
.head p{color:var(--ink-2);max-width:64ch;font-size:16px}
.search{display:flex;gap:10px;max-width:760px;margin-top:4px}
.search input{flex:1;min-width:0;min-height:48px;padding:10px 14px;border-radius:10px;border:1px solid var(--line-2);background:var(--graphite);color:var(--bone);font:500 15px/1.3 var(--f-mono)}
.search input::placeholder{color:var(--ash)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer;font-family:var(--f-sans)}
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
.msg{font-family:var(--f-mono);font-size:13px;color:var(--molten);min-height:18px}
.strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gap-tile)}
@ -128,28 +73,15 @@ p{margin:0}
@media (max-width:700px){.cell .v{font-size:19px}}
.cell .s{font-family:var(--f-mono);font-size:12px;color:var(--ash);text-wrap:balance}
.cell.status .v{display:flex;align-items:center;gap:10px}
.dot{width:10px;height:10px;border-radius:50%;background:var(--molten);display:inline-block;flex:0 0 10px;animation:pulse 2s ease-in-out infinite}
.off .dot{background:var(--ash);animation:none}.off .cell.status .v{color:var(--ash)}.on .cell.status .v{color:var(--molten)}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(255,179,92,.5)}50%{box-shadow:0 0 0 8px rgba(255,179,92,0)}}
@media (prefers-reduced-motion:reduce){.dot{animation:none}}
.viz-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 20px;margin-bottom:12px}
.tbl{overflow-x:auto;margin-top:12px;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;font-size:14px}
th,td{padding:10px 10px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap;vertical-align:top}
th{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500}
td.n,th.n{text-align:right;font-variant-numeric:tabular-nums}
tr:last-child td{border-bottom:0}
tbody tr:hover td{background:rgba(255,255,255,.025)}
.id{font-family:var(--f-mono);color:var(--molten)}
.hash{font-family:var(--f-mono)}
.sw{width:10px;height:10px;border-radius:3px;display:inline-block;margin-right:7px;vertical-align:middle;border:1.5px solid var(--ash)}
.sw.chain{background:var(--ember);border-color:var(--blue)}.sw.blue{background:rgba(92,184,255,.7);border-color:var(--blue)}.sw.red{background:linear-gradient(45deg,transparent 44%,var(--ash) 44%,var(--ash) 56%,transparent 56%) center/8px 8px no-repeat}
.chip{display:inline-block;font-family:var(--f-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:999px;border:1px solid var(--line-2);color:var(--ink-2);margin-right:6px}
.chip.hot{border-color:var(--molten);color:var(--molten)}.chip.ok{border-color:var(--green);color:var(--green)}
.legend{display:flex;flex-wrap:wrap;gap:6px 16px;margin-top:14px;font-size:13px;color:var(--ink-2)}
.legend span{display:inline-flex;align-items:center;white-space:nowrap}
.empty{color:var(--ash);font-size:14px;padding:12px 0}
.note{font-size:13px;color:var(--ash);margin-top:14px;max-width:80ch}
dl{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px 20px;margin:12px 0 0;font-size:14px}
dt{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);padding-top:3px}
dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
@ -158,7 +90,6 @@ dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
@media (min-width:900px){.two{grid-template-columns:1fr 1fr}.two .card{margin-top:0}}
.two-wrap{margin-top:var(--gap)}
main{padding-bottom:var(--sec)}
.foot{margin-top:0}
</style>
</head>
<body class="off">
@ -167,10 +98,9 @@ main{padding-bottom:var(--sec)}
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
@ -179,20 +109,32 @@ main{padding-bottom:var(--sec)}
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -244,7 +186,7 @@ main{padding-bottom:var(--sec)}
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -270,9 +212,9 @@ main{padding-bottom:var(--sec)}
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -280,10 +222,23 @@ main{padding-bottom:var(--sec)}
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script type="module">
import { classify, hrefFor, ign, shortHash, hashrate, rel, int } from '/lib/explorer.mjs';

View file

@ -27,9 +27,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -41,78 +42,22 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--ember-ink:#0C0C0E;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ok:#7BD88F;--bad:#FF8A6A;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--fs-h1:clamp(32px,5.5vw,56px);--fs-h3:clamp(18px,2vw,22px)}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;display:flex;flex-direction:column;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter);width:100%}
/* page block; tokens, type and components are in /site.css */
main{flex:1}
a{color:var(--ember)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1{font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;margin:12px 0 16px}
h2{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);line-height:1.3;margin:0 0 10px}
p{margin:0;color:var(--ink-2);max-width:60ch}
.head{padding-top:var(--sec);padding-bottom:32px}
.grid{display:grid;gap:24px;grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){.grid{grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr);gap:40px}}
.card{border:1px solid var(--line);border-radius:18px;background:var(--graphite);padding:clamp(18px,3vw,28px)}
form{display:flex;flex-direction:column;gap:12px;margin-top:16px}
label{font-size:14px;color:var(--ash)}
input{width:100%;min-height:52px;padding:12px 14px;border-radius:12px;border:1px solid var(--line-2);background:var(--obsidian);color:var(--bone);font:500 16px/1.3 var(--f-mono);letter-spacing:.01em}
input:focus{outline:2px solid var(--ember);outline-offset:2px;border-color:var(--ember)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:50px;padding:12px 22px;border-radius:12px;font:600 16px/1.2 var(--f-sans);border:1px solid var(--ember);color:var(--ember-ink);background:var(--ember);cursor:pointer;transition:background .15s ease,transform .15s ease}
.btn:hover{background:#FF6A2B;border-color:#FF6A2B;transform:translateY(-1px)}
.btn[disabled]{opacity:.6;cursor:default;transform:none}
.status{min-height:28px;margin-top:4px;font-size:15px;color:var(--ink-2);overflow-wrap:anywhere}
.status[data-state="ok"]{color:var(--ok)}
@ -126,7 +71,6 @@ input:focus{outline:2px solid var(--ember);outline-offset:2px;border-color:var(-
.notice{margin-top:14px;padding:12px 14px;border-radius:12px;border:1px dashed var(--line-2);font-size:14px;color:var(--molten)}
dl{display:grid;grid-template-columns:auto 1fr;gap:6px 16px;margin:14px 0 0;font-size:15px}
dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:14px;overflow-wrap:anywhere}
.foot{margin-top:var(--sec)}
@media (prefers-reduced-motion:reduce){.btn:hover{transform:none}}
</style>
</head>
@ -136,10 +80,9 @@ dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:14px;overflo
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -148,20 +91,32 @@ dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:14px;overflo
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -204,7 +159,7 @@ dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:14px;overflo
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -230,9 +185,9 @@ dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:14px;overflo
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -240,10 +195,23 @@ dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:14px;overflo
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script>
(function(){

File diff suppressed because one or more lines are too long

View file

@ -94,21 +94,9 @@ a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visi
</style>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(40px,6vw,72px)}
*{box-sizing:border-box}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:16px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:var(--ember);text-decoration:none}a:hover{color:var(--molten)}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1{font-family:var(--f-display);font-weight:900;font-size:clamp(30px,5vw,52px);line-height:1.08;margin:10px 0 18px}
h2{font-family:var(--f-display);font-weight:700;font-size:clamp(22px,3vw,30px);margin:var(--sec) 0 16px;padding-top:8px;border-top:1px solid var(--line);scroll-margin-top:84px}
h3{font-family:var(--f-sans);font-weight:600;font-size:17px;margin:0;flex:1 1 auto;min-width:0}
/* page block; tokens, type and components are in /site.css */
.entry code,.entry .did,.entry blockquote,.entry p,.entry h3{overflow-wrap:anywhere}.entry code{white-space:normal}
.intro{font-size:17px;color:var(--ink-2);max-width:76ch;margin:0 0 24px}
.tbl{overflow-x:auto;border:1px solid var(--line);border-radius:16px;background:var(--graphite);margin:0 0 20px}
table{border-collapse:collapse;width:100%;font-size:15px;min-width:520px}
th,td{padding:11px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500}
tr:last-child td{border-bottom:0}
td.num{font-family:var(--f-display);font-weight:700;font-size:22px;color:var(--ember);white-space:nowrap}
.chip{background:none;border:1px solid var(--line-2);color:var(--bone);border-radius:999px;padding:6px 12px;font:inherit;font-size:14px;cursor:pointer}
.chip:hover,.chip.on{border-color:var(--ember);color:var(--molten)}
@ -124,9 +112,7 @@ blockquote{margin:10px 0;padding:10px 14px;border-left:3px solid var(--line-2);c
.status{font-size:15px;color:var(--ink-2)}
.badge{display:inline-block;font-family:var(--f-mono);font-size:12px;letter-spacing:.06em;text-transform:uppercase;padding:3px 8px;border-radius:6px;border:1px solid var(--line-2);color:var(--bone);margin-right:6px;vertical-align:middle}
.b-open{border-color:var(--ember);color:var(--ember)}.b-conceded{border-color:var(--molten);color:var(--molten)}.b-fixed-or-built{border-color:#5cb85c;color:#8fd48f}
code{font-family:var(--f-mono);font-size:13px;color:var(--bone);background:#0C0C0E;padding:1px 5px;border-radius:5px}
details{margin-top:8px;font-size:14px;color:var(--ash)}summary{cursor:pointer;color:var(--ash)}details p{margin:8px 0 0;color:var(--ink-2)}
.foot{margin-top:var(--sec)}
</style>
</head>
<body>

View file

@ -32,9 +32,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -46,173 +47,84 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{
--ground:#F4F1EC; --surface:#FFFFFF; --surface-2:#ECE8E0; --line:#DAD5CB;
--ink:#0C0C0E; --ink-2:#3D3B37; --quiet:#6E6B65;
--ember:#D9430F; --ember-ink:#FFFFFF; --molten:#B9741C; --tint:#FBE9E0;
--code:#16161A; --code-ink:#E6E2DA;
/* the shared nav and footer follow the paper's scheme: light here, the dark defaults below */
--ui-bg:rgba(244,241,236,.86); --ui-menu:#F4F1EC; --ui-ink:#0C0C0E; --ui-ink-2:#3D3B37; --ui-ash:#6E6B65; --ui-line:#DAD5CB; --ui-line-2:#C9C3B8;
--ui-accent:#D9430F; --ui-accent-ink:#FFFFFF; --ui-hot:#B9741C; --ui-hover:#F2541B; --ui-tint:#FBE9E0;
/* one system for every page: container, gutter, section rhythm, card and tile boxes, heading scale (same tokens as the homepage) */
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px);
--lp-off:84px;
}
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){
--ground:#0C0C0E; --surface:#16161A; --surface-2:#1E1E23; --line:#2A2A30;
--ink:#F4F1EC; --ink-2:#C9C7C2; --quiet:#9A9A9E;
--ember:#F2541B; --ember-ink:#0C0C0E; --molten:#FFB35C; --tint:#2A1A12;
--code:#0C0C0E; --code-ink:#C9C7C2;
--ui-bg:rgba(12,12,14,.84); --ui-menu:#0C0C0E; --ui-ink:#F4F1EC; --ui-ink-2:#C9C7C2; --ui-ash:#9A9A9E; --ui-line:#2A2A30; --ui-line-2:#3A3A42;
--ui-accent:#F2541B; --ui-accent-ink:#0C0C0E; --ui-hot:#FFB35C; --ui-hover:#FF6A2B; --ui-tint:rgba(242,84,27,.12);
color-scheme:dark;
}
}
:root[data-theme="dark"]{
--ground:#0C0C0E; --surface:#16161A; --surface-2:#1E1E23; --line:#2A2A30;
--ink:#F4F1EC; --ink-2:#C9C7C2; --quiet:#9A9A9E;
--ember:#F2541B; --ember-ink:#0C0C0E; --molten:#FFB35C; --tint:#2A1A12;
--code:#0C0C0E; --code-ink:#C9C7C2;
--ui-bg:rgba(12,12,14,.84); --ui-menu:#0C0C0E; --ui-ink:#F4F1EC; --ui-ink-2:#C9C7C2; --ui-ash:#9A9A9E; --ui-line:#2A2A30; --ui-line-2:#3A3A42;
--ui-accent:#F2541B; --ui-accent-ink:#0C0C0E; --ui-hot:#FFB35C; --ui-hover:#FF6A2B; --ui-tint:rgba(242,84,27,.12);
color-scheme:dark;
}
*{box-sizing:border-box}
body{margin:0;background:var(--ground);color:var(--ink);font-family:var(--f-sans);font-size:17px;line-height:1.6;padding:0;-webkit-font-smoothing:antialiased}
a{color:var(--ember);text-decoration:none}
a:hover{text-decoration:underline}
/* every id lands under the sticky bar (and the sticky contents row on a phone); --lp-off is measured by the script */
[id]{scroll-margin-top:var(--lp-off)}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.cover{padding-block:var(--head) 40px;border-bottom:1px solid var(--line);display:flex;flex-wrap:wrap;gap:32px;align-items:flex-end;justify-content:space-between}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--quiet)}
.cover h1{margin:12px 0 10px;font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;letter-spacing:-.01em;text-wrap:balance}
.cover .tag{font-size:20px;color:var(--ink-2);max-width:40ch}
.meta{display:flex;flex-direction:column;gap:6px;font-family:var(--f-mono);font-size:13px;color:var(--quiet);text-align:right}
.meta b{color:var(--ink);font-weight:500}
.layout{display:grid;grid-template-columns:minmax(0,1fr);gap:32px;padding-block:40px var(--sec)}
/* litepaper block (docs/plans/site-ui-3.md section 4); tokens, type and components are in /site.css */
[id]{scroll-margin-top:var(--lp-off,84px)}
.cover{padding:calc(var(--sec) * .6) 0 var(--s-6);border-bottom:1px solid var(--line);display:flex;flex-wrap:wrap;gap:var(--s-6);align-items:flex-end;justify-content:space-between}
.cover h1{margin:var(--s-3) 0 var(--s-3)}
.cover .tag{font-size:var(--t-lg);line-height:1.5;color:var(--ink-2);max-width:44ch}
.meta{display:flex;flex-direction:column;gap:6px;font:400 13px/1.5 var(--f-mono);color:var(--ash);text-align:right}
.meta b{color:var(--bone);font-weight:500}
.layout{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-6);padding:var(--s-6) 0 var(--sec)}
@media (min-width:960px){.layout{grid-template-columns:240px minmax(0,1fr);gap:56px}}
.toc{align-self:start}
@media (min-width:960px){.toc{position:sticky;top:calc(var(--nav-h) + 24px)}}
@media (min-width:960px){.toc{position:sticky;top:calc(var(--nav-h) + 24px);max-height:calc(100vh - var(--nav-h) - 48px);overflow:auto}}
.toc ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;counter-reset:s}
.toc li a{display:flex;gap:10px;padding:7px 10px;border-radius:8px;color:var(--ink-2);font-size:14px;counter-increment:s;line-height:1.35}
.toc li a::before{content:counter(s,decimal-leading-zero);font-family:var(--f-mono);font-size:12px;color:var(--quiet);min-width:22px;padding-top:2px}
.toc li a:hover{background:var(--surface-2);text-decoration:none;color:var(--ink)}
.toc .dl{margin-top:18px;padding-top:18px;border-top:1px solid var(--line);font-size:13px;color:var(--quiet)}
article{max-width:72ch}
section{padding-block:40px;border-top:1px solid var(--line)}
section:first-of-type{border-top:0;padding-top:0}
h2{margin:0 0 18px;font-family:var(--f-display);font-weight:700;font-size:var(--fs-h2);line-height:1.1;text-wrap:balance}
h3{margin:28px 0 8px;font-family:var(--f-display);font-weight:500;font-size:var(--fs-h3);line-height:1.3}
p{margin:0 0 16px}
.lead{font-size:20px;line-height:1.5;color:var(--ink-2)}
ul,ol{margin:0 0 16px;padding-left:22px}
li{margin-bottom:8px}
strong{font-weight:600}
.pull{margin:24px 0;padding:20px var(--card-pad);border-left:4px solid var(--ember);background:var(--tint);border-radius:0 var(--tile-r) var(--tile-r) 0;font-family:var(--f-display);font-weight:500;font-size:19px;line-height:1.4}
.tbl{overflow-x:auto;margin:18px 0 24px;border:1px solid var(--line);border-radius:var(--card-r);background:var(--surface)}
table{border-collapse:collapse;width:100%;min-width:560px;font-size:15px}
th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--quiet);font-weight:500;background:var(--surface-2)}
tr:last-child td{border-bottom:0}
td.num{font-family:var(--f-mono);font-variant-numeric:tabular-nums;white-space:nowrap}
.figure{margin:24px 0;padding:var(--card-pad);background:var(--surface);border:1px solid var(--line);border-radius:var(--card-r);overflow-x:auto}
.toc li a::before{content:counter(s,decimal-leading-zero);font-family:var(--f-mono);font-size:12px;color:var(--ash);min-width:22px;padding-top:2px}
.toc li a:hover{background:var(--row);text-decoration:none;color:var(--bone)}
.toc li a.active{background:var(--row);color:var(--bone);font-weight:600;box-shadow:inset 2px 0 0 var(--ember)}
.toc li a.active::before{color:var(--ember-text)}
.toc .dl{margin-top:18px;padding-top:18px;border-top:1px solid var(--line);font-size:13px;color:var(--ash)}
.toc .mode{margin-top:14px;display:flex;border:1px solid var(--line);border-radius:var(--r-pill);overflow:hidden}
.toc .mode button{flex:1;min-height:36px;border:0;background:transparent;color:var(--ash);font:500 12px/1 var(--f-mono);cursor:pointer}
.toc .mode button.on{background:var(--graphite);color:var(--bone)}
article{max-width:68ch}
article section{padding:var(--s-6) 0;border-top:1px solid var(--line)}
article section:first-of-type{border-top:0;padding-top:0}
article h2{margin:0 0 var(--s-4)}
article h3{margin:var(--s-6) 0 var(--s-2);font-weight:700}
article .lead{margin-bottom:var(--s-4)}
article ul,article ol{margin:0 0 var(--s-4);padding-left:22px}
article li{margin-bottom:8px;color:var(--ink-2)}
article p{color:var(--ink-2)}
article strong{color:var(--bone);font-weight:600}
.pull{margin:var(--s-5) 0;padding:var(--s-4) var(--s-5);border-left:2px solid var(--ember);background:var(--row);border-radius:0 var(--r-row) var(--r-row) 0;font:500 var(--t-lg)/1.45 var(--f-sans);color:var(--bone)}
.tbl{margin:var(--s-4) 0 var(--s-5)}
table{min-width:560px}
.figure{margin:var(--s-5) 0;padding:var(--s-5);background:var(--graphite);border:1px solid var(--line);border-radius:var(--r-card);overflow-x:auto}
.figure svg{display:block;width:100%;height:auto;max-width:100%}
.figure .cap{margin-top:12px;font-family:var(--f-mono);font-size:12px;color:var(--quiet)}
.stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gap-tile);margin:8px 0 24px}
.figure .cap{margin-top:var(--s-3);font:400 var(--t-sm)/1.5 var(--f-mono);color:var(--ash)}
.stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-3);margin:var(--s-2) 0 var(--s-5)}
@media (min-width:700px){.stats{grid-template-columns:repeat(4,minmax(0,1fr))}}
.stat{padding:var(--tile-pad);background:var(--surface);border:1px solid var(--line);border-radius:var(--tile-r);min-width:0}
.stat .v{font-family:var(--f-display);font-weight:700;font-size:var(--fs-tile);color:var(--ember);line-height:1.1}
.stat .k{margin-top:6px;font-size:14px;color:var(--quiet);line-height:1.35;text-wrap:balance}
pre{margin:0 0 16px;padding:16px 18px;background:var(--code);color:var(--code-ink);border-radius:var(--card-r);font-family:var(--f-mono);font-size:13px;line-height:1.6;overflow-x:auto}
.stat{padding:var(--s-4);background:var(--row);border:1px solid var(--line);border-radius:var(--r-row);min-width:0}
.stat .v{font:700 var(--t-num)/1 var(--f-display);color:var(--bone);letter-spacing:-.02em}
.stat .k{margin-top:var(--s-2);font:400 var(--t-sm)/1.4 var(--f-mono);color:var(--ash);text-wrap:balance}
pre{margin:0 0 var(--s-4)}
.foot{margin-top:0}
article section{display:none;border-top:0;padding-top:0}
article section.active{display:block}
body.all article section{display:block;border-top:1px solid var(--line);padding-top:40px}
body.all article section{display:block;border-top:1px solid var(--line);padding-top:var(--s-6)}
body.all article section:first-of-type{border-top:0;padding-top:0}
article h2:focus,article h3:focus{outline:none}
article h2:focus-visible,article h3:focus-visible{outline:2px solid var(--ember);outline-offset:6px;border-radius:4px}
.toc li a.active{background:var(--tint);color:var(--ink);font-weight:600}
.toc li a.active::before{color:var(--ember)}
.toc .mode{margin-top:14px;display:flex;flex-direction:column;gap:8px}
@media (max-width:959px){.toc .mode{flex-direction:row}}
.toc .mode button{flex:1;min-height:40px;border-radius:8px;border:1px solid var(--line);background:var(--surface);color:var(--ink-2);font:inherit;font-size:13px;cursor:pointer}
.toc .mode button.on{background:var(--ink);color:var(--ground);border-color:var(--ink)}
.toc .mode button:hover:not(.on){border-color:var(--quiet)}
.pager{display:flex;justify-content:space-between;gap:12px;margin-top:36px;padding-top:22px;border-top:1px solid var(--line)}
.pager a{display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:10px 16px;border:1px solid var(--line);border-radius:10px;color:var(--ink);font-size:14px;max-width:48%}
.pager a:hover{text-decoration:none;border-color:var(--ember)}
.pager a span{color:var(--quiet);font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase}
.pager{display:flex;justify-content:space-between;gap:var(--s-3);margin-top:var(--s-6);padding-top:var(--s-5);border-top:1px solid var(--line)}
.pager a{display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:10px 16px;border:1px solid var(--line);border-radius:var(--r-btn);color:var(--bone);font-size:14px;max-width:48%}
.pager a:hover{text-decoration:none;border-color:var(--line-2);background:var(--hover)}
.pager a span{color:var(--ash);font:500 var(--t-xs)/1.4 var(--f-mono);letter-spacing:.12em;text-transform:uppercase}
.pager a.next{margin-left:auto;text-align:right}
body.all .pager{display:none}
/* the source line under a table or paragraph: where each number came from */
.src{margin:-8px 0 20px;font-family:var(--f-mono);font-size:13px;line-height:1.5;color:var(--quiet)}
.src{margin:calc(-1 * var(--s-2)) 0 var(--s-5);font:400 13px/1.5 var(--f-mono);color:var(--ash)}
.src b{font-weight:500;color:var(--ink-2)}
@media (max-width:600px){.pager{flex-direction:column}.pager a{max-width:none;justify-content:space-between}.pager a.next{margin-left:0}}
@media (max-width:959px){
.toc{position:sticky;top:var(--nav-h);z-index:5;background:var(--ground);margin-inline:calc(-1 * var(--gutter));padding:8px var(--gutter);border-bottom:1px solid var(--line)}
.toc{position:sticky;top:var(--nav-h);z-index:5;background:var(--obsidian);margin-inline:calc(-1 * var(--gutter));padding:8px var(--gutter);border-bottom:1px solid var(--line)}
.toc ol{flex-direction:row;overflow-x:auto;gap:6px;scrollbar-width:none;-webkit-overflow-scrolling:touch;padding-bottom:2px;scroll-padding-inline:var(--gutter)}
.toc ol::-webkit-scrollbar{display:none}
.toc li a{white-space:nowrap;border:1px solid var(--line);border-radius:999px;padding:7px 12px;font-size:13px;gap:6px}
.toc li a.active{box-shadow:none;border-color:var(--ember)}
.toc li a::before{min-width:0}
.toc .dl{display:none}
.toc .mode{margin-top:6px}
.toc .mode button{min-height:32px;font-size:12px}
.toc .mode button{min-height:32px}
}
@media print{article section{display:block!important;border-top:1px solid #bbb;padding-top:18pt}.toc,.pager,.cover .meta span:last-child{display:none!important}.layout{display:block}article{max-width:none}table{min-width:0}}
</style>
</head>
<body>
@ -222,10 +134,9 @@ body.all .pager{display:none}
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper" aria-current="page">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -234,20 +145,32 @@ body.all .pager{display:none}
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -260,7 +183,7 @@ body.all .pager{display:none}
<div class="tag">A proof-of-work chain whose miners also prove every block, run Ethereum's apps, and are protected from specialised chips by a program that changes every hour.</div>
</div>
<div class="meta">
<span>Published <b>3 October 2026</b> · updated <b>5 October 2026</b></span>
<span>Published <b>3 October 2026</b> · updated <b>6 October 2026</b></span>
<span>Coin <b>IGN</b> · cap <b>4,000,000,000</b></span>
<span>Status <b>devnet live, pre-testnet</b></span>
<span>Method <b>one founder with AI systems</b> · external review before gate 3</span>
@ -291,14 +214,14 @@ body.all .pager{display:none}
<li><a href="#shoulders">Built on the shoulders</a></li>
<li><a href="#limits">What Igneum does not claim</a></li>
</ol>
<div class="mode" role="group" aria-label="Reading mode"><button type="button" id="m-tabs" class="on" aria-pressed="true">One section at a time</button><button type="button" id="m-all" aria-pressed="false">Whole paper</button></div>
<div class="mode" role="group" aria-label="Reading mode"><button type="button" id="m-all" class="on" aria-pressed="true">Whole paper</button><button type="button" id="m-tabs" aria-pressed="false">One section at a time</button></div>
<div class="dl">Latin igneum: fiery. A cupel is the vessel in which metal is proven by fire.</div>
</nav>
<article>
<section id="abstract">
<h2>Abstract</h2>
<p class="lead">Igneum is a proof-of-work blockchain built for graphics cards, where NVIDIA cards also prove every block with zero-knowledge proofs and sell proving to other chains. The strongest recompute chip we can price, holding the whole 256 MiB cache on-die, reaches under 1x per chip against an RTX 5090. A memory-controller chip that stores the whole dataset reaches 1.2x per chip and, in our model, 5x to 9x per joule; the Ethash chips of this class reached 2.1x to 4.8x. The lever against it, program work in the latency shadow, is measured and in its gates: it brings the chip to about 2x. Sources: the chip model (<code>docs/analysis/chip-model-v3.md</code> section 5, 6 October 2026); the Ethash rows of the ASIC history (<code>docs/analysis/asic-resistance-history.md</code>, Linzhi Phoenix 2020, Jasminer X4 2021, Antminer E9 2022); Counter ASIC 3.0 item 8 (100,000 ops per hash: the chip's per-joule edge over the RTX 5090 falls from 5.6x to 2.1x on GDDR7 at a chip core equal to the GPU's, the 5090 at 0.2% less rate, gates G1 to G6 in progress). The model is public; the claim is tested by paid independent cryptanalysis and the public benchmark.</p>
<p class="lead">Igneum is a proof-of-work blockchain built for graphics cards, where NVIDIA cards also prove every block with zero-knowledge proofs and sell proving to other chains. The strongest recompute chip we can price, holding the whole 256 MiB cache on-die, reaches under 1x per chip against an RTX 5090. A memory-controller chip that stores the whole dataset reaches 1.2x per chip and, in our model, 5x to 9x per joule; the Ethash chips of this class reached 2.1x to 4.8x. The lever against it, program work in the latency shadow, is measured and in its gates: it brings the chip to about 2x. Sources: the chip model analysis (6 October 2026); the Ethash rows of the ASIC history (Linzhi Phoenix 2020, Jasminer X4 2021, Antminer E9 2022); Counter ASIC 3.0 item 8 (100,000 ops per hash: the chip's per-joule edge over the RTX 5090 falls from 5.6x to 2.1x on GDDR7 at a chip core equal to the GPU's, the 5090 at 0.2% less rate, gates G1 to G6 in progress). The model is public; the claim is tested by paid independent cryptanalysis and the public benchmark.</p>
<p>It runs the Ethereum virtual machine, so anything built for Ethereum runs on Igneum unchanged. Transactions are included in about one second, proven within about a minute at launch, and locked by miners within about two. There is no premine, no pre-sale, no treasury taken from emission, no stake anywhere in consensus, and no dependence on any other chain. Mining stays open to anyone with a GPU because the mining program changes every hour, so a chip built for one program is useless for the next, and a chip for the whole program space is a GPU without the graphics parts. No scheduled human release is needed to keep it that way. Writing new code, including an emergency fix to the proof system, is the one thing that takes a person, and it activates only on miner signalling.</p>
<div class="stats">
<div class="stat"><div class="v">1 / s</div><div class="k">blocks, rising to 10</div></div>
@ -398,7 +321,7 @@ body.all .pager{display:none}
<tr><td>Finality</td><td>Rule v2: a checkpoint every 30 s of chain, locked at two thirds of all 30-day weight. First live lock: checkpoint 242 at 77.4% of all weight, 17 vote keys</td><td class="num">4 Oct 2026</td></tr>
<tr><td>Proving</td><td>v0 active: shards are assigned to miners' keys, proven on their cards, and the records are carried in blocks</td><td class="num">DAA 84,100, 5 Oct 2026</td></tr>
<tr><td>Ember</td><td>The one-click miner on the fleet, version 0.3.13 (6 Oct 2026); the app window still says Igneum Miner</td><td class="num">4 Oct 2026, first install</td></tr>
<tr><td>Wallet</td><td>Igneum Wallet 0.1.1 on macOS</td><td class="num">5 Oct 2026</td></tr>
<tr><td>Wallet</td><td>Igneum Wallet 0.1.4 on macOS (0.1.1 first shipped 5 Oct 2026)</td><td class="num">6 Oct 2026</td></tr>
</tbody>
</table></div>
<p class="src"><b>Measured:</b> engineering log, "first hourly program swap on the live devnet", "difficulty rule v2 activated on the live devnet at DAA 33,000", "first finality lock on the live devnet" (4 October 2026); the 0.3.6 release plan (5 October 2026). The block rate is the devnet record in the evidence table, row 7.</p>
@ -437,7 +360,7 @@ body.all .pager{display:none}
<tr><td>Light verification</td><td>256 MB cache on a CPU, milliseconds</td><td>256 MB cache on a CPU (512 MB from year 4), one warp under 10 ms, the gate. Measured 2.1 ms on one Apple M5 Max core for class v3 (3.4x class v2's 0.61 ms); a 2019-class core not yet</td></tr>
<tr><td>Changes over time</td><td>None. A fixed design, unchanged for seven years</td><td>A new program every hour, its memory pattern with it; era draws and reserved families on a schedule fixed at genesis. Nobody touches it</td></tr>
<tr><td>Seed grinding</td><td>Not applicable, the program comes from the hash input</td><td>Closed by a verifiable delay between seed and program</td></tr>
<tr><td>Useful work</td><td>None. Hashing only</td><td>Every NVIDIA card from 8 GB proves; 12 GB and up mine and prove; 24 GB on the stock server (eleven rented cards, RTX 3060 to RTX 5090, 6 October 2026, <code>docs/analysis/prover-tiers-real-cards.md</code>); they sell proofs to other chains. AMD and Apple cards mine, and a prover for them lands when a zkVM ships one</td></tr>
<tr><td>Useful work</td><td>None. Hashing only</td><td>Every NVIDIA card from 8 GB proves; 12 GB and up mine and prove; 24 GB on the stock server (eleven rented cards, RTX 3060 to RTX 5090, 6 October 2026); they sell proofs to other chains. AMD and Apple cards mine, and a prover for them lands when a zkVM ships one</td></tr>
<tr><td>Track record</td><td>No chip publicly shipped in seven years, approximate</td><td>Zero years. Every number above is measured and logged with the commands that produced it. The specification, reference hash, test vectors and simulators are public now (github.com/igneum-network/spec). The node, the miner and the wallet are in a private repository until the public testnet</td></tr>
</tbody>
</table></div>
@ -449,7 +372,7 @@ body.all .pager{display:none}
<p>Every Igneum block is proven with a zero-knowledge proof, and the miners produce it. Proving is a useful GPU workload that is cheaply verifiable by construction. A proof is right or it is not. Wrapped for light clients, a phone checks it in milliseconds; the wrapping cost is a phase two measurement. Measured so far, the certificate half only: the browser verifier on the home page checks a devnet finality certificate, one BLS aggregate signature over 16 keys and 21 header hashes, in 139 to 155 ms cold and 58 to 68 ms warm in a phone-sized tab on a laptop core (5 October 2026). No phone has been measured, and no wrapped block proof exists yet.</p>
<h3>How a block gets proven</h3>
<p>Blocks carry transactions only and make no claim about state. Every node executes the ordered transactions natively at once, so users see their transaction land in about a second. The execution is then split into shards of a fixed proving cost. Shards are assigned by lot to eight provers for ten seconds, then open to anyone; there is no bond. Provers run them on consumer cards, and the shard proofs are folded by recursive aggregation into one proof for the block. That proof lands on-chain within about a minute at launch. Because the proof computes the state from the ordered sequence, no node accepts a block with a wrong state root. Full nodes also execute every block natively and reject a proof record whose result differs from their own execution, so a forged proof is a light-client problem and never a chain split. Implemented: the native-execution check on every carried proof record, proving v0 on the devnet (specification section 7). The emergency path for a soundness bug in the proof system is a human one: a new proof-system version is written by people and activates only on miner signalling. Invalid transactions are skipped by rule, the way Kaspa skips conflicting spends.</p>
<p>Proving: every NVIDIA card from 8 GB proves; 12 GB and up mine and prove; 24 GB on the stock server. Measured on eleven rented cards, RTX 3060 to RTX 5090, 6 October 2026, <code>docs/analysis/prover-tiers-real-cards.md</code>: the RTX 3060 (12 GB) mines at 23.78 MH/s and proves the v1 shard beside its miner at an 8.9 GB peak in 37.5 s; the RTX 4060 (8 GB) proves it alone at 7.4 GB in 18.4 s; the RTX 4090 (24 GB) proves it on the stock SP1 server in 5.6 s at 17.4 GB. The patched server that fits the smaller cards is not yet in the shipped app. AMD and Apple cards mine. A prover for them lands when a zkVM ships one.</p>
<p>Proving: every NVIDIA card from 8 GB proves; 12 GB and up mine and prove; 24 GB on the stock server. Measured on eleven rented cards, RTX 3060 to RTX 5090, 6 October 2026: the RTX 3060 (12 GB) mines at 23.78 MH/s and proves the v1 shard beside its miner at an 8.9 GB peak in 37.5 s; the RTX 4060 (8 GB) proves it alone at 7.4 GB in 18.4 s; the RTX 4090 (24 GB) proves it on the stock SP1 server in 5.6 s at 17.4 GB. The patched server that fits the smaller cards is not yet in the shipped app. AMD and Apple cards mine. A prover for them lands when a zkVM ships one.</p>
<h3>The proving budget</h3>
<p>Gas prices execution. Proving cost is a different number, so Igneum meters it separately: every transaction pays in both dimensions, and each block has a proving-cost budget set in consensus from measured prover throughput. A transaction that is cheap to run and expensive to prove pays for what it costs the provers. Measured on 5 October 2026 (an RTX 5090 under SP1 6.8.1's GPU prover, the shard size the chain adopts from its fee switch, 30,000 proving gas, about 4.7 million prover cycles): one full shard proves in 4.3 seconds and needs 20.4 GB of GPU memory with the card to itself, so a 24 GB card proves full shards and a 12 GB or 16 GB card does not on this prover build, whose floor is 13.9 GB for even an empty shard; mining and proving on one card needs 32 GB today (the prototype-size shard beside the miner peaked at 30.1 GB) and 24 GB once the adopted shard size is live (22.2 GB beside the miner, 13.2 seconds a shard, measured on the 32 GB card; a 24 GB card has not run it yet). The old 12 GB gate on the roadmap was withdrawn on 5 October until a prover build with a smaller floor was measured; on 6 October a patched server proved the same shard at 7.4 to 8.0 GB alone on eleven rented cards from the RTX 3060 to the RTX 5090 (the real-card table), so the gate returns as measured and the patched server is not yet in the shipped app. The first proofs exist: on 4 October 2026 an RTX 5090 proved a small two-transaction block in 1.4 seconds (2.7 seconds compressed), verified in 0.22 and 0.038 seconds, and a laptop CPU proved a three-shard block end to end in 19 minutes. Later that day the same card proved a full shard at the provisional size, 6.75 million prover gas, which executed in 60.8 million cycles: core proof 8.3 seconds, compressed proof 10.9 seconds, verified in 0.040 seconds; a four-shard block took 44.5 seconds of GPU stages end to end. Since 5 October 2026 shards are assigned and proven on the live devnet. The gate asks for a mid-range card, and an RTX 5090 is not one, so the gate stands open. Once the gate is measured, the budget rises by schedule as hardware improves. The proof system is hash-based, which is what runs on consumer cards, and sits behind a versioned interface, so Igneum can adopt a better proof system when one exists by a miner-signalled release, and runs for ever on the current one if none is adopted.</p>
<h3>Proving for everyone else</h3>
@ -573,7 +496,7 @@ body.all .pager{display:none}
</table></div>
<p>The honest bear-market case rests on cost. A miner's card is already running and the power is often domestic, so Igneum miners' marginal cost in the proving market is close to power, which is an edge over data-centre provers and nothing more. Which of the two in-chain streams pays more per GPU-second depends on the size of the fleet: on the devnet of 4 October 2026, three machines at 275 million hashes a second, a second of hashing paid about 4.9x a second of proving the pool share; at 10,000 cards the same arithmetic favours proving by about 930x. That is arithmetic on measured devnet rates, approximate, not a market measurement.</p>
<h3>Hardware</h3>
<p>The dataset starts at 2 GB and grows (the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28, the average of half a gigabyte a year), so a 4 GB card mines for about four years and an 8 GB card for about twelve, approximate. Every NVIDIA card from 8 GB proves; 12 GB and up mine and prove; 24 GB on the stock server (eleven rented cards, RTX 3060 to RTX 5090, 6 October 2026, <code>docs/analysis/prover-tiers-real-cards.md</code>). NVIDIA and AMD both work, because the mining program is generated for the architecture both share and the proof system is hash-based. Apple's chips are GPUs with unified memory, so Macs mine too, at about a fifth of a flagship card: Measured, 26.7 against 123 million hashes a second, an Apple M5 Max beside an RTX 5090 on the live devnet, 4 October 2026. A Mac is a poor miner per dollar. There is no CPU mining lane, on purpose, because CPU mining is what botnets farm. Nodes, wallets and exchanges need no GPU at all.</p>
<p>The dataset starts at 2 GB and grows (the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28, the average of half a gigabyte a year), so a 4 GB card mines for about four years and an 8 GB card for about twelve, approximate. Every NVIDIA card from 8 GB proves; 12 GB and up mine and prove; 24 GB on the stock server (eleven rented cards, RTX 3060 to RTX 5090, 6 October 2026). NVIDIA and AMD both work, because the mining program is generated for the architecture both share and the proof system is hash-based. Apple's chips are GPUs with unified memory, so Macs mine too, at about a fifth of a flagship card: Measured, 26.7 against 123 million hashes a second, an Apple M5 Max beside an RTX 5090 on the live devnet, 4 October 2026. A Mac is a poor miner per dollar. There is no CPU mining lane, on purpose, because CPU mining is what botnets farm. Nodes, wallets and exchanges need no GPU at all.</p>
<h3>What a miner's hour looks like</h3>
<p>The card hashes the lottery continuously. When the client sees a shard or an external job it can win, it switches the card to proving for a few seconds, posts the proof, and goes back to hashing. The client does the switching and the miner sees one balance.</p>
<p>The protocol carries no fee: no dev fund, no cut to any team. Ember, the miner software, takes an optional 1% dev fee, the way other GPU miners do. One block template in 100 is requested with the dev address instead of yours, by a counter, not a random draw, so it is exactly 1 in 100 and anyone can check it from the source or from the chain. One flag turns it off (<code>--dev-fee 0</code>, a switch in the app, a line in the HiveOS config). The miner prints the fee and the address when it starts. Any other client is welcome.</p>
@ -602,7 +525,7 @@ body.all .pager{display:none}
<tr><td>Dashboard</td><td>Per card: rate, accepted and rejected blocks, temperature, draw, MH per watt, the kernel variant in use, the proving state, and every event. Served on this machine only, behind a per-launch token</td></tr>
</tbody>
</table></div>
<p class="src"><b>Source:</b> the app's engine, detection, keys, prover, updater, jobs and watchdog modules (<code>app/igneum-app/src</code>), the HiveOS package (<code>packaging/hive</code>). Design, not yet measured on a real card: the watchdog rules and the fault guards, which were measured against a fake worker only (engineering log, "miner fault guards and the app watchdog", 4 October 2026).</p>
<p class="src"><b>Source:</b> the app's engine, detection, keys, prover, updater, jobs and watchdog modules (the app source), the HiveOS package (the HiveOS package). Design, not yet measured on a real card: the watchdog rules and the fault guards, which were measured against a fake worker only (engineering log, "miner fault guards and the app watchdog", 4 October 2026).</p>
<h3>Six levers</h3>
<div class="tbl"><table>
<thead><tr><th>Lever</th><th>What it does</th><th>State, 5 Oct 2026</th><th>Measured</th></tr></thead>
@ -646,7 +569,7 @@ body.all .pager{display:none}
<tr><td>Next</td><td>Touch ID and Windows Hello to unlock. In progress, not live. Windows build: next</td></tr>
</tbody>
</table></div>
<p class="src"><b>Source:</b> Igneum Wallet 0.1.1, 5 October 2026 (<code>app/igneum-wallet</code>: the vault, HD key, finality, QR and updater modules and the README). Verified: the over-the-air path end to end on one Mac against a test manifest. Not yet run: the Windows path, the rollback paths, a Developer ID signature.</p>
<p class="src"><b>Source:</b> Igneum Wallet 0.1.1, 5 October 2026, now 0.1.4 on the downloads host (the wallet source: the vault, HD key, finality, QR and updater modules and the README). Verified: the over-the-air path end to end on one Mac against a test manifest. Not yet run: the Windows path, the rollback paths, a Developer ID signature.</p>
</section>
<section id="governance">
@ -668,16 +591,16 @@ body.all .pager{display:none}
<thead><tr><th>Phase</th><th>When</th><th>What</th><th>Gate to pass</th></tr></thead>
<tbody>
<tr><td>1. Specification</td><td class="num">Oct to Nov 2026</td><td>Mining generator, shard proving, finality rules, written for external review</td><td></td></tr>
<tr><td>2. Prove the proving</td><td class="num">Nov 2026 to Jan 2027</td><td>Mining program prototype on GPU and CPU, shard proving benchmark on consumer cards. So far: an RTX 5090 proves a shard in 10.9 s compressed; a CPU verifies a warp in 0.41 to 0.58 ms. A mid-range card has not been measured</td><td>A mid-range GPU proves a shard in under 20 s and a CPU verifies a hash in 10 ms</td></tr>
<tr><td>3. Devnet</td><td class="num">Started 3 Oct 2026, 20 nodes by Mar 2027</td><td>BlockDAG node with the new mining program and EVM execution, 20 nodes. Live now: 1 block a second, difficulty v2, finality v2 locks, proving v0, Ember on every machine. Not yet measured on the live chain: the proof lag behind the tip</td><td>1 block a second held with proofs under 60 s behind the tip</td></tr>
<tr><td>2. Prove the proving</td><td class="num">Nov 2026 to Jan 2027</td><td>Mining program prototype on GPU and CPU, shard proving benchmark on consumer cards. So far: an RTX 5090 proves a shard in 10.9 s compressed; a CPU verifies a warp in 0.61 ms (class v2) to 2.1 ms (class v3). A mid-range card has not been measured</td><td>A mid-range GPU proves a shard in under 20 s and a CPU verifies a hash in 10 ms</td></tr>
<tr><td>3. Devnet</td><td class="num">Started 3 Oct 2026, 20 nodes by Mar 2027</td><td>BlockDAG node with the new mining program and EVM execution, 20 nodes. Live now: 1 block a second, difficulty v2, finality v2 locks, proving v0, Ember on every machine. Measured on the live chain on 6 October 2026: proofs land a median of about 380 s behind the tip (the observer, /live), against the 60 s gate</td><td>1 block a second held with proofs under 60 s behind the tip</td></tr>
<tr><td>4. Finality and job market</td><td class="num">Apr to Jul 2027</td><td>Sustained-mining finality, external proving jobs, miner client with auto-switching</td><td>Finality design passes external review and one rollup signs for testnet</td></tr>
<tr><td>5. Public testnet</td><td class="num">Aug to Oct 2027</td><td>One-click miner app on Windows, macOS and Linux, HiveOS, pools, the first rollup as a proving customer, no coin yet</td><td>1,000 independent miners run 30 days and rollup proofs are delivered on time</td></tr>
<tr><td>6. Mainnet fair launch</td><td class="num">Nov 2027</td><td>Genesis with no premine, 30-day ramp. No listing is arranged, promised or sought by the project</td><td></td></tr>
</tbody>
</table></div>
<p>Dates slip. Gates do not. Phase two decides everything. If consumer GPUs cannot prove shards fast enough, Igneum says so and does not launch on promises.</p>
<h3>What ships next: Ember 0.3.6</h3>
<p>The proving activation of 5 October 2026 set the next release. Each item is in the 0.3.6 plan; none is on the fleet yet.</p>
<h3>The 0.3.6 release plan, 5 October 2026</h3>
<p>The proving activation of 5 October 2026 set the next release. The table is the plan as written; Ember has since reached 0.3.14 (6 October 2026), and each item's state is in the engineering log.</p>
<div class="tbl"><table>
<thead><tr><th>Item</th><th>Why</th></tr></thead>
<tbody>
@ -703,11 +626,11 @@ body.all .pager{display:none}
<div class="tbl"><table>
<thead><tr><th>What</th><th>Number</th><th>Source</th></tr></thead>
<tbody>
<tr><td>Rented NVIDIA hash, 6 October 2026</td><td class="num">2 GH/s for USD 13 an hour</td><td>The fleet's bench entry (the rented cards of 6 October 2026); the entry lands tonight with the wave measurement below</td></tr>
<tr><td>The devnet's hash the same afternoon</td><td class="num">282 MH/s</td><td><code>/api/stats</code>, 6 October 2026, 16:40 UTC; 181 MH/s an hour earlier with one PC off</td></tr>
<tr><td>Rented NVIDIA hash, 6 October 2026</td><td class="num">2 GH/s for USD 13 an hour</td><td>The rental order for the rented cards of 6 October 2026, approximate; the fleet's bench entry is not yet written</td></tr>
<tr><td>The devnet's hash the same afternoon</td><td class="num">282 MH/s</td><td>the public stats API, 6 October 2026, 16:40 UTC; 181 MH/s an hour earlier with one PC off; 2.66 GH/s at 18:16 UTC with the rented cards mining</td></tr>
<tr><td>Weight a renter holds on day one</td><td class="num">0.0%</td><td>The finality simulator, table B (ledger M12)</td></tr>
<tr><td>Lock latency behind the checkpoint</td><td class="num">1,018 ms median</td><td>Engineering log, the finality harness, three nodes</td></tr>
<tr><td>A 50-miner wave against the devnet: blocks taken, locks moved</td><td class="num">measurement tonight</td><td>The fleet's bench entry, 6 October 2026</td></tr>
<tr><td>A 50-miner wave against the devnet: blocks taken, locks moved</td><td class="num">not yet measured</td><td>Owed to the fleet's bench entry; the rented cards joined on 6 October 2026</td></tr>
</tbody>
</table></div>
<h3>Finality weighted by mining history is new. New gets attacked.</h3>
@ -750,7 +673,7 @@ body.all .pager{display:none}
<li><strong>Bitcoin's bech32 and Bitcoin Cash's CashAddr checksum.</strong> The address format, through Kaspa, with Igneum prefixes.</li>
<li><strong>BLAKE2b, BLAKE3, SHA-256, Keccak.</strong> The hashes the node already uses, unchanged. ChaCha, SplitMix64 and FNV-1a inside the lottery hash, implemented from their definitions.</li>
</ul>
<p>What is Igneum's own: the hourly header-bound GPU program, the sustained-mining finality rule, two-dimensional gas with the per-frame app share, the shard market and proving precompile, and the automatic era draws. The full table, with what changed in each component, why the design needed it, and the measurement or specification section that covers it, is <code>docs/provenance.md</code> in the repository and is published with it at the public testnet. Licences stated from memory are marked approximate there and verified before the repository opens.</p>
<p>What is Igneum's own: the hourly header-bound GPU program, the sustained-mining finality rule, two-dimensional gas with the per-frame app share, the shard market and proving precompile, and the automatic era draws. The full table, with what changed in each component, why the design needed it, and the measurement or specification section that covers it, is the provenance document in the repository and is published with it at the public testnet. Licences stated from memory are marked approximate there and verified before the repository opens.</p>
</section>
<section id="limits">
@ -758,7 +681,7 @@ body.all .pager{display:none}
<p>Here are the limits, stated before anyone else states them.</p>
<ul>
<li><strong>A proof in seconds.</strong> Not at launch. Proving a full block today needs a cluster of 100 to 200 consumer GPUs, approximate, so Igneum launches with proofs within about a minute and tightens as hardware improves. Users still see their transaction land in one second.</li>
<li><strong>A chip is impossible.</strong> No. A chip is a bad bet, because the target moves before it ships. The published model (5 October 2026) prices the strongest chip we can name, one with the whole cache on-die computing dataset items on the fly, at 0.92x the hash rate of an RTX 5090 per unit of silicon with a 3x fixed-function allowance, approximate. The same model, drawn out to the chip that stores the dataset (6 October 2026): The strongest recompute chip we can price, holding the whole 256 MiB cache on-die, reaches under 1x per chip against an RTX 5090. A memory-controller chip that stores the whole dataset reaches 1.2x per chip and, in our model, 5x to 9x per joule; the Ethash chips of this class reached 2.1x to 4.8x. The lever against it, program work in the latency shadow, is measured and in its gates: it brings the chip to about 2x. Sources: the chip model (<code>docs/analysis/chip-model-v3.md</code> section 5, 6 October 2026); the Ethash rows of the ASIC history (<code>docs/analysis/asic-resistance-history.md</code>, Linzhi Phoenix 2020, Jasminer X4 2021, Antminer E9 2022); Counter ASIC 3.0 item 8 (100,000 ops per hash: the chip's per-joule edge over the RTX 5090 falls from 5.6x to 2.1x on GDDR7 at a chip core equal to the GPU's, the 5090 at 0.2% less rate, gates G1 to G6 in progress). No hash has stayed free of chips forever; Igneum does not claim to. Monero's seven years without a public chip are precedent, not proof, and a small prize: they say nothing about the price of a chip with the 256 MB cache on its die, and that price is a cost model, not a measurement.</li>
<li><strong>A chip is impossible.</strong> No. A chip is a bad bet, because the target moves before it ships. The published model (5 October 2026) prices the strongest chip we can name, one with the whole cache on-die computing dataset items on the fly, at 0.92x the hash rate of an RTX 5090 per unit of silicon with a 3x fixed-function allowance, approximate. The same model, drawn out to the chip that stores the dataset (6 October 2026): The strongest recompute chip we can price, holding the whole 256 MiB cache on-die, reaches under 1x per chip against an RTX 5090. A memory-controller chip that stores the whole dataset reaches 1.2x per chip and, in our model, 5x to 9x per joule; the Ethash chips of this class reached 2.1x to 4.8x. The lever against it, program work in the latency shadow, is measured and in its gates: it brings the chip to about 2x. Sources: the chip model analysis (6 October 2026); the Ethash rows of the ASIC history (Linzhi Phoenix 2020, Jasminer X4 2021, Antminer E9 2022); Counter ASIC 3.0 item 8 (100,000 ops per hash: the chip's per-joule edge over the RTX 5090 falls from 5.6x to 2.1x on GDDR7 at a chip core equal to the GPU's, the 5090 at 0.2% less rate, gates G1 to G6 in progress). No hash has stayed free of chips forever; Igneum does not claim to. Monero's seven years without a public chip are precedent, not proof, and a small prize: they say nothing about the price of a chip with the 256 MB cache on its die, and that price is a cost model, not a measurement.</li>
<li><strong>A guaranteed income floor.</strong> No. External proving is a small market today. Igneum's miners' marginal cost in it is close to power, which is an edge and nothing more.</li>
<li><strong>A memory-hard prototype on every vendor.</strong> Not yet. The 256 MB cache closed the shortcut on Apple silicon (computing items runs 4.8x slower than loading them, measured 3 October 2026). The same ratio on NVIDIA and on a discrete AMD card is Open.</li>
<li><strong>Finality in the first month.</strong> No. No checkpoint locks until the 30-day window has 30 days of history. The first month of mainnet is proof of work with a 12-hour depth, and the text above says so wherever a day count appears.</li>
@ -778,7 +701,7 @@ body.all .pager{display:none}
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -804,9 +727,9 @@ body.all .pager{display:none}
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -814,10 +737,23 @@ body.all .pager{display:none}
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script>
@ -831,7 +767,7 @@ body.all .pager{display:none}
var toc=document.querySelector('.toc'),links=Array.prototype.slice.call(document.querySelectorAll('.toc ol a'));
var mAll=document.getElementById('m-all'),mTabs=document.getElementById('m-tabs');
var reduce=window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var all=false;try{all=localStorage.getItem('lp-mode')==='all';}catch(e){}
var all=true;try{all=localStorage.getItem('lp-mode')!=='tabs';}catch(e){}
// every subsection heading gets an id from its text, so each one is reachable by URL
var used={};secs.forEach(function(s){used[s.id]=1;});
secs.forEach(function(s){Array.prototype.slice.call(s.querySelectorAll('h3')).forEach(function(h){if(h.id)return;

222
site/live-dag.js Normal file
View file

@ -0,0 +1,222 @@
/* Igneum live DAG view (docs/plans/site-ui-3.md section 6, 6 October 2026).
One module for /live and the home page. Reads /api/live only; every pixel is a block, edge or checkpoint the observer
stored. Blocks are placed by header time on x (the axis is labelled in DAA score) and by miner lane on y. Parent edges
are drawn; the edge between two selected-chain blocks is heavier and the chain glows as one path. Colour words are the
ledger's: pending, included (blue), excluded (red), proven (every shard verified or paid), locked (a checkpoint block).
Checkpoints are vertical bands at their blue score with the lock weight; a locked band is solid, a pending one dashed.
The view scrolls at chain speed, new blocks arrive from the right with a brief glow; wheel and pinch zoom the window
(30 to 300 s, the API's ?window=); a pause button; hover and tap show a block's details.
Usage: var dag = IgneumDag.mount(canvas, { compact, window, url, poll, mine, onState, onData, onWindow, pauseButton, tooltip })
- url: the /api/live endpoint (default '/api/live'); poll: false stops the module's own 2 s fetch, the host then calls
dag.push(reply) with a reply of the same shape ({ok, state:{stale, age_s, height, ...}, blocks:[...], finality:{checkpoints:[...]}, proving})
whenever it has new data (every poll or every new block, either is fine; blocks already on screen are updated in place).
- block record (the /api/live shape): hash (string, 16 hex), ts (ms, header time), blue_score, daa (numbers), parents ([hash]),
chain (bool, selected chain), color ('blue' | 'red' | 'pending'), miner (string lane key, 8 hex of the vote key hash),
locked (bool, a locked checkpoint block), final (bool, at or before the newest lock), shards ([{state: 'planned' | 'proving' |
'verified' | 'paid'}]), proven (bool, every shard verified or paid). Checkpoints: {index, blue_score, state: 'locked' | 'pending', fraction_total}.
- mine: a miner lane key (string) or function(block) -> bool; the user's own blocks take a molten ring and the lane is labelled "you".
- The legend is the host's; the module draws no HTML. Colours come from the CSS custom properties on :root (--ember, --ember-hi,
--molten, --bone, --ash, --line, --line-2, --row or --obsidian), re-read on data-theme and prefers-color-scheme changes.
- Sizing: the canvas fills its CSS box (width and height from the stylesheet); 280 px wide works, 600 or more shows lane labels well.
onState(state, reason): 'connecting' | 'live' | 'stale' | 'failed'; onData(d) with every fresh reply. */
(function (root) {
'use strict';
var COL = {
ember: '#F2541B', emberHi: '#FF6A2B', molten: '#FFB35C', bone: '#F4F1EC', ash: '#9A9A9E', line: '#2A2A30', line2: '#3A3A42', obsidian: '#0C0C0E',
red: '#B0362B'
};
function cssVar(name, fallback) { try { var v = getComputedStyle(document.documentElement).getPropertyValue(name).trim(); return v || fallback; } catch (e) { return fallback; } }
function readTheme() {
COL.ember = cssVar('--ember', COL.ember); COL.emberHi = cssVar('--ember-hi', COL.emberHi); COL.molten = cssVar('--molten', COL.molten);
COL.bone = cssVar('--bone', COL.bone); COL.ash = cssVar('--ash', COL.ash); COL.line = cssVar('--line', COL.line); COL.line2 = cssVar('--line-2', COL.line2);
COL.obsidian = cssVar('--row', cssVar('--obsidian', COL.obsidian));
}
function rgba(hex, a) { var h = hex.replace('#', ''); if (h.length !== 6) return hex; return 'rgba(' + parseInt(h.slice(0, 2), 16) + ',' + parseInt(h.slice(2, 4), 16) + ',' + parseInt(h.slice(4, 6), 16) + ',' + a + ')'; }
function fmt(n) { return n === null || n === undefined ? 'pending' : Number(n).toLocaleString('en-GB'); }
function fmtAge(sec) { sec = Math.round(sec); return sec < 90 ? sec + ' s' : sec < 5400 ? Math.round(sec / 60) + ' min' : Math.round(sec / 3600) + ' h'; }
function mount(canvas, opts) {
opts = opts || {};
var compact = !!opts.compact, windowS = Math.max(30, Math.min(300, opts.window || (compact ? 90 : 120)));
var reduce = root.matchMedia && root.matchMedia('(prefers-reduced-motion: reduce)').matches;
var isMine = typeof opts.mine === 'function' ? opts.mine : opts.mine ? function (b) { return b.miner === opts.mine; } : null;
var ctx = canvas.getContext('2d'), dpr = Math.min(root.devicePixelRatio || 1, 2), W = 0, H = 0;
var blocks = [], byHash = {}, lanes = [], laneOf = {}, cps = [], data = null, state = 'connecting', reason = 'connecting';
var paused = false, offsetMs = 0, hover = null, pinned = null, lastPoll = 0, failures = 0, running = false, inView = true, lastT = 0;
var vnow = function () { return Date.now() - 2500 - offsetMs; }; // the right edge sits 2.5 s behind the clock so a block has arrived before it is drawn
var padL = compact ? 10 : 56, padR = 14, padT = compact ? 12 : 22, padB = compact ? 10 : 26;
function size() { W = canvas.clientWidth; H = canvas.clientHeight; canvas.width = Math.round(W * dpr); canvas.height = Math.round(H * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0); layout(); }
root.addEventListener('resize', size);
// ---- data ----
function apply(d) {
data = d;
var seen = {}, now = performance.now();
d.blocks.forEach(function (b) {
seen[b.hash] = 1;
var e = byHash[b.hash];
if (e) { e.chain = !!b.chain; e.color = b.color || 'pending'; e.locked = !!b.locked; e.final = !!b.final; e.shards = b.shards || []; e.proven = !!b.proven; e.blue = b.blue_score; e.daa = b.daa; return; }
e = { hash: b.hash, ts: b.ts, blue: b.blue_score, daa: b.daa, parents: b.parents || [], chain: !!b.chain, color: b.color || 'pending', miner: b.miner || '?', locked: !!b.locked, final: !!b.final, shards: b.shards || [], proven: !!b.proven, born: now, x: 0, y: 0 };
byHash[b.hash] = e; blocks.push(e);
});
var cut = vnow() - windowS * 1000 - 20000;
blocks = blocks.filter(function (b) { if (b.ts < cut && !seen[b.hash]) { delete byHash[b.hash]; return false; } return true; });
blocks.sort(function (a, b) { return a.ts - b.ts; });
cps = (d.finality && d.finality.checkpoints) || [];
layout();
}
function layout() {
// lanes: miners of the visible window ordered by blocks, as many as fit at 22 px, the rest in one "others" lane
var vis = vnow() - windowS * 1000, count = {};
blocks.forEach(function (b) { if (b.ts >= vis) count[b.miner] = (count[b.miner] || 0) + 1; });
var ids = Object.keys(count).sort(function (a, b) { return count[b] - count[a] || (a < b ? -1 : 1); });
var usable = H - padT - padB, laneH = compact ? 26 : 22, maxLanes = Math.max(2, Math.floor(usable / laneH));
var shown = ids.length > maxLanes ? ids.slice(0, maxLanes - 1) : ids;
lanes = shown.slice(); if (ids.length > shown.length) lanes.push('others');
laneOf = {}; shown.forEach(function (id, i) { laneOf[id] = i; });
}
function laneY(b) { var i = b.miner in laneOf ? laneOf[b.miner] : lanes.length - 1; var n = Math.max(1, lanes.length); return padT + (i + 0.5) * ((H - padT - padB) / n); }
function xOf(ts) { return padL + (W - padL - padR) * (1 - (vnow() - ts) / (windowS * 1000)); }
// the host can feed replies itself (poll: false); the same path the fetch takes
function push(d) {
if (!d || !d.ok || !d.state || !d.blocks) { setState('failed', 'live feed unavailable'); return; }
failures = 0;
if (d.state.stale) setState('stale', 'observer offline, last update ' + (d.state.age_s !== null && d.state.age_s !== undefined ? fmtAge(d.state.age_s) + ' ago' : 'unknown'));
else setState('live', null);
apply(d); if (opts.onData) opts.onData(d); kick();
}
function poll() {
if (opts.poll === false || document.hidden) return;
var u = (opts.url || '/api/live') + '?window=' + windowS;
fetch(u, { cache: 'no-store' }).then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }).then(function (d) {
if (!d || !d.ok || !d.state || !d.blocks) throw new Error('bad reply');
failures = 0;
if (d.state.stale) { setState('stale', 'observer offline, last update ' + (d.state.age_s !== null && d.state.age_s !== undefined ? fmtAge(d.state.age_s) + ' ago' : 'unknown')); }
else setState('live', null);
apply(d); if (opts.onData) opts.onData(d);
kick();
}).catch(function () { failures++; if (failures >= 2) setState('failed', 'live feed unavailable'); });
}
function setState(s, r) { if (s === state && r === reason) return; state = s; reason = r; if (opts.onState) opts.onState(s, r); }
// ---- drawing ----
function draw(t) {
if (!W || !H) return;
var dt = lastT ? Math.min(50, t - lastT) : 16; lastT = t;
ctx.clearRect(0, 0, W, H);
var vis = vnow() - windowS * 1000, n = Math.max(1, lanes.length), lh = (H - padT - padB) / n;
// lane rails and labels
ctx.lineWidth = 1;
for (var i = 0; i < n; i++) {
var y = padT + (i + 0.5) * lh;
ctx.strokeStyle = rgba(COL.line, 0.9); ctx.beginPath(); ctx.moveTo(padL, y); ctx.lineTo(W - padR, y); ctx.stroke();
if (!compact) { var mineLane = isMine && typeof opts.mine === 'string' && lanes[i] === opts.mine; ctx.fillStyle = mineLane ? COL.molten : COL.ash; ctx.font = (mineLane ? '500' : '400') + ' 10px IBM Plex Mono, monospace'; ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; ctx.fillText(mineLane ? 'you' : lanes[i] === 'others' ? 'others' : String(lanes[i]).slice(0, 8), 6, y); }
}
// checkpoint bands at their blue score (x from the chain block carrying that score, else the nearest)
cps.forEach(function (c) {
var ref = null, best = Infinity;
for (var k = blocks.length - 1; k >= 0; k--) { var b = blocks[k]; if (b.blue === null || b.blue === undefined) continue; var dd = Math.abs(b.blue - c.blue_score); if (dd < best) { best = dd; ref = b; } }
if (!ref || best > 3 || ref.ts < vis) return;
var x = xOf(ref.ts), locked = c.state === 'locked';
ctx.strokeStyle = rgba(COL.ember, locked ? 0.55 : 0.3); ctx.lineWidth = locked ? 1.5 : 1; ctx.setLineDash(locked ? [] : [4, 6]);
ctx.beginPath(); ctx.moveTo(x, padT - 6); ctx.lineTo(x, H - padB + 6); ctx.stroke(); ctx.setLineDash([]);
if (!compact) { ctx.fillStyle = rgba(COL.ember, 0.9); ctx.font = '500 10px IBM Plex Mono, monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'alphabetic'; ctx.fillText((locked ? 'locked ' : 'pending ') + (c.fraction_total !== null && c.fraction_total !== undefined ? Math.round(c.fraction_total * 100) + '%' : ''), x, H - 8); }
});
// positions
blocks.forEach(function (b) { b.x = xOf(b.ts); b.y = laneY(b); b.on = b.ts >= vis && b.x >= padL - 10; });
// edges: merge edges thin, chain-to-chain heavier; the chain as one glowing path
blocks.forEach(function (b) {
if (!b.on) return;
b.parents.forEach(function (h) {
var p = byHash[h]; if (!p || !p.on) return;
var chainEdge = b.chain && p.chain;
ctx.strokeStyle = chainEdge ? rgba(COL.ember, 0.75) : rgba(COL.ash, b.color === 'red' ? 0.12 : 0.22);
ctx.lineWidth = chainEdge ? 2 : 1;
ctx.beginPath(); ctx.moveTo(b.x, b.y); var mx = (b.x + p.x) / 2; ctx.bezierCurveTo(mx, b.y, mx, p.y, p.x, p.y); ctx.stroke();
if (chainEdge) { ctx.strokeStyle = rgba(COL.ember, 0.18); ctx.lineWidth = 7; ctx.beginPath(); ctx.moveTo(b.x, b.y); ctx.bezierCurveTo(mx, b.y, mx, p.y, p.x, p.y); ctx.stroke(); }
});
});
// blocks
var S = compact ? 9 : 8, now = performance.now();
blocks.forEach(function (b) {
if (!b.on) return;
var age = now - b.born, glow = age < 1600 ? 1 - age / 1600 : 0, ex = b.color === 'red';
var r = b.chain ? S + 1 : S;
if (glow > 0) { var g = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, r * 3); g.addColorStop(0, rgba(COL.ember, 0.4 * glow)); g.addColorStop(1, rgba(COL.ember, 0)); ctx.fillStyle = g; ctx.beginPath(); ctx.arc(b.x, b.y, r * 3, 0, Math.PI * 2); ctx.fill(); }
ctx.globalAlpha = ex ? 0.45 : 1;
// fill: proven = ember, otherwise the row surface; outline: included (blue or chain) = ember, pending = grey, excluded = dim
var proving = !b.proven && b.shards.some(function (s) { return s.state === 'proving' || s.state === 'verified' || s.state === 'paid'; });
ctx.beginPath(); rr(b.x - r, b.y - r, 2 * r, 2 * r, 3);
ctx.fillStyle = b.proven ? COL.ember : COL.obsidian; ctx.fill();
ctx.lineWidth = b.chain ? 2 : 1.5;
// chain blocks in ember (the path), included blocks lit in bone, pending in ash, excluded dim
ctx.strokeStyle = b.proven ? COL.ember : proving ? COL.molten : b.chain ? rgba(COL.ember, 0.9) : b.color === 'blue' ? rgba(COL.bone, 0.8) : ex ? rgba(COL.ash, 0.5) : rgba(COL.ash, 0.75);
ctx.stroke();
if (b.locked) { ctx.strokeStyle = rgba(COL.ember, 0.8 + 0.2 * Math.sin(t / 350)); ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(b.x, b.y, r * 1.9, 0, Math.PI * 2); ctx.stroke(); }
if (isMine && isMine(b)) { ctx.strokeStyle = rgba(COL.molten, 0.9); ctx.lineWidth = 1.5; ctx.beginPath(); ctx.arc(b.x, b.y, r * 1.5, 0, Math.PI * 2); ctx.stroke(); }
ctx.globalAlpha = 1;
});
// hover or pinned ring
var f = pinned || hover; if (f && f.on) { ctx.strokeStyle = COL.bone; ctx.lineWidth = 1; ctx.setLineDash([2, 3]); ctx.beginPath(); ctx.arc(f.x, f.y, S * 2.4, 0, Math.PI * 2); ctx.stroke(); ctx.setLineDash([]); }
// axis in DAA at the left and right edges, and the lock line label
if (!compact) {
ctx.fillStyle = COL.ash; ctx.font = '400 10px IBM Plex Mono, monospace'; ctx.textBaseline = 'alphabetic';
var first = null, last = null; blocks.forEach(function (b) { if (!b.on) return; if (!first || b.ts < first.ts) first = b; if (!last || b.ts > last.ts) last = b; });
if (first && last) { ctx.textAlign = 'left'; ctx.fillText('daa ' + fmt(first.daa), padL, padT - 8); ctx.textAlign = 'right'; ctx.fillText('daa ' + fmt(last.daa) + (paused ? ' paused' : ''), W - padR, padT - 8); }
}
if (paused) { ctx.fillStyle = rgba(COL.obsidian, 0.0); }
}
function rr(x, y, w, h, r) { ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); }
function frame(t) { if (!running) return; if (!paused) draw(t); if (reduce || document.hidden || !inView) { running = false; return; } requestAnimationFrame(frame); }
function start() { if (running) return; running = true; requestAnimationFrame(frame); }
function kick() { if (!running) requestAnimationFrame(function (t) { draw(t); if (!reduce && !document.hidden && inView) start(); }); }
// ---- interaction ----
function hit(ev) {
var rect = canvas.getBoundingClientRect(), px = ev.clientX - rect.left, py = ev.clientY - rect.top, best = null, bd = 14 * 14;
blocks.forEach(function (b) { if (!b.on) return; var dx = b.x - px, dy = b.y - py, d = dx * dx + dy * dy; if (d < bd) { bd = d; best = b; } });
return best;
}
function words(b) {
var state = b.locked ? 'locked checkpoint' : b.proven ? 'proven' : b.chain ? 'included, selected chain' : b.color === 'blue' ? 'included' : b.color === 'red' ? 'excluded' : 'pending';
var sh = b.shards.length ? b.shards.map(function (s) { return s.state; }).join(', ') : 'no shard plan yet';
return { title: b.hash + ' ' + state, lines: [(isMine && isMine(b) ? 'your block, miner ' : 'miner ') + b.miner + (b.miner in laneOf ? '' : ' (others lane)'), 'blue score ' + fmt(b.blue) + ' daa ' + fmt(b.daa), b.parents.length + (b.parents.length === 1 ? ' parent' : ' parents'), 'proof: ' + sh + (b.final ? ' final' : '')] };
}
var tip = opts.tooltip || null;
function showTip(b, ev) {
if (!tip) return;
if (!b) { tip.hidden = true; return; }
var w = words(b); tip.hidden = false; tip.innerHTML = '<b>' + w.title + '</b>' + w.lines.map(function (l) { return '<span>' + l + '</span>'; }).join('');
var rect = canvas.getBoundingClientRect(); var x = Math.min(rect.width - tip.offsetWidth - 8, Math.max(8, b.x + 12)), y = Math.max(8, b.y - tip.offsetHeight - 10); if (y < 8) y = b.y + 14;
tip.style.transform = 'translate(' + Math.round(x) + 'px,' + Math.round(y) + 'px)';
}
canvas.addEventListener('mousemove', function (ev) { hover = hit(ev); canvas.style.cursor = hover ? 'pointer' : 'default'; if (!pinned) showTip(hover, ev); kick(); });
canvas.addEventListener('mouseleave', function () { hover = null; if (!pinned) showTip(null); kick(); });
canvas.addEventListener('click', function (ev) { var b = hit(ev); pinned = (pinned && b && pinned.hash === b.hash) ? null : b; showTip(pinned || hover); kick(); });
canvas.addEventListener('wheel', function (ev) { if (!ev.ctrlKey && Math.abs(ev.deltaY) < 1) return; ev.preventDefault(); zoom(ev.deltaY > 0 ? 1.15 : 1 / 1.15); }, { passive: false });
var pinch = null;
canvas.addEventListener('touchstart', function (ev) { if (ev.touches.length === 2) pinch = dist(ev.touches); }, { passive: true });
canvas.addEventListener('touchmove', function (ev) { if (ev.touches.length === 2 && pinch) { var d = dist(ev.touches); if (Math.abs(d - pinch) > 12) { zoom(d > pinch ? 1 / 1.1 : 1.1); pinch = d; } } }, { passive: true });
canvas.addEventListener('touchend', function () { pinch = null; }, { passive: true });
function dist(t) { var dx = t[0].clientX - t[1].clientX, dy = t[0].clientY - t[1].clientY; return Math.sqrt(dx * dx + dy * dy); }
function zoom(f) { var w = Math.round(Math.max(30, Math.min(300, windowS * f))); if (w === windowS) return; windowS = w; layout(); if (opts.onWindow) opts.onWindow(w); poll(); kick(); }
function setPaused(p) { paused = !!p; if (paused) { offsetMs = 0; pausedAt = Date.now(); } else { offsetMs = 0; } if (opts.pauseButton) { opts.pauseButton.setAttribute('aria-pressed', paused ? 'true' : 'false'); opts.pauseButton.textContent = paused ? 'Resume' : 'Pause'; } kick(); }
var pausedAt = 0;
if (opts.pauseButton) opts.pauseButton.addEventListener('click', function () { setPaused(!paused); });
// while paused the clock stops: offsetMs grows so the scene holds still but keeps its blocks
setInterval(function () { if (paused) offsetMs = Date.now() - pausedAt; }, 250);
if ('IntersectionObserver' in root) { new IntersectionObserver(function (es) { es.forEach(function (e) { inView = e.isIntersecting; if (inView) kick(); }); }, { threshold: 0.05 }).observe(canvas); }
document.addEventListener('visibilitychange', function () { if (!document.hidden) { poll(); kick(); } });
if (root.matchMedia) { try { root.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function () { readTheme(); kick(); }); } catch (e) { } }
new MutationObserver(function () { readTheme(); kick(); }).observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
readTheme(); size(); if (opts.poll !== false) { poll(); setInterval(poll, 2000); } kick();
return { push: push, setPaused: setPaused, zoom: zoom, getWindow: function () { return windowS; }, getState: function () { return { state: state, reason: reason }; }, redraw: kick, words: words };
}
root.IgneumDag = { mount: mount };
})(window);

View file

@ -31,9 +31,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -45,83 +46,26 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;
/* one system for every page: container, gutter, section rhythm, card and tile boxes, heading scale (same tokens as the homepage) */
/* page block; tokens, type and components are in /site.css */
.scene{position:relative}.scene canvas{width:100%;height:420px;border-radius:var(--r-row);background:var(--row);border:1px solid var(--line);display:block}@media (max-width:700px){.scene canvas{height:300px}}
.tip{position:absolute;left:0;top:0;pointer-events:none;background:var(--graphite);border:1px solid var(--line-2);border-radius:var(--r-row);padding:10px 12px;font:400 12px/1.5 var(--f-mono);color:var(--ink-2);display:flex;flex-direction:column;gap:2px;max-width:320px;box-shadow:0 14px 40px var(--shadow)}.tip b{color:var(--bone);font-weight:500;overflow-wrap:anywhere}.tip[hidden]{display:none}
.scene-foot{display:flex;flex-wrap:wrap;justify-content:space-between;gap:var(--s-3);align-items:flex-start;margin-top:var(--s-4)}
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-hero:clamp(40px,7vw,76px);--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}
a:hover{color:var(--molten)}
a:focus-visible,button:focus-visible{outline:2px solid var(--ember);outline-offset:3px;border-radius:6px}
img,svg,canvas{max-width:100%}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.mono{font-family:var(--f-mono)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1,h2,h3{font-family:var(--f-display);margin:0;line-height:1.08;text-wrap:balance}
h1{font-weight:900;font-size:var(--fs-h1);letter-spacing:-.01em}
h2{font-weight:700;font-size:var(--fs-h3)}
p{margin:0}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer}
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0}
/* nav and footer: shared, see site/partials */
/* page */
.head{padding-block:var(--head) var(--gap);display:flex;flex-direction:column;gap:14px}
.head p{color:var(--ink-2);max-width:60ch;font-size:16px}
@ -135,33 +79,18 @@ p{margin:0}
.cell .v.name{font-family:var(--f-mono);font-size:15px;font-weight:500}
.cell .s{font-family:var(--f-mono);font-size:12px;color:var(--ash);text-wrap:balance}
.cell.status .v{display:flex;align-items:center;gap:10px}
.dot{width:10px;height:10px;border-radius:50%;background:var(--molten);display:inline-block;flex:0 0 10px;animation:pulse 2s ease-in-out infinite}
.off .dot{background:var(--ash);animation:none}
.off .cell.status .v{color:var(--ash)}
.on .cell.status .v{color:var(--molten)}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(255,179,92,.5)}50%{box-shadow:0 0 0 8px rgba(255,179,92,0)}}
@media (prefers-reduced-motion:reduce){.dot{animation:none}}
.viz-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 20px;margin-bottom:12px}
.viz-stats{display:flex;flex-wrap:wrap;gap:14px 18px;font-size:12px;color:var(--ash)}
.viz-stats b{color:var(--bone);font-weight:500;font-variant-numeric:tabular-nums}
#dag{display:block;width:100%;height:calc(clamp(240px,38vw,380px) + 18px + 64px);border-radius:12px;background:var(--obsidian);touch-action:pan-y}
@media (max-width:640px){#dag{height:calc(clamp(240px,38vw,380px) + 18px + 50px)}}
.legend{display:flex;flex-direction:column;gap:8px;margin-top:16px;font-size:13px;color:var(--ink-2)}
.legend div{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px}
.legend .lk{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);min-width:64px}
.legend span{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.legend span.txt{white-space:normal;display:inline;color:var(--ash)}
.sw{width:14px;height:14px;border-radius:4px;display:inline-block}
.two{display:grid;gap:var(--gap);grid-template-columns:1fr;margin-top:var(--gap)}
@media (min-width:900px){.two{grid-template-columns:1.1fr .9fr}}
.tbl{overflow-x:auto;margin-top:12px;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;font-size:14px}
th,td{padding:10px 10px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap}
th{font-family:var(--f-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500}
td.n{font-variant-numeric:tabular-nums;text-align:right}th.n{text-align:right}
tr:last-child td{border-bottom:0}
.id{font-family:var(--f-mono);color:var(--molten)}
.bar{height:6px;border-radius:3px;background:var(--line);overflow:hidden;min-width:60px}
.bar i{display:block;height:100%;background:var(--ember)}
@ -170,11 +99,8 @@ tr:last-child td{border-bottom:0}
.feed div:last-child{border-bottom:0}
.feed .t{color:var(--ash);flex:0 0 auto;font-size:12px}
.feed .k{color:var(--molten)}
.empty{color:var(--ash);font-size:14px;padding:12px 0}
#spark{display:block;width:100%;height:120px;border-radius:12px;background:var(--obsidian);margin-top:12px}
.note{font-size:13px;color:var(--ash);margin-top:14px}
main{padding-bottom:var(--sec)}
.foot{margin-top:0}
</style>
</head>
<body class="off">
@ -184,10 +110,9 @@ main{padding-bottom:var(--sec)}
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
@ -196,30 +121,42 @@ main{padding-bottom:var(--sec)}
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
<main id="main" class="wrap">
<div class="head">
<div class="eyebrow">devnet v0</div>
<div class="eyebrow">live devnet</div>
<h1>Live devnet</h1>
<p>A node is read every two seconds. Every block below is real: its parents, its miner, whether it sits on the selected chain, and whether it is a locked checkpoint. A checkpoint locks when the finality rule's quorum signs it. Once the proving layer is activated, every chain block's shards show as they are planned, proven and paid.</p>
<p>Fees: the devnet meters with its prototype table until DAA score 210,000; from there the adopted table applies (300 pgas per transaction, 30,000-pgas shards, floors of 100 gwei per gas and 10,000 gwei per pgas) and the provers' program carries both.</p>
<p>Fees: from DAA score 210,000 (the chain's block-time clock, about one step a second) the adopted fee table applies: 300 proving gas per transaction, 30,000-gas shards, floors of 100 gwei per gas and 10,000 gwei per proving gas. Before it the devnet metered with its prototype table, and the provers' program carries both.</p>
</div>
<div class="strip" aria-live="polite">
@ -229,21 +166,26 @@ main{padding-bottom:var(--sec)}
<div class="cell"><div class="k">Blue score</div><div class="v" id="st-blue">0</div><div class="s" id="st-count">0 blocks</div></div>
<div class="cell"><div class="k">Difficulty</div><div class="v" id="st-diff">0</div><div class="s">target per block</div></div>
<div class="cell"><div class="k">Hash rate</div><div class="v" id="st-hps">0</div><div class="s">network estimate</div></div>
<div class="cell"><div class="k">Identities</div><div class="v" id="st-miners">0</div><div class="s">vote keys active in 10 min, a card runs several</div></div>
<div class="cell"><div class="k">Identities</div><div class="v" id="st-miners">0</div><div class="s">vote keys active in 10 min; a card runs several</div></div>
<div class="cell"><div class="k">Peers</div><div class="v" id="st-peers">0</div><div class="s" id="st-mempool">mempool 0</div></div>
</div>
<div class="card" aria-label="Block DAG, finality and proving, live">
<div class="card viz" aria-label="Block DAG, finality and proving, live">
<div class="viz-head">
<div class="eyebrow"><span class="dot" style="margin-right:8px"></span>blocks, finality, proving</div>
<div class="viz-stats mono"><span>on screen <b id="c-screen">0</b></span><span>chain <b id="c-chain">0</b></span><span>identities <b id="c-idents">0</b></span><span>last lock <b id="c-locked">none</b></span><span>proven <b id="c-proven">pending</b></span></div>
<div class="state" id="dag-state"><span class="dot off"></span>connecting to the devnet observer</div>
<div class="viz-stats mono"><span>on screen <b id="c-screen">0</b></span><span>chain <b id="c-chain">0</b></span><span>identities <b id="c-idents">0</b></span><span>last lock <b id="c-locked">pending</b></span><span>proven <b id="c-proven">pending</b></span></div>
</div>
<canvas id="dag" aria-hidden="true"></canvas>
<div class="scene"><canvas id="dag" aria-label="The devnet's block DAG, live: blocks by time and miner lane, parent edges, the selected chain, checkpoint bands with their lock weight, proofs filling blocks as segments are paid"></canvas><div class="tip" id="dag-tip" hidden></div></div>
<div class="legend">
<div><span class="lk">blocks</span><span><i class="sw" style="background:var(--ember);border:1.5px solid #5CB8FF"></i>chain</span><span><i class="sw" style="background:rgba(92,184,255,.7);border:1.5px solid #5CB8FF"></i>included, paid</span><span><i class="sw" style="border:1.5px solid var(--ash)"></i>pending</span><span><i class="sw" style="border:1.5px solid rgba(154,154,158,.6);background:linear-gradient(45deg,transparent 44%,var(--ash) 44%,var(--ash) 56%,transparent 56%) center/8px 8px no-repeat"></i>excluded</span><span><i class="sw" style="border:2px solid var(--molten);box-shadow:0 0 8px rgba(255,179,92,.7)"></i>just arrived</span><span><i class="sw" style="width:22px;height:0;border-top:2px solid var(--ember)"></i>selected chain</span><span class="txt">one lane per miner, colour from its id; hover a block, or tap it to keep its details up</span></div>
<div><span class="lk">finality</span><span><i class="sw" style="width:22px;height:10px;background:rgba(242,84,27,.22);border-radius:2px"></i>final</span><span><i class="sw" style="width:3px;height:12px;background:var(--molten);border-radius:1px"></i>locked checkpoint</span><span><i class="sw" style="width:22px;height:10px;background:rgba(154,154,158,.10);border:1px solid rgba(154,154,158,.3);border-radius:2px"></i>proposed</span><span class="txt">reads "paused" whenever under two thirds of the weight is signing</span></div>
<div><span class="lk">proving</span><span><i class="sw" style="width:12px;height:9px;border:1px solid rgba(154,154,158,.55);border-radius:2px"></i>shard planned</span><span><i class="sw" style="width:12px;height:9px;border:1px solid var(--molten);background:rgba(255,179,92,.25);border-radius:2px"></i>proving</span><span><i class="sw" style="width:12px;height:9px;background:#3FD39A;border-radius:2px"></i>verified, prover colour</span><span><i class="sw" style="width:12px;height:9px;background:#3FD39A;border-radius:2px;position:relative"><i style="position:absolute;left:3px;top:1px;width:3px;height:5px;border:solid #0C0C0E;border-width:0 1.5px 1.5px 0;transform:rotate(45deg)"></i></i>paid</span><span><i class="sw" style="width:0;height:12px;border-left:1px dashed var(--molten)"></i>where proofs land behind the tip</span><span class="txt">hover a cell, or tap it to keep its details up</span></div>
<span><i class="sw" style="border:2px solid var(--line-2)"></i>pending</span>
<span><i class="sw" style="border:2px solid var(--bone)"></i>included</span><span><i class="sw" style="border:2px solid var(--ember)"></i>selected chain</span>
<span><i class="sw" style="border:2px solid var(--line-2);opacity:.45"></i>excluded</span>
<span><i class="sw" style="background:var(--ember)"></i>proven</span>
<span><i class="sw" style="border:3px solid var(--ember);border-radius:50%;background:transparent"></i>locked checkpoint</span>
<span><i class="sw" style="width:0;height:12px;border-left:2px solid var(--ember);border-radius:0"></i>finality band, locked, with its weight</span>
<span><i class="sw" style="width:0;height:12px;border-left:2px dashed var(--ember);border-radius:0;opacity:.5"></i>finality band, pending</span>
</div>
<div class="scene-foot"><p class="note" style="margin:0"><span id="c-window">120 s of blocks on screen</span>. One lane per miner, the busiest first; the rest share the last lane. Hover or tap a block for its hash, miner, blue score, parents and proof state. Scroll or pinch to change the window; the page's <code>?window=</code> fixes it.</p><button type="button" class="btn small" id="dag-pause" aria-pressed="false">Pause</button></div>
</div>
<div class="two">
@ -275,7 +217,7 @@ main{padding-bottom:var(--sec)}
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -301,9 +243,9 @@ main{padding-bottom:var(--sec)}
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -311,12 +253,26 @@ main{padding-bottom:var(--sec)}
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script src="/live-dag.js" defer></script>
<script>
(function(){
var reduce=window.matchMedia('(prefers-reduced-motion: reduce)').matches;
@ -327,304 +283,32 @@ main{padding-bottom:var(--sec)}
// formatting
function $(id){return document.getElementById(id);}
function compact(n){if(n===null||n===undefined||isNaN(n))return '0';var a=Math.abs(n);if(a>=1e12)return (n/1e12).toFixed(2)+'T';if(a>=1e9)return (n/1e9).toFixed(2)+'G';if(a>=1e6)return (n/1e6).toFixed(2)+'M';if(a>=1e3)return (n/1e3).toFixed(1)+'k';return String(Math.round(n));}
function hashrate(n){if(n===null||n===undefined)return 'n/a';var u=['H/s','kH/s','MH/s','GH/s','TH/s'],i=0;while(n>=1000&&i<u.length-1){n/=1000;i++;}return (i?n.toFixed(1):Math.round(n))+' '+u[i];}
function hashrate(n){if(n===null||n===undefined)return 'pending';var u=['H/s','kH/s','MH/s','GH/s','TH/s'],i=0;while(n>=1000&&i<u.length-1){n/=1000;i++;}return (i?n.toFixed(1):Math.round(n))+' '+u[i];}
function rel(ms){var s=Math.max(0,Math.round(ms/1000));if(s<60)return s+' s ago';var m=Math.floor(s/60);if(m<60)return m+' min ago';var h=Math.floor(m/60);if(h<24)return h+' h '+(m%60)+' min ago';return Math.floor(h/24)+' d ago';}
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
// ---------- three strips on one time axis ----------
// One canvas, one requestAnimationFrame loop, newest at the right. BLOCKS (lanes: one per miner in the visible window,
// most blocks on top, the least active share an "others" lane; labels in a left gutter; edges under blocks), then a
// FINALITY bar, then a PROVING strip with one cell per shard of every chain block. All three share px-per-second.
var c=$('dag'),x=c.getContext('2d'),W=0,H=0,BH=0,S=22,G=78,narrow=false,level=0,maxLanes=8,FIN_H=18,PROV_H=64;
var blocks=[],byHash={},miners={},lanes=[],laneN=1,othersN=0,fin=null,prov=null,idents=0;
var LV=[{S:22,s:14,pps:40,npps:14},{S:16,s:12,pps:26,npps:10},{S:12,s:10,pps:18,npps:8}];
var pxPerSec=40,targetPx=40,bps=0,live=false,serverNow=0,serverAt=0,frozenAt=null,note='';
// ?window=N (30 to 300 s): a fixed long view for diagnosis, the API returns the same span; the default stays the adaptive scale
// ---------- the DAG: the shared module (/live-dag.js), fed by this page's poll ----------
var fixedWindow=Math.max(0,Math.min(300,Number(new URLSearchParams(location.search).get('window'))||0));if(fixedWindow&&fixedWindow<30)fixedWindow=30;
var running=false,needSize=true,inView=true,ptrX=-1,ptrY=-1,laneLast=[],laneRun=[],lagNote='';
// pin: a tapped or clicked block (i = -1) or shard cell (i = shard index). It stays until a tap or click lands elsewhere,
// Escape, or the block leaves the strip (then pinFade holds it for a 200 ms fade). Hover never touches it.
var pin=null,pinFade=null,PIN_FADE=200,closeRect=null,panelRect=null,down=null,TAP_SLOP=10;
var TOP=20,BOT=12;
// 12 hues that sit beside ember on obsidian; the index comes from the short id, so a miner (or a prover) keeps its colour across reloads
var PAL=['#5CB8FF','#3FD39A','#F5C542','#C49BFF','#FF7AA8','#8FD15F','#FFB08A','#6F8CFF','#D9E25A','#FF8FD6','#4FD8D8','#E0A96D'];
function colour(id){var v=0;for(var i=0;i<id.length;i++)v=(v*33+id.charCodeAt(i))>>>0;return PAL[v%PAL.length];}
function jitter(h){var v=0;for(var i=0;i<8;i++)v=(v*31+h.charCodeAt(i))%1000;return v/1000-0.5;}
function vnow(){if(frozenAt!==null)return frozenAt;return serverNow+(performance.now()-serverAt);}
function laneY(l){var u=BH-TOP-BOT,p=u/Math.max(1,laneN);return TOP+(l+0.5)*p;}
function size(){
needSize=false;W=c.clientWidth;H=c.clientHeight;if(!W||!H)return;
c.width=Math.round(W*dpr);c.height=Math.round(H*dpr);x.setTransform(dpr,0,0,dpr,0,0);
narrow=W<560;G=narrow?36:80;PROV_H=narrow?50:64;BH=H-FIN_H-PROV_H;applyLevel();
for(var id in miners)miners[id].y=laneY(miners[id].lane); // a resize is a layout change: lanes snap, nothing eases from a stale position
}
function applyLevel(){
var L=LV[level];S=narrow?L.s:L.S;targetPx=narrow?L.npps:L.pps;
if(fixedWindow){targetPx=(W-G)/fixedWindow;S=fixedWindow>120?(narrow?8:10):(narrow?L.s:LV[1].S);}
maxLanes=Math.max(2,Math.min(14,Math.floor((BH-TOP-BOT)/(S*1.7))));
layoutLanes();
}
function pickLevel(){
// projected blocks on screen at the base scale drives the step, with hysteresis so it never flickers
var proj=bps*(W-G)/(narrow?LV[0].npps:LV[0].pps),up=[48,110],down=[36,85],next=level;
if(level<2&&proj>up[level])next=level+1;else if(level>0&&proj<down[level-1])next=level-1;
if(fixedWindow)next=fixedWindow>120?2:1;
if(next!==level){level=next;applyLevel();}
}
function layoutLanes(){
// rank miners by blocks in the window; one lane each, the overflow shares the last lane with a count
var ids=Object.keys(miners);
ids.sort(function(a,b){return miners[b].n-miners[a].n||(a<b?-1:1);});
laneN=Math.max(1,Math.min(maxLanes,ids.length));
othersN=ids.length>laneN?ids.length-(laneN-1):0;
lanes=[];
ids.forEach(function(id,i){var m=miners[id],l=othersN&&i>=laneN-1?laneN-1:i;m.lane=l;if(m.y===undefined&&BH>0)m.y=laneY(l);if(!othersN||l<laneN-1)lanes[l]=m;});
while(laneLast.length<laneN){laneLast.push(0);laneRun.push(0);}
}
function ingest(d){
if(needSize)size(); // the first poll can land before the first frame; the counts below need the canvas width
var now=performance.now();serverNow=new Date(d.now).getTime();serverAt=now;
fin=d.finality||null;prov=d.proving||null;idents=d.state.miners_10m||0;
d.blocks.forEach(function(b){var e=byHash[b.hash];
if(e){e.chain=b.chain;e.locked=!!b.locked;e.final=!!b.final;e.color=b.color||'pending';e.shards=b.shards||[];e.proven=!!b.proven;return;}
e={hash:b.hash,ts:b.ts,blue_score:b.blue_score,daa:b.daa,parents:b.parents||[],chain:b.chain,color:b.color||'pending',miner:b.miner||'?',born:now,j:jitter(b.hash),locked:!!b.locked,final:!!b.final,shards:b.shards||[],proven:!!b.proven,px:0,py:0,on:false};
byHash[b.hash]=e;blocks.push(e);});
// keep what the target scale will show (the scale eases toward it), plus 20 s so a scale change never drops a block early
var cut=vnow()-((W-G)/Math.max(2,Math.min(pxPerSec,targetPx)))*1000-20000;
blocks=blocks.filter(function(b){var keep=b.ts>cut;if(!keep)delete byHash[b.hash];return keep;});
blocks.sort(function(a,b){return a.ts-b.ts;});
for(var id in miners)miners[id].n=0;
blocks.forEach(function(b){var m=miners[b.miner];if(!m){m=miners[b.miner]={id:b.miner,n:0,col:colour(b.miner),tag:b.miner.slice(0,3),lane:0};}m.n++;});
for(id in miners)if(!miners[id].n)delete miners[id];
bps=d.state.blocks_per_second_60s||0;
pickLevel();layoutLanes();
var vis=vnow()-((W-G)/Math.max(2,targetPx))*1000,on=0,chain=0,planned=0,proven=0;
blocks.forEach(function(b){if(b.ts>vis){on++;if(b.chain){chain++;if(b.shards.length){planned++;if(b.proven)proven++;}}}});
$('c-screen').textContent=on;$('c-chain').textContent=chain;$('c-idents').textContent=idents;
var f=fin;$('c-locked').textContent=f&&f.latest_locked_index?('#'+f.latest_locked_index+(f.checkpoints&&f.checkpoints.length?', '+lockAge(f):'')):(f&&f.supported?'none yet':'n/a');
// 6 Oct 2026: proofs land a median of minutes behind the tip, so the on-screen window (proven+'/'+planned) read 0 for ever; the
// counter now reads the observer's 10-minute truth: chain blocks with every shard paid, of the chain blocks planned in 10 min
var pe=$('c-proven');if(prov&&prov.supported&&prov.active){pe.textContent=(prov.blocks_fully_proven_10m||0)+'/'+(prov.blocks_10m||0)+' in 10 min';pe.title=(prov.shards_paid_10m||0)+' shards paid in the last 10 minutes'+(prov.median_proof_lag_s?', landing a median '+Math.round(prov.median_proof_lag_s)+' s behind the tip':'')+'; '+(prov.paid_shards_total!==null&&prov.paid_shards_total!==undefined?Number(prov.paid_shards_total).toLocaleString('en-GB')+' paid on the chain so far':'');}
var dagCanvas=$('dag'),dag=null,lastD=null;
function windowWord(w){return w+' s of blocks on screen';}
function mountDag(){if(dag||!dagCanvas||!window.IgneumDag)return;
dag=IgneumDag.mount(dagCanvas,{poll:false,window:fixedWindow||120,tooltip:$('dag-tip'),pauseButton:$('dag-pause'),
onWindow:function(w){$('c-window').textContent=windowWord(w);},
onState:function(st,reason){var el=$('dag-state');if(!el)return;el.className='state '+(st==='live'?'live':st==='failed'?'bad':'');el.innerHTML='<span class="dot '+(st==='live'?'live':st==='failed'?'bad':'off')+'"></span>'+(st==='live'?'live devnet':st==='connecting'?'connecting to the devnet observer':esc(reason||'live feed unavailable'));}});
$('c-window').textContent=windowWord(dag.getWindow());if(lastD)dag.push(lastD);}
if(window.IgneumDag)mountDag();else{var ms=document.querySelector('script[src="/live-dag.js"]');if(ms)ms.addEventListener('load',mountDag);}
// the strip above the scene reads the chain, never the window: on screen and chain count the window and say so
function sceneCounters(d){
var w=dag?dag.getWindow():(fixedWindow||120),cut=new Date(d.now).getTime()-w*1000,on=0,chain=0;
d.blocks.forEach(function(b){if(b.ts>=cut){on++;if(b.chain)chain++;}});
$('c-screen').textContent=on;$('c-chain').textContent=chain;$('c-idents').textContent=d.state.miners_10m||0;
var f=d.finality;$('c-locked').textContent=f&&f.supported&&!f.active?('finality paused'+(f.latest_locked_index?', last #'+f.latest_locked_index:'')):f&&f.latest_locked_index?('#'+f.latest_locked_index+(f.checkpoints&&f.checkpoints.length?', '+lockAge(f):'')):(f&&f.supported?'none yet':'no finality');
var prov=d.proving,pe=$('c-proven');
if(prov&&prov.supported&&prov.active){pe.textContent=(prov.blocks_fully_proven_10m||0)+'/'+(prov.blocks_10m||0)+' in 10 min';pe.title=(prov.shards_paid_10m||0)+' shards paid in the last 10 minutes'+(prov.median_proof_lag_s?', landing a median '+Math.round(prov.median_proof_lag_s)+' s behind the tip':'')+'; '+(prov.paid_shards_total!==null&&prov.paid_shards_total!==undefined?Number(prov.paid_shards_total).toLocaleString('en-GB')+' paid on the chain so far':'');}
else{pe.textContent=prov&&prov.supported?'not active':'no proving layer';pe.title='';}
if(!running&&!document.hidden)kick();
}
function lockAge(f){var cps=f.checkpoints||[],lk=null;for(var i=cps.length-1;i>=0;i--)if(cps[i].state==='locked'){lk=cps[i];break;}
if(!lk||!lk.locked_at)return '';return rel(vnow()-new Date(lk.locked_at).getTime());}
function rrect(px,py,w,h,r){x.beginPath();x.moveTo(px+r,py);x.lineTo(px+w-r,py);x.quadraticCurveTo(px+w,py,px+w,py+r);x.lineTo(px+w,py+h-r);x.quadraticCurveTo(px+w,py+h,px+w-r,py+h);x.lineTo(px+r,py+h);x.quadraticCurveTo(px,py+h,px,py+h-r);x.lineTo(px,py+r);x.quadraticCurveTo(px,py,px+r,py);x.closePath();}
// a cubic with horizontal tangents; the control offset is clamped so a link across many lanes reads as a gentle S, never a loop
function link(a,b){var d=Math.max(S*0.6,Math.min(48,Math.abs(b.px-a.px)*0.5));x.moveTo(a.px,a.py);x.bezierCurveTo(a.px+d,a.py,b.px-d,b.py,b.px,b.py);}
// shard cells: a column under each chain block, shard 0 at the top; sizes from the zoom level, never wider than the block
function cellGeom(n){var avail=PROV_H-12,gap=2,ch=Math.min(narrow?7:9,Math.floor((avail-(n-1)*gap)/Math.max(1,n))),cw=Math.max(3,Math.min(narrow?10:12,Math.round(S*0.7),Math.round(pxPerSec*0.85)));return {ch:ch,cw:cw,gap:gap,top:BH+FIN_H+6+Math.max(0,Math.floor((avail-(n*ch+(n-1)*gap))/2))};}
function hitAt(qx,qy,touch){
// returns {b, i}: a block (i = -1) in the blocks strip, or a shard cell (i = shard index) in the proving strip.
// A finger is wider than a pointer: a touch gets a larger radius, the nearest target still wins; hover is unchanged
if(qx<0)return null;
if(qy<BH){var best=null,bd=S*(touch?1.3:0.9);
for(var i=0;i<blocks.length;i++){var b=blocks[i];if(!b.on)continue;var dx=Math.abs(b.px-qx),dy=Math.abs(b.py-qy);if(dx<bd&&dy<bd&&dx+dy<bd*1.5){best=b;bd=Math.max(dx,dy);}}
return best?{b:best,i:-1}:null;}
if(qy>=BH+FIN_H&&provOn()){var bb=null,bdx=1e9;
for(i=0;i<blocks.length;i++){b=blocks[i];if(!b.on||!b.chain||!b.shards.length)continue;var ddx=Math.abs(b.px-qx);if(ddx<bdx){bdx=ddx;bb=b;}}
if(!bb)return null;var g=cellGeom(bb.shards.length),ns=bb.shards.length;if(bdx>Math.max(g.cw,S*0.5)*(touch?1.8:1))return null;
var row=Math.floor((qy-g.top)/(g.ch+g.gap));
if(touch&&(row<0||row>=ns)&&qy>=g.top-8&&qy<=g.top+ns*(g.ch+g.gap)+8)row=Math.max(0,Math.min(ns-1,row));
if(row<0||row>=ns)return null;return {b:bb,i:row};}
return null;
}
function hover(){return ptrX<0?null:hitAt(ptrX,ptrY,false);}
// the pinned target while its block is still drawn; null once it has scrolled out of the strip
function pinned(){if(!pin)return null;var p=byHash[pin.h];return p&&p.on?{b:p,i:pin.i}:null;}
function same(a,b){return !!(a&&b&&a.b===b.b&&a.i===b.i);}
function inRect(r,qx,qy){return !!(r&&qx>=r[0]&&qx<r[0]+r[2]&&qy>=r[1]&&qy<r[1]+r[3]);}
function setPin(t){pin=t?{h:t.b.hash,i:t.i,b:t.b}:null;pinFade=null;if(reduce)kick();}
function clearPin(){if(!pin&&!pinFade)return;pin=null;pinFade=null;if(reduce)kick();}
// a tap or click at (qx, qy): the close control clears, the panel itself is inert, a block or cell pins (a second tap on the
// pinned one keeps it), anything else on the canvas clears
function tap(qx,qy,touch){
if(inRect(closeRect,qx,qy)){clearPin();return;}
if(inRect(panelRect,qx,qy))return;
var t=hitAt(qx,qy,touch);
if(t)setPin(t);else clearPin();
}
// the three panel lines for a target, its anchor y for the floating panel, and its border colour
function lines(t){var b=t.b,tcol=miners[b.miner]?miners[b.miner].col:'#3A3A42',l1,l2,l3,ay;
if(t.i<0){l1=b.hash.slice(0,8)+' '+(b.locked?'locked':b.chain?'chain':b.color==='blue'?'included, paid':b.color==='red'?'excluded':'pending')+(b.chain&&b.shards.length?(b.proven?', proven':', '+b.shards.length+(b.shards.length===1?' shard':' shards')):'');l2='blue '+compact(b.blue_score)+' daa '+compact(b.daa);l3=b.parents.length+(b.parents.length===1?' parent':' parents')+' '+b.miner;ay=b.py;}
else{var sd=b.shards[t.i]||{};l1=b.hash.slice(0,8)+' shard '+(t.i+1)+' of '+b.shards.length+' '+(sd.state||'gone');l2='prover '+(sd.prover||'none yet')+' lag '+(sd.lag!==null&&sd.lag!==undefined?sd.lag+' s':'n/a');l3=(sd.payout!==null&&sd.payout!==undefined?sd.payout+' IGN':'unpaid')+(sd.pgas?' '+compact(sd.pgas)+' pgas':'');ay=BH+FIN_H+PROV_H/2;if(sd.prover)tcol=colour(sd.prover);}
return [l1,l2,l3,ay,tcol];}
function provOn(){return !!(prov&&prov.supported&&prov.active);}
function draw(now){
if(needSize)size();if(!W||!H)return;
pxPerSec+=(targetPx-pxPerSec)*0.06;
var t=vnow(),xr=W-S,i,b,m,n=blocks.length,id;
for(id in miners){m=miners[id];var ty=laneY(m.lane);if(m.y===undefined)m.y=ty;m.y+=(ty-m.y)*0.12;if(Math.abs(ty-m.y)<0.05)m.y=ty;}
for(i=0;i<laneN;i++){laneLast[i]=-1e9;laneRun[i]=0;}
// positions: time on x, lane on y; same-lane neighbours closer than a block are nudged up and down in a fixed sequence
for(i=0;i<n;i++){b=blocks[i];m=miners[b.miner];if(!m)continue;var l=m.lane;b.px=Math.min(xr,xr-(t-b.ts)*pxPerSec/1000);
if(b.px-laneLast[l]<S*1.25)laneRun[l]++;else laneRun[l]=0;laneLast[l]=b.px;
var k=laneRun[l]%3,dy=k===0?0:(k===1?1:-1)*S*0.62;b.py=m.y+dy;b.on=b.px>G-S;}
x.clearRect(0,0,W,H);
// the newest locked checkpoint on screen: its x bounds the final region of the finality bar
var lk=null,fx=-1;
for(i=0;i<n;i++){b=blocks[i];if(b.locked&&(!lk||b.ts>lk.ts))lk=b;if(b.final&&b.px>fx)fx=b.px;}
if(lk)fx=lk.px;
// lane rails
x.strokeStyle='rgba(42,42,48,0.75)';x.lineWidth=1;x.beginPath();for(i=0;i<laneN;i++){var y=Math.round(laneY(i))+0.5;x.moveTo(G,y);x.lineTo(W,y);}x.stroke();
x.save();x.beginPath();x.rect(G,0,W-G,BH);x.clip();
// side links: each side block to its first parent on screen, faint and short
x.strokeStyle='rgba(154,154,158,0.3)';x.lineWidth=1;x.beginPath();
for(i=0;i<n;i++){b=blocks[i];if(b.chain||!b.on)continue;var q=byHash[b.parents[0]];if(q&&q.px>G-S*2)link(q,b);}
x.stroke();
// the selected chain as one path through the chain blocks
x.strokeStyle=level?'rgba(242,84,27,0.6)':'rgba(242,84,27,0.85)';x.lineWidth=narrow||level?1.5:2;x.beginPath();var prev=null;
for(i=0;i<n;i++){b=blocks[i];if(!b.chain)continue;if(prev&&b.px>G-S*2)link(prev,b);prev=b;}
x.stroke();
// the pinned block scrolled out: let it go with a short fade instead of a snap (reduced motion: at once)
var pv=pinned();
if(pin&&!pv){pinFade=reduce?null:{b:pin.b,i:pin.i,at:now};pin=null;}
if(pinFade&&now-pinFade.at>=PIN_FADE)pinFade=null;
// hovered block (pointer devices only) and pinned block: all of their parent edges, bright
var hv=hover(),hb=hv?hv.b:null,pb=pv?pv.b:null;
if(pb&&pv.i<0){x.strokeStyle='rgba(255,179,92,0.7)';x.lineWidth=1.5;x.beginPath();for(i=0;i<pb.parents.length;i++){var pq0=byHash[pb.parents[i]];if(pq0)link(pq0,pb);}x.stroke();}
if(hb&&hv.i<0&&hb!==pb){x.strokeStyle='rgba(255,179,92,0.9)';x.lineWidth=1.5;x.beginPath();for(i=0;i<hb.parents.length;i++){var pq=byHash[hb.parents[i]];if(pq)link(pq,hb);}x.stroke();}
// blocks
var h=S/2,r=S*0.24,tip=null;
for(i=0;i<n;i++){b=blocks[i];if(!b.on)continue;m=miners[b.miner];var age=now-b.born,fl=age<900?1-age/900:0;
if(fl>0){var g=x.createRadialGradient(b.px,b.py,0,b.px,b.py,S*1.6);g.addColorStop(0,'rgba(255,179,92,'+(0.5*fl)+')');g.addColorStop(1,'rgba(255,179,92,0)');x.fillStyle=g;x.beginPath();x.arc(b.px,b.py,S*1.6,0,Math.PI*2);x.fill();}
if(b.chain){x.fillStyle='#F2541B';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=m?m.col:'#9A9A9E';x.lineWidth=1.5;rrect(b.px-h-1,b.py-h-1,S+2,S+2,r+1);x.stroke();tip=b;}
else if(b.color==='blue'){var mc=m?m.col:'#9A9A9E';x.fillStyle=mc+'B3';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=fl>0?'rgba(255,179,92,0.95)':mc;x.lineWidth=1.5;rrect(b.px-h,b.py-h,S,S,r);x.stroke();}
else{x.fillStyle='#0C0C0E';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=fl>0?'rgba(255,179,92,0.95)':(b.color==='red'?'rgba(154,154,158,0.6)':'rgba(154,154,158,0.85)');x.lineWidth=1.5;rrect(b.px-h,b.py-h,S,S,r);x.stroke();
if(b.color==='red'){x.strokeStyle='rgba(154,154,158,0.9)';x.lineWidth=1.5;x.beginPath();x.moveTo(b.px-h*0.5,b.py+h*0.5);x.lineTo(b.px+h*0.5,b.py-h*0.5);x.stroke();}}
if(fl>0&&b.chain){x.strokeStyle='rgba(255,179,92,'+fl+')';x.lineWidth=2;rrect(b.px-h-3,b.py-h-3,S+6,S+6,r+2);x.stroke();}
if(hb===b&&b!==pb){x.strokeStyle='#F4F1EC';x.lineWidth=1.5;rrect(b.px-h-3,b.py-h-3,S+6,S+6,r+2);x.stroke();}
// the pinned block: a molten ring with a glow, so the reader can find it as the strip moves
if(pb===b){x.save();x.shadowColor='rgba(255,179,92,0.8)';x.shadowBlur=8;x.strokeStyle='#FFB35C';x.lineWidth=2;rrect(b.px-h-5,b.py-h-5,S+10,S+10,r+3);x.stroke();x.restore();}}
x.font='500 10px IBM Plex Mono, monospace';
// the chain tip carries its short id; nothing else is labelled unless hovered or pinned
if(tip){var lt=tip.hash.slice(0,8),lw=x.measureText(lt).width,lh=11,pad=3;
// candidate label boxes around the tip: above-right, above-left, below-right, below-left, left, right; the first that
// stays inside the strip and overlaps no other on-screen block wins, so the id is never drawn over a neighbour
var cands=[[tip.px+h+2-lw,tip.py-h-6-lh],[tip.px-h-2-lw,tip.py-h-6-lh],[tip.px+h+2-lw,tip.py+h+4],[tip.px-h-2-lw,tip.py+h+4],[tip.px-h-6-lw,tip.py-lh/2],[tip.px+h+6,tip.py-lh/2]];
var pick=null,ci;for(ci=0;ci<cands.length&&!pick;ci++){var cx=cands[ci][0],cy=cands[ci][1];if(cx<G+2||cx+lw>W-2||cy<2||cy+lh>BH-2)continue;var clash=false;
for(var bi=0;bi<n&&!clash;bi++){var ob=blocks[bi];if(ob===tip||!ob.on)continue;if(cx-pad<ob.px+h&&cx+lw+pad>ob.px-h&&cy-pad<ob.py+h&&cy+lh+pad>ob.py-h)clash=true;}
if(!clash)pick=cands[ci];}
if(!pick)pick=cands[0];x.fillStyle='rgba(244,241,236,0.92)';x.textAlign='left';x.textBaseline='top';x.fillText(lt,Math.max(G+2,Math.min(W-2-lw,pick[0])),Math.max(2,pick[1]));x.textBaseline='alphabetic';}
x.restore();
// ---- FINALITY bar: regions along the same axis. Paused finality shows only the pause (R4.6.3: never 'final' while paused)
var y0=BH,yb=y0+3,hb2=FIN_H-6;
x.strokeStyle='rgba(42,42,48,0.9)';x.lineWidth=1;x.beginPath();x.moveTo(G,y0+0.5);x.lineTo(W,y0+0.5);x.moveTo(G,y0+FIN_H+0.5);x.lineTo(W,y0+FIN_H+0.5);x.stroke();
x.save();x.beginPath();x.rect(G,y0+1,W-G,FIN_H-1);x.clip();
x.font='500 10px IBM Plex Mono, monospace';x.textAlign='left';x.textBaseline='middle';
var ftxt=null,fcol='rgba(154,154,158,0.9)';
if(!fin||!fin.supported){ftxt='no finality data';}
else if(!fin.active){var wts=fin.weights,silent=wts&&wts.total_weight?Math.max(0,Math.round(100*(1-(wts.active_weight||0)/wts.total_weight))):null;
x.fillStyle='rgba(154,154,158,0.10)';x.fillRect(G,yb,W-G,hb2);ftxt='finality paused: '+(silent?silent+'% of weight silent':'not enough weight signing yet');fcol='#FFB35C';}
else{
// final: everything up to the newest locked checkpoint on screen
if(fx>G){x.fillStyle='rgba(242,84,27,0.22)';x.fillRect(G,yb,fx-G,hb2);}
// proposed: from the newest lock to the newest proposed checkpoint on screen, faint; every checkpoint a tick
var cps=fin.checkpoints||[],px2=-1,ci2,cb;
for(ci2=0;ci2<cps.length;ci2++){cb=byHash[cps[ci2].hash];if(!cb||!cb.on)continue;if(cps[ci2].state!=='locked'&&cb.px>px2)px2=cb.px;}
if(px2>Math.max(G,fx)){x.fillStyle='rgba(154,154,158,0.10)';x.fillRect(Math.max(G,fx),yb,px2-Math.max(G,fx),hb2);}
for(ci2=0;ci2<cps.length;ci2++){cb=byHash[cps[ci2].hash];if(!cb||!cb.on)continue;var lkd=cps[ci2].state==='locked';
x.fillStyle=lkd?'#FFB35C':'rgba(154,154,158,0.55)';x.fillRect(Math.round(cb.px)-(lkd?1:0.5),yb,lkd?2:1,hb2);}
if(fin.latest_locked_index){ftxt='locked #'+fin.latest_locked_index+(lockAge(fin)?', '+lockAge(fin):'');fcol='#FFB35C';}
else ftxt='no lock yet';
// the label sits beside the newest lock while that block is on screen, else at the left edge of the bar
if(ftxt&&lk&&fx>G){var tw0=x.measureText(ftxt).width,lx0=fx+8+tw0<W-4?fx+8:fx-8-tw0;x.fillStyle='rgba(12,12,14,0.82)';x.fillRect(lx0-3,yb,tw0+6,hb2);x.fillStyle=fcol;x.fillText(ftxt,lx0,y0+FIN_H/2);ftxt=null;}
}
if(ftxt){x.fillStyle=fcol;x.fillText(ftxt,G+8,y0+FIN_H/2);}
x.textBaseline='alphabetic';x.restore();
// ---- PROVING strip: one cell per shard under its chain block, and where proofs land behind the tip
var y1=BH+FIN_H;
x.save();x.beginPath();x.rect(G,y1+1,W-G,H-y1-1);x.clip();
x.font='500 10px IBM Plex Mono, monospace';x.textAlign='left';x.textBaseline='middle';
if(!provOn()){var ptxt=!prov||!prov.supported?'Proving layer: '+(prov&&prov.reason?prov.reason:'node without proving'):'Proving layer: not yet activated on this devnet; activation '+(prov.activation_daa!==null&&prov.activation_daa!==undefined?'at DAA '+prov.activation_daa.toLocaleString('en-GB')+(prov.tip_daa?' (tip '+prov.tip_daa.toLocaleString('en-GB')+')':''):'not set');
if(narrow)ptxt=ptxt.replace('Proving layer: ','').replace(' on this devnet','').replace(/ \(tip [^)]*\)/,'');
x.fillStyle='rgba(154,154,158,0.9)';
// a line wider than the strip breaks at its semicolon into two lines, so the phone never clips the activation height
if(x.measureText(ptxt).width>W-G-16&&ptxt.indexOf('; ')>0){var parts=ptxt.split('; ');x.fillText(parts[0]+';',G+8,y1+PROV_H/2-7);x.fillText(parts.slice(1).join('; '),G+8,y1+PROV_H/2+7);}
else x.fillText(ptxt,G+8,y1+PROV_H/2);}
else{
// the lag line: proofs land about median_proof_lag_s behind the tip
var lag=prov.median_proof_lag_s;
if(lag!==null&&lag!==undefined){var lx=Math.round(xr-lag*pxPerSec)+0.5,lagTxt=lag<1?'under 1 s':lag<120?Math.round(lag)+' s':Math.floor(lag/60)+' min '+Math.round(lag%60)+' s';
if(lx>G){x.strokeStyle='rgba(255,179,92,0.7)';x.lineWidth=1;x.setLineDash([3,4]);x.beginPath();x.moveTo(lx,y1+3);x.lineTo(lx,H-3);x.stroke();x.setLineDash([]);
var ltx=narrow?'lag '+lagTxt:'proofs land '+lagTxt+' behind the tip',ltw=x.measureText(ltx).width;x.fillStyle='#FFB35C';x.textAlign=lx+6+ltw<W-4?'left':'right';x.fillText(ltx,lx+(x.textAlign==='left'?6:-6),y1+9);x.textAlign='left';}
else{x.fillStyle='#FFB35C';x.fillText(narrow?'lag '+lagTxt+', off screen':'proofs land '+lagTxt+' behind the tip, left of this window',G+8,y1+9);}}
for(i=0;i<n;i++){b=blocks[i];if(!b.on||!b.chain||!b.shards.length)continue;var sh=b.shards,ng=cellGeom(sh.length);
for(var si=0;si<sh.length;si++){var sc=sh[si],cx0=Math.round(b.px-ng.cw/2),cy0=ng.top+si*(ng.ch+ng.gap),st=sc.state,pc=sc.prover?colour(sc.prover):'#9A9A9E';
if(st==='verified'||st==='paid'){x.fillStyle=pc;rrect(cx0,cy0,ng.cw,ng.ch,2);x.fill();
if(st==='paid'){x.strokeStyle='#0C0C0E';x.lineWidth=1.5;x.beginPath();x.moveTo(cx0+ng.cw*0.25,cy0+ng.ch*0.55);x.lineTo(cx0+ng.cw*0.45,cy0+ng.ch*0.8);x.lineTo(cx0+ng.cw*0.78,cy0+ng.ch*0.25);x.stroke();}}
else if(st==='proving'){x.fillStyle='rgba(255,179,92,0.25)';rrect(cx0,cy0,ng.cw,ng.ch,2);x.fill();x.strokeStyle='rgba(255,179,92,0.9)';x.lineWidth=1;rrect(cx0+0.5,cy0+0.5,ng.cw-1,ng.ch-1,2);x.stroke();}
else{x.strokeStyle='rgba(154,154,158,0.55)';x.lineWidth=1;rrect(cx0+0.5,cy0+0.5,ng.cw-1,ng.ch-1,2);x.stroke();}
if(pb===b&&pv.i===si){x.save();x.shadowColor='rgba(255,179,92,0.8)';x.shadowBlur=6;x.strokeStyle='#FFB35C';x.lineWidth=1.5;rrect(cx0-2.5,cy0-2.5,ng.cw+5,ng.ch+5,3);x.stroke();x.restore();}
else if(hb===b&&hv.i===si){x.strokeStyle='#F4F1EC';x.lineWidth=1;rrect(cx0-1.5,cy0-1.5,ng.cw+3,ng.ch+3,3);x.stroke();}}}
}
x.textBaseline='alphabetic';x.restore();
// the detail panel on the canvas, no DOM: a block, or one shard cell. A hovered target that is not the pinned one gets
// the floating panel beside it, as before. Otherwise the pinned target (or the one fading out) gets the fixed panel at
// the top left of the strip, where the oldest blocks are: it never moves as new blocks arrive.
x.save();x.beginPath();x.rect(G,0,W-G,H);x.clip();x.font='500 10px IBM Plex Mono, monospace';x.textAlign='left';
closeRect=null;panelRect=null;
var show=hv&&!same(hv,pv)?hv:(pv||pinFade),fixed=show&&show!==hv;
if(show){var L=lines(show),tw=Math.max(x.measureText(L[0]).width,x.measureText(L[1]).width,x.measureText(L[2]).width)+20,th,tx,tty;
if(!fixed){th=52;tx=show.b.px+h+10;tty=L[3]-th/2;
if(tx+tw>W-4)tx=show.b.px-h-10-tw;if(tx<G+4)tx=G+4;tty=Math.max(4,Math.min(H-th-4,tty));
x.fillStyle='rgba(22,22,26,0.96)';rrect(tx,tty,tw,th,6);x.fill();x.strokeStyle=L[4];x.lineWidth=1;rrect(tx+0.5,tty+0.5,tw-1,th-1,6);x.stroke();
x.fillStyle='#F4F1EC';x.fillText(L[0],tx+10,tty+16);x.fillStyle='#C9C7C2';x.fillText(L[1],tx+10,tty+31);x.fillStyle='#9A9A9E';x.fillText(L[2],tx+10,tty+46);}
else{th=72;tw=Math.max(tw,112);tx=G+6;tty=6;var CL=32,fade=show===pinFade?Math.max(0,1-(now-pinFade.at)/PIN_FADE):1;
x.globalAlpha=fade;
x.fillStyle='rgba(22,22,26,0.96)';rrect(tx,tty,tw,th,6);x.fill();x.strokeStyle=L[4];x.lineWidth=1;rrect(tx+0.5,tty+0.5,tw-1,th-1,6);x.stroke();
// header: a small pinned label and a 32 px close control for a thumb
x.font='500 9px IBM Plex Mono, monospace';x.fillStyle='#FFB35C';x.fillText('PINNED',tx+10,tty+14);
var cxm=tx+tw-CL/2,cym=tty+CL/2;x.strokeStyle='rgba(154,154,158,0.55)';x.lineWidth=1;x.beginPath();x.arc(cxm,cym,8.5,0,Math.PI*2);x.stroke();
x.strokeStyle='#F4F1EC';x.lineWidth=1.5;x.beginPath();x.moveTo(cxm-3.5,cym-3.5);x.lineTo(cxm+3.5,cym+3.5);x.moveTo(cxm+3.5,cym-3.5);x.lineTo(cxm-3.5,cym+3.5);x.stroke();
x.font='500 10px IBM Plex Mono, monospace';x.fillStyle='#F4F1EC';x.fillText(L[0],tx+10,tty+36);x.fillStyle='#C9C7C2';x.fillText(L[1],tx+10,tty+51);x.fillStyle='#9A9A9E';x.fillText(L[2],tx+10,tty+66);
x.globalAlpha=1;
if(show===pv){closeRect=[tx+tw-CL,tty,CL,CL];panelRect=[tx,tty,tw,th];}}}
x.restore();
// pointer devices: a hand over a block, a cell or the close control
var cur=hv||(ptrX>=0&&inRect(closeRect,ptrX,ptrY))?'pointer':'';if(c.style.cursor!==cur)c.style.cursor=cur;
// gutter: lane labels in the miner's colour, strip names, over anything that scrolled under it
x.fillStyle='#0C0C0E';x.fillRect(0,0,G,H);x.strokeStyle='rgba(42,42,48,0.9)';x.lineWidth=1;x.beginPath();x.moveTo(G-0.5,0);x.lineTo(G-0.5,H);x.stroke();
x.textAlign='left';x.font='500 '+(narrow?10:11)+'px IBM Plex Mono, monospace';
for(i=0;i<laneN;i++){var ly2=laneY(i)+4,lm=lanes[i];
if(lm){x.fillStyle=lm.col;x.fillText(narrow?lm.tag:lm.id,8,ly2);}
else{x.fillStyle='rgba(154,154,158,0.9)';x.fillText(narrow?'+'+othersN:'+'+othersN+' more',8,ly2);}}
x.font='500 '+(narrow?9:10)+'px IBM Plex Mono, monospace';x.fillStyle='rgba(154,154,158,0.9)';x.textBaseline='middle';
x.fillText(narrow?'fin':'finality',8,BH+FIN_H/2);x.fillText(narrow?'prf':'proving',8,BH+FIN_H+(provOn()?12:PROV_H/2));x.textBaseline='alphabetic';
if(lagNote){x.font='500 10px IBM Plex Mono, monospace';x.textAlign='right';x.fillStyle='#FFB35C';x.fillText(lagNote,W-6,12);}
if(!n){x.fillStyle='rgba(154,154,158,0.9)';x.font='500 13px IBM Plex Mono, monospace';x.textAlign='center';x.fillText(lagNote?'no block has reached the observer for '+lagNote.replace('observer ','').replace(' behind',''):(live?'waiting for the first block':'no observer data'),G+(W-G)/2,BH/2);}
if(note){x.fillStyle='rgba(12,12,14,0.72)';x.fillRect(0,BH/2-22,W,44);x.fillStyle='#F4F1EC';x.font='500 13px IBM Plex Mono, monospace';x.textAlign='center';x.fillText(note,W/2,BH/2+5);}
}
// Idle, the scene moves at 14 to 40 px/s, so 30 frames a second is enough; a pointer or a pinned block gets every frame.
// ?perf=1 exposes window.__igneumPerf = {frames, drawMs, since} so the draw cost can be read from the console.
var PERF=/[?&]perf=1/.test(location.search)?(window.__igneumPerf={frames:0,drawMs:0,since:performance.now()}):null,lastDraw=0;
function frame(now){if(!running)return;
if(ptrX>=0||pin||pinFade||now-lastDraw>=31){lastDraw=now;var t0=PERF?performance.now():0;draw(now);if(PERF){PERF.frames++;PERF.drawMs+=performance.now()-t0;}}
requestAnimationFrame(frame);}
function start(){if(running||reduce)return;if(document.hidden||!inView)return;running=true;requestAnimationFrame(frame);}
function stop(){running=false;}
function kick(){if(!running)requestAnimationFrame(function(now){draw(now);});}
document.addEventListener('visibilitychange',function(){if(document.hidden)stop();else start();});
if('IntersectionObserver' in window){new IntersectionObserver(function(es){inView=es[0].isIntersecting;if(inView)start();else stop();},{threshold:0}).observe(c);}
if('ResizeObserver' in window){new ResizeObserver(function(){needSize=true;kick();}).observe(c);}
else window.addEventListener('resize',function(){needSize=true;kick();});
// hover: pointer devices only, never a touch; it reads the strip and leaves the pin alone
c.addEventListener('pointermove',function(e){if(e.pointerType==='touch')return;ptrX=e.offsetX;ptrY=e.offsetY;if(reduce)kick();});
c.addEventListener('pointerleave',function(){ptrX=-1;ptrY=-1;if(reduce)kick();});
// tap or click: one pointer, down to up, under 10 px of travel, so a scroll on a phone never pins. Pointer events only;
// the canvas ignores click, so a touch tap fires once (its synthetic click is dropped below)
c.addEventListener('pointerdown',function(e){if(e.button&&e.button!==0)return;down=down?null:{id:e.pointerId,x:e.offsetX,y:e.offsetY};});
c.addEventListener('pointerup',function(e){if(!down||down.id!==e.pointerId){down=null;return;}
var dx=e.offsetX-down.x,dy=e.offsetY-down.y;down=null;if(dx*dx+dy*dy>TAP_SLOP*TAP_SLOP)return;tap(e.offsetX,e.offsetY,e.pointerType==='touch');});
c.addEventListener('pointercancel',function(){down=null;});
// anywhere else on the page clears the pin; Escape too
document.addEventListener('click',function(e){if(e.target!==c)clearPin();});
document.addEventListener('keydown',function(e){if(e.key==='Escape')clearPin();});
start();
function lockAge(f){var c=f.checkpoints.filter(function(x){return x.state==='locked';}).slice(-1)[0];if(!c||!c.locked_at)return '';var t=new Date(c.locked_at.replace(' ','T')).getTime();return isNaN(t)?'':rel(Date.now()-t);}
// ---------- sparkline ----------
var sc=$('spark'),sx=sc.getContext('2d');
@ -644,7 +328,7 @@ main{padding-bottom:var(--sec)}
function setStatus(on,ageText){document.body.className=on?'on':'off';$('st-status').textContent=on?'LIVE':'OFFLINE';$('st-age').textContent=ageText;}
function render(d){
var s=d.state,on=!s.stale;
var lag=s.observer_lag_s;lagNote=on&&lag!==null&&lag>30?'observer '+(lag<120?Math.round(lag)+' s':Math.round(lag/60)+' min')+' behind':'';
var lag=s.observer_lag_s,lagNote=on&&lag!==null&&lag>30?'observer '+(lag<120?Math.round(lag)+' s':Math.round(lag/60)+' min')+' behind':'';
setStatus(on,s.age_s===null?'no observer update yet':(lagNote?lagNote+(s.queue_depth?', '+s.queue_depth+' queued':''):'observer updated '+rel(s.age_s*1000)));
$('st-network').textContent=s.network||'igneum-devnet';$('st-node').textContent=s.node_version?'node '+s.node_version:'node';
$('st-bps').textContent=(s.blocks_per_second_60s||0).toFixed(2);
@ -658,11 +342,9 @@ main{padding-bottom:var(--sec)}
if(d.events.length){eventsEl.innerHTML=d.events.map(function(e){return '<div><span><span class="k">'+esc(e.kind)+'</span> '+esc(e.text)+'</span><span class="t">'+rel(now-new Date(e.ts).getTime())+'</span></div>';}).join('');}
else eventsEl.innerHTML='<div class="empty">No events yet.</div>';
spark(s.blocks_per_minute);
live=on;
if(on){frozenAt=null;note='';ingest(d);}
else{if(frozenAt===null)frozenAt=vnow()||now;note='observer offline, last update '+(s.age_s===null?'unknown':rel(s.age_s*1000));}
lastD=d;sceneCounters(d);if(dag)dag.push(d);
}
function poll(){fetch('/api/live'+(fixedWindow?'?window='+fixedWindow:''),{cache:'no-store'}).then(function(r){if(!r.ok)throw new Error(r.status);return r.json();}).then(function(d){if(!d.ok)throw new Error(d.error);failures=0;render(d);}).catch(function(){failures++;if(failures>=3){setStatus(false,'api unreachable');if(frozenAt===null)frozenAt=vnow();note='api unreachable, scene frozen';}});}
function poll(){fetch('/api/live'+(fixedWindow?'?window='+fixedWindow:''),{cache:'no-store'}).then(function(r){if(!r.ok)throw new Error(r.status);return r.json();}).then(function(d){if(!d.ok)throw new Error(d.error);failures=0;render(d);}).catch(function(){failures++;if(failures>=3){setStatus(false,'live feed unavailable');if(dag)dag.push(null);}});}
poll();setInterval(poll,2000);
})();
</script>

View file

@ -5,13 +5,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Add Igneum to MetaMask</title>
<meta name="description" content="Add the Igneum network to MetaMask or any Ethereum wallet in one click: chain id, RPC URL, the IGN symbol and 18 decimals. Devnet today, public testnet next.">
<link rel="canonical" href="https://igneum.network/wallet">
<link rel="canonical" href="https://igneum.network/metamask">
<meta name="theme-color" content="#0C0C0E">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Igneum">
<meta property="og:title" content="Add Igneum to MetaMask">
<meta property="og:description" content="Add the Igneum network to MetaMask or any Ethereum wallet in one click: chain id, RPC URL, the IGN symbol and 18 decimals. Devnet today, public testnet next.">
<meta property="og:url" content="https://igneum.network/wallet">
<meta property="og:url" content="https://igneum.network/metamask">
<meta property="og:image" content="https://igneum.network/og.png?v=3">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
@ -31,9 +31,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -45,87 +46,30 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
/* page block; tokens, type and components are in /site.css */
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:16px;line-height:1.6;padding:0;-webkit-font-smoothing:antialiased}
a{color:var(--ember);text-decoration:none}a:hover{text-decoration:underline}
[id]{scroll-margin-top:calc(var(--nav-h) + 16px)}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
.head{padding-top:var(--head)}
h1{font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;margin:12px 0 12px}
h2{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);line-height:1.3;margin:44px 0 12px;padding-top:24px;border-top:1px solid var(--line)}
.note{color:var(--ash);font-size:15px;max-width:72ch;margin:0 0 24px}
.note b{color:var(--bone);font-weight:500}
.grid{display:grid;gap:var(--gap);grid-template-columns:repeat(auto-fit,minmax(min(100%,320px),1fr));margin:8px 0 8px}
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0}
.card h3{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);margin:0 0 6px}
.card .tag{display:inline-block;font-family:var(--f-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--molten);border:1px solid var(--line);border-radius:999px;padding:3px 9px;margin-bottom:12px}
.card p{margin:0 0 12px;color:var(--ink-2);font-size:15px}
dl{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:6px 16px;margin:0 0 16px;font-size:14px}
dt{font-family:var(--f-mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--ash);padding-top:3px}
dd{margin:0;font-family:var(--f-mono);font-size:14px;color:var(--bone);overflow-wrap:anywhere}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--ember);color:var(--obsidian);background:var(--ember);cursor:pointer;font-family:inherit}
.btn:hover{background:var(--molten);border-color:var(--molten);text-decoration:none}
.btn[disabled]{opacity:.5;cursor:not-allowed}
.btn.ghost{background:transparent;color:var(--bone);border-color:var(--line)}
.status{font-family:var(--f-mono);font-size:13px;color:var(--ash);margin:10px 0 0;min-height:1.4em}
ol{margin:0 0 14px;padding-left:22px}li{margin-bottom:6px}
code{font-family:var(--f-mono);font-size:.92em;background:var(--graphite);padding:1px 5px;border-radius:4px;overflow-wrap:anywhere}
pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:var(--card-r);font-family:var(--f-mono);font-size:13px;line-height:1.55;overflow-x:auto}
.asof{color:var(--ash);font-size:13px;margin:40px 0 0;max-width:72ch}
</style>
</head>
@ -135,10 +79,9 @@ pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:v
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -147,20 +90,32 @@ pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:v
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -197,7 +152,7 @@ pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:v
<dt>RPC URL</dt><dd>http://127.0.0.1:26790</dd>
<dt>Symbol</dt><dd>IGN</dd>
<dt>Decimals</dt><dd>18</dd>
<dt>Explorer</dt><dd>none yet</dd>
<dt>Explorer</dt><dd><a href="/explorer">igneum.network/explorer</a></dd>
</dl>
<button class="btn" type="button" data-add="devnet">Add Igneum Devnet to MetaMask</button>
<p class="status" data-status="devnet"></p>
@ -228,7 +183,7 @@ pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:v
<p class="note">Igneum signs transactions with the Ethereum rules (EIP-155, EIP-1559 and legacy envelopes). A transaction signed for another chain id is refused. Gas has two dimensions on Igneum, execution and proving, and the node folds the second into the price it quotes, so <code>eth_gasPrice</code> and <code>eth_estimateGas</code> work as they do on Ethereum. The <a href="/litepaper#builders-ask">litepaper</a> has the differences.</p>
<h2 id="app-wallet">The app and the Igneum Wallet</h2>
<p class="note">The Igneum Miner app makes an address for your earnings and shows you its seed phrase once. That address is an ordinary Ethereum account: import the seed into MetaMask and the balance is there. An Igneum Wallet with the Apps tab and the explorer built in is in the roadmap; until it ships, MetaMask is the wallet.</p>
<p class="note">The Igneum Ember app (still labelled Igneum Miner in its window) makes an address for your earnings and shows you its seed phrase once. That address is an ordinary Ethereum account: import the seed into MetaMask and the balance is there. <a href="/wallet">Igneum Wallet</a> 0.1.4 is out for macOS and verifies finality itself; its Apps tab is in the roadmap. MetaMask works today and is the way in on Windows and Linux.</p>
<p class="asof">Testnet coin for testing: <a href="/faucet">the faucet</a> sends 10 IGN per address per day. Chain ids 4461 (mainnet), 4462 (testnet) and 4463 (devnet) are fixed in the node. The testnet RPC URL above is a placeholder until the testnet opens; this page is updated the day it does.</p>
</main>
@ -237,7 +192,7 @@ pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:v
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -263,9 +218,9 @@ pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:v
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -273,10 +228,23 @@ pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:v
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script>
(function(){

View file

@ -96,32 +96,16 @@ a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visi
</style>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
/* page block; tokens, type and components are in /site.css */
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-hero:clamp(32px,5.6vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
:root{--sm:84px}@media (max-width:900px){:root{--sm:80px}}
section,[id]{scroll-margin-top:var(--sm)}section[id]{scroll-margin-top:calc(var(--sm) - var(--sec))}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}a:hover{color:var(--molten)}
img,svg{max-width:100%}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.mono{font-family:var(--f-mono)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
.eyebrow.ember{color:var(--ember)}
h1,h2,h3{font-family:var(--f-display);margin:0;line-height:1.08;text-wrap:balance}
h1{font-weight:900;font-size:var(--fs-hero);letter-spacing:-.01em}
h2{font-weight:700;font-size:var(--fs-h2)}
h3{font-weight:700;font-size:var(--fs-h3)}
p{margin:0}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer;transition:transform .15s ease,background .15s ease,border-color .15s ease}
.btn:hover{transform:translateY(-1px);border-color:var(--ash);color:var(--bone)}
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0}
.grid{display:grid;gap:var(--gap);grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr))}
.reveal{opacity:0;transform:translateY(18px);transition:opacity .7s ease,transform .7s ease}
.reveal.in{opacity:1;transform:none}
@ -133,7 +117,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.pt{font-size:15px;color:var(--ash);max-width:64ch}
.lp{display:inline-block;margin-top:24px;color:var(--ember);font-weight:600;font-size:15px}
.cta{display:flex;flex-wrap:wrap;gap:12px}
/* hero: copy left, the dashboard in its frame right; one column under 980 px */
.hero{position:relative;padding-block:clamp(48px,8vw,96px) clamp(40px,6vw,72px);overflow:hidden}
.hero::before{content:'';position:absolute;inset:auto -10% -30% 40%;height:70%;background:radial-gradient(closest-side,rgba(242,84,27,.18),rgba(242,84,27,0));pointer-events:none}
@ -143,7 +126,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.hero p.lead{font-size:clamp(17px,2vw,20px);color:var(--ink-2);max-width:52ch}
.hero .os{display:flex;flex-wrap:wrap;gap:8px 18px;font-size:14px;color:var(--ash)}
.hero .os span{display:inline-flex;align-items:center;gap:6px}
/* the device frame: a window bar, the screenshot, an ember glow behind */
.shot{position:relative;min-width:0}
.shot::before{content:'';position:absolute;inset:12% -6% -8% -6%;background:radial-gradient(closest-side,rgba(242,84,27,.22),rgba(242,84,27,0));filter:blur(12px);pointer-events:none}
@ -165,7 +147,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.frame.light figcaption{background:#0C0C0E}
.row-tbl{margin-top:28px}
.row-tbl td:first-child{white-space:nowrap;font-weight:600}
/* the three steps */
.steps{display:grid;gap:var(--gap-tile);grid-template-columns:repeat(3,minmax(0,1fr));position:relative}
.step{background:var(--graphite);border:1px solid var(--line);border-radius:var(--tile-r);padding:var(--tile-pad);display:flex;flex-direction:column;gap:10px;position:relative;min-width:0}
@ -176,7 +157,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.step::after{content:'';position:absolute;right:-9px;top:50%;width:14px;height:14px;border-top:2px solid var(--line-2);border-right:2px solid var(--line-2);transform:translateY(-50%) rotate(45deg);z-index:1}
.step:last-child::after{display:none}
@media (max-width:640px){.steps{grid-template-columns:1fr}.step::after{right:auto;left:50%;top:auto;bottom:-9px;transform:translateX(-50%) rotate(135deg)}}
/* feature groups: a heading row, then tiles with one line each */
.group{display:grid;gap:var(--gap);grid-template-columns:1fr;margin-bottom:var(--sec)}
@media (min-width:900px){.group{grid-template-columns:300px minmax(0,1fr);gap:48px}}
@ -190,17 +170,10 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.feat p{font-size:14px;color:var(--ash);line-height:1.45}
.feat .src{font-family:var(--f-mono);font-size:11px;color:var(--ember);margin-top:4px}
.feat.wide{grid-column:1/-1}
/* the swap table and the measured table */
.tbl{overflow-x:auto;border:1px solid var(--line);border-radius:var(--card-r);background:var(--graphite);-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:560px;font-size:15px}
th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500;background:var(--obsidian)}
tr:last-child td{border-bottom:0}
td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
td a{color:var(--ember)}
.levers table{min-width:760px}.levers td:first-child{min-width:210px}.levers td:nth-child(2){min-width:260px}
/* the fee card: the switch as the app draws it, the start lines as the miner prints them */
.fee{display:grid;gap:var(--gap);grid-template-columns:1fr}
@media (min-width:900px){.fee{grid-template-columns:1.1fr .9fr;align-items:start}}
@ -208,16 +181,13 @@ td a{color:var(--ember)}
.switch .track{width:44px;height:26px;border-radius:13px;background:var(--ember);position:relative;flex:0 0 44px}
.switch .track::after{content:'';position:absolute;top:3px;right:3px;width:20px;height:20px;border-radius:50%;background:var(--bone)}
.switch.off .track{background:var(--line-2)}.switch.off .track::after{right:auto;left:3px}
pre{margin:0;padding:14px 16px;background:var(--obsidian);border:1px solid var(--line);border-radius:12px;font-family:var(--f-mono);font-size:12.5px;line-height:1.55;color:var(--ink-2);white-space:pre-wrap;overflow-wrap:anywhere}
pre b{color:var(--molten);font-weight:500}
.band{background:var(--ember);color:var(--ember-ink)}
.band .wrap{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:24px;padding-block:56px}
.band h2{font-weight:900;font-size:clamp(26px,3.5vw,36px)}
.band p{font-size:16px;max-width:56ch}
.btn.dark{background:var(--obsidian);color:var(--bone);border-color:var(--obsidian)}
.btn.dark:hover{background:#222226;color:var(--bone)}
.foot{margin-top:0}
</style>
</head>
<body>

View file

@ -96,19 +96,12 @@ a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visi
</style>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;
/* one system for every page: container, gutter, section rhythm, card and tile boxes, heading scale (same tokens as the homepage) */
/* page block; tokens, type and components are in /site.css */
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:16px;line-height:1.6;padding:0;-webkit-font-smoothing:antialiased}
a{color:var(--ember);text-decoration:none}a:hover{text-decoration:underline}
[id]{scroll-margin-top:calc(var(--nav-h) + 16px)}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
h1{font-family:var(--f-display);font-weight:900;font-size:var(--fs-h1);line-height:1.05;margin:12px 0 12px}
.head{padding-top:var(--head)}
.note{color:var(--ash);font-size:15px;max-width:72ch;margin:0 0 32px}
.layout{display:grid;grid-template-columns:minmax(0,1fr);gap:32px}
@media(min-width:960px){.layout{grid-template-columns:260px minmax(0,1fr);gap:56px}}
.toc{align-self:start;font-size:14px;border:1px solid var(--line);border-radius:var(--tile-r);background:var(--graphite);padding:8px}
@ -126,12 +119,7 @@ article h3{font-family:var(--f-display);font-weight:500;font-size:16px;margin:26
article h4{font-size:15px;margin:20px 0 6px;color:var(--molten)}
article p{margin:0 0 14px}article ul,article ol{margin:0 0 14px;padding-left:22px}article li{margin-bottom:6px}
article p,article li,article h2,article h3{overflow-wrap:anywhere}
code{font-family:var(--f-mono);font-size:.92em;background:var(--graphite);padding:1px 5px;border-radius:4px;overflow-wrap:anywhere}
pre{margin:0 0 16px;padding:16px 18px;background:var(--graphite);border-radius:var(--card-r);font-family:var(--f-mono);font-size:13px;line-height:1.55;overflow-x:auto;-webkit-overflow-scrolling:touch}
pre code{background:none;padding:0;font-size:inherit}
.tbl{overflow-x:auto;margin:18px 0 24px;border:1px solid var(--line);border-radius:var(--card-r);background:var(--graphite);-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:560px;font-size:15px}th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500;background:var(--obsidian)}tr:last-child td{border-bottom:0}
.gen{color:var(--ash);font-size:13px;margin:40px 0 0;max-width:72ch}
</style>
</head>

View file

@ -2,7 +2,7 @@
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -28,9 +28,9 @@
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -38,7 +38,20 @@
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>

View file

@ -1,8 +1,9 @@
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -14,52 +15,8 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>

View file

@ -2,10 +2,9 @@
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -14,19 +13,31 @@
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>

225
site/site.css Normal file
View file

@ -0,0 +1,225 @@
/* Igneum site: one design system for every page (docs/plans/site-ui-3.md, 6 October 2026).
Tokens, type, the shared components (nav, footer, card, tile, table, button, pill, note, callout, code) and the
state words. Each page keeps a short block of its own after this file. Fonts are declared in partials/head.html. */
/* ---------- 1. tokens ---------- */
:root{
--obsidian:#0C0C0E;--graphite:#16161A;--row:#111114;--line:#2A2A30;--line-2:#3A3A42;
--ember:#F2541B;--ember-hi:#FF6A2B;--ember-text:#F2541B;--molten:#FFB35C;--molten-text:#FFB35C;
--bone:#F4F1EC;--ink-2:#C9C7C2;--ash:#9A9A9E;--ember-ink:#0C0C0E;--ok:#FFB35C;
--scrim:rgba(12,12,14,.72);--top-bg:rgba(12,12,14,.86);--shadow:rgba(0,0,0,.6);--hover:rgba(255,255,255,.04);
--ember-12:rgba(242,84,27,.12);--ember-40:rgba(242,84,27,.4);--molten-10:rgba(255,179,92,.1);
/* the chrome tokens older page blocks still read */
--ground:var(--obsidian);--surface:var(--graphite);--surface-2:var(--row);--ink:var(--bone);--quiet:var(--ash);
--ui-bg:var(--top-bg);--ui-menu:var(--obsidian);--ui-ink:var(--bone);--ui-ink-2:var(--ink-2);--ui-ash:var(--ash);--ui-line:var(--line);--ui-line-2:var(--line-2);
--ui-accent:var(--ember);--ui-accent-ink:var(--ember-ink);--ui-hot:var(--molten);--ui-hover:var(--ember-hi);--ui-tint:var(--ember-12);
--code:var(--row);--code-ink:var(--ink-2);
/* type */
--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;
--f-display:'Unbounded','Unbounded Fallback',sans-serif;
--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
--t-xs:11px;--t-sm:12px;--t-base:15px;--t-md:17px;--t-lg:19px;--t-h3:20px;
--t-h2:clamp(26px,3.2vw,36px);--t-h1:clamp(32px,5vw,56px);--t-hero:clamp(40px,7vw,76px);--t-num:clamp(26px,3vw,40px);
/* space, radius */
--s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-5:24px;--s-6:32px;
--gutter:32px;--sec:96px;--max:1120px;--nav-h:64px;
--r-card:16px;--r-row:12px;--r-btn:10px;--r-pill:999px;
color-scheme:dark;
}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){
--obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;
--ember:#D0420D;--ember-hi:#E04A14;--ember-text:#B8390C;--molten:#B8731F;--molten-text:#8F5810;
--bone:#16161A;--ink-2:#3C3C42;--ash:#6B6B70;--ember-ink:#FFFFFF;--ok:#B8731F;
--scrim:rgba(244,241,236,.72);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--hover:rgba(0,0,0,.04);
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);
color-scheme:light;
}}
:root[data-theme="light"]{
--obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;
--ember:#D0420D;--ember-hi:#E04A14;--ember-text:#B8390C;--molten:#B8731F;--molten-text:#8F5810;
--bone:#16161A;--ink-2:#3C3C42;--ash:#6B6B70;--ember-ink:#FFFFFF;--ok:#B8731F;
--scrim:rgba(244,241,236,.72);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--hover:rgba(0,0,0,.04);
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);
color-scheme:light;
}
@media (max-width:1000px){:root{--gutter:24px}}
@media (max-width:720px){:root{--gutter:16px;--sec:64px}}
/* ---------- 2. base ---------- */
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + 16px)}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;background:var(--obsidian);color:var(--bone);font:400 var(--t-md)/1.6 var(--f-sans);-webkit-font-smoothing:antialiased;font-variant-numeric:tabular-nums;overflow-x:hidden}
h1,h2,h3,h4{font-family:var(--f-display);color:var(--bone);margin:0;text-wrap:balance;letter-spacing:-.01em}
h1{font-size:var(--t-h1);font-weight:900;line-height:1.05}
h2{font-size:var(--t-h2);font-weight:700;line-height:1.1}
h3{font-size:var(--t-h3);font-weight:700;line-height:1.25}
p,li,dd,figcaption,td{text-wrap:pretty}
p{margin:0 0 1em}
a{color:var(--ember-text);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:3px}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible,input:focus-visible{outline:2px solid var(--ember);outline-offset:3px;border-radius:6px}
::selection{background:var(--ember-40)}
img,canvas,svg,video{max-width:100%;display:block}
code,kbd,.mono{font-family:var(--f-mono)}
hr{border:0;border-top:1px solid var(--line);margin:var(--s-6) 0}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ember);color:var(--ember-ink);padding:10px 14px;border-radius:var(--r-btn);font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
.wrap{max-width:var(--max);margin:0 auto;padding:0 var(--gutter)}
.sec{padding:var(--sec) 0}
.sec+.sec{padding-top:0}
.sec-head{max-width:68ch;margin-bottom:var(--s-6)}
.sec-head p{color:var(--ink-2);font-size:var(--t-lg);line-height:1.5;margin:var(--s-3) 0 0}
.lead{font-size:var(--t-lg);line-height:1.55;color:var(--ink-2);max-width:62ch}
.eyebrow{font:500 var(--t-sm)/1.4 var(--f-mono);letter-spacing:.16em;text-transform:uppercase;color:var(--ash);margin-bottom:var(--s-3)}
.eyebrow.ember{color:var(--ember-text)}
.measure{max-width:68ch}
.muted{color:var(--ash)}
.right{text-align:right}
.visually-hidden{position:absolute!important;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
/* ---------- 3. nav ---------- */
.nav{position:sticky;top:0;z-index:40;background:var(--top-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.nav .wrap{display:flex;align-items:center;gap:var(--s-5);height:var(--nav-h)}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--bone);text-decoration:none}
.brand:hover{text-decoration:none}
.brand .mark{width:28px;height:28px;display:grid;place-items:center;border-radius:8px;background:var(--graphite);border:1px solid var(--line)}
.brand .word{font:900 20px/1 var(--f-display);letter-spacing:.06em}
.nav .links{display:flex;align-items:center;gap:var(--s-4);margin-left:auto}
.nav .links a{color:var(--ink-2);font-size:var(--t-base);font-weight:500;padding:8px 2px;border-bottom:2px solid transparent}
.nav .links a:hover{color:var(--bone);text-decoration:none}
.nav .links a[aria-current="page"]{color:var(--bone);border-bottom-color:var(--ember)}
.nav .cta{margin-left:var(--s-3)}
.burger{display:none;margin-left:auto;width:44px;height:44px;border-radius:var(--r-btn);border:1px solid var(--line);background:var(--graphite);color:var(--bone);cursor:pointer;align-items:center;justify-content:center}
.burger .bars{display:block;width:18px;height:2px;background:currentColor;position:relative}
.burger .bars::before,.burger .bars::after{content:"";position:absolute;left:0;width:18px;height:2px;background:currentColor}
.burger .bars::before{top:-6px}.burger .bars::after{top:6px}
.nav-sheet{display:none}
@media (max-width:900px){
.nav .links{display:none}
.nav .cta{display:none}
.burger{display:inline-flex}
.nav.open .nav-sheet{display:block;position:fixed;inset:var(--nav-h) 0 0 0;background:var(--obsidian);z-index:39;overflow:auto;padding:var(--s-5) var(--gutter)}
.nav-sheet a{display:block;padding:14px 0;border-bottom:1px solid var(--line);color:var(--bone);font-size:var(--t-lg);font-weight:500}
.nav-sheet a[aria-current="page"]{color:var(--ember-text)}
.nav-sheet .btn{margin-top:var(--s-5);width:100%}
}
/* ---------- 4. footer ---------- */
.foot{border-top:1px solid var(--line);padding:var(--s-6) 0 var(--s-5);margin-top:var(--sec);color:var(--ash);font-size:var(--t-base)}
.foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:var(--s-6)}
.foot-brand .brand{margin-bottom:var(--s-3)}
.foot-brand p{color:var(--ash);margin:0}
.foot-col .eyebrow{margin-bottom:var(--s-3)}
.foot-col a{display:block;color:var(--ink-2);padding:5px 0}
.foot-col a:hover{color:var(--bone)}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:var(--s-3);border-top:1px solid var(--line);margin-top:var(--s-6);padding-top:var(--s-4);font-size:var(--t-sm)}
.foot-imprint{max-width:60ch;line-height:1.5}
.theme{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-pill);overflow:hidden}
.theme button{background:transparent;border:0;color:var(--ash);font:500 var(--t-sm)/1 var(--f-mono);padding:8px 12px;cursor:pointer}
.theme button[aria-pressed="true"]{background:var(--graphite);color:var(--bone)}
@media (max-width:900px){.foot-grid{grid-template-columns:1fr 1fr}}
@media (max-width:500px){.foot-grid{grid-template-columns:1fr}}
/* ---------- 5. components ---------- */
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--r-card);padding:var(--s-5)}
.card h3{margin-bottom:var(--s-2)}
.card p:last-child{margin-bottom:0}
.grid{display:grid;gap:var(--s-4)}
.grid.c2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid.c3{grid-template-columns:repeat(3,minmax(0,1fr))}
.grid.c4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width:900px){.grid.c3,.grid.c4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.grid.c2,.grid.c3,.grid.c4{grid-template-columns:minmax(0,1fr)}}
/* a grid track never grows past the viewport for a long line inside it (the 390 px overflow class, 6 Oct 2026) */
.grid>*,.tiles>*,.card,.two>*,.split>*,.split2>*,.hero-grid>*,.download>*,.layout>*,.pillars>*,.phases>*,.group>*,.feats>*,.fee>*,.steps>*,.states>*,.ol>*,.cells>*,.strip>*{min-width:0}
pre{max-width:100%}
@media (max-width:720px){main table{display:block;overflow-x:auto;max-width:100%;-webkit-overflow-scrolling:touch}.tbl table{display:table}}
.tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-3)}
@media (max-width:700px){.tiles{grid-template-columns:repeat(2,minmax(0,1fr))}}
.tile{background:var(--row);border:1px solid var(--line);border-radius:var(--r-row);padding:var(--s-4) var(--s-4) var(--s-3);min-width:0}
.tile .v{font:700 var(--t-num)/1 var(--f-display);color:var(--bone);letter-spacing:-.02em;overflow-wrap:anywhere}
.tile .v.state{font:500 var(--t-base)/1.3 var(--f-mono);color:var(--ash);padding:6px 0}
.tile .k{font:400 var(--t-sm)/1.4 var(--f-mono);color:var(--ash);margin-top:var(--s-2)}
.tile.hot .v{color:var(--ember-text)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:10px 20px;border-radius:var(--r-btn);border:1px solid var(--line);background:transparent;color:var(--bone);font:600 var(--t-base)/1.2 var(--f-sans);text-decoration:none;cursor:pointer;white-space:nowrap}
.btn:hover{text-decoration:none;border-color:var(--line-2);background:var(--hover)}
.btn.primary{background:var(--ember);border-color:var(--ember);color:var(--ember-ink);font:700 15px/1.2 var(--f-display);letter-spacing:.01em}
.btn.primary:hover{background:var(--ember-hi);border-color:var(--ember-hi)}
.btn.small{min-height:36px;padding:6px 14px;font-size:14px}
.btn[aria-disabled="true"],.btn:disabled{opacity:.5;cursor:default}
@media (max-width:720px){.cta .btn{width:100%}}
.cta{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.pill{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:var(--r-pill);padding:5px 12px;font:500 var(--t-sm)/1.4 var(--f-mono);color:var(--ink-2);background:var(--row)}
.dot{width:8px;height:8px;border-radius:50%;background:var(--molten);display:inline-block;flex:none}
.dot.live{animation:pulse 2s ease-in-out infinite}
.dot.off{background:var(--ash);animation:none}
.dot.bad{background:var(--ember);animation:none}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.dot.live{animation:none}}
.note{color:var(--ash);font-size:var(--t-base);line-height:1.55;max-width:80ch;margin:var(--s-4) 0 0}
.callout{border-left:2px solid var(--ember);background:var(--row);border-radius:0 var(--r-row) var(--r-row) 0;padding:var(--s-4) var(--s-5);margin:var(--s-5) 0;color:var(--ink-2)}
.callout p:last-child{margin:0}
pre,.code{background:var(--row);border:1px solid var(--line);border-radius:var(--r-row);padding:var(--s-4);font:400 14px/1.6 var(--f-mono);color:var(--ink-2);overflow:auto;margin:0}
code{font-size:.92em;background:var(--row);border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:var(--ink-2)}
pre code{background:none;border:0;padding:0}
.tbl{overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid var(--line);border-radius:var(--r-row);background:var(--graphite);min-width:0;max-width:100%}
table{border-collapse:collapse;width:100%;font-size:var(--t-base);line-height:1.45}
th,td{text-align:left;padding:10px 14px;vertical-align:top}
th{font:500 var(--t-sm)/1.4 var(--f-mono);letter-spacing:.1em;text-transform:uppercase;color:var(--ash);border-bottom:1px solid var(--line);white-space:nowrap}
td{border-top:1px solid var(--line);color:var(--ink-2)}
tr:first-child td{border-top:0}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
td.id{font-family:var(--f-mono);font-size:14px;overflow-wrap:anywhere}
@media (max-width:720px){th,td{padding:8px 10px}}
.kv{display:grid;grid-template-columns:minmax(120px,30%) 1fr;gap:0 var(--s-4)}
.kv>div{padding:10px 0;border-top:1px solid var(--line);color:var(--ink-2)}
.kv>div:nth-child(-n+2){border-top:0}
.kv .k{font:500 var(--t-sm)/1.4 var(--f-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--ash);padding-top:12px}
details{border-top:1px solid var(--line)}
details:last-of-type{border-bottom:1px solid var(--line)}
summary{cursor:pointer;padding:14px 0;color:var(--bone);font-weight:500;list-style:none;display:flex;justify-content:space-between;gap:var(--s-3)}
summary::-webkit-details-marker{display:none}
summary::after{content:"+";font-family:var(--f-mono);color:var(--ash)}
details[open] summary::after{content:"\2212"}
details>:not(summary){padding-bottom:var(--s-4);color:var(--ink-2)}
input,select,textarea{font:400 var(--t-md)/1.4 var(--f-sans);color:var(--bone);background:var(--row);border:1px solid var(--line);border-radius:var(--r-btn);padding:10px 12px;width:100%}
input:focus,select:focus,textarea:focus{border-color:var(--line-2)}
.field{display:grid;gap:6px;margin-bottom:var(--s-4)}
.field label{font:500 var(--t-sm)/1.4 var(--f-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--ash)}
/* ---------- 6. states ---------- */
.state{display:inline-flex;align-items:center;gap:8px;font:500 var(--t-sm)/1.4 var(--f-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--ash)}
.state.live{color:var(--molten-text)}
.state.bad{color:var(--ember-text)}
.empty{color:var(--ash);font-size:var(--t-base);padding:var(--s-5) 0}
.empty.err{color:var(--ember-text)}
.spinner{width:24px;height:24px;border-radius:50%;border:2px solid var(--line);border-top-color:var(--ember);animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spinner{animation:none}}
/* ---------- 7. scenes ---------- */
.viz{padding:var(--s-4)}
.viz-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px var(--s-5);margin-bottom:var(--s-3)}
.viz-stats{display:flex;flex-wrap:wrap;gap:var(--s-3) var(--s-4);font:400 var(--t-sm)/1.4 var(--f-mono);color:var(--ash)}
.viz-stats b{color:var(--bone);font-weight:500}
.viz canvas{width:100%;height:340px;border-radius:var(--r-row);background:var(--row);border:1px solid var(--line)}
@media (max-width:600px){.viz canvas{height:240px}}
.legend{display:flex;flex-wrap:wrap;gap:var(--s-3) var(--s-4);margin-top:var(--s-4);font-size:14px;color:var(--ink-2)}
.legend[hidden]{display:none}
.legend span{display:inline-flex;align-items:center;gap:8px}
.sw{width:12px;height:12px;border-radius:3px;display:inline-block;flex:none}
/* ---------- 8. print ---------- */
@media print{
:root{--obsidian:#fff;--graphite:#fff;--row:#f3f3f3;--line:#bbb;--line-2:#999;--bone:#000;--ink-2:#111;--ash:#444;--ember:#B8390C;--ember-text:#B8390C;--molten-text:#8F5810}
.nav,.foot,.burger,.skip,.cta,.theme,.no-print{display:none!important}
body{font-size:11pt;line-height:1.45;background:#fff;color:#000}
.wrap{max-width:none;padding:0}
a{color:#000;text-decoration:underline}
a[href^="http"]::after{content:" (" attr(href) ")";font-size:9pt;color:#444}
.card,.tbl,.callout,pre{break-inside:avoid;border-color:#bbb}
h1,h2,h3{break-after:avoid}
tr{break-inside:avoid}
canvas{display:none}
}

View file

@ -31,9 +31,10 @@
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page. Source: site/partials/head.html, injected by site/build.mjs between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. */
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@ -45,83 +46,23 @@
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
:root{--f-sans:'IBM Plex Sans','Plex Sans Fallback',system-ui,-apple-system,sans-serif;--f-display:'Unbounded','Unbounded Fallback',sans-serif;--f-mono:'IBM Plex Mono','Plex Mono Fallback',ui-monospace,Menlo,monospace;
/* site chrome tokens (nav, footer, skip link). Dark by default; the litepaper sets light values and resets these in dark mode */
--nav-h:68px;--ui-bg:rgba(12,12,14,.84);--ui-menu:#0C0C0E;--ui-ink:#F4F1EC;--ui-ink-2:#C9C7C2;--ui-ash:#9A9A9E;--ui-line:#2A2A30;--ui-line-2:#3A3A42;--ui-accent:#F2541B;--ui-accent-ink:#0C0C0E;--ui-hot:#FFB35C;--ui-hover:#FF6A2B;--ui-tint:rgba(242,84,27,.12)}
html{-webkit-text-size-adjust:100%}
h1,h2,h3{text-wrap:balance}
p,li,dd,figcaption{text-wrap:pretty}
a:focus-visible,button:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--ui-accent);outline-offset:3px;border-radius:6px}
.skip{position:absolute;left:12px;top:-80px;z-index:50;background:var(--ui-accent);color:var(--ui-accent-ink);padding:10px 14px;border-radius:10px;font:600 15px/1.2 var(--f-sans);text-decoration:none}
.skip:focus{top:12px}
/* nav: one bar on every page, the mark in its black square, seven links and the miner button, a menu under 941 px (the eight items need 688 px beside the brand at 15 px, so the bar fits from 941 px up without a wrapped label) */
.nav{position:sticky;top:0;z-index:20;background:var(--ui-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-bottom:1px solid var(--ui-line);color:var(--ui-ink)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:var(--nav-h)}
.nav a{color:inherit;text-decoration:none}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ui-ink);text-decoration:none;flex:0 0 auto}
.brand .mark{display:block;flex:0 0 auto}
.brand .word{font-family:var(--f-display);font-weight:900;font-size:20px;letter-spacing:.06em;line-height:1}
.nav .links{display:flex;align-items:center;gap:clamp(14px,2vw,26px);font-size:15px;font-weight:500}
.nav .links a{color:var(--ui-ink-2);padding:4px 0;border-bottom:2px solid transparent;white-space:nowrap;transition:color .15s ease,border-color .15s ease}
.nav .links a:hover,.nav .brand:hover{color:var(--ui-hot);text-decoration:none}
.nav .brand:hover{color:var(--ui-ink)}
.nav .links a[aria-current="page"]{color:var(--ui-ink);border-bottom-color:var(--ui-accent)}
.nav .links a.cta{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border-radius:10px;font-weight:600;background:var(--ui-accent);color:var(--ui-accent-ink);border:1px solid var(--ui-accent);transition:background .15s ease,transform .15s ease}
.nav .links a.cta:hover{background:var(--ui-hover);border-color:var(--ui-hover);color:var(--ui-accent-ink);transform:translateY(-1px)}
.burger{display:none;background:none;border:1px solid var(--ui-line-2);color:var(--ui-ink);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer;padding:0;flex:0 0 auto}
.burger .x{display:none}.burger[aria-expanded="true"] .x{display:block}.burger[aria-expanded="true"] .bars{display:none}
@media (max-width:940px){
.nav .links{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ui-menu);border-bottom:1px solid var(--ui-line);box-shadow:0 24px 40px rgba(0,0,0,.35);padding:10px var(--gutter,16px) 18px;flex-direction:column;align-items:stretch;gap:2px}
.nav .links a{padding:13px 10px;border-radius:10px;border-bottom:0;font-size:16px}
.nav .links a[aria-current="page"]{background:var(--ui-tint)}
.nav .links a.cta{margin-top:8px;min-height:48px}
.nav .links.open{display:flex}
.burger{display:inline-flex}
}
/* footer, the same on every page */
.foot{border-top:1px solid var(--ui-line);color:var(--ui-ink);margin-top:var(--sec,64px)}
.foot .wrap{padding-block:48px 32px}
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
.foot-brand .brand{align-self:flex-start}.foot-brand .word{font-size:16px}
.foot-brand p{color:var(--ui-ash);font-size:14px;margin:0}
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
.foot-col .eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ui-ash);margin-bottom:4px}
.foot-col a{color:var(--ui-ink-2);text-decoration:none;display:inline-flex;align-items:center;gap:6px;align-self:flex-start}
.foot-col a:hover{color:var(--ui-hot);text-decoration:none}
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--ui-line);font-size:13px;color:var(--ui-ash)}
.foot-base span{text-wrap:balance}.foot-base .mono{font-family:var(--f-mono)}
.foot-imprint{flex-basis:100%;text-wrap:pretty}.foot-base a{color:inherit;text-decoration:none}.foot-base a:hover{color:var(--ui-hot)}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
</style>
<script>
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
</script>
<!-- head:end -->
<style>
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
/* page block; tokens, type and components are in /site.css */
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
--fs-hero:clamp(32px,5.6vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
:root{--sm:84px}@media (max-width:900px){:root{--sm:80px}}
section,[id]{scroll-margin-top:var(--sm)}section[id]{scroll-margin-top:calc(var(--sm) - var(--sec))}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--f-sans);font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}a:hover{color:var(--molten)}
img,svg{max-width:100%}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
.mono{font-family:var(--f-mono)}
.eyebrow{font-family:var(--f-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
.eyebrow.ember{color:var(--ember)}
h1,h2,h3{font-family:var(--f-display);margin:0;line-height:1.08;text-wrap:balance}
h1{font-weight:900;font-size:var(--fs-hero);letter-spacing:-.01em}
h2{font-weight:700;font-size:var(--fs-h2)}
h3{font-weight:700;font-size:var(--fs-h3)}
p{margin:0}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer;transition:transform .15s ease,background .15s ease,border-color .15s ease}
.btn:hover{transform:translateY(-1px);border-color:var(--ash);color:var(--bone)}
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0}
.reveal{opacity:0;transform:translateY(18px);transition:opacity .7s ease,transform .7s ease}
.reveal.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none;transition:none}}
@ -132,7 +73,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.pt{font-size:15px;color:var(--ash);max-width:64ch}
.lp{display:inline-block;margin-top:24px;color:var(--ember);font-weight:600;font-size:15px}
.cta{display:flex;flex-wrap:wrap;gap:12px}
.hero{position:relative;padding-block:clamp(48px,8vw,96px) clamp(40px,6vw,72px);overflow:hidden}
.hero::before{content:'';position:absolute;inset:auto -10% -30% 40%;height:70%;background:radial-gradient(closest-side,rgba(242,84,27,.18),rgba(242,84,27,0));pointer-events:none}
.hero .wrap{position:relative;display:grid;gap:36px;grid-template-columns:1fr;align-items:center}
@ -140,7 +80,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.hero-copy{display:flex;flex-direction:column;gap:22px}
.hero p.lead{font-size:clamp(17px,2vw,20px);color:var(--ink-2);max-width:52ch}
.hero .os{display:flex;flex-wrap:wrap;gap:8px 18px;font-size:14px;color:var(--ash)}
.shot{position:relative;min-width:0}
.shot::before{content:'';position:absolute;inset:12% -6% -8% -6%;background:radial-gradient(closest-side,rgba(242,84,27,.22),rgba(242,84,27,0));filter:blur(12px);pointer-events:none}
.frame{position:relative;border:1px solid var(--line-2);border-radius:16px;background:#0C0C0E;box-shadow:0 30px 80px rgba(0,0,0,.55);overflow:hidden}
@ -149,7 +88,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.frame .bar b{margin-left:auto;font:500 11px/1 var(--f-mono);letter-spacing:.12em;color:var(--ash);text-transform:uppercase}
.frame img{display:block;width:100%;height:auto}
.frame figcaption{padding:10px 14px;font-size:13px;color:var(--ash);border-top:1px solid var(--line)}
/* the three finality states */
.states{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--gap-tile);position:relative}
.state{border:1px solid var(--line);border-radius:var(--tile-r);padding:var(--tile-pad);background:var(--graphite);display:flex;flex-direction:column;gap:8px;position:relative;min-width:0}
@ -162,7 +100,6 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.state::after{content:'';position:absolute;right:-9px;top:50%;width:14px;height:14px;border-top:2px solid var(--line-2);border-right:2px solid var(--line-2);transform:translateY(-50%) rotate(45deg);z-index:1}
.state:last-child::after{display:none}
@media (max-width:640px){.states{grid-template-columns:1fr}.state::after{right:auto;left:50%;top:auto;bottom:-9px;transform:translateX(-50%) rotate(135deg)}}
.group{display:grid;gap:var(--gap);grid-template-columns:1fr;margin-bottom:var(--sec)}
@media (min-width:900px){.group{grid-template-columns:300px minmax(0,1fr);gap:48px}}
.group:last-child{margin-bottom:0}
@ -175,24 +112,16 @@ section+section{padding-top:0;scroll-margin-top:var(--sm)}
.feat p{font-size:14px;color:var(--ash);line-height:1.45}
.feat .src{font-family:var(--f-mono);font-size:11px;color:var(--ember);margin-top:4px}
.feat.wide{grid-column:1/-1}
.tbl{overflow-x:auto;border:1px solid var(--line);border-radius:var(--card-r);background:var(--graphite);-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:560px;font-size:15px}
th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);font-weight:500;background:var(--obsidian)}
tr:last-child td{border-bottom:0}
td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
.ol{counter-reset:s;display:grid;gap:var(--gap-tile);grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));margin:0;padding:0;list-style:none}
.ol li{counter-increment:s;background:var(--graphite);border:1px solid var(--line);border-radius:var(--tile-r);padding:var(--tile-pad);font-size:14px;color:var(--ink-2);line-height:1.45;min-width:0}
.ol li::before{content:counter(s,decimal-leading-zero);display:block;font-family:var(--f-mono);font-size:11px;letter-spacing:.14em;color:var(--ember);margin-bottom:8px}
.band{background:var(--ember);color:var(--ember-ink)}
.band .wrap{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:24px;padding-block:56px}
.band h2{font-weight:900;font-size:clamp(26px,3.5vw,36px)}
.band p{font-size:16px;max-width:56ch}
.btn.dark{background:var(--obsidian);color:var(--bone);border-color:var(--obsidian)}
.btn.dark:hover{background:#222226;color:var(--bone)}
.foot{margin-top:0}
</style>
</head>
<body>
@ -201,10 +130,9 @@ td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
<nav class="nav" aria-label="Main">
<div class="wrap">
<a href="/" class="brand" aria-label="Igneum home">
<svg class="mark" viewBox="0 0 1024 1024" width="36" height="36" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-links" aria-label="Menu"><svg class="bars" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg><svg class="x" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"></path></svg></button>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
@ -213,20 +141,32 @@ td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
<a href="/wallet" data-nav="wallet" aria-current="page">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner" class="cta">The miner</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download the miner</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/bench" data-nav="bench">Engineering log</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</a>
<a href="/ledger">Ledger</a>
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
<a href="/miner#get" class="btn primary">Download the miner</a>
</div>
</div>
</nav>
<script>
(function(){
// the menu: open on the button, closed by any link, a tap outside, Escape (focus back on the button) or a resize past 940 px
var b=document.getElementById('nav-burger'),l=document.getElementById('nav-links');if(!b||!l)return;
function set(o){l.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');}
b.addEventListener('click',function(){set(!l.classList.contains('open'));});
l.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('click',function(e){if(l.classList.contains('open')&&!l.contains(e.target)&&!b.contains(e.target))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&l.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:941px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
})();
</script>
<!-- nav:end -->
@ -393,7 +333,7 @@ td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
<div class="wrap">
<div class="foot-grid">
<div class="foot-brand">
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="30" height="30" aria-hidden="true"><rect width="1024" height="1024" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
<p>Mined by GPUs. Proven by fire.</p>
</div>
<nav class="foot-col" aria-label="Read">
@ -419,9 +359,9 @@ td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/#journey">Journey</a>
<a href="/wallet">Add Igneum to MetaMask</a>
<a href="/metamask">Add Igneum to MetaMask</a>
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
<a href="/miner#get">Miner downloads: public testnet</a>
<a href="/miner#get">Miner downloads: devnet build</a>
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
</nav>
@ -429,10 +369,23 @@ td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
<div class="foot-base">
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
<span class="mono">igneum.network</span>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
paint();
})();
</script>
<!-- footer:end -->
<script>
(function(){

View file

@ -0,0 +1,65 @@
// Ledger text check: every sentence docs/fud-ledger.md requires on a public page must appear on that page verbatim
// (docs/plans/site-ui-3.md step 6). The list below is the ledger's stated-sentence set as audited on 6 October 2026
// (docs/plans/site-ui-3-ticklist.md); a new ledger row that states a sentence adds a line here in the same commit.
// node tools/ci/ledger-text-check.mjs exit 1 listing every missing sentence
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const site = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'site');
const REQUIRED = {
'index.html': [
['E5', 'The one payment to the project is the Ember software’s optional 1% dev fee', 'The one payment to the project is the Ember software\'s optional 1% dev fee'],
['G4', 'admin keys in consensus'],
['C2', 'since 2019 (approximate)'],
['X2', 'Public testnet: not yet open; the devnet build is here for people who want to look'],
['X3', 'Live rows arrive with the public testnet, August 2027'],
['X7', 'hello@igneum.network'],
['X8', 'No listing is arranged, promised or sought by the project'],
],
'litepaper.html': [
['M2', 'computing items on the fly runs 4.8x slower than loading them'],
['M4', 'ProgPoW, as KAWPOW on Ravencoin since 2020'],
['M7', 'The hash is a lottery, not a general-purpose cryptographic hash'],
['M7', 'no analysis of the lottery properties exists yet'],
['M10', 'an RTX 5090 hashes at 95 GB/s of useful 4-byte loads against 1,638 GB/s of sequential writes'],
['M13', 'Macs mine too, at about a fifth of a flagship card'],
['F1', 'In the chain’s first 30 days no checkpoint locks at all', 'In the chain\'s first 30 days no checkpoint locks at all'],
['F5', 'An attacker matching the honest network needs twenty days for a third and never reaches two thirds'],
['F10', 'Pools carry their hashers’ votes, so vote concentration equals pool concentration, and it is public', 'Pools carry their hashers\' votes, so vote concentration equals pool concentration, and it is public'],
['P3', 'the wrapping cost is a phase two measurement'],
['P4', 'The consensus proof that makes the checkpoint self-verifying is phase two'],
['P6', 'a supplier whose marginal cost is close to power'],
['P7', 'Writing new code, including an emergency fix to the proof system, is the one thing that takes a person'],
['P10', 'settlement in IGN with a 10% burn follows'],
['P14', 'pays about 7,300 IGN a year'],
['E5', 'No fund, no foundation, no fee to the team'],
['E5', '1 block in 100 pays the project'],
['E6', 'The schedule is a bet, not a measurement'],
['G1', 'reviewers will be named and paid before gate 3'],
['G2', 'one founder with AI systems'],
['G3', 'there is no team page'],
['G4', 'no admin keys in consensus'],
['G6', 'Pools can be bypassed on transaction choice'],
['C2', 'precedent, not proof'],
['C3', 'Chips arrived, as on Kaspa, whose hash was designed to welcome them'],
['C3', 'Kaspa has run in production since 2021 (approximate)'],
['C5', 'Inclusion is not confirmation on either chain'],
['C6', 'the combination is the risk the gates price'],
['C6', 'Dates slip. Gates do not.'],
['C8', 'Ergo, Ravencoin and Conflux still mine on GPUs at a fraction of the 2022 fleet (approximate)'],
['C10', 'Boundless provers post ZKC and Succinct provers stake PROVE (approximate'],
['C11', 'We know of no chain that combines them'],
['L2', 'Nearly a quarter of all supply is mined in the first year and half in the first two'],
['X1', 'The node, the miner and the wallet are in a private repository until the public testnet'],
['X7', 'hello@igneum.network'],
['X8', 'No listing is arranged, promised or sought by the project'],
],
};
const norm = s => s.replace(/\s+/g, ' ').replace(/&#39;|&apos;/g, '\'').replace(/&quot;/g, '"').replace(/&amp;/g, '&');
let missing = 0, n = 0;
for (const [page, rows] of Object.entries(REQUIRED)) {
const html = norm(readFileSync(join(site, page), 'utf8'));
for (const [row, ...variants] of rows) { n++; if (!variants.some(v => html.includes(norm(v)))) { missing++; console.error(`ledger text: ${page} lacks ${row}: "${variants[0]}"`); } }
}
console.log(`ledger text check: ${n} sentences, ${missing} missing`);
process.exit(missing ? 1 : 0);

View file

@ -0,0 +1,20 @@
// Site contrast check: every text token on every surface token in site/site.css, dark and light, must reach 4.5:1
// (the design's rule, docs/plans/site-ui-3.md 2.1). Reads the :root blocks of site.css; no browser.
// node tools/ci/site-contrast-check.mjs exit 1 on any pair under 4.5
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const css = readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'site', 'site.css'), 'utf8');
function block(re) { const m = re.exec(css); if (!m) throw new Error('token block not found: ' + re); const out = {}; for (const t of m[1].matchAll(/--([a-z0-9-]+):\s*(#[0-9A-Fa-f]{6})/g)) out[t[1]] = t[2]; return out; }
const dark = block(/:root\{([\s\S]*?)\n\}/), light = block(/:root\[data-theme="light"\]\{([\s\S]*?)\n\}/);
const lum = h => { const c = [1, 3, 5].map(i => parseInt(h.slice(i, i + 2), 16) / 255).map(v => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4); return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; };
const ratio = (a, b) => { const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p); return (x + 0.05) / (y + 0.05); };
const text = ['bone', 'ink-2', 'ash', 'ember-text', 'molten-text'], surf = ['obsidian', 'graphite', 'row'];
let bad = 0, n = 0;
for (const [name, t] of [['dark', dark], ['light', light]]) for (const a of text) for (const b of surf) {
if (!t[a] || !t[b]) continue; n++; const r = ratio(t[a], t[b]);
if (r < 4.5) { bad++; console.error(`contrast: ${name} --${a} ${t[a]} on --${b} ${t[b]} = ${r.toFixed(2)} (under 4.5)`); }
}
for (const [name, t] of [['dark', dark], ['light', light]]) { const r = ratio(t['ember-ink'], t['ember']); n++; if (r < 4.5) { bad++; console.error(`contrast: ${name} --ember-ink on --ember = ${r.toFixed(2)}`); } }
console.log(`site contrast check: ${n} pairs, ${bad} under 4.5:1`);
process.exit(bad ? 1 : 0);

View file

@ -0,0 +1,48 @@
// Orphan check: no heading on a site page leaves one word alone on its last line at 390 px (copy rule 18, 24 Sep 2026).
// Renders each page headless at 390 px with Playwright's cached Chromium (never Chrome.app) through tools/site-serve.mjs on
// a private port, one browser, niced. Skips with a note when the Playwright build is absent (CI without it).
// node tools/ci/site-orphan-check.mjs [--port 4181] exit 1 listing every heading with a one-word last line
import { createRequire } from 'node:module';
import { existsSync, readdirSync } from 'node:fs';
import { spawn } from 'node:child_process';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url)), site = join(here, '..', '..', 'site');
const PW = [process.env.PLAYWRIGHT_CORE, '/Users/joshm/Projects/viva-tracking/node_modules/playwright-core/package.json'].filter(Boolean).find(p => existsSync(p));
const CHROME = process.env.PLAYWRIGHT_CHROMIUM || `${process.env.HOME}/Library/Caches/ms-playwright/chromium-1234/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing`;
if (!PW || !existsSync(CHROME)) { console.log('orphan check: skipped, no Playwright Chromium on this machine'); process.exit(0); }
const { chromium } = createRequire(PW)('playwright-core');
const port = Number((process.argv.find(a => a.startsWith('--port=')) || '').split('=')[1] || 4181);
const srv = spawn('nice', ['-n', '19', 'node', join(here, '..', 'site-serve.mjs')], { env: { ...process.env, PORT: String(port) }, stdio: 'ignore' });
await new Promise(r => setTimeout(r, 1200));
const browser = await chromium.launch({ executablePath: CHROME, args: ['--renderer-process-limit=1'] });
const page = await browser.newPage({ viewport: { width: 390, height: 844 }, reducedMotion: 'reduce' });
const pages = readdirSync(site).filter(f => f.endsWith('.html') && f !== '404.html').map(f => '/' + f.replace(/\.html$/, '').replace(/^index$/, ''));
const bad = []; let n = 0;
for (const p of pages) {
try { await page.goto(`http://127.0.0.1:${port}${p}`, { waitUntil: 'load', timeout: 20000 }); } catch { continue; }
const found = await page.evaluate(() => {
const out = []; const r = document.createRange();
document.querySelectorAll('h1, h2, h3').forEach(h => {
if (!h.offsetParent && getComputedStyle(h).position !== 'fixed') return;
const words = (h.textContent || '').trim().split(/\s+/); if (words.length < 3) return;
// line of the last word vs the line of the word before it
const tw = document.createTreeWalker(h, NodeFilter.SHOW_TEXT); const texts = []; let t; while ((t = tw.nextNode())) if (t.textContent.trim()) texts.push(t);
if (!texts.length) return; const last = texts[texts.length - 1]; const s = last.textContent; const i = s.trimEnd().lastIndexOf(' ');
if (i < 0) return; r.setStart(last, i + 1); r.setEnd(last, s.trimEnd().length); const a = r.getBoundingClientRect();
r.setStart(last, Math.max(0, s.lastIndexOf(' ', i - 1) + 1)); r.setEnd(last, i); const b = r.getBoundingClientRect();
const lines = Math.round(h.getBoundingClientRect().height / parseFloat(getComputedStyle(h).lineHeight || '1'));
if (lines > 1 && a.top > b.top + 2 && a.left <= b.left + 1) out.push((h.textContent || '').trim().slice(0, 80));
});
return out;
});
n++; found.forEach(f => bad.push(`${p}: "${f}"`));
}
await browser.close(); srv.kill();
// the engineering log's own entry titles (/bench, generated from docs/bench-log.md) are reported, not failed: they are the
// log's headings, edited in the log, not site copy
const gen = bad.filter(b => b.startsWith('/bench:')), fail = bad.filter(b => !b.startsWith('/bench:'));
gen.forEach(b => console.log('orphan (log title, reported): ' + b));
fail.forEach(b => console.error('orphan: ' + b));
console.log(`orphan check: ${n} pages at 390 px, ${fail.length} site headings with a one-word last line, ${gen.length} log titles reported`);
process.exit(fail.length ? 1 : 0);