<!-- RUBY-BACKUP · part 434 -->
new THREE.CanvasTexture(cv);
  var sign = new THREE.Mesh(new THREE.PlaneGeometry(12, 1.5),
    new THREE.MeshBasicMaterial({map: tex, transparent: true}));
  sign.position.set(x, y, -40);
  scene.add(sign);
  /* glow behind */
  var glow = new THREE.Mesh(new THREE.PlaneGeometry(14, 3),
    new THREE.MeshBasicMaterial({color: color, transparent: true, opacity: 0.15}));
  glow.position.set(x, y, -40.5);
  scene.add(glow);
}
function buildTower(x, z, height, hue){
  var g = new THREE.Group();
  /* tower — permanent, solid */
  var towerMat;
  try{
    if(typeof GOLD_MODE!=='undefined' && GOLD_MODE){
      towerMat = new THREE.MeshStandardMaterial({
        color: 0xd4af37, metalness: 0.95, roughness: 0.15
      });
    }else{
      towerMat = new THREE.MeshStandardMaterial({
        color: new THREE.Color().setHSL(hue, 0.3, 0.4),
        metalness: 0.6, roughness: 0.4
      });
    }
  }catch(e){
    towerMat = new THREE.MeshStandardMaterial({
      color: new THREE.Color().setHSL(hue, 0.3, 0.4),
      metalness: 0.6, roughness: 0.4
    });
  }
  var tower = new THREE.Mesh(new THREE.BoxGeometry(1.5, height, 1.5), towerMat);
  tower.position.y = height/2;
  tower.userData.permanent = true; /* permanent */
  g.add(tower);
  /* windows — lit */
  var winMat;
  try{
    if(typeof GOLD_MODE!=='undefined' && GOLD_MODE){
      var jewelColors = [0xffd700, 0xff66ff, 0x66ffff, 0xffffff, 0xff3355];
      var jc = jewelColors[Math.floor(Math.random()*jewelColors.length)];
      winMat = new THREE.MeshStandardMaterial({
        color: jc, emissive: jc, emissiveIntensity: 1.5
      });
    }else{
      winMat = new THREE.MeshStandardMaterial({
        color: 0xffffcc, emissive: 0xffffcc, emissiveIntensity: 0.8
      });
    }
  }catch(e){
    winMat = new THREE.MeshStandardMaterial({
      color: 0xffffcc, emissive: 0xffffcc, emissiveIntensity: 0.8
    });
  }
  for(var f=1; f<height; f+=0.8){
    for(var s=0; s<4; s++){
      var win = new THREE.Mesh(new THREE.PlaneGeometry(0.2, 0.2), winMat);
      var ang = s*Math.PI/2;
      win.position.set(Math.sin(ang)*0.76, f, Math.cos(ang)*0.76);
      win.rotation.y = ang;
      g.add(win);
    }
  }
  /* rooftop beacon */
  var beacon = new THREE.Mesh(new THREE.SphereGeometry(0.08, 10, 10),
    new THREE.MeshStandardMaterial({color: 0xff0000, emissive: 0xff0000, emissiveIntensity: 2}));
  beacon.position.y = height + 0.1;
  g.add(beacon);
  g.position.set(x*0.13, 0, z*0.13);
  /* persist */
  try{
    var key = 'city-tower-'+x+'-'+z;
    localStorage.setItem(key, JSON.stringify({x: x, z: z, h: height, hue: hue}));
  }catch(e){}
  return g;
}

/* ANCESTRAL STREETS — signs for the last names of his lineage */
var ANCESTOR_STREETS = [
  'UENUKU', 'LILOA', 'UMI', 'KEAWE', 'KUKOHOU', 'KUAIWA',
  'HAKAU', 'KAHOUKAPU', 'MOKULANI', 'ULULANI',
  'KAIWAOKALANI', 'KANAKAOLE', 'WILCOX', 'KAILIWAI', 'KAMAILE',
  /* first names of those who have only one */
  'POTIKI', 'HIHIRIOTU', 'TARANGA', 'TAHARAWA', 'HEKAPONGA',
  'RAKAU', 'WHATOMA', 'POUTIRIAO', 'TANE', 'KAMEA',
  'POHUKAINA', 'HUA', 'PAU', 'PAUMAKUA', 'HAHO',
  'PALENA', 'LANAKAWAI', 'PILI', 'KANIUIHI', 'KANIPAHU',
  'KALAPANA', 'KAHAIMOELEA', 'KALAUNUIOHUA', 'KAUHOLANUIMAHU',
  'KIHA', 'KEALIIOKALOA', 'KUKAILANI', 'KAPUKAMOLA',
  'MAKAKAUalii', 'IWIKAWIKAUA', 'KANEIKAIWILANI',
  'KEAWEMAUHILI', 'NIAU', 'NAHOLOWAA', 'MUKOI',
  'KALUA', 'KAIKIOEWA', 'MAIKAI', 'PAOKALANI',
  'KAPIHA', 'NALIPOEPOE', 'KAPIHANUI', 'LELEKU',
  /* Hawaiian royal family — no titles */
  'KALANIKAULELEIAWI', 'KALANINUIAMAMAO', 'KEAWEKAHIALII',
  'KAIMAMAO', 'LONAAKEAWE',
  /* Hawaiian words for extinct animals — gone but remembered */
  'MOANALO', 'MAMO', 'OO', 'POOULI',
  'AKIALOA', 'NUKUPUU', 'KAKAWAHIE', 'OU'
];
var STREET_SIGNS = [];
function makeIntaglioSignTexture(name){
  /* canvas — ultra-dense intaglio linework + future symbols */
  var cv = document.createElement('canvas');
  cv.width = 512; cv.height = 128;
  var ctx = cv.getContext('2d');
  /* dark background */
  ctx.fillStyle = '#0a0a12';
  ctx.fillRect(0, 0, 512, 128);
  /* intaglio — dense fine lines */
  ctx.strokeStyle = 'rgba(200,170,100,0.25)';
  ctx.lineWidth = 0.5;
  for(var i=0; i<40; i++){
    ctx.beginPath();
    ctx.moveTo(0, i*3.2);
    ctx.bezierCurveTo(170, i*3.2+8, 340, i*3.2-8, 512, i*3.2);
    ctx.stroke();
  }
  /* border — double line */
  ctx.strokeStyle = '#c8aa64';
  ctx.lineWidth = 3;
  ctx.strokeRect(6, 6, 500, 116);
  ctx.lineWidth = 1;
  ctx.strokeRect(12, 12, 488, 104);
  /* future symbols — geometric accents */
  ctx.fillStyle = 'rgba(100,200,255,0.6)';
  [[20,64],[492,64]].forEach(function(p){
    ctx.beginPath();
    ctx.moveTo(p[0], p[1]-12);
    ctx.lineTo(p[0]+8, p[1]);
    ctx.lineTo(p[0], p[1]+12);
    ctx.lineTo(p[0]-8, p[1]);
    ctx.closePath();
    ctx.fill();
  });
  /* the name — last name only */
  ctx.fillStyle = '#f0e0c0';
  ctx.font = 'bold 44px Georgia, serif';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle'