File size: 22,126 Bytes
c18b71e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5684452
55e5019
c18b71e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5684452
55e5019
c18b71e
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>a11oy Command Center</title>
  <meta name="description" content="Governed command on a-11-oy.com. Deny by default. Receipts. Proof lab. Lambda = Conjecture 1. Proof stays on a11oy.net."/>
  <link rel="canonical" href="https://a-11-oy.com/command"/>
  <meta property="og:title" content="a11oy Command Center"/>
  <meta property="og:url" content="https://a-11-oy.com/command"/>
  <style>
    :root {
      --void:#080c14; --panel:#0a1019; --panel-2:#12161d;
      --teal:#3af4c8; --fg:#e8eef6; --muted:#aebccf; --border:#1c2942;
      --deny:#e25c5c; --signal:#e4d3a5; --conjecture:#7b8898;
      --sans: ui-sans-serif, system-ui, sans-serif;
      --display: ui-sans-serif, system-ui, sans-serif;
      --mono: ui-monospace, Menlo, monospace;
    }
    * { box-sizing: border-box; }
    html, body { margin:0; background:var(--void); color:var(--fg); font-family:var(--sans); line-height:1.55; }
    a { color:var(--teal); text-decoration:none; }
    button, [role=button] { cursor:pointer; }
    button:disabled { cursor:not-allowed; opacity:.5; }
    h1,h2,h3 { font-family:var(--display); letter-spacing:-.03em; line-height:1.1; text-wrap:balance; }
    .wrap { max-width:72rem; margin:0 auto; padding:0 1.5rem; }
    header { position:sticky; top:0; z-index:40; border-bottom:1px solid var(--border); background:rgba(8,12,20,.8); backdrop-filter:blur(12px); }
    .bar { display:flex; align-items:center; justify-content:space-between; gap:1rem; min-height:3.5rem; }
    .brand { display:flex; align-items:center; gap:.6rem; color:var(--fg); font-family:var(--display); font-weight:600; letter-spacing:.16em; text-transform:uppercase; font-size:.85rem; }
    .mark { width:.9rem; height:.9rem; background:var(--teal); border-radius:2px; position:relative; }
    .mark::after { content:""; position:absolute; inset:3px; background:var(--void); border-radius:1px; }
    nav a { display:inline-flex; min-height:2.75rem; align-items:center; padding:0 .75rem; color:var(--muted); font-size:.875rem; }
    nav a.active { color:var(--teal); }
    .menu { display:none; min-width:2.75rem; min-height:2.75rem; border:1px solid var(--border); background:transparent; color:var(--fg); border-radius:6px; }
    main { min-height:70dvh; padding:2.5rem 0 4rem; }
    .kicker { font-family:var(--mono); font-size:.7rem; letter-spacing:.18em; text-transform:uppercase; color:var(--teal); }
    .muted { color:var(--muted); }
    .grid { display:grid; gap:.75rem; }
    .g3 { grid-template-columns:repeat(auto-fit,minmax(14rem,1fr)); }
    .g2 { grid-template-columns:repeat(auto-fit,minmax(16rem,1fr)); }
    .card { border:1px solid var(--border); background:var(--panel); border-radius:14px; padding:1.25rem; }
    .badge { display:inline-block; font-family:var(--mono); font-size:.65rem; letter-spacing:.08em; text-transform:uppercase; border:1px solid var(--border); padding:.2rem .45rem; border-radius:4px; color:var(--muted); }
    .badge.MEASURED { color:var(--teal); border-color:rgba(58,244,200,.3); background:rgba(58,244,200,.1); }
    .badge.BLOCKED, .badge.DENY { color:var(--deny); border-color:rgba(226,92,92,.3); background:rgba(226,92,92,.1); }
    .badge.CONJECTURE { color:var(--conjecture); }
    .badge.MODELED, .badge.REPORTED, .badge.SOFTWARE { background:var(--panel-2); }
    .badge.UNAVAILABLE { color:var(--signal); }
    .btn { display:inline-flex; min-height:2.75rem; align-items:center; justify-content:center; gap:.5rem; padding:0 1rem; border-radius:6px; border:1px solid transparent; font-size:.875rem; font-weight:500; }
    .btn-p { background:var(--teal); color:var(--void); }
    .btn-g { background:transparent; color:var(--fg); border-color:var(--border); }
    .btn-q { background:var(--panel-2); color:var(--muted); border-color:var(--border); }
    .skip { position:absolute; left:-999px; }
    .skip:focus { left:1rem; top:1rem; z-index:80; }
    input, select, textarea { width:100%; min-height:2.75rem; background:var(--void); color:var(--fg); border:1px solid var(--border); border-radius:6px; padding:.5rem .75rem; font:inherit; }
    textarea { min-height:6rem; }
    table { width:100%; border-collapse:collapse; font-size:.875rem; }
    th { text-align:left; font-family:var(--mono); font-size:.7rem; color:var(--muted); text-transform:uppercase; padding:.75rem 1rem; }
    td { border-top:1px solid var(--border); padding:.75rem 1rem; }
    footer { border-top:1px solid var(--border); padding:2rem 0; font-size:.75rem; color:var(--muted); }
    .mono { font-family:var(--mono); }
    .ok { color:var(--teal); } .no { color:var(--deny); }
    @media (max-width: 800px) {
      .desk { display:none; }
      .menu { display:inline-flex; }
      .mob { display:none; border-top:1px solid var(--border); padding:.5rem 1rem 1rem; }
      .mob.open { display:block; }
      .mob a { display:flex; min-height:2.75rem; color:var(--muted); }
    }
    @media (prefers-reduced-motion: reduce) { * { animation:none !important; transition:none !important; } }
  </style>
  <link rel="stylesheet" href="/assets/szl-flow.css" data-szl-flow-asset="style" />
  <link rel="stylesheet" href="/assets/szl-holo-v2.css" data-szl-holo-asset="style-v2" />
