<!-- RUBY-BACKUP · part 395 -->
    o.push(mkOp('cyl', 24, ec.x+18, ec.z-18));
    var wz = [-14,-12,-10,-8,-6,-4,-2,0,2,4,6,8,10,12,14];
    for(var wi=0; wi<wz.length; wi++){
      var wg = (wz[wi]===-8 || wz[wi]===0 || wz[wi]===8);
      o.push(mkOp(wg?'cube~ghost':'cube', 8, ec.x+18, ec.z+wz[wi]));
    }
    var nx = [2,4,6,8,10,12,14,16];
    for(var ni2=0; ni2<nx.length; ni2++){
      var ng = (nx[ni2]===6 || nx[ni2]===12);
      o.push(mkOp(ng?'cube~ghost':'cube', 8, ec.x+nx[ni2], ec.z-18));
    }
    var sx = [6,8,10,12,14,16];
    for(var si2=0; si2<sx.length; si2++){
      var sg = (sx[si2]===10);
      o.push(mkOp(sg?'cube~ghost':'cube', 8, ec.x+sx[si2], ec.z+18));
    }
    o.push(mkOp('cyl', 16, ec.x+4, ec.z+18));
    o.push(mkOp('cube', 28, ec.x, ec.z-6));
    o.push(mkOp('cyl', 12, ec.x+4, ec.z-10));
    o.push(mkOp('cyl', 12, ec.x+4, ec.z-2));
    o.push(mkOp('cube~ghost', 8, ec.x, ec.z+18));
    return o;
  },
  cas1: function(ec){
    var west = PROCEDURES['cas1-west'](ec);
    return west.concat(mirrorOps(west, ec.x));
  },
  /* ━━━ THE LIVING PROCEDURES — nature, one word each, stamped at the cursor ━━━ */
  tree: function(ec){ return [  /* the oak — trunk and a broad canopy */
    mkOp('cyl', 4, ec.x, ec.z),
    mkOp('sphere', 10, ec.x, ec.z, 4),
    mkOp('sphere', 7, ec.x, ec.z, 14) ]; },
  pine: function(ec){ return [  /* the pine — trunk and three stacked cones */
    mkOp('cyl', 3, ec.x, ec.z),
    mkOp('cone', 10, ec.x, ec.z, 3),
    mkOp('cone', 8, ec.x, ec.z, 13),
    mkOp('cone', 6, ec.x, ec.z, 21) ]; },
  flower: function(ec){ return [  /* stem, a ring of petals, a golden heart */
    mkOp('cyl', 6, ec.x, ec.z),
    mkOp('torus', 8, ec.x, ec.z, 6),
    mkOp('sphere', 2, ec.x, ec.z, 7) ]; },
  bush: function(ec){ return [  /* a low thicket of round stones */
    mkOp('sphere', 6, ec.x, ec.z),
    mkOp('sphere', 5, ec.x+6, ec.z+2),
    mkOp('sphere', 5, ec.x-6, ec.z-2),
    mkOp('sphere', 4, ec.x, ec.z-6) ]; },
  mushroom: function(ec){ return [  /* the toadstool — stem and a round cap */
    mkOp('cyl', 3, ec.x, ec.z),
    mkOp('sphere', 8, ec.x, ec.z, 3) ]; },
  rock: function(ec){ return [  /* faceted boulders, a little tumble */
    mkOp('dodeca', 7, ec.x, ec.z),
    mkOp('dodeca', 5, ec.x+6, ec.z+4),
    mkOp('icosa', 4, ec.x-6, ec.z+2) ]; },
  pond: function(ec){ return [  /* the basin — a stone ring with a little island */
    mkOp('torus', 12, ec.x, ec.z),
    mkOp('cone', 5, ec.x, ec.z, 1) ]; },
  grove: function(ec){ var o=[];  /* a whole grove in one word — oak, pine, thicket */
    o.push(mkOp('cyl', 4, ec.x, ec.z), mkOp('sphere', 10, ec.x, ec.z, 4), mkOp('sphere', 7, ec.x, ec.z, 14));
    o.push(mkOp('cyl', 3, ec.x+18, ec.z+6), mkOp('cone', 10, ec.x+18, ec.z+6, 3),
           mkOp('cone', 8, ec.x+18, ec.z+6, 13), mkOp('cone', 6, ec.x+18, ec.z+6, 21));
    o.push(mkOp('sphere', 6, ec.x-16, ec.z+8), mkOp('sphere', 5, ec.x-10, ec.z+12),
           mkOp('dodeca', 5, ec.x+2, ec.z+18));
    return o; }
};
var expanding = {};
function expandProc(name, ec, depth){
  if(!PROCEDURES[name]) return {ok:false, error:'unknown word'};
  if(expanding[name]) return {ok:false, error:'recursion'};
  if(depth > HARDEN.MAX_DEPTH) return {ok:false, error:'too deep'};
  expanding[name] = true;
  var ops = null;
  try{ ops = PROCEDURES[name](ec); }catch(e){}
  expanding[name] = false;
  if(!ops || !ops.length) return {ok:false, error:'procedure failed'};
  for(var i=0;i<ops.length;i++){ ops[i].x = snap(ops[i].x); ops[i].z = snap(ops[i].z); ops[i].proc = name; }
  return {ok:true, ops:ops};   /* stamps: the cursor stays where it stood */
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   MARK: MIRROR — flip. Every procedure carries its equal and opposite.
   EDIT: mirror axis. Currently across the cursor's x (west↔east).
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
function mirrorOps(ops, cx0){
  return ops.map(function(o){
    var c = {shape:o.shape, size:o.size, x:2*cx0-o.x, z:o.z, y:o.y||0, proc:o.proc, remove:!!o.remove};
    return c;
  });
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   MARK: LAW OF GROUND — what happens when a stone meets a stone
     own work (same code) → stepped over (never duplicated)
     a new project claims its site → every previous project inside the new
        footprint is rendered obsolete, at the stone: the old code deletes,
        atomic, whatever its mass. newness decides, not mass.
     hand work on a project → the old mass rule (a tap never nukes a project)
     the eternal → coexisted with (full build in the cliff; terrain never yields)
   Cells are 3D (x, z, y in shells) — the law of space. Same x,z at different
   heights coexist: that is how windows sit inside walls.
   EDIT: the mass measure, the displacement test. Mass = total rubies.
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
function procMassOf(ops){ var m=0; for(var i=0;i<ops.length;i++){ if(!ops[i].remove) m