<!-- SAGA2 · part 29 -->
erData.lights[2].intensity += (cf - avatarGrp.userData.lights[2].intensity)*0.1;
      if(avatarGrp.userData.lights[3]) avatarGrp.userData.lights[3].intensity += (cf - avatarGrp.userData.lights[3].intensity)*0.1;
    }
    if(avatarGrp.userData.antT){
      avatarGrp.userData.antT.material.color.setHex(Math.sin(t*6)>0?0x55ddff:0x1a5a77);
    }
    // energy pulses
    if(avatarGrp.userData.pulses){
      avatarGrp.userData.pulses.forEach(function(p){
        p.userData.t += 0.016*0.5;
        if(p.userData.t>1) p.userData.t = 0;
        p.position.y = -16 + p.userData.t*14;
        var f = Math.sin(p.userData.t*Math.PI);
        p.scale.set(f+0.3, f+0.3, f+0.3);
      });
    }
    // cannon heat
    [avatarCannonL, avatarCannonR].forEach(function(ac){
      if(ac && ac.userData.heats){
        ac.userData.heats.forEach(function(h){
          var tgt = weaponMode ? 0.4 : 0;
          h.material.opacity += (tgt - h.material.opacity)*0.07;
        });
      }
    });
    // head alive — subtle, human
    if(avatarGrp.userData.head){
      avatarGrp.userData.head.rotation.y = Math.sin(t*0.5)*0.06;
      avatarGrp.userData.head.rotation.x = Math.sin(t*0.7)*0.03;
    }
    // BREATHING — the whole being breathes
    var breath = 1+Math.sin(t*1.2)*0.015;
    avatarGrp.scale.set(0.55*breath, 0.55*breath, 0.55*breath);
    // ENERGY PULSE — brightness waves through lines
    if(avatarGrp.userData.pulseT === undefined) avatarGrp.userData.pulseT = 0;
    avatarGrp.userData.pulseT += 0.016;
    var pulse = 0.75+Math.sin(avatarGrp.userData.pulseT*2.5)*0.25;
    if(avatarGrp.userData.lineMats){
      avatarGrp.userData.lineMats[1].opacity = 0.7+pulse*0.3; // gold breathes
      avatarGrp.userData.lineMats[0].opacity = 0.65+pulse*0.25; // cyan follows
    }
    // mandala — the pure linework breathes
    // (halo removed)
    if(avatarGrp.userData.reticle){
      avatarGrp.userData.reticle.rotation.z = t*0.3;
      var rs = 1+Math.sin(t*2)*0.05;
      avatarGrp.userData.reticle.scale.set(rs, rs, 1);
    }

    if(avatarGrp.userData.antTip){
      avatarGrp.userData.antTip.material.color.setHex(Math.sin(t*5)>0?0x44ccff:0x226688);
    }
    // subtle breathing
    avatarGrp.position.y = -16 + Math.sin(t*1.5)*0.35;
  }

  var sp=40*dt, fx=-Math.sin(yaw), fz=-Math.cos(yaw), rx=Math.cos(yaw), rz=-Math.sin(yaw); // 6x slower
  if(keys['w']){camera.position.x+=fx*sp;camera.position.z+=fz*sp;}
  if(keys['s']){camera.position.x-=fx*sp;camera.position.z-=fz*sp;}
  if(keys['a']){camera.position.x-=rx*sp;camera.position.z-=rz*sp;}
  if(keys['d']){camera.position.x+=rx*sp;camera.position.z+=rz*sp;}
  if(keys[' '])camera.position.y+=sp;
  if(stickL.active){
    camera.position.x+=(fx*-stickL.dy+rx*stickL.dx)*sp;
    camera.position.z+=(fz*-stickL.dy+rz*stickL.dx)*sp;
  }
  camera.rotation.order='YXZ'; camera.rotation.y=yaw; camera.rotation.x=pitch; camera.rotation.z=0;
  // CONTINUOUS FIRE: CANNONS button toggles, lasers never stop while live
  if(weaponMode){
    if(!window._lastAuto) window._lastAuto = 0;
    if(t - window._lastAuto > 0.22){
      window._lastAuto = t;
      fireLaser();
    }
  }
  // laser fade
  for(var li=lasers.length-1; li>=0; li--){
    lasers[li].life -= dt;
    lasers[li].mesh.children.forEach(function(c){ c.material.opacity = Math.max(0, lasers[li].life*5); });
    if(lasers[li].life<=0){ scene.remove(lasers[li].mesh); lasers.splice(li,1); }
  }
  // sparkles drift + fade
  for(var si=sparkles.length-1; si>=0; si--){
    var spk=sparkles[si], u=spk.userData;
    u.life-=dt;
    spk.position.x+=u.vx*dt; spk.position.y+=u.vy*dt; spk.position.z+=u.vz*dt;
    spk.rotation.x+=dt*5; spk.rotation.y+=dt*7;
    spk.material.opacity=Math.max(0,u.life*2);
    if(u.life<=0){ scene.remove(spk); sparkles.splice(si,1); }
  }
  // scorch marks fade shortly
  for(var mi=scorchMarks.length-1; mi>=0; mi--){
    var sm=scorchMarks[mi];
    sm.life-=dt;
    if(sm.life<1.5) sm.m.material.opacity=Math.max(0, sm.life/1.5*0.85);
    if(sm.life<=0){ scene.remove(sm.m); scorchMarks.splice(mi,1); }
  }
  renderer.render(scene,camera);
}
// BLACKOUT — only avatars and h=10 remain
//buildSentinel();
//buildScout();
//buildHuman();
buildAvatar();
// BLACK WALL behind the winning face (King's order)
(function(){
  var wall = new THREE.Mesh(
    new THREE.PlaneGeometry(60, 40),
    new THREE.MeshBasicMaterial({color:0x080808})
  );
  wall.position.set(0, 14, 132);
  // face the soldiers/camera (toward -Z)
  wall.rotation.y = Math.PI;
  scene.add(wall);
})();

// MANNEQUIN HEAD — winner's face (index 0), clean 3D form (King's order)
(function(){ return; // BLACKOUT
  var mg = new THREE.Group();
  var mCyan = new THREE.LineBasicMaterial({color:0x66eeff, transparent:true, opacity:0.85});
  var mGold = new THREE.LineBasicMaterial({color:0xffcc44, transparent:true, opacity:0.85});
  function mline(pts, mat){
    mg.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts.map(function(p){
      return new THRE