{"project":{"id":"7rjad3V","userId":"davidyarham@gmail.com","username":null,"userPicture":null,"name":"Chrono Miner","thumbnail":"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","visible":true,"contributors":"","githubRepo":null,"forkedFrom":null,"tags":"","files":{"folder":"","files":[{"name":"index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Untitled</title>\n  <link rel=\"stylesheet\" href=\"style.css\">\n<link href=\"https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap\" rel=\"stylesheet\">\n</head>\n<body>\n<div id=\"game-wrapper\">\n\t<canvas id=\"game\" width=\"480\" height=\"640\"></canvas>\n\t<div id=\"scanlines\"></div>\n\t<div id=\"ui-overlay\">\n\t\t<div id=\"hud\">\n\t\t\t<div class=\"hud__group\">\n\t\t\t\t<span class=\"hud__label\">SCORE</span>\n\t\t\t\t<span class=\"hud__value\" id=\"hud-score\">0000000</span>\n\t\t\t</div>\n\t\t\t<div class=\"hud__group\">\n\t\t\t\t<span class=\"hud__label\">ZONE</span>\n\t\t\t\t<span class=\"hud__value\" id=\"hud-zone\">SUNLIT</span>\n\t\t\t</div>\n\t\t\t<div class=\"hud__group\">\n\t\t\t\t<span class=\"hud__label\">DEPTH</span>\n\t\t\t\t<span class=\"hud__value\" id=\"hud-depth\">0m</span>\n\t\t\t</div>\n\t\t\t<div class=\"hud__group\">\n\t\t\t\t<span class=\"hud__label\">BATTERY</span>\n\t\t\t\t<div class=\"hud__bar\">\n\t\t\t\t\t<div class=\"hud__bar-fill\" id=\"hud-battery\"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"hud__group hud__group--lives\">\n\t\t\t\t<span class=\"hud__label\">HULL</span>\n\t\t\t\t<span id=\"hud-hull\"></span>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\t<div id=\"title-screen\" class=\"overlay\">\n\t\t<div class=\"title\">\n\t\t\t<div class=\"title__main\">PHANTOM</div>\n\t\t\t<div class=\"title__sub\">DEPTHS</div>\n\t\t</div>\n\t\t<div class=\"title__tagline\">INTO THE ABYSS</div>\n\t\t<div class=\"title__instructions\">\n\t\t\t<p><kbd>←</kbd> <kbd>→</kbd> STEER &nbsp; <kbd>↑</kbd> THRUST &nbsp; <kbd>↓</kbd> DIVE</p>\n\t\t\t<p><kbd>SPACE</kbd> SONAR PING</p>\n\t\t</div>\n\t\t<button class=\"btn btn--start\" id=\"btn-start\">DIVE IN</button>\n\t\t<div class=\"title__credit\">© 2024 ABYSSAL ARCADE CO.</div>\n\t</div>\n\t<div id=\"game-over\" class=\"overlay overlay--hidden\">\n\t\t<div class=\"gameover__title\">HULL BREACH</div>\n\t\t<div class=\"gameover__score\">SCORE: <span id=\"final-score\">0</span></div>\n\t\t<div class=\"gameover__depth\">MAX DEPTH: <span id=\"final-depth\">0m</span></div>\n\t\t<button class=\"btn btn--start\" id=\"btn-restart\">RESURFACE? 9</button>\n\t</div>\n\t<div id=\"level-banner\" class=\"overlay overlay--hidden\">\n\t\t<div class=\"banner__era\" id=\"banner-zone\">TWILIGHT ZONE</div>\n\t\t<div class=\"banner__depth\" id=\"banner-depth\">DEPTH: 200m</div>\n\t</div>\n</div>\n  <script type=\"module\" src=\"main.js\"></script>\n</body>\n</html>"},{"name":"main.js","content":"(function() {\n\t'use strict';\n\n\tconst canvas = document.getElementById('game');\n\tconst ctx = canvas.getContext('2d');\n\tlet W = 480,\n\t\tH = 640;\n\tconst TILE = 16;\n\tlet ZOOM = 1.35;\n\tlet GEN_COLS = 30;\n\tconst ROWS = 250;\n\n\t// Audio\n\tlet audioCtx;\n\n\tfunction initAudio() {\n\t\tif (!audioCtx) audioCtx = new(window.AudioContext || window.webkitAudioContext)();\n\t}\n\n\tfunction playSound(type) {\n\t\tif (!audioCtx) return;\n\t\tconst t = audioCtx.currentTime;\n\t\tconst o = audioCtx.createOscillator();\n\t\tconst g = audioCtx.createGain();\n\t\tconst f = audioCtx.createBiquadFilter();\n\t\to.connect(f);\n\t\tf.connect(g);\n\t\tg.connect(audioCtx.destination);\n\t\tf.type = 'lowpass';\n\t\tswitch (type) {\n\t\t\tcase 'sonar': {\n\t\t\t\to.type = 'sine';\n\t\t\t\to.frequency.setValueAtTime(1200, t);\n\t\t\t\to.frequency.exponentialRampToValueAtTime(300, t + 0.6);\n\t\t\t\tf.frequency.setValueAtTime(3000, t);\n\t\t\t\tg.gain.setValueAtTime(0.15, t);\n\t\t\t\tg.gain.linearRampToValueAtTime(0, t + 0.6);\n\t\t\t\to.start(t);\n\t\t\t\to.stop(t + 0.6);\n\t\t\t\tconst o2 = audioCtx.createOscillator();\n\t\t\t\tconst g2 = audioCtx.createGain();\n\t\t\t\to2.connect(g2);\n\t\t\t\tg2.connect(audioCtx.destination);\n\t\t\t\to2.type = 'sine';\n\t\t\t\to2.frequency.setValueAtTime(800, t + 0.05);\n\t\t\t\to2.frequency.exponentialRampToValueAtTime(200, t + 0.65);\n\t\t\t\tg2.gain.setValueAtTime(0.08, t + 0.05);\n\t\t\t\tg2.gain.linearRampToValueAtTime(0, t + 0.65);\n\t\t\t\to2.start(t + 0.05);\n\t\t\t\to2.stop(t + 0.65);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'treasure': {\n\t\t\t\to.type = 'sine';\n\t\t\t\to.frequency.setValueAtTime(660, t);\n\t\t\t\to.frequency.linearRampToValueAtTime(1320, t + 0.12);\n\t\t\t\tf.frequency.setValueAtTime(5000, t);\n\t\t\t\tg.gain.setValueAtTime(0.15, t);\n\t\t\t\tg.gain.exponentialRampToValueAtTime(0.001, t + 0.3);\n\t\t\t\to.start(t);\n\t\t\t\to.stop(t + 0.3);\n\t\t\t\tconst o3 = audioCtx.createOscillator();\n\t\t\t\tconst g3 = audioCtx.createGain();\n\t\t\t\to3.connect(g3);\n\t\t\t\tg3.connect(audioCtx.destination);\n\t\t\t\to3.type = 'triangle';\n\t\t\t\to3.frequency.setValueAtTime(990, t + 0.06);\n\t\t\t\to3.frequency.linearRampToValueAtTime(1980, t + 0.2);\n\t\t\t\tg3.gain.setValueAtTime(0.08, t + 0.06);\n\t\t\t\tg3.gain.exponentialRampToValueAtTime(0.001, t + 0.35);\n\t\t\t\to3.start(t + 0.06);\n\t\t\t\to3.stop(t + 0.35);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'bubble': {\n\t\t\t\to.type = 'sine';\n\t\t\t\to.frequency.setValueAtTime(300 + Math.random() * 200, t);\n\t\t\t\to.frequency.exponentialRampToValueAtTime(800 + Math.random() * 400, t + 0.08);\n\t\t\t\tf.frequency.setValueAtTime(3000, t);\n\t\t\t\tg.gain.setValueAtTime(0.06, t);\n\t\t\t\tg.gain.linearRampToValueAtTime(0, t + 0.1);\n\t\t\t\to.start(t);\n\t\t\t\to.stop(t + 0.1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'hurt': {\n\t\t\t\to.type = 'sawtooth';\n\t\t\t\to.frequency.setValueAtTime(300, t);\n\t\t\t\to.frequency.linearRampToValueAtTime(80, t + 0.3);\n\t\t\t\tf.frequency.setValueAtTime(1500, t);\n\t\t\t\tg.gain.setValueAtTime(0.2, t);\n\t\t\t\tg.gain.linearRampToValueAtTime(0, t + 0.3);\n\t\t\t\to.start(t);\n\t\t\t\to.stop(t + 0.3);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'thrust': {\n\t\t\t\to.type = 'sawtooth';\n\t\t\t\to.frequency.setValueAtTime(60, t);\n\t\t\t\to.frequency.linearRampToValueAtTime(40, t + 0.06);\n\t\t\t\tf.frequency.setValueAtTime(500, t);\n\t\t\t\tg.gain.setValueAtTime(0.05, t);\n\t\t\t\tg.gain.linearRampToValueAtTime(0, t + 0.06);\n\t\t\t\to.start(t);\n\t\t\t\to.stop(t + 0.06);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'zonein': {\n\t\t\t\t[0, 0.15, 0.3, 0.45].forEach((d, i) => {\n\t\t\t\t\tconst os = audioCtx.createOscillator();\n\t\t\t\t\tconst gn = audioCtx.createGain();\n\t\t\t\t\tos.connect(gn);\n\t\t\t\t\tgn.connect(audioCtx.destination);\n\t\t\t\t\tos.type = 'triangle';\n\t\t\t\t\tos.frequency.setValueAtTime([392, 494, 587, 784][i], t + d);\n\t\t\t\t\tgn.gain.setValueAtTime(0.1, t + d);\n\t\t\t\t\tgn.gain.linearRampToValueAtTime(0, t + d + 0.18);\n\t\t\t\t\tos.start(t + d);\n\t\t\t\t\tos.stop(t + d + 0.18);\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'death': {\n\t\t\t\to.type = 'sawtooth';\n\t\t\t\to.frequency.setValueAtTime(400, t);\n\t\t\t\to.frequency.exponentialRampToValueAtTime(30, t + 1);\n\t\t\t\tf.frequency.setValueAtTime(2000, t);\n\t\t\t\tf.frequency.linearRampToValueAtTime(80, t + 1);\n\t\t\t\tg.gain.setValueAtTime(0.2, t);\n\t\t\t\tg.gain.linearRampToValueAtTime(0, t + 1);\n\t\t\t\to.start(t);\n\t\t\t\to.stop(t + 1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'combo': {\n\t\t\t\to.type = 'triangle';\n\t\t\t\to.frequency.setValueAtTime(500, t);\n\t\t\t\to.frequency.linearRampToValueAtTime(1000, t + 0.08);\n\t\t\t\tf.frequency.setValueAtTime(5000, t);\n\t\t\t\tg.gain.setValueAtTime(0.12, t);\n\t\t\t\tg.gain.linearRampToValueAtTime(0, t + 0.12);\n\t\t\t\to.start(t);\n\t\t\t\to.stop(t + 0.12);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Music\n\tlet musicPlaying = false,\n\t\tmusicInterval;\n\tconst bassline = [55, 65, 73, 65, 55, 49, 55, 65];\n\tlet bassIdx = 0;\n\n\tfunction startMusic() {\n\t\tif (musicPlaying) return;\n\t\tmusicPlaying = true;\n\t\tbassIdx = 0;\n\t\tmusicInterval = setInterval(() => {\n\t\t\tif (!audioCtx) return;\n\t\t\tconst t = audioCtx.currentTime;\n\t\t\tconst ob = audioCtx.createOscillator();\n\t\t\tconst gb = audioCtx.createGain();\n\t\t\tconst fb = audioCtx.createBiquadFilter();\n\t\t\tob.connect(fb);\n\t\t\tfb.connect(gb);\n\t\t\tgb.connect(audioCtx.destination);\n\t\t\tob.type = 'triangle';\n\t\t\tfb.type = 'lowpass';\n\t\t\tfb.frequency.setValueAtTime(300, t);\n\t\t\tob.frequency.setValueAtTime(bassline[bassIdx % bassline.length], t);\n\t\t\tgb.gain.setValueAtTime(0.07, t);\n\t\t\tgb.gain.linearRampToValueAtTime(0, t + 0.22);\n\t\t\tob.start(t);\n\t\t\tob.stop(t + 0.25);\n\t\t\t// Ambient blip\n\t\t\tif (bassIdx % 4 === 0) {\n\t\t\t\tconst oa = audioCtx.createOscillator();\n\t\t\t\tconst ga = audioCtx.createGain();\n\t\t\t\toa.connect(ga);\n\t\t\t\tga.connect(audioCtx.destination);\n\t\t\t\toa.type = 'sine';\n\t\t\t\toa.frequency.setValueAtTime(880 + (bassIdx % 8) * 110, t);\n\t\t\t\tga.gain.setValueAtTime(0.03, t);\n\t\t\t\tga.gain.linearRampToValueAtTime(0, t + 0.3);\n\t\t\t\toa.start(t);\n\t\t\t\toa.stop(t + 0.3);\n\t\t\t}\n\t\t\t// Bubble noise\n\t\t\tif (bassIdx % 3 === 0) {\n\t\t\t\tconst buf = audioCtx.createBuffer(1, audioCtx.sampleRate * 0.04, audioCtx.sampleRate);\n\t\t\t\tconst data = buf.getChannelData(0);\n\t\t\t\tfor (let i = 0; i < data.length; i++) data[i] = (Math.random() * 2 - 1) * 0.3;\n\t\t\t\tconst ns = audioCtx.createBufferSource();\n\t\t\t\tconst ng = audioCtx.createGain();\n\t\t\t\tconst nf = audioCtx.createBiquadFilter();\n\t\t\t\tns.buffer = buf;\n\t\t\t\tns.connect(nf);\n\t\t\t\tnf.connect(ng);\n\t\t\t\tng.connect(audioCtx.destination);\n\t\t\t\tnf.type = 'bandpass';\n\t\t\t\tnf.frequency.setValueAtTime(2000 + Math.random() * 2000, t);\n\t\t\t\tng.gain.setValueAtTime(0.02, t);\n\t\t\t\tng.gain.linearRampToValueAtTime(0, t + 0.04);\n\t\t\t\tns.start(t);\n\t\t\t}\n\t\t\tbassIdx++;\n\t\t}, 250);\n\t}\n\n\tfunction stopMusic() {\n\t\tmusicPlaying = false;\n\t\tclearInterval(musicInterval);\n\t}\n\n\t// Zones\n\tconst ZONES = [{\n\t\t\tname: 'SUNLIT ZONE',\n\t\t\tstartRow: 0,\n\t\t\twallColor: '#2a4a3a',\n\t\t\twallDark: '#1a3a2a',\n\t\t\twater: '#0a2030',\n\t\t\tcreature: '#40a080',\n\t\t\ttreasure: '#e0c040'\n\t\t},\n\t\t{\n\t\t\tname: 'TWILIGHT ZONE',\n\t\t\tstartRow: 35,\n\t\t\twallColor: '#1a3048',\n\t\t\twallDark: '#0a2038',\n\t\t\twater: '#061828',\n\t\t\tcreature: '#6060c0',\n\t\t\ttreasure: '#40d0d0'\n\t\t},\n\t\t{\n\t\t\tname: 'MIDNIGHT ZONE',\n\t\t\tstartRow: 75,\n\t\t\twallColor: '#121828',\n\t\t\twallDark: '#0a1018',\n\t\t\twater: '#030a14',\n\t\t\tcreature: '#c04080',\n\t\t\ttreasure: '#e08030'\n\t\t},\n\t\t{\n\t\t\tname: 'ABYSSAL ZONE',\n\t\t\tstartRow: 120,\n\t\t\twallColor: '#0a0a18',\n\t\t\twallDark: '#060610',\n\t\t\twater: '#020208',\n\t\t\tcreature: '#e04040',\n\t\t\ttreasure: '#40e0a0'\n\t\t},\n\t\t{\n\t\t\tname: 'HADAL ZONE',\n\t\t\tstartRow: 170,\n\t\t\twallColor: '#08060a',\n\t\t\twallDark: '#040308',\n\t\t\twater: '#010104',\n\t\t\tcreature: '#d040d0',\n\t\t\ttreasure: '#e0e040'\n\t\t},\n\t\t{\n\t\t\tname: 'THE VOID',\n\t\t\tstartRow: 215,\n\t\t\twallColor: '#040204',\n\t\t\twallDark: '#020102',\n\t\t\twater: '#000002',\n\t\t\tcreature: '#f02020',\n\t\t\ttreasure: '#f0f0f0'\n\t\t},\n\t];\n\n\tfunction getZone(row) {\n\t\tfor (let i = ZONES.length - 1; i >= 0; i--) {\n\t\t\tif (row >= ZONES[i].startRow) return ZONES[i];\n\t\t}\n\t\treturn ZONES[0];\n\t}\n\n\t// Terrain: 0=water, 1=rock, 2=coral, 3=treasure, 4=vent, 5=kelp, 6=wreck\n\tconst WATER = 0,\n\t\tROCK = 1,\n\t\tCORAL = 2,\n\t\tTREASURE = 3,\n\t\tVENT = 4,\n\t\tKELP = 5,\n\t\tWRECK = 6;\n\tconst terrain = [];\n\n\tfunction generateTerrain() {\n\t\tGEN_COLS = Math.ceil(W / TILE);\n\t\tterrain.length = 0;\n\t\t// Use a cave generation approach\n\t\tfor (let r = 0; r < ROWS; r++) {\n\t\t\tterrain[r] = [];\n\t\t\tfor (let c = 0; c < GEN_COLS; c++) {\n\t\t\t\tif (r < 2) {\n\t\t\t\t\tterrain[r][c] = WATER;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Walls on edges\n\t\t\t\tif (c <= 0 || c >= GEN_COLS - 1) {\n\t\t\t\t\tterrain[r][c] = ROCK;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst depth = r / ROWS;\n\t\t\t\t// Narrow passages deeper down\n\t\t\t\tconst wallThickness = 2 + Math.floor(depth * 4) + Math.floor(Math.sin(r * 0.15) * 2 + Math.sin(r * 0.07 + c * 0.3) * 1.5);\n\t\t\t\tif (c < wallThickness || c >= GEN_COLS - wallThickness) {\n\t\t\t\t\tterrain[r][c] = ROCK;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Stalactites/stalagmites\n\t\t\t\tconst noise = Math.sin(c * 0.8 + r * 0.1) * Math.cos(c * 0.3 - r * 0.2);\n\t\t\t\tif (noise > 0.7 + (1 - depth) * 0.2) {\n\t\t\t\t\tterrain[r][c] = ROCK;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst rnd = Math.random();\n\t\t\t\tif (rnd < 0.015 + depth * 0.01) terrain[r][c] = TREASURE;\n\t\t\t\telse if (rnd < 0.04 && depth < 0.4) terrain[r][c] = CORAL;\n\t\t\t\telse if (rnd < 0.05 && depth < 0.3) terrain[r][c] = KELP;\n\t\t\t\telse if (rnd < 0.008 + depth * 0.015) terrain[r][c] = ROCK;\n\t\t\t\telse if (rnd < 0.012 && depth > 0.4) terrain[r][c] = VENT;\n\t\t\t\telse if (rnd < 0.005 && depth > 0.2) terrain[r][c] = WRECK;\n\t\t\t\telse terrain[r][c] = WATER;\n\t\t\t}\n\t\t}\n\t\t// Carve some large caverns\n\t\tfor (let i = 0; i < 12; i++) {\n\t\t\tconst cr = 20 + Math.floor(Math.random() * (ROWS - 40));\n\t\t\tconst cc = 4 + Math.floor(Math.random() * (GEN_COLS - 8));\n\t\t\tconst rw = 3 + Math.floor(Math.random() * 5);\n\t\t\tconst rh = 2 + Math.floor(Math.random() * 3);\n\t\t\tfor (let r = cr - rh; r <= cr + rh; r++) {\n\t\t\t\tfor (let c = cc - rw; c <= cc + rw; c++) {\n\t\t\t\t\tif (r >= 0 && r < ROWS && c > 0 && c < GEN_COLS - 1) {\n\t\t\t\t\t\tconst dx = (c - cc) / rw,\n\t\t\t\t\t\t\tdy = (r - cr) / rh;\n\t\t\t\t\t\tif (dx * dx + dy * dy < 1) terrain[r][c] = WATER;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Particles\n\tconst particles = [];\n\n\tfunction spawnParticles(x, y, color, count, speed, life) {\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst angle = Math.random() * Math.PI * 2;\n\t\t\tconst spd = Math.random() * speed + speed * 0.2;\n\t\t\tparticles.push({\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tvx: Math.cos(angle) * spd,\n\t\t\t\tvy: Math.sin(angle) * spd,\n\t\t\t\tlife: life || 30,\n\t\t\t\tmaxLife: life || 30,\n\t\t\t\tcolor,\n\t\t\t\tsize: 1 + Math.random() * 2\n\t\t\t});\n\t\t}\n\t}\n\n\t// Bubbles (ambient)\n\tconst bubbles = [];\n\n\tfunction spawnBubble(x, y) {\n\t\tbubbles.push({\n\t\t\tx,\n\t\t\ty,\n\t\t\tvx: (Math.random() - 0.5) * 0.3,\n\t\t\tvy: -0.5 - Math.random() * 1,\n\t\t\tlife: 60 + Math.random() * 40,\n\t\t\tmaxLife: 100,\n\t\t\tsize: 1 + Math.random() * 2.5\n\t\t});\n\t}\n\n\t// Floating text\n\tconst floatingTexts = [];\n\n\tfunction spawnText(x, y, text, color) {\n\t\tfloatingTexts.push({\n\t\t\tx,\n\t\t\ty,\n\t\t\ttext,\n\t\t\tcolor,\n\t\t\tlife: 50,\n\t\t\tmaxLife: 50\n\t\t});\n\t}\n\n\t// Sonar pings\n\tconst sonarPings = [];\n\n\t// Player (submarine)\n\tconst sub = {\n\t\tx: 0,\n\t\ty: 32,\n\t\tvx: 0,\n\t\tvy: 0,\n\t\tw: 16,\n\t\th: 10,\n\t\tbattery: 100,\n\t\thull: 3,\n\t\tinvincible: 0,\n\t\tsonarReady: true,\n\t\tsonarCooldown: 0,\n\t\tcombo: 0,\n\t\tcomboTimer: 0,\n\t\tthrusting: false,\n\t\tfacing: 1,\n\t\tisDead: false,\n\t\tframe: 0\n\t};\n\n\t// Creatures\n\tconst creatures = [];\n\n\tfunction spawnCreature(row) {\n\t\tconst zone = getZone(row);\n\t\tconst col = 3 + Math.floor(Math.random() * Math.max(1, GEN_COLS - 6));\n\t\tconst types = ['jellyfish', 'anglerfish', 'eel', 'squid'];\n\t\tconst depth = row / ROWS;\n\t\tconst type = depth < 0.3 ? types[0] : depth < 0.5 ? types[Math.floor(Math.random() * 2)] : types[Math.floor(Math.random() * types.length)];\n\t\tcreatures.push({\n\t\t\tx: col * TILE + 8,\n\t\t\ty: row * TILE + 8,\n\t\t\tvx: (Math.random() > 0.5 ? 1 : -1) * (type === 'eel' ? 1.8 : type === 'squid' ? 1.2 : 0.5),\n\t\t\tvy: type === 'jellyfish' ? -0.3 : 0,\n\t\t\tw: type === 'eel' ? 16 : 12,\n\t\t\th: type === 'eel' ? 8 : 12,\n\t\t\ttype,\n\t\t\tcolor: zone.creature,\n\t\t\thp: type === 'anglerfish' ? 2 : 1,\n\t\t\tphase: Math.random() * Math.PI * 2,\n\t\t\tstunned: 0,\n\t\t\tactive: true,\n\t\t\tglowIntensity: 0.5 + Math.random() * 0.5\n\t\t});\n\t}\n\n\t// Shake\n\tlet shakeX = 0,\n\t\tshakeY = 0,\n\t\tshakeIntensity = 0;\n\n\tfunction shake(intensity) {\n\t\tshakeIntensity = Math.max(shakeIntensity, intensity);\n\t}\n\n\t// State\n\tlet score = 0,\n\t\tmaxDepth = 0,\n\t\tcameraY = 0,\n\t\ttargetCameraY = 0;\n\tlet gameState = 'title';\n\tlet levelBannerTimer = 0,\n\t\tcurrentZoneName = '';\n\tconst keys = {};\n\n\tfunction initGame() {\n\t\tgenerateTerrain();\n\t\tcreatures.length = 0;\n\t\tparticles.length = 0;\n\t\tfloatingTexts.length = 0;\n\t\tbubbles.length = 0;\n\t\tsonarPings.length = 0;\n\t\tsub.x = Math.floor(GEN_COLS / 2) * TILE;\n\t\tsub.y = 32;\n\t\tsub.vx = 0;\n\t\tsub.vy = 0;\n\t\tsub.battery = 100;\n\t\tsub.hull = 3;\n\t\tsub.invincible = 0;\n\t\tsub.sonarReady = true;\n\t\tsub.sonarCooldown = 0;\n\t\tsub.combo = 0;\n\t\tsub.comboTimer = 0;\n\t\tsub.isDead = false;\n\t\tsub.facing = 1;\n\t\tsub.thrusting = false;\n\t\tscore = 0;\n\t\tmaxDepth = 0;\n\t\tcameraY = 0;\n\t\ttargetCameraY = 0;\n\t\tcurrentZoneName = '';\n\t\tfor (let r = 10; r < ROWS; r += 3 + Math.floor(Math.random() * 5)) {\n\t\t\tspawnCreature(r);\n\t\t}\n\t}\n\n\t// Tile helpers\n\tfunction tileAt(px, py) {\n\t\tconst col = Math.floor(px / TILE),\n\t\t\trow = Math.floor(py / TILE);\n\t\tif (col < 0 || col >= GEN_COLS || row < 0 || row >= ROWS) return ROCK;\n\t\treturn terrain[row][col];\n\t}\n\n\tfunction isSolid(px, py) {\n\t\tconst t = tileAt(px, py);\n\t\treturn t === ROCK || t === CORAL || t === WRECK;\n\t}\n\n\tfunction collectAt(px, py) {\n\t\tconst col = Math.floor(px / TILE),\n\t\t\trow = Math.floor(py / TILE);\n\t\tif (col < 0 || col >= GEN_COLS || row < 0 || row >= ROWS) return;\n\t\tconst t = terrain[row][col];\n\t\tif (t === TREASURE) {\n\t\t\tterrain[row][col] = WATER;\n\t\t\tsub.combo++;\n\t\t\tsub.comboTimer = 120;\n\t\t\tconst pts = 100 * sub.combo;\n\t\t\tscore += pts;\n\t\t\tplaySound('treasure');\n\t\t\tif (sub.combo > 1) playSound('combo');\n\t\t\tconst zone = getZone(row);\n\t\t\tspawnParticles(col * TILE + 8, row * TILE + 8, zone.treasure, 10, 3, 25);\n\t\t\tspawnText(col * TILE + 8, row * TILE - 8, `+${pts}`, zone.treasure);\n\t\t\tif (sub.combo >= 5) spawnText(col * TILE + 8, row * TILE - 22, `COMBO x${sub.combo}!`, '#ffffff');\n\t\t} else if (t === VENT) {\n\t\t\t// Vents restore battery\n\t\t\tsub.battery = Math.min(100, sub.battery + 20);\n\t\t\tscore += 50;\n\t\t\tterrain[row][col] = WATER;\n\t\t\tplaySound('bubble');\n\t\t\tspawnParticles(col * TILE + 8, row * TILE + 8, '#e08040', 8, 3, 20);\n\t\t\tspawnText(col * TILE + 8, row * TILE - 8, '+POWER', '#e08040');\n\t\t}\n\t}\n\n\tfunction hurtSub() {\n\t\tif (sub.invincible > 0 || sub.isDead) return;\n\t\tsub.hull--;\n\t\tsub.invincible = 2;\n\t\tplaySound('hurt');\n\t\tshake(8);\n\t\tspawnParticles(sub.x, sub.y, '#ff4040', 12, 4, 25);\n\t\tif (sub.hull <= 0) subDeath();\n\t}\n\n\tfunction subDeath() {\n\t\tsub.isDead = true;\n\t\tplaySound('death');\n\t\tshake(12);\n\t\tspawnParticles(sub.x, sub.y, '#30e8b0', 25, 5, 40);\n\t\tspawnParticles(sub.x, sub.y, '#e0a030', 20, 4, 35);\n\t\tfor (let i = 0; i < 15; i++) spawnBubble(sub.x + (Math.random() - 0.5) * 20, sub.y);\n\t\tsetTimeout(() => {\n\t\t\tgameState = 'gameover';\n\t\t\tstopMusic();\n\t\t\tdocument.getElementById('game-over').classList.remove('overlay--hidden');\n\t\t\tdocument.getElementById('final-score').textContent = score.toString();\n\t\t\tdocument.getElementById('final-depth').textContent = maxDepth + 'm';\n\t\t}, 1000);\n\t}\n\n\tfunction update(dt) {\n\t\tif (gameState !== 'playing' || sub.isDead) return;\n\n\t\tconst thrust = 0.25;\n\t\tconst drag = 0.965;\n\t\tconst sinkRate = 0.008;\n\n\t\tsub.thrusting = false;\n\t\tif (keys['ArrowLeft'] || keys['KeyA']) {\n\t\t\tsub.vx -= thrust;\n\t\t\tsub.facing = -1;\n\t\t\tsub.thrusting = true;\n\t\t}\n\t\tif (keys['ArrowRight'] || keys['KeyD']) {\n\t\t\tsub.vx += thrust;\n\t\t\tsub.facing = 1;\n\t\t\tsub.thrusting = true;\n\t\t}\n\t\tif (keys['ArrowUp'] || keys['KeyW']) {\n\t\t\tsub.vy -= thrust * 1.5;\n\t\t\tsub.thrusting = true;\n\t\t}\n\t\tif (keys['ArrowDown'] || keys['KeyS']) {\n\t\t\tsub.vy += thrust * 0.6;\n\t\t\tsub.thrusting = true;\n\t\t}\n\n\t\tif (sub.thrusting && Math.random() < 0.3) playSound('thrust');\n\n\t\t// Sonar\n\t\tif (keys['Space'] && sub.sonarReady) {\n\t\t\tsub.sonarReady = false;\n\t\t\tsub.sonarCooldown = 200;\n\t\t\tplaySound('sonar');\n\t\t\tshake(2);\n\t\t\tsonarPings.push({\n\t\t\t\tx: sub.x,\n\t\t\t\ty: sub.y,\n\t\t\t\tradius: 0,\n\t\t\t\tmaxRadius: 120,\n\t\t\t\tlife: 40,\n\t\t\t\tmaxLife: 40\n\t\t\t});\n\t\t\t// Stun creatures\n\t\t\tcreatures.forEach(e => {\n\t\t\t\tconst dx = e.x - sub.x,\n\t\t\t\t\tdy = e.y - sub.y;\n\t\t\t\tif (Math.sqrt(dx * dx + dy * dy) < 100) {\n\t\t\t\t\te.stunned = 150;\n\t\t\t\t\te.vx *= -0.5;\n\t\t\t\t\tspawnParticles(e.x, e.y, '#4090e0', 6, 2, 15);\n\t\t\t\t}\n\t\t\t});\n\t\t\t// Reveal hidden treasures (make nearby rocks transparent briefly handled visually in sonar)\n\t\t\tsub.battery -= 5;\n\t\t}\n\t\tif (!sub.sonarReady) {\n\t\t\tsub.sonarCooldown--;\n\t\t\tif (sub.sonarCooldown <= 0) sub.sonarReady = true;\n\t\t}\n\n\t\t// Sink slowly\n\t\tsub.vy += sinkRate;\n\t\tsub.vx *= drag;\n\t\tsub.vy *= drag;\n\t\tif (Math.abs(sub.vx) < 0.01) sub.vx = 0;\n\t\tif (Math.abs(sub.vy) < 0.01) sub.vy = 0;\n\t\tconst maxSpeed = 2.8;\n\t\tconst spd = Math.sqrt(sub.vx * sub.vx + sub.vy * sub.vy);\n\t\tif (spd > maxSpeed) {\n\t\t\tsub.vx *= maxSpeed / spd;\n\t\t\tsub.vy *= maxSpeed / spd;\n\t\t}\n\n\t\t// Move X\n\t\tsub.x += sub.vx;\n\t\tif (sub.x - sub.w / 2 < 0) {\n\t\t\tsub.x = sub.w / 2;\n\t\t\tsub.vx = 0;\n\t\t}\n\t\tconst maxX = GEN_COLS * TILE;\n\t\tif (sub.x + sub.w / 2 > maxX) {\n\t\t\tsub.x = maxX - sub.w / 2;\n\t\t\tsub.vx = 0;\n\t\t}\n\t\tif (isSolid(sub.x + sub.w / 2, sub.y) || isSolid(sub.x + sub.w / 2, sub.y - sub.h / 2 + 2) || isSolid(sub.x + sub.w / 2, sub.y + sub.h / 2 - 2)) {\n\t\t\tsub.x = Math.floor((sub.x + sub.w / 2) / TILE) * TILE - sub.w / 2 - 0.1;\n\t\t\tsub.vx = 0;\n\t\t}\n\t\tif (isSolid(sub.x - sub.w / 2, sub.y) || isSolid(sub.x - sub.w / 2, sub.y - sub.h / 2 + 2) || isSolid(sub.x - sub.w / 2, sub.y + sub.h / 2 - 2)) {\n\t\t\tsub.x = Math.floor((sub.x - sub.w / 2) / TILE) * TILE + TILE + sub.w / 2 + 0.1;\n\t\t\tsub.vx = 0;\n\t\t}\n\n\t\t// Move Y\n\t\tsub.y += sub.vy;\n\t\tif (sub.y < sub.h / 2) {\n\t\t\tsub.y = sub.h / 2;\n\t\t\tsub.vy = 0;\n\t\t}\n\t\tif (sub.y > ROWS * TILE - TILE) sub.y = ROWS * TILE - TILE;\n\t\tif (isSolid(sub.x - sub.w / 2 + 2, sub.y + sub.h / 2) || isSolid(sub.x + sub.w / 2 - 2, sub.y + sub.h / 2)) {\n\t\t\tif (sub.vy > 0) {\n\t\t\t\tsub.y = Math.floor((sub.y + sub.h / 2) / TILE) * TILE - sub.h / 2 - 0.1;\n\t\t\t\tsub.vy = 0;\n\t\t\t}\n\t\t}\n\t\tif (isSolid(sub.x - sub.w / 2 + 2, sub.y - sub.h / 2) || isSolid(sub.x + sub.w / 2 - 2, sub.y - sub.h / 2)) {\n\t\t\tif (sub.vy < 0) {\n\t\t\t\tsub.y = Math.floor((sub.y - sub.h / 2) / TILE) * TILE + TILE + sub.h / 2 + 0.1;\n\t\t\t\tsub.vy = 0;\n\t\t\t}\n\t\t}\n\n\t\t// Collect\n\t\tcollectAt(sub.x, sub.y);\n\t\tcollectAt(sub.x - sub.w / 2, sub.y);\n\t\tcollectAt(sub.x + sub.w / 2, sub.y);\n\n\t\t// Battery drain\n\t\tconst depthRow = Math.floor(sub.y / TILE);\n\t\tsub.battery -= 0.006 + (depthRow / ROWS) * 0.01;\n\t\tif (sub.thrusting) sub.battery -= 0.01;\n\t\tif (sub.battery <= 0) {\n\t\t\tsub.battery = 0;\n\t\t\thurtSub();\n\t\t\tsub.battery = 20;\n\t\t}\n\t\tif (depthRow < 3) sub.battery = Math.min(100, sub.battery + 0.3);\n\n\t\t// Combo\n\t\tif (sub.comboTimer > 0) {\n\t\t\tsub.comboTimer--;\n\t\t\tif (sub.comboTimer <= 0) sub.combo = 0;\n\t\t}\n\t\tif (sub.invincible > 0) sub.invincible -= dt;\n\n\t\tconst depth = Math.floor(sub.y / TILE);\n\t\tif (depth > maxDepth) maxDepth = depth;\n\n\t\t// Zone check\n\t\tconst zone = getZone(depthRow);\n\t\tif (zone.name !== currentZoneName) {\n\t\t\tcurrentZoneName = zone.name;\n\t\t\tif (depthRow > 5) {\n\t\t\t\tplaySound('zonein');\n\t\t\t\tlevelBannerTimer = 120;\n\t\t\t\tdocument.getElementById('banner-zone').textContent = zone.name;\n\t\t\t\tdocument.getElementById('banner-depth').textContent = `DEPTH: ${depth * 10}m`;\n\t\t\t\tdocument.getElementById('level-banner').classList.remove('overlay--hidden');\n\t\t\t}\n\t\t}\n\t\tif (levelBannerTimer > 0) {\n\t\t\tlevelBannerTimer--;\n\t\t\tif (levelBannerTimer <= 0) document.getElementById('level-banner').classList.add('overlay--hidden');\n\t\t}\n\n\t\t// Camera\n\t\ttargetCameraY = sub.y - (H / ZOOM) * 0.4;\n\t\tif (targetCameraY < 0) targetCameraY = 0;\n\t\tcameraY += (targetCameraY - cameraY) * 0.08;\n\n\t\t// Ambient bubbles\n\t\tif (Math.random() < 0.04) {\n\t\t\tconst bx = cameraY > 0 ? (Math.random() * GEN_COLS * TILE) : sub.x + (Math.random() - 0.5) * W;\n\t\t\tspawnBubble(bx, cameraY + H + 10);\n\t\t}\n\t\t// Sub bubbles\n\t\tif (sub.thrusting && Math.random() < 0.4) {\n\t\t\tspawnBubble(sub.x - sub.facing * 10, sub.y + (Math.random() - 0.5) * 6);\n\t\t}\n\n\t\t// Creatures\n\t\tcreatures.forEach(e => {\n\t\t\tif (!e.active) return;\n\t\t\tif (Math.abs(e.y - sub.y) > H * 1.5) return;\n\t\t\tif (e.stunned > 0) {\n\t\t\t\te.stunned--;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\te.phase += 0.03;\n\t\t\tswitch (e.type) {\n\t\t\t\tcase 'jellyfish':\n\t\t\t\t\te.x += Math.sin(e.phase) * 0.3;\n\t\t\t\t\te.y += Math.sin(e.phase * 0.7) * 0.4 - 0.1;\n\t\t\t\t\tif (e.y < 16) e.vy = 0.5;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'anglerfish':\n\t\t\t\t\te.x += e.vx;\n\t\t\t\t\tif (Math.abs(e.y - sub.y) < H * 0.5) {\n\t\t\t\t\t\te.x += Math.sign(sub.x - e.x) * 0.2;\n\t\t\t\t\t\te.y += Math.sign(sub.y - e.y) * 0.15;\n\t\t\t\t\t}\n\t\t\t\t\tif (isSolid(e.x + e.w / 2 + 2, e.y) || isSolid(e.x - e.w / 2 - 2, e.y) || e.x < 16 || e.x > GEN_COLS * TILE - 16) e.vx *= -1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'eel':\n\t\t\t\t\te.x += e.vx;\n\t\t\t\t\te.y += Math.sin(e.phase * 2) * 0.8;\n\t\t\t\t\tif (isSolid(e.x + e.w / 2 + 2, e.y) || isSolid(e.x - e.w / 2 - 2, e.y) || e.x < 16 || e.x > GEN_COLS * TILE - 16) e.vx *= -1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'squid':\n\t\t\t\t\te.x += e.vx;\n\t\t\t\t\te.y += Math.sin(e.phase) * 0.6;\n\t\t\t\t\t// Dart toward player occasionally\n\t\t\t\t\tif (Math.abs(e.x - sub.x) < 80 && Math.abs(e.y - sub.y) < 60 && Math.random() < 0.02) {\n\t\t\t\t\t\te.vx = Math.sign(sub.x - e.x) * 3;\n\t\t\t\t\t}\n\t\t\t\t\tif (isSolid(e.x + e.w / 2 + 2, e.y) || isSolid(e.x - e.w / 2 - 2, e.y) || e.x < 16 || e.x > GEN_COLS * TILE - 16) e.vx *= -1;\n\t\t\t\t\te.vx *= 0.99;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// Collision\n\t\t\tif (sub.invincible <= 0 && !sub.isDead) {\n\t\t\t\tconst dx = Math.abs(e.x - sub.x),\n\t\t\t\t\tdy = Math.abs(e.y - sub.y);\n\t\t\t\tif (dx < (e.w + sub.w) / 2 - 2 && dy < (e.h + sub.h) / 2 - 2) {\n\t\t\t\t\thurtSub();\n\t\t\t\t\te.hp--;\n\t\t\t\t\tif (e.hp <= 0) {\n\t\t\t\t\t\te.active = false;\n\t\t\t\t\t\tscore += 200;\n\t\t\t\t\t\tspawnParticles(e.x, e.y, e.color, 12, 3, 25);\n\t\t\t\t\t\tspawnText(e.x, e.y - 12, '+200', e.color);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// Sonar pings\n\t\tsonarPings.forEach(p => {\n\t\t\tp.radius += 5;\n\t\t\tp.life--;\n\t\t});\n\t\tfor (let i = sonarPings.length - 1; i >= 0; i--)\n\t\t\tif (sonarPings[i].life <= 0) sonarPings.splice(i, 1);\n\n\t\t// Particles\n\t\tparticles.forEach(p => {\n\t\t\tp.x += p.vx;\n\t\t\tp.y += p.vy;\n\t\t\tp.vy += 0.02;\n\t\t\tp.life--;\n\t\t});\n\t\tfor (let i = particles.length - 1; i >= 0; i--)\n\t\t\tif (particles[i].life <= 0) particles.splice(i, 1);\n\n\t\t// Bubbles\n\t\tbubbles.forEach(b => {\n\t\t\tb.x += b.vx + Math.sin(b.y * 0.1) * 0.2;\n\t\t\tb.y += b.vy;\n\t\t\tb.life--;\n\t\t});\n\t\tfor (let i = bubbles.length - 1; i >= 0; i--)\n\t\t\tif (bubbles[i].life <= 0) bubbles.splice(i, 1);\n\n\t\t// Floating texts\n\t\tfloatingTexts.forEach(ft => {\n\t\t\tft.y -= 0.6;\n\t\t\tft.life--;\n\t\t});\n\t\tfor (let i = floatingTexts.length - 1; i >= 0; i--)\n\t\t\tif (floatingTexts[i].life <= 0) floatingTexts.splice(i, 1);\n\n\t\t// Shake\n\t\tif (shakeIntensity > 0) {\n\t\t\tshakeX = (Math.random() - 0.5) * shakeIntensity;\n\t\t\tshakeY = (Math.random() - 0.5) * shakeIntensity;\n\t\t\tshakeIntensity *= 0.85;\n\t\t\tif (shakeIntensity < 0.5) shakeIntensity = 0;\n\t\t} else {\n\t\t\tshakeX = 0;\n\t\t\tshakeY = 0;\n\t\t}\n\n\t\tsub.frame++;\n\n\t\t// HUD\n\t\tdocument.getElementById('hud-score').textContent = score.toString().padStart(7, '0');\n\t\tdocument.getElementById('hud-zone').textContent = zone.name;\n\t\tdocument.getElementById('hud-depth').textContent = (depth * 10) + 'm';\n\t\tconst batBar = document.getElementById('hud-battery');\n\t\tbatBar.style.width = sub.battery + '%';\n\t\tbatBar.classList.toggle('hud__bar-fill--low', sub.battery < 25);\n\t\tlet hullHTML = '';\n\t\tfor (let i = 0; i < sub.hull; i++) hullHTML += '<span style=\"color:#30e8b0;font-size:10px;\">◆</span>';\n\t\tdocument.getElementById('hud-hull').innerHTML = hullHTML;\n\t}\n\n\t// Draw tile\n\tfunction drawTile(col, row) {\n\t\tconst t = terrain[row][col];\n\t\tif (t === WATER) return;\n\t\tconst x = col * TILE,\n\t\t\ty = row * TILE;\n\t\tconst zone = getZone(row);\n\t\tconst seed = (col * 7 + row * 13) % 17;\n\t\tswitch (t) {\n\t\t\tcase ROCK:\n\t\t\t\tctx.fillStyle = zone.wallColor;\n\t\t\t\tctx.fillRect(x, y, TILE, TILE);\n\t\t\t\tctx.fillStyle = zone.wallDark;\n\t\t\t\tctx.fillRect(x + (seed % 3) * 4, y + (seed % 4) * 3, 6, 5);\n\t\t\t\tctx.fillRect(x + ((seed + 5) % 4) * 3, y + ((seed + 2) % 3) * 5, 4, 4);\n\t\t\t\tif (row > 0 && terrain[row - 1][col] === WATER) {\n\t\t\t\t\tctx.fillStyle = 'rgba(255,255,255,0.04)';\n\t\t\t\t\tctx.fillRect(x, y, TILE, 2);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase CORAL: {\n\t\t\t\tconst wave = Math.sin(Date.now() * 0.003 + col) * 0.2 + 0.8;\n\t\t\t\tctx.fillStyle = `rgba(200,80,120,${wave})`;\n\t\t\t\tctx.fillRect(x + 3, y + 4, 4, TILE - 4);\n\t\t\t\tctx.fillRect(x + 9, y + 2, 4, TILE - 2);\n\t\t\t\tctx.fillStyle = `rgba(240,120,160,${wave * 0.8})`;\n\t\t\t\tctx.fillRect(x + 1, y + 8, 6, 3);\n\t\t\t\tctx.fillRect(x + 8, y + 5, 6, 3);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase TREASURE: {\n\t\t\t\tconst glow = Math.sin(Date.now() * 0.004 + col + row) * 0.3 + 0.7;\n\t\t\t\tctx.globalAlpha = glow;\n\t\t\t\tctx.fillStyle = zone.treasure;\n\t\t\t\tctx.fillRect(x + 4, y + 5, 8, 6);\n\t\t\t\tctx.fillRect(x + 6, y + 3, 4, 2);\n\t\t\t\tctx.fillStyle = '#ffffff';\n\t\t\t\tctx.globalAlpha = glow * 0.5;\n\t\t\t\tctx.fillRect(x + 5, y + 6, 2, 2);\n\t\t\t\tctx.globalAlpha = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase VENT: {\n\t\t\t\tconst vp = Math.sin(Date.now() * 0.005 + col) * 0.4 + 0.6;\n\t\t\t\tctx.fillStyle = `rgba(180,100,40,${vp})`;\n\t\t\t\tctx.fillRect(x + 4, y + 6, 8, TILE - 6);\n\t\t\t\tctx.fillStyle = `rgba(255,180,60,${vp * 0.6})`;\n\t\t\t\tctx.fillRect(x + 5, y + 2, 2, 6);\n\t\t\t\tctx.fillRect(x + 9, y + 3, 2, 5);\n\t\t\t\tctx.fillStyle = `rgba(255,220,100,${vp * 0.4})`;\n\t\t\t\tctx.fillRect(x + 6, y, 4, 4);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase KELP: {\n\t\t\t\tconst sway = Math.sin(Date.now() * 0.002 + col * 2) * 3;\n\t\t\t\tctx.fillStyle = '#206030';\n\t\t\t\tctx.fillRect(x + 7 + sway, y, 2, TILE);\n\t\t\t\tctx.fillStyle = '#308040';\n\t\t\t\tctx.fillRect(x + 4 + sway * 0.7, y + 3, 4, 3);\n\t\t\t\tctx.fillRect(x + 9 + sway * 0.5, y + 8, 4, 3);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase WRECK: {\n\t\t\t\tctx.fillStyle = '#4a3a2a';\n\t\t\t\tctx.fillRect(x, y, TILE, TILE);\n\t\t\t\tctx.fillStyle = '#5a4a3a';\n\t\t\t\tctx.fillRect(x + 2, y + 2, TILE - 4, TILE - 4);\n\t\t\t\tctx.fillStyle = '#3a2a1a';\n\t\t\t\tctx.fillRect(x + 4, y + 6, 8, 2);\n\t\t\t\tctx.fillRect(x + 6, y + 2, 2, 12);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Draw sub\n\tfunction drawSub() {\n\t\tconst px = Math.floor(sub.x - sub.w / 2);\n\t\tconst py = Math.floor(sub.y - sub.h / 2);\n\t\tif (sub.invincible > 0 && Math.floor(sub.invincible * 10) % 2) return;\n\t\tconst f = sub.facing;\n\t\t// Hull\n\t\tctx.fillStyle = '#e0a030';\n\t\tctx.fillRect(px + 2, py + 2, 12, 6);\n\t\t// Cockpit\n\t\tctx.fillStyle = '#30e8b0';\n\t\tctx.fillRect(px + (f > 0 ? 12 : 0), py + 3, 4, 4);\n\t\t// Window glow\n\t\tconst wg = Math.sin(Date.now() * 0.005) * 0.2 + 0.8;\n\t\tctx.globalAlpha = wg;\n\t\tctx.fillStyle = '#80ffd0';\n\t\tctx.fillRect(px + (f > 0 ? 13 : 1), py + 4, 2, 2);\n\t\tctx.globalAlpha = 1;\n\t\t// Conning tower\n\t\tctx.fillStyle = '#c89028';\n\t\tctx.fillRect(px + 6, py, 4, 3);\n\t\t// Propeller\n\t\tconst propSpin = Math.sin(Date.now() * 0.02) * 2;\n\t\tctx.fillStyle = '#888';\n\t\tctx.fillRect(px + (f > 0 ? 0 : 14), py + 3 + propSpin, 2, 1);\n\t\tctx.fillRect(px + (f > 0 ? 0 : 14), py + 6 - propSpin, 2, 1);\n\t\tctx.fillRect(px + (f > 0 ? -1 : 15), py + 4, 2, 2);\n\t\t// Depth light cone\n\t\tconst depthRow = Math.floor(sub.y / TILE);\n\t\tif (depthRow > 30) {\n\t\t\tconst lightAlpha = Math.min(0.15, (depthRow - 30) / 100 * 0.15);\n\t\t\tctx.fillStyle = `rgba(200, 255, 230, ${lightAlpha})`;\n\t\t\tctx.beginPath();\n\t\t\tconst lx = px + (f > 0 ? 16 : 0);\n\t\t\tctx.moveTo(lx, py + 3);\n\t\t\tctx.lineTo(lx + f * 50, py - 10);\n\t\t\tctx.lineTo(lx + f * 50, py + 20);\n\t\t\tctx.closePath();\n\t\t\tctx.fill();\n\t\t}\n\t\t// Battery indicator on sub\n\t\tctx.fillStyle = 'rgba(0,0,0,0.4)';\n\t\tctx.fillRect(px + 3, py + 9, 10, 2);\n\t\tctx.fillStyle = sub.battery > 25 ? '#30e8b0' : '#e04040';\n\t\tctx.fillRect(px + 3, py + 9, Math.floor(10 * sub.battery / 100), 2);\n\t}\n\n\t// Draw creature\n\tfunction drawCreature(e) {\n\t\tconst ex = Math.floor(e.x - e.w / 2);\n\t\tconst ey = Math.floor(e.y - e.h / 2);\n\t\tconst screenEy = (ey - cameraY) * ZOOM;\n\t\tif (screenEy < -40 || screenEy > H + 40) return;\n\t\tif (e.stunned > 0) ctx.globalAlpha = 0.4 + Math.sin(Date.now() * 0.02) * 0.2;\n\t\tconst glow = Math.sin(Date.now() * 0.004 + e.phase) * 0.3 + 0.7;\n\t\tswitch (e.type) {\n\t\t\tcase 'jellyfish': {\n\t\t\t\tconst bob = Math.sin(Date.now() * 0.003 + e.phase) * 3;\n\t\t\t\tctx.globalAlpha = (e.stunned > 0 ? 0.3 : 1) * glow * 0.7;\n\t\t\t\tctx.fillStyle = e.color;\n\t\t\t\t// Dome\n\t\t\t\tctx.fillRect(ex + 2, ey + 1 + bob, 8, 5);\n\t\t\t\tctx.fillRect(ex + 1, ey + 3 + bob, 10, 2);\n\t\t\t\t// Tentacles\n\t\t\t\tfor (let i = 0; i < 4; i++) {\n\t\t\t\t\tconst tw = Math.sin(Date.now() * 0.005 + i * 1.5) * 2;\n\t\t\t\t\tctx.fillRect(ex + 2 + i * 2.5 + tw, ey + 6 + bob, 1, 4 + Math.sin(Date.now() * 0.004 + i) * 2);\n\t\t\t\t}\n\t\t\t\t// Bioluminescent glow\n\t\t\t\tctx.fillStyle = '#ffffff';\n\t\t\t\tctx.globalAlpha *= 0.5;\n\t\t\t\tctx.fillRect(ex + 4, ey + 2 + bob, 3, 2);\n\t\t\t\tctx.globalAlpha = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'anglerfish': {\n\t\t\t\tctx.fillStyle = e.color;\n\t\t\t\tctx.fillRect(ex + 1, ey + 3, 10, 6);\n\t\t\t\t// Jaw\n\t\t\t\tconst jawOpen = Math.sin(Date.now() * 0.003) * 1.5;\n\t\t\t\tctx.fillRect(ex + (e.vx > 0 ? 8 : 0), ey + 7 + jawOpen, 4, 3);\n\t\t\t\t// Teeth\n\t\t\t\tctx.fillStyle = '#ffffff';\n\t\t\t\tctx.fillRect(ex + (e.vx > 0 ? 9 : 1), ey + 6, 1, 2);\n\t\t\t\tctx.fillRect(ex + (e.vx > 0 ? 11 : 3), ey + 6, 1, 2);\n\t\t\t\t// Lure\n\t\t\t\tconst lureGlow = Math.sin(Date.now() * 0.006) * 0.4 + 0.6;\n\t\t\t\tctx.fillStyle = `rgba(200,255,100,${lureGlow})`;\n\t\t\t\tctx.fillRect(ex + 5, ey - 2, 2, 4);\n\t\t\t\tctx.fillStyle = `rgba(255,255,200,${lureGlow})`;\n\t\t\t\tctx.fillRect(ex + 4, ey - 3, 4, 2);\n\t\t\t\t// Eye\n\t\t\t\tctx.fillStyle = '#ff4040';\n\t\t\t\tctx.fillRect(ex + (e.vx > 0 ? 8 : 2), ey + 4, 2, 2);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'eel': {\n\t\t\t\tctx.fillStyle = e.color;\n\t\t\t\tfor (let s = 0; s < 5; s++) {\n\t\t\t\t\tconst sw = Math.sin(Date.now() * 0.006 + s * 0.8) * 2;\n\t\t\t\t\tctx.fillRect(ex + s * 3 + sw, ey + 3, 4, 4);\n\t\t\t\t}\n\t\t\t\tctx.fillStyle = '#ffffff';\n\t\t\t\tctx.fillRect(ex + (e.vx > 0 ? 14 : 0), ey + 4, 2, 2);\n\t\t\t\tctx.fillStyle = '#000';\n\t\t\t\tctx.fillRect(ex + (e.vx > 0 ? 15 : 0), ey + 4, 1, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'squid': {\n\t\t\t\tctx.fillStyle = e.color;\n\t\t\t\tctx.globalAlpha = (e.stunned > 0 ? 0.3 : 1) * glow * 0.8;\n\t\t\t\t// Mantle\n\t\t\t\tctx.fillRect(ex + 3, ey + 1, 6, 5);\n\t\t\t\t// Tentacles\n\t\t\t\tfor (let i = 0; i < 5; i++) {\n\t\t\t\t\tconst tw = Math.sin(Date.now() * 0.005 + i) * 1.5;\n\t\t\t\t\tctx.fillRect(ex + 1 + i * 2 + tw, ey + 6, 1, 4);\n\t\t\t\t}\n\t\t\t\t// Eyes\n\t\t\t\tctx.fillStyle = '#ffffff';\n\t\t\t\tctx.fillRect(ex + 4, ey + 2, 2, 2);\n\t\t\t\tctx.fillRect(ex + 7, ey + 2, 2, 2);\n\t\t\t\tctx.globalAlpha = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tctx.globalAlpha = 1;\n\t}\n\n\tfunction render() {\n\t\tctx.save();\n\t\t// Viewport dimensions in world space\n\t\tconst viewW = W / ZOOM;\n\t\tconst viewH = H / ZOOM;\n\t\t// Camera offset: center on sub\n\t\tconst camX = sub.x - viewW / 2;\n\t\tconst camYOffset = cameraY;\n\t\tctx.scale(ZOOM, ZOOM);\n\t\tctx.translate(-camX + shakeX, -camYOffset + shakeY);\n\n\t\tconst depthRow = Math.floor((cameraY + viewH / 2) / TILE);\n\t\tconst zone = getZone(depthRow);\n\n\t\t// Water background (fill visible area in world coords)\n\t\tctx.fillStyle = zone.water;\n\t\tctx.fillRect(camX - 10, cameraY - 10, viewW + 20, viewH + 20);\n\n\t\t// Depth gradient\n\t\tconst depthPct = Math.min(1, depthRow / ROWS);\n\t\tconst grad = ctx.createLinearGradient(camX, cameraY, camX, cameraY + viewH);\n\t\tgrad.addColorStop(0, `rgba(0,0,0,${depthPct * 0.3})`);\n\t\tgrad.addColorStop(1, `rgba(0,0,0,${0.1 + depthPct * 0.4})`);\n\t\tctx.fillStyle = grad;\n\t\tctx.fillRect(camX - 10, cameraY - 10, viewW + 20, viewH + 20);\n\n\t\t// Caustic light effect at shallow depths\n\t\tif (depthRow < 40) {\n\t\t\tconst causticAlpha = Math.max(0, 0.06 * (1 - depthRow / 40));\n\t\t\tctx.fillStyle = `rgba(100,200,180,${causticAlpha})`;\n\t\t\tfor (let i = 0; i < 8; i++) {\n\t\t\t\tconst cx = camX + (Math.sin(Date.now() * 0.001 + i * 2.3) * 0.5 + 0.5) * viewW;\n\t\t\t\tconst cy = cameraY + (Math.cos(Date.now() * 0.0008 + i * 1.7) * 0.5 + 0.5) * viewH;\n\t\t\t\tctx.fillRect(cx - 30, cy - 2, 60, 4);\n\t\t\t\tctx.fillRect(cx - 2, cy - 20, 4, 40);\n\t\t\t}\n\t\t}\n\n\t\t// Terrain\n\t\tconst startRow = Math.max(0, Math.floor(cameraY / TILE) - 1);\n\t\tconst endRow = Math.min(ROWS, startRow + Math.ceil(viewH / TILE) + 3);\n\t\tconst startCol = Math.max(0, Math.floor(camX / TILE) - 1);\n\t\tconst endCol = Math.min(GEN_COLS, Math.ceil((camX + viewW) / TILE) + 2);\n\t\tfor (let r = startRow; r < endRow; r++) {\n\t\t\tfor (let c = startCol; c < endCol; c++) {\n\t\t\t\tif (terrain[r] && terrain[r][c] !== undefined) drawTile(c, r);\n\t\t\t}\n\t\t}\n\n\t\t// Bubbles\n\t\tbubbles.forEach(b => {\n\t\t\tctx.globalAlpha = (b.life / b.maxLife) * 0.4;\n\t\t\tctx.strokeStyle = '#80c0d0';\n\t\t\tctx.lineWidth = 1 / ZOOM;\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(Math.floor(b.x), Math.floor(b.y), b.size, 0, Math.PI * 2);\n\t\t\tctx.stroke();\n\t\t});\n\t\tctx.globalAlpha = 1;\n\n\t\t// Creatures\n\t\tcreatures.forEach(e => {\n\t\t\tif (e.active) drawCreature(e);\n\t\t});\n\n\t\t// Sub\n\t\tif (!sub.isDead) drawSub();\n\n\t\t// Sonar pings\n\t\tsonarPings.forEach(p => {\n\t\t\tconst alpha = p.life / p.maxLife;\n\t\t\tctx.strokeStyle = `rgba(64,144,224,${alpha * 0.5})`;\n\t\t\tctx.lineWidth = 2;\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);\n\t\t\tctx.stroke();\n\t\t\t// Inner ring\n\t\t\tctx.strokeStyle = `rgba(100,200,255,${alpha * 0.3})`;\n\t\t\tctx.lineWidth = 1;\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(p.x, p.y, p.radius * 0.6, 0, Math.PI * 2);\n\t\t\tctx.stroke();\n\t\t});\n\n\t\t// Particles\n\t\tparticles.forEach(p => {\n\t\t\tctx.globalAlpha = p.life / p.maxLife;\n\t\t\tctx.fillStyle = p.color;\n\t\t\tctx.fillRect(Math.floor(p.x), Math.floor(p.y), p.size, p.size);\n\t\t});\n\t\tctx.globalAlpha = 1;\n\n\t\t// Floating texts\n\t\tctx.font = '8px \"Press Start 2P\", monospace';\n\t\tctx.textAlign = 'center';\n\t\tfloatingTexts.forEach(ft => {\n\t\t\tctx.globalAlpha = ft.life / ft.maxLife;\n\t\t\tctx.fillStyle = ft.color;\n\t\t\tctx.fillText(ft.text, Math.floor(ft.x), Math.floor(ft.y));\n\t\t});\n\t\tctx.globalAlpha = 1;\n\n\t\t// Sonar cooldown\n\t\tif (!sub.sonarReady && !sub.isDead) {\n\t\t\tconst pct = 1 - sub.sonarCooldown / 200;\n\t\t\tconst sx = sub.x - 8,\n\t\t\t\tsy = sub.y - 14;\n\t\t\tctx.fillStyle = 'rgba(0,0,0,0.5)';\n\t\t\tctx.fillRect(sx, sy, 16, 3);\n\t\t\tctx.fillStyle = '#4090e0';\n\t\t\tctx.fillRect(sx, sy, 16 * pct, 3);\n\t\t}\n\n\t\t// Deep pressure vignette\n\t\tif (depthRow > 100) {\n\t\t\tconst vigAlpha = Math.min(0.4, (depthRow - 100) / 150 * 0.4);\n\t\t\tconst vcx = sub.x,\n\t\t\t\tvcy = sub.y;\n\t\t\tconst vr = viewW * 0.5;\n\t\t\tconst vg = ctx.createRadialGradient(vcx, vcy, vr * 0.3, vcx, vcy, vr);\n\t\t\tvg.addColorStop(0, 'rgba(0,0,0,0)');\n\t\t\tvg.addColorStop(1, `rgba(0,0,10,${vigAlpha})`);\n\t\t\tctx.fillStyle = vg;\n\t\t\tctx.fillRect(camX - 10, cameraY - 10, viewW + 20, viewH + 20);\n\t\t}\n\n\t\t// Surface shimmer\n\t\tif (cameraY < viewH) {\n\t\t\tconst skyH = Math.max(0, 2 * TILE);\n\t\t\tif (cameraY < skyH) {\n\t\t\t\tconst sg = ctx.createLinearGradient(camX, 0, camX, skyH);\n\t\t\t\tsg.addColorStop(0, 'rgba(40,120,140,0.3)');\n\t\t\t\tsg.addColorStop(1, 'transparent');\n\t\t\t\tctx.fillStyle = sg;\n\t\t\t\tctx.fillRect(camX - 10, 0, viewW + 20, skyH);\n\t\t\t}\n\t\t}\n\n\t\tctx.restore();\n\t}\n\n\tlet lastTime = 0;\n\n\tfunction gameLoop(time) {\n\t\tconst dt = Math.min((time - lastTime) / 1000, 0.05);\n\t\tlastTime = time;\n\t\tupdate(dt);\n\t\trender();\n\t\trequestAnimationFrame(gameLoop);\n\t}\n\n\tdocument.addEventListener('keydown', e => {\n\t\tkeys[e.code] = true;\n\t\tif (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Space'].includes(e.code)) e.preventDefault();\n\t});\n\tdocument.addEventListener('keyup', e => {\n\t\tkeys[e.code] = false;\n\t});\n\n\tdocument.getElementById('btn-start').addEventListener('click', () => {\n\t\tinitAudio();\n\t\tinitGame();\n\t\tgameState = 'playing';\n\t\tdocument.getElementById('title-screen').classList.add('overlay--hidden');\n\t\tstartMusic();\n\t});\n\tdocument.getElementById('btn-restart').addEventListener('click', () => {\n\t\tinitGame();\n\t\tgameState = 'playing';\n\t\tdocument.getElementById('game-over').classList.add('overlay--hidden');\n\t\tstartMusic();\n\t});\n\n\tfunction resizeCanvas() {\n\t\tconst vw = window.innerWidth,\n\t\t\tvh = window.innerHeight;\n\t\tconst baseW = 480,\n\t\t\tbaseH = 640;\n\t\tconst scaleX = vw / baseW,\n\t\t\tscaleY = vh / baseH;\n\t\tconst scale = Math.max(scaleX, scaleY);\n\t\tW = Math.ceil(vw / scale);\n\t\tH = Math.ceil(vh / scale);\n\t\tcanvas.width = W;\n\t\tcanvas.height = H;\n\t\tcanvas.style.width = vw + 'px';\n\t\tcanvas.style.height = vh + 'px';\n\t\tctx.imageSmoothingEnabled = false;\n\t}\n\tresizeCanvas();\n\twindow.addEventListener('resize', resizeCanvas);\n\n\tctx.fillStyle = '#020810';\n\tctx.fillRect(0, 0, W, H);\n\trequestAnimationFrame(gameLoop);\n})();"},{"name":"style.css","content":":root {\n\tcolor-scheme: dark;\n\tfont-family: 'Press Start 2P', monospace;\n\t--c-bg: #020810;\n\t--c-primary: #30e8b0;\n\t--c-secondary: #e0a030;\n\t--c-tertiary: #4090e0;\n\t--c-danger: #e04040;\n\t--c-surface: #0a1828;\n\t--c-text: #c0d8e8;\n\t--c-glow: rgba(48, 232, 176, 0.6);\n}\n\n* {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 0;\n}\n\nbody {\n\tbackground: var(--c-bg);\n\tmargin: 0;\n\tpadding: 0;\n\toverflow: hidden;\n\twidth: 100vw;\n\theight: 100vh;\n}\n\n#game-wrapper {\n\tposition: fixed;\n\tinset: 0;\n\twidth: 100vw;\n\theight: 100vh;\n\toverflow: hidden;\n}\n\n#game {\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\timage-rendering: pixelated;\n\timage-rendering: crisp-edges;\n}\n\n#scanlines {\n\tposition: fixed;\n\tinset: 0;\n\tbackground: repeating-linear-gradient(to bottom,\n\t\t\ttransparent 0px,\n\t\t\ttransparent 2px,\n\t\t\trgba(0, 0, 0, 0.12) 2px,\n\t\t\trgba(0, 0, 0, 0.12) 4px);\n\tpointer-events: none;\n\tz-index: 5;\n}\n\n#ui-overlay {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\tz-index: 10;\n\tpointer-events: none;\n}\n\n#hud {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tpadding: 12px 20px;\n\tbackground: linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 0%, transparent 100%);\n\tgap: 12px;\n\tflex-wrap: wrap;\n}\n\n.hud__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.hud__group--lives {\n\tflex-direction: row;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.hud__label {\n\tfont-size: 8px;\n\tcolor: var(--c-tertiary);\n\tletter-spacing: 1px;\n}\n\n.hud__value {\n\tfont-size: 11px;\n\tcolor: var(--c-primary);\n\ttext-shadow: 0 0 6px var(--c-glow);\n}\n\n.hud__bar {\n\twidth: 70px;\n\theight: 8px;\n\tbackground: var(--c-surface);\n\tborder: 1px solid var(--c-tertiary);\n\tborder-radius: 1px;\n\toverflow: hidden;\n}\n\n.hud__bar-fill {\n\theight: 100%;\n\twidth: 100%;\n\tbackground: var(--c-primary);\n\ttransition: width 0.3s, background-color 0.3s;\n}\n\n.hud__bar-fill--low {\n\tbackground: var(--c-danger);\n\tanimation: pulse-bar 0.5s infinite alternate;\n}\n\n@keyframes pulse-bar {\n\tto {\n\t\topacity: 0.5;\n\t}\n}\n\n.overlay {\n\tposition: fixed;\n\tinset: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: center;\n\talign-items: center;\n\tz-index: 20;\n\tbackground: rgba(2, 8, 16, 0.92);\n}\n\n.overlay--hidden {\n\tdisplay: none;\n}\n\n.title__main {\n\tfont-size: 42px;\n\tcolor: var(--c-primary);\n\ttext-shadow:\n\t\t0 0 10px var(--c-glow),\n\t\t0 4px 0 #1a9070,\n\t\t0 6px 0 #106050;\n\tletter-spacing: 6px;\n\tline-height: 1;\n}\n\n.title__sub {\n\tfont-size: 36px;\n\tcolor: var(--c-secondary);\n\ttext-shadow:\n\t\t0 0 10px rgba(224, 160, 48, 0.6),\n\t\t0 3px 0 #a07020,\n\t\t0 5px 0 #705010;\n\tletter-spacing: 8px;\n\tline-height: 1;\n\tmargin-top: 4px;\n}\n\n.title__tagline {\n\tfont-size: 8px;\n\tcolor: var(--c-tertiary);\n\tletter-spacing: 4px;\n\tmargin-top: 16px;\n\tanimation: flicker 2s infinite;\n}\n\n@keyframes flicker {\n\t0%, 100% {\n\t\topacity: 1;\n\t}\n\n\t92% {\n\t\topacity: 1;\n\t}\n\n\t93% {\n\t\topacity: 0.3;\n\t}\n\n\t94% {\n\t\topacity: 1;\n\t}\n\n\t96% {\n\t\topacity: 0.5;\n\t}\n\n\t97% {\n\t\topacity: 1;\n\t}\n}\n\n.title__instructions {\n\tmargin-top: 30px;\n\ttext-align: center;\n}\n\n.title__instructions p {\n\tfont-size: 7px;\n\tcolor: var(--c-text);\n\tmargin: 6px 0;\n\tletter-spacing: 1px;\n}\n\n.title__instructions kbd {\n\tdisplay: inline-block;\n\tbackground: var(--c-surface);\n\tborder: 1px solid var(--c-primary);\n\tborder-radius: 2px;\n\tpadding: 2px 5px;\n\tfont-family: inherit;\n\tfont-size: 7px;\n\tcolor: var(--c-primary);\n}\n\n.btn--start {\n\tmargin-top: 30px;\n\tfont-family: inherit;\n\tfont-size: 12px;\n\tcolor: var(--c-bg);\n\tbackground: var(--c-primary);\n\tborder: none;\n\tpadding: 10px 24px;\n\tcursor: pointer;\n\tletter-spacing: 2px;\n\tanimation: btn-glow 1.5s infinite alternate;\n\ttransition: transform 0.1s;\n}\n\n.btn--start:hover {\n\ttransform: scale(1.05);\n}\n\n.btn--start:active {\n\ttransform: scale(0.95);\n}\n\n@keyframes btn-glow {\n\tfrom {\n\t\tbox-shadow: 0 0 8px var(--c-glow);\n\t}\n\n\tto {\n\t\tbox-shadow: 0 0 20px var(--c-glow), 0 0 40px rgba(48, 232, 176, 0.3);\n\t}\n}\n\n.title__credit {\n\tmargin-top: 24px;\n\tfont-size: 5px;\n\tcolor: #445;\n\tletter-spacing: 2px;\n}\n\n.gameover__title {\n\tfont-size: 24px;\n\tcolor: var(--c-danger);\n\ttext-shadow: 0 0 15px rgba(224, 64, 64, 0.6);\n\tletter-spacing: 3px;\n\tanimation: shake-text 0.3s infinite;\n}\n\n@keyframes shake-text {\n\t0%, 100% {\n\t\ttransform: translateX(0);\n\t}\n\n\t25% {\n\t\ttransform: translateX(-2px);\n\t}\n\n\t75% {\n\t\ttransform: translateX(2px);\n\t}\n}\n\n.gameover__score,\n.gameover__depth {\n\tfont-size: 10px;\n\tcolor: var(--c-text);\n\tmargin-top: 16px;\n\tletter-spacing: 2px;\n}\n\n.banner__era {\n\tfont-size: 20px;\n\tcolor: var(--c-primary);\n\ttext-shadow: 0 0 15px var(--c-glow);\n\tletter-spacing: 4px;\n\tanimation: banner-in 0.5s ease-out;\n}\n\n.banner__depth {\n\tfont-size: 9px;\n\tcolor: var(--c-tertiary);\n\tmargin-top: 8px;\n\tletter-spacing: 2px;\n}\n\n@keyframes banner-in {\n\tfrom {\n\t\ttransform: scale(3);\n\t\topacity: 0;\n\t}\n\n\tto {\n\t\ttransform: scale(1);\n\t\topacity: 1;\n\t}\n}"}],"folders":[]},"variants":null,"createdAt":"2026-03-26T22:58:04.667Z","updatedAt":"2026-03-26T22:59:02.587Z"}}