betterwithage commited on
Commit
80872ce
·
verified ·
1 Parent(s): 0e13799

series-a: sync Command Center from merged a11oy#1484 (MEASURED inventory, not a Hub write)

Browse files
routers/series_a_web/app.js CHANGED
@@ -1,5 +1,264 @@
1
  (() => {
2
  "use strict";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  const API = "/api/a11oy/v1/series-a";
4
  const EXECUTION_TIMEOUT_MS = 135000;
5
  const DEFAULT_TARGETS = {
@@ -33,16 +292,11 @@
33
  evaluationRevision += 1;
34
  resetExecution();
35
  };
36
- const sha256 = async (text) => {
37
- const bytes = new TextEncoder().encode(text);
38
- const digest = await crypto.subtle.digest("SHA-256", bytes);
39
- return [...new Uint8Array(digest)].map(value => value.toString(16).padStart(2, "0")).join("");
40
- };
41
  const request = async (path, options = {}, timeoutMs = 8000) => {
42
  const controller = new AbortController();
43
  const timer = setTimeout(() => controller.abort(), timeoutMs);
44
  try {
45
- const response = await fetch(API + path, {cache: "no-store", ...options, signal: controller.signal});
46
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
47
  return await response.json();
48
  } finally { clearTimeout(timer); }
@@ -77,14 +331,14 @@
77
  } : null;
78
  document.getElementById("updated").textContent = status.observed_at ? `Observed ${status.observed_at}` : status.detail || "Terminal state reached";
79
  const list = document.getElementById("receipts");
80
- list.replaceChildren(...(receipts.items || []).slice(0, 8).map(item => {
81
  const li = document.createElement("li");
82
  li.textContent = `${item.kind} · ${item.receipt_hash.slice(0, 14)}… · ${item.envelope.signature_status}`;
83
  return li;
84
  }));
85
  if (!list.children.length) list.innerHTML = "<li>OBSERVED · no receipts yet</li>";
86
  } catch (error) {
87
- ["estate", "trust", "signer"].forEach(key => set(key, error.name === "AbortError" ? "TIMED_OUT" : "UNAVAILABLE"));
88
  document.getElementById("updated").textContent = error.name === "AbortError" ? "Timed out after 8 seconds" : String(error.message || error);
89
  }
90
  };
@@ -98,7 +352,7 @@
98
  }
