import { Application, Graphics, Text, TextStyle, Container } from "pixi.js";
import { GlowFilter } from "pixi-filters";
import { CANVAS_WIDTH, CANVAS_HEIGHT } from "./game.config";
import {
  unlockAudio,
  startBgMusic,
  stopBgMusic,
  sfxGreenCandle,
  sfxRedCandle,
  sfxBooost,
  sfxCollapseWarning,
} from "./audio";
import { initParticles, updateParticles, spawnRGBParticles, spawnSimpleParticles } from "./particles";

const app = new Application();

// ─── PIXEL SIZE ────────────────────────────────────────────────────────────────
const PX = 1.5;

// ─── DETECT TOUCH DEVICE ─────────────────────────────────────────────────────
const isTouchDevice = () => ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);

// ─── COLORS ───────────────────────────────────────────────────────────────────
const C = {
  bg:        0x080810,
  bgGrid:    0x0d0d1f,
  red:       0xff2244,
  redDark:   0x8b0000,
  green:     0x00e676,
  greenDark: 0x006400,
  gold:      0xffd700,
  goldDark:  0xb8860b,
  cyan:      0x00e5ff,
  white:     0xffffff,
  orange:    0xff9800,
  purple:    0xb040ff,
  pink:      0xff1b6d,
  yellow:    0xffff00,
};

// ─── STYLES (created once) ────────────────────────────────────────────────────
const STYLES = {
  roi: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 44,
    fill: C.gold,
    letterSpacing: 3,
    dropShadow: { alpha: 1, angle: 0, blur: 18, color: C.gold, distance: 0 },
  }),
  roiLabel: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 13,
    fill: 0x888888,
    letterSpacing: 4,
  }),
  title: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 40,
    fill: C.cyan,
    letterSpacing: 3,
    dropShadow: { alpha: 1, angle: 0, blur: 22, color: C.cyan, distance: 0 },
  }),
  startBtn: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 22,
    fill: C.bg,
    letterSpacing: 3,
  }),
  gameOver: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 34,
    fill: C.red,
    letterSpacing: 2,
    dropShadow: { alpha: 1, angle: 0, blur: 16, color: C.red, distance: 0 },
  }),
  finalRoi: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 50,
    fill: C.gold,
    letterSpacing: 2,
    dropShadow: { alpha: 1, angle: 0, blur: 28, color: C.gold, distance: 0 },
  }),
  quote: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 15,
    fill: 0xcccccc,
    letterSpacing: 1,
    wordWrap: true,
    wordWrapWidth: CANVAS_WIDTH - 60,
    align: "center",
  }),
  cta: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 12,
    fill: 0x777777,
    letterSpacing: 1,
    wordWrap: true,
    wordWrapWidth: CANVAS_WIDTH - 60,
    align: "center",
  }),
  retryBtn: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 20,
    fill: C.bg,
    letterSpacing: 3,
  }),
  warning: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 13,
    fill: C.red,
    letterSpacing: 4,
    dropShadow: { alpha: 1, angle: 0, blur: 10, color: C.red, distance: 0 },
  }),
  booostLabel: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 10,
    fill: C.gold,
    letterSpacing: 1,
  }),
  booostActive: new TextStyle({
    fontFamily: "Russo One",
    fontSize: 13,
    fill: C.gold,
    letterSpacing: 1,
    dropShadow: { alpha: 0.9, angle: 0, blur: 8, color: C.gold, distance: 0 },
  }),
};

const QUOTES = [
  "Diamond hands… evaporated.",
  "Midas hand? You got Minus hand.",
  "Conviction lasted less than 1 min.",
  "Where is your thesis now?",
  "You said 10-year hold.",
  "It's just a dip.",
  "Still early.",
  "Long term vision.",
  "Trust the fundamentals.",
  "This is healthy correction.",
];

type GameState = "menu" | "playing" | "gameover";
type BooostType = "slow" | "invincible" | "projectile";

interface Candle {
  gfx: Graphics;
  isRed: boolean;
  isGold: boolean;
  speed: number;
  width: number;
  height: number;
}

interface Projectile {
  gfx: Graphics;
  speed: number;
}

interface VerticalStrike {
  phase: "warning" | "dropping";
  x: number;
  colW: number;
  warningTimer: number;
  gfx: Graphics;
  speed: number;
  yPos: number;
}

// ─── JOYSTICK STATE ───────────────────────────────────────────────────────────
interface JoystickState {
  active: boolean;
  baseX: number;
  baseY: number;
  knobX: number;
  knobY: number;
  dx: number;
  touchId: number | null;
}

