/* Igneum site motion and data boundary. The demo generator is never used by real observer mode. */ (function(root){ 'use strict'; const TAU=Math.PI*2,clamp=(v,a,b)=>Math.max(a,Math.min(b,v)),MINE='a17f4c82'; const clone=o=>JSON.parse(JSON.stringify(o)); function random(seed){let a=seed>>>0;return()=>{a+=0x6D2B79F5;let t=a;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};} function validateReply(raw){ if(!raw||raw.ok!==true||!raw.state||typeof raw.state!=='object'||!Array.isArray(raw.blocks)||raw.blocks.length>6000)throw new Error('Unexpected observer response. Expected the existing /api/live contract.'); if(raw.state.stale!=null&&typeof raw.state.stale!=='boolean')throw new Error('Invalid observer stale state.'); if(raw.state.age_s!=null&&(!Number.isFinite(raw.state.age_s)||raw.state.age_s<0))throw new Error('Invalid observer age.'); const seen=new Set(); for(const b of raw.blocks){ if(!b||typeof b.hash!=='string'||!/^[a-f0-9]{8,64}$/i.test(b.hash)||seen.has(b.hash))throw new Error('Invalid or repeated block hash in observer response.'); seen.add(b.hash); for(const flag of ['chain','locked','final','proven'])if(b[flag]!=null&&typeof b[flag]!=='boolean')throw new Error('Invalid block flag: '+flag); if(b.color!=null&&!['blue','red','pending'].includes(b.color))throw new Error('Invalid inclusion colour.'); for(const score of ['daa','blue_score'])if(b[score]!=null&&(!Number.isFinite(b[score])||b[score]<0))throw new Error('Invalid block score.'); if(typeof b.ts!=='number'||!Number.isFinite(b.ts)||b.ts<1e12||b.ts>Date.now()+60000)throw new Error('Invalid block time in observer response.'); if(!Array.isArray(b.parents)||b.parents.length>128||b.parents.some(p=>typeof p!=='string'||!/^[a-f0-9]{8,64}$/i.test(p)||p===b.hash))throw new Error('Invalid parent links in observer response.'); if(b.miner!=null&&(typeof b.miner!=='string'||b.miner.length>128))throw new Error('Invalid miner identity.'); if(b.shards!=null&&(!Array.isArray(b.shards)||b.shards.length>256||b.shards.some(s=>!s||!['planned','proving','verified','paid'].includes(s.state))))throw new Error('Invalid proof shard state.'); } if(raw.finality?.checkpoints!=null&&(!Array.isArray(raw.finality.checkpoints)||raw.finality.checkpoints.length>500))throw new Error('Invalid checkpoint list.'); for(const c of raw.finality?.checkpoints||[]){if(!c||!['locked','pending'].includes(c.state)||!Number.isFinite(c.blue_score)||c.blue_score<0)throw new Error('Invalid checkpoint record.');if(c.fraction_total!=null&&(!Number.isFinite(c.fraction_total)||c.fraction_total<0||c.fraction_total>1))throw new Error('Invalid checkpoint fraction.');} return clone(raw); } class Demo{ constructor(now=Date.now(),seed=174){this.r=random(seed);this.blocks=[];this.cps=[];this.seq=247810;this.head=null;this.sides=[];this.events=[];this.keys=[MINE,'6b41e008','dd09a7f2','347ca21b','5de0c719','c378af06'];for(let t=now-145000;t{const a=(now-b.ts)/1000-i*2.6;const n=b.color==='red'?0:Math.max(b.manual,a<3?0:a<10?1:a<22?2:3);s.state=states[n];});b.proven=b.shards.length>0&&b.shards.every(s=>s.state==='verified'||s.state==='paid');} // Explicit simulation fixtures only. Real mode never calls this method. for(const cp of this.cps){if(cp.forced||now-cp.at>17000){cp.state='locked';cp.fraction_total=.71;}else{cp.state='pending';cp.fraction_total=Math.min(.65,(now-cp.at)/17000*.65);}} const locked=this.cps.filter(c=>c.state==='locked'),max=locked.reduce((n,c)=>Math.max(n,c.blue_score),0),hashes=new Set(locked.map(c=>c.hash)); for(const b of this.blocks){b.locked=hashes.has(b.hash);b.final=b.color==='blue'&&b.blue_score<=max;} this.blocks=this.blocks.filter(b=>b.ts>now-320000);this.cps=this.cps.slice(-12); } trigger(kind){let target=null,now=Date.now();if(kind==='mine'){target=this.create(now,true);} if(kind==='proof'){target=[...this.blocks].reverse().find(b=>b.miner===MINE&&!b.proven)||this.create(now,true,false);target.manual=2;this.event('proof','Your sample proof completed');} if(kind==='lock'){target=[...this.blocks].reverse().find(b=>b.chain&&now-b.ts>3500);if(target){this.cps.push({index:Math.floor(this.seq/17)+1,blue_score:target.blue_score,state:'locked',fraction_total:.71,at:now,hash:target.hash,forced:true});this.event('lock','A sample checkpoint locked');}} this.update();return target; } snapshot(now=Date.now()){this.update(now);return{ok:true,state:{stale:false,age_s:0,height:this.seq,now},blocks:this.blocks.map(({manual,...b})=>clone(b)),finality:{checkpoints:this.cps.map(({at,hash,forced,...c})=>clone(c))},proving:{},_demo:true};} } class Feed{ constructor(config){this.config=config;this.mode='demo';this.windowSec=120;this.demo=new Demo();this.reply=this.demo.snapshot();this.status={state:'demo',message:'Design demo / simulated blocks'};this.listeners=new Set();this.timer=0;this.pollTimer=0;this.controller=null;this.generation=0;this.destroyed=false;this.failures=0;this.lastReceipt=0;this.paused=false;this.visibility=()=>{if(!document.hidden&&this.mode==='observer')this.poll(this.generation);};document.addEventListener('visibilitychange',this.visibility);} subscribe(fn){this.listeners.add(fn);fn(this.reply,this.status);this.start();return()=>{this.listeners.delete(fn);if(!this.listeners.size)this.stopTimers();};} emit(){for(const fn of this.listeners){try{fn(this.reply,this.status);}catch(e){console.error('Feed subscriber failed',e);}}} start(){if(this.timer||!this.listeners.size)return;this.timer=setInterval(()=>{if(document.hidden)return;if(this.mode==='demo'){let n=Date.now(),last=this.demo.blocks[this.demo.blocks.length-1];if(!last||n-last.ts>1850)this.demo.create(n);this.reply=this.demo.snapshot();this.emit();}else if(this.lastReceipt&&Date.now()-this.lastReceipt>12000&&this.status.state==='live'){this.status={state:'stale',message:'Observer connection is stale. Last received data is held.'};this.emit();}},1000);if(this.mode==='observer')this.poll(this.generation);} stopTimers(){clearInterval(this.timer);this.timer=0;clearTimeout(this.pollTimer);this.pollTimer=0;if(this.controller){this.controller.abort();this.controller=null;}} setMode(mode){if(!['demo','observer'].includes(mode))return;this.generation++;this.stopTimers();this.mode=mode;this.lastReceipt=0;this.failures=0;this.reply=null;this.status={state:'connecting',message:'Connecting to the read-only observer...'};if(mode==='demo'){this.demo=new Demo();this.reply=this.demo.snapshot();this.status={state:'demo',message:'Design demo / simulated blocks'};}this.emit();this.start();} setWindow(seconds){const next=Math.round(clamp(Number(seconds)||120,30,300));if(next===this.windowSec)return;this.windowSec=next;if(this.mode==='observer')this.poll(this.generation);} trigger(kind){if(this.mode!=='demo')return null;const b=this.demo.trigger(kind);this.reply=this.demo.snapshot();this.emit();return b;} async poll(g){if(this.controller||!this.listeners.size||this.mode!=='observer'||document.hidden||g!==this.generation)return;clearTimeout(this.pollTimer);const controller=new AbortController();this.controller=controller;const timeout=setTimeout(()=>controller.abort(),8000); try{const url=new URL(this.config.observerUrl,location.href);if(url.protocol!=='https:'&&!['localhost','127.0.0.1'].includes(url.hostname))throw new Error('The observer URL must use HTTPS.');if(url.username||url.password)throw new Error('Credentials must not be placed in the observer URL.');url.searchParams.set('window',String(this.windowSec)); const r=await fetch(url,{method:'GET',cache:'no-store',credentials:'omit',mode:'cors',redirect:'error',headers:{Accept:'application/json'},signal:controller.signal});if(!r.ok)throw new Error('Observer returned HTTP '+r.status+'.');if(Number(r.headers.get('content-length')||0)>2e6)throw new Error('Observer reply is too large.');const text=await r.text();if(text.length>2e6)throw new Error('Observer reply is too large.');const reply=validateReply(JSON.parse(text));if(g!==this.generation)return;this.lastReceipt=Date.now();this.failures=0;this.reply=reply;const stale=reply.state.stale===true||(typeof reply.state.age_s==='number'&&reply.state.age_s>30);this.status={state:stale?'stale':'live',message:stale?'Observer says its data is stale. No synthetic activity is substituted.':'Read-only observer connected. Data is reported by one observer, not independently verified.'};this.emit(); }catch(e){if(g!==this.generation)return;this.failures++;this.status={state:'failed',message:(e.name==='AbortError'?'Observer request timed out.':e.message)+' No demo fallback is used. Check connectivity and CORS.'};this.emit();} finally{clearTimeout(timeout);if(this.controller===controller)this.controller=null;if(g===this.generation&&this.mode==='observer'&&this.listeners.size)this.pollTimer=setTimeout(()=>this.poll(g),Math.min(16000,Math.max(2000,this.config.observerIntervalMs||2000)*2**Math.min(this.failures,3)));} } destroy(){this.destroyed=true;this.generation++;this.stopTimers();this.listeners.clear();document.removeEventListener('visibilitychange',this.visibility);} } class HardwareScene{ constructor(canvas){this.canvas=canvas;this.ctx=canvas.getContext('2d');this.stage=0;this.lastStage=-1;this.motion=true;this.disposed=false;this.inView=true;this.raf=0;this.stageAt=performance.now();this.last=0;this.w=0;this.h=0;this.reduced=matchMedia('(prefers-reduced-motion: reduce)');this.resize=()=>{this.w=canvas.clientWidth;this.h=canvas.clientHeight;this.dpr=Math.min(devicePixelRatio||1,2);canvas.width=Math.round(this.w*this.dpr);canvas.height=Math.round(this.h*this.dpr);this.paint(performance.now());};this.ro=new ResizeObserver(this.resize);this.ro.observe(canvas);this.io=new IntersectionObserver(e=>{this.inView=e[0].isIntersecting;if(this.inView)this.kick();else this.stop();});this.io.observe(canvas);this.mo=new MutationObserver(()=>this.paint(performance.now()));this.mo.observe(document.documentElement,{attributes:true,attributeFilter:['data-theme']});this.vis=()=>document.hidden?this.stop():this.kick();document.addEventListener('visibilitychange',this.vis);this.reduction=()=>{this.stop();this.paint(performance.now());this.kick();};this.reduced.addEventListener('change',this.reduction);this.resize();this.kick();} setStage(n){this.lastStage=this.stage;this.stage=n;this.stageAt=performance.now();this.kick();this.paint(this.stageAt);} setMotion(v){this.motion=v;this.stop();this.paint(performance.now());if(v)this.kick();} stop(){cancelAnimationFrame(this.raf);this.raf=0;} kick(){if(this.raf||this.disposed||document.hidden||!this.inView||this.reduced.matches||!this.motion)return;const frame=t=>{this.raf=0;if(this.disposed||document.hidden||!this.inView)return;if(t-this.last>32){this.paint(t);this.last=t;}if(t-this.stageAt<1000)this.raf=requestAnimationFrame(frame);};this.raf=requestAnimationFrame(frame);} paint(t){if(!this.w||!this.h||this.disposed)return;const c=this.ctx,w=this.w,h=this.h,d=this.dpr;c.setTransform(d,0,0,d,0,0);c.clearRect(0,0,w,h);const light=document.documentElement.dataset.theme==='light',cs=getComputedStyle(document.documentElement),ember=cs.getPropertyValue('--ember').trim(),gold=cs.getPropertyValue('--molten').trim(),bone=cs.getPropertyValue('--bone').trim(),ash=cs.getPropertyValue('--ash').trim(),line=cs.getPropertyValue('--line').trim();const s=Math.min(w/590,h/490),cx=w*.51,cy=h*.52,age=clamp((t-this.stageAt)/950,0,1),moving=this.motion&&!this.reduced.matches,ease=moving?1-Math.pow(1-age,3):1,pulse=moving?Math.sin(age*Math.PI):0;const p=(x,y,z=0)=>({x:cx+(x*.91-y*.86)*s,y:cy+(x*.42+y*.43-z)*s}); const poly=(ps,fill,stroke,lw=1)=>{c.beginPath();ps.forEach((a,i)=>i?c.lineTo(a.x,a.y):c.moveTo(a.x,a.y));c.closePath();if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.strokeStyle=stroke;c.lineWidth=lw;c.stroke();}}; const stroke=(ps,col,lw=1)=>{c.beginPath();ps.forEach((a,i)=>i?c.lineTo(a.x,a.y):c.moveTo(a.x,a.y));c.strokeStyle=col;c.lineWidth=lw;c.stroke();}; const box=(x,y,z,ww,hh,dep,top,edge)=>{poly([p(x,y+hh,z),p(x+ww,y+hh,z),p(x+ww,y+hh,z-dep),p(x,y+hh,z-dep)],light?'#e8e2d9':'#0d0d10',edge);poly([p(x+ww,y,z),p(x+ww,y+hh,z),p(x+ww,y+hh,z-dep),p(x+ww,y,z-dep)],light?'#d6d0c9':'#17171b',edge);poly([p(x,y,z),p(x+ww,y,z),p(x+ww,y+hh,z),p(x,y+hh,z)],top,edge);}; // Quiet engineering grid under the hardware. It stays still between explained events. for(let j=-6;j<=6;j++){stroke([p(-245,j*29,-60),p(245,j*29,-60)],light?'#d9d2c666':'#35313955',.7);stroke([p(j*40,-155,-60),p(j*40,155,-60)],light?'#d9d2c666':'#35313955',.7);} let g=c.createRadialGradient(cx,cy+15*s,5,cx,cy+15*s,235*s);g.addColorStop(0,light?'#F2541B16':'#F2541B14');g.addColorStop(1,'#F2541B00');c.fillStyle=g;c.fillRect(0,0,w,h); // PCB carrier and routed connections. box(-223,-112,-40,446,224,8,light?'#ede8e0':'#101013',light?'#c8c1b6':'#383139'); const connections=[[-205,-93,-173,-93,-173,-68],[-210,77,-174,77,-174,49],[80,91,80,115,190,115],[184,-74,214,-74,214,-28],[-105,97,-105,125,-157,125],[144,71,201,71,201,107]]; for(let i=0;i.02){const u=age,seg=u<.5?0:1,v=(u%0.5)*2,pt={x:pts[seg].x+(pts[seg+1].x-pts[seg].x)*v,y:pts[seg].y+(pts[seg+1].y-pts[seg].y)*v};c.beginPath();c.arc(pt.x,pt.y,2.2,0,TAU);c.fillStyle=gold;c.fill();}} for(const [x,y] of [[-200,-85],[181,-85],[-200,89],[181,89]]){box(x,y,-36,16,10,3,light?'#e2dbd0':'#24232a',line);} const lift=this.stage===1?15*ease:this.stage===2?8:0,top=18-lift; // Finned aluminium backplate and GPU shroud. No brand mark is altered or illuminated. box(-187,-61,top-18,374,122,17,light?'#b7b0a7':'#19191f',light?'#888279':'#444149'); for(let x=-180;x<180;x+=7)stroke([p(x,61,top-20),p(x,61,top-32)],light?'#6b655b':'#48434a',1); box(-197,-67,top,394,134,17,light?'#d0c9bf':'#222127',light?'#898176':'#62504b'); stroke([p(-196,68,top-6),p(198,68,top-6)],ember,2.5); const plane=(z,fn)=>{c.save();c.transform(.91*s,.42*s,-.86*s,.43*s,cx,cy-z*s);fn();c.restore();}; plane(top+1,()=>{ c.strokeStyle=light?'#827a71':'#555158';c.lineWidth=1; for(let i=0;i<12;i++){const x=-178+i*31;c.beginPath();c.moveTo(x,-62);c.lineTo(x+24,-62);c.stroke();} for(const fx of [-112,112]){c.beginPath();c.arc(fx,0,54,0,TAU);c.fillStyle=light?'#a29b91':'#101015';c.fill();c.strokeStyle=light?'#6c655b':'#4e464b';c.stroke();c.beginPath();c.arc(fx,0,48,0,TAU);c.strokeStyle=light?'#756d63':'#353039';c.stroke();const rot=this.stage===0&&moving?ease*.9:0; for(let i=0;i<11;i++){c.save();c.translate(fx,0);c.rotate(i*TAU/11+rot);c.beginPath();c.moveTo(10,-4);c.bezierCurveTo(22,-21,39,-23,45,-8);c.quadraticCurveTo(25,-9,15,6);c.closePath();const g=c.createLinearGradient(10,0,45,-15);g.addColorStop(0,light?'#736c62':'#474149');g.addColorStop(1,light?'#c0b8ac':'#25212a');c.fillStyle=g;c.fill();c.strokeStyle=light?'#8d8478':'#51454a';c.lineWidth=.6;c.stroke();c.restore();} c.beginPath();c.arc(fx,0,13,0,TAU);c.fillStyle=light?'#ddd4c8':'#2d272e';c.fill();c.strokeStyle=ember;c.lineWidth=1.4;c.stroke();c.beginPath();c.arc(fx,0,3,0,TAU);c.fillStyle=ember;c.fill(); } c.fillStyle=light?'#a99d8e':'#17161b';c.fillRect(-40,-45,80,90);c.strokeStyle=light?'#9a8268':'#8c4b2d';c.strokeRect(-40,-45,80,90);c.fillStyle=ember;c.fillRect(-27,-30,54,60);c.fillStyle=light?'#f5e6d5':'#261610';c.fillRect(-23,-26,46,52);c.strokeStyle=gold;c.strokeRect(-19,-22,38,44);for(let a=-30;a<=30;a+=8){c.fillStyle=gold;c.fillRect(-44,a,4,2);c.fillRect(40,a,4,2);}c.fillStyle=gold;c.font='500 7px IBM Plex Mono,monospace';c.textAlign='center';c.fillText('IGNEUM',0,-4);c.font='6px IBM Plex Mono,monospace';c.fillText('COMPUTE',0,9); c.font='500 7px IBM Plex Mono,monospace';c.textAlign='left';c.fillStyle=light?'#3f362b':'#bcaaa0';c.fillText('EMBER / GPU WORKER',-176,60); }); for(const [x,y] of [[-184,-56],[183,-56],[-184,54],[183,54]])plane(top+1,()=>{c.beginPath();c.arc(x,y,2,0,TAU);c.fillStyle=light?'#6a6057':'#878087';c.fill();}); // The sample block lifts from compute; proof shards are explicit illustrative stages. const z=this.stage===0?105:135,px=50,py=-23,blockS=this.stage===2?33:28; for(let i=0;i<3;i++)box(px-blockS,py-blockS,z+i*9,blockS*2,blockS*2,6,light?'#efe4d7':'#221a19',i===2?ember:light?'#b2825d':'#8d4428'); plane(z+20,()=>{c.strokeStyle=gold;c.lineWidth=1;c.strokeRect(px-17,py-17,34,34);c.fillStyle=light?'#bc6535':'#f2541b33';c.fillRect(px-13,py-13,26,26);if(this.stage>0){c.strokeStyle=gold;c.lineWidth=2;c.beginPath();c.moveTo(px-7,py);c.lineTo(px-2,py+5);c.lineTo(px+9,py-7);c.stroke();}else{c.fillStyle=ember;c.fillRect(px-2,py-2,4,4);}}); const below=p(px,py,top+7),above=p(px,py,z-10);stroke([below,above],light?'#b7734580':'#f2541b60',1);c.setLineDash([3,5]);stroke([p(-112,0,top+8),p(-112,0,top+39),p(px,py,z-10)],light?'#b78b6866':'#d18e4750',.8);stroke([p(112,0,top+8),p(112,0,top+36),p(px,py,z-10)],light?'#b78b6866':'#d18e4750',.8);c.setLineDash([]); if(this.stage>=1){for(let i=0;i<4;i++){const angle=i*TAU/4+.25,x=px+Math.cos(angle)*85,y=py+Math.sin(angle)*85,zz=z+12;stroke([p(x,y,zz),p(px,py,z+10)],light?'#b6774799':'#b8773e80',1);box(x-9,y-9,zz,18,18,5,this.stage===2?light?'#deba8e':'#75502c':light?'#ddab89':'#5a2d1d',gold);}} if(this.stage===2){const z0=z-8,z1=z+50,v=42;for(const zz of [z0,z1]){poly([p(px-v,py-v,zz),p(px+v,py-v,zz),p(px+v,py+v,zz),p(px-v,py+v,zz)],null,gold,.9);}for(const [x,y] of [[-v,-v],[v,-v],[v,v],[-v,v]])stroke([p(px+x,py+y,z0),p(px+x,py+y,z1)],gold,.9);const a=p(px,py,z1+12);c.strokeStyle=gold;c.lineWidth=1.2;c.strokeRect(a.x-5*s,a.y,10*s,8*s);c.beginPath();c.arc(a.x,a.y,3.4*s,Math.PI,0);c.stroke();} // Short, event-bound ripple. It rests after a single signal. if(pulse>.02){const pos=p(px,py,z+18);c.save();c.translate(pos.x,pos.y);c.scale(1,.5);c.beginPath();c.arc(0,0,(50+age*65)*s,0,TAU);c.strokeStyle=light?'#b86b3555':'#ffb35c66';c.globalAlpha=(1-age)*.8;c.lineWidth=1;c.stroke();c.restore();} const q=p(-203,90,-34);c.strokeStyle=line;c.lineWidth=1;c.beginPath();c.moveTo(q.x,q.y);c.lineTo(q.x-24*s,q.y+20*s);c.lineTo(q.x-52*s,q.y+20*s);c.stroke(); } destroy(){this.disposed=true;this.stop();this.ro.disconnect();this.io.disconnect();this.mo.disconnect();document.removeEventListener('visibilitychange',this.vis);this.reduced.removeEventListener('change',this.reduction);} } root.IgneumSiteEngine={Demo,Feed,HardwareScene,validateReply,MINE}; })(typeof window!=='undefined'?window:globalThis);