import { Application, Graphics, Text, TextStyle, Container } from "pixi.js";
import { CANVAS_WIDTH, CANVAS_HEIGHT } from "./game.config";

// =====================
// COLORS & CONSTANTS
// =====================
const COLORS = {
  skyTop: 0x4FC3F7,
  skyBot: 0xB3E5FC,
  ground: 0x8D6E63,
  groundTop: 0x6D4C41,
  grass: 0x66BB6A,
  grassDark: 0x388E3C,
  pipeBody: 0x43A047,
  pipeLight: 0x76D275,
  pipeDark: 0x1B5E20,
  pipeShine: 0xA5D6A7,
  pipeCap: 0x388E3C,
  pipeCapLight: 0x81C784,
  pipeCapDark: 0x1B5E20,
  penguinBody: 0x1A1A2E,
  penguinBelly: 0xF5F0E8,
  penguinFace: 0xF5F0E8,
  penguinBeak: 0xFF8C42,
  penguinFeet: 0xFF8C42,
  penguinEye: 0xFFFFFF,
  penguinPupil: 0x1A1A2E,
  penguinCheek: 0xFFB3C1,
  penguinWing: 0x16213E,
  penguinScarf: 0xFF6B6B,
  penguinScarfStripe: 0xFFE66D,
  hppGold: 0xFFD700,
  hppGoldLight: 0xFFF176,
  hppGoldDark: 0xF57F17,
  white: 0xFFFFFF,
  shadow: 0x000000,
};

const GRAVITY = 2000;          // was 1800
const JUMP_FORCE = -540;       // was -520
const PIPE_SPEED_INIT = 230;   // was 180
const PIPE_GAP = 145;          // was 165
const PIPE_WIDTH = 64;
const PIPE_INTERVAL = 1.45;    // was 1.8
const GROUND_HEIGHT = 80;
const BIRD_X = 120;
const COIN_SCORE = 10;
const PIPE_SCORE = 5;
const PENGUIN_SCALE = 1.0;

const LEADERBOARD_API = "https://api.hubrank.hpp.io/api/rankings";

// =====================
// SOUND ENGINE
// =====================
let audioCtx: AudioContext | null = null;

function getAudioCtx(): AudioContext {
  if (!audioCtx) audioCtx = new AudioContext();
  return audioCtx;
}

function playFlap() {
  try {
    const ctx = getAudioCtx();
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.connect(gain); gain.connect(ctx.destination);
    osc.type = "square";
    osc.frequency.setValueAtTime(400, ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(200, ctx.currentTime + 0.1);
    gain.gain.setValueAtTime(0.12, ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.12);
    osc.start(ctx.currentTime); osc.stop(ctx.currentTime + 0.12);
  } catch (_) {}
}

function playCoin() {
  try {
    const ctx = getAudioCtx();
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.connect(gain); gain.connect(ctx.destination);
    osc.type = "sine";
    osc.frequency.setValueAtTime(880, ctx.currentTime);
    osc.frequency.setValueAtTime(1320, ctx.currentTime + 0.08);
    gain.gain.setValueAtTime(0.2, ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.25);
    osc.start(ctx.currentTime); osc.stop(ctx.currentTime + 0.25);
  } catch (_) {}
}

function playHit() {
  try {
    const ctx = getAudioCtx();
    const bufferSize = ctx.sampleRate * 0.3;
    const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
    const data = buffer.getChannelData(0);
    for (let i = 0; i < bufferSize; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / bufferSize);
    const source = ctx.createBufferSource();
    source.buffer = buffer;
    const gain = ctx.createGain();
    gain.gain.setValueAtTime(0.4, ctx.currentTime);
    source.connect(gain); gain.connect(ctx.destination); source.start();
    const osc = ctx.createOscillator();
    const gainOsc = ctx.createGain();
    osc.connect(gainOsc); gainOsc.connect(ctx.destination);
    osc.frequency.setValueAtTime(200, ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(60, ctx.currentTime + 0.3);
    gainOsc.gain.setValueAtTime(0.3, ctx.currentTime);
    gainOsc.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.3);
    osc.start(ctx.currentTime); osc.stop(ctx.currentTime + 0.3);
  } catch (_) {}
}

function playDie() {
  try {
    const ctx = getAudioCtx();
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.connect(gain); gain.connect(ctx.destination);
    osc.type = "sawtooth";
    osc.frequency.setValueAtTime(440, ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(110, ctx.currentTime + 0.5);
    gain.gain.setValueAtTime(0.3, ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.5);
    osc.start(ctx.currentTime); osc.stop(ctx.currentTime + 0.5);
  } catch (_) {}
}

function playScore() {
  try {
    const ctx = getAudioCtx();
    [523, 659, 784].forEach((freq, i) => {
      const osc = ctx.createOscillator();
      const gain = ctx.createGain();
      osc.connect(gain); gain.connect(ctx.destination);
      osc.type = "sine";
      osc.frequency.setValueAtTime(freq, ctx.currentTime + i * 0.1);
      gain.gain.setValueAtTime(0, ctx.currentTime + i * 0.1);
      gain.gain.linearRampToValueAtTime(0.15, ctx.currentTime + i * 0.1 + 0.02);
      gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + i * 0.1 + 0.15);
      osc.start(ctx.currentTime + i * 0.1); osc.stop(ctx.currentTime + i * 0.1 + 0.2);
    });
  } catch (_) {}
}

// =====================
// BGM ENGINE
// =====================
let bgmNodes: { osc: OscillatorNode; gain: GainNode }[] = [];
let bgmPlaying = false;
let bgmInterval: ReturnType<typeof setInterval> | null = null;
let bgmNoteIndex = 0;

const BGM_MELODY = [
  523, 587, 659, 698, 784, 698, 659, 587,
  523, 523, 587, 659, 587, 523, 523, 0,
  659, 698, 784, 880, 784, 698, 659, 587,
  523, 587, 659, 523, 523, 0, 523, 0,
];

const BGM_BASS = [
  262, 0, 262, 0, 330, 0, 330, 0,
  262, 0, 262, 0, 262, 0, 262, 0,
  330, 0, 330, 0, 392, 0, 392, 0,
  262, 0, 330, 0, 262, 0, 0, 0,
];

function startBGM() {
  if (bgmPlaying) return;
  bgmPlaying = true;
  bgmNoteIndex = 0;

  const playNote = () => {
    if (!bgmPlaying) return;
    try {
      const ctx = getAudioCtx();
      const idx = bgmNoteIndex % BGM_MELODY.length;
      const melFreq = BGM_MELODY[idx];
      const bassFreq = BGM_BASS[idx];
      bgmNoteIndex++;

      if (melFreq > 0) {
        const osc = ctx.createOscillator();
        const gain = ctx.createGain();
        osc.type = "triangle";
        osc.frequency.setValueAtTime(melFreq, ctx.currentTime);
        gain.gain.setValueAtTime(0.08, ctx.currentTime);
        gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.18);
        osc.connect(gain); gain.connect(ctx.destination);
        osc.start(ctx.currentTime); osc.stop(ctx.currentTime + 0.2);
        bgmNodes.push({ osc, gain });
      }

      if (bassFreq > 0) {
        const osc2 = ctx.createOscillator();
        const gain2 = ctx.createGain();
        osc2.type = "sine";
        osc2.frequency.setValueAtTime(bassFreq, ctx.currentTime);
        gain2.gain.setValueAtTime(0.06, ctx.currentTime);
        gain2.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.18);
        osc2.connect(gain2); gain2.connect(ctx.destination);
        osc2.start(ctx.currentTime); osc2.stop(ctx.currentTime + 0.2);
        bgmNodes.push({ osc: osc2, gain: gain2 });
      }

      if (bgmNodes.length > 20) bgmNodes.splice(0, bgmNodes.length - 20);
    } catch (_) {}
  };

  playNote();
  bgmInterval = setInterval(playNote, 200);
}

function stopBGM() {
  bgmPlaying = false;
  if (bgmInterval !== null) {
    clearInterval(bgmInterval);
    bgmInterval = null;
  }
  bgmNodes.forEach(({ osc }) => { try { osc.stop(); } catch (_) {} });
  bgmNodes = [];
}

