<!-- SOLID CITY · part 3001 -->
ls[ei];
      var elLen = el.len;
      if (el.toEnd) {
        // direction is th (== final leg heading); length closes to V[nv-1]
        var ex = V[nv - 1][0] - cx, ez = V[nv - 1][1] - cz;
        elLen = ex * Math.sin(th) + ez * Math.cos(th);
        if (elLen < 0) elLen = 0;
      }
      var steps = Math.max(1, Math.ceil(elLen / DT)), dt = elLen / steps;
      for (var q = 0; q < steps; q++) {
        // heading at the substep's midpoint, from the element's own
        // law: θ(s) = h0 + k0·s + (k1−k0)·s²/(2L) — exact for lines,
        // arcs, and clothoids alike; the midpoint rule on an analytic
        // heading keeps position error at millimetres per element
        var sMid = (q + 0.5) * dt;
        var thMid = el.h0 + el.k0 * sMid + (elLen > 0 ? (el.k1 - el.k0) * sMid * sMid / (2 * elLen) : 0);
        cx += Math.sin(thMid) * dt; cz += Math.cos(thMid) * dt;
        sAcc += dt;
        var sEnd = (q + 1) * dt;
        var thEnd = el.h0 + el.k0 * sEnd + (elLen > 0 ? (el.k1 - el.k0) * sEnd * sEnd / (2 * elLen) : 0);
        var kEnd = el.k0 + (el.k1 - el.k0) * (elLen > 0 ? sEnd / elLen : 0);
        dense.push([cx, cz, thEnd, kEnd]);
        th = thEnd;
      }
    }
    /* the compass rule: distribute the closure error back along
     * the traverse, and set the last point exact — the junction
     * keeps its promise to the centimetre */
    var errX = dense[dense.length - 1][0] - V[nv - 1][0], errZ = dense[dense.length - 1][1] - V[nv - 1][1];
    /* two passes, always: measure the traverse BEFORE touching it.
     * A single pass that reads lengths off points it is already
     * correcting feeds on itself — the weights outrun the total
     * and a two-metre seed error leaves the county (measured:
     * trillion-metre steps from exactly that feedback). */
    var cum = [0];
    for (var di = 1; di < dense.length; di++) cum.push(cum[di - 1] + Math.hypot(dense[di][0] - dense[di - 1][0], dense[di][1] - dense[di - 1][1]));
    var totalLen = cum[cum.length - 1] || 1;
    for (di = 1; di < dense.length; di++) {
      var wgt = cum[di] / totalLen;
      dense[di][0] -= errX * wgt; dense[di][1] -= errZ * wgt;
    }
    dense[dense.length - 1][0] = V[nv - 1][0]; dense[dense.length - 1][1] = V[nv - 1][1];
    /* frequency sampling: close where curvature lives, easy elsewhere */
    var line = [[dense[0][0], dense[0][1]]], head = [dense[0][2]], kap = [dense[0][3]];
    var acc = 0;
    for (di = 1; di < dense.length; di++) {
      acc += Math.hypot(dense[di][0] - dense[di - 1][0], dense[di][1] - dense[di - 1][1]);
      var want = Math.abs(dense[di][3]) > 1 / 60 ? stepCurve : stepStraight;
      if (acc >= want || di === dense.length - 1) { line.push([dense[di][0], dense[di][1]]); head.push(dense[di][2]); kap.push(dense[di][3]); acc = 0; }
    }
    var minR = Infinity;
    for (si = 0; si < sites.length; si++) minR = Math.min(minR, sites[si].R);
    return { line: line, head: head, kap: kap, sites: sites.length, minR: minR };
  }

  /* ---------- buildRoad: the whole craft ---------- */
  function buildRoad(spec) {
    var pts = spec.points, W = spec.width || 7.6, hw = W / 2;
    var stepM = spec.stationStep || (spec.furniture === 'bench' ? 1.25 : 2);
    var gradeCap = spec.gradeCap || 0.38;
    var bankCap = (spec.bankCapDeg || 5) * Math.PI / 180;
    /* THE CORRIDOR FRONT: the alignment is fitted to elements and
     * sampled analytically; the stations inherit exact headings
     * and curvatures from the elements themselves. Frequency: the
     * corridor closes its stations where curvature lives and
     * walks easy on the straights — stepM stays the road's base
     * grain, and curves sample at 1.25 m whatever the base. */
    var aligned = fitAlignment(pts, Math.min(stepM, 1.25), stepM);
    var line = aligned.line;
    /* a fitted chain can still leave two stations close in plan
     * where an arc doubles near itself — merge micro-stations as
     * the spline era taught: no station within 1.2 m of its
     * predecessor (the ends are promises and stay exact). */
    var keptLine = [line[0]], keptHead = [aligned.head[0]], keptKap = [aligned.kap[0]];
    for (var li = 1; li < line.length - 1; li++) {
      var lastK = keptLine[keptLine.length - 1];
      if (Math.hypot(line[li][0] - lastK[0], line[li][1] - lastK[1]) >= 1.2) { keptLine.push(line[li]); keptHead.push(aligned.head[li]); keptKap.push(aligned.kap[li]); }
    }
    keptLine.push(line[line.length - 1]); keptHead.push(aligned.head[aligned.head.length - 1]); keptKap.push(aligned.kap[aligned.kap.length - 1]);
    line = keptLine;
    var fairDev = 0;   // the corridor moves no station after sampling; the name stays for the record books

    var N = line.length;

    /* tangents and headings — ANALYTIC, from the elements */
    var i, st = [];
    for (i = 0; i < N; i++) {
      var hh = keptHead[i];
      var fx = Math.sin(hh), fz = Math.cos(hh);
      st.push({ x: line[i][0], z: line[i][1], fx: fx, 