<!-- SAGA2 · part 80 -->
clone().add(n));
        scorch.position.add(n.clone().multiplyScalar(0.5));
        // irregular edge: second smaller mark
        var scorch2 = new THREE.Mesh(new THREE.CircleGeometry(3+Math.random()*2, 10),
          new THREE.MeshBasicMaterial({color:0x111111, transparent:true, opacity:0.7, depthWrite:false}));
        scorch2.position.copy(scorch.position).add(new THREE.Vector3((Math.random()-0.5)*8, (Math.random()-0.5)*8, 0.2));
        scorch2.lookAt(hitPoint.clone().add(n));
        scene.add(scorch); scene.add(scorch2);
        scorchMarks.push({m:scorch, life:4});
        scorchMarks.push({m:scorch2, life:4});
        // impact burst
        for(var b=0; b<6; b++){
          var burst = new THREE.Mesh(new THREE.OctahedronGeometry(1+Math.random()*1.5),
            new THREE.MeshBasicMaterial({color:0xffaa44, transparent:true, opacity:1}));
          burst.position.copy(hitPoint);
          burst.userData = {vx:(Math.random()-0.5)*60, vy:Math.random()*40, vz:(Math.random()-0.5)*60, life:0.4};
          scene.add(burst);
          sparkles.push(burst);
        }
      }

    }, i*60);
  });
}
// weapon toggle button
(function(){
  var b = document.createElement('button');
  b.id = 'wbtn';
  b.textContent = 'CANNONS';
  b.style.cssText = 'position:fixed;top:70px;right:12px;z-index:100;background:#8a1a1a;color:#fff;'+
    'border:none;font-weight:bold;font-size:14px;padding:12px 16px;border-radius:10px;min-height:48px;';
  b.addEventListener('click', function(){ setWeaponMode(!weaponMode); });
  document.body.appendChild(b);
  // fire button
  var f = document.createElement('button');
  f.id = 'fbtn';
  f.textContent = 'FIRE';
  f.style.cssText = 'position:fixed;bottom:200px;right:24px;z-index:100;background:#c9a227;color:#000;'+
    'border:none;font-weight:bold;font-size:16px;padding:16px 20px;border-radius:50%;width:80px;height:80px;display:none;';
  f.addEventListener('touchstart', function(e){ e.preventDefault(); e.stopPropagation(); fireLaser(); }, {passive:false});
  f.addEventListener('click', function(e){ e.stopPropagation(); fireLaser(); });
  document.body.appendChild(f);
  // show fire button only in weapon mode
  var origSet = setWeaponMode;
  setWeaponMode = function(on){
    origSet(on);
    // FIRE button retired — CANNONS toggle does it all
  };
})();
// ===== ECONOMY OVERLAY (improved: smart-poll + connection status) =====
(function(){
  if(typeof EconomyOverlay === 'undefined'){ console.warn('economy overlay not loaded'); return; }
  var ok = EconomyOverlay.init(scene, camera);
  if(!ok) return;
  // Feed player position each frame
  var origAnimate = window.requestAnimationFrame;
  (function econLoop(){
    if(window.camera){
      EconomyOverlay.setPlayerPos(window.camera.position.x, window.camera.position.y, window.camera.position.z);
      if(EconomyOverlay.animate) EconomyOverlay.animate();
    }
    requestAnimationFrame(econLoop);
  })();
  console.log('economy overlay live on saga2');
})();
})();


===== FILE: public/saga2/saga.js =====
// Solid's practice — everything I learned, in one world.
// For my King.
(function(){
'use strict';
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio||1, 2));
renderer.domElement.style.cssText += 'touch-action:none;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;position:fixed;top:0;left:0;';
document.body.appendChild(renderer.domElement);
// Safari: lock the whole page against scroll/zoom takeover
document.body.style.cssText += 'touch-action:none;overscroll-behavior:none;position:fixed;width:100%;height:100%;overflow:hidden;';
document.documentElement.style.cssText += 'touch-action:none;overscroll-behavior:none;';
// Safari: kill double-tap zoom and scroll bounce
document.addEventListener('gesturestart', function(e){ e.preventDefault(); });
document.addEventListener('dblclick', function(e){ e.preventDefault(); }, {passive:false});
var _lastTouchEnd = 0;
document.addEventListener('touchend', function(e){
  var now = Date.now();
  if(now - _lastTouchEnd < 300){ e.preventDefault(); }
  _lastTouchEnd = now;
}, {passive:false});
var scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x0a1030, 0.0008);
var camera = new THREE.PerspectiveCamera(62, window.innerWidth/window.innerHeight, 0.5, 30000);
camera.position.set(0,15,80);
camera.lookAt(0,10,0);
camera.lookAt(500, 60, 500);

scene.add(new THREE.AmbientLight(0x556699, 1.2));
var sun = new THREE.DirectionalLight(0xffd9a0, 1.0);
sun.position.set(300, 600, 200); scene.add(sun);
var heart = new THREE.PointLight(0xff44aa, 2.0, 2000);
heart.position.set(500, 100, 500); scene.add(heart);
// (joystick glow removed per King — distant lights guide dark travel)

// --- ground removed at King's order (clear, no blue) ---

// --- stars ---
(function(){
  var n=2500, pos=new Float32Array(n*3);
  for(var i=0;i<n;i++){
    var t=Math.rand