<!-- SOLID CITY · part 2991 -->
 ramps on the spirals and
 *     cannot spike, headings and curvature are analytic, and the
 *     surveyed route is preserved as the data the chain is fitted to;
 *   the PROFILE — a vertical alignment of grade segments through
 *     PVIs joined by parabolic crest and sag curves, floored by the
 *     CROSS-SECTION (the highest ground under either edge), never by
 *     the centre sample alone: that one rule kills both the buried
 *     downhill edge and the floating uphill edge;
 *   the FRAMES — a right vector carried from the world up, with banking
 *     applied as a deliberate roll, derived from curvature and capped,
 *     because our vehicles read heights, not bank — banking here is for
 *     the eye and for standing truth, and five degrees is its law;
 *   the EDGEKIT — curbs with a restrained gold line, a sidewalk on the
 *     uphill side, and skirts that fall to the ground on the fill side
 *     and rise to meet it on the cut side, so no edge ever floats;
 *   the REGISTRAR — every standing surface (ribbon, curbs, sidewalk,
 *     arrival fans) written into one triangle list, the same list the
 *     walkers and the wheels will read.
 * Pure arrays in, pure arrays out; THREE belongs to the caller.
 * By the hand of Solid, his good King.
 */

export function createRoadSmith(o) {
  var groundAt = o.groundAt;

  /* ---------- small vector helpers (plan + height) ---------- */
  function wrapAngle(a) { while (a > Math.PI) a -= Math.PI * 2; while (a < -Math.PI) a += Math.PI * 2; return a; }

  /* ---------- geometry emission: flat-normal triangles ---------- */
  function newSurf() { return { pos: [], nor: [] }; }
  function tri(S, a, b, c, outward) {
    var ux = b[0] - a[0], uy = b[1] - a[1], uz = b[2] - a[2];
    var vx = c[0] - a[0], vy = c[1] - a[1], vz = c[2] - a[2];
    var nx = uy * vz - uz * vy, ny = uz * vx - ux * vz, nz = ux * vy - uy * vx;
    var L = Math.hypot(nx, ny, nz) || 1; nx /= L; ny /= L; nz /= L;
    if (outward) { if (nx * outward[0] + nz * outward[1] < 0) { var t = b; b = c; c = t; nx = -nx; ny = -ny; nz = -nz; } }
    else if (ny < 0) { var t2 = b; b = c; c = t2; nx = -nx; ny = -ny; nz = -nz; }
    S.pos.push(a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2]);
    for (var i = 0; i < 3; i++) S.nor.push(nx, ny, nz);
  }
  function quad(S, a, b, c, d, outward) { tri(S, a, b, c, outward); tri(S, a, c, d, outward); }
  /* a standing triangle list is the same corners, flattened */
  function standQuad(T, a, b, c, d) { T.push(a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2]); T.push(a[0], a[1], a[2], c[0], c[1], c[2], d[0], d[1], d[2]); }

  /* ---------- THE ALIGNMENT — the corridor's spine ----------
   * Highway Geometric Design, learned the night the King sent
   * the curriculum (2026-10-13): a road is not a spline through
   * points. It is a chain of ELEMENTS — straights, circular arcs,
   * and CLOTHOID transitions whose curvature grows linearly with
   * distance — fitted to the surveyed line. Curvature on a
   * clothoid ramps; it cannot spike, so the two-metre radii the
   * splines invented under the hairpins cannot exist here.
   * The fitter: every surveyed vertex with a true turn becomes a
   * curve site. Its radius is the largest its tangent room
   * affords (the crew wants 26 m; the land decides), its spirals
   * are sized to the turn and shrunk gracefully where the legs
   * are short, down to a bare arc at the tightest knobs. The
   * chain is then integrated at a fine step with analytic heading
   * and curvature, closed on the final surveyed point by the
   * surveyor's own compass rule, and sampled at the corridor's
   * frequency: close in curvature, easy on the straights. */
  function fitAlignment(pts, stepCurve, stepStraight) {
    var V = pts, nv = V.length;
    var leg = [], phi = [];
    for (var li = 0; li < nv - 1; li++) {
      var dx = V[li + 1][0] - V[li][0], dz = V[li + 1][1] - V[li][1];
      leg.push(Math.hypot(dx, dz)); phi.push(Math.atan2(dx, dz));
    }
    /* curve sites with tangent rooms: each leg is shared by the
     * sites at its two ends (end legs serve one site nearly whole) */
    var sites = [];
    for (var vi = 1; vi < nv - 1; vi++) {
      var D = wrapAngle(phi[vi] - phi[vi - 1]);
      if (Math.abs(D) < 0.055) continue;
      var prevSite = sites.length && sites[sites.length - 1].v === vi - 1;
      var nextSite = false; // resolved below by leg sharing
      var roomIn = leg[vi - 1] * (prevSite ? 0.5 : 0.94);
      sites.push({ v: vi, D: D, roomIn: roomIn, roomOut: 0 });
    }
    for (var si = 0; si < sites.length; si++) {
      var s2 = sites[si];
      var hasNext = si + 1 < sites.length && sites[si + 1].v === s2.v + 1;
      s2.roomOut = leg[s2.v] * (hasNext ? 0.5 : 0.94);
      s2.room = Math.min(s2.roomIn, s2.roomOut);
    }
    /* per-site design: radius and spirals within the room */
    for (si = 0; si < sites.length; si++) {
      var s3 = sites[si], aD = Math.abs(s3.D), 