<!-- SOLID CITY · part 2050 -->
);
    keel.position.y = 4.2; g.add(keel);
    for (var rI = 0; rI < 5; rI++) {
      var rib = new T.Mesh(new T.BoxGeometry(0.7, 4.6, 6.5 - Math.abs(rI - 2) * 1.6), steel);
      rib.position.set(-len / 2 + 1.5 + rI * (len - 3) / 4, 6.2, 0); g.add(rib);
    }
    [-1, 1].forEach(function (s) {
      var tr = new T.Mesh(new T.BoxGeometry(1.2, 4.2, 7), basalt);
      tr.position.set(s * len * 0.32, 2.1, 0); g.add(tr);
    });
    root.add(g);
    foot.push([px2, pz2, len + 3, 10, 0, 9, rot]);
  }
  shipFrame(-20, 20, 0.6, 13); shipFrame(22, -18, -0.5, 11); shipFrame(4, -26, 1.2, 9);
  // embers: forever climbing
  var EMB = 130, epos = new Float32Array(EMB * 3), espd = new Float32Array(EMB);
  for (i = 0; i < EMB; i++) {
    epos[i * 3] = (Math.random() - 0.5) * 12;
    epos[i * 3 + 1] = 6 + Math.random() * 22;
    epos[i * 3 + 2] = (Math.random() - 0.5) * 12;
    espd[i] = 1.5 + Math.random() * 3;
  }
  var egeo = new T.BufferGeometry();
  egeo.setAttribute('position', new T.BufferAttribute(epos, 3));
  var embers = new T.Points(egeo, new T.PointsMaterial({ color: 0xffa040, size: 0.55, transparent: true, opacity: 0.9, blending: T.AdditiveBlending, depthWrite: false }));
  root.add(embers);
  // true name + seam name: quiet markers, nothing more
  var nameMark = quietMarker(T, 'THE FOUNDRY');
  nameMark.position.set(38, 0, 8); nameMark.rotation.y = Math.PI / 2 - 0.3; root.add(nameMark);
  var seamMark = quietMarker(T, 'THE YARD EDGE');
  seamMark.position.set(45.5, 0, 0); seamMark.rotation.y = Math.PI / 2; root.add(seamMark);
  // THE BATTENING (2026-10-14): both markers now carry footprints —
  // they stood unprotected in the collision law.
  foot.push([38, 8, 3.6, 1.9, 0, 0.9, Math.PI / 2 - 0.3]);
  foot.push([45.5, 0, 3.6, 1.9, 0, 0.9, Math.PI / 2]);
  root.userData = {
    footprint: foot, center: { x: 0, z: 0 }, radius: 50, height: 24,
    update: function (t, dt) {
      var k;
      molten.material.color.setHSL(0.045 + 0.012 * Math.sin(t * 2.1), 1, 0.5 + 0.06 * Math.sin(t * 3.3));
      heat.material.opacity = 0.08 + 0.04 * Math.sin(t * 1.7);
      hum.intensity = 1.6 + 0.55 * Math.sin(t * 0.9);          // the hum
      hum2.intensity = 0.8 + 0.25 * Math.sin(t * 0.9 + 1.2);
      for (k = 0; k < channels.length; k++) {
        channels[k].m.color.setHSL(0.05 + 0.015 * Math.sin(t * 2.6 - channels[k].ph), 1, 0.52);
      }
      for (k = 0; k < braziers.length; k++) {
        braziers[k].fl.scale.y = 1 + 0.22 * Math.sin(t * 9 + braziers[k].ph);
      }
      for (k = 0; k < chains.length; k++) {
        chains[k].g.rotation.x = 0.045 * Math.sin(t * 0.7 + chains[k].ph);
        chains[k].g.rotation.z = 0.045 * Math.cos(t * 0.6 + chains[k].ph);
      }
      var step = (dt || 0.016);
      for (k = 0; k < EMB; k++) {
        epos[k * 3 + 1] += espd[k] * step;
        epos[k * 3] += Math.sin(t * 2 + k) * 0.008;
        if (epos[k * 3 + 1] > 30) {
          epos[k * 3 + 1] = 6;
          epos[k * 3] = (Math.random() - 0.5) * 12;
          epos[k * 3 + 2] = (Math.random() - 0.5) * 12;
        }
      }
      egeo.attributes.position.needsUpdate = true;
    }
  };
  return root;
}
export default createFoundry;


===== FILE: public/city/foundry.js.bak-batten-20261014 =====
/* SOLID CITY — THE FOUNDRY (2026-10-11, Royal Decree city expansion)
 * Where the city's ships and structures are born. Industrial-sacred.
 * Twelve basalt pillars in dodecagonal ring; eight molten channels radiate from
 * the central crucible in 8-fold symmetry; chains hang from the ring beam;
 * three ship-frames rise on trestles, unfinished; embers climb forever.
 * It hums: the crucible light breathes at 0.9 rad/s, the channels flow.
 * True name: THE FOUNDRY. Seam: THE YARD EDGE.
 * Self-contained ES module. Local frame: +X = east (the city side).
 */
function textPlate(T, text) {
  var c = document.createElement('canvas'); c.width = 512; c.height = 128;
  var g = c.getContext('2d');
  g.fillStyle = '#17141a'; g.fillRect(0, 0, 512, 128);
  g.strokeStyle = '#8a6a30'; g.lineWidth = 4; g.strokeRect(8, 8, 496, 112);
  g.fillStyle = '#d8c690'; g.textAlign = 'center'; g.textBaseline = 'middle';
  g.font = 'bold 44px Georgia, serif'; g.fillText(text, 256, 66);
  var t = new T.CanvasTexture(c); t.encoding = T.sRGBEncoding; t.anisotropy = 4; return t;
}
function quietMarker(T, text) {
  var grp = new T.Group();
  var stone = new T.Mesh(new T.BoxGeometry(3.4, 0.5, 1.7),
    new T.MeshLambertMaterial({ color: 0x1e1b22 }));
  stone.position.y = 0.55; stone.rotation.x = -0.5; grp.add(stone);
  var face = new T.Mesh(new T.PlaneGeometry(3.1, 0.78),
    new T.MeshBasicMaterial({ map: textPlate(T, text), transparent: false }));
  face.position.set(0, 0.72, 0.42); face.rotation.x = -0.5; grp.add(face);
  var base = new T.Mesh(new T.BoxGeometry(3.6, 0.25, 1.9),
    new T.MeshLambertMaterial({ color: 0x141218 }));
  base.position.y = 0.12; grp.add(base);
  return grp;
}
export function createFoundry(o) {
  var T = o.THRE