.uv-scene{position:relative;width:100%;height:100%;display:grid;place-items:center;perspective:900px}
.uv-cube{position:relative;width:1px;height:1px;transform-style:preserve-3d}
.uv-face{position:absolute;top:0;left:0;display:block;border:1px solid #ffffff66;background-image:linear-gradient(#ffffff2a 1px,transparent 1px),linear-gradient(90deg,#ffffff2a 1px,transparent 1px);background-size:25% 25%;border-radius:2px}
.f0{background-color:#ff6b6b}.f1{background-color:#ffa94d}.f2{background-color:#51cf66}.f3{background-color:#339af0}.f4{background-color:#cc5de8}.f5{background-color:#20c997}
.uv-cap{position:absolute;left:0;right:0;bottom:5%;text-align:center;font-size:clamp(9px,2.6vmin,13px);color:var(--muted)}
const stage = document.querySelector('.uv-scene');
const cube = document.getElementById('uv-cube');
const faces = [...cube.children];
const FOLD = [
{ rx: 0, ry: 0 }, { rx: 0, ry: 180 }, { rx: 0, ry: 90 },
{ rx: 0, ry: -90 }, { rx: 90, ry: 0 }, { rx: -90, ry: 0 },
];
const NET = [
{ x: 0, y: 0 }, { x: 4, y: 0 }, { x: 2, y: 0 },
{ x: -2, y: 0 }, { x: 0, y: -2 }, { x: 0, y: 2 },
];
let S = 30;
function layout() {
const box = stage.getBoundingClientRect();
S = Math.max(16, Math.min(box.width, box.height) * 0.095);
faces.forEach((f) => { f.style.width = S * 2 + 'px'; f.style.height = S * 2 + 'px'; });
}
addEventListener('resize', layout); layout();
function lerp(a, b, p) { return a + (b - a) * p; }
function ease(p) { return p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2; }
function frame(t) {
const cycle = 9000;
const local = (t % cycle) / cycle;
let p;
if (local < 0.22) p = 0;
else if (local < 0.42) p = ease((local - 0.22) / 0.2);
else if (local < 0.62) p = 1;
else if (local < 0.82) p = 1 - ease((local - 0.62) / 0.2);
else p = 0;
faces.forEach((f, i) => {
const fold = FOLD[i], net = NET[i];
const rx = lerp(fold.rx, 0, p);
const ry = lerp(fold.ry, 0, p);
const tz = lerp(S, 0, p);
const tx = lerp(0, net.x * S, p);
const ty = lerp(0, net.y * S, p);
f.style.transform = `translate(-50%,-50%) translate3d(${tx}px, ${ty}px, 0) rotateX(${rx}deg) rotateY(${ry}deg) translateZ(${tz}px)`;
});
cube.style.transform = `rotateX(${-18 * (1 - p)}deg) rotateY(${((t * 0.012) % 360) * (1 - p)}deg)`;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);