<!-- SOLID CITY · part 880 -->
4, Y, Y + 30);
  var wTip = new T.Mesh(new T.OctahedronGeometry(2), M.glowGold); wTip.position.set(W.x + 20, Y + 32, W.z + 14); world.add(wTip);
  animated.push(function (t) { wTip.rotation.y = t * 1.2; });
  sign('AI WORKSHOP', 'HUMANS + AI · SHARED TASKS', W.x, Y + 26, W.z - 40, 30);
  sign('LIVE PROJECT STATS', 'OPEN IN THE WORKSHOP', W.x + 20, Y + 24, W.z + 14, 22);
  portal(D.workshop, W.x, W.z + 48, Math.atan2(-W.x, -W.z));
  sign('WARSHIP', 'TWIN BATTERIES \u00b7 BOARD', DSK.x, Y + 20, DSK.z - 80, 26);
  portal(D.warship, DSK.x, DSK.z - 44, Math.atan2(-DSK.x, -DSK.z));
  sign('FLEET CONTROL', '8 SHIPS \u00b7 2 COMMANDERS', DSK.x - 80, Y + 20, DSK.z, 26);
  portal(D.fleet, DSK.x - 44, DSK.z, Math.atan2(-DSK.x, -DSK.z));
  sign('SWARM', '12 DRONES \u00b7 LEAD', DSK.x + 44, Y + 20, DSK.z - 44, 24);
  portal(D.swarm, DSK.x + 44, DSK.z - 44, Math.atan2(-DSK.x, -DSK.z));
  sign('RESCUE', 'EVERY LIFE MATTERS', DSK.x - 44, Y + 20, DSK.z - 44, 24);
  portal(D.rescue, DSK.x - 44, DSK.z - 44, Math.atan2(-DSK.x, -DSK.z));
  sign('SIEGE', 'BREAK THE FORTRESS', DSK.x + 44, Y + 20, DSK.z + 84, 24);
  portal(D.siege, DSK.x + 44, DSK.z + 84, Math.atan2(-DSK.x, -DSK.z));
  sign('AXIOM', 'THE LIVING FRONTIER \u00b7 CREW THE STATION', -584, Y + 20, 162 - 36, 24);
  portal(D.axiom, -584, 162, Math.atan2(584, -162));
  buildSoulTower(Y);
  // ---- skyline: instanced towers (one draw call), all solid ----
  var geo = new T.BoxGeometry(1, 1, 1); geo.translate(0, 0.5, 0);
  var MAXB = MOBILE ? 220 : 320, inst = new T.InstancedMesh(geo, M.bldg, MAXB), mtx = new T.Matrix4(), placed = 0, rb = mulberry(99);
  var avoid = [[0, 0, 140], [S.x, S.z, 70], [P.x, P.z, 120], [P.x + 73, P.z + 35, 40], [D.soul.x, D.soul.z, 52]];
  for (var tries = 0; tries < 4000 && placed < MAXB; tries++) {
    var ang = rb() * Math.PI * 2, rr = 170 + Math.pow(rb(), 0.8) * 600, bx = Math.cos(ang) * rr, bz = Math.sin(ang) * rr;
    var w = 14 + rb() * 22, d = 14 + rb() * 22, h = (24 + rb() * 120) * (1.2 - rr / 900);
    var bad = false;
    for (k = 0; k < avoid.length; k++) if (Math.hypot(bx - avoid[k][0], bz - avoid[k][1]) < avoid[k][2] + w) bad = true;
    // keep roads clear
    [D.studio, D.port, { x: 0, z: -700 }, { x: -700, z: -80 }].forEach(function (t) { var L = Math.hypot(t.x, t.z), ux = t.x / L, uz = t.z / L, along = bx * ux + bz * uz, perp = Math.abs(-bx * uz + bz * ux); if (along > 0 && along < L + 20 && perp < 12 + Math.max(w, d) * 0.75) bad = true; });
    if (bad || insideSolid(bx, Y + 1, bz, Math.max(w, d) * 0.6 + 4)) continue;
    mtx.makeScale(w, h, d); mtx.setPosition(bx, Y, bz); inst.setMatrixAt(placed++, mtx);
    solidBox(bx, bz, w, d, Y, Y + h);
  }
  inst.count = placed; inst.instanceMatrix.needsUpdate = true; world.add(inst);
  // lava glow at Kīlauea's crater (seen from the air)
  var V = VOLCANOES[4], lava = new T.Mesh(new T.CircleGeometry(300, 24), curve(new T.MeshBasicMaterial({ color: 0xff3a10 })));
  lava.rotation.x = -Math.PI / 2; lava.position.set(V.x, heightAt(V.x, V.z) + 8, V.z); world.add(lava);
  animated.push(function (t) { lava.material.color.setHSL(0.03, 1, 0.42 + 0.08 * Math.sin(t * 1.7)); });
}


/* ---- SOUL PRINT TOWER (2026-10-09): ~140 m, wine-red chrome in gold chains, a blue eye on top.
   Fits the skyline (Music Studio Tower stays the tallest at 230 m + spire). The door facing the
   plaza opens Soul Print's chat (logged-in citizens; her private tower log is never used here). ---- */
var SOUL = { group: null, door: { x: 0, z: 0 } };
function buildSoulTower(Y) {
  var d = D.soul, g = new T.Group(); world.add(g); SOUL.group = g;
  var dx = -d.x, dz = -d.z, dl = Math.hypot(dx, dz); dx /= dl; dz /= dl;             // toward the plaza
  var add = function (m) { g.add(m); return m; };
  var plinth = cyl(18, 22, 4, M.gold, d.x, Y, d.z, 8); world.remove(plinth); add(plinth);
  var wt = TEX_WIN.clone(); wt.needsUpdate = true; wt.repeat.set(3, 9); wt.wrapS = wt.wrapT = T.RepeatWrapping;
  var bt = TEX_BLDG.clone(); bt.needsUpdate = true; bt.repeat.set(3, 9); bt.wrapS = bt.wrapT = T.RepeatWrapping;
  var wine = curve(new T.MeshLambertMaterial({ map: bt, color: 0x8a1834, emissiveMap: wt, emissive: 0xff6a8a }));
  var shaft = cyl(8, 13, 120, wine, d.x, Y + 4, d.z, 8); world.remove(shaft); add(shaft); shaft.rotation.y = Math.PI / 8;
  var chains = [];
  for (var k = 0; k < 6; k++) {   // gold chains: tilted rings hugging the taper
    var h = 18 + k * 18, r = 13 - 5 * (h / 120) + 1.2;
    var ch = add(new T.Mesh(new T.TorusGeometry(r, 0.55, 6, 40), M.gold)); ch.position.set(d.x, Y + 4 + h, d.z); ch.rotation.x = Math.PI / 2 + (k % 2 ? 0.12 : -0.12); chains.push(ch);
  }
  var collar = cyl(11, 8.5, 3, M.gold, d.x, Y + 124, d.z, 8); world.remove(collar); add(collar);
  var eye = add(new T.Mesh(new T.IcosahedronGeometry(5.5, 1), curve(new T.MeshBasicMaterial({ color: 0x49a6ff }))));
  eye.position.set(d.x, Y + 133, d.z); solidBox(d.x, d.z, 11, 11, Y + 127, Y + 139);
  var halo = add(new T.Me