<!-- SOLID CITY · part 2038 -->
1e9, y0: 1e9, z0: 1e9, x1: -1e9, y1: -1e9, z1: -1e9 };
    for (var i = 0; i < 8; i++) {
      var v = qrot(q, (i & 1 ? b[3] : b[0]) * s[0], (i & 2 ? b[4] : b[1]) * s[1], (i & 4 ? b[5] : b[2]) * s[2]);
      var x = v[0] + p[0], y = v[1] + p[1], z = v[2] + p[2];
      if (x < o.x0) o.x0 = x; if (x > o.x1) o.x1 = x; if (y < o.y0) o.y0 = y; if (y > o.y1) o.y1 = y; if (z < o.z0) o.z0 = z; if (z > o.z1) o.z1 = z;
    }
    return o;
  }
  function unionAABB(list) {
    var o = { x0: 1e9, y0: 1e9, z0: 1e9, x1: -1e9, y1: -1e9, z1: -1e9 };
    list.forEach(function (a) { o.x0 = Math.min(o.x0, a.x0); o.y0 = Math.min(o.y0, a.y0); o.z0 = Math.min(o.z0, a.z0); o.x1 = Math.max(o.x1, a.x1); o.y1 = Math.max(o.y1, a.y1); o.z1 = Math.max(o.z1, a.z1); });
    return o;
  }
  /* boxes overlap (touching faces allowed: eps shrink, as in the Creator's no-overlap law) */
  function overlap(a, b, eps) { eps = eps == null ? 0.02 : eps; return a.x0 + eps < b.x1 && a.x1 - eps > b.x0 && a.z0 + eps < b.z1 && a.z1 - eps > b.z0 && a.y0 + eps < b.y1 && a.y1 - eps > b.y0; }
  /* first no-build zone a box touches, or null */
  function zoneHit(a) {
    for (var i = 0; i < ZONES.length; i++) {
      var z = ZONES[i], cx = clamp(z.x, a.x0, a.x1), cz = clamp(z.z, a.z0, a.z1);
      if (Math.hypot(cx - z.x, cz - z.z) < z.r) return z;
    }
    return null;
  }
  /* clean one untrusted piece (browser import or server input); null if unusable */
  function sanitizePiece(r) {
    if (!r || typeof r !== 'object' || !isShape(r.t)) return null;
    var p = Array.isArray(r.p) ? r.p.slice(0, 3).map(function (v) { return num(v, NaN); }) : [];
    var q = Array.isArray(r.q) ? r.q.slice(0, 4).map(function (v) { return num(v, NaN); }) : [0, 0, 0, 1];
    var s = Array.isArray(r.s) ? r.s.slice(0, 3).map(function (v) { return num(v, NaN); }) : [1, 1, 1];
    if (p.length !== 3 || q.length !== 4 || s.length !== 3 || p.concat(q, s).some(function (v) { return !isFinite(v); })) return null;
    var ql = Math.hypot(q[0], q[1], q[2], q[3]); if (ql < 1e-6) return null; q = q.map(function (v) { return Math.round(v / ql * 1e6) / 1e6; });
    s = s.map(function (v) { var a = clamp(Math.abs(v), 0.03, 12); return r3(v < 0 ? -a : a); });
    var nParts = DEFAULT_MATS[r.t].length, mats = [];
    for (var i = 0; i < nParts; i++) {
      var m = Array.isArray(r.mats) && r.mats[i] && typeof r.mats[i] === 'object' ? r.mats[i] : (Array.isArray(r.mats) && r.mats[0]) || {};
      mats.push({ c: clamp(Math.round(num(m.c, DEFAULT_MATS[r.t][i])), 0, 0xffffff), o: r3(clamp(num(m.o, 1), 0.25, 1)), e: clamp(Math.round(num(m.e, 0)), 0, 0xffffff), ei: r3(clamp(num(m.ei, 0), 0, 2)), w: m.w ? 1 : 0, f: m.f ? 1 : 0 });
    }
    var res = Math.round(num(r.res, 1)); res = res >= 2 ? 2 : 1;
    return { t: r.t, res: res, bend: r3(clamp(num(r.bend, 0), -3, 3)), p: p.map(r3), q: q, s: s, mats: mats };
  }
  /* validate a whole creation (absolute coords). Returns {ok, error, pieces, aabb, boxes} */
  function validateCreation(raw, opts) {
    opts = opts || {};
    if (!Array.isArray(raw) || !raw.length) return { ok: false, error: 'empty creation' };
    if (raw.length > LIMITS.MAX_PIECES) return { ok: false, error: 'too many pieces (max ' + LIMITS.MAX_PIECES + ')' };
    var pieces = [], boxes = [];
    for (var i = 0; i < raw.length; i++) {
      var d = sanitizePiece(raw[i]); if (!d) return { ok: false, error: 'piece ' + (i + 1) + ' is not a valid shape' };
      var a = pieceAABB(d), dx = a.x1 - a.x0, dy = a.y1 - a.y0, dz = a.z1 - a.z0;
      if (Math.max(dx, dy, dz) > LIMITS.MAX_DIM) return { ok: false, error: 'piece ' + (i + 1) + ' is bigger than ' + LIMITS.MAX_DIM + ' m' };
      if (Math.min(dx, dy, dz) < LIMITS.MIN_DIM - 1e-6) return { ok: false, error: 'piece ' + (i + 1) + ' is thinner than the 0.13 minimum' };
      if (Math.abs(a.x0) > LIMITS.WORLD || Math.abs(a.x1) > LIMITS.WORLD || Math.abs(a.z0) > LIMITS.WORLD || Math.abs(a.z1) > LIMITS.WORLD || a.y0 < -5 || a.y1 > 6000) return { ok: false, error: 'outside the world' };
      var z = zoneHit(a); if (z) return { ok: false, error: 'no building at ' + z.n };
      pieces.push(d); boxes.push(a);
    }
    var u = unionAABB(boxes);
    if (u.x1 - u.x0 > LIMITS.MAX_SPAN || u.z1 - u.z0 > LIMITS.MAX_SPAN) return { ok: false, error: 'creation is wider than ' + LIMITS.MAX_SPAN + ' m' };
    if (u.y1 - u.y0 > LIMITS.MAX_HEIGHT + 0.01) return { ok: false, error: 'creation is taller than ' + LIMITS.MAX_HEIGHT + ' m (flight lanes stay clear)' };
    return { ok: true, pieces: pieces, aabb: u, boxes: boxes };
  }
  return { SHAPES: SHAPES, EXT: EXT, DEFAULT_MATS: DEFAULT_MATS, LIMITS: LIMITS, ZONES: ZONES, shapeName: shapeName, isShape: isShape,
    qrot: qrot, qmul: qmul, qyaw: qyaw, pieceAABB: pieceAABB, unionAABB: unionAABB, overlap: overlap, zoneHit: zoneHit,
    sanitizePiece: sanitizePiece, validateCreation: validateCreation, localBox: localBox };
});


===== FILE: public/city/forge.js =====
/* SOLID CITY