Spaces:
Running
Running
fix hero: working Sovereign Lattice cathedral.js (restores 3D sphere + live Λ pill)
Browse files- static/a11oy_cathedral.js +190 -145
static/a11oy_cathedral.js
CHANGED
|
@@ -1,166 +1,211 @@
|
|
| 1 |
/* ============================================================================
|
| 2 |
-
* a11oy_cathedral.js —
|
| 3 |
-
*
|
| 4 |
-
*
|
| 5 |
-
*
|
| 6 |
-
*
|
| 7 |
* Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
|
| 8 |
* ========================================================================== */
|
| 9 |
import * as THREE from 'three';
|
| 10 |
-
import { OrbitControls } from 'three/addons/OrbitControls.js';
|
| 11 |
-
|
| 12 |
-
const CAPABILITIES = [
|
| 13 |
-
{ id:'reasoning', title:'Reasoning & Provenance', color:0x5cc4bf, angle:0,
|
| 14 |
-
plain:'Grounded reasoning, memory/recall and provenance over a signed knowledge base — an internal a11oy function.',
|
| 15 |
-
functions:['grounded ask (cites its source, refuses to fabricate)','13-axis Trust Score (geometric-mean aggregate, floor 0.90)','knowledge ontology (axioms → theorems → formulas)','model router (5-tier, cost-aware)'],
|
| 16 |
-
proof:['Trust-Score CI from CONFORMAL (W5-3 + W7-4) — distribution-free, anti-overconfidence floor (never 100%, NOT Hoeffding)','Model-Router stability C20 + PAC-Bayes/router envelope W7-5 (min ≤ avg ≤ max)','Ontology label-invariance: F-G2 / F-G4 / F-G6 / W7-1 (graph substrate)'] },
|
| 17 |
-
{ id:'policy', title:'Policy & Compliance', color:0xd7b96b, angle:2.094,
|
| 18 |
-
plain:'Deny-by-default safety gates and full ALLOW/DENY verdicts with signed receipts — an internal a11oy function.',
|
| 19 |
-
functions:['8 deny-by-default safety gates','full verdict (ALLOW / DENY) with signals + receipt hash','30-signature threat corpus (MITRE ATT&CK + CVSS)','readiness / compliance (NIST / STIG / ISO)'],
|
| 20 |
-
proof:['Gate-soundness P2 — no action without BOTH policy AND kernel/doctrine check; a single DENY is absorbing','Agentic-loop P3 non-interference — poisoned input provably cannot flip a DENY into an ALLOW'] },
|
| 21 |
-
{ id:'operator', title:'Operator · Ask / Act / Approvals', color:0xe58e54, angle:4.189,
|
| 22 |
-
plain:'The governed run loop: ask, act with approvals, and emit replayable signed receipts — an internal a11oy function.',
|
| 23 |
-
functions:['governed run loop P1–P6 (sign → gate → chain → memory → replay)','human approvals gate for high-impact actions','replayable, hash-chained receipts (Khipu)','a11oy Code — governed agentic coder (P1–P6), open-weight models'],
|
| 24 |
-
proof:['Agentic-loop P1–P6: 28 kernel-verified theorems — run is auditable, gate-sound, injection-resistant end-to-end','P5 replay-determinism gated only by declared hashFn_collision_resistant axiom (named, not a hardness proof)'] }
|
| 25 |
-
];
|
| 26 |
-
|
| 27 |
-
const A11OY = {
|
| 28 |
-
id:'a11oy', title:'a11oy — Command Platform (the brain)',
|
| 29 |
-
plain:'The orchestrating governance substrate: one brain coordinating reasoning, policy and operator capabilities. Live: /healthz.',
|
| 30 |
-
functions:['command center + five superpowers + 25-demo Warhacker board (5×5 live)','a11oy Code (governed agentic coder, P1–P6, open-weight router)','one governance substrate (capabilities are internal, no service split)','13-axis Trust Score aggregate (yuyay_v3, geometric mean, floor 0.90)','/proven: 9 wave 9/10 formula cards (5 with live runtime checks)','signed Khipu receipts for every governed action'],
|
| 31 |
-
proof:['Locked proven kernel = 5 {F1,F11,F12,F18,F19} @ c7c0ba17 (749/14/163) — machine-enforced count','Experimental main: 1304 decl / 22 axioms, waves 3–10 CI-green (~36–44 theorems), excluded from locked count','Λ-Aggregator uniqueness (F23) = Conjecture 1 — NOT a theorem (unconditional uniqueness machine-checked FALSE)'],
|
| 32 |
-
url:'/console'
|
| 33 |
-
};
|
| 34 |
-
|
| 35 |
-
const ENDPOINTS = { health:'/healthz', lambda:'/api/a11oy/v1/lambda' };
|
| 36 |
|
|
|
|
| 37 |
const canvas = document.getElementById('scene');
|
| 38 |
-
const renderer = new THREE.WebGLRenderer({ canvas, antialias:true, alpha:false });
|
| 39 |
-
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.
|
| 40 |
-
renderer.setSize(
|
| 41 |
-
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
|
|
| 42 |
|
| 43 |
const scene = new THREE.Scene();
|
| 44 |
-
scene.fog = new THREE.FogExp2(
|
| 45 |
-
const camera = new THREE.PerspectiveCamera(
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
scene.add(new THREE.AmbientLight(
|
| 53 |
-
|
| 54 |
-
const rim = new THREE.DirectionalLight(
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
pos[i*3]=r*Math.sin(p)*Math.cos(t); pos[i*3+1]=r*Math.cos(p); pos[i*3+2]=r*Math.sin(p)*Math.sin(t); }
|
| 60 |
const g=new THREE.BufferGeometry(); g.setAttribute('position', new THREE.BufferAttribute(pos,3));
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
const interactables=[];
|
| 65 |
-
function registerBody(mesh,data){ mesh.userData.inspect=data; interactables.push(mesh); }
|
| 66 |
-
|
| 67 |
-
const sunGroup=new THREE.Group(); scene.add(sunGroup);
|
| 68 |
-
const sunCore=new THREE.Mesh(new THREE.IcosahedronGeometry(36,4),
|
| 69 |
-
new THREE.MeshStandardMaterial({ color:0xffce6e, emissive:0xd79a2e, emissiveIntensity:1.5, roughness:0.35, metalness:0.1 }));
|
| 70 |
-
sunGroup.add(sunCore); registerBody(sunCore, A11OY);
|
| 71 |
-
const sunGlow=new THREE.Mesh(new THREE.SphereGeometry(50,32,32),
|
| 72 |
-
new THREE.MeshBasicMaterial({ color:0xffd98a, transparent:true, opacity:0.10, side:THREE.BackSide }));
|
| 73 |
-
sunGroup.add(sunGlow);
|
| 74 |
-
const brainLattice=new THREE.Mesh(new THREE.IcosahedronGeometry(42,2),
|
| 75 |
-
new THREE.MeshBasicMaterial({ color:0xffe6a8, wireframe:true, transparent:true, opacity:0.22 }));
|
| 76 |
-
sunGroup.add(brainLattice);
|
| 77 |
-
sunGroup.add(makeLabel('a11oy', 0xffe6a8, 66));
|
| 78 |
-
|
| 79 |
-
const ORBIT_R=150; const capBodies=[];
|
| 80 |
-
CAPABILITIES.forEach((cap)=>{
|
| 81 |
-
const grp=new THREE.Group();
|
| 82 |
-
const mesh=new THREE.Mesh(new THREE.IcosahedronGeometry(15,2),
|
| 83 |
-
new THREE.MeshStandardMaterial({ color:cap.color, emissive:cap.color, emissiveIntensity:0.55, roughness:0.5, metalness:0.2 }));
|
| 84 |
-
grp.add(mesh);
|
| 85 |
-
const ring=new THREE.Mesh(new THREE.TorusGeometry(22,0.7,8,64), new THREE.MeshBasicMaterial({ color:cap.color, transparent:true, opacity:0.35 }));
|
| 86 |
-
ring.rotation.x=Math.PI/2; grp.add(ring);
|
| 87 |
-
grp.add(makeLabel(cap.title.split(/[ &·]/)[0], cap.color, 30));
|
| 88 |
-
scene.add(grp); registerBody(mesh, cap);
|
| 89 |
-
const tether=new THREE.Line(new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(),new THREE.Vector3()]),
|
| 90 |
-
new THREE.LineBasicMaterial({ color:cap.color, transparent:true, opacity:0.28 }));
|
| 91 |
-
scene.add(tether);
|
| 92 |
-
capBodies.push({ grp, mesh, cap, baseAngle:cap.angle, tether });
|
| 93 |
-
});
|
| 94 |
-
|
| 95 |
-
function makeLabel(text,color,size){
|
| 96 |
-
const c=document.createElement('canvas'); c.width=256; c.height=64; const ctx=c.getContext('2d');
|
| 97 |
-
ctx.font='700 34px ui-monospace, Menlo, Consolas, monospace';
|
| 98 |
-
ctx.fillStyle='#'+new THREE.Color(color).getHexString();
|
| 99 |
-
ctx.textAlign='center'; ctx.textBaseline='middle'; ctx.shadowColor='rgba(0,0,0,0.8)'; ctx.shadowBlur=8;
|
| 100 |
-
ctx.fillText(text,128,34);
|
| 101 |
-
const tex=new THREE.CanvasTexture(c); tex.anisotropy=4;
|
| 102 |
-
const spr=new THREE.Sprite(new THREE.SpriteMaterial({ map:tex, transparent:true, depthWrite:false }));
|
| 103 |
-
spr.scale.set(size*2,size*0.5,1); spr.position.y=size*0.9; spr.userData.isLabel=true; return spr;
|
| 104 |
}
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
const r=canvas.getBoundingClientRect();
|
| 109 |
-
ptr.x=((e.clientX-r.left)/r.width)*2-1; ptr.y=-((e.clientY-r.top)/r.height)*2+1;
|
| 110 |
-
ray.setFromCamera(ptr,camera);
|
| 111 |
-
const hits=ray.intersectObjects(interactables,false);
|
| 112 |
-
if(hits.length) openInspector(hits[0].object.userData.inspect);
|
| 113 |
-
});
|
| 114 |
|
| 115 |
-
const
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
if(!d) return;
|
| 119 |
-
document.getElementById('insp-title').textContent=d.title;
|
| 120 |
-
document.getElementById('insp-plain').textContent=d.plain;
|
| 121 |
-
let html='<div class="ih">Functions</div><ul>'+d.functions.map(f=>`<li>${esc(f)}</li>`).join('')+'</ul>';
|
| 122 |
-
if(d.proof) html+='<div class="ih">Proof support (honest)</div><ul>'+d.proof.map(p=>`<li class="proof">${esc(p)}</li>`).join('')+'</ul>';
|
| 123 |
-
if(d.url) html+=`<div class="ih">Open</div><ul><li><a href="${d.url}" style="color:#5cc4bf">Enter the working app ↗</a></li></ul>`;
|
| 124 |
-
document.getElementById('insp-body').innerHTML=html; insp.classList.add('show');
|
| 125 |
-
}
|
| 126 |
-
function esc(s){ return String(s).replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c])); }
|
| 127 |
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
async function poll(){
|
| 135 |
-
try{ const h=await getJSON(
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
catch(_){ if(liveLambda===null) feedSource='Λ feed reconnecting…'; }
|
| 139 |
-
paintHUD();
|
| 140 |
}
|
| 141 |
-
function
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
if(
|
| 145 |
-
|
| 146 |
-
document.getElementById('status-rows').innerHTML=rows;
|
| 147 |
-
document.getElementById('feed-src').textContent=feedSource;
|
| 148 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
function animate(){
|
| 152 |
-
requestAnimationFrame(animate);
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
}
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
/* ============================================================================
|
| 2 |
+
* a11oy_cathedral.js — "The Sovereign Lattice" front-door hero (vendored Three.js).
|
| 3 |
+
* A luminous gold lattice-core (a11oy) with three governed faculties orbiting it,
|
| 4 |
+
* in deep sovereign space. Cinematic: layered additive bloom, multi-depth starfield,
|
| 5 |
+
* faint nebula, pointer parallax, scroll-driven camera. Live /healthz + Λ.
|
| 6 |
+
* Honesty: locked proven = 5; Λ = Conjecture 1. No fakery; honest fallback.
|
| 7 |
* Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
|
| 8 |
* ========================================================================== */
|
| 9 |
import * as THREE from 'three';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
+
const REDUCED = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
| 12 |
const canvas = document.getElementById('scene');
|
| 13 |
+
const renderer = new THREE.WebGLRenderer({ canvas, antialias:true, alpha:false, powerPreference:'high-performance' });
|
| 14 |
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.9));
|
| 15 |
+
renderer.setSize(innerWidth, innerHeight);
|
| 16 |
+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
| 17 |
+
renderer.toneMappingExposure = 1.18;
|
| 18 |
|
| 19 |
const scene = new THREE.Scene();
|
| 20 |
+
scene.fog = new THREE.FogExp2(0x05060f, 0.0016);
|
| 21 |
+
const camera = new THREE.PerspectiveCamera(46, innerWidth/innerHeight, 0.1, 6000);
|
| 22 |
+
|
| 23 |
+
// composition: core sits on the right; hero text breathes on the left
|
| 24 |
+
const HOME = new THREE.Vector3(46, 16, 250);
|
| 25 |
+
const LOOK = new THREE.Vector3(64, 6, 0);
|
| 26 |
+
camera.position.copy(HOME);
|
| 27 |
+
|
| 28 |
+
scene.add(new THREE.AmbientLight(0x2a2942, 0.85));
|
| 29 |
+
const keyLight = new THREE.PointLight(0xffe2a0, 2.7, 2200, 1.5); keyLight.position.set(70, 30, 40); scene.add(keyLight);
|
| 30 |
+
const rim = new THREE.DirectionalLight(0x6d8fd6, 0.55); rim.position.set(-220, 120, -120); scene.add(rim);
|
| 31 |
+
const fill = new THREE.DirectionalLight(0x7fd6d1, 0.18); fill.position.set(120, -80, 120); scene.add(fill);
|
| 32 |
+
|
| 33 |
+
/* ---- faint nebula: large additive gradient sprites ---- */
|
| 34 |
+
function radialSprite(c1, c2, sz, op){
|
| 35 |
+
const cv=document.createElement('canvas'); cv.width=cv.height=256; const g=cv.getContext('2d');
|
| 36 |
+
const grd=g.createRadialGradient(128,128,0,128,128,128);
|
| 37 |
+
grd.addColorStop(0,c1); grd.addColorStop(0.45,c2); grd.addColorStop(1,'rgba(0,0,0,0)');
|
| 38 |
+
g.fillStyle=grd; g.fillRect(0,0,256,256);
|
| 39 |
+
const tex=new THREE.CanvasTexture(cv);
|
| 40 |
+
const s=new THREE.Sprite(new THREE.SpriteMaterial({ map:tex, transparent:true, opacity:op, blending:THREE.AdditiveBlending, depthWrite:false }));
|
| 41 |
+
s.scale.set(sz,sz,1); return s;
|
| 42 |
+
}
|
| 43 |
+
const neb1=radialSprite('rgba(215,185,107,0.5)','rgba(120,90,30,0.18)',1500,0.5); neb1.position.set(160,40,-500); scene.add(neb1);
|
| 44 |
+
const neb2=radialSprite('rgba(60,80,150,0.5)','rgba(30,40,90,0.16)',1900,0.45); neb2.position.set(-380,-120,-820); scene.add(neb2);
|
| 45 |
+
const neb3=radialSprite('rgba(127,214,209,0.32)','rgba(20,90,90,0.1)',1000,0.4); neb3.position.set(-180,180,-360); scene.add(neb3);
|
| 46 |
+
|
| 47 |
+
/* ---- multi-depth starfield ---- */
|
| 48 |
+
function starLayer(n, rMin, rMax, size, col, op){
|
| 49 |
+
const pos=new Float32Array(n*3);
|
| 50 |
+
for(let i=0;i<n;i++){ const r=rMin+Math.random()*(rMax-rMin), t=Math.random()*Math.PI*2, p=Math.acos(2*Math.random()-1);
|
| 51 |
pos[i*3]=r*Math.sin(p)*Math.cos(t); pos[i*3+1]=r*Math.cos(p); pos[i*3+2]=r*Math.sin(p)*Math.sin(t); }
|
| 52 |
const g=new THREE.BufferGeometry(); g.setAttribute('position', new THREE.BufferAttribute(pos,3));
|
| 53 |
+
const pts=new THREE.Points(g, new THREE.PointsMaterial({ color:col, size, sizeAttenuation:true, transparent:true, opacity:op, depthWrite:false }));
|
| 54 |
+
scene.add(pts); return pts;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
}
|
| 56 |
+
const stars1=starLayer(1700, 700, 1500, 1.1, 0x9aa0d0, 0.55);
|
| 57 |
+
const stars2=starLayer(1100, 1400, 2800, 2.2, 0xcdbb8e, 0.5);
|
| 58 |
+
const stars3=starLayer(500, 380, 900, 1.5, 0x7fd6d1, 0.32);
|
| 59 |
|
| 60 |
+
/* ---- the sovereign lattice-core (a11oy) ---- */
|
| 61 |
+
const core=new THREE.Group(); core.position.copy(LOOK); scene.add(core);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
+
const coreMesh=new THREE.Mesh(new THREE.IcosahedronGeometry(34,5),
|
| 64 |
+
new THREE.MeshStandardMaterial({ color:0xffcf73, emissive:0xe0a536, emissiveIntensity:1.55, roughness:0.34, metalness:0.12, flatShading:true }));
|
| 65 |
+
core.add(coreMesh);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
+
const latticeA=new THREE.Mesh(new THREE.IcosahedronGeometry(44,2),
|
| 68 |
+
new THREE.MeshBasicMaterial({ color:0xffe6a8, wireframe:true, transparent:true, opacity:0.26 }));
|
| 69 |
+
core.add(latticeA);
|
| 70 |
+
const latticeB=new THREE.Mesh(new THREE.IcosahedronGeometry(56,1),
|
| 71 |
+
new THREE.MeshBasicMaterial({ color:0xd7b96b, wireframe:true, transparent:true, opacity:0.12 }));
|
| 72 |
+
core.add(latticeB);
|
| 73 |
+
|
| 74 |
+
// layered additive bloom halos
|
| 75 |
+
for(const [r,o] of [[64,0.16],[92,0.09],[140,0.05]]){
|
| 76 |
+
const h=new THREE.Mesh(new THREE.SphereGeometry(r,32,32),
|
| 77 |
+
new THREE.MeshBasicMaterial({ color:0xffd98a, transparent:true, opacity:o, side:THREE.BackSide, blending:THREE.AdditiveBlending, depthWrite:false }));
|
| 78 |
+
core.add(h);
|
| 79 |
}
|
| 80 |
+
const flare=radialSprite('rgba(255,222,150,0.95)','rgba(224,165,54,0.35)',360,0.9); core.add(flare);
|
| 81 |
+
|
| 82 |
+
// inner sparks orbiting inside the core
|
| 83 |
+
const sparkN=80, sparkPos=new Float32Array(sparkN*3), sparkSeed=[];
|
| 84 |
+
for(let i=0;i<sparkN;i++){ sparkSeed.push({a:Math.random()*Math.PI*2,b:Math.random()*Math.PI,r:20+Math.random()*22,s:0.4+Math.random()}); }
|
| 85 |
+
const sparkGeo=new THREE.BufferGeometry(); sparkGeo.setAttribute('position',new THREE.BufferAttribute(sparkPos,3));
|
| 86 |
+
core.add(new THREE.Points(sparkGeo, new THREE.PointsMaterial({ color:0xfff0c8, size:1.7, transparent:true, opacity:0.85, blending:THREE.AdditiveBlending, depthWrite:false })));
|
| 87 |
+
|
| 88 |
+
/* ---- three orbiting faculties ---- */
|
| 89 |
+
const FAC=[
|
| 90 |
+
{color:0x7fd6d1, angle:0.2, rx:120, rz:120, tilt:0.12},
|
| 91 |
+
{color:0xd7b96b, angle:2.25, rx:138, rz:128, tilt:-0.22},
|
| 92 |
+
{color:0xe58e54, angle:4.2, rx:128, rz:140, tilt:0.30},
|
| 93 |
+
];
|
| 94 |
+
const facBodies=[];
|
| 95 |
+
FAC.forEach((f)=>{
|
| 96 |
+
const g=new THREE.Group();
|
| 97 |
+
const m=new THREE.Mesh(new THREE.IcosahedronGeometry(11,2),
|
| 98 |
+
new THREE.MeshStandardMaterial({ color:f.color, emissive:f.color, emissiveIntensity:0.7, roughness:0.45, metalness:0.25 }));
|
| 99 |
+
g.add(m);
|
| 100 |
+
const ring=new THREE.Mesh(new THREE.TorusGeometry(17,0.5,8,72),
|
| 101 |
+
new THREE.MeshBasicMaterial({ color:f.color, transparent:true, opacity:0.4 })); ring.rotation.x=Math.PI/2; g.add(ring);
|
| 102 |
+
g.add(radialSprite(`rgba(${(f.color>>16)&255},${(f.color>>8)&255},${f.color&255},0.8)`,'rgba(0,0,0,0)',70,0.7));
|
| 103 |
+
scene.add(g);
|
| 104 |
+
// curved tether (core -> faculty) as a tube we rebuild each frame
|
| 105 |
+
const tetherMat=new THREE.MeshBasicMaterial({ color:f.color, transparent:true, opacity:0.16, blending:THREE.AdditiveBlending, depthWrite:false });
|
| 106 |
+
let tetherMesh=null;
|
| 107 |
+
facBodies.push({ f, g, m, tetherMat, get tether(){return tetherMesh;}, set tether(v){tetherMesh=v;} });
|
| 108 |
+
});
|
| 109 |
+
|
| 110 |
+
/* ---- live mesh: /healthz + Λ ---- */
|
| 111 |
+
const lamDot=document.getElementById('lam-dot'), lamText=document.getElementById('lam-text');
|
| 112 |
+
async function getJSON(url,ms){ const c=new AbortController(); const t=setTimeout(()=>c.abort(),ms||7000);
|
| 113 |
+
try{ const r=await fetch(url,{signal:c.signal,headers:{accept:'application/json'}}); if(!r.ok) throw 0; return await r.json(); } finally{ clearTimeout(t); } }
|
| 114 |
+
let lambda=null, alive=false;
|
| 115 |
async function poll(){
|
| 116 |
+
try{ const h=await getJSON('/healthz',7000); alive=!!(h&&(h.status==='ok'||h.ok)); }catch(_){ alive=false; }
|
| 117 |
+
try{ const l=await getJSON('/api/a11oy/v1/lambda',7000); if(l&&typeof l.lambda==='number') lambda=l.lambda; }catch(_){}
|
| 118 |
+
paint();
|
|
|
|
|
|
|
| 119 |
}
|
| 120 |
+
function paint(){
|
| 121 |
+
if(!lamDot) return;
|
| 122 |
+
if(alive){ lamDot.classList.add('live'); }else{ lamDot.classList.remove('live'); }
|
| 123 |
+
if(lambda!==null) lamText.innerHTML = `<b>Λ ${lambda.toFixed(3)}</b> · live trust score · 13-axis`;
|
| 124 |
+
else lamText.textContent = alive ? 'mesh live · Λ reconnecting…' : 'live mesh offline · honest fallback';
|
|
|
|
|
|
|
| 125 |
}
|
| 126 |
+
paint(); poll(); setInterval(poll, 8000);
|
| 127 |
+
|
| 128 |
+
/* ---- pointer parallax + scroll ---- */
|
| 129 |
+
let px=0, py=0, tpx=0, tpy=0;
|
| 130 |
+
if(!REDUCED) addEventListener('pointermove',(e)=>{ tpx=(e.clientX/innerWidth-0.5); tpy=(e.clientY/innerHeight-0.5); }, {passive:true});
|
| 131 |
+
let scrollP=0;
|
| 132 |
+
function onScroll(){ const max=document.body.scrollHeight-innerHeight; scrollP = max>0 ? Math.min(1, scrollY/max) : 0; }
|
| 133 |
+
addEventListener('scroll', onScroll, {passive:true}); onScroll();
|
| 134 |
|
| 135 |
+
/* ---- intro dolly ---- */
|
| 136 |
+
let intro=REDUCED?1:0;
|
| 137 |
+
|
| 138 |
+
const _v=new THREE.Vector3();
|
| 139 |
+
const _look=new THREE.Vector3();
|
| 140 |
+
let t=0;
|
| 141 |
function animate(){
|
| 142 |
+
requestAnimationFrame(animate);
|
| 143 |
+
t+=0.0045;
|
| 144 |
+
if(intro<1){ intro=Math.min(1, intro+0.012); }
|
| 145 |
+
const e=intro<1 ? 1-Math.pow(1-intro,3) : 1; // easeOutCubic
|
| 146 |
+
|
| 147 |
+
// core motion
|
| 148 |
+
coreMesh.rotation.y+=0.0016; coreMesh.rotation.x+=0.0005;
|
| 149 |
+
latticeA.rotation.y-=0.0012; latticeA.rotation.x+=0.0007;
|
| 150 |
+
latticeB.rotation.y+=0.0009; latticeB.rotation.z-=0.0006;
|
| 151 |
+
const pulse=1+0.012*Math.sin(t*2.0); core.scale.setScalar(pulse);
|
| 152 |
+
flare.material.opacity=0.78+0.12*Math.sin(t*1.7);
|
| 153 |
+
|
| 154 |
+
// inner sparks
|
| 155 |
+
for(let i=0;i<sparkN;i++){ const s=sparkSeed[i]; const a=s.a+t*s.s; const b=s.b+t*0.3*s.s; const r=s.r;
|
| 156 |
+
sparkPos[i*3]=r*Math.sin(b)*Math.cos(a); sparkPos[i*3+1]=r*Math.cos(b); sparkPos[i*3+2]=r*Math.sin(b)*Math.sin(a); }
|
| 157 |
+
sparkGeo.attributes.position.needsUpdate=true;
|
| 158 |
+
|
| 159 |
+
// faculties orbit + curved tethers
|
| 160 |
+
facBodies.forEach((fb,i)=>{
|
| 161 |
+
const f=fb.f; const a=f.angle + t*0.22*(i%2?-1:1);
|
| 162 |
+
const x=LOOK.x+Math.cos(a)*f.rx;
|
| 163 |
+
const z=LOOK.z+Math.sin(a)*f.rz;
|
| 164 |
+
const y=LOOK.y+Math.sin(a*1.4+i)*26 + f.tilt*40*Math.cos(a*0.7);
|
| 165 |
+
fb.g.position.set(x,y,z); fb.m.rotation.y+=0.012;
|
| 166 |
+
const mid=new THREE.Vector3((LOOK.x+x)/2,(LOOK.y+y)/2+24,(LOOK.z+z)/2);
|
| 167 |
+
const curve=new THREE.QuadraticBezierCurve3(new THREE.Vector3(LOOK.x,LOOK.y,LOOK.z), mid, new THREE.Vector3(x,y,z));
|
| 168 |
+
if(fb.tether){ fb.tether.geometry.dispose(); core.parent.remove(fb.tether); }
|
| 169 |
+
fb.tether=new THREE.Mesh(new THREE.TubeGeometry(curve,20,0.45,6,false), fb.tetherMat); scene.add(fb.tether);
|
| 170 |
+
});
|
| 171 |
+
|
| 172 |
+
// starfield drift
|
| 173 |
+
stars1.rotation.y+=0.00012; stars2.rotation.y-=0.00007; stars3.rotation.y+=0.0002;
|
| 174 |
+
|
| 175 |
+
// camera: parallax + gentle idle + scroll framing.
|
| 176 |
+
// We aim LEFT of the core so it sits in the right third (text breathes on the
|
| 177 |
+
// left). As you scroll we push the core toward the right edge (no zoom-in that
|
| 178 |
+
// would swamp the headlines) and pull back slightly so it reads as an accent.
|
| 179 |
+
px+=(tpx-px)*0.04; py+=(tpy-py)*0.04;
|
| 180 |
+
const frameX = 60 + 84*scrollP; // aim offset: larger -> core further right
|
| 181 |
+
const frameY = 4 + 14*scrollP;
|
| 182 |
+
const camHome=HOME.clone();
|
| 183 |
+
camHome.y += 24*scrollP; // gentle lift
|
| 184 |
+
camHome.z += 30*scrollP; // pull back (core smaller) instead of zoom-in
|
| 185 |
+
camHome.x += 14*scrollP;
|
| 186 |
+
const idleX=Math.sin(t*0.5)*5, idleY=Math.cos(t*0.4)*4;
|
| 187 |
+
_v.set(camHome.x + px*32 + idleX, camHome.y - py*22 + idleY, camHome.z);
|
| 188 |
+
camera.position.lerp(_v, e<1?0.06*e+0.02:0.06);
|
| 189 |
+
const look=_look.set(LOOK.x - frameX + px*12, LOOK.y + frameY - py*6, LOOK.z);
|
| 190 |
+
camera.lookAt(look);
|
| 191 |
+
renderer.toneMappingExposure = 1.2 - 0.16*scrollP;
|
| 192 |
+
|
| 193 |
+
renderer.render(scene,camera);
|
| 194 |
+
if(!_shown){ _shown=true; requestAnimationFrame(()=>canvas.classList.add('on')); }
|
| 195 |
}
|
| 196 |
+
let _shown=false;
|
| 197 |
+
animate();
|
| 198 |
+
|
| 199 |
+
addEventListener('resize',()=>{ camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth,innerHeight); });
|
| 200 |
+
|
| 201 |
+
/* ---- nav stuck + scroll reveals + boot ---- */
|
| 202 |
+
const nav=document.getElementById('nav');
|
| 203 |
+
function navState(){ if(scrollY>24) nav.classList.add('stuck'); else nav.classList.remove('stuck'); }
|
| 204 |
+
addEventListener('scroll', navState, {passive:true}); navState();
|
| 205 |
+
|
| 206 |
+
const io=new IntersectionObserver((es)=>{ es.forEach(en=>{ if(en.isIntersecting){ en.target.classList.add('in'); io.unobserve(en.target); } }); }, {threshold:0.16});
|
| 207 |
+
document.querySelectorAll('.reveal').forEach((el,i)=>{ el.style.transitionDelay=(Math.min(i,6)*70)+'ms'; io.observe(el); });
|
| 208 |
+
|
| 209 |
+
/* boot overlay removed: editorial hero paints instantly; the 3D canvas fades in
|
| 210 |
+
on its first rendered frame (see #scene.on). Safety net if WebGL never starts: */
|
| 211 |
+
setTimeout(()=>canvas.classList.add('on'), 1800);
|