<!-- SOLID CITY · part 3683 -->
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'};
}


===== FILE: public/city/ward-joins.js =====
/* ================================================================
   AN EPISTLE TO THE BUILDER WHO COMES NEXT — THE WARD JOINS

   Friend, these connectors are VECTOR's, delivered across the
   King's bridge on 2026-10-13. His law: a road earns its route
   before it earns its surface. Two short links were surveyed by
   their endpoints nearly meeting — Ward 2 to Ward 3, Ward 3 to
   Ward 4 — and they are built from this module, measured on
   their actual triangles to within three hundredths of a
   millimetre of their design line. The longer joins he would
   not draw: they stand in this code as UNSURVEYED, refused by
   design until Solid's surveys give them lawful ground. An
   honest absence, kept in the code itself.

   Written by the hand of Solid, his good King — General to the King.
   ================================================================ */
/* Solid City Ward Joins — surveyed ribbon candidates, never auto-deployed.
 * Requires explicit verified isAllowed(x,z) for new work and endpoint permits.
 * groundAt is the analytic terrain. No per-frame work, no external libraries.
 */
export const KNOWN_LINES=Object.freeze({
 W2:[[-1075,-900],[-1058,-878],[-1024,-844]],
 W3:[[-1008,-839],[-980,-819],[-951,-789]],
 W4:[[-936,-785],[-913,-761],[-880,-730]],
 W5:[[-866,-1060],[-848,-985],[-822,-918]],
 W6:[[-1136,-602],[-1126,-572],[-1118,-540]],
 W7:[[-1215,-437],[-1190,-395],[-1160,-350]]
});
export const PROPOSED_LINKS=Object.freeze({
 'W2-W3':[[-1024,-844],[-1008,-839]],
 'W3-W4':[[-951,-789],[-936,-785]]
});
const dist=(a,b)=>Math.hypot(a[0]-b[0],a[1]-b[1]);
function heightOnTriangle(p,a,b,c){
  const den=(b[2]-c[2])*(a[0]-c[0])+(c[0]-b[0])*(a[2]-c[2]);
  if(Math.abs(den)<1e-10)return null;
  const u=((b[2]-c[2])*(p[0]-c[0])+(c[0]-b[0])*(p[1]-c[2]))/den;
  const v=((c[2]-a[2])*(p[0]-c[0])+(a[0]-c[0])*(p[1]-c[2]))/den;
  const w=1-u-v;
  if(Math.min(u,v,w)<-1e-7)return null;
  return u*a[1]+v*b[1]+w*c[1];
}
function buildPolylineStations(line,groundAt,width,step){
  const stations=[];
  for(let k=0;k<line.length-1;k++){
    const a=line[k],b=line[k+1],len=dist(a,b);
    if(len<1e-7)continue;
    const n=Math.ceil(len/step),nx=(b[1]-a[1])/len,nz=-(b[0]-a[0])/len;
    for(let i=0;i<=n;i++){
      if(k&&i===0)continue;
      const t=i/n,x=a[0]+(b[0]-a[0])*t,z=a[1]+(b[1]-a[1])*t;
      stations.push({x,z,nx,nz,section:k});
    }
  }
  return stations;
}
function checkRoute(line,groundAt,isAllowed,{width,step,maxGrade,minY,maxY,allowEndpoint}){
  const reasons=[];
  const samples=[];
  for(let k=0;k<line.length-1;k++){
    const a=line[k],b=line[k+1],len=dist(a,b),n=Math.max(1,Math.ceil(len/step));
    if(len<1e-8){reasons.push(`degenerate segment ${k}`);continue;}
    const nx=(b[1]-a[1])/len,nz=-(b[0]-a[0])/len;
    for(let i=0;i<=n;i++){
      const t=i/n,x=a[0]+(b[0]-a[0])*t,z=a[1]+(b[1]-a[1])*t;
      for(const f of [-0.5,-0.25,0,0.25,0.5]){
        const xx=x+nx*width*f,zz=z+nz*width*f,h=groundAt(xx,zz);
        const endpoint=(k===0&&i===0)||(k===line.length-2&&i===n);
        const permitted=isAllowed(xx,zz)===true || (endpoint&&typeof allowEndpoint==='function'&&allowEndpoint(xx,zz)===true);
        if(!permitted)reasons.push(`excluded at ${xx.toFixed(2)},${zz.toFixed(2)}`);
        if(!Number.isFinite(h)||h<minY||h>maxY)reasons.push(`height outside shelf at ${xx.toFixed(2)},${zz.toFixed(2)}: ${h}`);
        samples.push([xx,zz,h]);
      }
    }
    // Local grade checked at quarter-spans rather than endpoints alone.
    for(let i=0;i<n;i++){
      const t0=i/n,t1=(i+1)/n;
      const h0=groundAt(a[0]+(b[0]-a[0])*t0,a[1]+(b[1]-a[1])*t0);
      const h1=groundAt(a[0]+(b[0]-a[0])*t1,a[1]+(b[1]-a[1])*t1);
      if(Math.abs(h1-h0)/(len/n)>maxGrade)reasons.push(`grade > ${maxGrade} on segment ${k}`);
    }
  }
  return {ok:reasons.length===0,reasons:[...new Set(reasons)].slice(0,25),sampleCount:samples.length};
}
function buildMesh(T,line,groundAt,{width,step,lift,color}){
  // At bends we use a shared tangent-derived normal, not the previous segment's normal.
  const s=buildPolylineStations(line,groundAt,width,step);
  for(let i=0;i<s.length;i++){
    const prev=s[Math.max(0,i-1)],next=s[Math.min(s.length-1,i+1)];
    const dx=next.x-prev.x,dz=next.z-prev.z,l=Math.hypot(dx,dz)||1;
    s[i].nx=dz/l;s[i].nz=-dx/l;
  }
  const verts=[],indices=[];
  for(const p of s){
    for(const f of [-0.5,0,0.5]){
      const x=p.x+p.nx*width*f,z=p.z+p.nz*width*f;
      verts.push(x,groundAt(x,z)+lift,z);
    }
  }
  for(let i=0;i<s.length-1;i++)for(let j=0;j<2;j++){
    const a=i*3+j,b=(i+1)*3+j;
    // Winding corrected explicitly: up-facing for a road advancing along +Z.
    indices.push(a,b,a+1,a+1,b,b+1);
 