igneum/site/live.html
igneum-labs cd5d51cc0e site-ui-4: /live as the network observatory (prototype, not deployed)
The reference page's composition in our tokens and words, every number
from the observer: the header row (eyebrow, the title with its ember
full stop, one sentence, two buttons, the state word), six tiles in one
hairline row each with a definition, the block graph panel (our
live-dag.js in the graph slot, the block-count chip, selected chain
only, pause and follow, the inspector column with hash, lane, time, blue
score, DAA, chain, colour word, shards, checkpoint and lock state, the
drag-to-explore line, the zoom control, the legend row and the blue
score range), the three panels (block activity with per-minute bars,
mining identities with share bars and the reader's own key marked when
set, the event stream), the blocks table, our footer. live-dag.js gains
an onSelect hook on click (additive; the app's copy unchanged).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 08:30:23 +00:00

464 lines
42 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}
.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="Menu"><span class="bars"></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="/bench" data-nav="bench">Engineering log</a>
<a href="/evidence" data-nav="evidence">Evidence</a>
<a href="/explorer">Explorer</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, 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 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="toolbar">
<div class="title"><i></i>The block graph</div>
<span class="chip" id="c-count">0 blocks</span>
<div class="right">
<button type="button" class="tb" id="chain-only" aria-pressed="false">Selected chain only</button>
<span class="sep"></span>
<button type="button" class="tb" id="dag-pause" aria-pressed="false">&#10074;&#10074; Pause view</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>live devnet</div>
<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 class="chip-zoom" id="dag-chip" hidden>scroll to zoom · Esc to release</div></div>
<div class="gfoot">
<span>Drag to explore &nbsp;&middot;&nbsp; Select a block to inspect</span>
<span class="zoom"><button type="button" id="z-out" aria-label="Zoom out">&minus;</button><span id="z-pct">100%</span><button type="button" id="z-in" aria-label="Zoom in">+</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>
<div class="insp-empty" id="insp-empty">Click a block in the graph, or a row of the table below. Hover shows the short form.</div>
<div id="insp" hidden>
<div class="num" id="i-number"></div>
<div class="hash" id="i-hash"></div>
<dl class="insp">
<dt>Vote key</dt><dd id="i-miner"></dd>
<dt>Parents</dt><dd id="i-parents"></dd>
<dt>Selected chain</dt><dd id="i-chain"></dd>
<dt>Colour</dt><dd id="i-color"></dd>
<dt>Blue score</dt><dd id="i-blue"></dd>
<dt>DAA score</dt><dd id="i-daa"></dd>
<dt>Shards</dt><dd id="i-shards"></dd>
<dt>Checkpoint</dt><dd id="i-cp"></dd>
<dt>Lock</dt><dd id="i-lock"></dd>
<dt>Header time</dt><dd id="i-time"></dd>
</dl>
<p class="note">A selected-chain block is not final by itself. Only a checkpoint lock, signed by two thirds of all 30-day weight, makes the blocks under it final.</p>
<p style="margin:10px 0 0"><button type="button" class="linkish" id="insp-close">Clear</button></p>
</div>
</aside>
</div>
<div class="legend-row">
<div class="lg">
<span><i class="sw" style="background:var(--ember)"></i>Selected chain</span>
<span><i class="sw" style="border:1.5px solid var(--bone)"></i>Included</span>
<span><i class="sw" style="border:1.5px solid var(--line-2);opacity:.6"></i>Excluded</span>
<span><i class="sw" style="background:var(--molten);border-radius:50%;transform:none"></i>Locked checkpoint</span>
<span><i class="sw" style="width:14px;height:0;border-top:1px solid var(--ash);transform:none"></i>Parent reference</span>
</div>
<span id="blue-range">blue score pending</span>
</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. <button type="button" class="linkish" id="mine-btn">Mark my key</button></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>
<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="/litepaper#shoulders">Built on the shoulders</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 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: 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>
</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 · <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(){
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,chainOnly=false,zoomPct=100;
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){}if(lastD)render(lastD);}}
var row=t&&t.closest?t.closest('tr[data-hash]'):null;if(row&&lastD){var b=(lastD.blocks||[]).filter(function(x){return x.hash===row.getAttribute('data-hash');})[0];if(b)inspect(b,lastD);}});
// the inspector: a clicked block's facts from the observer, kept fresh on every read
function inspect(b,d){sel=b;$('insp-empty').hidden=true;$('insp').hidden=false;$('i-kind').textContent=b.locked?'checkpoint':b.chain?'chain block':b.color==='red'?'excluded':'side block';
$('i-number').textContent=b.number?'#'+fmtN(b.number):'blue '+fmtN(b.blue_score);$('i-hash').textContent=b.hash+(b.number?'':' · not yet planned onto the chain');
$('i-miner').textContent=b.miner+(mine&&b.miner===mine?' (you)':'');$('i-parents').textContent=(b.parents||[]).length?(b.parents.length+(b.parents.length===1?' reference: ':' references: ')+b.parents.join(', ')):'none';
$('i-chain').textContent=b.chain?'yes':'no';var col=$('i-color');col.textContent=b.color==='blue'?'blue, merged and paid':b.color==='red'?'red, excluded':'pending';col.className=b.color==='blue'?'hot':'';
$('i-blue').textContent=fmtN(b.blue_score);$('i-daa').textContent=fmtN(b.daa);
var sh=b.shards||[];$('i-shards').textContent=sh.length?sh.map(function(s){return (s.i+1)+'/'+s.n+' '+s.state;}).join(', '):(b.chain?'no plan yet':'none');
var fn=d.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):(b.final?'under the newest lock':'not a checkpoint');var lk=$('i-lock');lk.textContent=cp?(cp.state==='locked'?'locked'+(cp.fraction_total?', '+Math.round(cp.fraction_total*100)+'% of weight':''):cp.state):(b.final?'final':'not final');lk.className=(cp&&cp.state==='locked')||b.final?'hot':'';
$('i-time').textContent=hms(b.ts)+' UTC';}
function uninspect(){sel=null;$('insp').hidden=true;$('insp-empty').hidden=false;$('i-kind').textContent='select a block';}
$('insp-close').addEventListener('click',function(){uninspect();if(dag)dag.engage(false);});
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 w=dag?dag.getWindow():120,cut=now-w*1000,on=[],lo=null,hi=null;(d.blocks||[]).forEach(function(b){if(b.ts>=cut&&(!chainOnly||b.chain)){on.push(b);if(lo===null||b.blue_score<lo)lo=b.blue_score;if(hi===null||b.blue_score>hi)hi=b.blue_score;}});
$('c-count').textContent=on.length+' blocks';$('blue-range').textContent=lo===null?'blue score pending':'blue score '+fmtN(lo)+' to '+fmtN(hi);
// 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').innerHTML=(rest>0?'and '+rest+' more. ':'')+'Vote-key share of the displayed blocks. Several keys may belong to one GPU. <button type="button" class="linkish" id="mine-btn">'+(mine?'Change my key':'Mark my key')+'</button>';}
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>';
if(sel){var cur=(d.blocks||[]).filter(function(b){return b.hash===sel.hash;})[0];if(cur)inspect(cur,d);}
}
// the graph: the shared module, fed by this page's own reads so the chain-only toggle can filter what it draws
var failures=0;
function mountDag(){if(dag||!window.IgneumDag)return;dag=IgneumDag.mount($('dag'),{poll:false,window:120,tooltip:$('dag-tip'),chip:$('dag-chip'),pauseButton:$('dag-pause'),
onWindow:function(w){zoomPct=Math.round(120/w*100);$('z-pct').textContent=zoomPct+'%';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));},
onSelect:function(b){if(b&&lastD)inspect(b,lastD);else uninspect();}});
if(lastD)feed(lastD);}
function feed(d){if(!dag)return;if(!chainOnly){dag.push(d);return;}var c=Object.assign({},d);c.blocks=(d.blocks||[]).filter(function(b){return b.chain;});dag.push(c);}
if(window.IgneumDag)mountDag();else{var ms=document.querySelector('script[src="/live-dag.js"]');if(ms)ms.addEventListener('load',mountDag);}
$('chain-only').addEventListener('click',function(){chainOnly=!chainOnly;this.setAttribute('aria-pressed',chainOnly?'true':'false');this.classList.toggle('on',chainOnly);if(lastD){feed(lastD);render(lastD);}});
$('dag-pause').addEventListener('click',function(){var p=this.getAttribute('aria-pressed')!=='true';this.setAttribute('aria-pressed',p?'true':'false');$('dag-follow').classList.toggle('on',!p);});
$('dag-follow').addEventListener('click',function(){if(dag)dag.setPaused(false);$('dag-pause').setAttribute('aria-pressed','false');this.classList.add('on');});
$('z-in').addEventListener('click',function(){if(dag)dag.zoom(0.75);});$('z-out').addEventListener('click',function(){if(dag)dag.zoom(1/0.75);});
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>