<!DOCTYPE html>
<html class="bg-[#0f111a] text-zinc-200">
<head>
<meta charset="UTF-8">
<title>JC-SDK | Sovereign Hegemon Lens</title>
<script src="<https://cdn.tailwindcss.com>"></script>
<script src="<https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js>"></script>
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0; overflow: hidden; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
canvas { display: block; position: absolute; top: 0; left: 0; z-index: 0; }
#ui-layer { position: relative; z-index: 10; pointer-events: none; }
.pointer-events-auto { pointer-events: auto; }
/* BRIGHTENED: Tactical Frosted Glass */
.glass-panel { background: rgba(20, 24, 34, 0.75); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); border: 1px solid rgba(255, 255, 255, 0.1); }
</style>
</head>
<body class="bg-[#0f111a] h-screen w-screen text-sm">
<div id="ui-layer" class="w-full h-full p-6 flex justify-between">
<div class="flex flex-col gap-4 w-80">
<div class="glass-panel p-5 rounded pointer-events-auto shadow-2xl">
<h2 class="text-[#66b3ff] font-bold uppercase text-[14px] border-b border-zinc-700 pb-2 mb-3 tracking-widest text-shadow-sm">Global Lens Metrics</h2>
<div id="global-metrics" class="space-y-3 text-zinc-300">
<div class="flex justify-between items-center"><span>ACTIVE MOVERS</span><span id="g-count" class="text-[#00ff88] font-bold text-base">0000</span></div>
<div class="flex justify-between items-center"><span>AVG BAYESIAN PULL</span><span id="g-bayes" class="text-white font-bold text-base">0.0000</span></div>
<div class="flex justify-between items-center"><span>AVG ENTROPY (Z)</span><span id="g-entropy" class="text-white font-bold text-base">0.0000</span></div>
<div class="flex justify-between items-center pt-3 border-t border-zinc-700 mt-2">
<span>DOMINANT PHASE</span><span id="g-phase" class="font-bold text-[#ffd700] tracking-wider text-base">NEUTRAL</span>
</div>
</div>
</div>
<div class="glass-panel p-4 rounded pointer-events-auto text-[11px] grid grid-cols-1 gap-y-2 shadow-2xl font-semibold text-zinc-300">
<div class="text-zinc-400 mb-2 border-b border-zinc-700 pb-1 text-[12px]">HILBERT PHASE SPECTRUM</div>
<div class="grid grid-cols-2 gap-x-2 gap-y-3">
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#00ff88]"></div>EARLY_RECOVERY</div>
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#00e600]"></div>BULL_MARKET</div>
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#ffd700]"></div>MARKET_TOP</div>
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#ff9900]"></div>EARLY_RECESSION</div>
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#ff3333]"></div>FULL_RECESSION</div>
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#ff0066]"></div>BEAR_MARKET</div>
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#9966ff]"></div>LATE_BEAR</div>
<div class="flex items-center gap-2"><div class="w-4 h-4 rounded shadow-sm bg-[#3399ff]"></div>MARKET_BOTTOM</div>
</div>
</div>
</div>
<div class="flex flex-col gap-4 w-80">
<div id="inspector-pane" class="glass-panel p-5 rounded pointer-events-auto transition-opacity duration-200 opacity-0 shadow-2xl">
<div class="flex justify-between items-baseline border-b border-zinc-700 pb-3 mb-4">
<h2 id="ui-ticker" class="text-white font-bold text-xl tracking-wider text-shadow-sm">TICKER</h2>
<span id="ui-sdr-hex" class="px-2 py-1 rounded text-[11px] font-bold text-black shadow-inner">#000000</span>
</div>
<div class="space-y-3 text-sm font-medium">
<div class="flex justify-between"><span class="text-zinc-400">Arbitrator Stance:</span><span id="ui-stance" class="text-[#00ff88] font-bold">HOLD</span></div>
<div class="flex justify-between"><span class="text-zinc-400">Hilbert Phase:</span><span id="ui-phase" class="text-[#66b3ff] font-bold">UNKNOWN</span></div>
<div class="flex justify-between"><span class="text-zinc-400">Bayesian Conf:</span><span id="ui-bayes" class="text-white">0.0000</span></div>
<div class="flex justify-between"><span class="text-zinc-400">System Entropy (Z):</span><span id="ui-entropy" class="text-white">0.0000</span></div>
<div class="flex justify-between"><span class="text-zinc-400">Kinematic (X,Y):</span><span id="ui-kinematics" class="text-white">0.00, 0.00</span></div>
</div>
<div id="gradient-bar" class="h-2 w-full rounded mt-5 opacity-90 shadow-sm"></div>
<div id="lock-indicator" class="mt-4 text-[10px] text-[#ffd700] hidden text-center uppercase tracking-widest border-t border-zinc-700 pt-3 font-bold">Target Locked (Click to release)</div>
</div>
<div class="text-zinc-400 text-[11px] text-right mt-auto font-semibold bg-[rgba(20,24,34,0.5)] p-2 rounded inline-block self-end border border-[rgba(255,255,255,0.05)]">
JC-SDK V136 // M4 SILICON ACTIVE // UMA SYNCED
</div>
</div>
</div>
<script>
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x0f111a, 0.0012);
const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 30, 140);
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance", alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const GLOBE_RADIUS = 60;
// === BRIGHTER GLASS LENS LIGHTING ===
const ambient = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambient);
const lensLight = new THREE.PointLight(0xbbeeff, 2.5, 400);
lensLight.position.set(0, 40, 30);
scene.add(lensLight);
// === THE LENS BOUNDARY ===
const sphereGeo = new THREE.SphereGeometry(GLOBE_RADIUS, 32, 32);
const sphereMat = new THREE.MeshPhongMaterial({
color: 0x4a4d59,
wireframe: true,
transparent: true,
opacity: 0.45,
shininess: 100,
specular: 0xccffff,
flatShading: false
});
const boundingSphere = new THREE.Mesh(sphereGeo, sphereMat);
scene.add(boundingSphere);
// === AIRY DARK MATTER (SAND) ===
const sandCount = 15000;
const sandGeo = new THREE.BufferGeometry();
const sandPos = new Float32Array(sandCount * 3);
for(let i=0; i<sandCount*3; i+=3) {
const r = GLOBE_RADIUS * Math.cbrt(Math.random());
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos(2 * Math.random() - 1);
sandPos[i] = r * Math.sin(phi) * Math.cos(theta);
sandPos[i+1] = r * Math.sin(phi) * Math.sin(theta);
sandPos[i+2] = r * Math.cos(phi);
}
sandGeo.setAttribute('position', new THREE.BufferAttribute(sandPos, 3));
const sandMat = new THREE.PointsMaterial({ color: 0x73788c, size: 0.22, transparent: true, opacity: 0.5 });
const sandSystem = new THREE.Points(sandGeo, sandMat);
scene.add(sandSystem);
// === THE GREAT ATTRACTOR ===
const attractorCore = new THREE.Mesh(
new THREE.SphereGeometry(3.5, 64, 64),
new THREE.MeshBasicMaterial({ color: 0xffea00 })
);
scene.add(attractorCore);
const attractorHalo = new THREE.Mesh(
new THREE.SphereGeometry(7, 32, 32),
new THREE.MeshBasicMaterial({
color: 0xffcc00,
transparent: true,
opacity: 0.25,
blending: THREE.AdditiveBlending
})
);
scene.add(attractorHalo);
// === MOVERS (THE STREAKS) ===
const MAX_MOVERS = 2000;
let activeMovers = 0;
const streakGeo = new THREE.CylinderGeometry(0.5, 0.5, 7.5, 6);
streakGeo.translate(0, 3.75, 0);
streakGeo.rotateX(Math.PI / 2);
const pastelShader = new THREE.ShaderMaterial({
vertexShader: `
attribute vec3 instanceColor;
varying vec3 vColor;
void main() {
vColor = instanceColor;
gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec3 vColor;
void main() {
vec3 vibrant = mix(vColor, vec3(1.0), 0.2);
gl_FragColor = vec4(vibrant * 1.3, 1.0);
}
`,
transparent: true, depthWrite: false, blending: THREE.AdditiveBlending
});
const moversMesh = new THREE.InstancedMesh(streakGeo, pastelShader, MAX_MOVERS);
const dummy = new THREE.Object3D();
const colors = new Float32Array(MAX_MOVERS * 3);
let ledgerData = [];
moversMesh.instanceColor = new THREE.InstancedBufferAttribute(colors, 3);
scene.add(moversMesh);
const phaseColors = {
"EARLY_RECOVERY": [0.0, 1.0, 0.53], "BULL_MARKET": [0.0, 0.9, 0.0],
"MARKET_TOP": [1.0, 0.84, 0.0], "EARLY_RECESSION": [1.0, 0.6, 0.0],
"FULL_RECESSION": [1.0, 0.2, 0.2], "BEAR_MARKET": [1.0, 0.0, 0.4],
"LATE_BEAR_MARKET": [0.6, 0.4, 1.0], "MARKET_BOTTOM": [0.2, 0.6, 1.0]
};
function updateGlobalMetrics() {
if (activeMovers === 0) return;
const avgBayes = ledgerData.reduce((a, c) => a + c.bayes, 0) / activeMovers;
const avgEntropy = ledgerData.reduce((a, c) => a + c.z, 0) / activeMovers;
const phaseCounts = {};
ledgerData.forEach(d => phaseCounts[d.phase] = (phaseCounts[d.phase] || 0) + 1);
let dominant = "NEUTRAL";
if (Object.keys(phaseCounts).length > 0) {
dominant = Object.keys(phaseCounts).reduce((a, b) => phaseCounts[a] > phaseCounts[b] ? a : b);
}
document.getElementById('g-count').innerText = activeMovers.toString().padStart(4, '0');
document.getElementById('g-bayes').innerText = avgBayes.toFixed(4);
document.getElementById('g-entropy').innerText = avgEntropy.toFixed(4);
document.getElementById('g-phase').innerText = dominant;
const isBullish = dominant.includes('BULL') || dominant.includes('RECOVERY') || dominant.includes('BOTTOM');
document.getElementById('g-phase').className = `font-bold tracking-wider text-base ${isBullish ? 'text-[#00ff88]' : 'text-[#ffd700]'}`;
}
// === DATA POLLING (FIXED JSON PARSING) ===
async function syncSovereignNode() {
try {
const response = await fetch('<http://localhost:8080/api/state>');
const state = await response.json();
// FIXED: Handle both Array (new API) and Object (old API) responses gracefully
let assets = [];
if (Array.isArray(state.assets)) {
assets = state.assets;
} else if (state.assets && typeof state.assets === 'object') {
assets = Object.values(state.assets);
}
if (assets.length === 0) return;
activeMovers = Math.min(assets.length, MAX_MOVERS);
ledgerData = [];
for (let i = 0; i < activeMovers; i++) {
const data = assets[i];
const c = phaseColors[data.cycle_phase] || [0.8, 0.8, 0.8];
colors[i*3] = c[0]; colors[i*3+1] = c[1]; colors[i*3+2] = c[2];
ledgerData.push({
id: i,
ticker: data.ticker,
x: data.viz_x || 0,
y: data.viz_y || 0,
z: data.system_entropy || 0,
bayes: data.bayesian_conf || 0.5,
phase: data.cycle_phase,
stance: data.stance,
hex: '#' + c.map(x => Math.round(x*255).toString(16).padStart(2, '0')).join('').toUpperCase(),
pos: new THREE.Vector3((Math.random()-0.5)*10, (Math.random()-0.5)*10, (Math.random()-0.5)*10),
vel: new THREE.Vector3()
});
}
moversMesh.instanceColor.needsUpdate = true;
updateGlobalMetrics();
} catch (err) {
console.log("Searching for M4 Node on 8080...");
if(ledgerData.length === 0) generateMockData();
}
}
function generateMockData() {
activeMovers = 1200;
const phaseKeys = Object.keys(phaseColors);
for (let i = 0; i < activeMovers; i++) {
const phase = phaseKeys[Math.floor(Math.random() * phaseKeys.length)];
const c = phaseColors[phase];
colors[i*3] = c[0]; colors[i*3+1] = c[1]; colors[i*3+2] = c[2];
ledgerData.push({
id: i, ticker: `HGM-${Math.floor(Math.random()*9000)+1000}`,
x: (Math.random() - 0.5) * 40, y: (Math.random() - 0.5) * 40,
z: Math.random() * 50, bayes: Math.random(),
phase: phase, stance: Math.random() > 0.8 ? "STRATEGIC_ACCUMULATE" : "HOLD",
hex: '#' + c.map(x => Math.round(x*255).toString(16).padStart(2, '0')).join('').toUpperCase(),
pos: new THREE.Vector3((Math.random()-0.5)*GLOBE_RADIUS, (Math.random()-0.5)*GLOBE_RADIUS, (Math.random()-0.5)*GLOBE_RADIUS),
vel: new THREE.Vector3()
});
}
moversMesh.instanceColor.needsUpdate = true;
updateGlobalMetrics();
}
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const uiPane = document.getElementById('inspector-pane');
const lockIndicator = document.getElementById('lock-indicator');
let lockedId = null;
function updateInspectorUI(idx) {
const data = ledgerData[idx];
document.getElementById('ui-ticker').innerText = data.ticker;
document.getElementById('ui-sdr-hex').innerText = data.hex;
document.getElementById('ui-sdr-hex').style.backgroundColor = data.hex;
document.getElementById('ui-stance').innerText = data.stance;
document.getElementById('ui-stance').className = data.stance === "STRATEGIC_ACCUMULATE" ? "text-[#00ff88] font-bold" : (data.stance === "GLOBAL_CIRCUIT_FREEZE" ? "text-[#ff3333] font-bold" : "text-[#66b3ff] font-bold");
document.getElementById('ui-phase').innerText = data.phase;
document.getElementById('ui-bayes').innerText = data.bayes.toFixed(4);
document.getElementById('ui-entropy').innerText = data.z.toFixed(4);
document.getElementById('ui-kinematics').innerText = `${data.x.toFixed(2)}, ${data.y.toFixed(2)}`;
document.getElementById('gradient-bar').style.background = `linear-gradient(90deg, rgba(20,24,34,0) 0%, ${data.hex} 100%)`;
uiPane.style.opacity = '1';
}
window.addEventListener('mousemove', (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
if (lockedId !== null) return;
raycaster.setFromCamera(mouse, camera);
raycaster.params.InstancedMesh.threshold = 1.5;
const intersects = raycaster.intersectObject(moversMesh);
if (intersects.length > 0 && intersects[0].instanceId < activeMovers) {
updateInspectorUI(intersects[0].instanceId);
document.body.style.cursor = 'crosshair';
} else {
uiPane.style.opacity = '0';
document.body.style.cursor = 'default';
}
});
window.addEventListener('click', (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObject(moversMesh);
if (intersects.length > 0 && intersects[0].instanceId < activeMovers) {
lockedId = intersects[0].instanceId;
updateInspectorUI(lockedId);
document.body.style.cursor = 'none';
lockIndicator.classList.remove('hidden');
} else {
lockedId = null;
document.body.style.cursor = 'default';
lockIndicator.classList.add('hidden');
}
});
const targetPos = new THREE.Vector3();
function animate() {
requestAnimationFrame(animate);
boundingSphere.rotation.y += 0.0005;
sandSystem.rotation.y -= 0.0002;
attractorHalo.rotation.y += 0.002;
for (let i = 0; i < activeMovers; i++) {
const data = ledgerData[i];
const targetRadius = GLOBE_RADIUS * (1.0 - data.bayes);
const dist = data.pos.length();
data.vel.x = data.x * 0.01;
data.vel.y = (data.z - (GLOBE_RADIUS/2)) * 0.01;
data.vel.z = data.y * 0.01;
if (dist > targetRadius) {
data.pos.lerp(new THREE.Vector3(0,0,0), 0.001 * data.bayes);
}
data.pos.add(data.vel);
if (data.pos.length() > GLOBE_RADIUS) data.pos.multiplyScalar(-0.95);
targetPos.copy(data.pos).add(data.vel);
dummy.position.copy(data.pos);
dummy.lookAt(targetPos);
const kinForce = Math.sqrt(data.x**2 + data.y**2);
dummy.scale.set(1, 1, kinForce * 0.15 + 1);
dummy.updateMatrix();
moversMesh.setMatrixAt(i, dummy.matrix);
}
moversMesh.instanceMatrix.needsUpdate = true;
camera.position.x = Math.sin(Date.now() * 0.0001) * 140;
camera.position.z = Math.cos(Date.now() * 0.0001) * 140;
camera.lookAt(0, 0, 0);
renderer.render(scene, camera);
}
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
syncSovereignNode();
setInterval(syncSovereignNode, 5000);
animate();
</script>
</body>
</html>