← Flight Simulator results

Flight Simulator

Qwen 3.6 27b MTP · full

Initial view of Flight Simulator

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 91,833 bytes · SHA-256 2cfc72c83671
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apex Aero - Flight Simulator</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { overflow: hidden; background: #000; font-family: 'Courier New', monospace; }
canvas { display: block; }

/* Loading Screen */
#loading-screen {
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
  background: linear-gradient(135deg, #0a0a2e 0%, #1a1a4e 50%, #0d0d3d 100%);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  z-index: 1000; transition: opacity 0.8s ease;
}
#loading-screen.hidden { opacity: 0; pointer-events: none; }
#loading-screen h1 {
  font-size: 4em; color: #00e5ff; text-shadow: 0 0 30px #00e5ff, 0 0 60px #0077ff;
  letter-spacing: 8px; margin-bottom: 10px;
}
#loading-screen .subtitle { color: #88aacc; font-size: 1.2em; letter-spacing: 4px; margin-bottom: 40px; }
#load-bar-container { width: 400px; height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden; }
#load-bar { width: 0%; height: 100%; background: linear-gradient(90deg, #00e5ff, #0077ff); transition: width 0.3s; border-radius: 3px; }
#load-text { color: #6688aa; margin-top: 15px; font-size: 0.9em; letter-spacing: 2px; }

