<!-- RUBY-BACKUP · part 515 -->
nstall into', function(){ startInstall(); }]
  ];
  btns.forEach(function(b){
    var btn = document.createElement('button');
    btn.textContent = b[0];
    btn.title = b[1];
    btn.style.cssText = 'width:48px;height:48px;background:#d8a920;color:#111;border:none;border-radius:50%;font-size:20px;font-weight:bold;';
    btn.onclick = b[2];
    p.appendChild(btn);
  });
  document.body.appendChild(p);
  _grabPanel = p;
}
function hideGrabPanel(){
  var p = document.getElementById('grabpanel');
  if(p) p.remove();
  _grabPanel = null;
}
// tap to pick up when in grab mode
if(typeof window!=='undefined'){
  window.addEventListener('click', function(e){
    if(!GRAB_MODE || _grabbed) return;
    // raycast to find tapped object
    try{
      if(typeof raycaster!=='undefined' && typeof camera!=='undefined' && typeof scene!=='undefined'){
        var mouse = new THREE.Vector2();
        mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
        raycaster.setFromCamera(mouse, camera);
        var hits = raycaster.intersectObjects(scene.children, true);
        for(var i=0; i<hits.length; i++){
          var o = hits[i].object;
          // skip ground, streets, UI
          if(o.name && (o.name.indexOf('street')===0 || o.name==='plaza' || o.name.indexOf('ground')===0)) continue;
          // walk up to find a group
          var target = o;
          while(target.parent && target.parent!==scene && !target.userData.isPrimitive){
            target = target.parent;
          }
          pickUp(target);
          break;
        }
      }
    }catch(err){}
  });
}
/* STREETS — creative, winding, alive */
/* PERMANENT STREETS — load at start, never removed */
function ensurePermanentStreets(){
  try{
    if(typeof buildStreets==='function'){
      buildStreets();
      setTimeout(function(){
        try{
          if(typeof window._streets==='undefined' || !window._streets){
            buildStreets();
          } else {
            var _in = false;
            if(typeof scene!=='undefined'){
              scene.traverse(function(o){ if(o===window._streets) _in = true; });
              if(!_in) scene.add(window._streets);
            }
          }
        }catch(e){}
      }, 4000);
    }
  }catch(e){}
}
if(typeof window!=='undefined'){
  window.addEventListener('load', function(){ setTimeout(ensurePermanentStreets, 1500); });
  setTimeout(function(){ try{ ensurePermanentStreets(); }catch(e){} }, 2000);
}
function buildStreets(){
  try{
    // NEVER replace on startup/restart — only build if missing (King's law)
    if(window._streets){
      // already there — make sure it's in the scene, don't rebuild
      var _in = false;
      if(typeof scene!=='undefined'){
        scene.traverse(function(o){ if(o===window._streets) _in = true; });
        if(!_in) scene.add(window._streets);
      }
      return;
    }
    var g = new THREE.Group();
    var roadMat = new THREE.MeshStandardMaterial({color:0x1a1a1a, roughness:0.85, metalness:0.1});
    var lineMat = new THREE.MeshStandardMaterial({color:0xffd700, emissive:0xffd700, emissiveIntensity:0.8});
    var edgeMat = new THREE.MeshStandardMaterial({color:0xffffff, emissive:0xffffff, emissiveIntensity:0.3});
    var dashMat = new THREE.MeshBasicMaterial({color:0xf5f5f5});
    var plazaMat = new THREE.MeshStandardMaterial({color:0x3a3a3a, roughness:0.8});
    // helper: curved road from points
    function curvedRoad(points, width){
      var curve = new THREE.CatmullRomCurve3(points.map(function(p, idx){
        // realistic elevation: base 10 + rolling hills
        // NOTE: geo.scale(1,0.1,1) below divides Y by 10, so build 10x high
        var _d = Math.sqrt(p[0]*p[0]+p[1]*p[1]);
        var _h = (10 + Math.sin(_d*0.02)*4 + Math.sin(_d*0.05+1.3)*2) * 10;
        return new THREE.Vector3(p[0], _h, p[1]);
      })); /* highways rise and fall — not flat */
      var geo = new THREE.TubeGeometry(curve, 64, width/2, 8, false);
      // flatten the tube to make it a road
      geo.scale(1, 0.1, 1);
      var road = new THREE.Mesh(geo, roadMat);
      road.name = 'street-curve';
      g.add(road);
      // gold center line
      var lineGeo = new THREE.TubeGeometry(curve, 64, 0.1, 6, false);
      lineGeo.scale(1, 0.15, 1);
      var line = new THREE.Mesh(lineGeo, lineMat);
      line.position.y = 10.2; /* above the road */
      line.name = 'street-line';
      g.add(line);
      /* ROAD DESIGN — white edge lines that follow the curve properly */
      try{
        [-1, 1].forEach(function(s){
          /* build offset curve by sampling normals along the path */
          var edgePts = [];
          var segs = 64;
          for(var ei=0; ei<=segs; ei++){
            var t = ei/segs;
            var pt = curve.getPoint(t);
            var tan = curve.getTangent(t);
            var nx = -tan.z, nz = tan.x;
            var nlen = Math.sqrt(nx*nx + nz*nz) || 1;
            var off = s 