<!-- SAGA · part 46 -->

        window._dynamics.push(function(t, dt){
          schoolT += (dt || 0.016) * 0.5;
          for(var i = 0; i < fishes.length; i++){
            var fh = fishes[i];
            /* Swim in circle */
            var a = schoolT + fh.off * 0.3;
            var tx = sx + Math.cos(a) * 12;
            var tz = sz + Math.sin(a) * 12;
            fh.mesh.position.lerp(new THREE.Vector3(tx, 0.2 + Math.sin(t*2+fh.off)*0.1, tz), 0.08);
            fh.mesh.rotation.y = Math.atan2(tx - fh.mesh.position.x, tz - fh.mesh.position.z);
            /* Tail wag */
            fh.tail.rotation.y = Math.sin(t*8 + fh.off) * 0.5;
          }
        });
      })();
    }
  }catch(e){}
}
if(typeof buildFish === 'function'){ window._buildQueue.push(buildFish); }

/* BIRDS FLOCKING — life in the sky. 2026-10-04. */
function buildBirds(){
  try{
    window._dynamics = window._dynamics || [];
    var birdMat = new THREE.MeshBasicMaterial({color: 0x222222});
    /* 3 flocks */
    for(var f = 0; f < 3; f++){
      (function(){
        var flock = [];
        var cx = (Math.random()-0.5) * 400;
        var cz = (Math.random()-0.5) * 400;
        var cy = 80 + Math.random() * 40;
        for(var b = 0; b < 12; b++){
          var bird = new THREE.Group();
          var body = new THREE.Mesh(new THREE.ConeGeometry(0.5, 2, 4), birdMat);
          body.rotation.x = Math.PI/2;
          bird.add(body);
          var wingL = new THREE.Mesh(new THREE.PlaneGeometry(3, 1), birdMat);
          wingL.position.x = -1.5;
          bird.add(wingL);
          var wingR = new THREE.Mesh(new THREE.PlaneGeometry(3, 1), birdMat);
          wingR.position.x = 1.5;
          bird.add(wingR);
          bird.position.set(cx + (Math.random()-0.5)*30, cy + (Math.random()-0.5)*10, cz + (Math.random()-0.5)*30);
          scene.add(bird);
          flock.push({mesh: bird, wingL: wingL, wingR: wingR, off: Math.random()*10});
        }
        var flockT = Math.random() * 100;
        window._dynamics.push(function(t, dt){
          flockT += (dt || 0.016) * 0.3;
          /* Flock moves in a circle */
          var fx = cx + Math.cos(flockT) * 150;
          var fz = cz + Math.sin(flockT) * 150;
          for(var i = 0; i < flock.length; i++){
            var brd = flock[i];
            /* Follow flock center with offset */
            var tx = fx + Math.sin(brd.off + flockT*2) * 15;
            var tz = fz + Math.cos(brd.off + flockT*2) * 15;
            var ty = cy + Math.sin(brd.off*2 + t) * 5;
            brd.mesh.position.lerp(new THREE.Vector3(tx, ty, tz), 0.05);
            brd.mesh.rotation.y = Math.atan2(tx - brd.mesh.position.x, tz - brd.mesh.position.z);
            /* Flap */
            var flap = Math.sin(t*10 + brd.off) * 0.6;
            brd.wingL.rotation.z = flap;
            brd.wingR.rotation.z = -flap;
          }
        });
      })();
    }
  }catch(e){}
}
if(typeof buildBirds === 'function'){ window._buildQueue.push(buildBirds); }

/* DAY/NIGHT CYCLE — the world breathes. 2026-10-04. */
(function(){
  window._dynamics = window._dynamics || [];
  var cycleTime = 0;
  var dayLength = 300; /* 5 minutes per day */
  window._dynamics.push(function(t, dt){
    try{
      cycleTime += (dt || 0.016);
      var phase = (cycleTime % dayLength) / dayLength; /* 0-1 */
      var sunAngle = phase * Math.PI * 2;
      /* Sun position */
      var sunX = Math.cos(sunAngle) * 800;
      var sunY = Math.sin(sunAngle) * 500 + 100;
      /* Find the key light and move it */
      /* (We can't easily reference it, so we adjust fog and sky) */
      var dayness = Math.max(0, Math.sin(sunAngle));
      /* Fog color shifts: gold day, dark blue night */
      if(scene.fog){
        var r = 0.1 + dayness * 0.7;
        var g = 0.1 + dayness * 0.5;
        var b = 0.2 + dayness * 0.1;
        scene.fog.color.setRGB(r, g, b);
      }
    }catch(e){}
  });
})();




/* DYNAMIC FOV — widens with speed. Academic: FOV kick is the cheapest feel-of-speed.
   Base 72, +8 running, +12 flying. */
var _baseFOV = 72;
var _targetFOV = 72;
window._dynamics = window._dynamics || [];
window._dynamics.push(function(t, dt){
  try{
    var speed = 0;
    if(typeof lastDX !== 'undefined'){
      speed = Math.sqrt(lastDX*lastDX + lastDZ*lastDZ);
    }
    if(typeof moveMode !== 'undefined' && moveMode === 'fly'){
      _targetFOV = 84; /* Flying: wide */
    } else if(speed > 1.5){
      _targetFOV = 80; /* Running: kick */
    } else if(speed > 0.3){
      _targetFOV = 76; /* Walking */
    } else {
      _targetFOV = _baseFOV; /* Still */
    }
    /* Smooth lerp */
    var cur = camera.fov;
    var next = cur + (_targetFOV - cur) * 0.08;
    if(Math.abs(next - cur) > 0.01){
      camera.fov = next;
      camera.updateProjectionMatrix();
    }
  }catch(e){}
});





/* FLYING SWORD — directional flight, like the old game. 2026-10-04. */
var _flySword = null;
function ensureFlySword(){
  if(_flySword) return;
  _flySword = new THREE.Group();
  /* Blade 