// =====================
// TEXT STYLES
// =====================
const STYLES = {
  title: new TextStyle({
    fontFamily: "Russo One", fontSize: 52, fill: 0xFFD700, letterSpacing: 4,
    dropShadow: { alpha: 1, angle: Math.PI / 4, blur: 0, color: 0x7B4F00, distance: 5 },
  }),
  titleSub: new TextStyle({
    fontFamily: "Russo One", fontSize: 19, fill: 0xFFFFFF, letterSpacing: 2,
    dropShadow: { alpha: 0.7, angle: Math.PI / 4, blur: 4, color: 0x000000, distance: 2 },
  }),
  score: new TextStyle({
    fontFamily: "Russo One", fontSize: 42, fill: 0xFFFFFF, letterSpacing: 2,
    dropShadow: { alpha: 1, angle: Math.PI / 4, blur: 0, color: 0x000000, distance: 4 },
  }),
  scoreMini: new TextStyle({
    fontFamily: "Russo One", fontSize: 20, fill: 0xFFFFFF, letterSpacing: 1,
    dropShadow: { alpha: 0.7, angle: Math.PI / 4, blur: 3, color: 0x000000, distance: 2 },
  }),
  gameOverTitle: new TextStyle({
    fontFamily: "Russo One", fontSize: 46, fill: 0xFF5252, letterSpacing: 3,
    dropShadow: { alpha: 1, angle: Math.PI / 4, blur: 0, color: 0x880000, distance: 4 },
  }),
  finalScore: new TextStyle({
    fontFamily: "Russo One", fontSize: 34, fill: 0xFFD700, letterSpacing: 2,
    dropShadow: { alpha: 0.9, angle: Math.PI / 4, blur: 4, color: 0x000000, distance: 2 },
  }),
  btn: new TextStyle({
    fontFamily: "Russo One", fontSize: 20, fill: 0xFFFFFF, letterSpacing: 2,
  }),
  btnSmall: new TextStyle({
    fontFamily: "Russo One", fontSize: 17, fill: 0xFFFFFF, letterSpacing: 1,
  }),
  tapHint: new TextStyle({
    fontFamily: "Russo One", fontSize: 16, fill: 0xFFFFFF, letterSpacing: 1,
    dropShadow: { alpha: 0.7, angle: Math.PI / 4, blur: 4, color: 0x000000, distance: 2 },
  }),
  coinEffect: new TextStyle({
    fontFamily: "Russo One", fontSize: 22, fill: 0xFFD700,
    dropShadow: { alpha: 1, angle: Math.PI / 4, blur: 0, color: 0x7B4F00, distance: 2 },
  }),
  pipeScore: new TextStyle({
    fontFamily: "Russo One", fontSize: 20, fill: 0x69FF47,
    dropShadow: { alpha: 1, angle: Math.PI / 4, blur: 0, color: 0x1B5E20, distance: 2 },
  }),
  rankTitle: new TextStyle({
    fontFamily: "Russo One", fontSize: 15, fill: 0xFFD700, letterSpacing: 2,
  }),
  rankEntry: new TextStyle({
    fontFamily: "Russo One", fontSize: 13, fill: 0xFFFFFF, letterSpacing: 1,
  }),
  rankEntryHighlight: new TextStyle({
    fontFamily: "Russo One", fontSize: 13, fill: 0xFFD700, letterSpacing: 1,
  }),
  rankLoading: new TextStyle({
    fontFamily: "Russo One", fontSize: 13, fill: 0xAAAAAA, letterSpacing: 1,
  }),
  namePrompt: new TextStyle({
    fontFamily: "Russo One", fontSize: 18, fill: 0xFFFFFF, letterSpacing: 1,
    dropShadow: { alpha: 0.7, angle: Math.PI / 4, blur: 4, color: 0x000000, distance: 2 },
  }),
};

// =====================
// GAME STATE
// =====================
type GameState = "intro" | "playing" | "gameover";
let state: GameState = "intro";
let score = 0;
let bestScore = 0;
let pipeSpeed = PIPE_SPEED_INIT;

let birdY = 0;
let birdVY = 0;
let birdAngle = 0;
let birdWingTime = 0;
let birdAlive = true;

let pipeTimer = 0;
let gameTime = 0;

interface Pipe {
  container: Container;
  x: number;
  gapY: number;
  scored: boolean;
}
let pipes: Pipe[] = [];

interface Coin {
  container: Container;
  x: number;
  y: number;
  collected: boolean;
  bobTime: number;
}
let coins: Coin[] = [];

interface Cloud {
  g: Graphics;
  x: number;
  y: number;
  speed: number;
}
let clouds: Cloud[] = [];

interface ScoreEffect {
  container: Container;
  vy: number;
  life: number;
  maxLife: number;
}
let scoreEffects: ScoreEffect[] = [];

// =====================
// LEADERBOARD
// =====================
interface RankEntry {
  name: string;
  score: number;
  rank?: number;
}

let playerName = "";

