s&&(s=r)}return s>5?(e.state.lastEmitS=e.elapsedS,{ts:Date.now(),appId:"adversarial",eventId:3,eventName:"ANOMALY_DETECTED",value:s,detail:`log-jump ${s.toFixed(1)} — physically implausible step in |B|`}):null},ha=e=>{if(e.bHistory.length<128)return null;const t=e.state.lastEmitS??0;if(e.elapsedS-t<4)return null;e.state.lastEmitS=e.elapsedS;const a=e.bHistory.slice(-128),s=it(a)*1e9,i=Me(a);let r=0;for(const d of a){const v=Math.abs(d-i);v>r&&(r=v)}const n=r>4*(s*1e-9)?1:e.elapsedS>10?3:4,l=n===1?"impulsive":n===3?"drift":"random";return{ts:Date.now(),appId:"exo_ghost_hunter",eventId:651,eventName:"ANOMALY_CLASS",value:n,detail:`class=${l} · σ=${s.toFixed(3)} nT`}},wt={vital_trend:da,occupancy:ca,intrusion:pa,coherence:ua,adversarial:va,exo_ghost_hunter:ha};function ma(e){return e in wt}var ga=Object.defineProperty,ba=Object.getOwnPropertyDescriptor,kt=(e,t,a,s)=>{for(var i=s>1?void 0:s?ba(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&ga(t,a,i),i};const V=p(ra()),xe=p(""),U=p("all"),C=p("all");(async()=>{const e=await F("app-activations");e&&(V.value=e)})();g(()=>{const e=V.value;e.length>0&&O("app-activations",e);const t=new Set;for(const a of e)a.active&&t.add(a.id);bt.value=t});let qe=class extends x{constructor(){super(...arguments),this.renderTick=0}connectedCallback(){super.connectedCallback(),g(()=>{V.value,xe.value,U.value,C.value,We.value,Be.value,this.renderTick++})}isActive(e){return V.value.find(t=>t.id===e)?.active===!0}toggle(e){const t=this.isActive(e.id),a=V.value.map(s=>s.id===e.id?{...s,active:!s.active,lastActivatedAt:Date.now()}:s);if(V.value=a,t)c("info",`app ${e.id} deactivated`);else{const s=e.runtime??"mesh-only",i=s==="simulated"?" · live runtime engaged":s==="mesh-only"?" · queued (needs ESP32 mesh)":"";c("ok",`app ${e.id} activated${i}`)}}filtered(){let e=$e;return U.value!=="all"&&(e=e.filter(t=>t.category===U.value)),C.value!=="all"&&(e=e.filter(t=>t.status===C.value)),xe.value.trim()&&(e=e.map(t=>({a:t,s:na(xe.value,t)})).filter(t=>t.s>0).sort((t,a)=>a.s-t.s).map(t=>t.a)),e}categoryCounts(){const e={all:$e.length};for(const t of Object.keys(ye))e[t]=0;for(const t of $e)e[t.category]=(e[t.category]??0)+1;return e}render(){const e=this.filtered(),t=this.categoryCounts(),a=V.value.filter(s=>s.active).length;return o`
+
+
+ App Store
+ ${$e.length} edge apps · ${a} active
+
+
{xe.value=s.target.value}} />
+
+
+
+ U.value="all"}>
+ All${t.all}
+
+ ${Object.keys(ye).map(s=>o`
+ U.value=s}>
+
+ ${ye[s].label}
+ ${t[s]??0}
+
+ `)}
+
+ C.value="all"}>any
+ C.value="available"}>available
+ C.value="beta"}>beta
+ C.value="research"}>research
+
+
+ ${this.renderEventsFeed()}
+
+ ${e.length===0?o`No apps match the current filters.
`:o`${e.map(s=>this.card(s))}
`}
+ `}renderEventsFeed(){const e=We.value.slice(-12).reverse(),t=V.value.filter(a=>a.active&&ma(a.id)).length;return o`
+
+
Live runtime feed
+ ${t>0?o`${t} simulated app${t===1?"":"s"} active `:""}
+
+
+ Apps with the simulated
+ runtime emit real i32 event IDs against nvsim's live frame stream below.
+ Apps with mesh-only
+ need an ESP32-S3 + WS transport (deferred to V2). The
+ running
+ badge marks nvsim itself, which is always running.
+
+ ${e.length===0?o`
No events yet. Toggle a card with the simulated badge and press ▶ Run .
`:o`
${e.map(a=>{const s=new Date(a.ts),i=`${String(s.getSeconds()).padStart(2,"0")}.${String(s.getMilliseconds()).padStart(3,"0")}`;return o`
+
+ ${i}
+ ${a.appId}
+ ${a.eventName} · ${a.eventId} ${a.detail?`· ${a.detail}`:""}
+
+ `})}
`}
+
+ `}card(e){const t=this.isActive(e.id),a=ye[e.category],s=e.runtime??"mesh-only",i=Be.value[e.id]??0,r={running:"running",simulated:"simulated","mesh-only":"needs mesh"},n={running:"This app is genuinely running in your browser right now.",simulated:"A pared-down version of this algorithm runs against nvsim's magnetic frame stream as a proxy for its native CSI input. Toggle on, then press ▶ Run to see real event IDs in the feed.","mesh-only":"This algorithm needs CSI subcarrier data from an ESP32-S3 mesh. The toggle persists; activation is pushed via WS transport (V2)."};return o`
+
+
+
+ ${e.name}
+
+
${e.summary}
+
+ ${a.label}
+ ${e.status}
+ ${r[s]}
+ ${e.budget?o`budget ${e.budget} `:""}
+ ${e.adr?o`${e.adr} `:""}
+ ${e.events?.length?o`events ${e.events.join("·")} `:""}
+
+
+
+ `}};qe.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); }
+ .badge.rt-running { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.5); background: oklch(0.78 0.14 145 / 0.08); }
+ .badge.rt-simulated { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.5); background: oklch(0.78 0.14 70 / 0.08); }
+ .badge.rt-mesh-only { color: var(--ink-3); border-color: var(--line); }
+ .events-feed {
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ border-radius: var(--radius);
+ padding: 14px;
+ margin-bottom: 18px;
+ }
+ .events-feed h3 {
+ margin: 0 0 8px;
+ font-size: 13px; font-weight: 600;
+ color: var(--ink);
+ }
+ .events-feed .lead {
+ font-size: 12px; color: var(--ink-3);
+ margin: 0 0 10px;
+ line-height: 1.5;
+ }
+ .events-feed .lines {
+ display: flex; flex-direction: column; gap: 4px;
+ max-height: 160px; overflow-y: auto;
+ }
+ .ev-line {
+ display: grid;
+ grid-template-columns: 60px 90px 1fr;
+ gap: 10px;
+ padding: 4px 6px;
+ border-radius: 4px;
+ font-family: var(--mono);
+ font-size: 11px;
+ color: var(--ink-2);
+ }
+ .ev-line:hover { background: var(--bg-3); }
+ .ev-line .ts { color: var(--ink-4); font-size: 10.5px; }
+ .ev-line .id { color: var(--accent); font-size: 10.5px; }
+ .ev-line .body { color: var(--ink); }
+ .ev-empty {
+ font-size: 12px; color: var(--ink-3);
+ padding: 8px 0;
+ }
+ .card-events-count {
+ font-size: 10.5px;
+ color: var(--accent-4);
+ font-family: var(--mono);
+ }
+ .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;
+ }
+ `;kt([u()],qe.prototype,"renderTick",2);qe=kt([w("nv-app-store")],qe);var fa=Object.defineProperty,ya=Object.getOwnPropertyDescriptor,Pe=(e,t,a,s)=>{for(var i=s>1?void 0:s?ya(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&fa(t,a,i),i};let ee=class extends x{constructor(){super(...arguments),this.open=!1,this.filter="",this.idx=0,this.cmds=[{ico:"▶",label:"Run pipeline",kbd:"Space",run:async()=>{await f()?.run(),m.value=!0,W("Pipeline running","▶")}},{ico:"❚",label:"Pause pipeline",run:async()=>{await f()?.pause(),m.value=!1,W("Paused","❚❚")}},{ico:"+",label:"New scene…",kbd:"⌘N",run:()=>ke({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"),s=parseFloat(e.querySelector("#ns-distance")?.value??"0.5"),i=e.querySelector("#ns-ferrous")?.value==="1",r=e.querySelector("#ns-mains")?.value==="1",n={dipoles:[{position:[0,0,s],moment:[0,0,a]}],loops:r?[{centre:[0,1,0],normal:[0,1,0],radius:.05,current:2,n_segments:64}]:[],ferrous:i?[{position:[1,0,0],volume:1e-4,susceptibility:5e3}]:[],eddy:[],sensors:[[0,0,0]],ambient_field:[1e-6,0,0]};await f()?.loadScene(n),c("ok",`scene ${t} loaded · 1 dipole · ${r?"1 loop · ":""}${i?"1 ferrous · ":""}1 sensor`),W(`Scene "${t}" loaded`,"+")}}]})},{ico:"📦",label:"Export proof bundle…",kbd:"⌘E",run:async()=>{const e=f();if(e){c("dbg","building proof bundle…");try{const t=await e.exportProofBundle(),a=URL.createObjectURL(t),s=document.createElement("a");s.href=a,s.download=`nvsim-proof-${Date.now()}.json`,s.click(),URL.revokeObjectURL(a),c("ok",`proof bundle exported · ${t.size} bytes`),W(`Proof bundle saved (${t.size} B)`,"📦")}catch(t){c("err",`export failed: ${t.message}`)}}}},{ico:"⟳",label:"Reset pipeline",kbd:"⌘R",run:()=>ke({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 f()?.reset(),c("warn","pipeline reset · t=0"),W("Pipeline reset","⟳")}}]})},{ico:"✓",label:"Verify witness",run:async()=>{const e=f();if(!e)return;S.value="pending";const t=N.value,a=new Uint8Array(32);for(let i=0;i<32;i++)a[i]=parseInt(t.slice(i*2,i*2+2),16);(await e.verifyWitness(a)).ok?(S.value="ok",R.value=t,W("Witness verified","✓")):(S.value="fail",W("Witness mismatch!","✗"))}},{ico:"☼",label:"Toggle theme",kbd:"⌘/",run:()=>{$.value=$.value==="dark"?"light":"dark"}},{ico:"⚙",label:"Open settings",kbd:"⌘,",run:()=>window.dispatchEvent(new CustomEvent("open-settings"))},{ico:"?",label:"Keyboard shortcuts…",run:()=>ke({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:()=>ke({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 o`
+
+
+ {this.filter=t.target.value,this.idx=0}} />
+
+
+ ${e.map((t,a)=>o`
+
{this.idx=a,this.runIdx()}}>
+ ${t.ico}
+ ${t.label}
+ ${t.kbd?o`${t.kbd} `:""}
+
+ `)}
+
+
+ `}};ee.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;
+ }
+ `;Pe([u()],ee.prototype,"open",2);Pe([u()],ee.prototype,"filter",2);Pe([u()],ee.prototype,"idx",2);Pe([mt("#palette-input")],ee.prototype,"inputEl",2);ee=Pe([w("nv-palette")],ee);var xa=Object.defineProperty,wa=Object.getOwnPropertyDescriptor,rt=(e,t,a,s)=>{for(var i=s>1?void 0:s?wa(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&xa(t,a,i),i};let Ae=class extends x{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),g(()=>{_.value,et.value,J.value,Y.value,Ot.value,this.requestUpdate()}),this.tick()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.onKey),cancelAnimationFrame(this.rafId)}render(){return o`
+ nvsim · debug {this.open=!1,this.removeAttribute("open")}}>✕
+ render fps ${this.renderFps.toFixed(1)}
+ sim fps ${_.value>0?Math.round(_.value):"—"}
+ frames ${et.value.toString()}
+ |B| ${(J.value*1e9).toFixed(3)} nT
+ SNR ${Y.value>0?Y.value.toFixed(1):"—"}
+ DOM ${document.querySelectorAll("*").length}
+ `}};Ae.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); }
+ `;rt([u()],Ae.prototype,"open",2);rt([u()],Ae.prototype,"renderFps",2);Ae=rt([w("nv-debug-hud")],Ae);var ka=Object.defineProperty,$a=Object.getOwnPropertyDescriptor,$t=(e,t,a,s)=>{for(var i=s>1?void 0:s?$a(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&ka(t,a,i),i};let je=class extends x{constructor(){super(...arguments),this.open=!1}connectedCallback(){super.connectedCallback(),g(()=>{$.value,P.value,D.value,fe.value,z.value,H.value,this.requestUpdate()}),window.addEventListener("open-settings",()=>{this.open=!0,this.setAttribute("open","")})}close(){this.open=!1,this.removeAttribute("open")}async resetPrefs(){if(confirm("Reset all preferences and IndexedDB state? Reloads the page.")){try{const e=await indexedDB.databases?.();if(e)for(const t of e)t.name&&indexedDB.deleteDatabase(t.name)}catch{}location.reload()}}render(){return o`
+ this.close()}>
+
+
Settings
+
this.close()}>×
+
+
+
+
Appearance
+
+
+
Theme
+
Dark is the default; light has higher contrast for daylight work.
+
+
+ $.value="dark"}>dark
+ $.value="light"}>light
+
+
+
+
+
Density
+
Affects panel padding and font scale (15 / 14 / 13 px). Choose what your eyes prefer.
+
+
+ P.value="comfy"}>comfy
+ P.value="default"}>default
+ P.value="compact"}>compact
+
+
+
+
+
Reduce motion
+
Stops the rotating diamond, animated field lines, and chart easing. Auto-on if your system has the prefers-reduced-motion preference set.
+
+
D.value=!D.value}>
+
+
+
+
+
Pipeline
+
+
+
Auto-rerun on edit
+
When you change a Tunables slider or load a new scene, push the change to the worker without a manual restart.
+
+
fe.value=!fe.value}>
+
+
+
+
+
Transport
+
+
+
Mode
+
WASM runs nvsim in your browser (default, no server). WS connects to a host-supplied nvsim-server (REST + binary WebSocket); see ADR-092 §6.2.
+
+
+ z.value="wasm"}>WASM
+ z.value="ws"}>WS
+
+
+ ${z.value==="ws"?o`
+
+
+
WS URL
+
Where your nvsim-server is listening. The server defaults to 127.0.0.1:7878.
+
+
H.value=e.target.value} />
+
`:""}
+
+
+
+
Help
+
+
+
Open help center
+
Quickstart, glossary, FAQ, and shortcuts. Press ? any time.
+
+
{this.close(),window.dispatchEvent(new CustomEvent("nv-show-help"))}}
+ style="padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid var(--line);border-radius:6px;color:var(--ink);">
+ Open
+
+
+
+
+
Replay welcome tour
+
Re-show the 6-step first-run walkthrough.
+
+
{this.close(),window.dispatchEvent(new CustomEvent("nv-show-tour"))}}
+ style="padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid var(--line);border-radius:6px;color:var(--ink);">
+ Replay
+
+
+
+
+
Reset all preferences
+
Wipe theme, density, motion, scene drag positions, REPL history, and the onboarding-seen flag.
+
+
this.resetPrefs()}
+ style="padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid oklch(0.65 0.22 25 / 0.4);border-radius:6px;color:var(--bad);">
+ Reset
+
+
+
+
+
+
+ `}};je.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;
+ }
+ `;$t([u()],je.prototype,"open",2);je=$t([w("nv-settings-drawer")],je);var Sa=Object.defineProperty,_a=Object.getOwnPropertyDescriptor,nt=(e,t,a,s)=>{for(var i=s>1?void 0:s?_a(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&Sa(t,a,i),i};const G=[{icon:"👋",title:"Welcome to nvsim",body:`
+ nvsim is an open-source, deterministic forward simulator for
+ nitrogen-vacancy diamond magnetometry — a real Rust crate compiled
+ to WebAssembly and running in your browser, right now.
+
+ This 60-second tour walks you through the four panels, the App Store,
+ the Ghost Murmur research view, and the determinism contract that
+ makes nvsim distinctive.
+
+ Press Esc any time to skip. You can replay this tour from
+ Settings → Help .
`,cta:{label:"Start the tour →"}},{icon:"🌐",title:"The Scene canvas",body:`The middle panel shows your magnetic scene — a small simulated
+ environment with four sources and one NV-diamond sensor at the centre.
+ The four amber/cyan/magenta blobs are draggable: rebar coil
+ (steel χ=5000), heart proxy dipole, 60 Hz mains current loop,
+ and a steel door (eddy current). Field lines connect each source
+ to the sensor and animate while the pipeline runs.
+
+ Top-left toolbar: zoom in/out, fit-to-view, layer toggles. Bottom-right:
+ sim controls (step / play / step / speed cycle). Drag positions persist
+ across reloads.
`,hint:"Try dragging the heart_proxy after the tour ends."},{icon:"▶",title:"Run the pipeline",body:`Press ▶ Run in the topbar (or hit Space ) to start
+ the live frame stream. nvsim runs at ~1.8 kHz on x86_64 WASM —
+ well above the 1 kHz Cortex-A53 acceptance gate.
+ The FPS pill in the topbar updates with the throughput. The B-vector
+ trace and frame-stream sparkline in the right inspector update in real
+ time.
+
+ Space toggles run/pause from anywhere. Reset (⌘R )
+ rewinds t to 0 without changing the seed.
`},{icon:"🔍",title:"Inspector — three tabs, three depths",body:`The right rail shows the live inspector: Signal (B-vector
+ trace + frame-stream sparkline), Frame (decoded MagFrame fields +
+ raw 60-byte hex dump), Witness (SHA-256 determinism gate).
+ Click the magnifier icon in the left rail to expand the
+ inspector to the full main area, with bigger charts and an explainer
+ header. Click the shield icon to do the same focused on Witness.
+
+ Number keys 1 2 3 jump between the
+ three inspector tabs from anywhere.
`},{icon:"✓",title:"The witness — what makes nvsim distinctive",body:`nvsim's defining commitment: same (scene, config, seed) →
+ byte-identical SHA-256 across runs, machines, and transports.
+ Click the Witness tab and press Verify witness . The
+ dashboard re-derives the hash for the canonical reference scene
+ (seed=42, N=256) and asserts it matches the constant
+ pinned at compile time
+ (cc8de9b01b0ff5bd…).
+ A green check means every constant — γ_e, D_GS, μ₀, T₂*, contrast,
+ the PRNG stream, the frame layout — is byte-identical to the published
+ reference. A red ✗ means something drifted; the dashboard names which.
`},{icon:"🎚",title:"Tunables — change the simulation live",body:`The left sidebar's Tunables panel has four sliders:
+
+ Sample rate (1–100 kHz) — digitiser frame rate
+ Lock-in f_mod (0.1–5 kHz) — microwave modulation freq
+ Integration t (0.1–10 ms) — per-sample integration time
+ Shot noise (on/off) — toggle quantum noise
+
+ Edits debounce 300 ms then rebuild the WASM pipeline without restarting
+ the frame stream. Watch the noise floor and B-vector spread change
+ in the Signal trace.
`},{icon:"👻",title:"Ghost Murmur — research view",body:`Click the ghost icon in the left rail. This view audits the
+ publicly-reported April 2026 CIA Ghost Murmur NV-diamond
+ heartbeat-detection program against the open physics literature.
+ Includes a "Try it yourself" sandbox: place a cardiac dipole at
+ any distance from the sensor, hit Run, and see what the real nvsim
+ pipeline recovers. Per-tier detectability bars compare the predicted
+ signal vs each transport's noise floor (NV-ensemble lab, COTS DNV-B1,
+ SQUID, 60 GHz mmWave, WiFi CSI).
+
+ Spoiler: at 1 km the cardiac MCG is ~10⁻¹² of its 10 cm value.
+ Press claims of 40-mile detection sit far below any published instrument's
+ floor.
`},{icon:"🛍",title:"App Store — 65 edge apps",body:`Click the grid icon. The App Store catalogues every
+ hot-loadable WASM edge module RuView ships, organised by category:
+ medical, security, smart-building, retail, industrial, signal,
+ learning, autonomy, exotic.
+ Each card carries id / category / status / event IDs / compute budget /
+ ADR back-reference. The toggle marks an app active in this session;
+ the WS transport (when configured) pushes the activation set to a
+ connected ESP32 mesh.
+
+ Try searching for "ghost", "heart", or "occupancy" to fuzzy-filter
+ the catalogue.
`},{icon:"⌨",title:"Console + REPL",body:`The bottom panel is a structured event log with five filter tabs
+ (all / info / warn / err / dbg ) plus a REPL prompt.
+ REPL commands include
+ help, scene.list, sensor.config,
+ run, pause, seed [hex],
+ proof.verify, proof.export,
+ theme [light|dark], status, clear.
+
+ Press / to focus the REPL from anywhere. Arrow ↑/↓ recall
+ history (persisted across reloads). ⌘K opens the command
+ palette with every action discoverable.
`},{icon:"🚀",title:"You are ready",body:`That's the whole tour. A few last pointers:
+
+ Press ? any time to open the help center
+ (Quickstart / Glossary / FAQ / Shortcuts / About).
+ Press ⌘K for the command palette.
+ Press \` to toggle the debug HUD.
+ Settings (⌘, ) lets you switch theme, density, motion,
+ transport, and replay this tour.
+
+
+ Source: github.com/ruvnet/RuView · Apache-2.0 OR MIT ·
+ ADRs 089/090/091/092/093.
`,cta:{label:"Get started →"}}];let Ce=class extends x{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 F("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 O("onboarding-seen",!0)}next(){G[this.step].cta?.run?.(),this.step0&&this.step--}render(){const e=G[this.step],t=this.step===G.length-1;return o`
+
+
+
${e.icon}
+
+
${e.title}
+
Step ${this.step+1} of ${G.length}
+
+
this.dismiss()} aria-label="Skip tour" title="Skip tour">×
+
+
+
+ ${e.hint?o`
${e.hint}
`:""}
+
+
+
+ `}};Ce.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(640px, 94vw);
+ 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);
+ overflow: hidden;
+ }
+ :host([open]) .card { transform: translateY(0) scale(1); }
+ .h {
+ padding: 22px 26px 12px;
+ display: flex; align-items: flex-start; gap: 14px;
+ }
+ .h .icon {
+ width: 44px; height: 44px;
+ border-radius: 12px;
+ background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);
+ display: grid; place-items: center;
+ font-size: 22px;
+ flex-shrink: 0;
+ box-shadow: 0 4px 12px -2px oklch(0.55 0.16 30 / 0.35);
+ }
+ .h .title-wrap { flex: 1; min-width: 0; }
+ .h h2 {
+ margin: 0;
+ font-size: 18px;
+ letter-spacing: -0.01em;
+ color: var(--ink);
+ }
+ .h .step-label {
+ font-family: var(--mono);
+ font-size: 10.5px;
+ color: var(--ink-3);
+ margin-top: 4px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ }
+ .h .skip {
+ width: 28px; height: 28px;
+ background: transparent;
+ border: 1px solid var(--line);
+ border-radius: 6px;
+ color: var(--ink-2);
+ cursor: pointer;
+ flex-shrink: 0;
+ }
+ .h .skip:hover { color: var(--ink); border-color: var(--line-2); }
+ .body {
+ padding: 0 26px 16px;
+ font-size: 13px;
+ color: var(--ink-2);
+ line-height: 1.6;
+ overflow-y: auto;
+ flex: 1;
+ }
+ .body p { margin: 0 0 12px; }
+ .body p:last-child { margin-bottom: 0; }
+ .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;
+ }
+ .body code { color: var(--accent); }
+ .body kbd { color: var(--ink); }
+ .hint {
+ margin: 14px 0 0;
+ padding: 10px 12px;
+ background: oklch(0.78 0.12 195 / 0.06);
+ border: 1px solid oklch(0.78 0.12 195 / 0.25);
+ border-radius: 8px;
+ font-size: 12px;
+ color: var(--accent-2);
+ display: flex; gap: 8px; align-items: flex-start;
+ }
+ .hint::before {
+ content: '💡';
+ flex-shrink: 0;
+ }
+ .footer {
+ display: flex; align-items: center; gap: 14px;
+ padding: 14px 22px;
+ border-top: 1px solid var(--line);
+ background: var(--bg-1);
+ }
+ .progress { flex: 1; }
+ .dots { display: flex; gap: 5px; margin-bottom: 4px; }
+ .dot {
+ width: 6px; height: 6px; border-radius: 50%;
+ background: var(--bg-3);
+ border: 1px solid var(--line-2);
+ transition: background 0.15s, border-color 0.15s, transform 0.15s;
+ }
+ .dot.active {
+ background: var(--accent);
+ border-color: var(--accent);
+ transform: scale(1.2);
+ }
+ .dot.done {
+ background: var(--accent-4);
+ border-color: var(--accent-4);
+ }
+ .progress-label {
+ font-family: var(--mono);
+ font-size: 10px;
+ color: var(--ink-3);
+ }
+ button.primary, button.ghost {
+ padding: 9px 16px;
+ border-radius: 8px;
+ font-size: 13px;
+ font-weight: 500;
+ cursor: pointer;
+ font-family: inherit;
+ border: 1px solid var(--line);
+ background: var(--bg-2);
+ color: var(--ink);
+ }
+ button.ghost:hover { border-color: var(--line-2); }
+ button.primary {
+ background: var(--accent);
+ border-color: var(--accent);
+ color: #1a0f00;
+ }
+ button.primary:hover { filter: brightness(1.08); }
+ `;nt([u()],Ce.prototype,"open",2);nt([u()],Ce.prototype,"step",2);Ce=nt([w("nv-onboarding")],Ce);var Ta=Object.defineProperty,za=Object.getOwnPropertyDescriptor,he=(e,t,a,s)=>{for(var i=s>1?void 0:s?za(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&Ta(t,a,i),i};const Ma=[{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 j=class extends x{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=f();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),s={dipoles:[{position:[0,0,t],moment:[0,0,a]}],loops:[],ferrous:[],eddy:[],sensors:[[0,0,0]],ambient_field:[0,0,0]},i={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(s,i,42n,64),c("ok",`ghost-demo · r=${t.toFixed(3)} m · |B| recovered = ${(this.result.bMagT*1e12).toExponential(2)} pT`)}catch(t){this.err=t.message,c("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,s=(this.result?.noiseFloorPtSqrtHz??0)*1e-12,i=Ma.map(l=>{let d="bad",v="below floor";if(l.id==="mmw")this.distanceM<=5?(d="ok",v="µ-Doppler @ chest"):this.distanceM<=15?(d="warn",v="edge of range"):(d="bad",v="out of range");else if(l.id==="csi")this.distanceM<=30?(d=this.distanceM<=10?"ok":"warn",v="presence/breathing"):(d="bad",v="out of range");else if(l.floorT>0){const M=t/l.floorT;M>100?(d="ok",v=`${M.toExponential(1)}× floor`):M>1?(d="warn",v=`${M.toFixed(1)}× floor`):(d="bad",v=`${(1/M).toExponential(1)}× too weak`)}const L=l.floorT>0?Math.max(2,Math.min(100,100+12*Math.log10(t/l.floorT))):l.id==="mmw"?Math.max(2,100-this.distanceM*7):Math.max(2,100-this.distanceM*2);return o`
+
+
+
+ ${l.label}
+ ${v}
+
+
+ `}),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 o`
+
+
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,+l.target.value)}} />
+
+ 10 cm → 100 km log scale
+
+
+
+
+ Heart dipole moment
+ ${e.toExponential(2)} A·m²
+
+
{this.momentLog10=+l.target.value}} />
+
+ published cardiac MCG ≈ 5×10⁻⁹ A·m²
+
+
+
this.runDemo()}>
+ ${this.running?"Running nvsim…":"▶ Run nvsim at this distance"}
+
+ ${this.err?o`
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(s)+"/√Hz":"—"}
+
+
+ Frames run
+ ${this.result?.nFrames??"—"}
+
+
+ Witness (this run)
+ ${this.result?.witnessHex.slice(0,16)??"—"}…
+
+
+
+
+ Per-tier detectability
+
+ ${i}
+
+
+
+
${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 o`
+ 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.
+
+
+ `}};j.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;
+ }
+ `;he([u()],j.prototype,"distanceM",2);he([u()],j.prototype,"momentLog10",2);he([u()],j.prototype,"result",2);he([u()],j.prototype,"running",2);he([u()],j.prototype,"err",2);j=he([w("nv-ghost-murmur")],j);var Aa=Object.defineProperty,Ca=Object.getOwnPropertyDescriptor,Qe=(e,t,a,s)=>{for(var i=s>1?void 0:s?Ca(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&Aa(t,a,i),i};const pt=[{term:"NV-diamond",category:"physics",body:"Nitrogen-vacancy defect in synthetic diamond. The simulator models a 1 mm³ ensemble (~10¹² centers) addressed by 532 nm pump light + a 2.87 GHz microwave drive. Used as a room-temperature magnetometer with shot-noise floor ~1 pT/√Hz at the published lab record."},{term:"CW-ODMR",category:"physics",body:"Continuously-driven optically-detected magnetic resonance. Sweep the microwave frequency around the NV zero-field splitting (D = 2.87 GHz) and watch the photoluminescence dip when the microwave matches the spin transition. The dip splits with applied magnetic field along each of the four ⟨111⟩ NV axes."},{term:"MagFrame",category:"rust",body:"Fixed-layout 60-byte binary record nvsim emits per (sensor × sample). Magic 0xC51A_6E70, version 1, little-endian. Carries timestamp, recovered B vector (pT), per-axis sigma, noise floor, and flag bits for saturation / shot-noise-disabled / heavy-attenuation."},{term:"Witness",category:"rust",body:"SHA-256 hash over the concatenated MagFrame bytes for a canonical reference run (Proof::REFERENCE_SCENE_JSON @ seed=42, N=256). Same inputs → same hash, byte-for-byte, across runs and machines. The dashboard re-derives it in WASM and compares against Proof::EXPECTED_WITNESS_HEX pinned at build time."},{term:"Determinism gate",category:"rust",body:"A pass/fail check: did this build of nvsim produce the expected witness? If yes → every constant (γ_e, D_GS, μ₀, contrast, T₂*, the PRNG stream, the frame layout, the pipeline ordering) is byte-identical to the published reference. If no → something drifted; the dashboard names which."},{term:"Lock-in demod",category:"physics",body:"Multiply the photoluminescence signal by cos(2π·f_mod·t) and low-pass to recover the slowly-varying B-field component. The simulator emulates a lock-in with output gain 2 and a single-pole IIR LP filter; settable via the Tunables panel (f_mod default 1 kHz)."},{term:"Shot-noise floor",category:"physics",body:'δB = 1 / (γ_e · C · √(N · t · T₂*)) — the irreducible quantum noise floor for an NV ensemble. With nvsim defaults (N=10¹², C=0.03, T₂*=200 ns): ≈1.18 pT/√Hz. Toggleable via the Tunables panel for "analytic" runs without noise.'},{term:"Biot-Savart",category:"physics",body:"Closed-form magnetic field at a point from a current loop or a magnetic dipole. The Scene panel's sources (heart proxy, mains loop, ferrous body, eddy current) all reduce to Biot-Savart-style superpositions over the sensor position."},{term:"Multistatic fusion",category:"physics",body:"Combining evidence from multiple sensors at known geometric configurations. RuView's Cramer-Rao-weighted attention over WiFi CSI nodes + 60 GHz radar nodes + (hypothetically) NV nodes; documented in ADR-029 and the Ghost Murmur view."},{term:"Scene",category:"ui",body:'The simulated magnetic environment: a list of sources (dipole, current loop, ferrous body, eddy current) plus one or more sensor positions and an ambient field. The dashboard ships a "rebar-walkby-01" reference scene; click "New scene…" in the command palette (⌘K) to build your own.'},{term:"Tunables",category:"ui",body:"Sliders that change the running pipeline's digitiser config. Each edit debounces 300 ms, then rebuilds the WASM pipeline with the new f_s / f_mod / dt / shot-noise setting. The frame stream picks up the change without a restart."},{term:"Transport",category:"ui",body:"How the dashboard talks to nvsim. Default is WASM — the simulator runs in a Web Worker right here in your browser, no server. The optional WS transport is REST + binary WebSocket against a host-supplied nvsim-server (see ADR-092 §6.2). Toggle in Settings."},{term:"App Store",category:"ui",body:"Catalog of all 65+ hot-loadable WASM edge modules from wifi-densepose-wasm-edge plus the simulators. Each card carries id / category / status / event IDs; the toggle marks an app active in this session and (in WS mode) pushes the activation to a connected ESP32 mesh."},{term:"Ghost Murmur",category:"ui",body:'Research view that audits the publicly-reported April 2026 CIA NV-diamond heartbeat detector against the open physics literature. Includes a live "Try it yourself" sandbox where you can place a heart dipole at any distance from the sensor and ask: which transport tier would actually detect it?'}],Ea=[{q:"Is this a real simulator or a mockup?",a:"Real. The Rust crate at v2/crates/nvsim is the same code that runs in the browser via WASM. Press Verify witness on the Witness panel — the SHA-256 you see is byte-equivalent to what `cargo test -p nvsim` produces."},{q:'Why does my "Recovered |B|" sit much higher than "Predicted |B|" in the Ghost Murmur demo?',a:"The recovered value reads the simulator's ADC quantization floor, not the actual magnetic signal. With COTS-default sensor noise (~300 pT/√Hz) and 16-bit ADC at ±10 µT FS, anything below ~1 pT vanishes into ~2 nT of digitization residual. That's the lesson — the press claim sits far below this floor at any meaningful range."},{q:"Can I run my own scene?",a:'Yes. Press ⌘K to open the command palette and pick "New scene…". You get five fields (name, dipole moment, distance, ferrous toggle, mains toggle); the dashboard builds the JSON and pushes it via client.loadScene().'},{q:"Does any of my data leave the browser?",a:"No. WASM mode is local-only — the worker, the WASM binary, and the IndexedDB persistence all live in your browser. The optional WS transport (off by default) talks to a host of your choosing."},{q:"What does the witness mismatch (red ✗) mean?",a:"The current build of nvsim produced a SHA-256 that doesn't match the constant pinned at compile time. Possible causes: a different Rust toolchain, a dependency version drift, a manual edit to a physics constant, or an honest bug. Audit the diff against ADR-089 §5."},{q:"Why are the Inspector / Witness rail buttons there if there's already a right-side inspector?",a:'The right-side inspector is the compact live view; the rail buttons open a full-width version with bigger charts, an explainer header, reference-scene metadata cards, and (on Witness) a "what this verifies" panel. Both stay in sync — the right rail is for glancing, the main area is for diving in.'},{q:'Why is there an "App Store" if this is a magnetometer simulator?',a:"Because nvsim is one tile in a larger sensing platform. The catalog lists every hot-loadable WASM edge module RuView ships — medical, security, building, retail, industrial, signal, learning, autonomy. The simulators (nvsim today, more in future) are first-class entries in the same catalog."}],Pa=[{step:1,title:"Hit ▶ Run",body:"The big amber button in the topbar starts the live frame stream. The pipeline runs ~1.8 kHz on x86_64 WASM, well above the 1 kHz Cortex-A53 acceptance gate."},{step:2,title:"Watch the B-vector trace",body:"The Inspector → Signal tab shows the recovered field per axis updating in real time. The frame strip below it is one bar per ~32-frame batch."},{step:3,title:"Verify the witness",body:"Click the rail Witness button (or REPL: proof.verify). The dashboard re-runs the canonical reference scene and asserts the SHA-256 byte-for-byte."},{step:4,title:"Drag a source",body:"Grab the rebar / heart proxy / mains loop / ferrous door in the scene canvas; positions persist via IndexedDB."},{step:5,title:"Tweak the tunables",body:"Sliders in the left sidebar update the running pipeline (f_s, f_mod, integration time, shot-noise). Changes debounce 300 ms then push to the worker."},{step:6,title:"Open the Ghost Murmur view",body:'The ghost icon in the rail. Move the distance + moment sliders, hit "Run nvsim at this distance" — the live demo runs the real Rust pipeline through WASM and shows which transport tier would actually detect.'},{step:7,title:"Browse the App Store",body:"The grid icon. 65+ edge apps: medical, security, building, retail, industrial, signal, learning. Toggle to mark active in this session."}],Ra=[{keys:"⌘K / Ctrl K",label:"Command palette"},{keys:"Space",label:"Play / pause pipeline"},{keys:"⌘R / Ctrl R",label:"Reset pipeline (with confirm)"},{keys:"⌘, / Ctrl ,",label:"Settings drawer"},{keys:"⌘N / Ctrl N",label:"New scene"},{keys:"⌘E / Ctrl E",label:"Export proof bundle"},{keys:"⌘/ / Ctrl /",label:"Toggle theme (dark / light)"},{keys:"`",label:"Toggle debug HUD"},{keys:"?",label:"Open this help center"},{keys:"1 · 2 · 3",label:"Switch inspector tab (Signal / Frame / Witness)"},{keys:"Esc",label:"Close any modal / palette / drawer"},{keys:"/",label:"Focus the REPL prompt"}];let ue=class extends x{constructor(){super(...arguments),this.open=!1,this.section="quickstart",this.query="",this.closeListener=()=>this.close(),this.show=e=>{const t=e.detail;t?.section&&(this.section=t.section),this.open=!0,this.setAttribute("open","")},this.onKey=e=>{const t=e.target,a=t?.tagName==="INPUT"||t?.tagName==="TEXTAREA";e.key==="?"&&!a&&!e.ctrlKey&&!e.metaKey?(e.preventDefault(),this.show(new CustomEvent("nv-show-help"))):e.key==="Escape"&&this.open&&this.close()}}connectedCallback(){super.connectedCallback(),window.addEventListener("nv-show-help",this.show),window.addEventListener("nv-show-help-close",this.closeListener),window.addEventListener("keydown",this.onKey)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("nv-show-help",this.show),window.removeEventListener("nv-show-help-close",this.closeListener),window.removeEventListener("keydown",this.onKey)}close(){this.open=!1,this.removeAttribute("open")}filteredGlossary(){if(!this.query.trim())return pt;const e=this.query.toLowerCase();return pt.filter(t=>t.term.toLowerCase().includes(e)||t.body.toLowerCase().includes(e))}renderQuickstart(){return o`
+ Quickstart
+ Seven taps to get from "I just opened the dashboard" to "I'm running my own scene with verified determinism."
+ {window.dispatchEvent(new CustomEvent("nv-show-help-close")),window.dispatchEvent(new CustomEvent("nv-show-tour"))}}>
+ ★ Take the interactive 10-step tour
+
+ ${Pa.map(e=>o`
+
+ `)}
+ `}renderGlossary(){const e=this.filteredGlossary();return o`
+ Glossary
+ Every piece of jargon in the dashboard, defined in one paragraph each.
+ this.query=t.target.value} />
+ ${e.length===0?o`No terms match.
`:e.map(t=>o`
+
+
+ ${t.term}
+ ${t.category}
+
+
${t.body}
+
+ `)}
+ `}renderFaq(){return o`
+ FAQ
+ The questions I was asked twice in the first week of demos.
+ ${Ea.map(e=>o`
+
+ `)}
+ `}renderShortcuts(){return o`
+ Keyboard shortcuts
+ Everything is reachable without a mouse.
+
+ ${Ra.map(e=>o`
+ ${e.keys} ${e.label}
+ `)}
+
+ `}renderAbout(){return o`
+ About this dashboard
+ What you're looking at, in one screen.
+ nvsim is a deterministic forward simulator for nitrogen-vacancy diamond magnetometry.
+ The Rust crate at v2/crates/nvsim is the source of truth; this dashboard is a
+ Vite + Lit single-page app that ships the crate compiled to WebAssembly inside a Web Worker.
+ The defining commitment is determinism : same (scene, config, seed) →
+ byte-identical SHA-256 witness across browsers, OSes, and transports. Press the
+ Verify witness button on the Witness tab to assert this live.
+ The codebase is open source (Apache-2.0 OR MIT). Find it on GitHub:
+ github.com/ruvnet/RuView. Decisions are documented in ADRs 089 (nvsim),
+ 090 (Lindblad extension, conditional), 091 (sub-THz radar research),
+ 092 (this dashboard), 093 (UX gap analysis).
+ This dashboard is one of several RuView demos. Sibling demos at
+ github.io/RuView/ include the Observatory and Pose Fusion views.
+ `}render(){return o`
+
+
+
Help
+
this.close()}>×
+
+
+ ${["quickstart","glossary","faq","shortcuts","about"].map(e=>o`
+ this.section=e}>
+ ${e==="quickstart"?"🚀 Quickstart":e==="glossary"?"📖 Glossary":e==="faq"?"? FAQ":e==="shortcuts"?"⌨ Shortcuts":"ℹ About"}
+
+ `)}
+
+
+ ${this.section==="quickstart"?this.renderQuickstart():this.section==="glossary"?this.renderGlossary():this.section==="faq"?this.renderFaq():this.section==="shortcuts"?this.renderShortcuts():this.renderAbout()}
+
+
+ Press ? any time to reopen
+ nvsim · Apache-2.0 OR MIT
+
+
+ `}};ue.styles=y`
+ :host {
+ position: fixed; inset: 0;
+ background: rgba(0, 0, 0, 0.55);
+ backdrop-filter: blur(4px);
+ z-index: 230;
+ 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(880px, 94vw);
+ max-height: 86vh;
+ display: grid;
+ grid-template-columns: 200px 1fr;
+ grid-template-rows: auto 1fr auto;
+ overflow: hidden;
+ 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); }
+ @media (max-width: 700px) {
+ .modal { grid-template-columns: 1fr; grid-template-rows: auto auto 1fr auto; max-height: 92vh; }
+ .nav { border-right: 0; border-bottom: 1px solid var(--line); flex-direction: row; overflow-x: auto; }
+ .nav button { white-space: nowrap; }
+ }
+ .h {
+ grid-column: 1 / -1;
+ padding: 14px 18px;
+ border-bottom: 1px solid var(--line);
+ display: flex; align-items: center; justify-content: space-between;
+ }
+ .h .ttl { font-size: 15px; font-weight: 600; }
+ .nav {
+ border-right: 1px solid var(--line);
+ padding: 12px 8px;
+ display: flex; flex-direction: column; gap: 2px;
+ background: var(--bg-1);
+ }
+ .nav button {
+ text-align: left;
+ padding: 8px 12px;
+ background: transparent;
+ border: 1px solid transparent;
+ border-radius: 6px;
+ color: var(--ink-3);
+ font-size: 12.5px;
+ cursor: pointer;
+ transition: color 0.15s, background 0.15s;
+ }
+ .nav button:hover { color: var(--ink); background: var(--bg-2); }
+ .nav button.on {
+ color: var(--ink); background: var(--bg-3);
+ border-color: var(--line-2);
+ }
+ .body {
+ padding: 18px 22px;
+ overflow-y: auto;
+ font-size: 13px;
+ color: var(--ink-2);
+ line-height: 1.6;
+ }
+ .body h2 {
+ margin: 0 0 8px;
+ font-size: 18px;
+ color: var(--ink);
+ letter-spacing: -0.01em;
+ }
+ .body .lead {
+ color: var(--ink-3);
+ font-size: 12.5px;
+ margin: 0 0 14px;
+ }
+ .body p { margin: 0 0 12px; }
+ .body code {
+ font-family: var(--mono);
+ background: var(--bg-3);
+ padding: 1px 5px;
+ border-radius: 4px;
+ font-size: 11.5px;
+ color: var(--accent);
+ }
+ .body kbd {
+ font-family: var(--mono);
+ padding: 2px 6px;
+ background: var(--bg-3);
+ border: 1px solid var(--line);
+ border-radius: 4px;
+ font-size: 11.5px;
+ color: var(--ink);
+ }
+ .step {
+ display: grid;
+ grid-template-columns: 32px 1fr;
+ gap: 12px;
+ padding: 10px 0;
+ border-bottom: 1px solid var(--line);
+ }
+ .step:last-child { border-bottom: 0; }
+ .step .num {
+ width: 26px; height: 26px;
+ border-radius: 50%;
+ background: var(--accent);
+ color: #1a0f00;
+ font-family: var(--mono);
+ font-size: 12.5px;
+ font-weight: 700;
+ display: grid; place-items: center;
+ }
+ .step .ttl { color: var(--ink); font-weight: 600; font-size: 13.5px; margin-bottom: 2px; }
+ .step .body-text { font-size: 12.5px; color: var(--ink-2); line-height: 1.55; }
+ .glossary-search {
+ width: 100%;
+ padding: 8px 12px;
+ background: var(--bg-3);
+ border: 1px solid var(--line);
+ border-radius: 6px;
+ font-family: var(--mono);
+ font-size: 12.5px;
+ color: var(--ink);
+ outline: none;
+ margin-bottom: 14px;
+ }
+ .glossary-search:focus { border-color: var(--accent); }
+ .term {
+ padding: 10px 0;
+ border-bottom: 1px solid var(--line);
+ }
+ .term:last-child { border-bottom: 0; }
+ .term .head {
+ display: flex; align-items: center; gap: 8px; margin-bottom: 4px;
+ }
+ .term .name {
+ font-family: var(--mono);
+ font-size: 13.5px;
+ color: var(--accent);
+ font-weight: 600;
+ }
+ .term .badge {
+ font-family: var(--mono);
+ font-size: 9.5px;
+ padding: 1px 6px;
+ border-radius: 4px;
+ border: 1px solid var(--line);
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ }
+ .term .badge.physics { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.4); }
+ .term .badge.rust { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.4); }
+ .term .badge.ui { color: var(--accent-4); border-color: oklch(0.78 0.14 145 / 0.4); }
+ .term .body-text {
+ font-size: 12.5px;
+ color: var(--ink-2);
+ line-height: 1.55;
+ }
+ .faq-item {
+ padding: 10px 0;
+ border-bottom: 1px solid var(--line);
+ }
+ .faq-item:last-child { border-bottom: 0; }
+ .faq-item .q {
+ color: var(--ink);
+ font-weight: 600;
+ font-size: 13.5px;
+ margin-bottom: 4px;
+ }
+ .faq-item .a { font-size: 12.5px; color: var(--ink-2); line-height: 1.55; }
+ .shortcuts {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 8px 16px;
+ align-items: baseline;
+ }
+ .f {
+ grid-column: 1 / -1;
+ padding: 10px 18px;
+ border-top: 1px solid var(--line);
+ display: flex; align-items: center; justify-content: space-between;
+ font-size: 11.5px; color: var(--ink-3);
+ }
+ .close {
+ width: 28px; height: 28px;
+ background: transparent; border: 1px solid var(--line);
+ border-radius: 6px;
+ color: var(--ink-2);
+ cursor: pointer;
+ }
+ .close:hover { color: var(--ink); border-color: var(--line-2); }
+ `;Qe([u()],ue.prototype,"open",2);Qe([u()],ue.prototype,"section",2);Qe([u()],ue.prototype,"query",2);ue=Qe([w("nv-help")],ue);var Da=Object.getOwnPropertyDescriptor,Ia=(e,t,a,s)=>{for(var i=s>1?void 0:s?Da(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=n(i)||i);return i};let st=class extends x{connectedCallback(){super.connectedCallback(),g(()=>{m.value,S.value,_.value,this.requestUpdate()})}go(e){if(e==="tour"){window.dispatchEvent(new CustomEvent("nv-show-tour"));return}if(e==="help"){window.dispatchEvent(new CustomEvent("nv-show-help"));return}this.dispatchEvent(new CustomEvent("navigate",{detail:e,bubbles:!0,composed:!0}))}async runDemo(){const e=f();e&&(m.value||(await e.run(),m.value=!0,c("ok","demo started · streaming MagFrames")))}render(){const e=m.value,t=S.value==="ok";return o`
+
+
NV
+
An open-source quantum-magnetometer simulator, in your browser.
+
+ nvsim runs a real Rust simulator (the same code that
+ cargo test
+ uses) entirely in WebAssembly. No server, no upload, no telemetry.
+ Press the button to start the live magnetic-field simulation, or
+ take the 60-second tour first.
+
+
+ this.runDemo()}>
+ ${e?"✓ Demo running":"▶ Run the simulation"}
+
+ this.go("tour")}>
+ ★ Take the 60-second tour
+
+ this.go("help")}>
+ ? Help center
+
+
+
+
+ ${e?o`Live · ${_.value>0?(_.value/1e3).toFixed(2)+" kHz":"starting…"}${t?" · witness verified ✓":""}`:o`Idle${t?" · witness verified ✓":""}`}
+
+
+
+
+
this.go("scene")}
+ @keydown=${a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),this.go("scene"))}}>
+
🌐
+
Live scene
+
Drag magnetic sources, watch the recovered field update in real time, and tweak sample rate / noise / integration.
+
Open scene →
+
+
+
this.go("apps")}
+ @keydown=${a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),this.go("apps"))}}>
+
🛍
+
App Store · 66 edge apps
+
Browse 65 hot-loadable WASM sensing modules across medical, security, building, retail, industrial, learning. Six run live in the browser.
+
Browse the catalogue →
+
+
+
this.go("witness")}
+ @keydown=${a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),this.go("witness"))}}>
+
✓
+
Determinism gate
+
Re-derive the SHA-256 witness for the canonical reference scene right here in your browser. Same inputs → same hash, every time.
+
Verify the witness →
+
+
+
this.go("ghost-murmur")}
+ @keydown=${a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),this.go("ghost-murmur"))}}>
+
👻
+
Ghost Murmur reality check
+
Audit the publicly-reported April 2026 CIA NV-diamond program against published physics. Live distance/moment sliders.
+
Read the spec →
+
+
+
+
+ `}};st.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: 28px clamp(16px, 6vw, 56px) 60px;
+ }
+ .hero {
+ max-width: 800px;
+ margin: 16px auto 28px;
+ text-align: center;
+ }
+ .hero .icon {
+ width: 56px; height: 56px;
+ margin: 0 auto 18px;
+ border-radius: 14px;
+ background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);
+ display: grid; place-items: center;
+ font-family: var(--mono);
+ font-weight: 700;
+ font-size: 18px;
+ color: #1a0f00;
+ box-shadow: 0 8px 24px -6px oklch(0.55 0.16 30 / 0.4);
+ }
+ .hero h1 {
+ margin: 0 0 8px;
+ font-size: clamp(24px, 4vw, 34px);
+ letter-spacing: -0.02em;
+ color: var(--ink);
+ line-height: 1.15;
+ }
+ .hero .tag {
+ font-size: clamp(13px, 1.6vw, 15px);
+ color: var(--ink-2);
+ margin: 0 0 22px;
+ line-height: 1.55;
+ }
+ .hero .ctas {
+ display: flex; flex-wrap: wrap; gap: 8px;
+ justify-content: center;
+ }
+ .cta {
+ padding: 11px 20px;
+ border-radius: 10px;
+ font-size: 14px;
+ font-weight: 600;
+ cursor: pointer;
+ font-family: inherit;
+ border: 1px solid var(--line);
+ background: var(--bg-2);
+ color: var(--ink);
+ transition: transform 0.12s, border-color 0.12s, filter 0.12s;
+ }
+ .cta:hover { transform: translateY(-1px); border-color: var(--line-2); }
+ .cta.primary {
+ background: var(--accent);
+ border-color: var(--accent);
+ color: #1a0f00;
+ }
+ .cta.primary:hover { filter: brightness(1.08); }
+ .status {
+ display: inline-flex; align-items: center; gap: 8px;
+ padding: 6px 12px;
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ border-radius: 999px;
+ font-size: 12px;
+ font-family: var(--mono);
+ color: var(--ink-2);
+ margin-top: 18px;
+ }
+ .status .dot {
+ width: 8px; height: 8px; border-radius: 50%;
+ background: var(--ink-3);
+ }
+ .status.live .dot {
+ background: var(--ok);
+ box-shadow: 0 0 8px var(--ok);
+ animation: pulse 2s infinite;
+ }
+ @keyframes pulse { 50% { opacity: 0.5; } }
+
+ .grid {
+ max-width: 980px;
+ margin: 36px auto 0;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: 14px;
+ }
+ .card {
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ border-radius: var(--radius);
+ padding: 18px 20px;
+ cursor: pointer;
+ transition: transform 0.12s, border-color 0.12s, background 0.12s;
+ display: flex; flex-direction: column; gap: 6px;
+ text-align: left;
+ color: inherit;
+ }
+ .card:hover {
+ transform: translateY(-2px);
+ border-color: var(--accent);
+ background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 70 / 0.04) 100%);
+ }
+ .card .ico {
+ font-size: 22px;
+ line-height: 1;
+ margin-bottom: 4px;
+ }
+ .card h3 {
+ margin: 0;
+ font-size: 14.5px;
+ font-weight: 600;
+ color: var(--ink);
+ letter-spacing: -0.01em;
+ }
+ .card p {
+ margin: 0;
+ font-size: 12.5px;
+ color: var(--ink-2);
+ line-height: 1.55;
+ }
+ .card .arrow {
+ color: var(--accent);
+ font-family: var(--mono);
+ font-size: 11.5px;
+ margin-top: 6px;
+ }
+
+ .footnote {
+ max-width: 800px;
+ margin: 36px auto 0;
+ text-align: center;
+ font-size: 12px;
+ color: var(--ink-3);
+ line-height: 1.55;
+ }
+ .footnote code {
+ font-family: var(--mono);
+ background: var(--bg-3);
+ padding: 1px 5px;
+ border-radius: 4px;
+ color: var(--accent);
+ font-size: 11px;
+ }
+ .footnote a {
+ color: var(--accent-2);
+ text-decoration: underline dotted;
+ cursor: pointer;
+ }
+ `;st=Ia([w("nv-home")],st);var Fa=Object.defineProperty,Oa=Object.getOwnPropertyDescriptor,St=(e,t,a,s)=>{for(var i=s>1?void 0:s?Oa(t,a):t,r=e.length-1,n;r>=0;r--)(n=e[r])&&(i=(s?n(t,a,i):n(i))||i);return s&&i&&Fa(t,a,i),i};let Ue=class extends x{constructor(){super(...arguments),this.view="home"}render(){const e=this.view==="home";return o`
+ {t.preventDefault(),this.shadowRoot?.querySelector(".main")?.focus()}}>
+ Skip to main content
+
+
+ this.view=t.detail}>
+
+
+
+ ${this.view==="home"?o` `:this.view==="apps"?o` `:this.view==="ghost-murmur"?o` `:this.view==="inspector"?o` `:this.view==="witness"?o` `:o` `}
+
+
+
+
+
+
+
+
+
+
+
+
+ `}};Ue.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;
+ }
+ /* Home view simplifies: hides sidebar / inspector / console so the
+ hero gets the full screen. Power-user panels stay one rail click away. */
+ .app.simple {
+ grid-template-columns: 56px 1fr;
+ grid-template-rows: 48px 1fr;
+ grid-template-areas:
+ 'rail topbar'
+ 'rail main';
+ }
+ .app.simple nv-sidebar,
+ .app.simple nv-inspector,
+ .app.simple nv-console { display: none; }
+ 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; }
+ }
+ `;St([u()],Ue.prototype,"view",2);Ue=St([w("nv-app")],Ue);function Ha(e,t,a){const s=e.getUint32(t+0,!0),i=e.getUint16(t+4,!0),r=e.getUint16(t+6,!0),n=e.getUint16(t+8,!0),l=e.getBigUint64(t+12,!0),d=e.getFloat32(t+20,!0),v=e.getFloat32(t+24,!0),L=e.getFloat32(t+28,!0),M=e.getFloat32(t+32,!0),A=e.getFloat32(t+36,!0),me=e.getFloat32(t+40,!0),te=e.getFloat32(t+44,!0),ge=e.getFloat32(t+48,!0);return{magic:s,version:i,flags:r,sensorId:n,tUs:l,bPt:[d,v,L],sigmaPt:[M,A,me],noiseFloorPtSqrtHz:te,temperatureK:ge,raw:a.subarray(t,t+60)}}function _t(e){const a=new DataView(e.buffer,e.byteOffset,e.byteLength),s=[];for(let i=0;i+60<=e.byteLength;i+=60)s.push(Ha(a,i,e));return s}class Na{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 s=a.batch,i=new Uint8Array(s),n={frames:_t(i),bytes:i};this.frameSubs.forEach(d=>d(n));const l=a.fps;l>0&&this.eventSubs.forEach(d=>d({type:"fps",value:l}));return}if(a.type==="state"){this.eventSubs.forEach(s=>s({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(s=>s({type:"log",level:"err",msg:String(a.msg)}));return}if(typeof a.id=="number"&&this.pending.has(a.id)){const s=this.pending.get(a.id);this.pending.delete(a.id),a.type==="err"?s.reject(new Error(String(a.msg))):s.resolve(a)}}}rpc(t,a=[]){const s=this.nextId++;return new Promise((i,r)=>{this.pending.set(s,{resolve:i,reject:r}),this.worker.postMessage({...t,id:s},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,s=await this.rpc({type:"witnessVerify",samples:256,expected:a},[a]);return s.ok?{ok:!0}:{ok:!1,actual:new Uint8Array(s.actual)}}async runTransient(t,a,s,i){const r=await this.rpc({type:"runTransient",scene:JSON.stringify(t),config:JSON.stringify(a),seed:Number(s&0xFFFFFFFFn),samples:i});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(""),s=this.bootInfo??await this.boot(),i=JSON.stringify({kind:"nvsim-proof-bundle",version:s.buildVersion,seed:"0x0000002A",nSamples:256,witness:a,expected:s.expectedWitnessHex,ok:a===s.expectedWitnessHex,ts:new Date().toISOString()},null,2);return new Blob([i],{type:"application/json"})}async buildId(){return(await this.rpc({type:"buildId"})).buildId}async close(){this.worker.terminate()}}function La(e){return e.startsWith("ws://")||e.startsWith("wss://")?e:e.replace(/^http/,"ws")}class Wa{constructor(t){this.ws=null,this.bootInfo=null,this.frameSubs=new Set,this.eventSubs=new Set,this.running=!1,this.framesEmitted=0,this.fpsLast=performance.now(),this.fpsCount=0,this.baseUrl=t.replace(/\/$/,""),this.wsUrl=`${La(this.baseUrl)}/ws/stream`}async json(t,a){const s=await fetch(`${this.baseUrl}${t}`,{...a,headers:{"content-type":"application/json",...a?.headers??{}}});if(!s.ok)throw new Error(`${t}: ${s.status} ${s.statusText}`);return await s.json()}async boot(){if(this.bootInfo)return this.bootInfo;const t=await this.json("/api/health");return this.bootInfo={buildVersion:t.nvsim_version,frameMagic:t.magic,frameBytes:t.frame_bytes,expectedWitnessHex:t.expected_witness_hex},this.openWs(),this.bootInfo}openWs(){if(this.ws)return;const t=new WebSocket(this.wsUrl);t.binaryType="arraybuffer",t.onopen=()=>{this.eventSubs.forEach(a=>a({type:"log",level:"ok",msg:`ws/stream connected · ${this.wsUrl}`}))},t.onclose=()=>{this.ws=null,this.eventSubs.forEach(a=>a({type:"log",level:"warn",msg:"ws/stream closed"}))},t.onerror=()=>{this.eventSubs.forEach(a=>a({type:"log",level:"err",msg:`ws/stream error · ${this.wsUrl}`}))},t.onmessage=a=>{if(!(a.data instanceof ArrayBuffer))return;const s=new Uint8Array(a.data),i=_t(s);if(i.length===0)return;const r={frames:i,bytes:s};this.frameSubs.forEach(l=>l(r)),this.framesEmitted+=i.length,this.fpsCount+=i.length;const n=performance.now();if(n-this.fpsLast>=1e3){const l=this.fpsCount*1e3/(n-this.fpsLast);this.eventSubs.forEach(d=>d({type:"fps",value:l})),this.fpsLast=n,this.fpsCount=0}},this.ws=t}async loadScene(t){await this.json("/api/scene",{method:"PUT",body:JSON.stringify(t)})}async setConfig(t){await this.json("/api/config",{method:"PUT",body:JSON.stringify(t)})}async setSeed(t){await this.json("/api/seed",{method:"PUT",body:JSON.stringify({seed_hex:"0x"+t.toString(16).toUpperCase().padStart(16,"0")})})}async reset(){await this.json("/api/reset",{method:"POST"}),this.running=!1,this.framesEmitted=0,this.eventSubs.forEach(t=>t({type:"state",running:!1,t:0,framesEmitted:0}))}async run(t){await this.json("/api/run",{method:"POST"}),this.running=!0,this.eventSubs.forEach(a=>a({type:"state",running:!0,t:0,framesEmitted:this.framesEmitted}))}async pause(){await this.json("/api/pause",{method:"POST"}),this.running=!1,this.eventSubs.forEach(t=>t({type:"state",running:!1,t:0,framesEmitted:this.framesEmitted}))}async step(t,a){await this.json("/api/step",{method:"POST",body:JSON.stringify({direction:t,dt_ms:a})})}onFrames(t){this.frameSubs.add(t)}onEvent(t){this.eventSubs.add(t)}async generateWitness(t){const a=await this.json("/api/witness/generate",{method:"POST",body:JSON.stringify({samples:t})}),s=new Uint8Array(32);for(let i=0;i<32;i++)s[i]=parseInt(a.witness_hex.slice(i*2,i*2+2),16);return s}async verifyWitness(t){const a=Array.from(t).map(r=>r.toString(16).padStart(2,"0")).join(""),s=await this.json("/api/witness/verify",{method:"POST",body:JSON.stringify({expected_hex:a,samples:256})});if(s.ok)return{ok:!0};const i=new Uint8Array(32);for(let r=0;r<32;r++)i[r]=parseInt(s.actual_hex.slice(r*2,r*2+2),16);return{ok:!1,actual:i}}async exportProofBundle(){const t=await fetch(`${this.baseUrl}/api/export-proof`,{method:"POST"}).then(a=>a.text());return new Blob([t],{type:"application/json"})}async runTransient(t,a,s,i){return{bRecoveredT:[0,0,0],bMagT:0,noiseFloorPtSqrtHz:0,sigmaPt:[0,0,0],nFrames:0,witnessHex:"(transient route not available in WS transport — V1 limitation)"}}async buildId(){return`nvsim@${(this.bootInfo??await this.boot()).buildVersion} (ws)`}async close(){this.ws?.close(),this.ws=null}}function ut(e){document.documentElement.setAttribute("data-theme",e)}function vt(e){document.body.classList.remove("density-comfy","density-default","density-compact"),document.body.classList.add(`density-${e}`)}function ht(e){document.body.classList.toggle("reduce-motion",e)}(async()=>{const e=await F("theme")??"dark",t=await F("density")??"default",a=window.matchMedia?.("(prefers-reduced-motion: reduce)").matches??!1,s=await F("motionReduced")??a;$.value=e,ut(e),P.value=t,vt(t),D.value=s,ht(s),g(()=>{ut($.value),O("theme",$.value)}),g(()=>{vt(P.value),O("density",P.value)}),g(()=>{ht(D.value),O("motionReduced",D.value)});const i=await F("repl-history");i&&Array.isArray(i)&&(X.value=i),g(()=>{O("repl-history",X.value)});const r=await F("scene-positions");r&&Array.isArray(r)&&(Te.value=r),g(()=>{O("scene-positions",Te.value)});const n=await F("wsUrl")??"";n&&(H.value=n);const l=await F("transport")??"wasm";z.value=l,g(()=>{O("wsUrl",H.value)}),g(()=>{O("transport",z.value)});const d={},v=[],L=performance.now(),M=b=>{if(b.frames.length===0)return;const h=b.frames[b.frames.length-1];Q.value=h;const k=h.bPt[0]*1e-12,E=h.bPt[1]*1e-12,I=h.bPt[2]*1e-12;ze.value=[k,E,I];const T=Math.sqrt(k*k+E*E+I*I);for(J.value=T,Vt([k*1e9,E*1e9,I*1e9]),qt(Math.min(1,Math.abs(I*1e9)/5+.3)),v.push(T);v.length>256;)v.shift();const Re=bt.value;if(Re.size===0)return;const Tt=(performance.now()-L)/1e3;for(const be of Re){const lt=wt[be];if(!lt)continue;d[be]||(d[be]={});const zt={frame:h,bMagT:T,bRecoveredT:[k,E,I],bHistory:v,elapsedS:Tt,state:d[be]};try{const ae=lt(zt);if(!ae)continue;const Mt=Array.isArray(ae)?ae:[ae];for(const se of Mt)Lt(se),c("info",`[${se.appId}] ${se.eventName} (${se.eventId}) ${se.detail?" · "+se.detail:""}`)}catch(ae){c("warn",`[${be}] runtime error: ${ae.message}`)}}};let A=null;async function me(){try{A&&await A.close();const b=z.value;if(b==="ws"&&H.value.trim()){const h=new Wa(H.value.trim()),k=await h.boot();A=h,De.value=!0,Xe.value=null,N.value=k.expectedWitnessHex,te(h),c("ok",`transport WS · ${H.value} · nvsim@${k.buildVersion}`)}else{b==="ws"&&c("warn","WS transport selected but no URL set — falling back to WASM");const h=new Na,k=await h.boot();A=h,De.value=!0,Xe.value=null,N.value=k.expectedWitnessHex,te(h),c("ok",`transport WASM · nvsim@${k.buildVersion} · magic=0x${k.frameMagic.toString(16).toUpperCase()}`)}Wt(A)}catch(b){const h=b.message;Xe.value=h,De.value=!1,c("err",`transport boot failed: ${h}`)}}function te(b){b.onEvent(h=>{h.type==="log"&&c(h.level,h.msg),h.type==="fps"&&(_.value=h.value),h.type==="state"&&(et.value=BigInt(h.framesEmitted))}),b.onFrames(M)}let ge=!1;g(()=>{z.value,H.value,!ge&&(ge=!0,me().finally(()=>{ge=!1}))}),c("info","nvsim — booting transport");let ot=null;g(()=>{const b=N.value,h=De.value;!b||!h||ot!==b&&(ot=b,(async()=>{const k=A;if(k)try{const E=new Uint8Array(32);for(let T=0;T<32;T++)E[T]=parseInt(b.slice(T*2,T*2+2),16);const I=await k.verifyWitness(E);if(I.ok)R.value=b,c("ok",`witness verified · determinism gate ✓ · transport=${z.value}`);else{const T=Array.from(I.actual).map(Re=>Re.toString(16).padStart(2,"0")).join("");R.value=T,c("err",`WITNESS MISMATCH · expected ${b.slice(0,16)}… got ${T.slice(0,16)}…`)}}catch(E){c("warn",`witness verify skipped: ${E.message}`)}})())}),Ht.value="(reference scene)"})();
+//# sourceMappingURL=index-N2_7iSzr.js.map
diff --git a/nvsim/assets/index-N2_7iSzr.js.map b/nvsim/assets/index-N2_7iSzr.js.map
new file mode 100644
index 00000000..de1a526b
--- /dev/null
+++ b/nvsim/assets/index-N2_7iSzr.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index-N2_7iSzr.js","sources":["../../node_modules/@lit/reactive-element/decorators/custom-element.js","../../node_modules/@lit/reactive-element/decorators/property.js","../../node_modules/@lit/reactive-element/decorators/state.js","../../node_modules/@lit/reactive-element/decorators/base.js","../../node_modules/@lit/reactive-element/decorators/query.js","../../src/components/nv-rail.ts","../../src/store/appStore.ts","../../src/components/nv-modal.ts","../../src/components/nv-toast.ts","../../src/components/nv-topbar.ts","../../src/components/nv-sidebar.ts","../../src/components/nv-scene.ts","../../src/components/nv-inspector.ts","../../src/components/nv-console.ts","../../src/store/apps.ts","../../src/store/persistence.ts","../../src/store/appRuntimes.ts","../../src/components/nv-app-store.ts","../../src/components/nv-palette.ts","../../src/components/nv-debug-hud.ts","../../src/components/nv-settings-drawer.ts","../../src/components/nv-onboarding.ts","../../src/components/nv-ghost-murmur.ts","../../src/components/nv-help.ts","../../src/components/nv-home.ts","../../src/components/nv-app.ts","../../src/transport/NvsimClient.ts","../../src/transport/WasmClient.ts","../../src/transport/WsClient.ts","../../src/main.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst t=t=>(e,o)=>{void 0!==o?o.addInitializer(()=>{customElements.define(t,e)}):customElements.define(t,e)};export{t as customElement};\n//# sourceMappingURL=custom-element.js.map\n","import{notEqual as t,defaultConverter as e}from\"../reactive-element.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const o={attribute:!0,type:String,converter:e,reflect:!1,hasChanged:t},r=(t=o,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),\"setter\"===n&&((t=Object.create(t)).wrapped=!0),s.set(r.name,t),\"accessor\"===n){const{name:o}=r;return{set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t,!0,r)},init(e){return void 0!==e&&this.C(o,void 0,t,e),e}}}if(\"setter\"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t,!0,r)}}throw Error(\"Unsupported decorator location: \"+n)};function n(t){return(e,o)=>\"object\"==typeof o?r(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)}export{n as property,r as standardProperty};\n//# sourceMappingURL=property.js.map\n","import{property as t}from\"./property.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */function r(r){return t({...r,state:!0,attribute:!1})}export{r as state};\n//# sourceMappingURL=state.js.map\n","/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst e=(e,t,c)=>(c.configurable=!0,c.enumerable=!0,Reflect.decorate&&\"object\"!=typeof t&&Object.defineProperty(e,t,c),c);export{e as desc};\n//# sourceMappingURL=base.js.map\n","import{desc as t}from\"./base.js\";\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */function e(e,r){return(n,s,i)=>{const o=t=>t.renderRoot?.querySelector(e)??null;if(r){const{get:e,set:r}=\"object\"==typeof s?n:i??(()=>{const t=Symbol();return{get(){return this[t]},set(e){this[t]=e}}})();return t(n,s,{get(){let t=e.call(this);return void 0===t&&(t=o(this),(null!==t||this.hasUpdated)&&r.call(this,t)),t}})}return t(n,s,{get(){return o(this)}})}}export{e as query};\n//# sourceMappingURL=query.js.map\n","/* Left rail navigation. Emits `navigate` events for view switching. */\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { View } from './nv-app';\n\n@customElement('nv-rail')\nexport class NvRail extends LitElement {\n @property() view: View = 'scene';\n\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 10px 0;\n gap: 4px;\n background: var(--bg-1);\n border-right: 1px solid var(--line);\n }\n .logo {\n width: 36px; height: 36px;\n border-radius: 10px;\n background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);\n display: grid; place-items: center;\n color: #1a0f00;\n font-weight: 700;\n font-family: var(--mono);\n font-size: 11px;\n margin-bottom: 14px;\n box-shadow: 0 4px 12px -2px oklch(0.55 0.16 30 / 0.35);\n }\n .btn {\n width: 36px; height: 36px;\n border-radius: 8px;\n background: transparent;\n border: 1px solid transparent;\n color: var(--ink-3);\n display: grid; place-items: center;\n transition: all 0.15s;\n position: relative;\n cursor: pointer;\n }\n .btn:hover { color: var(--ink); background: var(--bg-2); }\n .btn.active {\n color: var(--ink);\n background: var(--bg-3);\n border-color: var(--line-2);\n }\n .btn.active::before {\n content: ''; position: absolute; left: -10px; top: 8px; bottom: 8px;\n width: 2px; background: var(--accent); border-radius: 2px;\n }\n .btn.ghost.active::before { background: var(--accent-3); }\n .spacer { flex: 1; }\n svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }\n `;\n\n private navigate(v: View): void {\n this.dispatchEvent(new CustomEvent('navigate', { detail: v }));\n }\n\n override render() {\n return html`\n NV
\n \n this.navigate('home')}>\n \n \n this.navigate('scene')}>\n \n \n this.navigate('apps')}>\n \n \n this.navigate('inspector')}>\n \n \n this.navigate('witness')}>\n \n \n this.navigate('ghost-murmur')}>\n \n \n \n \n \n \n \n
\n this.dispatchEvent(new CustomEvent('open-settings', { bubbles: true, composed: true }))}>\n \n \n `;\n }\n}\n","/* Application-wide reactive state.\n *\n * One signal per logical observable; components subscribe to only the\n * signals they read. Keeps re-renders surgical even at 1 kHz frame rates.\n * Persistence lives in `persistence.ts`; this module is pure state.\n */\nimport { signal, computed } from '@preact/signals-core';\nimport type { NvsimClient, MagFrameRecord, NvsimEvent } from '../transport/NvsimClient';\n\nexport type Theme = 'dark' | 'light';\nexport type Density = 'comfy' | 'default' | 'compact';\nexport type TransportMode = 'wasm' | 'ws';\n\nexport const transport = signal('wasm');\nexport const wsUrl = signal('');\nexport const connected = signal(false);\nexport const transportError = signal(null);\n\nexport const running = signal(false);\nexport const paused = signal(true);\nexport const speed = signal(1.0);\nexport const t = signal(0); // sim time (s)\nexport const framesEmitted = signal(0n);\n\nexport const seed = signal(0xCAFEBABEn);\n\nexport const fs = signal(10000); // sample rate Hz\nexport const fmod = signal(1000); // lockin Hz\nexport const dtMs = signal(1.0);\nexport const noiseEnabled = signal(true);\n\nexport const theme = signal('dark');\nexport const density = signal('default');\nexport const motionReduced = signal(false);\nexport const autoUpdate = signal(true);\n\nexport const lastB = signal<[number, number, number]>([0, 0, 0]); // T\nexport const bMag = signal(0);\nexport const snr = signal(0);\nexport const fps = signal(0);\n\nexport const witnessHex = signal('');\nexport const witnessVerified = signal<'pending' | 'ok' | 'fail' | 'idle'>('idle');\nexport const expectedWitness = signal('');\n\nexport const lastFrame = signal(null);\nexport const traceX = signal([]);\nexport const traceY = signal([]);\nexport const traceZ = signal([]);\nexport const stripBars = signal([]);\n\nexport const sceneName = signal('rebar-walkby-01');\nexport const sceneJson = signal('');\n\nexport const consolePaused = signal(false);\nexport const consoleFilter = signal<'all' | 'info' | 'warn' | 'err' | 'dbg' | 'ok'>('all');\n\n/** REPL command history, persisted via persistence.ts (kvSet 'repl-history'). */\nexport const replHistory = signal([]);\nexport function pushReplHistory(cmd: string): void {\n const next = replHistory.value.slice();\n next.push(cmd);\n while (next.length > 200) next.shift();\n replHistory.value = next;\n}\n\n/** Scene drag positions, persisted via persistence.ts (kvSet 'scene-positions'). */\nexport interface SceneItemPos { id: string; x: number; y: number }\nexport const scenePositions = signal([]);\n\n/** App-runtime emitted events. See appRuntimes.ts. */\nimport type { AppEvent } from './appRuntimes';\nexport const appEvents = signal([]);\nexport const appEventCounts = signal>({});\n\nexport function pushAppEvent(ev: AppEvent): void {\n const next = appEvents.value.slice();\n next.push(ev);\n while (next.length > 200) next.shift();\n appEvents.value = next;\n\n const c = { ...appEventCounts.value };\n c[ev.appId] = (c[ev.appId] ?? 0) + 1;\n appEventCounts.value = c;\n}\n\n/** Active app activations — driven by the App Store toggles. Mirrored\n * from `apps.ts` but exposed as a signal here so `main.ts` can dispatch\n * frames to active runtimes without importing the App Store component. */\nexport const activeAppIds = signal>(new Set());\n\nexport const transportLabel = computed(() =>\n transport.value === 'wasm' ? 'wasm' : 'ws',\n);\n\nlet _client: NvsimClient | null = null;\nexport function setClient(c: NvsimClient): void { _client = c; }\nexport function getClient(): NvsimClient | null { return _client; }\n\nexport interface ConsoleLine {\n ts: number;\n level: 'info' | 'warn' | 'err' | 'dbg' | 'ok';\n msg: string;\n}\nexport const consoleLines = signal([]);\nconst MAX_LINES = 200;\n\nexport function pushLog(level: ConsoleLine['level'], msg: string): void {\n if (consolePaused.value) return;\n const next = consoleLines.value.slice();\n next.push({ ts: Date.now(), level, msg });\n while (next.length > MAX_LINES) next.shift();\n consoleLines.value = next;\n}\n\nexport function pushTrace(b: [number, number, number]): void {\n const cap = 200;\n const x = traceX.value.slice(); x.push(b[0]); if (x.length > cap) x.shift();\n const y = traceY.value.slice(); y.push(b[1]); if (y.length > cap) y.shift();\n const z = traceZ.value.slice(); z.push(b[2]); if (z.length > cap) z.shift();\n traceX.value = x;\n traceY.value = y;\n traceZ.value = z;\n}\n\nexport function pushStripBar(amp: number): void {\n const cap = 48;\n const next = stripBars.value.slice();\n next.push(Math.max(0, Math.min(1, amp)));\n while (next.length > cap) next.shift();\n stripBars.value = next;\n}\n\nexport function recordEvent(_ev: NvsimEvent): void {\n // future: route NvsimEvent into store updates per type. For V1 the\n // worker pushes B-vector / frame data directly via the data plane.\n}\n","/* Modal dialog — opened via window.dispatchEvent('nv-modal', { title, body, buttons }). */\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\n\ninterface ModalButton {\n label: string;\n variant?: 'ghost' | 'primary' | 'danger';\n onClick?: () => void;\n}\ninterface ModalReq {\n title: string;\n body: string;\n buttons?: ModalButton[];\n}\n\n@customElement('nv-modal')\nexport class NvModal extends LitElement {\n @state() private open = false;\n @state() private mTitle = '';\n @state() private mBody = '';\n @state() private buttons: ModalButton[] = [];\n\n static styles = css`\n :host {\n position: fixed; inset: 0;\n background: rgba(0,0,0,0.55);\n backdrop-filter: blur(4px);\n z-index: 200;\n display: grid; place-items: center;\n opacity: 0; pointer-events: none;\n transition: opacity 0.18s;\n }\n :host([open]) { opacity: 1; pointer-events: auto; }\n .modal {\n background: var(--bg-1);\n border: 1px solid var(--line-2);\n border-radius: var(--radius);\n box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);\n width: min(520px, 92vw);\n max-height: 86vh;\n display: flex; flex-direction: column;\n transform: translateY(12px) scale(0.98);\n transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);\n }\n :host([open]) .modal { transform: translateY(0) scale(1); }\n .h {\n padding: 14px 16px;\n border-bottom: 1px solid var(--line);\n display: flex; align-items: center; justify-content: space-between;\n }\n .h .ttl { font-size: 14px; font-weight: 600; }\n .body { padding: 16px; overflow-y: auto; font-size: 13px; color: var(--ink-2); line-height: 1.55; }\n .f {\n padding: 12px 16px;\n border-top: 1px solid var(--line);\n display: flex; gap: 8px; justify-content: flex-end;\n }\n button {\n padding: 6px 12px;\n border-radius: 8px;\n font-size: 12.5px;\n cursor: pointer;\n font-family: inherit;\n border: 1px solid var(--line);\n background: var(--bg-2); color: var(--ink);\n }\n button.ghost { background: transparent; }\n button.primary { background: var(--accent); border-color: var(--accent); color: #1a0f00; }\n button.danger { background: var(--bad); border-color: var(--bad); color: #fff; }\n .close {\n width: 28px; height: 28px;\n background: transparent; border: 1px solid var(--line);\n border-radius: 6px;\n color: var(--ink-2);\n }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n window.addEventListener('nv-modal', this.onModal as EventListener);\n window.addEventListener('keydown', this.onKey);\n }\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('nv-modal', this.onModal as EventListener);\n window.removeEventListener('keydown', this.onKey);\n }\n\n private onModal = (e: Event): void => {\n const r = (e as CustomEvent).detail as ModalReq;\n this.mTitle = r.title; this.mBody = r.body;\n this.buttons = r.buttons ?? [{ label: 'Close', variant: 'primary' }];\n this.open = true; this.setAttribute('open', '');\n // a11y: focus the first interactive element inside the modal so keyboard\n // users land in the dialog rather than behind it. Light focus trap via\n // the keydown handler below catches Tab cycling.\n requestAnimationFrame(() => {\n const root = this.shadowRoot;\n if (!root) return;\n const first = root.querySelector('input, select, textarea, button:not(.close)');\n first?.focus();\n });\n };\n\n override updated(): void {\n if (!this.open) return;\n const root = this.shadowRoot;\n if (!root) return;\n // Trap Tab inside the modal while open.\n const trap = (e: KeyboardEvent): void => {\n if (e.key !== 'Tab') return;\n const focusables = Array.from(\n root.querySelectorAll('input, select, textarea, button, [href]'),\n ).filter((el) => !el.hasAttribute('disabled'));\n if (focusables.length === 0) return;\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = (root.activeElement as HTMLElement | null) ?? null;\n if (e.shiftKey && active === first) { e.preventDefault(); last.focus(); }\n else if (!e.shiftKey && active === last) { e.preventDefault(); first.focus(); }\n };\n root.removeEventListener('keydown', trap as EventListener);\n root.addEventListener('keydown', trap as EventListener);\n }\n\n private onKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape' && this.open) this.close();\n };\n\n private close(): void { this.open = false; this.removeAttribute('open'); }\n private clickBtn(b: ModalButton): void { b.onClick?.(); this.close(); }\n\n override render() {\n return html`\n \n
\n
${this.mTitle}
\n
this.close()}>× \n
\n
\n
\n ${this.buttons.map((b) => html`\n this.clickBtn(b)}>${b.label} \n `)}\n
\n
\n `;\n }\n}\n\nexport function openModal(req: ModalReq): void {\n window.dispatchEvent(new CustomEvent('nv-modal', { detail: req }));\n}\n","/* Toast notification — shown briefly via window.dispatchEvent('nv-toast', detail). */\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\n\n@customElement('nv-toast')\nexport class NvToast extends LitElement {\n @state() private visible = false;\n @state() private msg = '';\n @state() private icon = '✓';\n private timer: number | null = null;\n\n static styles = css`\n :host {\n position: fixed; bottom: 24px; left: 50%;\n transform: translateX(-50%) translateY(80px);\n background: var(--bg-2);\n border: 1px solid var(--line-2);\n border-radius: var(--radius);\n padding: 10px 14px;\n font-size: 12.5px;\n box-shadow: var(--shadow);\n z-index: 100;\n opacity: 0; pointer-events: none;\n transition: opacity 0.2s, transform 0.2s;\n display: flex; align-items: center; gap: 8px;\n }\n :host([visible]) {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n pointer-events: auto;\n }\n .icon { color: var(--accent); }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n window.addEventListener('nv-toast', this.onToast as EventListener);\n }\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('nv-toast', this.onToast as EventListener);\n }\n\n private onToast = (e: Event): void => {\n const detail = (e as CustomEvent).detail as { msg?: string; icon?: string };\n this.msg = detail.msg ?? 'Done';\n this.icon = detail.icon ?? '✓';\n this.visible = true;\n this.setAttribute('visible', '');\n if (this.timer !== null) window.clearTimeout(this.timer);\n this.timer = window.setTimeout(() => {\n this.visible = false;\n this.removeAttribute('visible');\n }, 1800);\n };\n\n override render() {\n return html`${this.icon} ${this.msg} `;\n }\n}\n\nexport function toast(msg: string, icon = '✓'): void {\n window.dispatchEvent(new CustomEvent('nv-toast', { detail: { msg, icon } }));\n}\n","/* Topbar — breadcrumbs, transport pill, FPS pill, seed pill, controls. */\nimport { LitElement, html, css } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport {\n fps, transportLabel, seed, theme, sceneName,\n running, getClient, pushLog,\n} from '../store/appStore';\nimport { openModal } from './nv-modal';\nimport { toast } from './nv-toast';\n\n@customElement('nv-topbar')\nexport class NvTopbar extends LitElement {\n static styles = css`\n :host {\n display: flex; align-items: center;\n padding: 0 16px; gap: 12px;\n background: var(--bg-1);\n border-bottom: 1px solid var(--line);\n z-index: 10;\n }\n .crumbs { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--ink-3); }\n .crumbs .sep { color: var(--ink-4); }\n .crumbs .cur { color: var(--ink); font-weight: 500; }\n .spacer { flex: 1; }\n .pill {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 5px 10px;\n background: var(--bg-2); border: 1px solid var(--line);\n border-radius: 999px;\n font-size: 12px; color: var(--ink-2);\n font-family: var(--mono); font-weight: 500;\n }\n .pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 6px var(--ok); animation: pulse 2s infinite; }\n .pill.wasm .dot { background: var(--accent-2); box-shadow: 0 0 6px var(--accent-2); }\n .pill.seed { color: var(--ink-3); cursor: pointer; }\n .pill.seed:hover { border-color: var(--line-2); }\n .pill.seed b { color: var(--accent); font-weight: 600; }\n .pill.wasm { cursor: pointer; }\n .pill.wasm:hover { border-color: var(--line-2); }\n button {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 12px;\n background: var(--bg-2); border: 1px solid var(--line);\n border-radius: 8px;\n font-size: 12.5px; font-weight: 500; color: var(--ink);\n cursor: pointer;\n transition: all 0.15s;\n }\n button:hover { border-color: var(--line-2); background: var(--bg-3); }\n button.primary { background: var(--accent); border-color: var(--accent); color: #1a0f00; }\n button.primary:hover { filter: brightness(1.08); }\n button.ghost { background: transparent; }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n effect(() => { fps.value; transportLabel.value; seed.value; theme.value; sceneName.value; running.value; this.requestUpdate(); });\n }\n\n private async toggleRun(): Promise {\n const c = getClient(); if (!c) return;\n if (running.value) { await c.pause(); running.value = false; }\n else { await c.run(); running.value = true; }\n }\n private async reset(): Promise {\n const c = getClient(); if (!c) return;\n await c.reset();\n }\n private toggleTheme(): void {\n theme.value = theme.value === 'dark' ? 'light' : 'dark';\n }\n private async openSeedModal(): Promise {\n const cur = `0x${seed.value.toString(16).toUpperCase().padStart(8, '0')}`;\n openModal({\n title: 'Set seed',\n body: `Set the 32-bit hex seed for the shot-noise PRNG. Same (scene, config, seed) → byte-identical witness.
\n Hex seed \n `,\n buttons: [\n { label: 'Cancel', variant: 'ghost' },\n { label: 'Apply', variant: 'primary', onClick: async () => {\n const inp = document.querySelector('nv-modal')?.shadowRoot?.querySelector('#seed-input');\n if (!inp) return;\n const raw = inp.value.trim().replace(/^0x/i, '');\n const v = BigInt('0x' + raw);\n seed.value = v;\n await getClient()?.setSeed(v);\n pushLog('ok', `seed → 0x${v.toString(16).toUpperCase()}`);\n toast(`Seed → 0x${v.toString(16).toUpperCase().slice(0, 8)}`, '⟳');\n } },\n ],\n });\n }\n private openTransportSettings(): void {\n window.dispatchEvent(new CustomEvent('open-settings'));\n }\n\n override render() {\n const seedHex = seed.value.toString(16).toUpperCase().padStart(8, '0');\n return html`\n \n RuView / \n nvsim / \n ${sceneName.value} \n
\n
\n \n \n ${fps.value > 0 ? (fps.value / 1000).toFixed(2) + ' kHz' : 'idle'} \n \n \n ${transportLabel.value}\n \n \n seed: 0x${seedHex} \n \n window.dispatchEvent(new CustomEvent('nv-show-tour'))}>\n ★ Tour\n \n window.dispatchEvent(new CustomEvent('nv-show-help'))}>\n ?\n \n \n ${theme.value === 'dark' ? '☼' : '☾'}\n \n ↺ Reset \n \n ${running.value ? '❚❚ Pause' : '▶ Run'}\n \n `;\n }\n}\n","/* Sidebar — Scene panel, NV sensor panel, Tunables, Pipeline diagram. */\nimport { LitElement, html, css } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport { fs, fmod, dtMs, noiseEnabled, running, getClient, pushLog } from '../store/appStore';\n\nlet configPushTimer: number | null = null;\nfunction pushConfigDebounced(): void {\n if (configPushTimer !== null) window.clearTimeout(configPushTimer);\n configPushTimer = window.setTimeout(async () => {\n const c = getClient();\n if (!c) return;\n try {\n await c.setConfig({\n digitiser: { f_s_hz: fs.value, f_mod_hz: fmod.value },\n sensor: {\n gamma_fwhm_hz: 1.0e6,\n t1_s: 5.0e-3,\n t2_s: 1.0e-6,\n t2_star_s: 200e-9,\n contrast: 0.03,\n n_spins: 1.0e12,\n shot_noise_disabled: !noiseEnabled.value,\n },\n dt_s: dtMs.value * 1e-3,\n });\n pushLog('dbg', `config pushed · fs=${fs.value} f_mod=${fmod.value} dt=${dtMs.value.toFixed(1)}ms noise=${noiseEnabled.value ? 'on' : 'off'}`);\n } catch (e) {\n pushLog('warn', `config push failed: ${(e as Error).message}`);\n }\n }, 300);\n}\n\n@customElement('nv-sidebar')\nexport class NvSidebar extends LitElement {\n static styles = css`\n :host {\n display: flex; flex-direction: column; gap: 14px;\n padding: 14px; overflow-y: auto;\n background: var(--bg-1); border-right: 1px solid var(--line);\n }\n .panel {\n background: var(--bg-2); border: 1px solid var(--line);\n border-radius: var(--radius); padding: 12px;\n }\n .panel-h {\n display: flex; align-items: center; justify-content: space-between;\n font-size: 11px; font-weight: 600; color: var(--ink-3);\n text-transform: uppercase; letter-spacing: 0.08em;\n margin-bottom: 6px;\n }\n .panel-help {\n font-size: 11.5px; color: var(--ink-3);\n margin: 0 0 10px;\n line-height: 1.5;\n }\n .help-link {\n color: var(--accent-2);\n cursor: pointer;\n text-decoration: underline dotted;\n }\n .help-link:hover { color: var(--accent); }\n .count {\n background: var(--bg-3); color: var(--ink-2);\n padding: 1px 6px; border-radius: 999px;\n font-family: var(--mono); font-size: 10px;\n text-transform: none; letter-spacing: 0;\n }\n .scene-item {\n display: flex; align-items: center; gap: 10px;\n padding: 8px 10px;\n border-radius: var(--radius-sm);\n cursor: pointer;\n transition: background 0.15s;\n border: 1px solid transparent;\n }\n .scene-item:hover { background: var(--bg-3); }\n .scene-item .swatch { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }\n .scene-item .name { font-size: 13px; flex: 1; }\n .scene-item .meta { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }\n .field-row {\n display: flex; align-items: center; justify-content: space-between;\n padding: 6px 0; font-size: 12.5px;\n border-bottom: 1px solid var(--line);\n }\n .field-row:last-child { border-bottom: 0; }\n .field-row .lbl { color: var(--ink-3); }\n .field-row .val { font-family: var(--mono); color: var(--ink); font-size: 12px; }\n .slider-row { padding: 8px 0; border-bottom: 1px solid var(--line); }\n .slider-row:last-child { border-bottom: 0; padding-bottom: 0; }\n .slider-row .top { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 12px; }\n .slider-row .top .lbl { color: var(--ink-3); }\n .slider-row .top .val { font-family: var(--mono); color: var(--ink); }\n input[type=\"range\"] {\n -webkit-appearance: none; appearance: none;\n width: 100%; height: 4px;\n background: var(--bg-3); border-radius: 2px; outline: none;\n }\n input[type=\"range\"]::-webkit-slider-thumb {\n -webkit-appearance: none; appearance: none;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--accent); cursor: pointer;\n border: 2px solid var(--bg-2);\n box-shadow: 0 0 0 1px var(--line-2);\n }\n .pipeline { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; margin-top: 6px; }\n .stage {\n flex: 1; min-width: 50px;\n padding: 4px 6px;\n background: var(--bg-3); border: 1px solid var(--line);\n border-radius: 6px; font-size: 9.5px; text-align: center;\n color: var(--ink-2); font-family: var(--mono);\n }\n .stage.live { border-color: var(--accent-2); color: var(--accent-2); }\n .stage-arrow { color: var(--ink-4); font-size: 10px; }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n effect(() => { fs.value; fmod.value; dtMs.value; noiseEnabled.value; running.value; this.requestUpdate(); });\n }\n\n override render() {\n return html`\n \n
Scene 4 sources
\n
\n Magnetic primitives in the simulated environment. Drag any in the\n canvas to reposition; positions persist across reloads.\n
\n
\n \n rebar.steel.coil \n χ=5000 \n
\n
\n \n heart_proxy \n 1e-6 A·m² \n
\n
\n \n mains_60Hz \n 2 A · 60 Hz \n
\n
\n \n door.steel \n eddy \n
\n
\n\n \n
NV sensor COTS
\n
\n Element Six DNV-B1 reference: 1 mm³ diamond, ~10¹² NV centers.\n Floor δB ≈ 1.18 pT/√Hz per Barry 2020 §III.A.\n window.dispatchEvent(new CustomEvent('nv-show-help', { detail: { section: 'glossary' } }))}>What's NV? \n
\n
V 1 mm³
\n
N 1e12 NV
\n
C 0.030
\n
T₂* 200 ns
\n
δB 1.18 pT/√Hz
\n
\n\n \n
Tunables
\n
\n Live pipeline parameters. Edits debounce 300 ms then rebuild the\n WASM pipeline without restarting the frame stream.\n
\n
\n
Sample rate ${(fs.value / 1000).toFixed(1)} kHz
\n
{ fs.value = +(e.target as HTMLInputElement).value; pushConfigDebounced(); }} />\n
\n
\n
Lockin f_mod ${(fmod.value / 1000).toFixed(3)} kHz
\n
{ fmod.value = +(e.target as HTMLInputElement).value; pushConfigDebounced(); }} />\n
\n
\n
Integration t ${dtMs.value.toFixed(1)} ms
\n
{ dtMs.value = +(e.target as HTMLInputElement).value; pushConfigDebounced(); }} />\n
\n
\n
Shot noise ${noiseEnabled.value ? 'ON' : 'OFF'}
\n
{ noiseEnabled.value = (e.target as HTMLInputElement).value === '1'; pushConfigDebounced(); }} />\n
\n
\n\n \n
Pipeline
\n
\n Forward simulator stages, left to right. Stages glow cyan while\n the pipeline is running.\n
\n
\n scene \n → \n B-S \n → \n prop \n → \n NV \n → \n ADC \n → \n frame \n
\n
\n `;\n }\n}\n","/* Scene canvas — SVG with draggable sources, NV crystal sensor, field lines, mini ODMR. */\nimport { LitElement, html, css, svg } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport { lastB, bMag, fps, snr, motionReduced, running, getClient, speed, pushLog, lastFrame, scenePositions } from '../store/appStore';\n\ninterface SceneItem { id: string; x: number; y: number; color: string; name: string; }\n\n@customElement('nv-scene')\nexport class NvScene extends LitElement {\n @state() private zoom = 1.0;\n @state() private layerVisible = { source: true, field: true, label: true };\n @state() private items: SceneItem[] = [\n { id: 'rebar', x: 740, y: 240, color: 'oklch(0.72 0.18 330)', name: 'rebar.steel' },\n { id: 'heart', x: 220, y: 180, color: 'oklch(0.78 0.14 195)', name: 'heart_proxy' },\n { id: 'mains', x: 180, y: 380, color: 'oklch(0.72 0.18 330)', name: 'mains_60Hz' },\n { id: 'door', x: 800, y: 470, color: 'oklch(0.78 0.14 145)', name: 'door.steel' },\n ];\n @state() private dragging: string | null = null;\n @state() private selected: string | null = null;\n private dragOffset = { dx: 0, dy: 0 };\n\n static styles = css`\n :host {\n display: block; height: 100%; width: 100%;\n background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);\n position: relative; overflow: hidden;\n border-bottom: 1px solid var(--line);\n }\n .grid {\n position: absolute; inset: 0;\n background-image:\n linear-gradient(var(--grid) 1px, transparent 1px),\n linear-gradient(90deg, var(--grid) 1px, transparent 1px);\n background-size: 32px 32px;\n pointer-events: none;\n mask-image: radial-gradient(ellipse at center, black 40%, transparent 100%);\n }\n svg { position: absolute; inset: 0; width: 100%; height: 100%; }\n .stat-card {\n background: rgba(13,17,23,0.7);\n backdrop-filter: blur(8px);\n border: 1px solid var(--line);\n border-radius: var(--radius-sm);\n padding: 8px 12px;\n font-size: 11px;\n min-width: 96px;\n }\n [data-theme=\"light\"] .stat-card { background: rgba(255,255,255,0.85); }\n .stat-card .lbl {\n color: var(--ink-3);\n text-transform: uppercase; font-weight: 600; letter-spacing: 0.06em; font-size: 9.5px;\n }\n .stat-card .val { font-family: var(--mono); font-size: 16px; font-weight: 600; margin-top: 2px; }\n .stat-card .val.amber { color: var(--accent); }\n .stat-card .val.cyan { color: var(--accent-2); }\n .stat-card .val.mint { color: var(--accent-4); }\n .scene-readout {\n position: absolute; top: 14px; right: 14px;\n display: flex; gap: 8px; z-index: 5;\n }\n .draggable { cursor: grab; transition: filter 0.15s; }\n .draggable:hover { filter: brightness(1.15) drop-shadow(0 0 6px currentColor); }\n .draggable.dragging { cursor: grabbing; filter: brightness(1.25) drop-shadow(0 0 10px currentColor); }\n .field-line { stroke-dasharray: 4 6; }\n @keyframes dash { to { stroke-dashoffset: -200; } }\n .field-line.anim { animation: dash 4s linear infinite; }\n @keyframes spin {\n 0% { transform: rotateY(0) rotateX(8deg); }\n 100% { transform: rotateY(360deg) rotateX(8deg); }\n }\n .crystal { transform-origin: center; transform-box: fill-box; }\n .crystal.anim { animation: spin 12s linear infinite; }\n .label {\n font-family: var(--mono); font-size: 11px; fill: var(--ink-2);\n pointer-events: none;\n }\n .scene-toolbar {\n position: absolute; top: 14px; left: 14px;\n display: flex; gap: 6px; z-index: 5;\n background: rgba(13,17,23,0.85);\n backdrop-filter: blur(8px);\n border: 1px solid var(--line);\n border-radius: 8px;\n padding: 4px;\n }\n [data-theme=\"light\"] .scene-toolbar { background: rgba(255,255,255,0.85); }\n .scene-toolbar button {\n width: 28px; height: 28px;\n background: transparent;\n border: 1px solid transparent;\n border-radius: 6px;\n color: var(--ink-2);\n cursor: pointer;\n display: grid; place-items: center;\n font-size: 13px;\n }\n .scene-toolbar button:hover { color: var(--ink); background: var(--bg-2); }\n .scene-toolbar button.on { background: var(--bg-3); color: var(--accent); border-color: var(--line-2); }\n\n .sim-controls {\n position: absolute; bottom: 14px; right: 14px;\n display: flex; gap: 6px; align-items: center;\n background: rgba(13,17,23,0.85);\n backdrop-filter: blur(12px);\n border: 1px solid var(--line-2);\n border-radius: 999px;\n padding: 6px 10px;\n z-index: 5;\n }\n [data-theme=\"light\"] .sim-controls { background: rgba(255,255,255,0.92); }\n .sim-controls .play {\n width: 32px; height: 32px;\n background: var(--accent);\n border: none;\n border-radius: 50%;\n color: #1a0f00;\n cursor: pointer;\n display: grid; place-items: center;\n font-size: 13px;\n }\n .sim-controls .play:hover { filter: brightness(1.08); }\n .sim-controls .step {\n width: 26px; height: 26px;\n border-radius: 6px;\n background: transparent;\n color: var(--ink-2);\n border: 1px solid var(--line);\n cursor: pointer;\n font-size: 11px;\n }\n .sim-controls .step:hover { color: var(--ink); border-color: var(--line-2); }\n .sim-controls .speed {\n font-family: var(--mono); font-size: 11px;\n color: var(--ink-2);\n padding: 0 6px;\n min-width: 36px;\n text-align: center;\n cursor: pointer;\n }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Restore drag positions if any are persisted.\n if (scenePositions.value.length > 0) {\n this.items = this.items.map((it) => {\n const saved = scenePositions.value.find((p) => p.id === it.id);\n return saved ? { ...it, x: saved.x, y: saved.y } : it;\n });\n }\n effect(() => {\n lastB.value; bMag.value; fps.value; snr.value; motionReduced.value;\n running.value; speed.value; lastFrame.value;\n this.requestUpdate();\n });\n // Compute SNR from the last frame: |B_pT| / max(σ_pT[k]) per ADR-093 P1.4.\n effect(() => {\n const f = lastFrame.value;\n if (!f) return;\n const bmag = Math.sqrt(f.bPt[0] ** 2 + f.bPt[1] ** 2 + f.bPt[2] ** 2);\n const sigmaMax = Math.max(Math.abs(f.sigmaPt[0]), Math.abs(f.sigmaPt[1]), Math.abs(f.sigmaPt[2]), 0.001);\n const snrVal = bmag / sigmaMax;\n if (Number.isFinite(snrVal)) snr.value = snrVal;\n });\n window.addEventListener('pointermove', this.onPointerMove);\n window.addEventListener('pointerup', this.onPointerUp);\n }\n\n private async toggleRun(): Promise {\n const c = getClient(); if (!c) return;\n if (running.value) { await c.pause(); running.value = false; }\n else { await c.run(); running.value = true; }\n }\n private async stepFwd(): Promise {\n const c = getClient(); if (!c) return;\n await c.step('fwd', 10);\n pushLog('dbg', 'sim step → +1 frame');\n }\n private async stepBack(): Promise {\n const c = getClient(); if (!c) return;\n await c.step('back', 10);\n pushLog('dbg', 'sim step ← -1 frame');\n }\n private cycleSpeed(): void {\n const speeds = [0.25, 0.5, 1.0, 2.0, 4.0];\n const idx = speeds.indexOf(speed.value);\n speed.value = speeds[(idx + 1) % speeds.length];\n }\n private zoomIn(): void { this.zoom = Math.min(2.5, this.zoom * 1.2); }\n private zoomOut(): void { this.zoom = Math.max(0.5, this.zoom / 1.2); }\n private fitView(): void { this.zoom = 1.0; }\n private toggleLayer(k: 'source' | 'field' | 'label'): void {\n this.layerVisible = { ...this.layerVisible, [k]: !this.layerVisible[k] };\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('pointermove', this.onPointerMove);\n window.removeEventListener('pointerup', this.onPointerUp);\n }\n\n private onDown = (id: string, e: PointerEvent): void => {\n e.preventDefault();\n this.dragging = id;\n this.selected = id;\n const item = this.items.find((i) => i.id === id);\n if (!item) return;\n const svgEl = this.renderRoot.querySelector('svg') as SVGSVGElement | null;\n if (!svgEl) return;\n const pt = this.toSvg(e, svgEl);\n this.dragOffset = { dx: pt.x - item.x, dy: pt.y - item.y };\n };\n\n private onPointerMove = (e: PointerEvent): void => {\n if (!this.dragging) return;\n const svgEl = this.renderRoot.querySelector('svg') as SVGSVGElement | null;\n if (!svgEl) return;\n const pt = this.toSvg(e, svgEl);\n this.items = this.items.map((it) =>\n it.id === this.dragging\n ? { ...it, x: pt.x - this.dragOffset.dx, y: pt.y - this.dragOffset.dy }\n : it,\n );\n };\n\n private onPointerUp = (): void => {\n if (this.dragging) {\n // Persist all positions on drop.\n scenePositions.value = this.items.map(({ id, x, y }) => ({ id, x, y }));\n }\n this.dragging = null;\n };\n\n private toSvg(e: PointerEvent, svgEl: SVGSVGElement): { x: number; y: number } {\n const r = svgEl.getBoundingClientRect();\n const vbX = ((e.clientX - r.left) / r.width) * 1000;\n const vbY = ((e.clientY - r.top) / r.height) * 600;\n return { x: vbX, y: vbY };\n }\n\n override render() {\n const b = lastB.value;\n const bnT = [b[0] * 1e9, b[1] * 1e9, b[2] * 1e9];\n const bMagNT = bMag.value * 1e9;\n const animClass = motionReduced.value ? '' : 'anim';\n\n const vbW = 1000 / this.zoom;\n const vbH = 600 / this.zoom;\n const vbX = (1000 - vbW) / 2;\n const vbY = (600 - vbH) / 2;\n\n return html`\n
\n \n \n \n \n \n \n \n \n\n \n ${this.layerVisible.field ? this.items.map((it) => svg`\n \n `) : ''}\n\n \n ${this.layerVisible.source ? this.items.map((it) => svg`\n this.onDown(it.id, e)}>\n \n \n ${this.layerVisible.label ? svg`${it.name} ` : ''}\n \n `) : ''}\n\n \n \n \n \n \n \n \n \n sensor · 〈111〉 NV\n \n \n B_in: [${bnT[0].toFixed(2)}, ${bnT[1].toFixed(2)}, ${bnT[2].toFixed(2)}] nT \n \n \n \n\n \n + \n − \n ⊡ \n this.toggleLayer('source')}>● \n this.toggleLayer('field')}>≈ \n this.toggleLayer('label')}>T \n
\n\n \n ⏮ \n \n ${running.value ? '❚❚' : '▶'}\n \n ⏭ \n ${speed.value}× \n
\n\n \n
\n
|B|
\n
${bMagNT.toFixed(3)} nT
\n
\n
\n
FPS
\n
${fps.value > 0 ? Math.round(fps.value) : '—'}
\n
\n
\n
SNR
\n
${snr.value > 0 ? snr.value.toFixed(1) : '—'}
\n
\n
\n `;\n }\n}\n","/* Inspector — tabbed: Signal / Frame / Witness. */\nimport { LitElement, html, css, svg, type PropertyValues } from 'lit';\nimport { customElement, state, property } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport {\n traceX, traceY, traceZ, stripBars, lastFrame,\n witnessHex, expectedWitness, witnessVerified, getClient,\n pushLog, lastB, bMag,\n} from '../store/appStore';\n\ntype Tab = 'signal' | 'frame' | 'witness';\n\n@customElement('nv-inspector')\nexport class NvInspector extends LitElement {\n @state() private tab: Tab = 'signal';\n /** When set by the parent, force the tab and pulse-highlight it. */\n @property({ attribute: false }) pinTab: Tab | null = null;\n /** When `expanded`, the inspector renders as a full-screen view with bigger\n * charts and a wider Witness panel. Used when the rail Inspector/Witness\n * button is clicked — see ADR-093 P1.13. */\n @property({ type: Boolean, reflect: true }) expanded = false;\n\n static styles = css`\n :host {\n display: flex; flex-direction: column;\n background: var(--bg-1);\n border-left: 1px solid var(--line);\n overflow: hidden;\n height: 100%;\n }\n :host([expanded]) {\n border-left: 0;\n background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);\n }\n :host([expanded]) .tabs {\n padding: 0 24px;\n background: var(--bg-1);\n }\n :host([expanded]) .tab {\n padding: 16px 22px;\n font-size: 13.5px;\n flex: 0 0 auto;\n }\n :host([expanded]) .body {\n padding: 24px 28px;\n max-width: 1400px;\n width: 100%;\n margin: 0 auto;\n }\n :host([expanded]) .card { padding: 18px 20px; }\n :host([expanded]) .card-h .ttl { font-size: 14px; }\n :host([expanded]) svg { height: 220px; }\n :host([expanded]) .frame-strip { height: 48px; }\n :host([expanded]) table { font-size: 12.5px; }\n :host([expanded]) td { padding: 6px 0; }\n :host([expanded]) .hex { font-size: 12px; padding: 14px; line-height: 1.7; }\n :host([expanded]) .witness-box { font-size: 13px; padding: 14px 16px; line-height: 1.6; }\n :host([expanded]) .verify-btn { padding: 12px; font-size: 13px; }\n :host([expanded]) .grid-2 {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n }\n :host([expanded]) .grid-2 > .card { margin-bottom: 0; }\n @media (max-width: 1024px) {\n :host([expanded]) .grid-2 { grid-template-columns: 1fr; }\n }\n .tabs {\n display: flex; border-bottom: 1px solid var(--line);\n }\n .tab {\n flex: 1;\n padding: 11px 8px;\n background: transparent; border: none;\n font-size: 11.5px; font-weight: 500;\n color: var(--ink-3);\n border-bottom: 2px solid transparent;\n cursor: pointer; transition: color 0.15s, border-color 0.15s;\n }\n .tab.active { color: var(--ink); border-bottom-color: var(--accent); }\n .tab:hover { color: var(--ink-2); }\n .body { padding: 14px; flex: 1; overflow-y: auto; }\n\n .card {\n background: var(--bg-2); border: 1px solid var(--line);\n border-radius: var(--radius); padding: 12px;\n margin-bottom: 12px;\n }\n .card-h {\n display: flex; justify-content: space-between; align-items: center;\n margin-bottom: 8px;\n }\n .card-h .ttl { font-size: 12px; font-weight: 600; }\n .badge {\n font-family: var(--mono); font-size: 10px;\n padding: 2px 6px;\n background: oklch(0.78 0.14 195 / 0.12);\n color: var(--accent-2);\n border-radius: 4px;\n border: 1px solid oklch(0.78 0.14 195 / 0.3);\n }\n svg { width: 100%; height: 130px; }\n .frame-strip {\n height: 28px;\n display: flex; align-items: flex-end; gap: 1px;\n padding: 4px 0;\n }\n .bar {\n flex: 1;\n background: linear-gradient(to top, var(--accent-2), var(--accent));\n border-radius: 1px;\n min-height: 2px;\n }\n table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 10.5px; }\n td { padding: 4px 0; border-bottom: 1px solid var(--line); }\n td:first-child { color: var(--ink-3); }\n td:last-child { text-align: right; color: var(--ink); }\n .hex {\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: var(--radius-sm);\n padding: 10px;\n font-family: var(--mono);\n font-size: 10.5px;\n color: var(--ink-2);\n line-height: 1.6;\n overflow-x: auto;\n white-space: nowrap;\n }\n .hex .magic { color: var(--accent); font-weight: 600; }\n .witness-box {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--ink-2);\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: 6px;\n padding: 8px 10px;\n word-break: break-all;\n line-height: 1.5;\n }\n .verify-btn {\n margin-top: 10px;\n width: 100%;\n padding: 8px;\n border: 1px solid var(--line);\n background: var(--bg-3);\n color: var(--ink);\n border-radius: 8px;\n cursor: pointer;\n font-family: var(--mono);\n font-size: 12px;\n }\n .verify-btn:hover { border-color: var(--accent); }\n .verify-btn.ok { border-color: var(--ok); color: var(--ok); }\n .verify-btn.fail { border-color: var(--bad); color: var(--bad); }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n effect(() => {\n traceX.value; traceY.value; traceZ.value; stripBars.value;\n lastFrame.value; witnessHex.value; witnessVerified.value;\n lastB.value; bMag.value;\n this.requestUpdate();\n });\n }\n\n override willUpdate(changed: PropertyValues): void {\n // Apply parent-driven tab pin during willUpdate so the new tab value\n // participates in this same render pass — avoids the \"update after\n // update completed\" Lit warning that would fire if we did this in\n // updated().\n if (changed.has('pinTab') && this.pinTab && this.tab !== this.pinTab) {\n this.tab = this.pinTab;\n }\n }\n\n private async verify(): Promise {\n const c = getClient(); if (!c) return;\n witnessVerified.value = 'pending';\n pushLog('info', 'verifying witness over 256 frames…');\n try {\n const exp = expectedWitness.value;\n const expBytes = new Uint8Array(32);\n for (let i = 0; i < 32; i++) expBytes[i] = parseInt(exp.slice(i * 2, i * 2 + 2), 16);\n const r = await c.verifyWitness(expBytes);\n if (r.ok) {\n witnessVerified.value = 'ok';\n witnessHex.value = exp;\n pushLog('ok', `witness ${exp.slice(0, 16)}… matches · determinism gate ✓`);\n } else {\n witnessVerified.value = 'fail';\n const actual = Array.from(r.actual).map((b) => b.toString(16).padStart(2, '0')).join('');\n witnessHex.value = actual;\n pushLog('err', `WITNESS MISMATCH actual=${actual.slice(0, 16)}…`);\n }\n } catch (e) {\n witnessVerified.value = 'fail';\n pushLog('err', `verify failed: ${(e as Error).message}`);\n }\n }\n\n private renderHeader() {\n if (!this.expanded) return '';\n const titles: Record = {\n signal: 'Signal inspector — live B-vector trace + frame stream',\n frame: 'Frame inspector — MagFrame v1 fields + raw bytes',\n witness: 'Witness panel — SHA-256 determinism gate',\n };\n return html`\n \n ${titles[this.tab]}\n \n \n ${this.tab === 'signal'\n ? '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.'\n : this.tab === 'frame'\n ? 'Decoded view of the most recent MagFrame: typed fields plus the raw 60-byte little-endian binary record (magic 0xC51A_6E70).'\n : '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.'}\n
\n `;\n }\n\n private renderSignalTab() {\n const W = 320, H = 130, cy = 65, scale = 22;\n const cap = 200;\n const make = (arr: number[]) => {\n let p = '';\n arr.forEach((v, i) => {\n const x = (i / Math.max(1, cap - 1)) * W;\n const y = cy - v * scale;\n p += (i === 0 ? 'M' : 'L') + ` ${x.toFixed(1)} ${y.toFixed(1)} `;\n });\n return p;\n };\n\n const b = lastB.value;\n const bnT = [b[0] * 1e9, b[1] * 1e9, b[2] * 1e9];\n const hasData = traceX.value.length > 0;\n\n return html`\n ${!hasData ? html`\n \n
\n No frames yet. Press ▶ Run in the topbar (or hit Space)\n to start the live B-vector trace.\n
\n
\n ` : ''}\n \n
\n
\n B-vector trace \n 3-axis · nT \n
\n
\n \n ${svg``}\n ${svg``}\n ${svg``}\n \n ${this.expanded ? html`
\n x: ${bnT[0].toFixed(3)} nT \n y: ${bnT[1].toFixed(3)} nT \n z: ${bnT[2].toFixed(3)} nT \n |B| ${(bMag.value * 1e9).toFixed(3)} nT \n
` : ''}\n
\n\n
\n
\n Frame stream \n live \n
\n
\n ${stripBars.value.map((v) => html`
`)}\n
\n ${this.expanded ? html`\n
\n frames in window: ${stripBars.value.length} \n noise floor: ${lastFrame.value ? lastFrame.value.noiseFloorPtSqrtHz.toFixed(2) + ' pT/√Hz' : '—'} \n
` : ''}\n
\n
\n `;\n }\n\n private renderFrameTab() {\n const f = lastFrame.value;\n const bytes = f?.raw;\n let hex = '';\n if (bytes) {\n const arr = Array.from(bytes).map((b) => b.toString(16).padStart(2, '0'));\n hex = arr.slice(0, 60).join(' ');\n }\n return html`\n ${!f ? html`\n \n
\n No MagFrame to display yet. Start the pipeline (▶ Run ) to populate.\n
\n
\n ` : ''}\n \n
\n
\n MagFrame v1 fields \n 60 B \n
\n
\n magic ${f ? '0x' + f.magic.toString(16).toUpperCase() : '—'} \n version ${f?.version ?? '—'} \n flags 0x${(f?.flags ?? 0).toString(16).padStart(4, '0')} \n sensor_id ${f?.sensorId ?? '—'} \n t_us ${f ? f.tUs.toString() : '—'} \n b_pT[0] ${f ? f.bPt[0].toFixed(1) : '—'} \n b_pT[1] ${f ? f.bPt[1].toFixed(1) : '—'} \n b_pT[2] ${f ? f.bPt[2].toFixed(1) : '—'} \n noise_floor ${f ? f.noiseFloorPtSqrtHz.toFixed(2) : '—'} \n temp_K ${f ? f.temperatureK.toFixed(1) : '—'} \n
\n
\n
\n
\n Hex dump \n LE \n
\n
${hex || '—'}
\n ${this.expanded ? html`\n
\n 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).\n
` : ''}\n
\n
\n `;\n }\n\n private renderWitnessTab() {\n const status = witnessVerified.value;\n const cls = status === 'ok' ? 'ok' : status === 'fail' ? 'fail' : '';\n const label =\n status === 'pending' ? 'Verifying…' :\n status === 'ok' ? '✓ Witness verified · determinism gate' :\n status === 'fail' ? '✗ Witness mismatch · audit required' :\n 'Verify witness';\n const match = expectedWitness.value && witnessHex.value && expectedWitness.value === witnessHex.value;\n return html`\n ${this.expanded ? html`\n \n
\n
Reference scene
\n
Proof::REFERENCE
\n
2 dipoles · 1 loop · 1 ferrous · 1 sensor
\n
\n
\n
Seed
\n
0x0000002A
\n
canonical Proof::SEED
\n
\n
\n
Sample count
\n
256
\n
Proof::N_SAMPLES
\n
\n
\n
Status
\n
\n ${status === 'ok' ? '✓ matches' : status === 'fail' ? '✗ drift' : status === 'pending' ? '… running' : '— idle'}\n
\n
${match ? 'byte-equivalent' : 'not yet verified'}
\n
\n
\n ` : ''}\n \n
\n Expected (Proof::EXPECTED_WITNESS_HEX) \n SHA-256 \n
\n
${expectedWitness.value || '(loading…)'}
\n
\n \n
\n Actual (last verify) \n SHA-256 \n
\n
${witnessHex.value || '(not verified yet)'}
\n
${label} \n
\n ${this.expanded ? html`\n \n
\n What this verifies \n ADR-089 §5 \n
\n
\n
Pressing Verify runs the canonical reference pipeline\n (Proof::generate) end-to-end inside this browser's WASM Worker:\n scene → Biot-Savart synthesis → material attenuation → NV ensemble → ADC + lock-in →\n concatenated MagFrame bytes → SHA-256.
\n
If the resulting hash matches the constant pinned at build time\n (cc8de9b01b0ff5bd…), every constant — γ_e, D_GS, μ₀, T₂*, contrast, the PRNG\n stream, the frame layout, the pipeline ordering — is byte-identical to the published\n reference. If it doesn't match, something drifted; the dashboard names which.
\n
This is the same regression test that runs in\n cargo test -p nvsim — running in your browser, against your own WASM build.
\n
\n
\n ` : ''}\n `;\n }\n\n override render() {\n return html`\n \n this.tab = 'signal'}>Signal \n this.tab = 'frame'}>Frame \n this.tab = 'witness'}>Witness \n
\n \n ${this.renderHeader()}\n ${this.tab === 'signal' ? this.renderSignalTab()\n : this.tab === 'frame' ? this.renderFrameTab()\n : this.renderWitnessTab()}\n
\n `;\n }\n}\n","/* Console — log stream + REPL. */\nimport { LitElement, html, css } from 'lit';\nimport { customElement, query } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport {\n consoleLines, consoleFilter, consolePaused, pushLog,\n getClient, seed, theme, expectedWitness, witnessHex, witnessVerified,\n running, replHistory, pushReplHistory,\n} from '../store/appStore';\n\n@customElement('nv-console')\nexport class NvConsole extends LitElement {\n @query('#console-input') private inputEl!: HTMLInputElement;\n private hIdx = -1;\n\n static styles = css`\n :host {\n display: flex; flex-direction: column;\n background: var(--bg-1);\n overflow: hidden;\n }\n .tabs {\n display: flex; align-items: center;\n border-bottom: 1px solid var(--line);\n padding: 0 10px;\n gap: 2px;\n }\n .tab {\n padding: 8px 12px;\n background: transparent; border: none;\n font-size: 11.5px; color: var(--ink-3);\n font-family: var(--mono);\n border-bottom: 2px solid transparent;\n cursor: pointer;\n margin-bottom: -1px;\n }\n .tab.active { color: var(--ink); border-bottom-color: var(--accent); }\n .tab .cnt {\n background: var(--bg-3); padding: 1px 5px; border-radius: 999px;\n font-size: 9.5px; color: var(--ink-2); margin-left: 4px;\n }\n .spacer { flex: 1; }\n .tools { display: flex; gap: 4px; padding: 4px 0; }\n .tools button {\n width: 24px; height: 24px;\n background: transparent; border: 1px solid var(--line);\n border-radius: 6px;\n color: var(--ink-3);\n font-size: 11px; cursor: pointer;\n }\n .tools button:hover { color: var(--ink); border-color: var(--line-2); }\n\n .body {\n flex: 1; overflow-y: auto;\n font-family: var(--mono);\n font-size: 11.5px;\n padding: 6px 0;\n background: var(--bg-0);\n }\n .line {\n display: grid;\n grid-template-columns: 70px 60px 1fr;\n gap: 12px;\n padding: 2px 12px;\n color: var(--ink-2);\n border-left: 2px solid transparent;\n }\n .line:hover { background: var(--bg-1); }\n .ts { color: var(--ink-4); font-size: 10.5px; padding-top: 1px; }\n .lvl {\n font-size: 10px; font-weight: 600;\n text-transform: uppercase; letter-spacing: 0.04em; padding-top: 1px;\n }\n .line.info .lvl { color: var(--accent-2); }\n .line.warn .lvl { color: var(--warn); }\n .line.warn { border-left-color: var(--warn); background: oklch(0.7 0.18 35 / 0.04); }\n .line.err .lvl { color: var(--bad); }\n .line.err { border-left-color: var(--bad); background: oklch(0.65 0.22 25 / 0.05); }\n .line.dbg .lvl { color: var(--ink-3); }\n .line.ok .lvl { color: var(--ok); }\n .msg { color: var(--ink); white-space: pre-wrap; word-break: break-word; }\n\n .input {\n display: flex; align-items: center;\n border-top: 1px solid var(--line);\n background: var(--bg-0);\n padding: 0 10px;\n height: 32px; gap: 8px;\n }\n .prompt { color: var(--accent); font-family: var(--mono); font-size: 12px; }\n input[type=\"text\"] {\n flex: 1; background: transparent; border: none; outline: none;\n color: var(--ink); font-family: var(--mono); font-size: 12px;\n height: 100%;\n }\n input::placeholder { color: var(--ink-4); }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n effect(() => {\n consoleLines.value; consoleFilter.value; consolePaused.value;\n this.requestUpdate();\n });\n }\n\n override updated(): void {\n const body = this.renderRoot.querySelector('.body') as HTMLElement | null;\n if (body) body.scrollTop = body.scrollHeight;\n }\n\n private counts(): Record {\n const c: Record = { info: 0, warn: 0, err: 0, dbg: 0, ok: 0 };\n for (const l of consoleLines.value) c[l.level] = (c[l.level] ?? 0) + 1;\n c.all = consoleLines.value.length;\n return c;\n }\n\n private async exec(line: string): Promise {\n line = line.trim();\n if (!line) return;\n pushLog('info', `nvsim> ${line}`);\n pushReplHistory(line);\n this.hIdx = replHistory.value.length;\n const [cmd, ...args] = line.split(/\\s+/);\n const arg = args.join(' ');\n const c = getClient();\n switch (cmd) {\n case 'help':\n pushLog('info', 'commands: help · scene.list · sensor.config · run · pause · reset · seed · proof.verify · proof.export · clear · theme · status');\n break;\n case 'scene.list':\n pushLog('info', 'scene rebar-walkby-01:');\n pushLog('info', ' rebar.steel.coil @ [+2.7, 0.0, +0.3] m χ=5000');\n pushLog('info', ' dipole.heart_proxy @ [-1.4, +0.2, +0.4] m m=1.0e-6 A·m²');\n pushLog('info', ' loop.mains_60Hz @ [-1.6, -0.4, 0.0] m I=2 A');\n pushLog('info', ' eddy.door_steel @ [+0.0, +1.8, +0.4] m σ=1e6 S/m');\n break;\n case 'sensor.config':\n pushLog('info', 'NvSensor::cots_defaults() {');\n pushLog('info', ' pos=[0,0,0], V=1mm³, N=1e12, C=0.03, T2*=200ns');\n pushLog('info', ' D=2.870 GHz, γe=28 GHz/T, Γ=1.0 MHz, axes=4×〈111〉');\n pushLog('info', ' δB ≈ 1.18 pT/√Hz (Barry 2020 §III.A) }');\n break;\n case 'run':\n if (c) { await c.run(); running.value = true; pushLog('ok', 'pipeline RUN'); }\n break;\n case 'pause':\n if (c) { await c.pause(); running.value = false; pushLog('warn', 'pipeline PAUSED'); }\n break;\n case 'reset':\n if (c) { await c.reset(); pushLog('info', 'pipeline reset · t=0'); }\n break;\n case 'seed': {\n if (!arg) { pushLog('info', `current seed = 0x${seed.value.toString(16).toUpperCase()}`); break; }\n const v = BigInt(arg.startsWith('0x') ? arg : '0x' + arg);\n seed.value = v;\n if (c) await c.setSeed(v);\n pushLog('ok', `seed → 0x${v.toString(16).toUpperCase()}`);\n break;\n }\n case 'proof.verify': {\n if (!c) break;\n pushLog('dbg', 'computing SHA-256 over 256 frames…');\n try {\n const exp = expectedWitness.value;\n const expBytes = new Uint8Array(32);\n for (let i = 0; i < 32; i++) expBytes[i] = parseInt(exp.slice(i * 2, i * 2 + 2), 16);\n const r = await c.verifyWitness(expBytes);\n if (r.ok) { witnessVerified.value = 'ok'; witnessHex.value = exp; pushLog('ok', `witness ${exp.slice(0, 16)}… matches · determinism gate ✓`); }\n else { witnessVerified.value = 'fail'; pushLog('err', 'WITNESS MISMATCH'); }\n } catch (e) { pushLog('err', `verify failed: ${(e as Error).message}`); }\n break;\n }\n case 'proof.export': {\n if (!c) break;\n pushLog('dbg', 'building proof bundle…');\n try {\n const blob = await c.exportProofBundle();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `nvsim-proof-${Date.now()}.json`;\n a.click();\n URL.revokeObjectURL(url);\n pushLog('ok', `proof bundle exported · ${blob.size} bytes`);\n } catch (e) { pushLog('err', `export failed: ${(e as Error).message}`); }\n break;\n }\n case 'clear':\n consoleLines.value = [];\n break;\n case 'theme': {\n const t = (arg || '').toLowerCase();\n if (t === 'light' || t === 'dark') { theme.value = t; pushLog('ok', `theme → ${t}`); }\n else pushLog('info', 'theme [light|dark]');\n break;\n }\n case 'status':\n pushLog('info', `running=${running.value} seed=0x${seed.value.toString(16).toUpperCase()} verified=${witnessVerified.value}`);\n break;\n default:\n pushLog('err', `unknown command: ${cmd} · try help`);\n }\n }\n\n private onKey = (e: KeyboardEvent): void => {\n if (e.key === 'Enter') { void this.exec(this.inputEl.value); this.inputEl.value = ''; }\n else if (e.key === 'ArrowUp') {\n const h = replHistory.value;\n if (h.length) {\n this.hIdx = Math.max(0, this.hIdx - 1);\n this.inputEl.value = h[this.hIdx] ?? '';\n e.preventDefault();\n }\n } else if (e.key === 'ArrowDown') {\n const h = replHistory.value;\n if (h.length) {\n this.hIdx = Math.min(h.length, this.hIdx + 1);\n this.inputEl.value = h[this.hIdx] ?? '';\n e.preventDefault();\n }\n }\n };\n\n override render() {\n const c = this.counts();\n const filter = consoleFilter.value;\n const visible = consoleLines.value.filter((l) => filter === 'all' || l.level === filter);\n return html`\n \n ${(['all', 'info', 'warn', 'err', 'dbg'] as const).map((k) => html`\n
consoleFilter.value = k}>\n ${k} ${c[k] ?? 0} \n \n `)}\n
\n
\n consoleLines.value = []}>× \n consolePaused.value = !consolePaused.value}>\n ${consolePaused.value ? '▶' : '❚❚'}\n \n
\n
\n \n ${visible.map((l) => {\n const ts = new Date(l.ts);\n const tsStr = `${String(ts.getSeconds()).padStart(2, '0')}.${String(ts.getMilliseconds()).padStart(3, '0')}`;\n // Use innerHTML pass-through via unsafe-html alt: inject raw html via property\n return html`
\n
${tsStr}
\n
${l.level}
\n
\n
`;\n })}\n
\n \n nvsim> \n \n
\n `;\n }\n}\n","/* RuView Edge App Store registry.\n *\n * Catalog of every WASM edge module shipping in the workspace plus the\n * `nvsim` simulator itself. Each entry maps to a hot-loadable algorithm\n * the dashboard can run in-browser (WASM transport) or push to a real\n * ESP32-S3 mesh (WS transport, deployed via WASM3 — ADR-040 Tier 3).\n *\n * Categories (ADR-041 event-ID ranges):\n * med 100–199 Medical & health\n * sec 200–299 Security & safety\n * bld 300–399 Smart building\n * ret 400–499 Retail & hospitality\n * ind 500–599 Industrial\n * sig 600–619 Signal-processing primitives\n * lrn 620–639 Online learning\n * spt 640–659 Spatial / graph\n * tmp 640–660 Temporal logic / planning\n * ais 700–719 AI safety\n * qnt 720–739 Quantum-flavoured signal\n * aut 740–759 Autonomy / mesh\n * exo 650–699 Exotic / research\n * sim — Pipeline simulators (nvsim)\n *\n * The `crate` field names the Cargo crate that owns the implementation.\n * `wasmEdge` apps are compiled out of `wifi-densepose-wasm-edge`;\n * `nvsim` apps come from `nvsim`. Future apps may target other crates.\n */\n\nexport type AppCategory =\n | 'sim'\n | 'med'\n | 'sec'\n | 'bld'\n | 'ret'\n | 'ind'\n | 'sig'\n | 'lrn'\n | 'spt'\n | 'tmp'\n | 'ais'\n | 'qnt'\n | 'aut'\n | 'exo';\n\n/** What actually happens when a card's toggle is on.\n * - `running` — the algorithm is genuinely running in the browser right now\n * (e.g. `nvsim` itself, which is the simulator the dashboard fronts).\n * - `simulated` — a pared-down version of the algorithm runs against nvsim's\n * live magnetic frame stream as a *proxy* for its native CSI input.\n * Emits real i32 event IDs into the console feed; output is illustrative,\n * not engineering-grade. Listed apps' Rust source is real, builds for\n * wasm32-unknown-unknown, and passes its native unit tests.\n * - `mesh-only` — algorithm needs CSI subcarrier data from a real ESP32-S3\n * mesh (or a future CSI simulator). Toggling persists the selection so\n * the WS transport can push activation when connected. */\nexport type AppRuntime = 'running' | 'simulated' | 'mesh-only';\n\nexport interface AppManifest {\n /** Stable kebab-case id; matches the wasm-edge module name (e.g. `med_sleep_apnea`). */\n id: string;\n /** Human-readable name. */\n name: string;\n /** Category short-code. */\n category: AppCategory;\n /** Cargo crate the implementation lives in. */\n crate: 'nvsim' | 'wifi-densepose-wasm-edge' | string;\n /** One-liner description. */\n summary: string;\n /** Optional longer markdown body. */\n body?: string;\n /** Numeric event IDs this app emits (i32 codes from `event_types` mod). */\n events?: number[];\n /** Compute budget tier the module advertises. S=<5ms, M=<15ms, L=<50ms. */\n budget?: 'S' | 'M' | 'L';\n /** Default activation state when listed. */\n active?: boolean;\n /** Tags for fuzzy search and filtering. */\n tags?: string[];\n /** \"Available\", \"Beta\", or \"Research\" maturity. */\n status: 'available' | 'beta' | 'research';\n /** ADR back-reference. */\n adr?: string;\n /** What actually happens when active — see AppRuntime docs. */\n runtime?: AppRuntime;\n}\n\nexport const APPS: AppManifest[] = [\n // ── Pipeline simulators ──────────────────────────────────────────────────\n {\n id: 'nvsim',\n name: 'nvsim — NV-diamond magnetometer',\n category: 'sim',\n crate: 'nvsim',\n summary:\n 'Deterministic forward simulator: scene → Biot–Savart → NV ensemble → ADC → MagFrame stream + SHA-256 witness.',\n budget: 'L',\n active: true,\n status: 'available',\n tags: ['quantum', 'magnetometer', 'simulator', 'witness', 'wasm'],\n adr: 'ADR-089',\n runtime: 'running',\n },\n\n // ── Core sensing primitives (ADR-014/040 flagship modules) ───────────────\n {\n id: 'gesture',\n name: 'Gesture (DTW)',\n category: 'sig',\n crate: 'wifi-densepose-wasm-edge',\n summary: 'Dynamic-Time-Warping gesture classifier from CSI motion templates.',\n events: [1],\n budget: 'M',\n status: 'available',\n tags: ['hci', 'csi', 'classifier', 'dtw'],\n adr: 'ADR-014',\n runtime: 'mesh-only',\n },\n {\n id: 'coherence',\n name: 'Coherence gate',\n category: 'sig',\n crate: 'wifi-densepose-wasm-edge',\n summary: 'Z-score coherence scoring + Accept/PredictOnly/Reject/Recalibrate gate.',\n events: [2],\n budget: 'S',\n status: 'available',\n tags: ['gate', 'csi', 'coherence', 'drift'],\n adr: 'ADR-029',\n runtime: 'simulated',\n },\n {\n id: 'adversarial',\n name: 'Adversarial-signal detector',\n category: 'ais',\n crate: 'wifi-densepose-wasm-edge',\n summary:\n 'Physically-impossible-signal detector — multi-link consistency, used to flag spoofed CSI.',\n events: [3],\n budget: 'M',\n status: 'available',\n tags: ['security', 'csi', 'spoofing', 'mesh'],\n adr: 'ADR-032',\n runtime: 'simulated',\n },\n {\n id: 'rvf',\n name: 'RVF — Rust Verified Feature stream',\n category: 'sig',\n crate: 'wifi-densepose-wasm-edge',\n summary: 'Verified-frame builder with SHA-256 hash + version metadata for the feature stream.',\n budget: 'S',\n status: 'available',\n tags: ['witness', 'csi', 'hash'],\n adr: 'ADR-040',\n },\n {\n id: 'occupancy',\n name: 'Occupancy estimator',\n category: 'bld',\n crate: 'wifi-densepose-wasm-edge',\n summary: 'Through-wall presence + person-count via CSI amplitude perturbation.',\n events: [300, 301, 302],\n budget: 'S',\n status: 'available',\n tags: ['csi', 'building', 'presence'],\n runtime: 'simulated',\n },\n {\n id: 'vital_trend',\n name: 'Vital-trend monitor',\n category: 'med',\n crate: 'wifi-densepose-wasm-edge',\n summary: 'HR + BR trend tracking with bradycardia/tachycardia/apnea events.',\n events: [100, 101, 102, 103, 104, 105],\n budget: 'S',\n status: 'available',\n tags: ['medical', 'vitals', 'csi'],\n adr: 'ADR-021',\n runtime: 'simulated',\n },\n {\n id: 'intrusion',\n name: 'Intrusion detector',\n category: 'sec',\n crate: 'wifi-densepose-wasm-edge',\n summary: 'Zone-based intrusion alert from CSI motion patterns.',\n events: [200, 201],\n budget: 'S',\n status: 'available',\n tags: ['security', 'zone', 'csi'],\n runtime: 'simulated',\n },\n\n // ── Medical & Health (100-series) ────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Security (200-series) ────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Smart Building (300-series) ──────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Retail (400-series) ──────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Industrial (500-series) ──────────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Signal primitives (600-series) ───────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Online learning ──────────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Spatial / graph ──────────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── Temporal / planning ──────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n { 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'] },\n\n // ── AI safety ────────────────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n\n // ── Quantum-flavoured ────────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n\n // ── Autonomy / mesh ──────────────────────────────────────────────────────\n { 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'] },\n { 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'] },\n\n // ── Exotic / Research (650-series) ───────────────────────────────────────\n { 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', runtime: 'simulated' },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n { 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'] },\n];\n\nexport const CATEGORIES: Record = {\n sim: { label: 'Simulators', color: 'oklch(0.78 0.14 70)', range: '—' },\n med: { label: 'Medical & Health', color: 'oklch(0.65 0.22 25)', range: '100–199' },\n sec: { label: 'Security & Safety', color: 'oklch(0.7 0.18 35)', range: '200–299' },\n bld: { label: 'Smart Building', color: 'oklch(0.78 0.12 195)', range: '300–399' },\n ret: { label: 'Retail & Hospitality', color: 'oklch(0.78 0.14 145)', range: '400–499' },\n ind: { label: 'Industrial', color: 'oklch(0.72 0.18 330)', range: '500–599' },\n sig: { label: 'Signal Processing', color: 'oklch(0.78 0.14 70)', range: '600–619' },\n lrn: { label: 'Online Learning', color: 'oklch(0.78 0.12 260)', range: '620–639' },\n spt: { label: 'Spatial / Graph', color: 'oklch(0.7 0.18 100)', range: '640–659' },\n tmp: { label: 'Temporal / Planning', color: 'oklch(0.7 0.16 50)', range: '660–679' },\n ais: { label: 'AI Safety', color: 'oklch(0.65 0.22 25)', range: '700–719' },\n qnt: { label: 'Quantum', color: 'oklch(0.72 0.18 290)', range: '720–739' },\n aut: { label: 'Autonomy', color: 'oklch(0.78 0.14 145)', range: '740–759' },\n exo: { label: 'Exotic / Research', color: 'oklch(0.72 0.18 330)', range: '650–699' },\n};\n\nexport interface AppActivation {\n id: string;\n /** Active in the current session. */\n active: boolean;\n /** Last activation timestamp. */\n lastActivatedAt?: number;\n /** Last event count seen (for the cards' counter). */\n eventCount?: number;\n}\n\nexport function defaultActivations(): AppActivation[] {\n return APPS.map((a) => ({ id: a.id, active: a.active === true, eventCount: 0 }));\n}\n\nexport function appsByCategory(): Record {\n const map = {} as Record;\n for (const c of Object.keys(CATEGORIES) as AppCategory[]) map[c] = [];\n for (const a of APPS) map[a.category].push(a);\n return map;\n}\n\nexport function findApp(id: string): AppManifest | undefined {\n return APPS.find((a) => a.id === id);\n}\n\nexport function fuzzyMatch(query: string, app: AppManifest): number {\n if (!query) return 1;\n const q = query.toLowerCase();\n let score = 0;\n if (app.id.toLowerCase().includes(q)) score += 3;\n if (app.name.toLowerCase().includes(q)) score += 3;\n if (app.summary.toLowerCase().includes(q)) score += 1;\n if (app.tags?.some((t) => t.toLowerCase().includes(q))) score += 2;\n if (app.category === q) score += 5;\n return score;\n}\n","/* IndexedDB-backed persistence for settings and saved scenes.\n * Mirrors the mockup's `nvsim/kv` store. */\n\nconst DB_NAME = 'nvsim';\nconst DB_VER = 1;\nconst STORE = 'kv';\n\nlet dbPromise: Promise | null = null;\n\nfunction openDb(): Promise {\n if (dbPromise) return dbPromise;\n dbPromise = new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, DB_VER);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE);\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n return dbPromise;\n}\n\nexport async function kvGet(key: string): Promise {\n const db = await openDb();\n return await new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readonly');\n const r = tx.objectStore(STORE).get(key);\n r.onsuccess = () => resolve(r.result as T | undefined);\n r.onerror = () => reject(r.error);\n });\n}\n\nexport async function kvSet(key: string, value: unknown): Promise {\n const db = await openDb();\n return await new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).put(value, key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n\nexport async function kvDelete(key: string): Promise {\n const db = await openDb();\n return await new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).delete(key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n","/* In-browser simulated runtimes for App Store apps.\n *\n * Each runtime takes the most recent nvsim MagFrame + a short rolling\n * history and decides whether to emit one or more app events. Outputs are\n * illustrative: nvsim produces magnetic-field samples, the wasm-edge\n * algorithms expect WiFi CSI subcarriers — different physical modalities.\n * The simulated runtime preserves *event-emission semantics* (the same\n * i32 event IDs, the same trigger logic shape) so users can see the\n * cards working without an ESP32 mesh.\n *\n * For engineering-grade output, deploy the real `wifi-densepose-wasm-edge`\n * crate to ESP32 firmware over the WS transport — see ADR-040 / ADR-092 §6.2.\n */\n\nimport type { MagFrameRecord } from '../transport/NvsimClient';\n\nexport interface AppEvent {\n /** Wall-clock timestamp (ms). */\n ts: number;\n /** App id that emitted. */\n appId: string;\n /** i32 event id from `event_types` mod in wifi-densepose-wasm-edge. */\n eventId: number;\n /** Human-readable event name (matches the constant name). */\n eventName: string;\n /** Numeric value the app reports (units app-specific). */\n value: number;\n /** Optional extra context for the console line. */\n detail?: string;\n}\n\nexport interface AppRuntimeContext {\n frame: MagFrameRecord;\n bMagT: number;\n bRecoveredT: [number, number, number];\n /** Rolling history of |B| in T. Most recent last. */\n bHistory: number[];\n /** Time since the runtime was activated (s). */\n elapsedS: number;\n /** Per-app scratch state — runtimes can persist counters here. */\n state: Record;\n}\n\nexport type AppRuntimeFn = (ctx: AppRuntimeContext) => AppEvent | AppEvent[] | null;\n\n/** Welford-style running-stat helper. */\nfunction rollingMean(arr: number[]): number {\n if (arr.length === 0) return 0;\n let s = 0;\n for (const v of arr) s += v;\n return s / arr.length;\n}\nfunction rollingStd(arr: number[]): number {\n if (arr.length < 2) return 0;\n const m = rollingMean(arr);\n let s = 0;\n for (const v of arr) s += (v - m) * (v - m);\n return Math.sqrt(s / (arr.length - 1));\n}\n\n/** vital_trend — periodic 1-Hz HR/BR estimate from the B_z oscillation. */\nconst vitalTrend: AppRuntimeFn = (ctx) => {\n if (ctx.bHistory.length < 64) return null;\n const last = ctx.state['lastEmitS'] ?? 0;\n if (ctx.elapsedS - last < 1.0) return null;\n ctx.state['lastEmitS'] = ctx.elapsedS;\n\n // Crude HR estimate: count zero-crossings of detrended B_z over the last\n // 64 samples; treat each crossing pair as one cardiac cycle.\n const tail = ctx.bHistory.slice(-64);\n const m = rollingMean(tail);\n let crossings = 0;\n for (let i = 1; i < tail.length; i++) {\n if ((tail[i] - m) * (tail[i - 1] - m) < 0) crossings++;\n }\n // 64 samples ≈ 0.65 s at the worker's 32-frame batches × 16 ms tick.\n const cycles = crossings / 2;\n const hr = Math.max(40, Math.min(180, Math.round((cycles / 0.65) * 60)));\n const br = Math.max(8, Math.min(30, Math.round(hr / 4))); // crude proxy\n\n const evs: AppEvent[] = [\n { ts: Date.now(), appId: 'vital_trend', eventId: 100, eventName: 'VITAL_TREND', value: hr, detail: `HR≈${hr} BPM, BR≈${br} br/min` },\n ];\n if (hr < 60) evs.push({ ts: Date.now(), appId: 'vital_trend', eventId: 103, eventName: 'BRADYCARDIA', value: hr, detail: `HR=${hr} BPM` });\n else if (hr > 100) evs.push({ ts: Date.now(), appId: 'vital_trend', eventId: 104, eventName: 'TACHYCARDIA', value: hr, detail: `HR=${hr} BPM` });\n if (br < 12) evs.push({ ts: Date.now(), appId: 'vital_trend', eventId: 101, eventName: 'BRADYPNEA', value: br, detail: `BR=${br} br/min` });\n else if (br > 24) evs.push({ ts: Date.now(), appId: 'vital_trend', eventId: 102, eventName: 'TACHYPNEA', value: br, detail: `BR=${br} br/min` });\n return evs;\n};\n\n/** occupancy — variance threshold on |B| over a 5-second window. */\nconst occupancy: AppRuntimeFn = (ctx) => {\n if (ctx.bHistory.length < 32) return null;\n const last = ctx.state['lastEmitS'] ?? 0;\n if (ctx.elapsedS - last < 2.0) return null;\n const std = rollingStd(ctx.bHistory.slice(-128)) * 1e9; // T → nT\n const occupied = std > 0.01; // empirical threshold for the demo\n const wasOccupied = (ctx.state['occ'] ?? 0) > 0.5;\n if (occupied !== wasOccupied) {\n ctx.state['occ'] = occupied ? 1 : 0;\n ctx.state['lastEmitS'] = ctx.elapsedS;\n return {\n ts: Date.now(),\n appId: 'occupancy',\n eventId: occupied ? 300 : 302,\n eventName: occupied ? 'ZONE_OCCUPIED' : 'ZONE_TRANSITION',\n value: std,\n detail: occupied ? `σ(|B|)=${std.toFixed(3)} nT — entered` : `σ(|B|)=${std.toFixed(3)} nT — left`,\n };\n }\n return null;\n};\n\n/** intrusion — |B| above ambient + dwell timer. */\nconst intrusion: AppRuntimeFn = (ctx) => {\n const ambient = ctx.state['ambient'] ?? ctx.bMagT;\n ctx.state['ambient'] = 0.95 * ambient + 0.05 * ctx.bMagT;\n const exceeds = ctx.bMagT > ambient * 1.5 && ctx.bMagT > 1e-12;\n const dwellStart = ctx.state['dwellStart'] ?? 0;\n if (exceeds && dwellStart === 0) {\n ctx.state['dwellStart'] = ctx.elapsedS;\n } else if (!exceeds) {\n ctx.state['dwellStart'] = 0;\n }\n if (exceeds && dwellStart > 0 && ctx.elapsedS - dwellStart > 0.5 && (ctx.state['lastEmitS'] ?? 0) < dwellStart) {\n ctx.state['lastEmitS'] = ctx.elapsedS;\n return {\n ts: Date.now(),\n appId: 'intrusion',\n eventId: 200,\n eventName: 'INTRUSION_ALERT',\n value: ctx.bMagT * 1e9,\n detail: `|B|=${(ctx.bMagT * 1e9).toFixed(2)} nT > 1.5× ambient (${(ambient * 1e9).toFixed(2)} nT) for ${(ctx.elapsedS - dwellStart).toFixed(1)} s`,\n };\n }\n return null;\n};\n\n/** coherence — z-score of recent |B| against a longer baseline. */\nconst coherence: AppRuntimeFn = (ctx) => {\n if (ctx.bHistory.length < 64) return null;\n const last = ctx.state['lastEmitS'] ?? 0;\n if (ctx.elapsedS - last < 0.5) return null;\n ctx.state['lastEmitS'] = ctx.elapsedS;\n\n const recent = ctx.bHistory.slice(-32);\n const baseline = ctx.bHistory.slice(-128, -32);\n if (baseline.length < 32) return null;\n const mu = rollingMean(baseline);\n const sd = rollingStd(baseline);\n if (sd === 0) return null;\n const recentMean = rollingMean(recent);\n const z = Math.abs(recentMean - mu) / sd;\n return {\n ts: Date.now(),\n appId: 'coherence',\n eventId: 2,\n eventName: 'COHERENCE_SCORE',\n value: z,\n detail: `z=${z.toFixed(2)} σ ${z > 3 ? '· DRIFT' : z > 1.5 ? '· marginal' : '· stable'}`,\n };\n};\n\n/** adversarial — detect physically-impossible 1/r³ violation. */\nconst adversarial: AppRuntimeFn = (ctx) => {\n if (ctx.bHistory.length < 32) return null;\n const last = ctx.state['lastEmitS'] ?? 0;\n if (ctx.elapsedS - last < 3.0) return null;\n\n // Fake \"multi-link consistency\": compare instantaneous |B| with the\n // smoothed |B|. A sharp factor-of-N step violates dipole physics\n // (real 1/r³ source moves continuously).\n const tail = ctx.bHistory.slice(-32);\n let maxJump = 0;\n for (let i = 1; i < tail.length; i++) {\n const j = Math.abs(Math.log(Math.max(tail[i], 1e-15)) - Math.log(Math.max(tail[i - 1], 1e-15)));\n if (j > maxJump) maxJump = j;\n }\n if (maxJump > 5) {\n ctx.state['lastEmitS'] = ctx.elapsedS;\n return {\n ts: Date.now(),\n appId: 'adversarial',\n eventId: 3,\n eventName: 'ANOMALY_DETECTED',\n value: maxJump,\n detail: `log-jump ${maxJump.toFixed(1)} — physically implausible step in |B|`,\n };\n }\n return null;\n};\n\n/** exo_ghost_hunter — empty-room CSI anomaly detector adapted to the\n * magnetic noise floor: flag impulsive / periodic / drift / random\n * patterns and a hidden-presence sub-detector at 0.15-0.5 Hz. */\nconst exoGhostHunter: AppRuntimeFn = (ctx) => {\n if (ctx.bHistory.length < 128) return null;\n const last = ctx.state['lastEmitS'] ?? 0;\n if (ctx.elapsedS - last < 4.0) return null;\n ctx.state['lastEmitS'] = ctx.elapsedS;\n\n const tail = ctx.bHistory.slice(-128);\n const std = rollingStd(tail) * 1e9;\n // Detect impulsive: max - mean > 4σ\n const m = rollingMean(tail);\n let maxDev = 0;\n for (const v of tail) {\n const d = Math.abs(v - m);\n if (d > maxDev) maxDev = d;\n }\n const cls: 1 | 3 | 4 = maxDev > 4 * (std * 1e-9) ? 1 // impulsive\n : ctx.elapsedS > 10 ? 3 // drift bias as a default after warmup\n : 4; // random\n const clsName = cls === 1 ? 'impulsive' : cls === 3 ? 'drift' : 'random';\n return {\n ts: Date.now(),\n appId: 'exo_ghost_hunter',\n eventId: 651,\n eventName: 'ANOMALY_CLASS',\n value: cls,\n detail: `class=${clsName} · σ=${std.toFixed(3)} nT`,\n };\n};\n\nexport const APP_RUNTIMES: Record = {\n vital_trend: vitalTrend,\n occupancy,\n intrusion,\n coherence,\n adversarial,\n exo_ghost_hunter: exoGhostHunter,\n};\n\nexport function hasRuntime(appId: string): boolean {\n return appId in APP_RUNTIMES;\n}\n","/* App Store — catalog of every WASM edge module + simulator app.\n *\n * Mirrors `wifi-densepose-wasm-edge`'s 60+ hot-loadable algorithms and\n * the `nvsim` simulator. Each card is filterable by category, fuzzy\n * name search, and maturity (available / beta / research). A toggle on\n * each card flips activation in the live session — that drives the\n * dashboard's event log when running. WS transport (future) pushes the\n * activation set to the connected ESP32 mesh.\n *\n * ADR-092 §18.\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { signal, effect } from '@preact/signals-core';\nimport {\n APPS, CATEGORIES, defaultActivations, fuzzyMatch,\n type AppCategory, type AppManifest, type AppActivation,\n} from '../store/apps';\nimport { kvGet, kvSet } from '../store/persistence';\nimport { pushLog, activeAppIds, appEvents, appEventCounts } from '../store/appStore';\nimport { hasRuntime } from '../store/appRuntimes';\n\nconst activations = signal(defaultActivations());\nconst query = signal('');\nconst activeCat = signal('all');\nconst statusFilter = signal<'all' | 'available' | 'beta' | 'research'>('all');\n\n(async () => {\n const saved = await kvGet('app-activations');\n if (saved) activations.value = saved;\n})();\n\neffect(() => {\n // Persist activations on change (post-load) AND mirror into the\n // active-set signal that main.ts watches to drive runtime dispatch.\n const v = activations.value;\n if (v.length > 0) void kvSet('app-activations', v);\n const set = new Set();\n for (const a of v) if (a.active) set.add(a.id);\n activeAppIds.value = set;\n});\n\n@customElement('nv-app-store')\nexport class NvAppStore extends LitElement {\n @state() private renderTick = 0;\n\n static styles = css`\n :host {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);\n padding: 24px;\n }\n .head {\n display: flex; align-items: center; gap: 16px;\n margin-bottom: 18px;\n flex-wrap: wrap;\n }\n .ttl {\n font-size: 22px; font-weight: 700; letter-spacing: -0.02em;\n color: var(--ink);\n flex: 1; min-width: 200px;\n }\n .ttl small {\n font-size: 12.5px; font-weight: 400;\n color: var(--ink-3); margin-left: 8px;\n }\n .search {\n width: 320px; max-width: 100%;\n padding: 8px 12px;\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: 8px;\n font-family: var(--mono);\n font-size: 12.5px;\n color: var(--ink); outline: none;\n }\n .search:focus { border-color: var(--accent); }\n .filters {\n display: flex; flex-wrap: wrap; gap: 6px;\n margin-bottom: 18px;\n }\n .chip {\n padding: 4px 10px;\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: 999px;\n font-size: 11.5px; color: var(--ink-3);\n cursor: pointer;\n font-family: var(--mono);\n display: inline-flex; align-items: center; gap: 4px;\n }\n .chip:hover { color: var(--ink); border-color: var(--line-2); }\n .chip.on { background: var(--bg-3); border-color: var(--accent); color: var(--ink); }\n .chip .swatch {\n width: 7px; height: 7px; border-radius: 50%;\n }\n .chip .count { color: var(--ink-3); font-size: 10px; }\n .grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 12px;\n }\n .card {\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: var(--radius);\n padding: 12px 14px;\n display: flex; flex-direction: column; gap: 6px;\n transition: border-color 0.15s, transform 0.15s;\n position: relative;\n }\n .card:hover { border-color: var(--line-2); transform: translateY(-1px); }\n .card.active {\n border-color: oklch(0.78 0.14 145 / 0.7);\n background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 145 / 0.04) 100%);\n }\n .card-h {\n display: flex; align-items: flex-start; gap: 8px;\n margin-bottom: 2px;\n }\n .card-h .name {\n font-size: 13.5px; font-weight: 600; color: var(--ink);\n flex: 1; line-height: 1.3;\n }\n .card-h .swatch {\n width: 10px; height: 10px; border-radius: 50%;\n flex-shrink: 0; margin-top: 4px;\n }\n .summary {\n font-size: 12px; color: var(--ink-2); line-height: 1.45;\n flex: 1;\n }\n .meta {\n display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px;\n font-family: var(--mono); font-size: 10px;\n }\n .badge {\n padding: 1px 6px; border-radius: 4px;\n background: var(--bg-3); color: var(--ink-3);\n border: 1px solid var(--line);\n }\n .badge.cat { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.3); }\n .badge.status-available { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }\n .badge.status-beta { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }\n .badge.status-research { color: var(--accent-3); border-color: oklch(0.72 0.18 330 / 0.4); }\n .badge.budget { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.3); }\n .badge.rt-running { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.5); background: oklch(0.78 0.14 145 / 0.08); }\n .badge.rt-simulated { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.5); background: oklch(0.78 0.14 70 / 0.08); }\n .badge.rt-mesh-only { color: var(--ink-3); border-color: var(--line); }\n .events-feed {\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: var(--radius);\n padding: 14px;\n margin-bottom: 18px;\n }\n .events-feed h3 {\n margin: 0 0 8px;\n font-size: 13px; font-weight: 600;\n color: var(--ink);\n }\n .events-feed .lead {\n font-size: 12px; color: var(--ink-3);\n margin: 0 0 10px;\n line-height: 1.5;\n }\n .events-feed .lines {\n display: flex; flex-direction: column; gap: 4px;\n max-height: 160px; overflow-y: auto;\n }\n .ev-line {\n display: grid;\n grid-template-columns: 60px 90px 1fr;\n gap: 10px;\n padding: 4px 6px;\n border-radius: 4px;\n font-family: var(--mono);\n font-size: 11px;\n color: var(--ink-2);\n }\n .ev-line:hover { background: var(--bg-3); }\n .ev-line .ts { color: var(--ink-4); font-size: 10.5px; }\n .ev-line .id { color: var(--accent); font-size: 10.5px; }\n .ev-line .body { color: var(--ink); }\n .ev-empty {\n font-size: 12px; color: var(--ink-3);\n padding: 8px 0;\n }\n .card-events-count {\n font-size: 10.5px;\n color: var(--accent-4);\n font-family: var(--mono);\n }\n .card-foot {\n display: flex; align-items: center; gap: 8px;\n padding-top: 8px; margin-top: 4px;\n border-top: 1px solid var(--line);\n font-size: 11px; color: var(--ink-3);\n }\n .toggle {\n position: relative;\n width: 32px; height: 18px;\n background: var(--bg-3); border: 1px solid var(--line-2);\n border-radius: 999px; cursor: pointer;\n transition: background 0.15s;\n flex-shrink: 0;\n }\n .toggle::after {\n content: ''; position: absolute;\n top: 1px; left: 1px;\n width: 12px; height: 12px;\n background: var(--ink-3); border-radius: 50%;\n transition: transform 0.15s, background 0.15s;\n }\n .toggle.on { background: var(--accent); border-color: var(--accent); }\n .toggle.on::after { background: #1a0f00; transform: translateX(14px); }\n .events {\n font-family: var(--mono); font-size: 10px; color: var(--ink-3);\n flex: 1;\n }\n .empty {\n padding: 40px;\n text-align: center; color: var(--ink-3);\n font-size: 13px;\n }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n effect(() => {\n activations.value; query.value; activeCat.value; statusFilter.value;\n appEvents.value; appEventCounts.value;\n this.renderTick++;\n });\n }\n\n private isActive(id: string): boolean {\n return activations.value.find((a) => a.id === id)?.active === true;\n }\n\n private toggle(app: AppManifest): void {\n const wasActive = this.isActive(app.id);\n const next = activations.value.map((a) => a.id === app.id ? { ...a, active: !a.active, lastActivatedAt: Date.now() } : a);\n activations.value = next;\n if (!wasActive) {\n const r = app.runtime ?? 'mesh-only';\n const note = r === 'simulated' ? ' · live runtime engaged'\n : r === 'mesh-only' ? ' · queued (needs ESP32 mesh)'\n : '';\n pushLog('ok', `app ${app.id} activated${note}`);\n } else {\n pushLog('info', `app ${app.id} deactivated`);\n }\n }\n\n private filtered(): AppManifest[] {\n let list = APPS;\n if (activeCat.value !== 'all') list = list.filter((a) => a.category === activeCat.value);\n if (statusFilter.value !== 'all') list = list.filter((a) => a.status === statusFilter.value);\n if (query.value.trim()) {\n list = list\n .map((a) => ({ a, s: fuzzyMatch(query.value, a) }))\n .filter((x) => x.s > 0)\n .sort((a, b) => b.s - a.s)\n .map((x) => x.a);\n }\n return list;\n }\n\n private categoryCounts(): Record {\n const counts: Record = { all: APPS.length };\n for (const k of Object.keys(CATEGORIES)) counts[k] = 0;\n for (const a of APPS) counts[a.category] = (counts[a.category] ?? 0) + 1;\n return counts;\n }\n\n override render() {\n const list = this.filtered();\n const counts = this.categoryCounts();\n const activeCount = activations.value.filter((a) => a.active).length;\n return html`\n \n
\n App Store\n ${APPS.length} edge apps · ${activeCount} active \n
\n
{ query.value = (e.target as HTMLInputElement).value; }} />\n
\n\n \n activeCat.value = 'all'}>\n All${counts.all} \n \n ${(Object.keys(CATEGORIES) as AppCategory[]).map((k) => html`\n activeCat.value = k}>\n \n ${CATEGORIES[k].label}\n ${counts[k] ?? 0} \n \n `)}\n \n statusFilter.value = 'all'}>any \n statusFilter.value = 'available'}>available \n statusFilter.value = 'beta'}>beta \n statusFilter.value = 'research'}>research \n
\n\n ${this.renderEventsFeed()}\n\n ${list.length === 0\n ? html`No apps match the current filters.
`\n : html`${list.map((app) => this.card(app))}
`}\n `;\n }\n\n private renderEventsFeed() {\n const evs = appEvents.value.slice(-12).reverse();\n const activeSimCount = activations.value.filter((a) => a.active && hasRuntime(a.id)).length;\n return html`\n \n
Live runtime feed\n ${activeSimCount > 0\n ? html`${activeSimCount} simulated app${activeSimCount === 1 ? '' : 's'} active `\n : ''}\n \n
\n Apps with the simulated \n runtime emit real i32 event IDs against nvsim's live frame stream below.\n Apps with mesh-only \n need an ESP32-S3 + WS transport (deferred to V2). The\n running \n badge marks nvsim itself, which is always running.\n
\n ${evs.length === 0\n ? html`
No events yet. Toggle a card with the simulated badge and press ▶ Run .
`\n : html`
${evs.map((ev) => {\n const dt = new Date(ev.ts);\n const ts = `${String(dt.getSeconds()).padStart(2, '0')}.${String(dt.getMilliseconds()).padStart(3, '0')}`;\n return html`\n
\n ${ts} \n ${ev.appId} \n ${ev.eventName} · ${ev.eventId} ${ev.detail ? `· ${ev.detail}` : ''} \n
\n `;\n })}
`}\n
\n `;\n }\n\n private card(app: AppManifest) {\n const active = this.isActive(app.id);\n const cat = CATEGORIES[app.category];\n const runtime = app.runtime ?? 'mesh-only';\n const evCount = appEventCounts.value[app.id] ?? 0;\n const runtimeLabel: Record = {\n 'running': 'running',\n 'simulated': 'simulated',\n 'mesh-only': 'needs mesh',\n };\n const runtimeTip: Record = {\n 'running': 'This app is genuinely running in your browser right now.',\n 'simulated': 'A pared-down version of this algorithm runs against nvsim\\'s magnetic frame stream as a proxy for its native CSI input. Toggle on, then press ▶ Run to see real event IDs in the feed.',\n 'mesh-only': 'This algorithm needs CSI subcarrier data from an ESP32-S3 mesh. The toggle persists; activation is pushed via WS transport (V2).',\n };\n return html`\n \n
\n \n ${app.name} \n
\n
${app.summary}
\n
\n ${cat.label} \n ${app.status} \n ${runtimeLabel[runtime]} \n ${app.budget ? html`budget ${app.budget} ` : ''}\n ${app.adr ? html`${app.adr} ` : ''}\n ${app.events?.length ? html`events ${app.events.join('·')} ` : ''}\n
\n \n
\n `;\n }\n}\n","/* Command palette ⌘K. */\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state, query } from 'lit/decorators.js';\nimport { toast } from './nv-toast';\nimport { openModal } from './nv-modal';\nimport {\n getClient, theme, expectedWitness, witnessHex, witnessVerified, pushLog, running,\n} from '../store/appStore';\n\ninterface Cmd { ico: string; label: string; kbd?: string; run: () => void; }\n\n@customElement('nv-palette')\nexport class NvPalette extends LitElement {\n @state() private open = false;\n @state() private filter = '';\n @state() private idx = 0;\n @query('#palette-input') private inputEl!: HTMLInputElement;\n\n static styles = css`\n :host {\n position: fixed; inset: 0; z-index: 220;\n background: rgba(0,0,0,0.5);\n opacity: 0; pointer-events: none;\n transition: opacity 0.15s;\n display: flex; justify-content: center; padding-top: 12vh;\n backdrop-filter: blur(4px);\n }\n :host([open]) { opacity: 1; pointer-events: auto; }\n .palette {\n width: min(560px, 92vw);\n background: var(--bg-1);\n border: 1px solid var(--line-2);\n border-radius: var(--radius);\n box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);\n overflow: hidden;\n display: flex; flex-direction: column;\n max-height: 60vh;\n }\n .input {\n padding: 14px 16px;\n border-bottom: 1px solid var(--line);\n }\n input {\n width: 100%;\n background: transparent; border: none; outline: none;\n color: var(--ink); font-size: 14px;\n font-family: inherit;\n }\n .list { flex: 1; overflow-y: auto; padding: 4px; }\n .item {\n display: flex; align-items: center; gap: 10px;\n padding: 8px 12px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12.5px;\n }\n .item.active { background: var(--bg-3); }\n .item .ico { width: 20px; text-align: center; color: var(--accent); }\n .item .lbl { flex: 1; }\n .item .kbd {\n font-family: var(--mono); font-size: 10.5px;\n color: var(--ink-3);\n padding: 1px 5px; background: var(--bg-3); border-radius: 4px;\n }\n `;\n\n private cmds: Cmd[] = [\n { ico: '▶', label: 'Run pipeline', kbd: 'Space', run: async () => { await getClient()?.run(); running.value = true; toast('Pipeline running', '▶'); } },\n { ico: '❚', label: 'Pause pipeline', run: async () => { await getClient()?.pause(); running.value = false; toast('Paused', '❚❚'); } },\n { ico: '+', label: 'New scene…', kbd: '⌘N', run: () => openModal({\n title: 'New scene',\n body: `Build a fresh magnetic scene. The dashboard generates the JSON\n and pushes it to the running pipeline (or you can copy the JSON\n for offline use).
\n Name \n \n Heart-proxy dipole moment (A·m²) \n \n Distance heart → sensor (m) \n \n Add ferrous distractor at +x = 1 m? \n \n No \n Yes (steel coil, χ=5000) \n \n Add 60 Hz mains-current loop? \n \n No \n Yes (2 A loop, 5 cm radius, +y = 1 m) \n `,\n buttons: [\n { label: 'Cancel', variant: 'ghost' },\n { label: 'Create', variant: 'primary', onClick: async () => {\n const root = document.querySelector('nv-app')?.shadowRoot?.querySelector('nv-modal')?.shadowRoot;\n if (!root) return;\n const name = (root.querySelector('#ns-name')?.value ?? 'custom').trim();\n const m = parseFloat(root.querySelector('#ns-moment')?.value ?? '1e-6');\n const d = parseFloat(root.querySelector('#ns-distance')?.value ?? '0.5');\n const ferr = root.querySelector('#ns-ferrous')?.value === '1';\n const mains = root.querySelector('#ns-mains')?.value === '1';\n const scene = {\n dipoles: [{ position: [0, 0, d] as [number, number, number], moment: [0, 0, m] as [number, number, number] }],\n loops: mains ? [{\n centre: [0, 1, 0] as [number, number, number],\n normal: [0, 1, 0] as [number, number, number],\n radius: 0.05, current: 2.0, n_segments: 64,\n }] : [],\n ferrous: ferr ? [{ position: [1, 0, 0] as [number, number, number], volume: 1e-4, susceptibility: 5000 }] : [],\n eddy: [],\n sensors: [[0, 0, 0] as [number, number, number]],\n ambient_field: [1e-6, 0, 0] as [number, number, number],\n };\n await getClient()?.loadScene(scene);\n pushLog('ok', `scene ${name} loaded · 1 dipole · ${mains ? '1 loop · ' : ''}${ferr ? '1 ferrous · ' : ''}1 sensor`);\n toast(`Scene \"${name}\" loaded`, '+');\n } },\n ],\n }) },\n { ico: '📦', label: 'Export proof bundle…', kbd: '⌘E', run: async () => {\n const c = getClient(); if (!c) return;\n pushLog('dbg', 'building proof bundle…');\n try {\n const blob = await c.exportProofBundle();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `nvsim-proof-${Date.now()}.json`;\n a.click();\n URL.revokeObjectURL(url);\n pushLog('ok', `proof bundle exported · ${blob.size} bytes`);\n toast(`Proof bundle saved (${blob.size} B)`, '📦');\n } catch (e) { pushLog('err', `export failed: ${(e as Error).message}`); }\n } },\n { ico: '⟳', label: 'Reset pipeline', kbd: '⌘R', run: () => openModal({\n title: 'Reset pipeline?',\n body: 'Clears the frame stream and rewinds t to 0.
',\n buttons: [\n { label: 'Cancel', variant: 'ghost' },\n { label: 'Reset', variant: 'danger', onClick: async () => { await getClient()?.reset(); pushLog('warn', 'pipeline reset · t=0'); toast('Pipeline reset', '⟳'); } },\n ],\n }) },\n { ico: '✓', label: 'Verify witness', run: async () => {\n const c = getClient(); if (!c) return;\n witnessVerified.value = 'pending';\n const exp = expectedWitness.value;\n const eb = new Uint8Array(32);\n for (let i = 0; i < 32; i++) eb[i] = parseInt(exp.slice(i * 2, i * 2 + 2), 16);\n const r = await c.verifyWitness(eb);\n if (r.ok) { witnessVerified.value = 'ok'; witnessHex.value = exp; toast('Witness verified', '✓'); }\n else { witnessVerified.value = 'fail'; toast('Witness mismatch!', '✗'); }\n } },\n { ico: '☼', label: 'Toggle theme', kbd: '⌘/', run: () => { theme.value = theme.value === 'dark' ? 'light' : 'dark'; } },\n { ico: '⚙', label: 'Open settings', kbd: '⌘,', run: () => window.dispatchEvent(new CustomEvent('open-settings')) },\n { ico: '?', label: 'Keyboard shortcuts…', run: () => openModal({\n title: 'Keyboard shortcuts',\n body: `\n
⌘K / Ctrl K
Command palette
\n
Space
Play / pause
\n
⌘R
Reset
\n
⌘,
Settings
\n
⌘/
Toggle theme
\n
\\`
Debug HUD
\n
1 · 2 · 3
Inspector tabs
\n
Esc
Close modal/palette
\n
/
Focus REPL
\n
`,\n buttons: [{ label: 'Close', variant: 'primary' }],\n }) },\n { ico: 'i', label: 'About nvsim…', run: () => openModal({\n title: 'About nvsim',\n body: `nvsim is a deterministic, byte-reproducible forward simulator for nitrogen-vacancy diamond magnetometry.
\n This dashboard runs nvsim as WASM in a Web Worker. Same (scene, config, seed) → byte-identical SHA-256 witness across runs and machines.
\n License: MIT OR Apache-2.0 · See ADR-089, ADR-092.
`,\n buttons: [{ label: 'Close', variant: 'primary' }],\n }) },\n ];\n\n override connectedCallback(): void {\n super.connectedCallback();\n window.addEventListener('keydown', this.onKey);\n window.addEventListener('nv-palette', this.onOpen as EventListener);\n }\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('keydown', this.onKey);\n window.removeEventListener('nv-palette', this.onOpen as EventListener);\n }\n\n private onKey = (e: KeyboardEvent): void => {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {\n e.preventDefault();\n this.openPal();\n } else if (e.key === 'Escape' && this.open) {\n this.closePal();\n } else if (this.open) {\n if (e.key === 'ArrowDown') { this.idx = Math.min(this.cmds.length - 1, this.idx + 1); e.preventDefault(); }\n else if (e.key === 'ArrowUp') { this.idx = Math.max(0, this.idx - 1); e.preventDefault(); }\n else if (e.key === 'Enter') { this.runIdx(); e.preventDefault(); }\n }\n };\n\n private onOpen = (): void => this.openPal();\n\n private openPal(): void {\n this.open = true; this.setAttribute('open', '');\n this.filter = ''; this.idx = 0;\n setTimeout(() => this.inputEl?.focus(), 0);\n }\n private closePal(): void { this.open = false; this.removeAttribute('open'); }\n\n private filtered(): Cmd[] {\n if (!this.filter.trim()) return this.cmds;\n const q = this.filter.toLowerCase();\n return this.cmds.filter((c) => c.label.toLowerCase().includes(q));\n }\n\n private runIdx(): void {\n const f = this.filtered();\n const c = f[this.idx];\n if (c) { c.run(); this.closePal(); }\n }\n\n override render() {\n const items = this.filtered();\n return html`\n \n
\n { this.filter = (e.target as HTMLInputElement).value; this.idx = 0; }} />\n
\n
\n ${items.map((c, i) => html`\n
{ this.idx = i; this.runIdx(); }}>\n ${c.ico} \n ${c.label} \n ${c.kbd ? html`${c.kbd} ` : ''}\n
\n `)}\n
\n
\n `;\n }\n}\n","/* Debug HUD toggled with `. Shows render fps, sim t, frames, |B|, SNR. */\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport { fps, framesEmitted, bMag, snr, t as simT } from '../store/appStore';\n\n@customElement('nv-debug-hud')\nexport class NvDebugHud extends LitElement {\n @state() private open = false;\n @state() private renderFps = 0;\n private lastTs = performance.now();\n private frameCount = 0;\n private rafId = 0;\n\n static styles = css`\n :host {\n position: fixed; bottom: 8px; right: 8px;\n width: 220px;\n background: rgba(13,17,23,0.85);\n backdrop-filter: blur(8px);\n border: 1px solid var(--line-2);\n border-radius: 8px;\n padding: 8px 10px;\n font-family: var(--mono); font-size: 11px;\n color: var(--ink-2);\n z-index: 99;\n display: none;\n box-shadow: var(--shadow);\n }\n :host([open]) { display: block; }\n .h {\n display: flex; justify-content: space-between;\n font-weight: 600; color: var(--ink);\n margin-bottom: 6px; padding-bottom: 4px;\n border-bottom: 1px solid var(--line);\n }\n .x { cursor: pointer; color: var(--ink-3); }\n .row {\n display: flex; justify-content: space-between;\n padding: 1px 0;\n }\n .k { color: var(--ink-3); }\n .v { color: var(--ink); }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n window.addEventListener('keydown', this.onKey);\n effect(() => { fps.value; framesEmitted.value; bMag.value; snr.value; simT.value; this.requestUpdate(); });\n this.tick();\n }\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('keydown', this.onKey);\n cancelAnimationFrame(this.rafId);\n }\n\n private onKey = (e: KeyboardEvent): void => {\n if (e.key === '`' && !(e.target as HTMLElement).matches('input, textarea')) {\n this.open = !this.open;\n this.toggleAttribute('open', this.open);\n }\n };\n\n private tick = (): void => {\n this.rafId = requestAnimationFrame(this.tick);\n const now = performance.now();\n this.frameCount++;\n if (now - this.lastTs >= 500) {\n this.renderFps = (this.frameCount * 1000) / (now - this.lastTs);\n this.frameCount = 0;\n this.lastTs = now;\n this.requestUpdate();\n }\n };\n\n override render() {\n return html`\n nvsim · debug { this.open = false; this.removeAttribute('open'); }}>✕
\n render fps ${this.renderFps.toFixed(1)}
\n sim fps ${fps.value > 0 ? Math.round(fps.value) : '—'}
\n frames ${framesEmitted.value.toString()}
\n |B| ${(bMag.value * 1e9).toFixed(3)} nT
\n SNR ${snr.value > 0 ? snr.value.toFixed(1) : '—'}
\n DOM ${document.querySelectorAll('*').length}
\n `;\n }\n}\n","/* Settings drawer — theme / density / motion / auto-update. */\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport { theme, density, motionReduced, autoUpdate, transport, wsUrl } from '../store/appStore';\n\n@customElement('nv-settings-drawer')\nexport class NvSettingsDrawer extends LitElement {\n @state() private open = false;\n\n static styles = css`\n :host {\n position: fixed; top: 0; right: 0; bottom: 0;\n width: 420px; max-width: 100vw;\n background: var(--bg-1);\n border-left: 1px solid var(--line);\n z-index: 51;\n transform: translateX(100%);\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex; flex-direction: column;\n box-shadow: -20px 0 60px -20px rgba(0,0,0,0.5);\n }\n :host([open]) { transform: translateX(0); }\n .scrim {\n position: fixed; inset: 0;\n background: rgba(0,0,0,0.5);\n z-index: 50;\n opacity: 0; pointer-events: none;\n transition: opacity 0.2s;\n }\n :host([open]) .scrim { opacity: 1; pointer-events: auto; }\n .h {\n padding: 14px 16px;\n border-bottom: 1px solid var(--line);\n display: flex; align-items: center; justify-content: space-between;\n }\n .h .ttl { font-size: 14px; font-weight: 600; }\n .body { flex: 1; overflow-y: auto; padding: 16px; }\n .group { margin-bottom: 22px; }\n .group h4 {\n margin: 0 0 10px;\n font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: 0.08em;\n color: var(--ink-3);\n }\n .row {\n display: flex; justify-content: space-between; align-items: center;\n padding: 10px 0;\n border-bottom: 1px solid var(--line);\n }\n .row:last-child { border-bottom: 0; }\n .row .lbl { font-size: 13px; }\n .row .desc { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }\n .row > div:first-child { flex: 1; padding-right: 12px; }\n .seg {\n display: inline-flex;\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: var(--radius-sm);\n padding: 2px;\n }\n .seg button {\n padding: 4px 10px;\n background: transparent; border: none;\n border-radius: 6px;\n font-size: 11.5px; color: var(--ink-3);\n font-family: var(--mono);\n cursor: pointer;\n }\n .seg button.on { background: var(--bg-1); color: var(--ink); }\n .toggle {\n position: relative;\n width: 36px; height: 20px;\n background: var(--bg-3);\n border: 1px solid var(--line-2);\n border-radius: 999px;\n cursor: pointer;\n flex-shrink: 0;\n }\n .toggle::after {\n content: ''; position: absolute;\n top: 2px; left: 2px;\n width: 14px; height: 14px;\n background: var(--ink-3);\n border-radius: 50%;\n transition: transform 0.15s, background 0.15s;\n }\n .toggle.on { background: var(--accent); border-color: var(--accent); }\n .toggle.on::after { background: #1a0f00; transform: translateX(16px); }\n .close {\n width: 28px; height: 28px;\n background: transparent; border: 1px solid var(--line);\n border-radius: 6px;\n color: var(--ink-2);\n }\n input[type=\"text\"] {\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: 6px;\n padding: 6px 10px;\n color: var(--ink); font-family: var(--mono); font-size: 12px;\n outline: none;\n }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n effect(() => { theme.value; density.value; motionReduced.value; autoUpdate.value; transport.value; wsUrl.value; this.requestUpdate(); });\n window.addEventListener('open-settings', () => { this.open = true; this.setAttribute('open', ''); });\n }\n\n private close(): void { this.open = false; this.removeAttribute('open'); }\n\n private async resetPrefs(): Promise {\n if (!confirm('Reset all preferences and IndexedDB state? Reloads the page.')) return;\n try {\n const dbs = await indexedDB.databases?.();\n if (dbs) for (const d of dbs) if (d.name) indexedDB.deleteDatabase(d.name);\n } catch { /* noop */ }\n location.reload();\n }\n\n override render() {\n return html`\n this.close()}>
\n \n
Settings
\n
this.close()}>× \n
\n \n
\n
Appearance \n
\n
\n
Theme
\n
Dark is the default; light has higher contrast for daylight work.
\n
\n
\n theme.value = 'dark'}>dark \n theme.value = 'light'}>light \n
\n
\n
\n
\n
Density
\n
Affects panel padding and font scale (15 / 14 / 13 px). Choose what your eyes prefer.
\n
\n
\n density.value = 'comfy'}>comfy \n density.value = 'default'}>default \n density.value = 'compact'}>compact \n
\n
\n
\n
\n
Reduce motion
\n
Stops the rotating diamond, animated field lines, and chart easing. Auto-on if your system has the prefers-reduced-motion preference set.
\n
\n
motionReduced.value = !motionReduced.value}> \n
\n
\n\n
\n
Pipeline \n
\n
\n
Auto-rerun on edit
\n
When you change a Tunables slider or load a new scene, push the change to the worker without a manual restart.
\n
\n
autoUpdate.value = !autoUpdate.value}> \n
\n
\n\n
\n
Transport \n
\n
\n
Mode
\n
WASM runs nvsim in your browser (default, no server). WS connects to a host-supplied nvsim-server (REST + binary WebSocket); see ADR-092 §6.2.
\n
\n
\n transport.value = 'wasm'}>WASM \n transport.value = 'ws'}>WS \n
\n
\n ${transport.value === 'ws' ? html`\n
\n
\n
WS URL
\n
Where your nvsim-server is listening. The server defaults to 127.0.0.1:7878.
\n
\n
wsUrl.value = (e.target as HTMLInputElement).value} />\n
` : ''}\n
\n\n
\n
Help \n
\n
\n
Open help center
\n
Quickstart, glossary, FAQ, and shortcuts. Press ? any time.
\n
\n
{ this.close(); window.dispatchEvent(new CustomEvent('nv-show-help')); }}\n style=\"padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid var(--line);border-radius:6px;color:var(--ink);\">\n Open\n \n
\n
\n
\n
Replay welcome tour
\n
Re-show the 6-step first-run walkthrough.
\n
\n
{ this.close(); window.dispatchEvent(new CustomEvent('nv-show-tour')); }}\n style=\"padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid var(--line);border-radius:6px;color:var(--ink);\">\n Replay\n \n
\n
\n
\n
Reset all preferences
\n
Wipe theme, density, motion, scene drag positions, REPL history, and the onboarding-seen flag.
\n
\n
this.resetPrefs()}\n style=\"padding:6px 12px;cursor:pointer;background:var(--bg-3);border:1px solid oklch(0.65 0.22 25 / 0.4);border-radius:6px;color:var(--bad);\">\n Reset\n \n
\n
\n\n
\n
\n `;\n }\n}\n","/* Welcome modal + step-by-step introduction tour.\n *\n * 10 steps walking the user through every panel of the dashboard with\n * concrete CTAs (\"Try it now\") that fire real navigation against the\n * live UI. First-run only by default; replayable via Settings → Help.\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { kvGet, kvSet } from '../store/persistence';\n\ninterface TourStep {\n /** Optional icon shown at the top of the step. */\n icon: string;\n title: string;\n /** Markdown-ish HTML body (rendered via .innerHTML). */\n body: string;\n /** Optional CTA: clicking runs the action then advances. */\n cta?: { label: string; run?: () => void };\n /** Optional \"do this yourself\" hint. */\n hint?: string;\n}\n\nconst STEPS: TourStep[] = [\n {\n icon: '👋',\n title: 'Welcome to nvsim',\n body: `\n nvsim is an open-source, deterministic forward simulator for\n nitrogen-vacancy diamond magnetometry — a real Rust crate compiled\n to WebAssembly and running in your browser, right now.
\n \n This 60-second tour walks you through the four panels, the App Store,\n the Ghost Murmur research view, and the determinism contract that\n makes nvsim distinctive.
\n \n Press Esc any time to skip. You can replay this tour from\n Settings → Help .
`,\n cta: { label: 'Start the tour →' },\n },\n {\n icon: '🌐',\n title: 'The Scene canvas',\n body: `The middle panel shows your magnetic scene — a small simulated\n environment with four sources and one NV-diamond sensor at the centre.
\n The four amber/cyan/magenta blobs are draggable: rebar coil \n (steel χ=5000), heart proxy dipole, 60 Hz mains current loop,\n and a steel door (eddy current). Field lines connect each source\n to the sensor and animate while the pipeline runs.
\n \n Top-left toolbar: zoom in/out, fit-to-view, layer toggles. Bottom-right:\n sim controls (step / play / step / speed cycle). Drag positions persist\n across reloads.
`,\n hint: 'Try dragging the heart_proxy after the tour ends.',\n },\n {\n icon: '▶',\n title: 'Run the pipeline',\n body: `Press ▶ Run in the topbar (or hit Space ) to start\n the live frame stream. nvsim runs at ~1.8 kHz on x86_64 WASM —\n well above the 1 kHz Cortex-A53 acceptance gate.
\n The FPS pill in the topbar updates with the throughput. The B-vector\n trace and frame-stream sparkline in the right inspector update in real\n time.
\n \n Space toggles run/pause from anywhere. Reset (⌘R )\n rewinds t to 0 without changing the seed.
`,\n },\n {\n icon: '🔍',\n title: 'Inspector — three tabs, three depths',\n body: `The right rail shows the live inspector: Signal (B-vector\n trace + frame-stream sparkline), Frame (decoded MagFrame fields +\n raw 60-byte hex dump), Witness (SHA-256 determinism gate).
\n Click the magnifier icon in the left rail to expand the\n inspector to the full main area, with bigger charts and an explainer\n header. Click the shield icon to do the same focused on Witness.
\n \n Number keys 1 2 3 jump between the\n three inspector tabs from anywhere.
`,\n },\n {\n icon: '✓',\n title: 'The witness — what makes nvsim distinctive',\n body: `nvsim's defining commitment: same (scene, config, seed) →\n byte-identical SHA-256 across runs, machines, and transports.
\n Click the Witness tab and press Verify witness . The\n dashboard re-derives the hash for the canonical reference scene\n (seed=42, N=256) and asserts it matches the constant\n pinned at compile time\n (cc8de9b01b0ff5bd…).
\n A green check means every constant — γ_e, D_GS, μ₀, T₂*, contrast,\n the PRNG stream, the frame layout — is byte-identical to the published\n reference. A red ✗ means something drifted; the dashboard names which.
`,\n },\n {\n icon: '🎚',\n title: 'Tunables — change the simulation live',\n body: `The left sidebar's Tunables panel has four sliders:
\n \n Sample rate (1–100 kHz) — digitiser frame rate \n Lock-in f_mod (0.1–5 kHz) — microwave modulation freq \n Integration t (0.1–10 ms) — per-sample integration time \n Shot noise (on/off) — toggle quantum noise \n \n Edits debounce 300 ms then rebuild the WASM pipeline without restarting\n the frame stream. Watch the noise floor and B-vector spread change\n in the Signal trace.
`,\n },\n {\n icon: '👻',\n title: 'Ghost Murmur — research view',\n body: `Click the ghost icon in the left rail. This view audits the\n publicly-reported April 2026 CIA Ghost Murmur NV-diamond\n heartbeat-detection program against the open physics literature.
\n Includes a \"Try it yourself\" sandbox: place a cardiac dipole at\n any distance from the sensor, hit Run, and see what the real nvsim\n pipeline recovers. Per-tier detectability bars compare the predicted\n signal vs each transport's noise floor (NV-ensemble lab, COTS DNV-B1,\n SQUID, 60 GHz mmWave, WiFi CSI).
\n \n Spoiler: at 1 km the cardiac MCG is ~10⁻¹² of its 10 cm value.\n Press claims of 40-mile detection sit far below any published instrument's\n floor.
`,\n },\n {\n icon: '🛍',\n title: 'App Store — 65 edge apps',\n body: `Click the grid icon. The App Store catalogues every\n hot-loadable WASM edge module RuView ships, organised by category:\n medical, security, smart-building, retail, industrial, signal,\n learning, autonomy, exotic.
\n Each card carries id / category / status / event IDs / compute budget /\n ADR back-reference. The toggle marks an app active in this session;\n the WS transport (when configured) pushes the activation set to a\n connected ESP32 mesh.
\n \n Try searching for \"ghost\", \"heart\", or \"occupancy\" to fuzzy-filter\n the catalogue.
`,\n },\n {\n icon: '⌨',\n title: 'Console + REPL',\n body: `The bottom panel is a structured event log with five filter tabs\n (all / info / warn / err / dbg ) plus a REPL prompt.
\n REPL commands include\n help, scene.list, sensor.config,\n run, pause, seed [hex],\n proof.verify, proof.export,\n theme [light|dark], status, clear.
\n \n Press / to focus the REPL from anywhere. Arrow ↑/↓ recall\n history (persisted across reloads). ⌘K opens the command\n palette with every action discoverable.
`,\n },\n {\n icon: '🚀',\n title: 'You are ready',\n body: `That's the whole tour. A few last pointers:
\n \n Press ? any time to open the help center\n (Quickstart / Glossary / FAQ / Shortcuts / About). \n Press ⌘K for the command palette. \n Press \\` to toggle the debug HUD. \n Settings (⌘, ) lets you switch theme, density, motion,\n transport, and replay this tour. \n \n \n Source: github.com/ruvnet/RuView · Apache-2.0 OR MIT ·\n ADRs 089/090/091/092/093.
`,\n cta: { label: 'Get started →' },\n },\n];\n\n@customElement('nv-onboarding')\nexport class NvOnboarding extends LitElement {\n @state() private open = false;\n @state() private step = 0;\n\n static styles = css`\n :host {\n position: fixed; inset: 0;\n background: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(4px);\n z-index: 240;\n display: grid; place-items: center;\n opacity: 0; pointer-events: none;\n transition: opacity 0.18s;\n }\n :host([open]) { opacity: 1; pointer-events: auto; }\n .card {\n background: var(--bg-1);\n border: 1px solid var(--line-2);\n border-radius: var(--radius);\n box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);\n width: min(640px, 94vw);\n max-height: 86vh;\n display: flex; flex-direction: column;\n transform: translateY(12px) scale(0.98);\n transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);\n overflow: hidden;\n }\n :host([open]) .card { transform: translateY(0) scale(1); }\n .h {\n padding: 22px 26px 12px;\n display: flex; align-items: flex-start; gap: 14px;\n }\n .h .icon {\n width: 44px; height: 44px;\n border-radius: 12px;\n background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);\n display: grid; place-items: center;\n font-size: 22px;\n flex-shrink: 0;\n box-shadow: 0 4px 12px -2px oklch(0.55 0.16 30 / 0.35);\n }\n .h .title-wrap { flex: 1; min-width: 0; }\n .h h2 {\n margin: 0;\n font-size: 18px;\n letter-spacing: -0.01em;\n color: var(--ink);\n }\n .h .step-label {\n font-family: var(--mono);\n font-size: 10.5px;\n color: var(--ink-3);\n margin-top: 4px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n }\n .h .skip {\n width: 28px; height: 28px;\n background: transparent;\n border: 1px solid var(--line);\n border-radius: 6px;\n color: var(--ink-2);\n cursor: pointer;\n flex-shrink: 0;\n }\n .h .skip:hover { color: var(--ink); border-color: var(--line-2); }\n .body {\n padding: 0 26px 16px;\n font-size: 13px;\n color: var(--ink-2);\n line-height: 1.6;\n overflow-y: auto;\n flex: 1;\n }\n .body p { margin: 0 0 12px; }\n .body p:last-child { margin-bottom: 0; }\n .body code, .body kbd {\n font-family: var(--mono);\n font-size: 11.5px;\n padding: 1px 5px;\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: 4px;\n }\n .body code { color: var(--accent); }\n .body kbd { color: var(--ink); }\n .hint {\n margin: 14px 0 0;\n padding: 10px 12px;\n background: oklch(0.78 0.12 195 / 0.06);\n border: 1px solid oklch(0.78 0.12 195 / 0.25);\n border-radius: 8px;\n font-size: 12px;\n color: var(--accent-2);\n display: flex; gap: 8px; align-items: flex-start;\n }\n .hint::before {\n content: '💡';\n flex-shrink: 0;\n }\n .footer {\n display: flex; align-items: center; gap: 14px;\n padding: 14px 22px;\n border-top: 1px solid var(--line);\n background: var(--bg-1);\n }\n .progress { flex: 1; }\n .dots { display: flex; gap: 5px; margin-bottom: 4px; }\n .dot {\n width: 6px; height: 6px; border-radius: 50%;\n background: var(--bg-3);\n border: 1px solid var(--line-2);\n transition: background 0.15s, border-color 0.15s, transform 0.15s;\n }\n .dot.active {\n background: var(--accent);\n border-color: var(--accent);\n transform: scale(1.2);\n }\n .dot.done {\n background: var(--accent-4);\n border-color: var(--accent-4);\n }\n .progress-label {\n font-family: var(--mono);\n font-size: 10px;\n color: var(--ink-3);\n }\n button.primary, button.ghost {\n padding: 9px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n border: 1px solid var(--line);\n background: var(--bg-2);\n color: var(--ink);\n }\n button.ghost:hover { border-color: var(--line-2); }\n button.primary {\n background: var(--accent);\n border-color: var(--accent);\n color: #1a0f00;\n }\n button.primary:hover { filter: brightness(1.08); }\n `;\n\n override async connectedCallback(): Promise {\n super.connectedCallback();\n window.addEventListener('nv-show-tour', this.show as EventListener);\n const seen = await kvGet('onboarding-seen');\n if (!seen) {\n this.open = true;\n this.setAttribute('open', '');\n }\n }\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('nv-show-tour', this.show as EventListener);\n }\n\n private show = (): void => {\n this.step = 0;\n this.open = true;\n this.setAttribute('open', '');\n };\n\n private async dismiss(): Promise {\n this.open = false;\n this.removeAttribute('open');\n await kvSet('onboarding-seen', true);\n }\n\n private next(): void {\n const s = STEPS[this.step];\n s.cta?.run?.();\n if (this.step < STEPS.length - 1) this.step++;\n else void this.dismiss();\n }\n\n private prev(): void {\n if (this.step > 0) this.step--;\n }\n\n override render() {\n const s = STEPS[this.step];\n const isLast = this.step === STEPS.length - 1;\n return html`\n \n
\n
${s.icon}
\n
\n
${s.title} \n
Step ${this.step + 1} of ${STEPS.length}
\n
\n
this.dismiss()} aria-label=\"Skip tour\" title=\"Skip tour\">× \n
\n
\n
\n ${s.hint ? html`
${s.hint}
` : ''}\n
\n \n
\n `;\n }\n}\n","/* Ghost Murmur — research view.\n *\n * Walks through the publicly-reported April 2026 CIA program and maps\n * the physically-defensible parts onto RuView's three-tier heartbeat\n * mesh. Source: docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md\n *\n * This view is reference material, not an operational mode. It exists\n * so practitioners (and journalists) can audit the physics-vs-press\n * gap in the open. ADR-092 §14b.\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { getClient, pushLog } from '../store/appStore';\nimport type { TransientRunResult } from '../transport/NvsimClient';\n\n// Tier detection thresholds — order-of-magnitude floor each transport\n// can resolve cardiac signal at, in Tesla. Source: Ghost Murmur spec\n// §4.7, Wolf 2015, Barry 2020. These are deliberately optimistic for the\n// \"available\" path; the shoot-the-moon press claim sits 6+ orders below.\nconst TIERS = [\n { id: 'nvBest', label: 'NV-ensemble (best lab)', floorT: 1e-12, color: 'oklch(0.78 0.14 70)' },\n { id: 'nvCots', label: 'NV-DNV-B1 (COTS)', floorT: 3e-10, color: 'oklch(0.72 0.18 50)' },\n { id: 'squid', label: 'SQUID (shielded room)', floorT: 1e-15, color: 'oklch(0.78 0.12 195)' },\n { id: 'mmw', label: '60 GHz mmWave (μ-Doppler)', floorT: 0, color: 'oklch(0.78 0.14 145)' },\n { id: 'csi', label: 'WiFi CSI (presence)', floorT: 0, color: 'oklch(0.72 0.18 330)' },\n];\n\n// Cardiac dipole moment (A·m²) — order-of-magnitude estimate from\n// Wikswo / Bison cardiac MCG modelling.\nconst HEART_DIPOLE_AM2 = 5e-9;\n\n@customElement('nv-ghost-murmur')\nexport class NvGhostMurmur extends LitElement {\n @state() private distanceM = 0.1;\n @state() private momentLog10 = -8.3; // log10(5e-9)\n @state() private result: TransientRunResult | null = null;\n @state() private running = false;\n @state() private err: string | null = null;\n static styles = css`\n :host {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);\n padding: 24px 28px 60px;\n }\n h1 {\n margin: 0 0 4px;\n font-size: 22px;\n letter-spacing: -0.02em;\n color: var(--ink);\n }\n .subtitle {\n color: var(--ink-3);\n font-size: 13px;\n margin-bottom: 22px;\n }\n .links {\n display: flex; flex-wrap: wrap; gap: 6px;\n margin-bottom: 22px;\n }\n .links a {\n padding: 5px 10px;\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: 999px;\n font-size: 11.5px;\n font-family: var(--mono);\n color: var(--accent-2);\n text-decoration: none;\n }\n .links a:hover { border-color: var(--accent-2); }\n h2 {\n font-size: 14px;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--ink-3);\n margin: 28px 0 10px;\n }\n .grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 12px;\n }\n .card {\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: var(--radius);\n padding: 14px;\n }\n .card h3 {\n margin: 0 0 8px;\n font-size: 13.5px; font-weight: 600;\n color: var(--ink);\n }\n .card p {\n font-size: 12.5px; color: var(--ink-2);\n margin: 0 0 8px;\n line-height: 1.5;\n }\n .card p:last-child { margin-bottom: 0; }\n .stat {\n display: inline-flex; align-items: baseline; gap: 6px;\n margin-right: 10px;\n }\n .stat .v {\n font-family: var(--mono); font-size: 16px; font-weight: 600;\n color: var(--accent);\n }\n .stat .l {\n font-size: 10px; color: var(--ink-3);\n text-transform: uppercase; letter-spacing: 0.04em;\n }\n table {\n width: 100%; border-collapse: collapse;\n font-size: 12.5px;\n }\n th, td {\n padding: 8px 10px;\n text-align: left;\n border-bottom: 1px solid var(--line);\n }\n th {\n color: var(--ink-3);\n font-weight: 600;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n }\n td.amber { color: var(--accent); font-family: var(--mono); }\n td.cyan { color: var(--accent-2); font-family: var(--mono); }\n td.bad { color: var(--bad); font-family: var(--mono); }\n .pill {\n display: inline-block;\n padding: 1px 6px;\n border-radius: 4px;\n font-family: var(--mono);\n font-size: 10px;\n border: 1px solid var(--line);\n }\n .pill.ok { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }\n .pill.skeptical { color: var(--bad); border-color: oklch(0.65 0.22 25 / 0.4); }\n .pill.partial { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }\n .architecture {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--ink-2);\n background: var(--bg-3);\n padding: 16px;\n border-radius: var(--radius-sm);\n border: 1px solid var(--line);\n white-space: pre;\n overflow-x: auto;\n line-height: 1.4;\n }\n .ethics {\n background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.65 0.22 25 / 0.04) 100%);\n border: 1px solid oklch(0.65 0.22 25 / 0.25);\n border-radius: var(--radius);\n padding: 16px;\n }\n .ethics h3 { color: var(--bad); margin-top: 0; }\n .ethics ul { padding-left: 18px; margin: 8px 0; }\n .ethics li { font-size: 12.5px; color: var(--ink-2); margin-bottom: 4px; }\n\n /* Demo */\n .demo {\n background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 70 / 0.04) 100%);\n border: 1px solid oklch(0.78 0.14 70 / 0.3);\n border-radius: var(--radius);\n padding: 18px;\n }\n .demo-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 18px;\n margin-top: 12px;\n }\n @media (max-width: 720px) { .demo-grid { grid-template-columns: 1fr; } }\n .control { margin-bottom: 14px; }\n .control .top {\n display: flex; justify-content: space-between;\n font-size: 12px; margin-bottom: 6px;\n }\n .control .top .lbl { color: var(--ink-3); }\n .control .top .val {\n font-family: var(--mono); color: var(--ink);\n }\n .control input[type=\"range\"] {\n -webkit-appearance: none; appearance: none;\n width: 100%; height: 4px;\n background: var(--bg-3); border-radius: 2px; outline: none;\n }\n .control input[type=\"range\"]::-webkit-slider-thumb {\n -webkit-appearance: none; appearance: none;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--accent); cursor: pointer;\n border: 2px solid var(--bg-2);\n }\n .demo-btn {\n width: 100%;\n padding: 10px;\n border: 1px solid var(--accent);\n background: var(--accent);\n color: #1a0f00;\n border-radius: 8px;\n font-size: 13px; font-weight: 600;\n cursor: pointer;\n }\n .demo-btn:hover { filter: brightness(1.08); }\n .demo-btn:disabled { opacity: 0.6; cursor: progress; }\n .readout {\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: 8px;\n padding: 12px;\n }\n .readout-row {\n display: flex; justify-content: space-between;\n padding: 4px 0;\n font-family: var(--mono); font-size: 12px;\n }\n .readout-row .l { color: var(--ink-3); }\n .readout-row .v { color: var(--ink); }\n .readout-row .v.amber { color: var(--accent); }\n .tier-bar {\n position: relative;\n margin: 6px 0;\n height: 22px;\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: 4px;\n overflow: hidden;\n }\n .tier-bar .fill {\n position: absolute; top: 0; bottom: 0; left: 0;\n transition: width 0.2s ease-out;\n border-right: 2px solid;\n }\n .tier-bar .lbl {\n position: relative; z-index: 1;\n font-family: var(--mono); font-size: 11px;\n padding: 3px 8px;\n color: var(--ink);\n display: flex; justify-content: space-between;\n pointer-events: none;\n }\n .verdict {\n margin-top: 10px;\n padding: 10px 12px;\n border-radius: 8px;\n font-size: 12.5px; font-weight: 500;\n border: 1px solid;\n }\n .verdict.ok { background: oklch(0.78 0.14 145 / 0.08); border-color: oklch(0.78 0.14 145 / 0.4); color: var(--ok); }\n .verdict.warn { background: oklch(0.7 0.18 35 / 0.08); border-color: oklch(0.7 0.18 35 / 0.4); color: var(--warn); }\n .verdict.bad { background: oklch(0.65 0.22 25 / 0.08); border-color: oklch(0.65 0.22 25 / 0.4); color: var(--bad); }\n .demo-notes {\n font-size: 11.5px; color: var(--ink-3);\n margin-top: 10px; line-height: 1.5;\n }\n `;\n\n /**\n * Predicted MCG dipole field (Tesla) at distance r in metres.\n * Far-field approximation: |B| ≈ μ₀ · m / (4π · r³). Source: Jackson 3e §5.\n */\n private predictedDipoleFieldT(r: number, m: number): number {\n const MU_0 = 4 * Math.PI * 1e-7;\n return (MU_0 * m) / (4 * Math.PI * Math.pow(Math.max(r, 1e-6), 3));\n }\n\n private async runDemo(): Promise {\n const c = getClient();\n if (!c) { this.err = 'WASM client not ready'; return; }\n this.err = null;\n this.running = true;\n this.requestUpdate();\n try {\n const r = this.distanceM;\n const m = Math.pow(10, this.momentLog10);\n // Heart proxy at +z = r, dipole moment along z = m A·m².\n const scene = {\n dipoles: [{ position: [0, 0, r] as [number, number, number], moment: [0, 0, m] as [number, number, number] }],\n loops: [],\n ferrous: [],\n eddy: [],\n sensors: [[0, 0, 0] as [number, number, number]],\n ambient_field: [0, 0, 0] as [number, number, number],\n };\n const config = {\n digitiser: { f_s_hz: 10000, f_mod_hz: 1000 },\n sensor: {\n gamma_fwhm_hz: 1.0e6,\n t1_s: 5.0e-3,\n t2_s: 1.0e-6,\n t2_star_s: 200e-9,\n contrast: 0.03,\n n_spins: 1.0e12,\n shot_noise_disabled: false,\n },\n dt_s: null,\n };\n this.result = await c.runTransient(scene, config, 42n, 64);\n pushLog('ok', `ghost-demo · r=${r.toFixed(3)} m · |B| recovered = ${(this.result.bMagT * 1e12).toExponential(2)} pT`);\n } catch (e) {\n this.err = (e as Error).message;\n pushLog('err', `ghost-demo failed: ${this.err}`);\n } finally {\n this.running = false;\n this.requestUpdate();\n }\n }\n\n private formatField(t: number): string {\n if (t === 0) return '0 T';\n const abs = Math.abs(t);\n if (abs >= 1e-3) return `${(t * 1e3).toFixed(2)} mT`;\n if (abs >= 1e-6) return `${(t * 1e6).toFixed(2)} µT`;\n if (abs >= 1e-9) return `${(t * 1e9).toFixed(3)} nT`;\n if (abs >= 1e-12) return `${(t * 1e12).toFixed(2)} pT`;\n if (abs >= 1e-15) return `${(t * 1e15).toFixed(2)} fT`;\n if (abs >= 1e-18) return `${(t * 1e18).toFixed(2)} aT`;\n return `${t.toExponential(2)} T`;\n }\n\n private formatDistance(r: number): string {\n if (r < 1) return `${(r * 100).toFixed(1)} cm`;\n if (r < 1000) return `${r.toFixed(2)} m`;\n if (r < 1e5) return `${(r / 1000).toFixed(2)} km`;\n return `${(r / 1609).toFixed(0)} mi`;\n }\n\n private renderDemo() {\n const m = Math.pow(10, this.momentLog10);\n const predicted = this.predictedDipoleFieldT(this.distanceM, m);\n const recovered = this.result?.bMagT ?? 0;\n const noiseFloor = (this.result?.noiseFloorPtSqrtHz ?? 0) * 1e-12; // pT/√Hz → T/√Hz\n\n const verdictPills = TIERS.map((t) => {\n let detect: 'ok' | 'warn' | 'bad' = 'bad';\n let label = 'below floor';\n if (t.id === 'mmw') {\n if (this.distanceM <= 5) { detect = 'ok'; label = 'µ-Doppler @ chest'; }\n else if (this.distanceM <= 15) { detect = 'warn'; label = 'edge of range'; }\n else { detect = 'bad'; label = 'out of range'; }\n } else if (t.id === 'csi') {\n if (this.distanceM <= 30) { detect = this.distanceM <= 10 ? 'ok' : 'warn'; label = 'presence/breathing'; }\n else { detect = 'bad'; label = 'out of range'; }\n } else if (t.floorT > 0) {\n const ratio = predicted / t.floorT;\n if (ratio > 100) { detect = 'ok'; label = `${ratio.toExponential(1)}× floor`; }\n else if (ratio > 1) { detect = 'warn'; label = `${ratio.toFixed(1)}× floor`; }\n else { detect = 'bad'; label = `${(1 / ratio).toExponential(1)}× too weak`; }\n }\n const fillPct = t.floorT > 0\n ? Math.max(2, Math.min(100, 100 + 12 * Math.log10(predicted / t.floorT)))\n : (t.id === 'mmw' ? Math.max(2, 100 - this.distanceM * 7) : Math.max(2, 100 - this.distanceM * 2));\n return html`\n \n
\n
\n ${t.label} \n ${label} \n
\n
\n `;\n });\n\n const overallDetect: 'ok' | 'warn' | 'bad' =\n predicted > 1e-12 ? 'ok' : predicted > 1e-15 ? 'warn' : 'bad';\n const overallText =\n overallDetect === 'ok'\n ? `Above NV-ensemble lab floor — close-range MCG plausible at ${this.formatDistance(this.distanceM)}.`\n : overallDetect === 'warn'\n ? `Below NV ensemble best, above SQUID — research-grade only at ${this.formatDistance(this.distanceM)}.`\n : `Below every published instrument's noise floor at ${this.formatDistance(this.distanceM)}. Press-release physics.`;\n\n return html`\n \n
Try it yourself \n
\n Place a cardiac dipole at variable distance from the NV sensor. The\n dashboard runs the real nvsim Rust pipeline (compiled to WASM)\n end-to-end and reports what each tier would actually detect. Same\n determinism contract as the rest of the dashboard.\n
\n
\n
\n
\n
\n Distance from sensor \n ${this.formatDistance(this.distanceM)} \n
\n
{ this.distanceM = Math.pow(10, +(e.target as HTMLInputElement).value); }} />\n
\n 10 cm → 100 km log scale\n
\n
\n
\n
\n Heart dipole moment \n ${m.toExponential(2)} A·m² \n
\n
{ this.momentLog10 = +(e.target as HTMLInputElement).value; }} />\n
\n published cardiac MCG ≈ 5×10⁻⁹ A·m²\n
\n
\n
this.runDemo()}>\n ${this.running ? 'Running nvsim…' : '▶ Run nvsim at this distance'}\n \n ${this.err ? html`
Error: ${this.err}
` : ''}\n
\n\n
\n
\n
\n Predicted |B| (1/r³) \n ${this.formatField(predicted)} \n
\n
\n Recovered |B| (nvsim) \n ${this.result ? this.formatField(recovered) : '—'} \n
\n
\n Sensor noise floor \n ${this.result ? this.formatField(noiseFloor) + '/√Hz' : '—'} \n
\n
\n Frames run \n ${this.result?.nFrames ?? '—'} \n
\n
\n Witness (this run) \n ${this.result?.witnessHex.slice(0, 16) ?? '—'}… \n
\n
\n
\n
\n Per-tier detectability\n
\n ${verdictPills}\n
\n
\n
\n
${overallText}
\n
\n The predicted value uses the closed-form magnetic-dipole\n far field |B| = μ₀·m / (4π·r³). The recovered\n value comes from the same Rust pipeline that drives the Witness panel —\n scene → Biot-Savart → NV ensemble → ADC → MagFrame. Use the moment\n slider to ask \"what if the heart were stronger?\". Use the distance\n slider to walk through 10 cm (clinical MCG), 1 m (close approach),\n 10 m (room-scale), 1 km (skeptic's range), and 65 km (the press claim).\n
\n
\n `;\n }\n\n override render() {\n return html`\n Ghost Murmur — open-source reality check \n \n The physics-vs-press audit for the publicly-reported April 2026\n CIA NV-diamond heartbeat detector, and how RuView's existing\n stack maps onto an honest, civilian version of the same idea.\n
\n\n \n\n What the press reported \n \n
\n
The story \n
3 Apr 2026: USAF F-15E pilot \"Dude 44 Bravo\" goes down in southern Iran during the regional exchange and evades for ~2 days.
\n
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.\"
\n
\n
\n
The named tech \n
\"Ghost Murmur\" — Lockheed Skunk Works system using NV defects in synthetic diamond + AI to extract a heartbeat from environmental noise.
\n
Outlets: Newsweek, Scientific American, Military.com, WION, Open The Magazine, Yahoo, Calcalist + HN thread #47679241.
\n
\n
\n
What physicists said \n
Wikswo (Vanderbilt), Orzel (Union College), Roth (Oakland) — all pushing back hard.
\n
\"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 .
\n
\n
\n\n Live demo — nvsim WASM \n ${this.renderDemo()}\n\n Physics reality check \n \n
\n \n Distance Cardiac MCG (peak QRS) vs Earth field (~50 µT) \n \n \n 10 cm 50 pT 10⁹× weaker \n 1 m 50 fT 10¹²× weaker \n 10 m 50 aT 10¹⁵× weaker \n 1 km 5 × 10⁻²³ T 10²⁷× weaker \n 40 mi (65 km) ~10⁻²⁸ T 10³³× weaker \n \n
\n
\n Best published NV-ensemble lab record: 0.9 pT/√Hz [Wolf 2015].\n Best SQUID in a shielded room: ~1 fT/√Hz . To detect a single heartbeat at 10 m\n you'd need ~2 billion× more sensitivity than any published ensemble has ever shown,\n in a magnetically silent environment. 40 miles is press-release physics. \n
\n
\n\n RuView's three-tier mesh — what is actually buildable \n ┌──────────────────────────┐\n │ Tier 3 — NV-diamond │ Range: 0.1–2 m (lab)\n │ magnetometer ring │ Status: nvsim simulator only\n │ (close-confirm) │ Hardware: $$$ (≥$8k DNV-B1)\n └──────────┬───────────────┘\n │\n ┌──────────┴───────────────┐\n │ Tier 2 — 60 GHz FMCW │ Range: 1–10 m HR/BR\n │ mmWave radar mesh │ Status: shipping (ADR-021)\n │ (vital signs, posture) │ Hardware: $15 (MR60BHA2 + ESP32-C6)\n └──────────┬───────────────┘\n │\n ┌──────────┴───────────────┐\n │ Tier 1 — WiFi CSI mesh │ Range: 10–30 m through-wall\n │ (presence, breathing, │ Status: shipping (ADR-014, ADR-029)\n │ pose, intention) │ Hardware: $9 (ESP32-S3 8MB)\n └──────────┬───────────────┘\n │\n ▼\n ┌────────────────────────────────┐\n │ RuvSense multistatic fusion │\n │ + cross-viewpoint attention │\n │ + AETHER re-ID embeddings │\n │ + Cramer-Rao gating │\n └────────────────────────────────┘
\n\n Press claim → RuView equivalent \n \n
\n \n Press claim RuView equivalent today Crate / ADR Honest range \n \n \n \n NV-diamond magnetometry \n Deterministic NV pipeline simulator \n nvsim · ADR-089 \n Simulator only \n \n \n \"AI strips environmental noise\" \n RuvSense multistatic fusion + AETHER \n signal/ruvsense/ · ADR-029 \n Mature \n \n \n Heartbeat at distance \n 60 GHz FMCW HR/BR + WiFi CSI breathing \n vitals · ADR-021 \n 1–5 m HR · 10–30 m presence \n \n \n Long-range localisation \n Multistatic time-of-flight + CRLB \n ruvector/viewpoint/ \n Limited by node spacing \n \n \n 40-mile single-heartbeat detection \n Not feasible at any tier \n — \n Press-release physics \n \n \n
\n
\n\n Build today on $165 \n \n
\n
Bill of materials \n
\n 3 × ESP32-S3 8 MB ($9 ea) \n 3 × PoE injector + cat6 ($6 ea) \n 1 × ESP32-C6 + Seeed MR60BHA2 ($15) \n 1 × Raspberry Pi 5 8 GB ($80) \n 1 × unmanaged GbE switch ($25)\n
\n
Total: $165
\n
\n
\n
Honest performance \n 95% TPR (LOS, 0–15 m) \n ±2 bpm HR (LOS 0–3 m) \n ±1 br/min BR (any mode) \n ~10 cm pose error \n 80–150 ms end-to-end latency \n \n
\n
Determinism \n
Same (scene, config, seed) → byte-identical SHA-256 witness across browsers, OSes, transports.
\n
Reference: cc8de9b01b0ff5bd…
\n
Try the Witness tab on the right — it re-derives the hash live in this browser and compares against the published reference.
\n
\n
\n\n Privacy, ethics, legal \n \n
This is the open-source version. Same physics, opposite governance. \n
\n Civilian opt-in only — search-and-rescue, elder-care, occupancy, ICU vitals. Not surveillance. \n No directional pursuit — no beam-steering, target-following, or remote person-of-interest tracking. \n Data minimisation — fused output is (presence, HR, BR, pose, p_alive); raw streams discarded at the edge. \n PII gates (ADR-040) block identifying biometric streams from leaving the local mesh without consent. \n Adversarial-signal detection flags physically-impossible signal patterns from compromised mesh nodes. \n No export-controlled hardware — RuView targets < $50 COTS. ITAR/EAR sub-THz coherent radars and shielded NV ensembles are out of scope. \n \n
\n RuView is not affiliated with the United States government, the CIA, Lockheed Martin,\n or any classified program. References to \"Ghost Murmur\" in this view refer\n exclusively to the publicly-reported program of that name as covered in the open\n press in April 2026.\n
\n
\n\n Cross-references \n \n
\n ADRs: 014 (signal) · 021 (vitals) · 024 (AETHER) · 027 (MERIDIAN) ·\n 028 (witness audit) · 029 (RuvSense) · 040 (PII gates) · 086 (ESP32 RaBitQ) ·\n 089 (nvsim, Accepted) · 090 (Lindblad, Proposed-conditional) ·\n 091 (sub-THz radar research) · 092 (this dashboard) . \n Primary physics: Cohen 1970 · Bison 2009 · Wolf 2015 · Barry RMP 2020 · Doherty 2013 · Jackson 3e §5.6/§5.8.\n
\n
\n `;\n }\n}\n","/* Help center — single dialog covering Quickstart / Glossary / FAQ /\n * Shortcuts. Opened from the topbar `?` button or by pressing `?` on\n * the keyboard. Self-contained, no external content. */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\n\ntype Section = 'quickstart' | 'glossary' | 'faq' | 'shortcuts' | 'about';\n\ninterface GlossaryItem {\n term: string;\n body: string;\n category: 'physics' | 'rust' | 'ui';\n}\n\nconst GLOSSARY: GlossaryItem[] = [\n { term: 'NV-diamond', category: 'physics', body: 'Nitrogen-vacancy defect in synthetic diamond. The simulator models a 1 mm³ ensemble (~10¹² centers) addressed by 532 nm pump light + a 2.87 GHz microwave drive. Used as a room-temperature magnetometer with shot-noise floor ~1 pT/√Hz at the published lab record.' },\n { term: 'CW-ODMR', category: 'physics', body: 'Continuously-driven optically-detected magnetic resonance. Sweep the microwave frequency around the NV zero-field splitting (D = 2.87 GHz) and watch the photoluminescence dip when the microwave matches the spin transition. The dip splits with applied magnetic field along each of the four ⟨111⟩ NV axes.' },\n { term: 'MagFrame', category: 'rust', body: 'Fixed-layout 60-byte binary record nvsim emits per (sensor × sample). Magic 0xC51A_6E70, version 1, little-endian. Carries timestamp, recovered B vector (pT), per-axis sigma, noise floor, and flag bits for saturation / shot-noise-disabled / heavy-attenuation.' },\n { term: 'Witness', category: 'rust', body: 'SHA-256 hash over the concatenated MagFrame bytes for a canonical reference run (Proof::REFERENCE_SCENE_JSON @ seed=42, N=256). Same inputs → same hash, byte-for-byte, across runs and machines. The dashboard re-derives it in WASM and compares against Proof::EXPECTED_WITNESS_HEX pinned at build time.' },\n { term: 'Determinism gate', category: 'rust', body: 'A pass/fail check: did this build of nvsim produce the expected witness? If yes → every constant (γ_e, D_GS, μ₀, contrast, T₂*, the PRNG stream, the frame layout, the pipeline ordering) is byte-identical to the published reference. If no → something drifted; the dashboard names which.' },\n { term: 'Lock-in demod', category: 'physics', body: 'Multiply the photoluminescence signal by cos(2π·f_mod·t) and low-pass to recover the slowly-varying B-field component. The simulator emulates a lock-in with output gain 2 and a single-pole IIR LP filter; settable via the Tunables panel (f_mod default 1 kHz).' },\n { term: 'Shot-noise floor', category: 'physics', body: 'δB = 1 / (γ_e · C · √(N · t · T₂*)) — the irreducible quantum noise floor for an NV ensemble. With nvsim defaults (N=10¹², C=0.03, T₂*=200 ns): ≈1.18 pT/√Hz. Toggleable via the Tunables panel for \"analytic\" runs without noise.' },\n { term: 'Biot-Savart', category: 'physics', body: 'Closed-form magnetic field at a point from a current loop or a magnetic dipole. The Scene panel\\'s sources (heart proxy, mains loop, ferrous body, eddy current) all reduce to Biot-Savart-style superpositions over the sensor position.' },\n { term: 'Multistatic fusion', category: 'physics', body: 'Combining evidence from multiple sensors at known geometric configurations. RuView\\'s Cramer-Rao-weighted attention over WiFi CSI nodes + 60 GHz radar nodes + (hypothetically) NV nodes; documented in ADR-029 and the Ghost Murmur view.' },\n { term: 'Scene', category: 'ui', body: 'The simulated magnetic environment: a list of sources (dipole, current loop, ferrous body, eddy current) plus one or more sensor positions and an ambient field. The dashboard ships a \"rebar-walkby-01\" reference scene; click \"New scene…\" in the command palette (⌘K) to build your own.' },\n { term: 'Tunables', category: 'ui', body: 'Sliders that change the running pipeline\\'s digitiser config. Each edit debounces 300 ms, then rebuilds the WASM pipeline with the new f_s / f_mod / dt / shot-noise setting. The frame stream picks up the change without a restart.' },\n { term: 'Transport', category: 'ui', body: 'How the dashboard talks to nvsim. Default is WASM — the simulator runs in a Web Worker right here in your browser, no server. The optional WS transport is REST + binary WebSocket against a host-supplied nvsim-server (see ADR-092 §6.2). Toggle in Settings.' },\n { term: 'App Store', category: 'ui', body: 'Catalog of all 65+ hot-loadable WASM edge modules from wifi-densepose-wasm-edge plus the simulators. Each card carries id / category / status / event IDs; the toggle marks an app active in this session and (in WS mode) pushes the activation to a connected ESP32 mesh.' },\n { term: 'Ghost Murmur', category: 'ui', body: 'Research view that audits the publicly-reported April 2026 CIA NV-diamond heartbeat detector against the open physics literature. Includes a live \"Try it yourself\" sandbox where you can place a heart dipole at any distance from the sensor and ask: which transport tier would actually detect it?' },\n];\n\nconst FAQ = [\n {\n q: 'Is this a real simulator or a mockup?',\n a: 'Real. The Rust crate at v2/crates/nvsim is the same code that runs in the browser via WASM. Press Verify witness on the Witness panel — the SHA-256 you see is byte-equivalent to what `cargo test -p nvsim` produces.',\n },\n {\n q: 'Why does my \"Recovered |B|\" sit much higher than \"Predicted |B|\" in the Ghost Murmur demo?',\n a: 'The recovered value reads the simulator\\'s ADC quantization floor, not the actual magnetic signal. With COTS-default sensor noise (~300 pT/√Hz) and 16-bit ADC at ±10 µT FS, anything below ~1 pT vanishes into ~2 nT of digitization residual. That\\'s the lesson — the press claim sits far below this floor at any meaningful range.',\n },\n {\n q: 'Can I run my own scene?',\n a: 'Yes. Press ⌘K to open the command palette and pick \"New scene…\". You get five fields (name, dipole moment, distance, ferrous toggle, mains toggle); the dashboard builds the JSON and pushes it via client.loadScene().',\n },\n {\n q: 'Does any of my data leave the browser?',\n a: 'No. WASM mode is local-only — the worker, the WASM binary, and the IndexedDB persistence all live in your browser. The optional WS transport (off by default) talks to a host of your choosing.',\n },\n {\n q: 'What does the witness mismatch (red ✗) mean?',\n a: 'The current build of nvsim produced a SHA-256 that doesn\\'t match the constant pinned at compile time. Possible causes: a different Rust toolchain, a dependency version drift, a manual edit to a physics constant, or an honest bug. Audit the diff against ADR-089 §5.',\n },\n {\n q: 'Why are the Inspector / Witness rail buttons there if there\\'s already a right-side inspector?',\n a: 'The right-side inspector is the compact live view; the rail buttons open a full-width version with bigger charts, an explainer header, reference-scene metadata cards, and (on Witness) a \"what this verifies\" panel. Both stay in sync — the right rail is for glancing, the main area is for diving in.',\n },\n {\n q: 'Why is there an \"App Store\" if this is a magnetometer simulator?',\n a: 'Because nvsim is one tile in a larger sensing platform. The catalog lists every hot-loadable WASM edge module RuView ships — medical, security, building, retail, industrial, signal, learning, autonomy. The simulators (nvsim today, more in future) are first-class entries in the same catalog.',\n },\n];\n\nconst QUICKSTART = [\n { step: 1, title: 'Hit ▶ Run', body: 'The big amber button in the topbar starts the live frame stream. The pipeline runs ~1.8 kHz on x86_64 WASM, well above the 1 kHz Cortex-A53 acceptance gate.' },\n { step: 2, title: 'Watch the B-vector trace', body: 'The Inspector → Signal tab shows the recovered field per axis updating in real time. The frame strip below it is one bar per ~32-frame batch.' },\n { step: 3, title: 'Verify the witness', body: 'Click the rail Witness button (or REPL: proof.verify). The dashboard re-runs the canonical reference scene and asserts the SHA-256 byte-for-byte.' },\n { step: 4, title: 'Drag a source', body: 'Grab the rebar / heart proxy / mains loop / ferrous door in the scene canvas; positions persist via IndexedDB.' },\n { step: 5, title: 'Tweak the tunables', body: 'Sliders in the left sidebar update the running pipeline (f_s, f_mod, integration time, shot-noise). Changes debounce 300 ms then push to the worker.' },\n { step: 6, title: 'Open the Ghost Murmur view', body: 'The ghost icon in the rail. Move the distance + moment sliders, hit \"Run nvsim at this distance\" — the live demo runs the real Rust pipeline through WASM and shows which transport tier would actually detect.' },\n { step: 7, title: 'Browse the App Store', body: 'The grid icon. 65+ edge apps: medical, security, building, retail, industrial, signal, learning. Toggle to mark active in this session.' },\n];\n\nconst SHORTCUTS = [\n { keys: '⌘K / Ctrl K', label: 'Command palette' },\n { keys: 'Space', label: 'Play / pause pipeline' },\n { keys: '⌘R / Ctrl R', label: 'Reset pipeline (with confirm)' },\n { keys: '⌘, / Ctrl ,', label: 'Settings drawer' },\n { keys: '⌘N / Ctrl N', label: 'New scene' },\n { keys: '⌘E / Ctrl E', label: 'Export proof bundle' },\n { keys: '⌘/ / Ctrl /', label: 'Toggle theme (dark / light)' },\n { keys: '`', label: 'Toggle debug HUD' },\n { keys: '?', label: 'Open this help center' },\n { keys: '1 · 2 · 3', label: 'Switch inspector tab (Signal / Frame / Witness)' },\n { keys: 'Esc', label: 'Close any modal / palette / drawer' },\n { keys: '/', label: 'Focus the REPL prompt' },\n];\n\n@customElement('nv-help')\nexport class NvHelp extends LitElement {\n @state() private open = false;\n @state() private section: Section = 'quickstart';\n @state() private query = '';\n\n static styles = css`\n :host {\n position: fixed; inset: 0;\n background: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(4px);\n z-index: 230;\n display: grid; place-items: center;\n opacity: 0; pointer-events: none;\n transition: opacity 0.18s;\n }\n :host([open]) { opacity: 1; pointer-events: auto; }\n .modal {\n background: var(--bg-1);\n border: 1px solid var(--line-2);\n border-radius: var(--radius);\n box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);\n width: min(880px, 94vw);\n max-height: 86vh;\n display: grid;\n grid-template-columns: 200px 1fr;\n grid-template-rows: auto 1fr auto;\n overflow: hidden;\n transform: translateY(12px) scale(0.98);\n transition: transform 0.22s cubic-bezier(0.2,0.7,0.3,1);\n }\n :host([open]) .modal { transform: translateY(0) scale(1); }\n @media (max-width: 700px) {\n .modal { grid-template-columns: 1fr; grid-template-rows: auto auto 1fr auto; max-height: 92vh; }\n .nav { border-right: 0; border-bottom: 1px solid var(--line); flex-direction: row; overflow-x: auto; }\n .nav button { white-space: nowrap; }\n }\n .h {\n grid-column: 1 / -1;\n padding: 14px 18px;\n border-bottom: 1px solid var(--line);\n display: flex; align-items: center; justify-content: space-between;\n }\n .h .ttl { font-size: 15px; font-weight: 600; }\n .nav {\n border-right: 1px solid var(--line);\n padding: 12px 8px;\n display: flex; flex-direction: column; gap: 2px;\n background: var(--bg-1);\n }\n .nav button {\n text-align: left;\n padding: 8px 12px;\n background: transparent;\n border: 1px solid transparent;\n border-radius: 6px;\n color: var(--ink-3);\n font-size: 12.5px;\n cursor: pointer;\n transition: color 0.15s, background 0.15s;\n }\n .nav button:hover { color: var(--ink); background: var(--bg-2); }\n .nav button.on {\n color: var(--ink); background: var(--bg-3);\n border-color: var(--line-2);\n }\n .body {\n padding: 18px 22px;\n overflow-y: auto;\n font-size: 13px;\n color: var(--ink-2);\n line-height: 1.6;\n }\n .body h2 {\n margin: 0 0 8px;\n font-size: 18px;\n color: var(--ink);\n letter-spacing: -0.01em;\n }\n .body .lead {\n color: var(--ink-3);\n font-size: 12.5px;\n margin: 0 0 14px;\n }\n .body p { margin: 0 0 12px; }\n .body code {\n font-family: var(--mono);\n background: var(--bg-3);\n padding: 1px 5px;\n border-radius: 4px;\n font-size: 11.5px;\n color: var(--accent);\n }\n .body kbd {\n font-family: var(--mono);\n padding: 2px 6px;\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: 4px;\n font-size: 11.5px;\n color: var(--ink);\n }\n .step {\n display: grid;\n grid-template-columns: 32px 1fr;\n gap: 12px;\n padding: 10px 0;\n border-bottom: 1px solid var(--line);\n }\n .step:last-child { border-bottom: 0; }\n .step .num {\n width: 26px; height: 26px;\n border-radius: 50%;\n background: var(--accent);\n color: #1a0f00;\n font-family: var(--mono);\n font-size: 12.5px;\n font-weight: 700;\n display: grid; place-items: center;\n }\n .step .ttl { color: var(--ink); font-weight: 600; font-size: 13.5px; margin-bottom: 2px; }\n .step .body-text { font-size: 12.5px; color: var(--ink-2); line-height: 1.55; }\n .glossary-search {\n width: 100%;\n padding: 8px 12px;\n background: var(--bg-3);\n border: 1px solid var(--line);\n border-radius: 6px;\n font-family: var(--mono);\n font-size: 12.5px;\n color: var(--ink);\n outline: none;\n margin-bottom: 14px;\n }\n .glossary-search:focus { border-color: var(--accent); }\n .term {\n padding: 10px 0;\n border-bottom: 1px solid var(--line);\n }\n .term:last-child { border-bottom: 0; }\n .term .head {\n display: flex; align-items: center; gap: 8px; margin-bottom: 4px;\n }\n .term .name {\n font-family: var(--mono);\n font-size: 13.5px;\n color: var(--accent);\n font-weight: 600;\n }\n .term .badge {\n font-family: var(--mono);\n font-size: 9.5px;\n padding: 1px 6px;\n border-radius: 4px;\n border: 1px solid var(--line);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n }\n .term .badge.physics { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.4); }\n .term .badge.rust { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.4); }\n .term .badge.ui { color: var(--accent-4); border-color: oklch(0.78 0.14 145 / 0.4); }\n .term .body-text {\n font-size: 12.5px;\n color: var(--ink-2);\n line-height: 1.55;\n }\n .faq-item {\n padding: 10px 0;\n border-bottom: 1px solid var(--line);\n }\n .faq-item:last-child { border-bottom: 0; }\n .faq-item .q {\n color: var(--ink);\n font-weight: 600;\n font-size: 13.5px;\n margin-bottom: 4px;\n }\n .faq-item .a { font-size: 12.5px; color: var(--ink-2); line-height: 1.55; }\n .shortcuts {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 8px 16px;\n align-items: baseline;\n }\n .f {\n grid-column: 1 / -1;\n padding: 10px 18px;\n border-top: 1px solid var(--line);\n display: flex; align-items: center; justify-content: space-between;\n font-size: 11.5px; color: var(--ink-3);\n }\n .close {\n width: 28px; height: 28px;\n background: transparent; border: 1px solid var(--line);\n border-radius: 6px;\n color: var(--ink-2);\n cursor: pointer;\n }\n .close:hover { color: var(--ink); border-color: var(--line-2); }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n window.addEventListener('nv-show-help', this.show as EventListener);\n window.addEventListener('nv-show-help-close', this.closeListener);\n window.addEventListener('keydown', this.onKey);\n }\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener('nv-show-help', this.show as EventListener);\n window.removeEventListener('nv-show-help-close', this.closeListener);\n window.removeEventListener('keydown', this.onKey);\n }\n private closeListener = (): void => this.close();\n\n private show = (e: Event): void => {\n const detail = (e as CustomEvent).detail as { section?: Section } | undefined;\n if (detail?.section) this.section = detail.section;\n this.open = true;\n this.setAttribute('open', '');\n };\n private close(): void {\n this.open = false;\n this.removeAttribute('open');\n }\n private onKey = (e: KeyboardEvent): void => {\n const target = e.target as HTMLElement | null;\n const isInput = target?.tagName === 'INPUT' || target?.tagName === 'TEXTAREA';\n if (e.key === '?' && !isInput && !e.ctrlKey && !e.metaKey) {\n e.preventDefault();\n this.show(new CustomEvent('nv-show-help'));\n } else if (e.key === 'Escape' && this.open) {\n this.close();\n }\n };\n\n private filteredGlossary(): GlossaryItem[] {\n if (!this.query.trim()) return GLOSSARY;\n const q = this.query.toLowerCase();\n return GLOSSARY.filter((g) =>\n g.term.toLowerCase().includes(q) || g.body.toLowerCase().includes(q),\n );\n }\n\n private renderQuickstart() {\n return html`\n Quickstart \n Seven taps to get from \"I just opened the dashboard\" to \"I'm running my own scene with verified determinism.\"
\n { window.dispatchEvent(new CustomEvent('nv-show-help-close')); window.dispatchEvent(new CustomEvent('nv-show-tour')); }}>\n ★ Take the interactive 10-step tour\n \n ${QUICKSTART.map((s) => html`\n \n `)}\n `;\n }\n\n private renderGlossary() {\n const items = this.filteredGlossary();\n return html`\n Glossary \n Every piece of jargon in the dashboard, defined in one paragraph each.
\n this.query = (e.target as HTMLInputElement).value} />\n ${items.length === 0\n ? html`No terms match.
`\n : items.map((g) => html`\n \n
\n ${g.term} \n ${g.category} \n
\n
${g.body}
\n
\n `)}\n `;\n }\n\n private renderFaq() {\n return html`\n FAQ \n The questions I was asked twice in the first week of demos.
\n ${FAQ.map((item) => html`\n \n `)}\n `;\n }\n\n private renderShortcuts() {\n return html`\n Keyboard shortcuts \n Everything is reachable without a mouse.
\n \n ${SHORTCUTS.map((s) => html`\n ${s.keys} ${s.label} \n `)}\n
\n `;\n }\n\n private renderAbout() {\n return html`\n About this dashboard \n What you're looking at, in one screen.
\n nvsim is a deterministic forward simulator for nitrogen-vacancy diamond magnetometry.\n The Rust crate at v2/crates/nvsim is the source of truth; this dashboard is a\n Vite + Lit single-page app that ships the crate compiled to WebAssembly inside a Web Worker.
\n The defining commitment is determinism : same (scene, config, seed) →\n byte-identical SHA-256 witness across browsers, OSes, and transports. Press the\n Verify witness button on the Witness tab to assert this live.
\n The codebase is open source (Apache-2.0 OR MIT). Find it on GitHub:\n github.com/ruvnet/RuView. Decisions are documented in ADRs 089 (nvsim),\n 090 (Lindblad extension, conditional), 091 (sub-THz radar research),\n 092 (this dashboard), 093 (UX gap analysis).
\n This dashboard is one of several RuView demos. Sibling demos at\n github.io/RuView/ include the Observatory and Pose Fusion views.
\n `;\n }\n\n override render() {\n return html`\n \n
\n
Help
\n
this.close()}>× \n
\n
\n ${(['quickstart', 'glossary', 'faq', 'shortcuts', 'about'] as Section[]).map((s) => html`\n this.section = s}>\n ${s === 'quickstart' ? '🚀 Quickstart'\n : s === 'glossary' ? '📖 Glossary'\n : s === 'faq' ? '? FAQ'\n : s === 'shortcuts' ? '⌨ Shortcuts'\n : 'ℹ About'}\n \n `)}\n \n
\n ${this.section === 'quickstart' ? this.renderQuickstart()\n : this.section === 'glossary' ? this.renderGlossary()\n : this.section === 'faq' ? this.renderFaq()\n : this.section === 'shortcuts' ? this.renderShortcuts()\n : this.renderAbout()}\n
\n
\n Press ? any time to reopen \n nvsim · Apache-2.0 OR MIT \n
\n
\n `;\n }\n}\n\nexport function showHelp(section?: Section): void {\n window.dispatchEvent(new CustomEvent('nv-show-help', { detail: { section } }));\n}\n","/* Home view — friendly landing surface for new users.\n *\n * The full-power scene + sidebar + inspector + console are intentionally\n * dense; that's the operator surface. Home is for first-time visitors:\n * a single hero CTA, four quick-jump action cards, and a 1-paragraph\n * explanation of what this dashboard is. No jargon above the fold.\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { effect } from '@preact/signals-core';\nimport { running, getClient, witnessVerified, fps, pushLog } from '../store/appStore';\n\nexport type Action = 'scene' | 'apps' | 'witness' | 'ghost-murmur' | 'help' | 'tour';\n\n@customElement('nv-home')\nexport class NvHome extends LitElement {\n static styles = css`\n :host {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);\n padding: 28px clamp(16px, 6vw, 56px) 60px;\n }\n .hero {\n max-width: 800px;\n margin: 16px auto 28px;\n text-align: center;\n }\n .hero .icon {\n width: 56px; height: 56px;\n margin: 0 auto 18px;\n border-radius: 14px;\n background: linear-gradient(135deg, oklch(0.78 0.14 70) 0%, oklch(0.55 0.16 30) 100%);\n display: grid; place-items: center;\n font-family: var(--mono);\n font-weight: 700;\n font-size: 18px;\n color: #1a0f00;\n box-shadow: 0 8px 24px -6px oklch(0.55 0.16 30 / 0.4);\n }\n .hero h1 {\n margin: 0 0 8px;\n font-size: clamp(24px, 4vw, 34px);\n letter-spacing: -0.02em;\n color: var(--ink);\n line-height: 1.15;\n }\n .hero .tag {\n font-size: clamp(13px, 1.6vw, 15px);\n color: var(--ink-2);\n margin: 0 0 22px;\n line-height: 1.55;\n }\n .hero .ctas {\n display: flex; flex-wrap: wrap; gap: 8px;\n justify-content: center;\n }\n .cta {\n padding: 11px 20px;\n border-radius: 10px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n font-family: inherit;\n border: 1px solid var(--line);\n background: var(--bg-2);\n color: var(--ink);\n transition: transform 0.12s, border-color 0.12s, filter 0.12s;\n }\n .cta:hover { transform: translateY(-1px); border-color: var(--line-2); }\n .cta.primary {\n background: var(--accent);\n border-color: var(--accent);\n color: #1a0f00;\n }\n .cta.primary:hover { filter: brightness(1.08); }\n .status {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 6px 12px;\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: 999px;\n font-size: 12px;\n font-family: var(--mono);\n color: var(--ink-2);\n margin-top: 18px;\n }\n .status .dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--ink-3);\n }\n .status.live .dot {\n background: var(--ok);\n box-shadow: 0 0 8px var(--ok);\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 50% { opacity: 0.5; } }\n\n .grid {\n max-width: 980px;\n margin: 36px auto 0;\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));\n gap: 14px;\n }\n .card {\n background: var(--bg-2);\n border: 1px solid var(--line);\n border-radius: var(--radius);\n padding: 18px 20px;\n cursor: pointer;\n transition: transform 0.12s, border-color 0.12s, background 0.12s;\n display: flex; flex-direction: column; gap: 6px;\n text-align: left;\n color: inherit;\n }\n .card:hover {\n transform: translateY(-2px);\n border-color: var(--accent);\n background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.78 0.14 70 / 0.04) 100%);\n }\n .card .ico {\n font-size: 22px;\n line-height: 1;\n margin-bottom: 4px;\n }\n .card h3 {\n margin: 0;\n font-size: 14.5px;\n font-weight: 600;\n color: var(--ink);\n letter-spacing: -0.01em;\n }\n .card p {\n margin: 0;\n font-size: 12.5px;\n color: var(--ink-2);\n line-height: 1.55;\n }\n .card .arrow {\n color: var(--accent);\n font-family: var(--mono);\n font-size: 11.5px;\n margin-top: 6px;\n }\n\n .footnote {\n max-width: 800px;\n margin: 36px auto 0;\n text-align: center;\n font-size: 12px;\n color: var(--ink-3);\n line-height: 1.55;\n }\n .footnote code {\n font-family: var(--mono);\n background: var(--bg-3);\n padding: 1px 5px;\n border-radius: 4px;\n color: var(--accent);\n font-size: 11px;\n }\n .footnote a {\n color: var(--accent-2);\n text-decoration: underline dotted;\n cursor: pointer;\n }\n `;\n\n override connectedCallback(): void {\n super.connectedCallback();\n effect(() => { running.value; witnessVerified.value; fps.value; this.requestUpdate(); });\n }\n\n private go(action: Action): void {\n if (action === 'tour') { window.dispatchEvent(new CustomEvent('nv-show-tour')); return; }\n if (action === 'help') { window.dispatchEvent(new CustomEvent('nv-show-help')); return; }\n this.dispatchEvent(new CustomEvent('navigate', { detail: action, bubbles: true, composed: true }));\n }\n\n private async runDemo(): Promise {\n const c = getClient(); if (!c) return;\n if (running.value) return;\n await c.run();\n running.value = true;\n pushLog('ok', 'demo started · streaming MagFrames');\n }\n\n override render() {\n const isRunning = running.value;\n const wasVerified = witnessVerified.value === 'ok';\n return html`\n \n
NV
\n
An open-source quantum-magnetometer simulator, in your browser. \n
\n nvsim runs a real Rust simulator (the same code that\n cargo test\n uses) entirely in WebAssembly. No server, no upload, no telemetry.\n Press the button to start the live magnetic-field simulation, or\n take the 60-second tour first.\n
\n
\n this.runDemo()}>\n ${isRunning ? '✓ Demo running' : '▶ Run the simulation'}\n \n this.go('tour')}>\n ★ Take the 60-second tour\n \n this.go('help')}>\n ? Help center\n \n
\n
\n \n ${isRunning\n ? html`Live · ${fps.value > 0 ? (fps.value / 1000).toFixed(2) + ' kHz' : 'starting…'}${wasVerified ? ' · witness verified ✓' : ''}`\n : html`Idle${wasVerified ? ' · witness verified ✓' : ''}`}\n
\n
\n\n \n
this.go('scene')}\n @keydown=${(e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.go('scene'); } }}>\n
🌐
\n
Live scene \n
Drag magnetic sources, watch the recovered field update in real time, and tweak sample rate / noise / integration.
\n
Open scene →
\n
\n\n
this.go('apps')}\n @keydown=${(e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.go('apps'); } }}>\n
🛍
\n
App Store · 66 edge apps \n
Browse 65 hot-loadable WASM sensing modules across medical, security, building, retail, industrial, learning. Six run live in the browser.
\n
Browse the catalogue →
\n
\n\n
this.go('witness')}\n @keydown=${(e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.go('witness'); } }}>\n
✓
\n
Determinism gate \n
Re-derive the SHA-256 witness for the canonical reference scene right here in your browser. Same inputs → same hash, every time.
\n
Verify the witness →
\n
\n\n
this.go('ghost-murmur')}\n @keydown=${(e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.go('ghost-murmur'); } }}>\n
👻
\n
Ghost Murmur reality check \n
Audit the publicly-reported April 2026 CIA NV-diamond program against published physics. Live distance/moment sliders.
\n
Read the spec →
\n
\n
\n\n \n `;\n }\n}\n","/* Top-level shell: 4-zone grid with rail / topbar / sidebar / scene / inspector / console.\n * View routing is per-rail-button: the central area swaps between\n * ``, ``, etc. */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport './nv-rail';\nimport './nv-topbar';\nimport './nv-sidebar';\nimport './nv-scene';\nimport './nv-inspector';\nimport './nv-console';\nimport './nv-app-store';\nimport './nv-toast';\nimport './nv-modal';\nimport './nv-palette';\nimport './nv-debug-hud';\nimport './nv-settings-drawer';\nimport './nv-onboarding';\nimport './nv-ghost-murmur';\nimport './nv-help';\nimport './nv-home';\n\nexport type View = 'home' | 'scene' | 'apps' | 'inspector' | 'witness' | 'ghost-murmur';\n\n@customElement('nv-app')\nexport class NvApp extends LitElement {\n @state() private view: View = 'home';\n\n static styles = css`\n :host {\n display: block;\n height: 100vh;\n width: 100vw;\n background: var(--bg-0);\n }\n .skip-link {\n position: absolute;\n top: -40px;\n left: 8px;\n padding: 6px 12px;\n background: var(--accent);\n color: #1a0f00;\n border-radius: 6px;\n font-size: 12.5px;\n font-weight: 600;\n text-decoration: none;\n z-index: 1000;\n transition: top 0.15s;\n }\n .skip-link:focus { top: 8px; }\n .app {\n display: grid;\n grid-template-columns: 56px 280px 1fr 340px;\n grid-template-rows: 48px 1fr 220px;\n grid-template-areas:\n 'rail topbar topbar topbar'\n 'rail sidebar main inspector'\n 'rail sidebar console inspector';\n height: 100vh;\n width: 100vw;\n }\n /* Home view simplifies: hides sidebar / inspector / console so the\n hero gets the full screen. Power-user panels stay one rail click away. */\n .app.simple {\n grid-template-columns: 56px 1fr;\n grid-template-rows: 48px 1fr;\n grid-template-areas:\n 'rail topbar'\n 'rail main';\n }\n .app.simple nv-sidebar,\n .app.simple nv-inspector,\n .app.simple nv-console { display: none; }\n nv-rail { grid-area: rail; }\n nv-topbar { grid-area: topbar; }\n nv-sidebar { grid-area: sidebar; }\n .main { grid-area: main; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n nv-inspector { grid-area: inspector; }\n nv-console { grid-area: console; min-height: 0; }\n @media (max-width: 1180px) {\n .app {\n grid-template-columns: 56px 1fr 320px;\n grid-template-areas:\n 'rail topbar topbar'\n 'rail main inspector'\n 'rail console console';\n }\n nv-sidebar { display: none; }\n }\n @media (max-width: 860px) {\n .app {\n grid-template-columns: 1fr;\n grid-template-rows: 52px 1fr 200px;\n grid-template-areas:\n 'topbar'\n 'main'\n 'console';\n }\n nv-rail, nv-sidebar, nv-inspector { display: none; }\n }\n `;\n\n override render() {\n const isSimple = this.view === 'home';\n return html`\n { e.preventDefault(); const sr = this.shadowRoot; sr?.querySelector('.main')?.focus(); }}>\n Skip to main content\n \n \n ) => (this.view = e.detail)}> \n \n \n \n ${this.view === 'home'\n ? html` `\n : this.view === 'apps'\n ? html` `\n : this.view === 'ghost-murmur'\n ? html` `\n : this.view === 'inspector'\n ? html` `\n : this.view === 'witness'\n ? html` `\n : html` `}\n \n \n \n \n
\n \n \n \n \n \n \n \n `;\n }\n}\n","/* Common NvsimClient interface — both WasmClient and WsClient implement it.\n * Dashboard binds to this interface and never to a concrete client.\n * Aligns with ADR-092 §5.2.\n */\n\nexport interface PipelineConfigJson {\n digitiser?: {\n f_s_hz: number;\n f_mod_hz: number;\n lp_cutoff_hz?: number;\n };\n sensor?: {\n gamma_fwhm_hz?: number;\n t1_s?: number;\n t2_s?: number;\n t2_star_s?: number;\n contrast?: number;\n n_spins?: number;\n n_centers?: number;\n shot_noise_disabled?: boolean;\n };\n dt_s?: number | null;\n}\n\nexport interface SceneJson {\n dipoles: { position: [number, number, number]; moment: [number, number, number] }[];\n loops: {\n centre: [number, number, number];\n normal: [number, number, number];\n radius: number;\n current: number;\n n_segments: number;\n }[];\n ferrous: {\n position: [number, number, number];\n volume: number;\n susceptibility: number;\n }[];\n eddy: unknown[];\n sensors: [number, number, number][];\n ambient_field: [number, number, number];\n}\n\nexport interface MagFrameRecord {\n magic: number;\n version: number;\n flags: number;\n sensorId: number;\n tUs: bigint;\n bPt: [number, number, number];\n sigmaPt: [number, number, number];\n noiseFloorPtSqrtHz: number;\n temperatureK: number;\n raw: Uint8Array;\n}\n\nexport interface MagFrameBatch {\n frames: MagFrameRecord[];\n bytes: Uint8Array;\n}\n\nexport type NvsimEvent =\n | { type: 'log'; level: 'info' | 'warn' | 'err' | 'dbg' | 'ok'; msg: string }\n | { type: 'witness'; hex: string }\n | { type: 'fps'; value: number }\n | { type: 'state'; running: boolean; t: number; framesEmitted: number };\n\nexport interface RunOpts { frames?: number }\n\n/** One-shot pipeline run for \"what would the sensor recover at this scene?\"\n * use cases. Doesn't disturb the running pipeline. */\nexport interface TransientRunResult {\n bRecoveredT: [number, number, number];\n bMagT: number;\n noiseFloorPtSqrtHz: number;\n sigmaPt: [number, number, number];\n nFrames: number;\n witnessHex: string;\n}\n\nexport interface NvsimClient {\n loadScene(scene: SceneJson): Promise;\n setConfig(cfg: PipelineConfigJson): Promise;\n setSeed(seed: bigint): Promise;\n reset(): Promise;\n run(opts?: RunOpts): Promise;\n pause(): Promise;\n step(direction: 'fwd' | 'back', dtMs: number): Promise;\n\n onFrames(cb: (batch: MagFrameBatch) => void): void;\n onEvent(cb: (ev: NvsimEvent) => void): void;\n\n generateWitness(samples: number): Promise;\n verifyWitness(expected: Uint8Array): Promise<{ ok: true } | { ok: false; actual: Uint8Array }>;\n exportProofBundle(): Promise;\n runTransient(scene: SceneJson, config: PipelineConfigJson, seed: bigint, samples: number): Promise;\n\n buildId(): Promise;\n close(): Promise;\n}\n\n/** Parse one MagFrame from a 60-byte slice. Layout matches `nvsim::frame`. */\nexport function parseMagFrame(view: DataView, offset: number, raw: Uint8Array): MagFrameRecord {\n // v1 layout: magic(u32) | version(u16) | flags(u16) | sensor_id(u16) | _reserved(u16) |\n // t_us(u64) | b_pt[3](f32) | sigma_pt[3](f32) | noise_floor_pt_sqrt_hz(f32) |\n // temperature_k(f32) — 60 bytes total. All little-endian.\n const magic = view.getUint32(offset + 0, true);\n const version = view.getUint16(offset + 4, true);\n const flags = view.getUint16(offset + 6, true);\n const sensorId = view.getUint16(offset + 8, true);\n // skip 2 bytes reserved at offset+10\n const tUs = view.getBigUint64(offset + 12, true);\n const bx = view.getFloat32(offset + 20, true);\n const by = view.getFloat32(offset + 24, true);\n const bz = view.getFloat32(offset + 28, true);\n const sx = view.getFloat32(offset + 32, true);\n const sy = view.getFloat32(offset + 36, true);\n const sz = view.getFloat32(offset + 40, true);\n const noiseFloorPtSqrtHz = view.getFloat32(offset + 44, true);\n const temperatureK = view.getFloat32(offset + 48, true);\n return {\n magic,\n version,\n flags,\n sensorId,\n tUs,\n bPt: [bx, by, bz],\n sigmaPt: [sx, sy, sz],\n noiseFloorPtSqrtHz,\n temperatureK,\n raw: raw.subarray(offset, offset + 60),\n };\n}\n\nexport function parseFrameBatch(bytes: Uint8Array): MagFrameRecord[] {\n const frameSize = 60;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const out: MagFrameRecord[] = [];\n for (let off = 0; off + frameSize <= bytes.byteLength; off += frameSize) {\n out.push(parseMagFrame(view, off, bytes));\n }\n return out;\n}\n","/* Default `NvsimClient` implementation. Talks to the Web Worker that\n * hosts the nvsim WASM module. ADR-092 §5.4 + §6.3. */\n\nimport {\n type NvsimClient,\n type SceneJson,\n type PipelineConfigJson,\n type RunOpts,\n type MagFrameBatch,\n type NvsimEvent,\n type TransientRunResult,\n parseFrameBatch,\n} from './NvsimClient';\n\ninterface PendingRequest {\n resolve: (v: T) => void;\n reject: (err: Error) => void;\n}\n\nexport interface WasmBootInfo {\n buildVersion: string;\n frameMagic: number;\n frameBytes: number;\n expectedWitnessHex: string;\n}\n\nexport class WasmClient implements NvsimClient {\n private worker: Worker;\n private nextId = 1;\n private pending = new Map>();\n private frameSubs = new Set<(b: MagFrameBatch) => void>();\n private eventSubs = new Set<(e: NvsimEvent) => void>();\n private bootInfo: WasmBootInfo | null = null;\n\n constructor() {\n this.worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });\n this.worker.addEventListener('message', (ev) => this.onMessage(ev));\n this.worker.addEventListener('error', (e) =>\n this.eventSubs.forEach((s) => s({ type: 'log', level: 'err', msg: String(e.message) })),\n );\n }\n\n private onMessage(ev: MessageEvent): void {\n const m = ev.data as { type: string; id?: number; [k: string]: unknown };\n if (m.type === 'frames') {\n const buf = m.batch as ArrayBuffer;\n const bytes = new Uint8Array(buf);\n const frames = parseFrameBatch(bytes);\n const batch: MagFrameBatch = { frames, bytes };\n this.frameSubs.forEach((s) => s(batch));\n const fps = m.fps as number;\n if (fps > 0) {\n this.eventSubs.forEach((s) => s({ type: 'fps', value: fps }));\n }\n return;\n }\n if (m.type === 'state') {\n this.eventSubs.forEach((s) =>\n s({\n type: 'state',\n running: Boolean(m.running),\n t: 0,\n framesEmitted: Number(m.framesEmitted ?? 0),\n }),\n );\n return;\n }\n if (m.type === 'ready') {\n return;\n }\n if (m.type === 'err' && m.id == null) {\n this.eventSubs.forEach((s) =>\n s({ type: 'log', level: 'err', msg: String(m.msg) }),\n );\n return;\n }\n if (typeof m.id === 'number' && this.pending.has(m.id)) {\n const p = this.pending.get(m.id)!;\n this.pending.delete(m.id);\n if (m.type === 'err') p.reject(new Error(String(m.msg)));\n else p.resolve(m);\n }\n }\n\n private rpc(msg: Record, transfer: Transferable[] = []): Promise {\n const id = this.nextId++;\n return new Promise((resolve, reject) => {\n this.pending.set(id, { resolve: resolve as (v: unknown) => void, reject });\n this.worker.postMessage({ ...msg, id }, transfer);\n });\n }\n\n async boot(): Promise {\n if (this.bootInfo) return this.bootInfo;\n // Pass Vite's resolved BASE_URL so the worker can locate /nvsim-pkg/\n // under the same prefix the dashboard is served from (e.g. /RuView/nvsim/\n // on GitHub Pages, \"/\" in dev).\n const base = import.meta.env.BASE_URL ?? '/';\n const r = await this.rpc<{ buildVersion: string; frameMagic: number; frameBytes: number; expectedWitnessHex: string }>(\n { type: 'boot', base },\n );\n this.bootInfo = {\n buildVersion: r.buildVersion,\n frameMagic: r.frameMagic,\n frameBytes: r.frameBytes,\n expectedWitnessHex: r.expectedWitnessHex,\n };\n return this.bootInfo;\n }\n\n async loadScene(scene: SceneJson): Promise {\n await this.rpc({ type: 'setScene', json: JSON.stringify(scene) });\n }\n\n async setConfig(cfg: PipelineConfigJson): Promise {\n await this.rpc({ type: 'setConfig', json: JSON.stringify(cfg) });\n }\n\n async setSeed(seed: bigint): Promise {\n await this.rpc({ type: 'setSeed', seed: Number(seed & 0xFFFFFFFFn) });\n }\n\n async reset(): Promise {\n await this.rpc({ type: 'reset' });\n }\n\n async run(_opts?: RunOpts): Promise {\n await this.rpc({ type: 'run' });\n }\n\n async pause(): Promise {\n await this.rpc({ type: 'pause' });\n }\n\n async step(_direction: 'fwd' | 'back', _dtMs: number): Promise {\n await this.rpc({ type: 'step' });\n }\n\n onFrames(cb: (batch: MagFrameBatch) => void): void { this.frameSubs.add(cb); }\n onEvent(cb: (ev: NvsimEvent) => void): void { this.eventSubs.add(cb); }\n\n async generateWitness(samples: number): Promise {\n const r = await this.rpc<{ witness: ArrayBuffer; hex: string }>({ type: 'witnessGenerate', samples });\n return new Uint8Array(r.witness);\n }\n\n async verifyWitness(expected: Uint8Array): Promise<{ ok: true } | { ok: false; actual: Uint8Array }> {\n const buf = expected.slice().buffer;\n const r = await this.rpc<{ ok: boolean; actual: ArrayBuffer; actualHex: string }>(\n { type: 'witnessVerify', samples: 256, expected: buf },\n [buf],\n );\n if (r.ok) return { ok: true };\n return { ok: false, actual: new Uint8Array(r.actual) };\n }\n\n async runTransient(\n scene: SceneJson,\n config: PipelineConfigJson,\n seed: bigint,\n samples: number,\n ): Promise {\n const r = await this.rpc<{\n bRecoveredT: number[];\n bMagT: number;\n noiseFloorPtSqrtHz: number;\n sigmaPt: number[];\n nFrames: number;\n witnessHex: string;\n }>({\n type: 'runTransient',\n scene: JSON.stringify(scene),\n config: JSON.stringify(config),\n seed: Number(seed & 0xFFFFFFFFn),\n samples,\n });\n return {\n bRecoveredT: [r.bRecoveredT[0], r.bRecoveredT[1], r.bRecoveredT[2]],\n bMagT: r.bMagT,\n noiseFloorPtSqrtHz: r.noiseFloorPtSqrtHz,\n sigmaPt: [r.sigmaPt[0], r.sigmaPt[1], r.sigmaPt[2]],\n nFrames: r.nFrames,\n witnessHex: r.witnessHex,\n };\n }\n\n async exportProofBundle(): Promise {\n // Bundle = REFERENCE_SCENE_JSON + computed witness hex + version. Wraps\n // the same artifacts `Proof::generate` produces natively. ADR-092 §6.1.\n const w = await this.generateWitness(256);\n const hex = Array.from(w).map((b) => b.toString(16).padStart(2, '0')).join('');\n const info = this.bootInfo ?? (await this.boot());\n const manifest = JSON.stringify(\n {\n kind: 'nvsim-proof-bundle',\n version: info.buildVersion,\n seed: '0x0000002A',\n nSamples: 256,\n witness: hex,\n expected: info.expectedWitnessHex,\n ok: hex === info.expectedWitnessHex,\n ts: new Date().toISOString(),\n },\n null,\n 2,\n );\n return new Blob([manifest], { type: 'application/json' });\n }\n\n async buildId(): Promise {\n const r = await this.rpc<{ buildId: string }>({ type: 'buildId' });\n return r.buildId;\n }\n\n async close(): Promise {\n this.worker.terminate();\n }\n}\n","/* WebSocket transport client — talks to a `nvsim-server` Axum host\n * (v2/crates/nvsim-server). REST for control plane, binary WebSocket\n * for the MagFrame stream. Mirrors the WasmClient interface so the\n * dashboard can swap transports at runtime without code changes.\n *\n * ADR-092 §5.2 / §6.2.\n */\n\nimport {\n type NvsimClient,\n type SceneJson,\n type PipelineConfigJson,\n type RunOpts,\n type MagFrameBatch,\n type NvsimEvent,\n type TransientRunResult,\n parseFrameBatch,\n} from './NvsimClient';\n\ninterface HealthBody {\n nvsim_version: string;\n magic: number;\n frame_bytes: number;\n expected_witness_hex: string;\n}\n\ninterface VerifyBody {\n ok: boolean;\n actual_hex: string;\n expected_hex: string;\n}\n\ninterface WitnessBody {\n witness_hex: string;\n samples: number;\n seed_hex: string;\n}\n\nexport interface WsBootInfo {\n buildVersion: string;\n frameMagic: number;\n frameBytes: number;\n expectedWitnessHex: string;\n}\n\n/** Convert a base URL (e.g. `http://host:7878`) to its WebSocket peer (`ws://host:7878`). */\nfunction toWsUrl(baseUrl: string): string {\n if (baseUrl.startsWith('ws://') || baseUrl.startsWith('wss://')) return baseUrl;\n return baseUrl.replace(/^http/, 'ws');\n}\n\nexport class WsClient implements NvsimClient {\n private baseUrl: string;\n private wsUrl: string;\n private ws: WebSocket | null = null;\n private bootInfo: WsBootInfo | null = null;\n private frameSubs = new Set<(b: MagFrameBatch) => void>();\n private eventSubs = new Set<(e: NvsimEvent) => void>();\n private running = false;\n private framesEmitted = 0;\n private fpsLast = performance.now();\n private fpsCount = 0;\n\n /** @param baseUrl e.g. `http://localhost:7878` */\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this.wsUrl = `${toWsUrl(this.baseUrl)}/ws/stream`;\n }\n\n private async json(path: string, init?: RequestInit): Promise {\n const res = await fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: { 'content-type': 'application/json', ...(init?.headers ?? {}) },\n });\n if (!res.ok) throw new Error(`${path}: ${res.status} ${res.statusText}`);\n return (await res.json()) as T;\n }\n\n async boot(): Promise {\n if (this.bootInfo) return this.bootInfo;\n const h = await this.json('/api/health');\n this.bootInfo = {\n buildVersion: h.nvsim_version,\n frameMagic: h.magic,\n frameBytes: h.frame_bytes,\n expectedWitnessHex: h.expected_witness_hex,\n };\n this.openWs();\n return this.bootInfo;\n }\n\n private openWs(): void {\n if (this.ws) return;\n const ws = new WebSocket(this.wsUrl);\n ws.binaryType = 'arraybuffer';\n ws.onopen = () => {\n this.eventSubs.forEach((s) =>\n s({ type: 'log', level: 'ok', msg: `ws/stream connected · ${this.wsUrl}` }),\n );\n };\n ws.onclose = () => {\n this.ws = null;\n this.eventSubs.forEach((s) =>\n s({ type: 'log', level: 'warn', msg: 'ws/stream closed' }),\n );\n };\n ws.onerror = () => {\n this.eventSubs.forEach((s) =>\n s({ type: 'log', level: 'err', msg: `ws/stream error · ${this.wsUrl}` }),\n );\n };\n ws.onmessage = (ev: MessageEvent) => {\n if (!(ev.data instanceof ArrayBuffer)) return;\n const bytes = new Uint8Array(ev.data);\n const frames = parseFrameBatch(bytes);\n if (frames.length === 0) return;\n const batch: MagFrameBatch = { frames, bytes };\n this.frameSubs.forEach((s) => s(batch));\n this.framesEmitted += frames.length;\n this.fpsCount += frames.length;\n const now = performance.now();\n if (now - this.fpsLast >= 1000) {\n const fps = (this.fpsCount * 1000) / (now - this.fpsLast);\n this.eventSubs.forEach((s) => s({ type: 'fps', value: fps }));\n this.fpsLast = now;\n this.fpsCount = 0;\n }\n };\n this.ws = ws;\n }\n\n async loadScene(scene: SceneJson): Promise {\n await this.json('/api/scene', { method: 'PUT', body: JSON.stringify(scene) });\n }\n async setConfig(cfg: PipelineConfigJson): Promise {\n await this.json('/api/config', { method: 'PUT', body: JSON.stringify(cfg) });\n }\n async setSeed(seed: bigint): Promise {\n await this.json('/api/seed', {\n method: 'PUT',\n body: JSON.stringify({ seed_hex: '0x' + seed.toString(16).toUpperCase().padStart(16, '0') }),\n });\n }\n async reset(): Promise {\n await this.json('/api/reset', { method: 'POST' });\n this.running = false;\n this.framesEmitted = 0;\n this.eventSubs.forEach((s) => s({ type: 'state', running: false, t: 0, framesEmitted: 0 }));\n }\n async run(_opts?: RunOpts): Promise {\n await this.json('/api/run', { method: 'POST' });\n this.running = true;\n this.eventSubs.forEach((s) =>\n s({ type: 'state', running: true, t: 0, framesEmitted: this.framesEmitted }),\n );\n }\n async pause(): Promise {\n await this.json('/api/pause', { method: 'POST' });\n this.running = false;\n this.eventSubs.forEach((s) =>\n s({ type: 'state', running: false, t: 0, framesEmitted: this.framesEmitted }),\n );\n }\n async step(direction: 'fwd' | 'back', dtMs: number): Promise {\n await this.json('/api/step', { method: 'POST', body: JSON.stringify({ direction, dt_ms: dtMs }) });\n }\n\n onFrames(cb: (b: MagFrameBatch) => void): void { this.frameSubs.add(cb); }\n onEvent(cb: (e: NvsimEvent) => void): void { this.eventSubs.add(cb); }\n\n async generateWitness(samples: number): Promise {\n const r = await this.json('/api/witness/generate', {\n method: 'POST',\n body: JSON.stringify({ samples }),\n });\n const out = new Uint8Array(32);\n for (let i = 0; i < 32; i++) out[i] = parseInt(r.witness_hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n }\n\n async verifyWitness(expected: Uint8Array): Promise<{ ok: true } | { ok: false; actual: Uint8Array }> {\n const expected_hex = Array.from(expected).map((b) => b.toString(16).padStart(2, '0')).join('');\n const r = await this.json('/api/witness/verify', {\n method: 'POST',\n body: JSON.stringify({ expected_hex, samples: 256 }),\n });\n if (r.ok) return { ok: true };\n const actual = new Uint8Array(32);\n for (let i = 0; i < 32; i++) actual[i] = parseInt(r.actual_hex.slice(i * 2, i * 2 + 2), 16);\n return { ok: false, actual };\n }\n\n async exportProofBundle(): Promise {\n const text = await fetch(`${this.baseUrl}/api/export-proof`, { method: 'POST' }).then((r) => r.text());\n return new Blob([text], { type: 'application/json' });\n }\n\n async runTransient(\n scene: SceneJson,\n config: PipelineConfigJson,\n _seed: bigint,\n samples: number,\n ): Promise {\n // Server doesn't expose a transient route in V1 — the dashboard's\n // Ghost Murmur sandbox falls back to the WASM client when transport\n // is WS. Stub here returns a zero-result so the caller can detect.\n void scene; void config; void samples;\n return {\n bRecoveredT: [0, 0, 0],\n bMagT: 0,\n noiseFloorPtSqrtHz: 0,\n sigmaPt: [0, 0, 0],\n nFrames: 0,\n witnessHex: '(transient route not available in WS transport — V1 limitation)',\n };\n }\n\n async buildId(): Promise {\n const info = this.bootInfo ?? (await this.boot());\n return `nvsim@${info.buildVersion} (ws)`;\n }\n\n async close(): Promise {\n this.ws?.close();\n this.ws = null;\n }\n}\n","/* nvsim dashboard entry — boots the WasmClient, mounts . */\nimport './app.css';\nimport './components/nv-app';\nimport { effect } from '@preact/signals-core';\n\nimport { WasmClient } from './transport/WasmClient';\nimport { WsClient } from './transport/WsClient';\nimport type { NvsimClient, MagFrameBatch } from './transport/NvsimClient';\nimport {\n setClient, transport, wsUrl, connected, transportError,\n theme, density, motionReduced,\n pushLog, expectedWitness, framesEmitted, fps, lastB, bMag,\n pushTrace, pushStripBar, lastFrame, sceneJson, witnessHex,\n replHistory, scenePositions, type SceneItemPos,\n activeAppIds, pushAppEvent,\n} from './store/appStore';\nimport { APP_RUNTIMES, type AppRuntimeContext } from './store/appRuntimes';\nimport { kvGet, kvSet } from './store/persistence';\n\nfunction applyTheme(t: string): void {\n document.documentElement.setAttribute('data-theme', t);\n}\nfunction applyDensity(d: string): void {\n document.body.classList.remove('density-comfy', 'density-default', 'density-compact');\n document.body.classList.add(`density-${d}`);\n}\nfunction applyMotion(reduced: boolean): void {\n document.body.classList.toggle('reduce-motion', reduced);\n}\n\n(async () => {\n // Restore persisted prefs\n const t = (await kvGet<'dark' | 'light'>('theme')) ?? 'dark';\n const d = (await kvGet<'comfy' | 'default' | 'compact'>('density')) ?? 'default';\n const sysMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;\n const m = (await kvGet('motionReduced')) ?? sysMotion;\n theme.value = t; applyTheme(t);\n density.value = d; applyDensity(d);\n motionReduced.value = m; applyMotion(m);\n\n // React to changes → persist\n effect(() => { applyTheme(theme.value); kvSet('theme', theme.value); });\n effect(() => { applyDensity(density.value); kvSet('density', density.value); });\n effect(() => { applyMotion(motionReduced.value); kvSet('motionReduced', motionReduced.value); });\n\n // REPL history + scene drag positions persistence (P0.10, P1.7)\n const histSaved = await kvGet('repl-history');\n if (histSaved && Array.isArray(histSaved)) replHistory.value = histSaved;\n effect(() => { void kvSet('repl-history', replHistory.value); });\n const positionsSaved = await kvGet('scene-positions');\n if (positionsSaved && Array.isArray(positionsSaved)) scenePositions.value = positionsSaved;\n effect(() => { void kvSet('scene-positions', scenePositions.value); });\n\n // Restore WS URL preference + transport mode\n const savedWsUrl = (await kvGet('wsUrl')) ?? '';\n if (savedWsUrl) wsUrl.value = savedWsUrl;\n const savedTransport = (await kvGet<'wasm' | 'ws'>('transport')) ?? 'wasm';\n transport.value = savedTransport;\n effect(() => { void kvSet('wsUrl', wsUrl.value); });\n effect(() => { void kvSet('transport', transport.value); });\n\n // Per-app runtime scratch state + history buffer (defined first so the\n // onFrames callback can close over them).\n const appState: Record> = {};\n const bMagHistory: number[] = [];\n const runtimeStartTs = performance.now();\n\n const onFrames = (batch: MagFrameBatch): void => {\n if (batch.frames.length === 0) return;\n const last = batch.frames[batch.frames.length - 1];\n lastFrame.value = last;\n const bx = last.bPt[0] * 1e-12;\n const by = last.bPt[1] * 1e-12;\n const bz = last.bPt[2] * 1e-12;\n lastB.value = [bx, by, bz];\n const bmagT = Math.sqrt(bx * bx + by * by + bz * bz);\n bMag.value = bmagT;\n pushTrace([bx * 1e9, by * 1e9, bz * 1e9]);\n pushStripBar(Math.min(1, Math.abs(bz * 1e9) / 5 + 0.3));\n bMagHistory.push(bmagT);\n while (bMagHistory.length > 256) bMagHistory.shift();\n\n const activeIds = activeAppIds.value;\n if (activeIds.size === 0) return;\n const elapsedS = (performance.now() - runtimeStartTs) / 1000;\n for (const id of activeIds) {\n const fn = APP_RUNTIMES[id];\n if (!fn) continue;\n if (!appState[id]) appState[id] = {};\n const ctx: AppRuntimeContext = {\n frame: last,\n bMagT: bmagT,\n bRecoveredT: [bx, by, bz],\n bHistory: bMagHistory,\n elapsedS,\n state: appState[id],\n };\n try {\n const result = fn(ctx);\n if (!result) continue;\n const evs = Array.isArray(result) ? result : [result];\n for (const ev of evs) {\n pushAppEvent(ev);\n pushLog('info',\n `[${ev.appId}] ${ev.eventName} (${ev.eventId}) ${ev.detail ? ' · ' + ev.detail : ''}`);\n }\n } catch (e) {\n pushLog('warn', `[${id}] runtime error: ${(e as Error).message}`);\n }\n }\n };\n\n // Boot transport (WASM by default, WS if user previously selected it)\n let activeClient: NvsimClient | null = null;\n async function bootTransport(): Promise {\n try {\n if (activeClient) await activeClient.close();\n const want = transport.value;\n if (want === 'ws' && wsUrl.value.trim()) {\n const c = new WsClient(wsUrl.value.trim());\n const info = await c.boot();\n activeClient = c;\n connected.value = true;\n transportError.value = null;\n expectedWitness.value = info.expectedWitnessHex;\n wireClient(c);\n pushLog('ok', `transport WS · ${wsUrl.value} · nvsim@${info.buildVersion}`);\n } else {\n if (want === 'ws') {\n pushLog('warn', 'WS transport selected but no URL set — falling back to WASM');\n }\n const c = new WasmClient();\n const info = await c.boot();\n activeClient = c;\n connected.value = true;\n transportError.value = null;\n expectedWitness.value = info.expectedWitnessHex;\n wireClient(c);\n pushLog('ok', `transport WASM · nvsim@${info.buildVersion} · magic=0x${info.frameMagic.toString(16).toUpperCase()}`);\n }\n setClient(activeClient);\n } catch (e) {\n const msg = (e as Error).message;\n transportError.value = msg;\n connected.value = false;\n pushLog('err', `transport boot failed: ${msg}`);\n }\n }\n function wireClient(c: NvsimClient): void {\n c.onEvent((ev) => {\n if (ev.type === 'log') pushLog(ev.level, ev.msg);\n if (ev.type === 'fps') fps.value = ev.value;\n if (ev.type === 'state') framesEmitted.value = BigInt(ev.framesEmitted);\n });\n c.onFrames(onFrames);\n }\n\n // React to transport-mode flips: tear down + re-boot.\n let bootInProgress = false;\n effect(() => {\n transport.value; wsUrl.value;\n if (bootInProgress) return;\n bootInProgress = true;\n void bootTransport().finally(() => { bootInProgress = false; });\n });\n\n pushLog('info', 'nvsim — booting transport');\n\n // Initial boot — handled by the effect() above.\n // Auto-verify witness whenever a fresh transport boot completes.\n let verifiedFor: string | null = null;\n effect(() => {\n const exp = expectedWitness.value;\n const isConn = connected.value;\n if (!exp || !isConn) return;\n if (verifiedFor === exp) return;\n verifiedFor = exp;\n void (async () => {\n const c = activeClient;\n if (!c) return;\n try {\n const expBytes = new Uint8Array(32);\n for (let i = 0; i < 32; i++) expBytes[i] = parseInt(exp.slice(i * 2, i * 2 + 2), 16);\n const r = await c.verifyWitness(expBytes);\n if (r.ok) {\n witnessHex.value = exp;\n pushLog('ok', `witness verified · determinism gate ✓ · transport=${transport.value}`);\n } else {\n const actual = Array.from(r.actual).map((b) => b.toString(16).padStart(2, '0')).join('');\n witnessHex.value = actual;\n pushLog('err', `WITNESS MISMATCH · expected ${exp.slice(0, 16)}… got ${actual.slice(0, 16)}…`);\n }\n } catch (e) {\n pushLog('warn', `witness verify skipped: ${(e as Error).message}`);\n }\n })();\n });\n\n sceneJson.value = '(reference scene)';\n})();\n"],"names":["t","e","o","r","n","s","c","NvRail","LitElement","v","html","css","__decorateClass","property","customElement","transport","signal","wsUrl","connected","transportError","running","speed","framesEmitted","seed","fs","fmod","dtMs","noiseEnabled","theme","density","motionReduced","autoUpdate","lastB","bMag","snr","fps","witnessHex","witnessVerified","expectedWitness","lastFrame","traceX","traceY","traceZ","stripBars","sceneName","sceneJson","consolePaused","consoleFilter","replHistory","pushReplHistory","cmd","next","scenePositions","appEvents","appEventCounts","pushAppEvent","ev","activeAppIds","transportLabel","computed","_client","setClient","getClient","consoleLines","MAX_LINES","pushLog","level","msg","pushTrace","b","x","y","z","pushStripBar","amp","NvModal","root","trap","focusables","el","first","last","active","state","openModal","req","NvToast","detail","toast","icon","NvTopbar","effect","cur","inp","raw","seedHex","configPushTimer","pushConfigDebounced","NvSidebar","NvScene","id","item","i","svgEl","pt","it","saved","p","f","bmag","sigmaMax","snrVal","speeds","idx","k","vbX","vbY","bnT","bMagNT","animClass","vbW","vbH","svg","NvInspector","changed","exp","expBytes","actual","make","arr","hasData","bytes","hex","status","cls","label","match","NvConsole","h","body","l","line","args","arg","blob","url","a","filter","visible","ts","tsStr","query","APPS","CATEGORIES","defaultActivations","fuzzyMatch","app","q","score","DB_NAME","DB_VER","STORE","dbPromise","openDb","resolve","reject","db","kvGet","key","kvSet","value","tx","rollingMean","rollingStd","m","vitalTrend","ctx","tail","crossings","cycles","hr","br","evs","occupancy","std","occupied","wasOccupied","intrusion","ambient","exceeds","dwellStart","coherence","recent","baseline","mu","sd","recentMean","adversarial","maxJump","j","exoGhostHunter","maxDev","d","clsName","APP_RUNTIMES","hasRuntime","appId","activations","activeCat","statusFilter","set","NvAppStore","wasActive","note","list","counts","activeCount","activeSimCount","dt","cat","runtime","evCount","runtimeLabel","runtimeTip","NvPalette","name","ferr","mains","scene","eb","items","NvDebugHud","now","simT","NvSettingsDrawer","dbs","STEPS","NvOnboarding","isLast","_","TIERS","NvGhostMurmur","config","abs","predicted","recovered","noiseFloor","verdictPills","detect","ratio","fillPct","overallDetect","overallText","GLOSSARY","FAQ","QUICKSTART","SHORTCUTS","NvHelp","target","isInput","g","NvHome","action","isRunning","wasVerified","NvApp","isSimple","parseMagFrame","view","offset","magic","version","flags","sensorId","tUs","bx","by","bz","sx","sy","sz","noiseFloorPtSqrtHz","temperatureK","parseFrameBatch","out","off","WasmClient","buf","batch","transfer","cfg","_opts","_direction","_dtMs","cb","samples","expected","w","info","manifest","toWsUrl","baseUrl","WsClient","path","init","res","ws","frames","direction","expected_hex","text","_seed","applyTheme","applyDensity","applyMotion","reduced","sysMotion","histSaved","positionsSaved","savedWsUrl","savedTransport","appState","bMagHistory","runtimeStartTs","onFrames","bmagT","activeIds","elapsedS","fn","result","activeClient","bootTransport","want","wireClient","bootInProgress","verifiedFor","isConn"],"mappings":"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BauO,EAMJ,OAAStO;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,IALCC,GAAA,CAAhBuE,EAAA,CAAM,EADI8J,EACM,UAAA,YAAA,CAAA,EACArO,GAAA,CAAhBuE,EAAA,CAAM,EAFI8J,EAEM,UAAA,cAAA,CAAA,EACArO,GAAA,CAAhBuE,EAAA,CAAM,EAHI8J,EAGM,UAAA,SAAA,CAAA,EACArO,GAAA,CAAhBuE,EAAA,CAAM,EAJI8J,EAIM,UAAA,UAAA,CAAA,EACArO,GAAA,CAAhBuE,EAAA,CAAM,EALI8J,EAKM,UAAA,MAAA,CAAA,EALNA,EAANrO,GAAA,CADNE,EAAc,iBAAiB,CAAA,EACnBmO,CAAA,uMClBb,MAAMY,GAA2B,CAC/B,CAAE,KAAM,aAAc,SAAU,UAAW,KAAM,uQAAA,EACjD,CAAE,KAAM,UAAW,SAAU,UAAW,KAAM,iTAAA,EAC9C,CAAE,KAAM,WAAY,SAAU,OAAQ,KAAM,qQAAA,EAC5C,CAAE,KAAM,UAAW,SAAU,OAAQ,KAAM,8SAAA,EAC3C,CAAE,KAAM,mBAAoB,SAAU,OAAQ,KAAM,+RAAA,EACpD,CAAE,KAAM,gBAAiB,SAAU,UAAW,KAAM,oQAAA,EACpD,CAAE,KAAM,mBAAoB,SAAU,UAAW,KAAM,oOAAA,EACvD,CAAE,KAAM,cAAe,SAAU,UAAW,KAAM,0OAAA,EAClD,CAAE,KAAM,qBAAsB,SAAU,UAAW,KAAM,2OAAA,EACzD,CAAE,KAAM,QAAS,SAAU,KAAM,KAAM,6RAAA,EACvC,CAAE,KAAM,WAAY,SAAU,KAAM,KAAM,sOAAA,EAC1C,CAAE,KAAM,YAAa,SAAU,KAAM,KAAM,iQAAA,EAC3C,CAAE,KAAM,YAAa,SAAU,KAAM,KAAM,6QAAA,EAC3C,CAAE,KAAM,eAAgB,SAAU,KAAM,KAAM,wSAAA,CAChD,EAEMC,GAAM,CACV,CACE,EAAG,wCACH,EAAG,+NAAA,EAEL,CACE,EAAG,6FACH,EAAG,uUAAA,EAEL,CACE,EAAG,0BACH,EAAG,sOAAA,EAEL,CACE,EAAG,yCACH,EAAG,iMAAA,EAEL,CACE,EAAG,+CACH,EAAG,0QAAA,EAEL,CACE,EAAG,gGACH,EAAG,2SAAA,EAEL,CACE,EAAG,mEACH,EAAG,qSAAA,CAEP,EAEMC,GAAa,CACjB,CAAE,KAAM,EAAG,MAAO,YAAa,KAAM,8JAAA,EACrC,CAAE,KAAM,EAAG,MAAO,2BAA4B,KAAM,+IAAA,EACpD,CAAE,KAAM,EAAG,MAAO,qBAAsB,KAAM,gKAAA,EAC9C,CAAE,KAAM,EAAG,MAAO,gBAAiB,KAAM,gHAAA,EACzC,CAAE,KAAM,EAAG,MAAO,qBAAsB,KAAM,sJAAA,EAC9C,CAAE,KAAM,EAAG,MAAO,6BAA8B,KAAM,iNAAA,EACtD,CAAE,KAAM,EAAG,MAAO,uBAAwB,KAAM,yIAAA,CAClD,EAEMC,GAAY,CAChB,CAAE,KAAM,gBAAiB,MAAO,iBAAA,EAChC,CAAE,KAAM,QAAS,MAAO,uBAAA,EACxB,CAAE,KAAM,gBAAiB,MAAO,+BAAA,EAChC,CAAE,KAAM,gBAAiB,MAAO,iBAAA,EAChC,CAAE,KAAM,gBAAiB,MAAO,WAAA,EAChC,CAAE,KAAM,gBAAiB,MAAO,qBAAA,EAChC,CAAE,KAAM,gBAAiB,MAAO,6BAAA,EAChC,CAAE,KAAM,IAAK,MAAO,kBAAA,EACpB,CAAE,KAAM,IAAK,MAAO,uBAAA,EACpB,CAAE,KAAM,YAAa,MAAO,iDAAA,EAC5B,CAAE,KAAM,MAAO,MAAO,oCAAA,EACtB,CAAE,KAAM,IAAK,MAAO,uBAAA,CACtB,EAGO,IAAMC,GAAN,cAAqBzP,CAAW,CAAhC,aAAA,CAAA,MAAA,GAAA,SAAA,EACI,KAAQ,KAAO,GACf,KAAQ,QAAmB,aAC3B,KAAQ,MAAQ,GAiNzB,KAAQ,cAAgB,IAAY,KAAK,MAAA,EAEzC,KAAQ,KAAQ,GAAmB,CACjC,MAAM+E,EAAU,EAAkB,OAC9BA,GAAQ,UAAS,KAAK,QAAUA,EAAO,SAC3C,KAAK,KAAO,GACZ,KAAK,aAAa,OAAQ,EAAE,CAC9B,EAKA,KAAQ,MAAS,GAA2B,CAC1C,MAAM2K,EAAS,EAAE,OACXC,EAAUD,GAAQ,UAAY,SAAWA,GAAQ,UAAY,WAC/D,EAAE,MAAQ,KAAO,CAACC,GAAW,CAAC,EAAE,SAAW,CAAC,EAAE,SAChD,EAAE,eAAA,EACF,KAAK,KAAK,IAAI,YAAY,cAAc,CAAC,GAChC,EAAE,MAAQ,UAAY,KAAK,MACpC,KAAK,MAAA,CAET,CAAA,CAjCS,mBAA0B,CACjC,MAAM,kBAAA,EACN,OAAO,iBAAiB,eAAgB,KAAK,IAAqB,EAClE,OAAO,iBAAiB,qBAAsB,KAAK,aAAa,EAChE,OAAO,iBAAiB,UAAW,KAAK,KAAK,CAC/C,CACS,sBAA6B,CACpC,MAAM,qBAAA,EACN,OAAO,oBAAoB,eAAgB,KAAK,IAAqB,EACrE,OAAO,oBAAoB,qBAAsB,KAAK,aAAa,EACnE,OAAO,oBAAoB,UAAW,KAAK,KAAK,CAClD,CASQ,OAAc,CACpB,KAAK,KAAO,GACZ,KAAK,gBAAgB,MAAM,CAC7B,CAYQ,kBAAmC,CACzC,GAAI,CAAC,KAAK,MAAM,KAAA,EAAQ,OAAON,GAC/B,MAAMhG,EAAI,KAAK,MAAM,YAAA,EACrB,OAAOgG,GAAS,OAAQO,GACtBA,EAAE,KAAK,cAAc,SAASvG,CAAC,GAAKuG,EAAE,KAAK,YAAA,EAAc,SAASvG,CAAC,CAAA,CAEvE,CAEQ,kBAAmB,CACzB,OAAOnJ;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,iBAKM,IAAM,CAAE,OAAO,cAAc,IAAI,YAAY,oBAAoB,CAAC,EAAG,OAAO,cAAc,IAAI,YAAY,cAAc,CAAC,CAAG,CAAC;AAAA;AAAA;AAAA,QAGtIqP,GAAW,IAAK1P,GAAMK;AAAAA;AAAAA,6BAEDL,EAAE,IAAI;AAAA;AAAA,+BAEJA,EAAE,KAAK;AAAA,gDACUA,EAAE,IAAI;AAAA;AAAA;AAAA,OAG/C,CAAC;AAAA,KAEN,CAEQ,gBAAiB,CACvB,MAAMiO,EAAQ,KAAK,iBAAA,EACnB,OAAO5N;AAAAA;AAAAA;AAAAA;AAAAA,iBAIM,KAAK,KAAK;AAAA,iBACTT,GAAa,KAAK,MAASA,EAAE,OAA4B,KAAK;AAAA,QACxEqO,EAAM,SAAW,EACf5N,uDACA4N,EAAM,IAAK8B,GAAM1P;AAAAA;AAAAA;AAAAA,qCAGU0P,EAAE,IAAI;AAAA,qCACNA,EAAE,QAAQ,KAAKA,EAAE,QAAQ;AAAA;AAAA,uCAEvBA,EAAE,IAAI;AAAA;AAAA,WAElC,CAAC;AAAA,KAEV,CAEQ,WAAY,CAClB,OAAO1P;AAAAA;AAAAA;AAAAA,QAGHoP,GAAI,IAAKzJ,GAAS3F;AAAAA;AAAAA,2BAEC2F,EAAK,CAAC;AAAA,sCACKA,EAAK,CAAC;AAAA;AAAA,OAErC,CAAC;AAAA,KAEN,CAEQ,iBAAkB,CACxB,OAAO3F;AAAAA;AAAAA;AAAAA;AAAAA,UAIDsP,GAAU,IAAK3P,GAAMK;AAAAA,iBACdL,EAAE,IAAI,eAAeA,EAAE,KAAK;AAAA,SACpC,CAAC;AAAA;AAAA,KAGR,CAEQ,aAAc,CACpB,OAAOK;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,KAgBT,CAES,QAAS,CAChB,OAAOA;AAAAA;AAAAA;AAAAA;AAAAA,iEAIsD,IAAM,KAAK,OAAO;AAAA;AAAA;AAAA,YAGtE,CAAC,aAAc,WAAY,MAAO,YAAa,OAAO,EAAgB,IAAKL,GAAMK;AAAAA,4BAClE,KAAK,UAAYL,EAAI,KAAO,EAAE;AAAA,8BAC5B,KAAK,UAAYA,CAAC;AAAA,uBACzB,IAAM,KAAK,QAAUA,CAAC;AAAA,gBAC7BA,IAAM,aAAe,gBACnBA,IAAM,WAAa,cACnBA,IAAM,MAAQ,QACdA,IAAM,YAAc,cACpB,SAAS;AAAA;AAAA,WAEhB,CAAC;AAAA;AAAA;AAAA,YAGA,KAAK,UAAY,aAAe,KAAK,iBAAA,EACnC,KAAK,UAAY,WAAa,KAAK,eAAA,EACnC,KAAK,UAAY,MAAQ,KAAK,UAAA,EAC9B,KAAK,UAAY,YAAc,KAAK,kBACpC,KAAK,YAAA,CAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQ9B,CACF,EA5Wa4P,GAKJ,OAAStP;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,IAJCC,GAAA,CAAhBuE,EAAA,CAAM,EADI8K,GACM,UAAA,OAAA,CAAA,EACArP,GAAA,CAAhBuE,EAAA,CAAM,EAFI8K,GAEM,UAAA,UAAA,CAAA,EACArP,GAAA,CAAhBuE,EAAA,CAAM,EAHI8K,GAGM,UAAA,QAAA,CAAA,EAHNA,GAANrP,GAAA,CADNE,EAAc,SAAS,CAAA,EACXmP,EAAA,iJCzEN,IAAMI,GAAN,cAAqB7P,CAAW,CA2J5B,mBAA0B,CACjC,MAAM,kBAAA,EACNmF,EAAO,IAAM,CAAEvE,EAAQ,MAAOiB,EAAgB,MAAOF,EAAI,MAAO,KAAK,cAAA,CAAiB,CAAC,CACzF,CAEQ,GAAGmO,EAAsB,CAC/B,GAAIA,IAAW,OAAQ,CAAE,OAAO,cAAc,IAAI,YAAY,cAAc,CAAC,EAAG,MAAQ,CACxF,GAAIA,IAAW,OAAQ,CAAE,OAAO,cAAc,IAAI,YAAY,cAAc,CAAC,EAAG,MAAQ,CACxF,KAAK,cAAc,IAAI,YAAY,WAAY,CAAE,OAAQA,EAAQ,QAAS,GAAM,SAAU,EAAA,CAAM,CAAC,CACnG,CAEA,MAAc,SAAyB,CACrC,MAAMhQ,EAAIwD,EAAA,EAAkBxD,IACxBc,EAAQ,QACZ,MAAMd,EAAE,IAAA,EACRc,EAAQ,MAAQ,GAChB6C,EAAQ,KAAM,oCAAoC,GACpD,CAES,QAAS,CAChB,MAAMsM,EAAYnP,EAAQ,MACpBoP,EAAcnO,EAAgB,QAAU,KAC9C,OAAO3B;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,iEAYsD,IAAM,KAAK,SAAS;AAAA,cACvE6P,EAAY,iBAAmB,sBAAsB;AAAA;AAAA,0DAET,IAAM,KAAK,GAAG,MAAM,CAAC;AAAA;AAAA;AAAA,0DAGrB,IAAM,KAAK,GAAG,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,6BAIlDA,EAAY,OAAS,EAAE;AAAA;AAAA,YAExCA,EACE7P,WAAcyB,EAAI,MAAQ,GAAKA,EAAI,MAAQ,KAAM,QAAQ,CAAC,EAAI,OAAS,WAAW,GAAGqO,EAAc,wBAA0B,EAAE,GAC/H9P,QAAW8P,EAAc,wBAA0B,EAAE,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAMlD,IAAM,KAAK,GAAG,OAAO,CAAC;AAAA,qBACnBvQ,GAAqB,EAAMA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,OAAOA,EAAE,eAAA,EAAkB,KAAK,GAAG,OAAO,EAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAQ7G,IAAM,KAAK,GAAG,MAAM,CAAC;AAAA,qBAClBA,GAAqB,EAAMA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,OAAOA,EAAE,eAAA,EAAkB,KAAK,GAAG,MAAM,EAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAQ5G,IAAM,KAAK,GAAG,SAAS,CAAC;AAAA,qBACrBA,GAAqB,EAAMA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,OAAOA,EAAE,eAAA,EAAkB,KAAK,GAAG,SAAS,EAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAQ/G,IAAM,KAAK,GAAG,cAAc,CAAC;AAAA,qBAC1BA,GAAqB,EAAMA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,OAAOA,EAAE,eAAA,EAAkB,KAAK,GAAG,cAAc,EAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BASzG,IAAM,KAAK,GAAG,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,KAMjD,CACF,EA7PaoQ,GACJ,OAAS1P;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,IADL0P,GAANzP,GAAA,CADNE,EAAc,SAAS,CAAA,EACXuP,EAAA,uMCUN,IAAMI,GAAN,cAAoBjQ,CAAW,CAA/B,aAAA,CAAA,MAAA,GAAA,SAAA,EACI,KAAQ,KAAa,MAAA,CA4ErB,QAAS,CAChB,MAAMkQ,EAAW,KAAK,OAAS,OAC/B,OAAOhQ;AAAAA;AAAAA,iBAEOT,GAAa,CAAEA,EAAE,eAAA,EAA6B,KAAK,YAAgB,cAA2B,OAAO,GAAG,MAAA,CAAS,CAAC;AAAA;AAAA;AAAA,wBAG5GyQ,EAAW,SAAW,EAAE;AAAA,yBACvB,KAAK,IAAI,cAAezQ,GAA0B,KAAK,KAAOA,EAAE,MAAO;AAAA;AAAA;AAAA;AAAA,YAIpF,KAAK,OAAS,OACZS,uBACA,KAAK,OAAS,OACZA,iCACA,KAAK,OAAS,eACZA,uCACA,KAAK,OAAS,YACZA,mCAAsC,QAAQ,mBAC9C,KAAK,OAAS,UACZA,mCAAsC,SAAS,mBAC/CA,wBAA2B;AAAA;AAAA;AAAA,oBAG7B,KAAK,OAAS,YAAc,SAClC,KAAK,OAAS,UAAY,UAAY,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAYtD,CACF,EApHa+P,GAGJ,OAAS9P;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,IAFCC,GAAA,CAAhBuE,EAAA,CAAM,EADIsL,GACM,UAAA,OAAA,CAAA,EADNA,GAAN7P,GAAA,CADNE,EAAc,QAAQ,CAAA,EACV2P,EAAA,EC4EN,SAASE,GAAcC,EAAgBC,EAAgB/K,EAAiC,CAI7F,MAAMgL,EAAQF,EAAK,UAAUC,EAAS,EAAG,EAAI,EACvCE,EAAUH,EAAK,UAAUC,EAAS,EAAG,EAAI,EACzCG,EAAQJ,EAAK,UAAUC,EAAS,EAAG,EAAI,EACvCI,EAAWL,EAAK,UAAUC,EAAS,EAAG,EAAI,EAE1CK,EAAMN,EAAK,aAAaC,EAAS,GAAI,EAAI,EACzCM,EAAKP,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtCO,EAAKR,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtCQ,EAAKT,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtCS,EAAKV,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtCU,EAAKX,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtCW,GAAKZ,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtCY,GAAqBb,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtDa,GAAed,EAAK,WAAWC,EAAS,GAAI,EAAI,EACtD,MAAO,CACL,MAAAC,EACA,QAAAC,EACA,MAAAC,EACA,SAAAC,EACA,IAAAC,EACA,IAAK,CAACC,EAAIC,EAAIC,CAAE,EAChB,QAAS,CAACC,EAAIC,EAAIC,EAAE,EACpB,mBAAAC,GACA,aAAAC,GACA,IAAK5L,EAAI,SAAS+K,EAAQA,EAAS,EAAE,CAAA,CAEzC,CAEO,SAASc,GAAgBxJ,EAAqC,CAEnE,MAAMyI,EAAO,IAAI,SAASzI,EAAM,OAAQA,EAAM,WAAYA,EAAM,UAAU,EACpEyJ,EAAwB,CAAA,EAC9B,QAASC,EAAM,EAAGA,EAAM,IAAa1J,EAAM,WAAY0J,GAAO,GAC5DD,EAAI,KAAKjB,GAAcC,EAAMiB,EAAK1J,CAAK,CAAC,EAE1C,OAAOyJ,CACT,CCpHO,MAAME,EAAkC,CAQ7C,aAAc,CANd,KAAQ,OAAS,EACjB,KAAQ,YAAc,IACtB,KAAQ,cAAgB,IACxB,KAAQ,cAAgB,IACxB,KAAQ,SAAgC,KAGtC,KAAK,OAAS,IAAI,OAAO,IAAA,IAAA,0CAAA,YAAA,GAAA,EAAyC,CAAE,KAAM,QAAA,CAAU,EACpF,KAAK,OAAO,iBAAiB,UAAYtO,GAAO,KAAK,UAAUA,CAAE,CAAC,EAClE,KAAK,OAAO,iBAAiB,QAAUvD,GACrC,KAAK,UAAU,QAASI,GAAMA,EAAE,CAAE,KAAM,MAAO,MAAO,MAAO,IAAK,OAAOJ,EAAE,OAAO,EAAG,CAAC,CAAA,CAE1F,CAEQ,UAAUuD,EAAwB,CACxC,MAAMsH,EAAItH,EAAG,KACb,GAAIsH,EAAE,OAAS,SAAU,CACvB,MAAMiH,EAAMjH,EAAE,MACR3C,EAAQ,IAAI,WAAW4J,CAAG,EAE1BC,EAAuB,CAAE,OADhBL,GAAgBxJ,CAAK,EACG,MAAAA,CAAA,EACvC,KAAK,UAAU,QAAS9H,GAAMA,EAAE2R,CAAK,CAAC,EACtC,MAAM7P,EAAM2I,EAAE,IACV3I,EAAM,GACR,KAAK,UAAU,QAAS9B,GAAMA,EAAE,CAAE,KAAM,MAAO,MAAO8B,CAAA,CAAK,CAAC,EAE9D,MACF,CACA,GAAI2I,EAAE,OAAS,QAAS,CACtB,KAAK,UAAU,QAAS,GACtB,EAAE,CACA,KAAM,QACN,QAAS,EAAQA,EAAE,QACnB,EAAG,EACH,cAAe,OAAOA,EAAE,eAAiB,CAAC,CAAA,CAC3C,CAAA,EAEH,MACF,CACA,GAAIA,EAAE,OAAS,QAGf,IAAIA,EAAE,OAAS,OAASA,EAAE,IAAM,KAAM,CACpC,KAAK,UAAU,QAAS,GACtB,EAAE,CAAE,KAAM,MAAO,MAAO,MAAO,IAAK,OAAOA,EAAE,GAAG,EAAG,CAAA,EAErD,MACF,CACA,GAAI,OAAOA,EAAE,IAAO,UAAY,KAAK,QAAQ,IAAIA,EAAE,EAAE,EAAG,CACtD,MAAMnE,EAAI,KAAK,QAAQ,IAAImE,EAAE,EAAE,EAC/B,KAAK,QAAQ,OAAOA,EAAE,EAAE,EACpBA,EAAE,OAAS,MAAOnE,EAAE,OAAO,IAAI,MAAM,OAAOmE,EAAE,GAAG,CAAC,CAAC,EAClDnE,EAAE,QAAQmE,CAAC,CAClB,EACF,CAEQ,IAAiB3G,EAA8B8N,EAA2B,GAAgB,CAChG,MAAM7L,EAAK,KAAK,SAChB,OAAO,IAAI,QAAW,CAACgE,EAASC,IAAW,CACzC,KAAK,QAAQ,IAAIjE,EAAI,CAAE,QAAAgE,EAA0C,OAAAC,EAAQ,EACzE,KAAK,OAAO,YAAY,CAAE,GAAGlG,EAAK,GAAAiC,CAAA,EAAM6L,CAAQ,CAClD,CAAC,CACH,CAEA,MAAM,MAA8B,CAClC,GAAI,KAAK,SAAU,OAAO,KAAK,SAK/B,MAAM9R,EAAI,MAAM,KAAK,IACnB,CAAE,KAAM,OAAQ,KAFL,gBAEK,CAAK,EAEvB,YAAK,SAAW,CACd,aAAcA,EAAE,aAChB,WAAYA,EAAE,WACd,WAAYA,EAAE,WACd,mBAAoBA,EAAE,kBAAA,EAEjB,KAAK,QACd,CAEA,MAAM,UAAUiO,EAAiC,CAC/C,MAAM,KAAK,IAAI,CAAE,KAAM,WAAY,KAAM,KAAK,UAAUA,CAAK,EAAG,CAClE,CAEA,MAAM,UAAU8D,EAAwC,CACtD,MAAM,KAAK,IAAI,CAAE,KAAM,YAAa,KAAM,KAAK,UAAUA,CAAG,EAAG,CACjE,CAEA,MAAM,QAAQ3Q,EAA6B,CACzC,MAAM,KAAK,IAAI,CAAE,KAAM,UAAW,KAAM,OAAOA,EAAO,WAAW,EAAG,CACtE,CAEA,MAAM,OAAuB,CAC3B,MAAM,KAAK,IAAI,CAAE,KAAM,QAAS,CAClC,CAEA,MAAM,IAAI4Q,EAAgC,CACxC,MAAM,KAAK,IAAI,CAAE,KAAM,MAAO,CAChC,CAEA,MAAM,OAAuB,CAC3B,MAAM,KAAK,IAAI,CAAE,KAAM,QAAS,CAClC,CAEA,MAAM,KAAKC,EAA4BC,EAA8B,CACnE,MAAM,KAAK,IAAI,CAAE,KAAM,OAAQ,CACjC,CAEA,SAASC,EAA0C,CAAE,KAAK,UAAU,IAAIA,CAAE,CAAG,CAC7E,QAAQA,EAAoC,CAAE,KAAK,UAAU,IAAIA,CAAE,CAAG,CAEtE,MAAM,gBAAgBC,EAAsC,CAC1D,MAAMpS,EAAI,MAAM,KAAK,IAA2C,CAAE,KAAM,kBAAmB,QAAAoS,EAAS,EACpG,OAAO,IAAI,WAAWpS,EAAE,OAAO,CACjC,CAEA,MAAM,cAAcqS,EAAiF,CACnG,MAAMT,EAAMS,EAAS,MAAA,EAAQ,OACvBrS,EAAI,MAAM,KAAK,IACnB,CAAE,KAAM,gBAAiB,QAAS,IAAK,SAAU4R,CAAA,EACjD,CAACA,CAAG,CAAA,EAEN,OAAI5R,EAAE,GAAW,CAAE,GAAI,EAAA,EAChB,CAAE,GAAI,GAAO,OAAQ,IAAI,WAAWA,EAAE,MAAM,CAAA,CACrD,CAEA,MAAM,aACJiO,EACAc,EACA3N,EACAgR,EAC6B,CAC7B,MAAM,EAAI,MAAM,KAAK,IAOlB,CACD,KAAM,eACN,MAAO,KAAK,UAAUnE,CAAK,EAC3B,OAAQ,KAAK,UAAUc,CAAM,EAC7B,KAAM,OAAO3N,EAAO,WAAW,EAC/B,QAAAgR,CAAA,CACD,EACD,MAAO,CACL,YAAa,CAAC,EAAE,YAAY,CAAC,EAAG,EAAE,YAAY,CAAC,EAAG,EAAE,YAAY,CAAC,CAAC,EAClE,MAAO,EAAE,MACT,mBAAoB,EAAE,mBACtB,QAAS,CAAC,EAAE,QAAQ,CAAC,EAAG,EAAE,QAAQ,CAAC,EAAG,EAAE,QAAQ,CAAC,CAAC,EAClD,QAAS,EAAE,QACX,WAAY,EAAE,UAAA,CAElB,CAEA,MAAM,mBAAmC,CAGvC,MAAME,EAAI,MAAM,KAAK,gBAAgB,GAAG,EAClCrK,EAAM,MAAM,KAAKqK,CAAC,EAAE,IAAKpO,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,EACvEqO,EAAO,KAAK,UAAa,MAAM,KAAK,KAAA,EACpCC,EAAW,KAAK,UACpB,CACE,KAAM,qBACN,QAASD,EAAK,aACd,KAAM,aACN,SAAU,IACV,QAAStK,EACT,SAAUsK,EAAK,mBACf,GAAItK,IAAQsK,EAAK,mBACjB,GAAI,IAAI,KAAA,EAAO,YAAA,CAAY,EAE7B,KACA,CAAA,EAEF,OAAO,IAAI,KAAK,CAACC,CAAQ,EAAG,CAAE,KAAM,mBAAoB,CAC1D,CAEA,MAAM,SAA2B,CAE/B,OADU,MAAM,KAAK,IAAyB,CAAE,KAAM,UAAW,GACxD,OACX,CAEA,MAAM,OAAuB,CAC3B,KAAK,OAAO,UAAA,CACd,CACF,CC3KA,SAASC,GAAQC,EAAyB,CACxC,OAAIA,EAAQ,WAAW,OAAO,GAAKA,EAAQ,WAAW,QAAQ,EAAUA,EACjEA,EAAQ,QAAQ,QAAS,IAAI,CACtC,CAEO,MAAMC,EAAgC,CAa3C,YAAYD,EAAiB,CAV7B,KAAQ,GAAuB,KAC/B,KAAQ,SAA8B,KACtC,KAAQ,cAAgB,IACxB,KAAQ,cAAgB,IACxB,KAAQ,QAAU,GAClB,KAAQ,cAAgB,EACxB,KAAQ,QAAU,YAAY,IAAA,EAC9B,KAAQ,SAAW,EAIjB,KAAK,QAAUA,EAAQ,QAAQ,MAAO,EAAE,EACxC,KAAK,MAAQ,GAAGD,GAAQ,KAAK,OAAO,CAAC,YACvC,CAEA,MAAc,KAAQG,EAAcC,EAAgC,CAClE,MAAMC,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAGF,CAAI,GAAI,CAChD,GAAGC,EACH,QAAS,CAAE,eAAgB,mBAAoB,GAAIA,GAAM,SAAW,CAAA,CAAC,CAAG,CACzE,EACD,GAAI,CAACC,EAAI,GAAI,MAAM,IAAI,MAAM,GAAGF,CAAI,KAAKE,EAAI,MAAM,IAAIA,EAAI,UAAU,EAAE,EACvE,OAAQ,MAAMA,EAAI,KAAA,CACpB,CAEA,MAAM,MAA4B,CAChC,GAAI,KAAK,SAAU,OAAO,KAAK,SAC/B,MAAMvK,EAAI,MAAM,KAAK,KAAiB,aAAa,EACnD,YAAK,SAAW,CACd,aAAcA,EAAE,cAChB,WAAYA,EAAE,MACd,WAAYA,EAAE,YACd,mBAAoBA,EAAE,oBAAA,EAExB,KAAK,OAAA,EACE,KAAK,QACd,CAEQ,QAAe,CACrB,GAAI,KAAK,GAAI,OACb,MAAMwK,EAAK,IAAI,UAAU,KAAK,KAAK,EACnCA,EAAG,WAAa,cAChBA,EAAG,OAAS,IAAM,CAChB,KAAK,UAAU,QAAS7S,GACtBA,EAAE,CAAE,KAAM,MAAO,MAAO,KAAM,IAAK,yBAAyB,KAAK,KAAK,GAAI,CAAA,CAE9E,EACA6S,EAAG,QAAU,IAAM,CACjB,KAAK,GAAK,KACV,KAAK,UAAU,QAAS7S,GACtBA,EAAE,CAAE,KAAM,MAAO,MAAO,OAAQ,IAAK,kBAAA,CAAoB,CAAA,CAE7D,EACA6S,EAAG,QAAU,IAAM,CACjB,KAAK,UAAU,QAAS7S,GACtBA,EAAE,CAAE,KAAM,MAAO,MAAO,MAAO,IAAK,qBAAqB,KAAK,KAAK,GAAI,CAAA,CAE3E,EACA6S,EAAG,UAAa1P,GAAqB,CACnC,GAAI,EAAEA,EAAG,gBAAgB,aAAc,OACvC,MAAM2E,EAAQ,IAAI,WAAW3E,EAAG,IAAI,EAC9B2P,EAASxB,GAAgBxJ,CAAK,EACpC,GAAIgL,EAAO,SAAW,EAAG,OACzB,MAAMnB,EAAuB,CAAE,OAAAmB,EAAQ,MAAAhL,CAAA,EACvC,KAAK,UAAU,QAAS9H,GAAMA,EAAE2R,CAAK,CAAC,EACtC,KAAK,eAAiBmB,EAAO,OAC7B,KAAK,UAAYA,EAAO,OACxB,MAAM3E,EAAM,YAAY,IAAA,EACxB,GAAIA,EAAM,KAAK,SAAW,IAAM,CAC9B,MAAMrM,EAAO,KAAK,SAAW,KAASqM,EAAM,KAAK,SACjD,KAAK,UAAU,QAASnO,GAAMA,EAAE,CAAE,KAAM,MAAO,MAAO8B,CAAA,CAAK,CAAC,EAC5D,KAAK,QAAUqM,EACf,KAAK,SAAW,CAClB,CACF,EACA,KAAK,GAAK0E,CACZ,CAEA,MAAM,UAAU9E,EAAiC,CAC/C,MAAM,KAAK,KAAK,aAAc,CAAE,OAAQ,MAAO,KAAM,KAAK,UAAUA,CAAK,CAAA,CAAG,CAC9E,CACA,MAAM,UAAU8D,EAAwC,CACtD,MAAM,KAAK,KAAK,cAAe,CAAE,OAAQ,MAAO,KAAM,KAAK,UAAUA,CAAG,CAAA,CAAG,CAC7E,CACA,MAAM,QAAQ3Q,EAA6B,CACzC,MAAM,KAAK,KAAK,YAAa,CAC3B,OAAQ,MACR,KAAM,KAAK,UAAU,CAAE,SAAU,KAAOA,EAAK,SAAS,EAAE,EAAE,cAAc,SAAS,GAAI,GAAG,EAAG,CAAA,CAC5F,CACH,CACA,MAAM,OAAuB,CAC3B,MAAM,KAAK,KAAK,aAAc,CAAE,OAAQ,OAAQ,EAChD,KAAK,QAAU,GACf,KAAK,cAAgB,EACrB,KAAK,UAAU,QAASlB,GAAMA,EAAE,CAAE,KAAM,QAAS,QAAS,GAAO,EAAG,EAAG,cAAe,CAAA,CAAG,CAAC,CAC5F,CACA,MAAM,IAAI8R,EAAgC,CACxC,MAAM,KAAK,KAAK,WAAY,CAAE,OAAQ,OAAQ,EAC9C,KAAK,QAAU,GACf,KAAK,UAAU,QAAS9R,GACtBA,EAAE,CAAE,KAAM,QAAS,QAAS,GAAM,EAAG,EAAG,cAAe,KAAK,cAAe,CAAA,CAE/E,CACA,MAAM,OAAuB,CAC3B,MAAM,KAAK,KAAK,aAAc,CAAE,OAAQ,OAAQ,EAChD,KAAK,QAAU,GACf,KAAK,UAAU,QAASA,GACtBA,EAAE,CAAE,KAAM,QAAS,QAAS,GAAO,EAAG,EAAG,cAAe,KAAK,cAAe,CAAA,CAEhF,CACA,MAAM,KAAK+S,EAA2B1R,EAA6B,CACjE,MAAM,KAAK,KAAK,YAAa,CAAE,OAAQ,OAAQ,KAAM,KAAK,UAAU,CAAE,UAAA0R,EAAW,MAAO1R,CAAA,CAAM,EAAG,CACnG,CAEA,SAAS4Q,EAAsC,CAAE,KAAK,UAAU,IAAIA,CAAE,CAAG,CACzE,QAAQA,EAAmC,CAAE,KAAK,UAAU,IAAIA,CAAE,CAAG,CAErE,MAAM,gBAAgBC,EAAsC,CAC1D,MAAMpS,EAAI,MAAM,KAAK,KAAkB,wBAAyB,CAC9D,OAAQ,OACR,KAAM,KAAK,UAAU,CAAE,QAAAoS,EAAS,CAAA,CACjC,EACKX,EAAM,IAAI,WAAW,EAAE,EAC7B,QAAS,EAAI,EAAG,EAAI,GAAI,IAAKA,EAAI,CAAC,EAAI,SAASzR,EAAE,YAAY,MAAM,EAAI,EAAG,EAAI,EAAI,CAAC,EAAG,EAAE,EACxF,OAAOyR,CACT,CAEA,MAAM,cAAcY,EAAiF,CACnG,MAAMa,EAAe,MAAM,KAAKb,CAAQ,EAAE,IAAKnO,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,EACvFlE,EAAI,MAAM,KAAK,KAAiB,sBAAuB,CAC3D,OAAQ,OACR,KAAM,KAAK,UAAU,CAAE,aAAAkT,EAAc,QAAS,IAAK,CAAA,CACpD,EACD,GAAIlT,EAAE,GAAI,MAAO,CAAE,GAAI,EAAA,EACvB,MAAM4H,EAAS,IAAI,WAAW,EAAE,EAChC,QAASzB,EAAI,EAAGA,EAAI,GAAIA,IAAKyB,EAAOzB,CAAC,EAAI,SAASnG,EAAE,WAAW,MAAMmG,EAAI,EAAGA,EAAI,EAAI,CAAC,EAAG,EAAE,EAC1F,MAAO,CAAE,GAAI,GAAO,OAAAyB,CAAA,CACtB,CAEA,MAAM,mBAAmC,CACvC,MAAMuL,EAAO,MAAM,MAAM,GAAG,KAAK,OAAO,oBAAqB,CAAE,OAAQ,MAAA,CAAQ,EAAE,KAAMnT,GAAMA,EAAE,MAAM,EACrG,OAAO,IAAI,KAAK,CAACmT,CAAI,EAAG,CAAE,KAAM,mBAAoB,CACtD,CAEA,MAAM,aACJlF,EACAc,EACAqE,EACAhB,EAC6B,CAK7B,MAAO,CACL,YAAa,CAAC,EAAG,EAAG,CAAC,EACrB,MAAO,EACP,mBAAoB,EACpB,QAAS,CAAC,EAAG,EAAG,CAAC,EACjB,QAAS,EACT,WAAY,iEAAA,CAEhB,CAEA,MAAM,SAA2B,CAE/B,MAAO,UADM,KAAK,UAAa,MAAM,KAAK,KAAA,GACrB,YAAY,OACnC,CAEA,MAAM,OAAuB,CAC3B,KAAK,IAAI,MAAA,EACT,KAAK,GAAK,IACZ,CACF,CC/MA,SAASiB,GAAWxT,EAAiB,CACnC,SAAS,gBAAgB,aAAa,aAAcA,CAAC,CACvD,CACA,SAASyT,GAAa/G,EAAiB,CACrC,SAAS,KAAK,UAAU,OAAO,gBAAiB,kBAAmB,iBAAiB,EACpF,SAAS,KAAK,UAAU,IAAI,WAAWA,CAAC,EAAE,CAC5C,CACA,SAASgH,GAAYC,EAAwB,CAC3C,SAAS,KAAK,UAAU,OAAO,gBAAiBA,CAAO,CACzD,EAEC,SAAY,CAEX,MAAM3T,EAAK,MAAMuK,EAAwB,OAAO,GAAM,OAChDmC,EAAK,MAAMnC,EAAuC,SAAS,GAAM,UACjEqJ,EAAY,OAAO,aAAa,kCAAkC,EAAE,SAAW,GAC/E9I,EAAK,MAAMP,EAAe,eAAe,GAAMqJ,EACrDhS,EAAM,MAAQ5B,EAAGwT,GAAWxT,CAAC,EAC7B6B,EAAQ,MAAQ6K,EAAG+G,GAAa/G,CAAC,EACjC5K,EAAc,MAAQgJ,EAAG4I,GAAY5I,CAAC,EAGtCnF,EAAO,IAAM,CAAE6N,GAAW5R,EAAM,KAAK,EAAG6I,EAAM,QAAS7I,EAAM,KAAK,CAAG,CAAC,EACtE+D,EAAO,IAAM,CAAE8N,GAAa5R,EAAQ,KAAK,EAAG4I,EAAM,UAAW5I,EAAQ,KAAK,CAAG,CAAC,EAC9E8D,EAAO,IAAM,CAAE+N,GAAY5R,EAAc,KAAK,EAAG2I,EAAM,gBAAiB3I,EAAc,KAAK,CAAG,CAAC,EAG/F,MAAM+R,EAAY,MAAMtJ,EAAgB,cAAc,EAClDsJ,GAAa,MAAM,QAAQA,CAAS,MAAe,MAAQA,GAC/DlO,EAAO,IAAM,CAAO8E,EAAM,eAAgBzH,EAAY,KAAK,CAAG,CAAC,EAC/D,MAAM8Q,EAAiB,MAAMvJ,EAAsB,iBAAiB,EAChEuJ,GAAkB,MAAM,QAAQA,CAAc,OAAkB,MAAQA,GAC5EnO,EAAO,IAAM,CAAO8E,EAAM,kBAAmBrH,GAAe,KAAK,CAAG,CAAC,EAGrE,MAAM2Q,EAAc,MAAMxJ,EAAc,OAAO,GAAM,GACjDwJ,MAAkB,MAAQA,GAC9B,MAAMC,EAAkB,MAAMzJ,EAAqB,WAAW,GAAM,OACpExJ,EAAU,MAAQiT,EAClBrO,EAAO,IAAM,CAAO8E,EAAM,QAASxJ,EAAM,KAAK,CAAG,CAAC,EAClD0E,EAAO,IAAM,CAAO8E,EAAM,YAAa1J,EAAU,KAAK,CAAG,CAAC,EAI1D,MAAMkT,EAAmD,CAAA,EACnDC,EAAwB,CAAA,EACxBC,EAAiB,YAAY,IAAA,EAE7BC,EAAYpC,GAA+B,CAC/C,GAAIA,EAAM,OAAO,SAAW,EAAG,OAC/B,MAAM/M,EAAO+M,EAAM,OAAOA,EAAM,OAAO,OAAS,CAAC,EACjDzP,EAAU,MAAQ0C,EAClB,MAAMkM,EAAKlM,EAAK,IAAI,CAAC,EAAI,MACnBmM,EAAKnM,EAAK,IAAI,CAAC,EAAI,MACnBoM,EAAKpM,EAAK,IAAI,CAAC,EAAI,MACzBjD,GAAM,MAAQ,CAACmP,EAAIC,EAAIC,CAAE,EACzB,MAAMgD,EAAQ,KAAK,KAAKlD,EAAKA,EAAKC,EAAKA,EAAKC,EAAKA,CAAE,EAKnD,IAJApP,EAAK,MAAQoS,EACbjQ,GAAU,CAAC+M,EAAK,IAAKC,EAAK,IAAKC,EAAK,GAAG,CAAC,EACxC5M,GAAa,KAAK,IAAI,EAAG,KAAK,IAAI4M,EAAK,GAAG,EAAI,EAAI,EAAG,CAAC,EACtD6C,EAAY,KAAKG,CAAK,EACfH,EAAY,OAAS,KAAKA,EAAY,MAAA,EAE7C,MAAMI,GAAY7Q,GAAa,MAC/B,GAAI6Q,GAAU,OAAS,EAAG,OAC1B,MAAMC,IAAY,YAAY,IAAA,EAAQJ,GAAkB,IACxD,UAAW/N,MAAMkO,GAAW,CAC1B,MAAME,GAAK5H,GAAaxG,EAAE,EAC1B,GAAI,CAACoO,GAAI,SACJP,EAAS7N,EAAE,IAAG6N,EAAS7N,EAAE,EAAI,CAAA,GAClC,MAAM4E,GAAyB,CAC7B,MAAO/F,EACP,MAAOoP,EACP,YAAa,CAAClD,EAAIC,EAAIC,CAAE,EACxB,SAAU6C,EACV,SAAAK,GACA,MAAON,EAAS7N,EAAE,CAAA,EAEpB,GAAI,CACF,MAAMqO,GAASD,GAAGxJ,EAAG,EACrB,GAAI,CAACyJ,GAAQ,SACb,MAAMnJ,GAAM,MAAM,QAAQmJ,EAAM,EAAIA,GAAS,CAACA,EAAM,EACpD,UAAWjR,MAAM8H,GACf/H,GAAaC,EAAE,EACfS,EAAQ,OACN,oBAAoBT,GAAG,KAAK,4BAA4BA,GAAG,SAAS,4BAA4BA,GAAG,OAAO,WAAWA,GAAG,OAAS,MAAQA,GAAG,OAAS,EAAE,EAAA,CAE7J,OAASvD,GAAG,CACVgE,EAAQ,OAAQ,IAAImC,EAAE,oBAAqBnG,GAAY,OAAO,EAAE,CAClE,CACF,CACF,EAGA,IAAIyU,EAAmC,KACvC,eAAeC,IAA+B,CAC5C,GAAI,CACED,GAAc,MAAMA,EAAa,MAAA,EACrC,MAAME,EAAO7T,EAAU,MACvB,GAAI6T,IAAS,MAAQ3T,EAAM,MAAM,OAAQ,CACvC,MAAMX,EAAI,IAAIwS,GAAS7R,EAAM,MAAM,MAAM,EACnCyR,EAAO,MAAMpS,EAAE,KAAA,EACrBoU,EAAepU,EACfY,GAAU,MAAQ,GAClBC,GAAe,MAAQ,KACvBmB,EAAgB,MAAQoQ,EAAK,mBAC7BmC,GAAWvU,CAAC,EACZ2D,EAAQ,KAAM,kBAAkBhD,EAAM,KAAK,YAAYyR,EAAK,YAAY,EAAE,CAC5E,KAAO,CACDkC,IAAS,MACX3Q,EAAQ,OAAQ,6DAA6D,EAE/E,MAAM3D,EAAI,IAAIwR,GACRY,EAAO,MAAMpS,EAAE,KAAA,EACrBoU,EAAepU,EACfY,GAAU,MAAQ,GAClBC,GAAe,MAAQ,KACvBmB,EAAgB,MAAQoQ,EAAK,mBAC7BmC,GAAWvU,CAAC,EACZ2D,EAAQ,KAAM,0BAA0ByO,EAAK,YAAY,cAAcA,EAAK,WAAW,SAAS,EAAE,EAAE,YAAA,CAAa,EAAE,CACrH,CACA7O,GAAU6Q,CAAY,CACxB,OAASzU,EAAG,CACV,MAAMkE,EAAOlE,EAAY,QACzBkB,GAAe,MAAQgD,EACvBjD,GAAU,MAAQ,GAClB+C,EAAQ,MAAO,0BAA0BE,CAAG,EAAE,CAChD,CACF,CACA,SAAS0Q,GAAWvU,EAAsB,CACxCA,EAAE,QAASkD,GAAO,CACZA,EAAG,OAAS,SAAeA,EAAG,MAAOA,EAAG,GAAG,EAC3CA,EAAG,OAAS,QAAOrB,EAAI,MAAQqB,EAAG,OAClCA,EAAG,OAAS,aAAuB,MAAQ,OAAOA,EAAG,aAAa,EACxE,CAAC,EACDlD,EAAE,SAAS8T,CAAQ,CACrB,CAGA,IAAIU,GAAiB,GACrBnP,EAAO,IAAM,CACX5E,EAAU,MAAOE,EAAM,MACnB,CAAA6T,KACJA,GAAiB,GACZH,GAAA,EAAgB,QAAQ,IAAM,CAAEG,GAAiB,EAAO,CAAC,EAChE,CAAC,EAED7Q,EAAQ,OAAQ,2BAA2B,EAI3C,IAAI8Q,GAA6B,KACjCpP,EAAO,IAAM,CACX,MAAMkC,EAAMvF,EAAgB,MACtB0S,EAAS9T,GAAU,MACrB,CAAC2G,GAAO,CAACmN,GACTD,KAAgBlN,IACpBkN,GAAclN,GACR,SAAY,CAChB,MAAMvH,EAAIoU,EACV,GAAKpU,EACL,GAAI,CACF,MAAMwH,EAAW,IAAI,WAAW,EAAE,EAClC,QAASxB,EAAI,EAAGA,EAAI,GAAIA,MAAcA,CAAC,EAAI,SAASuB,EAAI,MAAMvB,EAAI,EAAGA,EAAI,EAAI,CAAC,EAAG,EAAE,EACnF,MAAMnG,EAAI,MAAMG,EAAE,cAAcwH,CAAQ,EACxC,GAAI3H,EAAE,GACJiC,EAAW,MAAQyF,EACnB5D,EAAQ,KAAM,qDAAqDlD,EAAU,KAAK,EAAE,MAC/E,CACL,MAAMgH,EAAS,MAAM,KAAK5H,EAAE,MAAM,EAAE,IAAKkE,IAAMA,GAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,EACvFjC,EAAW,MAAQ2F,EACnB9D,EAAQ,MAAO,+BAA+B4D,EAAI,MAAM,EAAG,EAAE,CAAC,SAASE,EAAO,MAAM,EAAG,EAAE,CAAC,GAAG,CAC/F,CACF,OAAS9H,EAAG,CACVgE,EAAQ,OAAQ,2BAA4BhE,EAAY,OAAO,EAAE,CACnE,CACF,GAAA,EACF,CAAC,EAED4C,GAAU,MAAQ,mBACpB,GAAA","x_google_ignoreList":[0,1,2,3,4]}
\ No newline at end of file
diff --git a/nvsim/index.html b/nvsim/index.html
index e61f5ff0..14d0c0ea 100644
--- a/nvsim/index.html
+++ b/nvsim/index.html
@@ -10,7 +10,7 @@
-
+
diff --git a/nvsim/sw.js b/nvsim/sw.js
index 9c0afebb..43916c49 100644
--- a/nvsim/sw.js
+++ b/nvsim/sw.js
@@ -1,2 +1,2 @@
-if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const c=e||("document"in self?document.currentScript.src:"")||location.href;if(s[c])return;let o={};const d=e=>i(e,c),l={module:{uri:c},exports:o,require:d};s[c]=Promise.all(n.map(e=>l[e]||d(e))).then(e=>(r(...e),o))}}define(["./workbox-8c29f6e4"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"4bcaa063d693b37532f242f566232490"},{url:"index.html",revision:"e40113de696f646503f7b50e21256c13"},{url:"icon-512.svg",revision:"98fc1c8102c279bf1c0dd52559821ddc"},{url:"icon-192.svg",revision:"e73b6aa9016dca426a59e0f8ff4f1b04"},{url:"nvsim-pkg/nvsim_bg.wasm",revision:"a617e7e24b2d5e976ce7e8d4f473104d"},{url:"nvsim-pkg/nvsim.js",revision:"ff4ecdcc8ace84fd24c4ad396809bd88"},{url:"assets/worker-C19MRcXs.js",revision:null},{url:"assets/signals-SG45zFCj.js",revision:null},{url:"assets/lit-BS7WqYd5.js",revision:null},{url:"assets/index-CyUCBwRA.css",revision:null},{url:"assets/index-B87hr9g3.js",revision:null},{url:"icon-192.svg",revision:"e73b6aa9016dca426a59e0f8ff4f1b04"},{url:"icon-512.svg",revision:"98fc1c8102c279bf1c0dd52559821ddc"},{url:"nvsim-pkg/nvsim.js",revision:"ff4ecdcc8ace84fd24c4ad396809bd88"},{url:"nvsim-pkg/nvsim_bg.wasm",revision:"a617e7e24b2d5e976ce7e8d4f473104d"},{url:"manifest.webmanifest",revision:"e8c97968cae19a0ed3b64c1303a27a90"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))});
+if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const c=e||("document"in self?document.currentScript.src:"")||location.href;if(s[c])return;let d={};const o=e=>i(e,c),l={module:{uri:c},exports:d,require:o};s[c]=Promise.all(n.map(e=>l[e]||o(e))).then(e=>(r(...e),d))}}define(["./workbox-8c29f6e4"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"4bcaa063d693b37532f242f566232490"},{url:"index.html",revision:"1fe45cb5f31847e0e64cfd3f5f302d32"},{url:"icon-512.svg",revision:"98fc1c8102c279bf1c0dd52559821ddc"},{url:"icon-192.svg",revision:"e73b6aa9016dca426a59e0f8ff4f1b04"},{url:"nvsim-pkg/nvsim_bg.wasm",revision:"a617e7e24b2d5e976ce7e8d4f473104d"},{url:"nvsim-pkg/nvsim.js",revision:"ff4ecdcc8ace84fd24c4ad396809bd88"},{url:"assets/worker-C19MRcXs.js",revision:null},{url:"assets/signals-SG45zFCj.js",revision:null},{url:"assets/lit-BS7WqYd5.js",revision:null},{url:"assets/index-N2_7iSzr.js",revision:null},{url:"assets/index-CyUCBwRA.css",revision:null},{url:"icon-192.svg",revision:"e73b6aa9016dca426a59e0f8ff4f1b04"},{url:"icon-512.svg",revision:"98fc1c8102c279bf1c0dd52559821ddc"},{url:"nvsim-pkg/nvsim.js",revision:"ff4ecdcc8ace84fd24c4ad396809bd88"},{url:"nvsim-pkg/nvsim_bg.wasm",revision:"a617e7e24b2d5e976ce7e8d4f473104d"},{url:"manifest.webmanifest",revision:"e8c97968cae19a0ed3b64c1303a27a90"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))});
//# sourceMappingURL=sw.js.map
diff --git a/nvsim/sw.js.map b/nvsim/sw.js.map
index e19ba834..3c04488e 100644
--- a/nvsim/sw.js.map
+++ b/nvsim/sw.js.map
@@ -1 +1 @@
-{"version":3,"file":"sw.js","sources":["../../../AppData/Local/Temp/2c1a9aed06ee5de9315f06fc64fadda4/sw.js"],"sourcesContent":["import {clientsClaim as workbox_core_clientsClaim} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-core/clientsClaim.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {cleanupOutdatedCaches as workbox_precaching_cleanupOutdatedCaches} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-precaching/cleanupOutdatedCaches.mjs';\nimport {registerRoute as workbox_routing_registerRoute} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-routing/registerRoute.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"registerSW.js\",\n \"revision\": \"4bcaa063d693b37532f242f566232490\"\n },\n {\n \"url\": \"index.html\",\n \"revision\": \"e40113de696f646503f7b50e21256c13\"\n },\n {\n \"url\": \"icon-512.svg\",\n \"revision\": \"98fc1c8102c279bf1c0dd52559821ddc\"\n },\n {\n \"url\": \"icon-192.svg\",\n \"revision\": \"e73b6aa9016dca426a59e0f8ff4f1b04\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim_bg.wasm\",\n \"revision\": \"a617e7e24b2d5e976ce7e8d4f473104d\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim.js\",\n \"revision\": \"ff4ecdcc8ace84fd24c4ad396809bd88\"\n },\n {\n \"url\": \"assets/worker-C19MRcXs.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/signals-SG45zFCj.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/lit-BS7WqYd5.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-CyUCBwRA.css\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-B87hr9g3.js\",\n \"revision\": null\n },\n {\n \"url\": \"icon-192.svg\",\n \"revision\": \"e73b6aa9016dca426a59e0f8ff4f1b04\"\n },\n {\n \"url\": \"icon-512.svg\",\n \"revision\": \"98fc1c8102c279bf1c0dd52559821ddc\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim.js\",\n \"revision\": \"ff4ecdcc8ace84fd24c4ad396809bd88\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim_bg.wasm\",\n \"revision\": \"a617e7e24b2d5e976ce7e8d4f473104d\"\n },\n {\n \"url\": \"manifest.webmanifest\",\n \"revision\": \"e8c97968cae19a0ed3b64c1303a27a90\"\n }\n], {});\nworkbox_precaching_cleanupOutdatedCaches();\nworkbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"index.html\")));\n\n\n\n\n\n\n"],"names":["self","skipWaiting","workbox_core_clientsClaim","workbox_precaching_precacheAndRoute","url","revision","workbox_precaching_cleanupOutdatedCaches","workbox","registerRoute","workbox_routing_NavigationRoute","NavigationRoute","workbox_precaching_createHandlerBoundToURL"],"mappings":"inBAwBAA,KAAKC,cAELC,EAAAA,eAQAC,EAAAA,iBAAoC,CAClC,CACEC,IAAO,gBACPC,SAAY,oCAEd,CACED,IAAO,aACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,0BACPC,SAAY,oCAEd,CACED,IAAO,qBACPC,SAAY,oCAEd,CACED,IAAO,4BACPC,SAAY,MAEd,CACED,IAAO,6BACPC,SAAY,MAEd,CACED,IAAO,yBACPC,SAAY,MAEd,CACED,IAAO,4BACPC,SAAY,MAEd,CACED,IAAO,2BACPC,SAAY,MAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,qBACPC,SAAY,oCAEd,CACED,IAAO,0BACPC,SAAY,oCAEd,CACED,IAAO,uBACPC,SAAY,qCAEb,CAAE,GACLC,EAAAA,wBAC6BC,EAAAC,cAAC,IAAIC,EAA+BC,gBAACC,0BAA2C"}
\ No newline at end of file
+{"version":3,"file":"sw.js","sources":["../../../AppData/Local/Temp/fad3786d2060e2545e9a5a49c4920504/sw.js"],"sourcesContent":["import {clientsClaim as workbox_core_clientsClaim} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-core/clientsClaim.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {cleanupOutdatedCaches as workbox_precaching_cleanupOutdatedCaches} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-precaching/cleanupOutdatedCaches.mjs';\nimport {registerRoute as workbox_routing_registerRoute} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-routing/registerRoute.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from 'C:/Users/ruv/Projects/wifi-densepose/dashboard/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"registerSW.js\",\n \"revision\": \"4bcaa063d693b37532f242f566232490\"\n },\n {\n \"url\": \"index.html\",\n \"revision\": \"1fe45cb5f31847e0e64cfd3f5f302d32\"\n },\n {\n \"url\": \"icon-512.svg\",\n \"revision\": \"98fc1c8102c279bf1c0dd52559821ddc\"\n },\n {\n \"url\": \"icon-192.svg\",\n \"revision\": \"e73b6aa9016dca426a59e0f8ff4f1b04\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim_bg.wasm\",\n \"revision\": \"a617e7e24b2d5e976ce7e8d4f473104d\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim.js\",\n \"revision\": \"ff4ecdcc8ace84fd24c4ad396809bd88\"\n },\n {\n \"url\": \"assets/worker-C19MRcXs.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/signals-SG45zFCj.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/lit-BS7WqYd5.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-N2_7iSzr.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-CyUCBwRA.css\",\n \"revision\": null\n },\n {\n \"url\": \"icon-192.svg\",\n \"revision\": \"e73b6aa9016dca426a59e0f8ff4f1b04\"\n },\n {\n \"url\": \"icon-512.svg\",\n \"revision\": \"98fc1c8102c279bf1c0dd52559821ddc\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim.js\",\n \"revision\": \"ff4ecdcc8ace84fd24c4ad396809bd88\"\n },\n {\n \"url\": \"nvsim-pkg/nvsim_bg.wasm\",\n \"revision\": \"a617e7e24b2d5e976ce7e8d4f473104d\"\n },\n {\n \"url\": \"manifest.webmanifest\",\n \"revision\": \"e8c97968cae19a0ed3b64c1303a27a90\"\n }\n], {});\nworkbox_precaching_cleanupOutdatedCaches();\nworkbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"index.html\")));\n\n\n\n\n\n\n"],"names":["self","skipWaiting","workbox_core_clientsClaim","workbox_precaching_precacheAndRoute","url","revision","workbox_precaching_cleanupOutdatedCaches","workbox","registerRoute","workbox_routing_NavigationRoute","NavigationRoute","workbox_precaching_createHandlerBoundToURL"],"mappings":"inBAwBAA,KAAKC,cAELC,EAAAA,eAQAC,EAAAA,iBAAoC,CAClC,CACEC,IAAO,gBACPC,SAAY,oCAEd,CACED,IAAO,aACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,0BACPC,SAAY,oCAEd,CACED,IAAO,qBACPC,SAAY,oCAEd,CACED,IAAO,4BACPC,SAAY,MAEd,CACED,IAAO,6BACPC,SAAY,MAEd,CACED,IAAO,yBACPC,SAAY,MAEd,CACED,IAAO,2BACPC,SAAY,MAEd,CACED,IAAO,4BACPC,SAAY,MAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,qBACPC,SAAY,oCAEd,CACED,IAAO,0BACPC,SAAY,oCAEd,CACED,IAAO,uBACPC,SAAY,qCAEb,CAAE,GACLC,EAAAA,wBAC6BC,EAAAC,cAAC,IAAIC,EAA+BC,gBAACC,0BAA2C"}
\ No newline at end of file