File size: 19,717 Bytes
60d78d4
 
 
 
 
 
 
 
 
 
 
 
 
 
7153e35
 
 
 
 
 
60d78d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4258c06
 
60d78d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4258c06
 
 
60d78d4
4258c06
60d78d4
4258c06
60d78d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4258c06
 
60d78d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
<!doctype html>
<!-- v4-fleet-panel — SZL Holdings · Canonical Fleet Status Panel
     Doctrine v11 LOCKED 749/14/163 · Λ = Conjecture 1 (NOT a theorem)
     Visual: Khipu Gold #c8a84b · Space Grotesk + JetBrains Mono (HCI/UX PhD spec)
     Sign: Yachay <yachay@szlholdings.ai>
     Co-Authored-By: Perplexity Computer Agent <agent@perplexity.ai>
     SPDX-License-Identifier: Apache-2.0
     © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 -->
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Fleet Status — SZL Holdings V4</title>
<!-- HCI/UX PhD Spec: Space Grotesk (panel headers, labels) + JetBrains Mono (data) -->
<!-- SOVEREIGN: 0 runtime CDN. Fonts self-hosted in-image, served same-origin at
     /vendor/fonts/*.woff2 (SAME pattern as the clean console). NO external font CDN. -->
<style>
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:300 700;font-display:swap;src:url('/vendor/fonts/SpaceGrotesk.woff2') format('woff2');}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400 500;font-display:swap;src:url('/vendor/fonts/JetBrainsMono.woff2') format('woff2');}
</style>
<style>
:root{
  /* === SZL Visual System — Khipu Gold #c8a84b (HCI/UX PhD spec 2026-06-02) === */
  --bg:#0a0a0c;          /* Surface 0 — page background (near-black, cool tint) */
  --bg-100:#111116;      /* Surface 1 — panel background */
  --bg-200:#181820;      /* Surface 2 — card / elevated */
  --bg-300:#1e1e28;      /* Surface 3 — hover / selected */
  --bg-400:#252533;      /* Surface 4 — active / pressed */

  /* Khipu Gold — SZL brand primary (Andean sovereign, "color of verified truth") */
  --gold:#c8a84b;         /* PRIMARY — Khipu Gold */
  --gold-hover:#b89340;   /* Hover state */
  --gold-signal:#f0d080;  /* Signal Gold — high-contrast labels */
  --gold-deep:#8b6f2e;    /* Deep Quipu — footer / tertiary */
  --gold-shadow:rgba(200,168,75,.2);

  /* Mission state colors (traffic-light standard — do NOT substitute gold) */
  --em:#00e87a;           /* NOMINAL / PASS green */
  --amb:#ffb700;          /* ELEVATED / WARNING amber */
  --rose:#ff4444;         /* THREAT ACTIVE / FAIL red */
  --blue:#4488ff;         /* DEGRADED / OFFLINE blue */

  /* Text (warm white — not pure #fff, reduces eye fatigue on long shifts) */
  --tx:#f0edd4;           /* Primary text */
  --tx2:#a09880;          /* Secondary / muted */
  --txm:#605850;          /* Tertiary / metadata */

  /* Borders — gold-opacity for coherent dark surfaces */
  --bdr:rgba(200,168,75,.12);
  --bdr-med:rgba(200,168,75,.25);
  --bdr-strong:rgba(200,168,75,.50);
  --bdr-alert:#ffb700;

  /* Typography — HCI/UX PhD spec */
  --sans:"Space Grotesk","Inter",ui-sans-serif,system-ui,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,monospace;
}
*{box-sizing:border-box}
html,body{margin:0;background:var(--bg);color:var(--tx);font-family:var(--sans);font-size:14px;line-height:1.5}
.wrap{max-width:1100px;margin:0 auto;padding:24px 20px}

/* ── Doctrine banner (AC-4: required on every V4 page) ── */
.doctrine-banner{
  background:linear-gradient(135deg,rgba(200,168,75,.09),rgba(0,232,122,.05));
  border:1px solid var(--bdr-med);
  border-radius:6px;
  padding:11px 17px;margin-bottom:20px;
  font-family:var(--mono);font-size:11px;
  display:flex;flex-wrap:wrap;gap:10px;align-items:center;
  letter-spacing:0;
}
.db-key{color:var(--gold);font-weight:700}
.db-val{color:var(--tx)}
.db-conj{color:var(--amb);font-weight:700}
.db-ok{color:var(--em)}
.db-sep{color:var(--txm)}

/* ── Lambda Conjecture 1 banner (standalone, prominent) ── */
.lambda-banner{
  background:linear-gradient(135deg,rgba(255,183,0,.06),rgba(200,168,75,.04));
  border:1px solid rgba(255,183,0,.35);
  border-radius:6px;
  padding:10px 17px;margin-bottom:18px;
  font-family:var(--mono);font-size:12px;
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
}
.lambda-symbol{font-size:20px;color:var(--amb);font-weight:700;line-height:1}
.lambda-text{color:var(--tx)}
.lambda-warn{color:var(--amb);font-weight:600}

/* ── Section header ── */
.sec-hdr{display:flex;align-items:baseline;gap:10px;margin-bottom:14px}
.sec-hdr h2{
  font-family:var(--sans);font-size:10px;font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;color:var(--tx2);margin:0
}
.btn{
  background:transparent;border:1px solid var(--bdr);color:var(--tx2);
  border-radius:4px;padding:4px 10px;font-size:11px;font-family:var(--mono);cursor:pointer;
  transition:border-color 200ms,color 200ms;
}
.btn:hover{border-color:var(--gold);color:var(--gold)}
.ts{font-family:var(--mono);font-size:10px;color:var(--txm);margin-left:auto}

/* ── Fleet status bar (top-level NOMINAL/DEGRADED/THREAT indicator) ── */
.fleet-status-bar{
  background:var(--bg-100);
  border:1px solid var(--bdr);
  border-radius:6px;
  padding:10px 16px;margin-bottom:14px;
  display:flex;align-items:center;gap:16px;flex-wrap:wrap;
}
.fsb-label{font-family:var(--sans);font-size:13px;font-weight:700;letter-spacing:.04em}
.fsb-label.nominal{color:var(--em)}
.fsb-label.degraded{color:var(--blue)}
.fsb-label.threat{color:var(--rose)}
.fsb-meta{font-family:var(--mono);font-size:11px;color:var(--tx2)}
.fsb-clock{font-family:var(--mono);font-size:10px;color:var(--txm);margin-left:auto}

/* ── 5-peer grid ── */
.peers{display:grid;grid-template-columns:repeat(auto-fill,minmax(188px,1fr));gap:10px;margin-bottom:20px}
.peer{
  background:var(--bg-200);
  border:1px solid var(--bdr);
  border-radius:6px;
  padding:14px 15px;position:relative;overflow:hidden;
}
.peer::before{
  content:"";position:absolute;top:0;left:0;right:0;height:3px;
  background:var(--sc,var(--em));border-radius:6px 6px 0 0;
}
.peer.ok{--sc:var(--em)}.peer.warn{--sc:var(--amb)}.peer.down{--sc:var(--rose)}.peer.pend{--sc:var(--txm)}
.p-name{font-family:var(--mono);font-size:13px;font-weight:700;display:flex;align-items:center;gap:7px;margin-bottom:5px}
.p-dot{
  width:8px;height:8px;border-radius:50%;background:var(--sc,var(--txm));
  box-shadow:0 0 5px var(--sc,var(--txm));flex-shrink:0;
}
.p-q{font-size:10px;color:var(--gold);font-family:var(--sans);font-weight:600;margin-bottom:4px;letter-spacing:.06em}
.p-doc{font-size:10px;color:var(--txm);font-family:var(--mono);margin-bottom:3px}
.p-http{font-family:var(--mono);font-size:11px;color:var(--tx2)}
.p-url{font-size:10px;color:var(--txm);font-family:var(--mono);margin-top:6px;word-break:break-all}
.p-url a{color:var(--txm);text-decoration:none}.p-url a:hover{color:var(--gold)}
.ok-txt{color:var(--em)}.warn-txt{color:var(--amb)}.down-txt{color:var(--rose)}.pend-txt{color:var(--txm)}