async function submitScore(name: string, finalScore: number): Promise<void> {
  try {
    const res = await fetch(LEADERBOARD_API, {
      method: "POST",
      headers: {
        "accept": "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ name, score: finalScore }),
    });
    if (!res.ok) {
      console.warn("Score submit failed:", res.status);
    }
  } catch (err) {
    console.warn("Score submit error:", err);
  }
}

async function fetchLeaderboard(): Promise<RankEntry[]> {
  try {
    const res = await fetch(`${LEADERBOARD_API}?limit=10`, {
      headers: { "accept": "application/json" },
    });
    if (!res.ok) return [];
    const json = await res.json() as {
      success: boolean;
      data: {
        rankings: RankEntry[];
      };
    };
    if (json.success && json.data && Array.isArray(json.data.rankings)) {
      return json.data.rankings;
    }
    return [];
  } catch (err) {
    console.warn("Leaderboard fetch error:", err);
    return [];
  }
}

// =====================
// APP
// =====================
const app = new Application();

async function init() {
  await app.init({
    width: CANVAS_WIDTH,
    height: CANVAS_HEIGHT,
    antialias: true,
    backgroundColor: COLORS.skyTop,
  });

  document.body.appendChild(app.canvas);
  app.ticker.add(mainLoop);
  buildIntro();
}

// =====================
// DRAW PENGUIN
// =====================
function drawPenguin(g: Graphics, wingPhase: number) {
  g.clear();

  const S = 0.72;

  g.ellipse(1 * S, 11 * S, 24 * S, 6 * S);
  g.fill({ color: 0x000000, alpha: 0.13 });

  g.poly([
    -21 * S, 1 * S,
    -30 * S, -4 * S,
    -29 * S, 1 * S,
    -30 * S, 7 * S,
    -21 * S, 4 * S,
  ]);
  g.fill(COLORS.penguinBody);

  g.ellipse(1 * S, 1 * S, 28 * S, 16 * S);
  g.fill(COLORS.penguinBody);

  g.ellipse(2 * S, 3 * S, 18 * S, 11 * S);
  g.fill(COLORS.penguinBelly);

  g.ellipse(-1 * S, 0 * S, 9 * S, 5 * S);
  g.fill({ color: 0xFFFFFF, alpha: 0.35 });

  g.ellipse(14 * S, 0 * S, 5 * S, 10 * S);
  g.fill(COLORS.penguinScarf);
  g.ellipse(14 * S, 0 * S, 3 * S, 10 * S);
  g.fill(COLORS.penguinScarfStripe);

  const topWingAngle = Math.sin(wingPhase) * 0.75;
  const twPX = 0 * S, twPY = -6 * S;
  const twLen = 20 * S;
  const twTipX = twPX + Math.sin(topWingAngle) * twLen;
  const twTipY = twPY - Math.cos(topWingAngle) * twLen;

  g.poly([
    twPX - 6 * S, twPY,
    twTipX - 3 * S, twTipY + 3 * S,
    twTipX + 4 * S, twTipY + 4 * S,
    twPX + 7 * S, twPY + 3 * S,
  ]);
  g.fill(COLORS.penguinWing);

  g.poly([
    twPX - 4 * S, twPY,
    twTipX - 1 * S, twTipY + 2 * S,
    twTipX + 1 * S, twTipY + 1 * S,
    twPX - 1 * S, twPY - 1 * S,
  ]);
  g.fill({ color: 0x5A5A8A, alpha: 0.4 });

  const botWingAngle = Math.sin(wingPhase + Math.PI) * 0.55;
  const bwPX = -1 * S, bwPY = 8 * S;
  const bwLen = 18 * S;
  const bwTipX = bwPX + Math.sin(botWingAngle) * bwLen * 0.5;
  const bwTipY = bwPY + Math.cos(botWingAngle) * bwLen;

  g.poly([
    bwPX - 5 * S, bwPY,
    bwTipX - 2 * S, bwTipY - 1 * S,
    bwTipX + 3 * S, bwTipY,
    bwPX + 6 * S, bwPY - 2 * S,
  ]);
  g.fill({ color: 0x16213E, alpha: 0.85 });

  g.ellipse(6 * S, 13 * S, 7 * S, 4 * S);
  g.fill(COLORS.penguinFeet);
  g.ellipse(11 * S, 14 * S, 3 * S, 2.5 * S);
  g.fill(COLORS.penguinFeet);
  g.ellipse(-3 * S, 13 * S, 5 * S, 3 * S);
  g.fill({ color: COLORS.penguinFeet, alpha: 0.7 });

  g.circle(20 * S, 0 * S, 13 * S);
  g.fill(COLORS.penguinBody);

  g.ellipse(23 * S, 1 * S, 9 * S, 11 * S);
  g.fill(COLORS.penguinFace);

  g.circle(24 * S, -3 * S, 4 * S);
  g.fill(COLORS.penguinEye);

  g.circle(25.5 * S, -3 * S, 2.5 * S);
  g.fill(COLORS.penguinPupil);

  g.circle(26.5 * S, -4 * S, 0.9 * S);
  g.fill(COLORS.white);

  g.ellipse(27 * S, 0 * S, 3 * S, 2 * S);
  g.fill({ color: COLORS.penguinCheek, alpha: 0.75 });

  g.poly([
    28 * S, -5 * S,
    35 * S, -2 * S,
    28 * S, 1 * S,
  ]);
  g.fill(COLORS.penguinBeak);

  g.poly([
    28 * S, -5 * S,
    34 * S, -3 * S,
    28 * S, -3 * S,
  ]);
  g.fill({ color: 0xFFAA55, alpha: 0.6 });
}

/** 3D-style HPP coin */
function drawHppCoin(container: Container, radius: number = 18) {
  const glow = new Graphics();
  glow.circle(0, 0, radius + 6);
  glow.fill({ color: 0xFFD700, alpha: 0.18 });
  container.addChild(glow);

  const depth = new Graphics();
  depth.circle(2, 3, radius);
  depth.fill(COLORS.hppGoldDark);
  container.addChild(depth);

  const face = new Graphics();
  face.circle(0, 0, radius);
  face.fill(0xFFC107);
  container.addChild(face);

  const rim = new Graphics();
  rim.circle(0, 0, radius);
  rim.stroke({ color: COLORS.hppGoldDark, width: 2.5 });
  container.addChild(rim);

  const inner = new Graphics();
  inner.circle(0, 0, radius - 4);
  inner.fill(COLORS.hppGold);
  container.addChild(inner);

  const shine = new Graphics();
  shine.ellipse(-radius * 0.28, -radius * 0.3, radius * 0.48, radius * 0.28);
  shine.fill({ color: COLORS.hppGoldLight, alpha: 0.7 });
  container.addChild(shine);

  const logo = new Graphics();
  logo.rect(-7.5, -7, 4, 14);
  logo.fill(0x7B4F00);
  logo.rect(3.5, -7, 4, 14);
  logo.fill(0x7B4F00);
  logo.rect(-7.5, -1.5, 15, 3.5);
  logo.fill(0x7B4F00);
  container.addChild(logo);

  const logoShine = new Graphics();
  logoShine.rect(-7.5, -7, 4, 3);
  logoShine.fill({ color: 0xFFFFFF, alpha: 0.3 });
  logoShine.rect(3.5, -7, 4, 3);
  logoShine.fill({ color: 0xFFFFFF, alpha: 0.3 });
  container.addChild(logoShine);
}

/** 3D-style pipe */
function drawPipe(topHeight: number, bottomY: number): Container {
  const c = new Container();
  const totalH = CANVAS_HEIGHT - GROUND_HEIGHT;
  const pw = PIPE_WIDTH;

  const bodyH = topHeight - 28;

  const topShadow = new Graphics();
  topShadow.rect(pw - 6, 0, 8, bodyH);
  topShadow.fill({ color: 0x000000, alpha: 0.2 });
  c.addChild(topShadow);

  const topBase = new Graphics();
  topBase.rect(0, 0, pw, bodyH);
  topBase.fill(COLORS.pipeBody);
  c.addChild(topBase);

  const topShineL = new Graphics();
  topShineL.rect(3, 0, 8, bodyH);
  topShineL.fill({ color: COLORS.pipeShine, alpha: 0.6 });
  c.addChild(topShineL);

  const topDarkR = new Graphics();
  topDarkR.rect(pw - 10, 0, 10, bodyH);
  topDarkR.fill({ color: COLORS.pipeDark, alpha: 0.5 });
  c.addChild(topDarkR);

  const topStripe = new Graphics();
  topStripe.rect(pw / 2 - 2, 0, 4, bodyH);
  topStripe.fill({ color: COLORS.pipeLight, alpha: 0.3 });
  c.addChild(topStripe);

  const topCapShadow = new Graphics();
  topCapShadow.roundRect(-4, topHeight - 30, pw + 8, 30, 4);
  topCapShadow.fill({ color: 0x000000, alpha: 0.25 });
  c.addChild(topCapShadow);

  const topCap = new Graphics();
  topCap.roundRect(-8, topHeight - 30, pw + 16, 28, 5);
  topCap.fill(COLORS.pipeCap);
  c.addChild(topCap);

  const topCapShine = new Graphics();
  topCapShine.roundRect(-8, topHeight - 30, pw + 16, 10, 5);
  topCapShine.fill({ color: COLORS.pipeCapLight, alpha: 0.7 });
  c.addChild(topCapShine);

  const topCapDark = new Graphics();
  topCapDark.rect(-8, topHeight - 10, pw + 16, 8);
  topCapDark.fill({ color: COLORS.pipeCapDark, alpha: 0.6 });
  c.addChild(topCapDark);

  const topCapShineL = new Graphics();
  topCapShineL.rect(-4, topHeight - 28, 10, 24);
  topCapShineL.fill({ color: COLORS.pipeShine, alpha: 0.45 });
  c.addChild(topCapShineL);

  const botBodyH = totalH - bottomY;

  const botShadow = new Graphics();
  botShadow.rect(pw - 6, bottomY + 28, 8, botBodyH);
  botShadow.fill({ color: 0x000000, alpha: 0.2 });
  c.addChild(botShadow);

  const botBase = new Graphics();
  botBase.rect(0, bottomY + 28, pw, botBodyH);
  botBase.fill(COLORS.pipeBody);
  c.addChild(botBase);

  const botShineL = new Graphics();
  botShineL.rect(3, bottomY + 28, 8, botBodyH);
  botShineL.fill({ color: COLORS.pipeShine, alpha: 0.6 });
  c.addChild(botShineL);

  const botDarkR = new Graphics();
  botDarkR.rect(pw - 10, bottomY + 28, 10, botBodyH);
  botDarkR.fill({ color: COLORS.pipeDark, alpha: 0.5 });
  c.addChild(botDarkR);

  const botStripe = new Graphics();
  botStripe.rect(pw / 2 - 2, bottomY + 28, 4, botBodyH);
  botStripe.fill({ color: COLORS.pipeLight, alpha: 0.3 });
  c.addChild(botStripe);

  const botCapShadow = new Graphics();
  botCapShadow.roundRect(-4, bottomY, pw + 8, 30, 4);
  botCapShadow.fill({ color: 0x000000, alpha: 0.25 });
  c.addChild(botCapShadow);

  const botCap = new Graphics();
  botCap.roundRect(-8, bottomY, pw + 16, 28, 5);
  botCap.fill(COLORS.pipeCap);
  c.addChild(botCap);

  const botCapShine = new Graphics();
  botCapShine.roundRect(-8, bottomY, pw + 16, 10, 5);
  botCapShine.fill({ color: COLORS.pipeCapLight, alpha: 0.7 });
  c.addChild(botCapShine);

  const botCapDark = new Graphics();
  botCapDark.rect(-8, bottomY + 20, pw + 16, 8);
  botCapDark.fill({ color: COLORS.pipeCapDark, alpha: 0.6 });
  c.addChild(botCapDark);

  const botCapShineL = new Graphics();
  botCapShineL.rect(-4, bottomY + 2, 10, 24);
  botCapShineL.fill({ color: COLORS.pipeShine, alpha: 0.45 });
  c.addChild(botCapShineL);

  return c;
}

/** Cloud */
function drawCloud(g: Graphics) {
  g.clear();
  g.ellipse(4, 6, 40, 20);
  g.fill({ color: 0x000000, alpha: 0.08 });
  g.ellipse(32, 0, 30, 16);
  g.fill({ color: 0x000000, alpha: 0.08 });

  g.ellipse(0, 2, 42, 22);
  g.fill({ color: 0xDDEEFF, alpha: 0.9 });
  g.ellipse(28, -6, 32, 18);
  g.fill({ color: 0xDDEEFF, alpha: 0.9 });
  g.ellipse(-20, -4, 28, 16);
  g.fill({ color: 0xDDEEFF, alpha: 0.9 });

  g.ellipse(0, 0, 42, 22);
  g.fill({ color: 0xFFFFFF, alpha: 0.95 });
  g.ellipse(28, -8, 32, 18);
  g.fill({ color: 0xFFFFFF, alpha: 0.95 });
  g.ellipse(-20, -6, 28, 16);
  g.fill({ color: 0xFFFFFF, alpha: 0.95 });
  g.ellipse(12, -16, 22, 14);
  g.fill({ color: 0xFFFFFF, alpha: 0.95 });

  g.ellipse(-10, -10, 18, 9);
  g.fill({ color: 0xFFFFFF, alpha: 0.6 });
  g.ellipse(16, -18, 12, 7);
  g.fill({ color: 0xFFFFFF, alpha: 0.6 });
}

/** Sky gradient background */
function drawBackground(container: Container) {
  const sky1 = new Graphics();
  sky1.rect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT - GROUND_HEIGHT);
  sky1.fill(0x29B6F6);
  container.addChild(sky1);

  const sky2 = new Graphics();
  sky2.rect(0, 0, CANVAS_WIDTH, (CANVAS_HEIGHT - GROUND_HEIGHT) * 0.45);
  sky2.fill({ color: 0x0288D1, alpha: 0.35 });
  container.addChild(sky2);

  const sky3 = new Graphics();
  sky3.rect(0, (CANVAS_HEIGHT - GROUND_HEIGHT) * 0.6, CANVAS_WIDTH, (CANVAS_HEIGHT - GROUND_HEIGHT) * 0.4);
  sky3.fill({ color: 0x81D4FA, alpha: 0.4 });
  container.addChild(sky3);

  const sun = new Graphics();
  sun.circle(CANVAS_WIDTH - 80, 70, 36);
  sun.fill({ color: 0xFFF9C4, alpha: 0.9 });
  container.addChild(sun);

  const sunCore = new Graphics();
  sunCore.circle(CANVAS_WIDTH - 80, 70, 28);
  sunCore.fill({ color: 0xFFEE58, alpha: 1 });
  container.addChild(sunCore);

  const sunShine = new Graphics();
  sunShine.circle(CANVAS_WIDTH - 86, 64, 10);
  sunShine.fill({ color: 0xFFFFFF, alpha: 0.5 });
  container.addChild(sunShine);

  const mt1 = new Graphics();
  mt1.poly([100, CANVAS_HEIGHT - GROUND_HEIGHT, 250, CANVAS_HEIGHT - GROUND_HEIGHT - 140, 400, CANVAS_HEIGHT - GROUND_HEIGHT]);
  mt1.fill({ color: 0xB0C4DE, alpha: 0.4 });
  container.addChild(mt1);

  const mt1snow = new Graphics();
  mt1snow.poly([210, CANVAS_HEIGHT - GROUND_HEIGHT - 105, 250, CANVAS_HEIGHT - GROUND_HEIGHT - 140, 290, CANVAS_HEIGHT - GROUND_HEIGHT - 105]);
  mt1snow.fill({ color: 0xFFFFFF, alpha: 0.6 });
  container.addChild(mt1snow);

  const mt2 = new Graphics();
  mt2.poly([350, CANVAS_HEIGHT - GROUND_HEIGHT, 480, CANVAS_HEIGHT - GROUND_HEIGHT - 110, 610, CANVAS_HEIGHT - GROUND_HEIGHT]);
  mt2.fill({ color: 0x9FB4CC, alpha: 0.35 });
  container.addChild(mt2);

  const mt2snow = new Graphics();
  mt2snow.poly([450, CANVAS_HEIGHT - GROUND_HEIGHT - 82, 480, CANVAS_HEIGHT - GROUND_HEIGHT - 110, 510, CANVAS_HEIGHT - GROUND_HEIGHT - 82]);
  mt2snow.fill({ color: 0xFFFFFF, alpha: 0.55 });
  container.addChild(mt2snow);
}