async function init() {
  await app.init({
    width: CANVAS_WIDTH,
    height: CANVAS_HEIGHT,
    backgroundColor: C.bg,
    antialias: false,
    resolution: Math.min(window.devicePixelRatio || 1, 2),
    autoDensity: true,
  });

  document.body.appendChild(app.canvas);

  // ─── CONTAINERS ─────────────────────────────────────────────────────────────
  const bgContainer   = new Container();
  const gameContainer = new Container();
  const uiContainer   = new Container();
  const menuContainer = new Container();
  const overContainer = new Container();
  const joystickContainer = new Container();

  app.stage.addChild(bgContainer);
  app.stage.addChild(gameContainer);
  app.stage.addChild(uiContainer);
  app.stage.addChild(menuContainer);
  app.stage.addChild(overContainer);
  app.stage.addChild(joystickContainer);

  initParticles(app, gameContainer);

  // ─── BACKGROUND ─────────────────────────────────────────────────────────────
  const bgBase = new Graphics();
  bgBase.rect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
  bgBase.fill(C.bg);
  bgContainer.addChild(bgBase);

  const GRID_H = 18;
  const GRID_V = Math.ceil(CANVAS_WIDTH / 24);
  for (let row = 0; row * GRID_H < CANVAS_HEIGHT; row++) {
    const line = new Graphics();
    line.rect(0, row * GRID_H, CANVAS_WIDTH, 1);
    line.fill({ color: 0x111122, alpha: 0.7 });
    bgContainer.addChild(line);
  }
  for (let col = 0; col < GRID_V; col++) {
    const line = new Graphics();
    line.rect(col * 24, 0, 1, CANVAS_HEIGHT);
    line.fill({ color: 0x111122, alpha: 0.4 });
    bgContainer.addChild(line);
  }

  const floor = new Graphics();
  floor.rect(0, CANVAS_HEIGHT - 12, CANVAS_WIDTH, 12);
  floor.fill(0x1a1a3a);
  bgContainer.addChild(floor);
  for (let px = 0; px < CANVAS_WIDTH; px += 8) {
    const dot = new Graphics();
    dot.rect(px, CANVAS_HEIGHT - 12, 4, 4);
    dot.fill(0x2a2a4a);
    bgContainer.addChild(dot);
  }

  // ─── GAME STATE ─────────────────────────────────────────────────────────────
  let state: GameState = "menu";
  let roi = 0;
  let surviveTime = 0;
  let spawnTimer = 0;
  let spawnInterval = 0.73;
  let baseSpeed = 338;
  let nextSpawnZone = 0;
  let candles: Candle[] = [];
  let greenCandlesCollected = 0;
  let booostPowerup: Graphics | null = null;
  let booostTimer = 0;
  let booostType: BooostType | null = null;
  let booostSpawnTimer = 0;
  // Tripled spawn rate: interval reduced from 7 to ~2.33
  let booostSpawnInterval = 2.33;
  let verticalStrike: VerticalStrike | null = null;
  let nextStrikeTime = 15 + Math.random() * 10;
  let projectiles: Projectile[] = [];
  let projectileFireTimer = 0;
  let playerX = CANVAS_WIDTH / 2;
  const playerY = CANVAS_HEIGHT - 52;
  const playerW = 18;
  const playerH = 22;
  let playerGfx: Graphics | null = null;
  let playerGlow: GlowFilter | null = null;
  let roiText: Text | null = null;
  let roiLabelText: Text | null = null;
  let booostActiveText: Text | null = null;
  let booostBarGfx: Graphics | null = null;
  let lastScore = -1;
  let playerWalkFrame = 0;
  let playerWalkTimer = 0;

  // ─── KEYBOARD INPUT ─────────────────────────────────────────────────────────
  const keys: Record<string, boolean> = {};
  window.addEventListener("keydown", (e) => { keys[e.key] = true; });
  window.addEventListener("keyup",   (e) => { keys[e.key] = false; });

  // ─── JOYSTICK (touch only) ────────────────────────────────────────────────
  const joystick: JoystickState = {
    active: false,
    baseX: 0,
    baseY: 0,
    knobX: 0,
    knobY: 0,
    dx: 0,
    touchId: null,
  };

  // Enlarged joystick: radius increased from 48→72, knob from 22→32
  const JOYSTICK_RADIUS = 72;
  const KNOB_RADIUS = 32;

  const joystickBase = new Graphics();
  const joystickKnob = new Graphics();
  joystickContainer.addChild(joystickBase);
  joystickContainer.addChild(joystickKnob);
  joystickContainer.visible = false;

  function drawJoystick() {
    joystickBase.clear();
    joystickBase.circle(joystick.baseX, joystick.baseY, JOYSTICK_RADIUS);
    joystickBase.fill({ color: 0xffffff, alpha: 0.08 });
    joystickBase.circle(joystick.baseX, joystick.baseY, JOYSTICK_RADIUS);
    joystickBase.stroke({ color: C.cyan, alpha: 0.35, width: 2 });

    joystickKnob.clear();
    const kx = joystick.active ? joystick.knobX : joystick.baseX;
    const ky = joystick.active ? joystick.knobY : joystick.baseY;
    joystickKnob.circle(kx, ky, KNOB_RADIUS);
    joystickKnob.fill({ color: C.cyan, alpha: joystick.active ? 0.55 : 0.25 });
    joystickKnob.circle(kx, ky, KNOB_RADIUS);
    joystickKnob.stroke({ color: C.cyan, alpha: 0.8, width: 2 });
  }

  function getCanvasPoint(clientX: number, clientY: number): { x: number; y: number } {
    const rect = app.canvas.getBoundingClientRect();
    const scaleX = CANVAS_WIDTH  / rect.width;
    const scaleY = CANVAS_HEIGHT / rect.height;
    return {
      x: (clientX - rect.left) * scaleX,
      y: (clientY - rect.top)  * scaleY,
    };
  }

  // Only attach touch listeners if on a touch device
  if (isTouchDevice()) {
    app.canvas.addEventListener("touchstart", (e) => {
      e.preventDefault();
      unlockAudio();
      if (state !== "playing") return;
      for (const t of Array.from(e.changedTouches)) {
        if (joystick.touchId !== null) continue;
        const pt = getCanvasPoint(t.clientX, t.clientY);
        joystick.active  = true;
        joystick.touchId = t.identifier;
        joystick.baseX   = pt.x;
        joystick.baseY   = pt.y;
        joystick.knobX   = pt.x;
        joystick.knobY   = pt.y;
        joystick.dx      = 0;
      }
      drawJoystick();
    }, { passive: false });

    app.canvas.addEventListener("touchmove", (e) => {
      e.preventDefault();
      if (state !== "playing") return;
      for (const t of Array.from(e.changedTouches)) {
        if (t.identifier !== joystick.touchId) continue;
        const pt = getCanvasPoint(t.clientX, t.clientY);
        const rawDx = pt.x - joystick.baseX;
        const rawDy = pt.y - joystick.baseY;
        const dist  = Math.sqrt(rawDx * rawDx + rawDy * rawDy);
        const clamp = Math.min(dist, JOYSTICK_RADIUS);
        const angle = Math.atan2(rawDy, rawDx);
        joystick.knobX = joystick.baseX + Math.cos(angle) * clamp;
        joystick.knobY = joystick.baseY + Math.sin(angle) * clamp;
        // Smooth dx: linear mapping clamped to [-1, 1]
        joystick.dx = Math.max(-1, Math.min(1, rawDx / JOYSTICK_RADIUS));
      }
      drawJoystick();
    }, { passive: false });

    function endTouch(identifier: number) {
      if (identifier !== joystick.touchId) return;
      joystick.active  = false;
      joystick.touchId = null;
      joystick.dx      = 0;
      drawJoystick();
    }

    app.canvas.addEventListener("touchend",    (e) => { e.preventDefault(); Array.from(e.changedTouches).forEach(t => endTouch(t.identifier)); }, { passive: false });
    app.canvas.addEventListener("touchcancel", (e) => { e.preventDefault(); Array.from(e.changedTouches).forEach(t => endTouch(t.identifier)); }, { passive: false });
  }

  // ─── PIXEL RECT HELPER ──────────────────────────────────────────────────────
  function pxRect(g: Graphics, x: number, y: number, w: number, h: number, color: number, alpha = 1) {
    const sx = Math.round(x / PX) * PX;
    const sy = Math.round(y / PX) * PX;
    const sw = Math.round(w / PX) * PX;
    const sh = Math.round(h / PX) * PX;
    if (alpha < 1) {
      g.rect(sx, sy, sw, sh);
      g.fill({ color, alpha });
    } else {
      g.rect(sx, sy, sw, sh);
      g.fill(color);
    }
  }

  // ─── PLAYER ──────────────────────────────────────────────────────────────────
  function drawPixelPlayer(g: Graphics, _frame: number) {
    const p = PX;
    const cx = 0;
    const by = 0;
    pxRect(g, cx - 6*p, by - 3*p, 5*p, 3*p, 0x222233);
    pxRect(g, cx + 1*p, by - 3*p, 5*p, 3*p, 0x222233);
    pxRect(g, cx - 5*p, by - 9*p, 4*p, 6*p, 0x2a3a5c);
    pxRect(g, cx + 1*p, by - 9*p, 4*p, 6*p, 0x2a3a5c);
    pxRect(g, cx - 6*p, by - 18*p, 12*p, 9*p, 0x0077aa);
    pxRect(g, cx - 5*p, by - 17*p, 4*p,  2*p, 0x0099cc);
    pxRect(g, cx - 3*p, by - 13*p, 1*p, 3*p, C.green);
    pxRect(g, cx - 2*p, by - 14*p, 1*p, 1*p, C.green);
    pxRect(g, cx - 1*p, by - 12*p, 1*p, 2*p, C.green);
    pxRect(g, cx,       by - 15*p, 1*p, 3*p, C.green);
    pxRect(g, cx + 1*p, by - 13*p, 1*p, 2*p, C.green);
    pxRect(g, cx + 2*p, by - 11*p, 1*p, 4*p, C.green);
    pxRect(g, cx - 9*p, by - 17*p, 3*p, 7*p, 0x0077aa);
    pxRect(g, cx + 6*p, by - 17*p, 3*p, 7*p, 0x0077aa);
    pxRect(g, cx - 9*p, by - 11*p, 3*p, 3*p, 0xffcc99);
    pxRect(g, cx + 6*p, by - 11*p, 3*p, 3*p, 0xffcc99);
    pxRect(g, cx - 1*p, by - 20*p, 2*p, 2*p, 0xffcc99);
    pxRect(g, cx - 5*p, by - 31*p, 10*p, 11*p, 0xffcc99);
    pxRect(g, cx - 6*p, by - 28*p, 1*p, 3*p, 0xffcc99);
    pxRect(g, cx + 5*p, by - 28*p, 1*p, 3*p, 0xffcc99);
    pxRect(g, cx - 5*p, by - 34*p, 10*p, 4*p, 0x1a0f00);
    pxRect(g, cx - 6*p, by - 32*p, 2*p,  3*p, 0x1a0f00);
    pxRect(g, cx + 4*p, by - 32*p, 2*p,  3*p, 0x1a0f00);
    pxRect(g, cx - 1*p, by - 36*p, 2*p,  2*p, 0x1a0f00);
    pxRect(g, cx + 2*p, by - 35*p, 2*p,  2*p, 0x1a0f00);
    pxRect(g, cx - 3*p, by - 35*p, 2*p,  2*p, 0x1a0f00);
    pxRect(g, cx - 3*p, by - 27*p, 2*p, 2*p, C.white);
    pxRect(g, cx + 1*p, by - 27*p, 2*p, 2*p, C.white);
    pxRect(g, cx - 3*p, by - 27*p, 1*p, 1*p, 0x111111);
    pxRect(g, cx + 2*p, by - 27*p, 1*p, 1*p, 0x111111);
    pxRect(g, cx - 5*p, by - 25*p, 2*p, 1*p, 0xff8888);
    pxRect(g, cx + 3*p, by - 25*p, 2*p, 1*p, 0xff8888);
    pxRect(g, cx - 2*p, by - 23*p, 4*p, 1*p, 0xaa4433);
  }

  function drawPixelPlayerFull(g: Graphics, _frame: number) {
    const p = PX;
    const cx = 0;
    const by = 0;
    pxRect(g, cx - 7*p, by - 3*p, 5*p, 3*p, 0x222233);
    pxRect(g, cx + 2*p, by - 3*p, 5*p, 3*p, 0x222233);
    pxRect(g, cx - 5*p, by - 10*p, 4*p, 7*p, 0x2a3a5c);
    pxRect(g, cx + 1*p, by - 8*p,  4*p, 5*p, 0x2a3a5c);
    pxRect(g, cx - 6*p, by - 18*p, 12*p, 9*p, 0x0077aa);
    pxRect(g, cx - 5*p, by - 17*p, 4*p,  2*p, 0x0099cc);
    pxRect(g, cx - 3*p, by - 13*p, 1*p, 3*p, C.green);
    pxRect(g, cx - 2*p, by - 14*p, 1*p, 1*p, C.green);
    pxRect(g, cx - 1*p, by - 12*p, 1*p, 2*p, C.green);
    pxRect(g, cx,       by - 15*p, 1*p, 3*p, C.green);
    pxRect(g, cx + 1*p, by - 13*p, 1*p, 2*p, C.green);
    pxRect(g, cx + 2*p, by - 11*p, 1*p, 4*p, C.green);
    pxRect(g, cx - 9*p, by - 19*p, 3*p, 7*p, 0x0077aa);
    pxRect(g, cx + 6*p, by - 15*p, 3*p, 7*p, 0x0077aa);
    pxRect(g, cx - 9*p, by - 13*p, 3*p, 3*p, 0xffcc99);
    pxRect(g, cx + 6*p, by - 9*p,  3*p, 3*p, 0xffcc99);
    pxRect(g, cx - 1*p, by - 20*p, 2*p, 2*p, 0xffcc99);
    pxRect(g, cx - 5*p, by - 31*p, 10*p, 11*p, 0xffcc99);
    pxRect(g, cx - 6*p, by - 28*p, 1*p, 3*p, 0xffcc99);
    pxRect(g, cx + 5*p, by - 28*p, 1*p, 3*p, 0xffcc99);
    pxRect(g, cx - 5*p, by - 34*p, 10*p, 4*p, 0x1a0f00);
    pxRect(g, cx - 6*p, by - 32*p, 2*p, 3*p, 0x1a0f00);
    pxRect(g, cx + 4*p, by - 32*p, 2*p, 3*p, 0x1a0f00);
    pxRect(g, cx - 1*p, by - 36*p, 2*p, 2*p, 0x1a0f00);
    pxRect(g, cx + 2*p, by - 35*p, 2*p, 2*p, 0x1a0f00);
    pxRect(g, cx - 3*p, by - 35*p, 2*p, 2*p, 0x1a0f00);
    pxRect(g, cx - 3*p, by - 27*p, 2*p, 2*p, C.white);
    pxRect(g, cx + 1*p, by - 27*p, 2*p, 2*p, C.white);
    pxRect(g, cx - 3*p, by - 27*p, 1*p, 1*p, 0x111111);
    pxRect(g, cx + 2*p, by - 27*p, 1*p, 1*p, 0x111111);
    pxRect(g, cx - 5*p, by - 25*p, 2*p, 1*p, 0xff8888);
    pxRect(g, cx + 3*p, by - 25*p, 2*p, 1*p, 0xff8888);
    pxRect(g, cx - 2*p, by - 23*p, 4*p, 1*p, 0xaa4433);
  }

  // ─── SAFE GUARD ─────────────────────────────────────────────────────────────
  function isAlive(g: Graphics | null): g is Graphics {
    if (!g) return false;
    try {
      if ((g as unknown as { _destroyed?: boolean })._destroyed) return false;
      const _ = g.x; void _;
      return true;
    } catch (_) {
      return false;
    }
  }

  function createPlayer() {
    if (playerGfx) {
      try {
        if (playerGfx.parent) playerGfx.parent.removeChild(playerGfx);
        playerGfx.destroy({ children: true });
      } catch (_) {}
      playerGfx = null;
    }
    playerGlow = null;

    const g = new Graphics();
    drawPixelPlayer(g, 0);
    const glow = new GlowFilter({ distance: 10, outerStrength: 1.2, color: C.cyan, quality: 0.3 });
    g.filters = [glow];
    g.x = playerX;
    g.y = playerY;
    gameContainer.addChild(g);
    playerGfx = g;
    playerGlow = glow;
  }

  function updatePlayerWalk(dt: number, moving: boolean) {
    if (!isAlive(playerGfx)) return;
    try {
      if (!moving) {
        if (playerWalkFrame !== 0) {
          playerWalkFrame = 0;
          playerGfx!.clear();
          drawPixelPlayer(playerGfx!, 0);
        }
        return;
      }
      playerWalkTimer += dt;
      if (playerWalkTimer > 0.14) {
        playerWalkTimer = 0;
        playerWalkFrame = playerWalkFrame === 0 ? 1 : 0;
        playerGfx!.clear();
        if (playerWalkFrame === 0) {
          drawPixelPlayer(playerGfx!, 0);
        } else {
          drawPixelPlayerFull(playerGfx!, 1);
        }
      }
    } catch (_) {}
  }

  // ─── UI ─────────────────────────────────────────────────────────────────────
  function createGameUI() {
    roiLabelText = new Text({ text: "SURVIVAL ROI", style: STYLES.roiLabel });
    roiLabelText.anchor.set(0.5, 0);
    roiLabelText.x = CANVAS_WIDTH / 2;
    roiLabelText.y = 8;
    uiContainer.addChild(roiLabelText);

    roiText = new Text({ text: "+0.0%", style: STYLES.roi });
    roiText.anchor.set(0.5, 0);
    roiText.x = CANVAS_WIDTH / 2;
    roiText.y = 24;
    uiContainer.addChild(roiText);

    const roiBorder = new Graphics();
    const bw = 180; const bh = 68;
    const bx = CANVAS_WIDTH / 2 - bw / 2; const by2 = 4;
    roiBorder.rect(bx, by2, bw, 2);      roiBorder.fill(C.cyan);
    roiBorder.rect(bx, by2 + bh, bw, 2); roiBorder.fill(C.cyan);
    roiBorder.rect(bx, by2, 2, bh);      roiBorder.fill(C.cyan);
    roiBorder.rect(bx + bw, by2, 2, bh); roiBorder.fill(C.cyan);
    roiBorder.rect(bx, by2, 4, 4);       roiBorder.fill(C.bg);
    roiBorder.rect(bx + bw - 2, by2, 4, 4); roiBorder.fill(C.bg);
    roiBorder.rect(bx, by2 + bh - 2, 4, 4); roiBorder.fill(C.bg);
    roiBorder.rect(bx + bw - 2, by2 + bh - 2, 4, 4); roiBorder.fill(C.bg);
    uiContainer.addChild(roiBorder);

    booostActiveText = new Text({ text: "", style: STYLES.booostActive });
    booostActiveText.anchor.set(0.5, 0);
    booostActiveText.x = CANVAS_WIDTH / 2;
    booostActiveText.y = 80;
    uiContainer.addChild(booostActiveText);

    booostBarGfx = new Graphics();
    uiContainer.addChild(booostBarGfx);
  }

  function updateUI() {
    if (!roiText || roiText.destroyed) return;
    const roiDisplay = Math.floor(roi * 10) / 10;
    if (roiDisplay === lastScore) return;
    lastScore = roiDisplay;
    roiText.text = `+${roiDisplay.toFixed(1)}%`;
  }

  function updateBooostUI() {
    if (!booostBarGfx || booostBarGfx.destroyed) return;
    if (!booostActiveText || booostActiveText.destroyed) return;
    booostBarGfx.clear();
    if (booostType && booostTimer > 0) {
      const pct = booostTimer / 3;
      const barW = 140;
      const bx = CANVAS_WIDTH / 2 - barW / 2;
      const by2 = 98;
      for (let i = 0; i < Math.floor(barW / 6); i++) {
        booostBarGfx.rect(bx + i * 6, by2, 5, 6);
        booostBarGfx.fill(0x222244);
      }
      const fillW = Math.floor((barW * pct) / 6) * 6;
      for (let i = 0; i < Math.floor(fillW / 6); i++) {
        booostBarGfx.rect(bx + i * 6, by2, 5, 6);
        booostBarGfx.fill(C.gold);
      }
      const names: Record<BooostType, string> = {
        slow: "SLOW MARKET",
        invincible: "INVINCIBLE",
        projectile: "PROJECTILE",
      };
      booostActiveText.text = `[ ${names[booostType]} ]`;
    } else {
      booostActiveText.text = "";
    }
  }

  // ─── MENU ───────────────────────────────────────────────────────────────────
  function showMenu() {
    state = "menu";
    menuContainer.visible = true;
    overContainer.visible = false;
    uiContainer.visible = false;
    gameContainer.visible = false;
    joystickContainer.visible = false;

    while (menuContainer.children.length > 0) {
      const ch = menuContainer.children[0];
      menuContainer.removeChild(ch);
      ch.destroy({ children: true });
    }

    const overlay = new Graphics();
    overlay.rect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
    overlay.fill({ color: C.bg, alpha: 0.7 });
    menuContainer.addChild(overlay);

    const decoData = [
      { x: 20,  h: 55, isRed: true  },
      { x: 52,  h: 80, isRed: false },
      { x: 84,  h: 40, isRed: true  },
      { x: CANVAS_WIDTH - 48, h: 70, isRed: false },
      { x: CANVAS_WIDTH - 80, h: 45, isRed: true  },
      { x: CANVAS_WIDTH - 112, h: 90, isRed: false },
    ];
    const cy = CANVAS_HEIGHT * 0.62;
    for (const d of decoData) {
      const dc = new Graphics();
      const dw = 22;
      drawPixelCandle(dc, 0, 0, dw, d.h, d.isRed, false);
      dc.x = d.x;
      dc.y = cy - d.h;
      menuContainer.addChild(dc);
    }

    const title = new Text({ text: "SURVIVE", style: STYLES.title });
    title.anchor.set(0.5);
    title.x = CANVAS_WIDTH / 2;
    title.y = CANVAS_HEIGHT * 0.20;
    menuContainer.addChild(title);

    const title2 = new Text({ text: "THE DIP", style: new TextStyle({
      fontFamily: "Russo One", fontSize: 40,
      fill: C.red, letterSpacing: 3,
      dropShadow: { alpha: 1, angle: 0, blur: 22, color: C.red, distance: 0 },
    }) });
    title2.anchor.set(0.5);
    title2.x = CANVAS_WIDTH / 2;
    title2.y = CANVAS_HEIGHT * 0.20 + 52;
    menuContainer.addChild(title2);

    const tagline = new Text({ text: "HODL OR DIE", style: new TextStyle({
      fontFamily: "Russo One", fontSize: 13, fill: 0x888888, letterSpacing: 6,
    }) });
    tagline.anchor.set(0.5);
    tagline.x = CANVAS_WIDTH / 2;
    tagline.y = CANVAS_HEIGHT * 0.20 + 98;
    menuContainer.addChild(tagline);

    const instrBg = new Graphics();
    instrBg.rect(30, CANVAS_HEIGHT * 0.48 - 2, CANVAS_WIDTH - 60, 76);
    instrBg.fill({ color: 0x0d0d1f, alpha: 0.85 });
    instrBg.rect(30, CANVAS_HEIGHT * 0.48 - 2, CANVAS_WIDTH - 60, 2);
    instrBg.fill(C.cyan);
    instrBg.rect(30, CANVAS_HEIGHT * 0.48 + 74, CANVAS_WIDTH - 60, 2);
    instrBg.fill(C.cyan);
    menuContainer.addChild(instrBg);

    const instrStyle = new TextStyle({
      fontFamily: "Russo One", fontSize: 12, fill: 0x888888, letterSpacing: 1,
      wordWrap: true, wordWrapWidth: CANVAS_WIDTH - 80, align: "center",
    });
    const controlHint = isTouchDevice()
      ? "Drag joystick (bottom of screen) to move"
      : "Arrow keys / A D to move";
    const instrText = new Text({
      text: `${controlHint}\nCatch  to earn ROI — Avoid  instant liquidation!`,
      style: instrStyle,
    });
    instrText.anchor.set(0.5);
    instrText.x = CANVAS_WIDTH / 2;
    instrText.y = CANVAS_HEIGHT * 0.48 + 36;
    menuContainer.addChild(instrText);

    // Start button
    const btnW = 200, btnH = 50;
    const btnBg = new Graphics();
    btnBg.rect(-btnW/2 + 4, -btnH/2,     btnW - 8, btnH);     btnBg.fill(C.green);
    btnBg.rect(-btnW/2,     -btnH/2 + 4, btnW,     btnH - 8); btnBg.fill(C.green);
    btnBg.rect(-btnW/2 + 4, -btnH/2 + 4, btnW - 8, btnH - 8); btnBg.fill(0x005533);
    btnBg.x = CANVAS_WIDTH / 2;
    btnBg.y = CANVAS_HEIGHT * 0.82;
    btnBg.interactive = true;
    btnBg.cursor = "pointer";
    btnBg.filters = [new GlowFilter({ distance: 12, outerStrength: 2, color: C.green, quality: 0.3 })];
    const btnTxt = new Text({ text: "START TRADING", style: STYLES.startBtn });
    btnTxt.anchor.set(0.5);
    btnBg.addChild(btnTxt);
    menuContainer.addChild(btnBg);
    btnBg.on("pointerdown", () => { unlockAudio(); startGame(); });

    const hintText = new Text({ text: "30s = impressive  |  60s = legendary", style: new TextStyle({
      fontFamily: "Russo One", fontSize: 11, fill: 0x555577, letterSpacing: 1,
    }) });
    hintText.anchor.set(0.5);
    hintText.x = CANVAS_WIDTH / 2;
    hintText.y = CANVAS_HEIGHT * 0.92;
    menuContainer.addChild(hintText);
  }

  // ─── PIXEL CANDLE ───────────────────────────────────────────────────────────
  function drawPixelCandle(g: Graphics, x: number, y: number, w: number, h: number, isRed: boolean, isGold: boolean) {
    let col: number;
    let colDark: number;
    let highlight: number;
    if (isGold) {
      col = C.gold;
      colDark = C.goldDark;
      highlight = 0xfffaaa;
    } else if (isRed) {
      col = C.red;
      colDark = C.redDark;
      highlight = 0xff6688;
    } else {
      col = C.green;
      colDark = C.greenDark;
      highlight = 0x66ffaa;
    }
    const wickX = Math.round(x + w / 2);
    g.rect(wickX - 1, y - 10, 2, 10); g.fill(col);
    g.rect(x, y, w, h); g.fill(col);
    g.rect(x, y, 3, h); g.fill(colDark);
    g.rect(x + 3, y, w - 3, 3); g.fill(highlight);
    g.rect(x + 4, y + 6, w - 8, 2); g.fill(colDark);
    g.rect(wickX - 1, y + h, 2, 8); g.fill(col);
  }

  // ─── SPAWN CANDLE ───────────────────────────────────────────────────────────
  function spawnCandle(forceZone?: number) {
    const numZones = 8;
    const zoneW = (CANVAS_WIDTH - 40) / numZones;
    let zone: number;
    if (forceZone !== undefined) {
      zone = forceZone;
    } else if (Math.random() < 0.3) {
      zone = Math.floor(Math.random() * numZones);
    } else {
      zone = nextSpawnZone;
      nextSpawnZone = (nextSpawnZone + 1) % numZones;
    }
    const zoneCX = 20 + zone * zoneW + zoneW / 2;
    const cx = zoneCX + (Math.random() - 0.5) * zoneW * 1.0;
    const safeX = Math.max(10, Math.min(CANVAS_WIDTH - 30, cx));

    // Gold candle: 1/15 chance among non-red; red: 68% base
    const roll = Math.random();
    const isRed = roll < 0.68;
    // Among the 32% non-red, gold is 1/15 of those = ~2.1% overall
    const isGold = !isRed && (Math.random() < (1 / 15));

    const speedMult = booostType === "slow" ? 0.6 : 1;
    const speed = baseSpeed * (0.7 + Math.random() * 0.6) * speedMult;
    // 30% smaller candles
    const cw = Math.round((18 + Math.random() * 14) * 0.7 * 0.7 * 0.7 / PX) * PX;
    const ch = Math.round((40 + Math.random() * 60) * 0.7 * 0.7 * 0.7 / PX) * PX;
    const yOffset = -(ch + 10 + Math.random() * 80);
    const gfx = new Graphics();
    drawPixelCandle(gfx, 0, 0, cw, ch, isRed, isGold);
    if (isGold) {
      gfx.filters = [new GlowFilter({ distance: 14, outerStrength: 3, color: C.gold, quality: 0.3 })];
    } else if (!isRed) {
      gfx.filters = [new GlowFilter({ distance: 7, outerStrength: 1, color: C.green, quality: 0.2 })];
    }
    gfx.x = safeX - cw / 2;
    gfx.y = yOffset;
    gameContainer.addChild(gfx);
    candles.push({ gfx, isRed, isGold, speed, width: cw, height: ch });
  }

  // ─── SPAWN BOOOST ───────────────────────────────────────────────────────────
  function spawnBooost() {
    if (booostPowerup) return;
    const bx = 60 + Math.random() * (CANVAS_WIDTH - 120);
    const g = new Graphics();
    const p = PX;
    pxRect(g, -2*p, -5*p, 4*p, 10*p, C.gold);
    pxRect(g, -5*p, -2*p, 10*p, 4*p, C.gold);
    pxRect(g, -4*p, -4*p, 8*p,  8*p, C.gold);
    pxRect(g, -1*p, -1*p, 2*p,  2*p, C.white);
    pxRect(g, -6*p, -1*p, 1*p,  2*p, C.gold);
    pxRect(g,  5*p, -1*p, 1*p,  2*p, C.gold);
    pxRect(g, -1*p, -6*p, 2*p,  1*p, C.gold);
    pxRect(g, -1*p,  5*p, 2*p,  1*p, C.gold);
    g.filters = [new GlowFilter({ distance: 18, outerStrength: 3, color: C.gold, quality: 0.4 })];
    const label = new Text({ text: "BOOOST", style: STYLES.booostLabel });
    label.anchor.set(0.5);
    label.y = 24;
    g.addChild(label);
    g.x = bx;
    g.y = -40;
    gameContainer.addChild(g);
    booostPowerup = g;
  }

  // ─── VERTICAL STRIKE ────────────────────────────────────────────────────────
  function spawnVerticalStrike() {
    const colW = 28 + Math.random() * 20;
    const minX = colW / 2 + 10;
    const maxX = CANVAS_WIDTH - colW / 2 - 10;
    const strikeX = minX + Math.random() * (maxX - minX);
    sfxCollapseWarning();
    shake(4, 180);
    const gfx = new Graphics();
    const segH = 12;
    for (let y = 0; y < CANVAS_HEIGHT; y += segH * 2) {
      gfx.rect(strikeX - colW / 2, y, colW, segH);
      gfx.fill({ color: C.red, alpha: 0.35 });
    }
    const warnLabel = new Text({ text: "!!", style: new TextStyle({
      fontFamily: "Russo One", fontSize: 16, fill: C.red, letterSpacing: 4,
      dropShadow: { alpha: 1, angle: 0, blur: 8, color: C.red, distance: 0 },
    }) });
    warnLabel.anchor.set(0.5);
    warnLabel.x = strikeX;
    warnLabel.y = 30;
    gfx.addChild(warnLabel);
    gfx.filters = [new GlowFilter({ distance: 8, outerStrength: 1.5, color: C.red, quality: 0.2 })];
    gameContainer.addChild(gfx);
    verticalStrike = {
      phase: "warning",
      x: strikeX,
      colW,
      warningTimer: 0.65,
      gfx,
      speed: baseSpeed * 2.2,
      yPos: -CANVAS_HEIGHT,
    };
  }

  function safeRemoveFromGame(g: Graphics) {
    try {
      if (g && !g.destroyed) {
        if (g.parent) g.parent.removeChild(g);
        g.destroy({ children: true });
      }
    } catch (_) {}
  }

  function updateVerticalStrike(dt: number) {
    if (!verticalStrike) return;

    if (!verticalStrike.gfx || verticalStrike.gfx.destroyed) {
      verticalStrike = null;
      nextStrikeTime = surviveTime + 18 + Math.random() * 12;
      return;
    }

    if (verticalStrike.phase === "warning") {
      verticalStrike.warningTimer -= dt;
      try {
        verticalStrike.gfx.alpha = 0.5 + Math.sin(Date.now() / 60) * 0.5;
      } catch (_) {}

      if (verticalStrike.warningTimer <= 0) {
        const strikeX2 = verticalStrike.x;
        const strikeW2 = verticalStrike.colW;

        safeRemoveFromGame(verticalStrike.gfx);

        const dropGfx = new Graphics();
        const w = strikeW2;
        dropGfx.rect(-w / 2, 0, w, CANVAS_HEIGHT * 1.2); dropGfx.fill(C.red);
        dropGfx.rect(-w / 2, 0, 3, CANVAS_HEIGHT * 1.2); dropGfx.fill(0xff7799);
        dropGfx.rect(w / 2 - 3, 0, 3, CANVAS_HEIGHT * 1.2); dropGfx.fill(C.redDark);
        for (let py = 0; py < CANVAS_HEIGHT; py += 18) {
          dropGfx.rect(-w/2 + 4, py, w - 8, 2);
          dropGfx.fill({ color: 0xff4466, alpha: 0.6 });
        }
        dropGfx.filters = [new GlowFilter({ distance: 14, outerStrength: 2.5, color: C.red, quality: 0.3 })];
        dropGfx.x = strikeX2;
        dropGfx.y = -CANVAS_HEIGHT * 1.2;
        gameContainer.addChild(dropGfx);

        verticalStrike = {
          phase: "dropping",
          x: strikeX2,
          colW: strikeW2,
          warningTimer: 0,
          gfx: dropGfx,
          speed: baseSpeed * 2.2,
          yPos: -CANVAS_HEIGHT * 1.2,
        };
        shake(6, 120);
      }
      return;
    }

    // dropping phase
    if (verticalStrike.gfx.destroyed || !verticalStrike.gfx.parent) {
      verticalStrike = null;
      nextStrikeTime = surviveTime + 18 + Math.random() * 12;
      return;
    }

    verticalStrike.yPos += verticalStrike.speed * dt;
    try {
      verticalStrike.gfx.y = verticalStrike.yPos;
    } catch (_) {
      verticalStrike = null;
      nextStrikeTime = surviveTime + 18 + Math.random() * 12;
      return;
    }

    if (isAlive(playerGfx)) {
      const px_left = playerX - playerW / 2;
      const py_top  = playerY - playerH;
      const strikeLeft  = verticalStrike.x - verticalStrike.colW / 2;
      const strikeRight = verticalStrike.x + verticalStrike.colW / 2;
      const strikeTop   = verticalStrike.yPos;
      const strikeBot   = strikeTop + CANVAS_HEIGHT * 1.2;
      const hitX = px_left < strikeRight && px_left + playerW > strikeLeft;
      const hitY = py_top  < strikeBot   && py_top + playerH  > strikeTop;
      if (hitX && hitY && booostType !== "invincible") {
        sfxRedCandle();
        triggerGameOver();
        return;
      }
    }

    if (verticalStrike.yPos > CANVAS_HEIGHT + 20) {
      safeRemoveFromGame(verticalStrike.gfx);
      verticalStrike = null;
      nextStrikeTime = surviveTime + 18 + Math.random() * 12;
    }
  }

  // ─── PROJECTILE ─────────────────────────────────────────────────────────────
  function spawnProjectile() {
    if (!isAlive(playerGfx)) return;
    const g = new Graphics();
    pxRect(g, -PX, -3*PX, 2*PX, 5*PX, C.cyan);
    pxRect(g, -2*PX, -2*PX, 4*PX, 2*PX, C.cyan);
    pxRect(g, 0, -4*PX, PX, PX, C.white);
    g.filters = [new GlowFilter({ distance: 8, outerStrength: 1.5, color: C.cyan, quality: 0.25 })];
    g.x = playerX;
    g.y = playerY - playerH - 6;
    gameContainer.addChild(g);
    projectiles.push({ gfx: g, speed: 520 });
  }

  // ─── COLLISION ──────────────────────────────────────────────────────────────
  function rectsOverlap(
    ax: number, ay: number, aw: number, ah: number,
    bx: number, by: number, bw: number, bh: number
  ): boolean {
    return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by;
  }

  // ─── SHAKE ──────────────────────────────────────────────────────────────────
  function shake(intensity: number, duration: number = 200) {
    const ox = app.stage.x;
    const oy = app.stage.y;
    const start = Date.now();
    const loop = () => {
      const elapsed = Date.now() - start;
      if (elapsed < duration) {
        app.stage.x = ox + (Math.random() - 0.5) * intensity * 2;
        app.stage.y = oy + (Math.random() - 0.5) * intensity * 2;
        requestAnimationFrame(loop);
      } else {
        app.stage.x = ox;
        app.stage.y = oy;
      }
    };
    loop();
  }

  // ─── GAME OVER ──────────────────────────────────────────────────────────────
  function triggerGameOver() {
    if (state === "gameover") return;
    state = "gameover";
    stopBgMusic();
    sfxRedCandle();
    shake(14, 420);

    joystickContainer.visible = false;
    joystick.active  = false;
    joystick.touchId = null;
    joystick.dx      = 0;

    if (isAlive(playerGfx)) {
      try { spawnRGBParticles(playerX, playerY - playerH / 2, 20); } catch (_) {}
    }

    for (const c of candles) { safeRemoveFromGame(c.gfx); }
    candles = [];

    for (const p of projectiles) { safeRemoveFromGame(p.gfx); }
    projectiles = [];

    if (verticalStrike) {
      safeRemoveFromGame(verticalStrike.gfx);
      verticalStrike = null;
    }

    if (booostPowerup) {
      safeRemoveFromGame(booostPowerup);
      booostPowerup = null;
    }

    if (playerGfx) {
      safeRemoveFromGame(playerGfx);
      playerGfx = null;
      playerGlow = null;
    }

    gameContainer.visible = false;
    uiContainer.visible = false;
    overContainer.visible = true;

    while (overContainer.children.length > 0) {
      const ch = overContainer.children[0];
      overContainer.removeChild(ch);
      ch.destroy({ children: true });
    }

    // Overlay
    const flash = new Graphics();
    flash.rect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
    flash.fill({ color: C.bg, alpha: 0.92 });
    overContainer.addChild(flash);

    const flashStrip = new Graphics();
    flashStrip.rect(0, 0, CANVAS_WIDTH, 4);               flashStrip.fill(C.red);
    flashStrip.rect(0, CANVAS_HEIGHT - 4, CANVAS_WIDTH, 4); flashStrip.fill(C.red);
    overContainer.addChild(flashStrip);

    const goText = new Text({ text: "MARGIN CALL", style: STYLES.gameOver });
    goText.anchor.set(0.5);
    goText.x = CANVAS_WIDTH / 2;
    goText.y = CANVAS_HEIGHT * 0.09;
    overContainer.addChild(goText);

    // Stats card
    const cardW = CANVAS_WIDTH - 48;
    const cardH = 158;
    const cardX = 24;
    const cardY = CANVAS_HEIGHT * 0.18;
    const card = new Graphics();
    card.rect(cardX, cardY, cardW, cardH); card.fill({ color: 0x0a0a1a, alpha: 0.95 });
    card.rect(cardX, cardY, cardW, 3);          card.fill(C.cyan);
    card.rect(cardX, cardY + cardH, cardW, 3);  card.fill(C.cyan);
    card.rect(cardX, cardY, 3, cardH);          card.fill(C.cyan);
    card.rect(cardX + cardW, cardY, 3, cardH);  card.fill(C.cyan);
    card.rect(cardX, cardY, 6, 6);                               card.fill(C.bg);
    card.rect(cardX + cardW - 3, cardY, 6, 6);                   card.fill(C.bg);
    card.rect(cardX, cardY + cardH - 3, 6, 6);                   card.fill(C.bg);
    card.rect(cardX + cardW - 3, cardY + cardH - 3, 6, 6);       card.fill(C.bg);
    overContainer.addChild(card);

    const labelStyle = new TextStyle({ fontFamily: "Russo One", fontSize: 11, fill: 0x666688, letterSpacing: 3 });
    const valueStyle = new TextStyle({ fontFamily: "Russo One", fontSize: 20, fill: C.white, letterSpacing: 1 });

    const tl = new Text({ text: "SURVIVAL TIME", style: labelStyle });
    tl.anchor.set(0.5); tl.x = CANVAS_WIDTH / 2; tl.y = cardY + 16;
    overContainer.addChild(tl);
    const tv = new Text({ text: `${surviveTime.toFixed(2)}s`, style: valueStyle });
    tv.anchor.set(0.5); tv.x = CANVAS_WIDTH / 2; tv.y = cardY + 32;
    overContainer.addChild(tv);

    const div1 = new Graphics();
    for (let dx = cardX + 16; dx < cardX + cardW - 16; dx += 8) {
      div1.rect(dx, cardY + 58, 5, 2); div1.fill(0x222244);
    }
    overContainer.addChild(div1);

    const gl = new Text({ text: "GREEN CANDLES", style: labelStyle });
    gl.anchor.set(0.5); gl.x = CANVAS_WIDTH / 2; gl.y = cardY + 66;
    overContainer.addChild(gl);
    const gv = new Text({ text: `${greenCandlesCollected}`, style: new TextStyle({
      fontFamily: "Russo One", fontSize: 20, fill: C.green, letterSpacing: 1,
      dropShadow: { alpha: 0.8, angle: 0, blur: 10, color: C.green, distance: 0 },
    }) });
    gv.anchor.set(0.5); gv.x = CANVAS_WIDTH / 2; gv.y = cardY + 84;
    overContainer.addChild(gv);

    const div2 = new Graphics();
    for (let dx = cardX + 16; dx < cardX + cardW - 16; dx += 8) {
      div2.rect(dx, cardY + 110, 5, 2); div2.fill(0x222244);
    }
    overContainer.addChild(div2);

    const rl = new Text({ text: "FINAL ROI", style: labelStyle });
    rl.anchor.set(0.5); rl.x = CANVAS_WIDTH / 2; rl.y = cardY + 116;
    overContainer.addChild(rl);
    const rv = new Text({ text: `+${roi.toFixed(1)}%`, style: STYLES.finalRoi });
    rv.anchor.set(0.5); rv.x = CANVAS_WIDTH / 2; rv.y = cardY + 136;
    overContainer.addChild(rv);

    const quote = QUOTES[Math.floor(Math.random() * QUOTES.length)];
    const quoteText = new Text({ text: `"${quote}"`, style: STYLES.quote });
    quoteText.anchor.set(0.5);
    quoteText.x = CANVAS_WIDTH / 2;
    quoteText.y = CANVAS_HEIGHT * 0.62;
    overContainer.addChild(quoteText);

    const ctaText = new Text({
      text: "Can you reach +200% ROI?\nScreenshot and share your ROI.",
      style: STYLES.cta,
    });
    ctaText.anchor.set(0.5);
    ctaText.x = CANVAS_WIDTH / 2;
    ctaText.y = CANVAS_HEIGHT * 0.72;
    overContainer.addChild(ctaText);

    buildRetryButton();
  }

  // ─── RETRY BUTTON ────────────────────────────────────────────────────────────
  function buildRetryButton() {
    const btnW = 220;
    const btnH = 52;
    const btnY = CANVAS_HEIGHT * 0.86;

    const bg = new Graphics();
    bg.rect(CANVAS_WIDTH / 2 - btnW / 2 + 4, btnY - btnH / 2,     btnW - 8, btnH);     bg.fill(C.cyan);
    bg.rect(CANVAS_WIDTH / 2 - btnW / 2,     btnY - btnH / 2 + 4, btnW,     btnH - 8); bg.fill(C.cyan);
    bg.rect(CANVAS_WIDTH / 2 - btnW / 2 + 4, btnY - btnH / 2 + 4, btnW - 8, btnH - 8); bg.fill(0x003344);
    bg.filters = [new GlowFilter({ distance: 14, outerStrength: 2.5, color: C.cyan, quality: 0.3 })];
    bg.interactive = true;
    bg.cursor = "pointer";
    bg.hitArea = {
      contains(x: number, y: number) {
        const left   = CANVAS_WIDTH / 2 - btnW / 2;
        const right  = CANVAS_WIDTH / 2 + btnW / 2;
        const top    = btnY - btnH / 2;
        const bottom = btnY + btnH / 2;
        return x >= left && x <= right && y >= top && y <= bottom;
      }
    };
    overContainer.addChild(bg);

    const label = new Text({ text: "\u21BA  RETRY", style: STYLES.retryBtn });
    label.anchor.set(0.5);
    label.x = CANVAS_WIDTH / 2;
    label.y = btnY;
    label.interactive = true;
    label.cursor = "pointer";
    overContainer.addChild(label);

    const onRetry = () => {
      unlockAudio();
      startGame();
    };

    bg.on("pointerdown", onRetry);
    label.on("pointerdown", onRetry);
  }

  // ─── START GAME ─────────────────────────────────────────────────────────────
  function startGame() {
    if (state === "playing") return;

    state = "playing";
    roi = 0;
    lastScore = -1;
    surviveTime = 0;
    greenCandlesCollected = 0;
    spawnTimer = 0;
    spawnInterval = 0.73;
    baseSpeed = 338;
    nextSpawnZone = 0;
    booostType = null;
    booostTimer = 0;
    booostSpawnTimer = 0;
    // Tripled: was 7, now ~2.33
    booostSpawnInterval = 2.33;
    verticalStrike = null;
    nextStrikeTime = 15 + Math.random() * 10;
    projectiles = [];
    projectileFireTimer = 0;
    playerWalkFrame = 0;
    playerWalkTimer = 0;
    playerX = CANVAS_WIDTH / 2;
    booostPowerup = null;

    joystick.active  = false;
    joystick.touchId = null;
    joystick.dx      = 0;

    playerGfx  = null;
    playerGlow = null;
    roiText = null;
    roiLabelText = null;
    booostActiveText = null;
    booostBarGfx = null;
    candles = [];

    while (gameContainer.children.length > 0) {
      const ch = gameContainer.children[0];
      gameContainer.removeChild(ch);
      try { ch.destroy({ children: true }); } catch (_) {}
    }
    while (uiContainer.children.length > 0) {
      const ch = uiContainer.children[0];
      uiContainer.removeChild(ch);
      try { ch.destroy({ children: true }); } catch (_) {}
    }
    while (overContainer.children.length > 0) {
      const ch = overContainer.children[0];
      overContainer.removeChild(ch);
      try { ch.destroy({ children: true }); } catch (_) {}
    }

    menuContainer.visible = false;
    overContainer.visible = false;
    gameContainer.visible = true;
    uiContainer.visible = true;

    joystickContainer.visible = isTouchDevice();

    createPlayer();
    createGameUI();

    if (isTouchDevice()) {
      joystick.baseX = CANVAS_WIDTH / 2;
      joystick.baseY = CANVAS_HEIGHT - 90;
      drawJoystick();
    }

    startBgMusic();
  }

  // ─── MAIN GAME LOOP ─────────────────────────────────────────────────────────
  app.ticker.add((ticker) => {
    if (state !== "playing") return;

    if (!playerGfx) {
      state = "gameover";
      return;
    }

    let playerValid = false;
    try {
      const testX = playerGfx.x;
      const testY = playerGfx.y;
      void testX; void testY;
      if (playerGfx.parent && !playerGfx.destroyed) playerValid = true;
    } catch (_) {
      playerValid = false;
    }

    if (!playerValid) {
      state = "gameover";
      return;
    }

    const dt = ticker.deltaTime / 60;

    surviveTime += dt;
    updateParticles(dt);

    // ROI accumulation (1/20 of original rate)
    const roiRate = (0.4 + surviveTime * 0.009) / 20;
    roi += roiRate * dt;

    // Speed ramp: 1% every 2s
    const speedScale = 1 + Math.floor(surviveTime / 2) * 0.01;
    baseSpeed = Math.min(338 * speedScale, 900);

    // Spawn interval: gets tighter every 2s
    const spawnScale = 1 + Math.floor(surviveTime / 2) * 0.01;
    spawnInterval = Math.max(0.067, 0.243 / spawnScale);

    // Spawn candles — 200% more than before (base x3, extras x3)
    spawnTimer += dt;
    if (spawnTimer >= spawnInterval) {
      spawnTimer = 0;
      // Base: 9 candles (was 3)
      spawnCandle(); spawnCandle(); spawnCandle();
      spawnCandle(); spawnCandle(); spawnCandle();
      spawnCandle(); spawnCandle(); spawnCandle();
      // Extras scaled up
      if (Math.random() < 0.40) { spawnCandle(); spawnCandle(); spawnCandle(); }
      if (surviveTime > 8  && Math.random() < 0.35) { spawnCandle(); spawnCandle(); spawnCandle(); }
      if (surviveTime > 20 && Math.random() < 0.28) { spawnCandle(); spawnCandle(); spawnCandle(); }
    }

    // Booost powerup spawn (tripled: interval ~2.33s)
    booostSpawnTimer += dt;
    if (booostSpawnTimer >= booostSpawnInterval && !booostPowerup && !booostType) {
      booostSpawnTimer = 0;
      booostSpawnInterval = 2.33 + Math.random() * 1.67; // 2.33–4s
      spawnBooost();
    }

    // Booost timer
    if (booostType) {
      booostTimer -= dt;
      if (booostTimer <= 0) {
        booostType = null;
        booostTimer = 0;
        if (playerGlow) { playerGlow.color = C.cyan; playerGlow.outerStrength = 1.2; }
      } else {
        if (playerGlow) playerGlow.outerStrength = 2 + Math.sin(surviveTime * 12) * 0.8;
      }
    }

    // Vertical strike
    if (surviveTime >= nextStrikeTime && !verticalStrike) spawnVerticalStrike();
    if (verticalStrike) updateVerticalStrike(dt);
    if (state !== "playing") return;

    // ── Player movement
    const playerSpeed = 338;
    let moving = false;
    let dxInput = 0;
    if (keys["ArrowLeft"]  || keys["a"]) dxInput -= 1;
    if (keys["ArrowRight"] || keys["d"]) dxInput += 1;
    if (isTouchDevice() && joystick.active && Math.abs(joystick.dx) > 0.05) {
      dxInput = joystick.dx;
    }
    if (dxInput !== 0) {
      moving = true;
      playerX += dxInput * playerSpeed * dt;
      playerX = Math.max(playerW / 2, Math.min(CANVAS_WIDTH - playerW / 2, playerX));
    }

    if (!playerGfx || state !== "playing") return;

    try {
      playerGfx.x = playerX;
    } catch (_) {
      state = "gameover";
      return;
    }

    updatePlayerWalk(dt, moving);

    // Projectile fire
    if (booostType === "projectile") {
      projectileFireTimer += dt;
      if (projectileFireTimer >= 0.18) {
        projectileFireTimer = 0;
        spawnProjectile();
      }
    }

    // Update projectiles
    for (let i = projectiles.length - 1; i >= 0; i--) {
      const proj = projectiles[i];
      if (!proj.gfx || proj.gfx.destroyed) { projectiles.splice(i, 1); continue; }
      try {
        proj.gfx.y -= proj.speed * dt;
        if (proj.gfx.y < -20) {
          safeRemoveFromGame(proj.gfx);
          projectiles.splice(i, 1);
        }
      } catch (_) {
        projectiles.splice(i, 1);
      }
    }

    // Update candles
    const pLeft  = playerX - playerW / 2 + 4;
    const pTop   = playerY - playerH + 6;
    const pRight = playerX + playerW / 2 - 4;
    const pBot   = playerY;

    for (let i = candles.length - 1; i >= 0; i--) {
      const c = candles[i];
      if (!c.gfx || c.gfx.destroyed) { candles.splice(i, 1); continue; }

      let candleY = 0;
      try {
        const speedMult = booostType === "slow" ? 0.6 : 1;
        c.gfx.y += c.speed * speedMult * dt;
        candleY = c.gfx.y;
      } catch (_) {
        candles.splice(i, 1);
        continue;
      }

      if (candleY > CANVAS_HEIGHT + 20) {
        safeRemoveFromGame(c.gfx);
        candles.splice(i, 1);
        continue;
      }

      let candleX = 0;
      try { candleX = c.gfx.x; } catch (_) { candles.splice(i, 1); continue; }

      // Projectile hits red candles
      if (c.isRed) {
        let hit = false;
        for (let j = projectiles.length - 1; j >= 0; j--) {
          const pr = projectiles[j];
          if (!pr.gfx || pr.gfx.destroyed) continue;
          try {
            const prX = pr.gfx.x; const prY = pr.gfx.y;
            if (rectsOverlap(prX - 4, prY - 6, 8, 12, candleX, candleY, c.width, c.height)) {
              safeRemoveFromGame(pr.gfx);
              projectiles.splice(j, 1);
              hit = true;
              try { spawnSimpleParticles(candleX + c.width / 2, candleY + c.height / 2, 6, C.red); } catch (_) {}
              break;
            }
          } catch (_) {}
        }
        if (hit) {
          safeRemoveFromGame(c.gfx);
          candles.splice(i, 1);
          continue;
        }
      }

      // Player collision
      if (state !== "playing") return;
      try {
        if (rectsOverlap(pLeft, pTop, pRight - pLeft, pBot - pTop, candleX, candleY, c.width, c.height)) {
          if (c.isRed) {
            if (booostType !== "invincible") {
              sfxRedCandle();
              triggerGameOver();
              return;
            }
          } else {
            // Gold candle = 5x ROI bonus
            const roiBonus = c.isGold
              ? (3.0 + Math.random() * 2.5)
              : (0.6 + Math.random() * 0.5);
            greenCandlesCollected++;
            roi += roiBonus;
            sfxGreenCandle();
            const particleColor = c.isGold ? C.gold : C.green;
            try { spawnSimpleParticles(candleX + c.width / 2, candleY + c.height / 2, 8, particleColor); } catch (_) {}
            if (c.isGold) {
              try { spawnRGBParticles(candleX + c.width / 2, candleY + c.height / 2, 10); } catch (_) {}
            }
            safeRemoveFromGame(c.gfx);
            candles.splice(i, 1);
            continue;
          }
        }
      } catch (_) {}
    }

    // Booost powerup movement & collision
    if (booostPowerup) {
      if (booostPowerup.destroyed || !booostPowerup.parent) {
        booostPowerup = null;
      } else {
        let bpY = 0;
        let bpX = 0;
        try {
          booostPowerup.y += 110 * dt;
          booostPowerup.y += Math.sin(surviveTime * 5) * 0.5;
          booostPowerup.rotation += 1.2 * dt;
          bpX = booostPowerup.x;
          bpY = booostPowerup.y;
        } catch (_) {
          booostPowerup = null;
        }

        if (booostPowerup) {
          try {
            if (rectsOverlap(pLeft, pTop, pRight - pLeft, pBot - pTop,
                bpX - 22, bpY - 22, 44, 44)) {
              const types: BooostType[] = ["slow", "invincible", "projectile"];
              booostType = types[Math.floor(Math.random() * types.length)];
              booostTimer = 3;
              sfxBooost();
              const glowColors: Record<BooostType, number> = { slow: C.cyan, invincible: C.gold, projectile: C.purple };
              if (playerGlow) { playerGlow.color = glowColors[booostType]; playerGlow.outerStrength = 3; }
              safeRemoveFromGame(booostPowerup);
              booostPowerup = null;
              try { spawnRGBParticles(bpX, bpY, 16); } catch (_) {}
            } else if (bpY > CANVAS_HEIGHT + 60) {
              safeRemoveFromGame(booostPowerup);
              booostPowerup = null;
            }
          } catch (_) {
            booostPowerup = null;
          }
        }
      }
    }

    if (state !== "playing") return;
    updateUI();
    updateBooostUI();
  });

  // ─── BOOT ───────────────────────────────────────────────────────────────────
  showMenu();
}

init();
