:root { --bg:#03050c; --text:#edf4ff; --muted:#c2d1e8; --accent:#90e8ff; }
* { box-sizing: border-box; }
body { margin:0; background:var(--bg); color:var(--text); font-family:"Avenir Next","Segoe UI",sans-serif; }
#scene { position:fixed; inset:0; }
.topbar { position:fixed; inset:0 0 auto 0; z-index:20; display:flex; justify-content:space-between; align-items:center; padding:.75rem 1rem; background:rgba(0,0,0,.3); backdrop-filter: blur(8px); }
.topbar a { color:var(--accent); text-decoration:none; font-weight:700; }
button { border:1px solid rgba(255,255,255,.25); border-radius:999px; padding:.45rem .8rem; color:var(--text); background:rgba(255,255,255,.07); cursor:pointer; }
.overlay { position:relative; z-index:10; min-height:100vh; width:min(900px,92%); margin:0 auto; display:grid; align-content:center; text-align:center; gap:.7rem; }
.label { margin:0; color:var(--accent); text-transform:uppercase; letter-spacing:.1em; }
h1,p { margin:0; }
h1 { font-size: clamp(2rem, 7vw, 4rem); }
p { color:var(--muted); }
if (!window.MotionPreference) {
const __mql = window.matchMedia("(prefers-reduced-motion: reduce)");
const __listeners = new Set();
const MotionPreference = {
prefersReducedMotion() {
return __mql.matches;
},
setOverride(value) {
const reduced = Boolean(value);
document.documentElement.classList.toggle("reduced-motion", reduced);
window.dispatchEvent(new CustomEvent("motion-preference", { detail: { reduced } }));
for (const listener of __listeners) {
try {
listener({ reduced, override: reduced, systemReduced: __mql.matches });
} catch {}
}
},
onChange(listener) {
__listeners.add(listener);
try {
listener({
reduced: __mql.matches,
override: null,
systemReduced: __mql.matches,
});
} catch {}
return () => __listeners.delete(listener);
},
getState() {
return { reduced: __mql.matches, override: null, systemReduced: __mql.matches };
},
};
window.MotionPreference = MotionPreference;
}
import * as THREE from "three";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";
const host = document.getElementById("scene");
const toggle = document.getElementById("toggleMotion");
let motionEnabled = !window.MotionPreference.prefersReducedMotion();
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x02030a);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 2, 11);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
host.appendChild(renderer.domElement);
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloom = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.9, 0.5, 0.15);
composer.addPass(bloom);
const group = new THREE.Group();
scene.add(group);
const colors = [0x7ed7ff, 0xf3e8b8, 0xbb83ff, 0xf962d8];
for (let i = 0; i < 24; i += 1) {
const geometry = i % 2 ? new THREE.IcosahedronGeometry(0.25 + Math.random() * 0.3, 1) : new THREE.SphereGeometry(0.23 + Math.random() * 0.25, 22, 22);
const material = new THREE.MeshStandardMaterial({
color: colors[i % colors.length],
emissive: colors[i % colors.length],
emissiveIntensity: 0.45,
metalness: 0.25,
roughness: 0.35
});
const mesh = new THREE.Mesh(geometry, material);
const angle = (i / 24) * Math.PI * 2;
const radius = 3 + (i % 5) * 0.55;
mesh.position.set(Math.cos(angle) * radius, (i % 4 - 1.5) * 0.9, Math.sin(angle) * radius);
mesh.userData = { angle, radius, speed: 0.15 + (i % 5) * 0.035 };
group.add(mesh);
}
scene.add(new THREE.AmbientLight(0x9db9ff, 0.45));
const key = new THREE.PointLight(0x78d4ff, 1.3, 45);
key.position.set(6, 7, 7);
scene.add(key);
function setLabel() {
toggle.textContent = motionEnabled ? "Disable motion" : "Enable motion";
}
function animate(time) {
requestAnimationFrame(animate);
if (motionEnabled) {
const t = time * 0.001;
group.rotation.y += 0.002;
group.children.forEach((mesh) => {
mesh.userData.angle += mesh.userData.speed * 0.002;
mesh.position.x = Math.cos(mesh.userData.angle + t * 0.5) * mesh.userData.radius;
mesh.position.z = Math.sin(mesh.userData.angle + t * 0.5) * mesh.userData.radius;
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.008;
});
}
composer.render();
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
}
toggle.addEventListener("click", () => {
motionEnabled = !motionEnabled;
setLabel();
});
window.addEventListener("resize", onResize);
setLabel();
animate(0);