/** 3D ground */
function drawGround(container: Container) {
  const groundShadow = new Graphics();
  groundShadow.rect(0, CANVAS_HEIGHT - GROUND_HEIGHT - 6, CANVAS_WIDTH, 10);
  groundShadow.fill({ color: 0x000000, alpha: 0.18 });
  container.addChild(groundShadow);

  const ground = new Graphics();
  ground.rect(0, CANVAS_HEIGHT - GROUND_HEIGHT, CANVAS_WIDTH, GROUND_HEIGHT);
  ground.fill(0xC8DFF0);
  container.addChild(ground);

  const groundTop = new Graphics();
  groundTop.rect(0, CANVAS_HEIGHT - GROUND_HEIGHT, CANVAS_WIDTH, 6);
  groundTop.fill(0x90B8D4);
  container.addChild(groundTop);

  const snow = new Graphics();
  snow.rect(0, CANVAS_HEIGHT - GROUND_HEIGHT + 6, CANVAS_WIDTH, 20);
  snow.fill(0xEEF6FF);
  container.addChild(snow);

  const snowLight = new Graphics();
  snowLight.rect(0, CANVAS_HEIGHT - GROUND_HEIGHT + 6, CANVAS_WIDTH, 7);
  snowLight.fill({ color: 0xFFFFFF, alpha: 0.7 });
  container.addChild(snowLight);

  for (let i = 0; i < CANVAS_WIDTH; i += 30) {
    const bump = new Graphics();
    bump.ellipse(i + 15, CANVAS_HEIGHT - GROUND_HEIGHT + 6, 18, 7);
    bump.fill({ color: 0xFFFFFF, alpha: 0.5 });
    container.addChild(bump);
  }

  for (let i = 0; i < CANVAS_WIDTH; i += 40) {
    const stripe = new Graphics();
    stripe.rect(i, CANVAS_HEIGHT - GROUND_HEIGHT + 26, 20, GROUND_HEIGHT - 26);
    stripe.fill({ color: 0xA0C8E8, alpha: 0.25 });
    container.addChild(stripe);
  }
}

/** 3D button */
function createButton(label: string, topColor: number, botColor: number, width = 150, height = 52): Container {
  const btn = new Container();
  btn.interactive = true;
  btn.cursor = "pointer";

  const shadow = new Graphics();
  shadow.roundRect(-width / 2 + 4, -height / 2 + 6, width, height, 12);
  shadow.fill({ color: 0x000000, alpha: 0.3 });
  btn.addChild(shadow);

  const bottom = new Graphics();
  bottom.roundRect(-width / 2, -height / 2 + 5, width, height, 12);
  bottom.fill(botColor);
  btn.addChild(bottom);

  const top = new Graphics();
  top.roundRect(-width / 2, -height / 2, width, height, 12);
  top.fill(topColor);
  btn.addChild(top);

  const shine = new Graphics();
  shine.roundRect(-width / 2 + 6, -height / 2 + 5, width - 12, height / 2 - 4, 8);
  shine.fill({ color: 0xFFFFFF, alpha: 0.18 });
  btn.addChild(shine);

  const txt = new Text({ text: label, style: STYLES.btn });
  txt.anchor.set(0.5);
  btn.addChild(txt);

  btn.on("pointerover", () => { top.y = 2; shine.y = 2; txt.y = 2; });
  btn.on("pointerout", () => { top.y = 0; shine.y = 0; txt.y = 0; });
  btn.on("pointerdown", () => { top.y = 4; shine.y = 4; txt.y = 4; });
  btn.on("pointerup", () => { top.y = 0; shine.y = 0; txt.y = 0; });

  return btn;
}

