<!-- SOLID CITY · part 3036 -->
l edge sits a hair under its own ground (the
     * dirt meets the road), and the surface rises from that edge
     * toward the centre at a civil cross slope. The floor's law is
     * stated where it is computed (v3, below). */
    /* the cross-section is read through a median first: a rib that
     * crosses the line for ONE station is a feature of the land, not
     * an order to the road — the road answers the land's grain, and
     * the rib's corner is dressed by the cut bank where it stands.
     * The floor (v3) swears two things only: the centre is never
     * buried, and no edge may drag the whole surface more than
     * 1.35 m above the centre for its own sake. */
    var rawC = [], rawL = [], rawR = [];
    for (i = 0; i < N; i++) {
      var s0 = st[i];
      rawC.push(groundAt(s0.x, s0.z));
      rawL.push(groundAt(s0.x - s0.rx * hw, s0.z - s0.rz * hw));
      rawR.push(groundAt(s0.x + s0.rx * hw, s0.z + s0.rz * hw));
      s0.gC = rawC[i];
    }
    function med3(arr, j) {
      var a = arr[Math.max(0, j - 1)], b = arr[j], c = arr[Math.min(N - 1, j + 1)];
      return a + b + c - Math.min(a, b, c) - Math.max(a, b, c);
    }
    var prof = [], floorY = [];
    for (i = 0; i < N; i++) {
      var s = st[i];
      var gC = med3(rawC, i);
      var gL = med3(rawL, i), gR = med3(rawR, i);
      var crossMax = Math.max(gL, gC, gR);
      var gUp = upSide > 0 ? gR : gL;
      var sCross = Math.max(-0.04, Math.min(0.10, (gC - gUp) / hw));
      var want = gUp - 0.06 + hw * sCross;
      if (want > gC + 1.05) want = gC + 1.05;        // never fill the centre higher
      prof.push(want);
      floorY.push(Math.max(gC + 0.06, Math.min(crossMax - 0.30, gC + 1.35)));
      s.crossMax = crossMax; s.gUp = gUp;
    }
    /* The fairing is an ENVELOPE, not an average: the profile is the
     * LOWEST surface that never sinks under its floor and never
     * grades beyond the cap in either direction. Constraint passes
     * only ever RAISE a station — a limiter that could also lower
     * would drag the surface under the floor it was sworn to keep
     * (the first build's burial was exactly that crime). */
    for (i = 0; i < N; i++) if (prof[i] < floorY[i]) prof[i] = floorY[i];
    function envelope() {
      var i2;
      for (i2 = 1; i2 < N; i2++) { var d = st[i2].ds || stepM; var need = prof[i2 - 1] - gradeCap * d; if (prof[i2] < need) prof[i2] = need; }
      for (i2 = N - 2; i2 >= 0; i2--) { var d2 = st[i2 + 1].ds || stepM; var need2 = prof[i2 + 1] - gradeCap * d2; if (prof[i2] < need2) prof[i2] = need2; }
    }
    envelope(); envelope(); envelope();
    /* vertical curvature, honestly bought: each smoothing pass lifts
     * the low side of a grade break toward the chord, rounding the
     * ramp's foot and crest into a curve — the price is fill on the
     * approaches, and the examiner reports it. */
    for (var pass = 0; pass < 10; pass++) {
      var sm = prof.slice();
      for (i = 1; i < N - 1; i++) { var cand = prof[i - 1] * 0.3 + prof[i] * 0.4 + prof[i + 1] * 0.3; if (cand > prof[i]) sm[i] = cand; }
      prof = sm; envelope();
    }
    /* a pinned start (a junction): the first station takes the host
     * road's height exactly, and one pass grades this line away from
     * it under the cap — the junction disc covers the seam, and the
     * two roads leave the node as one surface. */
    if (spec.pinStartY != null) {
      prof[0] = spec.pinStartY;
      for (i = 1; i < N; i++) { var dpn = st[i].ds || stepM; var ndp = prof[i - 1] - gradeCap * dpn; if (prof[i] < ndp) prof[i] = ndp; }
    }
    if (spec.pinEndY != null) {
      prof[N - 1] = spec.pinEndY;
      for (i = N - 2; i >= 0; i--) { var dpe = st[i + 1].ds || stepM; var nde = prof[i + 1] - gradeCap * dpe; if (prof[i] < nde) prof[i] = nde; }
    }
    /* a guest at a node is given the host's PLANE for its first
     * stretch of arc: the host's grade along its own heading, the
     * host's bank across it, and the guest's profile held within a
     * hand's breadth of that plane for twelve metres — so two
     * roads leave a junction as ONE surface, and what seam remains
     * is the disc's to cover, never the traveller's to feel. Where
     * the floor and the plane quarrel, the floor wins: a road may
     * ride above its host's plane on honest fill, but it is never
     * buried to keep an appointment. */
    if (spec.pinHost) {
      var ph = spec.pinHost, tbH = Math.tan(ph.bank), arcH = 0;
      for (i = 0; i < N; i++) {
        if (i) arcH += st[i].ds || stepM;
        if (arcH > 12) break;
        var alH = (st[i].x - ph.x) * ph.fx + (st[i].z - ph.z) * ph.fz;
        var acH = (st[i].x - ph.x) * ph.rx + (st[i].z - ph.z) * ph.rz;
        var pyH = ph.y + ph.grade * alH + tbH * acH;
        if (prof[i] < pyH - 0.35) prof[i] = pyH - 0.35;
        if (prof[i] > pyH + 0.35) prof[i] = pyH + 0.35;
        if (prof[i] < floorY[i]) prof[i] = floorY[i];
      }
      envelope();
      if (spec.p