</head>
<body>
  <a href="#main" class="skip btn btn-p">Skip to main</a>
  <header>
    <div class="wrap bar">
      <a class="brand" href="/command">
        <span class="mark" aria-hidden="true"></span> a11oy <span class="muted" style="letter-spacing:0;font-weight:400">command</span>
      </a>
      <nav class="desk" aria-label="Primary">
        <a href="/command" data-nav="home">Origin</a>
        <a href="/command/console" data-nav="console">Receipts</a>
        <a href="/command/zk" data-nav="zk">Proof</a>
        <a href="/command/census" data-nav="census">Estate</a>
        <a href="/command/invest" data-nav="invest">Investor</a>
        <a href="/command/build" data-nav="build">Developer</a>
        <a href="/console">Operator</a>
      </nav>
      <button class="menu" type="button" aria-label="Menu">☰</button>
    </div>
    <nav class="mob" aria-label="Mobile"></nav>
  </header>
  <main id="main" class="wrap"></main>
  <footer>
    <div class="wrap">
      <p>Product origin <a href="https://a-11-oy.com">a-11-oy.com</a> · Proof <a href="https://a11oy.net">a11oy.net</a> · Λ = Conjecture 1 · Energy UNAVAILABLE in this browser.</p>
      <p class="mono">Signer UNSIGNED-honest · No secret values · This surface is MODELED on static origin until the Space runtime signs a write. Operator console stays at /console.</p>
    </div>
  </footer>