/** Share button */
function createShareButton(width = 180, height = 52): Container {
  const btn = new Container();
  btn.interactive = true;
  btn.cursor = "pointer";

  const shadow = new Graphics();
  shadow.roundRect(-width / 2 + 4, -height / 2 + 6, width, height, 12);
  shadow.fill({ color: 0x000000, alpha: 0.3 });
  btn.addChild(shadow);

  const bottom = new Graphics();
  bottom.roundRect(-width / 2, -height / 2 + 5, width, height, 12);
  bottom.fill(0x14171A);
  btn.addChild(bottom);

  const top = new Graphics();
  top.roundRect(-width / 2, -height / 2, width, height, 12);
  top.fill(0x000000);
  btn.addChild(top);

  const shine = new Graphics();
  shine.roundRect(-width / 2 + 6, -height / 2 + 5, width - 12, height / 2 - 4, 8);
  shine.fill({ color: 0xFFFFFF, alpha: 0.12 });
  btn.addChild(shine);

  const xLogo = new Graphics();
  xLogo.poly([-11, -9, -7, -9, 9, 9, 5, 9]);
  xLogo.fill(0xFFFFFF);
  xLogo.poly([5, -9, 9, -9, -7, 9, -11, 9]);
  xLogo.fill(0xFFFFFF);
  xLogo.x = -32;
  xLogo.y = 0;
  top.addChild(xLogo);

  const txt = new Text({ text: "Share", style: STYLES.btn });
  txt.anchor.set(0.5);
  txt.x = 14;
  txt.y = 0;
  top.addChild(txt);

  btn.on("pointerover", () => { top.y = 2; shine.y = 2; });
  btn.on("pointerout", () => { top.y = 0; shine.y = 0; });
  btn.on("pointerdown", () => { top.y = 4; shine.y = 4; });
  btn.on("pointerup", () => { top.y = 0; shine.y = 0; });

  return btn;
}

// =====================
// SCREEN SHAKE
// =====================
function shake(intensity: number, duration: number = 200) {
  const startTime = Date.now();
  const shakeLoop = () => {
    const elapsed = Date.now() - startTime;
    if (elapsed < duration) {
      app.stage.x = (Math.random() - 0.5) * intensity * 2;
      app.stage.y = (Math.random() - 0.5) * intensity * 2;
      requestAnimationFrame(shakeLoop);
    } else {
      app.stage.x = 0;
      app.stage.y = 0;
    }
  };
  shakeLoop();
}

// =====================
// INTRO
// =====================
let introContainer: Container | null = null;
let introPenguin: Graphics | null = null;
let introPenguinTime = 0;

function buildIntro() {
  state = "intro";
  stopBGM();
  cleanupGame();

  if (introContainer) {
    app.stage.removeChild(introContainer);
    introContainer.destroy({ children: true });
  }
  introContainer = new Container();
  app.stage.addChild(introContainer);

  drawBackground(introContainer);

  clouds = [];
  for (let i = 0; i < 5; i++) {
    const g = new Graphics();
    drawCloud(g);
    g.x = Math.random() * CANVAS_WIDTH;
    g.y = 50 + Math.random() * 160;
    introContainer.addChild(g);
    clouds.push({ g, x: g.x, y: g.y, speed: 18 + Math.random() * 22 });
  }

  drawGround(introContainer);

  for (let i = 0; i < 3; i++) {
    const cc = new Container();
    cc.x = 180 + i * 200;
    cc.y = CANVAS_HEIGHT - GROUND_HEIGHT - 70 - Math.random() * 80;
    drawHppCoin(cc, 16);
    introContainer.addChild(cc);
    (cc as Container & { floatT: number }).floatT = Math.random() * Math.PI * 2;
  }

  const panelShadow = new Graphics();
  panelShadow.roundRect(CANVAS_WIDTH / 2 - 252, 34, 504, 106, 20);
  panelShadow.fill({ color: 0x000000, alpha: 0.35 });
  introContainer.addChild(panelShadow);

  const panel = new Graphics();
  panel.roundRect(CANVAS_WIDTH / 2 - 250, 30, 500, 106, 20);
  panel.fill({ color: 0x0D47A1, alpha: 0.82 });
  introContainer.addChild(panel);

  const panelShine = new Graphics();
  panelShine.roundRect(CANVAS_WIDTH / 2 - 246, 32, 492, 38, 16);
  panelShine.fill({ color: 0xFFFFFF, alpha: 0.12 });
  introContainer.addChild(panelShine);

  const titleText = new Text({ text: "HPP PENGUINS", style: STYLES.title });
  titleText.anchor.set(0.5);
  titleText.x = CANVAS_WIDTH / 2; titleText.y = 72;
  introContainer.addChild(titleText);

  const subText = new Text({ text: "Fly  •  Dodge  •  Collect HPP", style: STYLES.titleSub });
  subText.anchor.set(0.5);
  subText.x = CANVAS_WIDTH / 2; subText.y = 122;
  introContainer.addChild(subText);

  introPenguin = new Graphics();
  introPenguin.scale.set(PENGUIN_SCALE);
  drawPenguin(introPenguin, 0);
  introPenguin.x = CANVAS_WIDTH / 2 - 70;
  introPenguin.y = CANVAS_HEIGHT / 2 - 10;
  introContainer.addChild(introPenguin);
  introPenguinTime = 0;

  const coinShowcase = new Container();
  coinShowcase.x = CANVAS_WIDTH / 2 + 90;
  coinShowcase.y = CANVAS_HEIGHT / 2 - 10;
  drawHppCoin(coinShowcase, 24);
  introContainer.addChild(coinShowcase);

  const coinLabel = new Text({ text: "+10 pts", style: STYLES.tapHint });
  coinLabel.anchor.set(0.5);
  coinLabel.x = CANVAS_WIDTH / 2 + 90;
  coinLabel.y = CANVAS_HEIGHT / 2 + 28;
  introContainer.addChild(coinLabel);

  const pipeHint = new Text({ text: "Pass pipe = +5 pts", style: STYLES.tapHint });
  pipeHint.anchor.set(0.5);
  pipeHint.x = CANVAS_WIDTH / 2;
  pipeHint.y = CANVAS_HEIGHT / 2 + 60;
  introContainer.addChild(pipeHint);

  const startBtn = createButton("▶  START", 0x43A047, 0x1B5E20, 190, 56);
  startBtn.x = CANVAS_WIDTH / 2;
  startBtn.y = CANVAS_HEIGHT - GROUND_HEIGHT - 90;
  introContainer.addChild(startBtn);
  startBtn.on("pointerdown", () => { buildGame(); });

  const hint = new Text({ text: "Click or press SPACE to flap!", style: STYLES.tapHint });
  hint.anchor.set(0.5);
  hint.x = CANVAS_WIDTH / 2;
  hint.y = CANVAS_HEIGHT - GROUND_HEIGHT - 28;
  introContainer.addChild(hint);

  if (bestScore > 0) {
    const best = new Text({ text: `Best: ${bestScore}`, style: STYLES.scoreMini });
    best.anchor.set(0.5);
    best.x = CANVAS_WIDTH / 2;
    best.y = CANVAS_HEIGHT - GROUND_HEIGHT - 150;
    introContainer.addChild(best);
  }

  app.stage.interactive = true;
  app.stage.removeAllListeners();
}

// =====================
// GAME SCREEN
// =====================
let gameContainer: Container | null = null;
let bgLayer: Container | null = null;
let pipeLayer: Container | null = null;
let coinLayer: Container | null = null;
let effectLayer: Container | null = null;
let uiLayer: Container | null = null;
let penguinGraphic: Graphics | null = null;
let scoreText: Text | null = null;
let lastScoreDisplay = -1;

