.hud{position:absolute;left:5%;top:5%;display:flex;gap:6px}
.tag{font:800 10px ui-monospace,monospace;padding:3px 8px;border-radius:20px;color:#fff;
background:rgba(255,255,255,.08);opacity:.25;transition:opacity .4s,background .4s;letter-spacing:.04em}
.tag.on{opacity:1}
.tag.key.on{background:#c98a2c}
.tag.fill.on{background:#3d5fbf}
.tag.back.on{background:#6a6a8a}
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(0x0a0a10);
const camera = new THREE.PerspectiveCamera(42, 1, 0.1, 100);
camera.position.set(0, 0.3, 4.6);
camera.lookAt(0, 0.1, 0);
const backdrop = new THREE.Mesh(
new THREE.PlaneGeometry(8, 5),
new THREE.MeshStandardMaterial({ color: 0x111118, roughness: 1 })
);
backdrop.position.set(0, 0.5, -2);
scene.add(backdrop);
const head = new THREE.Mesh(
new THREE.SphereGeometry(1, 48, 48),
new THREE.MeshStandardMaterial({ color: 0xd9b98c, roughness: 0.6 })
);
scene.add(head);
const base = new THREE.Mesh(
new THREE.CylinderGeometry(0.7, 0.9, 0.7, 32),
new THREE.MeshStandardMaterial({ color: 0x2c2f3d, roughness: 0.7 })
);
base.position.set(0, -1.25, 0);
scene.add(base);
scene.add(new THREE.AmbientLight(0x404050, 0.25));
const key = new THREE.DirectionalLight(0xffcf8f, 0);
key.position.set(3, 3, 3);
scene.add(key);
const fill = new THREE.DirectionalLight(0x8fb3ff, 0);
fill.position.set(-3.5, 1, 2.5);
scene.add(fill);
const back = new THREE.DirectionalLight(0xe4e4ff, 0);
back.position.set(0, 3, -3.5);
scene.add(back);
const tagKey = document.getElementById('tagKey');
const tagFill = document.getElementById('tagFill');
const tagBack = document.getElementById('tagBack');
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
const phase = Math.floor((t / 1600) % 4);
const wantKey = phase !== 3;
const wantFill = phase === 1 || phase === 2;
const wantBack = phase === 2;
key.intensity = THREE.MathUtils.lerp(key.intensity, wantKey ? 2.4 : 0, 0.12);
fill.intensity = THREE.MathUtils.lerp(fill.intensity, wantFill ? 0.9 : 0, 0.12);
back.intensity = THREE.MathUtils.lerp(back.intensity, wantBack ? 1.6 : 0, 0.12);
tagKey.classList.toggle('on', wantKey);
tagFill.classList.toggle('on', wantFill);
tagBack.classList.toggle('on', wantBack);
head.rotation.y = t * 0.0002;
renderer.render(scene, camera);
});