<script>
const P = (1n<<255n)-19n, N = P-1n, G = 2n;
const GENESIS = "0".repeat(64);
const CATALOG = [
  {id:"inspect", title:"Inspect receipt chain", needs:false, d:"ALLOW", reason:"Read-only local verification."},
  {id:"estate", title:"List public estate", needs:false, d:"ALLOW", reason:"Public inventory. Location only."},
  {id:"score", title:"Score Lambda advisory", needs:false, d:"ALLOW", reason:"Advisory organ. Output labeled CONJECTURE."},
  {id:"infer", title:"Governed inference", needs:true, d:"ALLOW", reason:"User-initiated. Static origin cannot spend a key — BLOCKED here."},
  {id:"zk_prove", title:"Seal modeled ZK transcript", needs:true, d:"ALLOW", reason:"Public transcript only."},
  {id:"claim_proven", title:"Declare Lambda proven", needs:false, d:"DENY", reason:"Lambda uniqueness is Conjecture 1."},
  {id:"exfiltrate", title:"Export signer material", needs:false, d:"DENY", reason:"No secret-value disclosure."},
  {id:"unsigned_deploy", title:"Unsigned production push", needs:false, d:"DENY", reason:"Unsigned deploy is outside authority."},
];
const ESTATE = [
  ["a11oy","CORE","Product origin","VERIFIED_CURRENT","SOFTWARE","Python"],
  ["a11oy-net","CORE","Proof registry","VERIFIED_CURRENT","SOFTWARE","HTML"],
  ["lutar-lean","CORE","Lean 4 kernel","VERIFIED_CURRENT","REPORTED","Lean"],
  ["szl-router","CORE","Governed router","VERIFIED_CURRENT","SOFTWARE","Python"],
  ["ouroboros","CORE","Self-host loop","LISTED","REPORTED","Python"],
];
function badge(h){ return `<span class="badge ${h}">${h}</span>`; }
function mod(a,m=P){ const r=a%m; return r<0n?r+m:r; }
function modPow(b,e,m=P){ let x=mod(b,m), k=e<0n?0n:e, o=1n; while(k>0n){ if(k&1n) o=(o*x)%m; x=(x*x)%m; k>>=1n;} return o; }
function randBelow(max){ const buf=new Uint8Array(40); crypto.getRandomValues(buf); let x=0n; for(const n of buf) x=(x<<8n)|BigInt(n); return (x%(max-1n))+1n; }
function hex(n){ return n.toString(16); }
function shortHex(n){ const h=hex(n); return h.length<=16?h:h.slice(0,8)+"…"+h.slice(-4); }
function shortHash(h){ return h.slice(0,8)+"…"+h.slice(-4); }
async function sha256Hex(s){ const b=await crypto.subtle.digest("SHA-256", new TextEncoder().encode(s)); return [...new Uint8Array(b)].map(x=>x.toString(16).padStart(2,"0")).join(""); }
function verifySchnorr(Y,t,c,s){ return modPow(G,s)===mod(t*modPow(Y,c)); }
function simulate(Y){ const c=randBelow(N), s=randBelow(N); const inv=modPow(modPow(Y,c), P-2n); return {t:mod(modPow(G,s)*inv), c, s}; }
function rangeCircuit(x,T,n=8){
  const v=Math.trunc(x); const bits=[]; for(let i=0;i<n;i++) bits.push((v>>i)&1);
  const recon=bits.reduce((a,b,i)=>a+b*2**i,0);
  const cs=[...bits.map((b,i)=>({ok:b===0||b===1,label:`b${i}·(b${i}−1)=0`})),
    {ok:recon===v,label:"x = Σ bᵢ·2ⁱ"}, {ok:v>=0&&v<2**n,label:`0 ≤ x < 2^${n}`}, {ok:v>=T,label:"x ≥ T"}];
  return {cs, sat:cs.every(c=>c.ok)};
}
const LAKE_KEY="a11oy-origin-lake";
function loadLake(){ try { return JSON.parse(localStorage.getItem(LAKE_KEY)||"[]"); } catch { return []; } }
function saveLake(r){ localStorage.setItem(LAKE_KEY, JSON.stringify(r)); }
async function sealReceipt(action, decision, reason, payload=""){
  const receipts=loadLake();
  const prev=receipts.at(-1)?.hash || GENESIS;
  const body={id:crypto.randomUUID(), ts:new Date().toISOString(), action, decision, reason, payload, prev};
  body.hash=await sha256Hex(JSON.stringify(body));
  receipts.push(body); saveLake(receipts); return body;
}
function evaluate(kind, prompt){
  const item=CATALOG.find(c=>c.id===kind); if(!item) return {decision:"DENY", reason:"Unknown. Deny by default."};
  if(item.d==="DENY") return {decision:"DENY", reason:item.reason};
  if(kind==="infer") return {decision:"BLOCKED", reason:"Static origin has no server key. Refused rather than guessed."};
  if(kind==="zk_prove"){
    const t=(prompt||"").trim();
    if(!t) return {decision:"BLOCKED", reason:"Empty transcript."};
    if(/("x"|"secret"|"witness"|"nonce")\s*:/i.test(t)) return {decision:"DENY", reason:"Witness material cannot enter the lake."};
  }
  return {decision:item.d, reason:item.reason};
}