function cleanupGame() {
  if (gameContainer) {
    app.stage.removeChild(gameContainer);
    gameContainer.destroy({ children: true });
    gameContainer = null;
  }
  if (uiLayer) {
    app.stage.removeChild(uiLayer);
    uiLayer.destroy({ children: true });
    uiLayer = null;
  }
  bgLayer = null;
  pipeLayer = null;
  coinLayer = null;
  effectLayer = null;
  penguinGraphic = null;
  scoreText = null;
  pipes = [];
  coins = [];
  scoreEffects = [];
}

function cleanupGameOver() {
  if (gameOverContainer) {
    app.stage.removeChild(gameOverContainer);
    gameOverContainer.destroy({ children: true });
    gameOverContainer = null;
  }
}

function buildGame() {
  state = "playing";
  score = 0;
  lastScoreDisplay = -1;
  pipeSpeed = PIPE_SPEED_INIT;
  pipeTimer = 0;
  gameTime = 0;
  birdY = CANVAS_HEIGHT / 2;
  birdVY = 0;
  birdAngle = 0;
  birdWingTime = 0;
  birdAlive = true;

  if (introContainer) {
    app.stage.removeChild(introContainer);
    introContainer.destroy({ children: true });
    introContainer = null;
  }

  cleanupGameOver();
  cleanupGame();

  startBGM();

  gameContainer = new Container();
  app.stage.addChild(gameContainer);

  bgLayer = new Container();
  gameContainer.addChild(bgLayer);
  drawBackground(bgLayer);

  clouds = [];
  for (let i = 0; i < 6; i++) {
    const g = new Graphics();
    drawCloud(g);
    g.x = Math.random() * CANVAS_WIDTH;
    g.y = 40 + Math.random() * 150;
    bgLayer.addChild(g);
    clouds.push({ g, x: g.x, y: g.y, speed: 18 + Math.random() * 22 });
  }

  pipeLayer = new Container();
  gameContainer.addChild(pipeLayer);

  coinLayer = new Container();
  gameContainer.addChild(coinLayer);

  drawGround(gameContainer);

  effectLayer = new Container();
  gameContainer.addChild(effectLayer);

  const penguinShadowGfx = new Graphics();
  penguinShadowGfx.ellipse(0, 0, 24, 6);
  penguinShadowGfx.fill({ color: 0x000000, alpha: 0.15 });
  penguinShadowGfx.x = BIRD_X;
  penguinShadowGfx.y = CANVAS_HEIGHT - GROUND_HEIGHT - 4;
  gameContainer.addChild(penguinShadowGfx);

  penguinGraphic = new Graphics();
  penguinGraphic.scale.set(PENGUIN_SCALE);
  drawPenguin(penguinGraphic, 0);
  penguinGraphic.x = BIRD_X;
  penguinGraphic.y = birdY;
  gameContainer.addChild(penguinGraphic);

  uiLayer = new Container();
  app.stage.addChild(uiLayer);

  const scoreBg = new Graphics();
  scoreBg.roundRect(-50, 0, 100, 52, 14);
  scoreBg.fill({ color: 0x000000, alpha: 0.4 });
  scoreBg.x = CANVAS_WIDTH / 2;
  scoreBg.y = 10;
  uiLayer.addChild(scoreBg);

  scoreText = new Text({ text: "0", style: STYLES.score });
  scoreText.anchor.set(0.5, 0);
  scoreText.x = CANVAS_WIDTH / 2;
  scoreText.y = 16;
  uiLayer.addChild(scoreText);

  const onTap = () => {
    if (state === "playing" && birdAlive) {
      birdVY = JUMP_FORCE;
      playFlap();
    }
  };

  app.stage.interactive = true;
  app.stage.removeAllListeners();
  app.stage.on("pointerdown", onTap);

  window.removeEventListener("keydown", window._hppKeyHandler as EventListener);
  const onKey = (e: KeyboardEvent) => {
    if (e.code === "Space" || e.code === "ArrowUp") {
      e.preventDefault();
      onTap();
    }
  };
  window._hppKeyHandler = onKey;
  window.addEventListener("keydown", onKey);
}

// =====================
// SPAWN PIPE
// =====================
function spawnPipe() {
  if (!pipeLayer || !coinLayer) return;

  const minTop = 80;
  const maxTop = CANVAS_HEIGHT - GROUND_HEIGHT - PIPE_GAP - 80;
  const topHeight = minTop + Math.random() * (maxTop - minTop);
  const bottomY = topHeight + PIPE_GAP;

  const container = drawPipe(topHeight, bottomY);
  container.x = CANVAS_WIDTH + 10;
  pipeLayer.addChild(container);
  pipes.push({ container, x: CANVAS_WIDTH + 10, gapY: topHeight, scored: false });

  if (Math.random() < 0.7) {
    const coinContainer = new Container();
    const gapCenter = topHeight + PIPE_GAP / 2;
    coinContainer.x = CANVAS_WIDTH + 10 + PIPE_WIDTH / 2;
    coinContainer.y = gapCenter;
    drawHppCoin(coinContainer, 17);
    coinLayer.addChild(coinContainer);
    coins.push({
      container: coinContainer,
      x: coinContainer.x,
      y: gapCenter,
      collected: false,
      bobTime: Math.random() * Math.PI * 2,
    });
  }
}

// =====================
// SCORE EFFECT
// =====================
function spawnScoreEffect(x: number, y: number, label: string, style: TextStyle) {
  if (!effectLayer) return;
  const container = new Container();
  container.x = x;
  container.y = y;
  const txt = new Text({ text: label, style });
  txt.anchor.set(0.5);
  container.addChild(txt);
  effectLayer.addChild(container);
  scoreEffects.push({ container, vy: -90, life: 0, maxLife: 0.75 });
}

// =====================
// NAME INPUT (HTML overlay)
// =====================
let nameInputEl: HTMLDivElement | null = null;

function showNameInput(onConfirm: (name: string) => void) {
  if (nameInputEl) {
    document.body.removeChild(nameInputEl);
    nameInputEl = null;
  }

  const canvas = app.canvas;
  const rect = canvas.getBoundingClientRect();

  const wrapper = document.createElement("div");
  wrapper.style.cssText = `
    position: fixed;
    left: ${rect.left}px;
    top: ${rect.top}px;
    width: ${rect.width}px;
    height: ${rect.height}px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    z-index: 9999;
  `;

  const box = document.createElement("div");
  box.style.cssText = `
    background: rgba(13,23,62,0.97);
    border: 2px solid #FFD700;
    border-radius: 16px;
    padding: 24px 32px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    pointer-events: all;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  `;

  const label = document.createElement("div");
  label.textContent = "Enter your name for the leaderboard:";
  label.style.cssText = `
    font-family: 'Russo One', sans-serif;
    font-size: 15px;
    color: #FFD700;
    letter-spacing: 1px;
    text-align: center;
  `;

  const input = document.createElement("input");
  input.type = "text";
  input.maxLength = 16;
  input.placeholder = "Your name";
  input.value = playerName;
  input.style.cssText = `
    font-family: 'Russo One', sans-serif;
    font-size: 18px;
    padding: 8px 16px;
    border-radius: 8px;
    border: 2px solid #FFD700;
    background: #0D1B3E;
    color: #FFFFFF;
    outline: none;
    width: 200px;
    text-align: center;
    letter-spacing: 2px;
  `;

  const confirmBtn = document.createElement("button");
  confirmBtn.textContent = "✓ Submit Score";
  confirmBtn.style.cssText = `
    font-family: 'Russo One', sans-serif;
    font-size: 16px;
    padding: 10px 28px;
    border-radius: 10px;
    border: none;
    background: #43A047;
    color: #FFFFFF;
    cursor: pointer;
    letter-spacing: 1px;
  `;

  const skipBtn = document.createElement("button");
  skipBtn.textContent = "Skip";
  skipBtn.style.cssText = `
    font-family: 'Russo One', sans-serif;
    font-size: 13px;
    padding: 6px 18px;
    border-radius: 8px;
    border: none;
    background: transparent;
    color: #888;
    cursor: pointer;
    letter-spacing: 1px;
  `;

  const doConfirm = () => {
    const name = input.value.trim() || "Anonymous";
    playerName = name;
    if (nameInputEl) {
      document.body.removeChild(nameInputEl);
      nameInputEl = null;
    }
    onConfirm(name);
  };

  confirmBtn.addEventListener("click", doConfirm);
  skipBtn.addEventListener("click", () => {
    if (nameInputEl) {
      document.body.removeChild(nameInputEl);
      nameInputEl = null;
    }
    onConfirm("");
  });
  input.addEventListener("keydown", (e) => {
    if (e.key === "Enter") doConfirm();
  });

  box.appendChild(label);
  box.appendChild(input);
  box.appendChild(confirmBtn);
  box.appendChild(skipBtn);
  wrapper.appendChild(box);
  document.body.appendChild(wrapper);
  nameInputEl = wrapper;

  setTimeout(() => input.focus(), 50);
}

