<!-- RUBY-BACKUP · part 193 -->
chestBadge = new THREE.Mesh(new THREE.CircleGeometry(0.05, 8),
    new THREE.MeshStandardMaterial({color: 0xffcc00, metalness: 0.8, roughness: 0.3}));
  chestBadge.position.set(0, 1.2, 0.2); g.add(chestBadge);
  [-1,1].forEach(function(s){
    var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.07, 0.06, 0.55, 8), uniform);
    leg.position.set(0, 0.28, s*0.1); g.add(leg);
  });
  /* empty hands — patting pockets, pad is missing */
  [-1,1].forEach(function(s){
    var arm = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.045, 0.4, 8), uniform);
    arm.position.set(-0.1, 1.1, s*0.25); arm.rotation.z = 0.5;
    g.add(arm);
  });
  /* speech — "Have you seen my pad?" */
  var cv = document.createElement('canvas');
  cv.width = 512; cv.height = 80;
  var ctx = cv.getContext('2d');
  ctx.fillStyle = 'rgba(240,240,255,0.95)';
  ctx.fillRect(0, 0, 512, 80);
  ctx.strokeStyle = '#1a2a5a'; ctx.lineWidth = 4;
  ctx.strokeRect(4, 4, 504, 72);
  ctx.fillStyle = '#1a2a5a'; ctx.font = 'bold 24px Arial'; ctx.textAlign = 'center';
  ctx.fillText('Have you seen my pad?', 256, 48);
  var tex = new THREE.CanvasTexture(cv);
  var bubble = new THREE.Mesh(new THREE.PlaneGeometry(2.2, 0.34),
    new THREE.MeshBasicMaterial({map: tex, transparent: true}));
  bubble.position.set(0, 2.1, 0);
  g.add(bubble);
  g.position.set(x, y, z);
  var t = 0;
  var anim = setInterval(function(){
    t += 0.04;
    /* looking around, patting pockets */
    g.rotation.y = Math.sin(t*0.8)*0.6;
    bubble.rotation.y = -g.rotation.y; /* face camera */
  }, 50);
  g.userData.anim = anim;
  return g;
}
function buildFirefighter(x, y, z){
  var g = new THREE.Group();
  var skin = new THREE.MeshStandardMaterial({color: 0xd4a574, roughness: 0.7});
  var gear = new THREE.MeshStandardMaterial({color: 0xcc3300, roughness: 0.6});
  var yellow = new THREE.MeshStandardMaterial({color: 0xffcc00, emissive: 0xffcc00, emissiveIntensity: 0.4});
  /* head */
  var head = new THREE.Mesh(new THREE.SphereGeometry(0.15, 18, 18), skin);
  head.position.y = 1.55; g.add(head);
  /* firefighter helmet */
  var helmet = new THREE.Mesh(new THREE.SphereGeometry(0.18, 14, 14, 0, Math.PI*2, 0, Math.PI*0.6), gear);
  helmet.position.y = 1.58; g.add(helmet);
  var brim = new THREE.Mesh(new THREE.CylinderGeometry(0.22, 0.22, 0.03, 14), gear);
  brim.position.y = 1.58; g.add(brim);
  /* reflective stripes */
  [-1,1].forEach(function(s){
    var stripe = new THREE.Mesh(new THREE.BoxGeometry(0.02, 0.06, 0.3), yellow);
    stripe.position.set(0.1, 1.1, s*0.15); g.add(stripe);
  });
  /* turnout gear body */
  var body = new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0.24, 0.9, 12), gear);
  body.position.y = 1.0; g.add(body);
  /* reflective band */
  var band = new THREE.Mesh(new THREE.CylinderGeometry(0.215, 0.215, 0.08, 12), yellow);
  band.position.y = 1.1; g.add(band);
  [-1,1].forEach(function(s){
    var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.07, 0.55, 8), gear);
    leg.position.set(0, 0.28, s*0.11); g.add(leg);
    var boot = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.1, 0.12),
      new THREE.MeshStandardMaterial({color: 0x222222, roughness: 0.8}));
    boot.position.set(0.05, 0.05, s*0.11); g.add(boot);
  });
  /* holding empty hands out — where's the hydrant? */
  [-1,1].forEach(function(s){
    var arm = new THREE.Mesh(new THREE.CylinderGeometry(0.055, 0.05, 0.45, 8), gear);
    arm.position.set(0.1, 1.1, s*0.28); arm.rotation.x = s*0.6; arm.rotation.z = -0.4;
    g.add(arm);
  });
  /* speech — "Where's the hydrant?" */
  var cv = document.createElement('canvas');
  cv.width = 512; cv.height = 80;
  var ctx = cv.getContext('2d');
  ctx.fillStyle = 'rgba(255,240,240,0.95)';
  ctx.fillRect(0, 0, 512, 80);
  ctx.strokeStyle = '#cc3300'; ctx.lineWidth = 4;
  ctx.strokeRect(4, 4, 504, 72);
  ctx.fillStyle = '#cc3300'; ctx.font = 'bold 24px Arial'; ctx.textAlign = 'center';
  ctx.fillText("Where's the hydrant?", 256, 48);
  var tex = new THREE.CanvasTexture(cv);
  var bubble = new THREE.Mesh(new THREE.PlaneGeometry(2.2, 0.34),
    new THREE.MeshBasicMaterial({map: tex, transparent: true}));
  bubble.position.set(0, 2.1, 0);
  g.add(bubble);
  /* fire hydrant — hidden nearby for the player to find */
  var hydrant = new THREE.Group();
  var hBody = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.18, 0.4, 10),
    new THREE.MeshStandardMaterial({color: 0xff0000, roughness: 0.5}));
  hBody.position.y = 0.3; hydrant.add(hBody);
  var hTop = new THREE.Mesh(new THREE.SphereGeometry(0.12, 10, 10),
    new THREE.MeshStandardMaterial({color: 0xcc0000, roughness: 0.5}));
  hTop.position.y = 0.55; hydrant.add(hTop);
  [-1,1].forEach(function(s){
    var cap = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.08, 8),
      new THREE.MeshStandardMaterial({color: 0xaa0000, roughness: 0.5}));
    cap.rotation.z = Math.PI/2;
    cap.position.set(0, 0.35, s*0.18); hydrant.add(cap);
  });
  /* hide it — behind a building, player 