/* ── Lambda + Kernel card ── */
.lc{
  background:var(--bg-200);
  border:1px solid var(--bdr-med);
  border-radius:6px;
  padding:15px 18px;margin-bottom:18px;
  display:grid;grid-template-columns:1fr 1fr;gap:16px;
}
@media(max-width:580px){.lc{grid-template-columns:1fr}}
.lc-lbl{
  font-family:var(--sans);font-size:10px;font-weight:600;
  letter-spacing:.12em;text-transform:uppercase;color:var(--gold);margin-bottom:4px;
}
.lc-val{font-family:var(--mono);font-size:14px;color:var(--tx)}
.lc-val.conj{color:var(--amb);font-weight:700}
.lc-sha{
  font-family:var(--mono);font-size:12px;color:var(--blue);
  background:rgba(68,136,255,.08);border:1px solid rgba(68,136,255,.2);
  padding:2px 7px;border-radius:4px;display:inline-block;
}
.lc-note{font-size:11px;color:var(--txm);margin-top:4px;line-height:1.4}

/* ── Wire status chips ── */
.wires{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px}
.wc{
  background:var(--bg-200);border:1px solid var(--bdr);border-radius:20px;
  padding:5px 12px;font-family:var(--mono);font-size:11px;display:flex;align-items:center;gap:6px;
}
.wc-dot{width:7px;height:7px;border-radius:50%}
.wc.live{border-color:rgba(0,232,122,.3)}.wc.live .wc-dot{background:var(--em);box-shadow:0 0 5px var(--em)}
.wc.pend .wc-dot{background:var(--txm)}
.wc-lbl{color:var(--tx2)}.wc.live .wc-lbl{color:var(--tx)}

/* ── Section 889 ── */
.s889{display:flex;gap:7px;flex-wrap:wrap;margin-bottom:18px;font-family:var(--mono);font-size:10px;align-items:center}
.vb{background:rgba(255,68,68,.08);border:1px solid rgba(255,68,68,.2);color:var(--rose);padding:3px 9px;border-radius:4px}
.s889-lbl{color:var(--txm);margin-right:3px}

/* ── Receipt chain (AC-3) ── */
.rcpt-wrap{margin-top:16px}
table.rcpt{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:11px}
table.rcpt th{
  text-align:left;padding:6px 10px;color:var(--tx2);font-family:var(--sans);font-size:10px;
  font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  border-bottom:1px solid var(--bdr-med);
}
table.rcpt td{padding:7px 10px;border-bottom:1px solid var(--bdr);color:var(--tx2);vertical-align:top}
table.rcpt tr:hover td{background:rgba(30,30,40,.7)}
.td-time{color:var(--txm);white-space:nowrap}
.td-op{color:var(--tx)}
.td-org{color:var(--gold)}
.sig-ok{color:var(--em)}.sig-pend{color:var(--amb)}.sig-no{color:var(--txm)}
.td-hash{font-size:10px;color:var(--txm);word-break:break-all}
.rekor{color:var(--blue);text-decoration:none;font-size:10px}.rekor:hover{text-decoration:underline}
.empty{
  text-align:center;color:var(--txm);font-family:var(--mono);font-size:12px;
  padding:28px;border:1px dashed var(--bdr);border-radius:6px;
}

/* ── SLSA + authority footer ── */
.authority-footer{
  margin-top:20px;
  background:var(--bg-100);
  border:1px solid var(--bdr);
  border-radius:6px;
  padding:10px 16px;
  font-family:var(--mono);font-size:11px;color:var(--txm);
  display:flex;gap:16px;flex-wrap:wrap;align-items:center;
}
.af-item{display:flex;gap:5px;align-items:center}
.af-ok{color:var(--em)}.af-gold{color:var(--gold)}
</style>
</head>
<body>
<div class="wrap">

