hypeorcode
Network offOpen alone (new tab)

Run details

Model
minimax/minimax-m3.1-flash:free
Route
OpenRouter API, one user message
Reasoning
provider default, 29,223 reasoning tokens
Limits
60 min · 524,288 output tokens
Date
21 Oct 2026
Time
8 min 7 s
Size
57 kB
Tokens
279 in, 47,868 out
List price
free (list price $0)
Paid
$0 (free route)

Raw files: output · meta.json

Exact prompt

Make a Breakout game I can play right now with the keyboard or the mouse. Give it several levels, a score and some juice.

It must be one complete, self-contained HTML file: inline all CSS and JavaScript, and make no network requests of any kind (no CDNs, web fonts or remote images). Reply with the file in a single ```html code block.

Source

57 kB 2,046 lines Raw

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="theme-color" content="#070914">
<title>Neon Break</title>
<style>
  :root {
    color-scheme: dark;
    --bg: #070914;
    --panel: rgba(15, 20, 39, 0.86);
    --line: rgba(139, 158, 255, 0.18);
    --text: #f4f7ff;
    --muted: #8e9ab8;
    --cyan: #56efff;
    --purple: #a879ff;
    --pink: #ff4f9a;
    --gold: #ffd166;
  }

  * { box-sizing: border-box; }

  html, body {
    margin: 0;
    min-height: 100%;
    background: var(--bg);
  }

  body {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 20px;
    color: var(--text);
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    background:
      radial-gradient(circle at 15% 5%, rgba(75, 67, 190, 0.22), transparent 34rem),
      radial-gradient(circle at 90% 85%, rgba(0, 205, 225, 0.12), transparent 32rem),
      #070914;
    overflow-x: hidden;
  }

  button {
    font: inherit;
  }

  [hidden] { display: none !important; }

  .shell {
    width: min(1040px, 100%);
    display: grid;
    gap: 14px;
  }

  .topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
  }

  .brand {
    display: flex;
    align-items: center;
    gap: 11px;
    min-width: max-content;
  }

  .brand-mark {
    width: 37px;
    height: 37px;
    position: relative;
    border-radius: 11px;
    transform: rotate(-8deg);
    background: linear-gradient(135deg, var(--cyan), var(--purple) 65%, var(--pink));
    box-shadow: 0 0 25px rgba(86, 239, 255, 0.27);
  }

  .brand-mark::before,
  .brand-mark::after {
    content: "";
    position: absolute;
    left: 8px;
    right: 8px;
    height: 4px;
    border-radius: 99px;
    background: #07101e;
  }

  .brand-mark::before { top: 10px; }
  .brand-mark::after { bottom: 10px; }

  .brand h1 {
    margin: 0;
    font-size: 17px;
    line-height: 1;
    letter-spacing: 0.16em;
    font-weight: 900;
  }

  .brand small {
    display: block;
    margin-top: 5px;
    color: var(--muted);
    font-size: 9px;
    font-weight: 800;
    letter-spacing: 0.2em;
  }

  .hud {
    display: flex;
    justify-content: flex-end;
    align-items: stretch;
    gap: 8px;
    flex-wrap: wrap;
  }

  .stat {
    min-width: 78px;
    padding: 8px 12px;
    border: 1px solid var(--line);
    border-radius: 11px;
    background: rgba(11, 15, 31, 0.72);
    box-shadow: inset 0 1px rgba(255,255,255,0.025);
  }

  .stat span {
    display: block;
    color: var(--muted);
    font-size: 8px;
    font-weight: 900;
    letter-spacing: 0.15em;
    margin-bottom: 3px;
  }

  .stat strong {
    display: block;
    font-size: 15px;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.03em;
  }

  #scoreValue { color: var(--cyan); }
  #livesValue { color: var(--pink); letter-spacing: 0.08em; }
  #comboValue { color: var(--gold); }
  #bestValue { color: #c4b5fd; }

  .stage {
    position: relative;
    overflow: hidden;
    border: 1px solid rgba(139, 158, 255, 0.28);
    border-radius: 20px;
    background: #050711;
    box-shadow:
      0 25px 80px rgba(0, 0, 0, 0.48),
      0 0 0 1px rgba(255,255,255,0.025) inset,
      0 0 70px rgba(81, 62, 181, 0.09);
    isolation: isolate;
  }

  .stage::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
    box-shadow: inset 0 0 80px rgba(0,0,0,0.24);
    z-index: 3;
  }

  canvas {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 10;
    touch-action: none;
    cursor: crosshair;
    outline: none;
  }

  .overlay {
    position: absolute;
    inset: 0;
    z-index: 4;
    display: grid;
    place-items: center;
    padding: 24px;
    pointer-events: none;
    background:
      radial-gradient(circle at center, rgba(25, 18, 61, 0.35), rgba(3, 5, 13, 0.68));
    opacity: 1;
    visibility: visible;
    transition: opacity 180ms ease, visibility 180ms ease;
  }

  .overlay.hidden {
    opacity: 0;
    visibility: hidden;
  }

  .overlay-card {
    width: min(470px, 94%);
    padding: 30px;
    text-align: center;
    border: 1px solid rgba(156, 177, 255, 0.24);
    border-radius: 20px;
    pointer-events: auto;
    background:
      linear-gradient(145deg, rgba(23, 29, 56, 0.96), rgba(9, 12, 27, 0.96));
    box-shadow:
      0 24px 70px rgba(0, 0, 0, 0.55),
      0 0 55px rgba(92, 72, 205, 0.16),
      inset 0 1px rgba(255,255,255,0.06);
    animation: cardIn 320ms cubic-bezier(.2,.8,.2,1);
  }

  @keyframes cardIn {
    from { opacity: 0; transform: translateY(12px) scale(0.96); }
    to { opacity: 1; transform: none; }
  }

  .eyebrow {
    color: var(--cyan);
    font-size: 10px;
    font-weight: 900;
    letter-spacing: 0.22em;
    text-transform: uppercase;
  }

  .overlay-card h2 {
    margin: 10px 0 9px;
    font-size: clamp(30px, 5vw, 52px);
    line-height: 0.98;
    letter-spacing: -0.045em;
    font-weight: 950;
    background: linear-gradient(110deg, #fff 5%, #a9f8ff 42%, #c4a4ff 90%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }

  .overlay-card p {
    margin: 0 auto;
    max-width: 390px;
    color: #aeb8d3;
    font-size: 14px;
    line-height: 1.55;
    white-space: pre-line;
  }

  .overlay-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    margin: 19px 0;
  }

  .overlay-stats div {
    padding: 10px 7px;
    border: 1px solid rgba(139, 158, 255, 0.13);
    border-radius: 11px;
    background: rgba(4, 7, 17, 0.48);
  }

  .overlay-stats span {
    display: block;
    color: var(--muted);
    font-size: 8px;
    font-weight: 900;
    letter-spacing: 0.12em;
    margin-bottom: 4px;
  }

  .overlay-stats strong {
    color: #eefaff;
    font-size: 16px;
  }

  .primary {
    min-width: 190px;
    margin-top: 20px;
    padding: 13px 23px;
    border: 0;
    border-radius: 12px;
    color: #06101c;
    background: linear-gradient(100deg, #5df3ff, #b47aff 65%, #ff71ad);
    box-shadow: 0 10px 30px rgba(88, 188, 255, 0.2);
    font-size: 12px;
    font-weight: 950;
    letter-spacing: 0.1em;
    cursor: pointer;
    transition: transform 130ms ease, filter 130ms ease, box-shadow 130ms ease;
  }

  .primary:hover {
    transform: translateY(-2px);
    filter: brightness(1.08);
    box-shadow: 0 13px 36px rgba(88, 188, 255, 0.3);
  }

  .primary:active { transform: translateY(1px) scale(0.98); }

  .keyhint {
    margin-top: 15px !important;
    color: #707d9e !important;
    font-size: 10px !important;
    letter-spacing: 0.035em;
  }

  .underbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    min-height: 38px;
  }

  .boosts {
    display: flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
  }

  .boost-label {
    color: #626e8d;
    font-size: 8px;
    font-weight: 900;
    letter-spacing: 0.15em;
  }

  .boost {
    padding: 5px 8px;
    border: 1px solid currentColor;
    border-radius: 999px;
    font-size: 9px;
    font-weight: 900;
    letter-spacing: 0.05em;
    white-space: nowrap;
    background: rgba(255,255,255,0.035);
  }

  .empty-boost {
    color: #59647f;
    font-size: 10px;
  }

  .controls {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 7px;
    flex-wrap: wrap;
  }

  .control {
    padding: 7px 10px;
    border: 1px solid var(--line);
    border-radius: 9px;
    color: #aab5ce;
    background: rgba(13, 17, 34, 0.78);
    font-size: 9px;
    font-weight: 850;
    letter-spacing: 0.08em;
    cursor: pointer;
    transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
  }

  .control:hover:not(:disabled) {
    color: white;
    border-color: rgba(86, 239, 255, 0.42);
    background: rgba(86, 239, 255, 0.07);
  }

  .control:disabled {
    opacity: 0.35;
    cursor: default;
  }

  kbd {
    display: inline-block;
    min-width: 18px;
    padding: 2px 5px;
    margin-right: 3px;
    border: 1px solid #343d59;
    border-bottom-width: 2px;
    border-radius: 4px;
    color: #dbe5ff;
    background: #11172b;
    font: 800 9px ui-monospace, SFMono-Regular, Menlo, monospace;
  }

  @media (max-width: 720px) {
    body { padding: 10px; }
    .topbar { align-items: flex-start; }
    .brand h1 { font-size: 14px; }
    .brand small { display: none; }
    .brand-mark { width: 31px; height: 31px; border-radius: 9px; }
    .brand-mark::before, .brand-mark::after { left: 6px; right: 6px; }
    .stat { min-width: 61px; padding: 7px 8px; }
    .stat strong { font-size: 12px; }
    .hud { gap: 5px; }
    .stage { border-radius: 14px; }
    .underbar { align-items: flex-start; flex-direction: column; }
    .controls { justify-content: flex-start; }
  }

  @media (max-width: 480px) {
    .shell { gap: 9px; }
    .topbar { flex-direction: column; }
    .hud { width: 100%; justify-content: flex-start; }
    .stat { flex: 1; min-width: 54px; }
    .brand { display: none; }
    .overlay { padding: 10px; }
    .overlay-card { padding: 15px 17px; border-radius: 14px; }
    .overlay-card h2 { margin: 6px 0; font-size: 28px; }
    .overlay-card p { font-size: 11px; line-height: 1.4; }
    .overlay-stats { margin: 10px 0; }
    .overlay-stats div { padding: 6px 4px; }
    .overlay-stats strong { font-size: 12px; }
    .primary { margin-top: 10px; padding: 10px 18px; }
    .keyhint { display: none; }
  }
</style>
</head>
<body>
<main class="shell">
  <header class="topbar">
    <div class="brand">
      <div class="brand-mark" aria-hidden="true"></div>
      <div>
        <h1>NEON BREAK</h1>
        <small>SEVEN SECTORS // ONE BREAKOUT RUN</small>
      </div>
    </div>

    <div class="hud" aria-label="Game statistics">
      <div class="stat"><span>SCORE</span><strong id="scoreValue">0</strong></div>
      <div class="stat"><span>SECTOR</span><strong id="levelValue">1 / 7</strong></div>
      <div class="stat"><span>LIVES</span><strong id="livesValue">♥♥♥</strong></div>
      <div class="stat"><span>CHAIN</span><strong id="comboValue">—</strong></div>
      <div class="stat"><span>BEST</span><strong id="bestValue">0</strong></div>
    </div>
  </header>

  <section class="stage" id="stage">
    <canvas id="game" width="960" height="600" tabindex="0"
      aria-label="Neon Breakout game. Move with the mouse or arrow keys, and press Space or click to launch."></canvas>

    <div class="overlay" id="overlay">
      <div class="overlay-card">
        <div class="eyebrow" id="overlayTag">ARCADE // 7 SECTORS</div>
        <h2 id="overlayTitle">NEON BREAK</h2>
        <p id="overlayText">Smash every destructible tile, catch power-ups, and chain hits for a massive score.</p>
        <div class="overlay-stats" id="overlayStats" hidden></div>
        <button class="primary" id="overlayButton" type="button">START GAME</button>
        <p class="keyhint" id="overlayHint">Mouse or ← → to move · Space / Click to launch · P to pause</p>
      </div>
    </div>
  </section>

  <footer class="underbar">
    <div class="boosts">
      <span class="boost-label">ACTIVE</span>
      <div id="boostList"><span class="empty-boost">No active boosts</span></div>
    </div>
    <div class="controls">
      <span class="control"><kbd>←</kbd><kbd>→</kbd> MOVE</span>
      <span class="control"><kbd>SPACE</kbd> LAUNCH</span>
      <button class="control" id="pauseBtn" type="button">PAUSE</button>
      <button class="control" id="soundBtn" type="button" aria-pressed="true">SOUND ON</button>
      <button class="control" id="resetBtn" type="button">RESTART</button>
    </div>
  </footer>
</main>

<script>
(() => {
  "use strict";

  const W = 960;
  const H = 600;
  const TAU = Math.PI * 2;
  const BASE_PADDLE_W = 126;
  const BRICK_W = 68;
  const BRICK_H = 25;
  const BRICK_GAP = 18;
  const ROW_GAP = 10;
  const BRICK_LEFT = 59;
  const BRICK_TOP = 78;

  const canvas = document.getElementById("game");
  const ctx = canvas.getContext("2d");
  const els = {
    score: document.getElementById("scoreValue"),
    level: document.getElementById("levelValue"),
    lives: document.getElementById("livesValue"),
    combo: document.getElementById("comboValue"),
    best: document.getElementById("bestValue"),
    boostList: document.getElementById("boostList"),
    overlay: document.getElementById("overlay"),
    overlayTag: document.getElementById("overlayTag"),
    overlayTitle: document.getElementById("overlayTitle"),
    overlayText: document.getElementById("overlayText"),
    overlayStats: document.getElementById("overlayStats"),
    overlayButton: document.getElementById("overlayButton"),
    overlayHint: document.getElementById("overlayHint"),
    pause: document.getElementById("pauseBtn"),
    sound: document.getElementById("soundBtn"),
    reset: document.getElementById("resetBtn")
  };

  const BALL_COLORS = ["#62f3ff", "#ff67ad", "#ffd166", "#a879ff", "#75ffbc", "#ff8b5c"];
  const POWER = {
    wide:   { symbol: "W", color: "#62f3ff", label: "WIDE PADDLE" },
    multi:  { symbol: "M", color: "#ff67ad", label: "MULTIBALL" },
    slow:   { symbol: "S", color: "#a879ff", label: "SLOW TIME" },
    life:   { symbol: "+", color: "#75ffbc", label: "EXTRA LIFE" }
  };

  const levels = [
    {
      name: "FIRST LIGHT",
      note: "Warm up your paddle. Learn the bounce.",
      accent: "#56efff",
      build() {
        for (let r = 0; r < 3; r++) {
          for (let c = 0; c < 10; c++) {
            const hp = r === 1 && c >= 3 && c <= 6 ? 2 : 1;
            addBrick(c, r, hp);
          }
        }
      }
    },
    {
      name: "PURPLE DESCENT",
      note: "A tight pyramid. Keep your angles clean.",
      accent: "#a879ff",
      build() {
        for (let r = 0; r < 5; r++) {
          const count = 10 - r * 2;
          const start = r;
          for (let i = 0; i < count; i++) {
            addBrick(start + i, r, i === 0 || i === count - 1 ? 2 : 1);
          }
        }
      }
    },
    {
      name: "IRON FORTRESS",
      note: "Steel cannot be broken. Aim around it.",
      accent: "#ffad42",
      build() {
        for (let r = 0; r < 3; r++) {
          for (let c = 0; c < 10; c++) {
            addBrick(c, r, r === 1 && c >= 3 && c <= 6 ? 2 : 1);
          }
        }
        addBrick(0, 3, 0, "steel");
        for (let c = 1; c <= 8; c++) addBrick(c, 3, 2);
        addBrick(9, 3, 0, "steel");
        for (let c = 0; c < 10; c++) addBrick(c, 4, c === 0 || c === 9 ? 3 : 2);
        addBrick(0, 5, 0, "steel");
        addBrick(1, 5, 3);
        for (let c = 2; c <= 7; c++) addBrick(c, 5, 2);
        addBrick(8, 5, 3);
        addBrick(9, 5, 0, "steel");
      }
    },
    {
      name: "CHECKER PULSE",
      note: "Find the gaps. Every opening is an opportunity.",
      accent: "#ff4f9a",
      build() {
        for (let r = 0; r < 6; r++) {
          for (let c = 0; c < 10; c++) {
            if ((r + c) % 2 === 0) {
              addBrick(c, r, (r + c) % 4 === 0 ? 3 : 1);
            }
          }
        }
        addBrick(0, 1, 0, "steel");
        addBrick(9, 0, 0, "steel");
      }
    },
    {
      name: "THE VAULT",
      note: "A steel shell protects a volatile core.",
      accent: "#75ffbc",
      build() {
        for (let c = 0; c < 10; c++) addBrick(c, 0, 0, "steel");
        for (let r = 1; r <= 4; r++) {
          addBrick(0, r, 0, "steel");
          addBrick(9, r, 0, "steel");
          for (let c = 1; c <= 8; c++) {
            addBrick(c, r, (r + c) % 3 === 0 ? 3 : 2);
          }
        }
        for (let c = 1; c <= 8; c++) addBrick(c, 5, 1);
      }
    },
    {
      name: "NEON GAUNTLET",
      note: "A dense final defense. Keep the chain alive.",
      accent: "#ff8b5c",
      build() {
        for (let r = 0; r < 5; r++) {
          for (let c = 0; c < 10; c++) {
            if (r % 2 === 1 && c % 5 === 0) {
              addBrick(c, r, 0, "steel");
            } else {
              const hp = c === 2 || c === 7 ? 3 : (r === 3 ? 2 : 1);
              addBrick(c, r, hp);
            }
          }
        }
      }
    },
    {
      name: "THE CORE",
      note: "One last shot at the heart of the system.",
      accent: "#ff386c",
      build() {
        for (let c = 1; c <= 8; c++) addBrick(c, 0, 1);
        for (let c = 2; c <= 7; c++) addBrick(c, 1, 2);
        for (let c = 1; c <= 8; c++) addBrick(c, 2, 2);
        addBrick(3, 3, 30, "boss", 4);
        addBrick(1, 4, 3);
        addBrick(2, 4, 2);
        addBrick(7, 4, 2);
        addBrick(8, 4, 3);
        for (let c = 0; c < 10; c++) addBrick(c, 5, c === 0 || c === 9 ? 0 : 1, c === 0 || c === 9 ? "steel" : "normal");
      }
    }
  ];

  let state = "title";
  let levelIndex = 0;
  let score = 0;
  let lives = 3;
  let best = 0;
  let combo = 0;
  let comboTimer = 0;
  let levelTotal = 0;
  let destructibleLeft = 0;
  let lastBonus = 0;

  try {
    best = Number(localStorage.getItem("neon-break-best") || 0) || 0;
  } catch (_) {}

  let bricks = [];
  let balls = [];
  let powerups = [];
  let particles = [];
  let rings = [];
  let popups = [];

  const paddle = {
    x: (W - BASE_PADDLE_W) / 2,
    y: 538,
    w: BASE_PADDLE_W,
    h: 18,
    vx: 0,
    flash: 0
  };

  let wideTimer = 0;
  let slowTimer = 0;
  let deathTimer = 0;
  let pausedFrom = "playing";
  let shake = 0;
  let flash = 0;
  let hitStop = 0;
  let visualTime = 0;
  let toast = null;
  let levelBanner = null;
  let boostSignature = "";
  let ballSerial = 0;
  let overlayAction = null;

  const keys = new Set();
  const mouse = { active: false, center: W / 2 };
  let inputMode = "keyboard";

  const stars = Array.from({ length: 55 }, () => ({
    x: Math.random() * W,
    y: Math.random() * H,
    r: Math.random() * 1.5 + 0.35,
    phase: Math.random() * TAU,
    speed: Math.random() * 0.8 + 0.25
  }));

  let dpr = 1;

  function resizeCanvas() {
    dpr = Math.min(2, window.devicePixelRatio || 1);
    canvas.width = Math.round(W * dpr);
    canvas.height = Math.round(H * dpr);
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  }

  function alphaColor(hex, alpha) {
    const n = parseInt(hex.slice(1), 16);
    return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${alpha})`;
  }

  function formatScore(n) {
    return n.toLocaleString("en-US");
  }

  function roundRectPath(context, x, y, w, h, r) {
    r = Math.min(r, w / 2, h / 2);
    context.beginPath();
    context.moveTo(x + r, y);
    context.arcTo(x + w, y, x + w, y + h, r);
    context.arcTo(x + w, y + h, x, y + h, r);
    context.arcTo(x, y + h, x, y, r);
    context.arcTo(x, y, x + w, y, r);
    context.closePath();
  }

  function addBrick(c, r, hp, type = "normal", span = 1) {
    let color = "#56efff";
    if (type === "steel") color = "#66738d";
    else if (type === "boss") color = "#ff386c";
    else if (hp >= 10) color = "#ff386c";
    else if (hp >= 4) color = "#ff4f9a";
    else if (hp === 3) color = "#ffad42";
    else if (hp === 2) color = "#a879ff";

    bricks.push({
      x: BRICK_LEFT + c * (BRICK_W + BRICK_GAP),
      y: BRICK_TOP + r * (BRICK_H + ROW_GAP),
      w: span * BRICK_W + (span - 1) * BRICK_GAP,
      h: BRICK_H,
      hp,
      maxHp: hp,
      type,
      color,
      alive: true,
      destructible: type !== "steel",
      flash: 0,
      cooldown: 0,
      id: bricks.length
    });
  }

  function loadBricks(index) {
    bricks = [];
    levels[index].build();
    levelTotal = bricks.filter(b => b.destructible).length;
    destructibleLeft = levelTotal;
  }

  function makeBall(x, y) {
    const id = ballSerial++;
    return {
      id,
      x,
      y,
      px: x,
      py: y,
      vx: 0,
      vy: 0,
      r: 8,
      speed: 480,
      launched: false,
      dead: false,
      trail: [],
      color: BALL_COLORS[id % BALL_COLORS.length]
    };
  }

  function prepareServe() {
    paddle.w = BASE_PADDLE_W;
    paddle.x = (W - paddle.w) / 2;
    paddle.vx = 0;
    paddle.flash = 0;
    balls = [makeBall(W / 2, paddle.y - 12)];
    powerups = [];
    combo = 0;
    comboTimer = 0;
    wideTimer = 0;
    slowTimer = 0;
    state = "serve";
    updateHud();
  }

  function loadLevel(index) {
    levelIndex = index;
    loadBricks(index);
    prepareServe();
    levelBanner = {
      title: levels[index].name,
      subtitle: levels[index].note,
      life: 1.8,
      max: 1.8
    };
  }

  function startGame() {
    ensureAudio();
    score = 0;
    lives = 3;
    combo = 0;
    comboTimer = 0;
    particles = [];
    rings = [];
    popups = [];
    shake = 0;
    flash = 0;
    hitStop = 0;
    toast = null;
    loadLevel(0);
    hideOverlay();
  }

  function nextLevel() {
    if (levelIndex >= levels.length - 1) {
      showVictory();
      return;
    }
    loadLevel(levelIndex + 1);
    hideOverlay();
  }

  function showOverlay(options) {
    els.overlay.classList.remove("hidden");
    els.overlayTag.textContent = options.tag || "NEON BREAK";
    els.overlayTitle.textContent = options.title || "";
    els.overlayText.textContent = options.text || "";
    els.overlayButton.textContent = options.button || "CONTINUE";
    els.overlayHint.textContent = options.hint || "Mouse or ← → to move · Space / Click to launch";
    els.overlayStats.hidden = !options.stats;
    els.overlayStats.innerHTML = options.stats || "";
    overlayAction = options.action || null;
  }

  function hideOverlay() {
    els.overlay.classList.add("hidden");
    overlayAction = null;
  }

  function showTitle() {
    state = "title";
    showOverlay({
      tag: "ARCADE // 7 SECTORS",
      title: "NEON BREAK",
      text: "Smash every destructible tile, catch power-ups, and chain hits for a massive score.",
      button: "START GAME",
      stats: `<div><span>BEST</span><strong>${formatScore(best)}</strong></div>
              <div><span>SECTORS</span><strong>7</strong></div>
              <div><span>POWER-UPS</span><strong>4</strong></div>`,
      action: startGame,
      hint: "Mouse or ← → to move · Space / Click to launch · P to pause"
    });
  }

  function showPause() {
    showOverlay({
      tag: "SIMULATION PAUSED",
      title: "TAKE A BREATH",
      text: "The matrix can wait. Your run is safe.",
      button: "RESUME",
      action: resumeGame,
      hint: "Press P, Space, or click Resume"
    });
  }

  function showLevelClear() {
    showOverlay({
      tag: `SECTOR ${String(levelIndex + 1).padStart(2, "0")} COMPLETE`,
      title: "CLEAN SWEEP.",
      text: `Bonus awarded: ${formatScore(lastBonus)} points.\nThe next sector is already waiting.`,
      button: levelIndex === levels.length - 1 ? "CLAIM VICTORY" : "NEXT SECTOR →",
      stats: `<div><span>SCORE</span><strong>${formatScore(score)}</strong></div>
              <div><span>BONUS</span><strong>+${formatScore(lastBonus)}</strong></div>
              <div><span>LIVES</span><strong>${lives}</strong></div>`,
      action: nextLevel,
      hint: "Space / Click to continue"
    });
  }

  function showGameOver() {
    showOverlay({
      tag: "RUN TERMINATED",
      title: "GAME OVER",
      text: "The matrix wins this round. Reset the system and go again.",
      button: "PLAY AGAIN",
      stats: `<div><span>SCORE</span><strong>${formatScore(score)}</strong></div>
              <div><span>BEST</span><strong>${formatScore(best)}</strong></div>
              <div><span>SECTOR</span><strong>${levelIndex + 1}</strong></div>`,
      action: startGame,
      hint: "Space / Click / R to restart"
    });
  }

  function showVictory() {
    state = "victory";
    saveBest();
    showOverlay({
      tag: "ALL SECTORS CLEARED",
      title: "SYSTEM BROKEN.",
      text: "You reached the Core and brought the whole neon grid down. That was a beautiful run.",
      button: "RUN IT BACK",
      stats: `<div><span>FINAL SCORE</span><strong>${formatScore(score)}</strong></div>
              <div><span>BEST</span><strong>${formatScore(best)}</strong></div>
              <div><span>LIVES</span><strong>${lives}</strong></div>`,
      action: startGame,
      hint: "Space / Click / R to play again"
    });
  }

  function saveBest() {
    try {
      localStorage.setItem("neon-break-best", String(best));
    } catch (_) {}
  }

  function togglePause() {
    if (state === "playing" || state === "serve") {
      pausedFrom = state;
      state = "paused";
      showPause();
      updateHud();
    } else if (state === "paused") {
      resumeGame();
    }
  }

  function resumeGame() {
    state = pausedFrom;
    hideOverlay();
    updateHud();
  }

  function activatePrimary() {
    if (state === "title" || state === "gameover" || state === "victory") startGame();
    else if (state === "serve") launchBalls();
    else if (state === "paused") resumeGame();
    else if (state === "levelclear") nextLevel();
  }

  function launchBalls() {
    if (state !== "serve") return;
    for (const b of balls) {
      if (!b.launched) {
        const offset = Math.random() * 0.52 - 0.26;
        b.launched = true;
        b.vx = Math.sin(offset) * b.speed;
        b.vy = -Math.cos(offset) * b.speed;
      }
    }
    state = "playing";
    sfx("launch");
  }

  function addScore(amount) {
    score += Math.max(0, amount);
    if (score > best) best = score;
    updateHud();
  }

  function comboMultiplier() {
    return Math.min(5, 1 + Math.floor(combo / 2));
  }

  function loseLife() {
    if (state !== "playing") return;
    lives--;
    updateHud();
    if (lives <= 0) {
      state = "gameover";
      saveBest();
      sfx("lose");
      showGameOver();
    } else {
      state = "dying";
      deathTimer = 1.05;
      powerups = [];
      sfx("lose");
    }
  }

  function completeLevel() {
    if (state !== "playing") return;
    lastBonus = 800 + levelIndex * 250 + lives * 125;
    addScore(lastBonus);
    spawnConfetti(90);
    sfx("clear");

    if (levelIndex === levels.length - 1) {
      showVictory();
    } else {
      state = "levelclear";
      showLevelClear();
    }
  }

  function burst(x, y, color, count = 10, force = 190, confetti = false) {
    for (let i = 0; i < count; i++) {
      const angle = Math.random() * TAU;
      const speed = force * (0.35 + Math.random() * 0.85);
      const life = 0.35 + Math.random() * (confetti ? 1.1 : 0.55);
      particles.push({
        x, y,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed - (confetti ? 80 : 0),
        gravity: confetti ? 260 : 95,
        life,
        maxLife: life,
        size: confetti ? 3 + Math.random() * 5 : 1.5 + Math.random() * 3.5,
        color,
        rot: Math.random() * TAU,
        spin: (Math.random() - 0.5) * 10,
        type: confetti ? "confetti" : "spark"
      });
    }
    if (particles.length > 650) particles.splice(0, particles.length - 650);
  }

  function spawnConfetti(count) {
    const colors = ["#56efff", "#a879ff", "#ff4f9a", "#ffd166", "#75ffbc"];
    for (let i = 0; i < count; i++) {
      burst(Math.random() * W, -10 - Math.random() * 120, colors[i % colors.length], 1, 60, true);
    }
  }

  function spawnRing(x, y, color, maxRadius = 45) {
    rings.push({ x, y, color, radius: 5, maxRadius, life: 0.35, maxLife: 0.35 });
  }

  function spawnPopup(x, y, text, color = "#ffffff") {
    popups.push({ x, y, text, color, life: 0.9, maxLife: 0.9 });
  }

  function showToast(text, color = "#ffffff") {
    toast = { text, color, life: 1.45, max: 1.45 };
  }

  function maybeDropPowerup(brick) {
    const chance = 0.14 + Math.min(0.08, combo * 0.01);
    if (Math.random() > chance) return;

    const roll = Math.random();
    let kind = "wide";
    if (roll >= 0.30 && roll < 0.62) kind = "multi";
    else if (roll >= 0.62 && roll < 0.91) kind = "slow";
    else if (roll >= 0.91) kind = "life";

    powerups.push({
      x: Math.max(18, Math.min(W - 46, brick.x + brick.w / 2 - 14)),
      y: brick.y + brick.h,
      w: 28,
      h: 28,
      vy: 125,
      rotation: 0,
      kind
    });
  }

  function applyPowerup(power) {
    const config = POWER[power.kind];
    addScore(100);
    showToast(config.label, config.color);
    burst(power.x + 14, power.y + 14, config.color, 20, 230);
    spawnRing(power.x + 14, power.y + 14, config.color, 70);
    sfx("power");

    if (power.kind === "wide") {
      wideTimer = 12;
    } else if (power.kind === "slow") {
      slowTimer = 8;
    } else if (power.kind === "life") {
      lives++;
    } else if (power.kind === "multi") {
      spawnMultiball();
    }
    updateHud();
  }

  function spawnMultiball() {
    const source = balls.find(b => b.launched) || balls[0];
    if (!source) return;
    const baseAngle = Math.atan2(source.vy, source.vx);
    for (const offset of [-0.58, 0.58]) {
      if (balls.length >= 7) break;
      const b = makeBall(source.x, source.y);
      const angle = baseAngle + offset;
      b.launched = true;
      b.speed = source.speed;
      b.vx = Math.cos(angle) * b.speed;
      b.vy = Math.sin(angle) * b.speed;
      balls.push(b);
    }
    spawnRing(source.x, source.y, "#ff67ad", 90);
  }

  function hitBrick(brick, ball) {
    brick.flash = 0.16;

    if (brick.type === "steel") {
      burst(ball.x, ball.y, "#c7d3ea", 6, 135);
      shake = Math.max(shake, 1.2);
      sfx("steel");
      return;
    }

    brick.hp--;
    addScore(15 * comboMultiplier());
    burst(ball.x, ball.y, brick.color, 5, 120);

    if (brick.hp <= 0) {
      brick.alive = false;
      destructibleLeft = Math.max(0, destructibleLeft - 1);

      if (comboTimer > 0) combo = Math.min(99, combo + 1);
      else combo = 1;
      comboTimer = 2.7;

      const multiplier = comboMultiplier();
      const points = 100 * brick.maxHp * multiplier;
      addScore(points);

      const cx = brick.x + brick.w / 2;
      const cy = brick.y + brick.h / 2;
      spawnPopup(cx, cy, `+${formatScore(points)}`, brick.color);
      spawnRing(cx, cy, brick.color, brick.type === "boss" ? 180 : 48);
      burst(cx, cy, brick.color, brick.type === "boss" ? 65 : 15, brick.type === "boss" ? 360 : 220);
      shake = Math.max(shake, brick.type === "boss" ? 16 : 3.2);
      flash = Math.max(flash, brick.type === "boss" ? 0.2 : 0.045);
      hitStop = Math.max(hitStop, brick.type === "boss" ? 0.065 : 0.016);
      ball.speed = Math.min(620, ball.speed + 2.5);
      maybeDropPowerup(brick);
      sfx("destroy");

      if (combo > 1 && combo % 5 === 0) {
        showToast(`${combo} HIT STREAK!`, "#ffd166");
      }
    } else {
      shake = Math.max(shake, 0.8);
      sfx("brick");
    }

    updateHud();
  }

  function currentBallSpeed(ball) {
    return ball.speed * (slowTimer > 0 ? 0.68 : 1);
  }

  function normalizeBall(ball) {
    const speed = currentBallSpeed(ball);
    let mag = Math.hypot(ball.vx, ball.vy);
    if (!isFinite(mag) || mag < 0.001) {
      ball.vx = 0;
      ball.vy = -speed;
      mag = speed;
    }
    ball.vx = ball.vx / mag * speed;
    ball.vy = ball.vy / mag * speed;

    if (Math.abs(ball.vy) < speed * 0.2) {
      const signY = ball.vy < 0 ? -1 : 1;
      const signX = ball.vx === 0 ? (Math.random() < 0.5 ? -1 : 1) : Math.sign(ball.vx);
      ball.vy = signY * speed * 0.2;
      ball.vx = signX * Math.sqrt(Math.max(0, speed * speed - ball.vy * ball.vy));
    }
  }

  function collideWithBrick(ball, brick) {
    const closestX = Math.max(brick.x, Math.min(ball.x, brick.x + brick.w));
    const closestY = Math.max(brick.y, Math.min(ball.y, brick.y + brick.h));
    const dx = ball.x - closestX;
    const dy = ball.y - closestY;
    const distSq = dx * dx + dy * dy;

    if (distSq > ball.r * ball.r) return false;

    let nx, ny, penetration;
    const dist = Math.sqrt(distSq);

    if (dist > 0.0001) {
      nx = dx / dist;
      ny = dy / dist;
      penetration = ball.r - dist;
    } else {
      const left = ball.x - brick.x;
      const right = brick.x + brick.w - ball.x;
      const top = ball.y - brick.y;
      const bottom = brick.y + brick.h - ball.y;
      const smallest = Math.min(left, right, top, bottom);
      if (smallest === left) { nx = -1; ny = 0; penetration = left + ball.r; }
      else if (smallest === right) { nx = 1; ny = 0; penetration = right + ball.r; }
      else if (smallest === top) { nx = 0; ny = -1; penetration = top + ball.r; }
      else { nx = 0; ny = 1; penetration = bottom + ball.r; }
    }

    ball.x += nx * (penetration + 0.25);
    ball.y += ny * (penetration + 0.25);

    const dot = ball.vx * nx + ball.vy * ny;
    if (dot < 0) {
      ball.vx -= 2 * dot * nx;
      ball.vy -= 2 * dot * ny;
    }
    return true;
  }

  function updateBall(ball, dt) {
    if (!ball.launched) {
      ball.x = paddle.x + paddle.w / 2;
      ball.y = paddle.y - ball.r - 2;
      ball.trail.length = 0;
      return;
    }

    ball.px = ball.x;
    ball.py = ball.y;
    normalizeBall(ball);
    ball.x += ball.vx * dt;
    ball.y += ball.vy * dt;

    ball.trail.push({ x: ball.x, y: ball.y });
    if (ball.trail.length > 11) ball.trail.shift();

    if (ball.x - ball.r < 0) {
      ball.x = ball.r;
      ball.vx = Math.abs(ball.vx);
      sfx("wall");
    } else if (ball.x + ball.r > W) {
      ball.x = W - ball.r;
      ball.vx = -Math.abs(ball.vx);
      sfx("wall");
    }

    if (ball.y - ball.r < 0) {
      ball.y = ball.r;
      ball.vy = Math.abs(ball.vy);
      sfx("wall");
    }

    const paddleCenter = paddle.x + paddle.w / 2;
    const previousBottom = ball.py + ball.r;
    const overlapsPaddle =
      ball.x + ball.r >= paddle.x &&
      ball.x - ball.r <= paddle.x + paddle.w;

    if (ball.vy > 0 && previousBottom <= paddle.y + 9 && ball.y + ball.r >= paddle.y && overlapsPaddle) {
      const offset = Math.max(-1, Math.min(1, (ball.x - paddleCenter) / (paddle.w / 2)));
      const angle = offset * 1.12 + Math.max(-0.12, Math.min(0.12, paddle.vx / 1200));
      const speed = currentBallSpeed(ball);
      ball.vx = Math.sin(angle) * speed + paddle.vx * 0.10;
      ball.vy = -Math.abs(Math.cos(angle) * speed);
      ball.y = paddle.y - ball.r - 0.5;
      paddle.flash = 0.14;
      burst(ball.x, ball.y, "#8df7ff", 6, 120);
      sfx("paddle");
    }

    for (const brick of bricks) {
      if (!brick.alive || brick.cooldown > 0) continue;
      if (collideWithBrick(ball, brick)) {
        brick.cooldown = 0.055;
        hitBrick(brick, ball);
        break;
      }
    }

    normalizeBall(ball);

    if (ball.y - ball.r > H + 8) {
      ball.dead = true;
      burst(ball.x, H - 4, "#ff4f9a", 13, 220);
    }
  }

  function updateBalls(dt) {
    balls = balls.filter(b => !b.dead);
    const steps = 2;
    const subDt = dt / steps;
    for (let s = 0; s < steps; s++) {
      for (const ball of balls.slice()) {
        if (!ball.dead) updateBall(ball, subDt);
      }
    }
    balls = balls.filter(b => !b.dead);
  }

  function updatePaddle(dt) {
    const desiredWidth = BASE_PADDLE_W + (wideTimer > 0 ? 54 : 0);
    paddle.w += (desiredWidth - paddle.w) * (1 - Math.exp(-11 * dt));

    const left = keys.has("ArrowLeft") || keys.has("KeyA");
    const right = keys.has("ArrowRight") || keys.has("KeyD");
    const axis = (right ? 1 : 0) - (left ? 1 : 0);
    const oldCenter = paddle.x + paddle.w / 2;
    let center = oldCenter;

    if (axis !== 0) {
      inputMode = "keyboard";
      center += axis * 720 * dt;
    } else if (inputMode === "mouse" && mouse.active) {
      center += (mouse.center - center) * (1 - Math.exp(-24 * dt));
    }

    center = Math.max(paddle.w / 2, Math.min(W - paddle.w / 2, center));
    paddle.x = center - paddle.w / 2;
    paddle.vx = dt > 0 ? (center - oldCenter) / dt : 0;
  }

  function updatePowerDrops(dt) {
    for (let i = powerups.length - 1; i >= 0; i--) {
      const p = powerups[i];
      p.y += p.vy * dt;
      p.rotation += dt * 2.2;

      const caught =
        p.y + p.h >= paddle.y &&
        p.y <= paddle.y + paddle.h &&
        p.x + p.w >= paddle.x &&
        p.x <= paddle.x + paddle.w;

      if (caught) {
        applyPowerup(p);
        powerups.splice(i, 1);
      } else if (p.y > H + 35) {
        powerups.splice(i, 1);
      }
    }
  }

  function updateTimers(dt) {
    wideTimer = Math.max(0, wideTimer - dt);
    slowTimer = Math.max(0, slowTimer - dt);
    const sig = `${Math.ceil(wideTimer)}:${Math.ceil(slowTimer)}`;
    if (sig !== boostSignature) updateHud();
  }

  function updateEffects(dt) {
    for (const p of particles) {
      p.life -= dt;
      p.x += p.vx * dt;
      p.y += p.vy * dt;
      p.vy += p.gravity * dt;
      p.vx *= Math.exp(-1.8 * dt);
      p.rot += p.spin * dt;
    }
    particles = particles.filter(p => p.life > 0);

    for (const r of rings) {
      r.life -= dt;
      r.radius += (r.maxRadius - r.radius) * (1 - Math.exp(-10 * dt));
    }
    rings = rings.filter(r => r.life > 0);

    for (const p of popups) {
      p.life -= dt;
      p.y -= 36 * dt;
    }
    popups = popups.filter(p => p.life > 0);

    for (const b of bricks) {
      b.flash = Math.max(0, b.flash - dt);
      b.cooldown = Math.max(0, b.cooldown - dt);
    }

    paddle.flash = Math.max(0, paddle.flash - dt);
    shake *= Math.exp(-13 * dt);
    flash *= Math.exp(-10 * dt);

    if (toast) {
      toast.life -= dt;
      if (toast.life <= 0) toast = null;
    }
    if (levelBanner) {
      levelBanner.life -= dt;
      if (levelBanner.life <= 0) levelBanner = null;
    }
  }

  function updatePlaying(dt) {
    if (comboTimer > 0) {
      comboTimer -= dt;
      if (comboTimer <= 0) {
        combo = 0;
        comboTimer = 0;
        updateHud();
      }
    }

    updateTimers(dt);
    updatePaddle(dt);
    updateBalls(dt);
    updatePowerDrops(dt);

    if (destructibleLeft <= 0) {
      completeLevel();
    } else if (balls.length === 0) {
      loseLife();
    }
  }

  function update(dt) {
    visualTime += dt;

    if (state === "paused") return;

    updateEffects(dt);

    if (state === "playing") {
      updatePlaying(dt);
    } else if (state === "serve") {
      updatePaddle(dt);
      for (const ball of balls) {
        if (!ball.launched) {
          ball.x = paddle.x + paddle.w / 2;
          ball.y = paddle.y - ball.r - 2;
        }
      }
    } else if (state === "dying") {
      deathTimer -= dt;
      if (deathTimer <= 0) prepareServe();
    }
  }

  function updateHud() {
    els.score.textContent = formatScore(score);
    els.level.textContent = `${levelIndex + 1} / ${levels.length}`;
    els.lives.textContent = "♥".repeat(Math.max(0, Math.min(3, lives))) + "♡".repeat(Math.max(0, 3 - Math.min(3, lives)));
    els.combo.textContent = comboTimer > 0 ? `×${comboMultiplier()}` : "—";
    els.best.textContent = formatScore(best);

    const boosts = [];
    if (wideTimer > 0) boosts.push(`<span class="boost" style="color:#62f3ff">WIDE ${Math.ceil(wideTimer)}s</span>`);
    if (slowTimer > 0) boosts.push(`<span class="boost" style="color:#a879ff">SLOW ${Math.ceil(slowTimer)}s</span>`);
    if (comboTimer > 0) boosts.push(`<span class="boost" style="color:#ffd166">CHAIN ×${comboMultiplier()}</span>`);
    els.boostList.innerHTML = boosts.length ? boosts.join("") : '<span class="empty-boost">No active boosts</span>';
    boostSignature = `${Math.ceil(wideTimer)}:${Math.ceil(slowTimer)}`;

    els.pause.disabled = !(state === "playing" || state === "serve" || state === "paused");
    els.pause.textContent = state === "paused" ? "RESUME" : "PAUSE";
  }

  function drawBackground() {
    const accent = levels[levelIndex].accent;
    ctx.fillStyle = "#050713";
    ctx.fillRect(0, 0, W, H);

    const glow = ctx.createRadialGradient(W * 0.5, 80, 15, W * 0.5, H * 0.52, W * 0.72);
    glow.addColorStop(0, alphaColor(accent, 0.16));
    glow.addColorStop(0.42, "rgba(16, 19, 43, 0.72)");
    glow.addColorStop(1, "rgba(3, 5, 13, 0)");
    ctx.fillStyle = glow;
    ctx.fillRect(0, 0, W, H);

    ctx.save();
    ctx.strokeStyle = "rgba(124, 144, 213, 0.065)";
    ctx.lineWidth = 1;
    const grid = 48;
    const offset = (visualTime * 9) % grid;
    for (let x = -grid + offset; x < W + grid; x += grid) {
      ctx.beginPath();
      ctx.moveTo(x, 0);
      ctx.lineTo(x, H);
      ctx.stroke();
    }
    for (let y = -grid + offset; y < H + grid; y += grid) {
      ctx.beginPath();
      ctx.moveTo(0, y);
      ctx.lineTo(W, y);
      ctx.stroke();
    }
    ctx.restore();

    for (const star of stars) {
      const twinkle = 0.25 + 0.55 * (0.5 + 0.5 * Math.sin(visualTime * star.speed + star.phase));
      ctx.globalAlpha = twinkle;
      ctx.fillStyle = "#dceaff";
      ctx.beginPath();
      ctx.arc(star.x, star.y, star.r, 0, TAU);
      ctx.fill();
    }
    ctx.globalAlpha = 1;

    const floor = ctx.createLinearGradient(0, H - 130, 0, H);
    floor.addColorStop(0, "rgba(5, 7, 19, 0)");
    floor.addColorStop(1, "rgba(4, 5, 14, 0.72)");
    ctx.fillStyle = floor;
    ctx.fillRect(0, H - 130, W, 130);
  }

  function drawBricks() {
    for (const b of bricks) {
      if (!b.alive) continue;
      ctx.save();

      const pulse = b.type === "boss" ? 0.75 + Math.sin(visualTime * 7) * 0.25 : 1;
      ctx.shadowColor = b.color;
      ctx.shadowBlur = b.flash > 0 ? 28 : b.type === "boss" ? 22 : 8;

      let gradient = ctx.createLinearGradient(b.x, b.y, b.x, b.y + b.h);
      if (b.type === "steel") {
        gradient.addColorStop(0, "#8390a8");
        gradient.addColorStop(0.5, "#465269");
        gradient.addColorStop(1, "#242c40");
      } else if (b.type === "boss") {
        gradient.addColorStop(0, `rgba(255, 70, 119, ${pulse})`);
        gradient.addColorStop(0.52, "#8d1747");
        gradient.addColorStop(1, "#250b29");
      } else {
        gradient.addColorStop(0, b.color);
        gradient.addColorStop(1, "#151a35");
      }

      roundRectPath(ctx, b.x, b.y, b.w, b.h, 5);
      ctx.fillStyle = gradient;
      ctx.fill();
      ctx.shadowBlur = 0;

      ctx.fillStyle = "rgba(255,255,255,0.18)";
      roundRectPath(ctx, b.x + 3, b.y + 3, b.w - 6, 5, 2);
      ctx.fill();

      ctx.fillStyle = "rgba(0,0,0,0.2)";
      roundRectPath(ctx, b.x + 4, b.y + b.h - 6, b.w - 8, 3, 1.5);
      ctx.fill();

      if (b.type === "steel") {
        ctx.save();
        roundRectPath(ctx, b.x, b.y, b.w, b.h, 5);
        ctx.clip();
        ctx.strokeStyle = "rgba(255,255,255,0.13)";
        ctx.lineWidth = 5;
        for (let x = b.x - b.h; x < b.x + b.w + b.h; x += 17) {
          ctx.beginPath();
          ctx.moveTo(x, b.y + b.h);
          ctx.lineTo(x + b.h, b.y);
          ctx.stroke();
        }
        ctx.restore();

        ctx.fillStyle = "#c7d1e3";
        for (const rx of [b.x + 7, b.x + b.w - 7]) {
          ctx.beginPath();
          ctx.arc(rx, b.y + b.h / 2, 1.8, 0, TAU);
          ctx.fill();
        }
      }

      if (b.destructible && b.hp < b.maxHp) {
        const cracks = Math.min(5, b.maxHp - b.hp + 1);
        ctx.strokeStyle = "rgba(7, 8, 18, 0.58)";
        ctx.lineWidth = 1.7;
        ctx.beginPath();
        for (let i = 0; i < cracks; i++) {
          const x = b.x + b.w * (0.2 + i * 0.16);
          ctx.moveTo(x, b.y + 2);
          ctx.lineTo(x - 4, b.y + b.h * 0.48);
          ctx.lineTo(x + 3, b.y + b.h - 2);
        }
        ctx.stroke();
      }

      if (b.type === "boss") {
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.font = "900 15px system-ui, sans-serif";
        ctx.fillStyle = "#fff0f5";
        ctx.fillText("THE CORE", b.x + b.w / 2, b.y + b.h / 2 - 2);
        ctx.fillStyle = "rgba(10,5,18,0.55)";
        ctx.fillRect(b.x + 10, b.y + b.h - 7, b.w - 20, 3);
        ctx.fillStyle = "#ff7ba9";
        ctx.fillRect(b.x + 10, b.y + b.h - 7, (b.w - 20) * Math.max(0, b.hp / b.maxHp), 3);
      }

      if (b.flash > 0) {
        ctx.globalAlpha = Math.min(1, b.flash / 0.16);
        ctx.fillStyle = "#ffffff";
        roundRectPath(ctx, b.x, b.y, b.w, b.h, 5);
        ctx.fill();
      }

      ctx.restore();
    }
  }

  function drawPaddle() {
    ctx.save();
    const gradient = ctx.createLinearGradient(paddle.x, paddle.y, paddle.x, paddle.y + paddle.h);
    gradient.addColorStop(0, wideTimer > 0 ? "#fff4bd" : "#b8fbff");
    gradient.addColorStop(0.42, wideTimer > 0 ? "#ffd166" : "#56efff");
    gradient.addColorStop(1, wideTimer > 0 ? "#d97832" : "#7b65ff");

    ctx.shadowColor = wideTimer > 0 ? "#ffd166" : "#56efff";
    ctx.shadowBlur = paddle.flash > 0 ? 35 : 21;
    roundRectPath(ctx, paddle.x, paddle.y, paddle.w, paddle.h, 9);
    ctx.fillStyle = gradient;
    ctx.fill();
    ctx.shadowBlur = 0;

    ctx.fillStyle = "rgba(255,255,255,0.68)";
    roundRectPath(ctx, paddle.x + 11, paddle.y + 3, paddle.w - 22, 4, 2);
    ctx.fill();

    if (wideTimer > 0) {
      ctx.strokeStyle = `rgba(255, 238, 150, ${0.55 + Math.sin(visualTime * 12) * 0.25})`;
      ctx.lineWidth = 2;
      roundRectPath(ctx, paddle.x - 2, paddle.y - 2, paddle.w + 4, paddle.h + 4, 10);
      ctx.stroke();
    }
    ctx.restore();
  }

  function drawBalls() {
    for (const ball of balls) {
      for (let i = 0; i < ball.trail.length; i++) {
        const point = ball.trail[i];
        const t = (i + 1) / ball.trail.length;
        ctx.globalAlpha = t * 0.28;
        ctx.fillStyle = ball.color;
        ctx.beginPath();
        ctx.arc(point.x, point.y, ball.r * (0.25 + t * 0.75), 0, TAU);
        ctx.fill();
      }
      ctx.globalAlpha = 1;

      const pulse = ball.launched ? 1 : 1 + Math.sin(visualTime * 8) * 0.09;
      ctx.save();
      ctx.shadowColor = ball.color;
      ctx.shadowBlur = 22;
      ctx.fillStyle = ball.color;
      ctx.beginPath();
      ctx.arc(ball.x, ball.y, ball.r * pulse, 0, TAU);
      ctx.fill();
      ctx.shadowBlur = 0;
      ctx.fillStyle = "#ffffff";
      ctx.beginPath();
      ctx.arc(ball.x - 2, ball.y - 2, ball.r * 0.36, 0, TAU);
      ctx.fill();
      ctx.restore();
    }
  }

  function drawPowerups() {
    for (const p of powerups) {
      const config = POWER[p.kind];
      const cx = p.x + p.w / 2;
      const cy = p.y + p.h / 2;

      ctx.save();
      ctx.globalAlpha = 0.18;
      ctx.fillStyle = config.color;
      ctx.beginPath();
      ctx.arc(cx, cy - 9, 13, 0, TAU);
      ctx.fill();
      ctx.restore();

      ctx.save();
      ctx.translate(cx, cy);
      ctx.rotate(p.rotation);
      ctx.shadowColor = config.color;
      ctx.shadowBlur = 18;
      roundRectPath(ctx, -14, -14, 28, 28, 7);
      ctx.fillStyle = alphaColor(config.color, 0.18);
      ctx.fill();
      ctx.strokeStyle = config.color;
      ctx.lineWidth = 2;
      ctx.stroke();
      ctx.restore();

      ctx.save();
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillStyle = config.color;
      ctx.font = "950 16px system-ui, sans-serif";
      ctx.fillText(config.symbol, cx, cy + 0.5);
      ctx.restore();
    }
  }

  function drawParticles() {
    for (const p of particles) {
      const alpha = Math.max(0, p.life / p.maxLife);
      ctx.save();
      ctx.globalAlpha = alpha;
      ctx.fillStyle = p.color;
      if (p.type === "confetti") {
        ctx.translate(p.x, p.y);
        ctx.rotate(p.rot);
        ctx.fillRect(-p.size / 2, -p.size / 4, p.size, p.size / 2);
      } else {
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.size * (0.4 + alpha * 0.6), 0, TAU);
        ctx.fill();
      }
      ctx.restore();
    }

    for (const r of rings) {
      ctx.save();
      ctx.globalAlpha = Math.max(0, r.life / r.maxLife);
      ctx.strokeStyle = r.color;
      ctx.lineWidth = 2.5;
      ctx.beginPath();
      ctx.arc(r.x, r.y, r.radius, 0, TAU);
      ctx.stroke();
      ctx.restore();
    }
  }

  function drawPopups() {
    ctx.save();
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.font = "900 16px system-ui, sans-serif";
    for (const p of popups) {
      const alpha = Math.max(0, p.life / p.maxLife);
      ctx.globalAlpha = alpha;
      ctx.lineWidth = 4;
      ctx.strokeStyle = "rgba(3,5,14,0.8)";
      ctx.strokeText(p.text, p.x, p.y);
      ctx.fillStyle = p.color;
      ctx.fillText(p.text, p.x, p.y);
    }
    ctx.restore();
  }

  function drawCanvasHud() {
    const accent = levels[levelIndex].accent;
    ctx.save();
    ctx.textBaseline = "middle";

    ctx.fillStyle = "#7784a7";
    ctx.font = "900 9px system-ui, sans-serif";
    ctx.textAlign = "left";
    ctx.fillText(`SECTOR ${String(levelIndex + 1).padStart(2, "0")} / ${String(levels.length).padStart(2, "0")}`, 24, 22);

    ctx.fillStyle = accent;
    ctx.font = "900 16px system-ui, sans-serif";
    ctx.fillText(levels[levelIndex].name, 24, 45);

    const progress = levelTotal ? 1 - destructibleLeft / levelTotal : 0;
    const barX = W - 212;
    const barY = 28;
    ctx.fillStyle = "rgba(120,140,190,0.15)";
    roundRectPath(ctx, barX, barY, 188, 7, 4);
    ctx.fill();
    ctx.fillStyle = accent;
    roundRectPath(ctx, barX, barY, Math.max(4, 188 * progress), 7, 4);
    ctx.fill();
    ctx.fillStyle = "#8290b2";
    ctx.font = "800 8px system-ui, sans-serif";
    ctx.textAlign = "right";
    ctx.fillText(`${Math.max(0, destructibleLeft)} TILES LEFT`, W - 24, 49);

    if (comboTimer > 0 && combo > 0) {
      const scale = 1 + Math.max(0, (comboTimer - 2.35)) * 0.35;
      ctx.translate(W / 2, 32);
      ctx.scale(scale, scale);
      ctx.textAlign = "center";
      ctx.fillStyle = "#ffd166";
      ctx.shadowColor = "rgba(255,209,102,0.5)";
      ctx.shadowBlur = 12;
      ctx.font = "950 17px system-ui, sans-serif";
      ctx.fillText(`CHAIN ${combo}  ×${comboMultiplier()}`, 0, 0);
    }

    if (toast) {
      const alpha = Math.min(1, toast.life * 2);
      ctx.globalAlpha = alpha;
      ctx.font = "950 12px system-ui, sans-serif";
      const textWidth = ctx.measureText(toast.text).width + 28;
      ctx.fillStyle = "rgba(7,10,24,0.88)";
      roundRectPath(ctx, W / 2 - textWidth / 2, H - 43, textWidth, 25, 12);
      ctx.fill();
      ctx.strokeStyle = toast.color;
      ctx.lineWidth = 1;
      ctx.stroke();
      ctx.fillStyle = toast.color;
      ctx.textAlign = "center";
      ctx.fillText(toast.text, W / 2, H - 30);
    }

    ctx.restore();
  }

  function drawStateUI() {
    if (levelBanner && state === "serve") {
      ctx.save();
      const alpha = Math.min(1, levelBanner.life / 0.4);
      ctx.globalAlpha = alpha;
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillStyle = levels[levelIndex].accent;
      ctx.font = "950 29px system-ui, sans-serif";
      ctx.fillText(levelBanner.title, W / 2, 323);
      ctx.fillStyle = "#a9b5d0";
      ctx.font = "700 12px system-ui, sans-serif";
      ctx.fillText(levelBanner.subtitle, W / 2, 350);
      ctx.restore();
    }

    if (state === "serve") {
      ctx.save();
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillStyle = "rgba(6,9,23,0.76)";
      roundRectPath(ctx, W / 2 - 186, 395, 372, 98, 15);
      ctx.fill();
      ctx.strokeStyle = "rgba(139,158,255,0.18)";
      ctx.lineWidth = 1;
      ctx.stroke();
      ctx.fillStyle = "#ffffff";
      ctx.font = "950 25px system-ui, sans-serif";
      ctx.fillText("READY?", W / 2, 425);
      ctx.fillStyle = "#8f9cba";
      ctx.font = "800 10px system-ui, sans-serif";
      ctx.fillText("SPACE / CLICK TO LAUNCH", W / 2, 462);
      ctx.restore();
    }

    if (state === "dying") {
      ctx.save();
      ctx.globalAlpha = Math.min(1, deathTimer * 1.4);
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.fillStyle = "#ff6fa7";
      ctx.font = "950 30px system-ui, sans-serif";
      ctx.fillText("BALL LOST", W / 2, H / 2 - 10);
      ctx.fillStyle = "#9aa7c5";
      ctx.font = "800 11px system-ui, sans-serif";
      ctx.fillText(`${lives} LIVES REMAINING`, W / 2, H / 2 + 22);
      ctx.restore();
    }
  }

  function render() {
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    ctx.clearRect(0, 0, W, H);
    drawBackground();

    ctx.save();
    if (shake > 0.05) {
      ctx.translate((Math.random() - 0.5) * shake, (Math.random() - 0.5) * shake);
    }

    drawBricks();
    drawPowerups();
    drawPaddle();
    drawBalls();
    drawParticles();
    drawPopups();
    drawCanvasHud();
    drawStateUI();
    ctx.restore();

    if (flash > 0.005) {
      ctx.fillStyle = `rgba(255,255,255,${flash})`;
      ctx.fillRect(0, 0, W, H);
    }
  }

  let audioCtx = null;
  let soundOn = true;
  let lastBrickSound = 0;

  function ensureAudio() {
    try {
      if (!audioCtx) {
        const AudioCtor = window.AudioContext || window.webkitAudioContext;
        if (AudioCtor) audioCtx = new AudioCtor();
      }
      if (audioCtx && audioCtx.state === "suspended") audioCtx.resume();
    } catch (_) {}
  }

  function tone(freq, duration, type = "sine", volume = 0.04, slide = 0, delay = 0) {
    if (!soundOn || !audioCtx) return;
    const now = audioCtx.currentTime + delay;
    const oscillator = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    oscillator.type = type;
    oscillator.frequency.setValueAtTime(Math.max(25, freq), now);
    if (slide) {
      oscillator.frequency.exponentialRampToValueAtTime(Math.max(25, freq + slide), now + duration);
    }
    gain.gain.setValueAtTime(0.0001, now);
    gain.gain.exponentialRampToValueAtTime(volume, now + 0.008);
    gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
    oscillator.connect(gain);
    gain.connect(audioCtx.destination);
    oscillator.start(now);
    oscillator.stop(now + duration + 0.02);
  }

  function sfx(name) {
    if (!soundOn) return;
    const now = performance.now();

    if (name === "paddle") {
      tone(180, 0.085, "sine", 0.045, 160);
    } else if (name === "wall") {
      tone(150, 0.035, "triangle", 0.018, 40);
    } else if (name === "brick") {
      if (now - lastBrickSound < 38) return;
      lastBrickSound = now;
      tone(360 + Math.random() * 100, 0.055, "square", 0.018, 120);
    } else if (name === "steel") {
      if (now - lastBrickSound < 55) return;
      lastBrickSound = now;
      tone(720, 0.07, "square", 0.022, -420);
    } else if (name === "destroy") {
      tone(430, 0.11, "triangle", 0.035, 310);
      tone(620, 0.08, "sine", 0.022, 170, 0.035);
    } else if (name === "power") {
      tone(440, 0.11, "sine", 0.04, 190);
      tone(660, 0.12, "sine", 0.035, 230, 0.08);
      tone(880, 0.14, "sine", 0.03, 160, 0.16);
    } else if (name === "launch") {
      tone(190, 0.16, "triangle", 0.04, 470);
    } else if (name === "lose") {
      tone(260, 0.4, "sawtooth", 0.035, -190);
    } else if (name === "clear") {
      [440, 554, 659, 880].forEach((f, i) => tone(f, 0.18, "triangle", 0.035, 90, i * 0.09));
    }
  }

  function toggleSound() {
    soundOn = !soundOn;
    if (soundOn) {
      ensureAudio();
      sfx("power");
    }
    els.sound.textContent = soundOn ? "SOUND ON" : "SOUND OFF";
    els.sound.setAttribute("aria-pressed", String(soundOn));
  }

  function pointerPosition(event) {
    const rect = canvas.getBoundingClientRect();
    return {
      x: (event.clientX - rect.left) * W / rect.width,
      y: (event.clientY - rect.top) * H / rect.height
    };
  }

  canvas.addEventListener("pointermove", event => {
    if (state === "title") return;
    const p = pointerPosition(event);
    mouse.active = true;
    mouse.center = Math.max(0, Math.min(W, p.x));
    inputMode = "mouse";
  });

  canvas.addEventListener("pointerdown", event => {
    if (event.pointerType === "mouse" && event.button !== 0) return;
    event.preventDefault();
    ensureAudio();
    const p = pointerPosition(event);
    mouse.active = true;
    mouse.center = Math.max(0, Math.min(W, p.x));
    inputMode = "mouse";
    try { canvas.setPointerCapture(event.pointerId); } catch (_) {}
    activatePrimary();
  });

  canvas.addEventListener("pointerup", event => {
    try { canvas.releasePointerCapture(event.pointerId); } catch (_) {}
  });

  window.addEventListener("keydown", event => {
    if (event.ctrlKey || event.metaKey || event.altKey) return;

    if (["ArrowLeft", "ArrowRight", "ArrowDown", "ArrowUp", "Space"].includes(event.code)) {
      event.preventDefault();
    }

    if (event.code === "ArrowLeft" || event.code === "ArrowRight" || event.code === "KeyA" || event.code === "KeyD") {
      keys.add(event.code);
      inputMode = "keyboard";
    }

    if (event.code === "Space" && !event.repeat) {
      activatePrimary();
    } else if (event.code === "KeyP" || event.code === "Escape") {
      if (!event.repeat) togglePause();
    } else if (event.code === "KeyR" && !event.repeat) {
      startGame();
    } else if (event.code === "KeyM" && !event.repeat) {
      toggleSound();
    }
  });

  window.addEventListener("keyup", event => {
    keys.delete(event.code);
  });

  window.addEventListener("blur", () => {
    keys.clear();
    if (state === "playing" || state === "serve") togglePause();
  });

  document.addEventListener("visibilitychange", () => {
    if (document.hidden && (state === "playing" || state === "serve")) togglePause();
  });

  els.overlayButton.addEventListener("click", event => {
    event.stopPropagation();
    ensureAudio();
    if (overlayAction) overlayAction();
  });

  els.pause.addEventListener("click", togglePause);
  els.sound.addEventListener("click", toggleSound);
  els.reset.addEventListener("click", startGame);

  window.addEventListener("resize", resizeCanvas);

  let lastTime = performance.now();

  function frame(now) {
    const dt = Math.min(0.033, Math.max(0, (now - lastTime) / 1000));
    lastTime = now;

    if (hitStop > 0) {
      hitStop = Math.max(0, hitStop - dt);
    } else {
      update(dt);
    }

    render();
    requestAnimationFrame(frame);
  }

  resizeCanvas();
  loadBricks(0);
  updateHud();
  showTitle();
  requestAnimationFrame(frame);
})();
</script>
</body>
</html>

Compare with

MiniMax M3.1 Flash: other attempts