import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0b14);
const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 30);
camera.position.set(0, 1.4, 7.4);
function makeLabel(text, color) {
const c = document.createElement('canvas'); c.width = 256; c.height = 96;
const ctx = c.getContext('2d');
ctx.fillStyle = 'rgba(10,11,20,.85)'; ctx.beginPath(); ctx.roundRect(4, 20, 248, 56, 20); ctx.fill();
ctx.fillStyle = color; ctx.font = 'bold 34px system-ui'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
ctx.fillText(text, 128, 50);
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ map: new THREE.CanvasTexture(c), transparent: true }));
sprite.scale.set(1.3, 0.49, 1);
return sprite;
}
const group = new THREE.Group(); scene.add(group);
const data = [{ x: -2.4, c: '#8ecbff', t: 'North' }, { x: 0, c: '#ff9d7a', t: 'Core' }, { x: 2.4, c: '#9dff9d', t: 'East' }];
data.forEach((d) => {
const sphere = new THREE.Mesh(new THREE.IcosahedronGeometry(0.6, 1), new THREE.MeshBasicMaterial({ color: d.c, wireframe: true }));
sphere.position.x = d.x;
const label = makeLabel(d.t, d.c);
label.position.set(d.x, 1.05, 0);
group.add(sphere, label);
});
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
group.rotation.y = t * 0.00045;
camera.lookAt(0, 0.3, 0);
renderer.render(scene, camera);
});