<!-- Fleet status bar — top-level operator state (3-second glance) -->
<div class="fleet-status-bar" role="banner" aria-label="Fleet mission state">
  <span class="fsb-label nominal" id="fleet-state">FLEET: NOMINAL</span>
  <span class="fsb-meta" id="fleet-meta">5 peers · checking…</span>
  <span class="fsb-clock" id="fleet-clock"></span>
</div>

<!-- Doctrine claim banner (AC-4: required on every V4 page) -->
<div class="doctrine-banner" role="region" aria-label="Doctrine status">
  <span><span class="db-key">DOCTRINE</span> <span class="db-val">v11 · LOCKED</span></span>
  <span class="db-sep">·</span>
  <span><span class="db-key">749</span> <span class="db-val">decls</span></span>
  <span class="db-sep">·</span>
  <span><span class="db-key">14</span> <span class="db-val">axioms</span></span>
  <span class="db-sep">·</span>
  <span><span class="db-key">163</span> <span class="db-val">sorries</span></span>
  <span class="db-sep">·</span>
  <span class="db-ok">kernel c7c0ba17</span>
  <span class="db-sep">·</span>
  <span class="db-ok">SLSA L1 (honest)</span>
</div>

<!-- Lambda Conjecture 1 banner (Λ honest disclosure — standalone, prominent) -->
<div class="lambda-banner" role="region" aria-label="Lambda convergence status">
  <span class="lambda-symbol">Λ</span>
  <span class="lambda-text">
    <span class="lambda-warn">Conjecture 1 — NOT a theorem.</span>
    Depends on 163 open sorries including CAUCHY_ND (Uniqueness.lean:120).
    Operator authority is human-final; Λ score is conjecture-grade, not closed proof.
  </span>
</div>

<!-- Fleet header -->
<div class="sec-hdr">
  <h2>Fleet Status — 5 Peers</h2>
  <button class="btn" onclick="loadFleet()">↻ Refresh</button>
  <span class="ts" id="fleet-ts"></span>
</div>

<!-- 5-peer cards -->
<div class="peers" id="peers-grid">
  <div class="empty">Loading fleet status…</div>
</div>

<!-- Lambda + Kernel card -->
<div class="lc" role="region" aria-label="Lambda kernel detail">
  <div>
    <div class="lc-lbl">Λ Status</div>
    <div class="lc-val conj">Conjecture 1</div>
    <div class="lc-note">NOT a theorem. Depends on open CAUCHY_ND sorry
      (Uniqueness.lean:120) + missing symmetry axiom. 163 sorries open.
      <em>Human decision required for all operational verdicts.</em></div>
  </div>
  <div>
    <div class="lc-lbl">Lean Kernel Commit</div>
    <div class="lc-val"><span class="lc-sha">c7c0ba17</span></div>
    <div class="lc-note">lutar-lean @ <code>lutar-v18.0.0</code> · 749 declarations ·
      14 unique axioms · 163 tracked sorries · <code>lake build</code> clean.</div>
  </div>
</div>

<!-- Wire status -->
<div class="wires" role="region" aria-label="Wire status">
  <div class="wc live"><div class="wc-dot"></div><span class="wc-lbl">Wire B: Orchestrator↔Policy immune — LIVE</span></div>
  <div class="wc live"><div class="wc-dot"></div><span class="wc-lbl">Wire C: Orchestrator↔Operator receipts — LIVE</span></div>
  <div class="wc pend"><div class="wc-dot"></div><span class="wc-lbl">Wire D: W3C traceparent — PENDING</span></div>
</div>

<!-- Section 889 -->
<div class="s889" role="region" aria-label="Section 889 compliance">
  <span class="s889-lbl">§889 — 5 excluded vendors:</span>
  <span class="vb">Huawei</span><span class="vb">ZTE</span><span class="vb">Hytera</span>
  <span class="vb">Hikvision</span><span class="vb">Dahua</span>
