<!-- PLAYA · part 4 -->
linear-gradient(135deg,#a14a58,#4d1a22)'],
  ['Emissive', {color:0xffd700, roughness:0.4, metalness:0.2, glow:1.2}, 'radial-gradient(circle,#fffbe0,#ffd700 45%,#b8860b)']
];
function applyBrush(i){
  var b = BRUSHES[i], p = b[1];
  edit('Brush ' + b[0], function(o){
    eachMat(o, function(mt){
      mt.color.setHex(p.color); mt.roughness = p.roughness; mt.metalness = p.metalness;
      var op = p.opacity || 1; mt.opacity = op; mt.transparent = op < 1; mt.depthWrite = op >= 1;
      if(p.glow){ mt.emissive.setHex(p.color); mt.emissiveIntensity = p.glow; } else { mt.emissive.setHex(0); mt.emissiveIntensity = 0; }
    });
  });
  say(b[0] + ' on ' + label(selected) + '.');
}

/* ---------- LINEWORK PRESETS ---------- */
var LW = [
  ['Blueprint', 0x0b2a5a, 0x6fa8ff, 0x0d244a], ['Parchment', 0xd9c49a, 0x6b4f2a, 0xbfa77a], ['Midnight', 0x05070f, 0x2a3a6a, 0x0b0d18],
  ['Mint', 0xbfe8d4, 0x2f6f58, 0x9fd0b8], ['Rose', 0xe8c0c8, 0x7a3a4a, 0xcf9fa9], ['Regalia', 0x07070c, 0x8a6a1c, 0x2a2418]
];

/* ---------- EXPORT ---------- */
function download(blob, name){
  var url = URL.createObjectURL(blob);
  var a = document.createElement('a'); a.href = url; a.download = name; a.rel = 'noopener';
  document.body.appendChild(a); a.click(); a.remove();
  setTimeout(function(){ URL.revokeObjectURL(url); }, 30000);
}
function stamp(){ var d = new Date(); function z(n){ return (n<10?'0':'') + n; } return d.getFullYear() + z(d.getMonth()+1) + z(d.getDate()) + '-' + z(d.getHours()) + z(d.getMinutes()) + z(d.getSeconds()); }
function exportPNG(mult){
  var oldPR = renderer.getPixelRatio();
  var w = vp.clientWidth, h = vp.clientHeight;
  var pr = Math.min(oldPR * mult, 4096 / Math.max(w, h));
  var showSel = selBox.visible; selBox.visible = false;
  renderer.setPixelRatio(pr); renderer.setSize(w, h, false);
  renderer.render(scene, camera);
  canvas.toBlob(function(blob){
    renderer.setPixelRatio(oldPR); renderer.setSize(w, h, false); selBox.visible = showSel;
    if(!blob){ say('⚠ PNG render failed — the browser returned no image.', true); return; }
    download(blob, 'creator-' + stamp() + '.png');
    say('Rendered PNG ' + Math.round(w*pr) + '×' + Math.round(h*pr) + ' — saved to your downloads.');
  }, 'image/png');
}
function exportOBJ(){
  var vis = blocks.filter(function(b){ return b.visible; });
  if(!vis.length){ say('Nothing to export — place a block first.', true); return; }
  var out = ['# THE CREATOR v1 — epistles.xyz/playa', '# ' + new Date().toISOString()];
  var vOff = 1, nOff = 1, v = new THREE.Vector3(), nm = new THREE.Matrix3();
  vis.forEach(function(b){
    b.updateMatrixWorld(true);
    meshesOf(b).forEach(function(m, mi){
      var g = m.geometry, pos = g.attributes.position, nor = g.attributes.normal, idx = g.index;
      var c = m.material.color;
      out.push('o ' + b.userData.blockType + '_' + b.userData.id + '_' + mi);
      out.push('# color ' + c.r.toFixed(3) + ' ' + c.g.toFixed(3) + ' ' + c.b.toFixed(3));
      for(var i=0;i<pos.count;i++){ v.fromBufferAttribute(pos, i).applyMatrix4(m.matrixWorld); out.push('v ' + v.x.toFixed(4) + ' ' + v.y.toFixed(4) + ' ' + v.z.toFixed(4)); }
      nm.getNormalMatrix(m.matrixWorld);
      for(var j=0;j<nor.count;j++){ v.fromBufferAttribute(nor, j).applyMatrix3(nm).normalize(); out.push('vn ' + v.x.toFixed(4) + ' ' + v.y.toFixed(4) + ' ' + v.z.toFixed(4)); }
      var flip = m.matrixWorld.determinant() < 0;
      var tri = idx ? idx.count / 3 : pos.count / 3;
      for(var t=0;t<tri;t++){
        var a = idx ? idx.getX(t*3) : t*3, bb = idx ? idx.getX(t*3+1) : t*3+1, cc = idx ? idx.getX(t*3+2) : t*3+2;
        if(flip){ var tmp = bb; bb = cc; cc = tmp; }
        out.push('f ' + (a+vOff) + '//' + (a+nOff) + ' ' + (bb+vOff) + '//' + (bb+nOff) + ' ' + (cc+vOff) + '//' + (cc+nOff));
      }
      vOff += pos.count; nOff += nor.count;
    });
  });
  download(new Blob([out.join('\n')], {type: 'text/plain'}), 'creator-' + stamp() + '.obj');
  say('Exported OBJ — ' + vis.length + ' block(s) — saved to your downloads.');
}

/* ---------- RECIPES (library + scene save) ---------- */
var LIB_KEY = 'playa-creator-v1-library', SCENE_KEY = 'playa-creator-v1-scene';
function recipe(b){
  var m0 = meshesOf(b).map(function(m){ var mt = m.material; return {c: mt.color.getHex(), r: mt.roughness, m: mt.metalness, o: mt.opacity, e: mt.emissive.getHex(), ei: mt.emissiveIntensity, w: mt.wireframe, f: mt.flatShading}; });
  return {t: b.userData.blockType, res: b.userData.res, bend: b.userData.bend, sec: b.userData.section, spin: b.userData.spin, bob: b.userData.bob,
    p: b.position.toArray(), q: b.quaternion.toArray(), s: b.scale.toArray(), vis: b.visible, mats: m0};
}
function fromRecipe(r){
  var b = makeBlock(r.t, r.res || 1);
  b.userData.bend = r.bend || 0; b.userData.section = !!r.sec; b.userData.spin = r.spin || 0; b.userData.bob = r.bob || 0;
  if(b.userData.bend) bendGeometry(b, b.userData.bend);
  b.position.fromArray(r.p); b.quaternion.fromArray(