<!-- SOLID CITY · part 422 -->
+ pad && z > s.z0 - pad && z < s.z1 + pad && y > s.y0 - pad && y < s.y1 + pad) return s; } return null; }

var M = {
  black: curve(new T.MeshLambertMaterial({ color: 0x0d0b0e })),
  gun: curve(new T.MeshLambertMaterial({ color: 0x2a2a30 })),
  gold: curve(new T.MeshLambertMaterial({ color: 0xc99a3a, emissive: 0x100a02 })),
  red: curve(new T.MeshBasicMaterial({ color: 0xff2a40 })),
  redDim: curve(new T.MeshLambertMaterial({ color: 0x5a0410, emissive: 0x3a020a })),
  glowGold: curve(new T.MeshBasicMaterial({ color: 0xffd27a })),
  board: curve(new T.MeshLambertMaterial({ map: TEX_BOARD, color: 0xffffff, emissive: 0x030001 })),
  road: curve(new T.MeshLambertMaterial({ color: 0x0b0a0c })),
  bldg: curve(new T.MeshLambertMaterial({ map: TEX_BLDG, emissiveMap: TEX_WIN, emissive: 0xffffff }))
};
function box(w, h, d, mat, x, y, z, solid) {
  var m = new T.Mesh(new T.BoxGeometry(w, h, d), mat); m.position.set(x, y + h / 2, z); world.add(m);
  if (solid !== false) solidBox(x, z, w, d, y, y + h);
  return m;
}
function cyl(rt, rb, h, mat, x, y, z, seg, solid) {
  var m = new T.Mesh(new T.CylinderGeometry(rt, rb, h, seg || 16), mat); m.position.set(x, y + h / 2, z); world.add(m);
  if (solid !== false) solidBox(x, z, Math.max(rt, rb) * 1.6, Math.max(rt, rb) * 1.6, y, y + h);
  return m;
}
function sign(text, sub, x, y, z, w, rotY) {
  var m = new T.Mesh(new T.PlaneGeometry(w, w / 4), curve(new T.MeshBasicMaterial({ map: labelTex(text, sub), side: T.DoubleSide, fog: true })));
  m.position.set(x, y, z); m.rotation.y = rotY || 0; world.add(m); return m;
}
var animated = [];   // {obj, fn}
var NP = { draw: null, playing: false }, RADIO_MASTS = [];

/* districts (absolute coords, city ground at CITY_Y) */
var D = {
  plaza:  { name: 'FORUM PLAZA', x: 0, z: 0, url: '/', label: 'The Forums', text: 'The boards and threads of epistles — the real Forum lives here.' },
  studio: { name: 'MUSIC STUDIO TOWER', x: 430, z: -170, url: '/ss', label: 'Music Studio', title: 'Open the full Music Studio', go: 'OPEN THE FULL MUSIC STUDIO', text: 'Solid Sound — write, record, sing and sell your songs. (Opens /ss; the city music stops when you leave.)' },
  soul:   { name: 'SOUL PRINT TOWER', x: 210, z: 170, label: 'Soul Print Tower' },
  port:   { name: 'ORBITAL SPACEPORT', x: -380, z: 330, url: '/warpspeed', label: 'Warp Speed', text: 'The Warp Speed arcade run — wormholes and Soliditronium. (Mini-game, opens a new page.)' },







  siege:    { name: 'SIEGE BREAKER', x: -496, z: 334, url: '/city/siege/', label: 'Siege', title: 'SIEGE BREAKER \u2014 breach the fortress', go: 'BEGIN SIEGE', text: 'Command the siege fleet: fleet command, bombardment, wings, aegis \u2014 each yours by touch, AI admirals on the rest. Three phases: turrets, generators, the core. (Opens Siege Breaker; your city session is kept.)' },  rescue:   { name: 'RESCUE WING', x: -584, z: 206, url: '/city/rescue/', label: 'Rescue', title: 'RESCUE WING \u2014 every life matters', go: 'FLY RESCUE', text: 'Pilot a rescue vessel with your AI crew: navigate, extract, protect, repair \u2014 each station yours by touch. Three pilots adrift beyond the debris belt; bring them home. (Opens Rescue Wing; your city session is kept.)' },  swarm:    { name: 'SWARM COMMAND', x: -496, z: 206, url: '/city/swarm/', label: 'Swarm', title: 'SWARM COMMAND \u2014 twelve drones, one commander', go: 'LEAD SWARM', text: 'Command twelve drones: group orders from your hand, any single drone yours by touch, AI sub-commanders on the rest. Clear the debris field. (Opens Swarm Command; your city session is kept.)' },  fleet:    { name: 'DOUBLE STRIKE \u2014 FLEET CONTROL', x: -584, z: 294, url: '/city/fleet/', label: 'Fleet Control', title: 'FLEET CONTROL \u2014 eight ships, two commanders', go: 'COMMAND FLEET', text: 'Two commanders, four ships each: lead the gold squadron while AI holds teal, or watch both fleets fight and reach in to take any ship. (Opens Fleet Control; your city session is kept.)' },  warship:  { name: 'WARSHIP \u2014 TWIN BATTERIES', x: -540, z: 206, url: '/city/warship/', label: 'Warship', title: 'WARSHIP \u2014 eight guns, two batteries', go: 'BOARD WARSHIP', text: 'Command a warship with your AI crew: navigation, two gun batteries, shields \u2014 each station claimable by touch under the coordination law. Fight to the Meridian Gate. (Opens Warship; your city session is kept.)' },  warship3: { name: 'WARSHIP III \u2014 THE WARDEN', x: -540, z: 162, url: '/city/warship-iii/', label: 'Warship III', title: 'WARSHIP III \u2014 the warden of Meridian Gate', go: 'BOARD WARSHIP III', text: "VECTOR's third edition: stand the watch at Meridian Gate as its Warden. (Opens Warship III; your city session is kept.)" },  axiom:    { name: 'AXIOM \u2014 THE LIVING FRONTIER', x: -584, z: 162, url: '/city/axiom/', label: 'AXIOM', title: 'AXIOM \u2014 the living frontier', go: 'ENTER AXIOM', text: "A frontier station riding out a meteor storm: you and SOLID cre