<!-- SOLID CITY · part 3680 -->
nt; report polygon.
  return {ok:true,ward,frac,side,cx,cz,ux,uz,vx,vz,floor,lo,hi,
    footprint:[world(-4,-5),world(4,-5),world(4,5),world(-4,5)],entry,road};
}
function addBox(T,g,mat,x,y,z,w,h,d){
  const m=new T.Mesh(new T.BoxGeometry(w,h,d),mat);m.position.set(x,y,z);g.add(m);return m;
}
function buildHouse(T,plot,index){
  const g=new T.Group();g.name=`Ward ${plot.ward} House ${index+1}`;
  const wall=MAT(T,0x8d989d),roof=MAT(T,0x333d48),glass=MAT(T,0x577989),gold=MAT(T,0xa78b4c),post=MAT(T,0x667078);
  // Local +X follows path tangent, local +Z points away from road.
  const base=plot.floor;
  addBox(T,g,wall,-3.9,base+1.55,0,0.2,3.1,10);
  addBox(T,g,wall,3.9,base+1.55,0,0.2,3.1,10);
  addBox(T,g,wall,0,base+1.55,4.9,8,3.1,0.2);
  addBox(T,g,wall,-2.35,base+1.55,-4.9,3.3,3.1,0.2);
  addBox(T,g,wall,2.35,base+1.55,-4.9,3.3,3.1,0.2);
  addBox(T,g,wall,0,base+2.75,-4.9,1.4,0.7,0.2);
  addBox(T,g,wall,0,base-0.11,0,8,0.22,10);
  addBox(T,g,roof,0,base+3.24,0,8.7,0.28,10.7);
  addBox(T,g,gold,0,base+2.95,-5.04,8.05,0.12,0.10);
  addBox(T,g,glass,0,base+1.5,-5.06,1.45,2.35,0.09);
  addBox(T,g,glass,-2.5,base+1.85,-5.07,1.4,1.35,0.09);
  addBox(T,g,glass,2.5,base+1.85,-5.07,1.4,1.35,0.09);
  // Supports extend to the actual sampled ground at their positions.
  for(const x of [-3.55,3.55])for(const z of [-4.55,4.55]){
    const wx=plot.cx+plot.ux*x+plot.vx*z,wz=plot.cz+plot.uz*x+plot.vz*z;
    const y=plot.groundAt(wx,wz),height=base-y;
    addBox(T,g,post,x,y+height/2,z,0.42,height,0.42);
  }
  g.position.set(plot.cx,0,plot.cz);
  // Local +X = tangent; local +Z = outward normal.
  g.rotation.y=Math.atan2(plot.vx,plot.vz);
  // Local X may reverse relative to path tangent on the opposite side.
  if(Math.abs(Math.cos(g.rotation.y)-plot.vz)>1e-6)throw Error('rotation mismatch');
  return g;
}
export function createWardHouses({THREE,groundAt,isAllowed,ward=2,existingFootprints=[]}={}){
  if(!THREE||typeof groundAt!=='function'||typeof isAllowed!=='function')throw Error('THREE, groundAt, verified isAllowed required');
  if(!HOUSE_WARDS[ward])throw Error('Only wards 2–5 have proposed dwellings');
  const group=new THREE.Group();group.name=`Ward ${ward} Dwellings — Preview`;
  const accepted=[],refused=[],occupied=[...existingFootprints];
  const desired=HOUSE_WARDS[ward];
  // Proposals spaced along existing path; refusals are not auto-relocated.
  for(let i=0;i<desired;i++){
    const frac=(i+1)/(desired+1),side=i%2?1:-1;
    const p=candidate({groundAt,isAllowed},ward,frac,side);
    if(!p.ok){refused.push(p);continue;}
    // Conservative footprint-center distance; require 4m beyond 8x10 envelope.
    if(occupied.some(q=>hypot(q.cx-p.cx,q.cz-p.cz)<17)){
      refused.push({ok:false,reason:'house spacing',ward,frac,side});continue;
    }
    p.groundAt=groundAt;
    const mesh=buildHouse(THREE,p,i);group.add(mesh);
    accepted.push({ward,plot:p,mesh,stand:{kind:'horizontal-rectangle',center:[p.cx,p.floor,p.cz],width:8,depth:10,ux:p.ux,uz:p.uz,vx:p.vx,vz:p.vz},
      solids:{kind:'wall-meshes',source:mesh.children.slice(0,6),requiresEngineRegistration:true}});
    occupied.push(p);
  }
  return {group,accepted,refused,planned:desired};
}
export function measureWardHouse(house,groundAt){
  const p=house.plot, heights=[];
  for(const [x,z] of p.footprint)heights.push(groundAt(x,z));
  const stand=house.stand;
  return {ward:p.ward,floorY:p.floor,cornerRelief:Math.max(...heights)-Math.min(...heights),
    minSupport:p.floor-Math.max(...heights),maxSupport:p.floor-Math.min(...heights),
    standMatchesFloor:Math.abs(stand.center[1]-p.floor)<1e-6,
    meshFloorY:p.floor,registrationStatus:'descriptor only; engine integration required'};
}

/* ----------------------------------------------------------------
 * SURVEYED-PLOT ADAPTER — added by Solid, 2026-10-13, implementing
 * the interface in VECTOR's schematics for the remaining program:
 * createSurveyedWardHouses({THREE, groundAt, isAllowed, plots,
 * occupiedFootprints}). Each plot arrives surveyed — center,
 * tangent (u), outward normal (v), and the survey's floor — and is
 * re-judged by the shipped plot law WITHOUT alteration: axes must
 * be unit and perpendicular; the footprint+margin must pass the
 * mask on a 5x5 grid; the entry corridor is tested at 9 points;
 * relief <= 1.2; floor-lowest <= 1.5; the floor is RECOMPUTED as
 * highest sample + 0.22 and must agree with the surveyed floor
 * within 0.05 m or the plot is refused for re-survey; centers keep
 * >= 17 m from every occupied and accepted plot. Houses are built
 * by the module's own buildHouse — the adapter changes WHERE the
 * law is applied, never WHAT the law is.
 * ---------------------------------------------------------------- */
export function createSurveyedWardHouses({THREE, groundAt, isAllowed, plots = [], occupiedFootprints = []} = {}) {
  if (!THREE || typeof groundAt !== 'function' || typeof isAllowed !== 'function') throw Error('THREE, groundAt,