<!-- SOLID CITY · part 2116 -->
n.y = -ang + Math.PI/2;
          // face outward
          hole.lookAt(hx*2-tb.x, py, hz*2-tb.z);
          root.add(hole);
          window._hyperPortholes.push(hole);
        }
      }
    });
    // Portholes on spire upper cylinders (every 30m from y=120 to 480)
    for (var sy = 130; sy < 480; sy += 32) {
      for (var sf = 0; sf < 6; sf++) {
        var sa = sf*Math.PI/3;
        var sr = 20 - (sy/480)*10; // taper
        var shx = Math.cos(sa)*sr, shz = -58 + Math.sin(sa)*sr;
        var shole = new T.Mesh(new T.CircleGeometry(2.8, 20), pmat);
        shole.position.set(shx, sy, shz);
        shole.lookAt(shx*2, sy, shz*2+58);
        root.add(shole);
        window._hyperPortholes.push(shole);
      }
    }
    // Portholes on all other towers (every 30m, 2 per face)
    built.forEach(function(tb){
      if(tb===pickA||tb===pickB) return;
      var liftB = tb.lift||0;
      for (var py2 = 40; py2 < tb.h; py2 += 34) {
        for (var f2 = 0; f2 < 4; f2++) {
          var ang2 = f2*Math.PI/2 + 0.4;
          var rad2 = tb.w*0.6;
          var hx2 = tb.x + Math.cos(ang2)*rad2, hz2 = tb.z + Math.sin(ang2)*rad2;
          var hole2 = new T.Mesh(new T.CircleGeometry(2.6, 20), pmat);
          hole2.position.set(hx2, py2+liftB, hz2);
          hole2.lookAt(hx2*2-tb.x, py2+liftB, hz2*2-tb.z);
          root.add(hole2);
          window._hyperPortholes.push(hole2);
        }
      }
    });
  })();

  // ---- central plaza: fountain ----
  var plaza=new T.Mesh(new T.CylinderGeometry(42,44,0.8,40),new T.MeshLambertMaterial({color:0x363c46}));
  plaza.position.y=0.5;root.add(plaza);
  var basin=new T.Mesh(new T.CylinderGeometry(12,14,2.4,24),steel);basin.position.y=1.6;root.add(basin);
  var water=new T.Mesh(new T.CylinderGeometry(11,11,0.6,24),
    new T.MeshBasicMaterial({color:0x6ab8e8,transparent:true,opacity:0.85}));
  water.position.y=2.6;water.name='fountainwater';root.add(water);
  var jet=new T.Mesh(new T.CylinderGeometry(0.8,1.6,16,8),
    new T.MeshBasicMaterial({color:0xbfe4ff,transparent:true,opacity:0.7}));
  jet.position.y=10;jet.name='fountainjet';root.add(jet);
  var obel=new T.Mesh(new T.CylinderGeometry(2,3.4,22,4),gold);
  obel.position.y=11;obel.rotation.y=Math.PI/4;root.add(obel);
  foot.push([0,0,30,30,0,24,0]);
  var dmark=quietMarker(T,'THE HYPER CITY');dmark.position.set(0,0,48);root.add(dmark);

  // ---- waterfront promenade (west edge) ----
  var prom=new T.Mesh(new T.BoxGeometry(36,1.2,380),new T.MeshLambertMaterial({color:0x3a3f47}));
  prom.position.set(-192,0.4,0);root.add(prom);
  // railing (collision so he doesn't stroll into the sea)
  var railM=new T.MeshLambertMaterial({color:0x8a94a0});
  for(var rz=-180;rz<=180;rz+=12){
    var post=new T.Mesh(new T.BoxGeometry(0.5,3,0.5),railM);
    post.position.set(-209,2.2,rz);root.add(post);
  }
  var rail=new T.Mesh(new T.BoxGeometry(0.6,0.5,372),railM);
  rail.position.set(-209,3.8,0);root.add(rail);
  foot.push([-209,0,2,372,0,4,0]);
  // piers + boats
  var boatHullM=new T.MeshLambertMaterial({color:0x5a3a28});
  var boatMastM=new T.MeshLambertMaterial({color:0x8a6a48});
  var sailM=new T.MeshLambertMaterial({color:0xe8e0d0,side:T.DoubleSide});
  [-90,30,120].forEach(function(pz){
    var pier=new T.Mesh(new T.BoxGeometry(70,1.6,10),deckM);
    pier.position.set(-244,0.2,pz);root.add(pier);
    foot.push([-244,pz,70,10,0,1.2,0]);
    for(var b=0;b<2;b++){
      var bx=-244+(b?18:-14), bz=pz+(b?6:-5);
      var hull=new T.Mesh(new T.BoxGeometry(10,3,4),boatHullM);
      hull.position.set(bx,-2.5,bz);hull.name='boat';root.add(hull);
      var mast=new T.Mesh(new T.CylinderGeometry(0.3,0.3,12,6),boatMastM);
      mast.position.set(bx,2,bz);root.add(mast);
      var sail=new T.Mesh(new T.PlaneGeometry(7,9),sailM);
      sail.position.set(bx,5,bz+0.5);root.add(sail);
    }
  });

  // animated bits
  var beaconM=null,beamM=null,fw=null,fj=null;
  root.traverse(function(m){
    if(m.name==='beacon')beaconM=m;
    if(m.name==='beam')beamM=m;
    if(m.name==='fountainwater')fw=m;
    if(m.name==='fountainjet')fj=m;
  });
  var boats=[];root.traverse(function(m){if(m.name==='boat')boats.push(m);});
  // ---- PORTAL NETWORK: hero portholes go live (GO FOR LAUNCH) ----
  // Walk into a gate disc → 2s swirl → arrive at the next node. Tower to tower.
  p2gates.push({x:14, z:-16, y:0, name:'THE SPIRE'});
  p2gates.push({x:28, z:28, y:0, name:'THE PLAZA'});
  p2gates.push({x:-520, z:8, y:0, name:'THE HARBOR'});
  p2gates.push({x:10, z:-58, y:32, name:'THE OBSERVATORY'});
  var towerGates = p2gates.slice(0, p2gates.length-4);
  var specialGates = p2gates.slice(p2gates.length-4);
  towerGates.sort(function(a,b){ return Math.atan2(a.z,a.x)-Math.atan2(b.z,b.x); });
  var ring = towerGates.concat(specialGates);
  var portalPlaza = null;
  var gpc = document.createElement('canvas'); gpc.width=gpc.height=128;
  var gpx = gpc.getContext('2d');
  var gpgr = gpx.createRadialGradient(64,64,4,64,64,64);
  gpgr.addColo