99
  const evaluated = await request("/passports/evaluate", {
100
  method: "POST",
101
- headers: {"content-type": "application/json"},
102
  body: JSON.stringify({
103
  principal_id: "series-a-ui",
104
  action: {
@@ -107,7 +361,7 @@
107
  impact: "MODERATE",
108
  irreversible: false
109
  },
110
- evidence: [{...currentEvidence}],
111
  expected_if_withheld: "Current estate observation remains unchanged",
112
  expected_if_acted: "A bounded governed estate refresh completes or fails closed"
113
  })
@@ -121,8 +375,8 @@
121
  }
122
  const value = await request("/passports/execute", {
123
  method: "POST",
124
- headers: {"content-type": "application/json"},
125
- body: JSON.stringify({passport_digest: evaluated.passport_digest})
126
  }, EXECUTION_TIMEOUT_MS);
127
  if (value.outcome?.status !== "SUCCEEDED") {
128
  throw new Error(
@@ -160,14 +414,14 @@
160
  });
161
  const evidence = (
162
  selectedLabel === "OBSERVED" && currentEvidence
163
- ) ? [{...currentEvidence}] : [{
164
  evidence_id: "ui-unverified",
165
  label: "UNKNOWN",
166
  content_digest: await sha256(evidenceStatement)
167
  }];
168
  const body = {
169
  principal_id: "series-a-ui",
170
- action: {type: form.get("type"), target: form.get("target"), impact: "MODERATE", irreversible: false},
171
  evidence,
172
  expected_if_withheld: "Current state persists",
173
  expected_if_acted: "Bounded action completes or fails closed"
@@ -175,12 +429,12 @@
175
  const output = document.getElementById("passport-result");
176
  output.textContent = "EVALUATING";
177
  try {
178
- const value = await request("/passports/evaluate", {method: "POST", headers: {"content-type": "application/json"}, body: JSON.stringify(body)});
179
  if (revision !== evaluationRevision) {
180
  output.textContent = "STALE · form changed before evaluation completed";
181
  return;
182
  }
183
- output.textContent = JSON.stringify({decision: value.passport.decision, reason_codes: value.passport.reason_codes, passport_digest: value.passport_digest, signature_status: value.decision_receipt.envelope.signature_status}, null, 2);
184
  if (value.passport.decision === "ALLOW") {
185
  executableDigest = value.passport_digest;
186
  executeButton.disabled = false;
@@ -209,9 +463,9 @@
209
  const value = await request(
210
  `/passports/outcomes/${encodeURIComponent(passportDigest)}`
211
  );
212
- return {outcome: value.outcome, receipt: value.outcome_receipt};
213
  } catch {}
214
- await new Promise(resolve => setTimeout(resolve, 2000));
215
  }
216
  return null;
217
  };
@@ -224,8 +478,8 @@
224
  try {
225
  const value = await request("/passports/execute", {
226
  method: "POST",
227
- headers: {"content-type": "application/json"},
228
- body: JSON.stringify({passport_digest: passportDigest})
229
  }, EXECUTION_TIMEOUT_MS);
230
  renderOutcome(value.outcome, value.outcome_receipt);
231
  } catch (error) {
@@ -251,7 +505,7 @@
251
  };
252
  if ("EventSource" in window) {
253
  const source = new EventSource(API + "/events");
254
- EVENT_KINDS.forEach(kind => source.addEventListener(kind, appendEvent));
255
  source.addEventListener("open", () => {
256
  if (eventRail.firstElementChild?.textContent === "CONNECTING") {
257
  eventRail.firstElementChild.textContent = "CONNECTED · waiting for governed events";
 
1
  (() => {
2
  "use strict";
3
+
4
+ const VIEWS = ["command", "estate", "claims", "policy", "twin", "passport", "audit"];
5
+ const KEEP = [
6
+ { id: "SZLHOLDINGS/README", role: "Org card", href: "https://huggingface.co/SZLHOLDINGS" },
7
+ { id: "SZLHOLDINGS/a11oy", role: "Product Command Center", href: "https://a-11-oy.com" },
8
+ { id: "SZLHOLDINGS/killinchu", role: "Defense vertical", href: "https://huggingface.co/spaces/SZLHOLDINGS/killinchu" },
9
+ { id: "SZLHOLDINGS/immune", role: "Safety kernel", href: "https://huggingface.co/spaces/SZLHOLDINGS/immune" },
10
+ { id: "SZLHOLDINGS/szl-khipu", role: "Model demo", href: "https://huggingface.co/spaces/SZLHOLDINGS/szl-khipu" },
11
+ { id: "SZLHOLDINGS/szl-atelier", role: "Artifact walk", href: "https://huggingface.co/spaces/SZLHOLDINGS/szl-atelier" },
12
+ { id: "SZLHOLDINGS/governed-receipt-verifier", role: "Receipt replay", href: "https://a11oy.net" }
13
+ ];
14
+ const CLAIMS = [
15
+ ["CLM-ORG-REPOS", "Public GitHub repositories in org szl-holdings", "98", "LIVE"],
16
+ ["CLM-HF-MODELS", "Hugging Face models under SZLHOLDINGS", "43", "LIVE"],
17
+ ["CLM-HF-SPACES", "Hugging Face Spaces under SZLHOLDINGS", "authenticated 7 including README card · unauth author-list 6 · 45 total", "MEASURED"],
18
+ ["CLM-HF-DATASETS", "Hugging Face datasets under SZLHOLDINGS", "36", "LIVE"],
19
+ ["CLM-KHIPU-DL", "SZL-Khipu-1.5B downloads", "502", "LIVE"],
20
+ ["CLM-KHIPU-GGUF-DL", "SZL-Khipu-1.5B-GGUF downloads", "553", "LIVE"],
21
+ ["CLM-FORGE-DL", "SZL-Forge-1.5B-ReceiptAgent downloads", "493", "LIVE"],
22
+ ["CLM-NORM-DL", "szl-governed-norm downloads", "209", "SNAPSHOT"],
23
+ ["CLM-LAMBDA-DL", "szl-lambda-gate downloads", "100", "SNAPSHOT"],
24
+ ["CLM-TABLES-848", "Database tables (verified 2026-05-12)", "848", "SNAPSHOT"],
25
+ ["CLM-ENDPOINTS-5524", "API endpoint declarations (verified 2026-05-12)", "5,524", "SNAPSHOT"],
26
+ ["CLM-TESTS-1220", "Passing platform tests (verified 2026-05-12)", "1,220", "SNAPSHOT"],
27
+ ["CLM-LAMBDA-MS", "Λ overhead median (verified 2026-05-12)", "≤0.59 ms", "SNAPSHOT"],
28
+ ["CLM-DOCTRINE", "Doctrine lock", "v11 LOCKED", "LIVE"],
29
+ ["CLM-LAMBDA", "Λ uniqueness", "Conjecture 1 — not a closed theorem", "LIVE"],
30
+ ["CLM-LOCKED-8", "Locked-proven formulas", "8 · F1 F4 F7 F11 F12 F18 F19 F22", "LIVE"],
31
+ ["CLM-LEAN-COUNTS", "lutar-lean pin (declarations / axioms / sorries)", "749 / 14 / 163", "LIVE"],
32
+ ["CLM-TRUST-CEILING", "Trust ceiling claimed on README", "omitted from /honest", "SNAPSHOT"],
33
+ ["CLM-SHARE-LINKS", "Four ChatGPT share-link transcripts", "unfetchable", "UNKNOWN"],
34
+ ["CLM-HF-WRITE", "Hugging Face write from this surface", "not claimed here", "UNAVAILABLE"],
35
+ ["CLM-BOSS", "boss.technologies identity", "BLOCKED_IDENTITY", "UNKNOWN"]
36
+ ];
37
+ const CONTRADICTIONS = [
38
+ ["CTR-KHIPU-DL", "BLOCKER", "Khipu download count", "Prior ~2.36k vs live 502. Retire 2.36k."],
39
+ ["CTR-LEXICON", "HIGH", "Naming lexicon drift", "Canonical: a11oy — governed execution fabric. Product a-11-oy.com · proof a11oy.net · never a11oy.com."],
40
+ ["CTR-STALE-METRICS", "HIGH", "May 12 platform metrics", "848 tables / 5,524 endpoints / 1,220 tests / ≤0.59 ms Λ stay SNAPSHOT."],
41
+ ["CTR-SPACES", "MEDIUM", "Keep-set listings disagree", "Unauth author-list is 6. Authenticated recapture includes README card. This page does not rewrite the published atlas."],
42
+ ["CTR-SPRAWL", "MEDIUM", "Repo sprawl vs canonical home", "98 repositories. Bind as packages. Do not mint a new flagship."],
43
+ ["CTR-TRUST-CEILING", "LOW", "Trust ceiling missing from honest API", "Either emit it from /honest or stop putting it on the first fold."]
44
+ ];
45
+ const TOOLS = [
46
+ ["echo.ping", "NONE", "admitted"],
47
+ ["receipt.inspect", "NONE", "admitted"],
48
+ ["policy.propose", "NONE", "operator"],
49
+ ["aql.query", "NONE", "admitted"],
50
+ ["fixture.rollback", "REVERSIBLE", "operator"],
51
+ ["prod.write", "IRREVERSIBLE", "prohibited"],
52
+ ["shell.exec", "IRREVERSIBLE", "prohibited"]
53
+ ];
54
+ const AUDIT = [
55
+ ["B1", "LIVE", "Canonical home is szl-holdings/a11oy — no 20th repo minted this session"],
56
+ ["B2", "LIVE", "Lexicon locked: a11oy — governed execution fabric"],
57
+ ["B3", "LIVE", "Claims ledger exists; May 12 metrics degraded to SNAPSHOT"],
58
+ ["B4", "LIVE", "Contradiction ledger scored separately from claims"],
59
+ ["B5", "LIVE", "Vertical slice uses a fixture tool, never a production connector"],
60
+ ["B6", "LIVE", "Receipts DEMO_SIGNED; DSSE UNAVAILABLE; PENDING_SYNC visible"],
61
+ ["B7", "MEASURED", "This page does not claim a Hub write. Authenticated recapture includes the README card; unauth author-list omits it."],
62
+ ["B8", "UNKNOWN", "ChatGPT share-link transcripts UNKNOWN — not fabricated"],
63
+ ["B9", "MODELED", "Bricklayer IP-risk register populated before policy-enforcement code"],
64
+ ["B10", "LIVE", "Calibration Plane proposal-only, outside TCB"]
65
+ ];
66
+
67
+ const badgeClass = (state) => {
68
+ const key = String(state || "").toLowerCase();
69
+ if (key === "live" || key === "admitted" || key === "measured") return "badge live";
70
+ if (key === "snapshot" || key === "modeled") return "badge snapshot";
71
+ if (key === "blocker" || key === "prohibited") return "badge blocker";
72
+ return "badge unknown";
73
+ };
74
+
75
+ const showView = () => {
76
+ const raw = (location.hash || "#command").replace(/^#/, "");
77
+ const view = VIEWS.includes(raw) ? raw : "command";
78
+ document.querySelectorAll("[data-view]").forEach((el) => {
79
+ el.hidden = el.getAttribute("data-view") !== view;
80
+ });
81
+ document.querySelectorAll("[data-nav]").forEach((a) => {
82
+ if (a.getAttribute("data-nav") === view) a.setAttribute("aria-current", "page");
83
+ else a.removeAttribute("aria-current");
84
+ });
85
+ };
86
+ window.addEventListener("hashchange", showView);
87
+ showView();
88
+
89
+ const fillList = (id, rows, render) => {
90
+ const node = document.getElementById(id);
91
+ if (!node) return;
92
+ node.replaceChildren(...rows.map(render));
93
+ };
94
+ fillList("keep-list", KEEP, (item) => {
95
+ const li = document.createElement("li");
96
+ const a = document.createElement("a");
97
+ a.className = "id";
98
+ a.href = item.href;
99
+ a.textContent = item.id;
100
+ if (/^https?:/.test(item.href)) {
101
+ a.target = "_blank";
102
+ a.rel = "noopener";
103
+ }
104
+ const role = document.createElement("span");
105
+ role.className = "badge live";
106
+ role.textContent = item.role;
107
+ li.append(a, role);
108
+ return li;
109
+ });
110
+ const claimsBody = document.getElementById("claims-body");
111
+ if (claimsBody) {
112
+ claimsBody.replaceChildren(...CLAIMS.map(([id, statement, value, state]) => {
113
+ const tr = document.createElement("tr");
114
+ const cells = [id, statement, value, state];
115
+ cells.forEach((text, index) => {
116
+ const td = document.createElement("td");
117
+ if (index === 3) {
118
+ const span = document.createElement("span");
119
+ span.className = badgeClass(state);
120
+ span.textContent = state;
121
+ td.append(span);
122
+ } else {
123
+ td.textContent = text;
124
+ }
125
+ tr.append(td);
126
+ });
127
+ return tr;
128
+ }));
129
+ }
130
+ fillList("ctr-list", CONTRADICTIONS, ([id, sev, title, remedy]) => {
131
+ const li = document.createElement("li");
132
+ const name = document.createElement("span");
133
+ name.className = "id";
134
+ name.textContent = `${id} · ${title}`;
135
+ const note = document.createElement("span");
136
+ note.textContent = remedy;
137
+ note.style.color = "var(--muted)";
138
+ note.style.flex = "1";
139
+ const badge = document.createElement("span");
140
+ badge.className = badgeClass(sev);
141
+ badge.textContent = sev;
142
+ li.append(name, note, badge);
143
+ return li;
144
+ });
145
+ fillList("tools-list", TOOLS, ([name, cls, status]) => {
146
+ const li = document.createElement("li");
147
+ const id = document.createElement("span");
148
+ id.className = "id";
149
+ id.textContent = `${name} · ${cls}`;
150
+ const badge = document.createElement("span");
151
+ badge.className = badgeClass(status);
152
+ badge.textContent = status;
153
+ li.append(id, badge);
154
+ return li;
155
+ });
156
+ fillList("audit-list", AUDIT, ([id, state, item]) => {
157
+ const li = document.createElement("li");
158
+ const idEl = document.createElement("span");
159
+ idEl.className = "id";
160
+ idEl.textContent = `${id} · ${item}`;
161
+ const badge = document.createElement("span");
162
+ badge.className = badgeClass(state);
163
+ badge.textContent = state;
164
+ li.append(idEl, badge);
165
+ return li;
166
+ });
167
+
168
+ const encoder = new TextEncoder();
169
+ const sha256 = async (text) => {
170
+ const digest = await crypto.subtle.digest("SHA-256", encoder.encode(text));
171
+ return [...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join("");
172
+ };
173
+ const hmacHex = async (keyRaw, msg) => {
174
+ const key = await crypto.subtle.importKey(
175
+ "raw",
176
+ encoder.encode(keyRaw),
177
+ { name: "HMAC", hash: "SHA-256" },
178
+ false,
179
+ ["sign"]
180
+ );
181
+ const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(msg));
182
+ return [...new Uint8Array(sig)].map((value) => value.toString(16).padStart(2, "0")).join("");
183
+ };
184
+ const getDemoKey = async () => {
185
+ const existing = localStorage.getItem("a11oy.demo-key");
186
+ if (existing) return existing;
187
+ const bytes = crypto.getRandomValues(new Uint8Array(32));
188
+ const key = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
189
+ localStorage.setItem("a11oy.demo-key", key);
190
+ return key;
191
+ };
192
+ const FIXTURE = {
193
+ id: "acc-fixture-v1",
194
+ version: "1.0.0",
195
+ expiresAt: "2026-11-27T16:00:00Z",
196
+ prohibited: ["shell.exec", "prod.write", "unsloth.codex"],
197
+ tools: {
198
+ "echo.ping": { allowed: true, cls: "NONE" },
199
+ "receipt.inspect": { allowed: true, cls: "NONE" },
200
+ "policy.propose": { allowed: true, cls: "NONE" },
201
+ "shell.exec": { allowed: false, cls: "IRREVERSIBLE" },
202
+ "prod.write": { allowed: false, cls: "IRREVERSIBLE" }
203
+ }
204
+ };
205
+ const twinForm = document.getElementById("twin-form");
206
+ if (twinForm) {
207
+ twinForm.addEventListener("submit", async (event) => {
208
+ event.preventDefault();
209
+ const tool = document.getElementById("twin-tool").value;
210
+ const arg = document.getElementById("twin-arg").value;
211
+ const spec = FIXTURE.tools[tool];
212
+ const expired = Date.parse(FIXTURE.expiresAt) < Date.now();
213
+ let decision = "ALLOW";
214
+ let reason = "Constitution admits the fixture.";
215
+ if (expired) {
216
+ decision = "DENY";
217
+ reason = "Constitution expired. Authority that cannot go stale cannot be audited.";
218
+ } else if (!spec) {
219
+ decision = "DENY";
220
+ reason = `Unknown tool ${tool}. Deny by default.`;
221
+ } else if (!spec.allowed || FIXTURE.prohibited.includes(tool)) {
222
+ decision = "DENY";
223
+ reason = `${tool} is prohibited. Not in the trusted computing base of this slice.`;
224
+ }
225
+ let payload;
226
+ if (decision === "ALLOW") {
227
+ if (tool === "echo.ping") payload = { pong: arg || "ok", ts: new Date().toISOString() };
228
+ else if (tool === "receipt.inspect") payload = { target: arg, intact: true };
229
+ else payload = { proposal: arg, executed: false, note: "Propose never execute." };
230
+ } else {
231
+ payload = { denied: tool, reason };
232
+ }
233
+ const payloadStr = JSON.stringify(payload);
234
+ const payloadHash = await sha256(payloadStr);
235
+ const body = JSON.stringify({
236
+ tool,
237
+ decision,
238
+ constitutionId: FIXTURE.id,
239
+ constitutionVersion: FIXTURE.version,
240
+ payloadHash
241
+ });
242
+ const receiptHash = await sha256(body);
243
+ const signature = await hmacHex(await getDemoKey(), receiptHash);
244
+ document.getElementById("twin-result").textContent = JSON.stringify({
245
+ tool,
246
+ decision,
247
+ reason,
248
+ payload,
249
+ payloadHash,
250
+ receiptHash,
251
+ signature,
252
+ signatureState: "DEMO_SIGNED",
253
+ dsseEnvelope: "UNAVAILABLE",
254
+ flightStatus: "LOCAL",
255
+ blastRadius: spec && spec.cls === "NONE"
256
+ ? "In-memory fixture only. No network, no disk, no prod."
257
+ : "Would touch production. Refused in this slice."
258
+ }, null, 2);
259
+ });
260
+ }
261
+
262
  const API = "/api/a11oy/v1/series-a";
263
  const EXECUTION_TIMEOUT_MS = 135000;
264
  const DEFAULT_TARGETS = {
 
292
  evaluationRevision += 1;
293
  resetExecution();
294
  };
 
 
 
 
 
295
  const request = async (path, options = {}, timeoutMs = 8000) => {
296
  const controller = new AbortController();
297
  const timer = setTimeout(() => controller.abort(), timeoutMs);
298
  try {
299
+ const response = await fetch(API + path, { cache: "no-store", ...options, signal: controller.signal });
300
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
301
  return await response.json();
302
  } finally { clearTimeout(timer); }
 
331
  } : null;
332
  document.getElementById("updated").textContent = status.observed_at ? `Observed ${status.observed_at}` : status.detail || "Terminal state reached";
333
  const list = document.getElementById("receipts");
334
+ list.replaceChildren(...(receipts.items || []).slice(0, 8).map((item) => {
335
  const li = document.createElement("li");
336
  li.textContent = `${item.kind} · ${item.receipt_hash.slice(0, 14)}… · ${item.envelope.signature_status}`;
337
  return li;
338
  }));
339
  if (!list.children.length) list.innerHTML = "<li>OBSERVED · no receipts yet</li>";
340
  } catch (error) {
341
+ ["estate", "trust", "signer"].forEach((key) => set(key, error.name === "AbortError" ? "TIMED_OUT" : "UNAVAILABLE"));
342
  document.getElementById("updated").textContent = error.name === "AbortError" ? "Timed out after 8 seconds" : String(error.message || error);
343
  }
344
  };
 
352
  }
353
  const evaluated = await request("/passports/evaluate", {
354
  method: "POST",
355
+ headers: { "content-type": "application/json" },
356
  body: JSON.stringify({
357
  principal_id: "series-a-ui",
358
  action: {
 
361
  impact: "MODERATE",
362
  irreversible: false
363
  },
364
+ evidence: [{ ...currentEvidence }],
365
  expected_if_withheld: "Current estate observation remains unchanged",
366
  expected_if_acted: "A bounded governed estate refresh completes or fails closed"
367
  })
 
375
  }
376
  const value = await request("/passports/execute", {
377
  method: "POST",
378
+ headers: { "content-type": "application/json" },
379
+ body: JSON.stringify({ passport_digest: evaluated.passport_digest })
380
  }, EXECUTION_TIMEOUT_MS);
381
  if (value.outcome?.status !== "SUCCEEDED") {
382
  throw new Error(
 
414
  });
415
  const evidence = (
416
  selectedLabel === "OBSERVED" && currentEvidence
417
+ ) ? [{ ...currentEvidence }] : [{
418
  evidence_id: "ui-unverified",
419
  label: "UNKNOWN",
420
  content_digest: await sha256(evidenceStatement)
421
  }];
422
  const body = {
423
  principal_id: "series-a-ui",
424
+ action: { type: form.get("type"), target: form.get("target"), impact: "MODERATE", irreversible: false },
425
  evidence,
426
  expected_if_withheld: "Current state persists",
427
  expected_if_acted: "Bounded action completes or fails closed"
 
429
  const output = document.getElementById("passport-result");
430
  output.textContent = "EVALUATING";
431
  try {
432
+ const value = await request("/passports/evaluate", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
433
  if (revision !== evaluationRevision) {
434
  output.textContent = "STALE · form changed before evaluation completed";
435
  return;
436
  }
437
+ output.textContent = JSON.stringify({ decision: value.passport.decision, reason_codes: value.passport.reason_codes, passport_digest: value.passport_digest, signature_status: value.decision_receipt.envelope.signature_status }, null, 2);
438
  if (value.passport.decision === "ALLOW") {
439
  executableDigest = value.passport_digest;
440
  executeButton.disabled = false;
 
463
  const value = await request(
464
  `/passports/outcomes/${encodeURIComponent(passportDigest)}`
465
  );
466
+ return { outcome: value.outcome, receipt: value.outcome_receipt };
467
  } catch {}
468
+ await new Promise((resolve) => setTimeout(resolve, 2000));
469
  }
470
  return null;
471
  };
 
478
  try {
479
  const value = await request("/passports/execute", {
480
  method: "POST",
481
+ headers: { "content-type": "application/json" },
482
+ body: JSON.stringify({ passport_digest: passportDigest })
483
  }, EXECUTION_TIMEOUT_MS);
484
  renderOutcome(value.outcome, value.outcome_receipt);
485
  } catch (error) {
 
505
  };
506
  if ("EventSource" in window) {
507
  const source = new EventSource(API + "/events");
508
+ EVENT_KINDS.forEach((kind) => source.addEventListener(kind, appendEvent));
509
  source.addEventListener("open", () => {
510
  if (eventRail.firstElementChild?.textContent === "CONNECTING") {
511
  eventRail.firstElementChild.textContent = "CONNECTED · waiting for governed events";
routers/series_a_web/index.html CHANGED
@@ -3,56 +3,163 @@
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
- <title>A11oy Series-A Live Control Plane</title>
 
 
 
 
7
  <link rel="stylesheet" href="/series-a/styles.css?v=__STYLE_ASSET_DIGEST__">
8
  </head>
9
  <body>
10
- <a class="skip" href="#main">Skip to control plane</a>
11
- <header>
12
- <p class="eyebrow">A11OY · ACTION ASSURANCE</p>
13
- <h1>Series‑A Live Control Plane</h1>
14
- <p>Current estate truth, counterfactual action passports, signed receipts, and one-attempt execution—observed, not assumed.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  </header>
 
16
  <main id="main" tabindex="-1">
17
- <section class="toolbar" aria-label="Control plane actions">
18
  <button id="refresh" type="button">Run governed refresh</button>
19
  <span id="updated" aria-live="polite">Not observed yet</span>
20
  </section>
21
- <section class="grid" id="cards" aria-label="Live status">
 
22
  <article><h2>Estate</h2><strong data-key="estate">CHECKING</strong><p>Signed current observation.</p></article>
23
  <article><h2>GitHub repos</h2><strong data-key="repos">—</strong><p>Complete paginated census.</p></article>
24
  <article><h2>Open PRs</h2><strong data-key="prs">—</strong><p>Current public queue.</p></article>
25
- <article><h2>HF Spaces</h2><strong data-key="spaces">—</strong><p>One canonical A11oy required.</p></article>
26
  <article><h2>Models</h2><strong data-key="models">—</strong><p>Official Hub listing.</p></article>
27
  <article><h2>Datasets</h2><strong data-key="datasets">—</strong><p>Official Hub listing.</p></article>
28
  <article><h2>Trust factor</h2><strong data-key="trust">CHECKING</strong><p>Derived from signed local decisions.</p></article>
29
  <article><h2>Receipt signer</h2><strong data-key="signer">CHECKING</strong><p>Persistent P‑256 preferred.</p></article>
30
  </section>
31
- <section class="panel">
32
- <h2>Counterfactual Action Passport</h2>
33
- <form id="passport">
34
- <label>Action
35
- <select name="type"><option value="estate.refresh">Refresh estate truth</option><option value="probe.public_surface">Probe public surface</option></select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  </label>
37
- <label>Target <input name="target" value="szl://estate/current" required></label>
38
- <label>Evidence label <select name="label"><option value="OBSERVED">OBSERVED · server-signed snapshot</option><option value="UNKNOWN">UNKNOWN · fail-closed demo</option></select></label>
39
- <button type="submit">Evaluate passport</button>
40
- <button id="execute" type="button" disabled>Execute authorized action</button>
41
  </form>
42
- <pre id="passport-result" aria-live="polite">No passport evaluated.</pre>
43
- <pre id="execution-result" aria-live="polite">No authorized execution attempted.</pre>
44
- <p>Execution is bounded to one attempt and is enabled only for an ALLOW passport.</p>
45
  </section>
46
- <section class="panel">
47
- <h2>Live event rail</h2>
48
- <ol id="events" aria-live="polite"><li>CONNECTING</li></ol>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  </section>
50
- <section class="panel">
51
- <h2>Latest signed receipts</h2>
52
- <ol id="receipts"><li>CHECKING</li></ol>
 
 
53
  </section>
54
  </main>
55
- <footer>MEASURED · REPORTED · MODELED · UNKNOWN · UNAVAILABLE · no private reasoning collected</footer>
 
 
 
 
 
 
 
 
 
 
56
  <script src="/series-a/app.js?v=__APP_ASSET_DIGEST__" defer></script>
57
  </body>
58
  </html>
 
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta name="theme-color" content="#080c14">
7
+ <meta name="color-scheme" content="dark">
8
+ <title>a11oy Series-A Command Center</title>
9
+ <meta name="description" content="Control before action. Evidence after. Series-A command center on a-11-oy.com — keep-7 Hub estate, claims ledger, fixture constitution, signed passports. Proof lives at a11oy.net.">
10
+ <link rel="canonical" href="https://a-11-oy.com/series-a">
11
  <link rel="stylesheet" href="/series-a/styles.css?v=__STYLE_ASSET_DIGEST__">
12
  </head>
13
  <body>
14
+ <a class="skip" href="#main">Skip to command center</a>
15
+ <header class="topbar">
16
+ <div class="wrap nav">
17
+ <a class="wordmark" href="https://a-11-oy.com/">
18
+ <span class="glyph" aria-hidden="true">Λ</span>
19
+ <span class="scope"><span class="scope-name">A11OY</span><span class="scope-sep">/</span><span class="scope-role">SERIES-A</span></span>
20
+ </a>
21
+ <div class="nav-chips">
22
+ <span class="chip live"><span class="dot"></span>PRODUCT</span>
23
+ <span class="chip honesty"><span class="dot"></span>DEMO_SIGNED · fixture</span>
24
+ </div>
25
+ <nav class="tabs" aria-label="Command views">
26
+ <a href="#command" data-nav="command" aria-current="page">Command</a>
27
+ <a href="#estate" data-nav="estate">Estate</a>
28
+ <a href="#claims" data-nav="claims">Claims</a>
29
+ <a href="#policy" data-nav="policy">Policy</a>
30
+ <a href="#twin" data-nav="twin">Twin</a>
31
+ <a href="#passport" data-nav="passport">Passport</a>
32
+ <a href="#audit" data-nav="audit">Audit</a>
33
+ </nav>
34
+ <span class="origin-switch" aria-label="Origins">
35
+ <a class="origin-product" href="https://a-11-oy.com/" aria-current="true">Product</a>
36
+ <span class="origin-sep" aria-hidden="true">|</span>
37
+ <a href="https://a11oy.net/" target="_blank" rel="noopener">Proof ↗</a>
38
+ </span>
39
+ </div>
40
  </header>
41
+
42
  <main id="main" tabindex="-1">
43
+ <section class="toolbar wrap" aria-label="Live observation">
44
  <button id="refresh" type="button">Run governed refresh</button>
45
  <span id="updated" aria-live="polite">Not observed yet</span>
46
  </section>
47
+
48
+ <section class="grid wrap" id="cards" aria-label="Live status">
49
  <article><h2>Estate</h2><strong data-key="estate">CHECKING</strong><p>Signed current observation.</p></article>
50
  <article><h2>GitHub repos</h2><strong data-key="repos">—</strong><p>Complete paginated census.</p></article>
51
  <article><h2>Open PRs</h2><strong data-key="prs">—</strong><p>Current public queue.</p></article>
52
+ <article><h2>HF Spaces</h2><strong data-key="spaces">—</strong><p>Org census from signed observation.</p></article>
53
  <article><h2>Models</h2><strong data-key="models">—</strong><p>Official Hub listing.</p></article>
54
  <article><h2>Datasets</h2><strong data-key="datasets">—</strong><p>Official Hub listing.</p></article>
55
  <article><h2>Trust factor</h2><strong data-key="trust">CHECKING</strong><p>Derived from signed local decisions.</p></article>
56
  <article><h2>Receipt signer</h2><strong data-key="signer">CHECKING</strong><p>Persistent P‑256 preferred.</p></article>
57
  </section>
58
+
59
+ <section class="view wrap" data-view="command">
60
+ <p class="eyebrow">Ninety seconds · Series‑A Live Control Plane</p>
61
+ <h1>Control before action. <span class="muted">Evidence after.</span></h1>
62
+ <p class="lede">Operators, security, and compliance who cannot take a model’s word for what it did. Four primitives sit in the path: Capability Constitution, Assurance Query Language, Operational Twin, Policy-to-Evidence Coverage Graph.</p>
63
+ <dl class="facts">
64
+ <div><dt>What is live</dt><dd>This origin is the product. Proof is a11oy.net. Receipts on the Twin tab from the fixture are DEMO_SIGNED. Passports below are the live Series-A controller. This page does not write the Hub and does not rewrite the published atlas keep-set.</dd></div>
65
+ <div><dt>OSS vs product</dt><dd>Source github.com/szl-holdings/a11oy (Apache-2.0). Hugging Face is the artifact registry, not the front door.</dd></div>
66
+ <div><dt>Why now</dt><dd>Agents are being handed tools. Ungoverned tool authority is the incident. Governance has to live in the path, not in a slide.</dd></div>
67
+ </dl>
68
+ </section>
69
+
70
+ <section class="view wrap" data-view="estate" hidden>
71
+ <p class="eyebrow">Hub inventory · MEASURED</p>
72
+ <h1>Operator keep-set. This page does not write the Hub.</h1>
73
+ <p class="lede">Product lives here. Proof lives at a11oy.net. Archive means pause and private — never delete. Authenticated recapture includes the org README card. Unauthenticated author-list omits README. This surface does not rewrite the published atlas keep-set.</p>
74
+ <ul class="keep" id="keep-list"></ul>
75
+ <p class="bound">amaru / sentra / vessels / rosie do not exist as Spaces. Collection: canonical-public-spaces-7. Live totals come from the signed observation above, not from a Hub write on this page.</p>
76
+ </section>
77
+
78
+ <section class="view wrap" data-view="claims" hidden>
79
+ <p class="eyebrow">P0 · Claims ledger</p>
80
+ <h1>Every public number has a home.</h1>
81
+ <p class="lede">Marketing copy reads this ledger. Default is UNKNOWN. Stale May 12 metrics stay SNAPSHOT.</p>
82
+ <div class="table-wrap">
83
+ <table class="ledger">
84
+ <thead><tr><th>ID</th><th>Statement</th><th>Value</th><th>State</th></tr></thead>
85
+ <tbody id="claims-body"></tbody>
86
+ </table>
87
+ </div>
88
+ <h2 class="sub">Contradictions</h2>
89
+ <ul class="keep" id="ctr-list"></ul>
90
+ </section>
91
+
92
+ <section class="view wrap" data-view="policy" hidden>
93
+ <p class="eyebrow">acc-fixture-v1 · expires 2026-11-27</p>
94
+ <h1>Authority that cannot expire cannot be audited.</h1>
95
+ <p class="lede">Two-person mode. Writes per run: 0. Prohibited: shell.exec, prod.write, unsloth.codex. This constitution is the fixture slice, not a production MCP gateway.</p>
96
+ <ul class="keep" id="tools-list"></ul>
97
+ </section>
98
+
99
+ <section class="view wrap" data-view="twin" hidden>
100
+ <p class="eyebrow">Operational Twin · fixture</p>
101
+ <h1>Preview the blast radius. Then maybe act.</h1>
102
+ <p class="lede">Safe fixture only. Signatures here are DEMO_SIGNED HMAC in the browser. DSSE is UNAVAILABLE. The live passport on the next tab is the product controller.</p>
103
+ <form id="twin-form" class="twin">
104
+ <label>Tool
105
+ <select id="twin-tool" name="tool">
106
+ <option value="echo.ping">echo.ping · NONE · admitted</option>
107
+ <option value="receipt.inspect">receipt.inspect · NONE · admitted</option>
108
+ <option value="policy.propose">policy.propose · NONE · operator</option>
109
+ <option value="shell.exec">shell.exec · IRREVERSIBLE · prohibited</option>
110
+ <option value="prod.write">prod.write · IRREVERSIBLE · prohibited</option>
111
+ </select>
112
  </label>
113
+ <label>Ping / proposal <input id="twin-arg" name="arg" value="fabric-ok"></label>
114
+ <button type="submit">Execute fixture</button>
 
 
115
  </form>
116
+ <pre id="twin-result" aria-live="polite">No fixture receipt yet.</pre>
 
 
117
  </section>
118
+
119
+ <section class="view wrap" data-view="passport" hidden>
120
+ <div class="panel">
121
+ <h2>Counterfactual Action Passport</h2>
122
+ <p>Live product controller. GET never mutates. Refresh, evaluate, and execute are explicit POSTs. One attempt. Fail closed.</p>
123
+ <form id="passport">
124
+ <label>Action
125
+ <select name="type"><option value="estate.refresh">Refresh estate truth</option><option value="probe.public_surface">Probe public surface</option></select>
126
+ </label>
127
+ <label>Target <input name="target" value="szl://estate/current" required></label>
128
+ <label>Evidence label <select name="label"><option value="OBSERVED">OBSERVED · server-signed snapshot</option><option value="UNKNOWN">UNKNOWN · fail-closed demo</option></select></label>
129
+ <button type="submit">Evaluate passport</button>
130
+ <button id="execute" type="button" disabled>Execute authorized action</button>
131
+ </form>
132
+ <pre id="passport-result" aria-live="polite">No passport evaluated.</pre>
133
+ <pre id="execution-result" aria-live="polite">No authorized execution attempted.</pre>
134
+ <p>Execution is bounded to one attempt and is enabled only for an ALLOW passport.</p>
135
+ </div>
136
+ <div class="panel">
137
+ <h2>Live event rail</h2>
138
+ <ol id="events" aria-live="polite"><li>CONNECTING</li></ol>
139
+ </div>
140
+ <div class="panel">
141
+ <h2>Latest signed receipts</h2>
142
+ <ol id="receipts"><li>CHECKING</li></ol>
143
+ </div>
144
  </section>
145
+
146
+ <section class="view wrap" data-view="audit" hidden>
147
+ <p class="eyebrow">FRONTIER_BASELINE_AUDIT</p>
148
+ <h1>Inventory before abstraction.</h1>
149
+ <ul class="keep" id="audit-list"></ul>
150
  </section>
151
  </main>
152
+ <footer>
153
+ <div class="wrap footer">
154
+ <p>MEASURED · REPORTED · MODELED · UNKNOWN · UNAVAILABLE · no private reasoning collected. Never a11oy.com.</p>
155
+ <div class="footer-links">
156
+ <a href="https://a-11-oy.com/console">Console</a>
157
+ <a href="https://a-11-oy.com/api/a11oy/v1/honest">/honest</a>
158
+ <a href="https://a11oy.net/estate/">Proof estate</a>
159
+ <a href="https://github.com/szl-holdings/a11oy">Source</a>
160
+ </div>
161
+ </div>
162
+ </footer>
163
  <script src="/series-a/app.js?v=__APP_ASSET_DIGEST__" defer></script>
164
  </body>
165
  </html>
routers/series_a_web/styles.css CHANGED
@@ -1 +1,91 @@
1
- :root{color-scheme:dark;--bg:#07101e;--card:#0d192a;--line:#24344d;--text:#eef5ff;--muted:#9db0c9;--accent:#83d6ff;--good:#7ce7ba}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at top,#13223a 0,#07101e 42%);color:var(--text);font:16px/1.5 system-ui,-apple-system,Segoe UI,sans-serif}.skip{position:absolute;left:-9999px}.skip:focus{left:1rem;top:1rem;background:white;color:black;padding:.7rem;z-index:5}header,main,footer{width:min(1180px,calc(100% - 2rem));margin:auto}header{padding:4rem 0 2rem}.eyebrow{color:var(--accent);letter-spacing:.16em;font-weight:800}h1{font-size:clamp(2.2rem,6vw,5rem);line-height:1;margin:.3rem 0}header>p:last-child{max-width:760px;color:var(--muted);font-size:1.12rem}.toolbar{display:flex;gap:1rem;align-items:center;justify-content:space-between;margin-bottom:1rem}.grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1rem}.grid article,.panel{background:linear-gradient(180deg,rgba(18,34,57,.96),rgba(10,22,38,.96));border:1px solid var(--line);border-radius:18px;padding:1.2rem;box-shadow:0 16px 60px rgba(0,0,0,.25)}.grid h2{font-size:.92rem;color:var(--muted);margin:0 0 .5rem}.grid strong{font-size:1.7rem;color:var(--good)}.grid p,.panel>p{color:var(--muted);margin:.4rem 0 0}.panel{margin-top:1rem}form{display:grid;grid-template-columns:1fr 2fr 1fr auto auto;gap:.8rem;align-items:end}label{display:grid;gap:.35rem;color:var(--muted)}input,select,button{font:inherit;border-radius:10px;border:1px solid var(--line);padding:.7rem .8rem;background:#071321;color:var(--text)}button{background:#dff5ff;color:#06101b;font-weight:800;cursor:pointer}button:disabled{opacity:.55}pre{white-space:pre-wrap;overflow-wrap:anywhere;background:#06101b;border-radius:12px;padding:1rem}#events,#receipts{display:grid;gap:.55rem;padding-left:1.4rem}#events li,#receipts li{overflow-wrap:anywhere}footer{padding:2rem 0 4rem;color:var(--muted)}@media(max-width:900px){.grid{grid-template-columns:repeat(2,minmax(0,1fr))}form{grid-template-columns:1fr 1fr}}@media(max-width:560px){header{padding-top:2.5rem}.grid,form{grid-template-columns:1fr}.toolbar{align-items:flex-start;flex-direction:column}.grid article{min-height:130px}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root{
2
+ color-scheme:dark;
3
+ --void:#080c14;
4
+ --deep:#0a1019;
5
+ --surface:#0e1626;
6
+ --card:#121d30;
7
+ --border:#1c2942;
8
+ --ink:#eef4fb;
9
+ --muted:#aebccf;
10
+ --ghost:#7f90a6;
11
+ --proof:#3af4c8;
12
+ --lattice:#5b8dee;
13
+ --gold:#d7b96b;
14
+ --down:#e8746e;
15
+ --sans:"Space Grotesk",-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,sans-serif;
16
+ --mono:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
17
+ }
18
+ *{box-sizing:border-box}
19
+ html,body{margin:0;min-height:100%;background:var(--void);color:var(--ink);font:16px/1.5 var(--sans)}
20
+ a{color:var(--proof);text-decoration:none}
21
+ a:hover{text-decoration:underline}
22
+ .skip{position:absolute;left:-9999px}
23
+ .skip:focus{left:1rem;top:1rem;background:var(--proof);color:var(--void);padding:.7rem;z-index:9}
24
+ .wrap{width:min(1180px,calc(100% - 2rem));margin-inline:auto}
25
+ .topbar{position:sticky;top:0;z-index:10;border-bottom:1px solid var(--border);background:rgba(8,12,20,.92);backdrop-filter:blur(10px)}
26
+ .nav{display:flex;flex-wrap:wrap;gap:.75rem 1rem;align-items:center;padding:.75rem 0}
27
+ .wordmark{display:inline-flex;align-items:center;gap:.6rem;color:var(--ink);font-weight:700}
28
+ .glyph{width:26px;height:26px;border-radius:7px;border:1px solid var(--border);display:grid;place-items:center;color:var(--ghost);font-family:var(--mono)}
29
+ .scope-role{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;color:var(--ghost)}
30
+ .scope-sep{color:var(--ghost);padding:0 .35rem}
31
+ .nav-chips{display:flex;gap:.5rem;flex-wrap:wrap}
32
+ .chip{display:inline-flex;align-items:center;gap:7px;padding:5px 11px;border-radius:999px;font:600 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;border:1px solid var(--border);color:var(--ghost)}
33
+ .chip .dot{width:7px;height:7px;border-radius:50%;background:currentColor}
34
+ .chip.live{color:var(--proof);border-color:rgba(58,244,200,.32)}
35
+ .chip.honesty{color:var(--ghost)}
36
+ .tabs{display:flex;gap:.2rem;flex-wrap:wrap;flex:1}
37
+ .tabs a{color:var(--muted);padding:.55rem .7rem;border-radius:8px;min-height:44px;display:inline-flex;align-items:center}
38
+ .tabs a[aria-current="page"]{color:var(--ink);background:var(--card)}
39
+ .origin-switch{display:inline-flex;align-items:center;gap:7px;padding-left:12px;border-left:1px solid var(--border);font-family:var(--mono);font-size:12px}
40
+ .origin-product{color:var(--proof);font-weight:700}
41
+ .origin-sep{color:var(--ghost)}
42
+ .toolbar{display:flex;gap:1rem;align-items:center;justify-content:space-between;margin:1.2rem auto}
43
+ .grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1rem}
44
+ .grid article,.panel{background:linear-gradient(180deg,rgba(18,34,57,.96),rgba(10,22,38,.96));border:1px solid var(--border);border-radius:16px;padding:1.1rem}
45
+ .grid h2,.panel h2{font-size:.92rem;color:var(--muted);margin:0 0 .5rem;font-weight:600}
46
+ .grid strong{font-size:1.55rem;color:var(--proof);font-variant-numeric:tabular-nums}
47
+ .grid p,.panel>p,.lede,.bound,.muted{color:var(--muted)}
48
+ .view{padding:1.5rem 0 3rem}
49
+ .view[hidden]{display:none!important}
50
+ .view[data-view="passport"]:not([hidden]){display:grid;gap:1rem}
51
+ .eyebrow{font:600 11px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--ghost);margin:0 0 .6rem}
52
+ h1{font-size:clamp(2rem,5vw,3.4rem);line-height:1.08;margin:.2rem 0 1rem;letter-spacing:-.02em}
53
+ .lede{max-width:44rem;font-size:1.05rem}
54
+ .facts{display:grid;gap:1.1rem;margin:2rem 0 0}
55
+ .facts dt{font:600 11px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ghost)}
56
+ .facts dd{margin:.4rem 0 0}
57
+ .keep{list-style:none;padding:0;margin:1.2rem 0;display:grid;gap:.55rem}
58
+ .keep li{border:1px solid var(--border);border-radius:12px;padding:.85rem 1rem;display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;align-items:center;min-height:44px}
59
+ .keep .id{font-family:var(--mono);font-size:.92rem}
60
+ .badge{font:600 10px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;border:1px solid var(--border);padding:.3rem .45rem;border-radius:4px;color:var(--ghost)}
61
+ .badge.live{color:var(--proof);border-color:rgba(58,244,200,.3)}
62
+ .badge.snapshot{color:var(--gold);border-color:rgba(215,185,107,.35)}
63
+ .badge.unknown{color:var(--ghost)}
64
+ .badge.blocker{color:var(--down);border-color:rgba(232,116,110,.4)}
65
+ .table-wrap{overflow-x:auto;border:1px solid var(--border);border-radius:14px}
66
+ .ledger{width:100%;min-width:640px;border-collapse:collapse;font-size:.92rem}
67
+ .ledger th{text-align:left;font:600 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--ghost);padding:.8rem;background:var(--card)}
68
+ .ledger td{padding:.8rem;border-top:1px solid var(--border);vertical-align:top}
69
+ .sub{margin:2rem 0 .8rem;font-size:1.2rem}
70
+ .twin,.panel form{display:grid;grid-template-columns:1fr 2fr auto;gap:.8rem;align-items:end}
71
+ label{display:grid;gap:.35rem;color:var(--muted);font-size:.92rem}
72
+ input,select,button{font:inherit;border-radius:10px;border:1px solid var(--border);padding:.75rem .85rem;background:var(--deep);color:var(--ink);min-height:44px}
73
+ button{background:var(--proof);color:var(--void);font-weight:800;cursor:pointer}
74
+ button:disabled{opacity:.55;cursor:not-allowed}
75
+ pre{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--deep);border-radius:12px;padding:1rem;border:1px solid var(--border);font:13px/1.45 var(--mono)}
76
+ #events,#receipts{display:grid;gap:.55rem;padding-left:1.4rem}
77
+ #events li,#receipts li{overflow-wrap:anywhere}
78
+ .footer{display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;padding:2rem 0 4rem;color:var(--muted);font-size:.9rem}
79
+ .footer-links{display:flex;gap:1rem;flex-wrap:wrap}
80
+ @media(max-width:900px){
81
+ .grid{grid-template-columns:repeat(2,minmax(0,1fr))}
82
+ .twin,.panel form{grid-template-columns:1fr}
83
+ .origin-switch{border-left:0;padding-left:0;width:100%}
84
+ }
85
+ @media(max-width:560px){
86
+ .grid{grid-template-columns:1fr}
87
+ .tabs a{flex:1;justify-content:center}
88
+ }
89
+ @media (prefers-reduced-motion:reduce){
90
+ *{animation:none!important;transition:none!important}
91
+ }