<!-- SOLID CITY · part 2047 -->
'reject': if (w) review(w, 'reject'); break;
  }
});
addEventListener('keydown', function (e) {
  var tg = e.target && e.target.tagName; if (tg === 'INPUT' || tg === 'TEXTAREA') return;
  if (e.code === 'KeyB' && !e.ctrlKey && !e.metaKey) { toggle(); return; }
  if (!F.on) return;
  if ((e.ctrlKey || e.metaKey) && e.code === 'KeyZ') { e.preventDefault(); undo(); return; }
  if (e.code === 'KeyZ') edit('Rotated \u221215\u00b0', rotY(-15));
  else if (e.code === 'KeyX') edit('Rotated +15\u00b0', rotY(15));
  else if (e.code === 'KeyR') nudge('up');
  else if (e.code === 'KeyF') nudge('down');
  else if (e.code === 'BracketLeft') edit('Smaller', scaleBy(0.8));
  else if (e.code === 'BracketRight') edit('Bigger', scaleBy(1.25));
  else if (e.code === 'Enter') { e.preventDefault(); place(); }
  else if (e.code === 'Delete' || e.code === 'Backspace') { e.preventDefault(); del(); }
  else if (e.code === 'Escape') { if (!sheet.hidden) closeSheet(); else if (F.sel) select(null); else close(); }
});

/* ---------------- open / close / per-frame ---------------- */
function open() {
  if (S.mode !== 'walk') { A.help('The Forge works on foot — land and walk first.', 4); return; }
  if (window.CityWatch && CityWatch.state && CityWatch.state.state !== 'idle' && CityWatch.exit) try { CityWatch.exit(); } catch (e) {}
  if (window.CityHyper && CityHyper.stop) CityHyper.stop();
  F.on = true; ui.hidden = false; scRoot.classList.add('forging');
  if (S.camPitch < 0.35) S.camPitch = 0.45; if (S.camDist < 9) S.camDist = 11;
  if (!F.ghostRel) setShape(F.shape); else F.lastKey = '';
  getMe().then(function (me) { render(); say(me.loggedIn ? (me.admin ? 'Build anything — your published builds go live at once.' : 'Build here; PUBLISH sends it to the King for approval.') : 'Build here — it autosaves on this device. Log in to publish to the world.'); });
  updateSel(); render();
}
function close() { F.on = false; select(null); ui.hidden = true; pop.hidden = true; sheet.hidden = true; scRoot.classList.remove('forging'); ghostFrame(); selHelp.visible = false; }
function toggle() { if (F.on) close(); else open(); }
function update() {
  if (F.on && S.mode !== 'walk') close();
  if (!F.on) return;
  ghostFrame();
}
/* stand on low pieces: highest forge top under (x,z) that is at most a step above the feet */
function standY(x, z, y) {
  var top = -1e9, sol = A.solids;
  for (var k = sol.length - 1; k >= 0; k--) { var s = sol[k]; if (!s.forge) continue; if (x > s.x0 - 0.25 && x < s.x1 + 0.25 && z > s.z0 - 0.25 && z < s.z1 + 0.25 && s.y1 <= y + 1.05 && s.y1 > top) top = s.y1; }
  return top;
}
function init() {
  if (F.ready) return; F.ready = true;
  try { loadBench(JSON.parse(localStorage.getItem(LS_BENCH) || '[]')); } catch (e) {}
  if (window.CityHyper && CityHyper.regrid) CityHyper.regrid();
  getMe().then(loadWorld);
}
if (A.isBuilt && A.isBuilt()) init(); else addEventListener('sc-city-built', init);

window.CityForge = { open: open, close: close, toggle: toggle, update: update, tap: tap, standY: standY, state: F, place: place, setShape: setShape, select: select, edit: edit, rotY: rotY, nudge: nudge, publish: publish, reload: loadWorld, scaleBy: scaleBy, tip: tip, clay: CLAY, setColor: setColor, setFinish: setFinish, check: check, importCreator: importCreator, libSave: libSave, libStamp: libStamp, undo: undo };
})();


===== FILE: public/city/foundry-place.js =====
/* Solid City — places THE FOUNDRY (public/city/foundry.js) west of the city.
   Where the city's ships and structures are born: crucible, eight molten channels,
   hanging chains, ship-frames on trestles, embers climbing. It hums.
   Lazy: imported after the city is built; any import/build error is caught and the city carries on.
   The placer adapts to terrain at runtime (grid sample, take max).
   Adds: collision (userData.footprint), a radar marker 'FOUNDRY', a Hyper Controller stop,
   and a Watch (GRAND TOUR) caption. */
(function () {
'use strict';
if (!window.SC || !SC.api) return;
var A = SC.api, S = A.S;
var POS = { x: -950, z: 50 };
var M = { state: 'idle', group: null, wait: 6.0, gy: 20, said: false };
function sampleGround() {
  var g = (typeof groundAt === 'function') ? groundAt : null;
  if (!g) return 20;
  var max = -1000, h, ix, iz;
  for (ix = -2; ix <= 2; ix++) for (iz = -2; iz <= 2; iz++) {
    try { h = g(POS.x + ix * 24, POS.z + iz * 24); } catch (e) { h = 20; }
    if (h > max) max = h;
  }
  return max + 0.4;
}
function addBox(cx, cz, w, d, rot, y0, y1) {
  var nx = Math.max(1, Math.ceil(w / 2.5)), nz = Math.max(1, Math.ceil(d / 2.5)), cw = w / nx, cd = d / nz, c = Math.cos(rot), s = Math.sin(rot);
  var hx = cw / 2 * Math.abs(c) + cd / 2 * Math.abs(s), hz = cw / 2 * Math.abs(s) + cd / 2 * Math.abs(c);
  for (var i = 0; i < nx; i++) for (var j = 0; j < nz; j++) {
    var ox = -w / 2 + cw * (i + 0.5), oz = -d / 2 + cd * (j + 0.5), lx = cx + ox * c + oz * s, lz = cz - ox * s + oz * c;
    A.solids.push({ x0: POS.x +