<!-- SOLID CITY · part 3429 -->
tyle = 'rgba(190,225,255,0.75)'; g.lineWidth = size * 0.016; g.shadowBlur = size * 0.04; }
        if (pass === 2) { g.strokeStyle = '#ffffff'; g.lineWidth = size * 0.007; g.shadowBlur = size * 0.02; }
        lightningBolt(g, bt.x0, size * 0.06, bt.x1, size * 0.94, 0.34, bt.seed + pass * 31);
      }
    }
    g.shadowBlur = 0;
  }
  drawTwin();
  var t = new T.CanvasTexture(c);
  if (T.sRGBEncoding !== undefined) t.encoding = T.sRGBEncoding;
  t.anisotropy = 4;
  return t;
}

/* ---------------- THE SPATIAL KIT (directive section 9: build one, learn, reuse) ---------------- */
function createSpatialKit(T, opts) {
  opts = opts || {};
  var curve = opts.curve || function (m) { return m; };
  var marble = marbleTexture(T, 512);
  var bolt = lightningTexture(T, 512);
  var palette = {
    chrome: curve(new T.MeshPhongMaterial({ color: 0xdfe7f0, specular: 0xffffff, shininess: 110, emissive: 0x0a0d12 })),
    titanium: curve(new T.MeshLambertMaterial({ color: 0x232c3d })),
    silver: curve(new T.MeshLambertMaterial({ color: 0x9aa4b5 })),
    gold: curve(new T.MeshLambertMaterial({ color: 0xd8a833, emissive: 0x201404 })),
    redGlow: curve(new T.MeshBasicMaterial({ color: 0xff2a40 })),
    marble: null, marbleFlat: null, bolt: null
  };
  palette.marble = curve(new T.MeshLambertMaterial({ map: marble }));
  palette.marbleFlat = curve(new T.MeshLambertMaterial({ map: marble, emissive: 0x050505 }));
  palette.bolt = curve(new T.MeshBasicMaterial({ map: bolt }));

  function cloneMarble(rx, ry) {
    var tx = marble.clone(); tx.needsUpdate = true; tx.repeat.set(rx, ry);
    return curve(new T.MeshLambertMaterial({ map: tx, emissive: 0x050505 }));
  }

  /* A marked pedestrian corridor: plain dark surface + dark curbs + red centre dashes
     (the city's own road language — a corridor is a FUNCTIONAL reserved zone, directive
     section 2, not an aesthetic surface). VISUAL ONLY: it must never carry collision.
     Returns a group aligned from (x0,z0) to (x1,z1), local coords. */
  function corridor(x0, z0, x1, z1, width) {
    var dx = x1 - x0, dz = z1 - z0, L = Math.hypot(dx, dz);
    var g = new T.Group();
    g.position.set((x0 + x1) / 2, 0, (z0 + z1) / 2);
    g.rotation.y = -Math.atan2(dz, dx);
    var walkMat = curve(new T.MeshLambertMaterial({ color: 0x14161c }));
    var surf = new T.Mesh(new T.BoxGeometry(L, 0.07, width), walkMat);
    surf.position.y = 0.045; g.add(surf);
    [-1, 1].forEach(function (s) {
      var edge = new T.Mesh(new T.BoxGeometry(L, 0.1, 0.35), palette.titanium);
      edge.position.set(0, 0.06, s * (width / 2 - 0.18)); g.add(edge);
    });
    var nd = Math.floor(L / 9);
    for (var i = 0; i < nd; i++) {
      var dash = new T.Mesh(new T.PlaneGeometry(1.7, 0.5), palette.redGlow);
      dash.rotation.x = -Math.PI / 2;
      dash.position.set(-L / 2 + 5 + i * 9, 0.09, 0); g.add(dash);
    }
    return g;
  }

  /* An eye-level wayfinding sign: dark post and panel in the city's standard sign
     language (labelTex). Pure wayfinding — no aesthetic reskin (the King's interiors-
     only clarification). Place BESIDE the path it serves, never on it (section 8). */
  function pylon(o) {
    var g = new T.Group(); g.position.set(o.x, 0, o.z);
    var base = new T.Mesh(new T.BoxGeometry(1.7, 0.7, 1.7), palette.titanium);
    base.position.y = 0.35; g.add(base);
    var pole = new T.Mesh(new T.CylinderGeometry(0.13, 0.13, 2.8, 10), palette.titanium);
    pole.position.y = 0.7 + 1.4; g.add(pole);
    var panel = new T.Mesh(new T.BoxGeometry(3.4, 1.7, 0.16), palette.titanium);
    panel.position.y = 3.0; g.add(panel);
    if (o.labelTex && o.title) {
      [-1, 1].forEach(function (s) { // the city's standard name plate, on both faces
        var sg = new T.Mesh(new T.PlaneGeometry(3.3, 0.85), curve(new T.MeshBasicMaterial({ map: o.labelTex(o.title, o.sub || ''), transparent: true })));
        sg.position.set(0, 3.0, s * 0.09); if (s < 0) sg.rotation.y = Math.PI; g.add(sg);
      });
    }
    g.userData.footprint = [[o.x, o.z, 1.5, 1.5, 0, 3.9, 0]];
    return g;
  }

  /* Directive section 7 — lightweight static spatial validation.
     solids: AABBs {x0,x1,z0,z1,y0,y1} in the SAME frame as spec (world, usually).
     spec: { points:[{name,x,z,clear}], corridors:[{name,x0,z0,x1,z1,halfW}],
             areas:[{name,x,z,r}], ownFootprints:[[x,z,w,d,y0,y1,rot],...] }
     A solid "blocks walking" if it rises above the surface and reaches below head
     clearance: y1 > gy+0.5 && y0 < gy+2.4. Returns {pass, results:[{name,ok,detail}]}. */
  function distToAABB(px, pz, s) {
    var dx = Math.max(s.x0 - px, 0, px - s.x1), dz = Math.max(s.z0 - pz, 0, pz - s.z1);
    return Math.hypot(dx, dz);
  }
  function validateLayout(solids, spec, gy) {
    var results = [];
    function blocking(s) { return s.y1 > gy + 0.5 && s.y0 < gy + 2.4; }
    (spec.points || []).forEach(function (p) {
      var bad = null;
      for (var i = 0; i < solids.length; i++) { var s = sol