</div>

<!-- Receipt chain viewer (AC-3) -->
<div class="rcpt-wrap" role="region" aria-label="Receipt chain">
  <div class="sec-hdr" style="margin-top:4px">
    <h2>Receipt Chain</h2>
    <button class="btn" onclick="loadReceipts()">↻ Refresh</button>
  </div>
  <div id="rcpt-container"><div class="empty">Loading receipts…</div></div>
</div>

<!-- Authority footer -->
<div class="authority-footer" role="contentinfo">
  <div class="af-item"><span class="af-ok"></span><span>A11OY GATES ACTIVE</span></div>
  <div class="af-item"><span class="af-gold">·</span><span>DOCTRINE v11 LOCKED (c7c0ba17)</span></div>
  <div class="af-item"><span class="af-ok"></span><span>SLSA L1 (honest; L2 via Wire D roadmap)</span></div>
  <div class="af-item"><span class="af-gold">·</span><span>§889: 5 vendors excluded</span></div>
  <div class="af-item" style="margin-left:auto"><span>HUMAN FINAL AUTHORITY — NO AUTONOMOUS DECISIONS PENDING</span></div>
</div>

</div><!-- /wrap -->

<script>
/* Fleet panel — V4 · Doctrine v11 LOCKED · Λ = Conjecture 1 (NOT a theorem)
   Visual: Khipu Gold #c8a84b · Space Grotesk + JetBrains Mono (HCI/UX PhD spec)
   Sign: Yachay <yachay@szlholdings.ai>
   Co-Authored-By: Perplexity Computer Agent <agent@perplexity.ai> */

// Only the two REAL, deployed services are shown (a11oy + killinchu). The retired
// 404 Spaces (no sentra/amaru/rosie Space exists) are NOT probed — no fabricated or
// permanently-offline peers. Display names are honest governance-role labels.
const PEERS=[
  {id:"a11oy",    name:"Orchestrator (a11oy)", q:"Yachay",    role:"Policy Gate · AI Constitution",
   api:"/api/a11oy/healthz",      href:"https://szlholdings-a11oy.hf.space"},
  {id:"killinchu",name:"Field Node (killinchu)", q:"Killinchu", role:"C-UAS · Drone Intelligence",
   api:"https://szlholdings-killinchu.hf.space/api/health", href:"https://szlholdings-killinchu.hf.space"},
];

function detectNs(){
  const h=location.hostname;
  for(const p of PEERS){if(h.includes(p.id))return p.id;}
  return "a11oy";
}
const NS=detectNs();

const peerApi=(p)=>{
  if(location.hostname.includes(p.id)){
    return p.api.startsWith("http")?new URL(p.api).pathname:p.api;
  }
  return p.api;
};

async function probePeer(p){
  try{
    const sig=AbortSignal.timeout(5000);
    const r=await fetch(peerApi(p),{signal:sig});
    let body={};
    try{body=await r.json();}catch(_){}
    return{...p,ok:r.ok,code:r.status,body};
  }catch(e){
    return{...p,ok:null,code:"ERR",err:String(e).slice(0,60)};
  }
}

function peerCard(p,probe){
  const cls=probe.ok===true?"ok":probe.ok===null?"pend":"down";
  const httpTxt=probe.ok===true
    ?`<span class="ok-txt">HTTP ${probe.code} NOMINAL</span>`
    :probe.ok===null
    ?`<span class="pend-txt">unreachable / cross-origin</span>`
    :`<span class="down-txt">HTTP ${probe.code||"ERR"} DEGRADED</span>`;
  const b=probe.body||{};
  const doc=b.counts||b.doctrine?`v${b.doctrine||"11"} · ${b.counts||"749/14/163"}`:"—";
  return`<div class="peer ${cls}" role="article" aria-label="${p.name||p.id} peer status">
    <div class="p-name"><div class="p-dot"></div>${p.name||p.id}</div>
    <div class="p-q">${p.q}${p.role}</div>
    <div class="p-doc">Doctrine ${doc}</div>
    <div class="p-http">${httpTxt}</div>
    <div class="p-url"><a href="${p.href}" target="_blank" rel="noopener">${p.href.replace("https://","")}</a></div>
  </div>`;
}