// =====================
// GAME OVER
// =====================
let gameOverContainer: Container | null = null;

function triggerGameOver() {
  birdAlive = false;
  state = "gameover";
  if (score > bestScore) bestScore = score;
  stopBGM();
  playDie();

  const flash = new Graphics();
  flash.rect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
  flash.fill({ color: 0xFFFFFF, alpha: 0.75 });
  app.stage.addChild(flash);
  setTimeout(() => {
    if (app.stage.children.includes(flash)) {
      app.stage.removeChild(flash);
    }
    flash.destroy();
    showNameInput(async (name) => {
      if (name) {
        await submitScore(name, score);
      }
      const rankings = await fetchLeaderboard();
      showGameOver(rankings);
    });
  }, 180);
}

function showGameOver(rankings: RankEntry[]) {
  cleanupGameOver();
  gameOverContainer = new Container();
  app.stage.addChild(gameOverContainer);

  const dim = new Graphics();
  dim.rect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
  dim.fill({ color: 0x000000, alpha: 0.52 });
  gameOverContainer.addChild(dim);

  // ---- LEFT PANEL: Score ----
  const leftW = 260, panelH = 400;
  const leftX = CANVAS_WIDTH / 2 - leftW - 10;
  const panelY = CANVAS_HEIGHT / 2 - panelH / 2 - 10;

  const leftShadow = new Graphics();
  leftShadow.roundRect(leftX + 5, panelY + 7, leftW, panelH, 20);
  leftShadow.fill({ color: 0x000000, alpha: 0.35 });
  gameOverContainer.addChild(leftShadow);

  const leftBot = new Graphics();
  leftBot.roundRect(leftX, panelY + 6, leftW, panelH, 20);
  leftBot.fill({ color: 0x0D1B3E, alpha: 1 });
  gameOverContainer.addChild(leftBot);

  const leftPanel = new Graphics();
  leftPanel.roundRect(leftX, panelY, leftW, panelH, 20);
  leftPanel.fill({ color: 0x1A2744, alpha: 0.97 });
  gameOverContainer.addChild(leftPanel);

  const leftShine = new Graphics();
  leftShine.roundRect(leftX + 8, panelY + 6, leftW - 16, 44, 14);
  leftShine.fill({ color: 0xFFFFFF, alpha: 0.07 });
  gameOverContainer.addChild(leftShine);

  const leftBorder = new Graphics();
  leftBorder.roundRect(leftX + 2, panelY + 2, leftW - 4, panelH - 4, 18);
  leftBorder.stroke({ color: 0xFFD700, width: 2, alpha: 0.6 });
  gameOverContainer.addChild(leftBorder);

  const goTitle = new Text({ text: "GAME OVER", style: STYLES.gameOverTitle });
  goTitle.anchor.set(0.5);
  goTitle.x = leftX + leftW / 2;
  goTitle.y = panelY + 50;
  gameOverContainer.addChild(goTitle);

  const scoreLabel = new Text({ text: "SCORE", style: STYLES.scoreMini });
  scoreLabel.anchor.set(0.5);
  scoreLabel.x = leftX + leftW / 2;
  scoreLabel.y = panelY + 106;
  gameOverContainer.addChild(scoreLabel);

  const finalScoreText = new Text({ text: `${score}`, style: STYLES.finalScore });
  finalScoreText.anchor.set(0.5);
  finalScoreText.x = leftX + leftW / 2;
  finalScoreText.y = panelY + 144;
  gameOverContainer.addChild(finalScoreText);

  const bestText = new Text({ text: `Best: ${bestScore}`, style: STYLES.scoreMini });
  bestText.anchor.set(0.5);
  bestText.x = leftX + leftW / 2;
  bestText.y = panelY + 192;
  gameOverContainer.addChild(bestText);

  // Buttons
  const homeBtn = createButton("🏠 Home", 0x1976D2, 0x0D47A1, 150, 48);
  homeBtn.x = leftX + leftW / 2 - 82;
  homeBtn.y = panelY + 260;
  gameOverContainer.addChild(homeBtn);
  homeBtn.on("pointerdown", () => {
    cleanupGameOver();
    cleanupGame();
    buildIntro();
  });

  const retryBtn = createButton("↺  Retry", 0xE53935, 0xB71C1C, 150, 48);
  retryBtn.x = leftX + leftW / 2 + 82;
  retryBtn.y = panelY + 260;
  gameOverContainer.addChild(retryBtn);
  retryBtn.on("pointerdown", () => {
    cleanupGameOver();
    buildGame();
  });

  const shareBtn = createShareButton(190, 46);
  shareBtn.x = leftX + leftW / 2;
  shareBtn.y = panelY + 326;
  gameOverContainer.addChild(shareBtn);
  shareBtn.on("pointerdown", () => {
    const tweetText = encodeURIComponent(
      `🐧 I scored ${score} pts in HPP PENGUINS!\n` +
      `Best record: ${bestScore} pts\n\n` +
      `Dodge pipes & collect HPP tokens in this cute penguin arcade game!\n` +
      `Play now 👇 https://hpp-penguins-fgau.apps.hpp.io/\n` +
      `#HPP #HPPPenguins #BlockchainGame #Crypto`
    );
    window.open(`https://x.com/intent/tweet?text=${tweetText}`, "_blank");
  });

  // ---- RIGHT PANEL: Leaderboard ----
  const rightW = 250;
  const rightX = CANVAS_WIDTH / 2 + 10;

  const rightShadow = new Graphics();
  rightShadow.roundRect(rightX + 5, panelY + 7, rightW, panelH, 20);
  rightShadow.fill({ color: 0x000000, alpha: 0.35 });
  gameOverContainer.addChild(rightShadow);

  const rightBot = new Graphics();
  rightBot.roundRect(rightX, panelY + 6, rightW, panelH, 20);
  rightBot.fill({ color: 0x0D1B3E, alpha: 1 });
  gameOverContainer.addChild(rightBot);

  const rightPanel = new Graphics();
  rightPanel.roundRect(rightX, panelY, rightW, panelH, 20);
  rightPanel.fill({ color: 0x1A2744, alpha: 0.97 });
  gameOverContainer.addChild(rightPanel);

  const rightShine = new Graphics();
  rightShine.roundRect(rightX + 8, panelY + 6, rightW - 16, 44, 14);
  rightShine.fill({ color: 0xFFFFFF, alpha: 0.07 });
  gameOverContainer.addChild(rightShine);

  const rightBorder = new Graphics();
  rightBorder.roundRect(rightX + 2, panelY + 2, rightW - 4, panelH - 4, 18);
  rightBorder.stroke({ color: 0xFFD700, width: 2, alpha: 0.6 });
  gameOverContainer.addChild(rightBorder);

  const crownStyle = new TextStyle({ fontSize: 22 });
  const crown = new Text({ text: "🏆", style: crownStyle });
  crown.anchor.set(0.5);
  crown.x = rightX + rightW / 2;
  crown.y = panelY + 30;
  gameOverContainer.addChild(crown);

  const rankTitleText = new Text({ text: "LEADERBOARD", style: STYLES.rankTitle });
  rankTitleText.anchor.set(0.5);
  rankTitleText.x = rightX + rightW / 2;
  rankTitleText.y = panelY + 58;
  gameOverContainer.addChild(rankTitleText);

  const divider = new Graphics();
  divider.rect(rightX + 16, panelY + 74, rightW - 32, 1.5);
  divider.fill({ color: 0xFFD700, alpha: 0.35 });
  gameOverContainer.addChild(divider);

  const medals = ["🥇", "🥈", "🥉"];
  const entryStartY = panelY + 90;
  const entryH = 30;

  if (rankings.length === 0) {
    const noData = new Text({ text: "No scores yet!", style: STYLES.rankLoading });
    noData.anchor.set(0.5);
    noData.x = rightX + rightW / 2;
    noData.y = entryStartY + 60;
    gameOverContainer.addChild(noData);
  } else {
    rankings.slice(0, 10).forEach((entry, idx) => {
      const rowY = entryStartY + idx * entryH;
      const isCurrentPlayer = playerName && entry.name === playerName && entry.score === score;

      if (isCurrentPlayer) {
        const highlight = new Graphics();
        highlight.roundRect(rightX + 10, rowY - 11, rightW - 20, 26, 6);
        highlight.fill({ color: 0xFFD700, alpha: 0.12 });
        gameOverContainer!.addChild(highlight);
      }

      const rankStyle = idx < 3 ? new TextStyle({ fontSize: 14 }) : STYLES.rankEntry;
      const rankLabel = idx < 3
        ? new Text({ text: medals[idx], style: rankStyle })
        : new Text({ text: `${idx + 1}.`, style: rankStyle });
      rankLabel.anchor.set(0, 0.5);
      rankLabel.x = rightX + 14;
      rankLabel.y = rowY;
      gameOverContainer!.addChild(rankLabel);

      const displayName = entry.name.length > 10 ? entry.name.slice(0, 10) + "…" : entry.name;
      const nameStyle = isCurrentPlayer ? STYLES.rankEntryHighlight : STYLES.rankEntry;
      const nameTxt = new Text({ text: displayName, style: nameStyle });
      nameTxt.anchor.set(0, 0.5);
      nameTxt.x = rightX + 44;
      nameTxt.y = rowY;
      gameOverContainer!.addChild(nameTxt);

      const scoreStyle = isCurrentPlayer ? STYLES.rankEntryHighlight : STYLES.rankEntry;
      const scoreTxt = new Text({ text: `${entry.score}`, style: scoreStyle });
      scoreTxt.anchor.set(1, 0.5);
      scoreTxt.x = rightX + rightW - 14;
      scoreTxt.y = rowY;
      gameOverContainer!.addChild(scoreTxt);

      if (idx < rankings.length - 1 && idx < 9) {
        const rowDiv = new Graphics();
        rowDiv.rect(rightX + 16, rowY + 13, rightW - 32, 1);
        rowDiv.fill({ color: 0xFFFFFF, alpha: 0.06 });
        gameOverContainer!.addChild(rowDiv);
      }
    });
  }
}

