igneum/site/live.html
igneum-labs a8461a2848 site-ui-4: the header on every page (six items, Download, the burger with its open state and aria), OS marks on the download cards, the footer redesign (icon row GitHub, Discord, Reddit; Contact eyebrow; one baseline grid; the state word and the year), the chip-model text (prize sentence cut, the bounded hot-set row), live-dag.js 2.0.2 narrow options for phones, the nav build check, the word-bounded rig-name check
The owner's orders of 7 October 2026 (header, logos, footer, phone scene) and the Counter ASIC lane's public text (docs/plans/counter-asic-3-public-text-2026-10-07.md, b10c7ac2) with the disclosure prize held back until its publish word.
Header: the one-screen home no longer hides items; the sheet carries exactly Litepaper, Miner, App, Wallet, Live devnet, Ledger and Download; 44 px bar links, 48 px sheet rows, the burger turns into an X; tools/ci/site-nav-check.mjs runs after every build and in the gate.
Cards: the Windows four-pane, Apple and Tux marks from 80c5618, white on the ember card, the card's own text colour on the mono cards (so they flip with the theme).
Footer: the ledger page renders through tools/ledger-page.mjs and now underlines Ledger; the Reddit profile is the one the owner named.
Renderer: narrow, narrowBreak 720, narrowWindow 30, narrowLanes 4, narrowMinNode 11; hairlines off except the selected chain; checkpoint labels as the percent; fps 60 on the home and /live mounts. Measured in headless chromium at 390 with 4x CPU throttling: 50 to 56 frames a second (the cap is 60).
Evidence row 17 rewritten in the table's eight columns. The forbidden list gains "disclosure prize" and the word-bounded rig names.
Tested with Playwright at 390 and 768 in both themes with real taps on every public page and the 404: 48 rows, 0 problems; captures in docs/plans/site-ui-4-shots/chrome.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 09:59:42 +00:00

