<!-- RUBY-BACKUP · part 227 -->
l({color: 0x8a6a4a, roughness: 0.9});
  var segments = 6;
  for(var i=0; i<segments; i++){
    var y = height - (i+0.5)*(height/segments);
    var segX = (i%2===0 ? 1.2 : -1.2);
    var trail = new THREE.Mesh(new THREE.BoxGeometry(2.5, 0.1, 0.8), trailMat);
    trail.position.set(segX*0.5, y, 0.6);
    g.add(trail);
    /* rope railing */
    var rail = new THREE.Mesh(
      new THREE.CylinderGeometry(0.02, 0.02, 2.5, 6),
      new THREE.MeshStandardMaterial({color: 0x6a4a2a, roughness: 0.8})
    );
    rail.rotation.z = Math.PI/2;
    rail.position.set(segX*0.5, y+0.4, 0.9);
    g.add(rail);
  }
  g.position.set(x*0.13, 0, z*0.13);
  return g;
}
function buildIsland(x, z, radius, hasVolcano){
  var g = new THREE.Group();
  /* island base — sand to grass */
  var sand = new THREE.Mesh(
    new THREE.CylinderGeometry(radius, radius*1.2, 1, 24),
    new THREE.MeshStandardMaterial({color: 0xd4b86a, roughness: 0.9})
  );
  sand.position.y = 0.5;
  g.add(sand);
  var grass = new THREE.Mesh(
    new THREE.CylinderGeometry(radius*0.8, radius*0.9, 0.8, 20),
    new THREE.MeshStandardMaterial({color: 0x3a7a3a, roughness: 0.9})
  );
  grass.position.y = 1.2;
  g.add(grass);
  /* water ring */
  var water = new THREE.Mesh(
    new THREE.TorusGeometry(radius*1.3, 0.5, 8, 32),
    new THREE.MeshStandardMaterial({color: 0x0088cc, transparent: true, opacity: 0.7, roughness: 0.2})
  );
  water.rotation.x = Math.PI/2;
  water.position.y = 0.3;
  g.add(water);
  if(hasVolcano){
    var v = buildVolcano(0, 0, radius*0.4);
    v.position.y = 1.5;
    g.add(v);
  }
  g.position.set(x*0.13, 0, z*0.13);
  return g;
}

/* HAWAIIAN VALLEYS — like home */
var VALLEYS = null;
function buildWaterfall(x, y, z, height){
  var g = new THREE.Group();
  /* falling water — translucent */
  var water = new THREE.Mesh(
    new THREE.PlaneGeometry(1.2, height),
    new THREE.MeshStandardMaterial({
      color: 0xaaddff, transparent: true, opacity: 0.7,
      roughness: 0.1, side: THREE.DoubleSide
    })
  );
  water.position.y = height/2;
  g.add(water);
  /* mist at base */
  for(var i=0; i<3; i++){
    var mist = new THREE.Mesh(
      new THREE.SphereGeometry(0.4+i*0.2, 8, 8),
      new THREE.MeshStandardMaterial({color: 0xffffff, transparent: true, opacity: 0.3})
    );
    mist.position.set((Math.random()-0.5)*1.5, 0.3, (Math.random()-0.5)*1.5);
    g.add(mist);
  }
  /* pool */
  var pool = new THREE.Mesh(
    new THREE.CylinderGeometry(1.5, 1.5, 0.2, 16),
    new THREE.MeshStandardMaterial({color: 0x0088aa, transparent: true, opacity: 0.8, roughness: 0.1})
  );
  pool.position.y = 0.1;
  g.add(pool);
  g.position.set(x, y, z);
  /* animated flow */
  var t = 0;
  var anim = setInterval(function(){
    t += 0.05;
    water.material.opacity = 0.6 + Math.sin(t*3)*0.15;
  }, 50);
  g.userData.anim = anim;
  return g;
}
function buildHawaiianValley(x, z, width, depth){
  var g = new THREE.Group();
  var green = new THREE.MeshStandardMaterial({color: 0x2a6a2a, roughness: 0.95});
  var darkGreen = new THREE.MeshStandardMaterial({color: 0x1a4a1a, roughness: 0.95});
  var paliMat = new THREE.MeshStandardMaterial({color: 0x3a5a3a, roughness: 0.9, flatShading: true});
  /* valley floor — lush */
  var floor = new THREE.Mesh(
    new THREE.PlaneGeometry(width, depth),
    green
  );
  floor.rotation.x = -Math.PI/2;
  floor.position.y = 0.05;
  g.add(floor);
  /* pali walls — steep, both sides */
  [-1, 1].forEach(function(s){
    for(var i=0; i<8; i++){
      var wallH = 8 + Math.random()*4;
      var wall = new THREE.Mesh(
        new THREE.BoxGeometry(width/8, wallH, depth),
        paliMat
      );
      wall.position.set(s*(width/2 + width/16), wallH/2, (Math.random()-0.5)*2);
      wall.rotation.z = s*0.08;
      g.add(wall);
    }
  });
  /* waterfalls — down the pali */
  var wf1 = buildWaterfall(-width/2+0.5, 0, 0, 9);
  g.add(wf1);
  var wf2 = buildWaterfall(width/2-0.5, 0, depth/4, 10);
  g.add(wf2);
  /* tropical plants */
  for(var p=0; p<25; p++){
    var plantH = 0.5 + Math.random()*1.2;
    /* trunk */
    var trunk = new THREE.Mesh(
      new THREE.CylinderGeometry(0.06, 0.09, plantH, 6),
      new THREE.MeshStandardMaterial({color: 0x5a3a1a, roughness: 0.9})
    );
    trunk.position.set(
      (Math.random()-0.5)*width*0.8,
      plantH/2,
      (Math.random()-0.5)*depth*0.8
    );
    g.add(trunk);
    /* fronds */
    for(var f=0; f<5; f++){
      var frond = new THREE.Mesh(
        new THREE.PlaneGeometry(0.3, 1.2),
        new THREE.MeshStandardMaterial({color: 0x3a8a3a, roughness: 0.8, side: THREE.DoubleSide})
      );
      var fa = f*(Math.PI*2/5);
      frond.position.set(
        trunk.position.x + Math.cos(fa)*0.4,
        plantH + 0.2,
        trunk.position.z + Math.sin(fa)*0.4
      );
      frond.rotation.set(Math.random()*0.5, fa, 0.6);
      g.add(frond);
    }
  }
  /* river — flows through the valley, from the waterfalls */
  var riverCurve = new THREE.CatmullRomCurve3([
    new T