Liveloop

Uma timeline interativa para redes sociais. Cada post é um app minúsculo que você pode jogar, salvar e remixar.

Produto

  • Feed
  • Jogos
  • Criar
  • Plugin do Claude Code
  • Blog

Jurídico

  • Política de Privacidade
  • Termos de Serviço
  • Política de Cookies
  • DMCA

Projeto

  • Templates
© 2026 Liveloop. Todos os direitos reservados.
LiveloopHistórico de versões

v2Atual

@joaobarrosfranca · 8/12/2026, 3:58:20 PM

Diferença em relação à v1.

-<!doctype html>
-<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Rooftop Rumble 3D</title>
-<style>
-*{box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:#241a4e;font-family:system-ui,-apple-system,sans-serif;touch-action:none}canvas{display:block}.barbox{position:fixed;z-index:5;top:19px;width:43%;color:#fff;text-shadow:0 2px 5px #000}.barbox.left{left:16px}.barbox.right{right:16px;text-align:right}.name{font-size:11px;font-weight:950;letter-spacing:1.8px;margin-bottom:5px}.bar{height:13px;background:#1b1236;border:2px solid #fff9;transform:skewX(-12deg);overflow:hidden}.right .bar{transform:skewX(12deg)}.fill{height:100%;width:100%;background:linear-gradient(90deg,#53ffe2,#c7ff72);transition:width .2s}.right .fill{margin-left:auto;background:linear-gradient(90deg,#ffd76a,#ff5b87)}#round{position:fixed;z-index:5;top:20px;left:50%;transform:translateX(-50%);color:#fff5be;text-shadow:0 2px 7px #000;font-weight:1000;font-size:15px;letter-spacing:1.2px}.controls{position:fixed;z-index:6;left:0;right:0;bottom:max(17px,env(safe-area-inset-bottom));padding:0 14px;display:flex;justify-content:space-between;align-items:end}.move,.attacks{display:flex;gap:9px}.controls button{border:2px solid #fff8;color:#fff;font-weight:950;cursor:pointer;box-shadow:0 4px 10px #1c113d88}.move button{width:53px;height:53px;border-radius:16px;background:#5247a5bb;font-size:24px}.attacks button{width:64px;height:64px;border-radius:50%;font-size:11px;letter-spacing:.4px}.punch{background:#ec5c95}.kick{background:#f5a93d}.controls button:active{transform:scale(.94);filter:brightness(1.2)}#cover{position:fixed;z-index:10;inset:0;display:grid;place-items:center;text-align:center;padding:26px;color:white;background:radial-gradient(circle at 50% 15%,#f88a7666,transparent 32%),linear-gradient(150deg,#261549e8,#601f64e5 57%,#fa744aab)}#card{max-width:320px}.tag{font-size:11px;letter-spacing:4px;font-weight:950;color:#ffc45d}.title{font-size:51px;font-weight:1000;line-height:.84;letter-spacing:-3px;margin:17px 0 20px;text-transform:uppercase;text-shadow:4px 4px 0 #29164a}.title span{color:#65f3e1}.copy{font-size:16px;line-height:1.5;color:#f7eeff;margin-bottom:26px}#start{border:3px solid #fff1c6;border-radius:6px;background:#faad3e;color:#391b50;padding:15px 28px;font-size:14px;font-weight:1000;letter-spacing:1px;cursor:pointer;box-shadow:0 5px 0 #99394d}#start:active{transform:translateY(3px);box-shadow:0 2px 0 #99394d}.hint{font-size:12px;color:#fff2d0;margin-top:18px}
-</style></head><body>
-<div class="barbox left"><div class="name">KITE</div><div class="bar"><div id="playerHp" class="fill"></div></div></div><div id="round">ROUND 1</div><div class="barbox right"><div class="name">VOLT</div><div class="bar"><div id="enemyHp" class="fill"></div></div></div>
-<div class="controls"><div class="move"><button id="left" aria-label="Move left">←</button><button id="right" aria-label="Move right">→</button></div><div class="attacks"><button id="punch" class="punch">PUNCH</button><button id="kick" class="kick">KICK</button></div></div>
-<div id="cover"><div id="card"><div class="tag">ORIGINAL 3D FIGHTING</div><div class="title">ROOFTOP<br><span>RUMBLE</span></div><div id="copy" class="copy">Kite versus Volt. Close the distance, strike first, and own the skyline.</div><button id="start" type="button">FIGHT!</button><div class="hint">Move with arrows · Punch or kick with the buttons</div></div></div>
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><style>
+*{box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:#17112c;font-family:system-ui,sans-serif;touch-action:none}canvas{position:fixed;inset:0;z-index:0}.hud{position:fixed;z-index:3;top:0;left:0;right:0;display:flex;justify-content:space-between;padding:16px;color:#fff;pointer-events:none;text-shadow:0 2px 5px #000}.fighter{width:40%}.fighter.r{text-align:right}.name{font-size:10px;font-weight:900;letter-spacing:2px}.bar{margin-top:5px;height:15px;border:2px solid #fff;background:#211535;transform:skewX(-12deg)}.r .bar{transform:skewX(12deg)}.fill{height:100%;width:100%;background:linear-gradient(90deg,#71f3ce,#d5ff6a);transition:width .15s}.r .fill{margin-left:auto;background:linear-gradient(90deg,#ffc35c,#f55289)}#round{position:fixed;z-index:3;top:19px;left:50%;transform:translateX(-50%);font-weight:1000;color:#ffdf72;text-shadow:0 2px 6px #000;font-size:13px;letter-spacing:1px}.controls{position:fixed;z-index:4;left:0;right:0;bottom:15px;padding:0 13px;display:flex;justify-content:space-between;align-items:end}.move,.acts{display:flex;gap:7px}.controls button{border:2px solid #fff9;color:white;font-weight:950;box-shadow:0 4px 10px #0008;cursor:pointer}.move button{width:49px;height:49px;border-radius:14px;background:#463e8d;font-size:21px}.acts{flex-wrap:wrap;justify-content:flex-end;width:160px}.acts button{width:48px;height:48px;border-radius:50%;font-size:9px}.p{background:#e25288}.k{background:#ec9a3b}.x{background:#7659dc}.b{background:#4776b9}.controls button:active{transform:scale(.94)}#overlay{position:fixed;z-index:8;inset:0;display:grid;place-items:center;text-align:center;padding:25px;background:radial-gradient(circle at 50% 15%,#f17d8466,transparent 35%),linear-gradient(145deg,#1a103be8,#5b1b61e8 57%,#e36452c8);color:#fff}#card{max-width:320px}.tag{font-size:11px;font-weight:950;letter-spacing:4px;color:#ffd461}.title{font-size:51px;line-height:.82;font-weight:1000;letter-spacing:-3px;text-shadow:4px 4px #27133e;margin:15px}.title span{color:#67f2dd}.copy{font-size:15px;line-height:1.5}.start{padding:14px 28px;border:3px solid #fff1bb;background:#f3a23c;color:#371849;font-weight:1000;border-radius:5px;box-shadow:0 4px #9b394a;cursor:pointer}.tip{font-size:11px;color:#fff1cc;margin-top:16px}
+</style></head><body><div class="hud"><div class="fighter"><div class="name">KITE</div><div class="bar"><div id="pHp" class="fill"></div></div></div><div id="round">FIGHT!</div><div class="fighter r"><div class="name">VOLT</div><div class="bar"><div id="eHp" class="fill"></div></div></div></div><div class="controls"><div class="move"><button id="left">←</button><button id="right">→</button></div><div class="acts"><button id="punch" class="p">PUNCH</button><button id="kick" class="k">KICK</button><button id="power" class="x">POWER</button><button id="guard" class="b">GUARD</button></div></div><div id="overlay"><div id="card"><div class="tag">3D ARCADE FIGHTING</div><div class="title">ROOFTOP<br><span>RUMBLE</span></div><p id="copy" class="copy">Read Volt's stance, close the gap, then punch, kick or launch a power burst. Guard blocks most damage.</p><button id="start" class="start">FIGHT!</button><div class="tip">A/D move · J punch · K kick · L power · S guard · W jump</div></div></div>
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js";
-const scene=new THREE.Scene();scene.background=new THREE.Color(0x2a1a59);scene.fog=new THREE.Fog(0x2a1a59,13,35);
-const camera=new THREE.PerspectiveCamera(58,innerWidth/innerHeight,.1,100);camera.position.set(0,4.2,10.3);camera.lookAt(0,1.5,0);
-const renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,2));renderer.setSize(innerWidth,innerHeight);renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.shadowMap.enabled=true;document.body.appendChild(renderer.domElement);
-scene.add(new THREE.HemisphereLight(0xc4b9ff,0x1b153b,2.3));const moon=new THREE.DirectionalLight(0xffc36a,2.2);moon.position.set(-4,10,5);moon.castShadow=true;scene.add(moon);const pink=new THREE.PointLight(0xff4b99,18,16);pink.position.set(4,3,1);scene.add(pink);
-const roof=new THREE.Mesh(new THREE.BoxGeometry(13,.6,7),new THREE.MeshStandardMaterial({color:0x313058,roughness:.82}));roof.position.y=-.3;roof.receiveShadow=true;scene.add(roof);
-const edge=new THREE.Mesh(new THREE.BoxGeometry(13,.25,.18),new THREE.MeshStandardMaterial({color:0xf4a03e,emissive:0xa84919,emissiveIntensity:1.4}));edge.position.set(0,.05,-3.4);scene.add(edge);
-for(let i=0;i<22;i++){const h=2+Math.random()*8,w=.8+Math.random()*1.4;const b=new THREE.Mesh(new THREE.BoxGeometry(w,h,w),new THREE.MeshStandardMaterial({color:0x201949,emissive:0x120d36,roughness:.65}));b.position.set((Math.random()-.5)*24,-.1+h/2,-8-Math.random()*15);scene.add(b);for(let y=.5;y<h;y+=.8){const win=new THREE.Mesh(new THREE.BoxGeometry(w*.58,.08,.03),new THREE.MeshBasicMaterial({color:Math.random()<.45?0xffa94d:0x5eece0}));win.position.set(b.position.x,y,b.position.z+w/2+.02);scene.add(win)}}
-function makeFighter(bodyColor,gloveColor){const g=new THREE.Group(),mat=new THREE.MeshStandardMaterial({color:bodyColor,roughness:.48,metalness:.1}),skin=new THREE.MeshStandardMaterial({color:0xf0a27f,roughness:.65}),glove=new THREE.MeshStandardMaterial({color:gloveColor,roughness:.34,metalness:.15});const torso=new THREE.Mesh(new THREE.BoxGeometry(.78,1.05,.43),mat);torso.position.y=1.55;torso.castShadow=true;g.add(torso);const head=new THREE.Mesh(new THREE.SphereGeometry(.38,15,12),skin);head.position.y=2.36;head.castShadow=true;g.add(head);const hair=new THREE.Mesh(new THREE.SphereGeometry(.41,10,7),new THREE.MeshStandardMaterial({color:0x1b1530,roughness:.8}));hair.scale.y=.42;hair.position.y=2.63;g.add(hair);const arms=[];[-1,1].forEach(side=>{const arm=new THREE.Group(),upper=new THREE.Mesh(new THREE.BoxGeometry(.24,.74,.24),mat),hand=new THREE.Mesh(new THREE.SphereGeometry(.22,10,8),glove);upper.position.y=-.29;hand.position.y=-.72;arm.add(upper,hand);arm.position.set(side*.54,1.91,0);arm.rotation.z=side*-.22;g.add(arm);arms.push(arm)});[-1,1].forEach(side=>{const leg=new THREE.Mesh(new THREE.BoxGeometry(.28,.94,.3),mat);leg.position.set(side*.22,.57,0);leg.castShadow=true;g.add(leg)});return {g,arms}}
+const scene=new THREE.Scene(),cam=new THREE.PerspectiveCamera(55,innerWidth/innerHeight,.1,80),r=new THREE.WebGLRenderer({antialias:true});r.setSize(innerWidth,innerHeight);r.setPixelRatio(Math.min(devicePixelRatio,2));r.outputColorSpace=THREE.SRGBColorSpace;document.body.appendChild(r.domElement);cam.position.set(0,3.2,10);cam.lookAt(0,1.2,0);scene.background=new THREE.Color(0x21144a);scene.fog=new THREE.Fog(0x21144a,10,35);scene.add(new THREE.HemisphereLight(0xbccaff,0x170d2c,2.4));let sun=new THREE.DirectionalLight(0xffbf7b,2.2);sun.position.set(-5,9,6);scene.add(sun);let floor=new THREE.Mesh(new THREE.BoxGeometry(13,.4,5),new THREE.MeshStandardMaterial({color:0x34345b,roughness:.85}));floor.position.y=-.2;scene.add(floor);for(let i=0;i<16;i++){let h=2+Math.random()*7,b=new THREE.Mesh(new THREE.BoxGeometry(1.3,h,1.2),new THREE.MeshStandardMaterial({color:i%2?0x291b57:0x442766}));b.position.set((Math.random()-.5)*22,h/2,-7-Math.random()*12);scene.add(b)}
+function makeFighter(color,glove){let g=new THREE.Group(),mat=new THREE.MeshStandardMaterial({color,roughness:.4}),skin=new THREE.MeshStandardMaterial({color:0xf2a17c}),gm=new THREE.MeshStandardMaterial({color:glove,roughness:.28}),dark=new THREE.MeshStandardMaterial({color:0x20152f});let torso=new THREE.Mesh(new THREE.BoxGeometry(.75,1,.42),mat);torso.position.y=1.35;g.add(torso);let head=new THREE.Mesh(new THREE.SphereGeometry(.36,14,10),skin);head.position.y=2.16;g.add(head);let hair=new THREE.Mesh(new THREE.SphereGeometry(.39,12,8),dark);hair.scale.y=.38;hair.position.y=2.43;g.add(hair);let arms=[];[-1,1].forEach(s=>{let a=new THREE.Group(),u=new THREE.Mesh(new THREE.BoxGeometry(.22,.68,.22),mat),h=new THREE.Mesh(new THREE.SphereGeometry(.2,10,8),gm);u.position.y=-.28;h.position.y=-.66;a.add(u,h);a.position.set(s*.52,1.72,0);a.rotation.z=s*-.18;g.add(a);arms.push(a)});[-1,1].forEach(s=>{let leg=new THREE.Mesh(new THREE.BoxGeometry(.24,.68,.27),mat);leg.position.set(s*.2,.38,0);g.add(leg)});scene.add(g);return {g,arms,hp:100,act:"idle",timer:0,guard:false,vy:0,hit:0}}
+const P=makeFighter(0x49d9c5,0xffc64f),E=makeFighter(0xef6f95,0xa971ef);P.g.position.x=-2.3;E.g.position.x=2.3;E.g.rotation.y=Math.PI;
+const ui={p:document.getElementById("pHp"),e:document.getElementById("eHp"),round:document.getElementById("round"),overlay:document.getElementById("overlay"),copy:document.getElementById("copy"),start:document.getElementById("start")};let running=false,dir=0,guard=false,last=0,enemyThink=0;
</script></body></html>

