the project lead's rule, 4 October 2026: the mark sits in a black square, never in a circle, never on another colour, 20% clear space; the Mac app is the model. brand/master/igneum-mark-square.svg (1024, #0C0C0E, the exact header polygons at 62%) and igneum-mark-square-rounded.svg (Apple's 824-on-1024 grid, DMG volume icon only). make-icons.py now rasterises the masters (rsvg-convert if installed, else Pillow draws the polygons) into igneum.icns (plain square, 16 to 1024), igneum-volume.icns, igneum.ico (each size from the vector), the Inno art, the DMG background, site favicons (favicon.ico 16/32/48, favicon-32, apple-touch 180, 192, 512, maskable 512 + manifest entry), relay favicons, and brand/profile (400/512/1024, github-org-512, X banner). Every page head's inline SVG favicon and the relay header lose the ring. The .rc and Info.plist paths are unchanged (same file names). docs/brand/before holds the old set. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
715 lines
55 KiB
HTML
715 lines
55 KiB
HTML
<!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, the GPU-mined layer 1</title>
|
|
<meta name="description" content="A GPU-mined chain whose mining program rewrites itself every hour. Miners also prove the blocks. No premine, no stake, no fee to any team.">
|
|
<link rel="canonical" href="https://igneum.network/">
|
|
<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, the GPU-mined layer 1">
|
|
<meta property="og:description" content="A GPU-mined chain whose mining program rewrites itself every hour. Miners also prove the blocks. No premine, no stake, no fee to any team.">
|
|
<meta property="og:url" content="https://igneum.network/">
|
|
<meta property="og:image" content="https://igneum.network/og-small.png">
|
|
<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, the GPU-mined layer 1">
|
|
<meta name="twitter:description" content="A GPU-mined chain whose mining program rewrites itself every hour. Miners also prove the blocks. No premine, no stake, no fee to any team.">
|
|
<meta name="twitter:image" content="https://igneum.network/og-small.png">
|
|
<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">
|
|
<script type="application/ld+json">
|
|
{"@context":"https://schema.org","@type":"Organization","name":"Igneum","url":"https://igneum.network/","logo":"https://igneum.network/icon-512.png","sameAs":["https://github.com/igneum-network"]}
|
|
</script>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Unbounded:wght@500;700;900&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
|
|
<style>
|
|
:root{--obsidian:#0C0C0E;--graphite:#16161A;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;
|
|
/* one system for every page: container, gutter, section rhythm, card and tile boxes, heading scale */
|
|
--max:1200px;--gutter:clamp(16px,4vw,32px);--sec:clamp(56px,8vw,96px);--head:clamp(40px,6vw,64px);
|
|
--card-pad:clamp(18px,3vw,28px);--card-r:18px;--tile-pad:18px 20px;--tile-r:14px;--gap:24px;--gap-tile:12px;
|
|
--fs-hero:clamp(40px,7vw,76px);--fs-h1:clamp(32px,5.5vw,56px);--fs-h2:clamp(28px,4.2vw,44px);--fs-h3:clamp(18px,2vw,22px);--fs-tile:clamp(20px,2.2vw,26px)}
|
|
*{box-sizing:border-box}
|
|
html{scroll-behavior:smooth}
|
|
/* anchors land with the heading 11 px under the sticky bar at every width: a padded section subtracts its own top padding */
|
|
:root{--sm:84px}
|
|
@media (max-width:900px){:root{--sm:80px}}
|
|
section,[id]{scroll-margin-top:var(--sm)}
|
|
section[id]{scroll-margin-top:calc(var(--sm) - var(--sec))}
|
|
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
|
|
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:'IBM Plex Sans',system-ui,-apple-system,sans-serif;font-size:17px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
|
|
a{color:inherit;text-decoration:none}
|
|
a:hover{color:var(--molten)}
|
|
a:focus-visible,button:focus-visible{outline:2px solid var(--ember);outline-offset:3px;border-radius:6px}
|
|
img,svg,canvas{max-width:100%}
|
|
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gutter)}
|
|
.mono{font-family:'IBM Plex Mono',monospace}
|
|
.eyebrow{font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ash)}
|
|
.eyebrow.ember{color:var(--ember)}
|
|
h1,h2,h3{font-family:'Unbounded',sans-serif;margin:0;line-height:1.08;text-wrap:balance}
|
|
h1{font-weight:900;font-size:var(--fs-hero);letter-spacing:-.01em}
|
|
h2{font-weight:700;font-size:var(--fs-h2)}
|
|
h3{font-weight:700;font-size:var(--fs-h3)}
|
|
p{margin:0}
|
|
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:12px 22px;border-radius:10px;font-weight:600;font-size:16px;border:1px solid var(--line-2);color:var(--bone);background:transparent;cursor:pointer;transition:transform .15s ease,background .15s ease,border-color .15s ease}
|
|
.btn:hover{transform:translateY(-1px);border-color:var(--ash);color:var(--bone)}
|
|
.btn.primary{background:var(--ember);color:var(--ember-ink);border-color:var(--ember)}
|
|
.btn.primary:hover{background:#FF6A2B;border-color:#FF6A2B;color:var(--ember-ink)}
|
|
.btn.dark{background:var(--obsidian);color:var(--bone);border-color:var(--obsidian)}
|
|
.btn.dark:hover{background:#222226;color:var(--bone)}
|
|
.card{background:var(--graphite);border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);min-width:0}
|
|
.grid{display:grid;gap:var(--gap);grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr))}
|
|
.reveal{opacity:0;transform:translateY(18px);transition:opacity .7s ease,transform .7s ease}
|
|
.reveal.in{opacity:1;transform:none}
|
|
@media (prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none;transition:none}}
|
|
|
|
/* nav */
|
|
.nav{position:sticky;top:0;z-index:20;background:rgba(12,12,14,.82);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid rgba(42,42,48,.6)}
|
|
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:68px}
|
|
.brand{display:flex;align-items:center;gap:10px}
|
|
.brand .word{font-family:'Unbounded',sans-serif;font-weight:900;font-size:20px;letter-spacing:.06em}
|
|
.links{display:flex;align-items:center;gap:26px;font-size:15px;font-weight:500}
|
|
.links .btn{min-height:42px;padding:10px 16px;font-size:15px}
|
|
.burger{display:none;background:none;border:1px solid var(--line-2);color:var(--bone);border-radius:10px;width:44px;height:44px;align-items:center;justify-content:center;cursor:pointer}
|
|
@media (max-width:900px){
|
|
.links{display:none;position:absolute;left:0;right:0;top:68px;background:var(--obsidian);border-bottom:1px solid var(--line);padding:16px var(--gutter) 24px;flex-direction:column;align-items:stretch;gap:6px}
|
|
.links a{padding:12px 6px;border-radius:8px}
|
|
.links .btn{width:100%;margin-top:6px}
|
|
.links.open{display:flex}
|
|
.burger{display:inline-flex}
|
|
}
|
|
|
|
/* hero */
|
|
.hero{position:relative;padding-block:clamp(56px,9vw,112px) clamp(40px,6vw,72px);overflow:hidden}
|
|
.hero canvas{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;-webkit-mask-image:linear-gradient(90deg,rgba(0,0,0,.08) 0,rgba(0,0,0,.35) 45%,rgba(0,0,0,.9) 100%);mask-image:linear-gradient(90deg,rgba(0,0,0,.08) 0,rgba(0,0,0,.35) 45%,rgba(0,0,0,.9) 100%)}
|
|
@media (max-width:979px){.hero canvas{-webkit-mask-image:linear-gradient(180deg,rgba(0,0,0,.12) 0,rgba(0,0,0,.12) 55%,rgba(0,0,0,.8) 100%);mask-image:linear-gradient(180deg,rgba(0,0,0,.12) 0,rgba(0,0,0,.12) 55%,rgba(0,0,0,.8) 100%)}}
|
|
.hero .wrap{position:relative;display:grid;gap:32px;grid-template-columns:1fr;align-items:center}
|
|
@media (min-width:980px){.hero .wrap{grid-template-columns:1.15fr .85fr;gap:32px 56px}}
|
|
.hero-copy{display:flex;flex-direction:column;gap:24px}
|
|
.hero p.lead{font-size:clamp(17px,2vw,20px);color:var(--ink-2);max-width:56ch}
|
|
.cta{display:flex;flex-wrap:wrap;gap:12px}
|
|
/* live stats strip: hidden until the observer reports fresh numbers; spans the full hero width, tiles share the economics tile spec */
|
|
.livestrip{display:none;flex-direction:column;gap:var(--gap-tile);grid-column:1/-1;min-width:0}
|
|
.livestrip.on{display:flex}
|
|
.livestrip-head{display:flex;align-items:center;gap:8px;font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--molten)}
|
|
.livestrip-head a{color:var(--molten)}
|
|
.livestrip-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gap-tile)}
|
|
.livestrip .tile .v{white-space:nowrap;font-variant-numeric:tabular-nums}
|
|
@media (max-width:700px){.livestrip-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
.verify{display:flex;flex-direction:column;gap:18px}
|
|
.verify-head{display:flex;justify-content:space-between;align-items:center;gap:12px}
|
|
.live{display:inline-flex;align-items:center;gap:8px;font-family:'IBM Plex Mono',monospace;font-size:12px;color:var(--molten)}
|
|
.dot{width:8px;height:8px;border-radius:50%;background:var(--molten);display:inline-block;animation:pulse 2s ease-in-out infinite}
|
|
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(255,179,92,.5)}50%{box-shadow:0 0 0 8px rgba(255,179,92,0)}}
|
|
.verify-row{display:flex;align-items:center;gap:16px}
|
|
.tick{width:56px;height:56px;border-radius:50%;background:var(--ember);display:flex;align-items:center;justify-content:center;flex:0 0 56px}
|
|
.tick svg path{stroke-dasharray:30;stroke-dashoffset:30;animation:draw 1s .4s ease forwards}
|
|
@keyframes draw{to{stroke-dashoffset:0}}
|
|
.verify-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
|
|
.cell{background:var(--obsidian);border-radius:12px;padding:14px;display:flex;flex-direction:column;gap:4px}
|
|
.cell .k{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.12em;color:var(--ash)}
|
|
.cell .v{font-family:'IBM Plex Mono',monospace;font-size:15px}
|
|
|
|
/* stats */
|
|
.stats{border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
|
|
.stats .wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:20px 24px;padding-block:28px}
|
|
.stat .v{font-family:'Unbounded',sans-serif;font-weight:700;font-size:var(--fs-tile);color:var(--ember);line-height:1.1}
|
|
.stat .k{margin-top:6px;font-size:14px;color:var(--ash);line-height:1.35;text-wrap:balance}
|
|
|
|
/* rhythm: every section keeps --sec from its content to the next boundary; two dark sections in a row share one */
|
|
section{padding-block:var(--sec)}
|
|
section:not(.light)+section:not(.light){padding-top:0;scroll-margin-top:var(--sm)}
|
|
.sec-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:flex-end;gap:16px;margin-bottom:32px}
|
|
.sec-head p{font-size:16px;color:var(--ash);max-width:46ch}
|
|
|
|
/* dag */
|
|
.viz{overflow:hidden}
|
|
.viz-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 20px;margin-bottom:12px}
|
|
.viz-stats{display:flex;gap:18px;font-size:12px;color:var(--ash)}
|
|
.viz-stats b{color:var(--bone);font-weight:500;font-variant-numeric:tabular-nums}
|
|
#chain{display:block;width:100%;height:clamp(200px,36vw,340px);border-radius:12px;background:var(--obsidian)}
|
|
.dag{overflow:hidden}
|
|
.dag-rows{display:flex;flex-direction:column;gap:var(--dg,14px);--bs:34px;--dg:14px}
|
|
@media (max-width:640px){.dag-rows{--bs:26px;--dg:9px}}
|
|
.dag-row{display:flex;gap:var(--dg);align-items:center;overflow:hidden}
|
|
.blk{width:var(--bs);height:var(--bs);flex:0 0 var(--bs);border-radius:calc(var(--bs) * .24);border:2px solid var(--line-2);transition:background .5s ease,border-color .5s ease,transform .3s ease}
|
|
.blk.proving{background:var(--molten);border-color:var(--molten)}
|
|
.blk.proven{background:var(--ember);border-color:var(--ember)}
|
|
.blk.lock{border-color:var(--ember);background:transparent;display:flex;align-items:center;justify-content:center}
|
|
.blk.lock svg{width:50%;height:50%}
|
|
.blk.new{animation:pop .4s ease}
|
|
@keyframes pop{from{transform:scale(.4);opacity:0}to{transform:scale(1);opacity:1}}
|
|
.legend{display:flex;flex-wrap:wrap;gap:20px;margin-top:24px;font-size:14px;color:var(--ink-2)}
|
|
.legend span{display:inline-flex;align-items:center;gap:8px}
|
|
.sw{width:14px;height:14px;border-radius:4px;display:inline-block}
|
|
pre{margin:0;font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.6;color:var(--ink-2);white-space:pre-wrap;min-height:9.6em}
|
|
.caret::after{content:'▍';color:var(--ember);animation:blink 1s steps(1) infinite}
|
|
@keyframes blink{50%{opacity:0}}
|
|
.feed{display:flex;flex-direction:column;gap:10px;font-family:'IBM Plex Mono',monospace;font-size:13px;color:var(--ink-2)}
|
|
.feed div{display:flex;justify-content:space-between;gap:12px;border-bottom:1px solid var(--line);padding-bottom:8px}
|
|
.feed div:last-child{border-bottom:0}
|
|
|
|
/* jobs cards */
|
|
.jobs{display:grid;gap:var(--gap);grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr))}
|
|
.job{display:flex;flex-direction:column;gap:14px;border:1px solid var(--line);border-radius:var(--card-r);padding:var(--card-pad);transition:border-color .2s ease,transform .2s ease}
|
|
.job:hover{border-color:var(--line-2);transform:translateY(-2px)}
|
|
.job p{font-size:15px;color:var(--ink-2)}
|
|
.job a.more{color:var(--ember);font-weight:600;font-size:15px}
|
|
.pt{font-size:15px;color:var(--ash);max-width:60ch}
|
|
.lp{display:inline-block;margin-top:24px;color:var(--ember);font-weight:600;font-size:15px}
|
|
.light .pt{color:#55534F}
|
|
.cmp{border-collapse:collapse;width:100%;font-size:15px}
|
|
.cmp th,.cmp td{text-align:left;padding:12px 20px;border-top:1px solid var(--line)}
|
|
.cmp thead tr{background:var(--obsidian)}
|
|
.cmp th{border-top:0}
|
|
@media (max-width:480px){.cmp th,.cmp td{padding:10px 12px;font-size:14px}}
|
|
|
|
/* miners */
|
|
.light{background:var(--bone);color:var(--obsidian)}
|
|
.light .card{background:#fff;border-color:#E4E0D8}
|
|
.light .sec-head p{color:#55534F}
|
|
.light .eyebrow{color:#7A776F}
|
|
.light .job p{color:#3D3B37}
|
|
|
|
/* journey */
|
|
.journey{display:grid;gap:var(--gap-tile);grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr))}
|
|
.phase{border:1px solid var(--line);border-radius:var(--tile-r);padding:var(--tile-pad);display:flex;flex-direction:column;gap:6px;position:relative;transition:border-color .2s ease;min-width:0}
|
|
.phase .w{font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--ash)}
|
|
.phase .n{font-weight:600;font-size:15px;text-wrap:balance}
|
|
.phase .g{font-size:12px;color:var(--ash);line-height:1.4;text-wrap:balance}
|
|
.phase.done{border-color:var(--line-2)}
|
|
.phase.done .n::after{content:' ✓';color:var(--molten)}
|
|
.phase.active{border:2px solid var(--ember);box-shadow:0 0 0 0 rgba(242,84,27,.35);animation:glow 2.4s ease-in-out infinite}
|
|
@keyframes glow{50%{box-shadow:0 0 0 10px rgba(242,84,27,0)}}
|
|
.phase.active .w{color:var(--ember)}
|
|
.now{display:inline-flex;align-items:center;gap:8px;font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--ember);letter-spacing:.12em}
|
|
.log{display:flex;flex-direction:column;gap:0;margin-top:var(--gap);border:1px solid var(--line);border-radius:var(--card-r);background:var(--graphite);padding:4px var(--card-pad)}
|
|
.log div{display:flex;gap:18px;padding:14px 0;border-bottom:1px solid var(--line);font-size:15px;color:var(--ink-2)}
|
|
.log div:last-of-type{border-bottom:0}
|
|
.log div.more{display:none}
|
|
.log.open div.more{display:flex}
|
|
.log .log-toggle{align-self:flex-start;margin:12px 0 10px;background:none;border:1px solid var(--line);border-radius:999px;color:var(--ink-2);font:inherit;font-size:13px;padding:8px 14px;cursor:pointer}
|
|
.log .log-toggle:hover{border-color:var(--ink-2)}
|
|
.log .d{font-family:'IBM Plex Mono',monospace;font-size:12px;color:var(--ash);flex:0 0 92px;padding-top:3px}
|
|
|
|
/* economics */
|
|
.bar{display:flex;height:22px;border-radius:8px;overflow:hidden}
|
|
.bar div{transition:width 1.2s ease}
|
|
.tiles{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gap-tile)}
|
|
.tile{background:var(--graphite);border:1px solid var(--line);border-radius:var(--tile-r);padding:var(--tile-pad);min-width:0}
|
|
.tile .v{font-family:'Unbounded',sans-serif;font-weight:700;font-size:var(--fs-tile);line-height:1.1;color:var(--bone)}
|
|
.tile .k{font-size:14px;color:var(--ash);margin-top:6px;line-height:1.35;text-wrap:balance}
|
|
|
|
.band{background:var(--ember);color:var(--ember-ink)}
|
|
.band .wrap{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:24px;padding-block:56px}
|
|
.band h2{font-weight:900;font-size:clamp(26px,3.5vw,36px)}
|
|
.band p{font-size:16px;max-width:56ch}
|
|
footer{border-top:1px solid var(--line)}
|
|
footer .wrap{padding-block:48px 32px}
|
|
.foot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:32px}
|
|
.foot-brand{display:flex;flex-direction:column;gap:12px;max-width:34ch}
|
|
.foot-brand p{color:var(--ash);font-size:14px}
|
|
.foot-col{display:flex;flex-direction:column;gap:10px;font-size:15px}
|
|
.foot-col .eyebrow{margin-bottom:4px}
|
|
.foot-col a{color:var(--ink-2)}
|
|
.foot-col a:hover{color:var(--molten)}
|
|
.foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px 24px;margin-top:36px;padding-top:20px;border-top:1px solid var(--line);font-size:13px;color:var(--ash)}
|
|
.foot-base span{text-wrap:balance}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="nav" aria-label="Main">
|
|
<div class="wrap">
|
|
<a href="#top" class="brand" aria-label="Igneum home">
|
|
<svg viewBox="0 0 100 100" width="30" height="30" aria-hidden="true"><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></svg>
|
|
<span class="word">IGNEUM</span>
|
|
</a>
|
|
<button class="burger" id="burger" aria-expanded="false" aria-controls="links" aria-label="Menu"><svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg></button>
|
|
<div class="links" id="links">
|
|
<a href="#mine">Mine</a>
|
|
<a href="#prove">Prove</a>
|
|
<a href="#build">Build</a>
|
|
<a href="#randomx">vs RandomX</a>
|
|
<a href="#journey">Journey</a>
|
|
<a href="#economics">Economics</a>
|
|
<a href="/litepaper">Litepaper</a>
|
|
<a href="/bench">Log</a>
|
|
<a href="/evidence">Evidence</a>
|
|
<a href="/live">Live</a>
|
|
<a href="#mine" class="btn primary">Get the miner</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<header class="hero" id="top">
|
|
<canvas id="bg" aria-hidden="true"></canvas>
|
|
<div class="wrap">
|
|
<div class="hero-copy">
|
|
<div class="eyebrow ember">GPUs are back · for good</div>
|
|
<h1>Mined by GPUs.<br>Proven by fire.</h1>
|
|
<p class="lead">The first chain built so no chip can ever take your place. A mining program that rewrites itself every hour, so no chip can be built for it. The same card proves every block and gets paid for it. No premine, no stake, no foundation, no merge to anything else, ever.</p>
|
|
<div class="cta">
|
|
<a href="#mine" class="btn primary">Start mining</a>
|
|
<a href="/litepaper" class="btn">Read the litepaper</a>
|
|
</div>
|
|
</div>
|
|
<div class="card verify reveal">
|
|
<div class="verify-head">
|
|
<div class="eyebrow">This tab · light client</div>
|
|
<div class="live" data-lc="badge"><span class="dot"></span><span data-lc="badge-text">PREVIEW</span></div>
|
|
</div>
|
|
<div class="verify-row">
|
|
<div class="tick"><svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="#0C0C0E" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"></path></svg></div>
|
|
<div>
|
|
<h3 style="font-size:20px">Browser checks Igneum</h3>
|
|
<p style="font-size:14px;color:var(--ash)" data-lc="line">A checkpoint certificate, verified in this tab. Voter list from the node.</p>
|
|
</div>
|
|
</div>
|
|
<div class="verify-grid">
|
|
<div class="cell"><div class="k">BLOCK PROOF</div><div class="v" data-lc="proof" title="Execution proofs do not exist yet">not yet</div></div>
|
|
<div class="cell"><div class="k">CHECKPOINT</div><div class="v" data-lc="checkpoint">checking</div></div>
|
|
<div class="cell"><div class="k">PROOF SYSTEM</div><div class="v" data-lc="system">BLS aggregate, version 1</div></div>
|
|
<div class="cell"><div class="k">VERIFIED IN THIS TAB</div><div class="v" style="color:var(--molten)" data-lc="tab">checking</div></div>
|
|
</div>
|
|
<script type="module" src="/verify/verify.js"></script>
|
|
</div>
|
|
<div class="livestrip" id="livestrip" aria-live="off">
|
|
<div class="livestrip-head"><span class="dot"></span><a href="/live">live devnet</a></div>
|
|
<div class="livestrip-grid">
|
|
<div class="tile"><div class="v" id="ls-miners">0</div><div class="k">miners active</div></div>
|
|
<div class="tile"><div class="v" id="ls-bps">0.0</div><div class="k">blocks / s</div></div>
|
|
<div class="tile"><div class="v" id="ls-hash">0.0 MH/s</div><div class="k">hash rate</div></div>
|
|
<div class="tile"><div class="v" id="ls-blocks">0</div><div class="k">devnet blocks</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="stats">
|
|
<div class="wrap">
|
|
<div class="stat reveal"><div class="v" data-count="1" data-suffix=" / s">1 / s</div><div class="k">blocks, rising to 10</div></div>
|
|
<div class="stat reveal"><div class="v">~60 s</div><div class="k">to a proof at launch</div></div>
|
|
<div class="stat reveal"><div class="v" data-count="0">0</div><div class="k">premine</div></div>
|
|
<div class="stat reveal"><div class="v" data-count="4" data-suffix="B">4B</div><div class="k">IGN hard cap, ever</div></div>
|
|
<div class="stat reveal"><div class="v" data-count="100" data-suffix="%">100%</div><div class="k">to miners and provers</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<section id="prove">
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<h2>Watch the chain prove itself</h2>
|
|
<p>Blocks every second, proven in shards, locked every 30 seconds.</p>
|
|
</div>
|
|
<div class="card viz reveal" aria-label="Animated block DAG, simulated preview">
|
|
<div class="viz-head">
|
|
<div class="eyebrow" id="viz-label"><span class="dot" style="background:var(--ember);margin-right:8px"></span>simulated preview</div>
|
|
<div class="viz-stats mono"><span>blocks <b id="c-blocks">0</b></span><span>proven <b id="c-proven">0</b></span><span>locked <b id="c-locked">0</b></span></div>
|
|
</div>
|
|
<canvas id="chain" aria-hidden="true"></canvas>
|
|
<div class="legend">
|
|
<span><i class="sw" style="border:2px solid var(--line-2)"></i>mined</span>
|
|
<span><i class="sw" style="background:var(--molten)"></i>shards being proven</span>
|
|
<span><i class="sw" style="background:var(--ember)"></i>proven</span>
|
|
<span><i class="sw" style="border:3px solid var(--ember);border-radius:50%;background:transparent"></i>locked checkpoint, final</span>
|
|
</div>
|
|
</div>
|
|
<div class="grid" style="margin-top:24px">
|
|
<div class="card reveal" style="display:flex;flex-direction:column;gap:16px">
|
|
<div class="verify-head"><div class="eyebrow">This hour's program</div><div class="mono" style="font-size:12px;color:var(--molten)">next in <span id="countdown">59:59</span></div></div>
|
|
<pre id="program" class="caret"></pre>
|
|
<p class="pt">A new program every hour. Last hour's chip is already obsolete.</p>
|
|
</div>
|
|
<div class="card reveal" style="display:flex;flex-direction:column;gap:16px">
|
|
<div class="eyebrow">Proofs sold to other chains</div>
|
|
<div class="feed">
|
|
<div><span>rollup · batch proof</span><span style="color:var(--molten)">at testnet</span></div>
|
|
<div><span>bridge · state proof</span><span style="color:var(--molten)">at testnet</span></div>
|
|
<div><span>rollup · fault proof</span><span style="color:var(--ash)">at testnet</span></div>
|
|
<div><span>IGN burned from jobs</span><span style="color:var(--ember)">phase two</span></div>
|
|
</div>
|
|
<p class="pt">The same cards sell proofs to rollups and bridges. Jobs are paid on the customer's chain at launch; settlement in IGN with a 10% burn follows the proof bridge in phase two.</p>
|
|
</div>
|
|
</div>
|
|
<a href="/litepaper#proving" class="lp reveal">Read more in the litepaper</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="randomx">
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<h2>Monero's idea, finished for GPUs</h2>
|
|
<p>The random program that kept chips off Monero, rebuilt for graphics cards.</p>
|
|
</div>
|
|
<div class="card reveal" style="overflow-x:auto;padding:0;max-width:640px">
|
|
<table class="cmp">
|
|
<thead><tr><th class="eyebrow">Property</th><th class="eyebrow">RandomX</th><th class="eyebrow"><span style="color:var(--ember)">Igneum</span></th></tr></thead>
|
|
<tbody>
|
|
<tr><td>Who mines</td><td>CPUs</td><td>GPUs</td></tr>
|
|
<tr><td>Program changes</td><td>Every hash</td><td>Every hour</td></tr>
|
|
<tr><td>Memory</td><td>2 GB, fixed</td><td>2 GB, growing</td></tr>
|
|
<tr><td>Verified by</td><td>Any CPU</td><td>Any CPU</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p class="pt reveal" style="margin-top:20px">The program keeps changing on a schedule fixed at genesis. Nobody touches it.</p>
|
|
<a href="/litepaper#randomx" class="lp reveal">Read more in the litepaper</a>
|
|
<p class="reveal" style="margin-top:28px;font-size:14px;color:var(--ash);max-width:72ch">Built on the shoulders: Kaspa's GHOSTDAG and node, Monero's RandomX idea, Chia's class-group VDF, Ethereum's EVM, Succinct's SP1, BLS12-381 and Bitcoin's address format.<br>What changed, why, and how it is measured: <a href="/litepaper#shoulders" style="color:var(--ember)">the provenance section of the litepaper</a>.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="build">
|
|
<div class="wrap">
|
|
<h2 class="reveal" style="margin-bottom:28px">One chain, three jobs</h2>
|
|
<div class="jobs">
|
|
<div class="job reveal">
|
|
<svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="#F2541B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="12" rx="2"></rect><path d="M7 9h4M7 13h2M15 9v4M18 9v4M9 21h6"></path></svg>
|
|
<h3>Mine</h3>
|
|
<p>Any 4 GB card. 80% of each block to its finder.</p>
|
|
<a href="#mine" class="more">Get the miner</a>
|
|
</div>
|
|
<div class="job reveal">
|
|
<svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="#F2541B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7l8-4z"></path><path d="M9 12l2 2 4-4"></path></svg>
|
|
<h3>Prove</h3>
|
|
<p>The same card proves shards and sells proofs to other chains.</p>
|
|
<a href="#prove" class="more">How proving pays</a>
|
|
</div>
|
|
<div class="job reveal">
|
|
<svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="#F2541B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 7l-5 5 5 5M16 7l5 5-5 5M14 4l-4 16"></path></svg>
|
|
<h3>Build</h3>
|
|
<p>Ethereum bytecode, with the differences documented. Proofs of any computation from the miners' cards, verified by the chain itself. 20% of priority fees to the contracts that ran, stated at the fee levels that exist.</p>
|
|
<a href="/litepaper#builders-ask" class="more">Deploy on Igneum</a>
|
|
</div>
|
|
</div>
|
|
<a href="/litepaper#building" class="lp reveal">Read more in the litepaper</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="mine" class="light">
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<h2>Three ways a card gets paid</h2>
|
|
<p>One client switches the card between hashing and proving by itself.</p>
|
|
</div>
|
|
<div class="jobs">
|
|
<div class="card reveal" style="display:flex;flex-direction:column;gap:10px"><div class="eyebrow">01</div><h3>Block reward</h3><p style="font-size:15px;color:#3D3B37">80% of emission to the miner who wins the block.</p></div>
|
|
<div class="card reveal" style="display:flex;flex-direction:column;gap:10px"><div class="eyebrow">02</div><h3>Proving the chain</h3><p style="font-size:15px;color:#3D3B37">20% of emission plus a share of every block's gas.</p></div>
|
|
<div class="card reveal" style="display:flex;flex-direction:column;gap:10px"><div class="eyebrow">03</div><h3>Proving for others</h3><p style="font-size:15px;color:#3D3B37">Rollups and bridges pay for proofs in their own money.</p></div>
|
|
</div>
|
|
<div class="cta reveal" style="margin-top:32px;align-items:center">
|
|
<a href="#journey" class="btn dark"><svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M3 5.5l7.5-1v7H3zM11.5 4.3L21 3v8.5h-9.5zM3 12.5h7.5v7L3 18.5zM11.5 12.5H21V21l-9.5-1.3z"/></svg>Windows</a>
|
|
<a href="#journey" class="btn dark"><svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M16.4 12.6c0-2.3 1.9-3.4 2-3.5-1.1-1.6-2.8-1.8-3.4-1.8-1.4-.1-2.8.8-3.5.8-.7 0-1.8-.8-3-.8-1.5 0-3 .9-3.8 2.3-1.6 2.8-.4 7 1.2 9.3.8 1.1 1.7 2.4 2.9 2.3 1.2 0 1.6-.7 3-.7s1.8.7 3 .7c1.3 0 2-1.1 2.8-2.3.9-1.3 1.2-2.6 1.3-2.6-.1 0-2.5-.9-2.5-3.7zM14.1 5.8c.6-.8 1.1-1.9.9-3-.9 0-2 .6-2.7 1.4-.6.7-1.1 1.8-1 2.9 1.1.1 2.1-.5 2.8-1.3z"/></svg>macOS</a>
|
|
<a href="#journey" class="btn dark"><svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M12 2c-2.4 0-4 1.9-4 4.6 0 1.2.2 2 0 2.8-.6 1.4-2.1 2.9-2.6 4.9-.3 1.1-.1 2 .3 2.6-.6.4-1.3 1-1.1 1.7.3 1 2.1 1.2 3.2 1.8.7.4 1.5.6 2.1.1.6.2 1.3.3 2.1.3s1.5-.1 2.1-.3c.6.5 1.4.3 2.1-.1 1.1-.6 2.9-.8 3.2-1.8.2-.7-.5-1.3-1.1-1.7.4-.6.6-1.5.3-2.6-.5-2-2-3.5-2.6-4.9-.2-.8 0-1.6 0-2.8C16 3.9 14.4 2 12 2zm-1.4 4.2c.5 0 .8.5.8 1.2s-.3 1.2-.8 1.2-.8-.5-.8-1.2.3-1.2.8-1.2zm2.8 0c.5 0 .8.5.8 1.2s-.3 1.2-.8 1.2-.8-.5-.8-1.2.3-1.2.8-1.2zM12 9.3c.9 0 1.9.5 1.9 1s-1 1.2-1.9 1.2-1.9-.7-1.9-1.2 1-1 1.9-1zm0 3.4c2.2 0 3.6 2.6 3.6 4.4 0 1.5-1.6 2.3-3.6 2.3s-3.6-.8-3.6-2.3c0-1.8 1.4-4.4 3.6-4.4z"/></svg>Linux</a>
|
|
<a href="#journey" class="btn" style="color:#0C0C0E;border-color:#0C0C0E"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M12 2.5l8.2 4.75v9.5L12 21.5l-8.2-4.75v-9.5z"/><path d="M12 7.5l4.3 2.5v5L12 17.5l-4.3-2.5v-5z"/></svg>HiveOS</a>
|
|
<p style="font-size:14px;color:#55534F">One click: install, press start, the card mines and proves to a wallet it makes for you. Public testnet first.<br>Download only from this domain. Nobody from Igneum will ask for your seed.</p>
|
|
</div>
|
|
<a href="/litepaper#miners" class="lp reveal">Read more in the litepaper</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="journey">
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<div>
|
|
<h2>Where Igneum is right now</h2>
|
|
<div class="now" style="margin-top:12px"><span class="dot" style="background:var(--ember)"></span><span id="stage-label">LOADING</span></div>
|
|
</div>
|
|
<p>Thirteen months, four public gates, each a measurement published pass or fail.</p>
|
|
</div>
|
|
<div class="journey reveal" id="phases"></div>
|
|
<div class="log reveal" id="log"></div>
|
|
<a href="/litepaper#roadmap" class="lp reveal">Read more in the litepaper</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="economics">
|
|
<div class="wrap">
|
|
<div style="display:grid;gap:48px;grid-template-columns:repeat(auto-fit,minmax(min(100%,380px),1fr));align-items:center">
|
|
<div class="reveal" style="display:flex;flex-direction:column;gap:20px">
|
|
<h2>Every coin, mined</h2>
|
|
<p style="color:var(--ink-2)">Hard cap of 4 billion IGN, halving every two years for ever.</p>
|
|
<div style="display:flex;flex-direction:column;gap:10px">
|
|
<div class="bar" id="bar"><div style="width:0;background:var(--ember)" data-w="80%"></div><div style="width:0;background:var(--molten)" data-w="20%"></div></div>
|
|
<div class="legend mono" style="margin-top:4px;font-size:13px">
|
|
<span><i class="sw" style="background:var(--ember)"></i>80% miners</span>
|
|
<span><i class="sw" style="background:var(--molten)"></i>20% provers</span>
|
|
<span><i class="sw" style="border:1px solid var(--line-2)"></i>0% anyone else</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tiles reveal">
|
|
<div class="tile"><div class="v">0</div><div class="k">premine</div></div>
|
|
<div class="tile"><div class="v">0</div><div class="k">stake, anywhere</div></div>
|
|
<div class="tile"><div class="v">0</div><div class="k">admin keys</div></div>
|
|
<div class="tile"><div class="v">90%</div><div class="k">of blocks must signal to upgrade</div></div>
|
|
</div>
|
|
</div>
|
|
<p class="pt reveal" style="margin-top:28px">No premine, no stake, no fee to any team. Base fee burned.</p>
|
|
<a href="/litepaper#economics" class="lp reveal">Read more in the litepaper</a>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="band">
|
|
<div class="wrap">
|
|
<div style="display:flex;flex-direction:column;gap:8px">
|
|
<h2>Read what Igneum does not claim.</h2>
|
|
<p>The litepaper states the limits before anyone else does. Proof lag, chip economics, the market size, and the one rule external review will try hardest to break.</p>
|
|
</div>
|
|
<a href="/litepaper" class="btn dark">Litepaper</a>
|
|
</div>
|
|
</div>
|
|
|
|
<footer>
|
|
<div class="wrap">
|
|
<div class="foot-grid">
|
|
<div class="foot-brand">
|
|
<div class="brand"><svg viewBox="0 0 100 100" width="26" height="26" aria-hidden="true"><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></svg><span class="word" style="font-size:16px;color:var(--bone)">IGNEUM</span></div>
|
|
<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>
|
|
</nav>
|
|
<nav class="foot-col" aria-label="Follow">
|
|
<div class="eyebrow">Follow</div>
|
|
<a href="#journey">Journey</a>
|
|
<a href="https://github.com/igneum-network/spec" rel="noopener" style="display:inline-flex;align-items:center;gap:6px"><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"/></svg>GitHub</a>
|
|
<a href="#mine">Get the miner</a>
|
|
</nav>
|
|
</div>
|
|
<div class="foot-base">
|
|
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
|
|
<span class="mono">igneum.network</span>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
(function(){
|
|
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
// nav
|
|
var burger=document.getElementById('burger'),navLinks=document.getElementById('links');
|
|
burger.addEventListener('click',function(){var o=navLinks.classList.toggle('open');burger.setAttribute('aria-expanded',o?'true':'false');});
|
|
navLinks.querySelectorAll('a').forEach(function(a){a.addEventListener('click',function(){navLinks.classList.remove('open');burger.setAttribute('aria-expanded','false');});});
|
|
document.addEventListener('click',function(e){if(navLinks.classList.contains('open')&&!navLinks.contains(e.target)&&!burger.contains(e.target)){navLinks.classList.remove('open');burger.setAttribute('aria-expanded','false');}});
|
|
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&navLinks.classList.contains('open')){navLinks.classList.remove('open');burger.setAttribute('aria-expanded','false');burger.focus();}});
|
|
|
|
// reveal on scroll
|
|
var io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target);}});},{threshold:.12});
|
|
document.querySelectorAll('.reveal').forEach(function(el){io.observe(el);});
|
|
|
|
// emission bar
|
|
var bar=document.getElementById('bar');
|
|
var bio=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){bar.querySelectorAll('div').forEach(function(d){d.style.width=d.getAttribute('data-w');});bio.disconnect();}});},{threshold:.4});
|
|
bio.observe(bar);
|
|
|
|
// live observer: ONE fetch of /api/live every 2 s feeds the hero, the stats strip and the chain scene.
|
|
// Subscribers get (fresh, d): the sampled new blocks since the last pull and the whole response, or ([], null) when off or stale.
|
|
var live={on:false,rate:1.3,seen:{},minerOf:{},subs:[]};
|
|
live.sub=function(fn){live.subs.push(fn);};
|
|
function emitLive(fresh,d){live.subs.forEach(function(fn){fn(fresh,d);});}
|
|
function pullLive(first){return fetch('/api/live',{cache:'no-store'}).then(function(r){return r.ok?r.json():null;}).then(function(d){
|
|
if(!d||!d.ok||!d.state||d.state.stale||!d.blocks||!d.miners){live.on=false;emitLive([],null);return false;}
|
|
live.on=true;
|
|
var fresh=d.blocks.filter(function(b){return !live.seen[b.hash];});if(first)fresh=fresh.slice(-12);
|
|
// the response is a 90 s window, so both maps are rebuilt from it and stay bounded
|
|
var seen={},minerOf={};d.blocks.forEach(function(b){seen[b.hash]=1;minerOf[b.hash]=b.miner||'?';});live.seen=seen;live.minerOf=minerOf;
|
|
/* the scenes keep a calm pace; faster chains are sampled, the counters stay true */
|
|
if(fresh.length>3){var step=fresh.length/3,pick=[];for(var i=0;i<3;i++)pick.push(fresh[Math.floor(i*step)]);fresh=pick;}
|
|
emitLive(fresh,d);return true;
|
|
}).catch(function(){live.on=false;emitLive([],null);return false;});}
|
|
|
|
// live stats strip under the hero buttons: shown only while the observer is fresh
|
|
var strip=document.getElementById('livestrip');
|
|
function lsSet(id,v){var el=document.getElementById(id);if(el)el.textContent=v;}
|
|
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';}
|
|
live.sub(function(fresh,d){var on=live.on&&!!d;if(strip)strip.classList.toggle('on',on);if(!on)return;var s=d.state;
|
|
lsSet('ls-miners',s.miners_10m!==null&&s.miners_10m!==undefined?s.miners_10m:d.miners.length);
|
|
lsSet('ls-bps',s.blocks_60s!==null&&s.blocks_60s!==undefined?(s.blocks_60s/60).toFixed(1):'pending');
|
|
lsSet('ls-hash',fmtHash(s.hashes_per_second_estimate));
|
|
lsSet('ls-blocks',s.block_count!==null&&s.block_count!==undefined?Number(s.block_count).toLocaleString('en-GB'):'pending');});
|
|
|
|
// hero background: a block graph that grows left to right, proves, and locks.
|
|
// Live: each dot is one miner seen in the last ten minutes (at most 24, no ids drawn); it flashes when that
|
|
// miner finds a sampled block and a faint line joins it to the miners of the block's parents for a moment.
|
|
var cv=document.getElementById('bg'),ctx=cv.getContext('2d'),nodes=[],W,H,dpr=Math.min(window.devicePixelRatio||1,2),lanes=5,count=0;
|
|
function size(){W=cv.clientWidth;H=cv.clientHeight;cv.width=W*dpr;cv.height=H*dpr;ctx.setTransform(dpr,0,0,dpr,0,0);}
|
|
size();window.addEventListener('resize',size);
|
|
var spacing=function(){return W<700?74:92;};
|
|
function laneY(l){return H*(0.18+l*(0.64/(lanes-1)));}
|
|
function edge(a,b){ctx.beginPath();ctx.moveTo(a.x,a.y);var mx=(a.x+b.x)/2;ctx.bezierCurveTo(mx,a.y,mx,b.y,b.x,b.y);ctx.stroke();}
|
|
function spawn(x){count++;var lane=Math.floor(Math.random()*lanes);var y=laneY(lane)+(Math.random()*18-9);
|
|
var cands=nodes.slice(-7).filter(function(p){return p.x<x-30;});var parents=[];
|
|
cands.sort(function(a,b){return b.x-a.x;});if(cands.length)parents.push(cands[0]);
|
|
cands.slice(1).forEach(function(p){if(Math.random()<0.45&&parents.length<3)parents.push(p);});
|
|
nodes.push({x:x,y:y,born:performance.now(),state:0,lock:count%10===0,parents:parents});if(nodes.length>60)nodes.shift();}
|
|
function seed(){nodes=[];count=0;var sp=spacing();for(var i=0;i<Math.ceil(W/sp)+2;i++){spawn(W-i*sp);nodes[nodes.length-1].born=performance.now()-i*1000;}}
|
|
var last=0,speed=0.22;
|
|
// live state: hm = miner dots, links = parent lines, hq = sampled blocks waiting to flash, simA = simulation opacity
|
|
var hm=[],links=[],hq=[],hLive=false,simA=1,hLast=0,MAXDOTS=24;
|
|
function minerDot(id){for(var i=0;i<hm.length;i++)if(hm[i].id===id)return hm[i];return null;}
|
|
function joinMiner(id){var m=minerDot(id);if(m){m.gone=false;return m;}
|
|
var used={};hm.forEach(function(q){used[q.slot]=1;});var s=-1;for(var i=0;i<MAXDOTS;i++)if(!used[i]){s=i;break;}if(s<0)return null;
|
|
m={id:id,slot:s,fx:0.06+0.88*((s*0.618034)%1),lane:s%lanes,jy:Math.random()*18-9,ph:Math.random()*Math.PI*2,a:0,gone:false,flash:0,x:0,y:0};hm.push(m);return m;}
|
|
live.sub(function(fresh,d){
|
|
if(reduce)return;
|
|
var on=live.on&&!!d;
|
|
if(on!==hLive){hLive=on;if(!on){hm.forEach(function(m){m.gone=true;});hq=[];}}
|
|
if(!on)return;
|
|
var keep={};d.miners.slice(0,MAXDOTS).forEach(function(m){if(!m.id)return;keep[m.id]=1;joinMiner(m.id);});
|
|
hm.forEach(function(m){if(!keep[m.id])m.gone=true;}); // not seen in ten minutes: fade out
|
|
fresh.forEach(function(b){hq.push(b);});if(hq.length>6)hq=hq.slice(-6);
|
|
});
|
|
function frame(t){
|
|
ctx.clearRect(0,0,W,H);
|
|
// simulation: unchanged while the observer is off or stale, fades out behind the live dots when it is on
|
|
simA=Math.max(0,Math.min(1,simA+(hLive?-0.02:0.02)));
|
|
if(!hLive){if(!nodes.length)seed();if(t-last>1000){spawn(W+16);last=t;}}
|
|
if(simA>0){
|
|
ctx.globalAlpha=simA;
|
|
nodes.forEach(function(n){n.x-=speed;var age=t-n.born;if(age>3500)n.state=2;else if(age>1800)n.state=1;});
|
|
ctx.lineWidth=1;
|
|
nodes.forEach(function(n){n.parents.forEach(function(p){if(nodes.indexOf(p)<0)return;var proven=n.state===2&&p.state===2;ctx.strokeStyle=proven?'rgba(242,84,27,0.35)':'rgba(154,154,158,0.18)';edge(n,p);});});
|
|
nodes.forEach(function(n){var r=n.state===2?5:4;
|
|
if(n.state===2&&n.lock){ctx.beginPath();ctx.arc(n.x,n.y,11,0,Math.PI*2);ctx.strokeStyle='rgba(242,84,27,0.9)';ctx.lineWidth=1.5;ctx.stroke();ctx.lineWidth=1;}
|
|
if(n.state===2){var g=ctx.createRadialGradient(n.x,n.y,0,n.x,n.y,16);g.addColorStop(0,'rgba(242,84,27,0.35)');g.addColorStop(1,'rgba(242,84,27,0)');ctx.fillStyle=g;ctx.beginPath();ctx.arc(n.x,n.y,16,0,Math.PI*2);ctx.fill();}
|
|
ctx.beginPath();ctx.arc(n.x,n.y,r,0,Math.PI*2);ctx.fillStyle=n.state===2?'#F2541B':(n.state===1?'#FFB35C':'#0C0C0E');ctx.fill();
|
|
if(n.state===0){ctx.strokeStyle='rgba(154,154,158,0.8)';ctx.stroke();}});
|
|
ctx.globalAlpha=1;
|
|
} else if(nodes.length) nodes=[];
|
|
// live: release one sampled block at the chain scene's pace; its miner flashes, lines reach its parents' miners
|
|
if(hLive&&hq.length&&t-hLast>Math.max(60,1000/live.rate)){var b=hq.shift();hLast=t;var m=joinMiner(b.miner||'?');
|
|
if(m){m.flash=1;var done={};(b.parents||[]).forEach(function(h){var pid=live.minerOf[h];if(!pid||pid===m.id||done[pid])return;done[pid]=1;var p=minerDot(pid);if(p&&!p.gone)links.push({a:m,b:p,born:t});});if(links.length>24)links=links.slice(-24);}}
|
|
hm.forEach(function(m){m.a=Math.max(0,Math.min(1,m.a+(m.gone?-0.012:0.012)));
|
|
m.fx-=speed/W;if(m.fx<-0.02){m.fx+=1.04;m.a=0;} // the same slow drift; a dot that leaves on the left fades back in on the right
|
|
m.x=W*m.fx+Math.sin(t/9000+m.ph)*14;m.y=laneY(m.lane)+m.jy+Math.cos(t/12000+m.ph*1.7)*6;
|
|
if(m.flash>0)m.flash=Math.max(0,m.flash-0.018);});
|
|
hm=hm.filter(function(m){return !(m.gone&&m.a<=0);});
|
|
links=links.filter(function(l){return t-l.born<1600&&hm.indexOf(l.a)>=0&&hm.indexOf(l.b)>=0;});
|
|
ctx.lineWidth=1;
|
|
links.forEach(function(l){if(Math.abs(l.a.x-l.b.x)>W*0.6)return;var p=(t-l.born)/1600;ctx.strokeStyle='rgba(242,84,27,'+(0.35*(1-p)*Math.min(l.a.a,l.b.a)).toFixed(3)+')';edge(l.a,l.b);});
|
|
hm.forEach(function(m){if(m.a<=0)return;var f=m.flash;ctx.globalAlpha=m.a;var gr=16+8*f;
|
|
var g=ctx.createRadialGradient(m.x,m.y,0,m.x,m.y,gr);g.addColorStop(0,'rgba(242,84,27,'+(0.2+0.35*f).toFixed(3)+')');g.addColorStop(1,'rgba(242,84,27,0)');ctx.fillStyle=g;ctx.beginPath();ctx.arc(m.x,m.y,gr,0,Math.PI*2);ctx.fill();
|
|
ctx.beginPath();ctx.arc(m.x,m.y,4+2*f,0,Math.PI*2);ctx.fillStyle='#F2541B';ctx.fill();
|
|
if(f>0){ctx.beginPath();ctx.arc(m.x,m.y,2+1.5*f,0,Math.PI*2);ctx.fillStyle='rgba(255,179,92,'+f.toFixed(3)+')';ctx.fill();}
|
|
ctx.globalAlpha=1;});
|
|
requestAnimationFrame(frame);
|
|
}
|
|
if(!reduce){seed();requestAnimationFrame(frame);}
|
|
|
|
// chain scene: blocks stream in, miners fly in to prove shards, locks ripple
|
|
(function(){
|
|
var c=document.getElementById('chain'),x=c.getContext('2d'),W,H,blocks=[],sparks=[],ripples=[],n=0,proven=0,locked=0,lanes=5,S,GAP,speed,k=1;
|
|
var cb=document.getElementById('c-blocks'),cp=document.getElementById('c-proven'),cl=document.getElementById('c-locked');
|
|
function size(){W=c.clientWidth;H=c.clientHeight;c.width=W*dpr;c.height=H*dpr;x.setTransform(dpr,0,0,dpr,0,0);S=W<600?18:26;GAP=W<600?S*2.5:S*3.1;speed=GAP/1000;k=Math.max(0.22,Math.min(1,(W/speed)/22000));}
|
|
size();window.addEventListener('resize',size);
|
|
function laneY(l){return H*0.14+l*(H*0.72/(lanes-1));}
|
|
// live path state: when the devnet observer is fresh, real blocks (lb) from the shared poller replace the random spawner
|
|
var liveMode=false,liveQ=[],liveLanes={},byHash={};
|
|
function spawn(px,lb){n++;var lane=lb?lb.lane:Math.floor(Math.random()*lanes);var b={id:n,x:px,y:laneY(lane),born:performance.now(),shards:[0,0,0,0],claimed:[false,false,false,false],state:'mined',cp:n%6===0,parents:[],glow:0,flash:0,hash:lb?lb.hash:null};
|
|
if(lb){byHash[lb.hash]=b;lb.parents.forEach(function(h){var q=byHash[h];if(q&&blocks.indexOf(q)>=0&&b.parents.length<4)b.parents.push(q);});if(!b.parents.length){var prev=blocks.slice(-6).filter(function(q){return q.x<px-S;}).sort(function(a2,b2){return b2.x-a2.x;});if(prev.length)b.parents.push(prev[0]);if(prev.length>1&&Math.random()<0.5)b.parents.push(prev[1]);}}
|
|
else{var cands=blocks.slice(-8).filter(function(q){return q.x<px-S;}).sort(function(a2,b2){return b2.x-a2.x;});
|
|
if(cands.length)b.parents.push(cands[0]);cands.slice(1).forEach(function(q){if(Math.random()<0.4&&b.parents.length<3)b.parents.push(q);});}
|
|
blocks.push(b);if(blocks.length>80){var old=blocks.shift();if(old.hash)delete byHash[old.hash];}cb.textContent=n;}
|
|
function rr(px,py,w,h,r){x.beginPath();x.moveTo(px+r,py);x.arcTo(px+w,py,px+w,py+h,r);x.arcTo(px+w,py+h,px,py+h,r);x.arcTo(px,py+h,px,py,r);x.arcTo(px,py,px+w,py,r);x.closePath();}
|
|
var last=0,lastShard=0;
|
|
function frame(t){
|
|
if(liveMode){if(liveQ.length&&t-last>Math.max(60,1000/live.rate)){spawn(W+S,liveQ.shift());last=t;}}
|
|
else if(t-last>1000){spawn(W+S);last=t;}
|
|
var dt=16;
|
|
var sp=liveMode?speed*live.rate:speed;blocks.forEach(function(b){b.x-=sp*dt;var age=t-b.born;
|
|
if(b.state==='mined'&&age>1200*k)b.state='proving';
|
|
if(b.state==='proving'){for(var i=0;i<4;i++){if(!b.claimed[i]&&Math.random()<0.012/k){b.claimed[i]=true;var fromTop=Math.random()<0.5;sparks.push({x:Math.random()*W,y:fromTop?-6:H+6,tx:b.x,ty:b.y,b:b,i:i,p:0});}}
|
|
if(b.shards.every(function(v){return v>=1;})){b.state='proven';b.glow=1;proven++;cp.textContent=proven;if(b.cp)setTimeout(function(){b.locked=true;locked++;cl.textContent=locked;ripples.push({x:b.x,y:b.y,r:S,a:1});ripples.push({x:b.x,y:b.y,r:S*0.5,a:1});},1800*k);}}
|
|
if(b.glow>0)b.glow-=0.02;});
|
|
sparks.forEach(function(sp){sp.p+=Math.min(0.08,0.035/k);sp.tx=sp.b.x;sp.ty=sp.b.y;if(sp.p>=1){sp.done=true;sp.b.shards[sp.i]=1;}});
|
|
sparks=sparks.filter(function(sp){return !sp.done;});
|
|
ripples.forEach(function(r){r.r+=2.2;r.a-=0.012;r.x-=sp*dt;});ripples=ripples.filter(function(r){return r.a>0;});
|
|
x.clearRect(0,0,W,H);
|
|
// faint lane rails
|
|
x.strokeStyle='rgba(42,42,48,0.6)';x.lineWidth=1;for(var l=0;l<lanes;l++){x.beginPath();x.moveTo(0,laneY(l));x.lineTo(W,laneY(l));x.stroke();}
|
|
// edges
|
|
blocks.forEach(function(b){b.parents.forEach(function(q){if(blocks.indexOf(q)<0)return;var both=b.state==='proven'&&q.state==='proven';x.strokeStyle=both?'rgba(242,84,27,0.45)':'rgba(154,154,158,0.22)';x.lineWidth=both?1.5:1;x.beginPath();x.moveTo(b.x,b.y);var mx=(b.x+q.x)/2;x.bezierCurveTo(mx,b.y,mx,q.y,q.x,q.y);x.stroke();});});
|
|
// lock line: everything left of the newest locked block is final
|
|
var lk=null;blocks.forEach(function(b){if(b.locked&&(!lk||b.x>lk.x))lk=b;});
|
|
if(lk){x.strokeStyle='rgba(242,84,27,0.55)';x.lineWidth=1.5;x.setLineDash([5,6]);x.beginPath();x.moveTo(lk.x,6);x.lineTo(lk.x,H-6);x.stroke();x.setLineDash([]);var topBusy=blocks.some(function(q){return Math.abs(q.x-lk.x)<S*2.2&&q.y<H*0.3;});var ly=topBusy?H-10:14;x.fillStyle='rgba(154,154,158,0.9)';x.font='500 '+Math.max(10,Math.round(S*0.42))+'px IBM Plex Mono, monospace';x.textAlign='right';x.fillText('final',lk.x-8,ly);}
|
|
// blocks
|
|
blocks.forEach(function(b){var h=S/2;
|
|
if(b.glow>0){var g=x.createRadialGradient(b.x,b.y,0,b.x,b.y,S*1.6);g.addColorStop(0,'rgba(242,84,27,'+(0.45*b.glow)+')');g.addColorStop(1,'rgba(242,84,27,0)');x.fillStyle=g;x.beginPath();x.arc(b.x,b.y,S*1.6,0,Math.PI*2);x.fill();}
|
|
if(b.state==='proven'){x.fillStyle='#F2541B';rr(b.x-h,b.y-h,S,S,S*0.24);x.fill();}
|
|
else{x.fillStyle='#0C0C0E';rr(b.x-h,b.y-h,S,S,S*0.24);x.fill();x.strokeStyle=b.state==='proving'?'rgba(255,179,92,0.9)':'rgba(58,58,66,1)';x.lineWidth=2;rr(b.x-h,b.y-h,S,S,S*0.24);x.stroke();
|
|
if(b.state==='proving'){var q=S/2-3;for(var i=0;i<4;i++){if(b.shards[i]>=1){x.fillStyle='#FFB35C';var qx=b.x-h+3+(i%2)*q,qy=b.y-h+3+Math.floor(i/2)*q;rr(qx,qy,q-1,q-1,2);x.fill();}}}}
|
|
if(b.locked){var pulse=0.8+0.2*Math.sin(t/350);x.strokeStyle='rgba(242,84,27,'+pulse+')';x.lineWidth=3;x.beginPath();x.arc(b.x,b.y,S*1.15,0,Math.PI*2);x.stroke();x.fillStyle='#F4F1EC';x.beginPath();x.arc(b.x,b.y,S*0.16,0,Math.PI*2);x.fill();}
|
|
});
|
|
// sparks (miners claiming shards)
|
|
sparks.forEach(function(sp){var e=1-Math.pow(1-sp.p,3);var sx=sp.x+(sp.tx-sp.x)*e,sy=sp.y+(sp.ty-sp.y)*e;x.strokeStyle='rgba(255,179,92,'+(0.5*(1-sp.p))+')';x.lineWidth=1;x.beginPath();x.moveTo(sp.x+(sp.tx-sp.x)*Math.max(0,e-0.15),sp.y+(sp.ty-sp.y)*Math.max(0,e-0.15));x.lineTo(sx,sy);x.stroke();x.fillStyle='#FFB35C';x.beginPath();x.arc(sx,sy,2.2,0,Math.PI*2);x.fill();});
|
|
ripples.forEach(function(r){x.strokeStyle='rgba(242,84,27,'+r.a+')';x.lineWidth=1.5;x.beginPath();x.arc(r.x,r.y,r.r,0,Math.PI*2);x.stroke();});
|
|
if(!reduce)requestAnimationFrame(frame);
|
|
}
|
|
var seedN=Math.ceil(W/GAP)+1,pv=Math.min(5,Math.max(2,Math.floor(seedN*0.45)));for(var i=0;i<seedN;i++){spawn(W-i*GAP);var b=blocks[blocks.length-1];b.born=performance.now()-i*1000;if(i>pv){b.state='proven';b.shards=[1,1,1,1];b.claimed=[true,true,true,true];proven++;if(b.cp){b.locked=true;locked++;}}else if(i>1){b.state='proving';b.claimed=[true,true,false,false];b.shards=[1,1,0,0];}}
|
|
cb.textContent=n;cp.textContent=proven;cl.textContent=locked;
|
|
requestAnimationFrame(frame);
|
|
// live path: the shared poller above feeds sampled real blocks; while fresh, the label links to /live
|
|
var label=document.getElementById('viz-label'),labelHTML=label?label.innerHTML:'';
|
|
function laneFor(m){if(!(m in liveLanes))liveLanes[m]=Object.keys(liveLanes).length%lanes;return liveLanes[m];}
|
|
function setLive(on){if(on===liveMode)return;liveMode=on;if(label)label.innerHTML=on?'<span class="dot" style="margin-right:8px"></span><a href="/live" style="color:var(--molten)">live devnet</a>':labelHTML;}
|
|
live.sub(function(fresh){setLive(live.on);if(!live.on)return;
|
|
fresh.forEach(function(b){liveQ.push({hash:b.hash,parents:b.parents||[],lane:laneFor(b.miner||'?')});});
|
|
if(liveQ.length>6)liveQ=liveQ.slice(-6);});
|
|
})();
|
|
// one fetch on load; only if the observer is fresh does the 2 s poll start (it feeds the hero, the strip and the chain scene)
|
|
pullLive(true).then(function(on){if(on)setInterval(function(){pullLive(false);},2000);});
|
|
|
|
// hourly program ticker
|
|
var ops=['rotl','rotr','xor','add','sub','mul','mulhi','mad','or'];
|
|
function hex(){return '0x'+Math.floor(Math.random()*0xFFFFFFFF).toString(16).toUpperCase().padStart(8,'0');}
|
|
function reg(){return 'r'+Math.floor(Math.random()*8);}
|
|
function line(){var o=ops[Math.floor(Math.random()*ops.length)];var r=Math.random();
|
|
if(r<0.25)return reg()+' ^= load(ds, '+reg()+' & MASK)';
|
|
if(r<0.35)return reg()+' = shfl_xor('+reg()+', '+[1,2,4,8,16][Math.floor(Math.random()*5)]+')';
|
|
if(o==='mad')return reg()+' = mad('+reg()+', '+reg()+' | 1, '+reg()+')';
|
|
if(o==='rotl'||o==='rotr')return reg()+' = '+o+'('+reg()+', '+Math.floor(Math.random()*31+1)+')';
|
|
if(o==='mulhi')return reg()+' = mulhi('+reg()+', '+hex()+')';
|
|
return reg()+' = '+reg()+' '+({xor:'^',add:'+',sub:'-',mul:'*',or:'|'})[o]+' '+(Math.random()<0.5?reg():hex());}
|
|
var pre=document.getElementById('program');
|
|
function program(){var ls=['// seed from a locked checkpoint, through a 10 minute delay'];for(var i=0;i<5;i++)ls.push(line());ls.push('// 64 instructions x 8 iterations, regenerated every hour');return ls;}
|
|
function type(ls){pre.textContent='';var i=0,j=0,out='';function t(){if(i>=ls.length)return;if(j<=ls[i].length){out=ls.slice(0,i).join('\n')+(i?'\n':'')+ls[i].slice(0,j);pre.textContent=out;j++;setTimeout(t,reduce?0:14);}else{i++;j=0;t();}}t();}
|
|
type(program());
|
|
if(!reduce)setInterval(function(){type(program());},16000);
|
|
var cd=document.getElementById('countdown');
|
|
function countdown(){var n=new Date();var s=3599-(n.getMinutes()*60+n.getSeconds());cd.textContent=String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0');}
|
|
countdown();setInterval(countdown,1000);
|
|
|
|
// journey
|
|
fetch('/journey.json').then(function(r){return r.json();}).then(function(j){
|
|
var ph=document.getElementById('phases'),lg=document.getElementById('log'),lab=document.getElementById('stage-label');
|
|
var active=j.phases.filter(function(p){return p.status==='active';});
|
|
lab.textContent=('NOW · '+active.map(function(p){return p.name;}).join(' and ')+' · updated '+j.updated).toUpperCase();
|
|
j.phases.forEach(function(p,i){var d=document.createElement('div');d.className='phase '+p.status;d.innerHTML='<div class="w">'+p.when.toUpperCase()+'</div><div class="n">'+(i+1)+'. '+p.name+'</div>'+(p.gate?'<div class="g">gate: '+p.gate+'</div>':'<div class="g">'+p.line+'</div>');ph.appendChild(d);});
|
|
var SHOW=8,last='';
|
|
j.log.forEach(function(e,i){var d=document.createElement('div');if(i>=SHOW)d.className='more';var day=e.date===last?'':e.date;last=e.date;
|
|
d.innerHTML='<span class="d">'+day+'</span><span>'+(e.short||e.text)+'</span>';lg.appendChild(d);});
|
|
if(j.log.length>SHOW){var t=document.createElement('button');t.type='button';t.className='log-toggle';t.textContent='Show all '+j.log.length+' entries';
|
|
t.onclick=function(){var open=lg.classList.toggle('open');t.textContent=open?'Show fewer':'Show all '+j.log.length+' entries';};lg.appendChild(t);}
|
|
}).catch(function(){document.getElementById('stage-label').textContent='PHASE 2 · PROVE THE PROVING';});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|