/* Menu Screen */
#menu-screen {
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
  background: linear-gradient(135deg, #0a0a2e 0%, #1a1a4e 50%, #0d0d3d 100%);
  display: none; flex-direction: column; align-items: center; justify-content: center; z-index: 900;
}
#menu-screen.active { display: flex; }
.menu-title { font-size: 3em; color: #00e5ff; text-shadow: 0 0 20px #00e5ff; letter-spacing: 6px; margin-bottom: 40px; }

/* Aircraft Selection */
.aircraft-select { display: flex; gap: 30px; margin-bottom: 40px; }
.aircraft-card {
  width: 280px; padding: 25px; background: rgba(0,229,255,0.05); border: 1px solid rgba(0,229,255,0.2);
  border-radius: 12px; cursor: pointer; transition: all 0.3s ease; text-align: center;
}
.aircraft-card:hover { background: rgba(0,229,255,0.1); border-color: #00e5ff; transform: translateY(-5px); box-shadow: 0 10px 40px rgba(0,229,255,0.2); }
.aircraft-card.selected { background: rgba(0,229,255,0.15); border-color: #00e5ff; box-shadow: 0 0 30px rgba(0,229,255,0.3); }
.aircraft-card h3 { color: #00e5ff; font-size: 1.4em; margin-bottom: 8px; letter-spacing: 2px; }
.aircraft-card .type { color: #6688aa; font-size: 0.85em; margin-bottom: 15px; letter-spacing: 1px; }
.stat-row { display: flex; justify-content: space-between; padding: 4px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
.stat-label { color: #88aacc; font-size: 0.8em; }
.stat-value { color: #ffffff; font-size: 0.8em; }
.stat-bar { width: 100%; height: 4px; background: rgba(255,255,255,0.1); border-radius: 2px; margin-top: 3px; }
.stat-fill { height: 100%; border-radius: 2px; transition: width 0.5s ease; }

/* Scenario Selection */
.scenario-select { display: flex; gap: 30px; margin-bottom: 40px; }
.scenario-card {
  width: 300px; padding: 25px; background: rgba(0,229,255,0.05); border: 1px solid rgba(0,229,255,0.2);
  border-radius: 12px; cursor: pointer; transition: all 0.3s ease; text-align: center;
}
.scenario-card:hover { background: rgba(0,229,255,0.1); border-color: #00e5ff; transform: translateY(-5px); }
.scenario-card.selected { background: rgba(0,229,255,0.15); border-color: #00e5ff; box-shadow: 0 0 30px rgba(0,229,255,0.3); }
.scenario-card h3 { color: #00e5ff; font-size: 1.3em; margin-bottom: 8px; letter-spacing: 2px; }
.scenario-card p { color: #88aacc; font-size: 0.85em; line-height: 1.6; }

/* Launch Button */
#launch-btn {
  padding: 15px 60px; background: linear-gradient(90deg, #00e5ff, #0077ff); border: none;
  color: #fff; font-size: 1.2em; letter-spacing: 4px; cursor: pointer; border-radius: 8px;
  transition: all 0.3s ease; text-transform: uppercase;
}
#launch-btn:hover { transform: scale(1.05); box-shadow: 0 0 40px rgba(0,229,255,0.5); }

/* HUD */
#hud {
  position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 100; display: none;
}
#hud.active { display: block; }

/* HUD Canvas */
#hud-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }

/* Crosshair */
.crosshair {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 40px; height: 40px;
}
.crosshair::before, .crosshair::after {
  content: ''; position: absolute; background: rgba(0,229,255,0.7);
}
.crosshair::before { top: 50%; left: 0; width: 100%; height: 1px; transform: translateY(-50%); }
.crosshair::after { top: 0; left: 50%; width: 1px; height: 100%; transform: translateX(-50%); }

/* HUD Panels */
.hud-panel {
  position: absolute; background: rgba(0,20,40,0.6); border: 1px solid rgba(0,229,255,0.3);
  border-radius: 8px; padding: 12px 16px; color: #00e5ff; font-size: 0.75em; letter-spacing: 1px;
}
.hud-panel .label { color: #4488aa; font-size: 0.85em; }
.hud-panel .value { color: #ffffff; font-size: 1.3em; margin-top: 2px; }

#hud-speed { top: 20px; left: 20px; }
#hud-altitude { top: 20px; right: 20px; text-align: right; }
#hud-throttle { bottom: 80px; left: 20px; }
#hud-gforce { bottom: 80px; right: 20px; text-align: right; }
#hud-heading { top: 50%; left: 20px; transform: translateY(-50%); }
#hud-aoa { top: 50%; right: 20px; transform: translateY(-50%); text-align: right; }

/* Stall Warning */
.stall-warning {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -80px);
  color: #ff4444; font-size: 1.5em; letter-spacing: 3px; text-shadow: 0 0 20px #ff0000;
  animation: blink 0.5s infinite; display: none;
}
.stall-warning.active { display: block; }
@keyframes blink { 0%,100%{opacity:1;} 50%{opacity:0.3;} }

/* G-Force Warning */
.g-warning {
  position: absolute; top: 20%; left: 50%; transform: translateX(-50%);
  color: #ffaa00; font-size: 1.2em; letter-spacing: 2px; text-shadow: 0 0 15px #ff8800;
  display: none;
}
.g-warning.active { display: block; }

/* Minimap */
#minimap-container {
  position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
  width: 200px; height: 200px; border-radius: 50%; overflow: hidden;
  border: 2px solid rgba(0,229,255,0.4); background: rgba(0,20,40,0.7);
}
#minimap-canvas { width: 100%; height: 100%; }

/* Controls Help */
.controls-help {
  position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
  color: #4488aa; font-size: 0.7em; letter-spacing: 1px; text-align: center;
}

/* Damage Overlay */
#damage-overlay {
  position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 99;
  border: 0px solid rgba(255,0,0,0); transition: border 0.3s; display: none;
}
#damage-overlay.active { display: block; }

/* Pause Menu */
#pause-menu {
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
  background: rgba(0,0,20,0.85); display: none; flex-direction: column;
  align-items: center; justify-content: center; z-index: 950;
}
#pause-menu.active { display: flex; }
#pause-menu h2 { color: #00e5ff; font-size: 2.5em; letter-spacing: 6px; margin-bottom: 30px; }
.pause-btn {
  padding: 12px 40px; background: rgba(0,229,255,0.1); border: 1px solid #00e5ff;
  color: #00e5ff; font-size: 1em; letter-spacing: 3px; cursor: pointer; margin: 8px;
  transition: all 0.3s ease; text-transform: uppercase; border-radius: 6px;
}
.pause-btn:hover { background: rgba(0,229,255,0.2); }

/* Mission Briefing */
#briefing {
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
  background: rgba(0,0,20,0.9); display: none; flex-direction: column;
  align-items: center; justify-content: center; z-index: 960;
}
#briefing.active { display: flex; }
#briefing h2 { color: #00e5ff; font-size: 2em; letter-spacing: 4px; margin-bottom: 15px; }
#briefing p { color: #88aacc; max-width: 600px; text-align: center; line-height: 1.8; margin-bottom: 30px; font-size: 0.9em; }

/* Dogfight HUD extras */
.enemy-indicator {
  position: absolute; width: 20px; height: 20px; border: 2px solid #ff4444;
  transform: translate(-50%, -50%); pointer-events: none; display: none;
}
.enemy-indicator.active { display: block; }

/* Score */
#score-panel {
  position: absolute; top: 80px; left: 20px; background: rgba(0,20,40,0.6);
  border: 1px solid rgba(0,229,255,0.3); border-radius: 8px; padding: 10px 14px;
  color: #00e5ff; font-size: 0.75em; letter-spacing: 1px; display: none;
}
#score-panel.active { display: block; }

/* Landing Score */
#landing-score {
  position: absolute; top: 30%; left: 50%; transform: translate(-50%, -50%);
  color: #00ff88; font-size: 2em; letter-spacing: 4px; text-shadow: 0 0 20px #00ff88;
  display: none; text-align: center;
}
#landing-score.active { display: block; }

/* Vignette */
.vignette {
  position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 98;
  background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.4) 100%);
}

/* Horizon Line */
.horizon-line {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 300px; height: 2px; background: rgba(0,229,255,0.3); display: none;
}

/* Pitch Ladder */
.pitch-ladder {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 400px; height: 200px; pointer-events: none; display: none;
}

/* Message popup */
.message-popup {
  position: absolute; top: 15%; left: 50%; transform: translateX(-50%);
  color: #ffffff; font-size: 1.2em; letter-spacing: 3px; text-shadow: 0 0 10px rgba(0,229,255,0.5);
  opacity: 0; transition: opacity 0.5s ease; pointer-events: none;
}
.message-popup.show { opacity: 1; }

/* Target reticle for dogfight */
.target-reticle {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 60px; height: 60px; pointer-events: none; display: none;
}
.target-reticle.active { display: block; }
</style>
</head>
<body>

<!-- Loading Screen -->
<div id="loading-screen">
  <h1>APEX AERO</h1>
  <div class="subtitle">FLIGHT SIMULATOR</div>
  <div id="load-bar-container"><div id="load-bar"></div></div>
  <div id="load-text">INITIALIZING SYSTEMS...</div>
</div>

<!-- Menu Screen -->
<div id="menu-screen">
  <div class="menu-title">APEX AERO</div>
  
  <h3 style="color:#6688aa;letter-spacing:3px;margin-bottom:20px;font-size:0.9em;">SELECT AIRCRAFT</h3>
  <div class="aircraft-select" id="aircraft-select"></div>
  
  <h3 style="color:#6688aa;letter-spacing:3px;margin-bottom:20px;font-size:0.9em;">SELECT MISSION</h3>
  <div class="scenario-select" id="scenario-select"></div>
  
  <button id="launch-btn">LAUNCH</button>
  
  <div style="margin-top:30px;color:#446688;font-size:0.7em;letter-spacing:2px;">
    MOUSE: PITCH/ROLL | W/S: THROTTLE UP/DOWN | Q/E: YAW | SHIFT: BRAKE | SPACE: FLARES
  </div>
</div>

<!-- HUD -->
<div id="hud">
  <canvas id="hud-canvas"></canvas>
  
  <div class="crosshair" id="crosshair"></div>
  
  <div class="hud-panel" id="hud-speed">
    <div class="label">AIRSPEED</div>
    <div class="value"><span id="speed-val">0</span> KTS</div>
  </div>
  
  <div class="hud-panel" id="hud-altitude">
    <div class="label">ALTITUDE</div>
    <div class="value"><span id="alt-val">0</span> FT</div>
  </div>
  
  <div class="hud-panel" id="hud-throttle">
    <div class="label">THROTTLE</div>
    <div class="value"><span id="throttle-val">0</span>%</div>
  </div>
  
  <div class="hud-panel" id="hud-gforce">
    <div class="label">G-FORCE</div>
    <div class="value"><span id="g-val">1.0</span>G</div>
  </div>
  
  <div class="hud-panel" id="hud-heading">
    <div class="label">HEADING</div>
    <div class="value"><span id="heading-val">360</span>°</div>
  </div>
  
  <div class="hud-panel" id="hud-aoa">
    <div class="label">ANGLE OF ATTACK</div>
    <div class="value"><span id="aoa-val">0.0</span>°</div>
  </div>
  
  <div class="stall-warning" id="stall-warning">⚠ STALL WARNING ⚠</div>
  <div class="g-warning" id="g-warning">HIGH G-FORCE</div>
  
  <canvas id="minimap-canvas" width="200" height="200"></canvas>
  <div id="minimap-container"><canvas id="minimap-inner" width="200" height="200"></canvas></div>
  
  <div class="message-popup" id="message-popup"></div>
  
  <div id="score-panel">
    <div class="label">SCORE</div>
    <div class="value"><span id="score-val">0</span></div>
    <div style="margin-top:5px;"><span class="label">ENEMIES:</span> <span id="enemy-count" style="color:#ff4444;">0</span></div>
  </div>
  
  <div id="landing-score"></div>
</div>

<div class="vignette"></div>
<div id="damage-overlay"></div>

<!-- Pause Menu -->
<div id="pause-menu">
  <h2>PAUSED</h2>
  <button class="pause-btn" onclick="resumeGame()">RESUME</button>
  <button class="pause-btn" onclick="restartGame()">RESTART</button>
  <button class="pause-btn" onclick="backToMenu()">MAIN MENU</button>
</div>

<!-- Briefing -->
<div id="briefing">
  <h2 id="briefing-title"></h2>
  <p id="briefing-text"></p>
  <button class="pause-btn" onclick="startMission()">COMMENCE MISSION</button>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// ============================================================
// APEX AERO - FLIGHT SIMULATOR
// Complete Newtonian Physics Flight Model
// ============================================================

// --- Simplex Noise Implementation (inline) ---
class SimplexNoise {
  constructor(seed = Math.random()) {
    this.grad3 = [
      [1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
      [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
      [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]
    ];
    this.p = [];
    for (let i = 0; i < 256; i++) this.p[i] = Math.floor(seed * 256 + i * 131.7) & 255;
    this.perm = new Array(512);
    for (let i = 0; i < 512; i++) this.perm[i] = this.p[i & 255];
  }
  
  dot(g, x, y) { return g[0]*x + g[1]*y; }
  
  noise2D(xin, yin) {
    const F2 = 0.5*(Math.sqrt(3)-1);
    const G2 = (3-Math.sqrt(3))/6;
    let s = (xin+yin)*F2;
    let i = Math.floor(xin+s), j = Math.floor(yin+s);
    let t = (i+j)*G2;
    let X0 = i-t, Y0 = j-t;
    let x0 = xin-X0, y0 = yin-Y0;
    let i1, j1;
    if(x0>y0) { i1=1; j1=0; } else { i1=0; j1=1; }
    let x1=x0-i1+G2, y1=y0-j1+G2;
    let x2=x0-1+2*G2, y2=y0-1+2*G2;
    let ii=i&255, jj=j&255;
    let gi0=this.perm[ii+this.perm[jj]]%12;
    let gi1=this.perm[ii+i1+this.perm[jj+j1]]%12;
    let gi2=this.perm[ii+1+this.perm[jj+1]]%12;
    let n0=0, n1=0, n2=0;
    let t0=0.5-x0*x0-y0*y0;
    if(t0>=0){t0*=t0;n0=t0*t0*this.dot(this.grad3[gi0],x0,y0);}
    let t1=0.5-x1*x1-y1*y1;
    if(t1>=0){t1*=t1;n1=t1*t1*this.dot(this.grad3[gi1],x1,y1);}
    let t2=0.5-x2*x2-y2*y2;
    if(t2>=0){t2*=t2;n2=t2*t2*this.dot(this.grad3[gi2],x2,y2);}
    return 70*(n0+n1+n2);
  }
  
  fbm(x, y, octaves=6, lacunarity=2, gain=0.5) {
    let val = 0, amp = 1, freq = 1, maxVal = 0;
    for(let i=0;i<octaves;i++){
      val += this.noise2D(x*freq,y*freq)*amp;
      maxVal += amp;
      amp *= gain;
      freq *= lacunarity;
    }
    return val/maxVal;
  }
}

// --- Aircraft Definitions ---
const AIRCRAFT = {
  interceptor: {
    name: "VIPER X-1",
    type: "Interceptor",
    color: 0x4488ff,
    accentColor: 0x00e5ff,
    mass: 8500,
    wingArea: 32,
    maxThrust: 160000,
    thrustToWeight: 1.9,
    liftCoeffMax: 1.4,
    dragProfile: { cd0: 0.015, k: 0.04 },
    turnRate: 2.8,
    rollRate: 3.5,
    yawRate: 1.2,
    maxSpeed: 700, // knots
    stallSpeed: 90,
    gLimit: 9,
    description: "High-speed interceptor with exceptional thrust-to-weight ratio and tight turning radius."
  },
  fighterBomber: {
    name: "RAVEN MK-III",
    type: "Fighter Bomber",
    color: 0x88aa44,
    accentColor: 0xaacc66,
    mass: 15000,
    wingArea: 48,
    maxThrust: 220000,
    thrustToWeight: 1.5,
    liftCoeffMax: 1.6,
    dragProfile: { cd0: 0.022, k: 0.06 },
    turnRate: 2.0,
    rollRate: 2.5,
    yawRate: 0.9,
    maxSpeed: 550,
    stallSpeed: 110,
    gLimit: 7.5,
    description: "Heavy fighter bomber with superior low-altitude performance and payload capacity."
  },
  stealthFighter: {
    name: "PHANTOM S-9",
    type: "Stealth Fighter",
    color: 0x334466,
    accentColor: 0x5577aa,
    mass: 12000,
    wingArea: 40,
    maxThrust: 180000,
    thrustToWeight: 1.6,
    liftCoeffMax: 1.5,
    dragProfile: { cd0: 0.012, k: 0.035 },
    turnRate: 2.4,
    rollRate: 3.0,
    yawRate: 1.0,
    maxSpeed: 620,
    stallSpeed: 95,
    gLimit: 8.5,
    description: "Balanced stealth fighter with low radar cross-section and excellent maneuverability."
  }
};

const SCENARIOS = {
  takeoffLanding: {
    name: "PRECISION FLIGHT",
    subtitle: "Takeoff & Landing",
    description: "Navigate the runway, achieve liftoff using proper aerodynamic principles, maintain stable flight patterns, and execute a precision landing. Manage airspeed, angle of attack, and descent rate carefully.",
    briefing: "Your mission is to demonstrate mastery of low-speed flight dynamics. Take off from the runway, climb to 3000 feet, perform a circuit pattern, and land back on the runway. Watch for stall warnings at low speeds and manage your approach speed carefully."
  },
  dogfight: {
    name: "COMBAT ENGAGEMENT",
    subtitle: "Dogfight Scenario",
    description: "Engage enemy aircraft in high-G aerial combat. Use superior maneuvering, energy management, and tactical positioning to outmaneuver opponents.",
    briefing: "Hostile aircraft detected in your sector. Engage and destroy all enemy fighters while maintaining positive G-forces within structural limits. Manage your energy state - speed and altitude are your weapons."
  }
};

// --- Global State ---
let gameState = 'loading'; // loading, menu, briefing, playing, paused, ended
let selectedAircraft = 'interceptor';
let selectedScenario = 'takeoffLanding';
let score = 0;
let enemiesRemaining = 0;
let landingScore = null;

// --- Three.js Setup ---
let scene, camera, renderer, clock;
let terrainMesh, waterMesh, skyDome;
let aircraftModel, aircraftGroup;
let enemyAircraft = [];
let runwayMesh;
let particles = [];
let smokeParticles = [];

// --- Physics State ---
const physics = {
  position: new THREE.Vector3(0, 50, 0),
  velocity: new THREE.Vector3(0, 0, 0),
  angularVelocity: new THREE.Vector3(0, 0, 0),
  orientation: new THREE.Quaternion(),
  euler: new THREE.Euler(0, 0, 0, 'YXZ'),
  throttle: 0, // 0-1
  gForce: 1.0,
  angleOfAttack: 0,
  airspeed: 0, // m/s
  altitude: 0,
  heading: 0,
  isStalled: false,
  onGround: true,
  damage: 0,
  engineRPM: 0,
  engineHeat: 0
};

// --- Input State ---
const input = {
  pitch: 0, roll: 0, yaw: 0, throttleUp: false, throttleDown: false,
  brake: false, flares: false, mouseLocked: false
};

// --- Audio System ---
let audioCtx, engineOsc, engineGain, windNoise, windGain;
let masterGain;

function initAudio() {
  try {
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    
    // Master gain
    masterGain = audioCtx.createGain();
    masterGain.gain.value = 0.3;
    masterGain.connect(audioCtx.destination);
    
    // Engine sound - layered oscillators for realism
    engineOsc = audioCtx.createOscillator();
    engineOsc.type = 'sawtooth';
    engineOsc.frequency.value = 80;
    
    const engineFilter = audioCtx.createBiquadFilter();
    engineFilter.type = 'lowpass';
    engineFilter.frequency.value = 400;
    engineFilter.Q.value = 2;
    
    engineGain = audioCtx.createGain();
    engineGain.gain.value = 0;
    
    // Second harmonic for richness
    const osc2 = audioCtx.createOscillator();
    osc2.type = 'square';
    osc2.frequency.value = 40;
    const gain2 = audioCtx.createGain();
    gain2.gain.value = 0.3;
    
    engineOsc.connect(engineFilter);
    osc2.connect(gain2);
    gain2.connect(engineFilter);
    engineFilter.connect(engineGain);
    engineGain.connect(masterGain);
    
    engineOsc.start();
    osc2.start();
    
    // Wind noise using buffer source
    const bufferSize = audioCtx.sampleRate * 2;
    const windBuffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
    const data = windBuffer.getChannelData(0);
    for (let i = 0; i < bufferSize; i++) {
      data[i] = Math.random() * 2 - 1;
    }
    
    windNoise = audioCtx.createBufferSource();
    windNoise.buffer = windBuffer;
    windNoise.loop = true;
    
    const windFilter = audioCtx.createBiquadFilter();
    windFilter.type = 'bandpass';
    windFilter.frequency.value = 800;
    windFilter.Q.value = 0.5;
    
    windGain = audioCtx.createGain();
    windGain.gain.value = 0;
    
    windNoise.connect(windFilter);
    windFilter.connect(windGain);
    windGain.connect(masterGain);
    windNoise.start();
    
    // Store references for updates
    physics._audioRefs = { engineOsc, osc2, gain2, engineFilter, engineGain, windFilter, windGain };
  } catch(e) {
    console.warn('Audio not available:', e);
  }
}

function updateAudio() {
  if (!physics._audioRefs) return;
  const refs = physics._audioRefs;
  const ac = audioCtx;
  
  // Engine frequency based on throttle and RPM
  const baseFreq = 60 + physics.throttle * 200;
  refs.engineOsc.frequency.setTargetAtTime(baseFreq, ac.currentTime, 0.1);
  refs.osc2.frequency.setTargetAtTime(baseFreq * 0.5, ac.currentTime, 0.1);
  
  // Engine volume based on throttle and airspeed
  const engineVol = Math.min(0.4, physics.throttle * 0.3 + (physics.airspeed / 300) * 0.1);
  refs.engineGain.gain.setTargetAtTime(engineVol, ac.currentTime, 0.05);
  
  // Filter opens with RPM
  refs.engineFilter.frequency.setTargetAtTime(200 + physics.throttle * 800, ac.currentTime, 0.1);
  
  // Wind noise based on airspeed
  const windVol = Math.min(0.3, (physics.airspeed / 350) * 0.25);
  refs.windGain.gain.setTargetAtTime(windVol, ac.currentTime, 0.1);
  refs.windFilter.frequency.setTargetAtTime(400 + physics.airspeed * 3, ac.currentTime, 0.1);
}

// --- Terrain Generation ---
const noise = new SimplexNoise(42);
let terrainData;
const TERRAIN_SIZE = 8000;
const TERRAIN_SEGMENTS = 256;

function generateTerrain() {
  const geometry = new THREE.PlaneGeometry(TERRAIN_SIZE, TERRAIN_SIZE, TERRAIN_SEGMENTS, TERRAIN_SEGMENTS);
  geometry.rotateX(-Math.PI / 2);
  
  const vertices = geometry.attributes.position.array;
  terrainData = [];
  
  for (let i = 0; i < vertices.length; i += 3) {
    const x = vertices[i];
    const z = vertices[i + 2];
    
    // Multi-octave noise for realistic terrain
    let height = 0;
    
    // Large scale mountains
    height += noise.fbm(x * 0.0003, z * 0.0003, 6, 2.1, 0.5) * 800;
    
    // Medium hills
    height += noise.fbm(x * 0.001, z * 0.001, 4, 2.0, 0.45) * 200;
    
    // Small details
    height += noise.fbm(x * 0.005, z * 0.005, 3, 2.0, 0.4) * 30;
    
    // Flatten near center area
    const distFromCenter = Math.sqrt(x*x + z*z);
    if (distFromCenter < 600) {
      const flattenFactor = Math.max(0, 1 - distFromCenter / 600);
      height *= (1 - flattenFactor * 0.95);
    }
    
    // Ensure runway area is completely flat at y=0
    if (Math.abs(x) < 250 && z > -400 && z < 1000) {
      const edgeX = Math.max(0, Math.abs(x) - 200);
      const edgeZ_front = Math.max(0, -(z + 350));
      const edgeZ_back = Math.max(0, z - 950);
      const edgeY = Math.max(0, Math.min(edgeX, Math.min(edgeZ_front, edgeZ_back)) - 40);
      
      if (edgeY < 1) {
        height *= Math.max(0, edgeY / 1);
      } else {
        height = 0; // Completely flat runway zone
      }
    }
    
    // Water level
    if (height < -20) height = -20;
    
    vertices[i + 1] = height;
    terrainData.push({ x, y: height, z });
  }
  
  geometry.computeVertexNormals();
  
  // Vertex colors for terrain
  const colors = new Float32Array(vertices.length);
  for (let i = 0; i < vertices.length; i += 3) {
    const h = vertices[i + 1];
    let r, g, b;
    
    if (h < -15) { // Water edge / beach
      r = 0.76; g = 0.7; b = 0.5;
    } else if (h < 20) { // Grass/lowland
      const t = Math.min(1, (h + 15) / 35);
      r = 0.15 + t * 0.1; g = 0.35 + t * 0.15; b = 0.08;
    } else if (h < 200) { // Forest/hills
      const t = Math.min(1, (h - 20) / 180);
      r = 0.1 + t * 0.2; g = 0.25 + t * 0.1; b = 0.05 + t * 0.05;
    } else if (h < 400) { // Rock
      const t = Math.min(1, (h - 200) / 200);
      r = 0.35 + t * 0.15; g = 0.32 + t * 0.12; b = 0.28 + t * 0.1;
    } else { // Snow caps
      const t = Math.min(1, (h - 400) / 200);
      r = 0.5 + t * 0.45; g = 0.47 + t * 0.48; b = 0.38 + t * 0.52;
    }
    
    colors[i] = r;
    colors[i+1] = g;
    colors[i+2] = b;
  }
  
  geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
  
  const material = new THREE.MeshStandardMaterial({
    vertexColors: true,
    roughness: 0.85,
    metalness: 0.1,
    flatShading: false
  });
  
  terrainMesh = new THREE.Mesh(geometry, material);
  scene.add(terrainMesh);
}

function getTerrainHeight(x, z) {
  // Sample from generated data using bilinear interpolation
  const halfSize = TERRAIN_SIZE / 2;
  const cellSize = TERRAIN_SIZE / TERRAIN_SEGMENTS;
  
  let nx = (x + halfSize) / cellSize;
  let nz = (z + halfSize) / cellSize;
  
  const ix = Math.floor(nx);
  const iz = Math.floor(nz);
  const fx = nx - ix;
  const fz = nz - iz;
  
  if (ix < 0 || ix >= TERRAIN_SEGMENTS || iz < 0 || iz >= TERRAIN_SEGMENTS) return 0;
  
  const getH = (i, j) => {
    if (i < 0 || i > TERRAIN_SEGMENTS || j < 0 || j > TERRAIN_SEGMENTS) return 0;
    const idx = (j * (TERRAIN_SEGMENTS + 1) + i);
    const geo = terrainMesh.geometry;
    return geo.attributes.position.array[idx * 3 + 1];
  };
  
  const h00 = getH(ix, iz);
  const h10 = getH(ix+1, iz);
  const h01 = getH(ix, iz+1);
  const h11 = getH(ix+1, iz+1);
  
  return h00*(1-fx)*(1-fz) + h10*fx*(1-fz) + h01*(1-fx)*fz + h11*fx*fz;
}

// --- Water Plane ---
function createWater() {
  const waterGeo = new THREE.PlaneGeometry(TERRAIN_SIZE * 2, TERRAIN_SIZE * 2);
  waterGeo.rotateX(-Math.PI / 2);
  
  const waterMat = new THREE.MeshStandardMaterial({
    color: 0x1a5276,
    transparent: true,
    opacity: 0.7,
    roughness: 0.1,
    metalness: 0.8
  });
  
  waterMesh = new THREE.Mesh(waterGeo, waterMat);
  waterMesh.position.y = -18;
  scene.add(waterMesh);
}

// --- Sky Dome ---
function createSky() {
  const skyGeo = new THREE.SphereGeometry(15000, 32, 32);
  
  // Gradient texture for sky
  const canvas = document.createElement('canvas');
  canvas.width = 512;
  canvas.height = 512;
  const ctx = canvas.getContext('2d');
  const gradient = ctx.createLinearGradient(0, 0, 0, 512);
  
  // Time-based sky colors (default: golden hour)
  gradient.addColorStop(0, '#0a1628');    // Top - deep blue
  gradient.addColorStop(0.3, '#1a3a5c');  // Mid-upper
  gradient.addColorStop(0.5, '#4a7aaa');  // Mid
  gradient.addColorStop(0.7, '#ff9966');  // Horizon - warm
  gradient.addColorStop(0.85, '#ffcc88'); // Near horizon
  gradient.addColorStop(1, '#ffe8bb');    // Bottom
  
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, 512, 512);
  
  const skyTex = new THREE.CanvasTexture(canvas);
  
  const skyMat = new THREE.MeshBasicMaterial({
    map: skyTex,
    side: THREE.BackSide
  });
  
  skyDome = new THREE.Mesh(skyGeo, skyMat);
  scene.add(skyDome);
}

// --- Runway ---
function createRunway() {
  const runwayGroup = new THREE.Group();
  
  // Main runway surface
  const rwGeo = new THREE.PlaneGeometry(40, 1200);
  rwGeo.rotateX(-Math.PI / 2);
  const rwMat = new THREE.MeshStandardMaterial({
    color: 0x333333,
    roughness: 0.9,
    metalness: 0.05
  });
  const rwMesh = new THREE.Mesh(rwGeo, rwMat);
  rwMesh.position.set(0, 1.5, 250);
  runwayGroup.add(rwMesh);
  
  // Runway markings - center line dashes
  for (let z = -400; z < 900; z += 30) {
    const dashGeo = new THREE.PlaneGeometry(1, 12);
    dashGeo.rotateX(-Math.PI / 2);
    const dashMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
    const dash = new THREE.Mesh(dashGeo, dashMat);
    dash.position.set(0, 1.6, z + 250);
    runwayGroup.add(dash);
  }
  
  // Edge lines
  for (let side of [-18, 18]) {
    const edgeGeo = new THREE.PlaneGeometry(1, 1200);
    edgeGeo.rotateX(-Math.PI / 2);
    const edgeMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
    const edge = new THREE.Mesh(edgeGeo, edgeMat);
    edge.position.set(side, 1.6, 250);
    runwayGroup.add(edge);
  }
  
  // Threshold markings
  for (let i = -3; i <= 3; i++) {
    if (i === 0) continue;
    const tGeo = new THREE.PlaneGeometry(4, 15);
    tGeo.rotateX(-Math.PI / 2);
    const tMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
    
    const t1 = new THREE.Mesh(tGeo, tMat);
    t1.position.set(i * 6, 1.6, -350 + 250);
    runwayGroup.add(t1);
    
    const t2 = new THREE.Mesh(tGeo, tMat);
    t2.position.set(i * 6, 1.6, 850 + 250);
    runwayGroup.add(t2);
  }
  
  // Runway numbers
  const numCanvas = document.createElement('canvas');
  numCanvas.width = 256;
  numCanvas.height = 128;
  const nctx = numCanvas.getContext('2d');
  nctx.fillStyle = '#333';
  nctx.fillRect(0, 0, 256, 128);
  nctx.font = 'bold 72px Arial';
  nctx.fillStyle = '#fff';
  nctx.textAlign = 'center';
  nctx.fillText('09', 128, 85);
  
  const numTex = new THREE.CanvasTexture(numCanvas);
  const numMat = new THREE.MeshBasicMaterial({ map: numTex });
  
  for (let z of [-370 + 250, 870 + 250]) {
    const numGeo = new THREE.PlaneGeometry(20, 10);
    numGeo.rotateX(-Math.PI / 2);
    const numMesh = new THREE.Mesh(numGeo, numMat);
    numMesh.position.set(0, 1.6, z);
    runwayGroup.add(numMesh);
  }
  
  // Taxiway lights (emissive)
  for (let side of [-22, 22]) {
    for (let z = -450; z < 950; z += 30) {
      const lightGeo = new THREE.SphereGeometry(0.3, 8, 8);
      const lightMat = new THREE.MeshBasicMaterial({ color: 0x00ff44 });
      const light = new THREE.Mesh(lightGeo, lightMat);
      light.position.set(side, 1.7, z + 250);
      runwayGroup.add(light);
    }
  }
  
  scene.add(runwayGroup);
}

// --- Aircraft Model Builder ---
function createAircraftModel(type) {
  const group = new THREE.Group();
  const ac = AIRCRAFT[type];
  const bodyColor = ac.color;
  const accentColor = ac.accentColor;
  
  // Materials
  const bodyMat = new THREE.MeshStandardMaterial({
    color: bodyColor, roughness: 0.3, metalness: 0.7
  });
  const darkMat = new THREE.MeshStandardMaterial({
    color: 0x222222, roughness: 0.5, metalness: 0.8
  });
  const glassMat = new THREE.MeshStandardMaterial({
    color: 0x88ccff, roughness: 0.1, metalness: 0.3, transparent: true, opacity: 0.6
  });
  const accentMat = new THREE.MeshStandardMaterial({
    color: accentColor, roughness: 0.2, metalness: 0.5, emissive: accentColor, emissiveIntensity: 0.1
  });
  
  // Fuselage - main body
  const fuselageGeo = new THREE.CylinderGeometry(0.8, 0.6, 14, 12);
  fuselageGeo.rotateZ(Math.PI / 2);
  const fuselage = new THREE.Mesh(fuselageGeo, bodyMat);
  group.add(fuselage);
  
  // Nose cone
  const noseGeo = new THREE.ConeGeometry(0.8, 4, 12);
  noseGeo.rotateZ(-Math.PI / 2);
  const nose = new THREE.Mesh(noseGeo, bodyMat);
  nose.position.x = 9;
  group.add(nose);
  
  // Cockpit canopy
  const cockpitGeo = new THREE.SphereGeometry(1.0, 12, 8, 0, Math.PI * 2, 0, Math.PI / 2);
  const cockpit = new THREE.Mesh(cockpitGeo, glassMat);
  cockpit.position.set(3, 0.6, 0);
  group.add(cockpit);
  
  // Wings - different shapes per type
  if (type === 'interceptor') {
    // Delta wing
    const wingShape = new THREE.Shape();
    wingShape.moveTo(0, 0);
    wingShape.lineTo(-8, 2);
    wingShape.lineTo(-6, 5);
    wingShape.lineTo(0, 4);
    
    const wingGeo = new THREE.ExtrudeGeometry(wingShape, { depth: 0.15, bevelEnabled: true, bevelThickness: 0.05, bevelSize: 0.05 });
    const leftWing = new THREE.Mesh(wingGeo, bodyMat);
    leftWing.position.set(2, -0.3, 0);
    leftWing.rotation.x = Math.PI / 2;
    group.add(leftWing);
    
    const rightWing = leftWing.clone();
    rightWing.scale.z = -1;
    group.add(rightWing);
  } else if (type === 'fighterBomber') {
    // Swept wing with larger area
    const wingShape = new THREE.Shape();
    wingShape.moveTo(0, 0);
    wingShape.lineTo(-12, 3);
    wingShape.lineTo(-10, 7);
    wingShape.lineTo(0, 5);
    
    const wingGeo = new THREE.ExtrudeGeometry(wingShape, { depth: 0.2, bevelEnabled: true, bevelThickness: 0.05, bevelSize: 0.05 });
    const leftWing = new THREE.Mesh(wingGeo, bodyMat);
    leftWing.position.set(1, -0.3, 0);
    leftWing.rotation.x = Math.PI / 2;
    group.add(leftWing);
    
    const rightWing = leftWing.clone();
    rightWing.scale.z = -1;
    group.add(rightWing);
    
    // Hardpoints under wings
    for (let side of [-8, 8]) {
      const hardpointGeo = new THREE.CylinderGeometry(0.15, 0.15, 2, 6);
      const hardpoint = new THREE.Mesh(hardpointGeo, darkMat);
      hardpoint.position.set(-1, -1.5, side);
      group.add(hardpoint);
    }
  } else { // stealthFighter
    // Diamond/diamond wing shape
    const wingShape = new THREE.Shape();
    wingShape.moveTo(0, 0);
    wingShape.lineTo(-9, 2);
    wingShape.lineTo(-7, 6);
    wingShape.lineTo(0, 4.5);
    
    const wingGeo = new THREE.ExtrudeGeometry(wingShape, { depth: 0.18, bevelEnabled: true, bevelThickness: 0.05, bevelSize: 0.05 });
    const leftWing = new THREE.Mesh(wingGeo, bodyMat);
    leftWing.position.set(2, -0.3, 0);
    leftWing.rotation.x = Math.PI / 2;
    group.add(leftWing);
    
    const rightWing = leftWing.clone();
    rightWing.scale.z = -1;
    group.add(rightWing);
  }
  
  // Tail fins (vertical stabilizers)
  if (type === 'stealthFighter') {
    // Canted vertical tails
    for (let side of [-1, 1]) {
      const tailShape = new THREE.Shape();
      tailShape.moveTo(0, 0);
      tailShape.lineTo(-3, 4);
      tailShape.lineTo(-2, 5);
      tailShape.lineTo(0, 3.5);
      
      const tailGeo = new THREE.ExtrudeGeometry(tailShape, { depth: 0.1, bevelEnabled: true });
      const tail = new THREE.Mesh(tailGeo, bodyMat);
      tail.position.set(-6, -0.2, side * 2);
      tail.rotation.x = Math.PI / 2;
      tail.rotation.z = side * 0.3;
      group.add(tail);
    }
  } else {
    // Standard vertical stabilizer
    const vTailShape = new THREE.Shape();
    vTailShape.moveTo(0, 0);
    vTailShape.lineTo(-4, 5);
    vTailShape.lineTo(-3, 5.5);
    vTailShape.lineTo(0, 4);
    
    const vTailGeo = new THREE.ExtrudeGeometry(vTailShape, { depth: 0.12, bevelEnabled: true });
    const vTail = new THREE.Mesh(vTailGeo, bodyMat);
    vTail.position.set(-6, -0.3, 0);
    vTail.rotation.x = Math.PI / 2;
    group.add(vTail);
    
    // Horizontal stabilizer
    for (let side of [-1, 1]) {
      const hTailShape = new THREE.Shape();
      hTailShape.moveTo(0, 0);
      hTailShape.lineTo(-4, 1.5);
      hTailShape.lineTo(-3, 2);
      hTailShape.lineTo(0, 1.5);
      
      const hTailGeo = new THREE.ExtrudeGeometry(hTailShape, { depth: 0.1, bevelEnabled: true });
      const hTail = new THREE.Mesh(hTailGeo, bodyMat);
      hTail.position.set(-6, -0.3, side * 2);
      hTail.rotation.x = Math.PI / 2;
      group.add(hTail);
    }
  }
  
  // Engine exhaust/nozzles
  for (let z of [-1.5, 1.5]) {
    const nozzleGeo = new THREE.CylinderGeometry(0.4, 0.6, 2, 8);
    nozzleGeo.rotateZ(Math.PI / 2);
    const nozzle = new THREE.Mesh(nozzleGeo, darkMat);
    nozzle.position.set(-7.5, 0, z);
    group.add(nozzle);
    
    // Exhaust glow
    const exhaustGeo = new THREE.CircleGeometry(0.35, 8);
    const exhaustMat = new THREE.MeshBasicMaterial({ 
      color: accentColor, transparent: true, opacity: 0.6, side: THREE.DoubleSide 
    });
    const exhaust = new THREE.Mesh(exhaustGeo, exhaustMat);
    exhaust.position.set(-8.5, 0, z);
    exhaust.rotation.y = Math.PI / 2;
    exhaust.name = 'exhaust';
    group.add(exhaust);
  }
  
  // Landing gear (simplified)
  const gearMat = new THREE.MeshStandardMaterial({ color: 0x444444, metalness: 0.9 });
  for (let x of [5, -3]) {
    const strutGeo = new THREE.CylinderGeometry(0.08, 0.08, 2, 6);
    const strut = new THREE.Mesh(strutGeo, gearMat);
    strut.position.set(x, -1.5, 0);
    group.add(strut);
    
    const wheelGeo = new THREE.CylinderGeometry(0.3, 0.3, 0.2, 8);
    wheelGeo.rotateZ(Math.PI / 2);
    const wheel = new THREE.Mesh(wheelGeo, darkMat);
    wheel.position.set(x, -2.5, 0);
    group.add(wheel);
  }
  
  // Nose gear
  const noseStrut = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 1.5, 6), gearMat);
  noseStrut.position.set(7, -1.2, 0);
  group.add(noseStrut);
  
  // Accent stripes
  const stripeGeo = new THREE.BoxGeometry(8, 0.05, 0.3);
  for (let z of [-0.9, 0.9]) {
    const stripe = new THREE.Mesh(stripeGeo, accentMat);
    stripe.position.set(2, 0.75, z);
    group.add(stripe);
  }
  
  // Scale the model appropriately
  group.scale.set(1.5, 1.5, 1.5);
  
  return group;
}

// --- Enemy Aircraft (simpler models) ---
function createEnemyAircraft() {
  const group = new THREE.Group();
  
  const bodyMat = new THREE.MeshStandardMaterial({ color: 0xcc3333, roughness: 0.4, metalness: 0.6 });
  const darkMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.5, metalness: 0.8 });
  
  // Simple fighter shape
  const fuselageGeo = new THREE.CylinderGeometry(0.7, 0.5, 12, 10);
  fuselageGeo.rotateZ(Math.PI / 2);
  group.add(new THREE.Mesh(fuselageGeo, bodyMat));
  
  const noseGeo = new THREE.ConeGeometry(0.7, 3, 10);
  noseGeo.rotateZ(-Math.PI / 2);
  const nose = new THREE.Mesh(noseGeo, bodyMat);
  nose.position.x = 7.5;
  group.add(nose);
  
  // Wings
  for (let side of [-1, 1]) {
    const wingShape = new THREE.Shape();
    wingShape.moveTo(0, 0);
    wingShape.lineTo(-8, 2);
    wingShape.lineTo(-6, 5);
    wingShape.lineTo(0, 4);
    
    const wingGeo = new THREE.ExtrudeGeometry(wingShape, { depth: 0.15 });
    const wing = new THREE.Mesh(wingGeo, bodyMat);
    wing.position.set(2, -0.3, 0);
    wing.rotation.x = Math.PI / 2;
    if (side === -1) wing.scale.z = -1;
    group.add(wing);
  }
  
  // Tail
  const tailShape = new THREE.Shape();
  tailShape.moveTo(0, 0);
  tailShape.lineTo(-3, 4);
  tailShape.lineTo(-2, 4.5);
  tailShape.lineTo(0, 3.5);
  
  const tailGeo = new THREE.ExtrudeGeometry(tailShape, { depth: 0.1 });
  const tail = new THREE.Mesh(tailGeo, bodyMat);
  tail.position.set(-5, -0.2, 0);
  tail.rotation.x = Math.PI / 2;
  group.add(tail);
  
  // Red accent
  const stripeGeo = new THREE.BoxGeometry(6, 0.05, 0.3);
  const stripeMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
  for (let z of [-0.8, 0.8]) {
    const s = new THREE.Mesh(stripeGeo, stripeMat);
    s.position.set(1, 0.75, z);
    group.add(s);
  }
  
  group.scale.set(1.3, 1.3, 1.3);
  return group;
}

// --- Particle System ---
function createSmokeParticle(pos) {
  const geo = new THREE.SphereGeometry(0.5 + Math.random() * 0.5, 6, 6);
  const mat = new THREE.MeshBasicMaterial({ 
    color: 0x888888, transparent: true, opacity: 0.4 
  });
  const mesh = new THREE.Mesh(geo, mat);
  mesh.position.copy(pos);
  
  scene.add(mesh);
  smokeParticles.push({
    mesh, life: 1.0, decay: 0.01 + Math.random() * 0.02,
    velocity: new THREE.Vector3((Math.random()-0.5)*2, (Math.random()-0.5)*2, (Math.random()-0.5)*2)
  });
}

function updateParticles(dt) {
  for (let i = smokeParticles.length - 1; i >= 0; i--) {
    const p = smokeParticles[i];
    p.life -= p.decay;
    p.mesh.position.add(p.velocity.clone().multiplyScalar(dt));
    p.mesh.material.opacity = Math.max(0, p.life * 0.4);
    p.mesh.scale.setScalar(1 + (1 - p.life) * 3);
    
    if (p.life <= 0) {
      scene.remove(p.mesh);
      smokeParticles.splice(i, 1);
    }
  }
}

// --- Physics Engine ---
const GRAVITY = 9.81; // m/s²
const SEA_LEVEL_DENSITY = 1.225; // kg/m³
const SCALE_FACTOR = 0.3; // Scale factor for game feel (not full real-world scale)

function getAirDensity(altitudeMeters) {
  // ISA atmosphere model approximation
  const h = Math.max(0, altitudeMeters);
  return SEA_LEVEL_DENSITY * Math.exp(-h / 8500);
}

function computeLiftCoeff(angleOfAttackRad) {
  const ac = AIRCRAFT[selectedAircraft];
  // Linear region: Cl = Cl_alpha * alpha (up to stall angle ~15 degrees)
  const stallAngle = Math.PI / 6; // ~15 degrees
  const clAlpha = ac.liftCoeffMax / stallAngle;
  
  if (Math.abs(angleOfAttackRad) < stallAngle) {
    return clAlpha * angleOfAttackRad + 0.2; // +0.2 for zero-alpha lift
  } else {
    // Post-stall: rapid decrease in lift
    const excess = Math.abs(angleOfAttackRad) - stallAngle;
    return ac.liftCoeffMax * (1 - excess * 3);
  }
}

function computeDragCoeff(angleOfAttackRad, liftCoeff) {
  const ac = AIRCRAFT[selectedAircraft];
  // Parasite drag + induced drag
  const cdParasite = ac.dragProfile.cd0;
  const cdInduced = (liftCoeff * liftCoeff) / (Math.PI * 0.9 * 8); // AR ~ 8 for typical fighter
  
  return cdParasite + cdInduced;
}

function updatePhysics(dt) {
  if (gameState !== 'playing') return;
  
  const ac = AIRCRAFT[selectedAircraft];
  dt = Math.min(dt, 0.05); // Cap delta time to prevent physics explosions
  
  // --- Input Processing ---
  const pitchInput = input.pitch * ac.turnRate;
  const rollInput = input.roll * ac.rollRate;
  const yawInput = input.yaw * ac.yawRate;
  
  // Throttle management
  if (input.throttleUp) physics.throttle = Math.min(1, physics.throttle + dt * 0.8);
  if (input.throttleDown) physics.throttle = Math.max(0, physics.throttle - dt * 0.8);
  
  // Engine RPM response (with lag)
  const targetRPM = physics.throttle;
  physics.engineRPM += (targetRPM - physics.engineRPM) * dt * 3;
  
  // --- Atmospheric Properties ---
  const altitudeMeters = Math.max(0, physics.position.y);
  const airDensity = getAirDensity(altitudeMeters);
  
  // --- Velocity in aircraft frame ---
  const worldVelocity = physics.velocity.clone();
  const localVelocity = worldVelocity.clone().applyQuaternion(physics.orientation.clone().invert());
  
  // Airspeed (magnitude of velocity)
  physics.airspeed = worldVelocity.length();
  
  // Angle of attack: angle between velocity vector and aircraft's x-axis in the pitch plane
  const forwardDir = new THREE.Vector3(1, 0, 0).applyQuaternion(physics.orientation);
  const velDir = worldVelocity.clone().normalize();
  
  if (physics.airspeed > 1) {
    // Project velocity onto aircraft's vertical plane
    const rightDir = new THREE.Vector3(0, 0, 1).applyQuaternion(physics.orientation);
    const upDir = new THREE.Vector3(0, 1, 0).applyQuaternion(physics.orientation);
    
    const velForward = worldVelocity.dot(forwardDir);
    const velUp = worldVelocity.dot(upDir);
    
    physics.angleOfAttack = Math.atan2(velUp, Math.max(1, velForward));
  } else {
    physics.angleOfAttack = 0;
  }
  
  // --- Aerodynamic Forces (in aircraft local frame) ---
  const vSq = physics.airspeed * physics.airspeed;
  const S = ac.wingArea;
  const rho = airDensity;
  
  // Lift coefficient based on AoA
  const cl = computeLiftCoeff(physics.angleOfAttack);
  
  // Drag coefficient
  const cd = computeDragCoeff(physics.angleOfAttack, cl);
  
  // Lift force (upward in aircraft frame)
  const liftForce = 0.5 * rho * vSq * S * cl;
  
  // Drag force (backward along velocity direction)
  const dragForce = 0.5 * rho * vSq * S * cd;
  
  // Thrust force
  const thrustForce = ac.maxThrust * physics.throttle * SCALE_FACTOR;
  
  // Weight
  const weight = ac.mass * GRAVITY;
  
  // --- Apply Forces ---
  // Transform forces to world frame
  
  // Lift is in aircraft's up direction
  const liftDir = new THREE.Vector3(0, 1, 0).applyQuaternion(physics.orientation);
  const liftVec = liftDir.multiplyScalar(liftForce * SCALE_FACTOR);
  
  // Drag opposes velocity
  let dragVec;
  if (physics.airspeed > 0.5) {
    dragVec = velDir.clone().multiplyScalar(-dragForce * SCALE_FACTOR);
  } else {
    dragVec = new THREE.Vector3(0, 0, 0);
  }
  
  // Thrust is in aircraft's forward direction
  const thrustVec = forwardDir.multiplyScalar(thrustForce);
  
  // Weight (downward)
  const weightVec = new THREE.Vector3(0, -weight, 0);
  
  // Total force
  const totalForce = new THREE.Vector3();
  totalForce.add(liftVec).add(dragVec).add(thrustVec).add(weightVec);
  
  // Ground friction when on ground
  if (physics.onGround) {
    const frictionCoeff = 0.8;
    const normalForce = Math.max(0, weight - liftForce * SCALE_FACTOR);
    const frictionForce = normalForce * frictionCoeff;
    
    if (worldVelocity.length() > 1) {
      totalForce.add(worldVelocity.clone().normalize().multiplyScalar(-frictionForce));
    }
    
    // Brake force
    if (input.brake && physics.airspeed > 5) {
      const brakeForce = normalForce * 0.6;
      totalForce.add(worldVelocity.clone().normalize().multiplyScalar(-brakeForce));
    }
  }
  
  // --- Newton's Second Law: F = ma ---
  const acceleration = totalForce.divideScalar(ac.mass);
  
  // Update velocity
  physics.velocity.add(acceleration.multiplyScalar(dt));
  
  // --- Angular Dynamics (Inertia) ---
  // Apply control surface deflections as angular accelerations
  
  const effectiveControlAuthority = Math.min(1, physics.airspeed / 50); // Controls less effective at low speed
  
  // Target angular velocities based on input
  const targetPitchRate = pitchInput * effectiveControlAuthority;
  const targetRollRate = rollInput * effectiveControlAuthority;
  const targetYawRate = yawInput * effectiveControlAuthority + 
    (input.roll * 0.3 * effectiveControlAuthority); // Adverse yaw from rolling
  
  // Angular damping (aerodynamic stability)
  const angularDamping = 2.5;
  
  physics.angularVelocity.x += (targetPitchRate - physics.angularVelocity.x) * dt * angularDamping;
  physics.angularVelocity.y += (targetYawRate - physics.angularVelocity.y) * dt * angularDamping;
  physics.angularVelocity.z += (targetRollRate - physics.angularVelocity.z) * dt * angularDamping;
  
  // Additional damping at high angles of attack
  if (Math.abs(physics.angleOfAttack) > Math.PI / 8) {
    const aoaFactor = (Math.abs(physics.angleOfAttack) - Math.PI / 8) * 5;
    physics.angularVelocity.multiplyScalar(1 - aoaFactor * dt);
  }
  
  // --- Update Orientation ---
  // Convert angular velocity to quaternion rotation
  const angVelVec = new THREE.Vector3(
    physics.angularVelocity.x,
    physics.angularVelocity.y,
    physics.angularVelocity.z
  );
  
  const halfAngle = angVelVec.length() * dt / 2;
  if (halfAngle > 0.001) {
    const quatDelta = new THREE.Quaternion();
    quatDelta.setFromAxisAngle(angVelVec.normalize(), angVelVec.length() * dt);
    physics.orientation.multiply(quatDelta);
    physics.orientation.normalize();
  }
  
  // --- Update Position ---
  physics.position.add(physics.velocity.clone().multiplyScalar(dt));
  
  // --- Ground Collision ---
  const terrainHeight = getTerrainHeight(physics.position.x, physics.position.z);
  const aircraftAltitude = physics.position.y;
  
  if (aircraftAltitude <= terrainHeight + 2) {
    physics.position.y = terrainHeight + 2;
    
    // Ground impact
    if (physics.velocity.y < -5) {
      // Hard landing/crash
      const impactForce = Math.abs(physics.velocity.y);
      physics.damage += impactForce * 0.1;
      
      // Bounce slightly
      physics.velocity.y = Math.abs(physics.velocity.y) * 0.2;
      
      if (physics.damage > 5) {
        showMessage("CRITICAL DAMAGE - MISSION FAILED");
        setTimeout(() => endGame(), 3000);
      }
    } else {
      physics.velocity.y = Math.max(0, physics.velocity.y);
    }
    
    // Check if on ground (low speed + low altitude)
    const horizontalSpeed = Math.sqrt(physics.velocity.x ** 2 + physics.velocity.z ** 2);
    physics.onGround = horizontalSpeed < 30 && aircraftAltitude <= terrainHeight + 5;
    
    // Dampen lateral movement on ground
    if (physics.onGround) {
      physics.velocity.x *= 0.98;
      physics.velocity.z *= 0.98;
      
      // Level the aircraft gradually when on ground
      const levelPitch = -physics.angularVelocity.x * dt * 2;
      const levelRoll = -physics.angularVelocity.z * dt * 2;
      physics.angularVelocity.x += levelPitch;
      physics.angularVelocity.z += levelRoll;
    }
  } else {
    physics.onGround = false;
  }
  
  // --- Stall Detection ---
  const stallSpeedMs = ac.stallSpeed * 0.514444; // Convert knots to m/s
  physics.isStalled = (physics.airspeed < stallSpeedMs && !physics.onGround) || 
                      Math.abs(physics.angleOfAttack) > Math.PI / 5;
  
  // --- G-Force Calculation ---
  const gForceVec = totalForce.clone().divideScalar(ac.mass);
  physics.gForce = Math.sqrt(gForceVec.x ** 2 + (gForceVec.y + GRAVITY) ** 2 + gForceVec.z ** 2) / GRAVITY;
  
  // --- Altitude and Heading ---
  physics.altitude = aircraftAltitude * 3.28084; // meters to feet
  
  const headingRad = Math.atan2(physics.velocity.x, physics.velocity.z);
  physics.heading = ((headingRad * 180 / Math.PI) + 360) % 360;
  
  // --- Smoke trail when at high AoA or low speed ---
  if (physics.isStalled && !physics.onGround && Math.random() < 0.3) {
    createSmokeParticle(physics.position.clone().add(
      new THREE.Vector3(-12, 0, 0).applyQuaternion(physics.orientation)
    ));
  }
  
  // --- Engine heat ---
  physics.engineHeat = physics.throttle * (1 + altitudeMeters / 5000);
}

// --- Enemy AI ---
function spawnEnemies(count) {
  for (let i = 0; i < count; i++) {
    const model = createEnemyAircraft();
    
    // Random position around the player
    const angle = Math.random() * Math.PI * 2;
    const dist = 1500 + Math.random() * 2000;
    const alt = 300 + Math.random() * 800;
    
    model.position.set(
      physics.position.x + Math.cos(angle) * dist,
      alt,
      physics.position.z + Math.sin(angle) * dist
    );
    
    scene.add(model);
    
    enemyAircraft.push({
      mesh: model,
      velocity: new THREE.Vector3((Math.random()-0.5)*100, 20, (Math.random()-0.5)*100),
      orientation: new THREE.Quaternion(),
      health: 100,
      state: 'patrol', // patrol, engage, evade
      targetHeading: Math.random() * Math.PI * 2,
      turnTimer: 0,
      alive: true
    });
  }
  
  enemiesRemaining = count;
}

function updateEnemies(dt) {
  const playerPos = physics.position.clone();
  
  for (const enemy of enemyAircraft) {
    if (!enemy.alive) continue;
    
    const toPlayer = playerPos.clone().sub(enemy.mesh.position);
    const distToPlayer = toPlayer.length();
    
    // State machine
    if (distToPlayer < 800) {
      enemy.state = 'engage';
    } else if (enemy.health < 30) {
      enemy.state = 'evade';
    } else {
      enemy.state = 'patrol';
    }
    
    // AI behavior
    let targetDir;
    switch(enemy.state) {
      case 'patrol':
        enemy.turnTimer -= dt;
        if (enemy.turnTimer <= 0) {
          enemy.targetHeading += (Math.random() - 0.5) * Math.PI;
          enemy.turnTimer = 3 + Math.random() * 5;
        }
        targetDir = new THREE.Vector3(
          Math.sin(enemy.targetHeading),
          0,
          Math.cos(enemy.targetHeading)
        );
        break;
        
      case 'engage':
        // Try to get behind the player
        const playerVelDir = physics.velocity.clone().normalize();
        const idealPos = playerPos.clone().sub(playerVelDir.multiplyScalar(200));
        targetDir = idealPos.clone().sub(enemy.mesh.position).normalize();
        
        // Fire at player if close enough and roughly aligned
        if (distToPlayer < 400 && Math.random() < dt * 0.5) {
          // Visual laser effect
          createLaserEffect(enemy.mesh.position, playerPos);
          
          // Hit chance based on distance
          if (Math.random() < 0.15) {
            physics.damage += 0.5;
            showDamageFlash();
          }
        }
        break;
        
      case 'evade':
        targetDir = toPlayer.clone().normalize().multiplyScalar(-1);
        break;
    }
    
    // Apply AI movement with inertia
    const currentHeading = new THREE.Vector3(1, 0, 0).applyQuaternion(enemy.orientation);
    const turnRate = 1.5 * dt;
    
    // Smoothly rotate towards target direction
    const dotProduct = Math.max(-1, Math.min(1, currentHeading.dot(targetDir)));
    const angleToTurn = Math.acos(dotProduct);
    
    if (angleToTurn > 0.01) {
      const axis = new THREE.Vector3().crossVectors(currentHeading, targetDir).normalize();
      const rotationQuat = new THREE.Quaternion().setFromAxisAngle(axis, Math.min(angleToTurn, turnRate));
      enemy.orientation.multiply(rotationQuat);
    }
    
    // Maintain altitude
    const upDir = new THREE.Vector3(0, 1, 0).applyQuaternion(enemy.orientation);
    if (enemy.mesh.position.y < 200) {
      const climbQuat = new THREE.Quaternion().setFromAxisAngle(
        new THREE.Vector3(0, 0, 1), dt * 0.5
      );
      enemy.orientation.multiply(climbQuat);
    } else if (enemy.mesh.position.y > 600) {
      const descendQuat = new THREE.Quaternion().setFromAxisAngle(
        new THREE.Vector3(0, 0, 1), -dt * 0.5
      );
      enemy.orientation.multiply(descendQuat);
    }
    
    // Move forward
    const speed = 80 + Math.random() * 20;
    const moveDir = new THREE.Vector3(1, 0, 0).applyQuaternion(enemy.orientation);
    enemy.mesh.position.add(moveDir.multiplyScalar(speed * dt));
    
    // Update mesh transform
    enemy.mesh.quaternion.copy(enemy.orientation);
    
    // Keep terrain clearance
    const th = getTerrainHeight(enemy.mesh.position.x, enemy.mesh.position.z);
    if (enemy.mesh.position.y < th + 50) {
      enemy.mesh.position.y = th + 50;
    }
  }
}

function createLaserEffect(from, to) {
  const dir = to.clone().sub(from).normalize();
  const midPoint = from.clone().add(dir.multiplyScalar(to.distanceTo(from) * 0.5));
  
  const geo = new THREE.SphereGeometry(2, 6, 6);
  const mat = new THREE.MeshBasicMaterial({ color: 0xff4444 });
  const mesh = new THREE.Mesh(geo, mat);
  mesh.position.copy(midPoint);
  scene.add(mesh);
  
  setTimeout(() => {
    scene.remove(mesh);
    geo.dispose();
    mat.dispose();
  }, 200);
}

// --- Player Shooting ---
function playerShoot() {
  if (gameState !== 'playing' || selectedScenario !== 'dogfight') return;
  
  const forward = new THREE.Vector3(1, 0, 0).applyQuaternion(physics.orientation);
  const fromPos = physics.position.clone().add(forward.multiplyScalar(15));
  
  // Check if any enemy is in crosshair
  for (const enemy of enemyAircraft) {
    if (!enemy.alive) continue;
    
    const toEnemy = enemy.mesh.position.clone().sub(physics.position);
    const dist = toEnemy.length();
    const dirToEnemy = toEnemy.normalize();
    
    // Check alignment with forward direction
    const alignment = forward.dot(dirToEnemy);
    
    if (alignment > 0.95 && dist < 600) {
      enemy.health -= 25 + Math.random() * 15;
      
      // Hit effect
      createLaserEffect(fromPos, enemy.mesh.position.clone());
      
      if (enemy.health <= 0) {
        enemy.alive = false;
        scene.remove(enemy.mesh);
        enemiesRemaining--;
        score += 1000;
        
        showMessage("ENEMY DESTROYED! +1000");
        
        // Explosion particles
        for (let i = 0; i < 20; i++) {
          createSmokeParticle(enemy.mesh.position.clone());
        }
      } else {
        score += 100;
      }
      
      break; // Only hit one enemy per shot
    }
  }
  
  // Visual muzzle flash
  const flashGeo = new THREE.SphereGeometry(3, 8, 8);
  const flashMat = new THREE.MeshBasicMaterial({ color: 0xffaa44 });
  const flash = new THREE.Mesh(flashGeo, flashMat);
  flash.position.copy(fromPos);
  scene.add(flash);
  
  setTimeout(() => {
    scene.remove(flash);
    flashGeo.dispose();
    flashMat.dispose();
  }, 100);
}

// --- Camera System ---
const cameraOffset = new THREE.Vector3(-25, 8, 0); // Behind and above aircraft
let cameraSmoothPos = new THREE.Vector3();
let cameraSmoothTarget = new THREE.Vector3();

function updateCamera() {
  if (!aircraftGroup) return;
  
  // Calculate desired camera position based on aircraft orientation
  const offsetTransformed = cameraOffset.clone().applyQuaternion(physics.orientation);
  const desiredPos = physics.position.clone().add(offsetTransformed);
  
  // Smooth camera follow (with slight lag for realism)
  const lerpFactor = 1 - Math.pow(0.02, clock.getDelta() || 0.016);
  cameraSmoothPos.lerp(desiredPos, lerpFactor * 3);
  
  // Camera target is slightly ahead of aircraft
  const lookAhead = new THREE.Vector3(30, 2, 0).applyQuaternion(physics.orientation);
  cameraSmoothTarget.copy(physics.position.clone().add(lookAhead));
  
  camera.position.lerp(cameraSmoothPos, 0.15);
  
  // Smoothly rotate camera to look at target
  const currentLook = new THREE.Vector3();
  camera.getWorldDirection(currentLook);
  const desiredLook = cameraSmoothTarget.clone().sub(camera.position).normalize();
  currentLook.lerp(desiredLook, 0.1);
  camera.lookAt(camera.position.clone().add(currentLook));
}

// --- HUD Rendering ---
const hudCanvas = document.getElementById('hud-canvas');
const hudCtx = hudCanvas.getContext('2d');

function resizeHUD() {
  hudCanvas.width = window.innerWidth;
  hudCanvas.height = window.innerHeight;
}

function renderHUD() {
  const w = hudCanvas.width;
  const h = hudCanvas.height;
  
  hudCtx.clearRect(0, 0, w, h);
  
  // --- Attitude Indicator (Artificial Horizon) ---
  const cx = w / 2;
  const cy = h / 2;
  const horizonRadius = Math.min(w, h) * 0.35;
  
  hudCtx.save();
  hudCtx.beginPath();
  hudCtx.arc(cx, cy, horizonRadius + 10, 0, Math.PI * 2);
  hudCtx.clip();
  
  // Get aircraft pitch and roll from orientation
  const euler = new THREE.Euler().setFromQuaternion(physics.orientation, 'YXZ');
  const pitchDeg = euler.x * 180 / Math.PI;
  const rollDeg = euler.z * 180 / Math.PI;
  
  hudCtx.translate(cx, cy);
  hudCtx.rotate(-rollDeg * Math.PI / 180);
  
  // Sky (above horizon)
  const pitchOffset = pitchDeg * (horizonRadius / 45);
  
  hudCtx.fillStyle = '#1a3a6c';
  hudCtx.fillRect(-horizonRadius*2, -horizonRadius*2 + pitchOffset, horizonRadius*4, horizonRadius*2);
  
  // Ground (below horizon)
  hudCtx.fillStyle = '#5a3a1a';
  hudCtx.fillRect(-horizonRadius*2, pitchOffset, horizonRadius*4, horizonRadius*2);
  
  // Horizon line
  hudCtx.strokeStyle = '#ffffff';
  hudCtx.lineWidth = 2;
  hudCtx.beginPath();
  hudCtx.moveTo(-horizonRadius, pitchOffset);
  hudCtx.lineTo(horizonRadius, pitchOffset);
  hudCtx.stroke();
  
  // Pitch ladder marks
  hudCtx.strokeStyle = 'rgba(255,255,255,0.6)';
  hudCtx.lineWidth = 1;
  hudCtx.font = '10px Courier New';
  hudCtx.fillStyle = '#ffffff';
  hudCtx.textAlign = 'center';
  
  for (let deg = -30; deg <= 30; deg += 5) {
    if (deg === 0) continue;
    const y = pitchOffset - deg * (horizonRadius / 45);
    const lineWidth = Math.abs(deg) % 10 === 0 ? 60 : 30;
    
    hudCtx.beginPath();
    hudCtx.moveTo(-lineWidth, y);
    hudCtx.lineTo(lineWidth, y);
    hudCtx.stroke();
    
    if (Math.abs(deg) % 10 === 0) {
      hudCtx.fillText(Math.abs(deg).toString(), lineWidth + 15, y + 3);
      hudCtx.fillText(Math.abs(deg).toString(), -lineWidth - 15, y + 3);
    }
  }
  
  hudCtx.restore();
  
  // --- Aircraft Symbol (fixed wings) ---
  hudCtx.strokeStyle = '#ffcc00';
  hudCtx.lineWidth = 2;
  
  // Left wing
  hudCtx.beginPath();
  hudCtx.moveTo(cx - 50, cy);
  hudCtx.lineTo(cx - 30, cy);
  hudCtx.stroke();
  
  // Right wing
  hudCtx.beginPath();
  hudCtx.moveTo(cx + 30, cy);
  hudCtx.lineTo(cx + 50, cy);
  hudCtx.stroke();
  
  // Center dot
  hudCtx.fillStyle = '#ffcc00';
  hudCtx.beginPath();
  hudCtx.arc(cx, cy, 3, 0, Math.PI * 2);
  hudCtx.fill();
  
  // --- Flight Path Vector ---
  const fpvX = cx + (physics.velocity.x / 150) * horizonRadius;
  const fpvY = cy - (physics.velocity.y / 150) * horizonRadius;
  
  hudCtx.strokeStyle = 'rgba(0,229,255,0.7)';
  hudCtx.lineWidth = 1.5;
  hudCtx.beginPath();
  hudCtx.arc(fpvX, fpvY, 8, 0, Math.PI * 2);
  hudCtx.stroke();
  
  // --- Heading Tape (top of screen) ---
  const headingTapeH = 40;
  const headingTapeW = w * 0.6;
  const headingTapeX = cx - headingTapeW / 2;
  const headingTapeY = h * 0.15;
  
  hudCtx.fillStyle = 'rgba(0,20,40,0.5)';
  hudCtx.fillRect(headingTapeX, headingTapeY, headingTapeW, headingTapeH);
  hudCtx.strokeStyle = 'rgba(0,229,255,0.3)';
  hudCtx.lineWidth = 1;
  hudCtx.strokeRect(headingTapeX, headingTapeY, headingTapeW, headingTapeH);
  
  hudCtx.font = '11px Courier New';
  hudCtx.fillStyle = '#88aacc';
  hudCtx.textAlign = 'center';
  
  const startHeading = Math.floor(physics.heading / 10) * 10 - 30;
  for (let hVal = startHeading; hVal <= startHeading + 60; hVal += 5) {
    const headingNorm = ((hVal % 360) + 360) % 360;
    const xOff = ((headingNorm - physics.heading + 180) % 360 - 180) / 60 * headingTapeW;
    
    if (xOff > -20 && xOff < headingTapeW + 20) {
      const markX = cx + xOff;
      
      hudCtx.beginPath();
      hudCtx.moveTo(markX, headingTapeY + headingTapeH - 5);
      hudCtx.lineTo(markX, headingTapeY + headingTapeH);
      hudCtx.stroke();
      
      if (hVal % 10 === 0) {
        const label = headingNorm < 10 ? '0' + Math.round(headingNorm) : Math.round(headingNorm).toString();
        hudCtx.fillText(label, markX, headingTapeY + 25);
      }
    }
  }
  
  // Heading pointer triangle
  hudCtx.fillStyle = '#ffcc00';
  hudCtx.beginPath();
  hudCtx.moveTo(cx, headingTapeY - 5);
  hudCtx.lineTo(cx - 6, headingTapeY - 12);
  hudCtx.lineTo(cx + 6, headingTapeY - 12);
  hudCtx.closePath();
  hudCtx.fill();
  
  // --- Speed Tape (left side) ---
  const speedTapeW = 50;
  const speedTapeH = h * 0.4;
  const speedTapeX = w * 0.12;
  const speedTapeY = cy - speedTapeH / 2;
  
  hudCtx.fillStyle = 'rgba(0,20,40,0.5)';
  hudCtx.fillRect(speedTapeX, speedTapeY, speedTapeW, speedTapeH);
  hudCtx.strokeStyle = 'rgba(0,229,255,0.3)';
  hudCtx.strokeRect(speedTapeX, speedTapeY, speedTapeW, speedTapeH);
  
  const speedKts = Math.round(physics.airspeed * 1.94384); // m/s to knots
  const startSpeed = Math.floor(speedKts / 20) * 20 - 50;
  
  hudCtx.font = '10px Courier New';
  hudCtx.fillStyle = '#88aacc';
  hudCtx.textAlign = 'right';
  
  for (let s = startSpeed; s <= startSpeed + 100; s += 10) {
    const yOff = ((s - speedKts) / 100) * speedTapeH;
    const markY = cy - yOff;
    
    if (markY > speedTapeY && markY < speedTapeY + speedTapeH) {
      hudCtx.beginPath();
      hudCtx.moveTo(speedTapeX + speedTapeW, markY);
      hudCtx.lineTo(speedTapeX + speedTapeW - 8, markY);
      hudCtx.stroke();
      
      if (s % 20 === 0) {
        hudCtx.fillText(s.toString(), speedTapeX + speedTapeW - 12, markY + 3);
      }
    }
  }
  
  // Speed value box
  hudCtx.fillStyle = 'rgba(0,40,80,0.8)';
  hudCtx.fillRect(speedTapeX + speedTapeW, cy - 12, 50, 24);
  hudCtx.strokeStyle = '#00e5ff';
  hudCtx.strokeRect(speedTapeX + speedTapeW, cy - 12, 50, 24);
  
  hudCtx.font = 'bold 14px Courier New';
  hudCtx.fillStyle = '#ffffff';
  hudCtx.textAlign = 'center';
  hudCtx.fillText(speedKts.toString(), speedTapeX + speedTapeW + 25, cy + 5);
  
  // --- Altitude Tape (right side) ---
  const altTapeX = w - w * 0.12 - speedTapeW;
  
  hudCtx.fillStyle = 'rgba(0,20,40,0.5)';
  hudCtx.fillRect(altTapeX, speedTapeY, speedTapeW, speedTapeH);
  hudCtx.strokeStyle = 'rgba(0,229,255,0.3)';
  hudCtx.strokeRect(altTapeX, speedTapeY, speedTapeW, speedTapeH);
  
  const altFt = Math.round(physics.altitude);
  const startAlt = Math.floor(altFt / 200) * 200 - 500;
  
  hudCtx.textAlign = 'left';
  
  for (let a = startAlt; a <= startAlt + 1000; a += 100) {
    const yOff = ((a - altFt) / 1000) * speedTapeH;
    const markY = cy - yOff;
    
    if (markY > speedTapeY && markY < speedTapeY + speedTapeH) {
      hudCtx.beginPath();
      hudCtx.moveTo(altTapeX, markY);
      hudCtx.lineTo(altTapeX + 8, markY);
      hudCtx.stroke();
      
      if (a % 200 === 0) {
        hudCtx.fillText(Math.round(a / 100).toString(), altTapeX + 12, markY + 3);
      }
    }
  }
  
  // Altitude value box
  hudCtx.fillStyle = 'rgba(0,40,80,0.8)';
  hudCtx.fillRect(altTapeX - 50, cy - 12, 50, 24);
  hudCtx.strokeStyle = '#00e5ff';
  hudCtx.strokeRect(altTapeX - 50, cy - 12, 50, 24);
  
  hudCtx.font = 'bold 14px Courier New';
  hudCtx.fillStyle = '#ffffff';
  hudCtx.textAlign = 'center';
  hudCtx.fillText(Math.round(altFt / 100).toString(), altTapeX - 25, cy + 5);
  
  // --- Throttle Bar (bottom left) ---
  const throttleBarW = 30;
  const throttleBarH = h * 0.25;
  const throttleBarX = w * 0.12;
  const throttleBarY = cy + speedTapeH / 2 + 40;
  
  hudCtx.fillStyle = 'rgba(0,20,40,0.5)';
  hudCtx.fillRect(throttleBarX, throttleBarY, throttleBarW, throttleBarH);
  hudCtx.strokeStyle = 'rgba(0,229,255,0.3)';
  hudCtx.strokeRect(throttleBarX, throttleBarY, throttleBarW, throttleBarH);
  
  const throttleFill = physics.throttle * (throttleBarH - 4);
  const throttleGrad = hudCtx.createLinearGradient(0, throttleBarY + throttleBarH, 0, throttleBarY);
  throttleGrad.addColorStop(0, '#00aa44');
  throttleGrad.addColorStop(0.7, '#ffaa00');
  throttleGrad.addColorStop(1, '#ff4444');
  hudCtx.fillStyle = throttleGrad;
  hudCtx.fillRect(throttleBarX + 2, throttleBarY + throttleBarH - throttleFill, throttleBarW - 4, throttleFill);
  
  // Throttle label
  hudCtx.font = '10px Courier New';
  hudCtx.fillStyle = '#88aacc';
  hudCtx.textAlign = 'center';
  hudCtx.fillText('THR', throttleBarX + throttleBarW / 2, throttleBarY - 5);
  
  // --- G-Meter (bottom right) ---
  const gMeterX = w - w * 0.12 - speedTapeW;
  const gMeterY = cy + speedTapeH / 2 + 40;
  
  hudCtx.fillStyle = 'rgba(0,20,40,0.5)';
  hudCtx.fillRect(gMeterX, gMeterY, throttleBarW, throttleBarH);
  hudCtx.strokeStyle = 'rgba(0,229,255,0.3)';
  hudCtx.strokeRect(gMeterX, gMeterY, throttleBarW, throttleBarH);
  
  const gCenter = gMeterY + throttleBarH / 2;
  const gNorm = Math.max(-1, Math.min(1, (physics.gForce - 1) / 5));
  const gMarkY = gCenter - gNorm * (throttleBarH / 2 - 4);
  
  const acRef = AIRCRAFT[selectedAircraft];
  hudCtx.fillStyle = physics.gForce > acRef.gLimit ? '#ff4444' : '#00e5ff';
  hudCtx.beginPath();
  hudCtx.arc(gMeterX + throttleBarW / 2, gMarkY, 6, 0, Math.PI * 2);
  hudCtx.fill();
  
  // G reference marks
  for (let g = -3; g <= 5; g++) {
    const gy = gCenter - ((g) / 5) * (throttleBarH / 2 - 4);
    if (gy > gMeterY && gy < gMeterY + throttleBarH) {
      hudCtx.strokeStyle = 'rgba(255,255,255,0.3)';
      hudCtx.beginPath();
      hudCtx.moveTo(gMeterX, gy);
      hudCtx.lineTo(gMeterX + 6, gy);
      hudCtx.stroke();
    }
  }
  
  hudCtx.font = '10px Courier New';
  hudCtx.fillStyle = '#88aacc';
  hudCtx.textAlign = 'center';
  hudCtx.fillText('G', gMeterX + throttleBarW / 2, gMeterY - 5);
  
  // --- Stall Warning Visual ---
  if (physics.isStalled) {
    document.getElementById('stall-warning').classList.add('active');
    
    // Red flash on horizon
    hudCtx.fillStyle = 'rgba(255,0,0,' + (0.1 + Math.sin(Date.now() * 0.01) * 0.05) + ')';
    hudCtx.fillRect(0, 0, w, h);
  } else {
    document.getElementById('stall-warning').classList.remove('active');
  }
  
  // --- G-Force Warning ---
  const acData = AIRCRAFT[selectedAircraft];
  if (physics.gForce > acData.gLimit * 0.85) {
    document.getElementById('g-warning').classList.add('active');
    
    // Yellow vignette at high G
    hudCtx.fillStyle = 'rgba(255,170,0,' + ((physics.gForce - acData.gLimit * 0.85) / (acData.gLimit * 0.3)) * 0.15 + ')';
    hudCtx.fillRect(0, 0, w, h);
  } else {
    document.getElementById('g-warning').classList.remove('active');
  }
  
  // --- Damage Overlay ---
  if (physics.damage > 0) {
    const dmgOverlay = document.getElementById('damage-overlay');
    dmgOverlay.style.border = `${Math.min(30, physics.damage * 5)}px solid rgba(255,0,0,${Math.min(0.4, physics.damage * 0.08)})`;
  } else {
    document.getElementById('damage-overlay').style.border = '0px solid rgba(255,0,0,0)';
  }
  
  // --- Update DOM HUD elements ---
  const speedKtsVal = Math.round(physics.airspeed * 1.94384);
  document.getElementById('speed-val').textContent = speedKtsVal;
  document.getElementById('alt-val').textContent = Math.round(physics.altitude);
  document.getElementById('throttle-val').textContent = Math.round(physics.throttle * 100);
  document.getElementById('g-val').textContent = physics.gForce.toFixed(1);
  document.getElementById('heading-val').textContent = Math.round(physics.heading);
  document.getElementById('aoa-val').textContent = (physics.angleOfAttack * 180 / Math.PI).toFixed(1);
  
  // --- Dogfight HUD extras ---
  if (selectedScenario === 'dogfight') {
    document.getElementById('score-panel').classList.add('active');
    document.getElementById('score-val').textContent = score;
    document.getElementById('enemy-count').textContent = enemiesRemaining;
    
    // Enemy indicators on screen edges
    for (const enemy of enemyAircraft) {
      if (!enemy.alive) continue;
      
      const toEnemy = enemy.mesh.position.clone().sub(camera.position);
      toEnemy.applyQuaternion(camera.quaternion.clone().invert());
      
      if (toEnemy.x > 0) { // In front of camera
        const screenX = cx + (toEnemy.y / Math.max(1, toEnemy.x)) * horizonRadius;
        const screenY = cy - (toEnemy.z / Math.max(1, toEnemy.x)) * horizonRadius;
        
        if (screenX > 30 && screenX < w - 30 && screenY > 30 && screenY < h - 30) {
          hudCtx.strokeStyle = '#ff4444';
          hudCtx.lineWidth = 2;
          
          // Diamond indicator
          const size = Math.max(8, 15 - toEnemy.x / 200);
          hudCtx.beginPath();
          hudCtx.moveTo(screenX, screenY - size);
          hudCtx.lineTo(screenX + size, screenY);
          hudCtx.lineTo(screenX, screenY + size);
          hudCtx.lineTo(screenX - size, screenY);
          hudCtx.closePath();
          hudCtx.stroke();
          
          // Distance label
          const distKm = (toEnemy.length() / 1000).toFixed(1);
          hudCtx.font = '9px Courier New';
          hudCtx.fillStyle = '#ff4444';
          hudCtx.textAlign = 'center';
          hudCtx.fillText(distKm + 'km', screenX, screenY - size - 5);
        }
      }
    }
    
    // Target reticle when enemy is near crosshair
    let targetLocked = false;
    for (const enemy of enemyAircraft) {
      if (!enemy.alive) continue;
      
      const toEnemy = enemy.mesh.position.clone().sub(physics.position);
      const dist = toEnemy.length();
      const dirToEnemy = toEnemy.normalize();
      const forward = new THREE.Vector3(1, 0, 0).applyQuaternion(physics.orientation);
      const alignment = forward.dot(dirToEnemy);
      
      if (alignment > 0.95 && dist < 600) {
        targetLocked = true;
        break;
      }
    }
    
    // Draw targeting reticle
    hudCtx.strokeStyle = targetLocked ? '#ff4444' : 'rgba(255,68,68,0.3)';
    hudCtx.lineWidth = 1;
    const retSize = 25;
    hudCtx.beginPath();
    hudCtx.moveTo(cx - retSize, cy);
    hudCtx.lineTo(cx - retSize + 8, cy);
    hudCtx.stroke();
    hudCtx.beginPath();
    hudCtx.moveTo(cx + retSize - 8, cy);
    hudCtx.lineTo(cx + retSize, cy);
    hudCtx.stroke();
    hudCtx.beginPath();
    hudCtx.moveTo(cx, cy - retSize);
    hudCtx.lineTo(cx, cy - retSize + 8);
    hudCtx.stroke();
    hudCtx.beginPath();
    hudCtx.moveTo(cx, cy + retSize - 8);
    hudCtx.lineTo(cx, cy + retSize);
    hudCtx.stroke();
    
    if (targetLocked) {
      hudCtx.fillStyle = '#ff4444';
      hudCtx.font = '10px Courier New';
      hudCtx.textAlign = 'center';
      hudCtx.fillText('TARGET LOCKED', cx, cy + retSize + 15);
    }
  } else {
    document.getElementById('score-panel').classList.remove('active');
    
    // Landing scenario: show runway alignment indicator
    if (physics.altitude < 2000 && !physics.onGround) {
      const runwayDir = new THREE.Vector3(0, 0, -1); // Runway direction
      const aircraftHeading = new THREE.Vector3(1, 0, 0).applyQuaternion(physics.orientation);
      
      const alignmentAngle = Math.acos(Math.max(-1, Math.min(1, 
        (aircraftHeading.x * runwayDir.x + aircraftHeading.z * runwayDir.z) / 
        (Math.sqrt(aircraftHeading.x**2+aircraftHeading.z**2) || 1)))) * 180 / Math.PI;
      
      hudCtx.font = '12px Courier New';
      hudCtx.fillStyle = alignmentAngle < 5 ? '#00ff88' : alignmentAngle < 15 ? '#ffaa00' : '#ff4444';
      hudCtx.textAlign = 'center';
      hudCtx.fillText(`RUNWAY ALIGN: ${alignmentAngle.toFixed(1)}°`, cx, h * 0.7);
      
      // Approach speed recommendation
      const acData2 = AIRCRAFT[selectedAircraft];
      const approachSpeed = Math.round(acData2.stallSpeed * 1.3);
      hudCtx.fillStyle = '#88aacc';
      hudCtx.fillText(`APPROACH SPD: ${approachSpeed} KTS`, cx, h * 0.7 + 20);
    }
    
    // Landing score display
    if (landingScore !== null) {
      const lsEl = document.getElementById('landing-score');
      lsEl.classList.add('active');
      
      let grade, color;
      if (landingScore >= 90) { grade = 'EXCELLENT'; color = '#00ff88'; }
      else if (landingScore >= 70) { grade = 'GOOD'; color = '#aaff00'; }
      else if (landingScore >= 50) { grade = 'FAIR'; color = '#ffaa00'; }
      else { grade = 'POOR'; color = '#ff4444'; }
      
      lsEl.innerHTML = `<div style="color:${color}">${grade}</div><div>LANDING SCORE: ${landingScore}/100</div>`;
    }
  }
  
  // --- Minimap ---
  renderMinimap();
}

function renderMinimap() {
  const canvas = document.getElementById('minimap-inner');
  const ctx = canvas.getContext('2d');
  const w = 200, h = 200;
  
  ctx.clearRect(0, 0, w, h);
  
  // Background
  ctx.fillStyle = 'rgba(0,20,40,0.8)';
  ctx.beginPath();
  ctx.arc(w/2, h/2, w/2, 0, Math.PI * 2);
  ctx.fill();
  
  const scale = 0.05; // pixels per meter
  const cx = w / 2;
  const cy = h / 2;
  
  // Draw terrain contour (simplified)
  for (let x = -100; x <= 100; x += 8) {
    for (let z = -100; z <= 100; z += 8) {
      const wx = physics.position.x + x / scale;
      const wz = physics.position.z + z / scale;
      const th = getTerrainHeight(wx, wz);
      
      if (th > 50) {
        ctx.fillStyle = `rgba(100,80,60,${Math.min(0.3, th/500)})`;
        ctx.fillRect(cx + x - 2, cy + z - 2, 4, 4);
      }
    }
  }
  
  // Draw runway
  const rwRelX = (0 - physics.position.x) * scale;
  const rwRelZ = (250 - physics.position.z) * scale;
  ctx.strokeStyle = '#ffffff';
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.moveTo(cx + rwRelX, cy + rwRelZ - 60);
  ctx.lineTo(cx + rwRelX, cy + rwRelZ + 60);
  ctx.stroke();
  
  // Draw enemies (dogfight)
  if (selectedScenario === 'dogfight') {
    for (const enemy of enemyAircraft) {
      if (!enemy.alive) continue;
      
      const ex = cx + (enemy.mesh.position.x - physics.position.x) * scale;
      const ez = cy + (enemy.mesh.position.z - physics.position.z) * scale;
      
      if (ex > 0 && ex < w && ez > 0 && ez < h) {
        ctx.fillStyle = '#ff4444';
        ctx.beginPath();
        ctx.arc(ex, ez, 3, 0, Math.PI * 2);
        ctx.fill();
      }
    }
  }
  
  // Draw player (center)
  const euler = new THREE.Euler().setFromQuaternion(physics.orientation, 'YXZ');
  ctx.save();
  ctx.translate(cx, cy);
  ctx.rotate(-euler.y);
  
  ctx.fillStyle = '#00e5ff';
  ctx.beginPath();
  ctx.moveTo(0, -8);
  ctx.lineTo(-5, 6);
  ctx.lineTo(5, 6);
  ctx.closePath();
  ctx.fill();
  
  ctx.restore();
  
  // Compass ring
  ctx.strokeStyle = 'rgba(0,229,255,0.3)';
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.arc(cx, cy, w/2 - 2, 0, Math.PI * 2);
  ctx.stroke();
  
  // Cardinal directions
  ctx.font = '8px Courier New';
  ctx.fillStyle = '#6688aa';
  ctx.textAlign = 'center';
  ctx.fillText('N', cx, 12);
  ctx.fillText('S', cx, h - 5);
  ctx.fillText('E', w - 8, cy + 3);
  ctx.fillText('W', 8, cy + 3);
}

// --- Message System ---
let messageTimeout;
function showMessage(text) {
  const el = document.getElementById('message-popup');
  el.textContent = text;
  el.classList.add('show');
  
  clearTimeout(messageTimeout);
  messageTimeout = setTimeout(() => {
    el.classList.remove('show');
  }, 3000);
}

function showDamageFlash() {
  const overlay = document.getElementById('damage-overlay');
  overlay.style.border = '15px solid rgba(255,0,0,0.6)';
  setTimeout(() => {
    if (physics.damage <= 0) {
      overlay.style.border = '0px solid rgba(255,0,0,0)';
    }
  }, 300);
}

// --- Landing Evaluation ---
function evaluateLanding() {
  const acData = AIRCRAFT[selectedAircraft];
  
  // Factors: touchdown speed, vertical speed, alignment with runway
  const touchSpeed = physics.airspeed * 1.94384; // knots
  const approachSpeed = acData.stallSpeed * 1.3;
  const vertSpeed = Math.abs(physics.velocity.y);
  
  // Speed score (0-40)
  let speedScore = 40;
  if (touchSpeed > approachSpeed + 20) {
    speedScore -= Math.min(40, (touchSpeed - approachSpeed - 20) * 2);
  } else if (touchSpeed < approachSpeed - 10) {
    speedScore -= Math.min(40, (approachSpeed - 10 - touchSpeed) * 3);
  }
  
  // Vertical speed score (0-30)
  let vertScore = 30;
  if (vertSpeed > 3) {
    vertScore -= Math.min(30, (vertSpeed - 3) * 10);
  }
  
  // Alignment score (0-30)
  const runwayDir = new THREE.Vector3(0, 0, -1);
  const aircraftHeading = new THREE.Vector3(1, 0, 0).applyQuaternion(physics.orientation);
  const headingAngle = Math.acos(Math.max(-1, Math.min(1, 
    (aircraftHeading.x * runwayDir.x + aircraftHeading.z * runwayDir.z) / 
    (Math.sqrt(aircraftHeading.x**2+aircraftHeading.z**2) || 1)))) * 180 / Math.PI;
  
  let alignScore = 30;
  if (headingAngle > 5) {
    alignScore -= Math.min(30, headingAngle * 2);
  }
  
  landingScore = Math.max(0, Math.round(speedScore + vertScore + alignScore));
  score += landingScore * 10;
}

// --- Game Flow ---
function startMission() {
  document.getElementById('briefing').classList.remove('active');
  gameState = 'playing';
  
  // Reset physics state based on scenario
  if (selectedScenario === 'takeoffLanding') {
    // Start on runway (runway surface is at y=1.5, aircraft sits above it)
    physics.position.set(0, 4, -250);
    physics.velocity.set(0, 0, 0);
    physics.orientation.set(0, 0, 0, 1);
    physics.throttle = 0;
    physics.onGround = true;
    
    showMessage("TAKEOFF MISSION - BUILD SPEED AND LIFTOFF");
  } else {
    // Dogfight: start airborne
    physics.position.set(0, 500, 0);
    physics.velocity.set(80, 10, 0);
    physics.orientation.set(0, 0, 0, 1);
    physics.throttle = 0.7;
    physics.onGround = false;
    
    spawnEnemies(4);
    showMessage("COMBAT ENGAGEMENT - DESTROY ALL HOSTILES");
  }
  
  // Reset score and damage
  score = 0;
  physics.damage = 0;
  landingScore = null;
  document.getElementById('landing-score').classList.remove('active');
}

function endGame() {
  gameState = 'ended';
  showMessage("MISSION COMPLETE - SCORE: " + score);
  
  setTimeout(() => {
    backToMenu();
  }, 5000);
}

function resumeGame() {
  document.getElementById('pause-menu').classList.remove('active');
  gameState = 'playing';
}

function restartGame() {
  // Reset everything
  physics.position.set(0, 50, 0);
  physics.velocity.set(0, 0, 0);
  physics.orientation.set(0, 0, 0, 1);
  physics.throttle = 0;
  physics.damage = 0;
  score = 0;
  
  // Remove enemies
  for (const enemy of enemyAircraft) {
    scene.remove(enemy.mesh);
  }
  enemyAircraft = [];
  
  document.getElementById('pause-menu').classList.remove('active');
  
  showBriefing();
}

function backToMenu() {
  gameState = 'menu';
  
  // Remove enemies
  for (const enemy of enemyAircraft) {
    scene.remove(enemy.mesh);
  }
  enemyAircraft = [];
  
  document.getElementById('hud').classList.remove('active');
  document.getElementById('pause-menu').classList.remove('active');
  document.getElementById('briefing').classList.remove('active');
  document.getElementById('menu-screen').classList.add('active');
}

function showBriefing() {
  gameState = 'briefing';
  
  const scenario = SCENARIOS[selectedScenario];
  document.getElementById('briefing-title').textContent = scenario.name;
  document.getElementById('briefing-text').textContent = scenario.briefing;
  document.getElementById('briefing').classList.add('active');
}

// --- Menu UI Generation ---
function generateMenuUI() {
  const aircraftContainer = document.getElementById('aircraft-select');
  
  for (const [key, ac] of Object.entries(AIRCRAFT)) {
    const card = document.createElement('div');
    card.className = 'aircraft-card' + (key === selectedAircraft ? ' selected' : '');
    card.onclick = () => selectAircraft(key);
    
    const stats = [
      ['MAX SPEED', ac.maxSpeed, 700],
      ['THRUST/WT', ac.thrustToWeight.toFixed(1), 2.5],
      ['TURN RATE', ac.turnRate.toFixed(1), 3.5],
      ['ROLL RATE', ac.rollRate.toFixed(1), 4.0],
      ['G LIMIT', ac.gLimit, 10]
    ];
    
    let statsHTML = '';
    for (const [label, value, max] of stats) {
      const pct = Math.min(100, (value / max) * 100);
      const color = pct > 70 ? '#00e5ff' : pct > 40 ? '#ffaa00' : '#ff4444';
      statsHTML += `
        <div class="stat-row">
          <span class="stat-label">${label}</span>
          <span class="stat-value">${value}</span>
        </div>
        <div class="stat-bar"><div class="stat-fill" style="width:${pct}%;background:${color};"></div></div>
      `;
    }
    
    card.innerHTML = `
      <h3>${ac.name}</h3>
      <div class="type">${ac.type}</div>
      ${statsHTML}
    `;
    
    aircraftContainer.appendChild(card);
  }
  
  const scenarioContainer = document.getElementById('scenario-select');
  
  for (const [key, sc] of Object.entries(SCENARIOS)) {
    const card = document.createElement('div');
    card.className = 'scenario-card' + (key === selectedScenario ? ' selected' : '');
    card.onclick = () => selectScenario(key);
    
    card.innerHTML = `
      <h3>${sc.name}</h3>
      <p style="color:#00e5ff;margin-bottom:8px;">${sc.subtitle}</p>
      <p>${sc.description}</p>
    `;
    
    scenarioContainer.appendChild(card);
  }
}

function selectAircraft(key) {
  selectedAircraft = key;
  document.querySelectorAll('.aircraft-card').forEach((card, i) => {
    card.classList.toggle('selected', Object.keys(AIRCRAFT)[i] === key);
  });
}

function selectScenario(key) {
  selectedScenario = key;
  document.querySelectorAll('.scenario-card').forEach((card, i) => {
    card.classList.toggle('selected', Object.keys(SCENARIOS)[i] === key);
  });
}

// --- Input Handling ---
function setupInput() {
  // Mouse movement for pitch/roll
  document.addEventListener('mousemove', (e) => {
    if (!input.mouseLocked || gameState !== 'playing') return;
    
    const sensitivity = 0.003;
    input.roll = Math.max(-1, Math.min(1, e.movementX * sensitivity));
    input.pitch = Math.max(-1, Math.min(1, -e.movementY * sensitivity));
  });
  
  // Keyboard controls
  document.addEventListener('keydown', (e) => {
    if (gameState === 'playing') {
      switch(e.code) {
        case 'KeyW': input.throttleUp = true; break;
        case 'KeyS': input.throttleDown = true; break;
        case 'KeyQ': input.yaw = -1; break;
        case 'KeyE': input.yaw = 1; break;
        case 'ShiftLeft': case 'ShiftRight': input.brake = true; break;
        case 'Space': 
          e.preventDefault();
          if (selectedScenario === 'dogfight') playerShoot();
          else {
            // Flare for landing - reduce descent rate
            physics.angularVelocity.x -= 0.5;
          }
          break;
        case 'Escape':
          gameState = 'paused';
          document.getElementById('pause-menu').classList.add('active');
          break;
      }
    } else if (gameState === 'paused') {
      if (e.code === 'Escape') resumeGame();
    }
  });
  
  document.addEventListener('keyup', (e) => {
    switch(e.code) {
      case 'KeyW': input.throttleUp = false; break;
      case 'KeyS': input.throttleDown = false; break;
      case 'KeyQ': input.yaw = 0; break;
      case 'KeyE': input.yaw = 0; break;
      case 'ShiftLeft': case 'ShiftRight': input.brake = false; break;
    }
  });
  
  // Click to lock mouse for flight controls
  document.addEventListener('click', () => {
    if (gameState === 'playing' && !input.mouseLocked) {
      document.body.requestPointerLock();
    }
  });
  
  document.addEventListener('pointerlockchange', () => {
    input.mouseLocked = !!document.pointerLockElement;
  });
}

// --- Lighting Setup ---
function setupLighting() {
  // Ambient light
  const ambient = new THREE.AmbientLight(0x4466aa, 0.4);
  scene.add(ambient);
  
  // Hemisphere light for sky/ground color variation
  const hemi = new THREE.HemisphereLight(0x87ceeb, 0x3a2a1a, 0.5);
  scene.add(hemi);
  
  // Main directional light (sun) - golden hour angle
  const sun = new THREE.DirectionalLight(0xffeedd, 1.2);
  sun.position.set(-2000, 800, 1000);
  sun.castShadow = true;
  scene.add(sun);
  
  // Secondary fill light
  const fill = new THREE.DirectionalLight(0x6688cc, 0.3);
  fill.position.set(2000, 400, -1000);
  scene.add(fill);
  
  // Sun visual (glow sphere)
  const sunGeo = new THREE.SphereGeometry(50, 16, 16);
  const sunMat = new THREE.MeshBasicMaterial({ color: 0xffeeaa });
  const sunMesh = new THREE.Mesh(sunGeo, sunMat);
  sunMesh.position.copy(sun.position).normalize().multiplyScalar(14000);
  scene.add(sunMesh);
}

// --- Clouds ---
function createClouds() {
  for (let i = 0; i < 60; i++) {
    const cloudGroup = new THREE.Group();
    
    // Each cloud is made of several spheres
    const numPuffs = 3 + Math.floor(Math.random() * 5);
    for (let j = 0; j < numPuffs; j++) {
      const puffGeo = new THREE.SphereGeometry(20 + Math.random() * 40, 8, 6);
      const puffMat = new THREE.MeshStandardMaterial({
        color: 0xffffff,
        transparent: true,
        opacity: 0.7 + Math.random() * 0.2,
        roughness: 1,
        metalness: 0
      });
      const puff = new THREE.Mesh(puffGeo, puffMat);
      puff.position.set(
        (Math.random() - 0.5) * 60,
        (Math.random() - 0.5) * 15,
        (Math.random() - 0.5) * 40
      );
      puff.scale.y = 0.3 + Math.random() * 0.2;
      cloudGroup.add(puff);
    }
    
    // Position clouds at various altitudes and locations
    const angle = Math.random() * Math.PI * 2;
    const dist = 500 + Math.random() * 4000;
    cloudGroup.position.set(
      Math.cos(angle) * dist,
      300 + Math.random() * 1500,
      Math.sin(angle) * dist
    );
    
    scene.add(cloudGroup);
  }
}

// --- Main Game Loop ---
function gameLoop() {
  requestAnimationFrame(gameLoop);
  
  const dt = clock.getDelta();
  
  if (gameState === 'playing') {
    // Update physics
    updatePhysics(dt);
    
    // Update enemies
    if (selectedScenario === 'dogfight') {
      updateEnemies(dt);
      
      // Check win condition
      if (enemiesRemaining <= 0) {
        showMessage("ALL HOSTILES NEUTRALIZED - MISSION COMPLETE!");
        score += 5000;
        setTimeout(() => endGame(), 3000);
      }
    } else {
      // Landing scenario: check if landed on runway
      if (physics.onGround && Math.abs(physics.position.x) < 25 && 
          physics.position.z > -100 && physics.position.z < 700) {
        if (!landingScore) {
          evaluateLanding();
          showMessage(`LANDING COMPLETE! SCORE: ${landingScore}/100`);
          
          setTimeout(() => endGame(), 4000);
        }
      }
    }
    
    // Update aircraft model transform
    if (aircraftGroup) {
      aircraftGroup.position.copy(physics.position);
      aircraftGroup.quaternion.copy(physics.orientation);
      
      // Engine exhaust glow based on throttle
      aircraftGroup.traverse((child) => {
        if (child.name === 'exhaust') {
          child.material.opacity = 0.2 + physics.throttle * 0.6;
          const heatColor = new THREE.Color().setHSL(
            0.08 - physics.engineHeat * 0.05, 
            1, 
            0.3 + physics.throttle * 0.4
          );
          child.material.color.copy(heatColor);
        }
      });
    }
    
    // Update camera
    updateCamera();
    
    // Update particles
    updateParticles(dt);
    
    // Update audio
    updateAudio();
    
    // Render HUD
    renderHUD();
  }
  
  renderer.render(scene, camera);
}

// --- Initialization ---
async function init() {
  const loadBar = document.getElementById('load-bar');
  const loadText = document.getElementById('load-text');
  
  // Step 1: Setup Three.js scene
  loadText.textContent = 'INITIALIZING RENDERER...';
  loadBar.style.width = '10%';
  
  scene = new THREE.Scene();
  scene.fog = new THREE.FogExp2(0x87ceeb, 0.00008);
  
  camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 1, 20000);
  camera.position.set(0, 100, -50);
  
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  renderer.toneMappingExposure = 1.0;
  document.body.appendChild(renderer.domElement);
  
  clock = new THREE.Clock();
  
  await sleep(200);
  
  // Step 2: Generate terrain
  loadText.textContent = 'GENERATING TERRAIN...';
  loadBar.style.width = '30%';
  generateTerrain();
  
  await sleep(200);
  
  // Step 3: Create water
  loadText.textContent = 'CREATING WATER BODIES...';
  loadBar.style.width = '45%';
  createWater();
  
  await sleep(150);
  
  // Step 4: Sky and lighting
  loadText.textContent = 'SETTING UP ATMOSPHERE...';
  loadBar.style.width = '60%';
  createSky();
  setupLighting();
  createClouds();
  
  await sleep(150);
  
  // Step 5: Runway
  loadText.textContent = 'BUILDING AIRFIELD...';
  loadBar.style.width = '75%';
  createRunway();
  
  await sleep(150);
  
  // Step 6: Aircraft model
  loadText.textContent = 'LOADING AIRCRAFT...';
  loadBar.style.width = '85%';
  aircraftGroup = createAircraftModel(selectedAircraft);
  scene.add(aircraftGroup);
  
  await sleep(150);
  
  // Step 7: Audio and input
  loadText.textContent = 'INITIALIZING AUDIO SYSTEMS...';
  loadBar.style.width = '95%';
  initAudio();
  setupInput();
  
  await sleep(200);
  
  // Done loading
  loadText.textContent = 'SYSTEMS READY';
  loadBar.style.width = '100%';
  
  await sleep(800);
  
  document.getElementById('loading-screen').classList.add('hidden');
  setTimeout(() => {
    document.getElementById('loading-screen').style.display = 'none';
    document.getElementById('menu-screen').classList.add('active');
    gameState = 'menu';
    
    generateMenuUI();
    resizeHUD();
    
    // Launch button handler
    document.getElementById('launch-btn').addEventListener('click', () => {
      if (audioCtx && audioCtx.state === 'suspended') {
        audioCtx.resume();
      }
      
      document.getElementById('menu-screen').classList.remove('active');
      showBriefing();
    });
  }, 500);
  
  // Start game loop
  gameLoop();
}

function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }

// --- Window Resize Handler ---
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  resizeHUD();
});

// --- Start ---
init().catch(console.error);
</script>
</body>
</html>
<!-- agent-meta {"model":"qwen3.6-27b-mtp","provider":"lmstudio","persona":"full","sessionId":"c5e10443-ad9f-495b-8fa6-c7c5967fab94","tokensIn":2205081,"tokensOut":39124,"tokensTotal":2244205,"turns":35,"toolCalls":34,"failedToolCalls":1,"timestamp":"2026-07-29T14:54:29.199Z"} -->