<!-- RUBY-BACKUP · part 147 -->
('cyl', 6, ec.x+3, ec.z-7),
      mkOp('cyl', 6, ec.x+3, ec.z-1),
      mkOp('cube~ghost', 2, ec.x+14, ec.z-6, 2),
      mkOp('cube~ghost', 2, ec.x+14, ec.z+6, 2),
      mkOp('cube~ghost', 2, ec.x+6, ec.z-14, 2),
      mkOp('cube~ghost', 2, ec.x+6, ec.z+14, 2),
      mkOp('cube~ghost', 4, ec.x, ec.z+14, 0)
    ];
  },
  'castle-real': function(ec){
    var west = PROCEDURES['castle-real-west'](ec);
    return west.concat(mirrorOps(west, ec.x));
  },
  /* a fence — posts with sphere caps. anchor a cap with oops-my-bad,
     then `swap p` for pointed pyramids, `swap v` for cones. */
  fence: function(ec){
    var o = [];
    for(var i=0;i<4;i++){
      o.push(mkOp('cube', 4, ec.x+i*2, ec.z));
      o.push(mkOp('sphere', 2, ec.x+i*2, ec.z, 4));
    }
    return o;
  },
  /* solid1 — Solid's first: The Lean. A plinth, a wedge buttress, the rise,
     the bend, and the crown. Five stones, the whole family in one column. */
  /* baby step — one step for now, for my children; we grow it together */
  babystep: function(ec){
    return [mkOp('cube', 15, ec.x, ec.z, 0)];
  },
  solid1: function(ec){
    return [
      mkOp('cube', 6, ec.x, ec.z),
      mkOp('tri', 4, ec.x-2, ec.z),
      mkOp('pyramid', 6, ec.x, ec.z, 6),
      mkOp('poff', 6, ec.x, ec.z, 12),
      mkOp('pdown', 4, ec.x, ec.z, 18)
    ];
  },
  /* cas1 — THE FULL CASTLE. west half; flip does the east.
     Continuous walls, ghost windows standing IN the wall line,
     gatehouse south, the great keep. Presence, not ground plan. */
  'cas1-west': function(ec){
    var o = [];
    o.push(mkOp('cyl', 24, ec.x+18, ec.z+18));
    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) re