async function loadFleet(){
  const g=document.getElementById("peers-grid");
  const ts=document.getElementById("fleet-ts");
  const state=document.getElementById("fleet-state");
  const meta=document.getElementById("fleet-meta");
  const clock=document.getElementById("fleet-clock");
  g.innerHTML='<div class="empty">Probing 5 peers…</div>';
  const probes=await Promise.all(PEERS.map(p=>probePeer(p)));
  g.innerHTML=PEERS.map((p,i)=>peerCard(p,probes[i])).join("");
  const now=new Date();
  const nowStr=now.toISOString().replace("T"," ").slice(0,19)+" UTC";
  ts.textContent=nowStr;
  clock.textContent=nowStr;
  const downs=probes.filter(p=>p.ok===false).length;
  const unreachable=probes.filter(p=>p.ok===null).length;
  if(downs>0){
    state.textContent="FLEET: DEGRADED";state.className="fsb-label degraded";
    meta.textContent=`${downs} degraded · ${5-downs-unreachable} nominal`;
  } else {
    state.textContent="FLEET: NOMINAL";state.className="fsb-label nominal";
    meta.textContent=`${probes.filter(p=>p.ok===true).length}/5 NOMINAL · ${unreachable} cross-origin`;
  }
}

async function loadReceipts(){
  const c=document.getElementById("rcpt-container");
  let receipts=[];
  try{
    const r=await fetch(`/api/${NS}/v4/receipts`);
    if(r.ok){const d=await r.json();receipts=Array.isArray(d)?d:(d.receipts||d.items||[]);}
  }catch(_){}
  if(!receipts.length){
    c.innerHTML='<div class="empty">No receipts yet — DSSE chain populates on first operation.</div>';
    return;
  }
  const rows=receipts.slice(0,10).map(r=>{
    const ts=(r.timestamp||r.ts||r.created_at||"—").slice(0,19).replace("T"," ");
    const op=r.operation||r.subject||r.action||"—";
    const org=r.organ||r.namespace||NS;
    const hash=(r.payload_hash||r.hash||r.digest||"").slice(0,16)||"—";
    const sig=r.signing_available===false?"unsigned":(r.signed?"signed":"pending");
    const sigCls=sig==="signed"?"sig-ok":sig==="pending"?"sig-pend":"sig-no";
    const sigTxt=sig==="signed"?"✓ DSSE-signed":sig==="pending"?"⏳ pending":"UNSIGNED (honest)";
    const rekor=`https://search.sigstore.dev/?hash=${hash}`;
    return`<tr>
      <td class="td-time">${ts}</td>
      <td class="td-op">${op}</td>
      <td class="td-org">${org}</td>
      <td><span class="${sigCls}">${sigTxt}</span><br>
      <span class="td-hash">${hash}</span>
      <a class="rekor" href="${rekor}" target="_blank" rel="noopener">Rekor ↗</a></td>
    </tr>`;
  }).join("");
  c.innerHTML=`<table class="rcpt">
    <thead><tr>
      <th>Time (UTC)</th><th>Operation</th><th>Organ</th><th>Signature / Hash</th>
    </tr></thead>
    <tbody>${rows}</tbody>
  </table>`;
}

document.addEventListener("DOMContentLoaded",()=>{
  loadFleet();
  loadReceipts();
  // UTC clock tick
  setInterval(()=>{
    const clock=document.getElementById("fleet-clock");
    if(clock)clock.textContent=new Date().toISOString().replace("T"," ").slice(0,19)+" UTC";
  },1000);
});
</script>
</body>
</html>