<!-- SAGA-STAGING · part 4 -->
size/2;
  var R = (size/2 - fm) * 0.92;
  
  // Family 1: Large hypotrochoid rosette (silver)
  function rose(a, b, d, scale, phase, lines, col, w, bcol) {
    for(var i=0;i<lines;i++){
      var t0=(i/lines)*Math.PI*2+phase, t1=((i+1)/lines)*Math.PI*2+phase;
      function pt(t){
        var x=(a-b)*Math.cos(t)+d*Math.cos((a-b)/b*t);
        var y=(a-b)*Math.sin(t)-d*Math.sin((a-b)/b*t);
        return [fx+x*scale, fy+y*scale];
      }
      var p0=pt(t0), p1=pt(t1);
      dline(p0[0],p0[1],p1[0],p1[1],w,col,bcol);
    }
  }
  
  // Three interwoven families with different frequencies
  rose(11, 7, R*0.28, R/11, 0, 220, '#6e7680', size*0.0012, '#909090');
  rose(13, 5, R*0.32, R/13, 1.1, 260, '#8a929c', size*0.001, '#a0a0a0');
  rose(9, 4, R*0.24, R/9, 2.3, 180, '#5e6670', size*0.0015, '#808080');
  
  // === FINE SECONDARY LINES (between major lines) ===
  // Ultra-fine concentric wavy circles in the gaps
  for(var wi=0; wi<6; wi++){
    var baseR = R*(0.55 + wi*0.06);
    var amp = R*0.015, freq = 40 + wi*8, ph = rand()*Math.PI*2;
    var steps = 360;
    for(var i=0;i<steps;i++){
      var t0=(i/steps)*Math.PI*2, t1=((i+1)/steps)*Math.PI*2;
      var r0=baseR+amp*Math.sin(freq*t0+ph), r1=baseR+amp*Math.sin(freq*t1+ph);
      // Alternate gold/silver selectively
      var col = (wi%2===0) ? '#9a8a4a' : '#7a828c';
      dline(fx+Math.cos(t0)*r0, fy+Math.sin(t0)*r0,
            fx+Math.cos(t1)*r1, fy+Math.sin(t1)*r1, size*0.0008, col, '#a0a0a0');
    }
  }
  
  // === NESTED ROSETTE (smaller, inside) ===
  var r2 = R*0.38;
  rose(7, 3, r2*0.3, r2/7, 0.5, 140, '#d4af37', size*0.0018, '#e0e0e0');
  rose(8, 3, r2*0.25, r2/8, 1.8, 160, '#a8b0b8', size*0.0012, '#c0c0c0');
  
  // === DENSITY GRADIENT RING ===
  // Lines get denser toward center (optical darkening)
  for(var dr=r2*0.7; dr>r2*0.3; dr-=r2*0.04){
    var density = Math.floor(60 + (r2*0.7-dr)*200/r2);
    for(var a=0;a<density;a++){
      var ang=(a/density)*Math.PI*2;
      var x1=fx+Math.cos(ang)*dr, y1=fy+Math.sin(ang)*dr;
      var x2=fx+Math.cos(ang)*(dr+r2*0.03), y2=fy+Math.sin(ang)*(dr+r2*0.03);
      dline(x1,y1,x2,y2, size*0.001, '#5e6670', '#808080');
    }
  }
  
  // === CENTRAL MAKER'S MARK (hammer/anvil geometric) ===
  var mr = R*0.14;
  // Outer mark border (gold)
  dcirc(fx,fy,mr, size*0.004, '#d4af37', '#ffffff');
  dcirc(fx,fy,mr*0.85, size*0.002, '#8a6d2f', '#c0c0c0');
  // Hammer: vertical bar with cross head
  var hw = mr*0.18;
  ctx.fillStyle='#d4af37';
  ctx.fillRect(fx-hw/2, fy-mr*0.6, hw, mr*1.2);
  ctx.fillRect(fx-mr*0.4, fy-mr*0.6, mr*0.8, mr*0.25);
  btx.fillStyle='#ffffff';
  btx.fillRect(fx-hw/2, fy-mr*0.6, hw, mr*1.2);
  btx.fillRect(fx-mr*0.4, fy-mr*0.6, mr*0.8, mr*0.25);
  // Anvil: triangle base
  ctx.beginPath();
  ctx.moveTo(fx-mr*0.35, fy+mr*0.6); ctx.lineTo(fx+mr*0.35, fy+mr*0.6); ctx.lineTo(fx, fy+mr*0.25);
  ctx.closePath(); ctx.fillStyle='#a8b0b8'; ctx.fill();
  btx.beginPath();
  btx.moveTo(fx-mr*0.35, fy+mr*0.6); btx.lineTo(fx+mr*0.35, fy+mr*0.6); btx.lineTo(fx, fy+mr*0.25);
  btx.closePath(); btx.fillStyle='#e0e0e0'; btx.fill();
  // Micro-text ring (dots suggesting inscription)
  for(var ti=0; ti<48; ti++){
    var ta=(ti/48)*Math.PI*2;
    var tx=fx+Math.cos(ta)*mr*0.72, ty=fy+Math.sin(ta)*mr*0.72;
    ctx.fillStyle = ti%2 ? '#d4af37' : '#8a6d2f';
    ctx.beginPath(); ctx.arc(tx,ty,size*0.0012,0,Math.PI*2); ctx.fill();
  }
  
  // === CONTROLLED CROSSING NODES ===
  // Where families intersect, add darker nodes (optical density)
  for(var ni=0; ni<24; ni++){
    var na=(ni/24)*Math.PI*2 + 0.3;
    var nr=R*(0.6+0.2*Math.sin(ni*2.7));
    var nx=fx+Math.cos(na)*nr, ny=fy+Math.sin(na)*nr;
    ctx.fillStyle='rgba(0,0,0,0.4)';
    ctx.beginPath(); ctx.arc(nx,ny,size*0.004,0,Math.PI*2); ctx.fill();
  }
  
  var ct=new THREE.CanvasTexture(cv);
  ct.anisotropy=8;
  ct.minFilter=THREE.LinearMipmapLinearFilter;
  ct.magFilter=THREE.LinearFilter;
  ct.generateMipmaps=true;
  var bt=new THREE.CanvasTexture(bv);
  bt.anisotropy=8;
  return {map:ct, bumpMap:bt};
}

/* ============================================================
   EPISTLES: Engine-Turned Directional Texture (Courier language)
   Parallel lines with directional flow — NOT radial guilloche
   ============================================================ */
function generateEngineTurned(opts) {
  opts = opts || {};
  var size = opts.size || 1024;
  var cv = document.createElement('canvas'); cv.width = cv.height = size;
  var ctx = cv.getContext('2d');
  ctx.imageSmoothingEnabled = true;
  var bv = document.createElement('canvas'); bv.width = bv.height = size;
  var btx = bv.getContext('2d');
  ctx.fillStyle = '#2e3440'; ctx.fillRect(0, 0, size, size);
  btx.fillStyle = '#808080'; btx.fillRect(0, 0, size, size);
  var lines = 120;
  for(var i=0; i<lines; i++){
    var x = (i/lines) * size;
    var densityMod = 1 + 0.4*Math.sin((i/lines)*Math.PI);
    var w = size*0.0012 * densityMod;
    var waveAmp = size*0.008, waveFr