const $ = (sel, el=document) => el.querySelector(sel);
function page(){
  const h = (location.hash || "").replace(/^#\/?/, "");
  if (h) return h.split("/")[0] || "home";
  const p = (location.pathname || "/").replace(/\/+$/, "") || "/";
  if (p === "/command" || p === "/origin" || p === "/") return "home";
  if (p.startsWith("/command/")) return p.slice("/command/".length).split("/")[0] || "home";
  return p.replace(/^\//, "").split("/")[0] || "home";
}
function nav(){
  document.querySelectorAll("[data-nav]").forEach(a => a.classList.toggle("active", a.dataset.nav===page() || (page()==="home"&&a.dataset.nav==="home")));
}
$(".menu").onclick = () => {
  const m=$(".mob"); m.classList.toggle("open");
  if(!m.dataset.ready){ m.innerHTML=[...document.querySelectorAll("nav.desk a")].map(a=>a.outerHTML).join(""); m.dataset.ready="1"; }
};

function home(){
  return `
  <p class="kicker">Product origin · a-11-oy.com</p>
  <h1>Control before capability.</h1>
  <p class="muted" style="max-width:40rem">a11oy Command lives on this host. Deny by default. Hash-chained receipts. Λ is Conjecture 1 — never green. The operator runtime stays at /console. Proof stays on a11oy.net.</p>
  <p style="margin-top:1.5rem;display:flex;gap:.75rem;flex-wrap:wrap">
    <a class="btn btn-p" href="/command/console">Open receipt lake</a>
    <a class="btn btn-g" href="/console">Operator console</a>
    <a class="btn btn-g" href="/command/zk">Proof lab</a>
    <a class="btn btn-g" href="https://a11oy.net">Proof registry</a>
  </p>
  <ul class="grid g3" style="margin-top:2.5rem;list-style:none;padding:0">
    <li class="card"><p class="kicker">Fail closed</p><h3>Deny by default</h3><p class="muted">Unknown acts, unsigned deploys, and proof-closure return DENY with a receipt.</p></li>
    <li class="card"><p class="kicker">Receipts</p><h3>Every change hashed</h3><p class="muted">SHA-256 chain in this browser. Re-verify offline. No secrets in the lake.</p></li>
    <li class="card"><p class="kicker">Honesty</p><h3>Gray over fake live</h3><p class="muted">Energy UNAVAILABLE. Signer UNSIGNED-honest. SNARK proving ROADMAP.</p></li>
  </ul>`;
}
function consolePage(){
  const radios = CATALOG.map((c,i)=>`<label style="display:flex;gap:.6rem;min-height:2.75rem;align-items:center"><input type="radio" name="act" value="${c.id}" ${i===0?"checked":""}/> ${c.title} ${badge(c.d)}</label>`).join("");
  return `<p class="kicker">Receipt lake · local to this browser</p><h1>Act. Get a receipt. Or a denial.</h1>
  <p class="muted">This lake is MODELED in-browser. The operator runtime is <a href="/console">/console</a>.</p>
  <div class="grid g2" style="margin-top:1.5rem">
    <div class="card">
      ${radios}
      <textarea id="prompt" placeholder="Prompt only for inference / ZK seal"></textarea>
      <div style="display:flex;gap:.5rem;margin-top:1rem;flex-wrap:wrap">
        <button class="btn btn-p" id="submit">Submit</button>
        <button class="btn btn-g" id="verify">Verify chain</button>
      </div>
      <p class="muted mono" id="status" style="margin-top:.75rem"></p>
    </div>
    <div class="card" id="lake"></div>
  </div>`;
}
function renderLake(){
  const r=loadLake();
  $("#lake").innerHTML = r.length? `<p class="kicker">${r.length} receipts</p>`+r.slice().reverse().map(x=>`<div style="border-top:1px solid var(--border);padding:.75rem 0"><div>${x.action} ${badge(x.decision)}</div><div class="mono muted">${shortHash(x.hash)}</div></div>`).join("") : `<p class="muted">Empty lake. Submit an allowed act.</p>`;
}
function censusPage(){
  return `<p class="kicker">Estate</p><h1>Location is not quality.</h1>
  <p class="muted">GitHub org szl-holdings · Hub SZLHOLDINGS. Hub RUNNING is not production ready. Labels below are SOFTWARE or REPORTED — not live meters.</p>
  <div class="card" style="margin-top:1.5rem;overflow:auto">
  <table><thead><tr><th>Repo</th><th>Group</th><th>Role</th><th>Disposition</th><th>Honesty</th></tr></thead>
  <tbody>${ESTATE.map(r=>`<tr><td class="mono">${r[0]}</td><td>${r[1]}</td><td>${r[2]}</td><td>${r[3]}</td><td>${badge(r[4])}</td></tr>`).join("")}</tbody></table></div>
  <p class="muted" style="margin-top:1rem">Full census lives on <a href="https://a11oy.net/estate/">a11oy.net/estate</a>.</p>`;
}
function investPage(){
  return `<p class="kicker">Investor map</p><h1>What is sold. What is not.</h1>
  <div class="grid g2" style="margin-top:1.5rem">
    <article class="card"><h3>Sold</h3><p class="muted">Governed command. Receipts. Deny-by-default policy. Honesty classes. A public origin on a-11-oy.com.</p></article>
    <article class="card"><h3>Not sold</h3><p class="muted">Λ proven. Energy meters. Production SNARKs. “Hub RUNNING means ready.” Signer keys.</p></article>
  </div>`;
}
function buildPage(){
  return `<p class="kicker">Developer map</p><h1>Public origin. GitHub is the source of truth.</h1>
  <ol class="muted" style="margin-top:1rem">
    <li>Product origin a-11-oy.com (this host).</li>
    <li>Proof a11oy.net (separate failure domain).</li>
    <li>Source GitHub szl-holdings/a11oy. Promote via Space sync. Not Replit.</li>
    <li>Unsigned deploy DENY. Witness material DENY.</li>
  </ol>`;
}
function zkPage(){
  return `<p class="kicker">Proof lab ${badge("MODELED")}</p>
  <h1>Prove knowledge. Reveal nothing.</h1>
  <p class="muted">Sigma is runnable here. Range circuits satisfy locally. Succinct proving is ROADMAP. Λ stays conjecture. Lasting public RECORD stays on a11oy.net.</p>
  <div style="display:flex;gap:.5rem;margin:1.25rem 0;flex-wrap:wrap">
    <button class="btn btn-g zk-tab active" data-z="sigma">Sigma</button>
    <button class="btn btn-g zk-tab" data-z="snark">SNARKs</button>
  </div>
  <div id="zkroot"></div>`;
}
function sigmaUI(){
  return `<div class="grid g2">
    <div class="card">
      <p class="kicker">Prover</p>
      <div style="display:flex;gap:.5rem;flex-wrap:wrap;margin:.75rem 0">
        <button class="btn btn-g mode" data-m="honest">Honest</button>
        <button class="btn btn-g mode" data-m="cheat">Cheater</button>
        <button class="btn btn-g mode" data-m="simulate">Simulator</button>
      </div>
      <p class="mono muted" id="pstate">Witness: —</p>
      <button class="btn btn-p" id="znext">Mint keypair</button>
      <button class="btn btn-q" id="zreset">Reset</button>
    </div>
    <div class="card">
      <p class="kicker">Verifier · wire</p>
      <p class="mono">g^s ≟ t · Y^c (mod p)</p>
      <p class="mono" id="vstate">No check yet.</p>
    </div>
  </div>`;
}
function snarkUI(){
  return `<div class="card">
    <p class="kicker">Range credential ${badge("MEASURED")}</p>
    <p class="muted">Prove age ≥ T. The value stays off the verifier. MEASURED here means the circuit ran in this browser — not a production SNARK.</p>
    <label class="muted">Age x <input type="number" id="age" value="21" min="0" max="255"/></label>
    <label class="muted">Threshold T <input type="number" id="th" value="18" min="0" max="255"/></label>
    <button class="btn btn-p" id="rsat" style="margin-top:1rem">Satisfy circuit</button>
    <p class="mono" id="rstate" style="margin-top:.75rem">Verifier sees T only. x = hidden.</p>
  </div>
  <p class="muted" style="margin-top:1rem">Groth16 proving ${badge("ROADMAP")}.</p>`;
}

const Z = { mode:"honest", phase:"idle", x:null, Y:null, r:null, t:null, c:null, s:null };
function zLabel(){
  if(Z.phase==="idle") return Z.mode==="simulate"?"Publish Y":Z.mode==="cheat"?"Publish a statement you cannot prove":"Mint keypair";
  if(Z.mode==="simulate"&&Z.phase==="keyed") return "Forge transcript (no witness)";
  if(Z.phase==="keyed") return "Commit t = g^r";
  if(Z.phase==="committed") return "Random challenge";
  if(Z.phase==="challenged") return Z.mode==="cheat"?"Guess a response":"Respond s = r + c·x";
  if(Z.phase==="responded") return "Verify g^s ≟ t·Y^c";
  return null;
}
function bindZk(){
  const root=$("#zkroot"); root.innerHTML=sigmaUI();
  document.querySelectorAll(".zk-tab").forEach(b=>b.onclick=()=>{
    document.querySelectorAll(".zk-tab").forEach(x=>x.classList.remove("active"));
    b.classList.add("active");
    if(b.dataset.z==="snark"){ root.innerHTML=snarkUI(); $("#rsat").onclick=()=>{ const o=rangeCircuit(Number($("#age").value), Number($("#th").value)); $("#rstate").innerHTML = (o.sat?'<span class="ok">CIRCUIT SAT</span>':'<span class="no">CIRCUIT UNSAT</span>')+" · verifier sees T="+$("#th").value+", x=hidden"; }; }
    else bindZk();
  });
  const next=$("#znext"), st=$("#pstate"), vs=$("#vstate");
  document.querySelectorAll(".mode").forEach(b=>b.onclick=()=>{ Z.mode=b.dataset.m; Z.phase="idle"; Z.x=Z.Y=Z.r=Z.t=Z.c=Z.s=null; next.textContent=zLabel(); st.textContent="Witness: —"; vs.textContent="No check yet."; });
  $("#zreset").onclick=()=>{ Z.phase="idle"; Z.x=Z.Y=Z.r=Z.t=Z.c=Z.s=null; next.textContent=zLabel(); };
  next.onclick=()=>{
    if(Z.phase==="idle"){ const x=randBelow(N); Z.Y=modPow(G,x); Z.x=Z.mode==="honest"?x:null; Z.phase="keyed"; st.textContent="Witness: "+(Z.x?"held locally":"absent")+" · Y "+shortHex(Z.Y); }
    else if(Z.mode==="simulate"&&Z.phase==="keyed"){ const f=simulate(Z.Y); Z.t=f.t; Z.c=f.c; Z.s=f.s; const ok=verifySchnorr(Z.Y,Z.t,Z.c,Z.s); vs.innerHTML=ok?'<span class="ok">ACCEPT · unused witness. MODELED zero-knowledge.</span>':'<span class="no">REJECT</span>'; Z.phase="done"; }
    else if(Z.phase==="keyed"){ Z.r=randBelow(N); Z.t=modPow(G,Z.r); Z.phase="committed"; }
    else if(Z.phase==="committed"){ Z.c=randBelow(N); Z.phase="challenged"; }
    else if(Z.phase==="challenged"){ Z.s=Z.mode==="honest"&&Z.x!=null? mod(Z.r+Z.c*Z.x,N): randBelow(N); Z.phase="responded"; }
    else if(Z.phase==="responded"){ const ok=verifySchnorr(Z.Y,Z.t,Z.c,Z.s); vs.innerHTML=ok?'<span class="ok">ACCEPT · equation holds</span>':'<span class="no">REJECT · equation fails</span>'; Z.phase="done"; }
    next.textContent=zLabel()||"Done";
    if(!zLabel()) next.disabled=true; else next.disabled=false;
  };
}

const PAGES = { home, console:consolePage, census:censusPage, invest:investPage, build:buildPage, zk:zkPage };
function render(){
  nav();
  const id = page();
  $("#main").innerHTML = (PAGES[id]||home)();
  if(id==="console"){
    renderLake();
    $("#submit").onclick=async()=>{
      const kind=document.querySelector("[name=act]:checked").value;
      const ev=evaluate(kind, $("#prompt").value);
      await sealReceipt(kind, ev.decision, ev.reason, kind==="zk_prove"?$("#prompt").value.slice(0,4000):"");
      $("#status").textContent=ev.decision+" · "+ev.reason;
      renderLake();
    };
    $("#verify").onclick=async()=>{
      const r=loadLake(); let prev=GENESIS, ok=true;
      for(const x of r){ const {hash, ...rest}=x; const h=await sha256Hex(JSON.stringify(rest)); if(h!==x.hash||x.prev!==prev) ok=false; prev=x.hash; }
      $("#status").textContent=ok?`MEASURED ${r.length} receipts. Chain intact.`:"Chain broken.";
    };
  }
  if(id==="zk") bindZk();
}
window.addEventListener("hashchange", render);
window.addEventListener("popstate", render);
render();
</script>
  <script src="/assets/szl-flow.js" defer data-szl-flow-asset="script"></script>
  <script src="/assets/szl-holo-v2.js" defer data-szl-holo-asset="script-v2"></script>
</body>
</html>