538 lines
51 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Igneum live devnet</title>
<meta name="description" content="The Igneum network observatory: every block and its connections, the miners, the provers paid and the finality rule, read from a node every two seconds.">
<link rel="canonical" href="https://igneum.network/live">
<meta name="theme-color" content="#0C0C0E">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Igneum">
<meta property="og:title" content="Igneum live devnet">
<meta property="og:description" content="The Igneum devnet as it happens: every block, the miners, the provers paid and the finality rule, read from a node every two seconds.">
<meta property="og:url" content="https://igneum.network/live">
<meta property="og:image" content="https://igneum.network/og-small.png?v=3">
<meta property="og:image:width" content="256">
<meta property="og:image:height" content="256">
<meta property="og:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Igneum live devnet">
<meta name="twitter:description" content="The Igneum devnet as it happens: every block, the miners, the provers paid and the finality rule, read from a node every two seconds.">
<meta name="twitter:image" content="https://igneum.network/og-small.png?v=3">
<meta name="twitter:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Crect width='1024' height='1024' fill='%230C0C0E'/%3E%3Cg transform='translate(166.95 166.95) scale(6.901)'%3E%3Cpolygon points='50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34' fill='%23F2541B'/%3E%3Cpolygon points='50,42 59,58 50,82 41,58' fill='%230C0C0E'/%3E%3C/g%3E%3C/svg%3E" type="image/svg+xml">
<link rel="icon" href="/favicon.ico" sizes="48x48">
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32">
<link rel="icon" href="/icon-192.png" type="image/png" sizes="192x192">
<link rel="icon" href="/icon-512.png" type="image/png" sizes="512x512">
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
<link rel="manifest" href="/site.webmanifest">
<!-- 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: 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}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-sans-400.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:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-sans-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:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-sans-600.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:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400.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:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-mono-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 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%}
</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>
/* live page block (7 Oct 2026, site-ui-4: the observatory composition, every number read from the observer); tokens and components are in /site.css */
.obs{--pr:8px;--hair:1px solid var(--line)}
.obs-head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--s-4) var(--s-5);padding:var(--s-6) 0 var(--s-5)}
.obs-head .eyebrow{display:flex;align-items:center;gap:8px;margin-bottom:var(--s-3)}
.obs-head .eyebrow i{width:7px;height:7px;background:var(--ember);transform:rotate(45deg);display:inline-block}
.obs-head h1{font-size:clamp(34px,4.6vw,56px);font-weight:700;letter-spacing:-.02em;margin:0}
.obs-head h1 b{color:var(--ember-text)}
.obs-head p{margin:var(--s-3) 0 0;color:var(--ink-2);max-width:60ch}
.obs-head .side{display:flex;flex-direction:column;align-items:flex-end;gap:var(--s-2)}
.obs-head .side .state{font-size:var(--t-xs)}
.tiles6{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));border:var(--hair);border-radius:var(--pr);background:var(--graphite);overflow:hidden}
.tiles6>div{padding:18px 19px 19px;border-right:var(--hair);min-width:0}
.tiles6>div:last-child{border-right:0}
.tiles6 .k{display:flex;justify-content:space-between;align-items:center;font:400 13px/1.4 var(--f-sans);color:var(--ink-2)}
.tiles6 .k i{color:var(--ash);font:400 12px/1 var(--f-mono)}
.tiles6 .v{font:500 clamp(22px,2vw,30px)/1.15 var(--f-sans);color:var(--bone);margin:14px 0 10px;letter-spacing:-.01em;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.tiles6 .v.word{font:500 15px/1.3 var(--f-mono);color:var(--ash)}
.tiles6 .d{font:400 11px/1.4 var(--f-mono);color:var(--ash)}
@media (max-width:1100px){.tiles6{grid-template-columns:repeat(3,minmax(0,1fr))}.tiles6>div:nth-child(3){border-right:0}.tiles6>div:nth-child(-n+3){border-bottom:var(--hair)}}
@media (max-width:600px){.tiles6{grid-template-columns:repeat(2,minmax(0,1fr))}.tiles6>div{border-right:var(--hair)!important;border-bottom:var(--hair)}.tiles6>div:nth-child(2n){border-right:0!important}.tiles6>div:nth-last-child(-n+2){border-bottom:0}}
.panel{border:var(--hair);border-radius:var(--pr);background:var(--graphite);margin-top:var(--s-5);overflow:hidden}
.toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:15px 21px;border-bottom:var(--hair);background:var(--row)}
.toolbar .title{display:flex;align-items:center;gap:10px;font:500 var(--t-md)/1.3 var(--f-sans);color:var(--bone)}
.toolbar .title i{width:14px;height:14px;border:1.5px solid var(--ember);border-radius:50%;display:inline-block;position:relative}
.toolbar .title i::after{content:"";position:absolute;inset:4px;background:var(--ember);border-radius:50%}
.chip{font:400 11px/1.2 var(--f-mono);color:var(--ash);border:var(--hair);border-radius:4px;padding:5px 8px;white-space:nowrap}
.toolbar .right{margin-left:auto;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.tb{background:transparent;border:1px solid transparent;color:var(--ash);font:400 13px/1.2 var(--f-sans);padding:7px 10px;border-radius:6px;cursor:pointer}
.tb:hover{color:var(--bone)}
.tb[aria-pressed="true"]{color:var(--bone);border-color:var(--line-2);background:var(--graphite)}
.tb.on{color:var(--ember-text);border-color:var(--ember-40)}
.sep{width:1px;height:18px;background:var(--line)}
.graph{display:grid;grid-template-columns:minmax(0,1fr) 250px}
@media (max-width:1000px){.graph{grid-template-columns:1fr}}
.gcol{padding:18px 21px 14px;min-width:0;border-right:var(--hair)}
@media (max-width:1000px){.gcol{border-right:0;border-bottom:var(--hair)}}
.gcol .state{margin-bottom:10px;font-size:var(--t-xs)}
.scene{position:relative}
#dag{display:block;width:100%;height:clamp(300px,36vw,440px);border-radius:6px;background:var(--row);touch-action:pan-y}
.tip{position:absolute;left:0;top:0;pointer-events:none;background:var(--graphite);border:1px solid var(--line-2);border-radius:6px;padding:10px 12px;font:400 12px/1.5 var(--f-mono);color:var(--ink-2);white-space:nowrap;z-index:5}
.tip[hidden]{display:none}
.gfoot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-top:12px;font:400 11px/1.4 var(--f-mono);color:var(--ash)}
.zoom{display:inline-flex;align-items:center;border:var(--hair);border-radius:6px;overflow:hidden}
.zoom button{background:transparent;border:0;color:var(--ink-2);font:400 13px/1 var(--f-mono);padding:8px 12px;cursor:pointer}
.zoom button:hover{background:var(--hover)}
.zoom span{font:400 11px/1 var(--f-mono);color:var(--ash);padding:0 6px;min-width:44px;text-align:center}
.icol{padding:18px 21px;min-width:0;font-size:13px}
.icol .ih{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:14px}
.icol .ih .eyebrow{margin:0}
.icol .num{font:500 22px/1.2 var(--f-sans);color:var(--bone);margin:6px 0 4px;overflow-wrap:anywhere}
.icol .hash{font:400 11px/1.5 var(--f-mono);color:var(--ash);overflow-wrap:anywhere;margin-bottom:14px}
.insp{display:grid;grid-template-columns:auto minmax(0,1fr);gap:0 10px;margin:0;border-top:var(--hair);padding-top:8px}
.insp dt{font:400 11px/1.3 var(--f-mono);color:var(--ash);padding:6px 0}
.insp dd{margin:0;font:400 11px/1.3 var(--f-mono);color:var(--bone);text-align:right;padding:6px 0;overflow-wrap:anywhere;min-width:0}
.insp dd.hot{color:var(--molten-text)}
.icol .note{font-size:12px;margin-top:14px;padding-top:12px;border-top:var(--hair)}
.insp-empty{color:var(--ash);font-size:13px;line-height:1.5;padding:6px 0 2px}
.mstats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));border-bottom:var(--hair)}
.mstats>div{padding:16px 21px 14px;border-right:var(--hair);min-width:0}
.mstats>div:last-child{border-right:0}
.mstats .mk{font:400 13px/1.4 var(--f-sans);color:var(--ink-2)}
.mstats .mv{display:flex;align-items:baseline;gap:8px;margin:8px 0 6px}
.mstats .mv b{font:500 26px/1.1 var(--f-sans);color:var(--bone);letter-spacing:-.01em}
.mstats .mv b.own{color:var(--molten-text)}.mstats .mv b.dim{color:var(--ash)}
.mstats .mv small{font:400 11px/1.4 var(--f-mono);color:var(--ash)}
.mstats .mf{font:400 11px/1.4 var(--f-mono);color:var(--ash);overflow-wrap:anywhere}
@media (max-width:900px){.mstats{grid-template-columns:repeat(2,minmax(0,1fr))}.mstats>div:nth-child(2){border-right:0}.mstats>div:nth-child(-n+2){border-bottom:var(--hair)}}
.tabs{display:inline-flex;border:var(--hair);border-radius:6px;padding:3px;gap:2px;background:var(--graphite)}
.tab{background:transparent;border:0;color:var(--ash);font:400 12px/1.2 var(--f-sans);padding:6px 10px;border-radius:4px;cursor:pointer}
.tab.on{background:var(--row);color:var(--bone)}
.ititle{font:500 22px/1.2 var(--f-sans);color:var(--bone);margin:6px 0 4px;letter-spacing:-.01em}
.proof-scene{height:200px;border:var(--hair);border-radius:6px;background:var(--row);margin:12px 0;overflow:hidden}
.proof-scene canvas{display:block;width:100%;height:100%}
.proof-head{display:flex;justify-content:space-between;align-items:baseline;font:500 13px/1.3 var(--f-sans);color:var(--bone);margin-bottom:8px}
.track{display:flex;gap:3px;height:3px;margin-bottom:10px}
.track i{flex:1 1 0;background:var(--line);border-radius:2px}
.track i.proving{background:var(--ember)}.track i.verified{background:var(--bone)}.track i.paid{background:var(--molten)}
.shards{list-style:none;margin:0 0 12px;padding:0;font:400 11px/1.6 var(--f-mono)}
.shards li{display:flex;justify-content:space-between;gap:8px;color:var(--ink-2)}
.shards li::before{content:"\25CF";font-size:7px;margin-right:6px;color:var(--line-2);vertical-align:middle}
.shards li.proving,.shards li.proving::before{color:var(--ember-text)}.shards li.verified,.shards li.verified::before{color:var(--bone)}.shards li.paid,.shards li.paid::before{color:var(--molten-text)}
.lgi{width:10px;height:10px;border-radius:3px;display:inline-block;flex:none;position:relative}
.lgi.tick::after{content:"\2713";position:absolute;left:0;top:-4px;font-size:11px;color:var(--bone)}
.motion{margin-top:var(--s-5);border:var(--hair);border-radius:7px;background:var(--graphite);padding:22px 21px}
.motion h2{font:500 var(--t-h3)/1.3 var(--f-sans);margin:0 0 14px;color:var(--bone)}
.beats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4)}
@media (max-width:900px){.beats{grid-template-columns:1fr}}
.beats .num{font:400 10px/1 var(--f-mono);color:var(--ember-text)}
.beats h3{font:500 14px/1.4 var(--f-sans);margin:6px 0 4px;color:var(--bone)}
.beats p{margin:0;font-size:13px;color:var(--ink-2);line-height:1.5}
.legend-row{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 18px;padding:12px 21px;border-top:var(--hair);font:400 11px/1.4 var(--f-mono);color:var(--ash)}
.legend-row .lg{display:flex;flex-wrap:wrap;gap:8px 16px}
.legend-row .lg span{display:inline-flex;align-items:center;gap:7px;color:var(--ink-2);font-family:var(--f-sans);font-size:12px}
.sw{width:9px;height:9px;transform:rotate(45deg);display:inline-block;flex:none}
.three{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4);margin-top:var(--s-4)}
@media (max-width:1000px){.three{grid-template-columns:1fr}}
.dp{border:var(--hair);border-radius:7px;background:var(--graphite);padding:20px 21px 16px;min-width:0}
.dp .ph{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:14px}
.dp h2{font:500 var(--t-md)/1.3 var(--f-sans);margin:0;color:var(--bone)}
.dp .tag{font:400 10px/1.2 var(--f-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ash)}
.big{display:flex;align-items:baseline;gap:10px;margin-bottom:8px}
.big b{font:500 30px/1 var(--f-sans);color:var(--bone)}
.big span{font:400 12px/1.4 var(--f-sans);color:var(--ash)}
.bars{display:flex;align-items:flex-end;gap:3px;height:80px;margin-top:10px}
.bars i{flex:1 1 0;background:linear-gradient(to top,var(--ember-40),var(--molten));border-radius:2px 2px 0 0;min-height:2px;opacity:.9}
.bars i.now{background:var(--molten)}
.axis{display:flex;justify-content:space-between;font:400 10px/1.4 var(--f-mono);color:var(--ash);margin-top:8px}
.ids{display:flex;flex-direction:column;gap:12px}
.id-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 10px;font:400 12px/1.4 var(--f-mono);color:var(--ink-2);align-items:center}
.id-row .name{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.id-row .name .sw{background:var(--line-2)}
.id-row.mine .name{color:var(--ember-text)}
.id-row.mine .name .sw{background:var(--ember)}
.id-row .pct{color:var(--bone);white-space:nowrap}
.id-row .bar{grid-column:1/-1;height:3px;background:var(--line);border-radius:2px;overflow:hidden}
.id-row .bar i{display:block;height:100%;background:var(--ember);opacity:.85}
.id-row.mine .bar i{background:var(--molten)}
.evs{display:flex;flex-direction:column;max-height:330px;overflow:auto}
.evs div{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;align-items:baseline;padding:9px 0;border-bottom:var(--hair);font:400 12px/1.4 var(--f-mono);color:var(--ink-2)}
.evs div:last-child{border-bottom:0}
.evs .dot{width:6px;height:6px;background:var(--molten);border-radius:50%;animation:none}
.evs .dot.lock{background:var(--ember)}.evs .dot.quiet{background:var(--ash)}
.evs .what{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.evs .what b{font-weight:500;color:var(--bone)}
.evs .t{color:var(--ash);font-size:11px;white-space:nowrap}
.evs div.new{animation:land .9s ease-out}
@keyframes land{0%{background:var(--ember-12)}100%{background:transparent}}
@media (prefers-reduced-motion:reduce){.evs div.new{animation:none}}
.tablebar{margin-top:var(--s-4);border:var(--hair);border-radius:7px;background:var(--graphite)}
.tablebar summary{padding:16px 21px;font:500 var(--t-md)/1.3 var(--f-sans);color:var(--bone)}
.tablebar summary::after{content:"+";font:400 14px/1 var(--f-mono);color:var(--ash)}
.tablebar[open] summary::after{content:"\2212"}
.tablebar summary small{font:400 11px/1.4 var(--f-mono);color:var(--ash);margin-left:auto;margin-right:12px}
.tablebar .tbl{border:0;border-top:var(--hair);border-radius:0}
.tablebar table{font-size:12px;font-family:var(--f-mono)}
.tablebar td,.tablebar th{padding:8px 21px}
.tablebar tr[data-hash]{cursor:pointer}
.tablebar tr[data-hash]:hover td{background:var(--hover)}
.obs-foot{display:flex;flex-wrap:wrap;justify-content:space-between;gap:10px;margin-top:var(--s-5);font:400 10px/1.5 var(--f-mono);letter-spacing:.1em;text-transform:uppercase;color:var(--ash)}
.linkish{background:none;border:0;padding:0;color:var(--ember-text);font:inherit;cursor:pointer}
.linkish:hover{text-decoration:underline}
details.tablebar{border-top:var(--hair);border-bottom:var(--hair)}
details.tablebar summary{display:flex;align-items:center}
</style>
</head>
<body>
<!-- nav:start -->
<a class="skip" href="#main">Skip to content</a>
<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="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>
<div class="links" id="nav-links">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/app" data-nav="app">App</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
<a href="/ledger" data-nav="ledger">Ledger</a>
</div>
<a href="/miner#get" class="btn primary small cta">Download</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Open the menu" aria-haspopup="true"><span class="bars" aria-hidden="true"></span></button>
<div class="nav-sheet" id="nav-sheet">
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
<a href="/miner" data-nav="miner">Miner</a>
<a href="/app" data-nav="app">App</a>
<a href="/wallet" data-nav="wallet">Wallet</a>
<a href="/live" data-nav="live">Live devnet</a>
<a href="/ledger" data-nav="ledger">Ledger</a>
<a href="/miner#get" class="btn primary">Download</a>
</div>
</div>
</nav>
<script>
(function(){
// 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');b.setAttribute('aria-label',o?'Close the menu':'Open the menu');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 obs">
<header class="obs-head" id="top">
<div>
<div class="eyebrow"><i></i>Network observatory</div>
<h1>Live devnet<b>.</b></h1>
<p>Every block has a story. Follow the connections.</p>
</div>
<div class="side">
<div class="cta"><a href="/miner#get" class="btn primary">Download the miner</a><a href="/explorer" class="btn">Open the explorer</a></div>
<div class="state" id="live-state"><span class="dot off"></span>connecting to the observer</div>
</div>
</header>
<div class="tiles6" aria-live="polite">
<div><div class="k">Blocks / second <i>&#8599;</i></div><div class="v tick" id="s-bps">pending</div><div class="d">counted over the node&rsquo;s last 60 s; the target is 1</div></div>
<div><div class="k">Blue score <i>#</i></div><div class="v tick" id="s-blue">pending</div><div class="d">the newest block&rsquo;s count of blue ancestors</div></div>
<div><div class="k">Network hash rate <i>&#8767;</i></div><div class="v tick" id="s-hash">pending</div><div class="d">the node&rsquo;s estimate from the last 1,000 blocks&rsquo; difficulty</div></div>
<div><div class="k">Active vote keys <i>&#9671;</i></div><div class="v tick" id="s-keys">pending</div><div class="d">keys with a block in 10 min; a card runs several</div></div>
<div><div class="k">Connected peers <i>&#8644;</i></div><div class="v tick" id="s-peers">pending</div><div class="d">peers of the observer&rsquo;s node</div></div>
<div><div class="k">Locked checkpoint <i>&#9679;</i></div><div class="v tick" id="s-lock">pending</div><div class="d" id="s-lock-d">signed by two thirds of all 30-day weight</div></div>
</div>
<section class="panel" aria-label="The block graph">
<div class="mstats">
<div><div class="mk">Blocks in window</div><div class="mv"><b id="m-blocks">0</b><small id="m-win">last 90 s</small></div><div class="mf" id="m-keys">waiting for the observer</div></div>
<div><div class="mk">Your blocks</div><div class="mv"><b id="m-mine" class="own">0</b><small>in this window</small></div><div class="mf">Key <span class="mono" id="m-key">not set</span> &middot; <button type="button" class="linkish" id="mine-btn">Mark my key</button></div></div>
<div><div class="mk">Proven blocks</div><div class="mv"><b id="m-proven">0</b><small id="m-proven-pct">no blocks</small></div><div class="mf">observer-reported proof status</div></div>
<div><div class="mk">Latest checkpoint</div><div class="mv"><b id="m-cp">None</b><small id="m-cp-w"></small></div><div class="mf" id="m-cp-note">no checkpoint in the reply</div></div>
</div>
<div class="toolbar">
<div class="title"><i></i>The block graph</div>
<span class="chip" id="c-count">0 blocks</span>
<div class="tabs" role="tablist" aria-label="Which blocks">
<button type="button" class="tab on" data-filter="all" aria-pressed="true">All blocks</button>
<button type="button" class="tab" data-filter="chain" aria-pressed="false">Selected chain</button>
<button type="button" class="tab" data-filter="mine" aria-pressed="false">Your blocks</button>
</div>
<div class="right">
<span class="chip" id="lane-caption">keys</span>
<span class="sep"></span>
<button type="button" class="tb" id="dag-pause" aria-pressed="false">Pause</button>
<button type="button" class="tb on" id="dag-follow">Follow</button>
</div>
</div>
<div class="graph">
<div class="gcol">
<div class="state" id="dag-state"><span class="dot off"></span>connecting to the observer</div>
<div class="scene"><canvas id="dag" aria-label="The devnet's block graph, live: time-aligned miner lanes, every parent connection, the selected chain as one path, checkpoint bands, your blocks framed"></canvas><div class="tip" id="dag-tip" hidden></div><div class="chip-zoom" id="dag-chip" hidden>scroll to zoom · Esc to release</div></div>
<div class="gfoot">
<span>Click to inspect &nbsp;&middot;&nbsp; Drag to explore</span>
<span class="zoom"><button type="button" id="z-out" aria-label="Wider window">&minus;</button><span id="z-pct">60 s</span><button type="button" id="z-in" aria-label="Narrower window">+</button></span>
</div>
</div>
<aside class="icol" id="inspector" aria-live="polite">
<div class="ih"><div class="eyebrow">Block inspector</div><span class="chip" id="i-kind">select a block</span></div>
<h3 class="ititle" id="i-title">Select a block</h3>
<div class="hash" id="i-hash">Click the graph, or a row of the table below.</div>
<div class="proof-scene"><canvas id="proof" aria-label="Select a block to see its shards"></canvas></div>
<div class="proof-head"><span>Proof shards</span><span id="i-shard-count" class="mono">not set</span></div>
<div class="track" id="i-track"></div>
<ul class="shards" id="i-shards"><li class="muted">No block selected.</li></ul>
<dl class="insp">
<dt>Inclusion</dt><dd id="i-incl">not set</dd>
<dt>Miner key</dt><dd id="i-miner">not set</dd>
<dt>Blue score</dt><dd id="i-blue">not set</dd>
<dt>DAA score</dt><dd id="i-daa">not set</dd>
<dt>Parent links</dt><dd id="i-parents">not set</dd>
<dt>Header time</dt><dd id="i-time">not set</dd>
<dt>Checkpoint</dt><dd id="i-cp">not set</dd>
<dt>Finality</dt><dd id="i-lock">not set</dd>
</dl>
<p class="note">Proof, chain selection and finality are separate states. None is inferred from a block&rsquo;s age; a lock is drawn only when the observer reports one.</p>
<p style="margin:10px 0 0"><button type="button" class="linkish" id="insp-close" hidden>Clear</button></p>
</aside>
</div>
<div class="legend-row">
<div class="lg">
<span><i class="lgi" style="border:1.5px solid var(--included)"></i>Included</span>
<span><i class="lgi" style="border:1.5px solid var(--excluded)"></i>Excluded</span>
<span><i class="lgi" style="border:1.5px solid var(--ember);background:var(--ember-12)"></i>Selected chain</span>
<span><i class="lgi" style="border:1.5px dashed var(--molten)"></i>Your block</span>
<span><i class="lgi tick"></i>Proven</span>
<span><i class="lgi" style="border:1.5px solid var(--ember);border-radius:50%"></i>Locked checkpoint</span>
</div>
<span id="blue-range">blue score pending</span>
</div>
</section>
<section class="motion">
<h2>Motion, with meaning.</h2>
<div class="beats">
<div><span class="num">01</span><h3>A block arrives.</h3><p>A brief signal traces its real parent edges. Your blocks take a molten frame.</p></div>
<div><span class="num">02</span><h3>The proof takes shape.</h3><p>Shard tiles follow the planned, proving, verified and paid states the observer reports.</p></div>
<div><span class="num">03</span><h3>A checkpoint locks.</h3><p>A pending marker becomes a solid band only when the observer reports it locked.</p></div>
</div>
</section>
<div class="three">
<div class="dp">
<div class="ph"><h2>Block activity</h2><span class="tag">header times / window</span></div>
<div class="big"><b id="a-count">0</b><span>blocks in this view</span></div>
<div class="bars" id="a-bars" aria-label="Blocks per minute over the last hour, newest on the right"></div>
<div class="axis"><span>one bar a minute, the last 30 min</span><span>now</span></div>
<p class="note" id="a-note" style="margin-top:12px;font-size:12px">Counted by the observer as blocks arrive.</p>
</div>
<div class="dp">
<div class="ph"><h2>Mining identities</h2><span class="tag">displayed window</span></div>
<div class="ids" id="miners"><div class="empty">No blocks in the window.</div></div>
<p class="note" style="margin-top:14px;font-size:12px" id="miners-more">Vote-key share of the displayed blocks. Several keys may belong to one GPU.</p>
</div>
<div class="dp">
<div class="ph"><h2>Event stream</h2><span class="tag" id="ev-count">live</span></div>
<div class="evs" id="events"><div class="empty">Waiting for the first read.</div></div>
</div>
</div>
<details class="tablebar" id="table">
<summary>Explore blocks as a table <small>keyboard-accessible view</small></summary>
<div class="tbl"><table>
<thead><tr><th>Blue score</th><th>Hash</th><th>Vote key</th><th>Parents</th><th>State</th><th>Chain block</th></tr></thead>
<tbody id="tbody"><tr><td colspan="6">Waiting for the first read.</td></tr></tbody>
</table></div>
</details>
<div class="obs-foot"><span>Igneum / observatory</span><span>One node read every 2 s. Nothing here is a replay.</span></div>
</main>
<!-- footer:start -->
<footer class="foot">
<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="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 class="social" aria-label="Igneum elsewhere">
<a href="https://github.com/igneum-network/spec" target="_blank" rel="noopener" aria-label="Igneum on GitHub: the spec, the vectors and the issues"><svg viewBox="0 0 24 24" width="22" height="22" 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></a>
<a href="https://discord.gg/igneum" target="_blank" rel="noopener" aria-label="The Igneum Discord"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M19.6 5.3A17 17 0 0 0 15.4 4l-.2.4a15.6 15.6 0 0 1 3.9 1.9 13.6 13.6 0 0 0-14.2 0A15.6 15.6 0 0 1 8.8 4.4L8.6 4a17 17 0 0 0-4.2 1.3C1.8 9.2 1.1 13 1.4 16.7a17.1 17.1 0 0 0 5.2 2.6l1.1-1.8a10.8 10.8 0 0 1-1.7-.8l.4-.3a12.2 12.2 0 0 0 11.2 0l.4.3-1.7.8 1.1 1.8a17 17 0 0 0 5.2-2.6c.4-4.3-.7-8-3-11.4zM8.7 14.4c-1 0-1.8-.9-1.8-2.1s.8-2.1 1.8-2.1 1.9 1 1.8 2.1c0 1.2-.8 2.1-1.8 2.1zm6.6 0c-1 0-1.8-.9-1.8-2.1s.8-2.1 1.8-2.1 1.9 1 1.8 2.1c0 1.2-.8 2.1-1.8 2.1z"></path></svg></a>
<a data-social="reddit" href="https://www.reddit.com/user/Igneum_network/" target="_blank" rel="noopener" aria-label="Igneum on Reddit"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M22 12.1a2.2 2.2 0 0 0-3.7-1.6 10.8 10.8 0 0 0-5.8-1.8l1-4.6 3.2.7a1.5 1.5 0 1 0 .2-.9l-3.6-.8a.5.5 0 0 0-.5.4l-1.1 5.2a10.8 10.8 0 0 0-5.9 1.8A2.2 2.2 0 1 0 3.4 14a4.3 4.3 0 0 0 0 .7c0 3.4 3.9 6.1 8.6 6.1s8.6-2.7 8.6-6.1a4.3 4.3 0 0 0 0-.7 2.2 2.2 0 0 0 1.4-1.9zM7 13.6a1.5 1.5 0 1 1 1.5 1.5A1.5 1.5 0 0 1 7 13.6zm8.6 4.1a5.7 5.7 0 0 1-3.6 1.1 5.7 5.7 0 0 1-3.6-1.1.4.4 0 0 1 .6-.6 4.9 4.9 0 0 0 3 .9 4.9 4.9 0 0 0 3-.9.4.4 0 0 1 .6.6zm-.3-2.6a1.5 1.5 0 1 1 1.5-1.5 1.5 1.5 0 0 1-1.5 1.5z"></path></svg></a>
</div>
</div>
<nav class="foot-col" aria-label="Read">
<div class="eyebrow">Read</div>
<a href="/litepaper">Litepaper</a>
<a href="/litepaper#limits">What Igneum does not claim</a>
<a href="/litepaper#randomx">Igneum vs RandomX</a>
<a href="/bench">Engineering log</a>
<a href="/evidence">Evidence</a>
<a href="/ledger">Ledger: every criticism</a>
</nav>
<nav class="foot-col" aria-label="Run">
<div class="eyebrow">Run</div>
<a href="/miner">The miner</a>
<a href="/app">The app</a>
<a href="/wallet">The wallet</a>
<a href="/miners">GPU bench table</a>
<a href="/miner#fee">The dev fee</a>
<a href="/faucet">Testnet faucet</a>
</nav>
<nav class="foot-col" aria-label="Follow">
<div class="eyebrow">Follow</div>
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/litepaper#roadmap">Journey</a>
<a href="/metamask">Add to MetaMask</a>
<a href="/miner#get">Downloads: devnet build</a>
</nav>
<nav class="foot-col" aria-label="Contact">
<div class="eyebrow">Contact</div>
<a href="mailto:hello@igneum.network">hello@igneum.network</a>
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">An issue on the spec</a>
<span class="foot-note">Report a flaw by either. Nobody from Igneum will ask for your seed.</span>
</nav>
</div>
<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</span>
<span class="foot-state"><span class="dot off" id="foot-dot"></span><span id="foot-state">devnet</span></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 foot-copy">© 2026 Igneum · not an offer to sell anything</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();
// the network state word in the footer: one read of the observer per page view
var fs=document.getElementById('foot-state'),fd=document.getElementById('foot-dot');if(fs&&fd){fetch('/api/live?window=30',{cache:'no-store'}).then(function(r){return r.json();}).then(function(d){var on=d&&d.ok&&d.state&&!d.state.stale;fs.textContent=on?'devnet live':'devnet, observer stale';fd.className='dot '+(on?'live':'off');}).catch(function(){fs.textContent='devnet';});}
})();
</script>
<!-- footer:end -->
<script src="/live-dag.js" defer></script>
<script src="/proof-core.js" defer></script>
<script>
(function(){
function $(id){return document.getElementById(id);}
var fmtN=function(v){return v===null||v===undefined?'pending':Number(v).toLocaleString('en-GB');};
function fmtHash(h){if(h===null||h===undefined||!(h>0))return 'pending';return h>=1e9?(h/1e9).toFixed(1)+' GH/s':(h/1e6).toFixed(1)+' MH/s';}
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 ago';return Math.floor(h/24)+' d ago';}
function hms(ms){var d=new Date(ms);return String(d.getUTCHours()).padStart(2,'0')+':'+String(d.getUTCMinutes()).padStart(2,'0')+':'+String(d.getUTCSeconds()).padStart(2,'0');}
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
// a number that changed glows for a frame: motion only from a real change
function set(id,text){var el=$(id);if(!el||el.textContent===text)return;el.textContent=text;el.classList.add('flash');setTimeout(function(){el.classList.remove('flash');},60);}
var seen={},shardState={},cpState={},evSeen={},evList=[],ready=false,sel=null,lastD=null,dag=null;
var mine=(function(){try{return localStorage.getItem('igneum-my-key')||'';}catch(e){return '';}})();
document.addEventListener('click',function(e){var t=e.target;if(t&&t.id==='mine-btn'){var v=prompt('Your vote key’s short id, the first 8 hex characters shown in the app or the explorer:',mine||'');if(v!==null){mine=v.trim().toLowerCase();try{if(mine)localStorage.setItem('igneum-my-key',mine);else localStorage.removeItem('igneum-my-key');}catch(x){}remount();if(lastD)render(lastD);}}
var row=t&&t.closest?t.closest('tr[data-hash]'):null;if(row&&dag){dag.select(row.getAttribute('data-hash'),'table');}});
function render(d){var s=d.state,pv=d.proving,fn=d.finality,now=new Date(d.now).getTime();lastD=d;
// the six tiles
set('s-bps',s.blocks_per_second_60s!==null&&s.blocks_per_second_60s!==undefined?Number(s.blocks_per_second_60s).toFixed(2):'pending');
set('s-blue',fmtN(s.blue_score));set('s-hash',fmtHash(s.hashes_per_second_estimate));
set('s-keys',fmtN(s.miners_10m!==null&&s.miners_10m!==undefined?s.miners_10m:(d.miners||[]).length));set('s-peers',fmtN(s.peers));
var lock=null;if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){if(c.state==='locked'&&(!lock||c.index>lock.index))lock=c;});
var le=$('s-lock');if(fn&&fn.supported&&!fn.active){set('s-lock',fn.latest_locked_index?'#'+fmtN(fn.latest_locked_index):'none');le.className='v tick';$('s-lock-d').textContent='finality paused: under two thirds of the weight is signing';}
else if(lock){set('s-lock','#'+fmtN(lock.index));le.className='v tick';var t=lock.locked_at?new Date(String(lock.locked_at).replace(' ','T')).getTime():NaN;$('s-lock-d').textContent=(isNaN(t)?'locked':'locked '+rel(now-t))+(lock.fraction_total?' with '+Math.round(lock.fraction_total*100)+'% of all 30-day weight':'');}
else{set('s-lock',fn&&fn.supported?'none yet':'no rule');le.className='v tick word';$('s-lock-d').textContent=fn&&fn.supported?'the 30-day weight window is filling':'';}
// the graph's count, the blue range, the chain-only view
var vr=dag?dag.getViewRange():null,w=dag?dag.getWindow():90,cut=vr?vr.start:now-w*1000,top0=vr?vr.end:now,on=[];(d.blocks||[]).forEach(function(b){if(b.ts>=cut&&b.ts<=top0)on.push(b);});
// block activity: the last 30 minutes, one bar a minute, from the observer's per-minute counts
var bpm=(s.blocks_per_minute||[]).slice(-30),max=Math.max(1,Math.max.apply(null,bpm.map(function(p){return p[1]||0;}).concat([1])));
$('a-count').textContent=on.length;$('a-bars').innerHTML=bpm.map(function(p,i){return '<i style="height:'+Math.max(2,Math.round((p[1]||0)/max*100))+'%" title="'+(p[1]||0)+' blocks"'+(i===bpm.length-1?' class="now"':'')+'></i>';}).join('');
var tot30=bpm.reduce(function(a,p){return a+(p[1]||0);},0);$('a-note').textContent=fmtN(tot30)+' blocks in the last '+bpm.length+' min, '+(bpm.length?(tot30/bpm.length).toFixed(1):'0')+' a minute on average; the target is 60.';
// mining identities: share of the displayed blocks, the reader's own key marked when known
var share={},tot=0;on.forEach(function(b){share[b.miner]=(share[b.miner]||0)+1;tot++;});var ids=Object.keys(share).sort(function(a,b){return share[b]-share[a];}),mel=$('miners');
if(ids.length){var top=ids.slice(0,8),rest=ids.length-top.length;mel.innerHTML=top.map(function(id){var n=share[id],pc=n/tot*100;return '<div class="id-row'+(mine&&id===mine?' mine':'')+'"><span class="name"><i class="sw"></i>'+esc(id)+(mine&&id===mine?' (you)':'')+'</span><span class="pct">'+pc.toFixed(1)+'%</span><div class="bar"><i style="width:'+Math.round(pc)+'%"></i></div></div>';}).join('');
$('miners-more').textContent=(rest>0?'and '+rest+' more. ':'')+'Vote-key share of the displayed blocks. Several keys may belong to one GPU.';}
else mel.innerHTML='<div class="empty">No blocks in the window.</div>';
// the event stream: the observer's own events plus what changed between reads
var evs=[];(d.events||[]).forEach(function(e){evs.push({ts:new Date(e.ts).getTime(),k:e.kind.replace(/_/g,' '),cls:e.kind==='checkpoint_locked'?'lock':/quiet/.test(e.kind)?'quiet':'',text:e.text});});
(d.blocks||[]).forEach(function(b){if(!seen[b.hash]){seen[b.hash]=1;if(ready)evs.push({ts:b.ts,k:'Block observed',cls:'',text:(b.number?'#'+fmtN(b.number):b.hash)+' by '+b.miner});}
(b.shards||[]).forEach(function(sh){var key=b.hash+':'+sh.i,prev=shardState[key];shardState[key]=sh.state;if(ready&&prev!==sh.state&&(sh.state==='verified'||sh.state==='paid'))evs.push({ts:now,k:sh.state==='paid'?'Shard paid':'Shard proven',cls:'',text:(sh.i+1)+' of '+sh.n+' for '+(b.number?'#'+fmtN(b.number):b.hash)+(sh.prover?' by '+sh.prover:'')});});});
if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){var prev=cpState[c.index];cpState[c.index]=c.state;if(ready&&prev!==c.state)evs.push({ts:c.state==='locked'&&c.locked_at?new Date(String(c.locked_at).replace(' ','T')).getTime():now,k:'Checkpoint '+c.state,cls:c.state==='locked'?'lock':'',text:'#'+fmtN(c.index)+(c.fraction_total?', '+Math.round(c.fraction_total*100)+'% of weight':'')});});
evs.forEach(function(e){var id=e.k+'|'+e.text+'|'+Math.round(e.ts/1000);if(!evSeen[id]){evSeen[id]=1;evList.unshift({id:id,fresh:ready,ts:e.ts,k:e.k,cls:e.cls,text:e.text});}});
evList.sort(function(a,b){return b.ts-a.ts;});evList=evList.slice(0,14);ready=true;
var eel=$('events');if(evList.length){eel.innerHTML=evList.map(function(e){var f=e.fresh;e.fresh=false;return '<div'+(f?' class="new"':'')+'><span class="dot '+e.cls+'"></span><span class="what"><b>'+esc(e.k)+'</b> '+esc(e.text)+'</span><span class="t">'+hms(e.ts)+'</span></div>';}).join('');$('ev-count').textContent=evList.length+' recent';}
// the table: the displayed blocks, newest first
var rows=on.slice().sort(function(a,b){return b.blue_score-a.blue_score;}).slice(0,40);
$('tbody').innerHTML=rows.length?rows.map(function(b){var word=b.locked?'locked checkpoint':b.proven?'proven':b.color==='red'?'excluded':b.chain?'selected chain':b.color==='blue'?'included':'pending';return '<tr data-hash="'+esc(b.hash)+'"><td class="num">'+fmtN(b.blue_score)+'</td><td>'+esc(b.hash)+'</td><td>'+esc(b.miner)+'</td><td class="num">'+(b.parents||[]).length+'</td><td>'+word+'</td><td class="num">'+(b.number?fmtN(b.number):'')+'</td></tr>';}).join(''):'<tr><td colspan="6">No blocks in the window.</td></tr>';
}
// the inspector: the pack's selected record, kept fresh on every reply through onSelect(block, 'update'); the proof scene is IgneumProof
var proof=null;
function inspect(b){sel=b;$('insp-close').hidden=false;var mineB=mine&&b.miner===mine;$('i-kind').textContent=mineB?'your block':b.locked?'checkpoint':b.chain?'chain block':b.color==='red'?'excluded':'side block';
$('i-title').textContent=b.locked?'Checkpoint locked':b.proven?'Proof complete':(b.shards||[]).some(function(x){return x.state==='proving';})?'Proof in progress':'Block observed';
$('i-hash').textContent=b.hash+(b.number?' · #'+fmtN(b.number):'');
var sh=b.shards||[],done=sh.filter(function(x){return x.state==='verified'||x.state==='paid';}).length;
$('i-shard-count').textContent=sh.length?done+' / '+sh.length+' complete':'no shard plan yet';$('i-track').innerHTML=sh.map(function(x){return '<i class="'+esc(x.state)+'"></i>';}).join('');
$('i-shards').innerHTML=sh.length?sh.map(function(x,i){return '<li class="'+esc(x.state)+'"><span>Shard '+String(i+1).padStart(2,'0')+'</span><span>'+esc(x.state.charAt(0).toUpperCase()+x.state.slice(1))+'</span></li>';}).join(''):'<li class="muted">'+(b.chain?'No shard plan yet.':'Not a chain block: no shards.')+'</li>';
$('i-incl').textContent=(b.color==='blue'?'Included':b.color==='red'?'Excluded':'Pending')+(b.chain?' / selected chain':'');
$('i-miner').textContent=b.miner+(mineB?' (you)':'');$('i-blue').textContent=fmtN(b.blue_score);$('i-daa').textContent=fmtN(b.daa);
$('i-parents').textContent=(b.parents||[]).length+' observed'+((b.parents||[]).length?': '+b.parents.join(', '):'');$('i-time').textContent=hms(b.ts)+' UTC';
var fn=lastD&&lastD.finality,cp=null;if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){if(b.locked&&c.hash===b.hash)cp=c;});
$('i-cp').textContent=cp?'this block, #'+fmtN(cp.index)+(cp.fraction_total?', '+Math.round(cp.fraction_total*100)+'% of weight':''):'not a checkpoint';
$('i-lock').textContent=b.final?'Final (reported)':'Not marked final';
if(proof)proof.setBlock(b);}
function uninspect(){sel=null;$('insp-close').hidden=true;$('i-kind').textContent='select a block';$('i-title').textContent='Select a block';$('i-hash').textContent='Click the graph, or a row of the table below.';$('i-shard-count').textContent='not set';$('i-track').innerHTML='';$('i-shards').innerHTML='<li class="muted">No block selected.</li>';['i-incl','i-miner','i-blue','i-daa','i-parents','i-time','i-cp','i-lock'].forEach(function(id){$(id).textContent='not set';});if(proof)proof.setBlock(null);}
$('insp-close').addEventListener('click',function(){if(dag)dag.select(null,'clear');uninspect();});
// the four stats above the graph, from the module's own window
function miner(st){if(!dag)return;var cps=dag.getCheckpoints(),cp=cps.length?cps[cps.length-1]:null;
$('m-blocks').textContent=fmtN(st.blocks);$('m-win').textContent='last '+st.window+' s';$('m-keys').textContent=st.minerKeys+' miner keys · not a card count';$('lane-caption').textContent=st.minerKeys+' keys';
$('m-mine').textContent=mine?fmtN(st.own):'0';$('m-mine').classList.toggle('dim',!mine);$('m-key').textContent=mine||'not set';
$('m-proven').textContent=fmtN(st.proven);$('m-proven-pct').textContent=st.blocks?Math.round(st.proven/st.blocks*100)+'% of window':'no blocks';
$('m-cp').textContent=cp?(cp.state==='locked'?'Locked':'Pending'):'None';$('m-cp-w').textContent=cp&&cp.fraction_total!==null&&cp.fraction_total!==undefined?Math.round(cp.fraction_total*100)+'% weight':'';$('m-cp-note').textContent=cp?'#'+fmtN(cp.index)+' · blue score '+fmtN(cp.blue_score):'no checkpoint in the reply';
$('c-count').textContent=st.blocks+' blocks';var r=dag.getViewRange(),lo=null,hi=null;dag.getBlocks().forEach(function(b){if(b.ts>=r.start&&b.ts<=r.end){if(lo===null||b.blue_score<lo)lo=b.blue_score;if(hi===null||b.blue_score>hi)hi=b.blue_score;}});$('blue-range').textContent=lo===null?'blue score pending':'blue score '+fmtN(lo)+' to '+fmtN(hi);}
function mountDag(){if(dag||!window.IgneumDag)return;
dag=IgneumDag.mount($('dag'),{poll:false,window:60,fps:60,mine:mine||undefined,tooltip:$('dag-tip'),chip:$('dag-chip'),pauseButton:$('dag-pause'),
onWindow:function(w){$('z-pct').textContent=w+' s';if(lastD)render(lastD);},
onState:function(st,reason){var el=$('dag-state');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':esc(reason||st));if(proof)proof.setPaused(st!=='live'||dag.getStats().paused);},
onPause:function(p){$('dag-follow').classList.toggle('on',!p);if(proof)proof.setPaused(p||dag.getState().state!=='live');},
onFollow:function(f){$('dag-follow').classList.toggle('on',f);},
onStats:miner,
onSelect:function(b,why){if(b){var full=lastD&&(lastD.blocks||[]).filter(function(x){return x.hash===b.hash;})[0];inspect(Object.assign({},b,full?{number:full.number}:{}));}else uninspect();}});
if(window.IgneumProof&&!proof)proof=IgneumProof.mount($('proof'));
if(lastD)feed(lastD);}
function remount(){if(dag){dag.destroy();dag=null;}mountDag();}
function feed(d){if(!dag)return;dag.push(d);}
if(window.IgneumDag)mountDag();else{var ms=document.querySelector('script[src="/live-dag.js"]');if(ms)ms.addEventListener('load',mountDag);}
if(!window.IgneumProof){var ps=document.querySelector('script[src="/proof-core.js"]');if(ps)ps.addEventListener('load',function(){if(dag&&!proof)proof=IgneumProof.mount($('proof'));});}
document.querySelectorAll('.tab[data-filter]').forEach(function(t){t.addEventListener('click',function(){document.querySelectorAll('.tab[data-filter]').forEach(function(x){x.classList.toggle('on',x===t);x.setAttribute('aria-pressed',x===t?'true':'false');});if(dag)dag.setFilter(t.getAttribute('data-filter'));});});
$('dag-follow').addEventListener('click',function(){if(dag){dag.setPaused(false);dag.follow();}});
$('z-in').addEventListener('click',function(){if(dag)dag.setWindow(dag.getWindow()-15);});$('z-out').addEventListener('click',function(){if(dag)dag.setWindow(dag.getWindow()+15);});
window.__dbg={render:render,feed:feed,getDag:function(){return dag;}};
function setLive(st,reason){var el=$('live-state');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 · one node read every 2 s':st==='connecting'?'connecting to the observer':esc(reason||st));}
function poll(){fetch('/api/live?window=300',{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;setLive(d.state.stale?'stale':'live',d.state.stale?'observer stale':'');render(d);feed(d);}).catch(function(){failures++;if(failures>=3)setLive('failed','live feed unavailable');});}
poll();setInterval(function(){if(!document.hidden)poll();},2000);
})();
</script>
</body>
</html>