← Canyon Run results

Canyon Run

Gemini 3.6 Flash · full

Initial view of Canyon Run

Browser check: Artifact runtime error. This is a technical check, not a quality review.

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 48,065 bytes · SHA-256 9fa5da528354
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <title>CYBER SUNSET: CANYON OVERDRIVE</title>
  <!-- Three.js CDN -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
  <style>
    * {
      box-sizing: border-box;
      user-select: none;
      -webkit-user-select: none;
      margin: 0;
      padding: 0;
    }
    html, body {
      width: 100%;
      height: 100%;
      overflow: hidden;
      background-color: #05020a;
      font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
      color: #fff;
    }

    #canvas-container {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 1;
    }

    /* HUD Overlay */
    #hud {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 10;
      pointer-events: none;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      padding: 24px;
      box-sizing: border-box;
    }

    .hud-top {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
    }

    .hud-card {
      background: rgba(15, 8, 28, 0.65);
      border: 1px solid rgba(255, 120, 50, 0.3);
      box-shadow: 0 0 20px rgba(255, 90, 0, 0.15), inset 0 0 10px rgba(255, 120, 50, 0.1);
      backdrop-filter: blur(8px);
      -webkit-backdrop-filter: blur(8px);
      border-radius: 12px;
      padding: 12px 20px;
      display: flex;
      flex-direction: column;
    }

    .score-label, .stat-label {
      font-size: 11px;
      letter-spacing: 2px;
      text-transform: uppercase;
      color: #ffaa55;
      margin-bottom: 2px;
    }

    .score-value {
      font-size: 32px;
      font-weight: 800;
      font-family: monospace;
      color: #ffffff;
      text-shadow: 0 0 12px rgba(255, 170, 85, 0.8);
    }

    .multiplier-badge {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      background: linear-gradient(135deg, #ff0055, #ff5500);
      color: #fff;
      font-weight: 900;
      font-size: 18px;
      padding: 4px 12px;
      border-radius: 20px;
      margin-left: 10px;
      box-shadow: 0 0 15px rgba(255, 0, 85, 0.6);
      transition: transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    .multiplier-badge.pulse {
      transform: scale(1.35);
    }

    .near-miss-banner {
      position: absolute;
      top: 18%;
      left: 50%;
      transform: translate(-50%, -50%) scale(0.8);
      opacity: 0;
      color: #00ffff;
      font-size: 28px;
      font-weight: 900;
      letter-spacing: 4px;
      text-transform: uppercase;
      text-shadow: 0 0 20px #00ffff, 0 0 40px #00ffff;
      transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
      pointer-events: none;
      white-space: nowrap;
    }

    .near-miss-banner.show {
      opacity: 1;
      transform: translate(-50%, -50%) scale(1.1);
    }

    /* Bottom Gauges */
    .hud-bottom {
      display: flex;
      justify-content: space-between;
      align-items: flex-end;
      width: 100%;
    }

    .gauge-group {
      display: flex;
      flex-direction: column;
      gap: 12px;
      width: 280px;
    }

    .gauge-container {
      display: flex;
      flex-direction: column;
      gap: 4px;
    }

    .gauge-header {
      display: flex;
      justify-content: space-between;
      font-size: 11px;
      letter-spacing: 1.5px;
      color: #d1bfff;
      font-weight: 600;
    }

    .gauge-bar-bg {
      width: 100%;
      height: 12px;
      background: rgba(20, 10, 35, 0.8);
      border: 1px solid rgba(255, 255, 255, 0.15);
      border-radius: 6px;
      overflow: hidden;
      padding: 2px;
      box-shadow: inset 0 0 6px rgba(0,0,0,0.8);
    }

    .gauge-fill {
      height: 100%;
      border-radius: 4px;
      width: 100%;
      transition: width 0.1s linear, background-color 0.3s ease;
    }

    #energy-fill {
      background: linear-gradient(90deg, #ff0055, #ffaa00, #00ffaa);
      box-shadow: 0 0 10px #00ffaa;
    }

    #energy-fill.warning {
      background: #ff0044;
      box-shadow: 0 0 15px #ff0044;
      animation: pulse-red 0.5s infinite alternate;
    }

    #boost-fill {
      background: linear-gradient(90deg, #0088ff, #00ffff);
      box-shadow: 0 0 10px #00ffff;
    }

    @keyframes pulse-red {
      0% { opacity: 0.6; }
      100% { opacity: 1; }
    }

    .speedometer {
      text-align: right;
    }

    .speed-number {
      font-size: 48px;
      font-weight: 900;
      font-family: monospace;
      line-height: 1;
      color: #ffffff;
      text-shadow: 0 0 15px rgba(255, 140, 0, 0.6);
    }

    .speed-unit {
      font-size: 12px;
      letter-spacing: 3px;
      color: #ff9933;
    }

    /* Screen Overlays (Start / Pause / Game Over) */
    .overlay-screen {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 20;
      background: radial-gradient(circle at center, rgba(30, 10, 45, 0.85) 0%, rgba(5, 2, 10, 0.95) 100%);
      backdrop-filter: blur(12px);
      -webkit-backdrop-filter: blur(12px);
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      padding: 30px;
      text-align: center;
      opacity: 1;
      transition: opacity 0.4s ease;
    }

    .overlay-screen.hidden {
      opacity: 0;
      pointer-events: none;
    }

    .game-title {
      font-size: 52px;
      font-weight: 900;
      letter-spacing: 6px;
      background: linear-gradient(135deg, #fff 20%, #ffaa00 60%, #ff0055 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      text-shadow: 0 0 30px rgba(255, 120, 0, 0.5);
      margin-bottom: 8px;
      text-transform: uppercase;
    }

    .game-subtitle {
      font-size: 14px;
      letter-spacing: 4px;
      color: #00ffff;
      margin-bottom: 36px;
      text-transform: uppercase;
    }

    .btn {
      pointer-events: auto;
      background: linear-gradient(135deg, #ff5500, #ff0055);
      border: none;
      color: #fff;
      font-size: 18px;
      font-weight: 800;
      letter-spacing: 3px;
      padding: 16px 42px;
      border-radius: 40px;
      cursor: pointer;
      box-shadow: 0 0 25px rgba(255, 50, 0, 0.6);
      transition: all 0.2s ease;
      text-transform: uppercase;
      outline: none;
    }

    .btn:hover {
      transform: scale(1.06);
      box-shadow: 0 0 35px rgba(255, 50, 0, 0.9);
      background: linear-gradient(135deg, #ff7700, #ff0077);
    }

    .btn:active {
      transform: scale(0.98);
    }

    .controls-guide {
      margin-top: 36px;
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
      gap: 16px;
      max-width: 520px;
      width: 100%;
    }

    .control-card {
      background: rgba(255, 255, 255, 0.05);
      border: 1px solid rgba(255, 255, 255, 0.1);
      border-radius: 10px;
      padding: 12px 8px;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 6px;
    }

    .key-badge {
      background: rgba(255, 255, 255, 0.15);
      border: 1px solid rgba(255, 255, 255, 0.3);
      border-radius: 6px;
      padding: 4px 10px;
      font-size: 12px;
      font-weight: bold;
      font-family: monospace;
      color: #00ffff;
    }

    .control-desc {
      font-size: 11px;
      color: #bbb;
      letter-spacing: 1px;
    }

    .stats-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 16px;
      margin: 24px 0 32px 0;
      width: 100%;
      max-width: 400px;
    }

    .stat-box {
      background: rgba(255, 255, 255, 0.04);
      border: 1px solid rgba(255, 120, 50, 0.2);
      border-radius: 10px;
      padding: 14px;
    }

    .stat-num {
      font-size: 26px;
      font-weight: 800;
      color: #fff;
      font-family: monospace;
    }

    /* Sound Button */
    .audio-btn {
      position: absolute;
      top: 24px;
      right: 24px;
      z-index: 30;
      pointer-events: auto;
      background: rgba(15, 8, 28, 0.7);
      border: 1px solid rgba(255, 255, 255, 0.2);
      color: #fff;
      width: 42px;
      height: 42px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      font-size: 18px;
      backdrop-filter: blur(6px);
      transition: all 0.2s ease;
    }

    .audio-btn:hover {
      background: rgba(255, 120, 50, 0.4);
      border-color: #ffaa00;
    }

    /* Mobile Controls Overlay */
    #touch-controls {
      position: absolute;
      bottom: 20px;
      left: 0;
      width: 100%;
      height: 180px;
      z-index: 15;
      pointer-events: none;
      display: none;
    }

    @media (hover: none) and (pointer: coarse) {
      #touch-controls {
        display: block;
      }
    }
  </style>
</head>
<body>

  <div id="canvas-container"></div>

  <!-- HUD Overlay -->
  <div id="hud">
    <div class="hud-top">
      <div class="hud-card">
        <div class="score-label">Score</div>
        <div style="display: flex; align-items: center;">
          <div class="score-value" id="hud-score">0</div>
          <div class="multiplier-badge" id="hud-multiplier">x1</div>
        </div>
      </div>

      <div class="hud-card" style="align-items: flex-end;">
        <div class="stat-label">High Score</div>
        <div class="score-value" id="hud-highscore" style="font-size: 22px; color: #ffaa00;">0</div>
      </div>
    </div>

    <div class="near-miss-banner" id="near-miss-banner">NEAR MISS +250</div>

    <div class="hud-bottom">
      <div class="gauge-group">
        <div class="gauge-container">
          <div class="gauge-header">
            <span>ENERGY / FUEL</span>
            <span id="energy-text">100%</span>
          </div>
          <div class="gauge-bar-bg">
            <div class="gauge-fill" id="energy-fill" style="width: 100%;"></div>
          </div>
        </div>

        <div class="gauge-container">
          <div class="gauge-header">
            <span>TURBO BOOST [SPACE]</span>
            <span id="boost-text">READY</span>
          </div>
          <div class="gauge-bar-bg">
            <div class="gauge-fill" id="boost-fill" style="width: 100%;"></div>
          </div>
        </div>
      </div>

      <div class="speedometer">
        <div class="speed-number" id="hud-speed">120</div>
        <div class="speed-unit">KM / H</div>
      </div>
    </div>
  </div>

  <button class="audio-btn" id="audio-toggle" title="Toggle Audio">🔊</button>

  <!-- Start Screen Overlay -->
  <div class="overlay-screen" id="start-screen">
    <div class="game-title">CYBER SUNSET</div>
    <div class="game-subtitle">CANYON OVERDRIVE</div>

    <button class="btn" id="start-btn">ENGINE LAUNCH</button>

    <div class="controls-guide">
      <div class="control-card">
        <span class="key-badge">A / D or ← / →</span>
        <span class="control-desc">BANK & ROLL</span>
      </div>
      <div class="control-card">
        <span class="key-badge">W / S or ↑ / ↓</span>
        <span class="control-desc">PITCH ELEVATION</span>
      </div>
      <div class="control-card">
        <span class="key-badge">SPACE</span>
        <span class="control-desc">TURBO BOOST</span>
      </div>
      <div class="control-card">
        <span class="key-badge">CLOSE FLIGHT</span>
        <span class="control-desc">NEAR MISS MULTIPLIER</span>
      </div>
    </div>
  </div>

  <!-- Game Over Overlay -->
  <div class="overlay-screen hidden" id="gameover-screen">
    <div class="game-title" style="font-size: 42px; background: linear-gradient(135deg, #ff0055, #ff5500);">CRASH DETECTED</div>
    <div class="game-subtitle" id="gameover-cause">ENERGY DEPLETED</div>

    <div class="stats-grid">
      <div class="stat-box">
        <div class="stat-label">FINAL SCORE</div>
        <div class="stat-num" id="go-score">0</div>
      </div>
      <div class="stat-box">
        <div class="stat-label">MAX SPEED</div>
        <div class="stat-num" id="go-speed">0</div>
      </div>
      <div class="stat-box">
        <div class="stat-label">NEAR MISSES</div>
        <div class="stat-num" id="go-nearmiss">0</div>
      </div>
      <div class="stat-box">
        <div class="stat-label">ORBS COLLECTED</div>
        <div class="stat-num" id="go-orbs">0</div>
      </div>
    </div>

    <button class="btn" id="restart-btn">REDEPLOY CRAFT</button>
  </div>

  <!-- JavaScript Logic -->
  <script>
    // --- STANDALONE / CDN FALLBACK SHIM FOR THREE.JS IF OFFLINE ---
    (function ensureThreeLoaded() {
      if (typeof THREE !== 'undefined') return;
      console.warn("Three.js not found from CDN. Initializing lightweight WebGL fallback stub...");

      // Lightweight THREE stub so game logic runs smoothly without breaking
      window.THREE = {
        Vector3: class {
          constructor(x=0,y=0,z=0){this.x=x;this.y=y;this.z=z;}
          set(x,y,z){this.x=x;this.y=y;this.z=z;return this;}
          copy(v){this.x=v.x;this.y=v.y;this.z=v.z;return this;}
          clone(){return new window.THREE.Vector3(this.x,this.y,this.z);}
          add(v){this.x+=v.x;this.y+=v.y;this.z+=v.z;return this;}
          sub(v){this.x-=v.x;this.y-=v.y;this.z-=v.z;return this;}
          multiplyScalar(s){this.x*=s;this.y*=s;this.z*=s;return this;}
          distanceTo(v){let dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return Math.sqrt(dx*dx+dy*dy+dz*dz);}
          length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z);}
          lerp(v,t){this.x+=(v.x-this.x)*t;this.y+=(v.y-this.y)*t;this.z+=(v.z-this.z)*t;return this;}
        },
        Euler: class {
          constructor(x=0,y=0,z=0){this.x=x;this.y=y;this.z=z;}
          set(x,y,z){this.x=x;this.y=y;this.z=z;return this;}
        },
        Color: class {
          constructor(c){this.hex=c||0xffffff;}
          setHex(h){this.hex=h;return this;}
        },
        Box3: class {
          constructor(min, max){
            this.min = min || new window.THREE.Vector3(-1,-1,-1);
            this.max = max || new window.THREE.Vector3(1,1,1);
          }
          setFromObject(obj){
            if(obj.position){
              let p = obj.position, s = obj.scale || {x:1,y:1,z:1};
              this.min.set(p.x - s.x, p.y - s.y, p.z - s.z);
              this.max.set(p.x + s.x, p.y + s.y, p.z + s.z);
            }
            return this;
          }
          intersectsBox(b){
            return !(this.max.x < b.min.x || this.min.x > b.max.x ||
                     this.max.y < b.min.y || this.min.y > b.max.y ||
                     this.max.z < b.min.z || this.min.z > b.max.z);
          }
        },
        Scene: class {
          constructor(){this.children=[];this.fog=null;}
          add(o){this.children.push(o);if(o.parent)o.parent=this;}
          remove(o){let i=this.children.indexOf(o);if(i>=0)this.children.splice(i,1);}
        },
        PerspectiveCamera: class {
          constructor(fov, aspect, near, far){
            this.fov=fov;this.aspect=aspect;this.near=near;this.far=far;
            this.position=new window.THREE.Vector3(0,0,0);
            this.rotation=new window.THREE.Euler(0,0,0);
          }
          lookAt(x,y,z){if(typeof x==='object'){y=x.y;z=x.z;x=x.x;}this.target={x,y,z};}
        },
        WebGLRenderer: class {
          constructor(opts={}){
            this.domElement = document.createElement('canvas');
            this.domElement.id = "fallback-canvas";
            this.domElement.style.width="100%";
            this.domElement.style.height="100%";
            this.ctx = this.domElement.getContext('2d');
            this.shadowMap = {enabled:false, type:0};
          }
          setSize(w,h){this.domElement.width=w;this.domElement.height=h;}
          setPixelRatio(r){}
          render(scene, camera){
            if(!this.ctx) return;
            let w=this.domElement.width, h=this.domElement.height;
            this.ctx.fillStyle = "#0c0414";
            this.ctx.fillRect(0,0,w,h);
            // Render basic gradient horizon for fallback
            let grad = this.ctx.createLinearGradient(0,0,0,h);
            grad.addColorStop(0, '#0a0312');
            grad.addColorStop(0.5, '#4a154b');
            grad.addColorStop(0.7, '#ff5500');
            grad.addColorStop(1, '#1a0525');
            this.ctx.fillStyle = grad;
            this.ctx.fillRect(0,0,w,h);
            
            // Draw glowing synthwave sun in center horizon
            this.ctx.beginPath();
            this.ctx.arc(w/2, h*0.6, 80, 0, Math.PI*2);
            let sunGrad = this.ctx.createRadialGradient(w/2, h*0.6, 10, w/2, h*0.6, 90);
            sunGrad.addColorStop(0, '#ffff55');
            sunGrad.addColorStop(0.5, '#ff5500');
            sunGrad.addColorStop(1, 'rgba(255,0,85,0)');
            this.ctx.fillStyle = sunGrad;
            this.ctx.fill();
          }
        },
        Group: class {
          constructor(){this.children=[];this.position=new window.THREE.Vector3();this.rotation=new window.THREE.Euler();this.scale=new window.THREE.Vector3(1,1,1);}
          add(o){this.children.push(o);}
          remove(o){let i=this.children.indexOf(o);if(i>=0)this.children.splice(i,1);}
        },
        Mesh: class {
          constructor(geo, mat){
            this.geometry=geo;this.material=mat;
            this.position=new window.THREE.Vector3();
            this.rotation=new window.THREE.Euler();
            this.scale=new window.THREE.Vector3(1,1,1);
            this.castShadow=false;this.receiveShadow=false;
          }
        },
        BoxGeometry: class { constructor(w,h,d){this.w=w;this.h=h;this.d=d;} },
        ConeGeometry: class { constructor(r,h,s){this.r=r;this.h=h;} },
        OctahedronGeometry: class { constructor(r){this.r=r;} },
        BufferGeometry: class { constructor(){this.attributes={};} setAttribute(){}},
        Float32BufferAttribute: class { constructor(a,s){this.array=a;this.itemSize=s;} },
        Points: class { constructor(g,m){this.geometry=g;this.material=m;this.position=new window.THREE.Vector3();} },
        PointsMaterial: class { constructor(p){Object.assign(this,p);} },
        MeshStandardMaterial: class { constructor(p){Object.assign(this,p);} },
        MeshBasicMaterial: class { constructor(p){Object.assign(this,p);} },
        AmbientLight: class { constructor(c,i){this.color=c;this.intensity=i;} },
        DirectionalLight: class { constructor(c,i){this.color=c;this.intensity=i;this.position=new window.THREE.Vector3();this.shadow={mapSize:{width:0,height:0},camera:{near:0,far:0,left:0,right:0,top:0,bottom:0}};} },
        PointLight: class { constructor(c,i,d){this.color=c;this.intensity=i;this.distance=d;this.position=new window.THREE.Vector3();} },
        FogExp2: class { constructor(c,d){this.color=c;this.density=d;} },
        PCFSoftShadowMap: 1
      };
    })();

    // --- AUDIO SYNTHESIZER ENGINE (Web Audio API) ---
    class SoundEngine {
      constructor() {
        this.ctx = null;
        this.muted = false;
        this.engineGain = null;
        this.engineOsc = null;
        this.engineFilter = null;
      }

      init() {
        if (this.ctx) return;
        const AudioCtx = window.AudioContext || window.webkitAudioContext;
        if (!AudioCtx) return;
        this.ctx = new AudioCtx();

        // Continuous Engine Hum Oscillator
        this.engineOsc = this.ctx.createOscillator();
        this.engineOsc.type = 'sawtooth';
        this.engineOsc.frequency.setValueAtTime(55, this.ctx.currentTime); // Low pitch A1

        this.engineFilter = this.ctx.createBiquadFilter();
        this.engineFilter.type = 'lowpass';
        this.engineFilter.frequency.setValueAtTime(180, this.ctx.currentTime);

        this.engineGain = this.ctx.createGain();
        this.engineGain.gain.setValueAtTime(0.12, this.ctx.currentTime);

        this.engineOsc.connect(this.engineFilter);
        this.engineFilter.connect(this.engineGain);
        this.engineGain.connect(this.ctx.destination);

        this.engineOsc.start();
      }

      updateEngineSound(speedRatio, isBoosting) {
        if (!this.ctx || this.muted) return;
        const targetFreq = 50 + speedRatio * 180 + (isBoosting ? 90 : 0);
        const filterFreq = 160 + speedRatio * 600 + (isBoosting ? 400 : 0);
        
        this.engineOsc.frequency.setTargetAtTime(targetFreq, this.ctx.currentTime, 0.05);
        this.engineFilter.frequency.setTargetAtTime(filterFreq, this.ctx.currentTime, 0.05);
      }

      playOrbPickup() {
        if (!this.ctx || this.muted) return;
        const t = this.ctx.currentTime;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();

        osc.type = 'sine';
        osc.frequency.setValueAtTime(523.25, t); // C5
        osc.frequency.exponentialRampToValueAtTime(1046.50, t + 0.15); // C6

        gain.gain.setValueAtTime(0.25, t);
        gain.gain.exponentialRampToValueAtTime(0.001, t + 0.25);

        osc.connect(gain);
        gain.connect(this.ctx.destination);

        osc.start(t);
        osc.stop(t + 0.25);
      }

      playNearMiss() {
        if (!this.ctx || this.muted) return;
        const t = this.ctx.currentTime;
        const osc = this.ctx.createOscillator();
        const filter = this.ctx.createBiquadFilter();
        const gain = this.ctx.createGain();

        osc.type = 'triangle';
        osc.frequency.setValueAtTime(220, t);
        osc.frequency.exponentialRampToValueAtTime(880, t + 0.18);

        filter.type = 'bandpass';
        filter.frequency.setValueAtTime(600, t);
        filter.Q.setValueAtTime(8, t);

        gain.gain.setValueAtTime(0.3, t);
        gain.gain.exponentialRampToValueAtTime(0.001, t + 0.22);

        osc.connect(filter);
        filter.connect(gain);
        gain.connect(this.ctx.destination);

        osc.start(t);
        osc.stop(t + 0.22);
      }

      playCrash() {
        if (!this.ctx || this.muted) return;
        const t = this.ctx.currentTime;
        
        // Noise buffer for explosion
        const bufferSize = this.ctx.sampleRate * 0.8;
        const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
        const data = buffer.getChannelData(0);
        for (let i = 0; i < bufferSize; i++) {
          data[i] = Math.random() * 2 - 1;
        }

        const noise = this.ctx.createBufferSource();
        noise.buffer = buffer;

        const filter = this.ctx.createBiquadFilter();
        filter.type = 'lowpass';
        filter.frequency.setValueAtTime(800, t);
        filter.frequency.exponentialRampToValueAtTime(40, t + 0.8);

        const gain = this.ctx.createGain();
        gain.gain.setValueAtTime(0.6, t);
        gain.gain.exponentialRampToValueAtTime(0.001, t + 0.8);

        noise.connect(filter);
        filter.connect(gain);
        gain.connect(this.ctx.destination);

        noise.start(t);
      }

      toggleMute() {
        this.muted = !this.muted;
        if (this.engineGain) {
          this.engineGain.gain.setValueAtTime(this.muted ? 0 : 0.12, this.ctx ? this.ctx.currentTime : 0);
        }
        return this.muted;
      }
    }

    const sound = new SoundEngine();

    // --- GAME ENGINE & THREE.JS SETUP ---
    let container, scene, camera, renderer;
    let playerCraft, sunMesh;
    let speedStardust;

    const STATE = {
      MENU: 'MENU',
      PLAYING: 'PLAYING',
      GAMEOVER: 'GAMEOVER'
    };
    let currentState = STATE.MENU;

    // Game Physics & Stats Variables
    const game = {
      score: 0,
      highScore: parseInt(localStorage.getItem('solar_runner_hi') || '0', 10),
      multiplier: 1,
      nearMissCount: 0,
      orbsCollected: 0,
      distance: 0,
      speed: 70,           // current forward speed units/sec
      baseSpeed: 70,
      maxSpeed: 170,
      energy: 100,         // 0 to 100
      boost: 100,          // boost meter
      isBoosting: false,
      cameraShake: 0,
      time: 0
    };

    // Controls state
    const keys = {
      left: false,
      right: false,
      up: false,
      down: false,
      boost: false
    };

    // Player Flight Kinematics
    const playerPhysics = {
      pos: new THREE.Vector3(0, 4, 0),
      vel: new THREE.Vector3(0, 0, 0),
      roll: 0,        // Current roll angle
      pitch: 0,       // Current pitch angle
      yaw: 0,         // Yaw angle
      targetRoll: 0,
      targetPitch: 0,
      bounds: new THREE.Box3()
    };

    // Procedural Canyon Segment Manager
    const SEGMENT_LENGTH = 120;
    const TOTAL_SEGMENTS = 12;
    let segments = [];
    let activeOrbs = [];
    let activeObstacles = [];
    let particles = [];

    // Initialize WebGL Application
    function initApp() {
      container = document.getElementById('canvas-container');

      scene = new THREE.Scene();
      scene.fog = new THREE.FogExp2(0x180528, 0.0035);

      camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1200);
      camera.position.set(0, 8, 14);

      renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
      if (renderer.shadowMap) {
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      }
      container.appendChild(renderer.domElement);

      // Lighting Setup (Sunset Atmosphere)
      const ambientLight = new THREE.AmbientLight(0x4a2060, 1.2);
      scene.add(ambientLight);

      const sunLight = new THREE.DirectionalLight(0xff7722, 2.5);
      sunLight.position.set(0, 20, -500);
      if (sunLight.shadow) {
        sunLight.castShadow = true;
        sunLight.shadow.mapSize.width = 1024;
        sunLight.shadow.mapSize.height = 1024;
        sunLight.shadow.camera.near = 10;
        sunLight.shadow.camera.far = 800;
        sunLight.shadow.camera.left = -100;
        sunLight.shadow.camera.right = 100;
        sunLight.shadow.camera.top = 100;
        sunLight.shadow.camera.bottom = -50;
      }
      scene.add(sunLight);

      // Cyber Sun Disk on Horizon
      const sunGeo = new THREE.OctahedronGeometry(45, 3);
      const sunMat = new THREE.MeshBasicMaterial({ color: 0xffaa00, wireframe: false });
      sunMesh = new THREE.Mesh(sunGeo, sunMat);
      sunMesh.position.set(0, 15, -700);
      scene.add(sunMesh);

      // Build Player Craft
      createPlayerCraft();

      // Build Environmental Stardust Speed Particles
      createSpeedStardust();

      // Spawn Initial Canyon Segments
      buildInitialCanyon();

      // Setup Event Listeners
      window.addEventListener('resize', onWindowResize, false);
      setupControls();
      updateHUD();

      // Start Render Loop
      requestAnimationFrame(gameLoop);
    }

    // --- PLAYER CRAFT GEOMETRY & MATERIAL ---
    function createPlayerCraft() {
      playerCraft = new THREE.Group();

      // Fuselage / Main Body
      const bodyGeo = new THREE.ConeGeometry(1.2, 5, 5);
      bodyGeo.rotateX(Math.PI / 2);
      const bodyMat = new THREE.MeshStandardMaterial({
        color: 0x111625,
        roughness: 0.2,
        metalness: 0.8,
        flatShading: true
      });
      const bodyMesh = new THREE.Mesh(bodyGeo, bodyMat);
      bodyMesh.castShadow = true;
      playerCraft.add(bodyMesh);

      // Swept Delta Wings
      const wingShape = new THREE.Group();
      const wingGeo = new THREE.BoxGeometry(7, 0.15, 2.2);
      const wingMat = new THREE.MeshStandardMaterial({
        color: 0xff3300,
        roughness: 0.3,
        metalness: 0.7,
        flatShading: true
      });
      const wings = new THREE.Mesh(wingGeo, wingMat);
      wings.position.set(0, 0, 0.4);
      wings.castShadow = true;
      playerCraft.add(wings);

      // Cockpit Canopy
      const canopyGeo = new THREE.BoxGeometry(0.8, 0.6, 1.8);
      const canopyMat = new THREE.MeshStandardMaterial({
        color: 0x00ffff,
        roughness: 0.1,
        metalness: 0.9,
        flatShading: true
      });
      const canopy = new THREE.Mesh(canopyGeo, canopyMat);
      canopy.position.set(0, 0.4, -0.2);
      playerCraft.add(canopy);

      // Engine Thruster Glow Nodes
      const engineGeo = new THREE.ConeGeometry(0.5, 1.5, 6);
      engineGeo.rotateX(-Math.PI / 2);
      const engineMat = new THREE.MeshBasicMaterial({ color: 0x00ffff });
      const engineMesh = new THREE.Mesh(engineGeo, engineMat);
      engineMesh.position.set(0, 0, 2.2);
      playerCraft.add(engineMesh);

      // Dynamic Ship Point Light
      const shipLight = new THREE.PointLight(0x00ffff, 1.5, 25);
      shipLight.position.set(0, 0, 1);
      playerCraft.add(shipLight);

      scene.add(playerCraft);
    }

    // Environmental Stardust Particles for Speed Feeling
    function createSpeedStardust() {
      const particleCount = 200;
      const geo = new THREE.BufferGeometry();
      const positions = new Float32Array(particleCount * 3);

      for (let i = 0; i < particleCount * 3; i += 3) {
        positions[i] = (Math.random() - 0.5) * 120;
        positions[i + 1] = Math.random() * 40;
        positions[i + 2] = -Math.random() * 400;
      }

      geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
      const mat = new THREE.PointsMaterial({
        color: 0xffaa55,
        size: 0.8,
        transparent: true,
        opacity: 0.6
      });

      speedStardust = new THREE.Points(geo, mat);
      scene.add(speedStardust);
    }

    // --- PROCEDURAL CANYON GENERATION ---
    function getCanyonCurveX(z) {
      // Smooth serpentine canyon winding
      return Math.sin(z * 0.008) * 28 + Math.cos(z * 0.003) * 15;
    }

    function createCanyonSegment(zPos, index) {
      const segmentGroup = new THREE.Group();
      segmentGroup.position.z = zPos;

      const centerX = getCanyonCurveX(zPos);
      const canyonWidth = Math.max(32, 65 - (index * 0.8)); // Gradually narrows with progress
      const wallHeight = 45;

      // Canyon Wall Materials
      const wallMat = new THREE.MeshStandardMaterial({
        color: 0x241138,
        roughness: 0.8,
        metalness: 0.2,
        flatShading: true
      });

      const floorMat = new THREE.MeshStandardMaterial({
        color: 0x0d0518,
        roughness: 0.9,
        flatShading: true
      });

      // Floor Mesh
      const floorGeo = new THREE.BoxGeometry(canyonWidth + 20, 2, SEGMENT_LENGTH);
      const floorMesh = new THREE.Mesh(floorGeo, floorMat);
      floorMesh.position.set(centerX, -1, -SEGMENT_LENGTH / 2);
      floorMesh.receiveShadow = true;
      segmentGroup.add(floorMesh);

      // Left Wall Formation
      const leftWallGeo = new THREE.BoxGeometry(30, wallHeight, SEGMENT_LENGTH);
      const leftWall = new THREE.Mesh(leftWallGeo, wallMat);
      leftWall.position.set(centerX - (canyonWidth / 2) - 15, wallHeight / 2 - 2, -SEGMENT_LENGTH / 2);
      leftWall.castShadow = true;
      leftWall.receiveShadow = true;
      segmentGroup.add(leftWall);

      // Right Wall Formation
      const rightWallGeo = new THREE.BoxGeometry(30, wallHeight, SEGMENT_LENGTH);
      const rightWall = new THREE.Mesh(rightWallGeo, wallMat);
      rightWall.position.set(centerX + (canyonWidth / 2) + 15, wallHeight / 2 - 2, -SEGMENT_LENGTH / 2);
      rightWall.castShadow = true;
      rightWall.receiveShadow = true;
      segmentGroup.add(rightWall);

      // --- OBSTACLES & PROCEDURAL VARIETY ---
      // Random obstacle placement based on index & difficulty
      if (index > 1) {
        const obstacleType = Math.floor(Math.random() * 4);
        const obstacleZ = zPos - SEGMENT_LENGTH * 0.5;

        if (obstacleType === 1) {
          // Monolith Pillar
          const pWidth = 4 + Math.random() * 4;
          const pHeight = 25 + Math.random() * 15;
          const pxOffset = centerX + (Math.random() - 0.5) * (canyonWidth - 12);

          const pillarGeo = new THREE.BoxGeometry(pWidth, pHeight, pWidth);
          const pillarMat = new THREE.MeshStandardMaterial({
            color: 0x3d1c59,
            roughness: 0.4,
            flatShading: true
          });
          const pillar = new THREE.Mesh(pillarGeo, pillarMat);
          pillar.position.set(pxOffset, pHeight / 2, -SEGMENT_LENGTH / 2);
          pillar.castShadow = true;
          pillar.receiveShadow = true;

          segmentGroup.add(pillar);
          activeObstacles.push({ mesh: pillar, pos: new THREE.Vector3(pxOffset, pHeight / 2, obstacleZ), radius: pWidth });
        }
        else if (obstacleType === 2) {
          // Tight Squeeze Arch / Gate
          const gapWidth = 12;
          const gateMat = new THREE.MeshStandardMaterial({ color: 0x5e1943, flatShading: true });

          const leftBar = new THREE.Mesh(new THREE.BoxGeometry(canyonWidth/2, 18, 4), gateMat);
          leftBar.position.set(centerX - gapWidth/2 - canyonWidth/4, 9, -SEGMENT_LENGTH / 2);

          const rightBar = new THREE.Mesh(new THREE.BoxGeometry(canyonWidth/2, 18, 4), gateMat);
          rightBar.position.set(centerX + gapWidth/2 + canyonWidth/4, 9, -SEGMENT_LENGTH / 2);

          segmentGroup.add(leftBar);
          segmentGroup.add(rightBar);

          activeObstacles.push({ mesh: leftBar, pos: new THREE.Vector3(leftBar.position.x, 9, obstacleZ), radius: canyonWidth/4 });
          activeObstacles.push({ mesh: rightBar, pos: new THREE.Vector3(rightBar.position.x, 9, obstacleZ), radius: canyonWidth/4 });
        }
      }

      // --- ENERGY ORB PICKUPS ---
      if (Math.random() < 0.75 && index > 0) {
        const orbGeo = new THREE.OctahedronGeometry(1.2, 0);
        const orbMat = new THREE.MeshBasicMaterial({ color: 0x00ffff, wireframe: false });
        const orbMesh = new THREE.Mesh(orbGeo, orbMat);

        const orbX = centerX + (Math.random() - 0.5) * (canyonWidth - 16);
        const orbY = 2 + Math.random() * 8;
        orbMesh.position.set(orbX, orbY, -SEGMENT_LENGTH / 2);

        // Glowing outer aura ring
        const auraGeo = new THREE.OctahedronGeometry(1.8, 1);
        const auraMat = new THREE.MeshBasicMaterial({ color: 0x00ffff, wireframe: true, transparent: true, opacity: 0.4 });
        const auraMesh = new THREE.Mesh(auraGeo, auraMat);
        orbMesh.add(auraMesh);

        segmentGroup.add(orbMesh);
        activeOrbs.push({
          mesh: orbMesh,
          group: segmentGroup,
          pos: new THREE.Vector3(orbX, orbY, zPos - SEGMENT_LENGTH / 2),
          collected: false
        });
      }

      scene.add(segmentGroup);
      return { group: segmentGroup, zPos: zPos, index: index, centerX: centerX, width: canyonWidth };
    }

    function buildInitialCanyon() {
      for (let i = 0; i < TOTAL_SEGMENTS; i++) {
        const seg = createCanyonSegment(-i * SEGMENT_LENGTH, i);
        segments.push(seg);
      }
    }

    function updateCanyonChunks() {
      const shipZ = playerPhysics.pos.z;

      // Recycle segments behind the player
      for (let i = 0; i < segments.length; i++) {
        const seg = segments[i];
        if (seg.zPos > shipZ + SEGMENT_LENGTH) {
          // Find furthest segment
          let minZ = 0;
          let maxIndex = 0;
          for (let s of segments) {
            if (s.zPos < minZ) minZ = s.zPos;
            if (s.index > maxIndex) maxIndex = s.index;
          }

          scene.remove(seg.group);

          // Spawn recycled segment ahead
          const newZ = minZ - SEGMENT_LENGTH;
          const newSeg = createCanyonSegment(newZ, maxIndex + 1);
          segments[i] = newSeg;
        }
      }

      // Cleanup despawned orbs & obstacles
      activeOrbs = activeOrbs.filter(o => o.pos.z < shipZ + 20 && !o.collected);
      activeObstacles = activeObstacles.filter(ob => ob.pos.z < shipZ + 20);
    }

    // --- CONTROLS & INPUT HANDLING ---
    function setupControls() {
      window.addEventListener('keydown', (e) => {
        if (e.code === 'ArrowLeft' || e.code === 'KeyA') keys.left = true;
        if (e.code === 'ArrowRight' || e.code === 'KeyD') keys.right = true;
        if (e.code === 'ArrowUp' || e.code === 'KeyW') keys.up = true;
        if (e.code === 'ArrowDown' || e.code === 'KeyS') keys.down = true;
        if (e.code === 'Space') keys.boost = true;
      });

      window.addEventListener('keyup', (e) => {
        if (e.code === 'ArrowLeft' || e.code === 'KeyA') keys.left = false;
        if (e.code === 'ArrowRight' || e.code === 'KeyD') keys.right = false;
        if (e.code === 'ArrowUp' || e.code === 'KeyW') keys.up = false;
        if (e.code === 'ArrowDown' || e.code === 'KeyS') keys.down = false;
        if (e.code === 'Space') keys.boost = false;
      });

      // Mouse / Touch Steering Support
      let touchStartX = 0;
      let touchStartY = 0;

      window.addEventListener('touchstart', (e) => {
        if (e.touches.length > 0) {
          touchStartX = e.touches[0].clientX;
          touchStartY = e.touches[0].clientY;
          if (currentState === STATE.PLAYING) sound.init();
        }
      }, { passive: true });

      window.addEventListener('touchmove', (e) => {
        if (e.touches.length > 0 && currentState === STATE.PLAYING) {
          const deltaX = e.touches[0].clientX - touchStartX;
          const deltaY = e.touches[0].clientY - touchStartY;

          keys.left = deltaX < -15;
          keys.right = deltaX > 15;
          keys.up = deltaY < -15;
          keys.down = deltaY > 15;
        }
      }, { passive: true });

      window.addEventListener('touchend', () => {
        keys.left = false; keys.right = false; keys.up = false; keys.down = false;
      });

      // UI Buttons
      document.getElementById('start-btn').addEventListener('click', startGame);
      document.getElementById('restart-btn').addEventListener('click', startGame);

      document.getElementById('audio-toggle').addEventListener('click', () => {
        const isMuted = sound.toggleMute();
        document.getElementById('audio-toggle').innerText = isMuted ? '🔇' : '🔊';
      });
    }

    // --- GAMEPLAY LOOPS & PHYSICS ---
    function startGame() {
      sound.init();

      // Reset Stats
      game.score = 0;
      game.multiplier = 1;
      game.nearMissCount = 0;
      game.orbsCollected = 0;
      game.distance = 0;
      game.speed = game.baseSpeed;
      game.energy = 100;
      game.boost = 100;
      game.isBoosting = false;

      // Reset Player Position
      playerPhysics.pos.set(0, 4, 0);
      playerPhysics.vel.set(0, 0, 0);
      playerPhysics.roll = 0;
      playerPhysics.pitch = 0;

      // Hide Overlays
      document.getElementById('start-screen').classList.add('hidden');
      document.getElementById('gameover-screen').classList.add('hidden');

      currentState = STATE.PLAYING;
    }

    function triggerGameOver(cause) {
      currentState = STATE.GAMEOVER;
      sound.playCrash();

      if (game.score > game.highScore) {
        game.highScore = Math.floor(game.score);
        localStorage.setItem('solar_runner_hi', game.highScore.toString());
      }

      document.getElementById('gameover-cause').innerText = cause || 'CRASH DETECTED';
      document.getElementById('go-score').innerText = Math.floor(game.score);
      document.getElementById('go-speed').innerText = Math.floor(game.maxSpeed) + " KM/H";
      document.getElementById('go-nearmiss').innerText = game.nearMissCount;
      document.getElementById('go-orbs').innerText = game.orbsCollected;

      document.getElementById('gameover-screen').classList.remove('hidden');
    }

    function triggerNearMiss() {
      game.nearMissCount++;
      game.multiplier = Math.min(10, game.multiplier + 1);
      game.score += 250 * game.multiplier;
      game.energy = Math.min(100, game.energy + 10);
      game.cameraShake = 0.4;

      sound.playNearMiss();

      const banner = document.getElementById('near-miss-banner');
      banner.innerText = `NEAR MISS +${250 * game.multiplier}`;
      banner.classList.add('show');
      setTimeout(() => banner.classList.remove('show'), 600);

      const badge = document.getElementById('hud-multiplier');
      badge.classList.add('pulse');
      setTimeout(() => badge.classList.remove('pulse'), 200);
    }

    function updateFlightPhysics(delta) {
      // Lateral Banking & Roll Momentum
      const bankSpeed = 2.8;
      const pitchSpeed = 2.2;

      if (keys.left) playerPhysics.targetRoll = 0.75;
      else if (keys.right) playerPhysics.targetRoll = -0.75;
      else playerPhysics.targetRoll = 0;

      if (keys.up) playerPhysics.targetPitch = -0.4;
      else if (keys.down) playerPhysics.targetPitch = 0.4;
      else playerPhysics.targetPitch = 0;

      // Lerp smooth movement
      playerPhysics.roll += (playerPhysics.targetRoll - playerPhysics.roll) * bankSpeed * delta * 5;
      playerPhysics.pitch += (playerPhysics.targetPitch - playerPhysics.pitch) * pitchSpeed * delta * 5;

      // Calculate Lateral Velocity based on Roll
      playerPhysics.vel.x = -playerPhysics.roll * 22;
      playerPhysics.vel.y = -playerPhysics.pitch * 16;

      // Turbo Boost Management
      if (keys.boost && game.boost > 5) {
        game.isBoosting = true;
        game.boost = Math.max(0, game.boost - delta * 35);
        game.speed = game.baseSpeed * 1.6;
        game.cameraShake = Math.max(game.cameraShake, 0.12);
      } else {
        game.isBoosting = false;
        game.boost = Math.min(100, game.boost + delta * 12);
        game.speed = game.baseSpeed + (game.distance * 0.005);
      }

      if (game.speed > game.maxSpeed) game.maxSpeed = game.speed;

      // Advance Forward Position
      playerPhysics.pos.z -= game.speed * delta;
      playerPhysics.pos.x += playerPhysics.vel.x * delta;
      playerPhysics.pos.y += playerPhysics.vel.y * delta;

      // Clamp altitude
      playerPhysics.pos.y = Math.max(1.5, Math.min(22, playerPhysics.pos.y));

      // Apply transformations to Three.js Craft Mesh
      playerCraft.position.copy(playerPhysics.pos);
      playerCraft.rotation.z = playerPhysics.roll;
      playerCraft.rotation.x = playerPhysics.pitch;

      // Energy Decay
      const energyDecayRate = game.isBoosting ? 8 : 3.5;
      game.energy -= energyDecayRate * delta;

      if (game.energy <= 0) {
        triggerGameOver('ENERGY DEPLETED');
      }

      // Update Sound Engine Pitch
      sound.updateEngineSound(game.speed / game.maxSpeed, game.isBoosting);
    }

    function checkCollisionsAndNearMisses() {
      const shipPos = playerPhysics.pos;
      const currentSegIndex = Math.floor(-shipPos.z / SEGMENT_LENGTH);
      const currentSeg = segments.find(s => s.index === currentSegIndex) || segments[0];

      if (currentSeg) {
        const canyonCenterX = currentSeg.centerX;
        const halfWidth = currentSeg.width / 2;

        const distLeftWall = shipPos.x - (canyonCenterX - halfWidth);
        const distRightWall = (canyonCenterX + halfWidth) - shipPos.x;

        // Collision with Canyon Walls
        if (distLeftWall < 1.8 || distRightWall < 1.8) {
          triggerGameOver('WALL IMPACT');
          return;
        }

        // Near-Miss Proximity Check with Canyon Walls
        if (distLeftWall < 5.5 || distRightWall < 5.5) {
          if (Math.random() < 0.08) {
            triggerNearMiss();
          }
        }
      }

      // Check Obstacle Collisions & Near Misses
      for (let ob of activeObstacles) {
        const dist = shipPos.distanceTo(ob.pos);
        if (dist < ob.radius * 0.8) {
          triggerGameOver('MONOLITH COLLISION');
          return;
        } else if (dist < ob.radius + 4.5 && dist >= ob.radius * 0.8) {
          if (Math.random() < 0.06) triggerNearMiss();
        }
      }

      // Check Orb Pickups
      for (let orb of activeOrbs) {
        if (!orb.collected) {
          orb.mesh.rotation.y += 0.05;
          const dist = shipPos.distanceTo(orb.pos);
          if (dist < 3.2) {
            orb.collected = true;
            orb.mesh.visible = false;
            game.orbsCollected++;
            game.energy = Math.min(100, game.energy + 25);
            game.score += 500 * game.multiplier;
            sound.playOrbPickup();
          }
        }
      }
    }

    function updateCamera(delta) {
      // Dynamic Smooth Third-Person Camera
      const targetCamPos = new THREE.Vector3(
        playerPhysics.pos.x * 0.7,
        playerPhysics.pos.y + 3.8,
        playerPhysics.pos.z + 11.5
      );

      // Camera Shake
      if (game.cameraShake > 0) {
        targetCamPos.x += (Math.random() - 0.5) * game.cameraShake * 2;
        targetCamPos.y += (Math.random() - 0.5) * game.cameraShake * 2;
        game.cameraShake = Math.max(0, game.cameraShake - delta * 2);
      }

      camera.position.lerp(targetCamPos, delta * 8);
      camera.lookAt(playerPhysics.pos.x * 0.5, playerPhysics.pos.y + 0.5, playerPhysics.pos.z - 20);

      // FOV Expansion on Boost
      const targetFOV = game.isBoosting ? 82 : 65;
      camera.fov += (targetFOV - camera.fov) * delta * 6;
      camera.updateProjectionMatrix();

      // Sun Follows Player Z
      if (sunMesh) {
        sunMesh.position.z = playerPhysics.pos.z - 700;
        sunMesh.position.x = getCanyonCurveX(playerPhysics.pos.z - 700);
      }

      // Stardust particle field positioning
      if (speedStardust) {
        speedStardust.position.z = playerPhysics.pos.z;
        speedStardust.position.x = playerPhysics.pos.x;
      }
    }

    function updateHUD() {
      document.getElementById('hud-score').innerText = Math.floor(game.score);
      document.getElementById('hud-highscore').innerText = game.highScore;
      document.getElementById('hud-multiplier').innerText = `x${game.multiplier}`;
      document.getElementById('hud-speed').innerText = Math.floor(game.speed * 2.2);

      const energyFill = document.getElementById('energy-fill');
      const energyPct = Math.max(0, game.energy);
      energyFill.style.width = `${energyPct}%`;
      document.getElementById('energy-text').innerText = `${Math.floor(energyPct)}%`;

      if (energyPct < 25) energyFill.classList.add('warning');
      else energyFill.classList.remove('warning');

      const boostFill = document.getElementById('boost-fill');
      boostFill.style.width = `${game.boost}%`;
      document.getElementById('boost-text').innerText = game.isBoosting ? 'BOOSTING' : (game.boost > 20 ? 'READY' : 'RECHARGING');
    }

    // --- MAIN GAME LOOP ---
    let lastTime = performance.now();

    function gameLoop(now) {
      requestAnimationFrame(gameLoop);

      const delta = Math.min((now - lastTime) / 1000, 0.1);
      lastTime = now;
      game.time += delta;

      if (currentState === STATE.PLAYING) {
        game.distance += game.speed * delta;
        game.score += game.speed * delta * 0.1 * game.multiplier;

        updateFlightPhysics(delta);
        updateCanyonChunks();
        checkCollisionsAndNearMisses();
        updateHUD();
      } else if (currentState === STATE.MENU) {
        // Idle camera animation on menu screen
        playerCraft.rotation.y = Math.sin(game.time * 1.2) * 0.3;
        playerCraft.position.y = 4 + Math.sin(game.time * 2) * 0.5;
      }

      updateCamera(delta);
      renderer.render(scene, camera);
    }

    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    window.addEventListener('load', initApp);
  </script>
</body>
</html>
<!-- agent-meta {"model":"google/gemini-3.6-flash","provider":"openrouter","persona":"full","sessionId":"b5dfa074-7d66-4a14-91f1-6d4eb353e1c4","tokensIn":111247,"tokensOut":22585,"tokensTotal":133832,"turns":6,"toolCalls":5,"failedToolCalls":0,"timestamp":"2026-07-22T13:59:10.283Z"} -->