// =====================
// MAIN LOOP
// =====================
function mainLoop(ticker: { deltaTime: number }) {
  const dt = ticker.deltaTime / 60;

  if (state === "intro") {
    introPenguinTime += dt;
    if (introPenguin) {
      drawPenguin(introPenguin, introPenguinTime * 5);
      introPenguin.y = CANVAS_HEIGHT / 2 - 10 + Math.sin(introPenguinTime * 2.2) * 22;
      introPenguin.rotation = -Math.sin(introPenguinTime * 2.2) * 0.12;
      const sq = 1 + Math.sin(introPenguinTime * 4.4) * 0.03;
      introPenguin.scale.set(PENGUIN_SCALE * sq, PENGUIN_SCALE * (2 - sq));
    }
    if (introContainer) {
      introContainer.children.forEach((child) => {
        const c = child as Container & { floatT?: number };
        if (c.floatT !== undefined) {
          c.floatT += dt * 2;
          child.y += Math.sin(c.floatT) * 0.5;
        }
      });
    }
    clouds.forEach((cloud) => {
      cloud.x -= cloud.speed * dt;
      if (cloud.x < -120) cloud.x = CANVAS_WIDTH + 120;
      cloud.g.x = cloud.x;
    });
    return;
  }

  if (state === "playing") {
    gameTime += dt;
    // Faster speed scaling: +8 per second (was +4)
    pipeSpeed = PIPE_SPEED_INIT + gameTime * 8;

    clouds.forEach((cloud) => {
      cloud.x -= cloud.speed * 0.45 * dt;
      if (cloud.x < -120) cloud.x = CANVAS_WIDTH + 120;
      cloud.g.x = cloud.x;
    });

    for (let i = scoreEffects.length - 1; i >= 0; i--) {
      const ef = scoreEffects[i];
      ef.life += dt;
      ef.container.y += ef.vy * dt;
      ef.vy *= 0.94;
      ef.container.alpha = 1 - ef.life / ef.maxLife;
      if (ef.life >= ef.maxLife) {
        if (effectLayer && effectLayer.children.includes(ef.container)) {
          effectLayer.removeChild(ef.container);
        }
        ef.container.destroy({ children: true });
        scoreEffects.splice(i, 1);
      }
    }

    if (!birdAlive) return;

    birdVY += GRAVITY * dt;
    birdY += birdVY * dt;
    birdWingTime += dt;

    const targetAngle = Math.min(Math.max(birdVY / 650, -0.45), 0.9);
    birdAngle += (targetAngle - birdAngle) * 0.14;

    if (penguinGraphic) {
      penguinGraphic.y = birdY;
      penguinGraphic.rotation = birdAngle;
      const sq = 1 + Math.sin(birdWingTime * 10) * 0.025;
      penguinGraphic.scale.set(PENGUIN_SCALE * sq, PENGUIN_SCALE * (2 - sq));
      drawPenguin(penguinGraphic, birdWingTime * 8);
    }

    pipeTimer += dt;
    if (pipeTimer >= PIPE_INTERVAL) {
      pipeTimer = 0;
      spawnPipe();
    }

    for (let i = pipes.length - 1; i >= 0; i--) {
      const pipe = pipes[i];
      pipe.x -= pipeSpeed * dt;
      pipe.container.x = pipe.x;

      if (!pipe.scored && pipe.x + PIPE_WIDTH / 2 < BIRD_X) {
        pipe.scored = true;
        score += PIPE_SCORE;
        playScore();
        spawnScoreEffect(BIRD_X + 30, birdY - 30, `+${PIPE_SCORE}`, STYLES.pipeScore);
      }

      const birdR = 14;
      if (
        BIRD_X + birdR > pipe.x + 6 &&
        BIRD_X - birdR < pipe.x + PIPE_WIDTH - 6
      ) {
        if (birdY - birdR < pipe.gapY - 8 || birdY + birdR > pipe.gapY + PIPE_GAP + 8) {
          playHit();
          shake(10);
          triggerGameOver();
          return;
        }
      }

      if (pipe.x < -PIPE_WIDTH - 30) {
        pipeLayer?.removeChild(pipe.container);
        pipe.container.destroy({ children: true });
        pipes.splice(i, 1);
      }
    }

    for (let i = coins.length - 1; i >= 0; i--) {
      const coin = coins[i];
      if (coin.collected) {
        coinLayer?.removeChild(coin.container);
        coin.container.destroy({ children: true });
        coins.splice(i, 1);
        continue;
      }

      coin.x -= pipeSpeed * dt;
      coin.bobTime += dt * 3;
      coin.container.x = coin.x;
      coin.container.y = coin.y + Math.sin(coin.bobTime) * 5;
      coin.container.rotation = Math.sin(coin.bobTime * 0.5) * 0.15;
      const pulse = 1 + Math.sin(coin.bobTime * 2) * 0.07;
      coin.container.scale.set(pulse);

      const dist = Math.hypot(BIRD_X - coin.x, birdY - coin.container.y);
      if (dist < 28) {
        coin.collected = true;
        score += COIN_SCORE;
        playCoin();
        spawnScoreEffect(coin.x, coin.container.y - 10, `+${COIN_SCORE}`, STYLES.coinEffect);
      }

      if (coin.x < -50) {
        coinLayer?.removeChild(coin.container);
        coin.container.destroy({ children: true });
        coins.splice(i, 1);
      }
    }

    if (birdY + 18 >= CANVAS_HEIGHT - GROUND_HEIGHT || birdY - 18 <= 0) {
      playHit();
      shake(8);
      triggerGameOver();
      return;
    }

    if (scoreText && score !== lastScoreDisplay) {
      scoreText.text = `${score}`;
      lastScoreDisplay = score;
    }
  }
}

// =====================
// GLOBAL KEY HANDLER
// =====================
declare global {
  interface Window {
    _hppKeyHandler: ((e: KeyboardEvent) => void) | undefined;
  }
}
window._hppKeyHandler = undefined;

document.fonts.ready.then(() => {
  init();
});
