betterwithage commited on
Commit
ab626bf
·
verified ·
1 Parent(s): 138f8e6

F4: 3D inference router + energy hologram + GRC OSCAL matrix (additive, 2D fallback, 0 CDN)

Browse files

F1 3D/holographic substrate kit — direct Space push (byte-identical to a11oy). Doctrine v11: locked=8 @ c7c0ba17; Lambda=Conjecture 1; 0 CDN.

Files changed (2) hide show
  1. web/energy.html +392 -0
  2. web/nemo.html +174 -0
web/energy.html ADDED
@@ -0,0 +1,392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8"/>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>a11oy — Proven Energy Engine · SZL Holdings</title>
7
+ <meta name="description" content="Live, honest view of the agentic-GPU engine: sovereign GPU posture (NVIDIA RTX 5000 @ betterwithage), the Bekenstein-gated energy+information budget ledger (F19/TH6, proven inequality), and the off-peak power window. Energy figures are SAMPLE/ESTIMATE — no real power meter is wired. No free-energy claims."/>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"/>
10
+ <style>
11
+ :root{
12
+ --ground:#0a0a0a; --panel:#0e0e0e; --panel2:#080808; --rail:#0b0b0b;
13
+ --gold:#c9b787; --gold-bright:#d6c69a;
14
+ --teal:#5fb3a3; --teal-soft:rgba(95,179,163,0.10);
15
+ --cream:#f5f5f5; --paragraph:#9a9a9a; --muted:#888; --dim:#555;
16
+ --gold-line:rgba(201,183,135,0.15); --gold-soft:rgba(201,183,135,0.04);
17
+ --teal-line:rgba(95,179,163,0.22);
18
+ --live:#5a8a6e; --err:#b06a5a; --warn:#c9a05f;
19
+ --green:#39d98a; --yellow:#f5c451; --red:#ff6a5a; --blue:#5bc8ff;
20
+ --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,monospace;
21
+ --display:'Space Grotesk',Georgia,serif;
22
+ }
23
+ *{box-sizing:border-box;}
24
+ html,body{margin:0;padding:0;background:var(--ground);color:var(--cream);font-family:var(--display);-webkit-font-smoothing:antialiased;}
25
+ .mono{font-family:var(--mono);}
26
+ a{color:inherit;text-decoration:none;}
27
+ .topbar{position:sticky;top:0;z-index:60;display:flex;align-items:center;gap:1rem;flex-wrap:wrap;padding:.5rem 1.1rem;background:rgba(10,10,10,.92);backdrop-filter:blur(10px);border-bottom:1px solid var(--gold-line);font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--gold);}
28
+ .topbar .sep{color:var(--dim);}
29
+ .topbar .live{display:inline-flex;align-items:center;gap:.4rem;color:var(--cream);}
30
+ .live-dot{width:6px;height:6px;border-radius:50%;background:var(--live);box-shadow:0 0 6px var(--live);animation:pulse 2.2s ease-in-out infinite;}
31
+ @keyframes pulse{0%,100%{opacity:1;}50%{opacity:.35;}}
32
+ .switcher{margin-left:auto;display:flex;align-items:center;gap:.3rem;}
33
+ .flag{padding:.22rem .55rem;border-radius:6px;border:1px solid transparent;color:var(--muted);transition:.15s;}
34
+ .flag:hover{color:var(--cream);border-color:var(--gold-line);background:var(--gold-soft);}
35
+ .flag.active{color:var(--ground);background:var(--gold);border-color:var(--gold);font-weight:600;}
36
+ .wrap{max-width:1320px;margin:0 auto;padding:1.6rem 1.4rem 4rem;}
37
+ h1{font-size:1.7rem;font-weight:600;margin:.2rem 0 .3rem;letter-spacing:-.01em;}
38
+ .badge{display:inline-block;font-family:var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--teal);border:1px solid var(--teal-line);background:var(--teal-soft);padding:.25rem .6rem;border-radius:5px;margin-left:.4rem;vertical-align:middle;}
39
+ .lede{color:var(--paragraph);font-size:.95rem;line-height:1.65;max-width:1180px;margin:.6rem 0 1rem;}
40
+ .lede b{color:var(--cream);font-weight:600;}
41
+ .econ{border:1px solid var(--gold-line);background:var(--gold-soft);border-radius:8px;padding:.7rem 1rem;margin:.4rem 0 1.2rem;font-family:var(--mono);font-size:12px;color:var(--paragraph);line-height:1.7;}
42
+ .econ .k{color:var(--gold);text-transform:uppercase;letter-spacing:.08em;font-size:10px;}
43
+ .econ .hi{color:var(--teal);}
44
+ .cards{display:grid;grid-template-columns:repeat(4,1fr);gap:.8rem;margin:0 0 1.2rem;}
45
+ .card{border:1px solid var(--gold-line);background:var(--panel);border-radius:8px;padding:.85rem 1rem;}
46
+ .card .lbl{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);}
47
+ .card .big{font-size:1.5rem;font-weight:600;margin:.35rem 0 .15rem;color:var(--teal);}
48
+ .card .sub{font-family:var(--mono);font-size:10.5px;color:var(--muted);}
49
+ .grid2{display:grid;grid-template-columns:1fr 1fr;gap:1rem;}
50
+ .panel{border:1px solid var(--gold-line);background:var(--panel);border-radius:10px;padding:1rem 1.1rem;margin-bottom:1rem;}
51
+ .panel h2{font-size:1.05rem;font-weight:600;margin:0 0 .2rem;display:flex;align-items:center;justify-content:space-between;}
52
+ .panel h2 .meta{font-family:var(--mono);font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.06em;}
53
+ .kv{font-family:var(--mono);font-size:12px;color:var(--paragraph);line-height:1.9;margin-top:.5rem;}
54
+ .kv .k{color:var(--dim);display:inline-block;min-width:128px;}
55
+ .kv .v{color:var(--cream);}
56
+ .pill{display:inline-block;font-family:var(--mono);font-size:10px;letter-spacing:.06em;text-transform:uppercase;padding:.18rem .5rem;border-radius:5px;border:1px solid var(--gold-line);}
57
+ .pill.green{color:var(--green);border-color:rgba(57,217,138,.4);background:rgba(57,217,138,.08);}
58
+ .pill.yellow{color:var(--yellow);border-color:rgba(245,196,81,.4);background:rgba(245,196,81,.08);}
59
+ .pill.red{color:var(--red);border-color:rgba(255,106,90,.4);background:rgba(255,106,90,.08);}
60
+ .pill.dim{color:var(--muted);}
61
+ .sample{font-family:var(--mono);font-size:9px;letter-spacing:.06em;text-transform:uppercase;color:var(--warn);border:1px solid var(--warn);border-radius:4px;padding:.05rem .3rem;margin-left:.35rem;white-space:nowrap;}
62
+ .simnote{border:1px solid var(--warn);background:rgba(201,160,95,.06);border-radius:8px;padding:.7rem .9rem;margin-top:.8rem;font-family:var(--mono);font-size:11px;color:#d8c39a;line-height:1.65;}
63
+ .simnote b{color:var(--warn);}
64
+ .bek{margin-top:.7rem;}
65
+ .bek-bar{position:relative;height:30px;border:1px solid var(--gold-line);border-radius:6px;background:#0c0c0c;overflow:hidden;}
66
+ .bek-fill{position:absolute;top:0;left:0;bottom:0;background:linear-gradient(90deg,rgba(95,179,163,.55),rgba(95,179,163,.9));transition:width .4s;}
67
+ .bek-fill.over{background:linear-gradient(90deg,rgba(255,106,90,.55),rgba(255,106,90,.95));}
68
+ .bek-ceiling{position:absolute;top:-3px;bottom:-3px;right:0;width:2px;background:var(--gold);box-shadow:0 0 6px var(--gold);}
69
+ .bek-lbl{display:flex;justify-content:space-between;font-family:var(--mono);font-size:10.5px;color:var(--muted);margin-top:.4rem;}
70
+ .win{display:grid;grid-template-columns:repeat(3,1fr);gap:.5rem;margin-top:.6rem;}
71
+ .win .cell{border:1px solid var(--gold-line);border-radius:7px;padding:.6rem .7rem;text-align:center;font-family:var(--mono);}
72
+ .win .cell .t{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);}
73
+ .win .cell .v{font-size:1.05rem;font-weight:600;margin-top:.25rem;color:var(--muted);}
74
+ .win .cell.on{border-color:var(--teal-line);background:var(--teal-soft);}
75
+ .win .cell.on .v{color:var(--teal);}
76
+ table{width:100%;border-collapse:collapse;margin-top:.6rem;font-family:var(--mono);font-size:11px;}
77
+ th,td{text-align:left;padding:.4rem .5rem;border-bottom:1px solid var(--gold-line);white-space:nowrap;}
78
+ th{color:var(--dim);font-weight:500;font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;}
79
+ td{color:var(--paragraph);}
80
+ td.ok{color:var(--green);}
81
+ td.bad{color:var(--red);}
82
+ .scrollx{overflow-x:auto;}
83
+ .empty{font-family:var(--mono);font-size:11.5px;color:var(--muted);border:1px dashed var(--gold-line);border-radius:8px;padding:.9rem 1rem;margin-top:.6rem;line-height:1.6;}
84
+ .footnote{margin-top:1.6rem;font-family:var(--mono);font-size:10.5px;color:var(--muted);line-height:1.7;border-top:1px solid var(--gold-line);padding-top:.9rem;}
85
+ .footnote b{color:var(--cream);}
86
+ @media(max-width:980px){.cards{grid-template-columns:repeat(2,1fr);}.grid2{grid-template-columns:1fr;}}
87
+ </style>
88
+ </head>
89
+ <body>
90
+ <div class="topbar">
91
+ <span>SZL HOLDINGS</span><span class="sep">/</span><span>a11oy</span><span class="sep">/</span><span>PROVEN ENERGY ENGINE</span>
92
+ <span class="sep">/</span><span class="live"><span class="live-dot"></span><span id="livelbl">LIVE · RT</span></span>
93
+ <span class="switcher">
94
+ <a class="flag active" href="/energy">Energy</a>
95
+ <a class="flag" href="/fleet-c2">Fleet C2</a>
96
+ <a class="flag" href="/living-anatomy">Living Anatomy</a>
97
+ <a class="flag" href="/code">Code</a>
98
+ </span>
99
+ </div>
100
+ <div class="wrap">
101
+ <h1>Proven Energy Engine<span class="badge">honest GPU posture · Bekenstein-gated budget (F19/TH6) · SAMPLE energy</span></h1>
102
+ <p class="lede">A live, <b>honest</b> view of the agentic-GPU engine. Sovereign posture is read from <b>/code/healthz</b> — <b>sovereign:true only when the local GPU actually serves</b> (NVIDIA RTX 5000 @ betterwithage, Ollama/vLLM), else router fallback. Every task receipt passes the <b>Bekenstein gate</b> — <code>shannon_bits ≤ output_bytes·8</code> (F19/TH6, a <b>proven inequality</b>, not an assertion) — and the ledger sums them. <b>Energy figures are SAMPLE/ESTIMATE</b>: no real power meter is wired, so joules are labeled, never presented as measured. <b>No free-energy / perpetual-motion claims</b>: the engine harvests WASTED energy and proves bounded information work.</p>
103
+ <div class="econ"><span class="k">Why it matters:</span> sovereignty and energy efficiency are <span class="hi">measured-or-labeled, never assumed</span> — the half-state (claiming more energy or sovereignty than is real) is the only unacceptable outcome, so this surface refuses to over-claim.</div>
104
+ <div class="cards">
105
+ <div class="card"><div class="lbl">Sovereign</div><div class="big" id="c_sov">—</div><div class="sub">local GPU serving?</div></div>
106
+ <div class="card"><div class="lbl">Tasks gated</div><div class="big" id="c_tasks" style="color:var(--blue)">—</div><div class="sub">Bekenstein ledger</div></div>
107
+ <div class="card"><div class="lbl">All within bound</div><div class="big" id="c_within" style="color:var(--green)">—</div><div class="sub">F19/TH6 gate</div></div>
108
+ <div class="card"><div class="lbl">Total joules <span class="sample">sample</span></div><div class="big" id="c_joules" style="color:var(--warn)">—</div><div class="sub">estimate · not metered</div></div>
109
+ </div>
110
+ <div class="grid2">
111
+ <div class="panel">
112
+ <h2>(1) GPU engine status<span class="meta">/code/healthz</span></h2>
113
+ <div class="kv" id="hz_kv" style="display:none">
114
+ <div><span class="k">Hardware</span><span class="v">NVIDIA RTX 5000 @ betterwithage</span></div>
115
+ <div><span class="k">Sovereign</span><span class="v" id="hz_sov">—</span></div>
116
+ <div><span class="k">Inference path</span><span class="v" id="hz_inf">—</span></div>
117
+ <div><span class="k">Backend / mode</span><span class="v" id="hz_back">—</span></div>
118
+ <div><span class="k">GPU</span><span class="v" id="hz_gpu">—</span></div>
119
+ <div><span class="k">Doctrine</span><span class="v" id="hz_doc">—</span></div>
120
+ <div><span class="k">Posture</span><span class="v" id="hz_posture">—</span></div>
121
+ </div>
122
+ <div class="empty" id="hz_empty">/code/healthz unreachable — posture cannot be confirmed. Showing no sovereignty claim (honest empty-state).</div>
123
+ </div>
124
+ <div class="panel">
125
+ <h2>(4) Power window<span class="meta">off-peak clock</span></h2>
126
+ <div class="win" id="win">
127
+ <div class="cell" data-w="cheap"><div class="t">Cheap (off-peak)</div><div class="v">—</div></div>
128
+ <div class="cell" data-w="normal"><div class="t">Normal</div><div class="v">—</div></div>
129
+ <div class="cell" data-w="dear"><div class="t">Dear (peak)</div><div class="v">—</div></div>
130
+ </div>
131
+ <div class="simnote"><b>Client-side estimate:</b> the window (cheap / normal / dear) is computed from <b>your local clock</b> as an off-peak heuristic (cheap before 08:00 / after 20:00) — it mirrors the engine's off-peak provider but is <b>not</b> a live tariff feed. No price is claimed as metered.</div>
132
+ </div>
133
+ </div>
134
+ <div class="panel">
135
+ <h2>(3) Bekenstein bound<span class="meta">Σ shannon_bits vs Σ (output_bytes · 8)</span></h2>
136
+ <div class="bek" id="bek" style="display:none">
137
+ <div class="bek-bar"><div class="bek-fill" id="bek_fill" style="width:0%"></div><div class="bek-ceiling" title="ceiling = Σ output_bytes·8"></div></div>
138
+ <div class="bek-lbl">
139
+ <span>Σ shannon_bits: <span id="bek_sh" style="color:var(--teal)">—</span></span>
140
+ <span>ceiling Σ n·8: <span id="bek_ceil" style="color:var(--gold)">—</span> · <span id="bek_pct">—</span></span>
141
+ </div>
142
+ <div class="simnote" id="bek_note"><b>Proven inequality (F19/TH6):</b> empirical byte-entropy is always ≤ n·8, so the fill never exceeds the gold ceiling unless an upstream over-claim occurs (then it turns red). Kernel-checked in EnergyBudgetWitness.lean (0-sorry).</div>
143
+ </div>
144
+ <div class="empty" id="bek_empty">/v1/energy/budget unreachable — no bound to plot (honest empty-state).</div>
145
+ </div>
146
+ <div class="panel">
147
+ <h2>(2) Energy-budget ledger<span class="meta">/v1/energy/budget · per-task receipts</span></h2>
148
+ <div class="scrollx" id="led_wrap" style="display:none">
149
+ <table>
150
+ <thead><tr>
151
+ <th>task_hash</th><th>bytes</th><th>shannon_bits</th><th>bekenstein n·8</th><th>within</th><th>energy_source</th><th>joules_est <span class="sample">sample</span></th><th>ts</th>
152
+ </tr></thead>
153
+ <tbody id="led_body"></tbody>
154
+ </table>
155
+ </div>
156
+ <div class="empty" id="led_empty">No per-task receipts available. The in-memory ledger resets on process restart; track a task via <code>/v1/energy/budget?bytes=…</code> to populate it. Showing summary only if present.</div>
157
+ <div class="simnote"><b>joules_est is SAMPLE/ESTIMATE</b> — no real power meter is wired (doctrine v11/v12). The <b>within</b> check is the real, proven F19/TH6 gate: <code>shannon_bits ≤ output_bytes·8</code>.</div>
158
+ </div>
159
+ <div class="panel" id="energy3d_panel">
160
+ <h2>(5) 3D Energy / GPU Hologram<span class="meta">holographic GPU blocks · live J/token · 0 CDN · 2D-fallback</span>
161
+ <button class="flag" id="e3d_toggle" style="float:right;font-size:11px;cursor:pointer;">Enable 3D</button>
162
+ </h2>
163
+ <div class="simnote" style="margin-bottom:.6rem">
164
+ A holographic view of the sovereign-inference engine on the shared 0-CDN WebGL2 kit: the <b>GPU/serving block</b>
165
+ colors by the live <code>/code/healthz</code> sovereign posture (<b>sovereign:true only on a real local-GPU probe</b>),
166
+ a <b>trust sphere</b> morphs with Λ (Conjecture 1, &lt;1.0), and the <b>J/token</b> readout is
167
+ <b>MEASURED only on a live GPU power probe</b> — with no meter wired it stays an honest <b>ROADMAP/SAMPLE</b> estimate,
168
+ never presented as measured. CPU/old-GPU renders the same data on a <b>2D canvas fallback</b>; the panels above are the
169
+ complete non-3D experience. Patterns: vLLM/SGLang J/token, NVIDIA Dynamo, LiteLLM/RouteLLM.
170
+ </div>
171
+ <div class="r3d-stats mono" style="display:flex;gap:1.2rem;flex-wrap:wrap;font-size:11px;color:var(--muted);margin:.4rem 0;">
172
+ <span>J/token <span id="e3d_jtok" style="color:var(--warn)">—</span> <span id="e3d_jtok_lbl"></span></span>
173
+ <span>sovereign <span id="e3d_sov" style="color:var(--cream)">—</span></span>
174
+ <span>within Bekenstein <span id="e3d_within" style="color:var(--green)">—</span></span>
175
+ <span id="e3d_caps" style="color:var(--dim)"></span>
176
+ </div>
177
+ <div id="energy3d_mount" style="width:100%;height:400px;border:1px solid var(--gold-line);border-radius:10px;background:#060606;display:none;position:relative;"></div>
178
+ <div class="empty" id="e3d_off">3D is off (default). Click <b>Enable 3D</b> to render the holographic energy view on the live <code>/v1/energy/budget</code> + <code>/code/healthz</code> endpoints. The 2D panels above are always available as the fallback.</div>
179
+ <div class="meta mono" style="color:var(--muted);font-size:10px;margin-top:.5rem;">
180
+ 0 runtime CDN · WebGL2 + honest 2D fallback · J/token MEASURED-or-ROADMAP (never fabricated) · sovereign:true only on live probe · Λ = Conjecture 1 (&lt;1.0) · trust &lt;100%.
181
+ </div>
182
+ </div>
183
+
184
+ <div class="footnote">
185
+ <b>Honest by design.</b> Posture is <b>derived</b> from /code/healthz: GREEN only when <code>sovereign:true</code> AND <code>inference=self-hosted-gpu</code>; MAINTENANCE when reachable but on router fallback; DOWN when unreachable — sovereignty is <b>never assumed</b>. The Bekenstein gate is <b>F19 (locked-8) / TH6</b>, a machine-checked inequality (EnergyBudgetWitness.lean, 0-sorry). <b>All energy figures are SAMPLE/ESTIMATE</b> until a real meter is wired. <b>No free-energy / perpetual-motion claims.</b> Λ trust = <b>Conjecture 1</b> (machine-checked FALSE — advisory, never a theorem). Coherence governor C(t)=C₀·e^(−t/τ_c) via /v1/qbio/coherence is EXPERIMENTAL. Fonts via Google Fonts; no runtime logic/3D CDN.
186
+ </div>
187
+ </div>
188
+ <script>
189
+ const A="/api/a11oy";
190
+ const $=id=>document.getElementById(id);
191
+ async function getJSON(u,opt){try{const r=await fetch(u,opt);if(!r.ok)return null;return await r.json();}catch(e){return null;}}
192
+ function esc(s){return String(s==null?'':s).replace(/[&<>]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));}
193
+ function fmt(n,d){if(n==null||isNaN(n))return '—';const x=Number(n);return d!=null?x.toFixed(d):String(x);}
194
+
195
+ // (1) GPU engine status — posture DERIVED honestly (never assume sovereign)
196
+ function derivePosture(hz){
197
+ if(!hz)return {cls:'red',txt:'DOWN — healthz unreachable'};
198
+ const sov=hz.sovereign===true, inf=String(hz.inference||'');
199
+ if(sov && inf==='self-hosted-gpu')return {cls:'green',txt:'GREEN — sovereign GPU serving'};
200
+ return {cls:'yellow',txt:'MAINTENANCE — router fallback (not sovereign)'};
201
+ }
202
+ async function loadHealthz(){
203
+ const hz=await getJSON(A+"/code/healthz");
204
+ if(!hz){
205
+ $('hz_kv').style.display='none'; $('hz_empty').style.display='block';
206
+ $('c_sov').textContent='—'; $('c_sov').style.color='var(--muted)';
207
+ $('livelbl').textContent='FALLBACK';
208
+ return;
209
+ }
210
+ $('hz_empty').style.display='none'; $('hz_kv').style.display='block';
211
+ const sov=hz.sovereign===true;
212
+ $('hz_sov').innerHTML=`<span class="pill ${sov?'green':'yellow'}">${sov?'true':'false'}</span>`;
213
+ $('hz_inf').textContent=esc(hz.inference||'—');
214
+ $('hz_back').textContent=esc((hz.backend||'—')+' / '+(hz.mode||'—'));
215
+ $('hz_gpu').textContent=esc(hz.gpu!=null?(typeof hz.gpu==='object'?JSON.stringify(hz.gpu):hz.gpu):'(not reported by healthz)');
216
+ $('hz_doc').textContent=esc(hz.doctrine||'—');
217
+ const p=derivePosture(hz);
218
+ $('hz_posture').innerHTML=`<span class="pill ${p.cls}">${esc(p.txt)}</span>`;
219
+ $('c_sov').textContent=sov?'YES':'NO';
220
+ $('c_sov').style.color=sov?'var(--green)':'var(--yellow)';
221
+ $('livelbl').textContent=(hz.mode==='live')?'LIVE · RT':'STUB';
222
+ }
223
+
224
+ // (4) power window — client-side off-peak heuristic (mirrors OffPeakProvider)
225
+ function offPeakWindow(d){const h=d.getHours();return (h<8||h>=20)?'cheap':'normal';}
226
+ function loadWindow(){
227
+ const now=new Date();
228
+ const w=offPeakWindow(now);
229
+ const hh=String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
230
+ document.querySelectorAll('#win .cell').forEach(c=>{
231
+ const on=c.dataset.w===w; c.classList.toggle('on',on);
232
+ c.querySelector('.v').textContent=on?('NOW · '+hh):'—';
233
+ });
234
+ }
235
+
236
+ // (2)+(3) Bekenstein ledger + bound visual
237
+ function renderBek(sumSh,sumCeil){
238
+ if(sumCeil==null||sumCeil<=0){$('bek').style.display='none';$('bek_empty').style.display='block';return;}
239
+ $('bek_empty').style.display='none'; $('bek').style.display='block';
240
+ const pct=sumSh/sumCeil; const over=sumSh>sumCeil+1e-9;
241
+ const f=$('bek_fill');
242
+ f.style.width=Math.min(pct,1)*100+'%';
243
+ f.classList.toggle('over',over);
244
+ $('bek_sh').textContent=fmt(sumSh,1);
245
+ $('bek_ceil').textContent=fmt(sumCeil,0);
246
+ $('bek_pct').textContent=(pct*100).toFixed(1)+'% of ceiling'+(over?' — OVER (upstream over-claim!)':'');
247
+ }
248
+ function ledgerRows(b){
249
+ // accept several shapes: explicit ledger/receipts array, single receipt, else none
250
+ if(Array.isArray(b.ledger))return b.ledger;
251
+ if(Array.isArray(b.receipts))return b.receipts;
252
+ if(b.receipt)return [b.receipt];
253
+ return [];
254
+ }
255
+ async function loadBudget(){
256
+ const b=await getJSON(A+"/v1/energy/budget");
257
+ if(!b){
258
+ $('led_wrap').style.display='none'; $('led_empty').style.display='block';
259
+ $('bek').style.display='none'; $('bek_empty').style.display='block';
260
+ $('c_tasks').textContent='—'; $('c_within').textContent='—'; $('c_joules').textContent='—';
261
+ return;
262
+ }
263
+ const rows=ledgerRows(b);
264
+ // summary cards (prefer explicit summary fields; fall back to row sums)
265
+ const s=b.summary||b;
266
+ const tc=s.task_count!=null?s.task_count:rows.length;
267
+ $('c_tasks').textContent=tc;
268
+ const allWithin=(s.all_within_bound!=null)?s.all_within_bound
269
+ :(rows.length?rows.every(r=>r.within_bound):null);
270
+ $('c_within').textContent=allWithin==null?'—':(allWithin?'YES':'NO');
271
+ $('c_within').style.color=allWithin===false?'var(--red)':'var(--green)';
272
+ const tj=(s.total_joules_est!=null)?s.total_joules_est
273
+ :rows.reduce((a,r)=>a+(Number(r.joules_est)||0),0);
274
+ $('c_joules').textContent=fmt(tj,2);
275
+ // bound visual
276
+ const sumSh=(s.total_shannon_bits!=null)?s.total_shannon_bits
277
+ :rows.reduce((a,r)=>a+(Number(r.shannon_bits)||0),0);
278
+ const sumCeil=(s.total_bekenstein_bound_bits!=null)?s.total_bekenstein_bound_bits
279
+ :rows.reduce((a,r)=>a+(Number(r.bekenstein_bound_bits)||0),0);
280
+ renderBek(sumSh,sumCeil);
281
+ // ledger table
282
+ if(!rows.length){$('led_wrap').style.display='none'; $('led_empty').style.display='block'; return;}
283
+ $('led_empty').style.display='none'; $('led_wrap').style.display='block';
284
+ $('led_body').innerHTML=rows.map(r=>{
285
+ const wb=r.within_bound===true;
286
+ return `<tr>
287
+ <td title="${esc(r.task_hash||'')}">${esc(String(r.task_hash||'—').slice(0,16))}…</td>
288
+ <td>${esc(r.output_bytes??'—')}</td>
289
+ <td>${fmt(r.shannon_bits,1)}</td>
290
+ <td>${esc(r.bekenstein_bound_bits??'—')}</td>
291
+ <td class="${wb?'ok':'bad'}">${r.within_bound==null?'—':(wb?'✓':'✗')}</td>
292
+ <td>${esc(r.energy_source||'—')}</td>
293
+ <td>${fmt(r.joules_est,3)}</td>
294
+ <td>${esc(r.ts||'—')}</td>
295
+ </tr>`;
296
+ }).join('');
297
+ }
298
+
299
+ async function loadAll(){loadWindow();await Promise.all([loadHealthz(),loadBudget()]);}
300
+ loadAll();
301
+ setInterval(loadAll,30000);
302
+ </script>
303
+
304
+ <!-- F4 · 3D Energy/GPU hologram (additive). Loads the shared 0-CDN holographic kit. -->
305
+ <script src="/static/shared/szl_holo3d.js"></script>
306
+ <script>
307
+ "use strict";
308
+ (function(){
309
+ var EA="/api/a11oy";
310
+ var mount=document.getElementById('energy3d_mount');
311
+ var offEl=document.getElementById('e3d_off');
312
+ var toggle=document.getElementById('e3d_toggle');
313
+ if(!mount||!toggle||!window.SZLHolo){ if(toggle){toggle.disabled=true;toggle.textContent='3D unavailable';} return; }
314
+ var scene=null, started=false, poll=null, anim=null;
315
+ var JT=document.getElementById('e3d_jtok'), JTL=document.getElementById('e3d_jtok_lbl');
316
+ var SOV=document.getElementById('e3d_sov'), WIN=document.getElementById('e3d_within'), CAP=document.getElementById('e3d_caps');
317
+ function esc3(s){return String(s==null?'':s).replace(/[&<>]/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;'}[c];});}
318
+ function getJSON(u,opt){return fetch(u,opt).then(function(r){return r.ok?r.json():null;}).catch(function(){return null;});}
319
+
320
+ function applyBudget(b){
321
+ if(!b){ JT.textContent='—'; JTL.innerHTML=''; WIN.textContent='—'; return; }
322
+ var lbl=String(b.joules_label||'').toLowerCase();
323
+ var ev=b.joules_evidence||{};
324
+ var measured=(lbl==='measured') && ev && Object.keys(ev).length>0;
325
+ var toks=Number(b.total_output_bytes||0);
326
+ var joules=Number(b.total_joules_est||0);
327
+ if(measured && toks>0){
328
+ JT.textContent=(joules/toks).toFixed(4)+' J/tok';
329
+ JTL.innerHTML='<span style="color:var(--green);font-size:9px;border:1px solid var(--green);padding:1px 5px;border-radius:5px">MEASURED · live probe</span>';
330
+ }else{
331
+ JT.textContent=(joules>0&&toks>0)?(joules/toks).toFixed(4)+' J/tok*':'— J/tok';
332
+ JTL.innerHTML='<span style="color:var(--yellow);font-size:9px;border:1px solid var(--yellow);padding:1px 5px;border-radius:5px">ROADMAP · no meter ('+esc3(b.total_joules_est_label||'SAMPLE/ESTIMATE')+')</span>';
333
+ }
334
+ WIN.textContent=(b.all_within_bound===true)?'✓ within (F19/TH6)':(b.all_within_bound===false?'✗ overclaim':'—');
335
+ }
336
+
337
+ var _sovereign=false;
338
+ function applyHealthz(hz){
339
+ var sov=!!(hz&&hz.sovereign===true&&String(hz.inference||'').indexOf('gpu')>=0);
340
+ _sovereign=sov;
341
+ SOV.textContent=hz?(sov?'true · local-GPU probe':'false · router fallback'):'— (healthz unreachable)';
342
+ rebuild();
343
+ }
344
+
345
+ function rebuild(){
346
+ if(!scene)return;
347
+ try{
348
+ scene.graphs=[]; scene.spheres=[]; scene.pulses=[];
349
+ // GPU/serving block + router; honest sovereign label baked into the node label
350
+ var gpuLab=_sovereign?'GPU·SOVEREIGN':'GPU·ROADMAP';
351
+ scene.addGraph({nodes:[
352
+ {id:'router',label:'\u039b-ENGINE'},
353
+ {id:'gpu',label:gpuLab},
354
+ {id:'bek',label:'BEKENSTEIN'}
355
+ ],edges:[
356
+ {id:'e0',from:'router',to:'gpu'},
357
+ {id:'e1',from:'router',to:'bek'}
358
+ ]});
359
+ var lam=(typeof window.__e3dLambda==='number')?window.__e3dLambda:0.62;
360
+ scene.addTrustSphere({lambda:lam}); scene.setLambda(lam);
361
+ }catch(e){}
362
+ }
363
+ function pulse(){ if(scene){ try{ scene.signPulse('e0'); }catch(_){} } }
364
+
365
+ function refresh(){
366
+ getJSON(EA+'/v1/energy/budget').then(applyBudget);
367
+ getJSON(EA+'/code/healthz').then(applyHealthz);
368
+ }
369
+ function start(){
370
+ if(started)return; started=true;
371
+ mount.style.display='block'; offEl.style.display='none'; toggle.textContent='Disable 3D'; toggle.classList.add('active');
372
+ scene=window.SZLHolo.createScene(mount,{sample:false});
373
+ var caps=window.SZLHolo.capabilities();
374
+ CAP.textContent='mode:'+caps.mode+(caps.webgpu?' · webgpu-detected(ROADMAP)':'');
375
+ rebuild(); scene.start();
376
+ refresh();
377
+ poll=setInterval(refresh,30000);
378
+ anim=setInterval(pulse,3000);
379
+ }
380
+ function stop(){
381
+ if(!started)return; started=false;
382
+ mount.style.display='none'; offEl.style.display='block'; toggle.textContent='Enable 3D'; toggle.classList.remove('active');
383
+ if(poll)clearInterval(poll); if(anim)clearInterval(anim);
384
+ try{ if(scene){scene.stop();scene.dispose();} }catch(e){}
385
+ scene=null;
386
+ }
387
+ toggle.addEventListener('click',function(){ started?stop():start(); });
388
+ })();
389
+ </script>
390
+
391
+ </body>
392
+ </html>
web/nemo.html CHANGED
@@ -165,6 +165,34 @@ table.tau th{color:var(--dim);text-transform:uppercase;font-size:9.5px;letter-sp
165
  </div>
166
  </div>
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  <div class="grid2">
169
  <!-- MTP -->
170
  <div class="panel">
@@ -218,6 +246,152 @@ table.tau th{color:var(--dim);text-transform:uppercase;font-size:9.5px;letter-sp
218
  <!-- Shared estate modules (0 CDN; same-origin). Honest labels + receipt verify. -->
219
  <script src="/static/shared/szl_label_engine.js"></script>
220
  <script src="/static/shared/szl_receipt_cosign.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  <script>
222
  const A="/api/a11oy/v1/nemo";
223
  const $=id=>document.getElementById(id);
 
165
  </div>
166
  </div>
167
 
168
+ <!-- ============ 3D ROUTER DASHBOARD (F4 · additive · toggle enhancement; 2D pages above remain the fallback) ============ -->
169
+ <div class="panel" id="router3d_panel">
170
+ <h2>3D Router Dashboard <span class="meta">GPU blocks · particle token-flows · signed MoE routing pulses · live J/token</span>
171
+ <button class="btn ghost" id="r3d_toggle" style="float:right;font-size:11px;">Enable 3D</button>
172
+ </h2>
173
+ <div class="desc">
174
+ Holographic view of the sovereign-inference fleet: each serving <b>tier is a GPU block</b> (color = where/sovereign,
175
+ honest <code>gpu_reachable</code> probe), animated <b>particle token-flows</b> stream from the router to the selected
176
+ domain-expert heads, and a <b>signed routing pulse</b> lights the chosen edge when a route receipt is signed.
177
+ The <b>J/token</b> counter is <b>MEASURED only on a live GPU power probe</b>; with no meter wired it stays an honest
178
+ <b>ROADMAP</b> estimate (never presented as measured). Renders on the shared 0-CDN WebGL2 kit; <b>CPU/old-GPU falls
179
+ back to a 2D canvas</b>; the panels above are the full non-3D experience. Patterns: vLLM/SGLang, NVIDIA Dynamo,
180
+ LiteLLM, RouteLLM.
181
+ </div>
182
+ <div class="r3d-stats mono" style="display:flex;gap:1.2rem;flex-wrap:wrap;font-size:11px;color:var(--muted);margin:.5rem 0;">
183
+ <span>J/token <span id="r3d_jtok" style="color:var(--warn)">—</span> <span id="r3d_jtok_lbl"></span></span>
184
+ <span>tokens routed <span id="r3d_toks" style="color:var(--blue)">0</span></span>
185
+ <span>signed pulses <span id="r3d_pulses" style="color:var(--teal)">0</span></span>
186
+ <span>Λ advisory <span id="r3d_lambda" style="color:var(--cream)">—</span></span>
187
+ <span id="r3d_caps" style="color:var(--dim)"></span>
188
+ </div>
189
+ <div id="router3d_mount" style="width:100%;height:440px;border:1px solid var(--gold-line);border-radius:10px;background:#060606;display:none;position:relative;"></div>
190
+ <div class="empty" id="r3d_off">3D is off (default). Click <b>Enable 3D</b> to render the holographic router on the live <code>/nemo/tiers</code> + <code>/nemo/route</code> + <code>/energy/budget</code> endpoints. The 2D dashboard above is always available as the fallback.</div>
191
+ <div class="meta mono" style="color:var(--dim);font-size:10px;margin-top:.5rem;">
192
+ 0 runtime CDN · WebGL2 baseline + honest 2D fallback · J/token MEASURED-or-ROADMAP (never fabricated) · sovereign:true only on live probe · Λ = Conjecture 1 (&lt;1.0) · trust &lt;100%.
193
+ </div>
194
+ </div>
195
+
196
  <div class="grid2">
197
  <!-- MTP -->
198
  <div class="panel">
 
246
  <!-- Shared estate modules (0 CDN; same-origin). Honest labels + receipt verify. -->
247
  <script src="/static/shared/szl_label_engine.js"></script>
248
  <script src="/static/shared/szl_receipt_cosign.js"></script>
249
+
250
+ <!-- F4 · 3D Router Dashboard logic (additive). Loads the shared 0-CDN holographic kit. -->
251
+ <script src="/static/shared/szl_holo3d.js"></script>
252
+ <script>
253
+ "use strict";
254
+ (function(){
255
+ var A3="/api/a11oy/v1";
256
+ var mount=document.getElementById('router3d_mount');
257
+ var offEl=document.getElementById('r3d_off');
258
+ var toggle=document.getElementById('r3d_toggle');
259
+ if(!mount||!toggle||!window.SZLHolo){ if(toggle){toggle.disabled=true;toggle.textContent='3D unavailable';} return; }
260
+ var scene=null, started=false, poll=null, animTimer=null;
261
+ var tokens=0, pulses=0;
262
+ var GP=document.getElementById('r3d_jtok'), GPL=document.getElementById('r3d_jtok_lbl');
263
+ var TK=document.getElementById('r3d_toks'), PU=document.getElementById('r3d_pulses');
264
+ var LM=document.getElementById('r3d_lambda'), CAP=document.getElementById('r3d_caps');
265
+
266
+ function getJSON(u,opt){return fetch(u,opt).then(function(r){return r.ok?r.json():null;}).catch(function(){return null;});}
267
+
268
+ // Honest J/token: MEASURED only if the budget endpoint reports a real metered
269
+ // joules figure (joules_label !== 'sample' AND a non-empty joules_evidence).
270
+ // Otherwise ROADMAP — never present an estimate as measured.
271
+ function applyJtoken(b){
272
+ if(!b){ GP.textContent='—'; GPL.innerHTML=''; return; }
273
+ var lbl=String(b.joules_label||'').toLowerCase();
274
+ var ev=b.joules_evidence||{};
275
+ var measured=(lbl==='measured') && ev && Object.keys(ev).length>0;
276
+ var toks=Number(b.total_output_bytes||0); // bytes proxy until a token meter exists
277
+ var joules=Number(b.total_joules_est||0);
278
+ var jpt=(measured && toks>0)?(joules/toks):null;
279
+ if(measured && jpt!=null){
280
+ GP.textContent=jpt.toFixed(4)+' J/tok';
281
+ GPL.innerHTML='<span class="pill green" style="font-size:9px">MEASURED · live probe</span>';
282
+ }else{
283
+ // honest ROADMAP: show the SAMPLE estimate context but never claim measured
284
+ GP.textContent=(joules>0&&toks>0)?(joules/toks).toFixed(4)+' J/tok*':'— J/tok';
285
+ GPL.innerHTML='<span class="pill yellow" style="font-size:9px">ROADMAP · no meter ('+esc3(b.total_joules_est_label||'SAMPLE/ESTIMATE')+')</span>';
286
+ }
287
+ }
288
+ function esc3(s){return String(s==null?'':s).replace(/[&<>]/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;'}[c];});}
289
+
290
+ // Build the 3D graph: router hub -> domain-expert heads; GPU blocks = tiers.
291
+ // NOTE: the F1 kit addGraph() consumes edge.from / edge.to (NOT source/target);
292
+ // signPulse(edgeId) matches edge.id. We assign explicit ids so pulses light the
293
+ // correct router->expert edge.
294
+ var _edgeIdFor={};
295
+ function buildGraph(tiers, experts){
296
+ var nodes=[{id:'router',label:'Λ-ROUTER'}];
297
+ var edges=[]; _edgeIdFor={}; var ei=0;
298
+ // GPU/tier blocks as nodes hanging off the router
299
+ (tiers||[]).forEach(function(t,i){
300
+ var sov=(t.sovereign===true);
301
+ var lab=(t.where==='gpu'?'GPU·':'CLOUD·')+(sov?'SOV':'ROADMAP');
302
+ var id='tier'+i;
303
+ nodes.push({id:id,label:lab});
304
+ edges.push({id:'e'+(ei++),from:'router',to:id});
305
+ });
306
+ // domain-expert heads (auditable MoE) from the last route
307
+ (experts||[]).forEach(function(e,i){
308
+ var id='exp_'+(e.expert_id||i);
309
+ var eid='e'+(ei++);
310
+ nodes.push({id:id,label:String(e.title||e.expert_id||('exp'+i)).slice(0,10)});
311
+ edges.push({id:eid,from:'router',to:id});
312
+ _edgeIdFor[id]=eid;
313
+ });
314
+ return {nodes:nodes,edges:edges};
315
+ }
316
+
317
+ var lastExperts=[];
318
+ function rebuild(tiers){
319
+ if(!scene)return;
320
+ try{
321
+ var spec=buildGraph(tiers,lastExperts);
322
+ // clear & re-add graph (kit is additive; we re-create scene graph list)
323
+ scene.graphs=[]; scene.pulses=[]; scene.spheres=[];
324
+ scene.addGraph(spec);
325
+ // drive trust sphere by latest Λ advisory (Conjecture 1, <1.0)
326
+ var lam=window.__r3dLambda; if(typeof lam!=='number')lam=0.62; scene.addTrustSphere({lambda:lam}); scene.setLambda(lam);
327
+ }catch(e){}
328
+ }
329
+
330
+ // Animate particle token-flows: emit signPulses along router->expert edges.
331
+ function tokenFlow(){
332
+ if(!scene||!lastExperts.length)return;
333
+ lastExperts.forEach(function(e){
334
+ var id='exp_'+(e.expert_id);
335
+ var eid=_edgeIdFor[id];
336
+ try{ scene.signPulse(eid!=null?eid:0); }catch(_){}
337
+ tokens+=Math.max(1,Math.round((e.selection_score||1)*4));
338
+ });
339
+ TK.textContent=tokens;
340
+ }
341
+
342
+ // A signed MoE routing pulse fires when a route receipt is signed.
343
+ function signedRoutePulse(d){
344
+ var r=(d&&d.receipt)||{};
345
+ var signed=(r.signatures&&r.signatures.length)||r.signed===true;
346
+ lastExperts=(d&&d.experts)||[];
347
+ var lam=null;
348
+ lastExperts.forEach(function(e){ if(e.lambda_advisory!=null){ lam=(lam==null)?e.lambda_advisory:Math.min(lam,e.lambda_advisory);} });
349
+ if(lam!=null){ window.__r3dLambda=Math.min(0.999,lam); LM.textContent=lam.toFixed(3)+(lam<0.90?' ⚠':''); }
350
+ rebuildLatest();
351
+ if(signed){ pulses++; PU.textContent=pulses; tokenFlow(); }
352
+ }
353
+ var _lastTiers=[];
354
+ function rebuildLatest(){ rebuild(_lastTiers); }
355
+
356
+ function refresh(){
357
+ getJSON(A3+'/nemo/tiers').then(function(t){ _lastTiers=(t&&t.tiers)||[]; rebuildLatest(); });
358
+ getJSON(A3+'/energy/budget').then(applyJtoken);
359
+ }
360
+
361
+ // Route on demand (reuses the page's query box if present); also auto-demo a route.
362
+ function routeOnce(){
363
+ var q=(document.getElementById('q')&&document.getElementById('q').value)||'prove a lemma and refactor the module';
364
+ getJSON(A3+'/nemo/route',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({query:q,top_k:2})})
365
+ .then(function(d){ if(d) signedRoutePulse(d); });
366
+ }
367
+
368
+ function start(){
369
+ if(started)return; started=true;
370
+ mount.style.display='block'; offEl.style.display='none'; toggle.textContent='Disable 3D';
371
+ scene=window.SZLHolo.createScene(mount,{sample:false});
372
+ var caps=window.SZLHolo.capabilities();
373
+ CAP.textContent='mode:'+caps.mode+(caps.webgpu?' · webgpu-detected(ROADMAP)':'');
374
+ scene.addGraph(buildGraph([], []));
375
+ scene.addTrustSphere({lambda:0.62});
376
+ scene.start();
377
+ refresh(); routeOnce();
378
+ poll=setInterval(refresh,30000);
379
+ animTimer=setInterval(tokenFlow,2600);
380
+ // hook the existing "Route & sign" button so a real signed route fires a pulse here too
381
+ var go=document.getElementById('go');
382
+ if(go && !go.__r3d){ go.__r3d=true; go.addEventListener('click',function(){ setTimeout(routeOnce,400); }); }
383
+ }
384
+ function stop(){
385
+ if(!started)return; started=false;
386
+ mount.style.display='none'; offEl.style.display='block'; toggle.textContent='Enable 3D';
387
+ if(poll)clearInterval(poll); if(animTimer)clearInterval(animTimer);
388
+ try{ if(scene)scene.stop(); if(scene)scene.dispose(); }catch(e){}
389
+ scene=null;
390
+ }
391
+ toggle.addEventListener('click',function(){ started?stop():start(); });
392
+ })();
393
+ </script>
394
+
395
  <script>
396
  const A="/api/a11oy/v1/nemo";
397
  const $=id=>document.getElementById(id);