/* ============================================================================ * a11oy_cathedral.js — "The Sovereign Lattice" front-door hero (vendored Three.js). * A luminous gold lattice-core (a11oy) with three governed faculties orbiting it, * in deep sovereign space. Cinematic: layered additive bloom, multi-depth starfield, * faint nebula, pointer parallax, scroll-driven camera. Live /healthz + Λ. * Honesty: locked proven = 5; Λ = Conjecture 1. No fakery; honest fallback. * Signed-off-by: Stephen P. Lutar Jr. * ========================================================================== */ import * as THREE from 'three'; const REDUCED = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const canvas = document.getElementById('scene'); const renderer = new THREE.WebGLRenderer({ canvas, antialias:true, alpha:false, powerPreference:'high-performance' }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.9)); renderer.setSize(innerWidth, innerHeight); renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.18; const scene = new THREE.Scene(); scene.fog = new THREE.FogExp2(0x05060f, 0.0016); const camera = new THREE.PerspectiveCamera(46, innerWidth/innerHeight, 0.1, 6000); // composition: core sits on the right; hero text breathes on the left const HOME = new THREE.Vector3(46, 16, 250); const LOOK = new THREE.Vector3(64, 6, 0); camera.position.copy(HOME); scene.add(new THREE.AmbientLight(0x2a2942, 0.85)); const keyLight = new THREE.PointLight(0xffe2a0, 2.7, 2200, 1.5); keyLight.position.set(70, 30, 40); scene.add(keyLight); const rim = new THREE.DirectionalLight(0x6d8fd6, 0.55); rim.position.set(-220, 120, -120); scene.add(rim); const fill = new THREE.DirectionalLight(0x7fd6d1, 0.18); fill.position.set(120, -80, 120); scene.add(fill); /* ---- faint nebula: large additive gradient sprites ---- */ function radialSprite(c1, c2, sz, op){ const cv=document.createElement('canvas'); cv.width=cv.height=256; const g=cv.getContext('2d'); const grd=g.createRadialGradient(128,128,0,128,128,128); grd.addColorStop(0,c1); grd.addColorStop(0.45,c2); grd.addColorStop(1,'rgba(0,0,0,0)'); g.fillStyle=grd; g.fillRect(0,0,256,256); const tex=new THREE.CanvasTexture(cv); const s=new THREE.Sprite(new THREE.SpriteMaterial({ map:tex, transparent:true, opacity:op, blending:THREE.AdditiveBlending, depthWrite:false })); s.scale.set(sz,sz,1); return s; } 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); 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); 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); /* ---- multi-depth starfield ---- */ function starLayer(n, rMin, rMax, size, col, op){ const pos=new Float32Array(n*3); for(let i=0;i{ const g=new THREE.Group(); const m=new THREE.Mesh(new THREE.IcosahedronGeometry(11,2), new THREE.MeshStandardMaterial({ color:f.color, emissive:f.color, emissiveIntensity:0.7, roughness:0.45, metalness:0.25 })); g.add(m); const ring=new THREE.Mesh(new THREE.TorusGeometry(17,0.5,8,72), new THREE.MeshBasicMaterial({ color:f.color, transparent:true, opacity:0.4 })); ring.rotation.x=Math.PI/2; g.add(ring); 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)); scene.add(g); // curved tether (core -> faculty) as a tube we rebuild each frame const tetherMat=new THREE.MeshBasicMaterial({ color:f.color, transparent:true, opacity:0.16, blending:THREE.AdditiveBlending, depthWrite:false }); let tetherMesh=null; facBodies.push({ f, g, m, tetherMat, get tether(){return tetherMesh;}, set tether(v){tetherMesh=v;} }); }); /* ---- live mesh: /healthz + Λ ---- */ const lamDot=document.getElementById('lam-dot'), lamText=document.getElementById('lam-text'); async function getJSON(url,ms){ const c=new AbortController(); const t=setTimeout(()=>c.abort(),ms||7000); 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); } } let lambda=null, alive=false; async function poll(){ try{ const h=await getJSON('/healthz',7000); alive=!!(h&&(h.status==='ok'||h.ok)); }catch(_){ alive=false; } try{ const l=await getJSON('/api/a11oy/v1/lambda',7000); if(l&&typeof l.lambda==='number') lambda=l.lambda; }catch(_){} paint(); } function paint(){ if(!lamDot) return; if(alive){ lamDot.classList.add('live'); }else{ lamDot.classList.remove('live'); } if(lambda!==null) lamText.innerHTML = `Λ ${lambda.toFixed(3)} · live trust score · 13-axis`; else lamText.textContent = alive ? 'mesh live · Λ reconnecting…' : 'live mesh offline · honest fallback'; } paint(); poll(); setInterval(poll, 8000); /* ---- pointer parallax + scroll ---- */ let px=0, py=0, tpx=0, tpy=0; if(!REDUCED) addEventListener('pointermove',(e)=>{ tpx=(e.clientX/innerWidth-0.5); tpy=(e.clientY/innerHeight-0.5); }, {passive:true}); let scrollP=0; function onScroll(){ const max=document.body.scrollHeight-innerHeight; scrollP = max>0 ? Math.min(1, scrollY/max) : 0; } addEventListener('scroll', onScroll, {passive:true}); onScroll(); /* ---- intro dolly ---- */ let intro=REDUCED?1:0; const _v=new THREE.Vector3(); const _look=new THREE.Vector3(); let t=0; function animate(){ requestAnimationFrame(animate); t+=0.0045; if(intro<1){ intro=Math.min(1, intro+0.012); } const e=intro<1 ? 1-Math.pow(1-intro,3) : 1; // easeOutCubic // core motion coreMesh.rotation.y+=0.0016; coreMesh.rotation.x+=0.0005; latticeA.rotation.y-=0.0012; latticeA.rotation.x+=0.0007; latticeB.rotation.y+=0.0009; latticeB.rotation.z-=0.0006; const pulse=1+0.012*Math.sin(t*2.0); core.scale.setScalar(pulse); flare.material.opacity=0.78+0.12*Math.sin(t*1.7); // inner sparks for(let i=0;i{ const f=fb.f; const a=f.angle + t*0.22*(i%2?-1:1); const x=LOOK.x+Math.cos(a)*f.rx; const z=LOOK.z+Math.sin(a)*f.rz; const y=LOOK.y+Math.sin(a*1.4+i)*26 + f.tilt*40*Math.cos(a*0.7); fb.g.position.set(x,y,z); fb.m.rotation.y+=0.012; const mid=new THREE.Vector3((LOOK.x+x)/2,(LOOK.y+y)/2+24,(LOOK.z+z)/2); const curve=new THREE.QuadraticBezierCurve3(new THREE.Vector3(LOOK.x,LOOK.y,LOOK.z), mid, new THREE.Vector3(x,y,z)); if(fb.tether){ fb.tether.geometry.dispose(); core.parent.remove(fb.tether); } fb.tether=new THREE.Mesh(new THREE.TubeGeometry(curve,20,0.45,6,false), fb.tetherMat); scene.add(fb.tether); }); // starfield drift stars1.rotation.y+=0.00012; stars2.rotation.y-=0.00007; stars3.rotation.y+=0.0002; // camera: parallax + gentle idle + scroll framing. // We aim LEFT of the core so it sits in the right third (text breathes on the // left). As you scroll we push the core toward the right edge (no zoom-in that // would swamp the headlines) and pull back slightly so it reads as an accent. px+=(tpx-px)*0.04; py+=(tpy-py)*0.04; const frameX = 60 + 84*scrollP; // aim offset: larger -> core further right const frameY = 4 + 14*scrollP; const camHome=HOME.clone(); camHome.y += 24*scrollP; // gentle lift camHome.z += 30*scrollP; // pull back (core smaller) instead of zoom-in camHome.x += 14*scrollP; const idleX=Math.sin(t*0.5)*5, idleY=Math.cos(t*0.4)*4; _v.set(camHome.x + px*32 + idleX, camHome.y - py*22 + idleY, camHome.z); camera.position.lerp(_v, e<1?0.06*e+0.02:0.06); const look=_look.set(LOOK.x - frameX + px*12, LOOK.y + frameY - py*6, LOOK.z); camera.lookAt(look); renderer.toneMappingExposure = 1.2 - 0.16*scrollP; renderer.render(scene,camera); if(!_shown){ _shown=true; requestAnimationFrame(()=>canvas.classList.add('on')); } } let _shown=false; animate(); addEventListener('resize',()=>{ camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth,innerHeight); }); /* ---- nav stuck + scroll reveals + boot ---- */ const nav=document.getElementById('nav'); function navState(){ if(scrollY>24) nav.classList.add('stuck'); else nav.classList.remove('stuck'); } addEventListener('scroll', navState, {passive:true}); navState(); const io=new IntersectionObserver((es)=>{ es.forEach(en=>{ if(en.isIntersecting){ en.target.classList.add('in'); io.unobserve(en.target); } }); }, {threshold:0.16}); document.querySelectorAll('.reveal').forEach((el,i)=>{ el.style.transitionDelay=(Math.min(i,6)*70)+'ms'; io.observe(el); }); /* boot overlay removed: editorial hero paints instantly; the 3D canvas fades in on its first rendered frame (see #scene.on). Safety net if WebGL never starts: */ setTimeout(()=>canvas.classList.add('on'), 1800);