<!-- RUBY-BACKUP · part 113 -->
 = new THREE.Mesh(new THREE.CylinderGeometry(0.14, 0.13, fl*0.32, 8), skinMat);
      p2.position.set(fx, 1.4 + fl*0.45 + fl*0.16, 0);
      p2.name = 'finger-middle';
      g.add(p2);
      /* distal + fingertip */
      var p3 = new THREE.Mesh(new THREE.CylinderGeometry(0.12, 0.1, fl*0.23, 8), skinMat);
      p3.position.set(fx, 1.4 + fl*0.45 + fl*0.32 + fl*0.11, 0);
      p3.name = 'finger-distal';
      g.add(p3);
      var tip = new THREE.Mesh(new THREE.SphereGeometry(0.11, 8, 6), skinMat);
      tip.position.set(fx, 1.4 + fl*0.45 + fl*0.32 + fl*0.23, 0);
      tip.name = 'fingertip';
      g.add(tip);
      /* nail */
      var nail = new THREE.Mesh(new THREE.BoxGeometry(0.16, 0.2, 0.04), new THREE.MeshStandardMaterial({color:0xe8d0b8, roughness:0.4}));
      nail.position.set(fx, 1.4 + fl*0.45 + fl*0.32 + fl*0.18, 0.12);
      nail.name = 'nail';
      g.add(nail);
    }
    /* thumb — 2 segments, angled */
    var th1 = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.16, 0.9, 8), skinMat);
    th1.position.set(-1.35, 0.3, 0.15);
    th1.rotation.z = 0.7;
    th1.name = 'thumb-proximal';
    g.add(th1);
    var th2 = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.12, 0.7, 8), skinMat);
    th2.position.set(-1.75, 0.85, 0.2);
    th2.rotation.z = 0.5;
    th2.name = 'thumb-distal';
    g.add(th2);
    var thTip = new THREE.Mesh(new THREE.SphereGeometry(0.13, 8, 6), skinMat);
    thTip.position.set(-1.92, 1.15, 0.22);
    thTip.name = 'thumb-tip';
    g.add(thTip);
    /* wrist */
    var wrist = new THREE.Mesh(new THREE.CylinderGeometry(0.85, 0.9, 1.2, 12), skinMat);
    wrist.position.set(0, -1.9, 0);
    wrist.name = 'wrist';
    g.add(wrist);
    g.position.set(12, 2, -24);
    g.scale.set(0.6, 0.6, 0.6);
    if(typeof scene!=='undefined') scene.add(g);
    window._solidHand = g;
    codeStatus('the solid hand — wrap it');
  }catch(e){ codeStatus('hand failed'); }
}
/* TRUE INTAGLIO — each line has boundaries, born from the 3D form */
function applyTrueIntaglio(){
  try{
    var target = window._solidHand || window._humanBody || window._intaglioEyes;
    if(!target){ try{ codeStatus('Build something first — solidhand, body, or eyes.'); }catch(e){} return; }
    var count = 0;
    target.traverse(function(o){
      if(!o.isMesh || !o.material) return;
      try{
        /* hatch shader — lines bounded by the surface, never crossing */
        o.material.onBeforeCompile = function(shader){
          shader.fragmentShader = shader.fragmentShader.replace(
            '#include <dithering_fragment>',
            [
              '/* intaglio hatch — lines respect the 3D boundaries */',
              'vec3 nrm = normalize(vNormal);',
              'vec3 viewDir = normalize(vViewPosition);',
              'float ndl = dot(nrm, normalize(vec3(0.5, 0.8, 0.6)));',
              'float shade = smoothstep(-0.5, 0.8, ndl);',
              '/* hatch density follows the shadow — like the engraver */',
              'float hatchScale = 180.0;',
              'vec2 huv = vUv * hatchScale;',
              'float hatch1 = step(0.5, fract(huv.y + huv.x*0.15));',
              'float hatch2 = step(0.5, fract((huv.x - huv.y)*0.7));',
              '/* more lines where darker — boundaries respected */',
              'float lineMask = mix(0.0, 1.0, smoothstep(0.7, 0.2, shade));',
              'float crossMask = mix(0.0, 1.0, smoothstep(0.4, 0.0, shade));',
              'vec3 hatchCol = vec3(0.05, 0.02, 0.0);',
              'gl_FragColor.rgb = mix(gl_FragColor.rgb, hatchCol, (1.0-hatch1)*lineMask*0.55);',
              'gl_FragColor.rgb = mix(gl_FragColor.rgb, hatchCol, (1.0-hatch2)*crossMask*0.4);',
              '#include <dithering_fragment>'
            ].join('\n')
          );
        };
        o.material.needsUpdate = true;
        count++;
      }catch(e){}
    });
    codeStatus('true intaglio — ' + count + ' parts lined');
  }catch(e){ codeStatus('intaglio failed'); }
}
/* PRIMITIVES — Playa 2.0 specs, real 3D meshes */
/* Command stack for functional undo (Playa 2.0: Command Pattern) */
window._cmdStack = window._cmdStack || [];
window._cmdIndex = window._cmdIndex || -1;
function pushCommand(cmd){
  /* cmd = {name, execute, undo} */
  window._cmdStack = window._cmdStack.slice(0, window._cmdIndex+1);
  window._cmdStack.push(cmd);
  window._cmdIndex++;
  try{ cmd.execute(); }catch(e){}
}
function undoCommand(){
  if(window._cmdIndex < 0){ try{ codeStatus('nothing to undo'); }catch(e){} return; }
  var cmd = window._cmdStack[window._cmdIndex];
  try{ cmd.undo(); }catch(e){}
  window._cmdIndex--;
  try{ codeStatus('undid ' + cmd.name); }catch(e){}
}
function redoCommand(){
  if(window._cmdIndex >= window._cmdStack.length-1){ try{ codeStatus('nothing to redo'); }catch(e){} return; }
  window._cmdIndex++;
  var cmd = window._cmdStack[window._cmdIndex];
  try{ cmd.execute(); }catch(e){}
  try{ codeStatus('redid ' + cmd.name); }catch(e){}
}
function buildHourglass(){
  try{
   