import{f as tt,u as at,i as y,a as w,b as c,w as Y}from"./lit-BS7WqYd5.js";import{y as p,g as st,j as f}from"./signals-SG45zFCj.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 r of s)if(r.type==="childList")for(const n of r.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&i(n)}).observe(document,{childList:!0,subtree:!0});function a(s){const r={};return s.integrity&&(r.integrity=s.integrity),s.referrerPolicy&&(r.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?r.credentials="include":s.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(s){if(s.ep)return;s.ep=!0;const r=a(s);fetch(s.href,r)}})();/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/const k=e=>(t,a)=>{a!==void 0?a.addInitializer(()=>{customElements.define(e,t)}):customElements.define(e,t)};/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/const it={attribute:!0,type:String,converter:at,reflect:!1,hasChanged:tt},rt=(e=it,t,a)=>{const{kind:i,metadata:s}=a;let r=globalThis.litPropertyMetadata.get(s);if(r===void 0&&globalThis.litPropertyMetadata.set(s,r=new Map),i==="setter"&&((e=Object.create(e)).wrapped=!0),r.set(a.name,e),i==="accessor"){const{name:n}=a;return{set(o){const l=t.get.call(this);t.set.call(this,o),this.requestUpdate(n,l,e,!0,o)},init(o){return o!==void 0&&this.C(n,void 0,e,o),o}}}if(i==="setter"){const{name:n}=a;return function(o){const l=this[n];t.call(this,o),this.requestUpdate(n,l,e,!0,o)}}throw Error("Unsupported decorator location: "+i)};function Ce(e){return(t,a)=>typeof a=="object"?rt(e,t,a):((i,s,r)=>{const n=s.hasOwnProperty(r);return s.constructor.createProperty(r,i),n?Object.getOwnPropertyDescriptor(s,r):void 0})(e,t,a)}/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/function u(e){return Ce({...e,state:!0,attribute:!1})}/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/const nt=(e,t,a)=>(a.configurable=!0,a.enumerable=!0,Reflect.decorate&&typeof t!="object"&&Object.defineProperty(e,t,a),a);/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/function Ue(e,t){return(a,i,s)=>{const r=n=>n.renderRoot?.querySelector(e)??null;return nt(a,i,{get(){return r(this)}})}}var ot=Object.defineProperty,lt=Object.getOwnPropertyDescriptor,qe=(e,t,a,i)=>{for(var s=i>1?void 0:i?lt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&ot(t,a,s),s};let ye=class extends w{constructor(){super(...arguments),this.view="scene"}navigate(e){this.dispatchEvent(new CustomEvent("navigate",{detail:e}))}render(){return c`
NV
this.navigate("scene")}>
this.navigate("apps")}>
this.navigate("inspector")}>
this.navigate("witness")}>
this.navigate("ghost-murmur")}>
this.dispatchEvent(new CustomEvent("open-settings",{bubbles:!0,composed:!0}))}>
`}};ye.styles=y`
:host {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 0;
gap: 4px;
background: var(--bg-1);
border-right: 1px solid var(--line);
}
.logo {
width: 36px; height: 36px;
border-radius: 10px;
background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);
display: grid; place-items: center;
color: #1a0f00;
font-weight: 700;
font-family: var(--mono);
font-size: 11px;
margin-bottom: 14px;
box-shadow: 0 4px 12px -2px oklch(0.55 0.16 30 / 0.35);
}
.btn {
width: 36px; height: 36px;
border-radius: 8px;
background: transparent;
border: 1px solid transparent;
color: var(--ink-3);
display: grid; place-items: center;
transition: all 0.15s;
position: relative;
cursor: pointer;
}
.btn:hover { color: var(--ink); background: var(--bg-2); }
.btn.active {
color: var(--ink);
background: var(--bg-3);
border-color: var(--line-2);
}
.btn.active::before {
content: ''; position: absolute; left: -10px; top: 8px; bottom: 8px;
width: 2px; background: var(--accent); border-radius: 2px;
}
.btn.ghost.active::before { background: var(--accent-3); }
.spacer { flex: 1; }
svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
`;qe([Ce()],ye.prototype,"view",2);ye=qe([k("nv-rail")],ye);const T=p("wasm"),Re=p("");p(!1);p(null);const g=p(!1);p(!0);const me=p(1),dt=p(0),Fe=p(0n),L=p(0xCAFEBABEn),q=p(1e4),G=p(1e3),K=p(1),X=p(!0),x=p("dark"),_=p("default"),C=p(!1),be=p(!0),ce=p([0,0,0]),V=p(0),N=p(0),M=p(0),z=p(""),$=p("idle"),R=p(""),B=p(null),we=p([]),ke=p([]),$e=p([]),le=p([]),Ne=p("rebar-walkby-01"),ct=p(""),re=p(!1),Ee=p("all"),W=p([]);function pt(e){const t=W.value.slice();for(t.push(e);t.length>200;)t.shift();W.value=t}const de=p([]),Be=st(()=>T.value==="wasm"?"wasm":"ws");let Ge=null;function ut(e){Ge=e}function h(){return Ge}const A=p([]),vt=200;function d(e,t){if(re.value)return;const a=A.value.slice();for(a.push({ts:Date.now(),level:e,msg:t});a.length>vt;)a.shift();A.value=a}function gt(e){const a=we.value.slice();a.push(e[0]),a.length>200&&a.shift();const i=ke.value.slice();i.push(e[1]),i.length>200&&i.shift();const s=$e.value.slice();s.push(e[2]),s.length>200&&s.shift(),we.value=a,ke.value=i,$e.value=s}function mt(e){const a=le.value.slice();for(a.push(Math.max(0,Math.min(1,e)));a.length>48;)a.shift();le.value=a}var bt=Object.defineProperty,ht=Object.getOwnPropertyDescriptor,ve=(e,t,a,i)=>{for(var s=i>1?void 0:i?ht(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&bt(t,a,s),s};let j=class extends w{constructor(){super(...arguments),this.open=!1,this.mTitle="",this.mBody="",this.buttons=[],this.onModal=e=>{const t=e.detail;this.mTitle=t.title,this.mBody=t.body,this.buttons=t.buttons??[{label:"Close",variant:"primary"}],this.open=!0,this.setAttribute("open",""),requestAnimationFrame(()=>{const a=this.shadowRoot;if(!a)return;a.querySelector("input, select, textarea, button:not(.close)")?.focus()})},this.onKey=e=>{e.key==="Escape"&&this.open&&this.close()}}connectedCallback(){super.connectedCallback(),window.addEventListener("nv-modal",this.onModal),window.addEventListener("keydown",this.onKey)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-modal",this.onModal),window.removeEventListener("keydown",this.onKey)}updated(){if(!this.open)return;const e=this.shadowRoot;if(!e)return;const t=a=>{if(a.key!=="Tab")return;const i=Array.from(e.querySelectorAll("input, select, textarea, button, [href]")).filter(o=>!o.hasAttribute("disabled"));if(i.length===0)return;const s=i[0],r=i[i.length-1],n=e.activeElement??null;a.shiftKey&&n===s?(a.preventDefault(),r.focus()):!a.shiftKey&&n===r&&(a.preventDefault(),s.focus())};e.removeEventListener("keydown",t),e.addEventListener("keydown",t)}close(){this.open=!1,this.removeAttribute("open")}clickBtn(e){e.onClick?.(),this.close()}render(){return c`
${this.mTitle}
this.close()}>×
${this.buttons.map(e=>c`
this.clickBtn(e)}>${e.label}
`)}
`}};j.styles=y`
:host {
position: fixed; inset: 0;
background: rgba(0,0,0,0.55);
backdrop-filter: blur(4px);
z-index: 200;
display: grid; place-items: center;
opacity: 0; pointer-events: none;
transition: opacity 0.18s;
}
:host([open]) { opacity: 1; pointer-events: auto; }
.modal {
background: var(--bg-1);
border: 1px solid var(--line-2);
border-radius: var(--radius);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
width: min(520px, 92vw);
max-height: 86vh;
display: flex; flex-direction: column;
transform: translateY(12px) scale(0.98);
transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);
}
:host([open]) .modal { transform: translateY(0) scale(1); }
.h {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
display: flex; align-items: center; justify-content: space-between;
}
.h .ttl { font-size: 14px; font-weight: 600; }
.body { padding: 16px; overflow-y: auto; font-size: 13px; color: var(--ink-2); line-height: 1.55; }
.f {
padding: 12px 16px;
border-top: 1px solid var(--line);
display: flex; gap: 8px; justify-content: flex-end;
}
button {
padding: 6px 12px;
border-radius: 8px;
font-size: 12.5px;
cursor: pointer;
font-family: inherit;
border: 1px solid var(--line);
background: var(--bg-2); color: var(--ink);
}
button.ghost { background: transparent; }
button.primary { background: var(--accent); border-color: var(--accent); color: #1a0f00; }
button.danger { background: var(--bad); border-color: var(--bad); color: #fff; }
.close {
width: 28px; height: 28px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-2);
}
`;ve([u()],j.prototype,"open",2);ve([u()],j.prototype,"mTitle",2);ve([u()],j.prototype,"mBody",2);ve([u()],j.prototype,"buttons",2);j=ve([k("nv-modal")],j);function ne(e){window.dispatchEvent(new CustomEvent("nv-modal",{detail:e}))}var ft=Object.defineProperty,xt=Object.getOwnPropertyDescriptor,Pe=(e,t,a,i)=>{for(var s=i>1?void 0:i?xt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&ft(t,a,s),s};let J=class extends w{constructor(){super(...arguments),this.visible=!1,this.msg="",this.icon="✓",this.timer=null,this.onToast=e=>{const t=e.detail;this.msg=t.msg??"Done",this.icon=t.icon??"✓",this.visible=!0,this.setAttribute("visible",""),this.timer!==null&&window.clearTimeout(this.timer),this.timer=window.setTimeout(()=>{this.visible=!1,this.removeAttribute("visible")},1800)}}connectedCallback(){super.connectedCallback(),window.addEventListener("nv-toast",this.onToast)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-toast",this.onToast)}render(){return c`${this.icon} ${this.msg} `}};J.styles=y`
:host {
position: fixed; bottom: 24px; left: 50%;
transform: translateX(-50%) translateY(80px);
background: var(--bg-2);
border: 1px solid var(--line-2);
border-radius: var(--radius);
padding: 10px 14px;
font-size: 12.5px;
box-shadow: var(--shadow);
z-index: 100;
opacity: 0; pointer-events: none;
transition: opacity 0.2s, transform 0.2s;
display: flex; align-items: center; gap: 8px;
}
:host([visible]) {
opacity: 1;
transform: translateX(-50%) translateY(0);
pointer-events: auto;
}
.icon { color: var(--accent); }
`;Pe([u()],J.prototype,"visible",2);Pe([u()],J.prototype,"msg",2);Pe([u()],J.prototype,"icon",2);J=Pe([k("nv-toast")],J);function P(e,t="✓"){window.dispatchEvent(new CustomEvent("nv-toast",{detail:{msg:e,icon:t}}))}var yt=Object.getOwnPropertyDescriptor,wt=(e,t,a,i)=>{for(var s=i>1?void 0:i?yt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=n(s)||s);return s};let Oe=class extends w{connectedCallback(){super.connectedCallback(),f(()=>{M.value,Be.value,L.value,x.value,Ne.value,g.value,this.requestUpdate()})}async toggleRun(){const e=h();e&&(g.value?(await e.pause(),g.value=!1):(await e.run(),g.value=!0))}async reset(){const e=h();e&&await e.reset()}toggleTheme(){x.value=x.value==="dark"?"light":"dark"}async openSeedModal(){const e=`0x${L.value.toString(16).toUpperCase().padStart(8,"0")}`;ne({title:"Set seed",body:`Set the 32-bit hex seed for the shot-noise PRNG. Same (scene, config, seed) → byte-identical witness.
Hex seed
`,buttons:[{label:"Cancel",variant:"ghost"},{label:"Apply",variant:"primary",onClick:async()=>{const t=document.querySelector("nv-modal")?.shadowRoot?.querySelector("#seed-input");if(!t)return;const a=t.value.trim().replace(/^0x/i,""),i=BigInt("0x"+a);L.value=i,await h()?.setSeed(i),d("ok",`seed → 0x${i.toString(16).toUpperCase()}`),P(`Seed → 0x${i.toString(16).toUpperCase().slice(0,8)}`,"⟳")}}]})}openTransportSettings(){window.dispatchEvent(new CustomEvent("open-settings"))}render(){const e=L.value.toString(16).toUpperCase().padStart(8,"0");return c`
RuView /
nvsim /
${Ne.value}
${M.value>0?(M.value/1e3).toFixed(2)+" kHz":"idle"}
${Be.value}
seed: 0x${e}
${x.value==="dark"?"☼":"☾"}
↺ Reset
${g.value?"❚❚ Pause":"▶ Run"}
`}};Oe.styles=y`
:host {
display: flex; align-items: center;
padding: 0 16px; gap: 12px;
background: var(--bg-1);
border-bottom: 1px solid var(--line);
z-index: 10;
}
.crumbs { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--ink-3); }
.crumbs .sep { color: var(--ink-4); }
.crumbs .cur { color: var(--ink); font-weight: 500; }
.spacer { flex: 1; }
.pill {
display: inline-flex; align-items: center; gap: 6px;
padding: 5px 10px;
background: var(--bg-2); border: 1px solid var(--line);
border-radius: 999px;
font-size: 12px; color: var(--ink-2);
font-family: var(--mono); font-weight: 500;
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 6px var(--ok); animation: pulse 2s infinite; }
.pill.wasm .dot { background: var(--accent-2); box-shadow: 0 0 6px var(--accent-2); }
.pill.seed { color: var(--ink-3); cursor: pointer; }
.pill.seed:hover { border-color: var(--line-2); }
.pill.seed b { color: var(--accent); font-weight: 600; }
.pill.wasm { cursor: pointer; }
.pill.wasm:hover { border-color: var(--line-2); }
button {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 12px;
background: var(--bg-2); border: 1px solid var(--line);
border-radius: 8px;
font-size: 12.5px; font-weight: 500; color: var(--ink);
cursor: pointer;
transition: all 0.15s;
}
button:hover { border-color: var(--line-2); background: var(--bg-3); }
button.primary { background: var(--accent); border-color: var(--accent); color: #1a0f00; }
button.primary:hover { filter: brightness(1.08); }
button.ghost { background: transparent; }
`;Oe=wt([k("nv-topbar")],Oe);var kt=Object.getOwnPropertyDescriptor,$t=(e,t,a,i)=>{for(var s=i>1?void 0:i?kt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=n(s)||s);return s};let De=null;function he(){De!==null&&window.clearTimeout(De),De=window.setTimeout(async()=>{const e=h();if(e)try{await e.setConfig({digitiser:{f_s_hz:q.value,f_mod_hz:G.value},sensor:{gamma_fwhm_hz:1e6,t1_s:.005,t2_s:1e-6,t2_star_s:2e-7,contrast:.03,n_spins:1e12,shot_noise_disabled:!X.value},dt_s:K.value*.001}),d("dbg",`config pushed · fs=${q.value} f_mod=${G.value} dt=${K.value.toFixed(1)}ms noise=${X.value?"on":"off"}`)}catch(t){d("warn",`config push failed: ${t.message}`)}},300)}let Ie=class extends w{connectedCallback(){super.connectedCallback(),f(()=>{q.value,G.value,K.value,X.value,g.value,this.requestUpdate()})}render(){return c`
Scene 4 sources
rebar.steel.coil
χ=5000
heart_proxy
1e-6 A·m²
mains_60Hz
2 A · 60 Hz
door.steel
eddy
NV sensor COTS
V 1 mm³
N 1e12 NV
C 0.030
T₂* 200 ns
δB 1.18 pT/√Hz
Pipeline
scene
→
B-S
→
prop
→
NV
→
ADC
→
frame
`}};Ie.styles=y`
:host {
display: flex; flex-direction: column; gap: 14px;
padding: 14px; overflow-y: auto;
background: var(--bg-1); border-right: 1px solid var(--line);
}
.panel {
background: var(--bg-2); border: 1px solid var(--line);
border-radius: var(--radius); padding: 12px;
}
.panel-h {
display: flex; align-items: center; justify-content: space-between;
font-size: 11px; font-weight: 600; color: var(--ink-3);
text-transform: uppercase; letter-spacing: 0.08em;
margin-bottom: 10px;
}
.count {
background: var(--bg-3); color: var(--ink-2);
padding: 1px 6px; border-radius: 999px;
font-family: var(--mono); font-size: 10px;
text-transform: none; letter-spacing: 0;
}
.scene-item {
display: flex; align-items: center; gap: 10px;
padding: 8px 10px;
border-radius: var(--radius-sm);
cursor: pointer;
transition: background 0.15s;
border: 1px solid transparent;
}
.scene-item:hover { background: var(--bg-3); }
.scene-item .swatch { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.scene-item .name { font-size: 13px; flex: 1; }
.scene-item .meta { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
.field-row {
display: flex; align-items: center; justify-content: space-between;
padding: 6px 0; font-size: 12.5px;
border-bottom: 1px solid var(--line);
}
.field-row:last-child { border-bottom: 0; }
.field-row .lbl { color: var(--ink-3); }
.field-row .val { font-family: var(--mono); color: var(--ink); font-size: 12px; }
.slider-row { padding: 8px 0; border-bottom: 1px solid var(--line); }
.slider-row:last-child { border-bottom: 0; padding-bottom: 0; }
.slider-row .top { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 12px; }
.slider-row .top .lbl { color: var(--ink-3); }
.slider-row .top .val { font-family: var(--mono); color: var(--ink); }
input[type="range"] {
-webkit-appearance: none; appearance: none;
width: 100%; height: 4px;
background: var(--bg-3); border-radius: 2px; outline: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 14px; height: 14px; border-radius: 50%;
background: var(--accent); cursor: pointer;
border: 2px solid var(--bg-2);
box-shadow: 0 0 0 1px var(--line-2);
}
.pipeline { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
.stage {
flex: 1; min-width: 50px;
padding: 4px 6px;
background: var(--bg-3); border: 1px solid var(--line);
border-radius: 6px; font-size: 9.5px; text-align: center;
color: var(--ink-2); font-family: var(--mono);
}
.stage.live { border-color: var(--accent-2); color: var(--accent-2); }
.stage-arrow { color: var(--ink-4); font-size: 10px; }
`;Ie=$t([k("nv-sidebar")],Ie);var St=Object.defineProperty,_t=Object.getOwnPropertyDescriptor,ee=(e,t,a,i)=>{for(var s=i>1?void 0:i?_t(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&St(t,a,s),s};let E=class extends w{constructor(){super(...arguments),this.zoom=1,this.layerVisible={source:!0,field:!0,label:!0},this.items=[{id:"rebar",x:740,y:240,color:"oklch(0.72 0.18 330)",name:"rebar.steel"},{id:"heart",x:220,y:180,color:"oklch(0.78 0.14 195)",name:"heart_proxy"},{id:"mains",x:180,y:380,color:"oklch(0.72 0.18 330)",name:"mains_60Hz"},{id:"door",x:800,y:470,color:"oklch(0.78 0.14 145)",name:"door.steel"}],this.dragging=null,this.selected=null,this.dragOffset={dx:0,dy:0},this.onDown=(e,t)=>{t.preventDefault(),this.dragging=e,this.selected=e;const a=this.items.find(r=>r.id===e);if(!a)return;const i=this.renderRoot.querySelector("svg");if(!i)return;const s=this.toSvg(t,i);this.dragOffset={dx:s.x-a.x,dy:s.y-a.y}},this.onPointerMove=e=>{if(!this.dragging)return;const t=this.renderRoot.querySelector("svg");if(!t)return;const a=this.toSvg(e,t);this.items=this.items.map(i=>i.id===this.dragging?{...i,x:a.x-this.dragOffset.dx,y:a.y-this.dragOffset.dy}:i)},this.onPointerUp=()=>{this.dragging&&(de.value=this.items.map(({id:e,x:t,y:a})=>({id:e,x:t,y:a}))),this.dragging=null}}connectedCallback(){super.connectedCallback(),de.value.length>0&&(this.items=this.items.map(e=>{const t=de.value.find(a=>a.id===e.id);return t?{...e,x:t.x,y:t.y}:e})),f(()=>{ce.value,V.value,M.value,N.value,C.value,g.value,me.value,B.value,this.requestUpdate()}),f(()=>{const e=B.value;if(!e)return;const t=Math.sqrt(e.bPt[0]**2+e.bPt[1]**2+e.bPt[2]**2),a=Math.max(Math.abs(e.sigmaPt[0]),Math.abs(e.sigmaPt[1]),Math.abs(e.sigmaPt[2]),.001),i=t/a;Number.isFinite(i)&&(N.value=i)}),window.addEventListener("pointermove",this.onPointerMove),window.addEventListener("pointerup",this.onPointerUp)}async toggleRun(){const e=h();e&&(g.value?(await e.pause(),g.value=!1):(await e.run(),g.value=!0))}async stepFwd(){const e=h();e&&(await e.step("fwd",10),d("dbg","sim step → +1 frame"))}async stepBack(){const e=h();e&&(await e.step("back",10),d("dbg","sim step ← -1 frame"))}cycleSpeed(){const e=[.25,.5,1,2,4],t=e.indexOf(me.value);me.value=e[(t+1)%e.length]}zoomIn(){this.zoom=Math.min(2.5,this.zoom*1.2)}zoomOut(){this.zoom=Math.max(.5,this.zoom/1.2)}fitView(){this.zoom=1}toggleLayer(e){this.layerVisible={...this.layerVisible,[e]:!this.layerVisible[e]}}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("pointermove",this.onPointerMove),window.removeEventListener("pointerup",this.onPointerUp)}toSvg(e,t){const a=t.getBoundingClientRect(),i=(e.clientX-a.left)/a.width*1e3,s=(e.clientY-a.top)/a.height*600;return{x:i,y:s}}render(){const e=ce.value,t=[e[0]*1e9,e[1]*1e9,e[2]*1e9],a=V.value*1e9,i=C.value?"":"anim",s=1e3/this.zoom,r=600/this.zoom,n=(1e3-s)/2,o=(600-r)/2;return c`
${this.layerVisible.field?this.items.map(l=>Y`
`):""}
${this.layerVisible.source?this.items.map(l=>Y`
this.onDown(l.id,v)}>
${this.layerVisible.label?Y`${l.name} `:""}
`):""}
sensor · 〈111〉 NV
B_in: [${t[0].toFixed(2)}, ${t[1].toFixed(2)}, ${t[2].toFixed(2)}] nT
+
−
⊡
this.toggleLayer("source")}>●
this.toggleLayer("field")}>≈
this.toggleLayer("label")}>T
⏮
${g.value?"❚❚":"▶"}
⏭
${me.value}×
FPS
${M.value>0?Math.round(M.value):"—"}
SNR
${N.value>0?N.value.toFixed(1):"—"}
`}};E.styles=y`
:host {
display: block; height: 100%; width: 100%;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
position: relative; overflow: hidden;
border-bottom: 1px solid var(--line);
}
.grid {
position: absolute; inset: 0;
background-image:
linear-gradient(var(--grid) 1px, transparent 1px),
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
background-size: 32px 32px;
pointer-events: none;
mask-image: radial-gradient(ellipse at center, black 40%, transparent 100%);
}
svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.stat-card {
background: rgba(13,17,23,0.7);
backdrop-filter: blur(8px);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 8px 12px;
font-size: 11px;
min-width: 96px;
}
[data-theme="light"] .stat-card { background: rgba(255,255,255,0.85); }
.stat-card .lbl {
color: var(--ink-3);
text-transform: uppercase; font-weight: 600; letter-spacing: 0.06em; font-size: 9.5px;
}
.stat-card .val { font-family: var(--mono); font-size: 16px; font-weight: 600; margin-top: 2px; }
.stat-card .val.amber { color: var(--accent); }
.stat-card .val.cyan { color: var(--accent-2); }
.stat-card .val.mint { color: var(--accent-4); }
.scene-readout {
position: absolute; top: 14px; right: 14px;
display: flex; gap: 8px; z-index: 5;
}
.draggable { cursor: grab; transition: filter 0.15s; }
.draggable:hover { filter: brightness(1.15) drop-shadow(0 0 6px currentColor); }
.draggable.dragging { cursor: grabbing; filter: brightness(1.25) drop-shadow(0 0 10px currentColor); }
.field-line { stroke-dasharray: 4 6; }
@keyframes dash { to { stroke-dashoffset: -200; } }
.field-line.anim { animation: dash 4s linear infinite; }
@keyframes spin {
0% { transform: rotateY(0) rotateX(8deg); }
100% { transform: rotateY(360deg) rotateX(8deg); }
}
.crystal { transform-origin: center; transform-box: fill-box; }
.crystal.anim { animation: spin 12s linear infinite; }
.label {
font-family: var(--mono); font-size: 11px; fill: var(--ink-2);
pointer-events: none;
}
.scene-toolbar {
position: absolute; top: 14px; left: 14px;
display: flex; gap: 6px; z-index: 5;
background: rgba(13,17,23,0.85);
backdrop-filter: blur(8px);
border: 1px solid var(--line);
border-radius: 8px;
padding: 4px;
}
[data-theme="light"] .scene-toolbar { background: rgba(255,255,255,0.85); }
.scene-toolbar button {
width: 28px; height: 28px;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
color: var(--ink-2);
cursor: pointer;
display: grid; place-items: center;
font-size: 13px;
}
.scene-toolbar button:hover { color: var(--ink); background: var(--bg-2); }
.scene-toolbar button.on { background: var(--bg-3); color: var(--accent); border-color: var(--line-2); }
.sim-controls {
position: absolute; bottom: 14px; right: 14px;
display: flex; gap: 6px; align-items: center;
background: rgba(13,17,23,0.85);
backdrop-filter: blur(12px);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 6px 10px;
z-index: 5;
}
[data-theme="light"] .sim-controls { background: rgba(255,255,255,0.92); }
.sim-controls .play {
width: 32px; height: 32px;
background: var(--accent);
border: none;
border-radius: 50%;
color: #1a0f00;
cursor: pointer;
display: grid; place-items: center;
font-size: 13px;
}
.sim-controls .play:hover { filter: brightness(1.08); }
.sim-controls .step {
width: 26px; height: 26px;
border-radius: 6px;
background: transparent;
color: var(--ink-2);
border: 1px solid var(--line);
cursor: pointer;
font-size: 11px;
}
.sim-controls .step:hover { color: var(--ink); border-color: var(--line-2); }
.sim-controls .speed {
font-family: var(--mono); font-size: 11px;
color: var(--ink-2);
padding: 0 6px;
min-width: 36px;
text-align: center;
cursor: pointer;
}
`;ee([u()],E.prototype,"zoom",2);ee([u()],E.prototype,"layerVisible",2);ee([u()],E.prototype,"items",2);ee([u()],E.prototype,"dragging",2);ee([u()],E.prototype,"selected",2);E=ee([k("nv-scene")],E);var zt=Object.defineProperty,Mt=Object.getOwnPropertyDescriptor,Te=(e,t,a,i)=>{for(var s=i>1?void 0:i?Mt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&zt(t,a,s),s};let Q=class extends w{constructor(){super(...arguments),this.tab="signal",this.pinTab=null,this.expanded=!1}connectedCallback(){super.connectedCallback(),f(()=>{we.value,ke.value,$e.value,le.value,B.value,z.value,$.value,ce.value,V.value,this.requestUpdate()})}willUpdate(e){e.has("pinTab")&&this.pinTab&&this.tab!==this.pinTab&&(this.tab=this.pinTab)}async verify(){const e=h();if(e){$.value="pending",d("info","verifying witness over 256 frames…");try{const t=R.value,a=new Uint8Array(32);for(let s=0;s<32;s++)a[s]=parseInt(t.slice(s*2,s*2+2),16);const i=await e.verifyWitness(a);if(i.ok)$.value="ok",z.value=t,d("ok",`witness ${t.slice(0,16)}… matches · determinism gate ✓`);else{$.value="fail";const s=Array.from(i.actual).map(r=>r.toString(16).padStart(2,"0")).join("");z.value=s,d("err",`WITNESS MISMATCH actual=${s.slice(0,16)}…`)}}catch(t){$.value="fail",d("err",`verify failed: ${t.message}`)}}}renderHeader(){return this.expanded?c`
${{signal:"Signal inspector — live B-vector trace + frame stream",frame:"Frame inspector — MagFrame v1 fields + raw bytes",witness:"Witness panel — SHA-256 determinism gate"}[this.tab]}
${this.tab==="signal"?"Real-time recovered field-vector and frame-stream sparkline. Both update at the running pipeline's frame rate. Use the Tunables panel in the sidebar to change f_s, f_mod, dt, and shot-noise behaviour.":this.tab==="frame"?"Decoded view of the most recent MagFrame: typed fields plus the raw 60-byte little-endian binary record (magic 0xC51A_6E70).":"Re-derive the SHA-256 witness for the canonical reference scene (seed=42, N=256) right now in your browser and compare against Proof::EXPECTED_WITNESS_HEX. Same inputs → same hash, byte-for-byte, across every machine and transport."}
`:""}renderSignalTab(){const r=l=>{let v="";return l.forEach((m,b)=>{const ae=b/Math.max(1,199)*320,Ae=65-m*22;v+=(b===0?"M":"L")+` ${ae.toFixed(1)} ${Ae.toFixed(1)} `}),v},n=ce.value,o=[n[0]*1e9,n[1]*1e9,n[2]*1e9];return c`
B-vector trace
3-axis · nT
${Y` `}
${Y` `}
${Y` `}
${this.expanded?c`
x: ${o[0].toFixed(3)} nT
y: ${o[1].toFixed(3)} nT
z: ${o[2].toFixed(3)} nT
|B| ${(V.value*1e9).toFixed(3)} nT
`:""}
Frame stream
live
${this.expanded?c`
frames in window: ${le.value.length}
noise floor: ${B.value?B.value.noiseFloorPtSqrtHz.toFixed(2)+" pT/√Hz":"—"}
`:""}
`}renderFrameTab(){const e=B.value,t=e?.raw;let a="";return t&&(a=Array.from(t).map(s=>s.toString(16).padStart(2,"0")).slice(0,60).join(" ")),c`
MagFrame v1 fields
60 B
magic ${e?"0x"+e.magic.toString(16).toUpperCase():"—"}
version ${e?.version??"—"}
flags 0x${(e?.flags??0).toString(16).padStart(4,"0")}
sensor_id ${e?.sensorId??"—"}
t_us ${e?e.tUs.toString():"—"}
b_pT[0] ${e?e.bPt[0].toFixed(1):"—"}
b_pT[1] ${e?e.bPt[1].toFixed(1):"—"}
b_pT[2] ${e?e.bPt[2].toFixed(1):"—"}
noise_floor ${e?e.noiseFloorPtSqrtHz.toFixed(2):"—"}
temp_K ${e?e.temperatureK.toFixed(1):"—"}
Hex dump
LE
${a||"—"}
${this.expanded?c`
Layout (little-endian): magic(u32) version(u16) flags(u16) sensor_id(u16) _reserved(u16) t_us(u64) b_pt[3](f32) sigma_pt[3](f32) noise_floor(f32) temp_K(f32).
`:""}
`}renderWitnessTab(){const e=$.value,t=e==="ok"?"ok":e==="fail"?"fail":"",a=e==="pending"?"Verifying…":e==="ok"?"✓ Witness verified · determinism gate":e==="fail"?"✗ Witness mismatch · audit required":"Verify witness",i=R.value&&z.value&&R.value===z.value;return c`
${this.expanded?c`
Reference scene
Proof::REFERENCE
2 dipoles · 1 loop · 1 ferrous · 1 sensor
Seed
0x0000002A
canonical Proof::SEED
Sample count
256
Proof::N_SAMPLES
Status
${e==="ok"?"✓ matches":e==="fail"?"✗ drift":e==="pending"?"… running":"— idle"}
${i?"byte-equivalent":"not yet verified"}
`:""}
Expected (Proof::EXPECTED_WITNESS_HEX)
SHA-256
${R.value||"(loading…)"}
Actual (last verify)
SHA-256
${z.value||"(not verified yet)"}
${a}
${this.expanded?c`
What this verifies
ADR-089 §5
Pressing Verify runs the canonical reference pipeline
(Proof::generate) end-to-end inside this browser's WASM Worker:
scene → Biot-Savart synthesis → material attenuation → NV ensemble → ADC + lock-in →
concatenated MagFrame bytes → SHA-256.
If the resulting hash matches the constant pinned at build time
(cc8de9b01b0ff5bd…), every constant — γ_e, D_GS, μ₀, T₂*, contrast, the PRNG
stream, the frame layout, the pipeline ordering — is byte-identical to the published
reference. If it doesn't match, something drifted; the dashboard names which.
This is the same regression test that runs in
cargo test -p nvsim — running in your browser, against your own WASM build.
`:""}
`}render(){return c`
this.tab="signal"}>Signal
this.tab="frame"}>Frame
this.tab="witness"}>Witness
${this.renderHeader()}
${this.tab==="signal"?this.renderSignalTab():this.tab==="frame"?this.renderFrameTab():this.renderWitnessTab()}
`}};Q.styles=y`
:host {
display: flex; flex-direction: column;
background: var(--bg-1);
border-left: 1px solid var(--line);
overflow: hidden;
height: 100%;
}
:host([expanded]) {
border-left: 0;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
}
:host([expanded]) .tabs {
padding: 0 24px;
background: var(--bg-1);
}
:host([expanded]) .tab {
padding: 16px 22px;
font-size: 13.5px;
flex: 0 0 auto;
}
:host([expanded]) .body {
padding: 24px 28px;
max-width: 1400px;
width: 100%;
margin: 0 auto;
}
:host([expanded]) .card { padding: 18px 20px; }
:host([expanded]) .card-h .ttl { font-size: 14px; }
:host([expanded]) svg { height: 220px; }
:host([expanded]) .frame-strip { height: 48px; }
:host([expanded]) table { font-size: 12.5px; }
:host([expanded]) td { padding: 6px 0; }
:host([expanded]) .hex { font-size: 12px; padding: 14px; line-height: 1.7; }
:host([expanded]) .witness-box { font-size: 13px; padding: 14px 16px; line-height: 1.6; }
:host([expanded]) .verify-btn { padding: 12px; font-size: 13px; }
:host([expanded]) .grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
:host([expanded]) .grid-2 > .card { margin-bottom: 0; }
@media (max-width: 1024px) {
:host([expanded]) .grid-2 { grid-template-columns: 1fr; }
}
.tabs {
display: flex; border-bottom: 1px solid var(--line);
}
.tab {
flex: 1;
padding: 11px 8px;
background: transparent; border: none;
font-size: 11.5px; font-weight: 500;
color: var(--ink-3);
border-bottom: 2px solid transparent;
cursor: pointer; transition: color 0.15s, border-color 0.15s;
}
.tab.active { color: var(--ink); border-bottom-color: var(--accent); }
.tab:hover { color: var(--ink-2); }
.body { padding: 14px; flex: 1; overflow-y: auto; }
.card {
background: var(--bg-2); border: 1px solid var(--line);
border-radius: var(--radius); padding: 12px;
margin-bottom: 12px;
}
.card-h {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 8px;
}
.card-h .ttl { font-size: 12px; font-weight: 600; }
.badge {
font-family: var(--mono); font-size: 10px;
padding: 2px 6px;
background: oklch(0.78 0.14 195 / 0.12);
color: var(--accent-2);
border-radius: 4px;
border: 1px solid oklch(0.78 0.14 195 / 0.3);
}
svg { width: 100%; height: 130px; }
.frame-strip {
height: 28px;
display: flex; align-items: flex-end; gap: 1px;
padding: 4px 0;
}
.bar {
flex: 1;
background: linear-gradient(to top, var(--accent-2), var(--accent));
border-radius: 1px;
min-height: 2px;
}
table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 10.5px; }
td { padding: 4px 0; border-bottom: 1px solid var(--line); }
td:first-child { color: var(--ink-3); }
td:last-child { text-align: right; color: var(--ink); }
.hex {
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 10px;
font-family: var(--mono);
font-size: 10.5px;
color: var(--ink-2);
line-height: 1.6;
overflow-x: auto;
white-space: nowrap;
}
.hex .magic { color: var(--accent); font-weight: 600; }
.witness-box {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-2);
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 6px;
padding: 8px 10px;
word-break: break-all;
line-height: 1.5;
}
.verify-btn {
margin-top: 10px;
width: 100%;
padding: 8px;
border: 1px solid var(--line);
background: var(--bg-3);
color: var(--ink);
border-radius: 8px;
cursor: pointer;
font-family: var(--mono);
font-size: 12px;
}
.verify-btn:hover { border-color: var(--accent); }
.verify-btn.ok { border-color: var(--ok); color: var(--ok); }
.verify-btn.fail { border-color: var(--bad); color: var(--bad); }
`;Te([u()],Q.prototype,"tab",2);Te([Ce({attribute:!1})],Q.prototype,"pinTab",2);Te([Ce({type:Boolean,reflect:!0})],Q.prototype,"expanded",2);Q=Te([k("nv-inspector")],Q);var Ct=Object.defineProperty,Pt=Object.getOwnPropertyDescriptor,Ke=(e,t,a,i)=>{for(var s=i>1?void 0:i?Pt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&Ct(t,a,s),s};let Se=class extends w{constructor(){super(...arguments),this.hIdx=-1,this.onKey=e=>{if(e.key==="Enter")this.exec(this.inputEl.value),this.inputEl.value="";else if(e.key==="ArrowUp"){const t=W.value;t.length&&(this.hIdx=Math.max(0,this.hIdx-1),this.inputEl.value=t[this.hIdx]??"",e.preventDefault())}else if(e.key==="ArrowDown"){const t=W.value;t.length&&(this.hIdx=Math.min(t.length,this.hIdx+1),this.inputEl.value=t[this.hIdx]??"",e.preventDefault())}}}connectedCallback(){super.connectedCallback(),f(()=>{A.value,Ee.value,re.value,this.requestUpdate()})}updated(){const e=this.renderRoot.querySelector(".body");e&&(e.scrollTop=e.scrollHeight)}counts(){const e={info:0,warn:0,err:0,dbg:0,ok:0};for(const t of A.value)e[t.level]=(e[t.level]??0)+1;return e.all=A.value.length,e}async exec(e){if(e=e.trim(),!e)return;d("info",`nvsim> ${e}`),pt(e),this.hIdx=W.value.length;const[t,...a]=e.split(/\s+/),i=a.join(" "),s=h();switch(t){case"help":d("info","commands: help · scene.list · sensor.config · run · pause · reset · seed · proof.verify · proof.export · clear · theme · status");break;case"scene.list":d("info","scene rebar-walkby-01:"),d("info"," rebar.steel.coil @ [+2.7, 0.0, +0.3] m χ=5000"),d("info"," dipole.heart_proxy @ [-1.4, +0.2, +0.4] m m=1.0e-6 A·m²"),d("info"," loop.mains_60Hz @ [-1.6, -0.4, 0.0] m I=2 A"),d("info"," eddy.door_steel @ [+0.0, +1.8, +0.4] m σ=1e6 S/m");break;case"sensor.config":d("info","NvSensor::cots_defaults() {"),d("info"," pos=[0,0,0], V=1mm³, N=1e12, C=0.03, T2*=200ns"),d("info"," D=2.870 GHz, γe=28 GHz/T, Γ=1.0 MHz, axes=4×〈111〉"),d("info"," δB ≈ 1.18 pT/√Hz (Barry 2020 §III.A) }");break;case"run":s&&(await s.run(),g.value=!0,d("ok","pipeline RUN"));break;case"pause":s&&(await s.pause(),g.value=!1,d("warn","pipeline PAUSED"));break;case"reset":s&&(await s.reset(),d("info","pipeline reset · t=0"));break;case"seed":{if(!i){d("info",`current seed = 0x${L.value.toString(16).toUpperCase()}`);break}const r=BigInt(i.startsWith("0x")?i:"0x"+i);L.value=r,s&&await s.setSeed(r),d("ok",`seed → 0x${r.toString(16).toUpperCase()}`);break}case"proof.verify":{if(!s)break;d("dbg","computing SHA-256 over 256 frames…");try{const r=R.value,n=new Uint8Array(32);for(let l=0;l<32;l++)n[l]=parseInt(r.slice(l*2,l*2+2),16);(await s.verifyWitness(n)).ok?($.value="ok",z.value=r,d("ok",`witness ${r.slice(0,16)}… matches · determinism gate ✓`)):($.value="fail",d("err","WITNESS MISMATCH"))}catch(r){d("err",`verify failed: ${r.message}`)}break}case"proof.export":{if(!s)break;d("dbg","building proof bundle…");try{const r=await s.exportProofBundle(),n=URL.createObjectURL(r),o=document.createElement("a");o.href=n,o.download=`nvsim-proof-${Date.now()}.json`,o.click(),URL.revokeObjectURL(n),d("ok",`proof bundle exported · ${r.size} bytes`)}catch(r){d("err",`export failed: ${r.message}`)}break}case"clear":A.value=[];break;case"theme":{const r=(i||"").toLowerCase();r==="light"||r==="dark"?(x.value=r,d("ok",`theme → ${r}`)):d("info","theme [light|dark]");break}case"status":d("info",`running=${g.value} seed=0x${L.value.toString(16).toUpperCase()} verified=${$.value}`);break;default:d("err",`unknown command: ${t} · try help`)}}render(){const e=this.counts(),t=Ee.value,a=A.value.filter(i=>t==="all"||i.level===t);return c`
${["all","info","warn","err","dbg"].map(i=>c`
Ee.value=i}>
${i} ${e[i]??0}
`)}
A.value=[]}>×
re.value=!re.value}>
${re.value?"▶":"❚❚"}
${a.map(i=>{const s=new Date(i.ts),r=`${String(s.getSeconds()).padStart(2,"0")}.${String(s.getMilliseconds()).padStart(3,"0")}`;return c`
`})}
nvsim>
`}};Se.styles=y`
:host {
display: flex; flex-direction: column;
background: var(--bg-1);
overflow: hidden;
}
.tabs {
display: flex; align-items: center;
border-bottom: 1px solid var(--line);
padding: 0 10px;
gap: 2px;
}
.tab {
padding: 8px 12px;
background: transparent; border: none;
font-size: 11.5px; color: var(--ink-3);
font-family: var(--mono);
border-bottom: 2px solid transparent;
cursor: pointer;
margin-bottom: -1px;
}
.tab.active { color: var(--ink); border-bottom-color: var(--accent); }
.tab .cnt {
background: var(--bg-3); padding: 1px 5px; border-radius: 999px;
font-size: 9.5px; color: var(--ink-2); margin-left: 4px;
}
.spacer { flex: 1; }
.tools { display: flex; gap: 4px; padding: 4px 0; }
.tools button {
width: 24px; height: 24px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-3);
font-size: 11px; cursor: pointer;
}
.tools button:hover { color: var(--ink); border-color: var(--line-2); }
.body {
flex: 1; overflow-y: auto;
font-family: var(--mono);
font-size: 11.5px;
padding: 6px 0;
background: var(--bg-0);
}
.line {
display: grid;
grid-template-columns: 70px 60px 1fr;
gap: 12px;
padding: 2px 12px;
color: var(--ink-2);
border-left: 2px solid transparent;
}
.line:hover { background: var(--bg-1); }
.ts { color: var(--ink-4); font-size: 10.5px; padding-top: 1px; }
.lvl {
font-size: 10px; font-weight: 600;
text-transform: uppercase; letter-spacing: 0.04em; padding-top: 1px;
}
.line.info .lvl { color: var(--accent-2); }
.line.warn .lvl { color: var(--warn); }
.line.warn { border-left-color: var(--warn); background: oklch(0.7 0.18 35 / 0.04); }
.line.err .lvl { color: var(--bad); }
.line.err { border-left-color: var(--bad); background: oklch(0.65 0.22 25 / 0.05); }
.line.dbg .lvl { color: var(--ink-3); }
.line.ok .lvl { color: var(--ok); }
.msg { color: var(--ink); white-space: pre-wrap; word-break: break-word; }
.input {
display: flex; align-items: center;
border-top: 1px solid var(--line);
background: var(--bg-0);
padding: 0 10px;
height: 32px; gap: 8px;
}
.prompt { color: var(--accent); font-family: var(--mono); font-size: 12px; }
input[type="text"] {
flex: 1; background: transparent; border: none; outline: none;
color: var(--ink); font-family: var(--mono); font-size: 12px;
height: 100%;
}
input::placeholder { color: var(--ink-4); }
`;Ke([Ue("#console-input")],Se.prototype,"inputEl",2);Se=Ke([k("nv-console")],Se);const oe=[{id:"nvsim",name:"nvsim — NV-diamond magnetometer",category:"sim",crate:"nvsim",summary:"Deterministic forward simulator: scene → Biot–Savart → NV ensemble → ADC → MagFrame stream + SHA-256 witness.",budget:"L",active:!0,status:"available",tags:["quantum","magnetometer","simulator","witness","wasm"],adr:"ADR-089"},{id:"gesture",name:"Gesture (DTW)",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Dynamic-Time-Warping gesture classifier from CSI motion templates.",events:[1],budget:"M",status:"available",tags:["hci","csi","classifier","dtw"],adr:"ADR-014"},{id:"coherence",name:"Coherence gate",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Z-score coherence scoring + Accept/PredictOnly/Reject/Recalibrate gate.",events:[2],budget:"S",status:"available",tags:["gate","csi","coherence","drift"],adr:"ADR-029"},{id:"adversarial",name:"Adversarial-signal detector",category:"ais",crate:"wifi-densepose-wasm-edge",summary:"Physically-impossible-signal detector — multi-link consistency, used to flag spoofed CSI.",events:[3],budget:"M",status:"available",tags:["security","csi","spoofing","mesh"],adr:"ADR-032"},{id:"rvf",name:"RVF — Rust Verified Feature stream",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Verified-frame builder with SHA-256 hash + version metadata for the feature stream.",budget:"S",status:"available",tags:["witness","csi","hash"],adr:"ADR-040"},{id:"occupancy",name:"Occupancy estimator",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Through-wall presence + person-count via CSI amplitude perturbation.",events:[300,301,302],budget:"S",status:"available",tags:["csi","building","presence"]},{id:"vital_trend",name:"Vital-trend monitor",category:"med",crate:"wifi-densepose-wasm-edge",summary:"HR + BR trend tracking with bradycardia/tachycardia/apnea events.",events:[100,101,102,103,104,105],budget:"S",status:"available",tags:["medical","vitals","csi"],adr:"ADR-021"},{id:"intrusion",name:"Intrusion detector",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Zone-based intrusion alert from CSI motion patterns.",events:[200,201],budget:"S",status:"available",tags:["security","zone","csi"]},{id:"med_sleep_apnea",name:"Sleep-apnea detector",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Episodic respiratory pause detection during sleep cycles.",events:[105],budget:"S",status:"available",tags:["medical","sleep","breathing"]},{id:"med_cardiac_arrhythmia",name:"Cardiac arrhythmia",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Beat-to-beat irregularity classifier from cardiac micro-Doppler.",events:[103,104],budget:"M",status:"available",tags:["medical","cardiac","arrhythmia"]},{id:"med_respiratory_distress",name:"Respiratory distress",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Distress signature: rapid shallow breathing + accessory-muscle motion.",events:[101,102],budget:"S",status:"available",tags:["medical","breathing","icu"]},{id:"med_gait_analysis",name:"Gait analysis",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Stride length, cadence, asymmetry from through-wall CSI pose tracking.",budget:"M",status:"available",tags:["medical","gait","pose"]},{id:"med_seizure_detect",name:"Seizure detector",category:"med",crate:"wifi-densepose-wasm-edge",summary:"Tonic-clonic seizure motion signature.",budget:"M",status:"beta",tags:["medical","neuro"]},{id:"sec_perimeter_breach",name:"Perimeter breach",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Approach/departure detection at user-defined boundary segments.",events:[210,211,212,213],budget:"S",status:"available",tags:["security","perimeter"]},{id:"sec_weapon_detect",name:"Metal anomaly / weapon",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Metal-perturbation flag in CSI; potential weapon presence (research).",events:[220,221,222],budget:"M",status:"research",tags:["security","metal","csi"]},{id:"sec_tailgating",name:"Tailgating detector",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Detect 2+ persons crossing a single-passage threshold.",events:[230,231,232],budget:"S",status:"available",tags:["security","access-control"]},{id:"sec_loitering",name:"Loitering detector",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"Stationary occupancy past a configurable dwell threshold.",events:[240,241,242],budget:"S",status:"available",tags:["security","dwell"]},{id:"sec_panic_motion",name:"Panic motion",category:"sec",crate:"wifi-densepose-wasm-edge",summary:"High-energy distress motion: struggle / fleeing pattern.",events:[250,251,252],budget:"S",status:"beta",tags:["security","distress"]},{id:"bld_hvac_presence",name:"HVAC presence",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Occupied/activity-level/departure-countdown for HVAC zones.",events:[310,311,312],budget:"S",status:"available",tags:["hvac","building","energy"]},{id:"bld_lighting_zones",name:"Lighting zones",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Per-zone light on/dim/off cues from occupancy.",events:[320,321,322],budget:"S",status:"available",tags:["lighting","building"]},{id:"bld_elevator_count",name:"Elevator count",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Person count inside elevator car from CSI.",events:[330],budget:"S",status:"available",tags:["elevator","building"]},{id:"bld_meeting_room",name:"Meeting-room utilization",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Meeting size + duration analytics for booking systems.",budget:"S",status:"available",tags:["meeting","analytics"]},{id:"bld_energy_audit",name:"Energy audit",category:"bld",crate:"wifi-densepose-wasm-edge",summary:"Continuous occupancy-vs-HVAC-state audit for energy savings.",budget:"M",status:"available",tags:["energy","audit"]},{id:"ret_queue_length",name:"Queue length",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Live queue-length tracking for checkout / kiosks.",budget:"S",status:"available",tags:["retail","queue"]},{id:"ret_dwell_heatmap",name:"Dwell heatmap",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Per-zone dwell time accumulation; analytics-only export.",budget:"M",status:"available",tags:["retail","heatmap"]},{id:"ret_customer_flow",name:"Customer flow",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Origin-destination flow graph through a store layout.",budget:"M",status:"available",tags:["retail","flow"]},{id:"ret_table_turnover",name:"Table turnover",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Restaurant table seat / vacate transitions.",budget:"S",status:"available",tags:["retail","restaurant"]},{id:"ret_shelf_engagement",name:"Shelf engagement",category:"ret",crate:"wifi-densepose-wasm-edge",summary:"Reach-to-shelf gestures and dwell at product zones.",budget:"M",status:"available",tags:["retail","shelf"]},{id:"ind_forklift_proximity",name:"Forklift proximity",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Worker-near-forklift safety alert.",budget:"S",status:"available",tags:["industrial","safety"]},{id:"ind_confined_space",name:"Confined-space monitor",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Last-person-out detection + presence audit for OSHA confined-space entries.",budget:"S",status:"available",tags:["industrial","osha"]},{id:"ind_clean_room",name:"Clean-room PPE / motion",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Motion patterns consistent with proper PPE-clad movement.",budget:"M",status:"beta",tags:["industrial","cleanroom"]},{id:"ind_livestock_monitor",name:"Livestock monitor",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Vital-sign + activity tracking for stall-bound livestock.",budget:"M",status:"beta",tags:["agriculture","livestock"]},{id:"ind_structural_vibration",name:"Structural vibration",category:"ind",crate:"wifi-densepose-wasm-edge",summary:"Building/equipment micro-vibration via CSI phase derivative.",budget:"M",status:"research",tags:["industrial","vibration"]},{id:"sig_coherence_gate",name:"Coherence gate (extended)",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Hysteresis + multi-state coherence gate driving downstream apps.",budget:"S",status:"available",tags:["gate","csi"]},{id:"sig_flash_attention",name:"Flash attention (CSI)",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Edge-friendly attention block for CSI subcarrier weighting.",budget:"M",status:"beta",tags:["attention","csi"]},{id:"sig_temporal_compress",name:"Temporal-tensor compress",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"RuVector temporal-tensor compression on the CSI buffer.",budget:"M",status:"available",tags:["compress","tensor"]},{id:"sig_sparse_recovery",name:"Sparse recovery",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"114→56 subcarrier sparse interpolation via L1 solver.",budget:"M",status:"available",tags:["sparse","csi"]},{id:"sig_mincut_person_match",name:"Mincut person-match",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"Min-cut person assignment across multistatic frames.",budget:"M",status:"available",tags:["mincut","matching"]},{id:"sig_optimal_transport",name:"Optimal transport",category:"sig",crate:"wifi-densepose-wasm-edge",summary:"OT-based feature alignment between mesh nodes.",budget:"M",status:"beta",tags:["ot","alignment"]},{id:"lrn_dtw_gesture_learn",name:"DTW gesture learn",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"On-device template learning for personalized gesture libraries.",budget:"M",status:"beta",tags:["lifelong","gesture"]},{id:"lrn_anomaly_attractor",name:"Anomaly attractor",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"Novelty detector with dynamic-attractor recall.",budget:"M",status:"research",tags:["novelty","lifelong"]},{id:"lrn_meta_adapt",name:"Meta-adapt",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"Meta-learning adapter for fast site-to-site transfer.",budget:"L",status:"research",tags:["meta-learning"]},{id:"lrn_ewc_lifelong",name:"EWC++ lifelong",category:"lrn",crate:"wifi-densepose-wasm-edge",summary:"Elastic-weight-consolidation gate to avoid catastrophic forgetting.",budget:"M",status:"beta",tags:["lifelong","ewc"]},{id:"spt_pagerank_influence",name:"PageRank influence",category:"spt",crate:"wifi-densepose-wasm-edge",summary:"Graph-influence ranking on the multistatic mesh.",budget:"M",status:"beta",tags:["graph","pagerank"]},{id:"spt_micro_hnsw",name:"µHNSW vector index",category:"spt",crate:"wifi-densepose-wasm-edge",summary:"Tiny HNSW index for AETHER re-ID embeddings on-device.",budget:"M",status:"available",tags:["hnsw","reid"]},{id:"spt_spiking_tracker",name:"Spiking tracker",category:"spt",crate:"wifi-densepose-wasm-edge",summary:"Spiking-network multi-target tracker.",budget:"L",status:"research",tags:["snn","tracker"]},{id:"tmp_pattern_sequence",name:"Pattern sequence",category:"tmp",crate:"wifi-densepose-wasm-edge",summary:"Sequence-of-events pattern matcher (e.g. ingress→linger→egress).",budget:"M",status:"available",tags:["temporal","pattern"]},{id:"tmp_temporal_logic_guard",name:"Temporal logic guard",category:"tmp",crate:"wifi-densepose-wasm-edge",summary:"LTL/MTL safety-property guard over event streams.",budget:"M",status:"beta",tags:["ltl","safety"]},{id:"tmp_goap_autonomy",name:"GOAP autonomy",category:"tmp",crate:"wifi-densepose-wasm-edge",summary:"Goal-oriented action planning for adaptive routines.",budget:"L",status:"research",tags:["planning","autonomy"]},{id:"ais_prompt_shield",name:"Prompt shield",category:"ais",crate:"wifi-densepose-wasm-edge",summary:"Edge-side LLM prompt-injection guard for on-device assistants.",budget:"M",status:"beta",tags:["security","llm"]},{id:"ais_behavioral_profiler",name:"Behavioral profiler",category:"ais",crate:"wifi-densepose-wasm-edge",summary:"Anomalous-behaviour profiler (drift in motion habits).",budget:"M",status:"beta",tags:["anomaly","behaviour"]},{id:"qnt_quantum_coherence",name:"Quantum coherence",category:"qnt",crate:"wifi-densepose-wasm-edge",summary:"Coherence diagnostics adapted for quantum-sensor signals.",budget:"M",status:"research",tags:["quantum","coherence"]},{id:"qnt_interference_search",name:"Interference search",category:"qnt",crate:"wifi-densepose-wasm-edge",summary:"Interferometric anomaly search across mesh viewpoints.",budget:"L",status:"research",tags:["quantum","interference"]},{id:"aut_psycho_symbolic",name:"Psycho-symbolic agent",category:"aut",crate:"wifi-densepose-wasm-edge",summary:"Symbolic-rule + neural-feature hybrid for low-power autonomy loops.",budget:"L",status:"research",tags:["autonomy","symbolic"]},{id:"aut_self_healing_mesh",name:"Self-healing mesh",category:"aut",crate:"wifi-densepose-wasm-edge",summary:"Mesh-topology repair with per-node health gossip.",budget:"M",status:"beta",tags:["mesh","health"]},{id:"exo_ghost_hunter",name:"Ghost hunter (anomaly)",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Empty-room CSI anomaly detector — impulsive/periodic/drift/random + hidden-presence sub-detector.",events:[650,651,652,653],budget:"S",status:"available",tags:["anomaly","paranormal","csi"],adr:"ADR-041"},{id:"exo_breathing_sync",name:"Breathing sync",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Multi-person breathing synchrony analytics.",budget:"M",status:"beta",tags:["breathing","sync"]},{id:"exo_dream_stage",name:"Dream-stage classifier",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"NREM/REM stage classification from breathing + micro-motion.",budget:"M",status:"research",tags:["sleep","rem"]},{id:"exo_emotion_detect",name:"Emotion detector",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Coarse arousal/valence from breathing + heart-rate variability.",budget:"M",status:"research",tags:["affect"]},{id:"exo_gesture_language",name:"Gesture language",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Sign-language pattern recognition.",budget:"L",status:"research",tags:["hci","sign"]},{id:"exo_happiness_score",name:"Happiness score",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Aggregate well-being score from co-occupancy + activity dynamics.",budget:"M",status:"research",tags:["affect","wellbeing"]},{id:"exo_hyperbolic_space",name:"Hyperbolic space embed",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Hyperbolic embeddings for hierarchical scene structure.",budget:"L",status:"research",tags:["embedding","hyperbolic"]},{id:"exo_music_conductor",name:"Music conductor",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Map gesture energy to MIDI tempo/dynamics.",budget:"M",status:"research",tags:["midi","art"]},{id:"exo_plant_growth",name:"Plant-growth tracker",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Slow CSI drift tracking for greenhouse foliage growth.",budget:"L",status:"research",tags:["agriculture"]},{id:"exo_rain_detect",name:"Rain detector",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Outdoor CSI signature of rainfall.",budget:"M",status:"research",tags:["weather"]},{id:"exo_time_crystal",name:"Time-crystal periodicity",category:"exo",crate:"wifi-densepose-wasm-edge",summary:"Periodicity diagnostics with anti-aliasing harmonics.",budget:"M",status:"research",tags:["periodicity"]}],se={sim:{label:"Simulators",color:"oklch(0.78 0.14 70)",range:"—"},med:{label:"Medical & Health",color:"oklch(0.65 0.22 25)",range:"100–199"},sec:{label:"Security & Safety",color:"oklch(0.7 0.18 35)",range:"200–299"},bld:{label:"Smart Building",color:"oklch(0.78 0.12 195)",range:"300–399"},ret:{label:"Retail & Hospitality",color:"oklch(0.78 0.14 145)",range:"400–499"},ind:{label:"Industrial",color:"oklch(0.72 0.18 330)",range:"500–599"},sig:{label:"Signal Processing",color:"oklch(0.78 0.14 70)",range:"600–619"},lrn:{label:"Online Learning",color:"oklch(0.78 0.12 260)",range:"620–639"},spt:{label:"Spatial / Graph",color:"oklch(0.7 0.18 100)",range:"640–659"},tmp:{label:"Temporal / Planning",color:"oklch(0.7 0.16 50)",range:"660–679"},ais:{label:"AI Safety",color:"oklch(0.65 0.22 25)",range:"700–719"},qnt:{label:"Quantum",color:"oklch(0.72 0.18 290)",range:"720–739"},aut:{label:"Autonomy",color:"oklch(0.78 0.14 145)",range:"740–759"},exo:{label:"Exotic / Research",color:"oklch(0.72 0.18 330)",range:"650–699"}};function Tt(){return oe.map(e=>({id:e.id,active:e.active===!0,eventCount:0}))}function At(e,t){if(!e)return 1;const a=e.toLowerCase();let i=0;return t.id.toLowerCase().includes(a)&&(i+=3),t.name.toLowerCase().includes(a)&&(i+=3),t.summary.toLowerCase().includes(a)&&(i+=1),t.tags?.some(s=>s.toLowerCase().includes(a))&&(i+=2),t.category===a&&(i+=5),i}const Rt="nvsim",Et=1,Z="kv";let fe=null;function Xe(){return fe||(fe=new Promise((e,t)=>{const a=indexedDB.open(Rt,Et);a.onupgradeneeded=()=>{const i=a.result;i.objectStoreNames.contains(Z)||i.createObjectStore(Z)},a.onsuccess=()=>e(a.result),a.onerror=()=>t(a.error)}),fe)}async function O(e){const t=await Xe();return await new Promise((a,i)=>{const r=t.transaction(Z,"readonly").objectStore(Z).get(e);r.onsuccess=()=>a(r.result),r.onerror=()=>i(r.error)})}async function I(e,t){const a=await Xe();return await new Promise((i,s)=>{const r=a.transaction(Z,"readwrite");r.objectStore(Z).put(t,e),r.oncomplete=()=>i(),r.onerror=()=>s(r.error)})}var Dt=Object.defineProperty,Ft=Object.getOwnPropertyDescriptor,Ye=(e,t,a,i)=>{for(var s=i>1?void 0:i?Ft(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&Dt(t,a,s),s};const H=p(Tt()),ie=p(""),F=p("all"),S=p("all");(async()=>{const e=await O("app-activations");e&&(H.value=e)})();f(()=>{const e=H.value;e.length>0&&I("app-activations",e)});let _e=class extends w{constructor(){super(...arguments),this.renderTick=0}connectedCallback(){super.connectedCallback(),f(()=>{H.value,ie.value,F.value,S.value,this.renderTick++})}isActive(e){return H.value.find(t=>t.id===e)?.active===!0}toggle(e){const t=H.value.map(a=>a.id===e.id?{...a,active:!a.active,lastActivatedAt:Date.now()}:a);H.value=t,d(this.isActive(e.id)?"ok":"info",`app ${e.id} deactivated`)}filtered(){let e=oe;return F.value!=="all"&&(e=e.filter(t=>t.category===F.value)),S.value!=="all"&&(e=e.filter(t=>t.status===S.value)),ie.value.trim()&&(e=e.map(t=>({a:t,s:At(ie.value,t)})).filter(t=>t.s>0).sort((t,a)=>a.s-t.s).map(t=>t.a)),e}categoryCounts(){const e={all:oe.length};for(const t of Object.keys(se))e[t]=0;for(const t of oe)e[t.category]=(e[t.category]??0)+1;return e}render(){const e=this.filtered(),t=this.categoryCounts(),a=H.value.filter(i=>i.active).length;return c`
F.value="all"}>
All${t.all}
${Object.keys(se).map(i=>c`
F.value=i}>
${se[i].label}
${t[i]??0}
`)}
S.value="all"}>any
S.value="available"}>available
S.value="beta"}>beta
S.value="research"}>research
${e.length===0?c`No apps match the current filters.
`:c`${e.map(i=>this.card(i))}
`}
`}card(e){const t=this.isActive(e.id),a=se[e.category];return c`
${e.name}
${e.summary}
${a.label}
${e.status}
${e.budget?c`budget ${e.budget} `:""}
${e.adr?c`${e.adr} `:""}
${e.events?.length?c`events ${e.events.join("·")} `:""}
`}};_e.styles=y`
:host {
display: block;
height: 100%;
overflow-y: auto;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
padding: 24px;
}
.head {
display: flex; align-items: center; gap: 16px;
margin-bottom: 18px;
flex-wrap: wrap;
}
.ttl {
font-size: 22px; font-weight: 700; letter-spacing: -0.02em;
color: var(--ink);
flex: 1; min-width: 200px;
}
.ttl small {
font-size: 12.5px; font-weight: 400;
color: var(--ink-3); margin-left: 8px;
}
.search {
width: 320px; max-width: 100%;
padding: 8px 12px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 8px;
font-family: var(--mono);
font-size: 12.5px;
color: var(--ink); outline: none;
}
.search:focus { border-color: var(--accent); }
.filters {
display: flex; flex-wrap: wrap; gap: 6px;
margin-bottom: 18px;
}
.chip {
padding: 4px 10px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 999px;
font-size: 11.5px; color: var(--ink-3);
cursor: pointer;
font-family: var(--mono);
display: inline-flex; align-items: center; gap: 4px;
}
.chip:hover { color: var(--ink); border-color: var(--line-2); }
.chip.on { background: var(--bg-3); border-color: var(--accent); color: var(--ink); }
.chip .swatch {
width: 7px; height: 7px; border-radius: 50%;
}
.chip .count { color: var(--ink-3); font-size: 10px; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 12px;
}
.card {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 12px 14px;
display: flex; flex-direction: column; gap: 6px;
transition: border-color 0.15s, transform 0.15s;
position: relative;
}
.card:hover { border-color: var(--line-2); transform: translateY(-1px); }
.card.active {
border-color: oklch(0.78 0.14 145 / 0.7);
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 145 / 0.04) 100%);
}
.card-h {
display: flex; align-items: flex-start; gap: 8px;
margin-bottom: 2px;
}
.card-h .name {
font-size: 13.5px; font-weight: 600; color: var(--ink);
flex: 1; line-height: 1.3;
}
.card-h .swatch {
width: 10px; height: 10px; border-radius: 50%;
flex-shrink: 0; margin-top: 4px;
}
.summary {
font-size: 12px; color: var(--ink-2); line-height: 1.45;
flex: 1;
}
.meta {
display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px;
font-family: var(--mono); font-size: 10px;
}
.badge {
padding: 1px 6px; border-radius: 4px;
background: var(--bg-3); color: var(--ink-3);
border: 1px solid var(--line);
}
.badge.cat { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.3); }
.badge.status-available { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }
.badge.status-beta { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }
.badge.status-research { color: var(--accent-3); border-color: oklch(0.72 0.18 330 / 0.4); }
.badge.budget { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.3); }
.card-foot {
display: flex; align-items: center; gap: 8px;
padding-top: 8px; margin-top: 4px;
border-top: 1px solid var(--line);
font-size: 11px; color: var(--ink-3);
}
.toggle {
position: relative;
width: 32px; height: 18px;
background: var(--bg-3); border: 1px solid var(--line-2);
border-radius: 999px; cursor: pointer;
transition: background 0.15s;
flex-shrink: 0;
}
.toggle::after {
content: ''; position: absolute;
top: 1px; left: 1px;
width: 12px; height: 12px;
background: var(--ink-3); border-radius: 50%;
transition: transform 0.15s, background 0.15s;
}
.toggle.on { background: var(--accent); border-color: var(--accent); }
.toggle.on::after { background: #1a0f00; transform: translateX(14px); }
.events {
font-family: var(--mono); font-size: 10px; color: var(--ink-3);
flex: 1;
}
.empty {
padding: 40px;
text-align: center; color: var(--ink-3);
font-size: 13px;
}
`;Ye([u()],_e.prototype,"renderTick",2);_e=Ye([k("nv-app-store")],_e);var Ot=Object.defineProperty,It=Object.getOwnPropertyDescriptor,ge=(e,t,a,i)=>{for(var s=i>1?void 0:i?It(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&Ot(t,a,s),s};let U=class extends w{constructor(){super(...arguments),this.open=!1,this.filter="",this.idx=0,this.cmds=[{ico:"▶",label:"Run pipeline",kbd:"Space",run:async()=>{await h()?.run(),g.value=!0,P("Pipeline running","▶")}},{ico:"❚",label:"Pause pipeline",run:async()=>{await h()?.pause(),g.value=!1,P("Paused","❚❚")}},{ico:"+",label:"New scene…",kbd:"⌘N",run:()=>ne({title:"New scene",body:`Build a fresh magnetic scene. The dashboard generates the JSON
and pushes it to the running pipeline (or you can copy the JSON
for offline use).
Name
Heart-proxy dipole moment (A·m²)
Distance heart → sensor (m)
Add ferrous distractor at +x = 1 m?
No
Yes (steel coil, χ=5000)
Add 60 Hz mains-current loop?
No
Yes (2 A loop, 5 cm radius, +y = 1 m)
`,buttons:[{label:"Cancel",variant:"ghost"},{label:"Create",variant:"primary",onClick:async()=>{const e=document.querySelector("nv-app")?.shadowRoot?.querySelector("nv-modal")?.shadowRoot;if(!e)return;const t=(e.querySelector("#ns-name")?.value??"custom").trim(),a=parseFloat(e.querySelector("#ns-moment")?.value??"1e-6"),i=parseFloat(e.querySelector("#ns-distance")?.value??"0.5"),s=e.querySelector("#ns-ferrous")?.value==="1",r=e.querySelector("#ns-mains")?.value==="1",n={dipoles:[{position:[0,0,i],moment:[0,0,a]}],loops:r?[{centre:[0,1,0],normal:[0,1,0],radius:.05,current:2,n_segments:64}]:[],ferrous:s?[{position:[1,0,0],volume:1e-4,susceptibility:5e3}]:[],eddy:[],sensors:[[0,0,0]],ambient_field:[1e-6,0,0]};await h()?.loadScene(n),d("ok",`scene ${t} loaded · 1 dipole · ${r?"1 loop · ":""}${s?"1 ferrous · ":""}1 sensor`),P(`Scene "${t}" loaded`,"+")}}]})},{ico:"📦",label:"Export proof bundle…",kbd:"⌘E",run:async()=>{const e=h();if(e){d("dbg","building proof bundle…");try{const t=await e.exportProofBundle(),a=URL.createObjectURL(t),i=document.createElement("a");i.href=a,i.download=`nvsim-proof-${Date.now()}.json`,i.click(),URL.revokeObjectURL(a),d("ok",`proof bundle exported · ${t.size} bytes`),P(`Proof bundle saved (${t.size} B)`,"📦")}catch(t){d("err",`export failed: ${t.message}`)}}}},{ico:"⟳",label:"Reset pipeline",kbd:"⌘R",run:()=>ne({title:"Reset pipeline?",body:"Clears the frame stream and rewinds t to 0.
",buttons:[{label:"Cancel",variant:"ghost"},{label:"Reset",variant:"danger",onClick:async()=>{await h()?.reset(),d("warn","pipeline reset · t=0"),P("Pipeline reset","⟳")}}]})},{ico:"✓",label:"Verify witness",run:async()=>{const e=h();if(!e)return;$.value="pending";const t=R.value,a=new Uint8Array(32);for(let s=0;s<32;s++)a[s]=parseInt(t.slice(s*2,s*2+2),16);(await e.verifyWitness(a)).ok?($.value="ok",z.value=t,P("Witness verified","✓")):($.value="fail",P("Witness mismatch!","✗"))}},{ico:"☼",label:"Toggle theme",kbd:"⌘/",run:()=>{x.value=x.value==="dark"?"light":"dark"}},{ico:"⚙",label:"Open settings",kbd:"⌘,",run:()=>window.dispatchEvent(new CustomEvent("open-settings"))},{ico:"?",label:"Keyboard shortcuts…",run:()=>ne({title:"Keyboard shortcuts",body:`
⌘K / Ctrl K
Command palette
Space
Play / pause
⌘R
Reset
⌘,
Settings
⌘/
Toggle theme
\`
Debug HUD
1 · 2 · 3
Inspector tabs
Esc
Close modal/palette
/
Focus REPL
`,buttons:[{label:"Close",variant:"primary"}]})},{ico:"i",label:"About nvsim…",run:()=>ne({title:"About nvsim",body:`nvsim is a deterministic, byte-reproducible forward simulator for nitrogen-vacancy diamond magnetometry.
This dashboard runs nvsim as WASM in a Web Worker. Same (scene, config, seed) → byte-identical SHA-256 witness across runs and machines.
License: MIT OR Apache-2.0 · See ADR-089, ADR-092.
`,buttons:[{label:"Close",variant:"primary"}]})}],this.onKey=e=>{(e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==="k"?(e.preventDefault(),this.openPal()):e.key==="Escape"&&this.open?this.closePal():this.open&&(e.key==="ArrowDown"?(this.idx=Math.min(this.cmds.length-1,this.idx+1),e.preventDefault()):e.key==="ArrowUp"?(this.idx=Math.max(0,this.idx-1),e.preventDefault()):e.key==="Enter"&&(this.runIdx(),e.preventDefault()))},this.onOpen=()=>this.openPal()}connectedCallback(){super.connectedCallback(),window.addEventListener("keydown",this.onKey),window.addEventListener("nv-palette",this.onOpen)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.onKey),window.removeEventListener("nv-palette",this.onOpen)}openPal(){this.open=!0,this.setAttribute("open",""),this.filter="",this.idx=0,setTimeout(()=>this.inputEl?.focus(),0)}closePal(){this.open=!1,this.removeAttribute("open")}filtered(){if(!this.filter.trim())return this.cmds;const e=this.filter.toLowerCase();return this.cmds.filter(t=>t.label.toLowerCase().includes(e))}runIdx(){const t=this.filtered()[this.idx];t&&(t.run(),this.closePal())}render(){const e=this.filtered();return c`
{this.filter=t.target.value,this.idx=0}} />
${e.map((t,a)=>c`
{this.idx=a,this.runIdx()}}>
${t.ico}
${t.label}
${t.kbd?c`${t.kbd} `:""}
`)}
`}};U.styles=y`
:host {
position: fixed; inset: 0; z-index: 220;
background: rgba(0,0,0,0.5);
opacity: 0; pointer-events: none;
transition: opacity 0.15s;
display: flex; justify-content: center; padding-top: 12vh;
backdrop-filter: blur(4px);
}
:host([open]) { opacity: 1; pointer-events: auto; }
.palette {
width: min(560px, 92vw);
background: var(--bg-1);
border: 1px solid var(--line-2);
border-radius: var(--radius);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
overflow: hidden;
display: flex; flex-direction: column;
max-height: 60vh;
}
.input {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
}
input {
width: 100%;
background: transparent; border: none; outline: none;
color: var(--ink); font-size: 14px;
font-family: inherit;
}
.list { flex: 1; overflow-y: auto; padding: 4px; }
.item {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 12.5px;
}
.item.active { background: var(--bg-3); }
.item .ico { width: 20px; text-align: center; color: var(--accent); }
.item .lbl { flex: 1; }
.item .kbd {
font-family: var(--mono); font-size: 10.5px;
color: var(--ink-3);
padding: 1px 5px; background: var(--bg-3); border-radius: 4px;
}
`;ge([u()],U.prototype,"open",2);ge([u()],U.prototype,"filter",2);ge([u()],U.prototype,"idx",2);ge([Ue("#palette-input")],U.prototype,"inputEl",2);U=ge([k("nv-palette")],U);var Ht=Object.defineProperty,Lt=Object.getOwnPropertyDescriptor,He=(e,t,a,i)=>{for(var s=i>1?void 0:i?Lt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&Ht(t,a,s),s};let pe=class extends w{constructor(){super(...arguments),this.open=!1,this.renderFps=0,this.lastTs=performance.now(),this.frameCount=0,this.rafId=0,this.onKey=e=>{e.key==="`"&&!e.target.matches("input, textarea")&&(this.open=!this.open,this.toggleAttribute("open",this.open))},this.tick=()=>{this.rafId=requestAnimationFrame(this.tick);const e=performance.now();this.frameCount++,e-this.lastTs>=500&&(this.renderFps=this.frameCount*1e3/(e-this.lastTs),this.frameCount=0,this.lastTs=e,this.requestUpdate())}}connectedCallback(){super.connectedCallback(),window.addEventListener("keydown",this.onKey),f(()=>{M.value,Fe.value,V.value,N.value,dt.value,this.requestUpdate()}),this.tick()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.onKey),cancelAnimationFrame(this.rafId)}render(){return c`
nvsim · debug {this.open=!1,this.removeAttribute("open")}}>✕
render fps ${this.renderFps.toFixed(1)}
sim fps ${M.value>0?Math.round(M.value):"—"}
frames ${Fe.value.toString()}
|B| ${(V.value*1e9).toFixed(3)} nT
SNR ${N.value>0?N.value.toFixed(1):"—"}
DOM ${document.querySelectorAll("*").length}
`}};pe.styles=y`
:host {
position: fixed; bottom: 8px; right: 8px;
width: 220px;
background: rgba(13,17,23,0.85);
backdrop-filter: blur(8px);
border: 1px solid var(--line-2);
border-radius: 8px;
padding: 8px 10px;
font-family: var(--mono); font-size: 11px;
color: var(--ink-2);
z-index: 99;
display: none;
box-shadow: var(--shadow);
}
:host([open]) { display: block; }
.h {
display: flex; justify-content: space-between;
font-weight: 600; color: var(--ink);
margin-bottom: 6px; padding-bottom: 4px;
border-bottom: 1px solid var(--line);
}
.x { cursor: pointer; color: var(--ink-3); }
.row {
display: flex; justify-content: space-between;
padding: 1px 0;
}
.k { color: var(--ink-3); }
.v { color: var(--ink); }
`;He([u()],pe.prototype,"open",2);He([u()],pe.prototype,"renderFps",2);pe=He([k("nv-debug-hud")],pe);var Nt=Object.defineProperty,Bt=Object.getOwnPropertyDescriptor,Je=(e,t,a,i)=>{for(var s=i>1?void 0:i?Bt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&Nt(t,a,s),s};let ze=class extends w{constructor(){super(...arguments),this.open=!1}connectedCallback(){super.connectedCallback(),f(()=>{x.value,_.value,C.value,be.value,T.value,Re.value,this.requestUpdate()}),window.addEventListener("open-settings",()=>{this.open=!0,this.setAttribute("open","")})}close(){this.open=!1,this.removeAttribute("open")}render(){return c`
this.close()}>
Appearance
x.value="dark"}>dark
x.value="light"}>light
Density
Affects panel padding and font scale.
_.value="comfy"}>comfy
_.value="default"}>default
_.value="compact"}>compact
Reduce motion
Disable rotating crystal & field-line animation.
C.value=!C.value}>
Pipeline
Auto-rerun on edit
Restart pipeline when scene/config changes.
be.value=!be.value}>
Transport
T.value="wasm"}>WASM
T.value="ws"}>WS
${T.value==="ws"?c`
`:""}
`}};ze.styles=y`
:host {
position: fixed; top: 0; right: 0; bottom: 0;
width: 420px; max-width: 100vw;
background: var(--bg-1);
border-left: 1px solid var(--line);
z-index: 51;
transform: translateX(100%);
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
display: flex; flex-direction: column;
box-shadow: -20px 0 60px -20px rgba(0,0,0,0.5);
}
:host([open]) { transform: translateX(0); }
.scrim {
position: fixed; inset: 0;
background: rgba(0,0,0,0.5);
z-index: 50;
opacity: 0; pointer-events: none;
transition: opacity 0.2s;
}
:host([open]) .scrim { opacity: 1; pointer-events: auto; }
.h {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
display: flex; align-items: center; justify-content: space-between;
}
.h .ttl { font-size: 14px; font-weight: 600; }
.body { flex: 1; overflow-y: auto; padding: 16px; }
.group { margin-bottom: 22px; }
.group h4 {
margin: 0 0 10px;
font-size: 11px; font-weight: 600;
text-transform: uppercase; letter-spacing: 0.08em;
color: var(--ink-3);
}
.row {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 0;
border-bottom: 1px solid var(--line);
}
.row:last-child { border-bottom: 0; }
.row .lbl { font-size: 13px; }
.row .desc { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.row > div:first-child { flex: 1; padding-right: 12px; }
.seg {
display: inline-flex;
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 2px;
}
.seg button {
padding: 4px 10px;
background: transparent; border: none;
border-radius: 6px;
font-size: 11.5px; color: var(--ink-3);
font-family: var(--mono);
cursor: pointer;
}
.seg button.on { background: var(--bg-1); color: var(--ink); }
.toggle {
position: relative;
width: 36px; height: 20px;
background: var(--bg-3);
border: 1px solid var(--line-2);
border-radius: 999px;
cursor: pointer;
flex-shrink: 0;
}
.toggle::after {
content: ''; position: absolute;
top: 2px; left: 2px;
width: 14px; height: 14px;
background: var(--ink-3);
border-radius: 50%;
transition: transform 0.15s, background 0.15s;
}
.toggle.on { background: var(--accent); border-color: var(--accent); }
.toggle.on::after { background: #1a0f00; transform: translateX(16px); }
.close {
width: 28px; height: 28px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink-2);
}
input[type="text"] {
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 6px;
padding: 6px 10px;
color: var(--ink); font-family: var(--mono); font-size: 12px;
outline: none;
}
`;Je([u()],ze.prototype,"open",2);ze=Je([k("nv-settings-drawer")],ze);var Wt=Object.defineProperty,Vt=Object.getOwnPropertyDescriptor,Le=(e,t,a,i)=>{for(var s=i>1?void 0:i?Vt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&Wt(t,a,s),s};const xe=[{title:"Welcome to nvsim",body:`nvsim is an open-source, deterministic forward simulator for
nitrogen-vacancy diamond magnetometry — a real Rust crate compiled to
WASM and running in your browser, right now.
This 30-second tour highlights the four panels you'll use most.
`,cta:"Start tour"},{title:"1. Scene canvas",body:`The middle panel shows your magnetic scene — sources you can
drag (rebar, heart proxy, mains hum, ferrous door) and a single NV-diamond
sensor in the centre. Field lines from each source connect to the sensor
and animate while the pipeline runs.
Click 2 on your keyboard any time to jump to the Frame inspector.
`},{title:"2. Run the pipeline",body:`Click the ▶ Run button (top-right) to start streaming
MagFrame records at the digitiser's sample rate. The B-vector
trace and Frame stream sparkline update live, and the FPS pill in the
topbar shows the simulator's throughput in kHz.
Space toggles run/pause from anywhere.
`},{title:"3. Witness panel",body:`The Witness tab is the heart of nvsim's determinism contract.
Click Verify and the pipeline re-derives the SHA-256 over a 256-frame
reference run and asserts it matches the constant pinned in the Rust crate.
Same input → same hash → byte-for-byte across browsers, OSes, transports.
If the hash drifts, your build is non-canonical.
`},{title:"4. App Store",body:`The grid icon on the left rail opens the App Store — every
hot-loadable WASM edge module RuView ships, plus the simulators. 66 apps
across 13 categories: medical, security, building, retail, industrial,
signal, learning, autonomy, and more.
Toggle any card to mark it active in this session; the WS transport
will push the activation set to a connected ESP32 mesh.
`},{title:"You are ready",body:`Press ⌘K (or Ctrl K ) any time for the command
palette, ? for the full shortcuts list, or just start clicking.
Source on GitHub:
github.com/ruvnet/RuView · ADR-089, ADR-092 · MIT/Apache-2.0.
`,cta:"Get started"}];let ue=class extends w{constructor(){super(...arguments),this.open=!1,this.step=0,this.show=()=>{this.step=0,this.open=!0,this.setAttribute("open","")}}async connectedCallback(){super.connectedCallback(),window.addEventListener("nv-show-tour",this.show),await O("onboarding-seen")||(this.open=!0,this.setAttribute("open",""))}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-show-tour",this.show)}async dismiss(){this.open=!1,this.removeAttribute("open"),await I("onboarding-seen",!0)}next(){this.step0&&this.step--}render(){const e=xe[this.step];return c`
${e.title}
this.dismiss()} aria-label="Skip tour">×
`}};ue.styles=y`
:host {
position: fixed; inset: 0;
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(4px);
z-index: 240;
display: grid; place-items: center;
opacity: 0; pointer-events: none;
transition: opacity 0.18s;
}
:host([open]) { opacity: 1; pointer-events: auto; }
.card {
background: var(--bg-1);
border: 1px solid var(--line-2);
border-radius: var(--radius);
box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
width: min(560px, 92vw);
max-height: 86vh;
display: flex; flex-direction: column;
transform: translateY(12px) scale(0.98);
transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);
}
:host([open]) .card { transform: translateY(0) scale(1); }
.h {
padding: 20px 22px 8px;
display: flex; justify-content: space-between; align-items: flex-start;
}
.h h2 { margin: 0; font-size: 18px; letter-spacing: -0.01em; }
.body {
padding: 8px 22px 16px;
font-size: 13px; color: var(--ink-2); line-height: 1.55;
overflow-y: auto;
}
.body p { margin: 0 0 12px; }
.body code, .body kbd {
font-family: var(--mono); font-size: 11.5px;
padding: 1px 5px; background: var(--bg-3);
border: 1px solid var(--line); border-radius: 4px;
color: var(--accent);
}
.footer {
display: flex; align-items: center; gap: 12px;
padding: 12px 22px;
border-top: 1px solid var(--line);
}
.dots { display: flex; gap: 6px; flex: 1; }
.dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--bg-3); border: 1px solid var(--line-2);
}
.dot.active { background: var(--accent); border-color: var(--accent); }
button {
padding: 8px 14px;
border-radius: 8px;
font-size: 12.5px; font-weight: 500;
border: 1px solid var(--line);
background: var(--bg-2); color: var(--ink);
cursor: pointer;
font-family: inherit;
}
button.primary {
background: var(--accent); border-color: var(--accent);
color: #1a0f00;
}
button.ghost { background: transparent; }
.skip {
width: 28px; height: 28px;
background: transparent; border: 1px solid var(--line);
border-radius: 6px; color: var(--ink-2);
}
`;Le([u()],ue.prototype,"open",2);Le([u()],ue.prototype,"step",2);ue=Le([k("nv-onboarding")],ue);var jt=Object.defineProperty,Ut=Object.getOwnPropertyDescriptor,te=(e,t,a,i)=>{for(var s=i>1?void 0:i?Ut(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&jt(t,a,s),s};const qt=[{id:"nvBest",label:"NV-ensemble (best lab)",floorT:1e-12,color:"oklch(0.78 0.14 70)"},{id:"nvCots",label:"NV-DNV-B1 (COTS)",floorT:3e-10,color:"oklch(0.72 0.18 50)"},{id:"squid",label:"SQUID (shielded room)",floorT:1e-15,color:"oklch(0.78 0.12 195)"},{id:"mmw",label:"60 GHz mmWave (μ-Doppler)",floorT:0,color:"oklch(0.78 0.14 145)"},{id:"csi",label:"WiFi CSI (presence)",floorT:0,color:"oklch(0.72 0.18 330)"}];let D=class extends w{constructor(){super(...arguments),this.distanceM=.1,this.momentLog10=-8.3,this.result=null,this.running=!1,this.err=null}predictedDipoleFieldT(e,t){return 4*Math.PI*1e-7*t/(4*Math.PI*Math.pow(Math.max(e,1e-6),3))}async runDemo(){const e=h();if(!e){this.err="WASM client not ready";return}this.err=null,this.running=!0,this.requestUpdate();try{const t=this.distanceM,a=Math.pow(10,this.momentLog10),i={dipoles:[{position:[0,0,t],moment:[0,0,a]}],loops:[],ferrous:[],eddy:[],sensors:[[0,0,0]],ambient_field:[0,0,0]},s={digitiser:{f_s_hz:1e4,f_mod_hz:1e3},sensor:{gamma_fwhm_hz:1e6,t1_s:.005,t2_s:1e-6,t2_star_s:2e-7,contrast:.03,n_spins:1e12,shot_noise_disabled:!1},dt_s:null};this.result=await e.runTransient(i,s,42n,64),d("ok",`ghost-demo · r=${t.toFixed(3)} m · |B| recovered = ${(this.result.bMagT*1e12).toExponential(2)} pT`)}catch(t){this.err=t.message,d("err",`ghost-demo failed: ${this.err}`)}finally{this.running=!1,this.requestUpdate()}}formatField(e){if(e===0)return"0 T";const t=Math.abs(e);return t>=.001?`${(e*1e3).toFixed(2)} mT`:t>=1e-6?`${(e*1e6).toFixed(2)} µT`:t>=1e-9?`${(e*1e9).toFixed(3)} nT`:t>=1e-12?`${(e*1e12).toFixed(2)} pT`:t>=1e-15?`${(e*1e15).toFixed(2)} fT`:t>=1e-18?`${(e*1e18).toFixed(2)} aT`:`${e.toExponential(2)} T`}formatDistance(e){return e<1?`${(e*100).toFixed(1)} cm`:e<1e3?`${e.toFixed(2)} m`:e<1e5?`${(e/1e3).toFixed(2)} km`:`${(e/1609).toFixed(0)} mi`}renderDemo(){const e=Math.pow(10,this.momentLog10),t=this.predictedDipoleFieldT(this.distanceM,e),a=this.result?.bMagT??0,i=(this.result?.noiseFloorPtSqrtHz??0)*1e-12,s=qt.map(o=>{let l="bad",v="below floor";if(o.id==="mmw")this.distanceM<=5?(l="ok",v="µ-Doppler @ chest"):this.distanceM<=15?(l="warn",v="edge of range"):(l="bad",v="out of range");else if(o.id==="csi")this.distanceM<=30?(l=this.distanceM<=10?"ok":"warn",v="presence/breathing"):(l="bad",v="out of range");else if(o.floorT>0){const b=t/o.floorT;b>100?(l="ok",v=`${b.toExponential(1)}× floor`):b>1?(l="warn",v=`${b.toFixed(1)}× floor`):(l="bad",v=`${(1/b).toExponential(1)}× too weak`)}const m=o.floorT>0?Math.max(2,Math.min(100,100+12*Math.log10(t/o.floorT))):o.id==="mmw"?Math.max(2,100-this.distanceM*7):Math.max(2,100-this.distanceM*2);return c`
`}),r=t>1e-12?"ok":t>1e-15?"warn":"bad",n=r==="ok"?`Above NV-ensemble lab floor — close-range MCG plausible at ${this.formatDistance(this.distanceM)}.`:r==="warn"?`Below NV ensemble best, above SQUID — research-grade only at ${this.formatDistance(this.distanceM)}.`:`Below every published instrument's noise floor at ${this.formatDistance(this.distanceM)}. Press-release physics.`;return c`
Try it yourself
Place a cardiac dipole at variable distance from the NV sensor. The
dashboard runs the real nvsim Rust pipeline (compiled to WASM)
end-to-end and reports what each tier would actually detect. Same
determinism contract as the rest of the dashboard.
Distance from sensor
${this.formatDistance(this.distanceM)}
{this.distanceM=Math.pow(10,+o.target.value)}} />
10 cm → 100 km log scale
Heart dipole moment
${e.toExponential(2)} A·m²
{this.momentLog10=+o.target.value}} />
published cardiac MCG ≈ 5×10⁻⁹ A·m²
this.runDemo()}>
${this.running?"Running nvsim…":"▶ Run nvsim at this distance"}
${this.err?c`
Error: ${this.err}
`:""}
Predicted |B| (1/r³)
${this.formatField(t)}
Recovered |B| (nvsim)
${this.result?this.formatField(a):"—"}
Sensor noise floor
${this.result?this.formatField(i)+"/√Hz":"—"}
Frames run
${this.result?.nFrames??"—"}
Witness (this run)
${this.result?.witnessHex.slice(0,16)??"—"}…
Per-tier detectability
${s}
${n}
The predicted value uses the closed-form magnetic-dipole
far field |B| = μ₀·m / (4π·r³). The recovered
value comes from the same Rust pipeline that drives the Witness panel —
scene → Biot-Savart → NV ensemble → ADC → MagFrame. Use the moment
slider to ask "what if the heart were stronger?". Use the distance
slider to walk through 10 cm (clinical MCG), 1 m (close approach),
10 m (room-scale), 1 km (skeptic's range), and 65 km (the press claim).
`}render(){return c`
Ghost Murmur — open-source reality check
The physics-vs-press audit for the publicly-reported April 2026
CIA NV-diamond heartbeat detector, and how RuView's existing
stack maps onto an honest, civilian version of the same idea.
What the press reported
The story
3 Apr 2026: USAF F-15E pilot "Dude 44 Bravo" goes down in southern Iran during the regional exchange and evades for ~2 days.
President Trump publicly suggests detection from 40 miles away on a mountainside at night; CIA Director Ratcliffe says "invisible to the enemy, but not to the CIA."
The named tech
"Ghost Murmur" — Lockheed Skunk Works system using NV defects in synthetic diamond + AI to extract a heartbeat from environmental noise.
Outlets: Newsweek, Scientific American, Military.com, WION, Open The Magazine, Yahoo, Calcalist + HN thread #47679241.
What physicists said
Wikswo (Vanderbilt), Orzel (Union College), Roth (Oakland) — all pushing back hard.
"At 1 km, the heartbeat field drops to ~10⁻¹² of its 10 cm value." MCG-only at multi-mile range is not consistent with published physics .
Live demo — nvsim WASM
${this.renderDemo()}
Physics reality check
Distance Cardiac MCG (peak QRS) vs Earth field (~50 µT)
10 cm 50 pT 10⁹× weaker
1 m 50 fT 10¹²× weaker
10 m 50 aT 10¹⁵× weaker
1 km 5 × 10⁻²³ T 10²⁷× weaker
40 mi (65 km) ~10⁻²⁸ T 10³³× weaker
Best published NV-ensemble lab record: 0.9 pT/√Hz [Wolf 2015].
Best SQUID in a shielded room: ~1 fT/√Hz . To detect a single heartbeat at 10 m
you'd need ~2 billion× more sensitivity than any published ensemble has ever shown,
in a magnetically silent environment. 40 miles is press-release physics.
RuView's three-tier mesh — what is actually buildable
┌──────────────────────────┐
│ Tier 3 — NV-diamond │ Range: 0.1–2 m (lab)
│ magnetometer ring │ Status: nvsim simulator only
│ (close-confirm) │ Hardware: $$$ (≥$8k DNV-B1)
└──────────┬───────────────┘
│
┌──────────┴───────────────┐
│ Tier 2 — 60 GHz FMCW │ Range: 1–10 m HR/BR
│ mmWave radar mesh │ Status: shipping (ADR-021)
│ (vital signs, posture) │ Hardware: $15 (MR60BHA2 + ESP32-C6)
└──────────┬───────────────┘
│
┌──────────┴───────────────┐
│ Tier 1 — WiFi CSI mesh │ Range: 10–30 m through-wall
│ (presence, breathing, │ Status: shipping (ADR-014, ADR-029)
│ pose, intention) │ Hardware: $9 (ESP32-S3 8MB)
└──────────┬───────────────┘
│
▼
┌────────────────────────────────┐
│ RuvSense multistatic fusion │
│ + cross-viewpoint attention │
│ + AETHER re-ID embeddings │
│ + Cramer-Rao gating │
└────────────────────────────────┘
Press claim → RuView equivalent
Press claim RuView equivalent today Crate / ADR Honest range
NV-diamond magnetometry
Deterministic NV pipeline simulator
nvsim · ADR-089
Simulator only
"AI strips environmental noise"
RuvSense multistatic fusion + AETHER
signal/ruvsense/ · ADR-029
Mature
Heartbeat at distance
60 GHz FMCW HR/BR + WiFi CSI breathing
vitals · ADR-021
1–5 m HR · 10–30 m presence
Long-range localisation
Multistatic time-of-flight + CRLB
ruvector/viewpoint/
Limited by node spacing
40-mile single-heartbeat detection
Not feasible at any tier
—
Press-release physics
Build today on $165
Bill of materials
3 × ESP32-S3 8 MB ($9 ea)
3 × PoE injector + cat6 ($6 ea)
1 × ESP32-C6 + Seeed MR60BHA2 ($15)
1 × Raspberry Pi 5 8 GB ($80)
1 × unmanaged GbE switch ($25)
Total: $165
Honest performance
95% TPR (LOS, 0–15 m)
±2 bpm HR (LOS 0–3 m)
±1 br/min BR (any mode)
~10 cm pose error
80–150 ms end-to-end latency
Determinism
Same (scene, config, seed) → byte-identical SHA-256 witness across browsers, OSes, transports.
Reference: cc8de9b01b0ff5bd…
Try the Witness tab on the right — it re-derives the hash live in this browser and compares against the published reference.
Privacy, ethics, legal
This is the open-source version. Same physics, opposite governance.
Civilian opt-in only — search-and-rescue, elder-care, occupancy, ICU vitals. Not surveillance.
No directional pursuit — no beam-steering, target-following, or remote person-of-interest tracking.
Data minimisation — fused output is (presence, HR, BR, pose, p_alive); raw streams discarded at the edge.
PII gates (ADR-040) block identifying biometric streams from leaving the local mesh without consent.
Adversarial-signal detection flags physically-impossible signal patterns from compromised mesh nodes.
No export-controlled hardware — RuView targets < $50 COTS. ITAR/EAR sub-THz coherent radars and shielded NV ensembles are out of scope.
RuView is not affiliated with the United States government, the CIA, Lockheed Martin,
or any classified program. References to "Ghost Murmur" in this view refer
exclusively to the publicly-reported program of that name as covered in the open
press in April 2026.
Cross-references
ADRs: 014 (signal) · 021 (vitals) · 024 (AETHER) · 027 (MERIDIAN) ·
028 (witness audit) · 029 (RuvSense) · 040 (PII gates) · 086 (ESP32 RaBitQ) ·
089 (nvsim, Accepted) · 090 (Lindblad, Proposed-conditional) ·
091 (sub-THz radar research) · 092 (this dashboard) .
Primary physics: Cohen 1970 · Bison 2009 · Wolf 2015 · Barry RMP 2020 · Doherty 2013 · Jackson 3e §5.6/§5.8.
`}};D.styles=y`
:host {
display: block;
height: 100%;
overflow-y: auto;
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
padding: 24px 28px 60px;
}
h1 {
margin: 0 0 4px;
font-size: 22px;
letter-spacing: -0.02em;
color: var(--ink);
}
.subtitle {
color: var(--ink-3);
font-size: 13px;
margin-bottom: 22px;
}
.links {
display: flex; flex-wrap: wrap; gap: 6px;
margin-bottom: 22px;
}
.links a {
padding: 5px 10px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 999px;
font-size: 11.5px;
font-family: var(--mono);
color: var(--accent-2);
text-decoration: none;
}
.links a:hover { border-color: var(--accent-2); }
h2 {
font-size: 14px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-3);
margin: 28px 0 10px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 12px;
}
.card {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 14px;
}
.card h3 {
margin: 0 0 8px;
font-size: 13.5px; font-weight: 600;
color: var(--ink);
}
.card p {
font-size: 12.5px; color: var(--ink-2);
margin: 0 0 8px;
line-height: 1.5;
}
.card p:last-child { margin-bottom: 0; }
.stat {
display: inline-flex; align-items: baseline; gap: 6px;
margin-right: 10px;
}
.stat .v {
font-family: var(--mono); font-size: 16px; font-weight: 600;
color: var(--accent);
}
.stat .l {
font-size: 10px; color: var(--ink-3);
text-transform: uppercase; letter-spacing: 0.04em;
}
table {
width: 100%; border-collapse: collapse;
font-size: 12.5px;
}
th, td {
padding: 8px 10px;
text-align: left;
border-bottom: 1px solid var(--line);
}
th {
color: var(--ink-3);
font-weight: 600;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
td.amber { color: var(--accent); font-family: var(--mono); }
td.cyan { color: var(--accent-2); font-family: var(--mono); }
td.bad { color: var(--bad); font-family: var(--mono); }
.pill {
display: inline-block;
padding: 1px 6px;
border-radius: 4px;
font-family: var(--mono);
font-size: 10px;
border: 1px solid var(--line);
}
.pill.ok { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }
.pill.skeptical { color: var(--bad); border-color: oklch(0.65 0.22 25 / 0.4); }
.pill.partial { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }
.architecture {
font-family: var(--mono);
font-size: 11px;
color: var(--ink-2);
background: var(--bg-3);
padding: 16px;
border-radius: var(--radius-sm);
border: 1px solid var(--line);
white-space: pre;
overflow-x: auto;
line-height: 1.4;
}
.ethics {
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.65 0.22 25 / 0.04) 100%);
border: 1px solid oklch(0.65 0.22 25 / 0.25);
border-radius: var(--radius);
padding: 16px;
}
.ethics h3 { color: var(--bad); margin-top: 0; }
.ethics ul { padding-left: 18px; margin: 8px 0; }
.ethics li { font-size: 12.5px; color: var(--ink-2); margin-bottom: 4px; }
/* Demo */
.demo {
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 70 / 0.04) 100%);
border: 1px solid oklch(0.78 0.14 70 / 0.3);
border-radius: var(--radius);
padding: 18px;
}
.demo-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 18px;
margin-top: 12px;
}
@media (max-width: 720px) { .demo-grid { grid-template-columns: 1fr; } }
.control { margin-bottom: 14px; }
.control .top {
display: flex; justify-content: space-between;
font-size: 12px; margin-bottom: 6px;
}
.control .top .lbl { color: var(--ink-3); }
.control .top .val {
font-family: var(--mono); color: var(--ink);
}
.control input[type="range"] {
-webkit-appearance: none; appearance: none;
width: 100%; height: 4px;
background: var(--bg-3); border-radius: 2px; outline: none;
}
.control input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 14px; height: 14px; border-radius: 50%;
background: var(--accent); cursor: pointer;
border: 2px solid var(--bg-2);
}
.demo-btn {
width: 100%;
padding: 10px;
border: 1px solid var(--accent);
background: var(--accent);
color: #1a0f00;
border-radius: 8px;
font-size: 13px; font-weight: 600;
cursor: pointer;
}
.demo-btn:hover { filter: brightness(1.08); }
.demo-btn:disabled { opacity: 0.6; cursor: progress; }
.readout {
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 8px;
padding: 12px;
}
.readout-row {
display: flex; justify-content: space-between;
padding: 4px 0;
font-family: var(--mono); font-size: 12px;
}
.readout-row .l { color: var(--ink-3); }
.readout-row .v { color: var(--ink); }
.readout-row .v.amber { color: var(--accent); }
.tier-bar {
position: relative;
margin: 6px 0;
height: 22px;
background: var(--bg-3);
border: 1px solid var(--line);
border-radius: 4px;
overflow: hidden;
}
.tier-bar .fill {
position: absolute; top: 0; bottom: 0; left: 0;
transition: width 0.2s ease-out;
border-right: 2px solid;
}
.tier-bar .lbl {
position: relative; z-index: 1;
font-family: var(--mono); font-size: 11px;
padding: 3px 8px;
color: var(--ink);
display: flex; justify-content: space-between;
pointer-events: none;
}
.verdict {
margin-top: 10px;
padding: 10px 12px;
border-radius: 8px;
font-size: 12.5px; font-weight: 500;
border: 1px solid;
}
.verdict.ok { background: oklch(0.78 0.14 145 / 0.08); border-color: oklch(0.78 0.14 145 / 0.4); color: var(--ok); }
.verdict.warn { background: oklch(0.7 0.18 35 / 0.08); border-color: oklch(0.7 0.18 35 / 0.4); color: var(--warn); }
.verdict.bad { background: oklch(0.65 0.22 25 / 0.08); border-color: oklch(0.65 0.22 25 / 0.4); color: var(--bad); }
.demo-notes {
font-size: 11.5px; color: var(--ink-3);
margin-top: 10px; line-height: 1.5;
}
`;te([u()],D.prototype,"distanceM",2);te([u()],D.prototype,"momentLog10",2);te([u()],D.prototype,"result",2);te([u()],D.prototype,"running",2);te([u()],D.prototype,"err",2);D=te([k("nv-ghost-murmur")],D);var Gt=Object.defineProperty,Kt=Object.getOwnPropertyDescriptor,Qe=(e,t,a,i)=>{for(var s=i>1?void 0:i?Kt(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(s=(i?n(t,a,s):n(s))||s);return i&&s&&Gt(t,a,s),s};let Me=class extends w{constructor(){super(...arguments),this.view="scene"}render(){return c`
{e.preventDefault(),this.shadowRoot?.querySelector(".main")?.focus()}}>
Skip to main content
this.view=e.detail}>
${this.view==="apps"?c` `:this.view==="ghost-murmur"?c` `:this.view==="inspector"?c` `:this.view==="witness"?c` `:c` `}
`}};Me.styles=y`
:host {
display: block;
height: 100vh;
width: 100vw;
background: var(--bg-0);
}
.skip-link {
position: absolute;
top: -40px;
left: 8px;
padding: 6px 12px;
background: var(--accent);
color: #1a0f00;
border-radius: 6px;
font-size: 12.5px;
font-weight: 600;
text-decoration: none;
z-index: 1000;
transition: top 0.15s;
}
.skip-link:focus { top: 8px; }
.app {
display: grid;
grid-template-columns: 56px 280px 1fr 340px;
grid-template-rows: 48px 1fr 220px;
grid-template-areas:
'rail topbar topbar topbar'
'rail sidebar main inspector'
'rail sidebar console inspector';
height: 100vh;
width: 100vw;
}
nv-rail { grid-area: rail; }
nv-topbar { grid-area: topbar; }
nv-sidebar { grid-area: sidebar; }
.main { grid-area: main; min-width: 0; min-height: 0; position: relative; overflow: hidden; }
nv-inspector { grid-area: inspector; }
nv-console { grid-area: console; min-height: 0; }
@media (max-width: 1180px) {
.app {
grid-template-columns: 56px 1fr 320px;
grid-template-areas:
'rail topbar topbar'
'rail main inspector'
'rail console console';
}
nv-sidebar { display: none; }
}
@media (max-width: 860px) {
.app {
grid-template-columns: 1fr;
grid-template-rows: 52px 1fr 200px;
grid-template-areas:
'topbar'
'main'
'console';
}
nv-rail, nv-sidebar, nv-inspector { display: none; }
}
`;Qe([u()],Me.prototype,"view",2);Me=Qe([k("nv-app")],Me);function Xt(e,t,a){const i=e.getUint32(t+0,!0),s=e.getUint16(t+4,!0),r=e.getUint16(t+6,!0),n=e.getUint16(t+8,!0),o=e.getBigUint64(t+12,!0),l=e.getFloat32(t+20,!0),v=e.getFloat32(t+24,!0),m=e.getFloat32(t+28,!0),b=e.getFloat32(t+32,!0),ae=e.getFloat32(t+36,!0),Ae=e.getFloat32(t+40,!0),Ze=e.getFloat32(t+44,!0),et=e.getFloat32(t+48,!0);return{magic:i,version:s,flags:r,sensorId:n,tUs:o,bPt:[l,v,m],sigmaPt:[b,ae,Ae],noiseFloorPtSqrtHz:Ze,temperatureK:et,raw:a.subarray(t,t+60)}}function Yt(e){const a=new DataView(e.buffer,e.byteOffset,e.byteLength),i=[];for(let s=0;s+60<=e.byteLength;s+=60)i.push(Xt(a,s,e));return i}class Jt{constructor(){this.nextId=1,this.pending=new Map,this.frameSubs=new Set,this.eventSubs=new Set,this.bootInfo=null,this.worker=new Worker(new URL("/RuView/nvsim/assets/worker-C19MRcXs.js",import.meta.url),{type:"module"}),this.worker.addEventListener("message",t=>this.onMessage(t)),this.worker.addEventListener("error",t=>this.eventSubs.forEach(a=>a({type:"log",level:"err",msg:String(t.message)})))}onMessage(t){const a=t.data;if(a.type==="frames"){const i=a.batch,s=new Uint8Array(i),n={frames:Yt(s),bytes:s};this.frameSubs.forEach(l=>l(n));const o=a.fps;o>0&&this.eventSubs.forEach(l=>l({type:"fps",value:o}));return}if(a.type==="state"){this.eventSubs.forEach(i=>i({type:"state",running:!!a.running,t:0,framesEmitted:Number(a.framesEmitted??0)}));return}if(a.type!=="ready"){if(a.type==="err"&&a.id==null){this.eventSubs.forEach(i=>i({type:"log",level:"err",msg:String(a.msg)}));return}if(typeof a.id=="number"&&this.pending.has(a.id)){const i=this.pending.get(a.id);this.pending.delete(a.id),a.type==="err"?i.reject(new Error(String(a.msg))):i.resolve(a)}}}rpc(t,a=[]){const i=this.nextId++;return new Promise((s,r)=>{this.pending.set(i,{resolve:s,reject:r}),this.worker.postMessage({...t,id:i},a)})}async boot(){if(this.bootInfo)return this.bootInfo;const a=await this.rpc({type:"boot",base:"/RuView/nvsim/"});return this.bootInfo={buildVersion:a.buildVersion,frameMagic:a.frameMagic,frameBytes:a.frameBytes,expectedWitnessHex:a.expectedWitnessHex},this.bootInfo}async loadScene(t){await this.rpc({type:"setScene",json:JSON.stringify(t)})}async setConfig(t){await this.rpc({type:"setConfig",json:JSON.stringify(t)})}async setSeed(t){await this.rpc({type:"setSeed",seed:Number(t&0xFFFFFFFFn)})}async reset(){await this.rpc({type:"reset"})}async run(t){await this.rpc({type:"run"})}async pause(){await this.rpc({type:"pause"})}async step(t,a){await this.rpc({type:"step"})}onFrames(t){this.frameSubs.add(t)}onEvent(t){this.eventSubs.add(t)}async generateWitness(t){const a=await this.rpc({type:"witnessGenerate",samples:t});return new Uint8Array(a.witness)}async verifyWitness(t){const a=t.slice().buffer,i=await this.rpc({type:"witnessVerify",samples:256,expected:a},[a]);return i.ok?{ok:!0}:{ok:!1,actual:new Uint8Array(i.actual)}}async runTransient(t,a,i,s){const r=await this.rpc({type:"runTransient",scene:JSON.stringify(t),config:JSON.stringify(a),seed:Number(i&0xFFFFFFFFn),samples:s});return{bRecoveredT:[r.bRecoveredT[0],r.bRecoveredT[1],r.bRecoveredT[2]],bMagT:r.bMagT,noiseFloorPtSqrtHz:r.noiseFloorPtSqrtHz,sigmaPt:[r.sigmaPt[0],r.sigmaPt[1],r.sigmaPt[2]],nFrames:r.nFrames,witnessHex:r.witnessHex}}async exportProofBundle(){const t=await this.generateWitness(256),a=Array.from(t).map(r=>r.toString(16).padStart(2,"0")).join(""),i=this.bootInfo??await this.boot(),s=JSON.stringify({kind:"nvsim-proof-bundle",version:i.buildVersion,seed:"0x0000002A",nSamples:256,witness:a,expected:i.expectedWitnessHex,ok:a===i.expectedWitnessHex,ts:new Date().toISOString()},null,2);return new Blob([s],{type:"application/json"})}async buildId(){return(await this.rpc({type:"buildId"})).buildId}async close(){this.worker.terminate()}}function We(e){document.documentElement.setAttribute("data-theme",e)}function Ve(e){document.body.classList.remove("density-comfy","density-default","density-compact"),document.body.classList.add(`density-${e}`)}function je(e){document.body.classList.toggle("reduce-motion",e)}(async()=>{const e=await O("theme")??"dark",t=await O("density")??"default",a=window.matchMedia?.("(prefers-reduced-motion: reduce)").matches??!1,i=await O("motionReduced")??a;x.value=e,We(e),_.value=t,Ve(t),C.value=i,je(i),f(()=>{We(x.value),I("theme",x.value)}),f(()=>{Ve(_.value),I("density",_.value)}),f(()=>{je(C.value),I("motionReduced",C.value)});const s=await O("repl-history");s&&Array.isArray(s)&&(W.value=s),f(()=>{I("repl-history",W.value)});const r=await O("scene-positions");r&&Array.isArray(r)&&(de.value=r),f(()=>{I("scene-positions",de.value)});const n=new Jt;ut(n),d("info","nvsim — booting WASM runtime"),n.onEvent(o=>{o.type==="log"&&d(o.level,o.msg),o.type==="fps"&&(M.value=o.value),o.type==="state"&&(Fe.value=BigInt(o.framesEmitted))}),n.onFrames(o=>{if(o.frames.length===0)return;const l=o.frames[o.frames.length-1];B.value=l;const v=l.bPt[0]*1e-12,m=l.bPt[1]*1e-12,b=l.bPt[2]*1e-12;ce.value=[v,m,b],V.value=Math.sqrt(v*v+m*m+b*b),gt([v*1e9,m*1e9,b*1e9]);const ae=Math.min(1,Math.abs(b*1e9)/5+.3);mt(ae)});try{const o=await n.boot();R.value=o.expectedWitnessHex,d("ok",`WASM module ready · nvsim@${o.buildVersion} · magic=0x${o.frameMagic.toString(16).toUpperCase()}`),d("info",`expected witness · ${o.expectedWitnessHex.slice(0,16)}…`),ct.value="(reference scene)",T.value="wasm"}catch(o){d("err",`boot failed: ${o.message}`)}try{const o=R.value;if(o){const l=new Uint8Array(32);for(let m=0;m<32;m++)l[m]=parseInt(o.slice(m*2,m*2+2),16);const v=await n.verifyWitness(l);if(v.ok)z.value=o,d("ok","witness verified · determinism gate ✓");else{const m=Array.from(v.actual).map(b=>b.toString(16).padStart(2,"0")).join("");z.value=m,d("err",`WITNESS MISMATCH · expected ${o.slice(0,16)}… got ${m.slice(0,16)}…`)}}}catch(o){d("warn",`witness verify skipped: ${o.message}`)}})();
//# sourceMappingURL=index-DPROtCvT.js.map