<!-- RUBY-BACKUP · part 550 -->
y+gz*scale;
        ctx.fillRect(px, py, Math.max(1,step*scale), Math.max(1,step*scale));
      }
    }
    // contour rings every 4 units of elevation
    ctx.strokeStyle = 'rgba(216,169,32,0.3)';
    ctx.lineWidth = 1;
    for(var e=-12; e<=12; e+=4){
      ctx.beginPath();
      var first = true;
      for(var a=0; a<=Math.PI*2+0.1; a+=0.1){
        // approximate contour as deformed circle
        var rad = 300 + e*20;
        var wx = Math.cos(a)*rad, wz = Math.sin(a)*rad;
        // nudge by terrain
        var hh = getTerrainH(wx, wz);
        var adj = (hh-e)*8;
        wx += Math.cos(a)*adj; wz += Math.sin(a)*adj;
        var px2 = cx+wx*scale, py2 = cy+wz*scale;
        if(first){ ctx.moveTo(px2,py2); first=false; }
        else ctx.lineTo(px2,py2);
      }
      ctx.stroke();
    }
    // highways
    ctx.strokeStyle = '#666';
    ctx.lineWidth = 3;
    if(typeof _highwayPts!=='undefined'){
      _highwayPts.forEach(function(pts){
        ctx.beginPath();
        pts.forEach(function(p, i){
          var px = cx+p[0]*scale, py = cy+p[1]*scale;
          if(i===0) ctx.moveTo(px,py); else ctx.lineTo(px,py);
        });
        ctx.stroke();
      });
    }
    // heatmap — where he's been
    if(_metrics.positions.length>0){
      _metrics.positions.forEach(function(p){
        var px = cx+p.x*scale, py = cy+p.z*scale;
        var grad = ctx.createRadialGradient(px,py,0,px,py,8);
        grad.addColorStop(0, 'rgba(255,0,0,0.6)');
        grad.addColorStop(1, 'rgba(255,0,0,0)');
        ctx.fillStyle = grad;
        ctx.beginPath();
        ctx.arc(px, py, 8, 0, Math.PI*2);
        ctx.fill();
      });
    }
    // player
    if(typeof mePos!=='undefined'){
      ctx.fillStyle = '#00ff00';
      ctx.beginPath();
      ctx.arc(cx+mePos.x*scale, cy+mePos.z*scale, 5, 0, Math.PI*2);
      ctx.fill();
    }
    // legend
    var legend = document.createElement('div');
    legend.innerHTML = '<span style="color:#ff0000">\u25cf</span> where you have been &nbsp; <span style="color:#00ff00">\u25cf</span> you are &nbsp; <span style="color:#888">\u2014</span> highways';
    legend.style.cssText = 'color:#aaa;font-size:12px;margin-top:10px;font-family:sans-serif;';
    ov.appendChild(legend);
    var hint = document.createElement('div');
    hint.innerHTML = 'tap anywhere to close • type "topo" to reopen';
    hint.style.cssText = 'color:#666;font-size:11px;margin-top:5px;font-family:sans-serif;';
    ov.appendChild(hint);
    ov.addEventListener('click', function(){ ov.remove(); });
    document.body.appendChild(ov);
    if(typeof codeStatus==='function') codeStatus('topographic map — '+_metrics.positions.length+' tracks');
  }catch(e){
    if(typeof codeStatus==='function') codeStatus('topo failed');
  }
}
function showMetrics(){
  try{
    if(typeof codeStatus==='function')
      codeStatus('metrics: '+_metrics.positions.length+' positions tracked, '+_metrics.fixes+' auto-fixes');
  }catch(e){}
}
/* HIGHWAY GUARDIAN — nothing builds on highways except power lines */
var _highwayPts = [
  [[12,0],[50,15],[100,10],[200,30],[400,20],[600,40],[800,60],[1000,50],[1200,70]],
  [[12,0],[40,-25],[90,-40],[180,-35],[350,-50],[550,-40],[750,-60],[950,-50],[1150,-70]],
  [[0,12],[-15,50],[-10,100],[-30,200],[-20,400],[-40,600],[-60,800],[-50,1000],[-70,1200]],
  [[0,12],[20,55],[15,110],[35,220],[30,420],[50,600],[60,800],[70,1000],[80,1180]],
  [[-12,0],[-50,-15],[-100,-10],[-200,-30],[-400,-20],[-600,-40],[-800,-60],[-1000,-50],[-1200,-70]],
  [[0,-12],[15,-50],[10,-100],[30,-200],[20,-400],[40,-600],[50,-800],[60,-1000],[70,-1200]],
  [[60,60],[120,110],[200,100],[280,150],[220,220],[140,200],[80,240]]
];
function isOnHighway(x, z){
  try{
    for(var r=0; r<_highwayPts.length; r++){
      var pts = _highwayPts[r];
      for(var i=0; i<pts.length-1; i++){
        var ax=pts[i][0], az=pts[i][1], bx=pts[i+1][0], bz=pts[i+1][1];
        var dx=bx-ax, dz=bz-az;
        var len2 = dx*dx+dz*dz;
        var t = len2>0 ? Math.max(0, Math.min(1, ((x-ax)*dx+(z-az)*dz)/len2)) : 0;
        var px=ax+dx*t, pz=az+dz*t;
        var dist = Math.sqrt((x-px)*(x-px)+(z-pz)*(z-pz));
        if(dist < 8) return true;
      }
    }
  }catch(e){}
  return false;
}
var _buildingPower = false;
function buildPowerline(tl){
  try{
    _buildingPower = true;
    var parts = (tl||'').split(' ');
    var count = parseInt(parts[1]) || 10;
    var pts = _highwayPts[0];
    var g = new THREE.Group();
    var poleMat = new THREE.MeshStandardMaterial({color:0x4a3728, roughness:0.8});
    var wireMat = new THREE.MeshBasicMaterial({color:0x222222});
    var prevTop = null;
    for(var i=0; i<count && i<pts.length; i++){
      var px = pts[i][0] + 10, pz = pts[i][1] + 10;
      var pole = new THREE.Mesh(new THREE.CylinderGeometry(0.3, 0.4, 12, 8), poleMat);
      pole.position.set(px, 6, pz);
      pole.name = 'power-pole';
      g.add(pole);
      var arm = new THREE.Mesh(new THREE.BoxGeometry(4, 0.3, 0.3), poleMat);
