<!-- SOLID CITY · part 2110 -->
.2);
  }
  // model buildings on display table
  bx(6,0.9,2,M.table, 0,0.45,-4.5);
  for (var mI=0;mI<4;mI++){
    var mh=0.8+mI*0.4;
    bx(0.7,mh,0.7,M.paper, -2+mI*1.3,mh/2+0.9,-4.5);
  }
  sl(0,-4.5,6,2,0,2.5);
  // blueprint wall
  for (var bp=0;bp<3;bp++) bx(2.6,3.4,0.15,M.blueprint, -4+bp*4,3.2,-6.7);
  // material samples shelf
  bx(4,2.2,0.8,M.shelf, 6,1.1,4.5);
  var sampCols=[0xc84a4a,0x4a8ac8,0xd8aa48,0x4ac86a];
  for (var sm=0;sm<4;sm++) bx(0.8,0.5,0.6,new T.MeshLambertMaterial({color:sampCols[sm]}), 4.8+sm*0.8,1.6,4.5);
  sl(6,4.5,4,0.8,0,2.2);
}

function buildHarbor(T, root, foot, o, BM) {
  var gold = o.gold, steel = o.steel;
  var conc = new T.MeshLambertMaterial({color: 0x4a4e55});
  var darkM = new T.MeshLambertMaterial({color: 0x23272e});
  var contCols = [0xc84a3a, 0x3a7ac8, 0x3aa84a, 0xe8b83a, 0x8a4ac8].map(function(c){
    return new T.MeshLambertMaterial({color: c});
  });
  function box(w,h,d,mat,x,y,z){
    var m = new T.Mesh(new T.BoxGeometry(w,h,d), mat);
    m.position.set(x,y,z); root.add(m); return m;
  }
  function solid(x,z,w,d,y0,y1){ foot.push([x,z,w,d,y0,y1,0]); }

  // ---- QUAY: x -580..-470, z -140..140 ----
  box(110,3,280,conc, -525,0,0); solid(-525,0,110,280,-2,1.5);
  var qmark = (function(){
    // quiet marker "THE HARBOR"
    var g = new T.Group();
    var c = document.createElement('canvas'); c.width=512; c.height=128;
    var x2 = c.getContext('2d');
    x2.fillStyle='#101418'; x2.fillRect(0,0,512,128);
    x2.strokeStyle='#d8aa48'; x2.lineWidth=5; x2.strokeRect(7,7,498,114);
    x2.fillStyle='#e8c86a'; x2.textAlign='center'; x2.textBaseline='middle';
    x2.font='bold 52px Georgia,serif'; x2.fillText('THE HARBOR',256,66);
    var t = new T.CanvasTexture(c); t.encoding = T.sRGBEncoding;
    var face = new T.Mesh(new T.PlaneGeometry(8,2), new T.MeshBasicMaterial({map:t}));
    face.position.set(-475,4,0); face.rotation.y = Math.PI/2; g.add(face);
    var post = new T.Mesh(new T.BoxGeometry(0.5,4,0.5), darkM);
    post.position.set(-475,2,0); g.add(post);
    root.add(g);
  })();

  // ---- 3 CARGO PIERS ----
  [-80, 0, 80].forEach(function(pz){
    box(120,2.5,16,conc, -640,0.25,pz); solid(-640,pz,120,16,-2,1.5);
    // pilings
    for (var px=-695; px<=-585; px+=18) {
      box(2,10,2,darkM, px,-4,pz-6.5);
      box(2,10,2,darkM, px,-4,pz+6.5);
    }
    // bollards
    for (var bx2=-690; bx2<=-590; bx2+=25) {
      box(1.1,1.3,1.1,steel, bx2,2,pz-6);
      box(1.1,1.3,1.1,steel, bx2,2,pz+6);
      solid(bx2,pz-6,1.1,1.1,1,3); solid(bx2,pz+6,1.1,1.1,1,3);
    }
    // pier edge lights
    for (var lx=-690; lx<=-590; lx+=33) {
      var lb = new T.Mesh(new T.SphereGeometry(0.5,8,6), new T.MeshBasicMaterial({color:0xffe8a0}));
      lb.position.set(lx,2.2,pz-7); root.add(lb);
      var lb2 = lb.clone(); lb2.position.set(lx,2.2,pz+7); root.add(lb2);
    }
  });

  // ---- 6 GANTRY CRANES ----
  [-80, 0, 80].forEach(function(pz){
    [-662, -618].forEach(function(cx){
      [[-9,-9],[-9,9],[9,-9],[9,9]].forEach(function(l){
        box(2.2,30,2.2,steel, cx+l[0],15,pz+l[1]);
        solid(cx+l[0],pz+l[1],2.2,2.2,0,30);
      });
      box(24,3.5,22,steel, cx,31.5,pz); // top beam
      box(24,1,24,new T.MeshLambertMaterial({color:0x8a94a0}), cx,29.5,pz); // machinery deck
      // trolley (animated)
      var tr = box(4,2,4,gold, cx,28,pz); tr.name='trolley'; tr.userData.cx=cx; tr.userData.pz=pz;
      box(0.35,12,0.35,darkM, cx,21,pz);
      box(7,1.2,3.5,gold, cx,14.5,pz); // spreader
      if (!window._hyperTrolleys) window._hyperTrolleys = [];
      window._hyperTrolleys.push(tr);
    });
  });

  // ---- CONTAINER STACKS on quay ----
  var ci = 0;
  for (var rx=-565; rx<=-490; rx+=16) {
    for (var rz=-125; rz<=125; rz+=22) {
      if (Math.abs(rz) < 30) continue; // working lane
      if ((rx+rz)%3 === 0) continue; // gaps
      var sh = 2 + (Math.abs(rx*7+rz*13) % 3); // 2-4 high
      for (var s2=0; s2<sh; s2++) {
        box(13,2.7,3,contCols[ci % contCols.length], rx, 3+s2*2.8, rz);
        ci++;
      }
      solid(rx,rz,13,3,0,3+sh*2.8);
    }
  }

  // ---- 2 CARGO SHIPS ----
  [[-80,-1],[80,1]].forEach(function(sp, si){
    var pz = sp[0], side = sp[1];
    var sz = pz + side*26;
    var sg = new T.Group(); sg.position.set(-640, 0, sz);
    var hullM = new T.MeshLambertMaterial({color: 0x2a3a4a});
    var hull = new T.Mesh(new T.BoxGeometry(72,9,17), hullM);
    hull.position.y = -2.5; sg.add(hull);
    var bow = new T.Mesh(new T.BoxGeometry(16,9,17), hullM);
    bow.position.set(42,-2.5,0); bow.rotation.y = 0.45; sg.add(bow);
    var stripeM = new T.MeshLambertMaterial({color: 0xc84a3a});
    var stripe = new T.Mesh(new T.BoxGeometry(72.5,1.2,17.5), stripeM);
    stripe.position.y = 0.5; sg.add(stripe);
    // deck containers
    for (var dx=-28; dx<=28; dx+=14) {
      for (var dz=-5.5; dz<=5.5; dz+=6) {
        var cm = contCols[Math.abs(dx+dz+si*7) % contCols.length];
        var cb = new T.Mesh(new T.BoxGeometry