<!-- SAGA2 · part 37 -->
THREE.Scene();
scene.background = new THREE.Color(0x050508);
var camera = new THREE.PerspectiveCamera(45, innerWidth/innerHeight, 0.1, 100);
camera.position.set(0, 2, 14);
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

// Lights
scene.add(new THREE.HemisphereLight(0xffffff, 0x223344, 0.6));
var key = new THREE.DirectionalLight(0xffffff, 1.2);
key.position.set(8, 12, 10); scene.add(key);
var cyan = new THREE.PointLight(0x00ccff, 1.5, 30);
cyan.position.set(-6, 2, 6); scene.add(cyan);
var gold = new THREE.PointLight(0xffcc44, 1.0, 30);
gold.position.set(6, -2, 6); scene.add(gold);

var loader = new THREE.TextureLoader();

// COIN — cylinder with ridged edge (not flat)
var coinGroup = new THREE.Group();
scene.add(coinGroup);

var coinRadius = 3, coinThick = 0.35;
loader.load('solid-coin.png', function(frontTex){
  loader.load('epistolary-coin.png', function(backTex){
    // Edge material (gold ridge)
    var edgeMat = new THREE.MeshStandardMaterial({color:0xc9a54f, roughness:0.25, metalness:0.9});
    // Face materials
    var frontMat = new THREE.MeshStandardMaterial({map:frontTex, roughness:0.35, metalness:0.6});
    var backMat = new THREE.MeshStandardMaterial({map:backTex, roughness:0.35, metalness:0.6});
    // Cylinder: [side, top, bottom] — top=front, bottom=back
    var coinGeo = new THREE.CylinderGeometry(coinRadius, coinRadius, coinThick, 64);
    // Add a thin ridge (torus) around the edge for the raised rim
    var coin = new THREE.Mesh(coinGeo, [edgeMat, frontMat, backMat]);
    coin.rotation.x = Math.PI/2; // face forward
    coinGroup.add(coin);
    // Ridge: two thin tori on front and back rims
    var ridgeGeo = new THREE.TorusGeometry(coinRadius-0.05, 0.08, 12, 64);
    var ridgeF = new THREE.Mesh(ridgeGeo, edgeMat);
    ridgeF.position.z = coinThick/2; coinGroup.add(ridgeF);
    var ridgeB = new THREE.Mesh(ridgeGeo, edgeMat);
    ridgeB.position.z = -coinThick/2; coinGroup.add(ridgeB);
  });
});

// BEZEL — futuristic ring around the coin (with its own ridge)
var bezelGroup = new THREE.Group();
scene.add(bezelGroup);
loader.load('future-bezel-btn.png', function(bezelTex){
  var bezelMat = new THREE.MeshStandardMaterial({
    color:0x444455, roughness:0.3, metalness:0.85,
    emissive:0x003344, emissiveIntensity:0.4
  });
  // Main ring
  var ringGeo = new THREE.TorusGeometry(3.8, 0.45, 16, 72);
  var ring = new THREE.Mesh(ringGeo, bezelMat);
  bezelGroup.add(ring);
  // Thin ridge on bezel (outer edge)
  var bezRidge = new THREE.Mesh(
    new THREE.TorusGeometry(4.25, 0.08, 10, 72),
    new THREE.MeshStandardMaterial({color:0x00ccff, roughness:0.2, metalness:0.9, emissive:0x00aacc, emissiveIntensity:0.8})
  );
  bezelGroup.add(bezRidge);
  // Inner ridge
  var bezInner = new THREE.Mesh(
    new THREE.TorusGeometry(3.35, 0.06, 10, 72),
    new THREE.MeshStandardMaterial({color:0x00ccff, roughness:0.2, metalness:0.9, emissive:0x0088aa, emissiveIntensity:0.6})
  );
  bezelGroup.add(bezInner);
});

// Animate: coin FLIPS inside spinning bezel (correct direction)
var t = 0;
function animate(){
  requestAnimationFrame(animate);
  t += 0.016;
  // Coin flips end-over-end (rotation.x) — like a real coin toss
  coinGroup.rotation.x = t * 1.5;
  coinGroup.rotation.z = Math.PI/2; // 90° CCW
  // Gentle float
  coinGroup.position.y = Math.sin(t*1.5)*0.15;
  // Bezel spins (rotation.z) — gyroscope ring around the flipping coin
  bezelGroup.rotation.z = Math.PI/2 + t * 0.8; // 90° CCW + spin
  // Bezel also tilts slowly for 3D effect
  bezelGroup.rotation.x = Math.sin(t*0.4)*0.25;
  renderer.render(scene, camera);
}
animate();

window.addEventListener('resize', function(){
  camera.aspect = innerWidth/innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

// Touch: drag to orbit
var px=0, py=0, drag=false;
renderer.domElement.addEventListener('pointerdown', function(e){ drag=true; px=e.clientX; py=e.clientY; });
renderer.domElement.addEventListener('pointermove', function(e){
  if(!drag) return;
  var dx=e.clientX-px, dy=e.clientY-py; px=e.clientX; py=e.clientY;
  scene.rotation.y += dx*0.005;
  scene.rotation.x += dy*0.005;
});
renderer.domElement.addEventListener('pointerup', function(){ drag=false; });
</script>
</body>
</html>


===== FILE: public/saga2/faces.html =====
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Face Lab — 1000 Faces</title>
<style>
  body { margin:0; background:#000; overflow:hidden; font-family:sans-serif; }
  #ui { position:fixed; top:10px; left:10px; color:#0cc; z-index:10; }
  button { background:#ffcc44; border:none; padding:12px 20px; font-size:16px; margin:5px; border-radius:8px; }
</style>
<script src="/saga2/three.min.js"></script>
</head>
<body>
<div id="ui">
  <div>Face Lab — <span id="count">0