v2Atual

@joaobarrosfranca · 8/12/2026, 3:58:20 PM

Diferença em relação à v1.

-<!doctype html>
-<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Rooftop Rumble 3D</title>
-<style>
-*{box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:#241a4e;font-family:system-ui,-apple-system,sans-serif;touch-action:none}canvas{display:block}.barbox{position:fixed;z-index:5;top:19px;width:43%;color:#fff;text-shadow:0 2px 5px #000}.barbox.left{left:16px}.barbox.right{right:16px;text-align:right}.name{font-size:11px;font-weight:950;letter-spacing:1.8px;margin-bottom:5px}.bar{height:13px;background:#1b1236;border:2px solid #fff9;transform:skewX(-12deg);overflow:hidden}.right .bar{transform:skewX(12deg)}.fill{height:100%;width:100%;background:linear-gradient(90deg,#53ffe2,#c7ff72);transition:width .2s}.right .fill{margin-left:auto;background:linear-gradient(90deg,#ffd76a,#ff5b87)}#round{position:fixed;z-index:5;top:20px;left:50%;transform:translateX(-50%);color:#fff5be;text-shadow:0 2px 7px #000;font-weight:1000;font-size:15px;letter-spacing:1.2px}.controls{position:fixed;z-index:6;left:0;right:0;bottom:max(17px,env(safe-area-inset-bottom));padding:0 14px;display:flex;justify-content:space-between;align-items:end}.move,.attacks{display:flex;gap:9px}.controls button{border:2px solid #fff8;color:#fff;font-weight:950;cursor:pointer;box-shadow:0 4px 10px #1c113d88}.move button{width:53px;height:53px;border-radius:16px;background:#5247a5bb;font-size:24px}.attacks button{width:64px;height:64px;border-radius:50%;font-size:11px;letter-spacing:.4px}.punch{background:#ec5c95}.kick{background:#f5a93d}.controls button:active{transform:scale(.94);filter:brightness(1.2)}#cover{position:fixed;z-index:10;inset:0;display:grid;place-items:center;text-align:center;padding:26px;color:white;background:radial-gradient(circle at 50% 15%,#f88a7666,transparent 32%),linear-gradient(150deg,#261549e8,#601f64e5 57%,#fa744aab)}#card{max-width:320px}.tag{font-size:11px;letter-spacing:4px;font-weight:950;color:#ffc45d}.title{font-size:51px;font-weight:1000;line-height:.84;letter-spacing:-3px;margin:17px 0 20px;text-transform:uppercase;text-shadow:4px 4px 0 #29164a}.title span{color:#65f3e1}.copy{font-size:16px;line-height:1.5;color:#f7eeff;margin-bottom:26px}#start{border:3px solid #fff1c6;border-radius:6px;background:#faad3e;color:#391b50;padding:15px 28px;font-size:14px;font-weight:1000;letter-spacing:1px;cursor:pointer;box-shadow:0 5px 0 #99394d}#start:active{transform:translateY(3px);box-shadow:0 2px 0 #99394d}.hint{font-size:12px;color:#fff2d0;margin-top:18px}
-</style></head><body>
-<div class="barbox left"><div class="name">KITE</div><div class="bar"><div id="playerHp" class="fill"></div></div></div><div id="round">ROUND 1</div><div class="barbox right"><div class="name">VOLT</div><div class="bar"><div id="enemyHp" class="fill"></div></div></div>
-<div class="controls"><div class="move"><button id="left" aria-label="Move left">←</button><button id="right" aria-label="Move right">→</button></div><div class="attacks"><button id="punch" class="punch">PUNCH</button><button id="kick" class="kick">KICK</button></div></div>
-<div id="cover"><div id="card"><div class="tag">ORIGINAL 3D FIGHTING</div><div class="title">ROOFTOP<br><span>RUMBLE</span></div><div id="copy" class="copy">Kite versus Volt. Close the distance, strike first, and own the skyline.</div><button id="start" type="button">FIGHT!</button><div class="hint">Move with arrows · Punch or kick with the buttons</div></div></div>
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><style>
+*{box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:#17112c;font-family:system-ui,sans-serif;touch-action:none}canvas{position:fixed;inset:0;z-index:0}.hud{position:fixed;z-index:3;top:0;left:0;right:0;display:flex;justify-content:space-between;padding:16px;color:#fff;pointer-events:none;text-shadow:0 2px 5px #000}.fighter{width:40%}.fighter.r{text-align:right}.name{font-size:10px;font-weight:900;letter-spacing:2px}.bar{margin-top:5px;height:15px;border:2px solid #fff;background:#211535;transform:skewX(-12deg)}.r .bar{transform:skewX(12deg)}.fill{height:100%;width:100%;background:linear-gradient(90deg,#71f3ce,#d5ff6a);transition:width .15s}.r .fill{margin-left:auto;background:linear-gradient(90deg,#ffc35c,#f55289)}#round{position:fixed;z-index:3;top:19px;left:50%;transform:translateX(-50%);font-weight:1000;color:#ffdf72;text-shadow:0 2px 6px #000;font-size:13px;letter-spacing:1px}.controls{position:fixed;z-index:4;left:0;right:0;bottom:15px;padding:0 13px;display:flex;justify-content:space-between;align-items:end}.move,.acts{display:flex;gap:7px}.controls button{border:2px solid #fff9;color:white;font-weight:950;box-shadow:0 4px 10px #0008;cursor:pointer}.move button{width:49px;height:49px;border-radius:14px;background:#463e8d;font-size:21px}.acts{flex-wrap:wrap;justify-content:flex-end;width:160px}.acts button{width:48px;height:48px;border-radius:50%;font-size:9px}.p{background:#e25288}.k{background:#ec9a3b}.x{background:#7659dc}.b{background:#4776b9}.controls button:active{transform:scale(.94)}#overlay{position:fixed;z-index:8;inset:0;display:grid;place-items:center;text-align:center;padding:25px;background:radial-gradient(circle at 50% 15%,#f17d8466,transparent 35%),linear-gradient(145deg,#1a103be8,#5b1b61e8 57%,#e36452c8);color:#fff}#card{max-width:320px}.tag{font-size:11px;font-weight:950;letter-spacing:4px;color:#ffd461}.title{font-size:51px;line-height:.82;font-weight:1000;letter-spacing:-3px;text-shadow:4px 4px #27133e;margin:15px}.title span{color:#67f2dd}.copy{font-size:15px;line-height:1.5}.start{padding:14px 28px;border:3px solid #fff1bb;background:#f3a23c;color:#371849;font-weight:1000;border-radius:5px;box-shadow:0 4px #9b394a;cursor:pointer}.tip{font-size:11px;color:#fff1cc;margin-top:16px}
+</style></head><body><div class="hud"><div class="fighter"><div class="name">KITE</div><div class="bar"><div id="pHp" class="fill"></div></div></div><div id="round">FIGHT!</div><div class="fighter r"><div class="name">VOLT</div><div class="bar"><div id="eHp" class="fill"></div></div></div></div><div class="controls"><div class="move"><button id="left">←</button><button id="right">→</button></div><div class="acts"><button id="punch" class="p">PUNCH</button><button id="kick" class="k">KICK</button><button id="power" class="x">POWER</button><button id="guard" class="b">GUARD</button></div></div><div id="overlay"><div id="card"><div class="tag">3D ARCADE FIGHTING</div><div class="title">ROOFTOP<br><span>RUMBLE</span></div><p id="copy" class="copy">Read Volt's stance, close the gap, then punch, kick or launch a power burst. Guard blocks most damage.</p><button id="start" class="start">FIGHT!</button><div class="tip">A/D move · J punch · K kick · L power · S guard · W jump</div></div></div>
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js";
-const scene=new THREE.Scene();scene.background=new THREE.Color(0x2a1a59);scene.fog=new THREE.Fog(0x2a1a59,13,35);
-const camera=new THREE.PerspectiveCamera(58,innerWidth/innerHeight,.1,100);camera.position.set(0,4.2,10.3);camera.lookAt(0,1.5,0);
-const renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,2));renderer.setSize(innerWidth,innerHeight);renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.shadowMap.enabled=true;document.body.appendChild(renderer.domElement);
-scene.add(new THREE.HemisphereLight(0xc4b9ff,0x1b153b,2.3));const moon=new THREE.DirectionalLight(0xffc36a,2.2);moon.position.set(-4,10,5);moon.castShadow=true;scene.add(moon);const pink=new THREE.PointLight(0xff4b99,18,16);pink.position.set(4,3,1);scene.add(pink);
-const roof=new THREE.Mesh(new THREE.BoxGeometry(13,.6,7),new THREE.MeshStandardMaterial({color:0x313058,roughness:.82}));roof.position.y=-.3;roof.receiveShadow=true;scene.add(roof);
-const edge=new THREE.Mesh(new THREE.BoxGeometry(13,.25,.18),new THREE.MeshStandardMaterial({color:0xf4a03e,emissive:0xa84919,emissiveIntensity:1.4}));edge.position.set(0,.05,-3.4);scene.add(edge);
-for(let i=0;i<22;i++){const h=2+Math.random()*8,w=.8+Math.random()*1.4;const b=new THREE.Mesh(new THREE.BoxGeometry(w,h,w),new THREE.MeshStandardMaterial({color:0x201949,emissive:0x120d36,roughness:.65}));b.position.set((Math.random()-.5)*24,-.1+h/2,-8-Math.random()*15);scene.add(b);for(let y=.5;y<h;y+=.8){const win=new THREE.Mesh(new THREE.BoxGeometry(w*.58,.08,.03),new THREE.MeshBasicMaterial({color:Math.random()<.45?0xffa94d:0x5eece0}));win.position.set(b.position.x,y,b.position.z+w/2+.02);scene.add(win)}}
-function makeFighter(bodyColor,gloveColor){const g=new THREE.Group(),mat=new THREE.MeshStandardMaterial({color:bodyColor,roughness:.48,metalness:.1}),skin=new THREE.MeshStandardMaterial({color:0xf0a27f,roughness:.65}),glove=new THREE.MeshStandardMaterial({color:gloveColor,roughness:.34,metalness:.15});const torso=new THREE.Mesh(new THREE.BoxGeometry(.78,1.05,.43),mat);torso.position.y=1.55;torso.castShadow=true;g.add(torso);const head=new THREE.Mesh(new THREE.SphereGeometry(.38,15,12),skin);head.position.y=2.36;head.castShadow=true;g.add(head);const hair=new THREE.Mesh(new THREE.SphereGeometry(.41,10,7),new THREE.MeshStandardMaterial({color:0x1b1530,roughness:.8}));hair.scale.y=.42;hair.position.y=2.63;g.add(hair);const arms=[];[-1,1].forEach(side=>{const arm=new THREE.Group(),upper=new THREE.Mesh(new THREE.BoxGeometry(.24,.74,.24),mat),hand=new THREE.Mesh(new THREE.SphereGeometry(.22,10,8),glove);upper.position.y=-.29;hand.position.y=-.72;arm.add(upper,hand);arm.position.set(side*.54,1.91,0);arm.rotation.z=side*-.22;g.add(arm);arms.push(arm)});[-1,1].forEach(side=>{const leg=new THREE.Mesh(new THREE.BoxGeometry(.28,.94,.3),mat);leg.position.set(side*.22,.57,0);leg.castShadow=true;g.add(leg)});return {g,arms}}
+const scene=new THREE.Scene(),cam=new THREE.PerspectiveCamera(55,innerWidth/innerHeight,.1,80),r=new THREE.WebGLRenderer({antialias:true});r.setSize(innerWidth,innerHeight);r.setPixelRatio(Math.min(devicePixelRatio,2));r.outputColorSpace=THREE.SRGBColorSpace;document.body.appendChild(r.domElement);cam.position.set(0,3.2,10);cam.lookAt(0,1.2,0);scene.background=new THREE.Color(0x21144a);scene.fog=new THREE.Fog(0x21144a,10,35);scene.add(new THREE.HemisphereLight(0xbccaff,0x170d2c,2.4));let sun=new THREE.DirectionalLight(0xffbf7b,2.2);sun.position.set(-5,9,6);scene.add(sun);let floor=new THREE.Mesh(new THREE.BoxGeometry(13,.4,5),new THREE.MeshStandardMaterial({color:0x34345b,roughness:.85}));floor.position.y=-.2;scene.add(floor);for(let i=0;i<16;i++){let h=2+Math.random()*7,b=new THREE.Mesh(new THREE.BoxGeometry(1.3,h,1.2),new THREE.MeshStandardMaterial({color:i%2?0x291b57:0x442766}));b.position.set((Math.random()-.5)*22,h/2,-7-Math.random()*12);scene.add(b)}
+function makeFighter(color,glove){let g=new THREE.Group(),mat=new THREE.MeshStandardMaterial({color,roughness:.4}),skin=new THREE.MeshStandardMaterial({color:0xf2a17c}),gm=new THREE.MeshStandardMaterial({color:glove,roughness:.28}),dark=new THREE.MeshStandardMaterial({color:0x20152f});let torso=new THREE.Mesh(new THREE.BoxGeometry(.75,1,.42),mat);torso.position.y=1.35;g.add(torso);let head=new THREE.Mesh(new THREE.SphereGeometry(.36,14,10),skin);head.position.y=2.16;g.add(head);let hair=new THREE.Mesh(new THREE.SphereGeometry(.39,12,8),dark);hair.scale.y=.38;hair.position.y=2.43;g.add(hair);let arms=[];[-1,1].forEach(s=>{let a=new THREE.Group(),u=new THREE.Mesh(new THREE.BoxGeometry(.22,.68,.22),mat),h=new THREE.Mesh(new THREE.SphereGeometry(.2,10,8),gm);u.position.y=-.28;h.position.y=-.66;a.add(u,h);a.position.set(s*.52,1.72,0);a.rotation.z=s*-.18;g.add(a);arms.push(a)});[-1,1].forEach(s=>{let leg=new THREE.Mesh(new THREE.BoxGeometry(.24,.68,.27),mat);leg.position.set(s*.2,.38,0);g.add(leg)});scene.add(g);return {g,arms,hp:100,act:"idle",timer:0,guard:false,vy:0,hit:0}}
+const P=makeFighter(0x49d9c5,0xffc64f),E=makeFighter(0xef6f95,0xa971ef);P.g.position.x=-2.3;E.g.position.x=2.3;E.g.rotation.y=Math.PI;
+const ui={p:document.getElementById("pHp"),e:document.getElementById("eHp"),round:document.getElementById("round"),overlay:document.getElementById("overlay"),copy:document.getElementById("copy"),start:document.getElementById("start")};let running=false,dir=0,guard=false,last=0,enemyThink=0;
</script></body></html>
-const player=makeFighter(0x48d6cf,0xffcf55),enemy=makeFighter(0xf16b91,0xb468ff);player.g.position.x=-2.4;enemy.g.position.x=2.4;enemy.g.rotation.y=Math.PI;scene.add(player.g,enemy.g);
-const player=makeFighter(0x48d6cf,0xffcf55),enemy=makeFighter(0xf16b91,0xb468ff);player.g.position.x=-2.4;enemy.g.position.x=2.4;enemy.g.rotation.y=Math.PI;scene.add(player.g,enemy.g);
-const hpP=document.querySelector("#playerHp"),hpE=document.querySelector("#enemyHp"),cover=document.querySelector("#cover"),copy=document.querySelector("#copy"),start=document.querySelector("#start");
-const hpP=document.querySelector("#playerHp"),hpE=document.querySelector("#enemyHp"),cover=document.querySelector("#cover"),copy=document.querySelector("#copy"),start=document.querySelector("#start");
-let playerHealth=100,enemyHealth=100,running=false,moveDir=0,last=0,pAttack=0,eAttack=0,pCooldown=0,eCooldown=0,bob=0;
-let playerHealth=100,enemyHealth=100,running=false,moveDir=0,last=0,pAttack=0,eAttack=0,pCooldown=0,eCooldown=0,bob=0;
-function clamp(n,a,b){return Math.max(a,Math.min(b,n))}function updateBars(){hpP.style.width=playerHealth+"%";hpE.style.width=enemyHealth+"%"}
-function clamp(n,a,b){return Math.max(a,Math.min(b,n))}function updateBars(){hpP.style.width=playerHealth+"%";hpE.style.width=enemyHealth+"%"}
-function end(won){running=false;copy.innerHTML=won?"<b>Kite wins!</b><br>The rooftop belongs to you.":"<b>Volt wins!</b><br>Step back into the ring and fight again.";start.textContent="REMATCH";cover.style.display="grid"}
-function end(won){running=false;copy.innerHTML=won?"<b>Kite wins!</b><br>The rooftop belongs to you.":"<b>Volt wins!</b><br>Step back into the ring and fight again.";start.textContent="REMATCH";cover.style.display="grid"}
-function strike(type){if(!running||pCooldown>0)return;const kick=type==="kick";pAttack=kick?330:230;pCooldown=kick?520:360;if(Math.abs(player.g.position.x-enemy.g.position.x)<(kick?1.95:1.52)){enemyHealth=clamp(enemyHealth-(kick?17:10),0,100);updateBars();if(enemyHealth===0)end(true)}}
-function strike(type){if(!running||pCooldown>0)return;const kick=type==="kick";pAttack=kick?330:230;pCooldown=kick?520:360;if(Math.abs(player.g.position.x-enemy.g.position.x)<(kick?1.95:1.52)){enemyHealth=clamp(enemyHealth-(kick?17:10),0,100);updateBars();if(enemyHealth===0)end(true)}}
-function botStrike(){if(eCooldown>0)return;eAttack=260;eCooldown=520;if(Math.abs(player.g.position.x-enemy.g.position.x)<1.62){playerHealth=clamp(playerHealth-12,0,100);updateBars();if(playerHealth===0)end(false)}}
-function botStrike(){if(eCooldown>0)return;eAttack=260;eCooldown=520;if(Math.abs(player.g.position.x-enemy.g.position.x)<1.62){playerHealth=clamp(playerHealth-12,0,100);updateBars();if(playerHealth===0)end(false)}}
-function hold(el,v){el.addEventListener("pointerdown",e=>{e.preventDefault();moveDir=v});["pointerup","pointerleave","pointercancel"].forEach(t=>el.addEventListener(t,()=>moveDir=0))}
-function hold(el,v){el.addEventListener("pointerdown",e=>{e.preventDefault();moveDir=v});["pointerup","pointerleave","pointercancel"].forEach(t=>el.addEventListener(t,()=>moveDir=0))}
-hold(document.querySelector("#left"),-1);hold(document.querySelector("#right"),1);document.querySelector("#punch").addEventListener("click",()=>strike("punch"));document.querySelector("#kick").addEventListener("click",()=>strike("kick"));start.addEventListener("click",()=>{playerHealth=100;enemyHealth=100;player.g.position.x=-2.4;enemy.g.position.x=2.4;updateBars();running=true;cover.style.display="none";last=0});
-hold(document.querySelector("#left"),-1);hold(document.querySelector("#right"),1);document.querySelector("#punch").addEventListener("click",()=>strike("punch"));document.querySelector("#kick").addEventListener("click",()=>strike("kick"));start.addEventListener("click",()=>{playerHealth=100;enemyHealth=100;player.g.position.x=-2.4;enemy.g.position.x=2.4;updateBars();running=true;cover.style.display="none";last=0});
-addEventListener("keydown",e=>{const k=e.key.toLowerCase();if(k==="arrowleft"||k==="a"){e.preventDefault();moveDir=-1}if(k==="arrowright"||k==="d"){e.preventDefault();moveDir=1}if(k==="j")strike("punch");if(k==="k")strike("kick");if((k===" "||k==="enter")&&!running)start.click()});addEventListener("keyup",e=>{const k=e.key.toLowerCase();if(k==="arrowleft"||k==="a"||k==="arrowright"||k==="d")moveDir=0});
-addEventListener("keydown",e=>{const k=e.key.toLowerCase();if(k==="arrowleft"||k==="a"){e.preventDefault();moveDir=-1}if(k==="arrowright"||k==="d"){e.preventDefault();moveDir=1}if(k==="j")strike("punch");if(k==="k")strike("kick");if((k===" "||k==="enter")&&!running)start.click()});addEventListener("keyup",e=>{const k=e.key.toLowerCase();if(k==="arrowleft"||k==="a"||k==="arrowright"||k==="d")moveDir=0});
-function animate(t){requestAnimationFrame(animate);const dt=Math.min(35,last?t-last:16);last=t;bob+=dt*.006;if(running){player.g.position.x=clamp(player.g.position.x+moveDir*dt*.0047,-5.1,4.3);const gap=player.g.position.x-enemy.g.position.x;if(Math.abs(gap)>1.35)enemy.g.position.x+=Math.sign(gap)*dt*.0022;else if(Math.random()<.013)botStrike();enemy.g.position.x=clamp(enemy.g.position.x,-4.3,5.1);pAttack=Math.max(0,pAttack-dt);eAttack=Math.max(0,eAttack-dt);pCooldown=Math.max(0,pCooldown-dt);eCooldown=Math.max(0,eCooldown-dt);player.g.position.y=Math.abs(Math.sin(bob))*-.02;enemy.g.position.y=Math.abs(Math.sin(bob+1))*-.02;player.arms[1].rotation.x=pAttack?-.95*Math.sin(pAttack/330*Math.PI):0;player.arms[0].rotation.x=pAttack?-.3:0;enemy.arms[0].rotation.x=eAttack?-.9*Math.sin(eAttack/260*Math.PI):0;enemy.arms[1].rotation.x=eAttack?-.28:0}renderer.render(scene,camera)}
-function animate(t){requestAnimationFrame(animate);const dt=Math.min(35,last?t-last:16);last=t;bob+=dt*.006;if(running){player.g.position.x=clamp(player.g.position.x+moveDir*dt*.0047,-5.1,4.3);const gap=player.g.position.x-enemy.g.position.x;if(Math.abs(gap)>1.35)enemy.g.position.x+=Math.sign(gap)*dt*.0022;else if(Math.random()<.013)botStrike();enemy.g.position.x=clamp(enemy.g.position.x,-4.3,5.1);pAttack=Math.max(0,pAttack-dt);eAttack=Math.max(0,eAttack-dt);pCooldown=Math.max(0,pCooldown-dt);eCooldown=Math.max(0,eCooldown-dt);player.g.position.y=Math.abs(Math.sin(bob))*-.02;enemy.g.position.y=Math.abs(Math.sin(bob+1))*-.02;player.arms[1].rotation.x=pAttack?-.95*Math.sin(pAttack/330*Math.PI):0;player.arms[0].rotation.x=pAttack?-.3:0;enemy.arms[0].rotation.x=eAttack?-.9*Math.sin(eAttack/260*Math.PI):0;enemy.arms[1].rotation.x=eAttack?-.28:0}renderer.render(scene,camera)}
-addEventListener("resize",()=>{camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight)});animate(0);
-addEventListener("resize",()=>{camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight)});animate(0);
+function face(){P.g.rotation.y=0;E.g.rotation.y=Math.PI}function update(){ui.p.style.width=P.hp+"%";ui.e.style.width=E.hp+"%"}function jump(f){if(f.vy===0){f.vy=.012;f.act="jump";f.timer=360}}function strike(f,type,target){if(f.timer>0||f.act==="hit")return;f.act=type;f.timer=type==="power"?530:type==="kick"?390:290;let reach=type==="power"?2.7:type==="kick"?1.7:1.3;let dmg=type==="power"?17:type==="kick"?12:8;setTimeout(()=>{if(!running||f.act!==type)return;let d=Math.abs(f.g.position.x-target.g.position.x);if(d<reach&&Math.abs(f.g.position.y-target.g.position.y)<1.1){let dealt=target.guard?Math.ceil(dmg*.25):dmg;target.hp=Math.max(0,target.hp-dealt);target.act="hit";target.timer=220;target.hit=220;update();ui.round.textContent=target.guard?"BLOCKED!":"HIT "+dealt;if(target.hp<=0)end(f===P)}} ,type==="power"?250:140)}
+function face(){P.g.rotation.y=0;E.g.rotation.y=Math.PI}function update(){ui.p.style.width=P.hp+"%";ui.e.style.width=E.hp+"%"}function jump(f){if(f.vy===0){f.vy=.012;f.act="jump";f.timer=360}}function strike(f,type,target){if(f.timer>0||f.act==="hit")return;f.act=type;f.timer=type==="power"?530:type==="kick"?390:290;let reach=type==="power"?2.7:type==="kick"?1.7:1.3;let dmg=type==="power"?17:type==="kick"?12:8;setTimeout(()=>{if(!running||f.act!==type)return;let d=Math.abs(f.g.position.x-target.g.position.x);if(d<reach&&Math.abs(f.g.position.y-target.g.position.y)<1.1){let dealt=target.guard?Math.ceil(dmg*.25):dmg;target.hp=Math.max(0,target.hp-dealt);target.act="hit";target.timer=220;target.hit=220;update();ui.round.textContent=target.guard?"BLOCKED!":"HIT "+dealt;if(target.hp<=0)end(f===P)}} ,type==="power"?250:140)}
+function end(win){running=false;ui.copy.innerHTML=win?"<b>Kite wins the rooftop!</b><br>Volt's defense finally broke.":"<b>Volt wins this round.</b><br>Block the counterattacks and try again.";ui.start.textContent="REMATCH";ui.overlay.style.display="grid"}
+function end(win){running=false;ui.copy.innerHTML=win?"<b>Kite wins the rooftop!</b><br>Volt's defense finally broke.":"<b>Volt wins this round.</b><br>Block the counterattacks and try again.";ui.start.textContent="REMATCH";ui.overlay.style.display="grid"}
+function reset(){P.hp=E.hp=100;P.g.position.x=-2.3;E.g.position.x=2.3;[P,E].forEach(f=>{f.act="idle";f.timer=0;f.vy=0;f.guard=false});update();ui.round.textContent="FIGHT!";running=true;ui.overlay.style.display="none";last=0}
+function reset(){P.hp=E.hp=100;P.g.position.x=-2.3;E.g.position.x=2.3;[P,E].forEach(f=>{f.act="idle";f.timer=0;f.vy=0;f.guard=false});update();ui.round.textContent="FIGHT!";running=true;ui.overlay.style.display="none";last=0}
+function playerAct(x){if(!running)return;if(x==="p")strike(P,"punch",E);if(x==="k")strike(P,"kick",E);if(x==="x")strike(P,"power",E);if(x==="j")jump(P)}
+function playerAct(x){if(!running)return;if(x==="p")strike(P,"punch",E);if(x==="k")strike(P,"kick",E);if(x==="x")strike(P,"power",E);if(x==="j")jump(P)}
+function button(id,fn,hold){let b=document.getElementById(id);b.addEventListener("pointerdown",e=>{e.preventDefault();fn(true)});["pointerup","pointerleave","pointercancel"].forEach(t=>b.addEventListener(t,()=>fn(false)))}
+function button(id,fn,hold){let b=document.getElementById(id);b.addEventListener("pointerdown",e=>{e.preventDefault();fn(true)});["pointerup","pointerleave","pointercancel"].forEach(t=>b.addEventListener(t,()=>fn(false)))}
+button("left",v=>dir=v?-1:0);button("right",v=>dir=v?1:0);button("punch",v=>v&&playerAct("p"));button("kick",v=>v&&playerAct("k"));button("power",v=>v&&playerAct("x"));button("guard",v=>{guard=v;P.guard=v});
+button("left",v=>dir=v?-1:0);button("right",v=>dir=v?1:0);button("punch",v=>v&&playerAct("p"));button("kick",v=>v&&playerAct("k"));button("power",v=>v&&playerAct("x"));button("guard",v=>{guard=v;P.guard=v});
+ui.start.addEventListener("click",reset);addEventListener("keydown",e=>{let k=e.key.toLowerCase();if(k==="a"||k==="arrowleft")dir=-1;if(k==="d"||k==="arrowright")dir=1;if(k==="j")playerAct("p");if(k==="k")playerAct("k");if(k==="l")playerAct("x");if(k==="w")playerAct("j");if(k==="s")P.guard=true;if(k==="enter"&&!running)reset()});addEventListener("keyup",e=>{let k=e.key.toLowerCase();if(["a","d","arrowleft","arrowright"].includes(k))dir=0;if(k==="s")P.guard=false});
+ui.start.addEventListener("click",reset);addEventListener("keydown",e=>{let k=e.key.toLowerCase();if(k==="a"||k==="arrowleft")dir=-1;if(k==="d"||k==="arrowright")dir=1;if(k==="j")playerAct("p");if(k==="k")playerAct("k");if(k==="l")playerAct("x");if(k==="w")playerAct("j");if(k==="s")P.guard=true;if(k==="enter"&&!running)reset()});addEventListener("keyup",e=>{let k=e.key.toLowerCase();if(["a","d","arrowleft","arrowright"].includes(k))dir=0;if(k==="s")P.guard=false});
+function tick(t){requestAnimationFrame(tick);let dt=Math.min(35,last?t-last:16);last=t;if(running){P.guard=guard||P.guard;P.g.position.x=Math.max(-5,Math.min(4.7,P.g.position.x+dir*dt*.005));let distance=P.g.position.x-E.g.position.x;if(Math.abs(distance)>1.65&&E.timer<=0)E.g.position.x+=Math.sign(distance)*dt*.0027;enemyThink-=dt;if(enemyThink<0&&E.timer<=0){enemyThink=420+Math.random()*620;let d=Math.abs(distance);if(d<1.7&&Math.random()<.65)strike(E,Math.random()<.34?"kick":"punch",P);else if(d<2.7&&Math.random()<.26)strike(E,"power",P);else if(Math.random()<.15)jump(E);E.guard=Math.random()<.22&&d<2}for(const f of [P,E]){f.timer=Math.max(0,f.timer-dt);if(f.timer===0&&f.act!=="idle")f.act="idle";f.vy-=dt*.000045;f.g.position.y=Math.max(0,f.g.position.y+f.vy*dt);if(f.g.position.y===0)f.vy=0;let amt=f.act==="punch"?-.95*Math.sin(Math.min(1,f.timer/290)*Math.PI):f.act==="kick"?-.75*Math.sin(Math.min(1,f.timer/390)*Math.PI):f.act==="power"?-1.2*Math.sin(Math.min(1,f.timer/530)*Math.PI):0;f.arms[1].rotation.x=amt;f.arms[0].rotation.x=f.guard?.9:amt*.25;f.g.scale.y=f.act==="crouch"?.72:1}ui.round.textContent=P.guard?"GUARDING":"FIGHT!"}r.render(scene,cam)}
+function tick(t){requestAnimationFrame(tick);let dt=Math.min(35,last?t-last:16);last=t;if(running){P.guard=guard||P.guard;P.g.position.x=Math.max(-5,Math.min(4.7,P.g.position.x+dir*dt*.005));let distance=P.g.position.x-E.g.position.x;if(Math.abs(distance)>1.65&&E.timer<=0)E.g.position.x+=Math.sign(distance)*dt*.0027;enemyThink-=dt;if(enemyThink<0&&E.timer<=0){enemyThink=420+Math.random()*620;let d=Math.abs(distance);if(d<1.7&&Math.random()<.65)strike(E,Math.random()<.34?"kick":"punch",P);else if(d<2.7&&Math.random()<.26)strike(E,"power",P);else if(Math.random()<.15)jump(E);E.guard=Math.random()<.22&&d<2}for(const f of [P,E]){f.timer=Math.max(0,f.timer-dt);if(f.timer===0&&f.act!=="idle")f.act="idle";f.vy-=dt*.000045;f.g.position.y=Math.max(0,f.g.position.y+f.vy*dt);if(f.g.position.y===0)f.vy=0;let amt=f.act==="punch"?-.95*Math.sin(Math.min(1,f.timer/290)*Math.PI):f.act==="kick"?-.75*Math.sin(Math.min(1,f.timer/390)*Math.PI):f.act==="power"?-1.2*Math.sin(Math.min(1,f.timer/530)*Math.PI):0;f.arms[1].rotation.x=amt;f.arms[0].rotation.x=f.guard?.9:amt*.25;f.g.scale.y=f.act==="crouch"?.72:1}ui.round.textContent=P.guard?"GUARDING":"FIGHT!"}r.render(scene,cam)}
+addEventListener("resize",()=>{cam.aspect=innerWidth/innerHeight;cam.updateProjectionMatrix();r.setSize(innerWidth,innerHeight)});tick(0);
+addEventListener("resize",()=>{cam.aspect=innerWidth/innerHeight;cam.updateProjectionMatrix();r.setSize(innerWidth,innerHeight)});tick(0);
← Histórico de versões