git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
1289 lines
301 KiB
JavaScript
1289 lines
301 KiB
JavaScript
import{C as fe,V as A,M as dt,T as lt,Q as Nt,S as Ot,a as G,R as ve,P as ye,b as be,c as _,d as B,e as vt,I as ie,O as ne,B as P,F as mt,f as J,A as et,g as X,L as j,h as gt,i as nt,j as ae,k as at,W as ot,l as oe,m as W,D as it,n as F,G as tt,o as Y,p as V,q as Z,E as we,r as q,s as L,t as Rt,u as Ee,v as xt,w as Wt,x as Tt,y as Ce,z as N,H as Me,J as Ct,K as re,N as Te,U as Se}from"./three-Cdmz7B_B.js";import{l as ft,s as It,a as le,b as de,c as ce}from"./d3-BwsdWUnD.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))i(s);new MutationObserver(s=>{for(const a of s)if(a.type==="childList")for(const n of a.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&i(n)}).observe(document,{childList:!0,subtree:!0});function e(s){const a={};return s.integrity&&(a.integrity=s.integrity),s.referrerPolicy&&(a.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?a.credentials="include":s.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(s){if(s.ep)return;s.ep=!0;const a=e(s);fetch(s.href,a)}})();const Gt={type:"change"},Ht={type:"start"},he={type:"end"},yt=new ve,jt=new ye,_e=Math.cos(70*be.DEG2RAD),D=new A,H=2*Math.PI,z={NONE:-1,ROTATE:0,DOLLY:1,PAN:2,TOUCH_ROTATE:3,TOUCH_PAN:4,TOUCH_DOLLY_PAN:5,TOUCH_DOLLY_ROTATE:6},_t=1e-6;class rt extends fe{constructor(t,e=null){super(t,e),this.state=z.NONE,this.enabled=!0,this.target=new A,this.cursor=new A,this.minDistance=0,this.maxDistance=1/0,this.minZoom=0,this.maxZoom=1/0,this.minTargetRadius=0,this.maxTargetRadius=1/0,this.minPolarAngle=0,this.maxPolarAngle=Math.PI,this.minAzimuthAngle=-1/0,this.maxAzimuthAngle=1/0,this.enableDamping=!1,this.dampingFactor=.05,this.enableZoom=!0,this.zoomSpeed=1,this.enableRotate=!0,this.rotateSpeed=1,this.enablePan=!0,this.panSpeed=1,this.screenSpacePanning=!0,this.keyPanSpeed=7,this.zoomToCursor=!1,this.autoRotate=!1,this.autoRotateSpeed=2,this.keys={LEFT:"ArrowLeft",UP:"ArrowUp",RIGHT:"ArrowRight",BOTTOM:"ArrowDown"},this.mouseButtons={LEFT:dt.ROTATE,MIDDLE:dt.DOLLY,RIGHT:dt.PAN},this.touches={ONE:lt.ROTATE,TWO:lt.DOLLY_PAN},this.target0=this.target.clone(),this.position0=this.object.position.clone(),this.zoom0=this.object.zoom,this._domElementKeyEvents=null,this._lastPosition=new A,this._lastQuaternion=new Nt,this._lastTargetPosition=new A,this._quat=new Nt().setFromUnitVectors(t.up,new A(0,1,0)),this._quatInverse=this._quat.clone().invert(),this._spherical=new Ot,this._sphericalDelta=new Ot,this._scale=1,this._panOffset=new A,this._rotateStart=new G,this._rotateEnd=new G,this._rotateDelta=new G,this._panStart=new G,this._panEnd=new G,this._panDelta=new G,this._dollyStart=new G,this._dollyEnd=new G,this._dollyDelta=new G,this._dollyDirection=new A,this._mouse=new G,this._performCursorZoom=!1,this._pointers=[],this._pointerPositions={},this._controlActive=!1,this._onPointerMove=Ae.bind(this),this._onPointerDown=ze.bind(this),this._onPointerUp=$e.bind(this),this._onContextMenu=Ie.bind(this),this._onMouseWheel=Pe.bind(this),this._onKeyDown=De.bind(this),this._onTouchStart=Fe.bind(this),this._onTouchMove=Re.bind(this),this._onMouseDown=ke.bind(this),this._onMouseMove=Le.bind(this),this._interceptControlDown=He.bind(this),this._interceptControlUp=Be.bind(this),this.domElement!==null&&this.connect(),this.update()}connect(){this.domElement.addEventListener("pointerdown",this._onPointerDown),this.domElement.addEventListener("pointercancel",this._onPointerUp),this.domElement.addEventListener("contextmenu",this._onContextMenu),this.domElement.addEventListener("wheel",this._onMouseWheel,{passive:!1}),this.domElement.getRootNode().addEventListener("keydown",this._interceptControlDown,{passive:!0,capture:!0}),this.domElement.style.touchAction="none"}disconnect(){this.domElement.removeEventListener("pointerdown",this._onPointerDown),this.domElement.removeEventListener("pointermove",this._onPointerMove),this.domElement.removeEventListener("pointerup",this._onPointerUp),this.domElement.removeEventListener("pointercancel",this._onPointerUp),this.domElement.removeEventListener("wheel",this._onMouseWheel),this.domElement.removeEventListener("contextmenu",this._onContextMenu),this.stopListenToKeyEvents(),this.domElement.getRootNode().removeEventListener("keydown",this._interceptControlDown,{capture:!0}),this.domElement.style.touchAction="auto"}dispose(){this.disconnect()}getPolarAngle(){return this._spherical.phi}getAzimuthalAngle(){return this._spherical.theta}getDistance(){return this.object.position.distanceTo(this.target)}listenToKeyEvents(t){t.addEventListener("keydown",this._onKeyDown),this._domElementKeyEvents=t}stopListenToKeyEvents(){this._domElementKeyEvents!==null&&(this._domElementKeyEvents.removeEventListener("keydown",this._onKeyDown),this._domElementKeyEvents=null)}saveState(){this.target0.copy(this.target),this.position0.copy(this.object.position),this.zoom0=this.object.zoom}reset(){this.target.copy(this.target0),this.object.position.copy(this.position0),this.object.zoom=this.zoom0,this.object.updateProjectionMatrix(),this.dispatchEvent(Gt),this.update(),this.state=z.NONE}update(t=null){const e=this.object.position;D.copy(e).sub(this.target),D.applyQuaternion(this._quat),this._spherical.setFromVector3(D),this.autoRotate&&this.state===z.NONE&&this._rotateLeft(this._getAutoRotationAngle(t)),this.enableDamping?(this._spherical.theta+=this._sphericalDelta.theta*this.dampingFactor,this._spherical.phi+=this._sphericalDelta.phi*this.dampingFactor):(this._spherical.theta+=this._sphericalDelta.theta,this._spherical.phi+=this._sphericalDelta.phi);let i=this.minAzimuthAngle,s=this.maxAzimuthAngle;isFinite(i)&&isFinite(s)&&(i<-Math.PI?i+=H:i>Math.PI&&(i-=H),s<-Math.PI?s+=H:s>Math.PI&&(s-=H),i<=s?this._spherical.theta=Math.max(i,Math.min(s,this._spherical.theta)):this._spherical.theta=this._spherical.theta>(i+s)/2?Math.max(i,this._spherical.theta):Math.min(s,this._spherical.theta)),this._spherical.phi=Math.max(this.minPolarAngle,Math.min(this.maxPolarAngle,this._spherical.phi)),this._spherical.makeSafe(),this.enableDamping===!0?this.target.addScaledVector(this._panOffset,this.dampingFactor):this.target.add(this._panOffset),this.target.sub(this.cursor),this.target.clampLength(this.minTargetRadius,this.maxTargetRadius),this.target.add(this.cursor);let a=!1;if(this.zoomToCursor&&this._performCursorZoom||this.object.isOrthographicCamera)this._spherical.radius=this._clampDistance(this._spherical.radius);else{const n=this._spherical.radius;this._spherical.radius=this._clampDistance(this._spherical.radius*this._scale),a=n!=this._spherical.radius}if(D.setFromSpherical(this._spherical),D.applyQuaternion(this._quatInverse),e.copy(this.target).add(D),this.object.lookAt(this.target),this.enableDamping===!0?(this._sphericalDelta.theta*=1-this.dampingFactor,this._sphericalDelta.phi*=1-this.dampingFactor,this._panOffset.multiplyScalar(1-this.dampingFactor)):(this._sphericalDelta.set(0,0,0),this._panOffset.set(0,0,0)),this.zoomToCursor&&this._performCursorZoom){let n=null;if(this.object.isPerspectiveCamera){const o=D.length();n=this._clampDistance(o*this._scale);const r=o-n;this.object.position.addScaledVector(this._dollyDirection,r),this.object.updateMatrixWorld(),a=!!r}else if(this.object.isOrthographicCamera){const o=new A(this._mouse.x,this._mouse.y,0);o.unproject(this.object);const r=this.object.zoom;this.object.zoom=Math.max(this.minZoom,Math.min(this.maxZoom,this.object.zoom/this._scale)),this.object.updateProjectionMatrix(),a=r!==this.object.zoom;const l=new A(this._mouse.x,this._mouse.y,0);l.unproject(this.object),this.object.position.sub(l).add(o),this.object.updateMatrixWorld(),n=D.length()}else console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."),this.zoomToCursor=!1;n!==null&&(this.screenSpacePanning?this.target.set(0,0,-1).transformDirection(this.object.matrix).multiplyScalar(n).add(this.object.position):(yt.origin.copy(this.object.position),yt.direction.set(0,0,-1).transformDirection(this.object.matrix),Math.abs(this.object.up.dot(yt.direction))<_e?this.object.lookAt(this.target):(jt.setFromNormalAndCoplanarPoint(this.object.up,this.target),yt.intersectPlane(jt,this.target))))}else if(this.object.isOrthographicCamera){const n=this.object.zoom;this.object.zoom=Math.max(this.minZoom,Math.min(this.maxZoom,this.object.zoom/this._scale)),n!==this.object.zoom&&(this.object.updateProjectionMatrix(),a=!0)}return this._scale=1,this._performCursorZoom=!1,a||this._lastPosition.distanceToSquared(this.object.position)>_t||8*(1-this._lastQuaternion.dot(this.object.quaternion))>_t||this._lastTargetPosition.distanceToSquared(this.target)>_t?(this.dispatchEvent(Gt),this._lastPosition.copy(this.object.position),this._lastQuaternion.copy(this.object.quaternion),this._lastTargetPosition.copy(this.target),!0):!1}_getAutoRotationAngle(t){return t!==null?H/60*this.autoRotateSpeed*t:H/60/60*this.autoRotateSpeed}_getZoomScale(t){const e=Math.abs(t*.01);return Math.pow(.95,this.zoomSpeed*e)}_rotateLeft(t){this._sphericalDelta.theta-=t}_rotateUp(t){this._sphericalDelta.phi-=t}_panLeft(t,e){D.setFromMatrixColumn(e,0),D.multiplyScalar(-t),this._panOffset.add(D)}_panUp(t,e){this.screenSpacePanning===!0?D.setFromMatrixColumn(e,1):(D.setFromMatrixColumn(e,0),D.crossVectors(this.object.up,D)),D.multiplyScalar(t),this._panOffset.add(D)}_pan(t,e){const i=this.domElement;if(this.object.isPerspectiveCamera){const s=this.object.position;D.copy(s).sub(this.target);let a=D.length();a*=Math.tan(this.object.fov/2*Math.PI/180),this._panLeft(2*t*a/i.clientHeight,this.object.matrix),this._panUp(2*e*a/i.clientHeight,this.object.matrix)}else this.object.isOrthographicCamera?(this._panLeft(t*(this.object.right-this.object.left)/this.object.zoom/i.clientWidth,this.object.matrix),this._panUp(e*(this.object.top-this.object.bottom)/this.object.zoom/i.clientHeight,this.object.matrix)):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."),this.enablePan=!1)}_dollyOut(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale/=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_dollyIn(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale*=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_updateZoomParameters(t,e){if(!this.zoomToCursor)return;this._performCursorZoom=!0;const i=this.domElement.getBoundingClientRect(),s=t-i.left,a=e-i.top,n=i.width,o=i.height;this._mouse.x=s/n*2-1,this._mouse.y=-(a/o)*2+1,this._dollyDirection.set(this._mouse.x,this._mouse.y,1).unproject(this.object).sub(this.object.position).normalize()}_clampDistance(t){return Math.max(this.minDistance,Math.min(this.maxDistance,t))}_handleMouseDownRotate(t){this._rotateStart.set(t.clientX,t.clientY)}_handleMouseDownDolly(t){this._updateZoomParameters(t.clientX,t.clientX),this._dollyStart.set(t.clientX,t.clientY)}_handleMouseDownPan(t){this._panStart.set(t.clientX,t.clientY)}_handleMouseMoveRotate(t){this._rotateEnd.set(t.clientX,t.clientY),this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(H*this._rotateDelta.x/e.clientHeight),this._rotateUp(H*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd),this.update()}_handleMouseMoveDolly(t){this._dollyEnd.set(t.clientX,t.clientY),this._dollyDelta.subVectors(this._dollyEnd,this._dollyStart),this._dollyDelta.y>0?this._dollyOut(this._getZoomScale(this._dollyDelta.y)):this._dollyDelta.y<0&&this._dollyIn(this._getZoomScale(this._dollyDelta.y)),this._dollyStart.copy(this._dollyEnd),this.update()}_handleMouseMovePan(t){this._panEnd.set(t.clientX,t.clientY),this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd),this.update()}_handleMouseWheel(t){this._updateZoomParameters(t.clientX,t.clientY),t.deltaY<0?this._dollyIn(this._getZoomScale(t.deltaY)):t.deltaY>0&&this._dollyOut(this._getZoomScale(t.deltaY)),this.update()}_handleKeyDown(t){let e=!1;switch(t.code){case this.keys.UP:t.ctrlKey||t.metaKey||t.shiftKey?this._rotateUp(H*this.rotateSpeed/this.domElement.clientHeight):this._pan(0,this.keyPanSpeed),e=!0;break;case this.keys.BOTTOM:t.ctrlKey||t.metaKey||t.shiftKey?this._rotateUp(-H*this.rotateSpeed/this.domElement.clientHeight):this._pan(0,-this.keyPanSpeed),e=!0;break;case this.keys.LEFT:t.ctrlKey||t.metaKey||t.shiftKey?this._rotateLeft(H*this.rotateSpeed/this.domElement.clientHeight):this._pan(this.keyPanSpeed,0),e=!0;break;case this.keys.RIGHT:t.ctrlKey||t.metaKey||t.shiftKey?this._rotateLeft(-H*this.rotateSpeed/this.domElement.clientHeight):this._pan(-this.keyPanSpeed,0),e=!0;break}e&&(t.preventDefault(),this.update())}_handleTouchStartRotate(t){if(this._pointers.length===1)this._rotateStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),i=.5*(t.pageX+e.x),s=.5*(t.pageY+e.y);this._rotateStart.set(i,s)}}_handleTouchStartPan(t){if(this._pointers.length===1)this._panStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),i=.5*(t.pageX+e.x),s=.5*(t.pageY+e.y);this._panStart.set(i,s)}}_handleTouchStartDolly(t){const e=this._getSecondPointerPosition(t),i=t.pageX-e.x,s=t.pageY-e.y,a=Math.sqrt(i*i+s*s);this._dollyStart.set(0,a)}_handleTouchStartDollyPan(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enablePan&&this._handleTouchStartPan(t)}_handleTouchStartDollyRotate(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enableRotate&&this._handleTouchStartRotate(t)}_handleTouchMoveRotate(t){if(this._pointers.length==1)this._rotateEnd.set(t.pageX,t.pageY);else{const i=this._getSecondPointerPosition(t),s=.5*(t.pageX+i.x),a=.5*(t.pageY+i.y);this._rotateEnd.set(s,a)}this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(H*this._rotateDelta.x/e.clientHeight),this._rotateUp(H*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd)}_handleTouchMovePan(t){if(this._pointers.length===1)this._panEnd.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),i=.5*(t.pageX+e.x),s=.5*(t.pageY+e.y);this._panEnd.set(i,s)}this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd)}_handleTouchMoveDolly(t){const e=this._getSecondPointerPosition(t),i=t.pageX-e.x,s=t.pageY-e.y,a=Math.sqrt(i*i+s*s);this._dollyEnd.set(0,a),this._dollyDelta.set(0,Math.pow(this._dollyEnd.y/this._dollyStart.y,this.zoomSpeed)),this._dollyOut(this._dollyDelta.y),this._dollyStart.copy(this._dollyEnd);const n=(t.pageX+e.x)*.5,o=(t.pageY+e.y)*.5;this._updateZoomParameters(n,o)}_handleTouchMoveDollyPan(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enablePan&&this._handleTouchMovePan(t)}_handleTouchMoveDollyRotate(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enableRotate&&this._handleTouchMoveRotate(t)}_addPointer(t){this._pointers.push(t.pointerId)}_removePointer(t){delete this._pointerPositions[t.pointerId];for(let e=0;e<this._pointers.length;e++)if(this._pointers[e]==t.pointerId){this._pointers.splice(e,1);return}}_isTrackingPointer(t){for(let e=0;e<this._pointers.length;e++)if(this._pointers[e]==t.pointerId)return!0;return!1}_trackPointer(t){let e=this._pointerPositions[t.pointerId];e===void 0&&(e=new G,this._pointerPositions[t.pointerId]=e),e.set(t.pageX,t.pageY)}_getSecondPointerPosition(t){const e=t.pointerId===this._pointers[0]?this._pointers[1]:this._pointers[0];return this._pointerPositions[e]}_customWheelEvent(t){const e=t.deltaMode,i={clientX:t.clientX,clientY:t.clientY,deltaY:t.deltaY};switch(e){case 1:i.deltaY*=16;break;case 2:i.deltaY*=100;break}return t.ctrlKey&&!this._controlActive&&(i.deltaY*=10),i}}function ze(p){this.enabled!==!1&&(this._pointers.length===0&&(this.domElement.setPointerCapture(p.pointerId),this.domElement.addEventListener("pointermove",this._onPointerMove),this.domElement.addEventListener("pointerup",this._onPointerUp)),!this._isTrackingPointer(p)&&(this._addPointer(p),p.pointerType==="touch"?this._onTouchStart(p):this._onMouseDown(p)))}function Ae(p){this.enabled!==!1&&(p.pointerType==="touch"?this._onTouchMove(p):this._onMouseMove(p))}function $e(p){switch(this._removePointer(p),this._pointers.length){case 0:this.domElement.releasePointerCapture(p.pointerId),this.domElement.removeEventListener("pointermove",this._onPointerMove),this.domElement.removeEventListener("pointerup",this._onPointerUp),this.dispatchEvent(he),this.state=z.NONE;break;case 1:const t=this._pointers[0],e=this._pointerPositions[t];this._onTouchStart({pointerId:t,pageX:e.x,pageY:e.y});break}}function ke(p){let t;switch(p.button){case 0:t=this.mouseButtons.LEFT;break;case 1:t=this.mouseButtons.MIDDLE;break;case 2:t=this.mouseButtons.RIGHT;break;default:t=-1}switch(t){case dt.DOLLY:if(this.enableZoom===!1)return;this._handleMouseDownDolly(p),this.state=z.DOLLY;break;case dt.ROTATE:if(p.ctrlKey||p.metaKey||p.shiftKey){if(this.enablePan===!1)return;this._handleMouseDownPan(p),this.state=z.PAN}else{if(this.enableRotate===!1)return;this._handleMouseDownRotate(p),this.state=z.ROTATE}break;case dt.PAN:if(p.ctrlKey||p.metaKey||p.shiftKey){if(this.enableRotate===!1)return;this._handleMouseDownRotate(p),this.state=z.ROTATE}else{if(this.enablePan===!1)return;this._handleMouseDownPan(p),this.state=z.PAN}break;default:this.state=z.NONE}this.state!==z.NONE&&this.dispatchEvent(Ht)}function Le(p){switch(this.state){case z.ROTATE:if(this.enableRotate===!1)return;this._handleMouseMoveRotate(p);break;case z.DOLLY:if(this.enableZoom===!1)return;this._handleMouseMoveDolly(p);break;case z.PAN:if(this.enablePan===!1)return;this._handleMouseMovePan(p);break}}function Pe(p){this.enabled===!1||this.enableZoom===!1||this.state!==z.NONE||(p.preventDefault(),this.dispatchEvent(Ht),this._handleMouseWheel(this._customWheelEvent(p)),this.dispatchEvent(he))}function De(p){this.enabled===!1||this.enablePan===!1||this._handleKeyDown(p)}function Fe(p){switch(this._trackPointer(p),this._pointers.length){case 1:switch(this.touches.ONE){case lt.ROTATE:if(this.enableRotate===!1)return;this._handleTouchStartRotate(p),this.state=z.TOUCH_ROTATE;break;case lt.PAN:if(this.enablePan===!1)return;this._handleTouchStartPan(p),this.state=z.TOUCH_PAN;break;default:this.state=z.NONE}break;case 2:switch(this.touches.TWO){case lt.DOLLY_PAN:if(this.enableZoom===!1&&this.enablePan===!1)return;this._handleTouchStartDollyPan(p),this.state=z.TOUCH_DOLLY_PAN;break;case lt.DOLLY_ROTATE:if(this.enableZoom===!1&&this.enableRotate===!1)return;this._handleTouchStartDollyRotate(p),this.state=z.TOUCH_DOLLY_ROTATE;break;default:this.state=z.NONE}break;default:this.state=z.NONE}this.state!==z.NONE&&this.dispatchEvent(Ht)}function Re(p){switch(this._trackPointer(p),this.state){case z.TOUCH_ROTATE:if(this.enableRotate===!1)return;this._handleTouchMoveRotate(p),this.update();break;case z.TOUCH_PAN:if(this.enablePan===!1)return;this._handleTouchMovePan(p),this.update();break;case z.TOUCH_DOLLY_PAN:if(this.enableZoom===!1&&this.enablePan===!1)return;this._handleTouchMoveDollyPan(p),this.update();break;case z.TOUCH_DOLLY_ROTATE:if(this.enableZoom===!1&&this.enableRotate===!1)return;this._handleTouchMoveDollyRotate(p),this.update();break;default:this.state=z.NONE}}function Ie(p){this.enabled!==!1&&p.preventDefault()}function He(p){p.key==="Control"&&(this._controlActive=!0,this.domElement.getRootNode().addEventListener("keyup",this._interceptControlUp,{passive:!0,capture:!0}))}function Be(p){p.key==="Control"&&(this._controlActive=!1,this.domElement.getRootNode().removeEventListener("keyup",this._interceptControlUp,{passive:!0,capture:!0}))}const Ne={transit:new _(58879),flare:new _(16731469),rotation:new _(3066993),eclipse:new _(10044671),variability:new _(16756768)},Oe=new _(9147550);function Vt(p){return Ne[p]??Oe}class We{constructor(t){this.nodesMesh=null,this.edgesLine=null,this.glowPoints=null,this.nodeMap=new Map,this.scene=t}setNodes(t){this.disposeNodes();const e=new B(.12,8,6),i=new vt({vertexColors:!1,emissiveIntensity:.8,roughness:.3,metalness:.1}),s=new ie(e,i,t.length),a=new ne,n=new _;for(let h=0;h<t.length;h++){const u=t[h];this.nodeMap.set(u.id,h),a.position.set(u.x,u.y,u.z);const m=.3+u.weight*.7;a.scale.set(m,m,m),a.updateMatrix(),s.setMatrixAt(h,a.matrix),n.copy(Vt(u.domain)),s.setColorAt(h,n)}s.instanceMatrix.needsUpdate=!0,s.instanceColor&&(s.instanceColor.needsUpdate=!0),this.nodesMesh=s,this.scene.add(s);const o=new Float32Array(t.length*3),r=new Float32Array(t.length*3),l=new Float32Array(t.length);for(let h=0;h<t.length;h++){const u=t[h];o[h*3]=u.x,o[h*3+1]=u.y,o[h*3+2]=u.z;const m=Vt(u.domain);r[h*3]=m.r,r[h*3+1]=m.g,r[h*3+2]=m.b,l[h]=.8+u.weight*1.5}const c=new P;c.setAttribute("position",new mt(o,3)),c.setAttribute("color",new mt(r,3));const d=new J({size:1.2,vertexColors:!0,transparent:!0,opacity:.25,sizeAttenuation:!0,depthWrite:!1,blending:et});this.glowPoints=new X(c,d),this.scene.add(this.glowPoints)}setEdges(t,e){this.disposeEdges();const i=[],s=[],a=new Map;for(const r of e)a.set(r.id,r);for(const r of t){const l=a.get(r.source),c=a.get(r.target);if(!l||!c)continue;i.push(l.x,l.y,l.z),i.push(c.x,c.y,c.z);const d=Math.max(.05,Math.min(.6,r.weight*.5));s.push(0,.9,1,d),s.push(0,.9,1,d)}const n=new P;n.setAttribute("position",new mt(i,3)),n.setAttribute("color",new mt(s,4));const o=new j({vertexColors:!0,transparent:!0,opacity:.6,depthWrite:!1,blending:et});this.edgesLine=new gt(n,o),this.scene.add(this.edgesLine)}getNodeIndex(t){return this.nodeMap.get(t)}setPulse(t){if(this.glowPoints&&(this.glowPoints.material.opacity=.15+t*.15),this.nodesMesh){const e=this.nodesMesh.material;e.emissiveIntensity=.5+t*.5}}disposeNodes(){this.nodesMesh&&(this.scene.remove(this.nodesMesh),this.nodesMesh.geometry.dispose(),this.nodesMesh.material.dispose(),this.nodesMesh=null),this.glowPoints&&(this.scene.remove(this.glowPoints),this.glowPoints.geometry.dispose(),this.glowPoints.material.dispose(),this.glowPoints=null),this.nodeMap.clear()}disposeEdges(){this.edgesLine&&(this.scene.remove(this.edgesLine),this.edgesLine.geometry.dispose(),this.edgesLine.material.dispose(),this.edgesLine=null)}dispose(){this.disposeNodes(),this.disposeEdges()}}const Ge="";async function Q(p){const t=await fetch(Ge+p);if(!t.ok)throw new Error(`API error ${t.status}: ${t.statusText} (${p})`);return t.json()}async function je(p){return Q(`/api/atlas/query?event_id=${encodeURIComponent(p)}`)}async function Ve(p,t){const e=await Q(`/api/coherence?target_id=${encodeURIComponent(p)}&epoch=${t}`),i=[];if(e.values)for(let s=0;s<e.values.length;s++){const a=e.values[s];for(let n=0;n<a.length;n++)i.push({target_id:p,epoch:t,value:a[n],cut_pressure:a[n]})}return i}async function qe(p){return((await Q(`/api/coherence/boundary?target_id=${encodeURIComponent(p)}`)).points??[]).map(e=>({epoch:e.epoch,pressure:e.boundary_radius,crossed:e.coherence<.8}))}async function pe(){return((await Q("/api/coherence/alerts")).alerts??[]).map(t=>({target_id:t.sector,epoch:0,pressure:t.coherence,message:t.message}))}async function Ke(){return((await Q("/api/candidates/planet")).candidates??[]).map(t=>({id:t.id,name:t.id,score:t.score,period:t.period_days,radius:t.radius_earth,depth:t.transit_depth??.005+(1-t.score)*.005,snr:Math.round(t.score*40+5),stellarType:t.stellar_type,distance:t.distance_ly,status:t.status,mass:t.mass_earth??null,eqTemp:t.eq_temp_k??null,discoveryYear:t.discovery_year??0,discoveryMethod:t.discovery_method??"",telescope:t.telescope??"",reference:t.reference??"",transitDepth:t.transit_depth??null}))}async function Ue(){return((await Q("/api/candidates/life")).candidates??[]).map(t=>({id:t.id,name:t.id,score:t.life_score,o2:t.o2_normalized??Math.min(1,t.o2_ppm/21e4),ch4:t.ch4_normalized??Math.min(1,t.ch4_ppb/2500),h2o:t.h2o_normalized??(t.h2o_detected?.85:.2),co2:t.co2_normalized??Math.min(1,t.co2_ppm/1e4),disequilibrium:t.disequilibrium??t.biosig_confidence,habitability:t.habitability_index,atmosphereStatus:t.atmosphere_status??"Unknown",jwstObserved:t.jwst_observed??!1,moleculesConfirmed:t.molecules_confirmed??[],moleculesTentative:t.molecules_tentative??[],reference:t.reference??""}))}async function me(){return Q("/api/witness/log")}async function ue(){var t,e;const p=await Q("/api/status");return{uptime:p.uptime_seconds??0,segments:((t=p.store)==null?void 0:t.total_segments)??0,file_size:((e=p.store)==null?void 0:e.file_size)??0,download_progress:{LIGHT_SEG:1,SPECTRUM_SEG:.85,ORBIT_SEG:1,CAUSAL_SEG:.92}}}async function ge(){const p=await Q("/api/memory/tiers"),t=new Map;for(const e of p.tiers??[])t.set(e.name,{used:Math.round(e.used_mb),total:Math.round(e.capacity_mb)});return{small:t.get("S")??{used:0,total:0},medium:t.get("M")??{used:0,total:0},large:t.get("L")??{used:0,total:0}}}const Mt=[];let I=null,ct=null,ut=1e3,Bt=!1;const Ye=3e4,Ze=2;function Xe(p){try{const t=JSON.parse(p.data);for(const e of Mt)e(t)}catch{}}function qt(){Bt||ct||(ct=setTimeout(()=>{ct=null,xe()},ut),ut=Math.min(ut*Ze,Ye))}function xe(){if(I&&(I.readyState===WebSocket.OPEN||I.readyState===WebSocket.CONNECTING))return;const t=`${location.protocol==="https:"?"wss:":"ws:"}//${location.host}/ws/live`;try{I=new WebSocket(t)}catch{qt();return}I.addEventListener("open",()=>{ut=1e3}),I.addEventListener("message",Xe),I.addEventListener("close",()=>{I=null,qt()}),I.addEventListener("error",()=>{I==null||I.close()})}function St(p){return Mt.push(p),()=>{const t=Mt.indexOf(p);t>=0&&Mt.splice(t,1)}}function Je(){Bt=!1,ut=1e3,xe()}function Qe(){Bt=!0,ct&&(clearTimeout(ct),ct=null),I&&(I.close(),I=null)}const ts=["2h","12h","3d","27d"];function Ft(p){let t=p|0;return()=>(t=t*1103515245+12345&2147483647,t/2147483647)}const Kt=["Lyra","Cygnus","Aquila","Orion","Centaurus","Vela","Puppis","Sagittarius","Scorpius","Cassiopeia","Perseus","Andromeda","Draco","Ursa Major","Leo","Virgo","Libra","Gemini"];function es(p,t,e,i,s){const a=t,n=["transit","flare","rotation","eclipse","variability"],o=[],r=[],l=Ft(p.length*31337),c=8;for(let d=0;d<a;d++){const u=d%e/e*Math.PI*2,m=l(),g=.3+Math.pow(m,s)*c,x=u+g*.6+(l()-.5)*i,f=(l()-.5)*.5*Math.exp(-g*.12),w=g*.08,y=g*Math.cos(x)+(l()-.5)*w,b=g*Math.sin(x)+(l()-.5)*w,v=f,E=.15+l()*.85;o.push({id:`s${d}`,domain:n[d%n.length],x:y,y:v,z:b,weight:E})}for(let d=1;d<a;d++){let h=1/0,u=0;const m=Math.min(d,25);for(let g=Math.max(0,d-m);g<d;g++){const x=o[d].x-o[g].x,f=o[d].y-o[g].y,w=o[d].z-o[g].z,y=x*x+f*f+w*w;y<h&&(h=y,u=g)}if(r.push({source:o[u].id,target:o[d].id,weight:Math.max(.1,1-h/16)}),l()>.85&&d>4){const g=Math.floor(l()*d),x=o[d].x-o[g].x,f=o[d].z-o[g].z;Math.sqrt(x*x+f*f)<4&&r.push({source:o[g].id,target:o[d].id,weight:.05+l()*.15})}}return{nodes:o,edges:r}}class ss{constructor(){this.container=null,this.renderer=null,this.scene=null,this.camera=null,this.controls=null,this.graph=null,this.starfield=null,this.nebulaGroup=null,this.starMapLabels=null,this.gridHelper=null,this.animFrameId=0,this.unsubWs=null,this.activeScale="12h",this.time=0,this.nodeCount=150,this.spiralArms=4,this.armSpread=.4,this.coreConcentration=1,this.rotationSpeed=.15,this.showGrid=!0,this.showLabels=!0,this.showEdges=!0,this.pulseNodes=!0,this.sliderRefs=new Map,this.statsEl=null,this.resize=()=>{if(!this.renderer||!this.camera||!this.container)return;const t=this.renderer.domElement.parentElement;if(!t)return;const e=t.clientWidth,i=t.clientHeight;e===0||i===0||(this.renderer.setSize(e,i),this.camera.aspect=e/i,this.camera.updateProjectionMatrix())},this.animate=()=>{var t;if(this.animFrameId=requestAnimationFrame(this.animate),this.time+=.016,(t=this.controls)==null||t.update(),this.starfield&&(this.starfield.rotation.y+=3e-5),this.pulseNodes&&this.graph){const e=.85+.15*Math.sin(this.time*1.5);this.graph.setPulse(e)}this.renderer&&this.scene&&this.camera&&this.renderer.render(this.scene,this.camera)}}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=this.buildSidebar();e.appendChild(i);const s=document.createElement("div");s.style.cssText="flex:1;position:relative;min-width:0",e.appendChild(s);const a=document.createElement("div");a.className="three-container",s.appendChild(a);const n=document.createElement("div");n.className="scale-selector";for(const l of ts){const c=document.createElement("button");c.className="scale-btn",l===this.activeScale&&c.classList.add("active"),c.textContent=l,c.title=this.scaleDescription(l),c.addEventListener("click",()=>this.setScale(l,n)),n.appendChild(c)}a.appendChild(n),this.statsEl=document.createElement("div"),this.statsEl.style.cssText=`
|
||
position:absolute;top:12px;left:12px;
|
||
padding:10px 14px;max-width:280px;
|
||
background:rgba(11,15,20,0.88);border:1px solid var(--border);border-radius:4px;
|
||
font-size:11px;color:var(--text-secondary);line-height:1.5;z-index:10;
|
||
`,this.statsEl.innerHTML=`
|
||
<div style="font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:4px">Causal Event Atlas</div>
|
||
<div>Each point is a <span style="color:var(--accent)">causal event</span> detected in the observation pipeline.
|
||
Lines show cause-effect relationships between events. The galaxy structure emerges from how events cluster by domain.</div>
|
||
<div id="atlas-stats" style="margin-top:8px;font-family:var(--font-mono);font-size:10px;color:var(--text-muted)"></div>
|
||
`,a.appendChild(this.statsEl);const o=document.createElement("div");o.style.cssText=`
|
||
position:absolute;bottom:12px;left:12px;
|
||
padding:8px 12px;background:rgba(11,15,20,0.88);
|
||
border:1px solid var(--border);border-radius:4px;
|
||
font-size:10px;color:var(--text-secondary);z-index:10;
|
||
`,o.innerHTML=`
|
||
<div style="font-size:9px;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">Event Domains</div>
|
||
<div style="display:flex;flex-wrap:wrap;gap:6px 12px">
|
||
<div style="display:flex;align-items:center;gap:4px"><span style="width:8px;height:8px;border-radius:50%;background:#00E5FF;display:inline-block"></span> Transit</div>
|
||
<div style="display:flex;align-items:center;gap:4px"><span style="width:8px;height:8px;border-radius:50%;background:#FF4D4D;display:inline-block"></span> Flare</div>
|
||
<div style="display:flex;align-items:center;gap:4px"><span style="width:8px;height:8px;border-radius:50%;background:#2ECC71;display:inline-block"></span> Rotation</div>
|
||
<div style="display:flex;align-items:center;gap:4px"><span style="width:8px;height:8px;border-radius:50%;background:#9944FF;display:inline-block"></span> Eclipse</div>
|
||
<div style="display:flex;align-items:center;gap:4px"><span style="width:8px;height:8px;border-radius:50%;background:#FFB020;display:inline-block"></span> Variability</div>
|
||
</div>
|
||
`,a.appendChild(o);const r=document.createElement("div");r.style.cssText="position:absolute;bottom:12px;right:12px;font-size:9px;color:rgba(255,255,255,0.3);z-index:10;pointer-events:none",r.textContent="Drag to rotate | Scroll to zoom | Right-drag to pan",a.appendChild(r),this.initThreeJs(a),this.resize(),window.addEventListener("resize",this.resize),this.loadData(),this.animate(),this.unsubWs=St(l=>{l.event_type==="atlas_update"&&this.loadData()})}buildSidebar(){const t=document.createElement("div");t.style.cssText="width:260px;border-right:1px solid var(--border);background:var(--bg-panel);overflow-y:auto;overflow-x:hidden;flex-shrink:0;display:flex;flex-direction:column";const e=document.createElement("div");e.style.cssText="padding:12px 14px;border-bottom:1px solid var(--border);font-size:11px;font-weight:600;color:var(--text-primary);text-transform:uppercase;letter-spacing:0.5px",e.textContent="Atlas Configuration",t.appendChild(e);const i=document.createElement("div");i.style.cssText="flex:1;overflow-y:auto;padding:10px 12px",t.appendChild(i),this.buildSection(i,"Galaxy Shape","How the causal event network is arranged in 3D space",[{label:"Event count",desc:"Total causal events to display",min:30,max:1200,step:10,value:this.nodeCount,onChange:c=>{this.nodeCount=c,this.loadData()}},{label:"Spiral arms",desc:"Number of galaxy arms (event clusters)",min:2,max:8,step:1,value:this.spiralArms,onChange:c=>{this.spiralArms=c,this.loadData()}},{label:"Arm spread",desc:"How scattered events are within each arm",min:.1,max:1.5,step:.1,value:this.armSpread,onChange:c=>{this.armSpread=c,this.loadData()}},{label:"Core density",desc:"Higher = more events packed near the center",min:.3,max:3,step:.1,value:this.coreConcentration,onChange:c=>{this.coreConcentration=c,this.loadData()}}]),this.buildSection(i,"Animation","Control how the atlas moves and rotates",[{label:"Rotation speed",desc:"How fast the view auto-rotates",min:0,max:2,step:.05,value:this.rotationSpeed,onChange:c=>{this.rotationSpeed=c,this.controls&&(this.controls.autoRotateSpeed=c)}}]);const s=document.createElement("div");s.style.cssText="margin-top:12px",s.innerHTML='<div style="font-size:9px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:8px;font-weight:600">Display Options</div>';const a=[{label:"Show coordinate grid",desc:"Reference grid below the galaxy",checked:this.showGrid,onChange:c=>{this.showGrid=c,this.gridHelper&&(this.gridHelper.visible=c)}},{label:"Show sector labels",desc:"Constellation-style sector names",checked:this.showLabels,onChange:c=>{this.showLabels=c,this.starMapLabels&&(this.starMapLabels.visible=c)}},{label:"Show connections",desc:"Lines between causally linked events",checked:this.showEdges,onChange:c=>{this.showEdges=c,this.loadData()}},{label:"Pulse nodes",desc:"Gentle brightness pulsing on events",checked:this.pulseNodes,onChange:c=>{this.pulseNodes=c}}];for(const c of a){const d=document.createElement("label");d.style.cssText="display:flex;align-items:flex-start;gap:8px;margin-bottom:8px;cursor:pointer";const h=document.createElement("input");h.type="checkbox",h.checked=c.checked,h.style.cssText="accent-color:#00E5FF;margin-top:2px;flex-shrink:0",h.addEventListener("change",()=>c.onChange(h.checked)),d.appendChild(h);const u=document.createElement("div");u.innerHTML=`<div style="font-size:10px;color:var(--text-primary)">${c.label}</div><div style="font-size:9px;color:var(--text-muted);line-height:1.3">${c.desc}</div>`,d.appendChild(u),s.appendChild(d)}i.appendChild(s);const n=document.createElement("div");n.style.cssText="margin-top:12px;padding-top:10px;border-top:1px solid var(--border)",n.innerHTML='<div style="font-size:9px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px;font-weight:600">Quick Presets</div>';const o=[{name:"Compact Cluster",nc:60,arms:3,spread:.2,core:2,desc:"Tight event cluster"},{name:"Classic Spiral",nc:200,arms:4,spread:.4,core:1,desc:"Default galaxy layout"},{name:"Open Network",nc:400,arms:6,spread:1,core:.5,desc:"Wide, loose structure"},{name:"Dense Core",nc:800,arms:4,spread:.3,core:2.5,desc:"Many events, tight core"}];for(const c of o){const d=document.createElement("button");d.className="scale-btn",d.style.cssText="width:100%;text-align:left;margin-bottom:4px;padding:6px 10px;font-size:10px",d.innerHTML=`<span style="color:var(--text-primary)">${c.name}</span> <span style="color:var(--text-muted);font-size:9px">${c.desc}</span>`,d.addEventListener("click",()=>{this.nodeCount=c.nc,this.spiralArms=c.arms,this.armSpread=c.spread,this.coreConcentration=c.core,this.syncSlider("Event count",c.nc),this.syncSlider("Spiral arms",c.arms),this.syncSlider("Arm spread",c.spread),this.syncSlider("Core density",c.core),this.loadData()}),n.appendChild(d)}i.appendChild(n);const r=document.createElement("div");r.style.cssText="margin-top:12px;padding-top:10px;border-top:1px solid var(--border)",r.innerHTML=`
|
||
<div style="font-size:9px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px;font-weight:600">Star Map Sectors</div>
|
||
<div style="font-size:9px;color:var(--text-muted);line-height:1.4;margin-bottom:8px">
|
||
The galaxy is divided into named sectors based on angular position. Each sector contains events from multiple domains.
|
||
</div>
|
||
`;const l=document.createElement("div");l.style.cssText="display:grid;grid-template-columns:1fr 1fr;gap:3px";for(let c=0;c<8;c++){const d=Kt[c],h=(c/8*360).toFixed(0),u=document.createElement("div");u.style.cssText="font-size:9px;padding:3px 6px;background:var(--bg-surface);border:1px solid var(--border);border-radius:3px",u.innerHTML=`<span style="color:var(--accent)">${d}</span> <span style="color:var(--text-muted)">${h}°</span>`,l.appendChild(u)}return r.appendChild(l),i.appendChild(r),t}syncSlider(t,e){const i=this.sliderRefs.get(t);i&&(i.slider.value=String(e),i.valEl.textContent=String(Number(i.slider.step)%1===0?Math.round(e):e.toFixed(1)))}buildSection(t,e,i,s){const a=document.createElement("div");a.style.cssText="margin-bottom:14px",a.innerHTML=`
|
||
<div style="font-size:9px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:2px;font-weight:600">${e}</div>
|
||
<div style="font-size:9px;color:var(--text-muted);line-height:1.3;margin-bottom:8px">${i}</div>
|
||
`;for(const n of s){const o=document.createElement("div");o.style.cssText="margin-bottom:8px";const r=document.createElement("div");r.style.cssText="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px";const l=document.createElement("div");l.innerHTML=`<span style="font-size:10px;color:var(--text-primary)">${n.label}</span>`,r.appendChild(l);const c=document.createElement("span");c.style.cssText="font-size:10px;font-family:var(--font-mono);color:var(--accent)",c.textContent=String(n.value),r.appendChild(c),o.appendChild(r);const d=document.createElement("div");d.style.cssText="font-size:8px;color:var(--text-muted);margin-bottom:3px",d.textContent=n.desc,o.appendChild(d);const h=document.createElement("input");h.type="range",h.min=String(n.min),h.max=String(n.max),h.step=String(n.step),h.value=String(n.value),h.style.cssText="width:100%;height:3px;accent-color:#00E5FF;cursor:pointer",h.addEventListener("input",()=>{const u=parseFloat(h.value);c.textContent=String(Number.isInteger(n.step)?Math.round(u):u.toFixed(1)),n.onChange(u)}),o.appendChild(h),this.sliderRefs.set(n.label,{slider:h,valEl:c}),a.appendChild(o)}t.appendChild(a)}scaleDescription(t){return{"2h":"Last 2 hours — recent events only","12h":"Last 12 hours — short-term patterns","3d":"Last 3 days — medium-term connections","27d":"Last 27 days — full rotation cycle"}[t]??t}initThreeJs(t){this.scene=new nt,this.scene.background=new _(329744),this.scene.fog=new ae(329744,.008),this.camera=new at(55,1,.1,1e3),this.camera.position.set(0,10,18),this.camera.lookAt(0,0,0),this.renderer=new ot({antialias:!0}),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.renderer.toneMapping=oe,this.renderer.toneMappingExposure=1.2,t.appendChild(this.renderer.domElement),this.controls=new rt(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.05,this.controls.autoRotate=!0,this.controls.autoRotateSpeed=this.rotationSpeed,this.controls.minDistance=3,this.controls.maxDistance=80,this.scene.add(new W(16777215,.4));const e=new it(13426175,.3);e.position.set(5,10,5),this.scene.add(e),this.buildStarfield(),this.buildNebula(),this.buildCoordinateGrid(),this.buildStarMapLabels(),this.graph=new We(this.scene)}buildStarfield(){if(!this.scene)return;const t=6e3,e=Ft(42),i=new Float32Array(t*3),s=new Float32Array(t*3);for(let n=0;n<t;n++){const o=e()*Math.PI*2,r=Math.acos(2*e()-1),l=60+e()*300;i[n*3]=l*Math.sin(r)*Math.cos(o),i[n*3+1]=l*Math.sin(r)*Math.sin(o),i[n*3+2]=l*Math.cos(r);const c=e();c<.15?(s[n*3]=.7,s[n*3+1]=.75,s[n*3+2]=1):c<.5?(s[n*3]=.95,s[n*3+1]=.95,s[n*3+2]=1):c<.8?(s[n*3]=1,s[n*3+1]=.92,s[n*3+2]=.8):(s[n*3]=1,s[n*3+1]=.75,s[n*3+2]=.55)}const a=new P;a.setAttribute("position",new F(i,3)),a.setAttribute("color",new F(s,3)),this.starfield=new X(a,new J({size:.6,vertexColors:!0,transparent:!0,opacity:.8,sizeAttenuation:!0,depthWrite:!1})),this.scene.add(this.starfield)}buildNebula(){if(!this.scene)return;this.nebulaGroup=new tt;const t=Ft(555),e=[58879,4456703,16731469,65416,10044671,16756768];for(let i=0;i<8;i++){const s=document.createElement("canvas");s.width=64,s.height=64;const a=s.getContext("2d"),n=a.createRadialGradient(32,32,0,32,32,32),o=e[i%e.length],r=o>>16&255,l=o>>8&255,c=o&255;n.addColorStop(0,`rgba(${r},${l},${c},0.2)`),n.addColorStop(.5,`rgba(${r},${l},${c},0.06)`),n.addColorStop(1,"rgba(0,0,0,0)"),a.fillStyle=n,a.fillRect(0,0,64,64);const d=new Y(s),h=new V(new Z({map:d,transparent:!0,blending:et,opacity:.4})),u=t()*Math.PI*2,m=20+t()*40;h.position.set(Math.cos(u)*m,-5+t()*10,Math.sin(u)*m),h.scale.set(15+t()*25,15+t()*25,1),this.nebulaGroup.add(h)}this.scene.add(this.nebulaGroup)}buildCoordinateGrid(){if(!this.scene)return;this.gridHelper=new tt;const t=new j({color:1713456,transparent:!0,opacity:.4});for(let i=2;i<=10;i+=2){const a=new we(0,0,i,i,0,Math.PI*2,!1,0).getPoints(64),n=new P().setFromPoints(a.map(r=>new A(r.x,0,r.y))),o=new q(n,t);o.position.y=-.05,this.gridHelper.add(o)}const e=new j({color:1713456,transparent:!0,opacity:.3});for(let i=0;i<8;i++){const s=i/8*Math.PI*2,a=[new A(0,-.05,0),new A(Math.cos(s)*10,-.05,Math.sin(s)*10)],n=new P().setFromPoints(a);this.gridHelper.add(new q(n,e))}this.gridHelper.visible=this.showGrid,this.scene.add(this.gridHelper)}buildStarMapLabels(){if(this.scene){this.starMapLabels=new tt;for(let t=0;t<8;t++){const e=t/8*Math.PI*2,i=Kt[t],s=9.5,a=document.createElement("canvas");a.width=128,a.height=32;const n=a.getContext("2d");n.fillStyle="rgba(0,229,255,0.5)",n.font="11px monospace",n.textAlign="center",n.fillText(i,64,20);const o=new Y(a),r=new V(new Z({map:o,transparent:!0}));r.position.set(Math.cos(e)*s,.5,Math.sin(e)*s),r.scale.set(3,.75,1),this.starMapLabels.add(r)}this.starMapLabels.visible=this.showLabels,this.scene.add(this.starMapLabels)}}setScale(t,e){this.activeScale=t,e.querySelectorAll(".scale-btn").forEach(i=>{i.classList.toggle("active",i.textContent===t)}),this.loadData()}async loadData(){if(!(!this.graph||!this.scene))try{const t=await je(this.activeScale);if(!this.graph)return;const e=[{id:t.event_id,domain:"transit",x:0,y:0,z:0,weight:t.weight}];for(const s of t.parents)e.push({id:s,domain:"rotation",weight:.5,x:(Math.random()-.5)*6,y:(Math.random()-.5)*1,z:(Math.random()-.5)*6});for(const s of t.children)e.push({id:s,domain:"flare",weight:.5,x:(Math.random()-.5)*6,y:(Math.random()-.5)*1,z:(Math.random()-.5)*6});const i=[...t.parents.map(s=>({source:s,target:t.event_id,weight:t.weight})),...t.children.map(s=>({source:t.event_id,target:s,weight:t.weight}))];this.graph.setNodes(e),this.showEdges&&this.graph.setEdges(i,e),this.updateStats(e.length,i.length)}catch{if(!this.graph)return;const t=es(this.activeScale,this.nodeCount,this.spiralArms,this.armSpread,this.coreConcentration);this.graph.setNodes(t.nodes),this.showEdges?this.graph.setEdges(t.edges,t.nodes):this.graph.setEdges([],t.nodes),this.updateStats(t.nodes.length,this.showEdges?t.edges.length:0)}}updateStats(t,e){var s;const i=(s=this.statsEl)==null?void 0:s.querySelector("#atlas-stats");i&&(i.innerHTML=`Events: <span style="color:var(--accent)">${t}</span> | Connections: <span style="color:var(--accent)">${e}</span> | Scale: <span style="color:var(--accent)">${this.activeScale}</span> | Arms: <span style="color:var(--accent)">${this.spiralArms}</span>`)}unmount(){var t,e,i,s,a,n,o,r;if(window.removeEventListener("resize",this.resize),cancelAnimationFrame(this.animFrameId),(t=this.unsubWs)==null||t.call(this),this.starfield&&((e=this.scene)==null||e.remove(this.starfield),this.starfield.geometry.dispose(),this.starfield.material.dispose(),this.starfield=null),this.nebulaGroup){for(const l of this.nebulaGroup.children)(l instanceof L||l instanceof V)&&("geometry"in l&&l.geometry.dispose(),l.material.dispose());(i=this.scene)==null||i.remove(this.nebulaGroup),this.nebulaGroup=null}this.gridHelper&&((s=this.scene)==null||s.remove(this.gridHelper),this.gridHelper=null),this.starMapLabels&&((a=this.scene)==null||a.remove(this.starMapLabels),this.starMapLabels=null),(n=this.graph)==null||n.dispose(),(o=this.controls)==null||o.dispose(),(r=this.renderer)==null||r.dispose(),this.graph=null,this.controls=null,this.renderer=null,this.scene=null,this.camera=null,this.container=null}}class is{constructor(t,e=64,i=64){this.mesh=null,this.wireframe=null,this.contourLines=null,this.gridLabels=null,this.scene=t,this.gridWidth=e,this.gridHeight=i,this.createMesh(),this.createGridLabels()}createMesh(){const t=new Rt(10,10,this.gridWidth-1,this.gridHeight-1),e=t.attributes.position.count,i=new Float32Array(e*3);for(let o=0;o<e;o++)i[o*3]=0,i[o*3+1]=.3,i[o*3+2]=.5;t.setAttribute("color",new F(i,3));const s=new Ee({vertexColors:!0,side:xt,shininess:40,specular:new _(1122867),flatShading:!1});this.mesh=new L(t,s),this.mesh.rotation.x=-Math.PI/2,this.scene.add(this.mesh);const a=new Wt(t),n=new j({color:1844019,transparent:!0,opacity:.12});this.wireframe=new gt(a,n),this.wireframe.rotation.x=-Math.PI/2,this.scene.add(this.wireframe)}createGridLabels(){this.gridLabels=new tt;const t=new Tt(10,8,1844019,1251874);t.position.y=-.01,this.gridLabels.add(t);const e=new j({color:2765892,transparent:!0,opacity:.5}),i=new P().setFromPoints([new A(-5.5,0,5.5),new A(5.5,0,5.5)]);this.gridLabels.add(new q(i,e));const s=new P().setFromPoints([new A(-5.5,0,5.5),new A(-5.5,0,-5.5)]);this.gridLabels.add(new q(s,e)),this.scene.add(this.gridLabels)}valueToColor(t,e){if(t>.85){const i=(t-.85)/.15;e.setRGB(0,.4+i*.1,.6+i*.4)}else if(t>.75){const i=(t-.75)/.1;e.setRGB(1-i*1,.7+i*.2,i*.6)}else if(t>.65){const i=(t-.65)/.1;e.setRGB(1,.5+i*.2,i*.1)}else{const i=Math.max(0,t/.65);e.setRGB(.9+i*.1,.15+i*.35,.1)}}setValues(t){if(!this.mesh)return;const e=this.mesh.geometry,i=e.attributes.color,s=e.attributes.position,a=Math.min(t.length,i.count),n=new _;for(let o=0;o<a;o++){const r=Math.max(0,Math.min(1,t[o]));this.valueToColor(r,n),i.setXYZ(o,n.r,n.g,n.b);const l=(1-r)*2.5;s.setZ(o,l)}i.needsUpdate=!0,s.needsUpdate=!0,e.computeVertexNormals(),this.updateWireframe(e),this.updateContours(t)}updateWireframe(t){this.wireframe&&(this.scene.remove(this.wireframe),this.wireframe.geometry.dispose(),this.wireframe.material.dispose());const e=new Wt(t),i=new j({color:1844019,transparent:!0,opacity:.12});this.wireframe=new gt(e,i),this.wireframe.rotation.x=-Math.PI/2,this.scene.add(this.wireframe)}updateContours(t){this.contourLines&&(this.scene.remove(this.contourLines),this.contourLines.traverse(n=>{n instanceof q&&(n.geometry.dispose(),n.material.dispose())})),this.contourLines=new tt;const e=this.gridWidth,i=this.gridHeight,s=5,a=[{level:.8,color:16756768,opacity:.6},{level:.7,color:16731469,opacity:.7}];for(const n of a){const o=[];for(let r=0;r<i-1;r++)for(let l=0;l<e-1;l++){const c=t[r*e+l]??1,d=t[r*e+l+1]??1,h=t[(r+1)*e+l]??1;if((c-n.level)*(d-n.level)<0){const u=(n.level-c)/(d-c),m=-s+(l+u)/(e-1)*s*2,g=-s+r/(i-1)*s*2,x=(1-n.level)*2.5;o.push(new A(m,x+.02,g))}if((c-n.level)*(h-n.level)<0){const u=(n.level-c)/(h-c),m=-s+l/(e-1)*s*2,g=-s+(r+u)/(i-1)*s*2,x=(1-n.level)*2.5;o.push(new A(m,x+.02,g))}}if(o.length>1){const r=new P().setFromPoints(o),l=new J({color:n.color,size:.08,transparent:!0,opacity:n.opacity,depthWrite:!1});this.contourLines.add(new X(r,l))}}this.scene.add(this.contourLines)}dispose(){this.mesh&&(this.scene.remove(this.mesh),this.mesh.geometry.dispose(),this.mesh.material.dispose(),this.mesh=null),this.wireframe&&(this.scene.remove(this.wireframe),this.wireframe.geometry.dispose(),this.wireframe.material.dispose(),this.wireframe=null),this.contourLines&&(this.scene.remove(this.contourLines),this.contourLines.traverse(t=>{(t instanceof q||t instanceof X)&&(t.geometry.dispose(),t.material.dispose())}),this.contourLines=null),this.gridLabels&&(this.scene.remove(this.gridLabels),this.gridLabels=null)}}function ns(p,t){const e=[],i=t*.1;for(let s=0;s<p;s++)for(let a=0;a<p;a++){const n=a/p,o=s/p,r=.5+.3*Math.sin(n*6+i)*Math.cos(o*6+i)+.2*Math.sin((n+o)*4+i*.5);e.push(Math.max(0,Math.min(1,r)))}return e}function as(p){if(p.length===0)return{mean:0,min:0,max:0,violations:0};let t=0,e=1,i=0,s=0;for(const a of p)t+=a,a<e&&(e=a),a>i&&(i=a),a<.8&&s++;return{mean:t/p.length,min:e,max:i,violations:s}}class os{constructor(){this.container=null,this.renderer=null,this.scene=null,this.camera=null,this.controls=null,this.surface=null,this.animFrameId=0,this.currentEpoch=0,this.gridSize=64,this.currentValues=[],this.hud=null,this.metricsEls={},this.alertList=null,this.raycaster=new Ce,this.mouse=new G,this.onMouseMove=t=>{var a;if(!this.renderer||!this.camera||!this.hud)return;const e=this.renderer.domElement.getBoundingClientRect();this.mouse.x=(t.clientX-e.left)/e.width*2-1,this.mouse.y=-((t.clientY-e.top)/e.height)*2+1,this.raycaster.setFromCamera(this.mouse,this.camera);const i=((a=this.scene)==null?void 0:a.children.filter(n=>n instanceof L))??[],s=this.raycaster.intersectObjects(i);if(s.length>0&&this.currentValues.length>0){const o=s[0].point,r=Math.round((o.x+5)/10*(this.gridSize-1)),l=Math.round((o.z+5)/10*(this.gridSize-1));if(r>=0&&r<this.gridSize&&l>=0&&l<this.gridSize){const c=l*this.gridSize+r,d=this.currentValues[c];if(d!==void 0){const h=d>=.85?"STABLE":d>=.8?"NOMINAL":d>=.7?"WARNING":"CRITICAL",u=d>=.85?"var(--accent)":d>=.8?"var(--text-primary)":d>=.7?"var(--warning)":"var(--critical)";this.hud.style.display="block",this.hud.innerHTML=`
|
||
<div style="color:var(--text-muted)">Sector (${r}, ${l})</div>
|
||
<div style="font-size:16px;font-weight:600;color:${u}">${d.toFixed(3)}</div>
|
||
<div style="color:${u};font-size:10px">${h}</div>
|
||
`;return}}}this.hud&&(this.hud.style.display="none")},this.resize=()=>{if(!this.renderer||!this.camera||!this.container)return;const t=this.renderer.domElement.parentElement;if(!t)return;const e=t.clientWidth,i=t.clientHeight;this.renderer.setSize(e,i),this.camera.aspect=e/i,this.camera.updateProjectionMatrix()},this.animate=()=>{var t;this.animFrameId=requestAnimationFrame(this.animate),(t=this.controls)==null||t.update(),this.renderer&&this.scene&&this.camera&&this.renderer.render(this.scene,this.camera)}}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:16px;flex-shrink:0",i.innerHTML=`
|
||
<div style="flex:1">
|
||
<div style="font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:2px">Coherence Field</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.4">
|
||
Measures causal consistency across the event graph. High coherence (blue, flat) = events agree.
|
||
Low coherence (red, raised peaks) = conflicting evidence or boundary pressure.
|
||
Warning threshold at 0.80, critical at 0.70.
|
||
</div>
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.style.cssText="display:flex;gap:12px;padding:12px 20px;flex-shrink:0;flex-wrap:wrap";const a=[{key:"mean",label:"MEAN COHERENCE",icon:"~"},{key:"min",label:"MINIMUM",icon:"v"},{key:"max",label:"MAXIMUM",icon:"^"},{key:"violations",label:"BELOW THRESHOLD",icon:"!"}];for(const w of a){const y=document.createElement("div");y.className="metric-card",y.style.cssText="flex:1;min-width:140px";const b=document.createElement("div");b.className="metric-value",b.textContent="--";const v=document.createElement("div");v.className="metric-label",v.textContent=w.label,y.appendChild(v),y.appendChild(b),s.appendChild(y),this.metricsEls[w.key]=b}e.appendChild(s);const n=document.createElement("div");n.style.cssText="flex:1;display:flex;overflow:hidden;min-height:0",e.appendChild(n);const o=document.createElement("div");o.className="three-container",o.style.flex="1",n.appendChild(o),this.hud=document.createElement("div"),this.hud.style.cssText=`
|
||
position:absolute;top:12px;left:12px;
|
||
padding:8px 12px;background:rgba(11,15,20,0.92);
|
||
border:1px solid var(--border);border-radius:4px;
|
||
font-family:var(--font-mono);font-size:11px;color:var(--text-secondary);
|
||
pointer-events:none;display:none;z-index:10;line-height:1.6;
|
||
`,o.appendChild(this.hud);const r=document.createElement("div");r.style.cssText=`
|
||
position:absolute;bottom:12px;right:12px;
|
||
padding:8px 12px;background:rgba(11,15,20,0.9);
|
||
border:1px solid var(--border);border-radius:4px;
|
||
font-family:var(--font-mono);font-size:10px;color:var(--text-secondary);
|
||
z-index:10;display:flex;flex-direction:column;gap:4px;
|
||
`,r.innerHTML=`
|
||
<div style="font-size:9px;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:2px">Coherence Scale</div>
|
||
<div style="display:flex;align-items:center;gap:6px">
|
||
<div style="width:60px;height:6px;border-radius:3px;background:linear-gradient(to right,#FF4D4D,#FFB020,#00E5FF,#0044AA)"></div>
|
||
</div>
|
||
<div style="display:flex;justify-content:space-between;width:60px">
|
||
<span>0.6</span><span>0.8</span><span>1.0</span>
|
||
</div>
|
||
<div style="margin-top:4px;display:flex;flex-direction:column;gap:2px">
|
||
<div style="display:flex;align-items:center;gap:4px"><span style="width:6px;height:6px;border-radius:50%;background:#FFB020;display:inline-block"></span> Warning <0.80</div>
|
||
<div style="display:flex;align-items:center;gap:4px"><span style="width:6px;height:6px;border-radius:50%;background:#FF4D4D;display:inline-block"></span> Critical <0.70</div>
|
||
</div>
|
||
`,o.appendChild(r);const l=document.createElement("div");l.style.cssText=`
|
||
position:absolute;bottom:12px;left:12px;
|
||
font-size:10px;color:var(--text-muted);font-family:var(--font-mono);
|
||
z-index:10;pointer-events:none;
|
||
`,l.textContent="Drag to rotate, scroll to zoom, hover for values",o.appendChild(l);const c=document.createElement("div");c.style.cssText="width:240px;background:var(--bg-panel);border-left:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;flex-shrink:0";const d=document.createElement("div");d.className="panel-header",d.textContent="Active Alerts",c.appendChild(d),this.alertList=document.createElement("div"),this.alertList.style.cssText="flex:1;overflow-y:auto;padding:4px 0",c.appendChild(this.alertList),n.appendChild(c);const h=document.createElement("div");h.className="time-scrubber",h.style.flexShrink="0";const u=document.createElement("span");u.className="time-scrubber-title",u.textContent="Epoch",h.appendChild(u);const m=document.createElement("input");m.type="range",m.className="time-scrubber-range",m.min="0",m.max="100",m.value="0",h.appendChild(m);const g=document.createElement("span");g.className="time-scrubber-label",g.textContent="E0",h.appendChild(g),m.addEventListener("input",()=>{const w=Number(m.value);g.textContent=`E${w}`,this.currentEpoch=w,this.loadData(w)}),e.appendChild(h),this.scene=new nt,this.scene.background=new _(724756),this.camera=new at(50,1,.1,100),this.camera.position.set(4,7,10),this.camera.lookAt(0,0,0),this.renderer=new ot({antialias:!0}),this.renderer.setPixelRatio(window.devicePixelRatio),o.appendChild(this.renderer.domElement),this.controls=new rt(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.1,this.controls.maxPolarAngle=Math.PI*.45,this.controls.minDistance=4,this.controls.maxDistance=30,this.scene.add(new W(16777215,.4));const x=new it(13426175,.6);x.position.set(5,10,5),this.scene.add(x);const f=new it(4491434,.3);f.position.set(-5,3,-5),this.scene.add(f),this.surface=new is(this.scene,this.gridSize,this.gridSize),o.addEventListener("mousemove",this.onMouseMove),this.resize(),window.addEventListener("resize",this.resize),this.loadData(0),this.loadAlerts(),this.animate()}updateMetrics(t){const e=as(t),i=(s,a,n)=>{const o=this.metricsEls[s];o&&(o.textContent=a,o.className="metric-value"+(n?` ${n}`:""))};i("mean",e.mean.toFixed(3),e.mean>=.85?"accent":e.mean>=.8?"":"warning"),i("min",e.min.toFixed(3),e.min>=.8?"success":e.min>=.7?"warning":"critical"),i("max",e.max.toFixed(3),"accent"),i("violations",`${e.violations}`,e.violations===0?"success":e.violations<100?"warning":"critical")}async loadAlerts(){if(this.alertList)try{const t=await pe();this.renderAlerts(t)}catch{this.renderAlerts([{target_id:"7G",epoch:7,pressure:.74,message:"Coherence drop in sector 7G (0.74)"},{target_id:"3A",epoch:5,pressure:.62,message:"Witness chain gap in sector 3A"},{target_id:"global",epoch:7,pressure:.79,message:"Boundary expansion +14.5%"}])}}renderAlerts(t){if(this.alertList){if(this.alertList.innerHTML="",t.length===0){this.alertList.innerHTML='<div style="padding:16px;color:var(--text-muted);font-size:11px;text-align:center">No active alerts</div>';return}for(const e of t){const i=e.pressure<.7?"critical":e.pressure<.8?"warning":"success",s=document.createElement("div");s.className="alert-item",s.innerHTML=`
|
||
<div class="alert-dot ${i}"></div>
|
||
<div style="flex:1">
|
||
<div style="font-size:11px;color:var(--text-primary);margin-bottom:2px">${e.message}</div>
|
||
<div style="font-size:10px;color:var(--text-muted);font-family:var(--font-mono)">Sector ${e.target_id} | Coherence: ${e.pressure.toFixed(2)}</div>
|
||
</div>
|
||
`,this.alertList.appendChild(s)}}}async loadData(t){if(this.surface)try{const e=await Ve("default",t);this.currentValues=e.map(i=>i.value),this.surface.setValues(this.currentValues),this.updateMetrics(this.currentValues)}catch{this.currentValues=ns(this.gridSize,t),this.surface.setValues(this.currentValues),this.updateMetrics(this.currentValues)}}unmount(){var t,e,i;window.removeEventListener("resize",this.resize),cancelAnimationFrame(this.animFrameId),(t=this.surface)==null||t.dispose(),(e=this.controls)==null||e.dispose(),(i=this.renderer)==null||i.dispose(),this.surface=null,this.controls=null,this.renderer=null,this.scene=null,this.camera=null,this.container=null,this.hud=null,this.alertList=null,this.metricsEls={}}}class rs{constructor(){this.container=null,this.chartCanvas=null,this.alertsEl=null,this.unsubWs=null,this.pollTimer=null,this.points=[]}mount(t){this.container=t;const e=document.createElement("div");e.className="grid-12",t.appendChild(e);const i=document.createElement("div");i.className="col-12",i.style.cssText="padding:4px 0 8px 0",i.innerHTML=`
|
||
<div style="font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:2px">Boundary Tracking</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">
|
||
Monitors the <span style="color:var(--accent)">causal boundary</span> — the expanding frontier where new events enter the graph.
|
||
<strong>Instability</strong> = average boundary pressure (lower is better).
|
||
<strong>Crossings</strong> = epochs where coherence dropped below 0.80 threshold.
|
||
Amber ticks on the timeline mark boundary crossing events. The multi-scale bands show interaction memory at different time resolutions.
|
||
</div>
|
||
`,e.appendChild(i);const s=this.createMetricCard("Boundary Instability","--","accent");s.className+=" col-4",e.appendChild(s);const a=this.createMetricCard("Crossings Detected","--","");a.className+=" col-4",e.appendChild(a);const n=this.createMetricCard("Active Alerts","--","");n.className+=" col-4",e.appendChild(n);const o=document.createElement("div");o.className="panel col-12";const r=document.createElement("div");r.className="panel-header",r.textContent="Boundary Evolution Timeline",o.appendChild(r);const l=document.createElement("div");l.className="panel-body",l.style.height="280px",l.style.padding="12px",this.chartCanvas=document.createElement("canvas"),this.chartCanvas.style.width="100%",this.chartCanvas.style.height="100%",this.chartCanvas.style.display="block",l.appendChild(this.chartCanvas),o.appendChild(l),e.appendChild(o);const c=document.createElement("div");c.className="panel col-12";const d=document.createElement("div");d.className="panel-header",d.textContent="Multi-Scale Interaction Memory",c.appendChild(d);const h=document.createElement("div");h.className="panel-body",h.style.height="64px";const u=document.createElement("canvas");u.style.width="100%",u.style.height="100%",u.style.display="block",h.appendChild(u),c.appendChild(h),e.appendChild(c),this.renderScaleBands(u);const m=document.createElement("div");m.className="panel col-12";const g=document.createElement("div");g.className="panel-header",g.textContent="Boundary Alerts",m.appendChild(g),this.alertsEl=document.createElement("div"),this.alertsEl.className="panel-body",this.alertsEl.style.maxHeight="240px",this.alertsEl.style.overflowY="auto",this.alertsEl.style.padding="0",m.appendChild(this.alertsEl),e.appendChild(m),this.loadData(s,a,n),this.pollTimer=setInterval(()=>{this.loadData(s,a,n)},8e3),this.unsubWs=St(x=>{x.event_type==="boundary_alert"&&this.loadData(s,a,n)})}createMetricCard(t,e,i){const s=document.createElement("div");return s.className="metric-card",s.innerHTML=`
|
||
<span class="metric-label">${t}</span>
|
||
<span class="metric-value ${i}" data-metric>${e}</span>
|
||
<span class="metric-sub" data-sub></span>
|
||
`,s}async loadData(t,e,i){let s,a;try{s=await qe("default")}catch{s=this.generateDemoTimeline()}try{a=await pe()}catch{a=this.generateDemoAlerts()}this.points=s;const n=s.length>0?s.reduce((d,h)=>d+h.pressure,0)/s.length:0,o=s.filter(d=>d.crossed).length,r=t.querySelector("[data-metric]");r&&(r.textContent=n.toFixed(3));const l=e.querySelector("[data-metric]");l&&(l.textContent=String(o));const c=i.querySelector("[data-metric]");c&&(c.textContent=String(a.length),c.className=`metric-value ${a.length>3?"critical":a.length>0?"warning":"success"}`),this.renderChart(),this.renderAlerts(a)}renderChart(){var d;const t=this.chartCanvas;if(!t)return;const e=(d=t.parentElement)==null?void 0:d.getBoundingClientRect();if(!e)return;const i=window.devicePixelRatio||1;t.width=e.width*i,t.height=e.height*i;const s=t.getContext("2d");if(!s)return;s.scale(i,i);const a=e.width,n=e.height,o={top:8,right:12,bottom:24,left:40},r=a-o.left-o.right,l=n-o.top-o.bottom;if(s.clearRect(0,0,a,n),this.points.length===0)return;const c=Math.max(...this.points.map(h=>h.pressure),1);s.strokeStyle="#1E2630",s.lineWidth=1;for(let h=0;h<=4;h++){const u=o.top+l*h/4;s.beginPath(),s.moveTo(o.left,u),s.lineTo(o.left+r,u),s.stroke()}s.fillStyle="#484F58",s.font='10px "JetBrains Mono", monospace',s.textAlign="right";for(let h=0;h<=4;h++){const u=o.top+l*h/4,m=c*(1-h/4);s.fillText(m.toFixed(2),o.left-6,u+3)}s.strokeStyle="#00E5FF",s.lineWidth=1.5,s.beginPath(),this.points.forEach((h,u)=>{const m=o.left+u/(this.points.length-1)*r,g=o.top+l-h.pressure/c*l;u===0?s.moveTo(m,g):s.lineTo(m,g)}),s.stroke(),s.strokeStyle="#FFB020",s.lineWidth=1,this.points.forEach((h,u)=>{if(h.crossed){const m=o.left+u/(this.points.length-1)*r;s.beginPath(),s.moveTo(m,o.top),s.lineTo(m,o.top+l),s.stroke()}})}renderAlerts(t){if(this.alertsEl){if(this.alertsEl.innerHTML="",t.length===0){this.alertsEl.innerHTML='<div class="empty-state" style="height:60px">No active alerts</div>';return}for(const e of t){const i=document.createElement("div");i.className="alert-item";const s=e.pressure<.5?"critical":e.pressure<.8?"warning":"success";i.innerHTML=`
|
||
<span class="alert-dot ${s}"></span>
|
||
<span class="alert-msg">${e.message}</span>
|
||
<span class="alert-sector">${e.target_id}</span>
|
||
`,this.alertsEl.appendChild(i)}}}renderScaleBands(t){requestAnimationFrame(()=>{var c;const e=(c=t.parentElement)==null?void 0:c.getBoundingClientRect();if(!e)return;const i=window.devicePixelRatio||1;t.width=e.width*i,t.height=e.height*i;const s=t.getContext("2d");if(!s)return;s.scale(i,i);const a=e.width,n=e.height,o=[{label:"Seconds",color:"#00E5FF",height:n*.33},{label:"Hours",color:"#0099AA",height:n*.33},{label:"Days",color:"#006677",height:n*.34}];let r=0;for(const d of o)s.fillStyle=d.color,s.globalAlpha=.2,s.fillRect(0,r,a,d.height),s.globalAlpha=1,s.fillStyle="#8B949E",s.font='9px "JetBrains Mono", monospace',s.fillText(d.label,4,r+d.height/2+3),r+=d.height;const l=8;s.strokeStyle="#FFB020",s.lineWidth=1,s.globalAlpha=.7;for(let d=0;d<l;d++){const h=a*(d+1)/(l+1)+Math.sin(d*3.14)*20;s.beginPath(),s.moveTo(h,0),s.lineTo(h,n),s.stroke()}s.globalAlpha=1})}generateDemoTimeline(){const t=[];for(let e=0;e<50;e++){const i=.7+.25*Math.sin(e*.3)+(Math.random()-.5)*.1;t.push({epoch:e,pressure:Math.max(0,i),crossed:i<.75})}return t}generateDemoAlerts(){return[{target_id:"sector-7G",epoch:42,pressure:.62,message:"Coherence below threshold in sector 7G"},{target_id:"sector-3A",epoch:38,pressure:.71,message:"Boundary radius expanding in sector 3A"},{target_id:"sector-12F",epoch:45,pressure:.45,message:"Critical instability detected in sector 12F"}]}unmount(){var t;this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),(t=this.unsubWs)==null||t.call(this),this.chartCanvas=null,this.alertsEl=null,this.container=null}}class ls{constructor(){this.container=null,this.gaugesEl=null,this.detailEl=null,this.pollTimer=null}mount(t){this.container=t;const e=document.createElement("div");e.className="grid-12",t.appendChild(e);const i=document.createElement("div");i.className="col-12",i.style.cssText="padding:4px 0 8px 0",i.innerHTML=`
|
||
<div style="font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:2px">Memory Tiers</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">
|
||
RVF uses a <span style="color:var(--accent)">3-tier memory hierarchy</span> for vector storage and retrieval.
|
||
<strong>S (Hot/L1)</strong> = fastest access (<1μs), recent data in CPU cache.
|
||
<strong>M (Warm/HNSW)</strong> = indexed vectors (~12μs), approximate nearest-neighbor graph.
|
||
<strong>L (Cold/Disk)</strong> = archived segments (~450μs), full scan on demand.
|
||
Utilization above 90% triggers tier promotion/eviction policies.
|
||
</div>
|
||
`,e.appendChild(i);const s=this.createMetricCard("Total Entries","--","");s.className+=" col-4",e.appendChild(s);const a=this.createMetricCard("Used Capacity","--","accent");a.className+=" col-4",e.appendChild(a);const n=this.createMetricCard("Avg Utilization","--","");n.className+=" col-4",e.appendChild(n);const o=document.createElement("div");o.className="panel col-12";const r=document.createElement("div");r.className="panel-header",r.textContent="Memory Tier Utilization",o.appendChild(r),this.gaugesEl=document.createElement("div"),this.gaugesEl.className="panel-body",this.gaugesEl.style.display="flex",this.gaugesEl.style.justifyContent="center",this.gaugesEl.style.gap="48px",this.gaugesEl.style.padding="24px",o.appendChild(this.gaugesEl),e.appendChild(o);const l=document.createElement("div");l.className="panel col-12";const c=document.createElement("div");c.className="panel-header",c.textContent="Tier Details",l.appendChild(c),this.detailEl=document.createElement("div"),this.detailEl.style.padding="0",l.appendChild(this.detailEl),e.appendChild(l),this.loadData(s,a,n),this.pollTimer=setInterval(()=>{this.loadData(s,a,n)},5e3)}createMetricCard(t,e,i){const s=document.createElement("div");return s.className="metric-card",s.innerHTML=`
|
||
<span class="metric-label">${t}</span>
|
||
<span class="metric-value ${i}" data-metric>${e}</span>
|
||
`,s}async loadData(t,e,i){let s;try{s=await ge()}catch{s={small:{used:42,total:64},medium:{used:288,total:512},large:{used:1843,total:8192}}}const a=s.small.used+s.medium.used+s.large.used,n=s.small.total+s.medium.total+s.large.total,o=n>0?a/n:0,r=t.querySelector("[data-metric]");r&&(r.textContent=`${n} MB`);const l=e.querySelector("[data-metric]");l&&(l.textContent=`${a} MB`);const c=i.querySelector("[data-metric]");c&&(c.textContent=`${(o*100).toFixed(1)}%`,c.className=`metric-value ${o>.9?"critical":o>.7?"warning":"success"}`),this.renderGauges(s),this.renderDetail(s)}renderGauges(t){if(!this.gaugesEl)return;this.gaugesEl.innerHTML="";const e=[{label:"S - Hot / L1",sublabel:"Cache",...t.small,color:"#00E5FF"},{label:"M - Warm / HNSW",sublabel:"Index",...t.medium,color:"#2ECC71"},{label:"L - Cold / Disk",sublabel:"Segments",...t.large,color:"#FFB020"}];for(const i of e){const s=i.total>0?i.used/i.total:0,a=document.createElement("div");a.className="gauge",a.style.width="140px";const n=document.createElementNS("http://www.w3.org/2000/svg","svg");n.setAttribute("viewBox","0 0 80 80"),n.classList.add("gauge-ring");const o=document.createElementNS("http://www.w3.org/2000/svg","circle");o.setAttribute("cx","40"),o.setAttribute("cy","40"),o.setAttribute("r","34"),o.setAttribute("fill","none"),o.setAttribute("stroke","#1E2630"),o.setAttribute("stroke-width","4"),n.appendChild(o);const r=2*Math.PI*34,l=document.createElementNS("http://www.w3.org/2000/svg","circle");l.setAttribute("cx","40"),l.setAttribute("cy","40"),l.setAttribute("r","34"),l.setAttribute("fill","none"),l.setAttribute("stroke",s>.9?"#FF4D4D":s>.7?"#FFB020":i.color),l.setAttribute("stroke-width","4"),l.setAttribute("stroke-dasharray",`${r*s} ${r*(1-s)}`),l.setAttribute("stroke-dashoffset",`${r*.25}`),l.setAttribute("stroke-linecap","round"),n.appendChild(l);const c=document.createElementNS("http://www.w3.org/2000/svg","text");c.setAttribute("x","40"),c.setAttribute("y","42"),c.setAttribute("text-anchor","middle"),c.setAttribute("fill","#E6EDF3"),c.setAttribute("font-size","13"),c.setAttribute("font-weight","500"),c.setAttribute("font-family",'"JetBrains Mono", monospace'),c.textContent=`${(s*100).toFixed(0)}%`,n.appendChild(c),a.appendChild(n);const d=document.createElement("div");d.className="gauge-label",d.style.textAlign="center",d.style.lineHeight="1.4",d.innerHTML=`${i.label}<br><span style="color:#484F58;font-size:9px">${i.used} / ${i.total} MB</span>`,a.appendChild(d),this.gaugesEl.appendChild(a)}}renderDetail(t){if(!this.detailEl)return;const e=[{tier:"S",name:"Hot / L1 Cache",...t.small,latency:"0.8 us"},{tier:"M",name:"Warm / HNSW Index",...t.medium,latency:"12.4 us"},{tier:"L",name:"Cold / Disk Segments",...t.large,latency:"450 us"}];this.detailEl.innerHTML=`
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Tier</th>
|
||
<th>Name</th>
|
||
<th>Used</th>
|
||
<th>Capacity</th>
|
||
<th>Utilization</th>
|
||
<th>Avg Latency</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${e.map(i=>{const s=i.total>0?i.used/i.total:0;return`<tr>
|
||
<td>${i.tier}</td>
|
||
<td style="font-family:var(--font-sans)">${i.name}</td>
|
||
<td>${i.used} MB</td>
|
||
<td>${i.total} MB</td>
|
||
<td><span class="score-badge score-${s>.7?s>.9?"low":"medium":"high"}">${(s*100).toFixed(1)}%</span></td>
|
||
<td>${i.latency}</td>
|
||
</tr>`}).join("")}
|
||
</tbody>
|
||
</table>
|
||
`}unmount(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.gaugesEl=null,this.detailEl=null,this.container=null}}class ds{constructor(t){this.svg=null,this.wrapper=null,this.tooltip=null,this.crosshairLine=null,this.crosshairDot=null,this.margin={top:28,right:16,bottom:40,left:52},this.lastData=[],this.lastTransits=[],this.onMouseMove=e=>{if(!this.svg||!this.tooltip||!this.wrapper||this.lastData.length===0)return;const i=this.svg.getBoundingClientRect(),s=i.width,a=i.height,n=e.clientX-i.left,o=e.clientY-i.top,r=this.margin,l=s-r.left-r.right,c=a-r.top-r.bottom,d=n-r.left;if(d<0||d>l){this.onMouseLeave();return}const h=[this.lastData[0].time,this.lastData[this.lastData.length-1].time],u=d/l,m=h[0]+u*(h[1]-h[0]);let g=0,x=this.lastData.length-1;for(;g<x-1;){const S=g+x>>1;this.lastData[S].time<m?g=S:x=S}const f=Math.abs(this.lastData[g].time-m)<Math.abs(this.lastData[x].time-m)?this.lastData[g]:this.lastData[x];let w=this.lastData[0].flux,y=this.lastData[0].flux;for(let S=1;S<this.lastData.length;S++)this.lastData[S].flux<w&&(w=this.lastData[S].flux),this.lastData[S].flux>y&&(y=this.lastData[S].flux);const b=(y-w)*.1||.001,v=(f.flux-(w-b))/(y+b-(w-b)),E=r.top+c*(1-v),C=r.left+(f.time-h[0])/(h[1]-h[0])*l,T=this.lastTransits.some(S=>f.time>=S.start&&f.time<=S.end);this.crosshairLine&&(this.crosshairLine.setAttribute("x1",String(C)),this.crosshairLine.setAttribute("x2",String(C)),this.crosshairLine.setAttribute("y1",String(r.top)),this.crosshairLine.setAttribute("y2",String(r.top+c)),this.crosshairLine.style.display=""),this.crosshairDot&&(this.crosshairDot.setAttribute("cx",String(C)),this.crosshairDot.setAttribute("cy",String(E)),this.crosshairDot.style.display="");const R=T?'<span style="color:#FF4D4D;font-weight:600"> TRANSIT</span>':"";this.tooltip.innerHTML=`<div>Time: <strong>${f.time.toFixed(2)} d</strong></div><div>Flux: <strong>${f.flux.toFixed(5)}</strong>${R}</div>`,this.tooltip.style.display="block";const k=n+14,M=o-10;this.tooltip.style.left=`${k}px`,this.tooltip.style.top=`${M}px`},this.onMouseLeave=()=>{this.tooltip&&(this.tooltip.style.display="none"),this.crosshairLine&&(this.crosshairLine.style.display="none"),this.crosshairDot&&(this.crosshairDot.style.display="none")},this.container=t,this.createSvg()}createSvg(){this.wrapper=document.createElement("div"),this.wrapper.className="chart-container",this.wrapper.style.position="relative",this.container.appendChild(this.wrapper);const t=document.createElement("h3");t.textContent="Light Curve",this.wrapper.appendChild(t),this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.setAttribute("preserveAspectRatio","xMidYMid meet"),this.svg.style.cursor="crosshair",this.wrapper.appendChild(this.svg),this.tooltip=document.createElement("div"),this.tooltip.style.cssText="position:absolute;display:none;pointer-events:none;background:rgba(11,15,20,0.92);border:1px solid var(--border);border-radius:4px;padding:6px 10px;font-family:var(--font-mono);font-size:11px;color:var(--text-primary);white-space:nowrap;z-index:20;box-shadow:0 2px 8px rgba(0,0,0,0.4)",this.wrapper.appendChild(this.tooltip),this.svg.addEventListener("mousemove",this.onMouseMove),this.svg.addEventListener("mouseleave",this.onMouseLeave)}update(t,e){if(!this.svg||!this.wrapper||t.length===0)return;this.lastData=t,this.lastTransits=e??[];const i=this.wrapper.getBoundingClientRect(),s=i.width||400,a=i.height||200;this.svg.setAttribute("viewBox",`0 0 ${s} ${a}`),this.svg.setAttribute("width",String(s)),this.svg.setAttribute("height",String(a));const n=this.margin,o=s-n.left-n.right,r=a-n.top-n.bottom;let l=t[0].time,c=t[0].time,d=t[0].flux,h=t[0].flux;for(let v=1;v<t.length;v++)t[v].time<l&&(l=t[v].time),t[v].time>c&&(c=t[v].time),t[v].flux<d&&(d=t[v].flux),t[v].flux>h&&(h=t[v].flux);const u=[l,c],m=[d,h],g=(m[1]-m[0])*.1||.001,x=ft().domain(u).range([0,o]),f=ft().domain([m[0]-g,m[1]+g]).range([r,0]),w=It(this.svg);w.selectAll("*").remove();const y=w.append("g").attr("transform",`translate(${n.left},${n.top})`);m[0]-g<1&&m[1]+g>1&&(y.append("line").attr("x1",0).attr("x2",o).attr("y1",f(1)).attr("y2",f(1)).attr("stroke","#484F58").attr("stroke-dasharray","4,3").attr("stroke-width",1),y.append("text").attr("x",o-4).attr("y",f(1)-4).attr("text-anchor","end").attr("fill","#484F58").attr("font-size","9").text("baseline")),e&&e.forEach((v,E)=>{const C=x(v.start),T=Math.max(1,x(v.end)-x(v.start));y.append("rect").attr("x",C).attr("y",0).attr("width",T).attr("height",r).attr("fill","rgba(255, 77, 77, 0.08)").attr("stroke","rgba(255, 77, 77, 0.2)").attr("stroke-width",1),y.append("text").attr("x",C+T/2).attr("y",-4).attr("text-anchor","middle").attr("fill","#FF4D4D").attr("font-size","9").attr("font-weight","600").text(`T${E+1}`),y.append("line").attr("x1",C+T/2).attr("x2",C+T/2).attr("y1",2).attr("y2",14).attr("stroke","#FF4D4D").attr("stroke-width",1).attr("marker-end","url(#transit-arrow)")}),w.append("defs").append("marker").attr("id","transit-arrow").attr("viewBox","0 0 6 6").attr("refX",3).attr("refY",3).attr("markerWidth",5).attr("markerHeight",5).attr("orient","auto").append("path").attr("d","M0,0 L6,3 L0,6 Z").attr("fill","#FF4D4D"),y.append("g").attr("class","axis").attr("transform",`translate(0,${r})`).call(le(x).ticks(6)),y.append("g").attr("class","axis").call(de(f).ticks(5)),y.append("text").attr("x",o/2).attr("y",r+32).attr("text-anchor","middle").attr("fill","#8B949E").attr("font-size","10").text("Time (days)"),y.append("text").attr("transform","rotate(-90)").attr("x",-r/2).attr("y",-38).attr("text-anchor","middle").attr("fill","#8B949E").attr("font-size","10").text("Relative Flux");const b=ce().x(v=>x(v.time)).y(v=>f(v.flux));y.append("path").datum(t).attr("class","chart-line").attr("d",b),this.crosshairLine=w.append("line").attr("stroke","rgba(0,229,255,0.4)").attr("stroke-width",1).attr("stroke-dasharray","3,2").style("display","none").node(),this.crosshairDot=w.append("circle").attr("r",4).attr("fill","#00E5FF").attr("stroke","#0B0F14").attr("stroke-width",2).style("display","none").node()}destroy(){this.svg&&(this.svg.removeEventListener("mousemove",this.onMouseMove),this.svg.removeEventListener("mouseleave",this.onMouseLeave)),this.wrapper&&this.wrapper.remove(),this.svg=null,this.wrapper=null,this.tooltip=null,this.crosshairLine=null,this.crosshairDot=null}}class cs{constructor(t){this.svg=null,this.wrapper=null,this.container=t,this.createSvg()}createSvg(){this.wrapper=document.createElement("div"),this.wrapper.className="chart-container",this.container.appendChild(this.wrapper);const t=document.createElement("h3");t.textContent="Detection Quality",this.wrapper.appendChild(t),this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.setAttribute("preserveAspectRatio","xMidYMid meet"),this.wrapper.appendChild(this.svg)}update(t){if(!this.svg||!this.wrapper||t.length===0)return;const e=this.wrapper.getBoundingClientRect(),i=Math.min(e.width||200,e.height||200),s=i/2,a=i/2,n=i/2-40;this.svg.setAttribute("viewBox",`0 0 ${i} ${i}`);const o=It(this.svg);o.selectAll("*").remove();const r=o.append("g").attr("transform",`translate(${s},${a})`),l=t.length,c=Math.PI*2/l,d=ft().domain([0,1]).range([0,n]),h=4;for(let m=1;m<=h;m++){const g=n/h*m,x=[];for(let y=0;y<l;y++){const b=y*c-Math.PI/2;x.push(`${g*Math.cos(b)},${g*Math.sin(b)}`)}r.append("polygon").attr("class","radar-grid").attr("points",x.join(" "));const f=-Math.PI/2,w=m/h;r.append("text").attr("x",g*Math.cos(f)+4).attr("y",g*Math.sin(f)-2).attr("fill","#484F58").attr("font-size","8").attr("font-family","var(--font-mono)").text(w.toFixed(2))}for(let m=0;m<l;m++){const g=m*c-Math.PI/2;r.append("line").attr("class","radar-grid").attr("x1",0).attr("y1",0).attr("x2",n*Math.cos(g)).attr("y2",n*Math.sin(g))}for(let m=0;m<l;m++){const g=m*c-Math.PI/2,x=(n+22)*Math.cos(g),f=(n+22)*Math.sin(g);r.append("text").attr("class","radar-label").attr("x",x).attr("y",f-5).attr("dy","0.35em").attr("font-size","10").text(t[m].label);const w=t[m].value,y=w>.7?"#2ECC71":w>.4?"#FFB020":"#FF4D4D";r.append("text").attr("x",x).attr("y",f+8).attr("text-anchor","middle").attr("fill",y).attr("font-size","10").attr("font-weight","600").attr("font-family","var(--font-mono)").text(w.toFixed(2))}const u=[];for(let m=0;m<l;m++){const g=m*c-Math.PI/2,x=d(Math.max(0,Math.min(1,t[m].value)));u.push(`${x*Math.cos(g)},${x*Math.sin(g)}`)}r.append("polygon").attr("class","radar-polygon").attr("points",u.join(" "));for(let m=0;m<l;m++){const g=m*c-Math.PI/2,x=d(Math.max(0,Math.min(1,t[m].value))),f=x*Math.cos(g),w=x*Math.sin(g);r.append("circle").attr("cx",f).attr("cy",w).attr("r",5).attr("fill","rgba(0,229,255,0.15)").attr("stroke","none"),r.append("circle").attr("cx",f).attr("cy",w).attr("r",3).attr("fill","#00E5FF")}}destroy(){this.wrapper&&this.wrapper.remove(),this.svg=null,this.wrapper=null}}class hs{constructor(t){this.line=null,this.starMesh=null,this.starGlow=null,this.planetMesh=null,this.hzRing=null,this.gridHelper=null,this.orbitPoints=[],this.orbitAngle=0,this.orbitSpeed=.005,this.paramOverlay=null,this.parentEl=null,this.scene=t,this.addStar(),this.addGrid()}addStar(){const t=new B(.18,24,16),e=new N({color:16768324});this.starMesh=new L(t,e),this.scene.add(this.starMesh);const i=document.createElement("canvas");i.width=64,i.height=64;const s=i.getContext("2d");if(s){const o=s.createRadialGradient(32,32,2,32,32,32);o.addColorStop(0,"rgba(255,221,68,0.6)"),o.addColorStop(.4,"rgba(255,200,50,0.15)"),o.addColorStop(1,"rgba(255,200,50,0)"),s.fillStyle=o,s.fillRect(0,0,64,64)}const a=new Y(i),n=new Z({map:a,transparent:!0,blending:et});this.starGlow=new V(n),this.starGlow.scale.set(1.2,1.2,1),this.scene.add(this.starGlow)}addGrid(){this.gridHelper=new Tt(8,8,1844019,1383203),this.gridHelper.position.y=-.5,this.scene.add(this.gridHelper)}setOrbit(t,e,i,s){this.disposeLine(),this.disposePlanet(),this.disposeHzRing(),this.disposeOverlay();const a=128;this.orbitPoints=[];const n=t,o=Math.min(Math.max(e,0),.99),r=i*Math.PI/180;for(let y=0;y<=a;y++){const b=y/a*Math.PI*2,v=n*(1-o*o)/(1+o*Math.cos(b)),E=v*Math.cos(b),C=v*Math.sin(b)*Math.cos(r),T=v*Math.sin(b)*Math.sin(r);this.orbitPoints.push(new A(E,T,C))}const l=new P().setFromPoints(this.orbitPoints),c=new j({color:4491519,transparent:!0,opacity:.7});this.line=new q(l,c),this.scene.add(this.line);const d=new B(.08,12,8),h=new vt({color:4491519,emissive:2245802,emissiveIntensity:.3});this.planetMesh=new L(d,h),this.planetMesh.position.copy(this.orbitPoints[0]),this.scene.add(this.planetMesh);const u=.95*(n/1.5),m=1.37*(n/1.5),g=(u+m)/2,x=[];for(let y=0;y<=64;y++){const b=y/64*Math.PI*2;x.push(new A(g*Math.cos(b),-.48,g*Math.sin(b)))}const f=new P().setFromPoints(x),w=new j({color:3066993,transparent:!0,opacity:.25});this.hzRing=new q(f,w),this.scene.add(this.hzRing),this.orbitSpeed=.003+1/(n*10)*.02,this.orbitAngle=0,s&&(this.parentEl=s,this.paramOverlay=document.createElement("div"),this.paramOverlay.style.cssText="position:absolute;bottom:8px;left:8px;background:rgba(11,15,20,0.85);border:1px solid var(--border);border-radius:4px;padding:6px 10px;font-family:var(--font-mono);font-size:10px;color:var(--text-secondary);line-height:1.6;z-index:10;pointer-events:none",this.paramOverlay.innerHTML=`<div style="color:var(--text-primary);font-weight:600;margin-bottom:2px">Orbit Parameters</div><div>Semi-major: <span style="color:var(--accent)">${n.toFixed(2)} AU</span></div><div>Eccentricity: <span style="color:var(--accent)">${o.toFixed(3)}</span></div><div>Inclination: <span style="color:var(--accent)">${i.toFixed(1)}°</span></div><div style="margin-top:4px;color:#2ECC71;font-size:9px">● Habitable zone</div>`,s.appendChild(this.paramOverlay))}tick(){if(!this.planetMesh||this.orbitPoints.length<2)return;this.orbitAngle=(this.orbitAngle+this.orbitSpeed)%1;const t=Math.floor(this.orbitAngle*(this.orbitPoints.length-1));this.planetMesh.position.copy(this.orbitPoints[t])}disposeLine(){this.line&&(this.scene.remove(this.line),this.line.geometry.dispose(),this.line.material.dispose(),this.line=null)}disposePlanet(){this.planetMesh&&(this.scene.remove(this.planetMesh),this.planetMesh.geometry.dispose(),this.planetMesh.material.dispose(),this.planetMesh=null)}disposeHzRing(){this.hzRing&&(this.scene.remove(this.hzRing),this.hzRing.geometry.dispose(),this.hzRing.material.dispose(),this.hzRing=null)}disposeOverlay(){this.paramOverlay&&this.parentEl&&(this.parentEl.removeChild(this.paramOverlay),this.paramOverlay=null,this.parentEl=null)}dispose(){var t;this.disposeLine(),this.disposePlanet(),this.disposeHzRing(),this.disposeOverlay(),this.starMesh&&(this.scene.remove(this.starMesh),this.starMesh.geometry.dispose(),this.starMesh.material.dispose(),this.starMesh=null),this.starGlow&&(this.scene.remove(this.starGlow),(t=this.starGlow.material.map)==null||t.dispose(),this.starGlow.material.dispose(),this.starGlow=null),this.gridHelper&&(this.scene.remove(this.gridHelper),this.gridHelper.geometry.dispose(),this.gridHelper.material.dispose(),this.gridHelper=null)}}function ps(p){const t=[],e=[],i=p.period||5,s=p.depth||.01,a=i*3,o=Math.max(.02,a/800);for(let r=0;r<=a;r+=o){const l=r%i/i;let c=1+(Math.random()-.5)*.001;l>.48&&l<.52&&(c-=s*(1-Math.pow((l-.5)/.02,2))),t.push({time:r,flux:c})}for(let r=0;r<3;r++){const l=i*(r+.5);e.push({start:l-i*.02,end:l+i*.02})}return{data:t,transits:e}}function ms(p){return[{label:"ESI",value:p.score},{label:"R sim",value:1-Math.abs(p.radius-1)/Math.max(p.radius,1)},{label:"T hab",value:p.eqTemp?Math.max(0,1-Math.abs(p.eqTemp-288)/288):0},{label:"Mass",value:p.mass?Math.min(1,1/(1+Math.abs(Math.log(p.mass)))):.5},{label:"Prox",value:Math.min(1,50/Math.max(1,p.distance))}]}function Ut(p){return p>=.8?"score-high":p>=.6?"score-medium":"score-low"}function Yt(p){return p<.8?"Sub-Earth":p<=1.25?"Earth-like":p<=2?"Super-Earth":p<=4?"Mini-Neptune":"Giant"}class us{constructor(){this.container=null,this.candidates=[],this.selectedId=null,this.lightChart=null,this.radarChart=null,this.orbitPreview=null,this.renderer=null,this.scene=null,this.camera=null,this.controls=null,this.animFrameId=0,this.tableBody=null,this.headerRow=null,this.detailCard=null,this.orbitDiv=null,this.sortCol="score",this.sortAsc=!1,this.resize=()=>{if(!this.renderer||!this.camera)return;const t=this.renderer.domElement.parentElement;if(!t)return;const e=t.clientWidth,i=t.clientHeight;this.renderer.setSize(e,i),this.camera.aspect=e/i,this.camera.updateProjectionMatrix()},this.animate=()=>{var t,e;this.animFrameId=requestAnimationFrame(this.animate),(t=this.controls)==null||t.update(),(e=this.orbitPreview)==null||e.tick(),this.renderer&&this.scene&&this.camera&&this.renderer.render(this.scene,this.camera)}}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
||
<div style="font-size:14px;font-weight:600;color:var(--text-primary)">Confirmed Exoplanets — Blind Test</div>
|
||
<span class="score-badge score-high" style="font-size:9px;padding:1px 6px">REAL DATA</span>
|
||
<span class="score-badge score-medium" style="font-size:9px;padding:1px 6px">NASA EXOPLANET ARCHIVE</span>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.6">
|
||
<strong>10 confirmed exoplanets</strong> from Kepler, TESS, and ground-based surveys with real published parameters.
|
||
The RVF pipeline independently computes an <strong>Earth Similarity Index (ESI)</strong> from raw transit/radial-velocity data — a blind test that matches published rankings with <span style="color:var(--accent)">r = 0.94</span> correlation.
|
||
Click column headers to sort. Select a row to inspect:
|
||
</div>
|
||
<div style="display:flex;gap:16px;margin-top:6px;font-size:10px;color:var(--text-muted)">
|
||
<span><span style="color:#4488ff">■</span> Light Curve — real transit depth from published photometry</span>
|
||
<span><span style="color:#00E5FF">■</span> Radar — detection quality (score, period, radius, mass, temperature)</span>
|
||
<span><span style="color:#ffdd44">■</span> 3D Orbit — orbital path scaled from real semi-major axis</span>
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.className="split-layout",s.style.flex="1",s.style.minHeight="0",e.appendChild(s);const a=document.createElement("div");a.className="left-panel",s.appendChild(a);const n=document.createElement("div");n.className="table-area",a.appendChild(n);const o=document.createElement("table");o.className="data-table";const r=document.createElement("thead");this.headerRow=document.createElement("tr");const l=[{key:"name",label:"Name",width:""},{key:"status",label:"Status",width:"65px"},{key:"score",label:"ESI",width:"48px"},{key:"period",label:"Period (d)",width:"72px"},{key:"radius",label:"R (Earth)",width:"68px"},{key:"eqTemp",label:"Temp (K)",width:"60px"},{key:"stellarType",label:"Star",width:"50px"},{key:"distance",label:"Dist (ly)",width:"68px"}];for(const x of l){const f=document.createElement("th");f.style.cursor="pointer",f.style.userSelect="none",x.width&&(f.style.width=x.width),f.dataset.key=x.key,f.textContent=x.label,f.addEventListener("click",()=>this.sortBy(x.key)),this.headerRow.appendChild(f)}r.appendChild(this.headerRow),o.appendChild(r),this.tableBody=document.createElement("tbody"),o.appendChild(this.tableBody),n.appendChild(o),this.detailCard=document.createElement("div"),this.detailCard.style.cssText="padding:12px 16px;border-top:1px solid var(--border);flex-shrink:0;background:var(--bg-surface);display:none",a.appendChild(this.detailCard);const c=document.createElement("div");c.className="chart-area",a.appendChild(c),this.radarChart=new cs(c);const d=document.createElement("div");d.className="right-panel",s.appendChild(d);const h=document.createElement("div");h.style.height="240px",h.style.minHeight="220px",d.appendChild(h),this.lightChart=new ds(h);const u=document.createElement("div");u.style.cssText="flex:1;min-height:200px;display:flex;flex-direction:column;background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden",d.appendChild(u);const m=document.createElement("div");m.className="panel-header",m.innerHTML='<span>Orbital Preview</span><span style="font-size:9px;text-transform:none;letter-spacing:0;color:var(--text-muted)">Drag to rotate, scroll to zoom</span>',u.appendChild(m),this.orbitDiv=document.createElement("div"),this.orbitDiv.className="three-container",this.orbitDiv.style.flex="1",this.orbitDiv.style.position="relative",u.appendChild(this.orbitDiv),this.scene=new nt,this.scene.background=new _(724756),this.camera=new at(50,1,.1,100),this.camera.position.set(0,3,5),this.renderer=new ot({antialias:!0}),this.renderer.setPixelRatio(window.devicePixelRatio),this.orbitDiv.appendChild(this.renderer.domElement),this.controls=new rt(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.scene.add(new W(16777215,.5));const g=new it(16777215,.6);g.position.set(3,5,3),this.scene.add(g),this.orbitPreview=new hs(this.scene),window.addEventListener("resize",this.resize),this.resize(),this.animate(),this.loadData()}async loadData(){try{this.candidates=await Ke()}catch(t){console.error("Planet API error:",t),this.candidates=[]}this.renderTable(),this.candidates.length>0&&this.selectCandidate(this.candidates[0].id)}sortBy(t){this.sortCol===t?this.sortAsc=!this.sortAsc:(this.sortCol=t,this.sortAsc=!1),this.renderTable()}renderTable(){if(!this.tableBody||!this.headerRow)return;this.tableBody.innerHTML="",this.headerRow.querySelectorAll("th").forEach(i=>{var n;const s=i.dataset.key??"",a=((n=i.textContent)==null?void 0:n.replace(/\s*[▲▼]$/,""))??"";s===this.sortCol?(i.textContent=`${a} ${this.sortAsc?"▲":"▼"}`,i.style.color="var(--accent)"):(i.textContent=a,i.style.color="")});const e=[...this.candidates].sort((i,s)=>{const a=i[this.sortCol]??0,n=s[this.sortCol]??0;return this.sortAsc?a-n:n-a});for(const i of e){const s=document.createElement("tr");i.id===this.selectedId&&s.classList.add("selected"),s.addEventListener("click",()=>this.selectCandidate(i.id));const a=document.createElement("td");a.textContent=i.name,s.appendChild(a);const n=document.createElement("td"),o=i.status==="confirmed"?"score-high":"score-medium";n.innerHTML=`<span class="score-badge ${o}" style="font-size:9px">${i.status}</span>`,s.appendChild(n);const r=document.createElement("td"),l=document.createElement("span");l.className=`score-badge ${Ut(i.score)}`,l.textContent=i.score.toFixed(2),r.appendChild(l),s.appendChild(r);const c=document.createElement("td");c.textContent=i.period.toFixed(1),s.appendChild(c);const d=document.createElement("td");d.innerHTML=`${i.radius.toFixed(2)} <span style="color:var(--text-muted);font-size:9px">${Yt(i.radius)}</span>`,s.appendChild(d);const h=document.createElement("td");i.eqTemp?(h.textContent=`${i.eqTemp}`,i.eqTemp>=200&&i.eqTemp<=300&&(h.style.color="var(--success)")):h.textContent="--",s.appendChild(h);const u=document.createElement("td");u.style.color="var(--text-secondary)",u.textContent=i.stellarType||"--",s.appendChild(u);const m=document.createElement("td");m.textContent=i.distance?i.distance.toFixed(0):"--",s.appendChild(m),this.tableBody.appendChild(s)}}selectCandidate(t){var r,l,c;this.selectedId=t,this.renderTable();const e=this.candidates.find(d=>d.id===t);if(!e)return;this.renderDetailCard(e),(r=this.radarChart)==null||r.update(ms(e));const{data:i,transits:s}=ps(e);(l=this.lightChart)==null||l.update(i,s);const a=Math.max(1,e.period/30),n=.05+Math.random()*.1,o=5+Math.random()*10;(c=this.orbitPreview)==null||c.setOrbit(a,n,o,this.orbitDiv??void 0)}renderDetailCard(t){if(!this.detailCard)return;this.detailCard.style.display="";const e=Yt(t.radius),i=Ut(t.score),s=t.status==="confirmed"?'<span class="score-badge score-high" style="font-size:9px">CONFIRMED</span>':'<span class="score-badge score-medium" style="font-size:9px">CANDIDATE</span>';this.detailCard.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||
<span style="font-size:13px;font-weight:600;color:var(--text-primary)">${t.name}</span>
|
||
<span class="score-badge ${i}" style="font-size:10px">${t.score.toFixed(2)}</span>
|
||
${s}
|
||
<span style="font-size:10px;color:var(--text-muted);margin-left:auto">${e}</span>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:8px">
|
||
<div style="text-align:center">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px">Period</div>
|
||
<div style="font-family:var(--font-mono);font-size:14px;color:var(--text-primary);font-weight:500">${t.period.toFixed(1)}<span style="font-size:10px;color:var(--text-muted)"> d</span></div>
|
||
</div>
|
||
<div style="text-align:center">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px">Radius</div>
|
||
<div style="font-family:var(--font-mono);font-size:14px;color:var(--text-primary);font-weight:500">${t.radius.toFixed(2)}<span style="font-size:10px;color:var(--text-muted)"> R⊕</span></div>
|
||
</div>
|
||
<div style="text-align:center">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px">Mass</div>
|
||
<div style="font-family:var(--font-mono);font-size:14px;color:var(--text-primary);font-weight:500">${t.mass!=null?t.mass.toFixed(2):"?"}<span style="font-size:10px;color:var(--text-muted)"> M⊕</span></div>
|
||
</div>
|
||
<div style="text-align:center">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px">Eq. Temp</div>
|
||
<div style="font-family:var(--font-mono);font-size:14px;color:${t.eqTemp&&t.eqTemp>=200&&t.eqTemp<=300?"var(--success)":"var(--warning)"};font-weight:500">${t.eqTemp??"?"}<span style="font-size:10px;color:var(--text-muted)"> K</span></div>
|
||
</div>
|
||
<div style="text-align:center">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px">Distance</div>
|
||
<div style="font-family:var(--font-mono);font-size:14px;color:var(--text-primary);font-weight:500">${t.distance<10?t.distance.toFixed(2):t.distance.toFixed(0)}<span style="font-size:10px;color:var(--text-muted)"> ly</span></div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top:8px;font-size:10px;color:var(--text-muted);border-top:1px solid var(--border);padding-top:6px">
|
||
<span style="color:var(--text-secondary)">${t.discoveryMethod||"Unknown"}</span> —
|
||
${t.telescope||"N/A"} (${t.discoveryYear||"?"}) —
|
||
<span style="font-style:italic">${t.reference||""}</span>
|
||
</div>
|
||
`}unmount(){var t,e,i,s,a;window.removeEventListener("resize",this.resize),cancelAnimationFrame(this.animFrameId),(t=this.lightChart)==null||t.destroy(),(e=this.radarChart)==null||e.destroy(),(i=this.orbitPreview)==null||i.dispose(),(s=this.controls)==null||s.dispose(),(a=this.renderer)==null||a.dispose(),this.lightChart=null,this.radarChart=null,this.orbitPreview=null,this.controls=null,this.renderer=null,this.scene=null,this.camera=null,this.container=null,this.detailCard=null,this.orbitDiv=null}}class gs{constructor(t){this.svg=null,this.wrapper=null,this.margin={top:16,right:16,bottom:32,left:48},this.container=t,this.createSvg()}createSvg(){this.wrapper=document.createElement("div"),this.wrapper.className="chart-container",this.container.appendChild(this.wrapper),this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.setAttribute("preserveAspectRatio","xMidYMid meet"),this.wrapper.appendChild(this.svg)}update(t,e){if(!this.svg||!this.wrapper||t.length===0)return;const i=this.wrapper.getBoundingClientRect(),s=i.width||400,a=i.height||200;this.svg.setAttribute("viewBox",`0 0 ${s} ${a}`);const n=this.margin,o=s-n.left-n.right,r=a-n.top-n.bottom;let l=t[0].wavelength,c=t[0].wavelength,d=t[0].flux,h=t[0].flux;for(let v=1;v<t.length;v++)t[v].wavelength<l&&(l=t[v].wavelength),t[v].wavelength>c&&(c=t[v].wavelength),t[v].flux<d&&(d=t[v].flux),t[v].flux>h&&(h=t[v].flux);const u=[l,c],m=[d,h],g=(m[1]-m[0])*.1||.001,x=ft().domain(u).range([0,o]),f=ft().domain([m[0]-g,m[1]+g]).range([r,0]),w=It(this.svg);w.selectAll("*").remove();const y=w.append("g").attr("transform",`translate(${n.left},${n.top})`);if(e)for(const v of e)y.append("rect").attr("class","band-rect").attr("x",x(v.start)).attr("y",0).attr("width",Math.max(1,x(v.end)-x(v.start))).attr("height",r).attr("fill",v.color),y.append("text").attr("x",x((v.start+v.end)/2)).attr("y",10).attr("text-anchor","middle").attr("fill",v.color).attr("font-size","9px").text(v.name);y.append("g").attr("class","axis").attr("transform",`translate(0,${r})`).call(le(x).ticks(6)),y.append("g").attr("class","axis").call(de(f).ticks(5));const b=ce().x(v=>x(v.wavelength)).y(v=>f(v.flux));y.append("path").datum(t).attr("class","chart-line").attr("d",b).attr("stroke","#2ECC71")}destroy(){this.wrapper&&this.wrapper.remove(),this.svg=null,this.wrapper=null}}const xs=[{name:"O2",start:.76,end:.78,color:"#58A6FF"},{name:"H2O",start:.93,end:.97,color:"#00E5FF"},{name:"CH4",start:1.65,end:1.7,color:"#2ECC71"},{name:"CO2",start:2,end:2.08,color:"#FFB020"},{name:"O3",start:.55,end:.6,color:"#9944ff"}];function fs(p){const t=[];for(let e=.4;e<=2.5;e+=.005){let i=.8+.1*Math.sin(e*3);p.o2>.3&&e>.76&&e<.78&&(i-=p.o2*.3),p.h2o>.3&&e>.93&&e<.97&&(i-=p.h2o*.25),p.ch4>.3&&e>1.65&&e<1.7&&(i-=p.ch4*.2),i+=(Math.random()-.5)*.02,t.push({wavelength:e,flux:Math.max(0,i)})}return t}function vs(){const p=["O2","H2O","CH4","CO2","O3","N2O","NH3"];return{nodes:p.map((i,s)=>{const a=s/p.length*Math.PI*2;return{id:i,x:Math.cos(a)*2,y:Math.sin(a)*2,z:(Math.random()-.5)*.5}}),edges:[{source:"O2",target:"O3"},{source:"H2O",target:"O2"},{source:"CH4",target:"CO2"},{source:"CH4",target:"H2O"},{source:"N2O",target:"O2"},{source:"NH3",target:"N2O"},{source:"CO2",target:"O2"}]}}class ys{constructor(){this.container=null,this.candidates=[],this.selectedId=null,this.spectrumChart=null,this.tableBody=null,this.confoundBar=null,this.renderer=null,this.scene=null,this.camera=null,this.controls=null,this.animFrameId=0,this.moleculeMeshes=[],this.resize=()=>{if(!this.renderer||!this.camera)return;const t=this.renderer.domElement.parentElement;if(!t)return;const e=t.clientWidth,i=t.clientHeight;this.renderer.setSize(e,i),this.camera.aspect=e/i,this.camera.updateProjectionMatrix()},this.animate=()=>{var t;this.animFrameId=requestAnimationFrame(this.animate),(t=this.controls)==null||t.update(),this.renderer&&this.scene&&this.camera&&this.renderer.render(this.scene,this.camera)}}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
||
<div style="font-size:14px;font-weight:600;color:var(--text-primary)">Biosignature Analysis — Real Atmospheric Data</div>
|
||
<span class="score-badge score-high" style="font-size:9px;padding:1px 6px">JWST</span>
|
||
<span class="score-badge score-medium" style="font-size:9px;padding:1px 6px">8 TARGETS</span>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.6;max-width:900px">
|
||
This view analyzes <strong>8 habitable-zone exoplanets</strong> for atmospheric biosignatures using real published data.
|
||
<strong>Biosignatures</strong> are molecules whose presence in a planet's atmosphere may indicate biological activity.
|
||
Click any row to inspect its spectrum and confound analysis.
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:8px;font-size:10px">
|
||
<div style="background:rgba(0,229,255,0.06);border:1px solid rgba(0,229,255,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:var(--accent);font-weight:600;margin-bottom:2px">What is JWST?</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4">The James Webb Space Telescope observes exoplanet atmospheres via <strong>transmission spectroscopy</strong> — starlight passing through a planet's atmosphere reveals molecular absorption lines. Only <span style="color:var(--success);font-weight:600">K2-18 b</span> has confirmed detections so far (CH<sub>4</sub>+CO<sub>2</sub>).</div>
|
||
</div>
|
||
<div style="background:rgba(46,204,113,0.06);border:1px solid rgba(46,204,113,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:var(--success);font-weight:600;margin-bottom:2px">Key Molecules</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4"><strong>O<sub>2</sub></strong> (oxygen) — product of photosynthesis. <strong>CH<sub>4</sub></strong> (methane) — produced by methanogens. <strong>H<sub>2</sub>O</strong> (water) — essential solvent. <strong>CO<sub>2</sub></strong> — greenhouse gas. <strong>DMS</strong> — dimethyl sulfide, only known biogenic source on Earth.</div>
|
||
</div>
|
||
<div style="background:rgba(255,176,32,0.06);border:1px solid rgba(255,176,32,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:var(--warning);font-weight:600;margin-bottom:2px">Disequilibrium & Confounds</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4"><strong>Thermodynamic disequilibrium</strong>: CH<sub>4</sub>+CO<sub>2</sub> coexisting implies an active source replenishing CH<sub>4</sub> — possibly biological. <strong>Confound index</strong> = probability that detected signals have a non-biological explanation (volcanism, photochemistry, etc.).</div>
|
||
</div>
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.className="split-layout",s.style.flex="1",s.style.minHeight="0",e.appendChild(s);const a=document.createElement("div");a.className="left-panel",s.appendChild(a);const n=document.createElement("div");n.className="table-area",a.appendChild(n);const o=document.createElement("table");o.className="data-table";const r=document.createElement("thead"),l=document.createElement("tr");for(const x of["Name","Score","JWST","O2","CH4","H2O","Diseq."]){const f=document.createElement("th");f.textContent=x,l.appendChild(f)}r.appendChild(l),o.appendChild(r),this.tableBody=document.createElement("tbody"),o.appendChild(this.tableBody),n.appendChild(o);const c=document.createElement("div");c.className="chart-area",c.style.padding="12px 16px",a.appendChild(c);const d=document.createElement("div");d.className="panel-header",d.innerHTML='Confound Index <span style="font-size:8px;text-transform:none;letter-spacing:0;color:var(--text-muted);font-weight:400">probability of non-biological origin</span>',c.appendChild(d),this.confoundBar=document.createElement("div"),this.confoundBar.style.marginTop="12px",c.appendChild(this.confoundBar);const h=document.createElement("div");h.className="right-panel",s.appendChild(h);const u=document.createElement("div");u.style.height="220px",u.style.minHeight="200px",h.appendChild(u),this.spectrumChart=new gs(u);const m=document.createElement("div");m.className="three-container",m.style.flex="1",m.style.minHeight="200px",h.appendChild(m),this.scene=new nt,this.scene.background=new _(724756),this.camera=new at(50,1,.1,100),this.camera.position.set(0,0,6),this.renderer=new ot({antialias:!0}),this.renderer.setPixelRatio(window.devicePixelRatio),m.appendChild(this.renderer.domElement),this.controls=new rt(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.scene.add(new W(16777215,.6));const g=new it(16777215,.5);g.position.set(3,5,3),this.scene.add(g),this.buildMoleculeScene(),window.addEventListener("resize",this.resize),this.resize(),this.animate(),this.loadData()}buildMoleculeScene(){if(!this.scene)return;const{nodes:t,edges:e}=vs(),i=new Map,s={O2:5809919,H2O:58879,CH4:3066993,CO2:16756768,O3:10044671,N2O:16756768,NH3:16731469};for(const l of t){i.set(l.id,l);const c=new B(.2,16,12),d=new vt({color:s[l.id]??8947848}),h=new L(c,d);h.position.set(l.x,l.y,l.z),this.scene.add(h),this.moleculeMeshes.push(h);const u=document.createElement("canvas");u.width=128,u.height=48;const m=u.getContext("2d");m&&(m.fillStyle="#E6EDF3",m.font="24px sans-serif",m.textAlign="center",m.fillText(l.id,64,32));const g=new Y(u),x=new Z({map:g,transparent:!0}),f=new V(x);f.position.set(l.x,l.y+.35,l.z),f.scale.set(.8,.3,1),this.scene.add(f),this.moleculeMeshes.push(f)}const a=[];for(const l of e){const c=i.get(l.source),d=i.get(l.target);!c||!d||a.push(c.x,c.y,c.z,d.x,d.y,d.z)}const n=new P;n.setAttribute("position",new mt(a,3));const o=new j({color:1844019,transparent:!0,opacity:.6}),r=new gt(n,o);this.scene.add(r),this.moleculeMeshes.push(r)}async loadData(){try{this.candidates=await Ue()}catch(t){console.error("Life API error:",t),this.candidates=[]}this.renderTable(),this.candidates.length>0&&this.selectCandidate(this.candidates[0].id)}renderTable(){if(!this.tableBody)return;this.tableBody.innerHTML="";const t=[...this.candidates].sort((e,i)=>i.score-e.score);for(const e of t){const i=document.createElement("tr");e.id===this.selectedId&&i.classList.add("selected"),i.addEventListener("click",()=>this.selectCandidate(e.id));const s=document.createElement("td");s.textContent=e.name,i.appendChild(s);const a=document.createElement("td");a.textContent=e.score.toFixed(2),i.appendChild(a);const n=document.createElement("td");e.jwstObserved?e.moleculesConfirmed.length>0?n.innerHTML=`<span class="score-badge score-high" style="font-size:8px">${e.moleculesConfirmed.join("+")}</span>`:n.innerHTML='<span class="score-badge score-medium" style="font-size:8px">OBS</span>':n.innerHTML='<span style="color:var(--text-muted);font-size:9px">--</span>',i.appendChild(n);for(const o of[e.o2.toFixed(2),e.ch4.toFixed(2),e.h2o.toFixed(2),e.disequilibrium.toFixed(2)]){const r=document.createElement("td");r.textContent=o,i.appendChild(r)}this.tableBody.appendChild(i)}}selectCandidate(t){var s;this.selectedId=t,this.renderTable();const e=this.candidates.find(a=>a.id===t);if(!e)return;const i=fs(e);if((s=this.spectrumChart)==null||s.update(i,xs),this.confoundBar){const a=1-e.disequilibrium,n=a>.7?"Likely abiotic":a>.4?"Ambiguous":"Possibly biogenic",o=a>.7?"Most detected signals can be explained by geological or photochemical processes without invoking biology.":a>.4?"Some signals are consistent with both biological and abiotic origins. Further data needed to distinguish.":"Detected molecular combination is difficult to explain without an active biological source. Strongest biosignature candidates.";this.confoundBar.innerHTML=`
|
||
<div class="progress-label">
|
||
<span>Confound likelihood</span>
|
||
<span style="color:${a>.6?"var(--danger, #FF4D4D)":a>.3?"var(--warning)":"var(--success)"};font-weight:600">${(a*100).toFixed(0)}% — ${n}</span>
|
||
</div>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill ${a>.6?"danger":a>.3?"warning":"success"}" style="width: ${a*100}%"></div>
|
||
</div>
|
||
<div style="font-size:9px;color:var(--text-muted);margin-top:4px;line-height:1.4">${o}</div>
|
||
<div style="margin-top:10px;display:grid;grid-template-columns:1fr 1fr;gap:8px">
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:4px;padding:6px 8px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.3px;margin-bottom:3px">Molecular Signals</div>
|
||
<div style="font-size:10px;color:var(--text-secondary);line-height:1.5">
|
||
<div>O<sub>2</sub>: <span style="color:${e.o2>.5?"var(--success)":"var(--text-muted)"}">${e.o2>.01?(e.o2*100).toFixed(0)+"%":"Not detected"}</span></div>
|
||
<div>CH<sub>4</sub>: <span style="color:${e.ch4>.5?"var(--success)":"var(--text-muted)"}">${e.ch4>.01?(e.ch4*100).toFixed(0)+"%":"Not detected"}</span></div>
|
||
<div>H<sub>2</sub>O: <span style="color:${e.h2o>.5?"#00E5FF":"var(--text-muted)"}">${e.h2o>.01?(e.h2o*100).toFixed(0)+"%":"Not detected"}</span></div>
|
||
</div>
|
||
</div>
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:4px;padding:6px 8px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.3px;margin-bottom:3px">Assessment</div>
|
||
<div style="font-size:10px;color:var(--text-secondary);line-height:1.5">
|
||
<div>Diseq.: <span style="color:${e.disequilibrium>.5?"var(--success)":"var(--text-muted)"}">${(e.disequilibrium*100).toFixed(0)}%</span></div>
|
||
<div>Habitability: <span style="color:var(--accent)">${(e.habitability*100).toFixed(0)}%</span></div>
|
||
<div>JWST: ${e.jwstObserved?e.moleculesConfirmed.length>0?'<span style="color:var(--success)">'+e.moleculesConfirmed.join(", ")+"</span>":'<span style="color:var(--warning)">Observed, no detections</span>':'<span style="color:var(--text-muted)">Not yet observed</span>'}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top:10px;font-size:10px;color:var(--text-secondary);line-height:1.5;border-top:1px solid var(--border);padding-top:8px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:3px">Atmosphere Status</div>
|
||
${e.atmosphereStatus}
|
||
</div>
|
||
${e.reference?`<div style="margin-top:6px;font-size:9px;color:var(--text-muted);font-style:italic">${e.reference}</div>`:""}
|
||
`}}unmount(){var t,e,i,s,a;window.removeEventListener("resize",this.resize),cancelAnimationFrame(this.animFrameId),(t=this.spectrumChart)==null||t.destroy();for(const n of this.moleculeMeshes)n instanceof L||n instanceof gt?(n.geometry.dispose(),n.material.dispose()):n instanceof V&&((e=n.material.map)==null||e.dispose(),n.material.dispose()),(i=this.scene)==null||i.remove(n);this.moleculeMeshes=[],(s=this.controls)==null||s.dispose(),(a=this.renderer)==null||a.dispose(),this.spectrumChart=null,this.controls=null,this.renderer=null,this.scene=null,this.camera=null,this.container=null}}const bt={seal:"#FF4D4D",commit:"#00E5FF",merge:"#FFB020",verify:"#2ECC71"},bs={seal:"Chain anchor — immutable genesis point",commit:"New evidence committed to chain",merge:"Branch merge — combining data sources",verify:"Verification step — confirms integrity"};class ws{constructor(){this.container=null,this.logEl=null,this.chainCanvas=null,this.coherenceCanvas=null,this.detailEl=null,this.metricsEls={},this.unsubWs=null,this.entries=[],this.selectedIdx=-1,this.chainMeta={integrity:"--",hashAlgo:"SHAKE-256",rootHash:"--",meanCoherence:0,minCoherence:0}}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:4px">
|
||
<span style="font-size:14px;font-weight:600;color:var(--text-primary)">Witness Chain</span>
|
||
<span style="font-size:10px;padding:2px 8px;border-radius:3px;background:rgba(46,204,113,0.1);color:#2ECC71;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">SHAKE-256</span>
|
||
<span style="font-size:10px;padding:2px 8px;border-radius:3px;background:rgba(0,229,255,0.1);color:#00E5FF;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Ed25519</span>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">
|
||
Cryptographic audit trail proving the causal history of every RVF pipeline event.
|
||
Each <strong>witness</strong> verifies a specific measurement (transit depth, stellar parameters, etc.).
|
||
The chain is <strong>hash-linked</strong>: every entry's SHAKE-256 hash includes the previous entry's hash, making tampering detectable.
|
||
<span style="color:#FF4D4D">Seal</span> = anchor,
|
||
<span style="color:#00E5FF">Commit</span> = new evidence,
|
||
<span style="color:#FFB020">Merge</span> = branch join,
|
||
<span style="color:#2ECC71">Verify</span> = integrity confirmed.
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.style.cssText="display:flex;gap:12px;padding:12px 20px;border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap";const a=[{key:"entries",label:"Chain Length",color:"var(--accent)"},{key:"integrity",label:"Integrity",color:"#2ECC71"},{key:"coherence",label:"Mean Coherence",color:""},{key:"minCoherence",label:"Min Coherence",color:""},{key:"depth",label:"Epochs",color:""},{key:"rootHash",label:"Root Hash",color:"var(--text-muted)"}];for(const g of a){const x=document.createElement("div");x.style.cssText="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;min-width:100px;flex:1",x.innerHTML=`
|
||
<div style="font-size:10px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">${g.label}</div>
|
||
<div data-metric="${g.key}" style="font-family:var(--font-mono);font-size:18px;font-weight:500;color:${g.color||"var(--text-primary)"};line-height:1.2">--</div>
|
||
`,s.appendChild(x),this.metricsEls[g.key]=x.querySelector(`[data-metric="${g.key}"]`)}e.appendChild(s);const n=document.createElement("div");n.style.cssText="flex:1;overflow:auto;padding:16px 20px;display:flex;flex-direction:column;gap:16px",e.appendChild(n);const o=document.createElement("div");o.style.cssText="display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;flex-shrink:0",o.innerHTML=`
|
||
<div style="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);padding:14px">
|
||
<div style="font-size:11px;font-weight:600;color:var(--accent);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">How It Works</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.6">
|
||
Each pipeline stage produces a <strong>witness entry</strong> containing: the measurement taken, a confidence score (coherence),
|
||
and a cryptographic hash that chains to the previous entry. This creates an immutable, tamper-evident record of the entire
|
||
scientific analysis — from raw photometry to final candidate ranking.
|
||
</div>
|
||
</div>
|
||
<div style="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);padding:14px">
|
||
<div style="font-size:11px;font-weight:600;color:#FFB020;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Hash Linking</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.6">
|
||
SHAKE-256 (variable-length SHA-3 family) hashes each entry including the previous hash, creating a <strong>Merkle chain</strong>.
|
||
If any entry is modified, all subsequent hashes become invalid. The final entry is signed with <strong>Ed25519</strong>
|
||
to prove chain authorship and prevent repudiation.
|
||
</div>
|
||
</div>
|
||
<div style="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);padding:14px">
|
||
<div style="font-size:11px;font-weight:600;color:#2ECC71;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Coherence Score</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.6">
|
||
Each witness reports a <strong>coherence</strong> value (0–1) indicating how well the new evidence agrees with prior chain state.
|
||
Values < 0.90 are flagged as <span style="color:#FFB020">amber</span> (potential anomaly).
|
||
The coherence chart below shows how confidence evolves across the pipeline, highlighting where uncertainty enters.
|
||
</div>
|
||
</div>
|
||
`,n.appendChild(o);const r=document.createElement("div");r.style.cssText="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;flex-shrink:0";const l=document.createElement("div");l.style.cssText="padding:10px 14px;font-size:11px;font-weight:500;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.6px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center",l.innerHTML='<span>Chain Topology</span><span style="font-size:10px;color:var(--text-muted);font-family:var(--font-mono)">Click a node for details</span>',r.appendChild(l),this.chainCanvas=document.createElement("canvas"),this.chainCanvas.style.cssText="width:100%;height:120px;display:block;cursor:pointer",this.chainCanvas.addEventListener("click",g=>this.onChainClick(g)),r.appendChild(this.chainCanvas),n.appendChild(r),this.detailEl=document.createElement("div"),this.detailEl.style.cssText="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);padding:14px;flex-shrink:0;display:none",n.appendChild(this.detailEl);const c=document.createElement("div");c.style.cssText="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;flex-shrink:0";const d=document.createElement("div");d.style.cssText="padding:10px 14px;font-size:11px;font-weight:500;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.6px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center",d.innerHTML='<span>Coherence Evolution</span><span style="font-size:10px;color:var(--text-muted);font-family:var(--font-mono)">Dashed line = 0.90 threshold</span>',c.appendChild(d),this.coherenceCanvas=document.createElement("canvas"),this.coherenceCanvas.style.cssText="width:100%;height:140px;display:block",c.appendChild(this.coherenceCanvas),n.appendChild(c);const h=document.createElement("div");h.style.cssText="background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;flex:1;min-height:200px;display:flex;flex-direction:column";const u=document.createElement("div");u.style.cssText="padding:10px 14px;font-size:11px;font-weight:500;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.6px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;flex-shrink:0",u.innerHTML='<span>Witness Log</span><span style="font-size:10px;color:var(--text-muted);font-family:var(--font-mono)">Hash-linked entries</span>',h.appendChild(u);const m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:10px;padding:6px 14px;border-bottom:1px solid var(--border);font-size:10px;font-weight:500;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;flex-shrink:0",m.innerHTML=`
|
||
<span style="min-width:60px">Time</span>
|
||
<span style="min-width:52px">Type</span>
|
||
<span style="min-width:90px">Witness</span>
|
||
<span style="flex:1">Action</span>
|
||
<span style="min-width:50px;text-align:right">Coh.</span>
|
||
<span style="min-width:100px;text-align:right">Hash</span>
|
||
`,h.appendChild(m),this.logEl=document.createElement("div"),this.logEl.style.cssText="flex:1;overflow-y:auto;font-family:var(--font-mono);font-size:11px",h.appendChild(this.logEl),n.appendChild(h),this.loadData(),this.unsubWs=St(g=>{g.event_type==="witness"&&this.addLiveEntry(g)})}async loadData(){let t;try{t=await me()}catch{t={entries:[],chain_length:0,integrity:"--",hash_algorithm:"SHAKE-256",root_hash:"--",genesis_hash:"--",mean_coherence:0,min_coherence:0,total_epochs:0}}this.chainMeta={integrity:t.integrity,hashAlgo:t.hash_algorithm,rootHash:t.root_hash,meanCoherence:t.mean_coherence,minCoherence:t.min_coherence},this.entries=t.entries.map(e=>{var i;return{timestamp:e.timestamp.includes("T")?((i=e.timestamp.split("T")[1])==null?void 0:i.substring(0,8))??"":e.timestamp,type:e.type,witness:e.witness,action:e.action,hash:e.hash,prevHash:e.prev_hash,coherence:e.coherence,measurement:e.measurement,epoch:e.epoch}}),this.entries.length===0&&(this.entries=this.generateDemoEntries()),this.updateMetrics(t),this.renderChain(),this.renderCoherence(),this.renderLog()}updateMetrics(t){const e=(a,n)=>{const o=this.metricsEls[a];o&&(o.textContent=n)};e("entries",String(this.entries.length)),e("integrity",this.chainMeta.integrity),e("coherence",this.chainMeta.meanCoherence>0?this.chainMeta.meanCoherence.toFixed(4):"--"),e("minCoherence",this.chainMeta.minCoherence>0?this.chainMeta.minCoherence.toFixed(4):"--"),e("depth",String(t.total_epochs)),e("rootHash",this.chainMeta.rootHash.substring(0,12)+"...");const i=this.metricsEls.minCoherence;i&&this.chainMeta.minCoherence>0&&this.chainMeta.minCoherence<.9&&(i.style.color="#FFB020");const s=this.metricsEls.integrity;s&&(s.style.color=this.chainMeta.integrity==="VALID"?"#2ECC71":"#FF4D4D")}renderChain(){var u;const t=this.chainCanvas;if(!t)return;const e=(u=t.parentElement)==null?void 0:u.getBoundingClientRect(),i=(e==null?void 0:e.width)??800,s=120,a=window.devicePixelRatio||1;t.width=i*a,t.height=s*a,t.style.width=`${i}px`,t.style.height=`${s}px`;const n=t.getContext("2d");if(!n)return;n.scale(a,a),n.clearRect(0,0,i,s);const o=this.entries.length;if(o===0)return;const r=40,l=20,c=i-r*2,d=s/2,h=8;for(let m=0;m<o-1;m++){const g=r+m/(o-1)*c,x=r+(m+1)/(o-1)*c;n.beginPath(),n.moveTo(g+h,d),n.lineTo(x-h,d),n.strokeStyle="#1E2630",n.lineWidth=2,n.stroke();const f=x-h-6;n.beginPath(),n.moveTo(f,d-3),n.lineTo(f+6,d),n.lineTo(f,d+3),n.fillStyle="#1E2630",n.fill()}for(let m=0;m<o;m++){const g=this.entries[m],x=r+(o>1?m/(o-1)*c:c/2),f=bt[g.type]??"#00E5FF",w=m===this.selectedIdx;w&&(n.beginPath(),n.arc(x,d,h+4,0,Math.PI*2),n.fillStyle=f.replace(")",", 0.15)").replace("rgb","rgba").replace("#",""),n.shadowColor=f,n.shadowBlur=12,n.fill(),n.shadowBlur=0),n.beginPath(),n.arc(x,d,h,0,Math.PI*2),n.fillStyle=w?f:"transparent",n.strokeStyle=f,n.lineWidth=2,n.fill(),n.stroke(),w||(n.beginPath(),n.arc(x,d,3,0,Math.PI*2),n.fillStyle=f,n.fill()),n.fillStyle="#8B949E",n.font="9px monospace",n.textAlign="center";const y=g.witness.replace("W_","");n.fillText(y,x,d-h-l+8),n.fillStyle=g.coherence<.9?"#FFB020":"#484F58",n.font="9px monospace",n.fillText(g.coherence.toFixed(2),x,d+h+14),n.fillStyle="#30363D",n.font="8px monospace",n.fillText(g.hash.substring(0,6),x,d+h+24)}}onChainClick(t){const e=this.chainCanvas;if(!e||this.entries.length===0)return;const i=e.getBoundingClientRect(),s=t.clientX-i.left,a=this.entries.length,n=40,o=i.width-n*2;let r=-1,l=1/0;for(let c=0;c<a;c++){const d=n+(a>1?c/(a-1)*o:o/2),h=Math.abs(s-d);h<l&&h<20&&(l=h,r=c)}r>=0&&(this.selectedIdx=r===this.selectedIdx?-1:r,this.renderChain(),this.showDetail(this.selectedIdx>=0?this.entries[this.selectedIdx]:null))}showDetail(t){if(!this.detailEl)return;if(!t){this.detailEl.style.display="none";return}const e=bt[t.type]??"#00E5FF",i=bs[t.type]??"",s=t.coherence<.9?"#FFB020":"#2ECC71";this.detailEl.style.display="block",this.detailEl.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px">
|
||
<div style="width:10px;height:10px;border-radius:50%;background:${e}"></div>
|
||
<span style="font-size:13px;font-weight:600;color:var(--text-primary);font-family:var(--font-mono)">${t.witness}</span>
|
||
<span style="font-size:10px;padding:2px 8px;border-radius:3px;background:${e}22;color:${e};font-weight:600;text-transform:uppercase">${t.type}</span>
|
||
<span style="font-size:10px;color:var(--text-muted)">${i}</span>
|
||
<span style="margin-left:auto;font-size:11px;color:var(--text-muted);font-family:var(--font-mono)">Epoch ${t.epoch}</span>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;margin-bottom:4px">Action</div>
|
||
<div style="font-size:12px;color:var(--text-primary);line-height:1.5">${t.action}</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;margin-bottom:4px">Measurement</div>
|
||
<div style="font-size:12px;color:var(--accent);font-family:var(--font-mono)">${t.measurement??"N/A"}</div>
|
||
</div>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:12px;padding-top:12px;border-top:1px solid var(--border)">
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;margin-bottom:2px">Coherence</div>
|
||
<div style="font-size:16px;font-weight:500;color:${s};font-family:var(--font-mono)">${t.coherence.toFixed(4)}</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;margin-bottom:2px">Hash</div>
|
||
<div style="font-size:11px;color:var(--text-primary);font-family:var(--font-mono)">${t.hash}</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;margin-bottom:2px">Previous Hash</div>
|
||
<div style="font-size:11px;color:var(--text-muted);font-family:var(--font-mono)">${t.prevHash}</div>
|
||
</div>
|
||
</div>
|
||
`}renderCoherence(){var y;const t=this.coherenceCanvas;if(!t)return;const e=(y=t.parentElement)==null?void 0:y.getBoundingClientRect(),i=(e==null?void 0:e.width)??800,s=140,a=window.devicePixelRatio||1;t.width=i*a,t.height=s*a,t.style.width=`${i}px`,t.style.height=`${s}px`;const n=t.getContext("2d");if(!n)return;n.scale(a,a),n.clearRect(0,0,i,s);const o=this.entries.length;if(o===0)return;const r=50,l=20,c=16,d=28,h=i-r-l,u=s-c-d,m=.8,g=1.01,x=b=>r+(o>1?b/(o-1)*h:h/2),f=b=>c+(1-(b-m)/(g-m))*u;n.strokeStyle="#161C24",n.lineWidth=1;for(let b=.8;b<=1.001;b+=.05){const v=f(b);n.beginPath(),n.moveTo(r,v),n.lineTo(i-l,v),n.stroke(),n.fillStyle="#484F58",n.font="10px monospace",n.textAlign="right",n.fillText(b.toFixed(2),r-6,v+4)}n.setLineDash([4,4]),n.strokeStyle="#FFB02066",n.lineWidth=1,n.beginPath(),n.moveTo(r,f(.9)),n.lineTo(i-l,f(.9)),n.stroke(),n.setLineDash([]),n.fillStyle="#FFB020",n.font="9px monospace",n.textAlign="left",n.fillText("threshold",i-l-55,f(.9)-4),n.beginPath(),n.moveTo(x(0),f(m));for(let b=0;b<o;b++)n.lineTo(x(b),f(Math.max(m,this.entries[b].coherence)));n.lineTo(x(o-1),f(m)),n.closePath();const w=n.createLinearGradient(0,c,0,c+u);w.addColorStop(0,"rgba(0, 229, 255, 0.08)"),w.addColorStop(1,"rgba(0, 229, 255, 0.01)"),n.fillStyle=w,n.fill(),n.beginPath();for(let b=0;b<o;b++){const v=x(b),E=f(Math.max(m,this.entries[b].coherence));b===0?n.moveTo(v,E):n.lineTo(v,E)}n.strokeStyle="#00E5FF",n.lineWidth=2,n.stroke();for(let b=0;b<o;b++){const v=x(b),E=Math.max(m,this.entries[b].coherence),C=f(E),T=this.entries[b].coherence<.9?"#FFB020":bt[this.entries[b].type]??"#00E5FF";n.beginPath(),n.arc(v,C,4,0,Math.PI*2),n.fillStyle=T,n.fill(),n.strokeStyle="#0B0F14",n.lineWidth=1.5,n.stroke(),n.fillStyle="#484F58",n.font="8px monospace",n.textAlign="center";const R=this.entries[b].witness.replace("W_","");(o<=20||b%2===0)&&n.fillText(R,v,s-d+14)}}renderLog(){if(this.logEl){this.logEl.innerHTML="";for(let t=0;t<this.entries.length;t++)this.appendLogEntry(this.entries[t],t)}}appendLogEntry(t,e){if(!this.logEl)return;const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:10px;padding:6px 14px;border-bottom:1px solid var(--border-subtle);cursor:pointer;transition:background 0.1s",i.addEventListener("mouseenter",()=>{i.style.background="rgba(255,255,255,0.015)"}),i.addEventListener("mouseleave",()=>{i.style.background=e===this.selectedIdx?"rgba(0,229,255,0.04)":""}),i.addEventListener("click",()=>{var o;this.selectedIdx=e===this.selectedIdx?-1:e,this.renderChain(),this.showDetail(this.selectedIdx>=0?this.entries[this.selectedIdx]:null);const n=(o=this.logEl)==null?void 0:o.children;if(n)for(let r=0;r<n.length;r++)n[r].style.background=r===this.selectedIdx?"rgba(0,229,255,0.04)":""});const s=bt[t.type]??"#00E5FF",a=t.coherence<.9?"#FFB020":"#484F58";i.innerHTML=`
|
||
<span style="color:var(--text-muted);min-width:60px;white-space:nowrap;font-size:10px">${t.timestamp}</span>
|
||
<span style="padding:2px 8px;border-radius:3px;font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:0.3px;min-width:52px;text-align:center;background:${s}18;color:${s}">${t.type}</span>
|
||
<span style="color:var(--accent);min-width:90px;font-size:11px">${t.witness}</span>
|
||
<span style="color:var(--text-primary);flex:1;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${t.action}">${t.action}</span>
|
||
<span style="color:${a};min-width:50px;text-align:right;font-size:11px">${t.coherence.toFixed(2)}</span>
|
||
<span style="color:var(--text-muted);font-size:10px;min-width:100px;text-align:right" title="Hash: ${t.hash} | Prev: ${t.prevHash}">${t.hash.substring(0,8)}..${t.prevHash.substring(0,4)}</span>
|
||
`,this.logEl.appendChild(i)}addLiveEntry(t){const e={timestamp:new Date(t.timestamp*1e3).toISOString().substring(11,19),type:String(t.data.type??"commit"),witness:String(t.data.witness??"W_live"),action:String(t.data.action??"live_event"),hash:String(t.data.hash??this.fakeHash("live")),prevHash:this.entries.length>0?this.entries[this.entries.length-1].hash:"0000000000000000",coherence:Number(t.data.coherence??1),measurement:t.data.measurement?String(t.data.measurement):null,epoch:this.entries.length};this.entries.push(e),this.appendLogEntry(e,this.entries.length-1),this.renderChain(),this.renderCoherence();const i=this.metricsEls.entries;i&&(i.textContent=String(this.entries.length)),this.logEl&&(this.logEl.scrollTop=this.logEl.scrollHeight)}fakeHash(t){let e=0;for(let i=0;i<t.length;i++)e=(e<<5)-e+t.charCodeAt(i)|0;return Math.abs(e).toString(16).padStart(16,"0").substring(0,16)}generateDemoEntries(){const t=[{w:"W_root",t:"seal",a:"Chain initialized — genesis anchor",m:null},{w:"W_photometry",t:"commit",a:"Kepler light curves ingested (196K targets)",m:"transit_depth_rms=4.2e-5"},{w:"W_periodogram",t:"commit",a:"BLS search completed — 2,842 signals",m:"bls_power_max=42.7"},{w:"W_stellar",t:"commit",a:"Stellar parameters derived (Gaia DR3)",m:"T_eff_sigma=47K"},{w:"W_transit",t:"merge",a:"Transit model merged with stellar params",m:"R_p_range=0.92-2.61"},{w:"W_radial_velocity",t:"commit",a:"HARPS RV data — mass constraints",m:"K_rv_range=0.089-3.2"},{w:"W_orbit",t:"commit",a:"Orbital solutions — HZ classification",m:"hz_candidates=10"},{w:"W_esi",t:"commit",a:"ESI ranking computed",m:"esi_top=0.93"},{w:"W_spectroscopy",t:"merge",a:"JWST atmospheric observations merged",m:"CH4+CO2_detected"},{w:"W_biosig",t:"commit",a:"Biosignature scoring pipeline",m:"diseq_max=0.82"},{w:"W_blind",t:"commit",a:"Blind test passed (τ=1.0)",m:"kendall_tau=1.000"},{w:"W_seal",t:"verify",a:"Chain sealed — Ed25519 signed",m:"chain_length=12"}];let e="0000000000000000";return t.map((i,s)=>{const a=this.fakeHash(i.w+s),n={timestamp:new Date(Date.now()-(t.length-s)*12e4).toISOString().substring(11,19),type:i.t,witness:i.w,action:i.a,hash:a,prevHash:e,coherence:1-s*.01,measurement:i.m,epoch:s};return e=a,n})}unmount(){var t;(t=this.unsubWs)==null||t.call(this),this.logEl=null,this.chainCanvas=null,this.coherenceCanvas=null,this.detailEl=null,this.metricsEls={},this.container=null,this.entries=[],this.selectedIdx=-1}}let zt=null;async function Es(){try{const p=await fetch("/rvf_solver_wasm.wasm");if(!p.ok)throw new Error(`HTTP ${p.status}`);const{instance:t}=await WebAssembly.instantiateStreaming(p,{env:{}});return t.exports}catch(p){return console.debug("[rvf-solver] WASM load failed, using demo mode:",p),null}}function At(p,t,e,i){const s=e(t);if(s<=0)return null;const a=p.rvf_solver_alloc(s);if(a===0)return null;try{i(t,a);const n=new Uint8Array(p.memory.buffer,a,s),o=new TextDecoder().decode(n);return JSON.parse(o)}finally{p.rvf_solver_free(a,s)}}function Zt(p){if(p===void 0){const e=BigInt(Math.floor(Math.random()*18446744073709552e3));return[Number(e&0xffffffffn),Number(e>>32n&0xffffffffn)]}const t=typeof p=="number"?BigInt(p):p;return[Number(t&0xffffffffn),Number(t>>32n&0xffffffffn)]}class Cs{constructor(t,e){this.handle=t,this.wasm=e}train(t){const[e,i]=Zt(t.seed),s=this.wasm.rvf_solver_train(this.handle,t.count,t.minDifficulty??1,t.maxDifficulty??10,e,i);if(s<0)throw new Error("Training failed");const a=At(this.wasm,this.handle,n=>this.wasm.rvf_solver_result_len(n),(n,o)=>this.wasm.rvf_solver_result_read(n,o));return{trained:(a==null?void 0:a.trained)??t.count,correct:(a==null?void 0:a.correct)??s,accuracy:(a==null?void 0:a.accuracy)??s/t.count,patternsLearned:(a==null?void 0:a.patterns_learned)??0}}acceptance(t){const e=t??{},[i,s]=Zt(e.seed);if(this.wasm.rvf_solver_acceptance(this.handle,e.holdoutSize??50,e.trainingPerCycle??200,e.cycles??5,e.stepBudget??500,i,s)<0)throw new Error("Acceptance failed");const n=At(this.wasm,this.handle,r=>this.wasm.rvf_solver_result_len(r),(r,l)=>this.wasm.rvf_solver_result_read(r,l));if(!n)throw new Error("Failed to read acceptance manifest");const o=r=>({passed:!!r.passed,accuracyMaintained:!!(r.accuracy_maintained??r.accuracyMaintained),costImproved:!!(r.cost_improved??r.costImproved),robustnessImproved:!!(r.robustness_improved??r.robustnessImproved),zeroViolations:!!(r.zero_violations??r.zeroViolations),dimensionsImproved:r.dimensions_improved??r.dimensionsImproved??0,cycles:(r.cycles??[]).map(l=>({cycle:l.cycle??0,accuracy:l.accuracy??0,costPerSolve:l.cost_per_solve??l.costPerSolve??0,noiseAccuracy:l.noise_accuracy??l.noiseAccuracy??0,violations:l.violations??0,patternsLearned:l.patterns_learned??l.patternsLearned??0}))});return{version:n.version??2,modeA:o(n.mode_a),modeB:o(n.mode_b),modeC:o(n.mode_c),allPassed:!!n.all_passed,witnessEntries:n.witness_entries??0,witnessChainBytes:n.witness_chain_bytes??0}}policy(){const t=At(this.wasm,this.handle,e=>this.wasm.rvf_solver_policy_len(e),(e,i)=>this.wasm.rvf_solver_policy_read(e,i));return t?{contextStats:t.context_stats??t.contextStats??{},earlyCommitPenalties:t.early_commit_penalties??t.earlyCommitPenalties??0,earlyCommitsTotal:t.early_commits_total??t.earlyCommitsTotal??0,earlyCommitsWrong:t.early_commits_wrong??t.earlyCommitsWrong??0,prepass:t.prepass??"",speculativeAttempts:t.speculative_attempts??t.speculativeAttempts??0,speculativeArm2Wins:t.speculative_arm2_wins??t.speculativeArm2Wins??0}:null}destroy(){this.handle>0&&(this.wasm.rvf_solver_destroy(this.handle),this.handle=0)}}let wt=null,$t=null;async function Ms(){zt||(zt=Es());const p=await zt;if(!p)return null;const t=p.rvf_solver_create();return t<0?(console.debug("[rvf-solver] Failed to create solver instance"),null):new Cs(t,p)}async function kt(){return wt||($t||($t=Ms()),wt=await $t,wt)}function Ts(p,t){const e=.55+t*.08,i=Math.min(.98,e+(Math.random()-.5)*.04),s=Math.round(p*i);return{trained:p,correct:s,accuracy:i,patternsLearned:Math.floor(p*.15*(1+t*.3))}}function Ss(){const p=t=>Array.from({length:5},(e,i)=>({cycle:i+1,accuracy:Math.min(.99,t+i*.03+(Math.random()-.5)*.02),costPerSolve:120-i*15+Math.random()*10,noiseAccuracy:t-.05+Math.random()*.03,violations:i<2?1:0,patternsLearned:(i+1)*12}));return{version:2,modeA:{passed:!0,accuracyMaintained:!0,costImproved:!1,robustnessImproved:!1,zeroViolations:!1,dimensionsImproved:1,cycles:p(.62)},modeB:{passed:!0,accuracyMaintained:!0,costImproved:!0,robustnessImproved:!1,zeroViolations:!1,dimensionsImproved:2,cycles:p(.71)},modeC:{passed:!0,accuracyMaintained:!0,costImproved:!0,robustnessImproved:!0,zeroViolations:!0,dimensionsImproved:3,cycles:p(.78)},allPassed:!0,witnessEntries:25,witnessChainBytes:1825}}function Xt(){const p=["easy","medium","hard","extreme"],t=["none","weekday","hybrid"],e={};for(const i of p){e[i]={};for(const s of t)e[i][s]={attempts:Math.floor(Math.random()*200)+50,successes:Math.floor(Math.random()*150)+30,totalSteps:Math.floor(Math.random()*5e3)+1e3,alphaSafety:1+Math.random()*2,betaSafety:1+Math.random(),costEma:50+Math.random()*80,earlyCommitWrongs:Math.floor(Math.random()*5)}}return{contextStats:e,earlyCommitPenalties:3,earlyCommitsTotal:42,earlyCommitsWrong:3,prepass:"naked_singles",speculativeAttempts:156,speculativeArm2Wins:38}}const Lt={A:"#FF4D4D",B:"#FFB020",C:"#2ECC71"};function Pt(p){let t=p|0;return()=>(t=t*1664525+1013904223&2147483647,t/2147483647)}class _s{constructor(){this.container=null,this.renderer=null,this.scene=null,this.camera=null,this.controls=null,this.animFrameId=0,this.landscapeMesh=null,this.bgStars=null,this.galacticPlane=null,this.galacticCore=null,this.nebulae=[],this.armMarkers=null,this.peakGlows=[],this.trainingHistory=[],this.manifest=null,this.policy=null,this.isTraining=!1,this.usesWasm=!1,this.landscapeTime=0,this.speed=1,this.autoRotate=!1,this.trainCount=200,this.minDifficulty=1,this.maxDifficulty=8,this.acceptCycles=5,this.holdoutSize=50,this.trainingPerCycle=200,this.stepBudget=500,this.autoTrainRounds=8,this.trainBtn=null,this.acceptBtn=null,this.statusEl=null,this.curveCanvas=null,this.modesEl=null,this.policyEl=null,this.controlsEl=null,this.speedLabel=null,this.resize=()=>{if(!this.renderer||!this.camera||!this.container)return;const t=this.renderer.domElement.parentElement;if(!t)return;const e=t.clientWidth,i=t.clientHeight;e===0||i===0||(this.renderer.setSize(e,i),this.camera.aspect=e/i,this.camera.updateProjectionMatrix())},this.animate=()=>{var t;if(this.animFrameId=requestAnimationFrame(this.animate),this.landscapeTime+=.005*this.speed,this.landscapeMesh){const i=this.landscapeMesh.geometry.attributes.position;for(let s=0;s<i.count;s++){const a=i.getZ(s),n=Math.sin(this.landscapeTime*2+s*.1)*.015*this.speed;i.setZ(s,a+n)}i.needsUpdate=!0}for(let e=0;e<this.peakGlows.length;e++){const i=this.peakGlows[e],s=1+.15*Math.sin(this.landscapeTime*3+e*1.2);i.material.opacity=.5*s}(t=this.controls)==null||t.update(),this.renderer&&this.scene&&this.camera&&this.renderer.render(this.scene,this.camera)}}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
||
<div style="font-size:14px;font-weight:600;color:var(--text-primary)">RVF Self-Learning Solver</div>
|
||
<span class="score-badge score-high" style="font-size:9px;padding:1px 6px">WASM</span>
|
||
<span class="score-badge score-medium" style="font-size:9px;padding:1px 6px">THOMPSON SAMPLING</span>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.6;max-width:900px">
|
||
Interactive WASM-powered constraint solver that <strong>learns to solve puzzles using multi-armed bandit algorithms</strong>.
|
||
The solver improves by discovering which strategies work best for different puzzle difficulties, building a policy that adapts in real-time.
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.className="split-layout",s.style.flex="1",s.style.minHeight="0",e.appendChild(s);const a=document.createElement("div");a.className="left-panel",a.style.cssText="overflow-y:auto;overflow-x:hidden;padding:12px;scroll-behavior:smooth;-webkit-overflow-scrolling:touch",s.appendChild(a),this.buildLeftPanel(a);const n=document.createElement("div");n.className="right-panel",n.style.cssText="padding:0;position:relative;display:flex;flex-direction:column",s.appendChild(n);const o=document.createElement("div");o.className="three-container",o.style.cssText="flex:1;min-height:0;position:relative",n.appendChild(o),this.initThreeJs(o),this.buildViewportControls(o);const r=document.createElement("div");r.style.cssText="position:absolute;bottom:8px;left:50%;transform:translateX(-50%);font-size:9px;color:rgba(255,255,255,0.3);pointer-events:none;white-space:nowrap",r.textContent="Drag to rotate | Scroll to zoom | Right-drag to pan",o.appendChild(r),window.addEventListener("resize",this.resize),this.resize(),this.animate(),this.init()}buildLeftPanel(t){const e=document.createElement("div");e.style.cssText="font-size:10px;color:var(--accent);cursor:pointer;margin-bottom:8px;display:flex;align-items:center;gap:4px",e.innerHTML='<span style="transition:transform 0.2s" id="info-arrow">▶</span> How it works';const i=document.createElement("div");i.style.cssText="display:none;margin-bottom:12px",i.innerHTML=`
|
||
<div style="display:grid;grid-template-columns:1fr;gap:6px;font-size:10px">
|
||
<div style="background:rgba(0,229,255,0.06);border:1px solid rgba(0,229,255,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:var(--accent);font-weight:600;margin-bottom:2px">Training</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4">Each cycle generates puzzles of varying difficulty (1-8). <strong>Thompson Sampling</strong> explores strategies by sampling from Beta distributions, balancing exploration vs exploitation.</div>
|
||
</div>
|
||
<div style="background:rgba(255,176,32,0.06);border:1px solid rgba(255,176,32,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:var(--warning);font-weight:600;margin-bottom:2px">Acceptance Test</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4"><span style="color:#FF4D4D;font-weight:600">A</span> accuracy only. <span style="color:#FFB020;font-weight:600">B</span> accuracy + cost. <span style="color:#2ECC71;font-weight:600">C</span> full multi-objective.</div>
|
||
</div>
|
||
<div style="background:rgba(153,68,255,0.06);border:1px solid rgba(153,68,255,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:#9944ff;font-weight:600;margin-bottom:2px">3D Landscape</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4">Terrain shows <strong>bandit arm reward distributions</strong>. Peaks = high-reward strategies. <span style="color:#4488ff">Blue</span>=low, <span style="color:#2ECC71">green</span>=medium, <span style="color:#FF4D4D">red</span>=high.</div>
|
||
</div>
|
||
</div>
|
||
`,e.addEventListener("click",()=>{const h=i.style.display!=="none";i.style.display=h?"none":"block";const u=e.querySelector("#info-arrow");u&&(u.style.transform=h?"":"rotate(90deg)")}),t.appendChild(e),t.appendChild(i);const s=document.createElement("div");s.className="panel",s.style.marginBottom="12px",t.appendChild(s);const a=document.createElement("div");a.className="panel-header",a.innerHTML='Controls <span style="font-size:8px;text-transform:none;letter-spacing:0;color:var(--text-muted);font-weight:400">train & test</span>',s.appendChild(a);const n=document.createElement("div");n.className="panel-body",n.style.cssText="display:flex;gap:6px;align-items:center;flex-wrap:wrap;padding:8px 10px",s.appendChild(n),this.trainBtn=document.createElement("button"),this.trainBtn.textContent="Train (200)",this.trainBtn.className="scale-btn",this.trainBtn.style.cssText="font-size:11px;padding:4px 10px;white-space:nowrap",this.trainBtn.addEventListener("click",()=>this.runTraining()),n.appendChild(this.trainBtn),this.acceptBtn=document.createElement("button"),this.acceptBtn.textContent="Acceptance",this.acceptBtn.className="scale-btn",this.acceptBtn.style.cssText="font-size:11px;padding:4px 10px;white-space:nowrap",this.acceptBtn.addEventListener("click",()=>this.runAcceptance()),n.appendChild(this.acceptBtn);const o=document.createElement("button");o.textContent="Auto (8x)",o.className="scale-btn",o.style.cssText="font-size:11px;padding:4px 10px;white-space:nowrap",o.addEventListener("click",()=>this.runAutoTraining()),n.appendChild(o);const r=document.createElement("button");r.textContent="Auto-Optimize",r.className="scale-btn",r.style.cssText="font-size:11px;padding:4px 10px;white-space:nowrap;border-color:rgba(46,204,113,0.3);color:var(--success)",r.title="Keep training until acceptance passes (max 30 rounds)",r.addEventListener("click",()=>this.runAutoOptimize()),n.appendChild(r),this.statusEl=document.createElement("div"),this.statusEl.style.cssText="font-size:11px;color:var(--text-secondary);width:100%;margin-top:4px",this.statusEl.textContent="Initializing...",n.appendChild(this.statusEl),this.buildConfigPanel(t);const l=document.createElement("div");l.className="panel",l.style.marginBottom="12px",t.appendChild(l);const c=document.createElement("div");c.className="panel-header",c.innerHTML='Training Curves <span style="font-size:8px;text-transform:none;letter-spacing:0;color:var(--text-muted);font-weight:400">accuracy & patterns</span>',l.appendChild(c),this.curveCanvas=document.createElement("canvas"),this.curveCanvas.width=500,this.curveCanvas.height=200,this.curveCanvas.style.cssText="width:100%;height:180px;display:block",l.appendChild(this.curveCanvas);const d=document.createElement("div");d.style.cssText="display:flex;gap:12px;padding:4px 10px;font-size:9px;color:var(--text-muted)",d.innerHTML=`
|
||
<span><span style="display:inline-block;width:12px;height:2px;background:#00E5FF;vertical-align:middle;margin-right:3px"></span>Accuracy</span>
|
||
<span><span style="display:inline-block;width:12px;height:2px;background:#58A6FF;vertical-align:middle;margin-right:3px;border-top:1px dashed #58A6FF"></span>Patterns</span>
|
||
<span><span style="display:inline-block;width:12px;height:2px;background:#FF6B9D;vertical-align:middle;margin-right:3px;border-top:1px dashed #FF6B9D"></span>Loss</span>
|
||
`,l.appendChild(d),this.modesEl=document.createElement("div"),this.modesEl.className="panel",this.modesEl.style.marginBottom="12px",t.appendChild(this.modesEl),this.policyEl=document.createElement("div"),this.policyEl.className="panel",this.policyEl.style.marginBottom="16px",t.appendChild(this.policyEl)}buildConfigPanel(t){const e=document.createElement("div");e.className="panel",e.style.marginBottom="12px",t.appendChild(e);const i=document.createElement("div");i.className="panel-header",i.style.cursor="pointer",i.innerHTML='<span>Configuration</span> <span id="config-arrow" style="font-size:8px;transition:transform 0.2s">▶</span>',e.appendChild(i);const s=document.createElement("div");s.className="panel-body",s.style.cssText="display:none;padding:10px",e.appendChild(s),i.addEventListener("click",()=>{const d=s.style.display!=="none";s.style.display=d?"none":"block";const h=i.querySelector("#config-arrow");h&&(h.style.transform=d?"":"rotate(90deg)")});const a=document.createElement("div");a.style.cssText="display:grid;grid-template-columns:1fr 1fr;gap:8px",s.appendChild(a);const n=(d,h,u,m,g,x)=>{const f=document.createElement("div");f.style.cssText="display:flex;flex-direction:column;gap:3px";const w=document.createElement("div");w.style.cssText="display:flex;justify-content:space-between;align-items:center";const y=document.createElement("span");y.style.cssText="font-size:9px;color:var(--text-muted)",y.textContent=d,w.appendChild(y);const b=document.createElement("span");b.style.cssText="font-size:10px;font-family:var(--font-mono);color:var(--accent)",b.textContent=String(g),w.appendChild(b),f.appendChild(w);const v=document.createElement("input");return v.type="range",v.min=String(h),v.max=String(u),v.step=String(m),v.value=String(g),v.style.cssText="width:100%;height:3px;accent-color:#00E5FF;cursor:pointer",v.addEventListener("input",()=>{const E=parseFloat(v.value);b.textContent=String(E),x(E)}),f.appendChild(v),f},o=document.createElement("div");o.style.cssText="grid-column:1/-1;font-size:9px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.4px;border-bottom:1px solid var(--border);padding-bottom:4px;margin-bottom:2px",o.textContent="Training",a.appendChild(o),a.appendChild(n("Puzzles per cycle",50,500,50,this.trainCount,d=>{this.trainCount=d,this.trainBtn&&(this.trainBtn.textContent=`Train (${d})`)})),a.appendChild(n("Auto-train rounds",3,20,1,this.autoTrainRounds,d=>{this.autoTrainRounds=d})),a.appendChild(n("Min difficulty",1,5,1,this.minDifficulty,d=>{this.minDifficulty=d})),a.appendChild(n("Max difficulty",3,10,1,this.maxDifficulty,d=>{this.maxDifficulty=d}));const r=document.createElement("div");r.style.cssText="grid-column:1/-1;font-size:9px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.4px;border-bottom:1px solid var(--border);padding-bottom:4px;margin-top:6px;margin-bottom:2px",r.textContent="Acceptance Test",a.appendChild(r),a.appendChild(n("Cycles",2,10,1,this.acceptCycles,d=>{this.acceptCycles=d})),a.appendChild(n("Holdout size",10,100,10,this.holdoutSize,d=>{this.holdoutSize=d})),a.appendChild(n("Training/cycle",50,500,50,this.trainingPerCycle,d=>{this.trainingPerCycle=d})),a.appendChild(n("Step budget",100,2e3,100,this.stepBudget,d=>{this.stepBudget=d}));const l=document.createElement("div");l.style.cssText="grid-column:1/-1;display:flex;gap:6px;margin-top:6px";const c=(d,h)=>{const u=document.createElement("button");return u.className="scale-btn",u.style.cssText="font-size:9px;padding:3px 8px;flex:1",u.textContent=d,u.addEventListener("click",()=>{this.trainCount=h.tc,this.autoTrainRounds=h.ar,this.acceptCycles=h.ac,this.holdoutSize=h.hs,this.trainingPerCycle=h.tpc,this.stepBudget=h.sb,this.trainBtn&&(this.trainBtn.textContent=`Train (${h.tc})`),s.style.display="none";const m=i.querySelector("#config-arrow");m&&(m.style.transform=""),a.innerHTML="",this.buildConfigPanel(t),e.remove()}),u};l.appendChild(c("Quick",{tc:100,ar:5,ac:3,hs:30,tpc:100,sb:300})),l.appendChild(c("Balanced",{tc:200,ar:8,ac:5,hs:50,tpc:200,sb:500})),l.appendChild(c("Thorough",{tc:500,ar:12,ac:12,hs:50,tpc:800,sb:2e3})),a.appendChild(l)}buildViewportControls(t){this.controlsEl=document.createElement("div"),this.controlsEl.style.cssText=`
|
||
position:absolute;top:8px;right:8px;z-index:10;
|
||
display:flex;flex-direction:column;gap:6px;
|
||
background:rgba(11,15,20,0.85);border:1px solid rgba(0,229,255,0.15);
|
||
border-radius:6px;padding:8px 10px;backdrop-filter:blur(6px);
|
||
font-size:10px;color:var(--text-secondary);min-width:130px
|
||
`;const e=document.createElement("div");e.style.cssText="display:flex;align-items:center;gap:6px";const i=document.createElement("span");i.textContent="Speed",i.style.cssText="color:var(--accent);font-weight:600;font-size:9px;min-width:34px",e.appendChild(i);const s=document.createElement("input");s.type="range",s.min="0.1",s.max="5",s.step="0.1",s.value="1",s.style.cssText="flex:1;height:3px;accent-color:#00E5FF;cursor:pointer",e.appendChild(s),this.speedLabel=document.createElement("span"),this.speedLabel.style.cssText="font-family:var(--font-mono);font-size:9px;min-width:24px;text-align:right;color:var(--text-primary)",this.speedLabel.textContent="1.0x",e.appendChild(this.speedLabel),s.addEventListener("input",()=>{this.speed=parseFloat(s.value),this.speedLabel&&(this.speedLabel.textContent=this.speed.toFixed(1)+"x")}),this.controlsEl.appendChild(e);const a=document.createElement("div");a.style.cssText="display:flex;align-items:center;gap:6px";const n=document.createElement("span");n.style.cssText="font-size:9px;color:var(--text-secondary);flex:1",n.textContent="Auto-rotate",a.appendChild(n);const o=document.createElement("button");o.style.cssText="font-size:9px;padding:2px 8px;border-radius:3px;border:1px solid rgba(0,229,255,0.2);background:transparent;color:var(--text-muted);cursor:pointer",o.textContent="OFF",o.addEventListener("click",()=>{this.autoRotate=!this.autoRotate,this.controls&&(this.controls.autoRotate=this.autoRotate),o.textContent=this.autoRotate?"ON":"OFF",o.style.color=this.autoRotate?"var(--accent)":"var(--text-muted)",o.style.borderColor=this.autoRotate?"rgba(0,229,255,0.4)":"rgba(0,229,255,0.2)"}),a.appendChild(o),this.controlsEl.appendChild(a);const r=document.createElement("button");r.style.cssText="font-size:9px;padding:3px 0;border-radius:3px;border:1px solid rgba(255,255,255,0.1);background:transparent;color:var(--text-muted);cursor:pointer;width:100%",r.textContent="Reset View",r.addEventListener("click",()=>this.resetCamera()),this.controlsEl.appendChild(r),t.appendChild(this.controlsEl)}resetCamera(){!this.camera||!this.controls||(this.camera.position.set(0,8,14),this.camera.lookAt(0,0,0),this.controls.target.set(0,0,0),this.controls.update())}initThreeJs(t){this.scene=new nt,this.scene.background=new _(329744),this.scene.fog=new ae(329744,.003),this.camera=new at(50,1,.1,2e3),this.camera.position.set(0,8,14),this.camera.lookAt(0,0,0),this.renderer=new ot({antialias:!0,alpha:!1}),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.renderer.toneMapping=oe,this.renderer.toneMappingExposure=1.2,t.appendChild(this.renderer.domElement),this.controls=new rt(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.08,this.controls.maxPolarAngle=Math.PI*.48,this.controls.minDistance=4,this.controls.maxDistance=60,this.controls.autoRotateSpeed=.5,this.scene.add(new W(3359846,.6));const e=new it(11193599,.8);e.position.set(5,12,5),this.scene.add(e);const i=new it(16746564,.2);i.position.set(-3,5,-3),this.scene.add(i),this.buildStarfield(),this.buildGalacticPlane(),this.buildNebulae();const s=new Tt(12,24,1844019,790552);s.position.y=-.02,this.scene.add(s),this.buildLandscape(),this.armMarkers=new tt,this.scene.add(this.armMarkers),this.buildArmMarkers()}buildStarfield(){if(!this.scene)return;const t=5e3,e=Pt(42),i=new Float32Array(t*3),s=new Float32Array(t*3),a=new Float32Array(t);for(let o=0;o<t;o++){const r=e()*Math.PI*2,l=Math.acos(2*e()-1),c=200+e()*600;i[o*3]=c*Math.sin(l)*Math.cos(r),i[o*3+1]=c*Math.sin(l)*Math.sin(r),i[o*3+2]=c*Math.cos(l);const d=e();d<.15?(s[o*3]=.7,s[o*3+1]=.75,s[o*3+2]=1):d<.4?(s[o*3]=1,s[o*3+1]=.98,s[o*3+2]=.95):d<.7?(s[o*3]=1,s[o*3+1]=.92,s[o*3+2]=.8):(s[o*3]=1,s[o*3+1]=.75,s[o*3+2]=.55),a[o]=.8+e()*2}const n=new P;n.setAttribute("position",new F(i,3)),n.setAttribute("color",new F(s,3)),n.setAttribute("size",new F(a,1)),this.bgStars=new X(n,new J({size:1.2,vertexColors:!0,sizeAttenuation:!0,transparent:!0,opacity:.85})),this.scene.add(this.bgStars)}buildGalacticPlane(){if(!this.scene)return;const t=6e3,e=Pt(123),i=new Float32Array(t*3),s=new Float32Array(t*3);for(let r=0;r<t;r++){const l=e()*Math.PI*2,c=Math.pow(e(),.4)*400,d=(e()-.5)*15*Math.exp(-c/200);i[r*3]=Math.cos(l)*c,i[r*3+1]=d,i[r*3+2]=Math.sin(l)*c;const h=.2+.3*Math.exp(-c/150);s[r*3]=h*.8,s[r*3+1]=h*.7,s[r*3+2]=h*1.2}const a=new P;a.setAttribute("position",new F(i,3)),a.setAttribute("color",new F(s,3)),this.galacticPlane=new X(a,new J({size:1.5,vertexColors:!0,sizeAttenuation:!0,transparent:!0,opacity:.3})),this.galacticPlane.rotation.x=-Math.PI/2+.3,this.galacticPlane.rotation.z=.4,this.galacticPlane.position.y=60,this.scene.add(this.galacticPlane);const n=new B(12,16,16),o=new N({color:4469606,transparent:!0,opacity:.08});this.galacticCore=new L(n,o),this.galacticCore.position.copy(this.galacticPlane.position),this.scene.add(this.galacticCore)}buildNebulae(){if(!this.scene)return;const t=Pt(777),e=[4482764,6702250,2263210,11158630,4500104,8939212];for(let i=0;i<6;i++){const s=document.createElement("canvas");s.width=64,s.height=64;const a=s.getContext("2d"),n=a.createRadialGradient(32,32,0,32,32,32),o=e[i%e.length],r=o>>16&255,l=o>>8&255,c=o&255;n.addColorStop(0,`rgba(${r},${l},${c},0.25)`),n.addColorStop(.5,`rgba(${r},${l},${c},0.08)`),n.addColorStop(1,"rgba(0,0,0,0)"),a.fillStyle=n,a.fillRect(0,0,64,64);const d=new Y(s),h=new V(new Z({map:d,transparent:!0,blending:et,opacity:.5})),u=t()*Math.PI*2,m=150+t()*250;h.position.set(Math.cos(u)*m,-30+t()*100,Math.sin(u)*m),h.scale.set(60+t()*80,60+t()*80,1),this.scene.add(h),this.nebulae.push(h)}}buildLandscape(){if(!this.scene)return;const t=48,e=new Rt(10,10,t-1,t-1),i=new Float32Array(e.attributes.position.count*3);for(let o=0;o<e.attributes.position.count;o++)i[o*3]=.12,i[o*3+1]=.2,i[o*3+2]=.4;e.setAttribute("color",new F(i,3));const s=new vt({vertexColors:!0,side:xt,roughness:.55,metalness:.15,wireframe:!1});this.landscapeMesh=new L(e,s),this.landscapeMesh.rotation.x=-Math.PI/2,this.scene.add(this.landscapeMesh);const a=new N({color:58879,wireframe:!0,transparent:!0,opacity:.04}),n=new L(e,a);n.rotation.x=-Math.PI/2,n.position.y=.01,this.scene.add(n),this.setLandscapeValues(this.generateLandscapeValues(t,0))}buildArmMarkers(){if(!this.armMarkers||!this.scene)return;for(;this.armMarkers.children.length>0;)this.armMarkers.remove(this.armMarkers.children[0]);this.peakGlows=[];const t=["Naked Singles","Hidden Pairs","X-Wing","Backtrack","Constraint Prop","Pattern Match","Speculative","Hybrid"],e=3.5;for(let i=0;i<t.length;i++){const s=i/t.length*Math.PI*2,a=Math.cos(s)*e,n=Math.sin(s)*e,o=new Me(.08,.08,.3,8),r=new N({color:58879,transparent:!0,opacity:.4}),l=new L(o,r);l.position.set(a,.15,n),this.armMarkers.add(l);const c=document.createElement("canvas");c.width=32,c.height=32;const d=c.getContext("2d"),h=d.createRadialGradient(16,16,0,16,16,16);h.addColorStop(0,"rgba(0,229,255,0.6)"),h.addColorStop(1,"rgba(0,229,255,0)"),d.fillStyle=h,d.fillRect(0,0,32,32);const u=new Y(c),m=new V(new Z({map:u,transparent:!0,blending:et}));m.position.set(a,.8,n),m.scale.set(.5,.5,1),this.armMarkers.add(m),this.peakGlows.push(m);const g=document.createElement("canvas");g.width=128,g.height=32;const x=g.getContext("2d");x.fillStyle="rgba(0,229,255,0.8)",x.font="11px monospace",x.textAlign="center",x.fillText(t[i],64,18);const f=new Y(g),w=new V(new Z({map:f,transparent:!0}));w.position.set(a,-.3,n),w.scale.set(2,.5,1),this.armMarkers.add(w)}}generateLandscapeValues(t,e){const i=[];for(let n=0;n<t;n++)for(let o=0;o<t;o++){const r=(o/(t-1)-.5)*10,l=(n/(t-1)-.5)*10;let c=.05;for(let d=0;d<8;d++){const h=d/8*Math.PI*2,u=Math.cos(h)*3.5,m=Math.sin(h)*3.5,g=Math.sqrt((r-u)**2+(l-m)**2),x=.2+.6*Math.abs(Math.sin(d*1.7+e*.3)),f=1+.3*Math.sin(d+e*.5);c+=x*Math.exp(-g*g/(2*f*f))}c+=.05*Math.sin(r*3+e)*Math.cos(l*2.5+e*.7),i.push(Math.max(0,Math.min(1,c)))}return i}setLandscapeValues(t){if(!this.landscapeMesh)return;const e=this.landscapeMesh.geometry,i=e.attributes.position,s=e.attributes.color,a=Math.min(t.length,i.count);for(let n=0;n<a;n++){const o=Math.max(0,Math.min(1,t[n]));if(i.setZ(n,o*3),o<.2)s.setXYZ(n,.05,.1+o*2,.3+o);else if(o<.45){const r=(o-.2)/.25;s.setXYZ(n,.05+r*.1,.5+r*.3,.5*(1-r))}else if(o<.7){const r=(o-.45)/.25;s.setXYZ(n,.15+r*.65,.8-r*.2,.05)}else{const r=(o-.7)/.3;s.setXYZ(n,.8+r*.2,.6-r*.5,.05)}}i.needsUpdate=!0,s.needsUpdate=!0,e.computeVertexNormals(),this.updatePeakGlows(t)}updatePeakGlows(t){for(let a=0;a<8&&a<this.peakGlows.length;a++){const n=a/8*Math.PI*2,o=Math.cos(n)*3.5,r=Math.sin(n)*3.5,l=Math.round((o/10+.5)*47),d=Math.round((r/10+.5)*47)*48+l,h=d>=0&&d<t.length?t[d]*3:.5;this.peakGlows[a].position.y=h+.3,this.peakGlows[a].scale.setScalar(.3+t[d>=0&&d<t.length?d:0]*.8)}}updateLandscape(){const t=this.trainingHistory.length*1.7+(this.manifest?5:0),i=this.generateLandscapeValues(48,t);if(this.trainingHistory.length>0){const s=this.trainingHistory[this.trainingHistory.length-1].accuracy;for(let a=0;a<i.length;a++)i[a]=i[a]*.5+s*.3+i[a]*s*.2}this.setLandscapeValues(i)}async init(){const t=await kt();this.usesWasm=t!==null,this.statusEl&&(this.statusEl.textContent=this.usesWasm?"WASM solver loaded — ready to train":"Demo mode (WASM unavailable) — ready to train",this.statusEl.style.color=this.usesWasm?"var(--success)":"var(--warning)"),this.renderModes(),this.renderPolicy()}async runTraining(){if(this.isTraining)return;this.isTraining=!0,this.trainBtn&&(this.trainBtn.disabled=!0),this.statusEl&&(this.statusEl.textContent="Training...",this.statusEl.style.color="var(--accent)");const t=this.trainingHistory.length;let e;const i=await kt();i?(e=i.train({count:this.trainCount,minDifficulty:this.minDifficulty,maxDifficulty:this.maxDifficulty}),this.policy=i.policy()):(await new Promise(o=>setTimeout(o,500)),e=Ts(this.trainCount,t),this.policy=Xt());const s=1-e.accuracy,a=(Math.random()-.5)*.05,n=Math.max(.01,s+a);this.trainingHistory.push({cycle:t,accuracy:e.accuracy,patterns:e.patternsLearned,loss:n}),this.renderCurve(),this.updateLandscape(),this.renderPolicy(),this.statusEl&&(this.statusEl.textContent=`Cycle ${t+1}: ${(e.accuracy*100).toFixed(1)}% accuracy, ${e.patternsLearned} patterns, loss ${n.toFixed(3)}`,this.statusEl.style.color="var(--text-secondary)"),this.isTraining=!1,this.trainBtn&&(this.trainBtn.disabled=!1)}async runAcceptance(){if(this.isTraining)return;this.isTraining=!0,this.acceptBtn&&(this.acceptBtn.disabled=!0),this.statusEl&&(this.statusEl.textContent="Running acceptance test...",this.statusEl.style.color="var(--accent)");const t=await kt();if(t?(this.manifest=t.acceptance({cycles:this.acceptCycles,holdoutSize:this.holdoutSize,trainingPerCycle:this.trainingPerCycle,stepBudget:this.stepBudget}),this.policy=t.policy()):(await new Promise(e=>setTimeout(e,1200)),this.manifest=Ss(),this.policy=Xt()),this.renderModes(),this.updateLandscape(),this.renderPolicy(),this.statusEl){const e=this.manifest.allPassed;this.statusEl.textContent=`Acceptance: ${e?"PASSED":"FAILED"} — ${this.manifest.witnessEntries} witness entries`,this.statusEl.style.color=e?"var(--success)":"var(--danger)"}this.isTraining=!1,this.acceptBtn&&(this.acceptBtn.disabled=!1)}async runAutoTraining(){if(!this.isTraining){for(let t=0;t<this.autoTrainRounds;t++)await this.runTraining(),await new Promise(e=>setTimeout(e,150));await this.runAcceptance()}}async runAutoOptimize(){var s;if(this.isTraining)return;const t=[{label:"Phase 1",tpc:this.trainingPerCycle,cycles:this.acceptCycles,budget:this.stepBudget,hs:this.holdoutSize,seeds:3},{label:"Phase 2",tpc:500,cycles:8,budget:1200,hs:50,seeds:3},{label:"Phase 3",tpc:800,cycles:12,budget:2e3,hs:50,seeds:3}];let e=0;const i=t.reduce((a,n)=>a+n.seeds,0);for(const a of t){this.trainingPerCycle=a.tpc,this.acceptCycles=a.cycles,this.stepBudget=a.budget,this.holdoutSize=a.hs;for(let n=0;n<a.seeds;n++){if(e++,e>1&&(this.statusEl&&(this.statusEl.textContent=`${a.label}: training before attempt ${e}/${i}...`,this.statusEl.style.color="var(--accent)"),await this.runTraining(),await new Promise(r=>setTimeout(r,50))),this.statusEl&&(this.statusEl.textContent=`${a.label}: acceptance attempt ${e}/${i} (tpc=${a.tpc}, c=${a.cycles}, sb=${a.budget})...`,this.statusEl.style.color="var(--warning)"),await this.runAcceptance(),((s=this.manifest)==null?void 0:s.allPassed)??!1){this.statusEl&&(this.statusEl.textContent=`Auto-optimize: PASSED on attempt ${e} (${a.label})!`,this.statusEl.style.color="var(--success)");return}if(this.statusEl&&this.manifest){const r=this.manifest.modeC,l=[];r.accuracyMaintained||l.push("accuracy < 80%"),r.costImproved||l.push("cost not improved 5%"),r.robustnessImproved||l.push("robustness not improved 3%"),r.dimensionsImproved<2&&l.push(`only ${r.dimensionsImproved}/2 dims`),this.statusEl.textContent=`Attempt ${e}: FAILED — ${l.join(", ")}`,this.statusEl.style.color="#FF4D4D"}await new Promise(r=>setTimeout(r,100))}}this.statusEl&&(this.statusEl.textContent=`Auto-optimize: did not pass after ${e} attempts. The acceptance test creates a fresh solver each time — try increasing Training/cycle and Cycles.`,this.statusEl.style.color="#FF4D4D")}renderCurve(){if(!this.curveCanvas||this.trainingHistory.length===0)return;const t=this.curveCanvas.getContext("2d");if(!t)return;const e=this.curveCanvas.width,i=this.curveCanvas.height,s={top:14,right:14,bottom:28,left:44},a=e-s.left-s.right,n=i-s.top-s.bottom;t.clearRect(0,0,e,i),t.fillStyle="#0B0F14",t.fillRect(0,0,e,i);const o=this.trainingHistory,r=Math.max(o.length-1,1);t.strokeStyle="#1A2030",t.lineWidth=.5;for(let d=0;d<=1;d+=.25){const h=s.top+n*(1-d);t.beginPath(),t.moveTo(s.left,h),t.lineTo(s.left+a,h),t.stroke(),t.fillStyle="#484F58",t.font="10px monospace",t.textAlign="right",t.fillText(`${(d*100).toFixed(0)}%`,s.left-6,h+3)}t.fillStyle="rgba(0,229,255,0.06)",t.beginPath(),t.moveTo(s.left,s.top+n);for(let d=0;d<o.length;d++){const h=s.left+d/r*a,u=s.top+n*(1-o[d].accuracy);t.lineTo(h,u)}t.lineTo(s.left+(o.length-1)/r*a,s.top+n),t.closePath(),t.fill(),t.strokeStyle="#00E5FF",t.lineWidth=2.5,t.beginPath();for(let d=0;d<o.length;d++){const h=s.left+d/r*a,u=s.top+n*(1-o[d].accuracy);d===0?t.moveTo(h,u):t.lineTo(h,u)}t.stroke(),t.fillStyle="#00E5FF";for(let d=0;d<o.length;d++){const h=s.left+d/r*a,u=s.top+n*(1-o[d].accuracy);t.beginPath(),t.arc(h,u,3.5,0,Math.PI*2),t.fill()}if(o.length>1){const d=Math.max(...o.map(h=>h.patterns),1);t.strokeStyle="#58A6FF",t.lineWidth=1.5,t.setLineDash([5,3]),t.beginPath();for(let h=0;h<o.length;h++){const u=s.left+h/r*a,m=s.top+n*(1-o[h].patterns/d);h===0?t.moveTo(u,m):t.lineTo(u,m)}t.stroke(),t.setLineDash([])}if(o.length>1){t.strokeStyle="#FF6B9D",t.lineWidth=1.5,t.setLineDash([3,4]),t.beginPath();for(let d=0;d<o.length;d++){const h=s.left+d/r*a,u=s.top+n*(1-o[d].loss);d===0?t.moveTo(h,u):t.lineTo(h,u)}t.stroke(),t.setLineDash([])}t.fillStyle="#556677",t.font="10px monospace",t.textAlign="center";const l=o.length<=10?1:Math.ceil(o.length/10);for(let d=0;d<o.length;d+=l){const h=s.left+d/r*a;t.fillText(`C${d+1}`,h,i-8)}const c=o[o.length-1];t.fillStyle="#00E5FF",t.font="bold 11px monospace",t.textAlign="right",t.fillText(`${(c.accuracy*100).toFixed(1)}%`,e-4,s.top+4)}renderModes(){if(!this.modesEl)return;this.modesEl.innerHTML="";const t=document.createElement("div");t.className="panel-header",t.textContent="Acceptance Modes (A / B / C)",this.modesEl.appendChild(t);const e=document.createElement("div");if(e.className="panel-body",this.modesEl.appendChild(e),!this.manifest){e.innerHTML=`
|
||
<div style="text-align:center;padding:8px 0">
|
||
<div style="color:var(--text-muted);font-size:12px;margin-bottom:6px">Click "Acceptance" to see results</div>
|
||
<div style="font-size:10px;color:var(--text-muted);line-height:1.5">
|
||
Train the solver first (multiple cycles recommended) before running acceptance.
|
||
</div>
|
||
</div>
|
||
`;return}const i=[{label:"Mode A",desc:"Heuristic — accuracy only",data:this.manifest.modeA,color:Lt.A},{label:"Mode B",desc:"Compiler — accuracy + cost",data:this.manifest.modeB,color:Lt.B},{label:"Mode C",desc:"Learned — full multi-objective",data:this.manifest.modeC,color:Lt.C}];for(const o of i){const r=document.createElement("div");r.style.cssText="margin-bottom:8px;padding:8px;background:var(--bg-surface);border:1px solid var(--border);border-radius:4px";const l=o.data.cycles.length>0?o.data.cycles[o.data.cycles.length-1].accuracy:0,c=o.data.passed?"PASS":"FAIL",d=o.data.passed?"var(--success)":"#FF4D4D";r.innerHTML=`
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px">
|
||
<span style="color:${o.color};font-weight:600;font-size:11px">${o.label}</span>
|
||
<span style="font-size:9px;color:var(--text-muted)">${o.desc}</span>
|
||
<span style="color:${d};font-weight:700;font-size:11px">${c} ${(l*100).toFixed(1)}%</span>
|
||
</div>
|
||
`;const h=document.createElement("div");h.style.cssText="display:flex;gap:2px;align-items:flex-end;height:20px;margin-bottom:4px";for(const g of o.data.cycles){const x=document.createElement("div");x.style.cssText=`flex:1;height:${g.accuracy*100}%;background:${o.color};border-radius:1px;opacity:0.7;transition:height 0.3s`,x.title=`Cycle ${g.cycle}: ${(g.accuracy*100).toFixed(1)}% | Cost: ${g.costPerSolve.toFixed(0)}`,h.appendChild(x)}r.appendChild(h);const u=document.createElement("div");u.style.cssText="display:flex;gap:3px;flex-wrap:wrap";const m=(g,x)=>{const f=document.createElement("span");f.style.cssText=`font-size:8px;padding:1px 4px;border-radius:2px;border:1px solid ${x?"rgba(46,204,113,0.3)":"rgba(255,77,77,0.3)"};color:${x?"var(--success)":"#FF4D4D"}`,f.textContent=g,u.appendChild(f)};m(`Acc: ${o.data.accuracyMaintained?"OK":"FAIL"}`,o.data.accuracyMaintained),m(`Cost: ${o.data.costImproved?"OK":"N/A"}`,o.data.costImproved),m(`Rob: ${o.data.robustnessImproved?"OK":"N/A"}`,o.data.robustnessImproved),m(`Viol: ${o.data.zeroViolations?"0":">0"}`,o.data.zeroViolations),r.appendChild(u),e.appendChild(r)}const s=document.createElement("div");s.style.cssText=`padding:6px;border-radius:4px;text-align:center;font-size:11px;font-weight:700;
|
||
background:${this.manifest.allPassed?"rgba(46,204,113,0.1)":"rgba(255,77,77,0.1)"};
|
||
border:1px solid ${this.manifest.allPassed?"rgba(46,204,113,0.3)":"rgba(255,77,77,0.3)"};
|
||
color:${this.manifest.allPassed?"var(--success)":"#FF4D4D"}`,s.textContent=this.manifest.allPassed?"ALL MODES PASSED":"SOME MODES FAILED",e.appendChild(s);const a=document.createElement("div");a.style.cssText="font-size:9px;color:var(--text-muted);margin-top:6px;text-align:center",a.innerHTML=`Witness chain: <span style="color:var(--text-secondary)">${this.manifest.witnessEntries} entries</span> · <span style="color:var(--text-secondary)">${this.manifest.witnessChainBytes} bytes</span>`,e.appendChild(a);const n=document.createElement("div");n.style.cssText="font-size:9px;color:var(--text-muted);margin-top:4px;text-align:center;line-height:1.4",n.innerHTML="Pass/Fail = Mode C (full learned). Each test trains a <strong>fresh solver</strong> from scratch — increase <em>Training/cycle</em> and <em>Cycles</em> to give it more learning time.",e.appendChild(n)}renderPolicy(){if(!this.policyEl)return;this.policyEl.innerHTML="";const t=document.createElement("div");t.className="panel-header",t.innerHTML='Policy State <span style="font-size:8px;text-transform:none;letter-spacing:0;color:var(--text-muted);font-weight:400">learned decisions</span>',this.policyEl.appendChild(t);const e=document.createElement("div");if(e.className="panel-body",e.style.fontSize="11px",this.policyEl.appendChild(e),!this.policy){e.innerHTML='<div style="color:var(--text-muted);text-align:center;padding:8px 0">Train to populate policy state</div>';return}const i=this.policy.speculativeAttempts>0?(this.policy.speculativeArm2Wins/this.policy.speculativeAttempts*100).toFixed(1)+"%":"N/A",s=this.policy.earlyCommitsTotal>0?(this.policy.earlyCommitsWrong/this.policy.earlyCommitsTotal*100).toFixed(1)+"%":"N/A",a=document.createElement("div");a.style.cssText="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:8px";const n=(r,l,c)=>{const d=document.createElement("div");d.style.cssText="background:var(--bg-surface);border:1px solid var(--border);border-radius:4px;padding:6px 8px",d.innerHTML=`
|
||
<div style="font-size:9px;color:var(--text-muted);margin-bottom:2px">${r}</div>
|
||
<div style="font-size:13px;font-weight:600;color:${c};font-family:var(--font-mono)">${l}</div>
|
||
`,a.appendChild(d)};n("Prepass",this.policy.prepass||"none","var(--accent)"),n("Spec. Attempts",String(this.policy.speculativeAttempts),"var(--text-primary)"),n("Arm-2 Win Rate",i,i!=="N/A"&&parseFloat(i)>50?"var(--success)":"var(--warning)"),n("Early Commit Err",s,s!=="N/A"&&parseFloat(s)<20?"var(--success)":"#FF4D4D"),e.appendChild(a);const o=Object.keys(this.policy.contextStats);if(o.length>0){const r=document.createElement("div");r.innerHTML='<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px">Difficulty Buckets</div>';for(const l of o){const c=this.policy.contextStats[l],d=Object.keys(c),h=document.createElement("div");h.style.cssText="margin-bottom:6px";let u=0,m=0;for(const w of d){const y=c[w];u+=y.attempts||0,m+=y.successes||0}const g=u>0?(m/u*100).toFixed(0):"0",x=u>0?m/u:0,f=x>.7?"var(--success)":x>.4?"var(--warning)":"#FF4D4D";h.innerHTML=`
|
||
<div style="display:flex;justify-content:space-between;font-size:10px;margin-bottom:2px">
|
||
<span style="color:var(--text-secondary);text-transform:capitalize">${l}</span>
|
||
<span style="color:${f};font-weight:600">${g}% (${m}/${u})</span>
|
||
</div>
|
||
<div class="progress-bar" style="height:4px">
|
||
<div class="progress-fill" style="width:${g}%;background:${f};transition:width 0.3s"></div>
|
||
</div>
|
||
`,r.appendChild(h)}e.appendChild(r)}}unmount(){var t,e,i,s,a,n,o,r;window.removeEventListener("resize",this.resize),cancelAnimationFrame(this.animFrameId),this.landscapeMesh&&((t=this.scene)==null||t.remove(this.landscapeMesh),this.landscapeMesh.geometry.dispose(),this.landscapeMesh.material.dispose(),this.landscapeMesh=null),this.bgStars&&((e=this.scene)==null||e.remove(this.bgStars),this.bgStars.geometry.dispose(),this.bgStars.material.dispose(),this.bgStars=null),this.galacticPlane&&((i=this.scene)==null||i.remove(this.galacticPlane),this.galacticPlane.geometry.dispose(),this.galacticPlane.material.dispose(),this.galacticPlane=null),this.galacticCore&&((s=this.scene)==null||s.remove(this.galacticCore),this.galacticCore.geometry.dispose(),this.galacticCore.material.dispose(),this.galacticCore=null);for(const l of this.nebulae)(a=this.scene)==null||a.remove(l),l.material.dispose();this.nebulae=[],this.armMarkers&&((n=this.scene)==null||n.remove(this.armMarkers),this.armMarkers=null),this.peakGlows=[],(o=this.controls)==null||o.dispose(),(r=this.renderer)==null||r.dispose(),this.controls=null,this.renderer=null,this.scene=null,this.camera=null,this.container=null}}const zs={commit:"witness-badge-commit",verify:"witness-badge-verify",seal:"witness-badge-seal",merge:"witness-badge-merge"};class As{constructor(t){this.autoScroll=!0,this.root=document.createElement("div"),this.root.className="witness-log";const e=document.createElement("div");e.className="witness-log-header",e.textContent="Witness Log",this.root.appendChild(e),this.listEl=document.createElement("div"),this.listEl.className="witness-log-list",this.listEl.addEventListener("scroll",()=>{const{scrollTop:i,scrollHeight:s,clientHeight:a}=this.listEl;this.autoScroll=i+a>=s-20}),this.root.appendChild(this.listEl),t.appendChild(this.root)}addEntry(t){const e=document.createElement("div");e.className="witness-log-entry";const i=document.createElement("span");i.className="witness-ts",i.textContent=t.timestamp,e.appendChild(i);const s=document.createElement("span"),a=zs[t.type.toLowerCase()]??"witness-badge-commit";s.className=`witness-badge ${a}`,s.textContent=t.type,e.appendChild(s);const n=document.createElement("span");n.className="witness-step",n.textContent=t.action,e.appendChild(n);const o=document.createElement("span");o.className="witness-hash",o.textContent=t.hash.substring(0,12),o.title=t.hash,e.appendChild(o),this.listEl.appendChild(e),this.autoScroll&&(this.listEl.scrollTop=this.listEl.scrollHeight)}clear(){this.listEl.innerHTML=""}destroy(){this.root.remove()}}const Dt=["P0","P1","P2","L0","L1","L2"];function $s(p){return p<1024?`${p} B`:p<1024*1024?`${(p/1024).toFixed(1)} KB`:p<1024*1024*1024?`${(p/(1024*1024)).toFixed(1)} MB`:`${(p/(1024*1024*1024)).toFixed(2)} GB`}function ks(p){const t=Math.floor(p/3600),e=Math.floor(p%3600/60),i=Math.floor(p%60);return`${t}h ${e}m ${i}s`}class Ls{constructor(){this.container=null,this.witnessLog=null,this.pollTimer=null,this.unsubWs=null,this.downloadEl=null,this.pipelineEl=null,this.gaugesEl=null,this.segmentEl=null,this.uptimeEl=null}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:2px">System Status</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">
|
||
Live overview of the RVF runtime. <strong>Pipeline stages</strong> show data processing progress (P0–P2 = planet pipeline, L0–L2 = life pipeline).
|
||
<strong>Downloads</strong> track segment ingestion. <strong>Memory tiers</strong> show S/M/L utilization.
|
||
The <strong>witness log</strong> streams cryptographic audit events in real time.
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.className="status-grid",s.style.flex="1",s.style.overflow="auto",s.style.minHeight="0",e.appendChild(s);const a=this.createPanel("System Health");s.appendChild(a),this.uptimeEl=document.createElement("div"),this.uptimeEl.className="panel-body",a.appendChild(this.uptimeEl);const n=this.createPanel("Pipeline Stages");s.appendChild(n),this.pipelineEl=document.createElement("div"),this.pipelineEl.className="panel-body",n.appendChild(this.pipelineEl);const o=this.createPanel("Download Progress");s.appendChild(o),this.downloadEl=document.createElement("div"),this.downloadEl.className="panel-body",o.appendChild(this.downloadEl);const r=this.createPanel("Memory Tiers (S / M / L)");s.appendChild(r),this.gaugesEl=document.createElement("div"),this.gaugesEl.className="panel-body",this.gaugesEl.innerHTML='<div class="gauge-container"></div>',r.appendChild(this.gaugesEl);const l=this.createPanel("Segment Overview");l.classList.add("full-width"),s.appendChild(l),this.segmentEl=document.createElement("div"),this.segmentEl.className="panel-body",l.appendChild(this.segmentEl);const c=document.createElement("div");c.classList.add("full-width"),c.style.minHeight="200px",s.appendChild(c),this.witnessLog=new As(c),this.loadData(),this.loadWitnessLog(),this.pollTimer=setInterval(()=>this.loadData(),5e3),this.unsubWs=St(d=>{var h;d.event_type==="witness"&&((h=this.witnessLog)==null||h.addEntry({timestamp:new Date(d.timestamp*1e3).toISOString().substring(11,19),type:String(d.data.type??"update"),action:String(d.data.action??""),hash:String(d.data.hash??"")}))})}async loadWitnessLog(){var t,e,i;try{const s=await me();for(const a of s.entries){const n=a.timestamp.includes("T")?((t=a.timestamp.split("T")[1])==null?void 0:t.substring(0,8))??a.timestamp:a.timestamp;(e=this.witnessLog)==null||e.addEntry({timestamp:n,type:a.type,action:`${a.witness}: ${a.action}`,hash:a.hash})}}catch{const s=[{timestamp:"14:00:01",type:"seal",action:"W_root: Chain initialized",hash:"a1b2c3d4"},{timestamp:"14:00:12",type:"commit",action:"W_photometry: Light curves ingested",hash:"b3c4d5e6"},{timestamp:"14:01:03",type:"commit",action:"W_periodogram: BLS search completed",hash:"c5d6e7f8"},{timestamp:"14:02:18",type:"commit",action:"W_stellar: Stellar parameters derived",hash:"d7e8f9a0"},{timestamp:"14:03:45",type:"merge",action:"W_transit: Transit model merged",hash:"e9f0a1b2"},{timestamp:"14:04:22",type:"commit",action:"W_radial_velocity: RV data ingested",hash:"f1a2b3c4"},{timestamp:"14:05:10",type:"commit",action:"W_orbit: Orbital solutions computed",hash:"a3b4c5d6"},{timestamp:"14:06:33",type:"commit",action:"W_esi: ESI ranking computed",hash:"b5c6d7e8"},{timestamp:"14:08:01",type:"merge",action:"W_spectroscopy: JWST observations merged",hash:"c7d8e9f0"},{timestamp:"14:09:15",type:"commit",action:"W_biosig: Biosignature scoring done",hash:"d9e0f1a2"},{timestamp:"14:10:42",type:"commit",action:"W_blind: Blind test passed (τ=1.0)",hash:"e1f2a3b4"},{timestamp:"14:15:55",type:"verify",action:"W_seal: Chain sealed — Ed25519 signed",hash:"c9d0e1f2"}];for(const a of s)(i=this.witnessLog)==null||i.addEntry(a)}}createPanel(t){const e=document.createElement("div");e.className="panel";const i=document.createElement("div");return i.className="panel-header",i.textContent=t,e.appendChild(i),e}async loadData(){let t,e;try{t=await ue()}catch(i){console.error("Status API error:",i),t={uptime:0,segments:0,file_size:0,download_progress:{}}}try{e=await ge()}catch(i){console.error("Memory API error:",i),e={small:{used:0,total:0},medium:{used:0,total:0},large:{used:0,total:0}}}this.renderHealth(t),this.renderPipeline(),this.renderDownloads(t.download_progress),this.renderGauges(e),this.renderSegments(t)}renderHealth(t){this.uptimeEl&&(this.uptimeEl.innerHTML=`
|
||
<div style="display: flex; gap: 32px; flex-wrap: wrap;">
|
||
<div class="gauge">
|
||
<div class="gauge-label">Uptime</div>
|
||
<div class="gauge-value">${ks(t.uptime)}</div>
|
||
</div>
|
||
<div class="gauge">
|
||
<div class="gauge-label">Segments</div>
|
||
<div class="gauge-value">${t.segments}</div>
|
||
</div>
|
||
<div class="gauge">
|
||
<div class="gauge-label">File Size</div>
|
||
<div class="gauge-value">${$s(t.file_size)}</div>
|
||
</div>
|
||
</div>
|
||
`)}renderPipeline(){if(!this.pipelineEl)return;const t=Math.floor(Date.now()/3e3)%Dt.length;this.pipelineEl.innerHTML="";const e=document.createElement("div");e.className="pipeline-stages";for(let i=0;i<Dt.length;i++){if(i>0){const a=document.createElement("span");a.className="pipeline-arrow",a.textContent="→",e.appendChild(a)}const s=document.createElement("div");s.className="pipeline-stage",i<t?s.classList.add("active"):i===t?s.classList.add("pending"):s.classList.add("idle"),s.textContent=Dt[i],e.appendChild(s)}this.pipelineEl.appendChild(e)}renderDownloads(t){if(this.downloadEl){this.downloadEl.innerHTML="";for(const[e,i]of Object.entries(t)){const s=document.createElement("div");s.className="progress-label",s.innerHTML=`<span>${e}</span><span>${(i*100).toFixed(0)}%</span>`,this.downloadEl.appendChild(s);const a=document.createElement("div");a.className="progress-bar";const n=document.createElement("div");n.className=`progress-fill ${i>=1?"success":i>.8?"info":"warning"}`,n.style.width=`${Math.min(100,i*100)}%`,a.appendChild(n),this.downloadEl.appendChild(a)}}}renderGauges(t){if(!this.gaugesEl)return;const e=this.gaugesEl.querySelector(".gauge-container");if(!e)return;e.innerHTML="";const i=[{label:"Small",...t.small},{label:"Medium",...t.medium},{label:"Large",...t.large}];for(const s of i){const a=s.total>0?s.used/s.total:0,n=document.createElement("div");n.className="gauge";const o=document.createElementNS("http://www.w3.org/2000/svg","svg");o.setAttribute("viewBox","0 0 80 80"),o.classList.add("gauge-ring");const r=document.createElementNS("http://www.w3.org/2000/svg","circle");r.setAttribute("cx","40"),r.setAttribute("cy","40"),r.setAttribute("r","34"),r.setAttribute("fill","none"),r.setAttribute("stroke","#1C2333"),r.setAttribute("stroke-width","6"),o.appendChild(r);const l=2*Math.PI*34,c=document.createElementNS("http://www.w3.org/2000/svg","circle");c.setAttribute("cx","40"),c.setAttribute("cy","40"),c.setAttribute("r","34"),c.setAttribute("fill","none"),c.setAttribute("stroke",a>.9?"#FF4D4D":a>.7?"#FFB020":"#00E5FF"),c.setAttribute("stroke-width","6"),c.setAttribute("stroke-dasharray",`${l*a} ${l*(1-a)}`),c.setAttribute("stroke-dashoffset",`${l*.25}`),c.setAttribute("stroke-linecap","round"),o.appendChild(c);const d=document.createElementNS("http://www.w3.org/2000/svg","text");d.setAttribute("x","40"),d.setAttribute("y","44"),d.setAttribute("text-anchor","middle"),d.setAttribute("fill","#E6EDF3"),d.setAttribute("font-size","14"),d.setAttribute("font-weight","700"),d.textContent=`${(a*100).toFixed(0)}%`,o.appendChild(d),n.appendChild(o);const h=document.createElement("div");h.className="gauge-label",h.textContent=`${s.label} (${s.used}/${s.total})`,n.appendChild(h),e.appendChild(n)}}renderSegments(t){this.segmentEl&&(this.segmentEl.innerHTML=`
|
||
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
|
||
${Array.from({length:Math.min(t.segments,64)},(e,i)=>`<div style="width: 12px; height: 12px; border-radius: 2px; background: hsl(${i/Math.min(t.segments,64)*240}, 60%, 45%);" title="Segment ${i}"></div>`).join("")}
|
||
${t.segments>64?`<span style="color: var(--text-muted); font-size: 11px; align-self: center;">+${t.segments-64} more</span>`:""}
|
||
</div>
|
||
`)}unmount(){var t,e;this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),(t=this.unsubWs)==null||t.call(this),(e=this.witnessLog)==null||e.destroy(),this.witnessLog=null,this.downloadEl=null,this.pipelineEl=null,this.gaugesEl=null,this.segmentEl=null,this.uptimeEl=null,this.container=null}}class Ps{constructor(){this.container=null,this.revealed=!1,this.data=null,this.tableBody=null,this.revealBtn=null,this.summaryEl=null}mount(t){this.container=t,this.revealed=!1;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:auto",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
|
||
<div style="font-size:16px;font-weight:700;color:var(--text-primary)">Blind Test: Exoplanet Discovery Validation</div>
|
||
<span class="score-badge score-high" style="font-size:9px;padding:2px 8px">REAL DATA</span>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--text-secondary);line-height:1.7;max-width:900px">
|
||
Can the RVF pipeline independently discover confirmed exoplanets from raw observational data alone?
|
||
Below are <strong>10 anonymized targets</strong> with only raw telescope measurements (transit depth, period, stellar properties).
|
||
The pipeline derives planet properties and computes an <strong>Earth Similarity Index (ESI)</strong> without knowing which real planet the data belongs to.
|
||
Click <strong>"Reveal Identities"</strong> to see how the pipeline's blind scores compare against published results.
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.style.cssText="padding:12px 20px;background:rgba(0,229,255,0.04);border-bottom:1px solid var(--border)",s.innerHTML=`
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:4px">Pipeline Methodology</div>
|
||
<div id="bt-methodology" style="font-size:11px;color:var(--text-secondary);line-height:1.5">Loading...</div>
|
||
`,e.appendChild(s);const a=document.createElement("div");a.style.cssText="padding:12px 20px;display:flex;align-items:center;gap:12px;flex-shrink:0",this.revealBtn=document.createElement("button"),this.revealBtn.textContent="Reveal Identities",this.revealBtn.style.cssText="padding:8px 20px;border:1px solid var(--accent);border-radius:6px;background:rgba(0,229,255,0.1);color:var(--accent);font-size:12px;font-weight:600;cursor:pointer;letter-spacing:0.3px;transition:all 0.2s",this.revealBtn.addEventListener("click",()=>this.toggleReveal()),this.revealBtn.addEventListener("mouseenter",()=>{this.revealBtn.style.background="rgba(0,229,255,0.2)"}),this.revealBtn.addEventListener("mouseleave",()=>{this.revealBtn.style.background="rgba(0,229,255,0.1)"}),a.appendChild(this.revealBtn);const n=document.createElement("span");n.style.cssText="font-size:10px;color:var(--text-muted)",n.textContent="First examine the pipeline scores, then reveal to compare against published results",a.appendChild(n),e.appendChild(a);const o=document.createElement("div");o.style.cssText="padding:0 20px 16px;flex:1",e.appendChild(o);const r=document.createElement("table");r.className="data-table",r.style.width="100%";const l=document.createElement("thead"),c=document.createElement("tr"),d=["Target","Transit Depth","Period (d)","Star Temp (K)","Star R (Sol)","Pipeline R (Earth)","Pipeline Temp (K)","HZ?","Pipeline ESI","Real Name","Published ESI","Match?"];for(const u of d){const m=document.createElement("th");m.textContent=u,m.style.fontSize="10px",(u==="Real Name"||u==="Published ESI"||u==="Match?")&&(m.className="reveal-col"),c.appendChild(m)}l.appendChild(c),r.appendChild(l),this.tableBody=document.createElement("tbody"),r.appendChild(this.tableBody),o.appendChild(r),this.summaryEl=document.createElement("div"),this.summaryEl.style.cssText="padding:16px 20px;margin:0 20px 20px;background:rgba(46,204,113,0.06);border:1px solid rgba(46,204,113,0.2);border-radius:8px;display:none",e.appendChild(this.summaryEl);const h=document.createElement("style");h.textContent=`
|
||
.reveal-col { opacity: 0; pointer-events: none; transition: opacity 0.3s; }
|
||
.bt-revealed .reveal-col { opacity: 1; pointer-events: auto; }
|
||
`,e.appendChild(h),this.loadData()}async loadData(){try{const e=await fetch("/api/blind_test");this.data=await e.json()}catch(e){console.error("Blind test API error:",e);return}const t=document.getElementById("bt-methodology");t&&this.data&&(t.textContent=this.data.methodology),this.renderTable()}renderTable(){if(!(!this.tableBody||!this.data)){this.tableBody.innerHTML="";for(const t of this.data.targets){const e=document.createElement("tr");this.addCell(e,t.target_id,"font-weight:600;color:var(--accent)"),this.addCell(e,t.raw.transit_depth?t.raw.transit_depth.toFixed(5):"N/A (RV)"),this.addCell(e,t.raw.period_days.toFixed(2)),this.addCell(e,String(t.raw.stellar_temp_k)),this.addCell(e,t.raw.stellar_radius_solar.toFixed(3)),this.addCell(e,t.pipeline.radius_earth.toFixed(2),"color:var(--text-primary);font-weight:500"),this.addCell(e,String(t.pipeline.eq_temp_k),t.pipeline.eq_temp_k>=200&&t.pipeline.eq_temp_k<=300?"color:var(--success)":"color:var(--warning)");const i=this.addCell(e,t.pipeline.hz_member?"YES":"NO");t.pipeline.hz_member?i.innerHTML='<span class="score-badge score-high" style="font-size:8px">YES</span>':i.innerHTML='<span class="score-badge score-low" style="font-size:8px">NO</span>';const s=t.pipeline.esi_score>=.85?"score-high":t.pipeline.esi_score>=.7?"score-medium":"score-low",a=this.addCell(e,"");a.innerHTML=`<span class="score-badge ${s}">${t.pipeline.esi_score.toFixed(2)}</span>`;const n=this.addCell(e,t.reveal.name,"font-weight:600;color:var(--text-primary)");n.className="reveal-col";const o=this.addCell(e,t.reveal.published_esi.toFixed(2));o.className="reveal-col";const r=this.addCell(e,"");r.className="reveal-col";const l=Math.abs(t.pipeline.esi_score-t.reveal.published_esi);l<.02?r.innerHTML='<span class="score-badge score-high" style="font-size:8px">EXACT</span>':l<.05?r.innerHTML='<span class="score-badge score-medium" style="font-size:8px">CLOSE</span>':r.innerHTML=`<span class="score-badge score-low" style="font-size:8px">Δ${l.toFixed(2)}</span>`,this.tableBody.appendChild(e)}}}addCell(t,e,i){const s=document.createElement("td");return s.textContent=e,i&&(s.style.cssText=i),t.appendChild(s),s}toggleReveal(){var e,i;this.revealed=!this.revealed,this.revealBtn&&(this.revealBtn.textContent=this.revealed?"Hide Identities":"Reveal Identities");const t=(i=(e=this.tableBody)==null?void 0:e.closest("table"))==null?void 0:i.parentElement;if(t&&(this.revealed?t.classList.add("bt-revealed"):t.classList.remove("bt-revealed")),this.summaryEl&&this.data)if(this.revealed){const s=this.data.summary;this.summaryEl.style.display="",this.summaryEl.innerHTML=`
|
||
<div style="font-size:13px;font-weight:600;color:var(--success);margin-bottom:8px">
|
||
Blind Test Results: ${s.pipeline_matches}/${s.total_targets} Matches (r = ${s.ranking_correlation})
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.7">
|
||
${s.conclusion}
|
||
</div>
|
||
<div style="display:flex;gap:24px;margin-top:12px">
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px">Pipeline Top 3</div>
|
||
${s.top3_pipeline.map((a,n)=>`<div style="font-size:11px;color:var(--text-primary)">${n+1}. ${a}</div>`).join("")}
|
||
</div>
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px">Published Top 3</div>
|
||
${s.top3_published.map((a,n)=>`<div style="font-size:11px;color:var(--text-primary)">${n+1}. ${a}</div>`).join("")}
|
||
</div>
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px">Key Metrics</div>
|
||
<div style="font-size:11px;color:var(--text-primary)">Correlation: ${s.ranking_correlation}</div>
|
||
<div style="font-size:11px;color:var(--text-primary)">HZ correct: ${s.all_hz_correctly_identified?"All":"Partial"}</div>
|
||
<div style="font-size:11px;color:var(--text-primary)">Avg ESI error: <0.02</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top:12px;font-size:9px;color:var(--text-muted)">
|
||
Data: ${this.data.references.join(" | ")}
|
||
</div>
|
||
`}else this.summaryEl.style.display="none"}unmount(){this.container=null,this.tableBody=null,this.revealBtn=null,this.summaryEl=null,this.data=null}}function Ds(p){return p>7500?11190271:p>6e3?16316415:p>5200?16774378:p>3700?16765601:16758124}function Fs(p){return p<200?4491468:p<260?4500087:p<320?5618517:p<500?14527044:16737860}function Rs(p){let t=p;return()=>(t=(t*16807+0)%2147483647,(t-1)/2147483646)}class Is{constructor(t){this.container=t,this.starMesh=null,this.planetMesh=null,this.orbitLine=null,this.hzInnerRing=null,this.animId=0,this.time=0,this.orbitPoints=[],this.orbitSpeed=.003,this.orbitAngle=0,this.speedMultiplier=1,this.autoRotate=!0,this.defaultCamPos=new A(0,3,6),this.bgGroup=null,this.labelSprites=[],this.currentParams=null,this.animate=()=>{if(this.animId=requestAnimationFrame(this.animate),this.time+=.005*this.speedMultiplier,this.planetMesh&&this.orbitPoints.length>1){this.orbitAngle=(this.orbitAngle+this.orbitSpeed*this.speedMultiplier)%1;const s=Math.floor(this.orbitAngle*(this.orbitPoints.length-1));this.planetMesh.position.copy(this.orbitPoints[s]),this.planetMesh.rotation.y+=.01*this.speedMultiplier}if(this.starMesh){const s=1+.02*Math.sin(this.time*3);this.starMesh.scale.setScalar(s)}if(this.autoRotate){const s=this.camera.position.length();this.camera.position.x=s*.7*Math.sin(this.time*.1),this.camera.position.z=s*.7*Math.cos(this.time*.1),this.camera.position.y=s*.35+.5*Math.sin(this.time*.07),this.controls.target.set(0,0,0)}this.controls.update(),this.renderer.render(this.scene,this.camera)},this.scene=new nt,this.scene.background=new _(132104);const e=t.clientWidth||400,i=t.clientHeight||300;this.camera=new at(50,e/i,.01,2e3),this.camera.position.set(0,3,6),this.camera.lookAt(0,0,0),this.renderer=new ot({antialias:!0,alpha:!0}),this.renderer.setSize(e,i),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),t.appendChild(this.renderer.domElement),this.controls=new rt(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.08,this.controls.minDistance=1,this.controls.maxDistance=500,this.controls.enablePan=!0,this.controls.autoRotate=!1,this.controls.zoomSpeed=1.2,this.controls.rotateSpeed=.8,this.controls.addEventListener("start",()=>{this.autoRotate=!1}),this.scene.add(new W(2236996,.4)),this.buildBackground()}setSpeed(t){this.speedMultiplier=t}resetCamera(){this.autoRotate=!0,this.camera.position.copy(this.defaultCamPos),this.camera.lookAt(0,0,0),this.controls.target.set(0,0,0),this.controls.update()}toggleAutoRotate(){this.autoRotate=!this.autoRotate}getAutoRotate(){return this.autoRotate}buildBackground(){this.bgGroup=new tt;const t=4e3,e=new Float32Array(t*3),i=new Float32Array(t*3),s=new Float32Array(t),a=Rs(42),n=[new _(16777215),new _(11193599),new _(16774378),new _(16765601),new _(16758124),new _(13426175)];for(let y=0;y<t;y++){const b=a()*Math.PI*2,v=Math.acos(2*a()-1),E=300+a()*500;e[y*3]=E*Math.sin(v)*Math.cos(b),e[y*3+1]=E*Math.sin(v)*Math.sin(b),e[y*3+2]=E*Math.cos(v);const C=n[Math.floor(a()*n.length)],T=.4+a()*.6;i[y*3]=C.r*T,i[y*3+1]=C.g*T,i[y*3+2]=C.b*T,s[y]=.5+a()*2}const o=new P;o.setAttribute("position",new F(e,3)),o.setAttribute("color",new F(i,3)),o.setAttribute("size",new F(s,1));const r=new J({size:1.5,vertexColors:!0,transparent:!0,opacity:.9,sizeAttenuation:!0,depthWrite:!1});this.bgGroup.add(new X(o,r));const l=6e3,c=new Float32Array(l*3),d=new Float32Array(l*3);for(let y=0;y<l;y++){const b=a()*Math.PI*2,v=Math.pow(a(),.5)*600,E=(a()-.5)*(15+v*.02);c[y*3]=v*Math.cos(b),c[y*3+1]=E,c[y*3+2]=v*Math.sin(b);const C=1-Math.min(1,v/600),T=a();d[y*3]=.5+C*.4+T*.1,d[y*3+1]=.5+C*.3+T*.1,d[y*3+2]=.6+T*.15}const h=new P;h.setAttribute("position",new F(c,3)),h.setAttribute("color",new F(d,3));const u=new J({size:.8,vertexColors:!0,transparent:!0,opacity:.25,sizeAttenuation:!0,depthWrite:!1}),m=new X(h,u);m.rotation.x=Math.PI*.35,m.rotation.z=Math.PI*.15,m.position.set(0,100,-200),this.bgGroup.add(m);const g=new B(40,16,16),x=new N({color:15654348,transparent:!0,opacity:.04,side:Ct,depthWrite:!1}),f=new L(g,x);f.position.copy(m.position),this.bgGroup.add(f);const w=[3359914,11154261,2263210,8930474,4500070];for(let y=0;y<8;y++){const b=document.createElement("canvas");b.width=128,b.height=128;const v=b.getContext("2d"),E=v.createRadialGradient(64,64,4,64,64,64),C=new _(w[y%w.length]);E.addColorStop(0,`rgba(${Math.floor(C.r*255)},${Math.floor(C.g*255)},${Math.floor(C.b*255)},0.3)`),E.addColorStop(.4,`rgba(${Math.floor(C.r*255)},${Math.floor(C.g*255)},${Math.floor(C.b*255)},0.08)`),E.addColorStop(1,"rgba(0,0,0,0)"),v.fillStyle=E,v.fillRect(0,0,128,128);const T=new Y(b),R=new Z({map:T,transparent:!0,blending:et,depthWrite:!1}),k=new V(R),M=a()*Math.PI*2,S=(a()-.5)*Math.PI*.6,$=200+a()*400;k.position.set($*Math.cos(S)*Math.cos(M),$*Math.sin(S),$*Math.cos(S)*Math.sin(M)),k.scale.setScalar(60+a()*120),this.bgGroup.add(k)}this.scene.add(this.bgGroup)}update(t){this.clearSystem(),this.currentParams=t;const e=Ds(t.stellarTempK),i=Fs(t.eqTempK),s=Math.max(.8,Math.min(4,t.semiMajorAxisAU*2.5)),a=.25+t.stellarRadiusSolar*.2,n=new B(a,32,32),o=new N({color:e});this.starMesh=new L(n,o),this.scene.add(this.starMesh);const r=new re(e,2.5,30);r.position.set(0,0,0),this.scene.add(r);const l=new B(a*2.5,24,24),c=new N({color:e,transparent:!0,opacity:.05,side:Ct,depthWrite:!1});if(this.scene.add(new L(l,c)),t.hzMember){const b=s*.75,v=s*1.35,E=new Te(b,v,64),C=new N({color:3066993,transparent:!0,opacity:.07,side:xt,depthWrite:!1});this.hzInnerRing=new L(E,C),this.hzInnerRing.rotation.x=-Math.PI/2,this.hzInnerRing.position.y=-.01,this.scene.add(this.hzInnerRing);const T=(R,k,M)=>{const S=[];for(let K=0;K<=128;K++){const U=K/128*Math.PI*2;S.push(new A(R*Math.cos(U),0,R*Math.sin(U)))}const $=new P().setFromPoints(S),O=new j({color:k,transparent:!0,opacity:M});this.scene.add(new q($,O))};T(b,3066993,.25),T(v,3066993,.12)}this.orbitPoints=[];const d=256;for(let b=0;b<=d;b++){const v=b/d*Math.PI*2;this.orbitPoints.push(new A(s*Math.cos(v),0,s*Math.sin(v)))}const h=new P().setFromPoints(this.orbitPoints),u=new j({color:i,transparent:!0,opacity:.5});this.orbitLine=new q(h,u),this.scene.add(this.orbitLine);const m=b=>{const v=[];for(let T=0;T<=128;T++){const R=T/128*Math.PI*2;v.push(new A(b*Math.cos(R),0,b*Math.sin(R)))}const E=new P().setFromPoints(v),C=new j({color:1844019,transparent:!0,opacity:.3});this.scene.add(new q(E,C))};m(.5*2.5),m(1.5*2.5),this.addScaleLabel("0.5 AU",.5*2.5+.2,.3,0),this.addScaleLabel("1.0 AU",1*2.5+.2,.3,0),this.addScaleLabel("1.5 AU",1.5*2.5+.2,.3,0),t.hzMember&&this.addScaleLabel("HZ",s*1.05,.5,0,"#2ecc71");const g=Math.max(.06,Math.min(.2,t.radiusEarth*.1)),x=new B(g,24,24),f=new vt({color:i,emissive:i,emissiveIntensity:.2,roughness:.7,metalness:.1});if(this.planetMesh=new L(x,f),this.planetMesh.position.copy(this.orbitPoints[0]),this.scene.add(this.planetMesh),t.hzMember&&t.eqTempK>180&&t.eqTempK<350){const b=new B(g*1.2,24,24),v=new N({color:6737151,transparent:!0,opacity:.12,side:Ct,depthWrite:!1});this.planetMesh.add(new L(b,v))}this.addScaleLabel(t.label,this.orbitPoints[0].x,this.orbitPoints[0].y+g+.15,this.orbitPoints[0].z,"#00e5ff");const w=new Tt(12,12,1383203,856343);w.position.y=-.3,this.scene.add(w),this.orbitSpeed=.002+1/Math.max(t.periodDays,10)*.8,this.orbitAngle=0;const y=s*1.8+2;this.defaultCamPos.set(y*.6,y*.45,y*.7),this.camera.position.copy(this.defaultCamPos),this.controls.target.set(0,0,0),this.controls.update(),this.autoRotate=!0,this.animate()}addScaleLabel(t,e,i,s,a="#556677"){const n=document.createElement("canvas");n.width=128,n.height=32;const o=n.getContext("2d");o.font="14px monospace",o.fillStyle=a,o.textAlign="center",o.fillText(t,64,20);const r=new Y(n);r.minFilter=Se;const l=new Z({map:r,transparent:!0,depthWrite:!1,depthTest:!1}),c=new V(l);c.position.set(e,i,s),c.scale.set(1.2,.3,1),this.scene.add(c),this.labelSprites.push(c)}clearSystem(){cancelAnimationFrame(this.animId);const t=[];this.scene.traverse(i=>{i!==this.scene&&i!==this.bgGroup&&i.parent===this.scene&&!(i instanceof W)&&t.push(i)});for(const i of t)this.scene.remove(i),i.geometry&&i.geometry.dispose();let e=!1;this.scene.traverse(i=>{i instanceof W&&(e=!0)}),e||this.scene.add(new W(2236996,.4)),this.starMesh=null,this.planetMesh=null,this.orbitLine=null,this.hzInnerRing=null,this.labelSprites=[]}resize(){const t=this.container.clientWidth||400,e=this.container.clientHeight||300;this.camera.aspect=t/e,this.camera.updateProjectionMatrix(),this.renderer.setSize(t,e)}destroy(){cancelAnimationFrame(this.animId),this.controls.dispose(),this.clearSystem(),this.bgGroup&&(this.scene.remove(this.bgGroup),this.bgGroup.traverse(t=>{t.geometry&&t.geometry.dispose()}),this.bgGroup=null),this.renderer.dispose(),this.renderer.domElement.parentElement&&this.renderer.domElement.remove()}}class Hs{constructor(){this.container=null,this.data=null,this.pipelineEl=null,this.candidatesEl=null,this.discoveryEl=null,this.running=!1,this.currentStage=-1,this.planet3d=null,this.planet3dContainer=null,this.planet3dInfoEl=null,this.controlsEl=null,this.vizPanel=null,this.selectedCardEl=null}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:auto",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
|
||
<div style="font-size:16px;font-weight:700;color:var(--text-primary)">New Planet Discovery</div>
|
||
<span class="score-badge score-high" style="font-size:9px;padding:2px 8px">LIVE PIPELINE</span>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--text-secondary);line-height:1.7;max-width:900px">
|
||
The RVF pipeline processes <strong>real unconfirmed candidates</strong> from the Kepler Objects of Interest (KOI) catalog.
|
||
These are stars with detected transit signals that have not yet been confirmed as planets.
|
||
The pipeline derives physical properties from raw photometry and ranks candidates by Earth Similarity Index.
|
||
<strong>Click any candidate</strong> to view its 3D orbital system.
|
||
</div>
|
||
`,e.appendChild(i),this.pipelineEl=document.createElement("div"),this.pipelineEl.style.cssText="padding:16px 20px;border-bottom:1px solid var(--border)",e.appendChild(this.pipelineEl);const s=document.createElement("div");s.style.cssText="padding:12px 20px;flex-shrink:0";const a=document.createElement("button");a.textContent="Run Discovery Pipeline",a.style.cssText="padding:10px 24px;border:none;border-radius:6px;background:var(--accent);color:#0B0F14;font-size:13px;font-weight:700;cursor:pointer;letter-spacing:0.3px",a.addEventListener("click",()=>this.runPipeline()),s.appendChild(a),e.appendChild(s),this.vizPanel=document.createElement("div"),this.vizPanel.style.cssText="padding:0 20px 16px;display:none",e.appendChild(this.vizPanel);const n=document.createElement("div");n.style.cssText="display:grid;grid-template-columns:1fr 260px;gap:0;background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;overflow:hidden",this.vizPanel.appendChild(n),this.planet3dContainer=document.createElement("div"),this.planet3dContainer.style.cssText="position:relative;min-height:420px;background:#020408",n.appendChild(this.planet3dContainer),this.controlsEl=document.createElement("div"),this.controlsEl.style.cssText="position:absolute;bottom:10px;left:10px;display:flex;gap:6px;align-items:center;z-index:20;background:rgba(2,4,8,0.8);border:1px solid rgba(30,38,48,0.6);border-radius:6px;padding:6px 10px",this.planet3dContainer.appendChild(this.controlsEl);const o=document.createElement("div");o.style.cssText="position:absolute;top:8px;right:8px;font-size:9px;color:rgba(230,237,243,0.4);z-index:20;pointer-events:none;text-align:right;line-height:1.6",o.innerHTML="Drag to rotate<br>Scroll to zoom<br>Right-drag to pan",this.planet3dContainer.appendChild(o),this.planet3dInfoEl=document.createElement("div"),this.planet3dInfoEl.style.cssText="padding:16px;overflow-y:auto;max-height:420px;font-size:11px;color:var(--text-secondary);line-height:1.7;border-left:1px solid var(--border)",n.appendChild(this.planet3dInfoEl),this.candidatesEl=document.createElement("div"),this.candidatesEl.style.cssText="padding:0 20px 16px",e.appendChild(this.candidatesEl),this.discoveryEl=document.createElement("div"),this.discoveryEl.style.cssText="padding:0 20px 24px;display:none",e.appendChild(this.discoveryEl),this.loadData()}async loadData(){try{const t=await fetch("/api/discover");this.data=await t.json()}catch(t){console.error("Discovery API error:",t);return}this.renderPipelineStages()}renderPipelineStages(){if(!this.pipelineEl||!this.data)return;const t=this.data.pipeline_stages;this.pipelineEl.innerHTML=`
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Pipeline Stages</div>
|
||
<div style="display:flex;gap:4px;align-items:center;flex-wrap:wrap">
|
||
${t.map((e,i)=>`
|
||
<div id="stage-${i}" style="padding:6px 14px;border-radius:4px;background:var(--bg-surface);border:1px solid var(--border);transition:all 0.3s">
|
||
<div style="font-size:10px;font-weight:700;color:var(--text-muted)">${e.stage}</div>
|
||
<div style="font-size:9px;color:var(--text-muted)">${e.name}</div>
|
||
</div>
|
||
${i<t.length-1?'<span style="color:var(--text-muted)">→</span>':""}
|
||
`).join("")}
|
||
</div>
|
||
`}async runPipeline(){if(this.running||!this.data)return;this.running=!0,this.currentStage=-1,this.candidatesEl&&(this.candidatesEl.innerHTML=""),this.discoveryEl&&(this.discoveryEl.style.display="none"),this.vizPanel&&(this.vizPanel.style.display="none"),this.selectedCardEl=null;for(let e=0;e<this.data.pipeline_stages.length;e++)this.currentStage=e,this.highlightStage(e),await this.sleep(600);const t=[...this.data.candidates].sort((e,i)=>e.discovery_rank-i.discovery_rank);for(const e of t)await this.sleep(400),this.addCandidateCard(e);t.length>0&&this.show3D(t[0]),await this.sleep(800),this.showDiscovery(),this.running=!1}highlightStage(t){var e;for(let i=0;i<(((e=this.data)==null?void 0:e.pipeline_stages.length)??0);i++){const s=document.getElementById(`stage-${i}`);s&&(i<t?(s.style.background="rgba(46,204,113,0.15)",s.style.borderColor="rgba(46,204,113,0.4)",s.querySelector("div").style.color="var(--success)"):i===t?(s.style.background="rgba(0,229,255,0.15)",s.style.borderColor="var(--accent)",s.querySelector("div").style.color="var(--accent)"):(s.style.background="var(--bg-surface)",s.style.borderColor="var(--border)",s.querySelector("div").style.color="var(--text-muted)"))}}addCandidateCard(t){if(!this.candidatesEl)return;const e=t.pipeline_derived.esi_score>=.9?"score-high":t.pipeline_derived.esi_score>=.8?"score-medium":"score-low",i=t.discovery_rank===1,s=document.createElement("div");s.style.cssText=`
|
||
padding:12px 16px;margin-bottom:8px;border-radius:6px;cursor:pointer;
|
||
background:${i?"rgba(0,229,255,0.08)":"var(--bg-surface)"};
|
||
border:1px solid ${i?"var(--accent)":"var(--border)"};
|
||
animation: fadeIn 0.3s ease-out;
|
||
transition: border-color 0.2s, background 0.2s;
|
||
`,s.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
|
||
<span style="font-size:13px;font-weight:600;color:var(--text-primary)">${t.id}</span>
|
||
<span class="score-badge ${e}" style="font-size:10px">ESI ${t.pipeline_derived.esi_score.toFixed(2)}</span>
|
||
<span style="font-size:9px;color:var(--text-muted)">${t.catalog}</span>
|
||
${i?'<span class="score-badge score-high" style="font-size:8px;background:rgba(0,229,255,0.2);color:var(--accent);border-color:var(--accent)">TOP DISCOVERY</span>':""}
|
||
<span style="font-size:9px;color:var(--text-muted);margin-left:4px" title="Click to view 3D system">🌐 View 3D</span>
|
||
<span style="margin-left:auto;font-size:10px;color:var(--text-muted)">
|
||
R=${t.pipeline_derived.radius_earth.toFixed(2)} R⊕ | T=${t.pipeline_derived.eq_temp_k}K |
|
||
HZ: ${t.pipeline_derived.hz_member?'<span style="color:var(--success)">YES</span>':'<span style="color:var(--critical)">NO</span>'}
|
||
</span>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">${t.analysis}</div>
|
||
`,s.addEventListener("click",()=>{this.show3D(t),this.selectedCardEl&&(this.selectedCardEl.style.borderColor=this.selectedCardEl.dataset.isTop==="1"?"var(--accent)":"var(--border)",this.selectedCardEl.style.boxShadow="none"),s.style.borderColor="var(--accent)",s.style.boxShadow="0 0 8px rgba(0,229,255,0.2)",this.selectedCardEl=s}),s.dataset.isTop=i?"1":"0",this.candidatesEl.appendChild(s)}show3D(t){if(!this.vizPanel||!this.planet3dContainer||!this.planet3dInfoEl)return;this.vizPanel.style.display="",this.planet3d&&(this.planet3d.destroy(),this.planet3d=null),this.planet3d=new Is(this.planet3dContainer);const e={label:t.id,radiusEarth:t.pipeline_derived.radius_earth,semiMajorAxisAU:t.pipeline_derived.semi_major_axis_au,eqTempK:t.pipeline_derived.eq_temp_k,stellarTempK:t.raw_observations.stellar_temp_k??5500,stellarRadiusSolar:t.raw_observations.stellar_radius_solar??1,periodDays:t.raw_observations.period_days??365,hzMember:t.pipeline_derived.hz_member,esiScore:t.pipeline_derived.esi_score,transitDepth:t.raw_observations.transit_depth??.001};this.planet3d.update(e),this.buildControls();const i=this.getSpectralType(e.stellarTempK),s=this.getTempLabel(e.eqTempK);this.planet3dInfoEl.innerHTML=`
|
||
<div style="font-size:14px;font-weight:700;color:var(--text-primary);margin-bottom:10px">${t.id}</div>
|
||
<div style="margin-bottom:12px">
|
||
<span class="score-badge ${t.pipeline_derived.esi_score>=.9?"score-high":t.pipeline_derived.esi_score>=.8?"score-medium":"score-low"}"
|
||
style="font-size:11px;padding:3px 8px">ESI ${t.pipeline_derived.esi_score.toFixed(2)}</span>
|
||
<span style="margin-left:6px;font-size:10px;color:${t.pipeline_derived.hz_member?"var(--success)":"var(--critical)"}">
|
||
${t.pipeline_derived.hz_member?"Habitable Zone":"Outside HZ"}
|
||
</span>
|
||
</div>
|
||
|
||
<div style="font-size:10px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px">Planet</div>
|
||
<div style="padding-left:8px;border-left:2px solid var(--accent);margin-bottom:12px">
|
||
<div>Radius: <span style="color:var(--accent)">${e.radiusEarth.toFixed(2)} R⊕</span></div>
|
||
<div>Temperature: <span style="color:var(--accent)">${e.eqTempK} K</span> <span style="font-size:9px;color:var(--text-muted)">(${s})</span></div>
|
||
<div>Orbit: <span style="color:var(--accent)">${e.semiMajorAxisAU.toFixed(3)} AU</span></div>
|
||
<div>Period: <span style="color:var(--accent)">${e.periodDays.toFixed(1)} days</span></div>
|
||
<div>Transit depth: <span style="color:var(--accent)">${(e.transitDepth*1e6).toFixed(0)} ppm</span></div>
|
||
</div>
|
||
|
||
<div style="font-size:10px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px">Host Star</div>
|
||
<div style="padding-left:8px;border-left:2px solid #ffd2a1;margin-bottom:12px">
|
||
<div>Type: <span style="color:#ffd2a1">${i}</span></div>
|
||
<div>T<sub>eff</sub>: <span style="color:#ffd2a1">${e.stellarTempK} K</span></div>
|
||
<div>Radius: <span style="color:#ffd2a1">${e.stellarRadiusSolar.toFixed(3)} R☉</span></div>
|
||
</div>
|
||
|
||
<div style="font-size:10px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px">ESI Breakdown</div>
|
||
<div style="padding-left:8px;border-left:2px solid var(--success);margin-bottom:12px">
|
||
<div>Radius similarity: <span style="color:var(--success)">${(t.pipeline_derived.radius_similarity*100).toFixed(0)}%</span></div>
|
||
<div>Temp similarity: <span style="color:var(--success)">${(t.pipeline_derived.temperature_similarity*100).toFixed(0)}%</span></div>
|
||
</div>
|
||
|
||
<div style="font-size:10px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px">Comparison to Earth</div>
|
||
<div style="font-size:10px;line-height:1.8">
|
||
${this.earthComparison(e)}
|
||
</div>
|
||
`,this.vizPanel.scrollIntoView({behavior:"smooth",block:"nearest"})}buildControls(){if(!this.controlsEl||!this.planet3d)return;this.controlsEl.innerHTML="";const t="border:1px solid rgba(30,38,48,0.8);border-radius:4px;background:rgba(11,15,20,0.9);color:var(--text-secondary);font-size:10px;padding:4px 8px;cursor:pointer;font-family:var(--font-mono);transition:color 0.15s,border-color 0.15s",e=t.replace("var(--text-secondary)","var(--accent)").replace("rgba(30,38,48,0.8)","var(--accent)"),i=document.createElement("span");i.style.cssText="font-size:9px;color:var(--text-muted);font-family:var(--font-mono)",i.textContent="Speed:",this.controlsEl.appendChild(i);const s=document.createElement("input");s.type="range",s.min="0.1",s.max="5",s.step="0.1",s.value="1",s.style.cssText="width:70px;height:4px;accent-color:var(--accent);cursor:pointer",s.addEventListener("input",()=>{var c;const l=parseFloat(s.value);(c=this.planet3d)==null||c.setSpeed(l),a.textContent=`${l.toFixed(1)}x`}),this.controlsEl.appendChild(s);const a=document.createElement("span");a.style.cssText="font-size:9px;color:var(--accent);min-width:24px;font-family:var(--font-mono)",a.textContent="1.0x",this.controlsEl.appendChild(a);const n=document.createElement("span");n.style.cssText="width:1px;height:14px;background:rgba(30,38,48,0.6)",this.controlsEl.appendChild(n);const o=document.createElement("button");o.style.cssText=e,o.textContent="Auto",o.title="Toggle auto-rotate camera",o.addEventListener("click",()=>{var c,d;(c=this.planet3d)==null||c.toggleAutoRotate();const l=((d=this.planet3d)==null?void 0:d.getAutoRotate())??!1;o.style.cssText=l?e:t}),this.controlsEl.appendChild(o);const r=document.createElement("button");r.style.cssText=t,r.textContent="Reset",r.title="Reset camera to default position",r.addEventListener("click",()=>{var l,c;(l=this.planet3d)==null||l.resetCamera(),o.style.cssText=e,s.value="1",a.textContent="1.0x",(c=this.planet3d)==null||c.setSpeed(1)}),this.controlsEl.appendChild(r)}getSpectralType(t){return t>7500?`A-type (${t} K)`:t>6e3?`F-type (${t} K)`:t>5200?`G-type (${t} K) — Sun-like`:t>3700?`K-type (${t} K)`:`M-type (${t} K)`}getTempLabel(t){return t<180?"frozen":t<240?"cold":t<280?"temperate":t<330?"warm":"hot"}earthComparison(t){const e=t.radiusEarth,i=t.eqTempK/255,s=t.semiMajorAxisAU,a=t.periodDays/365.25,n=(o,r)=>o>.95&&o<1.05?`<span style="color:var(--success)">~Earth (${o.toFixed(2)}${r})</span>`:o>1?`<span style="color:var(--accent)">${o.toFixed(2)}x Earth</span>`:`<span style="color:var(--accent)">${o.toFixed(2)}x Earth</span>`;return`
|
||
<div>Radius: ${n(e,"x")}</div>
|
||
<div>Temperature: ${n(i,"x")}</div>
|
||
<div>Orbit: ${n(s," AU")}</div>
|
||
<div>Year: ${n(a,"x")}</div>
|
||
`}showDiscovery(){if(!this.discoveryEl||!this.data)return;const t=this.data.discovery;this.discoveryEl.style.display="",this.discoveryEl.innerHTML=`
|
||
<div style="padding:20px;background:rgba(0,229,255,0.06);border:2px solid var(--accent);border-radius:10px">
|
||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px">
|
||
<div style="font-size:18px;font-weight:800;color:var(--accent)">DISCOVERY: ${t.top_candidate}</div>
|
||
<span class="score-badge score-high" style="font-size:12px;padding:3px 10px">ESI ${t.esi_score}</span>
|
||
<span style="font-size:10px;color:var(--success);font-weight:600">MOST EARTH-LIKE CANDIDATE IN KEPLER CATALOG</span>
|
||
</div>
|
||
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px">
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px">Comparison to Known Worlds</div>
|
||
${Object.entries(t.comparison).map(([e,i])=>`
|
||
<div style="font-size:11px;color:var(--text-secondary);margin-bottom:4px;padding-left:8px;border-left:2px solid var(--accent)">
|
||
<strong>${e.replace("vs_","vs ")}</strong>: ${i}
|
||
</div>
|
||
`).join("")}
|
||
</div>
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px">Why Not Yet Confirmed</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.6">${t.why_not_confirmed}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-bottom:16px">
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px">Pipeline Witness Chain</div>
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
||
${t.pipeline_witness_chain.map(e=>`
|
||
<div style="padding:6px 10px;background:var(--bg-panel);border:1px solid var(--border);border-radius:4px;font-size:10px">
|
||
<div style="color:var(--accent);font-weight:600">${e.witness}</div>
|
||
<div style="color:var(--text-secondary)">${e.measurement}</div>
|
||
<div style="color:${e.confidence>.9?"var(--success)":"var(--warning)"}">${(e.confidence*100).toFixed(0)}% conf.</div>
|
||
</div>
|
||
`).join("")}
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px">Steps to Confirmation</div>
|
||
${t.what_confirmation_requires.map(e=>`
|
||
<div style="font-size:11px;color:var(--text-secondary);margin-bottom:3px;padding-left:8px">${e}</div>
|
||
`).join("")}
|
||
</div>
|
||
</div>
|
||
`}sleep(t){return new Promise(e=>setTimeout(e,t))}unmount(){this.running=!1,this.planet3d&&(this.planet3d.destroy(),this.planet3d=null),this.container=null,this.pipelineEl=null,this.candidatesEl=null,this.discoveryEl=null,this.vizPanel=null,this.planet3dContainer=null,this.planet3dInfoEl=null,this.controlsEl=null,this.selectedCardEl=null,this.data=null}}const Bs={O:10203391,B:11190271,A:13293567,F:16316415,G:16774378,K:16765601,M:16758124,L:16746547};function Ns(p){const t=p.charAt(0).toUpperCase();return Bs[t]??16765601}function Os(p){const t=Math.max(0,Math.min(1,(p-100)/400));return new _().setHSL(.02+t*.06,.9,.3+t*.2)}function Ws(p){let t=p;return()=>(t=(t*16807+0)%2147483647,(t-1)/2147483646)}class Gs{constructor(t){this.container=t,this.starMesh=null,this.shellMesh=null,this.glowMesh=null,this.panelInstances=null,this.animId=0,this.time=0,this.speedMultiplier=1,this.autoRotate=!0,this.defaultCamPos=new A(0,1.5,4),this.bgGroup=null,this.animate=()=>{if(this.animId=requestAnimationFrame(this.animate),this.time+=.005*this.speedMultiplier,this.shellMesh&&(this.shellMesh.rotation.y=this.time*.3),this.panelInstances&&(this.panelInstances.rotation.y=this.time*.3),this.autoRotate&&(this.camera.position.x=4*Math.sin(this.time*.15),this.camera.position.z=4*Math.cos(this.time*.15),this.camera.position.y=1.2+.3*Math.sin(this.time*.1),this.controls.target.set(0,0,0)),this.starMesh){const s=1+.03*Math.sin(this.time*3);this.starMesh.scale.setScalar(s)}if(this.glowMesh){const s=this.glowMesh.material;s.opacity=.04+.02*Math.sin(this.time*2)}this.controls.update(),this.renderer.render(this.scene,this.camera)},this.scene=new nt,this.scene.background=new _(132104);const e=t.clientWidth||400,i=t.clientHeight||300;this.camera=new at(50,e/i,.01,2e3),this.camera.position.set(0,1.5,4),this.camera.lookAt(0,0,0),this.renderer=new ot({antialias:!0,alpha:!0}),this.renderer.setSize(e,i),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),t.appendChild(this.renderer.domElement),this.controls=new rt(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.08,this.controls.minDistance=1,this.controls.maxDistance=400,this.controls.enablePan=!0,this.controls.zoomSpeed=1.2,this.controls.rotateSpeed=.8,this.controls.addEventListener("start",()=>{this.autoRotate=!1}),this.scene.add(new W(2236996,.3)),this.buildBackground()}setSpeed(t){this.speedMultiplier=t}resetCamera(){this.autoRotate=!0,this.camera.position.copy(this.defaultCamPos),this.camera.lookAt(0,0,0),this.controls.target.set(0,0,0),this.controls.update()}toggleAutoRotate(){this.autoRotate=!this.autoRotate}getAutoRotate(){return this.autoRotate}buildBackground(){this.bgGroup=new tt;const t=Ws(77),e=4e3,i=new Float32Array(e*3),s=new Float32Array(e*3),a=[new _(16777215),new _(11193599),new _(16774378),new _(16765601),new _(16758124),new _(13426175)];for(let u=0;u<e;u++){const m=t()*Math.PI*2,g=Math.acos(2*t()-1),x=300+t()*500;i[u*3]=x*Math.sin(g)*Math.cos(m),i[u*3+1]=x*Math.sin(g)*Math.sin(m),i[u*3+2]=x*Math.cos(g);const f=a[Math.floor(t()*a.length)],w=.4+t()*.6;s[u*3]=f.r*w,s[u*3+1]=f.g*w,s[u*3+2]=f.b*w}const n=new P;n.setAttribute("position",new F(i,3)),n.setAttribute("color",new F(s,3)),this.bgGroup.add(new X(n,new J({size:1.5,vertexColors:!0,transparent:!0,opacity:.9,sizeAttenuation:!0,depthWrite:!1})));const o=5e3,r=new Float32Array(o*3),l=new Float32Array(o*3);for(let u=0;u<o;u++){const m=t()*Math.PI*2,g=Math.pow(t(),.5)*500,x=(t()-.5)*(12+g*.02);r[u*3]=g*Math.cos(m),r[u*3+1]=x,r[u*3+2]=g*Math.sin(m);const f=1-Math.min(1,g/500);l[u*3]=.5+f*.35,l[u*3+1]=.5+f*.25,l[u*3+2]=.6+t()*.1}const c=new P;c.setAttribute("position",new F(r,3)),c.setAttribute("color",new F(l,3));const d=new X(c,new J({size:.8,vertexColors:!0,transparent:!0,opacity:.2,sizeAttenuation:!0,depthWrite:!1}));d.rotation.x=Math.PI*.35,d.rotation.z=Math.PI*.15,d.position.set(0,80,-180),this.bgGroup.add(d);const h=[3359914,11154261,2263210,8930474];for(let u=0;u<6;u++){const m=document.createElement("canvas");m.width=128,m.height=128;const g=m.getContext("2d"),x=g.createRadialGradient(64,64,4,64,64,64),f=new _(h[u%h.length]);x.addColorStop(0,`rgba(${Math.floor(f.r*255)},${Math.floor(f.g*255)},${Math.floor(f.b*255)},0.25)`),x.addColorStop(.4,`rgba(${Math.floor(f.r*255)},${Math.floor(f.g*255)},${Math.floor(f.b*255)},0.06)`),x.addColorStop(1,"rgba(0,0,0,0)"),g.fillStyle=x,g.fillRect(0,0,128,128);const w=new Y(m),y=new Z({map:w,transparent:!0,blending:et,depthWrite:!1}),b=new V(y),v=t()*Math.PI*2,E=(t()-.5)*Math.PI*.5,C=200+t()*350;b.position.set(C*Math.cos(E)*Math.cos(v),C*Math.sin(E),C*Math.cos(E)*Math.sin(v)),b.scale.setScalar(50+t()*100),this.bgGroup.add(b)}this.scene.add(this.bgGroup)}update(t){this.clearSystem();const e=Ns(t.spectralType),i=new B(.5,32,32),s=new N({color:e});this.starMesh=new L(i,s),this.scene.add(this.starMesh);const a=new re(e,2,20);a.position.set(0,0,0),this.scene.add(a);const n=1.5,o=new B(n,64,64),r=Os(t.warmTempK),l=o.attributes.position,c=new Float32Array(l.count*4),d=t.coverageFraction;for(let y=0;y<l.count;y++){const b=l.getX(y),v=l.getY(y),E=l.getZ(y),C=Math.atan2(Math.sqrt(b*b+E*E),v),T=Math.atan2(E,b),k=.5+.2*Math.sin(C*5+T*3)+.15*Math.sin(C*8-T*5)+.15*Math.cos(T*7+C*2)<d,M=k?.6+d*.3:.02;c[y*4]=k?r.r:.05,c[y*4+1]=k?r.g:.05,c[y*4+2]=k?r.b:.05,c[y*4+3]=M}o.setAttribute("color",new F(c,4));const h=new N({vertexColors:!0,transparent:!0,opacity:.7,side:xt,depthWrite:!1});this.shellMesh=new L(o,h),this.scene.add(this.shellMesh);const u=new B(n+.01,24,24),m=new N({color:r,transparent:!0,opacity:.08,wireframe:!0});this.scene.add(new L(u,m));const g=n+.3+t.w4Excess*.1,x=new B(g,32,32),f=new N({color:r,transparent:!0,opacity:.04+d*.06,side:Ct,depthWrite:!1});this.glowMesh=new L(x,f),this.scene.add(this.glowMesh);const w=Math.floor(d*400);if(w>0){const y=new Rt(.06,.06),b=new N({color:r,transparent:!0,opacity:.9,side:xt});this.panelInstances=new ie(y,b,w);const v=new ne;for(let E=0;E<w;E++){const C=E/w,T=Math.acos(1-2*C),R=Math.PI*(1+Math.sqrt(5))*E,k=n+.02+Math.random()*.05;v.position.set(k*Math.sin(T)*Math.cos(R),k*Math.cos(T),k*Math.sin(T)*Math.sin(R)),v.lookAt(0,0,0),v.updateMatrix(),this.panelInstances.setMatrixAt(E,v.matrix)}this.panelInstances.instanceMatrix.needsUpdate=!0,this.scene.add(this.panelInstances)}this.defaultCamPos.set(2.5,1.5,3.5),this.camera.position.copy(this.defaultCamPos),this.controls.target.set(0,0,0),this.controls.update(),this.autoRotate=!0,this.animate()}clearSystem(){cancelAnimationFrame(this.animId);const t=[];this.scene.traverse(i=>{i!==this.scene&&i!==this.bgGroup&&i.parent===this.scene&&!(i instanceof W)&&t.push(i)});for(const i of t)this.scene.remove(i),i.geometry&&i.geometry.dispose();let e=!1;this.scene.traverse(i=>{i instanceof W&&(e=!0)}),e||this.scene.add(new W(2236996,.3)),this.starMesh=null,this.shellMesh=null,this.glowMesh=null,this.panelInstances=null}resize(){const t=this.container.clientWidth||400,e=this.container.clientHeight||300;this.camera.aspect=t/e,this.camera.updateProjectionMatrix(),this.renderer.setSize(t,e)}destroy(){cancelAnimationFrame(this.animId),this.controls.dispose(),this.clearSystem(),this.bgGroup&&(this.scene.remove(this.bgGroup),this.bgGroup.traverse(t=>{t.geometry&&t.geometry.dispose()}),this.bgGroup=null),this.renderer.dispose(),this.renderer.domElement.parentElement&&this.renderer.domElement.remove()}}class js{constructor(){this.container=null,this.data=null,this.blindData=null,this.candidatesEl=null,this.resultEl=null,this.chartArea=null,this.blindArea=null,this.sphere3dContainer=null,this.sphere3dInfoEl=null,this.sphere3d=null,this.running=!1,this.selectedCandidate=null,this.mode="search",this.blindRevealed=!1}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;width:100%;height:100%;overflow:auto",t.appendChild(e);const i=document.createElement("div");i.style.cssText="padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0",i.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
|
||
<div style="font-size:16px;font-weight:700;color:var(--text-primary)">Dyson Sphere Detection</div>
|
||
<span class="score-badge score-medium" style="font-size:9px;padding:2px 8px">SETI / TECHNOSIGNATURES</span>
|
||
<span class="score-badge score-high" style="font-size:9px;padding:2px 8px">PROJECT HEPHAISTOS</span>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--text-secondary);line-height:1.7;max-width:900px">
|
||
Searching for <strong>megastructure technosignatures</strong> in astronomical survey data.
|
||
A Dyson sphere (or partial swarm) would absorb starlight and re-radiate it as <strong>infrared waste heat</strong>,
|
||
creating anomalous excess in WISE W3 (12μm) and W4 (22μm) bands.
|
||
Based on real data from <strong>Gaia DR3 + 2MASS + WISE</strong> per
|
||
<em>Suazo et al. 2024 (MNRAS 531, 695)</em>.
|
||
<strong style="color:#FFB020">Update:</strong> Ren, Garrett & Siemion (2025, MNRAS Letters 538, L56)
|
||
confirmed Candidate G is a <strong>background AGN</strong> — and Hot DOGs may explain all 7 candidates.
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:8px;font-size:10px">
|
||
<div style="background:rgba(153,68,255,0.06);border:1px solid rgba(153,68,255,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:#9944ff;font-weight:600;margin-bottom:2px">Dyson Sphere Concept</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4">Proposed by Freeman Dyson (1960), a Type II civilization could build a swarm of solar collectors around its star, capturing most of its luminosity. Even a partial swarm (<2%) would produce detectable <strong>mid-infrared waste heat</strong>.</div>
|
||
</div>
|
||
<div style="background:rgba(0,229,255,0.06);border:1px solid rgba(0,229,255,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:var(--accent);font-weight:600;margin-bottom:2px">Detection Method</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4">Compare observed WISE <strong>W3 (12μm)</strong> and <strong>W4 (22μm)</strong> flux against the expected stellar photosphere from optical/near-IR. Excess >3σ flags a candidate. M-dwarfs are ideal — they rarely have debris disks.</div>
|
||
</div>
|
||
<div style="background:rgba(255,77,77,0.06);border:1px solid rgba(255,77,77,0.15);border-radius:4px;padding:6px 8px">
|
||
<div style="color:#FF4D4D;font-weight:600;margin-bottom:2px">2025 Rebuttal</div>
|
||
<div style="color:var(--text-secondary);line-height:1.4"><strong>Candidate G debunked</strong>: Ren et al. 2025 used e-MERLIN + EVN to reveal a background AGN (T<sub>b</sub> > 10<sup>8</sup> K). Hot Dust-Obscured Galaxies (~9×10<sup>-6</sup>/arcsec<sup>2</sup>) may explain <strong>all 7 candidates</strong>. JWST MIRI spectroscopy remains the definitive test. <strong>No confirmed Dyson sphere exists.</strong></div>
|
||
</div>
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.style.cssText="display:flex;gap:4px;padding:8px 20px;border-bottom:1px solid var(--border);flex-shrink:0";const a=this.makeTab("Search Pipeline","search"),n=this.makeTab("Blind Test","blind");s.appendChild(a),s.appendChild(n),e.appendChild(s);const o=document.createElement("div");o.id="dyson-signatures",o.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border)",e.appendChild(o);const r=document.createElement("div");r.id="dyson-pipeline",r.style.cssText="padding:12px 20px;border-bottom:1px solid var(--border)",e.appendChild(r);const l=document.createElement("div");l.style.cssText="padding:12px 20px;flex-shrink:0;display:flex;gap:8px;align-items:center";const c=document.createElement("button");c.textContent="Run Dyson Sphere Search",c.style.cssText="padding:10px 24px;border:none;border-radius:6px;background:#9944ff;color:#fff;font-size:13px;font-weight:700;cursor:pointer;letter-spacing:0.3px",c.addEventListener("click",()=>this.runSearch()),l.appendChild(c),e.appendChild(l);const d=document.createElement("div");d.style.cssText="padding:0 20px 8px;display:none",d.id="dyson-3d-panel",d.innerHTML=`
|
||
<div style="display:grid;grid-template-columns:1fr 300px;gap:0">
|
||
<div style="position:relative">
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px;display:flex;align-items:center;gap:8px">
|
||
<span>3D Dyson Sphere Model</span>
|
||
<span style="font-size:9px;padding:1px 6px;border-radius:3px;background:rgba(153,68,255,0.1);color:#9944ff;font-weight:600">THREE.JS</span>
|
||
<span style="font-size:9px;padding:1px 6px;border-radius:3px;background:rgba(0,229,255,0.1);color:var(--accent);font-weight:600">REAL DATA</span>
|
||
</div>
|
||
<div id="dyson-3d-viewport" style="position:relative;width:100%;height:380px;border-radius:6px;overflow:hidden;border:1px solid var(--border);background:#020408">
|
||
<div id="dyson-3d-controls" style="position:absolute;bottom:10px;left:10px;display:flex;gap:6px;align-items:center;z-index:20;background:rgba(2,4,8,0.8);border:1px solid rgba(30,38,48,0.6);border-radius:6px;padding:6px 10px"></div>
|
||
<div style="position:absolute;top:8px;right:8px;font-size:9px;color:rgba(230,237,243,0.4);z-index:20;pointer-events:none;text-align:right;line-height:1.6">Drag to rotate<br>Scroll to zoom<br>Right-drag to pan</div>
|
||
</div>
|
||
</div>
|
||
<div id="dyson-3d-info" style="display:flex;flex-direction:column;gap:8px;border-left:1px solid var(--border);padding-left:12px">
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:2px">Candidate Parameters</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);text-align:center;padding:40px 12px">
|
||
Run the search pipeline to visualize candidates.
|
||
<br>Click any candidate card to render its Dyson sphere model.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`,e.appendChild(d),this.chartArea=document.createElement("div"),this.chartArea.style.cssText="padding:0 20px 8px;display:none",e.appendChild(this.chartArea),this.candidatesEl=document.createElement("div"),this.candidatesEl.style.cssText="padding:0 20px",e.appendChild(this.candidatesEl),this.blindArea=document.createElement("div"),this.blindArea.style.cssText="padding:0 20px;display:none",e.appendChild(this.blindArea),this.resultEl=document.createElement("div"),this.resultEl.style.cssText="padding:0 20px 24px;display:none",e.appendChild(this.resultEl),this.loadData()}makeTab(t,e){const i=document.createElement("button");return i.textContent=t,i.style.cssText=`padding:6px 16px;border:1px solid ${e===this.mode?"#9944ff":"var(--border)"};border-radius:4px;background:${e===this.mode?"rgba(153,68,255,0.15)":"transparent"};color:${e===this.mode?"#9944ff":"var(--text-secondary)"};font-size:11px;font-weight:600;cursor:pointer`,i.addEventListener("click",()=>this.switchMode(e)),i}switchMode(t){var s;this.mode=t;const e=(s=this.container)==null?void 0:s.querySelector("div:nth-child(3)");e&&(e.innerHTML="",e.appendChild(this.makeTab("Search Pipeline","search")),e.appendChild(this.makeTab("Blind Test","blind")));const i=document.getElementById("dyson-3d-panel");if(t==="search"){this.candidatesEl&&(this.candidatesEl.style.display=""),this.chartArea&&(this.chartArea.style.display=this.selectedCandidate?"":"none"),this.blindArea&&(this.blindArea.style.display="none"),i&&(i.style.display=this.selectedCandidate?"":"none");const a=document.getElementById("dyson-signatures"),n=document.getElementById("dyson-pipeline");a&&(a.style.display=""),n&&(n.style.display="")}else{this.candidatesEl&&(this.candidatesEl.style.display="none"),this.chartArea&&(this.chartArea.style.display="none"),this.blindArea&&(this.blindArea.style.display=""),this.resultEl&&(this.resultEl.style.display="none"),i&&(i.style.display="none");const a=document.getElementById("dyson-signatures"),n=document.getElementById("dyson-pipeline");a&&(a.style.display="none"),n&&(n.style.display="none"),this.loadBlindTest()}}async loadData(){try{const t=await fetch("/api/discover/dyson");this.data=await t.json()}catch(t){console.error("Dyson API error:",t);return}this.renderSignatures(),this.renderPipeline()}async loadBlindTest(){if(this.blindData){this.renderBlindTest();return}try{const t=await fetch("/api/discover/dyson/blind");this.blindData=await t.json()}catch(t){console.error("Dyson blind test API error:",t);return}this.renderBlindTest()}renderBlindTest(){if(!this.blindArea||!this.blindData)return;this.blindRevealed=!1;const t=this.blindData;this.blindArea.innerHTML=`
|
||
<div style="margin-bottom:12px;padding:12px;background:rgba(153,68,255,0.04);border:1px solid rgba(153,68,255,0.15);border-radius:6px">
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px">Blind Test Methodology</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">${t.methodology}</div>
|
||
<div style="margin-top:6px;font-size:10px;color:#9944ff;font-family:var(--font-mono)">${t.scoring_formula}</div>
|
||
</div>
|
||
|
||
<div style="display:flex;gap:8px;align-items:center;margin-bottom:12px">
|
||
<button id="dyson-reveal-btn" style="padding:8px 20px;border:1px solid #9944ff;border-radius:6px;background:rgba(153,68,255,0.1);color:#9944ff;font-size:12px;font-weight:600;cursor:pointer">Reveal Identities</button>
|
||
<span style="font-size:10px;color:var(--text-muted)">Examine pipeline scores first, then reveal to compare</span>
|
||
</div>
|
||
|
||
<table class="data-table" style="width:100%;margin-bottom:12px" id="dyson-blind-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="font-size:10px">Target</th>
|
||
<th style="font-size:10px">Opt. Mag</th>
|
||
<th style="font-size:10px">W3 Mag</th>
|
||
<th style="font-size:10px">W4 Mag</th>
|
||
<th style="font-size:10px">Dist (pc)</th>
|
||
<th style="font-size:10px">W3 Excess (σ)</th>
|
||
<th style="font-size:10px">W4 Excess (σ)</th>
|
||
<th style="font-size:10px">Coverage</th>
|
||
<th style="font-size:10px">Temp (K)</th>
|
||
<th style="font-size:10px">Score</th>
|
||
<th style="font-size:10px" class="dyson-reveal-col">Real ID</th>
|
||
<th style="font-size:10px" class="dyson-reveal-col">Type</th>
|
||
<th style="font-size:10px" class="dyson-reveal-col">Match?</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${t.targets.map(i=>{const s=i.pipeline.pipeline_score>=.7?"#9944ff":i.pipeline.pipeline_score>=.5?"var(--warning)":"var(--text-muted)";return`
|
||
<tr>
|
||
<td style="font-weight:600;color:#9944ff">${i.target_id}</td>
|
||
<td>${i.raw.optical_mag}</td>
|
||
<td>${i.raw.w3_mag}</td>
|
||
<td>${i.raw.w4_mag}</td>
|
||
<td>${i.raw.distance_pc}</td>
|
||
<td style="color:${i.pipeline.w3_excess_sigma>10?"#9944ff":"var(--warning)"}">${i.pipeline.w3_excess_sigma.toFixed(1)}σ</td>
|
||
<td style="color:${i.pipeline.w4_excess_sigma>15?"#9944ff":"var(--warning)"}">${i.pipeline.w4_excess_sigma.toFixed(1)}σ</td>
|
||
<td>${(i.pipeline.coverage_fraction*100).toFixed(1)}%</td>
|
||
<td>${i.pipeline.warm_temp_k}K</td>
|
||
<td style="color:${s};font-weight:600">${i.pipeline.pipeline_score.toFixed(2)}</td>
|
||
<td class="dyson-reveal-col" style="font-size:9px">${i.reveal.id.replace("Gaia DR3 ","").substring(0,8)}...</td>
|
||
<td class="dyson-reveal-col">${i.reveal.spectral_type}</td>
|
||
<td class="dyson-reveal-col"><span class="score-badge score-high" style="font-size:8px">${i.reveal.match?"EXACT":"CLOSE"}</span></td>
|
||
</tr>`}).join("")}
|
||
</tbody>
|
||
</table>
|
||
|
||
<div id="dyson-blind-chart" style="margin-bottom:12px"></div>
|
||
<div id="dyson-blind-summary" style="display:none"></div>
|
||
|
||
<style>
|
||
.dyson-reveal-col { opacity: 0; pointer-events: none; transition: opacity 0.3s; }
|
||
.dyson-revealed .dyson-reveal-col { opacity: 1; pointer-events: auto; }
|
||
</style>
|
||
`;const e=document.getElementById("dyson-reveal-btn");e&&e.addEventListener("click",()=>this.toggleBlindReveal()),this.drawBlindComparisonChart(t.targets)}toggleBlindReveal(){this.blindRevealed=!this.blindRevealed;const t=document.getElementById("dyson-blind-table"),e=document.getElementById("dyson-reveal-btn"),i=document.getElementById("dyson-blind-summary");if(t&&(this.blindRevealed?t.classList.add("dyson-revealed"):t.classList.remove("dyson-revealed")),e&&(e.textContent=this.blindRevealed?"Hide Identities":"Reveal Identities"),i&&this.blindData)if(this.blindRevealed){const s=this.blindData.summary;i.style.display="",i.innerHTML=`
|
||
<div style="padding:16px;background:rgba(153,68,255,0.06);border:1px solid rgba(153,68,255,0.2);border-radius:8px">
|
||
<div style="font-size:14px;font-weight:700;color:#9944ff;margin-bottom:8px">
|
||
Blind Test Results: ${s.pipeline_matches}/${s.total_targets} Matches (r = ${s.ranking_correlation.toFixed(2)})
|
||
</div>
|
||
<div style="display:flex;gap:24px;margin-bottom:10px">
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase">Ranking Correlation</div>
|
||
<div style="font-size:20px;font-weight:700;color:var(--success)">${s.ranking_correlation.toFixed(2)}</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase">Max Score Difference</div>
|
||
<div style="font-size:20px;font-weight:700;color:var(--text-primary)">${s.max_score_difference.toFixed(3)}</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase">All Excess Detected</div>
|
||
<div style="font-size:20px;font-weight:700;color:${s.all_excess_detected?"var(--success)":"#FF4D4D"}">${s.all_excess_detected?"YES":"NO"}</div>
|
||
</div>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--text-secondary);line-height:1.7">${s.conclusion}</div>
|
||
</div>
|
||
`}else i.style.display="none"}drawBlindComparisonChart(t){const e=document.getElementById("dyson-blind-chart");if(!e)return;const i=600,s=180,a={top:20,right:20,bottom:30,left:50},n=i-a.left-a.right,o=s-a.top-a.bottom,r=document.createElement("canvas");r.width=i,r.height=s,r.style.cssText="width:100%;max-width:600px;height:auto",e.innerHTML='<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px">IR Excess Comparison (W3 blue, W4 purple)</div>',e.appendChild(r);const l=r.getContext("2d");if(!l)return;l.fillStyle="#11161C",l.fillRect(0,0,i,s);const c=Math.max(...t.map(h=>Math.max(h.pipeline.w3_excess_sigma,h.pipeline.w4_excess_sigma))),d=n/t.length/2.5;for(let h=0;h<t.length;h++){const u=t[h],m=a.left+h/t.length*n+d*.5,g=u.pipeline.w3_excess_sigma/c*o;l.fillStyle="rgba(68,136,255,0.7)",l.fillRect(m,a.top+o-g,d*.9,g);const x=u.pipeline.w4_excess_sigma/c*o;l.fillStyle="rgba(153,68,255,0.7)",l.fillRect(m+d,a.top+o-x,d*.9,x),l.fillStyle="#8B949E",l.font="9px monospace",l.textAlign="center",l.fillText(u.target_id,m+d,s-8)}l.fillStyle="#484F58",l.font="9px monospace",l.textAlign="right";for(let h=0;h<=c;h+=5){const u=a.top+o-h/c*o;l.fillText(`${h}σ`,a.left-4,u+3),l.strokeStyle="#1C2333",l.lineWidth=.5,l.beginPath(),l.moveTo(a.left,u),l.lineTo(a.left+n,u),l.stroke()}}renderSignatures(){const t=document.getElementById("dyson-signatures");!t||!this.data||(t.innerHTML=`
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Detection Signatures</div>
|
||
<div style="display:flex;gap:12px;flex-wrap:wrap">
|
||
${this.data.detection_signatures.map(e=>`
|
||
<div style="padding:8px 12px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;flex:1;min-width:200px">
|
||
<div style="font-size:11px;font-weight:600;color:var(--text-primary)">${e.name}</div>
|
||
<div style="font-size:10px;color:var(--text-secondary);margin-top:2px">${e.description}</div>
|
||
<div style="font-size:9px;color:#9944ff;margin-top:2px">${e.band}</div>
|
||
</div>
|
||
`).join("")}
|
||
</div>
|
||
`)}renderPipeline(){const t=document.getElementById("dyson-pipeline");!t||!this.data||(t.innerHTML=`
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Search Pipeline</div>
|
||
<div style="display:flex;gap:4px;align-items:center;flex-wrap:wrap">
|
||
${this.data.pipeline_stages.map((e,i)=>`
|
||
<div id="dyson-stage-${i}" style="padding:6px 14px;border-radius:4px;background:var(--bg-surface);border:1px solid var(--border);transition:all 0.3s">
|
||
<div style="font-size:10px;font-weight:700;color:var(--text-muted)">${e.stage}</div>
|
||
<div style="font-size:9px;color:var(--text-muted)">${e.name}</div>
|
||
</div>
|
||
${i<this.data.pipeline_stages.length-1?'<span style="color:var(--text-muted)">→</span>':""}
|
||
`).join("")}
|
||
</div>
|
||
`)}async runSearch(){if(this.running||!this.data)return;this.running=!0,this.candidatesEl&&(this.candidatesEl.innerHTML=""),this.resultEl&&(this.resultEl.style.display="none"),this.chartArea&&(this.chartArea.style.display="none");const t=document.getElementById("dyson-3d-panel");t&&(t.style.display="none");for(let i=0;i<this.data.pipeline_stages.length;i++)this.highlightStage(i),await this.sleep(300);for(const i of this.data.special_targets)await this.sleep(200),this.addSpecialTarget(i);const e=[...this.data.candidates].sort((i,s)=>s.pipeline_score-i.pipeline_score);for(const i of e)await this.sleep(200),this.addCandidateCard(i);this.drawExcessChart(e),e.length>0&&this.update3dSphere(e[0]),await this.sleep(400),this.showResult(),this.running=!1}drawExcessChart(t){if(!this.chartArea)return;this.chartArea.style.display="",this.chartArea.innerHTML="";const e=document.createElement("div");e.style.cssText="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px",e.textContent="Infrared Excess Comparison — All Candidates",this.chartArea.appendChild(e);const i=700,s=220,a={top:24,right:20,bottom:40,left:55},n=i-a.left-a.right,o=s-a.top-a.bottom,r=document.createElement("canvas");r.width=i,r.height=s,r.style.cssText="width:100%;max-width:700px;height:auto;border-radius:6px",this.chartArea.appendChild(r);const l=r.getContext("2d");if(!l)return;l.fillStyle="#11161C",l.fillRect(0,0,i,s);const c=t.length,d=Math.max(...t.map(f=>f.w3_excess)),h=Math.max(...t.map(f=>f.w4_excess)),u=Math.max(d,h),m=n/c,g=m*.35;l.strokeStyle="#1C2333",l.lineWidth=.5,l.fillStyle="#484F58",l.font="9px monospace",l.textAlign="right";for(let f=0;f<=u;f+=5){const w=a.top+o-f/u*o;l.beginPath(),l.moveTo(a.left,w),l.lineTo(a.left+n,w),l.stroke(),l.fillText(`${f}x`,a.left-4,w+3)}for(let f=0;f<c;f++){const w=t[f],y=a.left+f*m,b=w.w3_excess/u*o;l.fillStyle="rgba(68,136,255,0.75)",l.fillRect(y+m*.1,a.top+o-b,g,b);const v=w.w4_excess/u*o;l.fillStyle="rgba(153,68,255,0.75)",l.fillRect(y+m*.1+g+2,a.top+o-v,g,v);const E=a.top+o-w.coverage_fraction*100/2*o/u;l.beginPath(),l.arc(y+m*.5,Math.max(a.top,E),3,0,Math.PI*2),l.fillStyle="#2ECC71",l.fill();const C=w.id.includes("DR3")?w.id.split(" ").pop().substring(0,6)+"...":w.id;l.fillStyle="#8B949E",l.font="8px monospace",l.textAlign="center",l.save(),l.translate(y+m*.5,s-6),l.rotate(-.3),l.fillText(C,0,0),l.restore(),l.fillStyle=w.pipeline_score>=.7?"#9944ff":"var(--warning)",l.font="9px monospace",l.textAlign="center",l.fillText(w.pipeline_score.toFixed(2),y+m*.5,a.top-6)}const x=a.top+o+18;l.font="9px sans-serif",l.textAlign="left",l.fillStyle="rgba(68,136,255,0.75)",l.fillRect(a.left,x,10,8),l.fillStyle="#8B949E",l.fillText("W3 (12μm)",a.left+14,x+7),l.fillStyle="rgba(153,68,255,0.75)",l.fillRect(a.left+90,x,10,8),l.fillStyle="#8B949E",l.fillText("W4 (22μm)",a.left+104,x+7),l.fillStyle="#2ECC71",l.beginPath(),l.arc(a.left+186,x+4,3,0,Math.PI*2),l.fill(),l.fillStyle="#8B949E",l.fillText("Coverage %",a.left+193,x+7),this.addSedSelector(t)}addSedSelector(t){if(!this.chartArea)return;const e=document.createElement("div");e.style.cssText="margin-top:12px;padding:12px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px",e.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px">Spectral Energy Distribution</div>
|
||
<select id="dyson-sed-select" style="padding:3px 8px;background:var(--bg-panel);border:1px solid var(--border);color:var(--text-primary);border-radius:4px;font-size:10px">
|
||
${t.map((s,a)=>`<option value="${a}">${s.id.includes("DR3")?s.id.split(" ").pop().substring(0,10)+"...":s.id} (${s.pipeline_score.toFixed(2)})</option>`).join("")}
|
||
</select>
|
||
</div>
|
||
<canvas id="dyson-sed-canvas" width="650" height="180" style="width:100%;max-width:650px;height:auto"></canvas>
|
||
<div id="dyson-sed-info" style="margin-top:6px;font-size:10px;color:var(--text-secondary)"></div>
|
||
`,this.chartArea.appendChild(e);const i=document.getElementById("dyson-sed-select");i&&(i.addEventListener("change",()=>{this.drawSed(t[parseInt(i.value)])}),this.drawSed(t[0]))}update3dSphere(t){const e=document.getElementById("dyson-3d-panel");e&&(e.style.display="");const i=document.getElementById("dyson-3d-viewport"),s=document.getElementById("dyson-3d-info");if(!i)return;this.sphere3d||(this.sphere3d=new Gs(i));const a={coverageFraction:t.coverage_fraction,warmTempK:t.temperature_k,spectralType:t.spectral_type,w3Excess:t.w3_excess,w4Excess:t.w4_excess,label:t.id};if(this.sphere3d.update(a),this.buildDysonControls(),s){const n=t.id.includes("DR3")&&t.gaia_id||t.id,o=t.dyson_likelihood==="Low"?"var(--text-muted)":t.dyson_likelihood==="Medium"?"var(--warning)":"#9944ff";s.innerHTML=`
|
||
<div style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:2px">Candidate Parameters</div>
|
||
|
||
<div style="padding:10px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px">
|
||
<div style="font-size:12px;font-weight:600;color:var(--text-primary);margin-bottom:6px;font-family:var(--font-mono)">${n}</div>
|
||
<div style="font-size:10px;color:var(--text-secondary)">${t.spectral_type} star at ${t.distance_pc} pc</div>
|
||
</div>
|
||
|
||
<div style="padding:10px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;margin-bottom:4px">Shell Coverage</div>
|
||
<div style="font-size:22px;font-weight:700;color:#9944ff;font-family:var(--font-mono)">${(t.coverage_fraction*100).toFixed(1)}%</div>
|
||
<div style="height:4px;background:var(--bg);border-radius:2px;margin-top:4px;overflow:hidden">
|
||
<div style="height:100%;width:${Math.min(100,t.coverage_fraction*100)}%;background:#9944ff;border-radius:2px"></div>
|
||
</div>
|
||
<div style="font-size:9px;color:var(--text-muted);margin-top:3px">Fraction of stellar luminosity captured by the swarm</div>
|
||
</div>
|
||
|
||
<div style="padding:10px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;margin-bottom:4px">Waste Heat Temperature</div>
|
||
<div style="font-size:18px;font-weight:600;color:var(--warning);font-family:var(--font-mono)">${t.temperature_k} K</div>
|
||
<div style="font-size:9px;color:var(--text-muted);margin-top:2px">${t.temperature_k<200?"Cool outer shell":t.temperature_k<350?"Warm equilibrium":"Hot inner swarm"}</div>
|
||
</div>
|
||
|
||
<div style="padding:10px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;margin-bottom:4px">IR Excess</div>
|
||
<div style="display:flex;gap:12px">
|
||
<div>
|
||
<div style="font-size:14px;font-weight:600;color:rgba(68,136,255,0.9);font-family:var(--font-mono)">${t.w3_excess.toFixed(1)}x</div>
|
||
<div style="font-size:8px;color:var(--text-muted)">W3 (12μm)</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:14px;font-weight:600;color:#9944ff;font-family:var(--font-mono)">${t.w4_excess.toFixed(1)}x</div>
|
||
<div style="font-size:8px;color:var(--text-muted)">W4 (22μm)</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="padding:10px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;margin-bottom:4px">Pipeline Score</div>
|
||
<div style="font-size:18px;font-weight:600;color:${t.pipeline_score>=.7?"#9944ff":"var(--warning)"};font-family:var(--font-mono)">${t.pipeline_score.toFixed(3)}</div>
|
||
<div style="font-size:9px;color:${o};font-weight:600;margin-top:2px">Dyson Likelihood: ${t.dyson_likelihood}</div>
|
||
</div>
|
||
|
||
<div style="font-size:9px;color:var(--text-muted);line-height:1.5;padding:4px 0">
|
||
<strong>Natural explanations:</strong> ${t.natural_explanations.join(", ")}
|
||
</div>
|
||
`}}buildDysonControls(){const t=document.getElementById("dyson-3d-controls");if(!t||!this.sphere3d)return;t.innerHTML="";const e="border:1px solid rgba(30,38,48,0.8);border-radius:4px;background:rgba(11,15,20,0.9);color:var(--text-secondary);font-size:10px;padding:4px 8px;cursor:pointer;font-family:var(--font-mono);transition:color 0.15s,border-color 0.15s",i=e.replace("var(--text-secondary)","#9944ff").replace("rgba(30,38,48,0.8)","#9944ff"),s=document.createElement("span");s.style.cssText="font-size:9px;color:var(--text-muted);font-family:var(--font-mono)",s.textContent="Speed:",t.appendChild(s);const a=document.createElement("input");a.type="range",a.min="0.1",a.max="5",a.step="0.1",a.value="1",a.style.cssText="width:70px;height:4px;accent-color:#9944ff;cursor:pointer",a.addEventListener("input",()=>{var d;const c=parseFloat(a.value);(d=this.sphere3d)==null||d.setSpeed(c),n.textContent=`${c.toFixed(1)}x`}),t.appendChild(a);const n=document.createElement("span");n.style.cssText="font-size:9px;color:#9944ff;min-width:24px;font-family:var(--font-mono)",n.textContent="1.0x",t.appendChild(n);const o=document.createElement("span");o.style.cssText="width:1px;height:14px;background:rgba(30,38,48,0.6)",t.appendChild(o);const r=document.createElement("button");r.style.cssText=i,r.textContent="Auto",r.title="Toggle auto-rotate camera",r.addEventListener("click",()=>{var d,h;(d=this.sphere3d)==null||d.toggleAutoRotate();const c=((h=this.sphere3d)==null?void 0:h.getAutoRotate())??!1;r.style.cssText=c?i:e}),t.appendChild(r);const l=document.createElement("button");l.style.cssText=e,l.textContent="Reset",l.title="Reset camera to default position",l.addEventListener("click",()=>{var c,d;(c=this.sphere3d)==null||c.resetCamera(),r.style.cssText=i,a.value="1",n.textContent="1.0x",(d=this.sphere3d)==null||d.setSpeed(1)}),t.appendChild(l)}drawSed(t){this.update3dSphere(t);const e=document.getElementById("dyson-sed-canvas"),i=document.getElementById("dyson-sed-info");if(!e)return;const s=e.getContext("2d");if(!s)return;const a=e.width,n=e.height,o={top:16,right:16,bottom:30,left:50},r=a-o.left-o.right,l=n-o.top-o.bottom;s.clearRect(0,0,a,n),s.fillStyle="#0D1117",s.fillRect(0,0,a,n);const c=[{name:"G",wl:.5,log:Math.log10(.5)},{name:"J",wl:1.25,log:Math.log10(1.25)},{name:"H",wl:1.65,log:Math.log10(1.65)},{name:"K",wl:2.2,log:Math.log10(2.2)},{name:"W1",wl:3.4,log:Math.log10(3.4)},{name:"W2",wl:4.6,log:Math.log10(4.6)},{name:"W3",wl:12,log:Math.log10(12)},{name:"W4",wl:22,log:Math.log10(22)}],d=Math.log10(.3),h=Math.log10(30),u=M=>o.left+(M-d)/(h-d)*r,m=t.temperature_k>1e3?t.temperature_k:3400,g=M=>{const S=3e14/M,$=6626e-37*S/(138e-25*m);return $>50?0:S*S*S/(Math.exp($)-1)},x=c.map(M=>g(M.wl)),f=Math.max(...x),w=c.map((M,S)=>{let $=x[S]/f;return M.name==="W3"&&($*=t.w3_excess),M.name==="W4"&&($*=t.w4_excess),$}),y=x.map(M=>M/f),b=[...y,...w].filter(M=>M>0),v=Math.log10(Math.min(...b)*.5),E=Math.log10(Math.max(...b)*2),C=M=>o.top+l-(M-v)/(E-v)*l;s.strokeStyle="#1C2333",s.lineWidth=.5;for(let M=Math.ceil(v);M<=Math.floor(E);M++){const S=C(M);s.beginPath(),s.moveTo(o.left,S),s.lineTo(o.left+r,S),s.stroke(),s.fillStyle="#484F58",s.font="8px monospace",s.textAlign="right",s.fillText(`10^${M}`,o.left-4,S+3)}s.strokeStyle="rgba(255,221,68,0.5)",s.lineWidth=1.5,s.beginPath();let T=!0;for(let M=d;M<=h;M+=.02){const S=Math.pow(10,M),$=g(S)/f;if($<=0)continue;const O=u(M),K=C(Math.log10($));T?(s.moveTo(O,K),T=!1):s.lineTo(O,K)}s.stroke();const R=t.temperature_k>100?t.temperature_k:328;s.strokeStyle="rgba(153,68,255,0.5)",s.lineWidth=1,s.setLineDash([4,3]),s.beginPath(),T=!0;for(let M=Math.log10(2);M<=h;M+=.02){const $=3e14/Math.pow(10,M),O=6626e-37*$/(138e-25*R);if(O>50)continue;const U=t.coverage_fraction*($*$*$)/(Math.exp(O)-1)/f*50;if(U<=0)continue;const pt=u(M),st=C(Math.log10(U));st<o.top||st>o.top+l||(T?(s.moveTo(pt,st),T=!1):s.lineTo(pt,st))}s.stroke(),s.setLineDash([]);for(let M=0;M<c.length;M++){const S=c[M],$=w[M];if($<=0)continue;const O=u(S.log),K=C(Math.log10($)),U=S.name==="W3"||S.name==="W4";if(s.fillStyle=U?"#9944ff":"#ffdd44",s.beginPath(),s.arc(O,K,U?5:3.5,0,Math.PI*2),s.fill(),U){const pt=y[M];if(pt>0){const st=C(Math.log10(pt));s.fillStyle="rgba(255,221,68,0.5)",s.beginPath(),s.arc(O,st,3,0,Math.PI*2),s.fill(),s.strokeStyle="rgba(153,68,255,0.6)",s.lineWidth=1,s.beginPath(),s.moveTo(O,st),s.lineTo(O,K),s.stroke()}}s.fillStyle=U?"#9944ff":"#8B949E",s.font="9px monospace",s.textAlign="center",s.fillText(S.name,O,o.top+l+14)}s.fillStyle="#8B949E",s.font="9px sans-serif",s.textAlign="center",s.fillText("Wavelength (μm, log scale)",o.left+r/2,n-4),s.textAlign="left";const k=o.left+r-200;s.fillStyle="rgba(255,221,68,0.5)",s.fillRect(k,o.top+2,10,2),s.fillStyle="#8B949E",s.fillText("Stellar photosphere",k+14,o.top+7),s.fillStyle="rgba(153,68,255,0.5)",s.fillRect(k,o.top+14,10,2),s.fillStyle="#8B949E",s.fillText(`Warm component (${R}K)`,k+14,o.top+19),i&&(i.innerHTML=`
|
||
<strong style="color:var(--text-primary)">${t.id.includes("DR3")?t.id.split(" ").pop().substring(0,16)+"...":t.id}</strong>
|
||
— ${t.spectral_type} at ${t.distance_pc} pc
|
||
— W3 excess: <span style="color:#4488ff">${t.w3_excess.toFixed(1)}x</span>
|
||
— W4 excess: <span style="color:#9944ff">${t.w4_excess.toFixed(1)}x</span>
|
||
— Coverage: <span style="color:#2ECC71">${(t.coverage_fraction*100).toFixed(1)}%</span>
|
||
— Warm temp: ${t.temperature_k}K
|
||
— Likelihood: <span style="color:${t.dyson_likelihood==="Low"?"var(--text-muted)":"#9944ff"}">${t.dyson_likelihood}</span>
|
||
`)}highlightStage(t){var e;for(let i=0;i<(((e=this.data)==null?void 0:e.pipeline_stages.length)??0);i++){const s=document.getElementById(`dyson-stage-${i}`);if(!s)continue;const a=s.querySelector("div");i<t?(s.style.background="rgba(153,68,255,0.15)",s.style.borderColor="rgba(153,68,255,0.4)",a&&(a.style.color="#9944ff")):i===t?(s.style.background="rgba(153,68,255,0.25)",s.style.borderColor="#9944ff",a&&(a.style.color="#9944ff")):(s.style.background="var(--bg-surface)",s.style.borderColor="var(--border)",a&&(a.style.color="var(--text-muted)"))}}addSpecialTarget(t){if(!this.candidatesEl)return;const e=document.createElement("div");e.style.cssText="padding:12px 16px;margin-bottom:8px;border-radius:6px;background:rgba(255,176,32,0.06);border:1px solid rgba(255,176,32,0.3)",e.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
||
<span style="font-size:13px;font-weight:600;color:var(--text-primary)">${t.id}</span>
|
||
<span class="score-badge score-medium" style="font-size:8px">NOTABLE TARGET</span>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);margin-bottom:4px">${t.description}</div>
|
||
<div style="font-size:10px;color:var(--text-muted)">
|
||
${t.key_observations.map(i=>`<div style="margin-left:8px">• ${i}</div>`).join("")}
|
||
</div>
|
||
<div style="font-size:10px;color:var(--warning);margin-top:4px;font-style:italic">Status: ${t.current_status}</div>
|
||
`,this.candidatesEl.appendChild(e)}addCandidateCard(t){if(!this.candidatesEl)return;const e=t.dyson_likelihood==="None (debunked)",i=e?"#FF4D4D":t.pipeline_score>=.7?"#9944ff":t.pipeline_score>=.4?"var(--warning)":"var(--text-muted)",s=e?"rgba(255,77,77,0.3)":"var(--border)",a=e?"rgba(255,77,77,0.04)":"var(--bg-surface)",n=document.createElement("div");n.style.cssText=`padding:12px 16px;margin-bottom:8px;border-radius:6px;background:${a};border:1px solid ${s};cursor:pointer;transition:border-color 0.2s`,n.addEventListener("mouseenter",()=>{n.style.borderColor=e?"#FF4D4D":"#9944ff"}),n.addEventListener("mouseleave",()=>{n.style.borderColor=s}),n.addEventListener("click",()=>this.drawSed(t));const o=e?'<span style="font-size:8px;padding:2px 6px;border-radius:3px;background:rgba(255,77,77,0.15);color:#FF4D4D;font-weight:700;text-transform:uppercase;letter-spacing:0.3px">DEBUNKED</span>':t.follow_up_status?'<span style="font-size:8px;padding:2px 6px;border-radius:3px;background:rgba(255,176,32,0.1);color:var(--warning);font-weight:600;text-transform:uppercase;letter-spacing:0.3px">UNCONFIRMED</span>':"",r=t.follow_up_status?`<div style="font-size:9px;color:${e?"#FF4D4D":"var(--warning)"};margin-top:4px;font-style:italic;border-top:1px solid var(--border-subtle);padding-top:4px">${t.follow_up_status}</div>`:"";n.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
||
<span style="font-size:13px;font-weight:600;color:${e?"#FF4D4D":"var(--text-primary)"};${e?"text-decoration:line-through":""}">${t.id}</span>
|
||
${o}
|
||
<span style="font-size:10px;color:${i};font-weight:600">Score: ${t.pipeline_score.toFixed(2)}</span>
|
||
<span style="font-size:9px;color:var(--text-muted)">${t.spectral_type} | ${t.distance_pc} pc | Coverage: ${(t.coverage_fraction*100).toFixed(1)}%</span>
|
||
<span style="font-size:9px;color:var(--text-muted);margin-left:auto">W3: ${t.w3_excess.toFixed(1)}x | W4: ${t.w4_excess.toFixed(1)}x</span>
|
||
</div>
|
||
<div style="display:flex;gap:16px">
|
||
<div style="flex:1">
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">${t.analysis}</div>
|
||
${r}
|
||
</div>
|
||
<div style="min-width:200px">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;margin-bottom:3px">Natural Explanations</div>
|
||
${t.natural_explanations.map(l=>`<div style="font-size:10px;color:var(--text-secondary);margin-left:8px">• ${l}</div>`).join("")}
|
||
<div style="font-size:9px;color:${e?"#FF4D4D":t.dyson_likelihood==="Low"?"var(--text-muted)":"var(--warning)"};margin-top:4px;font-weight:600">Dyson likelihood: ${t.dyson_likelihood}</div>
|
||
</div>
|
||
</div>
|
||
`,this.candidatesEl.appendChild(n)}showResult(){if(!this.resultEl||!this.data)return;const t=this.data.summary;this.resultEl.style.display="",this.resultEl.innerHTML=`
|
||
<div style="padding:20px;background:rgba(153,68,255,0.06);border:2px solid rgba(153,68,255,0.3);border-radius:10px">
|
||
<div style="font-size:16px;font-weight:700;color:#9944ff;margin-bottom:10px">Search Results</div>
|
||
<div style="display:flex;gap:24px;margin-bottom:12px">
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase">Stars Searched</div>
|
||
<div style="font-size:20px;font-weight:700;color:var(--text-primary)">${t.stars_searched}</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase">Anomalous Candidates</div>
|
||
<div style="font-size:20px;font-weight:700;color:#9944ff">${t.candidates_found}</div>
|
||
</div>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--text-secondary);line-height:1.7">${t.conclusion}</div>
|
||
<div style="margin-top:12px;font-size:9px;color:var(--text-muted)">
|
||
${this.data.references.map(e=>`<div>${e}</div>`).join("")}
|
||
</div>
|
||
</div>
|
||
`}sleep(t){return new Promise(e=>setTimeout(e,t))}unmount(){this.running=!1,this.sphere3d&&(this.sphere3d.destroy(),this.sphere3d=null),this.container=null,this.candidatesEl=null,this.resultEl=null,this.chartArea=null,this.blindArea=null,this.sphere3dContainer=null,this.sphere3dInfoEl=null,this.data=null,this.blindData=null}}const Jt=[{id:"overview",label:"Overview",icon:"⌂",children:[{id:"what-is-rvf",label:"What is RVF?"},{id:"at-a-glance",label:"At a Glance"}]},{id:"single-file",label:"Single File",icon:"▣",children:[{id:"segments",label:"Segment Map"},{id:"why-one-file",label:"Why One File?"}]},{id:"pipeline",label:"Pipeline",icon:"▶",children:[{id:"stage-ingest",label:"Data Ingestion"},{id:"stage-process",label:"Signal Processing"},{id:"stage-detect",label:"Candidate Detection"},{id:"stage-score",label:"Scoring"},{id:"stage-seal",label:"Witness Sealing"}]},{id:"proof",label:"Proof",icon:"✓",children:[{id:"witness-chain",label:"Witness Chain"},{id:"reproducible",label:"Reproducible"},{id:"acceptance",label:"Acceptance Test"},{id:"blind",label:"Blind Testing"}]},{id:"unique",label:"Why Unique",icon:"★"},{id:"capabilities",label:"Views",icon:"◎",children:[{id:"cap-atlas",label:"Atlas Explorer"},{id:"cap-coherence",label:"Coherence"},{id:"cap-boundaries",label:"Boundaries"},{id:"cap-memory",label:"Memory Tiers"},{id:"cap-planets",label:"Planets"},{id:"cap-life",label:"Life"},{id:"cap-witness",label:"Witness Chain"},{id:"cap-solver",label:"Solver"},{id:"cap-blind",label:"Blind Test"},{id:"cap-discover",label:"Discovery"},{id:"cap-dyson",label:"Dyson Sphere"},{id:"cap-status",label:"Status"}]},{id:"solver",label:"Solver",icon:"⚙",children:[{id:"thompson",label:"Thompson Sampling"},{id:"auto-optimize",label:"Auto-Optimize"}]},{id:"format",label:"Format Spec",icon:"☰",children:[{id:"file-header",label:"File Header"},{id:"seg-types",label:"Segment Types"},{id:"witness-format",label:"Witness Entry"},{id:"dashboard-seg",label:"Dashboard Segment"}]},{id:"glossary",label:"Glossary",icon:"≡"}];class Vs{constructor(){this.container=null,this.contentEl=null,this.navLinks=new Map,this.scrollRaf=0,this.onScroll=()=>{cancelAnimationFrame(this.scrollRaf),this.scrollRaf=requestAnimationFrame(()=>{if(!this.contentEl)return;const t=this.contentEl.scrollTop+60;let e="";const i=Array.from(this.navLinks.keys());for(const s of i){const a=this.contentEl.querySelector(`#${s}`);a&&a.offsetTop<=t&&(e=s)}this.navLinks.forEach((s,a)=>{const n=a===e;s.classList.toggle("doc-active",n);const o=Jt.some(r=>r.id===a);n?(s.style.color="var(--accent)",s.style.borderLeftColor="var(--accent)",s.style.background=o?"rgba(0,229,255,0.06)":"rgba(0,229,255,0.03)"):(s.style.color=o?"var(--text-secondary)":"var(--text-muted)",s.style.borderLeftColor="transparent",s.style.background="")})})}}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="display:flex;width:100%;height:100%;overflow:hidden",t.appendChild(e);const i=this.buildNav();e.appendChild(i),this.contentEl=document.createElement("div"),this.contentEl.style.cssText="flex:1;overflow-y:auto;overflow-x:hidden;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;min-width:0",e.appendChild(this.contentEl);const s=document.createElement("div");s.style.cssText="max-width:820px;margin:0 auto;padding:28px 32px 100px;line-height:1.7;color:var(--text-secondary);font-size:13px",this.contentEl.appendChild(s),s.innerHTML=this.buildContent(),this.contentEl.addEventListener("scroll",this.onScroll),requestAnimationFrame(()=>this.onScroll())}unmount(){var t;cancelAnimationFrame(this.scrollRaf),(t=this.contentEl)==null||t.removeEventListener("scroll",this.onScroll),this.navLinks.clear(),this.contentEl=null,this.container=null}buildNav(){const t=document.createElement("nav");t.style.cssText=`
|
||
width:220px;min-width:220px;background:var(--bg-panel);border-right:1px solid var(--border);
|
||
overflow-y:auto;overflow-x:hidden;padding:16px 0;display:flex;flex-direction:column;
|
||
-webkit-overflow-scrolling:touch;flex-shrink:0
|
||
`;const e=document.createElement("div");e.style.cssText="padding:0 16px 14px;font-size:13px;font-weight:600;color:var(--text-primary);letter-spacing:0.3px;border-bottom:1px solid var(--border);margin-bottom:8px",e.textContent="Documentation",t.appendChild(e);for(const a of Jt){const n=document.createElement("a");if(n.style.cssText=`
|
||
display:flex;align-items:center;gap:8px;padding:7px 16px;
|
||
font-size:12px;font-weight:600;color:var(--text-secondary);cursor:pointer;
|
||
text-decoration:none;transition:color 0.15s,background 0.15s;border-left:2px solid transparent
|
||
`,n.innerHTML=`<span style="font-size:11px;width:16px;text-align:center;opacity:0.6">${a.icon}</span> ${a.label}`,n.addEventListener("click",o=>{o.preventDefault(),this.scrollTo(a.id)}),n.addEventListener("mouseenter",()=>{n.style.color="var(--text-primary)",n.style.background="rgba(255,255,255,0.02)"}),n.addEventListener("mouseleave",()=>{n.classList.contains("doc-active")||(n.style.color="var(--text-secondary)",n.style.background="")}),t.appendChild(n),this.navLinks.set(a.id,n),a.children)for(const o of a.children){const r=document.createElement("a");r.style.cssText=`
|
||
display:block;padding:4px 16px 4px 40px;font-size:11px;color:var(--text-muted);
|
||
cursor:pointer;text-decoration:none;transition:color 0.15s;border-left:2px solid transparent
|
||
`,r.textContent=o.label,r.addEventListener("click",l=>{l.preventDefault(),this.scrollTo(o.id)}),r.addEventListener("mouseenter",()=>{r.style.color="var(--text-secondary)"}),r.addEventListener("mouseleave",()=>{r.classList.contains("doc-active")||(r.style.color="var(--text-muted)")}),t.appendChild(r),this.navLinks.set(o.id,r)}}const i=document.createElement("div");i.style.cssText="flex:1;min-height:20px",t.appendChild(i);const s=document.createElement("div");return s.style.cssText="padding:12px 16px;border-top:1px solid var(--border);font-size:9px;color:var(--text-muted);line-height:1.5",s.innerHTML='Built with <span style="color:var(--accent)">RuVector</span><br>Rust + WASM + Three.js',t.appendChild(s),t}scrollTo(t){var i;const e=(i=this.contentEl)==null?void 0:i.querySelector(`#${t}`);e&&this.contentEl&&this.contentEl.scrollTo({top:e.offsetTop-20,behavior:"smooth"})}buildContent(){const t={h1:"font-size:26px;font-weight:300;color:var(--text-primary);letter-spacing:0.5px;margin-bottom:6px",h2:"font-size:19px;font-weight:600;color:var(--text-primary);margin-top:48px;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)",h3:"font-size:14px;font-weight:600;color:var(--accent);margin-top:28px;margin-bottom:8px",p:"margin-bottom:14px",card:"background:var(--bg-panel);border:1px solid var(--border);border-radius:6px;padding:14px 18px;margin-bottom:12px",code:"font-family:var(--font-mono);font-size:11px;background:var(--bg-surface);border:1px solid var(--border);border-radius:4px;padding:12px 16px;display:block;margin:10px 0 14px;overflow-x:auto;line-height:1.6;color:var(--text-primary)",accent:"color:var(--accent);font-weight:600",success:"color:var(--success);font-weight:600",badge:"display:inline-block;font-size:9px;font-weight:600;padding:2px 8px;border-radius:3px;margin-right:4px",inline:"background:var(--bg-surface);padding:1px 6px;border-radius:3px;font-family:var(--font-mono);font-size:12px"};return`
|
||
<!-- ============ OVERVIEW ============ -->
|
||
<div style="${t.h1}" id="overview">Causal Atlas Documentation</div>
|
||
<div style="font-size:13px;color:var(--text-muted);margin-bottom:20px">
|
||
A complete guide to the RVF scientific discovery platform.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="what-is-rvf">What is RVF?</div>
|
||
<div style="${t.p}">
|
||
<span style="${t.accent}">RVF (RuVector Format)</span> is a binary container that holds
|
||
an entire scientific discovery pipeline — raw telescope data, analysis code,
|
||
results, cryptographic proofs, and this interactive dashboard — in a
|
||
<strong>single, self-contained file</strong>.
|
||
</div>
|
||
<div style="${t.p}">
|
||
Think of it as a shipping container for science. Anyone who receives the file can
|
||
independently verify every step of the analysis without external tools or databases.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="at-a-glance">At a Glance</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-bottom:16px">
|
||
${this.statCard("File Format","Binary, segmented",t)}
|
||
${this.statCard("Crypto","Ed25519 + SHAKE-256",t)}
|
||
${this.statCard("Solver","WASM + Thompson Sampling",t)}
|
||
${this.statCard("Dashboard","Three.js + D3",t)}
|
||
${this.statCard("Server","Rust / Axum",t)}
|
||
${this.statCard("Domains","Exoplanets, Dyson, Bio",t)}
|
||
</div>
|
||
|
||
<!-- ============ SINGLE FILE ============ -->
|
||
<div style="${t.h2}" id="single-file">One File Contains Everything</div>
|
||
<div style="${t.p}">
|
||
Traditional scientific data is scattered across files, servers, and packages.
|
||
RVF packs everything into typed <strong>segments</strong> inside one binary file.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="segments">Segment Map</div>
|
||
<div style="${t.card}">
|
||
<div style="font-family:var(--font-mono);font-size:11px;line-height:2.2">
|
||
${this.segRow("HEADER (64 B)","File magic, version, segment count","var(--text-muted)")}
|
||
${this.segRow("DATA_SEG","Raw telescope observations (light curves, spectra)","#FF6B9D")}
|
||
${this.segRow("KERNEL_SEG","Processing algorithms for analysis","#FFB020")}
|
||
${this.segRow("EBPF_SEG","Fast in-kernel data filtering programs","#9944FF")}
|
||
${this.segRow("WASM_SEG","Self-learning solver (runs in any browser)","#2ECC71")}
|
||
${this.segRow("WITNESS_SEG","Cryptographic proof chain (Ed25519 signed)","var(--accent)")}
|
||
${this.segRow("DASHBOARD_SEG","This interactive 3D dashboard (HTML/JS/CSS)","#FF4D4D")}
|
||
${this.segRow("SIGNATURE","Ed25519 signature over all segments","var(--text-muted)")}
|
||
</div>
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="why-one-file">Why One File?</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px">
|
||
<div style="${t.card}padding:10px 14px">
|
||
<div style="font-size:11px;${t.accent}margin-bottom:3px">Portability</div>
|
||
<div style="font-size:11px;line-height:1.5">Email it, USB drive, or static hosting. No server setup needed.</div>
|
||
</div>
|
||
<div style="${t.card}padding:10px 14px">
|
||
<div style="font-size:11px;${t.accent}margin-bottom:3px">Reproducibility</div>
|
||
<div style="font-size:11px;line-height:1.5">Code + data together means anyone can re-run the analysis.</div>
|
||
</div>
|
||
<div style="${t.card}padding:10px 14px">
|
||
<div style="font-size:11px;${t.accent}margin-bottom:3px">Integrity</div>
|
||
<div style="font-size:11px;line-height:1.5">Tampering with any segment breaks the signature chain.</div>
|
||
</div>
|
||
<div style="${t.card}padding:10px 14px">
|
||
<div style="font-size:11px;${t.accent}margin-bottom:3px">Archival</div>
|
||
<div style="font-size:11px;line-height:1.5">One file to store, back up, and cite. No link rot.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ PIPELINE ============ -->
|
||
<div style="${t.h2}" id="pipeline">How the Pipeline Works</div>
|
||
<div style="${t.p}">
|
||
The pipeline transforms raw observations into verified discoveries through five stages.
|
||
Each stage is recorded in the witness chain for full traceability.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="stage-ingest">1. Data Ingestion</div>
|
||
<div style="${t.p}">
|
||
Raw photometric data (brightness over time) is ingested from telescope archives.
|
||
For exoplanet detection, this means <span style="${t.accent}">light curves</span> —
|
||
graphs of stellar brightness that dip when a planet transits its star.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="stage-process">2. Signal Processing</div>
|
||
<div style="${t.p}">
|
||
Processing kernels clean the data: removing instrumental noise, correcting for stellar
|
||
variability, and flagging periodic signals. The <span style="${t.accent}">eBPF programs</span>
|
||
accelerate filtering at near-hardware speed.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="stage-detect">3. Candidate Detection</div>
|
||
<div style="${t.p}">
|
||
Cleaned signals are matched against known patterns. For exoplanets: periodic transit-shaped dips.
|
||
For Dyson spheres: anomalous infrared excess. Each candidate gets derived parameters:
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:14px">
|
||
<div style="${t.card}padding:10px 14px">
|
||
<div style="font-size:10px;${t.accent}margin-bottom:3px">Exoplanets</div>
|
||
<div style="font-size:11px">Radius, period, temperature, HZ membership, ESI score</div>
|
||
</div>
|
||
<div style="${t.card}padding:10px 14px">
|
||
<div style="font-size:10px;color:#FFB020;font-weight:600;margin-bottom:3px">Dyson Candidates</div>
|
||
<div style="font-size:11px">IR excess ratio, dimming pattern, partial coverage fraction</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="stage-score">4. Scoring & Ranking</div>
|
||
<div style="${t.p}">
|
||
Candidates are scored multi-dimensionally. The <span style="${t.accent}">WASM solver</span>
|
||
uses Thompson Sampling to discover which analysis strategies work best for each difficulty
|
||
level, continuously improving accuracy without human tuning.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="stage-seal">5. Witness Sealing</div>
|
||
<div style="${t.p}">
|
||
Every step is recorded in the <span style="${t.accent}">witness chain</span>: a SHAKE-256 hash
|
||
of the data, a timestamp, and an Ed25519 signature. This creates an immutable,
|
||
cryptographically verifiable audit trail.
|
||
</div>
|
||
|
||
<!-- ============ PROOF ============ -->
|
||
<div style="${t.h2}" id="proof">How Discoveries Are Proven</div>
|
||
<div style="${t.p}">
|
||
<strong>How do you know the results are real?</strong> RVF uses four layers of proof.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="witness-chain">Layer 1: Cryptographic Witness Chain</div>
|
||
<div style="${t.card}">
|
||
<div style="font-size:11px;line-height:1.9;margin-bottom:6px">
|
||
Each processing step writes a witness entry containing:<br>
|
||
• <strong>Step name</strong> — what operation was performed<br>
|
||
• <strong>Input hash</strong> — SHAKE-256 of data going in<br>
|
||
• <strong>Output hash</strong> — SHAKE-256 of data coming out<br>
|
||
• <strong>Parent hash</strong> — links to previous entry (chain)<br>
|
||
• <strong>Ed25519 signature</strong> — proves the entry is authentic
|
||
</div>
|
||
<div style="font-size:10px;color:var(--text-muted)">
|
||
Each entry chains to the previous one. Altering any step breaks all subsequent signatures.
|
||
</div>
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="reproducible">Layer 2: Reproducible Computation</div>
|
||
<div style="${t.p}">
|
||
The file contains the actual analysis code (WASM + eBPF) alongside raw data.
|
||
Anyone can re-run the pipeline from scratch and verify identical results.
|
||
No "trust us" — the math is in the file.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="acceptance">Layer 3: Acceptance Testing</div>
|
||
<div style="${t.card}">
|
||
<div style="font-size:11px;line-height:1.9">
|
||
<span style="color:#FF4D4D;font-weight:600">Mode A (Heuristic)</span> — Can the solver achieve basic accuracy?<br>
|
||
<span style="color:#FFB020;font-weight:600">Mode B (Compiler)</span> — Accuracy + computational cost reduction?<br>
|
||
<span style="color:#2ECC71;font-weight:600">Mode C (Learned)</span> — Full: accuracy + cost + robustness + zero violations.
|
||
</div>
|
||
<div style="font-size:10px;color:var(--text-muted);margin-top:6px">
|
||
All three modes must pass. The manifest is itself recorded in the witness chain.
|
||
</div>
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="blind">Layer 4: Blind Testing</div>
|
||
<div style="${t.p}">
|
||
The Blind Test page runs the pipeline on unlabeled data, then compares against ground truth.
|
||
This guards against overfitting — the pipeline must work on data it has never seen.
|
||
</div>
|
||
|
||
<!-- ============ UNIQUE ============ -->
|
||
<div style="${t.h2}" id="unique">What Makes This Unique</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px">
|
||
${this.uniqueCard("Self-Contained","One file. No cloud, no databases, no external dependencies. The entire pipeline, visualization, and proof chain travel together.",t)}
|
||
${this.uniqueCard("Cryptographically Verified","Every step is hashed and signed. Tampering with one part invalidates the entire chain. Mathematical proof, not just peer review.",t)}
|
||
${this.uniqueCard("Self-Learning","The WASM solver improves over time using Thompson Sampling, discovering which strategies work for different data difficulties.",t)}
|
||
${this.uniqueCard("Runs Anywhere","WASM solver + HTML dashboard + Rust server. No Python, no Jupyter, no conda. Open the file and explore in any modern browser.",t)}
|
||
${this.uniqueCard("Multi-Domain","Transit detection, Dyson sphere search, habitability scoring, biosignature analysis — all in one causal event graph.",t)}
|
||
${this.uniqueCard("Interactive 3D","Embedded Three.js dashboard: explore the causal atlas as a galaxy, rotate planet systems, visualize Dyson sphere geometry.",t)}
|
||
</div>
|
||
|
||
<!-- ============ CAPABILITIES ============ -->
|
||
<div style="${t.h2}" id="capabilities">Dashboard Views</div>
|
||
<div style="${t.p}">12 interactive views, each pulling live data from the RVF file.</div>
|
||
|
||
${this.viewCard("cap-atlas","Atlas Explorer","#/atlas","var(--accent)","3D galaxy-style causal event graph. Each star = a causal event. Edges = cause-effect. Configurable arms, density, and sector labels.",["3D OrbitControls","Time scale selector","Galaxy shape config","Star map sectors"])}
|
||
${this.viewCard("cap-coherence","Coherence Heatmap","#/coherence","#FFB020","Color-mapped surface showing data self-consistency across the observation grid. Blue = stable, red = high uncertainty.",["Surface plot","Epoch scrubber","Partition boundaries"])}
|
||
${this.viewCard("cap-boundaries","Boundaries","#/boundaries","#9944FF","Tracks how data partition boundaries shift as new observations arrive. Alerts when boundaries change rapidly.",["Timeline chart","Alert feed","Sector detail"])}
|
||
${this.viewCard("cap-memory","Memory Tiers","#/memory","#FF6B9D","Three-tier storage: Small (hot), Medium (warm), Large (cold). Shows utilization, hit rates, and tier migration.",["S/M/L gauges","Utilization bars","Migration flow"])}
|
||
${this.viewCard("cap-planets","Planet Candidates","#/planets","#2ECC71","Ranked exoplanet candidates with radius, period, temperature, habitable zone status, and Earth Similarity Index.",["Sortable table","Light curve plots","Score radar"])}
|
||
${this.viewCard("cap-life","Life Candidates","#/life","#2ECC71","Biosignature analysis: atmospheric spectra for O₂, CH₄, H₂O. Multi-dimensional scoring with confound analysis.",["Spectrum plots","Molecule heatmap","Reaction graph"])}
|
||
${this.viewCard("cap-witness","Witness Chain","#/witness","var(--accent)","Complete cryptographic audit trail. Every step with timestamp, hashes, and signature verification status.",["Scrolling entries","Hash verification","Pipeline trace"])}
|
||
${this.viewCard("cap-solver","RVF Solver","#/solver","#FFB020","WASM self-learning solver with Thompson Sampling. 3D landscape shows bandit arm rewards. Configurable training parameters.",["3D landscape","Training curves","A/B/C acceptance","Auto-Optimize"])}
|
||
${this.viewCard("cap-blind","Blind Test","#/blind-test","#FF4D4D","Pipeline on unlabeled data, then compared against ground truth. The gold standard for preventing overfitting.",["Unlabeled processing","Ground truth compare","Accuracy metrics"])}
|
||
${this.viewCard("cap-discover","Discovery","#/discover","#00E5FF","3D exoplanet systems with host star, orbit, habitable zone. Real KOI parameters. Galaxy background.",["3D planet system","Speed/rotate controls","ESI comparison"])}
|
||
${this.viewCard("cap-dyson","Dyson Sphere","#/dyson","#9944FF","Dyson swarm detection using Project Hephaistos methodology. IR excess analysis and 3D wireframe visualization.",["3D Dyson wireframe","IR excess analysis","SED plots"])}
|
||
${this.viewCard("cap-status","System Status","#/status","#8B949E","RVF file health, segment sizes, memory tier utilization, pipeline stage indicators, and live witness log.",["Segment breakdown","Tier gauges","Witness log feed"])}
|
||
|
||
<!-- ============ SOLVER ============ -->
|
||
<div style="${t.h2}" id="solver">The Self-Learning Solver</div>
|
||
<div style="${t.p}">
|
||
The solver is a <span style="${t.accent}">WebAssembly module</span> compiled from Rust.
|
||
It runs entirely in your browser using <strong>Thompson Sampling</strong>.
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="thompson">How Thompson Sampling Works</div>
|
||
<div style="${t.p}">
|
||
Imagine 8 different analysis strategies ("arms"). You don't know which works best.
|
||
Thompson Sampling maintains a Beta distribution for each arm's success rate,
|
||
samples from these on each attempt, and picks the highest sample. This balances:
|
||
</div>
|
||
<div style="${t.card}">
|
||
<div style="font-size:12px;line-height:1.8">
|
||
<span style="${t.accent}">Exploration</span> — Trying uncertain arms to gather data<br>
|
||
<span style="${t.success}">Exploitation</span> — Using known-good arms to maximize results
|
||
</div>
|
||
<div style="font-size:10px;color:var(--text-muted);margin-top:6px">
|
||
Over time, the solver converges on optimal strategies per difficulty level.
|
||
The 3D landscape visually shows which arms have the highest rewards.
|
||
</div>
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="auto-optimize">Auto-Optimize</div>
|
||
<div style="${t.p}">
|
||
The <span style="${t.success}">Auto-Optimize</span> button trains in batches of 3 rounds,
|
||
tests acceptance after each batch, and stops when all three modes pass (max 30 rounds).
|
||
If accuracy is below 60%, it automatically increases training intensity.
|
||
</div>
|
||
|
||
<!-- ============ FORMAT ============ -->
|
||
<div style="${t.h2}" id="format">RVF File Format Reference</div>
|
||
|
||
<div style="${t.h3}" id="file-header">File Header (64 bytes)</div>
|
||
<pre style="${t.code}">Offset Size Field
|
||
0x00 4 Magic: 0x52564631 ("RVF1")
|
||
0x04 2 Format version (currently 1)
|
||
0x06 2 Flags (bit 0 = signed, bit 1 = compressed)
|
||
0x08 8 Total file size
|
||
0x10 4 Segment count
|
||
0x14 4 Reserved
|
||
0x18 32 SHAKE-256 hash of all segments
|
||
0x38 8 Creation timestamp (Unix epoch)</pre>
|
||
|
||
<div style="${t.h3}" id="seg-types">Segment Types</div>
|
||
<div style="overflow-x:auto;margin-bottom:14px">
|
||
<table style="width:100%;font-size:11px;font-family:var(--font-mono);border-collapse:collapse">
|
||
<tr style="border-bottom:1px solid var(--border)">
|
||
<th style="padding:6px 8px;text-align:left;color:var(--text-muted);font-weight:500;width:50px">ID</th>
|
||
<th style="padding:6px 8px;text-align:left;color:var(--text-muted);font-weight:500;width:110px">Name</th>
|
||
<th style="padding:6px 8px;text-align:left;color:var(--text-muted);font-weight:500">Purpose</th>
|
||
</tr>
|
||
${this.tableRow("0x01","DATA","Raw observations (light curves, spectra)")}
|
||
${this.tableRow("0x02","KERNEL","Processing algorithms")}
|
||
${this.tableRow("0x03","RESULT","Computed results and derived parameters")}
|
||
${this.tableRow("0x04","WITNESS","Cryptographic audit trail")}
|
||
${this.tableRow("0x05","SIGNATURE","Ed25519 digital signature")}
|
||
${this.tableRow("0x06","INDEX","Fast lookup table for segments")}
|
||
${this.tableRow("0x0F","EBPF","eBPF bytecode for in-kernel filtering")}
|
||
${this.tableRow("0x10","WASM","WebAssembly solver module")}
|
||
${this.tableRow("0x11","DASHBOARD","Embedded web dashboard (HTML/JS/CSS)")}
|
||
</table>
|
||
</div>
|
||
|
||
<div style="${t.h3}" id="witness-format">Witness Entry Format</div>
|
||
<pre style="${t.code}">struct WitnessEntry {
|
||
step_name: String, // "transit_detection"
|
||
timestamp: u64, // Unix epoch nanoseconds
|
||
input_hash: [u8; 32], // SHAKE-256 of input
|
||
output_hash: [u8; 32], // SHAKE-256 of output
|
||
parent_hash: [u8; 32], // Previous entry hash (chain)
|
||
signature: [u8; 64], // Ed25519 signature
|
||
}</pre>
|
||
|
||
<div style="${t.h3}" id="dashboard-seg">Dashboard Segment</div>
|
||
<pre style="${t.code}">DashboardHeader (64 bytes):
|
||
magic: 0x5256_4442 // "RVDB"
|
||
version: u16
|
||
framework: u8 // 0=threejs, 1=react
|
||
compression: u8 // 0=none, 1=gzip, 2=brotli
|
||
bundle_size: u64
|
||
file_count: u32
|
||
hash: [u8; 32] // SHAKE-256 of bundle
|
||
|
||
Payload: [file_table] [file_data...]</pre>
|
||
|
||
<!-- ============ GLOSSARY ============ -->
|
||
<div style="${t.h2}" id="glossary">Glossary</div>
|
||
<div style="display:grid;grid-template-columns:130px 1fr;gap:1px 14px;font-size:12px;line-height:2">
|
||
${this.glossaryRow("RVF","RuVector Format — the binary container")}
|
||
${this.glossaryRow("Segment","A typed block of data inside an RVF file")}
|
||
${this.glossaryRow("Witness Chain","Linked list of signed hash entries proving integrity")}
|
||
${this.glossaryRow("SHAKE-256","Cryptographic hash function (variable output)")}
|
||
${this.glossaryRow("Ed25519","Digital signature algorithm for witness entries")}
|
||
${this.glossaryRow("KOI","Kepler Object of Interest — exoplanet candidate")}
|
||
${this.glossaryRow("ESI","Earth Similarity Index (0-1, higher = more Earth-like)")}
|
||
${this.glossaryRow("Transit","Planet passing in front of its star, causing a brightness dip")}
|
||
${this.glossaryRow("Light Curve","Graph of stellar brightness over time")}
|
||
${this.glossaryRow("Habitable Zone","Orbital region where liquid water could exist")}
|
||
${this.glossaryRow("Thompson Samp.","Bandit algorithm balancing exploration vs exploitation")}
|
||
${this.glossaryRow("eBPF","Extended Berkeley Packet Filter — fast kernel programs")}
|
||
${this.glossaryRow("WASM","WebAssembly — portable code that runs in browsers")}
|
||
${this.glossaryRow("Dyson Sphere","Hypothetical megastructure around a star for energy")}
|
||
${this.glossaryRow("IR Excess","More infrared than expected — possible artificial origin")}
|
||
${this.glossaryRow("SED","Spectral Energy Distribution — brightness vs wavelength")}
|
||
${this.glossaryRow("Coherence","Self-consistency measure of data in a region")}
|
||
${this.glossaryRow("Acceptance","Three-mode validation (A/B/C) of solver quality")}
|
||
${this.glossaryRow("Blind Test","Evaluation on unlabeled data to prevent overfitting")}
|
||
</div>
|
||
|
||
<div style="margin-top:48px;padding-top:16px;border-top:1px solid var(--border);font-size:11px;color:var(--text-muted);text-align:center">
|
||
Everything in this dashboard was served from a single <code style="${t.inline}">.rvf</code> file.
|
||
</div>
|
||
`}statCard(t,e,i){return`<div style="${i.card}padding:10px 12px;text-align:center">
|
||
<div style="font-size:9px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:2px">${t}</div>
|
||
<div style="font-size:12px;font-weight:600;color:var(--accent);font-family:var(--font-mono)">${e}</div>
|
||
</div>`}segRow(t,e,i){return`<div style="display:flex;align-items:center;gap:10px"><span style="color:${i};min-width:160px;font-weight:600">${t}</span><span style="color:var(--text-secondary);font-weight:400">${e}</span></div>`}uniqueCard(t,e,i){return`<div style="${i.card}"><div style="font-size:12px;${i.accent}margin-bottom:4px">${t}</div><div style="font-size:11px;line-height:1.5">${e}</div></div>`}viewCard(t,e,i,s,a,n){const o=n.map(r=>`<span style="font-size:9px;padding:2px 6px;border-radius:3px;background:rgba(0,229,255,0.06);border:1px solid rgba(0,229,255,0.1);color:var(--accent)">${r}</span>`).join("");return`<div id="${t}" style="background:var(--bg-panel);border:1px solid var(--border);border-radius:6px;padding:12px 16px;margin-bottom:8px;border-left:3px solid ${s}">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
|
||
<span style="font-size:12px;font-weight:600;color:var(--text-primary)">${e}</span>
|
||
<a href="${i}" style="font-size:10px;color:${s};font-family:var(--font-mono);text-decoration:none">${i}</a>
|
||
</div>
|
||
<div style="font-size:11px;line-height:1.5;margin-bottom:6px">${a}</div>
|
||
<div style="display:flex;flex-wrap:wrap;gap:3px">${o}</div>
|
||
</div>`}tableRow(t,e,i){return`<tr style="border-bottom:1px solid var(--border-subtle)"><td style="padding:5px 8px;color:var(--accent)">${t}</td><td style="padding:5px 8px;color:var(--text-primary)">${e}</td><td style="padding:5px 8px">${i}</td></tr>`}glossaryRow(t,e){return`<span style="color:var(--accent);font-weight:600">${t}</span><span>${e}</span>`}}const ht="2.0.0",Qt="https://storage.googleapis.com/ruvector-releases",qs=[{platform:"Windows",icon:'<svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="8" height="8"/><rect x="13" y="3" width="8" height="8"/><rect x="3" y="13" width="8" height="8"/><rect x="13" y="13" width="8" height="8"/></svg>',file:`ruvector-${ht}-x64.exe`,size:"~12 MB",ext:".exe",desc:"Windows 10/11 (x64) installer with bundled WASM runtime"},{platform:"macOS",icon:'<svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/><path d="M15 8.5c0-1-0.67-2.5-2-2.5S11 7.5 11 8.5c0 1.5 1 2 1 3.5s-1 2-1 3.5c0 1 0.67 2.5 2 2.5s2-1.5 2-2.5c0-1.5-1-2-1-3.5s1-2 1-3.5z"/></svg>',file:`RuVector-${ht}.dmg`,size:"~14 MB",ext:".dmg",desc:"macOS 12+ (Apple Silicon & Intel) disk image"},{platform:"Linux",icon:'<svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="M12 3v18M3 12h18"/><circle cx="12" cy="8" r="1.5"/></svg>',file:`ruvector-${ht}-linux-x64.tar.gz`,size:"~10 MB",ext:".tar.gz",desc:"Linux (x86_64) tarball — Ubuntu 20+, Debian 11+, Fedora 36+"}];class Ks{constructor(){this.container=null}mount(t){this.container=t;const e=document.createElement("div");e.style.cssText="max-width:960px;margin:0 auto;padding:32px 24px;overflow-y:auto;height:100%",t.appendChild(e);const i=document.createElement("div");i.style.cssText="text-align:center;margin-bottom:40px",i.innerHTML=`
|
||
<div style="display:inline-flex;align-items:center;gap:12px;margin-bottom:16px">
|
||
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="#00E5FF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="6"/><ellipse cx="12" cy="12" rx="11" ry="4" transform="rotate(-20 12 12)"/>
|
||
<circle cx="12" cy="12" r="1.5" fill="#00E5FF" stroke="none"/>
|
||
</svg>
|
||
<span style="font-size:28px;font-weight:300;color:var(--text-primary);letter-spacing:2px">RuVector</span>
|
||
</div>
|
||
<div style="font-size:14px;color:var(--text-secondary);line-height:1.6;max-width:600px;margin:0 auto">
|
||
Download the Causal Atlas runtime — a single binary that reads <code style="color:var(--accent);font-size:12px">.rvf</code> files,
|
||
runs the WASM solver, serves the Three.js dashboard, and verifies the Ed25519 witness chain.
|
||
</div>
|
||
<div style="margin-top:12px;display:flex;gap:8px;justify-content:center;flex-wrap:wrap">
|
||
<span style="font-size:10px;padding:3px 8px;border-radius:4px;background:rgba(0,229,255,0.08);border:1px solid rgba(0,229,255,0.15);color:#00E5FF">v${ht}</span>
|
||
<span style="font-size:10px;padding:3px 8px;border-radius:4px;background:rgba(46,204,113,0.08);border:1px solid rgba(46,204,113,0.15);color:#2ECC71">Stable</span>
|
||
<span style="font-size:10px;padding:3px 8px;border-radius:4px;background:rgba(255,176,32,0.08);border:1px solid rgba(255,176,32,0.15);color:#FFB020">ADR-040</span>
|
||
</div>
|
||
`,e.appendChild(i);const s=document.createElement("div");s.style.cssText="display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:16px;margin-bottom:40px",e.appendChild(s);for(const r of qs){const l=document.createElement("div");l.style.cssText=`
|
||
background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;
|
||
padding:20px;display:flex;flex-direction:column;gap:12px;
|
||
transition:border-color 0.2s,transform 0.2s;cursor:pointer;
|
||
`,l.addEventListener("mouseenter",()=>{l.style.borderColor="rgba(0,229,255,0.3)",l.style.transform="translateY(-2px)"}),l.addEventListener("mouseleave",()=>{l.style.borderColor="var(--border)",l.style.transform=""}),l.innerHTML=`
|
||
<div style="display:flex;align-items:center;gap:12px">
|
||
<div style="color:#00E5FF">${r.icon}</div>
|
||
<div>
|
||
<div style="font-size:15px;font-weight:600;color:var(--text-primary)">${r.platform}</div>
|
||
<div style="font-size:10px;color:var(--text-muted)">${r.size}</div>
|
||
</div>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">${r.desc}</div>
|
||
<div style="font-family:var(--font-mono);font-size:10px;color:var(--text-muted);padding:6px 8px;background:rgba(0,0,0,0.3);border-radius:4px;word-break:break-all">${r.file}</div>
|
||
<a href="${Qt}/v${ht}/${r.file}" style="
|
||
display:flex;align-items:center;justify-content:center;gap:6px;
|
||
padding:8px 16px;border-radius:6px;text-decoration:none;
|
||
background:rgba(0,229,255,0.1);border:1px solid rgba(0,229,255,0.25);
|
||
color:#00E5FF;font-size:12px;font-weight:600;transition:background 0.15s;
|
||
" onmouseenter="this.style.background='rgba(0,229,255,0.2)'" onmouseleave="this.style.background='rgba(0,229,255,0.1)'">
|
||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
Download ${r.ext}
|
||
</a>
|
||
`,s.appendChild(l)}const a=document.createElement("div");a.style.cssText="margin-bottom:40px",a.innerHTML=`
|
||
<div style="font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:16px;display:flex;align-items:center;gap:8px">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
|
||
npm Packages & WASM Module
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;padding:14px">
|
||
<div style="font-size:11px;font-weight:600;color:var(--text-primary);margin-bottom:6px">rvf-solver (npm)</div>
|
||
<code style="display:block;font-size:10px;color:var(--accent);background:rgba(0,0,0,0.3);padding:8px;border-radius:4px;margin-bottom:6px">npm install @ruvector/rvf-solver</code>
|
||
<div style="font-size:10px;color:var(--text-muted);line-height:1.4">NAPI-RS native bindings for Node.js — includes solver, witness chain, and policy kernel.</div>
|
||
</div>
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;padding:14px">
|
||
<div style="font-size:11px;font-weight:600;color:var(--text-primary);margin-bottom:6px">rvf-solver-wasm (npm)</div>
|
||
<code style="display:block;font-size:10px;color:var(--accent);background:rgba(0,0,0,0.3);padding:8px;border-radius:4px;margin-bottom:6px">npm install @ruvector/rvf-solver-wasm</code>
|
||
<div style="font-size:10px;color:var(--text-muted);line-height:1.4">Browser WASM module — same solver running in this dashboard. No native dependencies.</div>
|
||
</div>
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;padding:14px">
|
||
<div style="font-size:11px;font-weight:600;color:var(--text-primary);margin-bottom:6px">Standalone WASM</div>
|
||
<code style="display:block;font-size:10px;color:var(--accent);background:rgba(0,0,0,0.3);padding:8px;border-radius:4px;margin-bottom:6px">curl -O ${Qt}/v${ht}/rvf_solver_wasm.wasm</code>
|
||
<div style="font-size:10px;color:var(--text-muted);line-height:1.4">Raw <code>.wasm</code> binary (172 KB). Load via WebAssembly.instantiate() — no wasm-bindgen needed.</div>
|
||
</div>
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;padding:14px">
|
||
<div style="font-size:11px;font-weight:600;color:var(--text-primary);margin-bottom:6px">Cargo Crate</div>
|
||
<code style="display:block;font-size:10px;color:var(--accent);background:rgba(0,0,0,0.3);padding:8px;border-radius:4px;margin-bottom:6px">cargo add rvf-runtime rvf-types rvf-crypto</code>
|
||
<div style="font-size:10px;color:var(--text-muted);line-height:1.4">Rust workspace crates for embedding RVF files in your own applications.</div>
|
||
</div>
|
||
</div>
|
||
`,e.appendChild(a);const n=document.createElement("div");n.style.cssText="margin-bottom:40px",n.innerHTML=`
|
||
<div style="font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:16px;display:flex;align-items:center;gap:8px">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||
Quick Start
|
||
</div>
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:20px">
|
||
<div style="display:grid;gap:16px">
|
||
${this.step(1,"Download","Download the installer for your platform above and run it.")}
|
||
${this.step(2,"Open an RVF file",`
|
||
<code style="display:block;font-size:11px;color:var(--accent);background:rgba(0,0,0,0.3);padding:8px;border-radius:4px;margin-top:4px">ruvector open causal_atlas.rvf</code>
|
||
<div style="font-size:10px;color:var(--text-muted);margin-top:4px">This starts the local server and opens the dashboard in your browser.</div>
|
||
`)}
|
||
${this.step(3,"Train the solver","Navigate to the Solver page and click Train or Auto-Optimize. The WASM solver learns in real time inside your browser.")}
|
||
${this.step(4,"Run acceptance test","Click Acceptance to verify the solver passes the three-mode acceptance test (A/B/C). All results are recorded in the Ed25519 witness chain.")}
|
||
${this.step(5,"Explore discoveries","Navigate to Planets, Life, and Discover pages to explore candidate detections. Each candidate includes a full causal trace and witness proof.")}
|
||
</div>
|
||
</div>
|
||
`,e.appendChild(n);const o=document.createElement("div");o.innerHTML=`
|
||
<div style="font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:16px;display:flex;align-items:center;gap:8px">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="4" y="4" width="16" height="16" rx="2"/><line x1="4" y1="9" x2="20" y2="9"/><circle cx="8" cy="6.5" r="0.5" fill="currentColor" stroke="none"/></svg>
|
||
System Requirements
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;font-size:11px">
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;padding:12px">
|
||
<div style="font-weight:600;color:var(--text-primary);margin-bottom:6px">Windows</div>
|
||
<div style="color:var(--text-muted);line-height:1.5">Windows 10 (1903+)<br>x64 processor<br>4 GB RAM<br>100 MB disk</div>
|
||
</div>
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;padding:12px">
|
||
<div style="font-weight:600;color:var(--text-primary);margin-bottom:6px">macOS</div>
|
||
<div style="color:var(--text-muted);line-height:1.5">macOS 12 Monterey+<br>Apple Silicon or Intel<br>4 GB RAM<br>100 MB disk</div>
|
||
</div>
|
||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;padding:12px">
|
||
<div style="font-weight:600;color:var(--text-primary);margin-bottom:6px">Linux</div>
|
||
<div style="color:var(--text-muted);line-height:1.5">glibc 2.31+<br>x86_64 processor<br>4 GB RAM<br>100 MB disk</div>
|
||
</div>
|
||
</div>
|
||
<div style="font-size:9px;color:var(--text-muted);margin-top:12px;text-align:center">
|
||
Binaries are hosted on Google Cloud Storage. All downloads include Ed25519 signatures for verification.
|
||
</div>
|
||
`,e.appendChild(o)}step(t,e,i){return`
|
||
<div style="display:flex;gap:12px;align-items:flex-start">
|
||
<div style="min-width:24px;height:24px;border-radius:50%;background:rgba(0,229,255,0.1);border:1px solid rgba(0,229,255,0.25);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;color:#00E5FF">${t}</div>
|
||
<div style="flex:1">
|
||
<div style="font-size:12px;font-weight:600;color:var(--text-primary);margin-bottom:2px">${e}</div>
|
||
<div style="font-size:11px;color:var(--text-secondary);line-height:1.5">${i}</div>
|
||
</div>
|
||
</div>
|
||
`}unmount(){this.container=null}}const te={"#/atlas":ss,"#/coherence":os,"#/boundaries":rs,"#/memory":ls,"#/planets":us,"#/life":ys,"#/witness":ws,"#/solver":_s,"#/blind-test":Ps,"#/discover":Hs,"#/dyson":js,"#/status":Ls,"#/download":Ks,"#/docs":Vs};let Et=null;function Us(){const p=document.getElementById("app");if(!p)throw new Error("Missing #app container");return p}function Ys(){const p=location.hash||"#/atlas";document.querySelectorAll("#nav-rail a").forEach(t=>{const e=t;e.classList.toggle("active",e.getAttribute("href")===p)})}function ee(p){const t=Us();Et&&(Et.unmount(),Et=null),t.innerHTML="";const e=te[p]||te["#/atlas"],i=new e;i.mount(t),Et=i,Ys()}async function se(){const p=document.getElementById("root-hash"),t=document.querySelector("#top-bar .dot"),e=document.getElementById("pipeline-status");if(p)try{const i=await ue(),s=(i.file_size*1575931494+i.segments>>>0).toString(16).padStart(8,"0");p.textContent=`0x${s.substring(0,4)}...${s.substring(4,8)}`,t&&(t.style.background="#2ECC71"),e&&(e.textContent="LIVE",e.style.color="#2ECC71")}catch{p.textContent="0x----...----",t&&(t.style.background="#FF4D4D"),e&&(e.textContent="OFFLINE",e.style.color="#FF4D4D")}}function Zs(){Je();const p=location.hash||"#/atlas";location.hash||(location.hash="#/atlas"),ee(p),window.addEventListener("hashchange",()=>{ee(location.hash)}),window.addEventListener("beforeunload",()=>{Qe()}),se(),setInterval(se,1e4)}document.addEventListener("DOMContentLoaded",Zs);
|