PageSourceSearch

https://www.hamienet.com/assets/HamieGameCenterPage-mruGgKDx.js

js hamienet.com collected 2026-10-02 05:00:49 UTC 469,740 bytes, 10,039 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { u as useTranslation } from "./vendor-i18n-BC7ZMXWQ.js";
3import { dh as SlidersVertical, aW as X, bg as Music, bX as VolumeX, bU as Volume2, e2 as Palette, fb as Dices, bi as Sparkles, b7 as Check, bt as Tv, aX as ArrowLeft, cI as Disc, cG as Keyboard, cD as Drum, fc as PawPrint, fd as Boxes, ev as Bookmark, b2 as Upload, bE as Link, fe as Radar, c1 as Share2, bR as ExternalLink, b_ as Pause, bV as Play, bF as ChevronRight, co as FileMusic, cM as RefreshCw, bf as Search, cB as ChevronUp, b6 as LoaderCircle, db as Music2, cC as ChevronDown, cg as Activity, ch as Gauge, cz as Trophy, bs as Zap, c4 as RotateCcw, c7 as Shuffle, cv as Award, ff as Smartphone, bT as Mic, cR as MicOff, ck as Layers, cS as Minus, cf as Plus, c$ as Minimize2, d0 as Maximize2, b1 as CircleAlert } from "./vendor-D0P4XgNj.js";
4import { a as Midi } from "./vendor-audio-D-wTggbx.js";
5import { a as api } from "./index-w5lXls15.js";
6import { a as generateTrackUrl, e as isMobileDevice } from "./utils-CEVb7nN1.js";
7import { P as PianoRollGame } from "./PianoRollGame-BVidPvgJ.js";
8import { T as TaikoGame } from "./TaikoGame-D89Exv0S.js";
9import "./synth-Fr-K52Ox.js";
10import "./vendor-react-three-BCww3P1x.js";
11import "./wasm-synth-adapter-DpNGIIyM.js";
12import "./vendor-spessasynth-DH8n6o_V.js";
13import "./SharedKeyboard-B8ktwnUd.js";
14import "./useMidiKeyboard-DlyEIN8T.js";
15import "./GlobeFlag-V1uaxVEK.js";
16import "./SouthparkInstruments-C-w5kAUh.js";
17import "./IkMath-D6fi1ppd.js";
18import "./vendor-framer-B-4FZk1Z.js";
19import "./SouthparkVariants-DYvo6xjJ.js";
20const ARCADE_THEMES = {
21  "5thmix": {
22    id: "5thmix",
23    name: "CYBER 5thMIX",
24    subtitle: "DDR 5thMIX / 4thMIX CLASSIC",
25    eraLabel: "ARCADE 2001",
26    primary: "#00f0ff",
27    // Electric Cyan
28    primaryHover: "#38bdf8",
29    secondary: "#facc15",
30    // Sunburst Yellow
31    accent: "#ec4899",
32    // Hot Pink
33    backgroundDark: "#041026",
34    deckBorder: "border-4 border-cyan-400 shadow-[0_0_35px_rgba(0,240,255,0.35)]",
35    // Header
36    selectMusicTitle: "SELECT MUSIC",
37    selectMusicBannerClass: "bg-gradient-to-r from-cyan-400 via-sky-300 to-cyan-400 text-slate-950 border-2 border-white shadow-[0_0_20px_rgba(0,240,255,0.9),0_0_40px_rgba(0,240,255,0.4)] font-black tracking-[0.2em] px-8 py-1.5 rounded-full text-base sm:text-lg italic",
38    timerBoxClass: "bg-gradient-to-b from-yellow-300 via-amber-400 to-yellow-500 text-blue-950 border-2 border-yellow-200 shadow-[0_0_15px_rgba(250,204,21,0.8)] font-black text-lg sm:text-xl px-2.5 py-0.5 rounded-xl",
39    headerArrowColor: "#facc15",
40    marqueeGradient: "from-blue-950 via-cyan-950 to-blue-950",
41    marqueeBorder: "border-b-4 border-cyan-400/90 shadow-[0_0_25px_rgba(0,240,255,0.4)]",
42    creditsBadge: "bg-yellow-400 text-black border-2 border-yellow-300 shadow-[0_0_12px_rgba(250,204,21,0.6)]",
43    // Wheel & Cartridge
44    cartridgeShape: "pill",
45    cartridgeBg: "bg-blue-950/70 border-2 border-cyan-700/50 text-cyan-200 hover:border-cyan-400/70",
46    cartridgeBorder: "border-cyan-500/40",
47    cartridgeActiveBg: "bg-gradient-to-r from-blue-700 via-cyan-600 to-blue-800 text-white",
48    cartridgeActiveBorder: "border-yellow-400 shadow-[0_0_25px_rgba(0,240,255,0.7)]",
49    cartridgeActiveThickBorder: "border-4 border-yellow-300 shadow-[0_0_30px_rgba(0,240,255,0.8),inset_0_0_15px_rgba(250,204,21,0.4)]",
50    cartridgeActiveGlow: "rgba(0,240,255,0.7)",
51    titleColorActive: "text-yellow-300 drop-shadow-[0_2px_6px_rgba(0,0,0,0.9)]",
52    titleColorInactive: "text-amber-400/90 hover:text-yellow-300",
53    artistColorActive: "text-cyan-200 font-bold",
54    artistColorInactive: "text-blue-300/60",
55    wheelHalftoneType: "dots",
56    wheelBgGradient: "bg-gradient-to-b from-blue-950/80 via-slate-950/90 to-blue-950/95",
57    // Information Deck & Banner
58    bannerBorderClass: "border-4 border-cyan-400 bg-gradient-to-r from-blue-950 via-cyan-950 to-blue-900 shadow-[0_0_25px_rgba(0,240,255,0.4)]",
59    bannerBgClass: "bg-gradient-to-b from-blue-900/60 via-slate-900/80 to-blue-950/90",
60    hudStageBoxClass: "bg-blue-950/90 border-2 border-cyan-400/80 text-cyan-300 shadow-[0_0_10px_rgba(0,240,255,0.3)]",
61    badgeStyle: "grade",
62    footprintColor: "#facc15",
63    footprint1PColor: "#facc15",
64    // Yellow
65    footprint2PColor: "#ec4899",
66    // Hot Pink
67    // Radar
68    radarPolygonFill: "rgba(0, 240, 255, 0.45)",
69    radarPolygonStroke: "#00f0ff",
70    radarGridStroke: "rgba(0, 240, 255, 0.3)",
71    radarLabelColor: "#facc15",
72    // Canvas Background
73    bgGradientTop: "#020617",
74    bgGradientHorizon: "#082f49",
75    bgGradientBottom: "#020c1b",
76    horizonGlowColor: "rgba(0, 240, 255, 0.45)",
77    particleColors: ["#00f0ff", "#facc15", "#ec4899", "#38bdf8"],
78    gridLineColor: "rgba(0, 240, 255, 0.28)"
79  },
80  "max2": {
81    id: "max2",
82    name: "GROOVE LIME MAX2",
83    subtitle: "DDR MAX2 / MATRIX TECH HUD",
84    eraLabel: "ARCADE 2002",
85    primary: "#a3e635",
86    // Acid Cyber Lime
87    primaryHover: "#84cc16",
88    secondary: "#22c55e",
89    // Emerald Green
90    accent: "#eab308",
91    // Tech Amber
92    backgroundDark: "#031405",
93    deckBorder: "border-4 border-lime-400 shadow-[0_0_35px_rgba(163,230,53,0.35)]",
94    // Header
95    selectMusicTitle: "// Select Music",
96    selectMusicBannerClass: "bg-gradient-to-r from-lime-400 via-lime-300 to-lime-400 text-slate-950 border-2 border-white shadow-[0_0_20px_rgba(163,230,53,0.9),0_0_40px_rgba(163,230,53,0.4)] font-black tracking-[0.2em] px-8 py-1.5 rounded-md text-base sm:text-lg italic",
97    timerBoxClass: "bg-gradient-to-b from-lime-400 via-lime-500 to-emerald-600 text-slate-950 border-2 border-lime-200 shadow-[0_0_15px_rgba(163,230,53,0.8)] font-black text-lg sm:text-xl px-2.5 py-0.5 rounded-md",
98    headerArrowColor: "#a3e635",
99    marqueeGradient: "from-emerald-950 via-lime-950 to-green-950",
100    marqueeBorder: "border-b-4 border-lime-400/90 shadow-[0_0_25px_rgba(163,230,53,0.4)]",
101    creditsBadge: "bg-lime-400 text-black border-2 border-lime-300 shadow-[0_0_12px_rgba(163,230,53,0.6)]",
102    // Wheel & Cartridge
103    cartridgeShape: "trapezoid",
104    cartridgeBg: "bg-emerald-950/70 border-2 border-lime-800/40 text-lime-200 hover:border-lime-500/70",
105    cartridgeBorder: "border-lime-500/40",
106    cartridgeActiveBg: "bg-gradient-to-r from-lime-400 via-lime-500 to-emerald-400 text-slate-950 font-black",
107    cartridgeActiveBorder: "border-white shadow-[0_0_25px_rgba(163,230,53,0.8)]",
108    cartridgeActiveThickBorder: "border-4 border-white shadow-[0_0_30px_rgba(163,230,53,0.9),inset_0_0_15px_rgba(255,255,255,0.6)]",
109    cartridgeActiveGlow: "rgba(163,230,53,0.8)",
110    titleColorActive: "text-slate-950 font-black drop-shadow-[0_1px_2px_rgba(255,255,255,0.6)]",
111    titleColorInactive: "text-lime-300/80 hover:text-lime-200",
112    artistColorActive: "text-emerald-950 font-black",
113    artistColorInactive: "text-lime-500/60",
114    wheelHalftoneType: "matrix",
115    wheelBgGradient: "bg-gradient-to-b from-emerald-950/80 via-black/85 to-lime-950/90",
116    // Information Deck & Banner
117    bannerBorderClass: "border-4 border-lime-400 bg-gradient-to-r from-emerald-950 via-lime-950 to-black shadow-[0_0_25px_rgba(163,230,53,0.4)]",
118    bannerBgClass: "bg-gradient-to-b from-emerald-950/70 via-slate-950/80 to-lime-950/90",
119    hudStageBoxClass: "bg-emerald-950/90 border-2 border-lime-400/80 text-lime-300 shadow-[0_0_10px_rgba(163,230,53,0.3)]",
120    badgeStyle: "kanji",
121    footprintColor: "#a3e635",
122    footprint1PColor: "#a3e635",
123    // Acid Lime
124    footprint2PColor: "#f97316",
125    // Orange
126    // Radar
127    radarPolygonFill: "rgba(163, 230, 53, 0.45)",
128    radarPolygonStroke: "#bef264",
129    radarGridStroke: "rgba(163, 230, 53, 0.3)",
130    radarLabelColor: "#a3e635",
131    // Canvas Background
132    bgGradientTop: "#020f04",
133    bgGradientHorizon: "#063810",
134    bgGradientBottom: "#010a03",
135    horizonGlowColor: "rgba(163, 230, 53, 0.4)",
136    particleColors: ["#a3e635", "#22c55e", "#86efac", "#eab308"],
137    gridLineColor: "rgba(163, 230, 53, 0.25)"
138  },
139  "sunset": {
140    id: "sunset",
141    name: "NEON SUNSET MAX",
142    subtitle: "DDR MAX / CARBON FIRE CHASSIS",
143    eraLabel: "ARCADE 2001",
144    primary: "#f97316",
145    // Molten Neon Orange
146    primaryHover: "#ea580c",
147    secondary: "#ef4444",
148    // Crimson Red
149    accent: "#00f0ff",
150    // Ice Cyan Contrast
151    backgroundDark: "#140602",
152    deckBorder: "border-4 border-orange-500 shadow-[0_0_35px_rgba(249,115,22,0.35)]",
153    // Header
154    selectMusicTitle: "// Select Music",
155    selectMusicBannerClass: "bg-gradient-to-r from-amber-400 via-yellow-300 to-amber-500 text-slate-950 border-2 border-white shadow-[0_0_20px_rgba(245,158,11,0.9),0_0_40px_rgba(245,158,11,0.4)] font-black tracking-[0.2em] px-8 py-1.5 rounded-md text-base sm:text-lg italic",
156    timerBoxClass: "bg-gradient-to-b from-amber-300 via-orange-400 to-amber-500 text-slate-950 border-2 border-yellow-200 shadow-[0_0_15px_rgba(245,158,11,0.9)] font-black text-lg sm:text-xl px-2.5 py-0.5 rounded-md",
157    headerArrowColor: "#00f0ff",
158    marqueeGradient: "from-red-950 via-orange-950 to-amber-950",
159    marqueeBorder: "border-b-4 border-orange-500/90 shadow-[0_0_25px_rgba(249,115,22,0.4)]",
160    creditsBadge: "bg-orange-500 text-black border-2 border-orange-300 shadow-[0_0_12px_rgba(249,115,22,0.6)]",
161    // Wheel & Cartridge
162    cartridgeShape: "trapezoid",
163    cartridgeBg: "bg-zinc-950/75 border-2 border-orange-900/50 text-orange-200 hover:border-orange-500/70",
164    cartridgeBorder: "border-orange-500/40",
165    cartridgeActiveBg: "bg-gradient-to-r from-amber-600 via-orange-600 to-amber-500 text-white font-black",
166    cartridgeActiveBorder: "border-amber-300 shadow-[0_0_25px_rgba(249,115,22,0.8)]",
167    cartridgeActiveThickBorder: "border-4 border-amber-300 shadow-[0_0_35px_rgba(249,115,22,0.9),inset_0_0_15px_rgba(251,191,36,0.5)]",
168    cartridgeActiveGlow: "rgba(249,115,22,0.85)",
169    titleColorActive: "text-cyan-200 font-black drop-shadow-[0_2px_6px_rgba(0,0,0,0.9)]",
170    titleColorInactive: "text-cyan-400/90 hover:text-cyan-300",
171    artistColorActive: "text-amber-200 font-bold",
172    artistColorInactive: "text-cyan-600/70",
173    wheelHalftoneType: "sunset",
174    wheelBgGradient: "bg-gradient-to-b from-amber-950/70 via-red-950/80 to-black/95",
175    // Information Deck & Banner
176    bannerBorderClass: "border-4 border-orange-500 bg-gradient-to-r from-red-950 via-orange-950 to-zinc-950 shadow-[0_0_25px_rgba(249,115,22,0.4)]",
177    bannerBgClass: "bg-gradient-to-b from-red-950/70 via-slate-950/85 to-orange-950/90",
178    hudStageBoxClass: "bg-orange-950/90 border-2 border-orange-500/80 text-orange-300 shadow-[0_0_10px_rgba(249,115,22,0.3)]",
179    badgeStyle: "kanji",
180    footprintColor: "#f97316",
181    footprint1PColor: "#22c55e",
182    // Green
183    footprint2PColor: "#f97316",
184    // Orange
185    // Radar
186    radarPolygonFill: "rgba(249, 115, 22, 0.45)",
187    radarPolygonStroke: "#fdba74",
188    radarGridStroke: "rgba(249, 115, 22, 0.3)",
189    radarLabelColor: "#fb923c",
190    // Canvas Background
191    bgGradientTop: "#0c0201",
192    bgGradientHorizon: "#3b0d05",
193    bgGradientBottom: "#080100",
194    horizonGlowColor: "rgba(249, 115, 22, 0.45)",
195    particleColors: ["#f97316", "#ef4444", "#facc15", "#00f0ff"],
196    gridLineColor: "rgba(249, 115, 22, 0.25)"
197  }
198};
199const THEME_LIST = ["5thmix", "max2", "sunset"];
200function computeGrooveRadar(song) {
201  if (!song) {
202    return { stream: 50, voltage: 50, chaos: 50, freeze: 50, air: 50 };
203  }
204  const bpm = typeof song.bpm === "number" && song.bpm > 0 ? song.bpm : 120;
205  const duration = typeof song.duration === "number" && song.duration > 0 ? song.duration : 180;
206  const stars = typeof song.difficultyStars === "number" ? song.difficultyStars : 3;
207  const title = String(song.title || song.id || "HamieTrack");
208  let hash = 0;
209  for (let i = 0; i < title.length; i++) {
210    hash = hash * 31 + title.charCodeAt(i) & 4294967295;
211  }
212  const pseudo = (offset) => {
213    const val = Math.sin(hash + offset) * 1e4;
214    return val - Math.floor(val);
215  };
216  const streamBase = Math.min(100, Math.max(20, Math.round(bpm / 180 * 55 + stars * 9 + pseudo(1) * 15)));
217  const voltageBase = Math.min(100, Math.max(25, Math.round(stars * 16 + (bpm > 140 ? 20 : 5) + pseudo(2) * 20)));
218  const chaosBase = Math.min(100, Math.max(15, Math.round(bpm % 17 * 3 + stars * 12 + pseudo(3) * 25)));
219  const freezeBase = Math.min(100, Math.max(20, Math.round(duration / 240 * 40 + pseudo(4) * 45)));
220  const airBase = Math.min(100, Math.max(20, Math.round(stars * 14 + pseudo(5) * 30)));
221  return {
222    stream: streamBase,
223    voltage: voltageBase,
224    chaos: chaosBase,
225    freeze: freezeBase,
226    air: airBase
227  };
228}
229function getRandomTheme() {
230  const idx = Math.floor(Math.random() * THEME_LIST.length);
231  return THEME_LIST[idx];
232}
233const ArcadeBackground = ({
234  enableScanlines = true,
235  theme = ARCADE_THEMES["5thmix"],
236  isActive = true
237}) => {
238  const canvasRef = reactExports.useRef(null);
239  const themeRef = reactExports.useRef(theme);
240  themeRef.current = theme;
241  const isActiveRef = reactExports.useRef(isActive);
242  isActiveRef.current = isActive;
243  reactExports.useEffect(() => {
244    const canvas = canvasRef.current;
245    if (!canvas) return;
246    const ctx = canvas.getContext("2d", { alpha: false });
247    if (!ctx) return;
248    let animationFrameId;
249    let width = canvas.width = window.innerWidth;
250    let height = canvas.height = window.innerHeight;
251    const handleResize = () => {
252      if (!canvas) return;
253      width = canvas.width = window.innerWidth;
254      height = canvas.height = window.innerHeight;
255    };
256    window.addEventListener("resize", handleResize);
257    const particles = [];
258    for (let i = 0; i < 30; i++) {
259      particles.push({
260        x: Math.random() * width,
261        y: Math.random() * height,
262        size: Math.random() * 2.2 + 1,
263        speedY: -(Math.random() * 0.5 + 0.2),
264        colorIndex: Math.floor(Math.random() * 4),
265        alpha: Math.random() * 0.6 + 0.2
266      });
267    }
268    let gridOffset = 0;
269    let lastFrameTime = performance.now();
270    const render = (now) => {
271      if (!isActiveRef.current) {
272        animationFrameId = requestAnimationFrame(render);
273        return;
274      }
275      const delta = now - lastFrameTime;
276      if (delta < 15) {
277        animationFrameId = requestAnimationFrame(render);
278        return;
279      }
280      lastFrameTime = now;
281      const currentTheme = themeRef.current;
282      const bgGrad = ctx.createLinearGradient(0, 0, 0, height);
283      bgGrad.addColorStop(0, currentTheme.bgGradientTop);
284      bgGrad.addColorStop(0.5, currentTheme.bgGradientHorizon);
285      bgGrad.addColorStop(1, currentTheme.bgGradientBottom);
286      ctx.fillStyle = bgGrad;
287      ctx.fillRect(0, 0, width, height);
288      const horizonY = height * 0.52;
289      const horizonGlow = ctx.createRadialGradient(width / 2, horizonY, 10, width / 2, horizonY, width * 0.6);
290      horizonGlow.addColorStop(0, currentTheme.horizonGlowColor);
291      horizonGlow.addColorStop(1, "transparent");
292      ctx.fillStyle = horizonGlow;
293      ctx.fillRect(0, 0, width, height);
294      gridOffset = (gridOffset + 0.7) % 36;
295      ctx.save();
296      ctx.beginPath();
297      ctx.rect(0, horizonY, width, height - horizonY);
298      ctx.clip();
299      const vpX = width / 2;
300      const vpY = horizonY;
301      ctx.lineWidth = 1;
302      const numRays = Math.min(28, Math.floor(width / 50));
303      const rayGrad = ctx.createLinearGradient(vpX, vpY, vpX, height);
304      rayGrad.addColorStop(0, `${currentTheme.secondary}15`);
305      rayGrad.addColorStop(0.5, `${currentTheme.primary}40`);
306      rayGrad.addColorStop(1, `${currentTheme.primary}80`);
307      ctx.strokeStyle = rayGrad;
308      ctx.beginPath();
309      for (let i = -numRays; i <= numRays; i++) {
310        const bottomX = vpX + i * 55 * 2.2;
311        ctx.moveTo(vpX, vpY);
312        ctx.lineTo(bottomX, height);
313      }
314      ctx.stroke();
315      for (let yStep = 0; yStep < 12; yStep++) {
316        const progress = (yStep * 36 + gridOffset) % 432 / 432;
317        const depthY = horizonY + Math.pow(progress, 2.6) * (height - horizonY);
318        const alpha = Math.min(1, Math.pow(progress, 1.4));
319        ctx.strokeStyle = currentTheme.gridLineColor.replace("0.25", `${alpha * 0.55}`).replace("0.22", `${alpha * 0.55}`);
320        ctx.lineWidth = 1 + progress * 1.5;
321        ctx.beginPath();
322        ctx.moveTo(0, depthY);
323        ctx.lineTo(width, depthY);
324        ctx.stroke();
325      }
326      ctx.restore();
327      const laserGrad = ctx.createLinearGradient(0, 0, width, 0);
328      laserGrad.addColorStop(0, "transparent");
329      laserGrad.addColorStop(0.2, currentTheme.primary);
330      laserGrad.addColorStop(0.5, currentTheme.secondary);
331      laserGrad.addColorStop(0.8, currentTheme.primary);
332      laserGrad.addColorStop(1, "transparent");
333      ctx.strokeStyle = laserGrad;
334      ctx.lineWidth = 2;
335      ctx.beginPath();
336      ctx.moveTo(0, horizonY);
337      ctx.lineTo(width, horizonY);
338      ctx.stroke();
339      const pColors = currentTheme.particleColors;
340      for (const p of particles) {
341        p.y += p.speedY;
342        if (p.y < 0) {
343          p.y = height;
344          p.x = Math.random() * width;
345        }
346        ctx.fillStyle = pColors[p.colorIndex % pColors.length];
347        ctx.globalAlpha = p.alpha;
348        ctx.fillRect(p.x, p.y, p.size, p.size);
349      }
350      ctx.globalAlpha = 1;
351      animationFrameId = requestAnimationFrame(render);
352    };
353    animationFrameId = requestAnimationFrame(render);
354    return () => {
355      window.removeEventListener("resize", handleResize);
356      cancelAnimationFrame(animationFrameId);
357    };
358  }, []);
359  if (!isActive) {
360    return null;
361  }
362  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 pointer-events-none z-0 overflow-hidden", children: [
363    /* @__PURE__ */ jsxRuntimeExports.jsx("canvas", { ref: canvasRef, className: "absolute inset-0 w-full h-full will-change-transform", style: { transform: "translateZ(0)" } }),
364    /* @__PURE__ */ jsxRuntimeExports.jsx(
365      "div",
366      {
367        className: "absolute top-0 right-0 h-full pointer-events-none transition-all duration-300",
368        style: {
369          width: "52%",
370          backgroundImage: theme.wheelHalftoneType === "matrix" ? "linear-gradient(rgba(34, 197, 94, 0.12) 2px, transparent 2px), linear-gradient(90deg, rgba(34, 197, 94, 0.12) 2px, transparent 2px)" : theme.wheelHalftoneType === "sunset" ? "radial-gradient(circle, rgba(15, 5, 5, 0.72) 2.2px, transparent 2.3px)" : "radial-gradient(circle, rgba(0, 240, 255, 0.18) 2px, transparent 2.2px)",
371          backgroundSize: theme.wheelHalftoneType === "matrix" ? "15px 15px" : "14px 14px"
372        }
373      }
374    ),
375    enableScanlines && /* @__PURE__ */ jsxRuntimeExports.jsx(
376      "div",
377      {
378        className: "absolute inset-0 opacity-[0.07] pointer-events-none",
379        style: {
380          backgroundImage: "repeating-linear-gradient(0deg, #000, #000 2px, transparent 2px, transparent 4px)",
381          backgroundSize: "100% 4px"
382        }
383      }
384    ),
385    /* @__PURE__ */ jsxRuntimeExports.jsx(
386      "div",
387      {
388        className: "absolute inset-0 pointer-events-none",
389        style: {
390          background: "radial-gradient(circle at center, transparent 40%, rgba(3, 7, 18, 0.75) 100%)"
391        }
392      }
393    )
394  ] });
395};
396class ArcadeAudioEngine {
397  constructor() {
398    this.ctx = null;
399    this.masterGain = null;
400    this.bgmGain = null;
401    this.sfxGain = null;
402    this.isBgmPlaying = false;
403    this.isBgmPaused = false;
404    this.isBgmAllowed = false;
405    this.bgmInterval = null;
406    this.currentBgmStep = 0;
407    this.bgmVolume = 0.25;
408    this.sfxVolume = 0.6;
409    this.isMuted = false;
410  }
411  enableBgm() {
412    this.isBgmAllowed = true;
413  }
414  initContext() {
415    if (!this.ctx && typeof window !== "undefined") {
416      const AudioContextClass = window.AudioContext || window.webkitAudioContext;
417      if (AudioContextClass) {
418        this.ctx = new AudioContextClass();
419        this.masterGain = this.ctx.createGain();
420        this.masterGain.gain.setValueAtTime(this.isMuted ? 0 : 1, this.ctx.currentTime);
421        this.masterGain.connect(this.ctx.destination);
422        this.bgmGain = this.ctx.createGain();
423        this.bgmGain.gain.setValueAtTime(this.bgmVolume, this.ctx.currentTime);
424        this.bgmGain.connect(this.masterGain);
425        this.sfxGain = this.ctx.createGain();
426        this.sfxGain.gain.setValueAtTime(this.sfxVolume, this.ctx.currentTime);
427        this.sfxGain.connect(this.masterGain);
428      }
429    }
430    if (this.ctx && this.ctx.state === "suspended") {
431      this.ctx.resume().catch(() => {
432      });
433    }
434  }
435  getAudioContext() {
436    this.initContext();
437    return this.ctx;
438  }
439  // ==========================================
440  // SOUND EFFECTS (SFX)
441  // ==========================================
442  /**
443   * Classic Arcade Double-Tone Coin Drop (B5 -> E6)
444   */
445  playCoin() {
446    try {
447      this.initContext();
448      if (!this.ctx || !this.sfxGain || this.isMuted) return;
449      const t = this.ctx.currentTime;
450      const osc = this.ctx.createOscillator();
451      const gain = this.ctx.createGain();
452      osc.type = "sine";
453      osc.frequency.setValueAtTime(987.77, t);
454      osc.frequency.setValueAtTime(1318.51, t + 0.08);
455      gain.gain.setValueAtTime(1e-3, t);
456      gain.gain.linearRampToValueAtTime(0.4, t + 0.01);
457      gain.gain.setValueAtTime(0.4, t + 0.08);
458      gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.45);
459      osc.connect(gain);
460      gain.connect(this.sfxGain);
461      osc.start(t);
462      osc.stop(t + 0.46);
463    } catch (e) {
464    }
465  }
466  /**
467   * Snappy Arcade Menu Select Blip
468   */
469  playSelect() {
470    try {
471      this.initContext();
472      if (!this.ctx || !this.sfxGain || this.isMuted) return;
473      const t = this.ctx.currentTime;
474      const osc = this.ctx.createOscillator();
475      const gain = this.ctx.createGain();
476      osc.type = "triangle";
477      osc.frequency.setValueAtTime(440, t);
478      osc.frequency.exponentialRampToValueAtTime(880, t + 0.06);
479      gain.gain.setValueAtTime(0.3, t);
480      gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.08);
481      osc.connect(gain);
482      gain.connect(this.sfxGain);
483      osc.start(t);
484      osc.stop(t + 0.09);
485    } catch (e) {
486    }
487  }
488  /**
489   * Arcade Confirmation / Decide Tone
490   */
491  playDecide() {
492    this.playCoin();
493  }
494  /**
495   * Subtle Hover Click
496   */
497  playHover() {
498    try {
499      this.initContext();
500      if (!this.ctx || !this.sfxGain || this.isMuted) return;
501      const t = this.ctx.currentTime;
502      const osc = this.ctx.createOscillator();
503      const gain = this.ctx.createGain();
504      osc.type = "sine";
505      osc.frequency.setValueAtTime(620, t);
506      gain.gain.setValueAtTime(0.08, t);
507      gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.03);
508      osc.connect(gain);
509      gain.connect(this.sfxGain);
510      osc.start(t);
511      osc.stop(t + 0.04);
512    } catch (e) {
513    }
514  }
515  /**
516   * Song Roulette Wheel Tick with variable pitch
517   */
518  playRouletteTick(pitchMultiplier = 1) {
519    try {
520      this.initContext();
521      if (!this.ctx || !this.sfxGain || this.isMuted) return;
522      const t = this.ctx.currentTime;
523      const osc = this.ctx.createOscillator();
524      const gain = this.ctx.createGain();
525      osc.type = "square";
526      const baseFreq = 520 * pitchMultiplier;
527      osc.frequency.setValueAtTime(baseFreq, t);
528      osc.frequency.exponentialRampToValueAtTime(baseFreq * 1.5, t + 0.03);
529      gain.gain.setValueAtTime(0.2, t);
530      gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.04);
531      osc.connect(gain);
532      gain.connect(this.sfxGain);
533      osc.start(t);
534      osc.stop(t + 0.05);
535    } catch (e) {
536    }
537  }
538  /**
539   * Rapid Arcade Roulette Spin SFX Sequence
540   */
541  playRoulette() {
542    try {
543      for (let i = 0; i < 8; i++) {
544        setTimeout(() => {
545          this.playRouletteTick(1 + i * 0.07);
546        }, i * 60);
547      }
548    } catch (e) {
549    }
550  }
551  /**
552   * Power-Up Stage Start Fanfare
553   */
554  playStageStart() {
555    try {
556      this.initContext();
557      if (!this.ctx || !this.sfxGain || this.isMuted) return;
558      const t = this.ctx.currentTime;
559      const notes = [261.63, 329.63, 392, 523.25, 659.25, 783.99, 1046.5];
560      notes.forEach((freq, idx) => {
561        const noteTime = t + idx * 0.045;
562        const osc = this.ctx.createOscillator();
563        const gain = this.ctx.createGain();
564        osc.type = "sawtooth";
565        osc.frequency.setValueAtTime(freq, noteTime);
566        gain.gain.setValueAtTime(1e-3, noteTime);
567        gain.gain.linearRampToValueAtTime(0.25, noteTime + 0.01);
568        gain.gain.exponentialRampToValueAtTime(1e-3, noteTime + (idx === notes.length - 1 ? 0.6 : 0.2));
569        osc.connect(gain);
570        gain.connect(this.sfxGain);
571        osc.start(noteTime);
572        osc.stop(noteTime + (idx === notes.length - 1 ? 0.65 : 0.25));
573      });
574    } catch (e) {
575    }
576  }
577  /**
578   * Classic Arcade Error / Invalid Action Low Buzzer
579   */
580  playBuzzer() {
581    try {
582      this.initContext();
583      if (!this.ctx || !this.sfxGain || this.isMuted) return;
584      const t = this.ctx.currentTime;
585      const osc = this.ctx.createOscillator();
586      const gain = this.ctx.createGain();
587      osc.type = "sawtooth";
588      osc.frequency.setValueAtTime(140, t);
589      osc.frequency.setValueAtTime(120, t + 0.08);
590      gain.gain.setValueAtTime(0.2, t);
591      gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.22);
592      osc.connect(gain);
593      gain.connect(this.sfxGain);
594      osc.start(t);
595      osc.stop(t + 0.23);
596    } catch (e) {
597    }
598  }
599  /**
600   * Victory / Stage Clear Jingle
601   */
602  playStageClear() {
603    try {
604      this.initContext();
605      if (!this.ctx || !this.sfxGain || this.isMuted) return;
606      const t = this.ctx.currentTime;
607      const chords = [
608        { time: 0, freqs: [392, 493.88, 587.33] },
609        // G
610        { time: 0.18, freqs: [392, 493.88, 587.33] },
611        { time: 0.36, freqs: [440, 554.37, 659.25] },
612        // A
613        { time: 0.6, freqs: [523.25, 659.25, 783.99, 1046.5] }
614        // C high
615      ];
616      chords.forEach((chord) => {
617        const noteTime = t + chord.time;
618        chord.freqs.forEach((freq) => {
619          const osc = this.ctx.createOscillator();
620          const gain = this.ctx.createGain();
621          osc.type = "triangle";
622          osc.frequency.setValueAtTime(freq, noteTime);
623          gain.gain.setValueAtTime(1e-3, noteTime);
624          gain.gain.linearRampToValueAtTime(0.2, noteTime + 0.02);
625          gain.gain.exponentialRampToValueAtTime(1e-3, noteTime + 0.4);
626          osc.connect(gain);
627          gain.connect(this.sfxGain);
628          osc.start(noteTime);
629          osc.stop(noteTime + 0.42);
630        });
631      });
632    } catch (e) {
633    }
634  }
635  // ==========================================
636  // PROCEDURAL ARCADE BACKGROUND MUSIC (BGM)
637  // ==========================================
638  /**
639   * Catchy 16-bit Synthwave / Chiptune Arcade Loop (128 BPM)
640   * Melody + Bassline + Percussion synthesize continuously.
641   */
642  startBgm() {
643    if (!this.isBgmAllowed) return;
644    if (this.isBgmPlaying) return;
645    this.initContext();
646    if (!this.ctx || !this.bgmGain) return;
647    try {
648      this.bgmGain.gain.cancelScheduledValues(this.ctx.currentTime);
649      this.bgmGain.gain.setValueAtTime(this.isMuted ? 0 : this.bgmVolume, this.ctx.currentTime);
650    } catch (e) {
651    }
652    this.isBgmPlaying = true;
653    this.isBgmPaused = false;
654    this.currentBgmStep = 0;
655    const stepTimeMs = 6e4 / 128 / 4;
656    const bassLine = [
657      // Bar 1: Am
658      220,
659      0,
660      220,
661      0,
662      220,
663      0,
664      330,
665      0,
666      220,
667      0,
668      220,
669      0,
670      220,
671      330,
672      220,
673      0,
674      // Bar 2: F
675      174.61,
676      0,
677      174.61,
678      0,
679      174.61,
680      0,
681      261.63,
682      0,
683      174.61,
684      0,
685      174.61,
686      0,
687      174.61,
688      261.63,
689      174.61,
690      0,
691      // Bar 3: C
692      261.63,
693      0,
694      261.63,
695      0,
696      261.63,
697      0,
698      392,
699      0,
700      261.63,
701      0,
702      261.63,
703      0,
704      261.63,
705      392,
706      261.63,
707      0,
708      // Bar 4: G / Em
709      196,
710      0,
711      196,
712      0,
713      196,
714      0,
715      293.66,
716      0,
717      196,
718      0,
719      196,
720      0,
721      246.94,
722      293.66,
723      392,
724      0
725    ];
726    const arpLine = [
727      // Bar 1: Am arp
728      440,
729      523.25,
730      659.25,
731      880,
732      659.25,
733      523.25,
734      440,
735      523.25,
736      659.25,
737      880,
738      1046.5,
739      880,
740      659.25,
741      523.25,
742      659.25,
743      880,
744      // Bar 2: F arp
745      349.23,
746      440,
747      523.25,
748      698.46,
749      523.25,
750      440,
751      349.23,
752      440,
753      523.25,
754      698.46,
755      880,
756      698.46,
757      523.25,
758      440,
759      523.25,
760      698.46,
761      // Bar 3: C arp
762      523.25,
763      659.25,
764      783.99,
765      1046.5,
766      783.99,
767      659.25,
768      523.25,
769      659.25,
770      783.99,
771      1046.5,
772      1318.5,
773      1046.5,
774      783.99,
775      659.25,
776      783.99,
777      1046.5,
778      // Bar 4: G arp
779      392,
780      493.88,
781      587.33,
782      783.99,
783      587.33,
784      493.88,
785      392,
786      493.88,
787      587.33,
788      783.99,
789      987.77,
790      783.99,
791      587.33,
792      493.88,
793      587.33,
794      783.99
795    ];
796    this.bgmInterval = setInterval(() => {
797      if (!this.ctx || !this.bgmGain || this.isBgmPaused || this.isMuted) return;
798      const t = this.ctx.currentTime + 0.02;
799      const step = this.currentBgmStep % 64;
800      const arpFreq = arpLine[step];
801      if (arpFreq) {
802        const osc = this.ctx.createOscillator();
803        const filter = this.ctx.createBiquadFilter();
804        const noteGain = this.ctx.createGain();
805        osc.type = "square";
806        osc.frequency.setValueAtTime(arpFreq, t);
807        filter.type = "lowpass";
808        filter.frequency.setValueAtTime(2800, t);
809        filter.frequency.exponentialRampToValueAtTime(800, t + 0.1);
810        noteGain.gain.setValueAtTime(0.07, t);
811        noteGain.gain.exponentialRampToValueAtTime(1e-3, t + 0.1);
812        osc.connect(filter);
813        filter.connect(noteGain);
814        noteGain.connect(this.bgmGain);
815        osc.start(t);
816        osc.stop(t + 0.11);
817      }
818      const bassFreq = bassLine[step];
819      if (bassFreq) {
820        const bOsc = this.ctx.createOscillator();
821        const bGain = this.ctx.createGain();
822        bOsc.type = "sawtooth";
823        bOsc.frequency.setValueAtTime(bassFreq / 2, t);
824        bGain.gain.setValueAtTime(0.12, t);
825        bGain.gain.exponentialRampToValueAtTime(1e-3, t + 0.12);
826        bOsc.connect(bGain);
827        bGain.connect(this.bgmGain);
828        bOsc.start(t);
829        bOsc.stop(t + 0.13);
830      }
831      if (step % 2 === 0) {
832        this.synthesizeChiptuneNoise(t, 0.03, step % 8 === 4 ? 0.08 : 0.04);
833      }
834      if (step % 8 === 0) {
835        this.synthesizeRetroKick(t);
836      } else if (step % 8 === 4) {
837        this.synthesizeRetroSnare(t);
838      }
839      this.currentBgmStep++;
840    }, stepTimeMs);
841  }
842  synthesizeChiptuneNoise(t, duration, vol) {
843    if (!this.ctx || !this.bgmGain) return;
844    try {
845      const bufferSize = this.ctx.sampleRate * duration;
846      const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
847      const data = buffer.getChannelData(0);
848      for (let i = 0; i < bufferSize; i++) {
849        data[i] = Math.random() * 2 - 1;
850      }
851      const noise = this.ctx.createBufferSource();
852      noise.buffer = buffer;
853      const filter = this.ctx.createBiquadFilter();
854      filter.type = "highpass";
855      filter.frequency.setValueAtTime(7e3, t);
856      const gain = this.ctx.createGain();
857      gain.gain.setValueAtTime(vol, t);
858      gain.gain.exponentialRampToValueAtTime(1e-3, t + duration);
859      noise.connect(filter);
860      filter.connect(gain);
861      gain.connect(this.bgmGain);
862      noise.start(t);
863      noise.stop(t + duration);
864    } catch (e) {
865    }
866  }
867  synthesizeRetroKick(t) {
868    if (!this.ctx || !this.bgmGain) return;
869    try {
870      const osc = this.ctx.createOscillator();
871      const gain = this.ctx.createGain();
872      osc.frequency.setValueAtTime(140, t);
873      osc.frequency.exponentialRampToValueAtTime(35, t + 0.08);
874      gain.gain.setValueAtTime(0.2, t);
875      gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.1);
876      osc.connect(gain);
877      gain.connect(this.bgmGain);
878      osc.start(t);
879      osc.stop(t + 0.11);
880    } catch (e) {
881    }
882  }
883  synthesizeRetroSnare(t) {
884    this.synthesizeChiptuneNoise(t, 0.08, 0.12);
885  }
886  stopBgm() {
887    this.isBgmAllowed = false;
888    if (this.bgmInterval) {
889      clearInterval(this.bgmInterval);
890      this.bgmInterval = null;
891    }
892    this.isBgmPlaying = false;
893    this.isBgmPaused = false;
894    this.currentBgmStep = 0;
895    if (this.bgmGain && this.ctx) {
896      try {
897        this.bgmGain.gain.cancelScheduledValues(this.ctx.currentTime);
898        this.bgmGain.gain.setValueAtTime(0, this.ctx.currentTime);
899      } catch (e) {
900      }
901    }
902  }
903  pauseBgm() {
904    this.isBgmPaused = true;
905  }
906  resumeBgm() {
907    if (!this.isBgmPlaying) {
908      this.startBgm();
909    } else {
910      this.isBgmPaused = false;
911    }
912  }
913  isPlaying() {
914    return this.isBgmPlaying && !this.isBgmPaused && !this.isMuted;
915  }
916  setBgmVolume(volume) {
917    this.bgmVolume = Math.max(0, Math.min(1, volume));
918    if (this.bgmGain && this.ctx) {
919      this.bgmGain.gain.setValueAtTime(this.bgmVolume, this.ctx.currentTime);
920    }
921  }
922  getBgmVolume() {
923    return this.bgmVolume;
924  }
925  toggleMute() {
926    this.setMuted(!this.isMuted);
927    return this.isMuted;
928  }
929  setMuted(muted) {
930    this.isMuted = muted;
931    if (this.masterGain && this.ctx) {
932      this.masterGain.gain.setValueAtTime(this.isMuted ? 0 : 1, this.ctx.currentTime);
933    }
934  }
935  /**
936   * Mechanical Ratchet Wheel Tick for carousel and keyboard navigation
937   */
938  playWheelTick() {
939    try {
940      this.initContext();
941      if (!this.ctx || !this.sfxGain || this.isMuted) return;
942      const t = this.ctx.currentTime;
943      const osc = this.ctx.createOscillator();
944      const gain = this.ctx.createGain();
945      osc.type = "triangle";
946      osc.frequency.setValueAtTime(360, t);
947      osc.frequency.exponentialRampToValueAtTime(180, t + 0.025);
948      gain.gain.setValueAtTime(0.18, t);
949      gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.03);
950      osc.connect(gain);
951      gain.connect(this.sfxGain);
952      osc.start(t);
953      osc.stop(t + 0.035);
954    } catch (e) {
955    }
956  }
957  duckBGM(ducked) {
958    if (!this.bgmGain || !this.ctx) return;
959    const target = ducked ? Math.min(0.03, this.bgmVolume * 0.12) : this.bgmVolume;
960    const t = this.ctx.currentTime;
961    this.bgmGain.gain.cancelScheduledValues(t);
962    this.bgmGain.gain.setValueAtTime(this.bgmGain.gain.value, t);
963    this.bgmGain.gain.linearRampToValueAtTime(target, t + 0.3);
964  }
965  isMuteActive() {
966    return this.isMuted;
967  }
968}
969const arcadeAudio = new ArcadeAudioEngine();
970const ArcadeSettingsModal = ({
971  isOpen,
972  onClose,
973  theme,
974  themeMode,
975  onSelectTheme,
976  enableScanlines,
977  setEnableScanlines,
978  isBgmActive,
979  onToggleBgm,
980  isMuted,
981  onToggleMute,
982  bgmVol,
983  onVolumeChange
984}) => {
985  const { t } = useTranslation();
986  const modalRef = reactExports.useRef(null);
987  reactExports.useEffect(() => {
988    if (!isOpen) return;
989    const handleOutsideClick = (e) => {
990      if (modalRef.current && !modalRef.current.contains(e.target)) {
991        onClose();
992      }
993    };
994    const handleKeyDown = (e) => {
995      if (e.key === "Escape") {
996        e.stopPropagation();
997        onClose();
998      }
999    };
1000    document.addEventListener("mousedown", handleOutsideClick);
1001    window.addEventListener("keydown", handleKeyDown);
1002    return () => {
1003      document.removeEventListener("mousedown", handleOutsideClick);
1004      window.removeEventListener("keydown", handleKeyDown);
1005    };
1006  }, [isOpen, onClose]);
1007  if (!isOpen) return null;
1008  const THEME_OPTIONS = [
1009    { id: "random", name: "RANDOM", subtitle: "Auto-Cycle Styles", color: "#ec4899" },
1010    { id: "5thmix", name: "CYBER 5thMIX", subtitle: "DDR 5thMIX / 4thMIX", color: "#00f0ff" },
1011    { id: "max2", name: "MATRIX TECH", subtitle: "DDR MAX2 Cyber", color: "#22c55e" },
1012    { id: "sunset", name: "SUNSET RETRO", subtitle: "Outrun 80s Synthwave", color: "#f97316" }
1013  ];
1014  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1015    "div",
1016    {
1017      ref: modalRef,
1018      className: "absolute right-0 top-full mt-2 w-80 sm:w-96 rounded-2xl bg-[#090d19]/95 backdrop-blur-xl border-2 shadow-[0_20px_60px_rgba(0,0,0,0.9)] z-50 p-4 sm:p-5 font-mono text-white animate-in fade-in zoom-in-95 duration-150 select-none",
1019      style: { borderColor: `${theme.primary}88`, boxShadow: `0 10px 40px -10px ${theme.primary}33` },
1020      onClick: (e) => e.stopPropagation(),
1021      children: [
1022        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pb-3 border-b border-white/10 mb-4", children: [
1023          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1024            /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 15, style: { color: theme.primary } }),
1025            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-extrabold text-xs tracking-wider uppercase text-white", children: t("gameCenter.settings", "ARCADE SETTINGS") })
1026          ] }),
1027          /* @__PURE__ */ jsxRuntimeExports.jsx(
1028            "button",
1029            {
1030              onClick: () => {
1031                arcadeAudio.playSelect();
1032                onClose();
1033              },
1034              className: "p-1 rounded-lg text-zinc-400 hover:text-white hover:bg-white/10 transition-colors cursor-pointer",
1035              title: "Close Settings [ESC]",
1036              children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 15 })
1037            }
1038          )
1039        ] }),
1040        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
1041          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2.5", children: [
1042            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] font-black uppercase tracking-wider text-zinc-400 flex items-center gap-1.5", children: [
1043              /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 11, style: { color: theme.primary } }),
1044              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.audioSettings", "AUDIO & BGM") })
1045            ] }),
1046            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-2.5 rounded-xl bg-zinc-900/60 border border-white/5", children: [
1047              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
1048                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold text-zinc-200", children: t("gameCenter.arcadeBgm", "Arcade BGM") }),
1049                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500", children: "16-Bit Chiptune Synth" })
1050              ] }),
1051              /* @__PURE__ */ jsxRuntimeExports.jsx(
1052                "button",
1053                {
1054                  type: "button",
1055                  onClick: () => {
1056                    arcadeAudio.playSelect();
1057                    onToggleBgm();
1058                  },
1059                  className: `px-3 py-1 rounded-lg text-xs font-black transition-all cursor-pointer border ${isBgmActive && !isMuted ? "bg-cyan-500/20 text-cyan-300 border-cyan-400/60 shadow-[0_0_12px_rgba(6,182,212,0.3)]" : "bg-zinc-800 text-zinc-400 border-white/10 hover:text-white"}`,
1060                  children: isBgmActive && !isMuted ? "ON" : "OFF"
1061                }
1062              )
1063            ] }),
1064            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2.5 rounded-xl bg-zinc-900/60 border border-white/5 space-y-2", children: [
1065              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
1066                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-300 font-bold", children: t("gameCenter.bgmVolume", "BGM Volume") }),
1067                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-cyan-400 font-black", children: [
1068                  Math.round(bgmVol),
1069                  "%"
1070                ] })
1071              ] }),
1072              /* @__PURE__ */ jsxRuntimeExports.jsx(
1073                "input",
1074                {
1075                  type: "range",
1076                  min: "0",
1077                  max: "100",
1078                  value: bgmVol,
1079                  onChange: (e) => onVolumeChange(Number(e.target.value)),
1080                  className: "w-full h-1.5 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-cyan-400"
1081                }
1082              )
1083            ] }),
1084            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-2.5 rounded-xl bg-zinc-900/60 border border-white/5", children: [
1085              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1086                isMuted ? /* @__PURE__ */ jsxRuntimeExports.jsx(VolumeX, { size: 14, className: "text-red-400" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 14, className: "text-emerald-400" }),
1087                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
1088                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold text-zinc-200", children: t("gameCenter.masterMute", "Master Sound") }),
1089                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500", children: "SFX & Game Audio" })
1090                ] })
1091              ] }),
1092              /* @__PURE__ */ jsxRuntimeExports.jsx(
1093                "button",
1094                {
1095                  type: "button",
1096                  onClick: () => {
1097                    arcadeAudio.playSelect();
1098                    onToggleMute();
1099                  },
1100                  className: `px-3 py-1 rounded-lg text-xs font-black transition-all cursor-pointer border ${isMuted ? "bg-red-950/70 text-red-300 border-red-500/60 shadow-[0_0_12px_rgba(239,68,68,0.3)]" : "bg-emerald-950/60 text-emerald-300 border-emerald-500/50"}`,
1101                  children: isMuted ? "MUTED" : "ACTIVE"
1102                }
1103              )
1104            ] })
1105          ] }),
1106          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2.5 pt-1 border-t border-white/10", children: [
1107            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
1108              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] font-black uppercase tracking-wider text-zinc-400 flex items-center gap-1.5", children: [
1109                /* @__PURE__ */ jsxRuntimeExports.jsx(Palette, { size: 11, style: { color: theme.primary } }),
1110                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.cabinetStyle", "CABINET STYLE") })
1111              ] }),
1112              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] px-1.5 py-0.5 rounded bg-zinc-800 text-zinc-400 border border-white/10 font-bold", children: "Shortcut: [T]" })
1113            ] }),
1114            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 gap-1.5", children: THEME_OPTIONS.map((opt) => {
1115              const isSelected = themeMode === opt.id;
1116              return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1117                "button",
1118                {
1119                  type: "button",
1120                  onClick: () => {
1121                    arcadeAudio.playWheelTick();
1122                    onSelectTheme(opt.id);
1123                  },
1124                  className: `p-2 rounded-xl text-left transition-all border flex flex-col justify-between cursor-pointer ${isSelected ? "bg-zinc-800/90 border-2 shadow-sm" : "bg-zinc-900/50 border-white/10 hover:bg-zinc-800/60 hover:border-white/20"}`,
1125                  style: isSelected ? { borderColor: opt.color } : {},
1126                  children: [
1127                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
1128                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
1129                        opt.id === "random" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Dices, { size: 12, className: "text-pink-400 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 12, style: { color: opt.color } }),
1130                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1131                          "span",
1132                          {
1133                            className: "text-[11px] font-black tracking-tight",
1134                            style: isSelected ? { color: opt.color } : { color: "#e4e4e7" },
1135                            children: opt.name
1136                          }
1137                        )
1138                      ] }),
1139                      isSelected && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 12, style: { color: opt.color } })
1140                    ] }),
1141                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-zinc-500 font-normal leading-tight", children: opt.subtitle })
1142                  ]
1143                },
1144                opt.id
1145              );
1146            }) })
1147          ] }),
1148          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 pt-1 border-t border-white/10", children: [
1149            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] font-black uppercase tracking-wider text-zinc-400 flex items-center gap-1.5", children: [
1150              /* @__PURE__ */ jsxRuntimeExports.jsx(Tv, { size: 11, style: { color: theme.primary } }),
1151              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.displayFilters", "DISPLAY FILTERS") })
1152            ] }),
1153            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-2.5 rounded-xl bg-zinc-900/60 border border-white/5", children: [
1154              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
1155                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold text-zinc-200", children: t("gameCenter.crtScanlines", "CRT Scanlines") }),
1156                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500", children: "Phosphor Raster Overlay" })
1157              ] }),
1158              /* @__PURE__ */ jsxRuntimeExports.jsx(
1159                "button",
1160                {
1161                  type: "button",
1162                  onClick: () => {
1163                    arcadeAudio.playSelect();
1164                    setEnableScanlines(!enableScanlines);
1165                  },
1166                  className: `px-3 py-1 rounded-lg text-xs font-black transition-all cursor-pointer border ${enableScanlines ? "bg-purple-950/70 text-purple-300 border-purple-500/60 shadow-[0_0_12px_rgba(168,85,247,0.3)]" : "bg-zinc-800 text-zinc-400 border-white/10 hover:text-white"}`,
1167                  children: enableScanlines ? "ON" : "OFF"
1168                }
1169              )
1170            ] })
1171          ] })
1172        ] }),
1173        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 pt-2.5 border-t border-white/10 flex items-center justify-between text-[9px] text-zinc-500", children: [
1174          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "[T] Cycle Style • [M] Mute" }),
1175          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "[ESC] Close" })
1176        ] })
1177      ]
1178    }
1179  );
1180};
1181const GAME_MODULES = [
1182  {
1183    id: "piano-roll",
1184    name: "Piano Roll Rush",
1185    japaneseName: "ピアノ・ロール・ラッシュ",
1186    tagline: "High-speed falling note keyboard action",
1187    description: "Test your reflexes with falling notes! Connect your MIDI keyboard or jam on your computer keys. Features accurate timing judgements, combo streaks, and soundfont synthesis.",
1188    badge: "RHYTHM SYNTH",
1189    icon: "Keyboard",
1190    themeColor: {
1191      primary: "#06b6d4",
1192      // cyan-500
1193      accent: "#8b5cf6",
1194      // purple-500
1195      glow: "rgba(6, 182, 212, 0.4)",
1196      gradient: "from-cyan-500 via-indigo-500 to-purple-600",
1197      border: "border-cyan-500/50"
1198    },
1199    cabinetStyle: {
1200      headerBg: "bg-gradient-to-r from-cyan-950 via-indigo-950 to-purple-950",
1201      marqueeText: "HAMIE KEYS",
1202      bezelColor: "#0891b2"
1203    },
1204    supportedInputs: ["keyboard", "midi_device", "mouse", "touch"],
1205    minPlayers: 1,
1206    maxPlayers: 1,
1207    enabled: true,
1208    defaultSettings: {
1209      speed: 1.2,
1210      playMelodyTrackOnly: false
1211    }
1212  },
1213  {
1214    id: "taiko",
1215    name: "Taiko no Hamie",
1216    japaneseName: "太鼓のハミー",
1217    tagline: "Authentic Japanese festival drumming excitement",
1218    description: "Feel the rhythm of the festival! Hit the center (Don) and the rim (Ka) in sync with the music. Features 1 to 4 local players on the same keyboard, Hamie mascot animations, and authentic drum synthesis.",
1219    badge: "ARCADE DRUM",
1220    icon: "Drum",
1221    themeColor: {
1222      primary: "#f43f5e",
1223      // rose-500
1224      accent: "#f59e0b",
1225      // amber-500
1226      glow: "rgba(244, 63, 94, 0.4)",
1227      gradient: "from-rose-500 via-orange-500 to-amber-500",
1228      border: "border-rose-500/50"
1229    },
1230    cabinetStyle: {
1231      headerBg: "bg-gradient-to-r from-red-950 via-rose-950 to-amber-950",
1232      marqueeText: "TAIKO NO HAMIE",
1233      bezelColor: "#e11d48"
1234    },
1235    supportedInputs: ["keyboard", "touch"],
1236    minPlayers: 1,
1237    maxPlayers: 4,
1238    enabled: true,
1239    defaultSettings: {
1240      difficulty: "futsuu"
1241      // kantan, futsuu, muzukashii, oni
1242    }
1243  },
1244  {
1245    id: "meow-choir",
1246    name: "Meow Choir",
1247    japaneseName: "にゃんこ合唱団",
1248    tagline: "Kitten stage chorus rhythm adventure",
1249    description: "Lead the kitten chorus around the center stage microphone! Pop musical note bubbles over the piano keys to harmonize sweet meows in perfect tune.",
1250    badge: "CHOIR",
1251    icon: "Cat",
1252    themeColor: {
1253      primary: "#a855f7",
1254      // purple-500
1255      accent: "#f43f5e",
1256      // rose-500
1257      glow: "rgba(168, 85, 247, 0.4)",
1258      gradient: "from-purple-500 via-pink-500 to-rose-400",
1259      border: "border-purple-500/50"
1260    },
1261    cabinetStyle: {
1262      headerBg: "bg-gradient-to-r from-indigo-950 via-purple-950 to-pink-950",
1263      marqueeText: "MEOW CHOIR",
1264      bezelColor: "#9333ea"
1265    },
1266    supportedInputs: ["keyboard", "touch", "mouse"],
1267    minPlayers: 1,
1268    maxPlayers: 1,
1269    enabled: true,
1270    defaultSettings: {
1271      difficulty: "playful",
1272      speed: 1.15
1273    }
1274  },
1275  {
1276    id: "rhythm-test",
1277    name: "Rhythm Test",
1278    japaneseName: "リズムテスト",
1279    tagline: "Measure beat pattern assessment & training",
1280    description: "Test your rhythm sense with 4x4 measure grids! Tap along to beat patterns with keyboard, fingers, or clap into your microphone with Bongo Cat.",
1281    badge: "RHYTHM TEST",
1282    icon: "Boxes",
1283    themeColor: {
1284      primary: "#9333ea",
1285      // purple-600
1286      accent: "#ea580c",
1287      // orange-600
1288      glow: "rgba(147, 51, 234, 0.4)",
1289      gradient: "from-purple-600 via-pink-600 to-amber-500",
1290      border: "border-purple-500/50"
1291    },
1292    cabinetStyle: {
1293      headerBg: "bg-gradient-to-r from-purple-950 via-indigo-950 to-stone-950",
1294      marqueeText: "RHYTHM TEST",
1295      bezelColor: "#7e22ce"
1296    },
1297    supportedInputs: ["keyboard", "touch", "microphone"],
1298    minPlayers: 1,
1299    maxPlayers: 1,
1300    enabled: true,
1301    defaultSettings: {
1302      mode: "play-along",
1303      difficulty: "medium",
1304      soundType: "bongo"
1305    }
1306  }
1307];
1308const ArcadeMarqueeHeader = ({
1309  stage,
1310  selectedSong,
1311  selectedModule,
1312  onGoToStage,
1313  onSelectModule,
1314  onExit,
1315  enableScanlines,
1316  setEnableScanlines,
1317  theme,
1318  themeMode,
1319  onCycleTheme,
1320  onSelectTheme = () => {
1321  },
1322  onSelectMusic
1323}) => {
1324  const { t } = useTranslation();
1325  const [isBgmActive, setIsBgmActive] = reactExports.useState(false);
1326  const [isMuted, setIsMuted] = reactExports.useState(arcadeAudio.isMuteActive());
1327  const [bgmVol, setBgmVol] = reactExports.useState(arcadeAudio.getBgmVolume() * 100);
1328  const [showSettings, setShowSettings] = reactExports.useState(false);
1329  reactExports.useEffect(() => {
1330    const handleFirstInteraction = (e) => {
1331      const target = e.target;
1332      if (target && typeof target.closest === "function" && target.closest("[data-exit-arcade]")) {
1333        return;
1334      }
1335      if (!arcadeAudio.isPlaying()) {
1336        arcadeAudio.startBgm();
1337        setIsBgmActive(true);
1338      }
1339      window.removeEventListener("click", handleFirstInteraction);
1340      window.removeEventListener("keydown", handleFirstInteraction);
1341    };
1342    window.addEventListener("click", handleFirstInteraction);
1343    window.addEventListener("keydown", handleFirstInteraction);
1344    return () => {
1345      window.removeEventListener("click", handleFirstInteraction);
1346      window.removeEventListener("keydown", handleFirstInteraction);
1347      arcadeAudio.stopBgm();
1348    };
1349  }, []);
1350  reactExports.useEffect(() => {
1351    const handleGlobalShortcuts = (e) => {
1352      var _a;
1353      const targetTag = (_a = e.target) == null ? void 0 : _a.tagName;
1354      if (targetTag === "INPUT" || targetTag === "TEXTAREA") return;
1355      if (e.key === "m" || e.key === "M") {
1356        const nextMute = arcadeAudio.toggleMute();
1357        setIsMuted(nextMute);
1358      }
1359    };
1360    window.addEventListener("keydown", handleGlobalShortcuts);
1361    return () => window.removeEventListener("keydown", handleGlobalShortcuts);
1362  }, []);
1363  const toggleBgm = () => {
1364    arcadeAudio.playSelect();
1365    if (arcadeAudio.isPlaying()) {
1366      arcadeAudio.stopBgm();
1367      setIsBgmActive(false);
1368    } else {
1369      arcadeAudio.startBgm();
1370      setIsBgmActive(true);
1371    }
1372  };
1373  const toggleMute = () => {
1374    arcadeAudio.playSelect();
1375    const nextMute = arcadeAudio.toggleMute();
1376    setIsMuted(nextMute);
1377  };
1378  const handleVolumeChange = (val) => {
1379    setBgmVol(val);
1380    arcadeAudio.setBgmVolume(val / 100);
1381  };
1382  return /* @__PURE__ */ jsxRuntimeExports.jsxs("header", { className: `relative z-30 w-full bg-gradient-to-r ${theme.marqueeGradient} bg-[#070b16] border-b-2 ${theme.marqueeBorder} shadow-lg transition-all duration-200`, children: [
1383    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1.5 w-full bg-gradient-to-r from-pink-500 via-yellow-400 via-cyan-400 to-pink-500 animate-pulse" }),
1384    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `max-w-7xl mx-auto px-3 sm:px-4 flex items-center justify-between gap-2 sm:gap-4 transition-all ${stage === "ARENA" ? "py-1.5" : "py-2 sm:py-2.5"}`, children: [
1385      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
1386        stage === "SONG" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1387          "button",
1388          {
1389            "data-exit-arcade": "true",
1390            type: "button",
1391            onClick: (e) => {
1392              e.stopPropagation();
1393              arcadeAudio.stopBgm();
1394              arcadeAudio.playCoin();
1395              onExit();
1396            },
1397            className: "group flex items-center gap-1.5 sm:gap-2 px-2.5 sm:px-3 py-1.5 rounded-lg bg-red-950/60 hover:bg-red-900/80 border border-red-500/40 hover:border-red-400 text-red-300 hover:text-white font-mono text-xs font-bold transition-all shadow-[0_0_15px_rgba(239,68,68,0.2)] active:scale-95 cursor-pointer",
1398            title: "Return to standard HamieNET website",
1399            children: [
1400              /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 14, className: "group-hover:-translate-x-0.5 transition-transform text-red-400" }),
1401              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tracking-wider uppercase", children: t("gameCenter.exitArcade", "EXIT ARCADE") })
1402            ]
1403          }
1404        ),
1405        stage === "CABINET" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
1406          /* @__PURE__ */ jsxRuntimeExports.jsxs(
1407            "button",
1408            {
1409              type: "button",
1410              onClick: () => {
1411                arcadeAudio.playSelect();
1412                if (onGoToStage) onGoToStage("SONG");
1413                else if (onSelectMusic) onSelectMusic();
1414              },
1415              className: "group flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-cyan-950/70 hover:bg-cyan-900/90 border border-cyan-500/40 hover:border-cyan-400 text-cyan-300 hover:text-white font-mono text-xs font-bold transition-all shadow-[0_0_15px_rgba(6,182,212,0.25)] active:scale-95 cursor-pointer",
1416              title: "Return to music selector [ESC]",
1417              children: [
1418                /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 14, className: "group-hover:-translate-x-0.5 transition-transform text-cyan-400" }),
1419                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tracking-wider uppercase font-black", children: t("gameCenter.musicSelect", "MUSIC SELECT") }),
1420                /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "hidden sm:inline text-[9px] px-1 py-0.5 rounded bg-cyan-950 border border-cyan-800 text-cyan-300 font-mono", children: "ESC" })
1421              ]
1422            }
1423          ),
1424          /* @__PURE__ */ jsxRuntimeExports.jsx(
1425            "button",
1426            {
1427              "data-exit-arcade": "true",
1428              type: "button",
1429              onClick: (e) => {
1430                e.stopPropagation();
1431                arcadeAudio.stopBgm();
1432                arcadeAudio.playCoin();
1433                onExit();
1434              },
1435              className: "p-1.5 rounded-lg bg-slate-900/60 hover:bg-red-950/60 border border-slate-700/60 hover:border-red-500/40 text-slate-400 hover:text-red-300 transition-all cursor-pointer",
1436              title: "Exit Arcade to Main Site",
1437              children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 13 })
1438            }
1439          )
1440        ] }),
1441        stage === "ARENA" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
1442          /* @__PURE__ */ jsxRuntimeExports.jsxs(
1443            "button",
1444            {
1445              type: "button",
1446              onClick: () => {
1447                arcadeAudio.playSelect();
1448                if (onGoToStage) onGoToStage("CABINET");
1449              },
1450              className: "group flex items-center gap-1 px-2.5 py-1 rounded-lg bg-amber-950/70 hover:bg-amber-900/90 border border-amber-500/40 hover:border-amber-400 text-amber-300 hover:text-white font-mono text-[11px] font-bold transition-all shadow-[0_0_12px_rgba(245,158,11,0.25)] active:scale-95 cursor-pointer",
1451              title: "Return to Cabinet Selection [ESC]",
1452              children: [
1453                /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 12, className: "group-hover:-translate-x-0.5 transition-transform text-amber-400" }),
1454                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tracking-wider uppercase font-black", children: t("gameCenter.cabinets", "CABINETS") })
1455              ]
1456            }
1457          ),
1458          /* @__PURE__ */ jsxRuntimeExports.jsxs(
1459            "button",
1460            {
1461              type: "button",
1462              onClick: () => {
1463                arcadeAudio.playSelect();
1464                if (onGoToStage) onGoToStage("SONG");
1465                else if (onSelectMusic) onSelectMusic();
1466              },
1467              className: "flex items-center gap-1 px-2 py-1 rounded-lg bg-slate-900/80 hover:bg-slate-800 border border-slate-700 hover:border-cyan-400 text-slate-300 hover:text-cyan-300 font-mono text-[11px] font-bold transition-all active:scale-95 cursor-pointer",
1468              title: "Return to Song Selection",
1469              children: [
1470                /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 12, className: "text-cyan-400" }),
1471                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden sm:inline", children: "SONGS" })
1472              ]
1473            }
1474          )
1475        ] })
1476      ] }),
1477      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center select-none flex-1 min-w-0 px-1 sm:px-2", children: [
1478        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 sm:gap-2 text-[10px] font-mono font-bold tracking-wider mb-1", children: [
1479          /* @__PURE__ */ jsxRuntimeExports.jsx(
1480            "button",
1481            {
1482              type: "button",
1483              onClick: () => {
1484                arcadeAudio.playSelect();
1485                if (onGoToStage) onGoToStage("SONG");
1486                else if (onSelectMusic) onSelectMusic();
1487              },
1488              className: `px-2 sm:px-2.5 py-0.5 rounded-full flex items-center gap-1 transition-all cursor-pointer ${stage === "SONG" ? "bg-cyan-500 text-slate-950 font-black shadow-[0_0_12px_rgba(6,182,212,0.6)]" : "bg-slate-900/90 text-cyan-300 hover:text-white border border-cyan-800/60 hover:border-cyan-400 active:scale-95"}`,
1489              title: "1. Music Selection [Click to Browse Songs]",
1490              children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "1. MUSIC" })
1491            }
1492          ),
1493          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-600 text-[10px]", children: "▶" }),
1494          /* @__PURE__ */ jsxRuntimeExports.jsx(
1495            "button",
1496            {
1497              type: "button",
1498              onClick: () => {
1499                if (selectedSong && onGoToStage) {
1500                  arcadeAudio.playSelect();
1501                  onGoToStage("CABINET");
1502                }
1503              },
1504              disabled: !selectedSong,
1505              className: `px-2 sm:px-2.5 py-0.5 rounded-full flex items-center gap-1 transition-all ${stage === "CABINET" ? "bg-amber-400 text-slate-950 font-black shadow-[0_0_12px_rgba(245,158,11,0.6)] cursor-pointer" : selectedSong ? "bg-slate-900/90 text-amber-300 hover:text-white border border-amber-800/60 hover:border-amber-400 cursor-pointer active:scale-95" : "bg-slate-950/40 text-zinc-600 border border-zinc-800/50 cursor-not-allowed opacity-50"}`,
1506              title: selectedSong ? "2. Cabinet Selection [Click to Choose Game Mode]" : "Pick a song first",
1507              children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "2. CABINET" })
1508            }
1509          ),
1510          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-600 text-[10px]", children: "▶" }),
1511          /* @__PURE__ */ jsxRuntimeExports.jsx(
1512            "button",
1513            {
1514              type: "button",
1515              onClick: () => {
1516                if (selectedSong && onGoToStage) {
1517                  arcadeAudio.playStageStart();
1518                  onGoToStage("ARENA");
1519                }
1520              },
1521              disabled: !selectedSong,
1522              className: `px-2 sm:px-2.5 py-0.5 rounded-full flex items-center gap-1 transition-all ${stage === "ARENA" ? "bg-rose-500 text-white font-black shadow-[0_0_12px_rgba(244,63,94,0.6)] cursor-pointer animate-pulse" : selectedSong ? "bg-slate-900/90 text-rose-300 hover:text-white border border-rose-800/60 hover:border-rose-400 cursor-pointer active:scale-95" : "bg-slate-950/40 text-zinc-600 border border-zinc-800/50 cursor-not-allowed opacity-50"}`,
1523              title: selectedSong ? "3. Game Arena [Enter Stage]" : "Pick a song first",
1524              children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "3. ARENA" })
1525            }
1526          )
1527        ] }),
1528        stage === "SONG" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1529          "button",
1530          {
1531            type: "button",
1532            onClick: () => {
1533              if (onSelectMusic) {
1534                arcadeAudio.playSelect();
1535                onSelectMusic();
1536              }
1537            },
1538            className: "flex flex-col items-center cursor-pointer group select-none transition-transform active:scale-95 focus:outline-none",
1539            title: "Music Selection",
1540            children: [
1541              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `${theme.selectMusicBannerClass} flex items-center justify-center gap-2 uppercase group-hover:brightness-110 transition-all`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: theme.selectMusicTitle }) }),
1542              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono tracking-widest uppercase font-extrabold mt-0.5 group-hover:text-white transition-colors", style: { color: `${theme.primary}cc` }, children: theme.subtitle })
1543            ]
1544          }
1545        ),
1546        stage === "CABINET" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
1547          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-0.5 rounded-full font-black text-xs tracking-widest uppercase bg-gradient-to-r from-amber-400 via-orange-400 to-amber-500 text-slate-950 shadow-[0_0_15px_rgba(245,158,11,0.5)] border border-yellow-200", children: "// CHOOSE GAME CABINET //" }),
1548          selectedSong && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1549            "button",
1550            {
1551              type: "button",
1552              onClick: () => {
1553                arcadeAudio.playSelect();
1554                if (onGoToStage) onGoToStage("SONG");
1555                else if (onSelectMusic) onSelectMusic();
1556              },
1557              className: "flex items-center gap-1.5 mt-1 text-[10px] font-mono text-cyan-300 hover:text-white bg-slate-900/80 px-2.5 py-0.5 rounded-full border border-cyan-500/40 hover:border-cyan-400 transition-all cursor-pointer shadow-sm active:scale-95",
1558              title: "Click to change selected song [ESC]",
1559              children: [
1560                /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 11, className: "text-cyan-400 animate-spin" }),
1561                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold max-w-[200px] truncate", children: selectedSong.title }),
1562                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-400", children: [
1563                  "• ",
1564                  selectedSong.bpm,
1565                  " BPM"
1566                ] }),
1567                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-amber-400 ml-1 font-black", children: "[CHANGE SONG]" })
1568              ]
1569            }
1570          )
1571        ] }),
1572        stage === "ARENA" && selectedModule && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1573          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1 p-0.5 rounded-xl bg-slate-950/90 border border-slate-800 shadow-inner", children: GAME_MODULES.map((mod) => {
1574            const isActiveMod = mod.id === selectedModule.id;
1575            return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1576              "button",
1577              {
1578                type: "button",
1579                onClick: () => onSelectModule && onSelectModule(mod),
1580                className: `px-2 sm:px-2.5 py-1 rounded-lg text-[10px] font-black tracking-wider uppercase transition-all flex items-center gap-1 cursor-pointer active:scale-95 ${isActiveMod ? mod.id === "piano-roll" ? "bg-cyan-500 text-slate-950 shadow-[0_0_12px_rgba(6,182,212,0.6)] font-extrabold" : mod.id === "taiko" ? "bg-rose-500 text-white shadow-[0_0_12px_rgba(244,63,94,0.6)] font-extrabold" : mod.id === "meow-choir" ? "bg-purple-500 text-slate-950 shadow-[0_0_12px_rgba(168,85,247,0.6)] font-extrabold" : "bg-amber-500 text-slate-950 shadow-[0_0_12px_rgba(245,158,11,0.6)] font-extrabold" : "text-slate-400 hover:text-white hover:bg-slate-800/60"}`,
1581                title: `Quick-switch to ${mod.name}`,
1582                children: [
1583                  mod.id === "piano-roll" && /* @__PURE__ */ jsxRuntimeExports.jsx(Keyboard, { size: 12 }),
1584                  mod.id === "taiko" && /* @__PURE__ */ jsxRuntimeExports.jsx(Drum, { size: 12 }),
1585                  mod.id === "meow-choir" && /* @__PURE__ */ jsxRuntimeExports.jsx(PawPrint, { size: 12 }),
1586                  mod.id === "rhythm-test" && /* @__PURE__ */ jsxRuntimeExports.jsx(Boxes, { size: 12 }),
1587                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden md:inline", children: mod.name })
1588                ]
1589              },
1590              mod.id
1591            );
1592          }) }),
1593          selectedSong && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden lg:flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-slate-900/90 border border-cyan-500/30 text-[10px] font-mono text-cyan-300", children: [
1594            /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 11, className: "text-cyan-400 animate-spin" }),
1595            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold max-w-[140px] truncate", children: selectedSong.title }),
1596            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-500", children: [
1597              "• ",
1598              selectedSong.bpm,
1599              " BPM"
1600            ] })
1601          ] })
1602        ] })
1603      ] }),
1604      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex items-center gap-1.5 sm:gap-2 shrink-0", children: [
1605        /* @__PURE__ */ jsxRuntimeExports.jsxs(
1606          "button",
1607          {
1608            type: "button",
1609            onClick: (e) => {
1610              e.stopPropagation();
1611              toggleBgm();
1612            },
1613            className: `flex items-center gap-1 sm:gap-1.5 px-2 sm:px-3 py-1 sm:py-1.5 rounded-xl border font-mono text-xs font-black transition-all cursor-pointer select-none active:scale-95 ${isBgmActive && !isMuted ? "bg-cyan-950/80 border-cyan-400 text-cyan-300 shadow-[0_0_12px_rgba(6,182,212,0.4)]" : "bg-slate-900/70 hover:bg-slate-800 border-zinc-700/60 text-zinc-400 hover:text-zinc-200"}`,
1614            title: isBgmActive && !isMuted ? "Mute Arcade Background Music [M]" : "Play Arcade Background Music [M]",
1615            children: [
1616              isBgmActive && !isMuted ? /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 13, className: "text-cyan-400 animate-pulse" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(VolumeX, { size: 13, className: "text-zinc-500" }),
1617              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tracking-wider text-[10px] sm:text-[11px] hidden sm:inline", children: isBgmActive && !isMuted ? "BGM ON" : "BGM OFF" })
1618            ]
1619          }
1620        ),
1621        /* @__PURE__ */ jsxRuntimeExports.jsxs(
1622          "button",
1623          {
1624            type: "button",
1625            onClick: (e) => {
1626              e.stopPropagation();
1627              arcadeAudio.playSelect();
1628              setShowSettings(!showSettings);
1629            },
1630            className: `flex items-center gap-1 sm:gap-1.5 px-2 sm:px-3 py-1 sm:py-1.5 rounded-xl border font-mono text-xs font-black transition-all cursor-pointer select-none active:scale-95 ${showSettings ? "bg-zinc-800 text-white shadow-lg" : "bg-slate-900/70 hover:bg-slate-800 text-zinc-300 hover:text-white"}`,
1631            style: { borderColor: `${theme.primary}aa`, boxShadow: showSettings ? `0 0 15px ${theme.primary}44` : void 0 },
1632            title: "Arcade Settings [BGM, Style, Volume]",
1633            children: [
1634              /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 13, style: { color: theme.primary } }),
1635              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tracking-wider text-[10px] sm:text-[11px] hidden sm:inline", children: t("gameCenter.settings", "SETTINGS") })
1636            ]
1637          }
1638        ),
1639        /* @__PURE__ */ jsxRuntimeExports.jsx(
1640          ArcadeSettingsModal,
1641          {
1642            isOpen: showSettings,
1643            onClose: () => setShowSettings(false),
1644            theme,
1645            themeMode,
1646            onSelectTheme,
1647            enableScanlines,
1648            setEnableScanlines,
1649            isBgmActive,
1650            onToggleBgm: toggleBgm,
1651            isMuted,
1652            onToggleMute: toggleMute,
1653            bgmVol,
1654            onVolumeChange: handleVolumeChange
1655          }
1656        )
1657      ] })
1658    ] })
1659  ] });
1660};
1661const ArcadeGrooveRadar = ({
1662  stats,
1663  theme,
1664  size = 240,
1665  showLabels = true
1666}) => {
1667  const axes = reactExports.useMemo(() => [
1668    { key: "stream", ja: "全体密度", en: "STREAM", value: stats.stream, angle: -Math.PI / 2 },
1669    { key: "chaos", ja: "変則度", en: "CHAOS", value: stats.chaos, angle: -Math.PI / 2 + 2 * Math.PI / 5 },
1670    { key: "freeze", ja: "踏みっぱ度", en: "FREEZE", value: stats.freeze, angle: -Math.PI / 2 + 4 * Math.PI / 5 },
1671    { key: "air", ja: "ジャンプ度", en: "AIR", value: stats.air, angle: -Math.PI / 2 + 6 * Math.PI / 5 },
1672    { key: "voltage", ja: "最大密度", en: "VOLTAGE", value: stats.voltage, angle: -Math.PI / 2 + 8 * Math.PI / 5 }
1673  ], [stats]);
1674  const center = size / 2;
1675  const maxRadius = size / 2 * 0.68;
1676  const getPoint = (radius, angle) => {
1677    return {
1678      x: center + radius * Math.cos(angle),
1679      y: center + radius * Math.sin(angle)
1680    };
1681  };
1682  const getRingPath = (fraction) => {
1683    const points = axes.map((a) => getPoint(maxRadius * fraction, a.angle));
1684    return points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ") + " Z";
1685  };
1686  const polygonPath = reactExports.useMemo(() => {
1687    const points = axes.map((a) => {
1688      const r = Math.max(10, a.value / 100 * maxRadius);
1689      return getPoint(r, a.angle);
1690    });
1691    return points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ") + " Z";
1692  }, [axes, maxRadius]);
1693  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex flex-col items-center justify-center select-none", style: { width: size, height: size }, children: [
1694    /* @__PURE__ */ jsxRuntimeExports.jsxs(
1695      "div",
1696      {
1697        className: "absolute inset-1 rounded-full border border-zinc-800/80 bg-zinc-950/70 shadow-inner overflow-hidden pointer-events-none",
1698        style: {
1699          boxShadow: `inset 0 0 25px rgba(0,0,0,0.8), 0 0 15px ${theme.primary}18`
1700        },
1701        children: [
1702          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 opacity-20 bg-[radial-gradient(#ffffff_1px,transparent_1px)] [background-size:12px_12px]" }),
1703          /* @__PURE__ */ jsxRuntimeExports.jsx(
1704            "div",
1705            {
1706              className: "absolute inset-3 rounded-full border border-dashed opacity-25",
1707              style: { borderColor: theme.radarGridStroke }
1708            }
1709          )
1710        ]
1711      }
1712    ),
1713    /* @__PURE__ */ jsxRuntimeExports.jsxs(
1714      "svg",
1715      {
1716        width: size,
1717        height: size,
1718        viewBox: `0 0 ${size} ${size}`,
1719        className: "relative z-10 overflow-visible",
1720        children: [
1721          /* @__PURE__ */ jsxRuntimeExports.jsxs("defs", { children: [
1722            /* @__PURE__ */ jsxRuntimeExports.jsxs("radialGradient", { id: `radarGlow-${theme.id}`, cx: "50%", cy: "50%", r: "50%", children: [
1723              /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: theme.radarPolygonStroke, stopOpacity: "0.6" }),
1724              /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: theme.primary, stopOpacity: "0.15" })
1725            ] }),
1726            /* @__PURE__ */ jsxRuntimeExports.jsx("filter", { id: `polygonGlow-${theme.id}`, x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ jsxRuntimeExports.jsx("feDropShadow", { dx: "0", dy: "0", stdDeviation: "4", floodColor: theme.radarPolygonStroke, floodOpacity: "0.7" }) })
1727          ] }),
1728          [0.25, 0.5, 0.75, 1].map((frac, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(
1729            "path",
1730            {
1731              d: getRingPath(frac),
1732              fill: "none",
1733              stroke: theme.radarGridStroke,
1734              strokeWidth: i === 3 ? 1.5 : 0.75,
1735              strokeDasharray: i < 3 ? "2 2" : "none"
1736            },
1737            i
1738          )),
1739          axes.map((a, i) => {
1740            const p = getPoint(maxRadius, a.angle);
1741            return /* @__PURE__ */ jsxRuntimeExports.jsx(
1742              "line",
1743              {
1744                x1: center,
1745                y1: center,
1746                x2: p.x,
1747                y2: p.y,
1748                stroke: theme.radarGridStroke,
1749                strokeWidth: 0.8
1750              },
1751              i
1752            );
1753          }),
1754          /* @__PURE__ */ jsxRuntimeExports.jsx(
1755            "path",
1756            {
1757              d: polygonPath,
1758              fill: `url(#radarGlow-${theme.id})`,
1759              stroke: theme.radarPolygonStroke,
1760              strokeWidth: 2,
1761              filter: `url(#polygonGlow-${theme.id})`,
1762              className: "transition-all duration-500 ease-out"
1763            }
1764          ),
1765          axes.map((a, i) => {
1766            const r = Math.max(10, a.value / 100 * maxRadius);
1767            const p = getPoint(r, a.angle);
1768            return /* @__PURE__ */ jsxRuntimeExports.jsx(
1769              "circle",
1770              {
1771                cx: p.x,
1772                cy: p.y,
1773                r: 3,
1774                fill: theme.secondary,
1775                stroke: "#000",
1776                strokeWidth: 1,
1777                className: "transition-all duration-500 ease-out"
1778              },
1779              i
1780            );
1781          })
1782        ]
1783      }
1784    ),
1785    showLabels && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 pointer-events-none text-[9px] font-mono leading-tight", children: [
1786      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-1 left-1/2 -translate-x-1/2 text-center", children: [
1787        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold tracking-tight text-white/90 drop-shadow-[0_1px_2px_rgba(0,0,0,1)]", children: "全体密度" }),
1788        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-black tracking-widest text-[8px]", style: { color: theme.primary }, children: "STREAM" })
1789      ] }),
1790      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-7 left-1 text-left", children: [
1791        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold tracking-tight text-white/90 drop-shadow-[0_1px_2px_rgba(0,0,0,1)]", children: "最大密度" }),
1792        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-black tracking-widest text-[8px]", style: { color: theme.primary }, children: "VOLTAGE" })
1793      ] }),
1794      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-7 right-1 text-right", children: [
1795        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold tracking-tight text-white/90 drop-shadow-[0_1px_2px_rgba(0,0,0,1)]", children: "変則度" }),
1796        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-black tracking-widest text-[8px]", style: { color: theme.primary }, children: "CHAOS" })
1797      ] }),
1798      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-2 left-3 text-left", children: [
1799        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold tracking-tight text-white/90 drop-shadow-[0_1px_2px_rgba(0,0,0,1)]", children: "ジャンプ度" }),
1800        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-black tracking-widest text-[8px]", style: { color: theme.primary }, children: "AIR" })
1801      ] }),
1802      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-2 right-3 text-right", children: [
1803        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold tracking-tight text-white/90 drop-shadow-[0_1px_2px_rgba(0,0,0,1)]", children: "踏みっぱ度" }),
1804        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-black tracking-widest text-[8px]", style: { color: theme.primary }, children: "FREEZE" })
1805      ] })
1806    ] })
1807  ] });
1808};
1809const HamieFaceIcon = ({
1810  size = 16,
1811  active = true,
1812  color,
1813  emptyColor = "#3f3f46",
1814  variant = "don",
1815  className = "",
1816  title
1817}) => {
1818  if (!active) {
1819    return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1820      "svg",
1821      {
1822        width: size,
1823        height: size,
1824        viewBox: "0 0 32 32",
1825        fill: "none",
1826        xmlns: "http://www.w3.org/2000/svg",
1827        className: `shrink-0 opacity-40 ${className}`,
1828        children: [
1829          title && /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: title }),
1830          /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "4.5", fill: emptyColor }),
1831          /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "24", cy: "8", r: "4.5", fill: emptyColor }),
1832          /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "16", cy: "18", r: "11", fill: emptyColor })
1833        ]
1834      }
1835    );
1836  }
1837  const headFill = color || (variant === "don" ? "#ef4444" : variant === "ka" ? "#3b82f6" : variant === "gold" ? "#f59e0b" : "#06b6d4");
1838  const innerEarColor = variant === "ka" ? "#93c5fd" : "#fca5a5";
1839  const blushColor = variant === "ka" ? "rgba(56, 189, 248, 0.9)" : "rgba(244, 63, 94, 0.9)";
1840  const noseColor = variant === "ka" ? "#0284c7" : "#e11d48";
1841  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1842    "svg",
1843    {
1844      width: size,
1845      height: size,
1846      viewBox: "0 0 32 32",
1847      fill: "none",
1848      xmlns: "http://www.w3.org/2000/svg",
1849      className: `shrink-0 transition-transform drop-shadow-[0_0_5px_rgba(245,158,11,0.5)] ${className}`,
1850      children: [
1851        title && /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: title }),
1852        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "4.5", fill: headFill, stroke: "#ffffff", strokeWidth: "1.2" }),
1853        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "8", cy: "8", r: "2.4", fill: innerEarColor }),
1854        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "24", cy: "8", r: "4.5", fill: headFill, stroke: "#ffffff", strokeWidth: "1.2" }),
1855        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "24", cy: "8", r: "2.4", fill: innerEarColor }),
1856        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "16", cy: "18", r: "11", fill: headFill, stroke: "#ffffff", strokeWidth: "1.5" }),
1857        /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "16", cy: "20.5", rx: "7.5", ry: "4.5", fill: "#ffffff" }),
1858        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10.5", cy: "20", r: "2", fill: blushColor }),
1859        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "21.5", cy: "20", r: "2", fill: blushColor }),
1860        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "16", r: "1.8", fill: "#18181b" }),
1861        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "11.4", cy: "15.4", r: "0.65", fill: "#ffffff" }),
1862        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "20", cy: "16", r: "1.8", fill: "#18181b" }),
1863        /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "19.4", cy: "15.4", r: "0.65", fill: "#ffffff" }),
1864        /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "16", cy: "19.2", rx: "1.1", ry: "0.8", fill: noseColor }),
1865        /* @__PURE__ */ jsxRuntimeExports.jsx(
1866          "path",
1867          {
1868            d: "M 14 21 Q 15 22.2 16 21.2 Q 17 22.2 18 21",
1869            stroke: "#881337",
1870            strokeWidth: "0.9",
1871            strokeLinecap: "round",
1872            fill: "none"
1873          }
1874        )
1875      ]
1876    }
1877  );
1878};
1879const formatArcadeDuration = (val) => {
1880  if (val === null || val === void 0) return "0:00";
1881  if (typeof val === "string") {
1882    if (val.includes(":")) return val;
1883    const num = parseFloat(val);
1884    if (!isNaN(num) && isFinite(num) && num >= 0) {
1885      const mins = Math.floor(num / 60);
1886      const secs = Math.floor(num % 60);
1887      return `${mins}:${secs.toString().padStart(2, "0")}`;
1888    }
1889    return "0:00";
1890  }
1891  if (typeof val === "number") {
1892    if (isNaN(val) || !isFinite(val) || val < 0) return "0:00";
1893    const mins = Math.floor(val / 60);
1894    const secs = Math.floor(val % 60);
1895    return `${mins}:${secs.toString().padStart(2, "0")}`;
1896  }
1897  return "0:00";
1898};
1899const parseDurationSec = (val) => {
1900  if (typeof val === "number") {
1901    return !isNaN(val) && isFinite(val) && val >= 0 ? val : 180;
1902  }
1903  if (typeof val === "string") {
1904    if (val.includes(":")) {
1905      const parts = val.split(":").map(Number);
1906      if (parts.length === 2 && !isNaN(parts[0]) && !isNaN(parts[1])) {
1907        return parts[0] * 60 + parts[1];
1908      }
1909      if (parts.length === 3 && !isNaN(parts[0]) && !isNaN(parts[1]) && !isNaN(parts[2])) {
1910        return parts[0] * 3600 + parts[1] * 60 + parts[2];
1911      }
1912    }
1913    const num = parseFloat(val);
1914    return !isNaN(num) && isFinite(num) && num >= 0 ? num : 180;
1915  }
1916  return 180;
1917};
1918const CATEGORIES = [
1919  { key: "all", label: "ALL SONGS" },
1920  { key: "anime", label: "ANIME & GAME" },
1921  { key: "classical", label: "CLASSICAL SHREDS" },
1922  { key: "pop", label: "POP / ROCK" },
1923  { key: "fast", label: "HIGH SPEED (140+)" }
1924];
1925const ArcadeSongSelector = ({
1926  currentUser,
1927  selectedSong,
1928  onSelectSong,
1929  onProceedToCabinet,
1930  theme = ARCADE_THEMES["5thmix"],
1931  onCycleTheme = () => {
1932  }
1933}) => {
1934  const { t } = useTranslation();
1935  const [activeTab, setActiveTab] = reactExports.useState("site");
1936  const [watchLaterIds, setWatchLaterIds] = reactExports.useState(/* @__PURE__ */ new Set());
1937  const [isCopiedShare, setIsCopiedShare] = reactExports.useState(false);
1938  const [toastMessage, setToastMessage] = reactExports.useState(null);
1939  const toastTimerRef = reactExports.useRef(null);
1940  const showToast = reactExports.useCallback((msg) => {
1941    if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
1942    setToastMessage(msg);
1943    toastTimerRef.current = setTimeout(() => {
1944      setToastMessage(null);
1945      toastTimerRef.current = null;
1946    }, 2600);
1947  }, []);
1948  const [deckView, setDeckView] = reactExports.useState(
1949    theme.id === "5thmix" ? "vinyl" : "radar"
1950  );
1951  reactExports.useEffect(() => {
1952    setDeckView(theme.id === "5thmix" ? "vinyl" : "radar");
1953  }, [theme.id]);
1954  const [searchQuery, setSearchQuery] = reactExports.useState("");
1955  const [selectedCategory, setSelectedCategory] = reactExports.useState("all");
1956  const [tracks, setTracks] = reactExports.useState([]);
1957  const [isLoadingTracks, setIsLoadingTracks] = reactExports.useState(false);
1958  const [isSpinningRoulette, setIsSpinningRoulette] = reactExports.useState(false);
1959  const [selectedWheelIndex, setSelectedWheelIndex] = reactExports.useState(0);
1960  const tracksRef = reactExports.useRef([]);
1961  tracksRef.current = tracks;
1962  const selectedWheelIndexRef = reactExports.useRef(selectedWheelIndex);
1963  selectedWheelIndexRef.current = selectedWheelIndex;
1964  const selectedSongRef = reactExports.useRef(selectedSong);
1965  selectedSongRef.current = selectedSong;
1966  const selectedCategoryRef = reactExports.useRef(selectedCategory);
1967  selectedCategoryRef.current = selectedCategory;
1968  const onProceedToCabinetRef = reactExports.useRef(onProceedToCabinet);
1969  onProceedToCabinetRef.current = onProceedToCabinet;
1970  const onCycleThemeRef = reactExports.useRef(onCycleTheme);
1971  onCycleThemeRef.current = onCycleTheme;
1972  const isSpinningRouletteRef = reactExports.useRef(isSpinningRoulette);
1973  isSpinningRouletteRef.current = isSpinningRoulette;
1974  const previewDebounceTimerRef = reactExports.useRef(null);
1975  const playChorusPreviewRef = reactExports.useRef(() => {
1976  });
1977  const onSelectSongRef = reactExports.useRef(onSelectSong);
1978  onSelectSongRef.current = onSelectSong;
1979  const grooveRadarStats = reactExports.useMemo(() => {
1980    return computeGrooveRadar(selectedSong || (tracks.length > 0 ? tracks[selectedWheelIndex] : null));
1981  }, [selectedSong, tracks, selectedWheelIndex]);
1982  const [isPlayingPreview, setIsPlayingPreview] = reactExports.useState(false);
1983  const [previewProgress, setPreviewProgress] = reactExports.useState(0);
1984  const [previewCurrentTime, setPreviewCurrentTime] = reactExports.useState(0);
1985  const previewAudioRef = reactExports.useRef(null);
1986  const [isParsingFile, setIsParsingFile] = reactExports.useState(false);
1987  const [dragOver, setDragOver] = reactExports.useState(false);
1988  const [urlInput, setUrlInput] = reactExports.useState("");
1989  const [urlError, setUrlError] = reactExports.useState(null);
1990  const fileInputRef = reactExports.useRef(null);
1991  const wheelContainerRef = reactExports.useRef(null);
1992  const [refreshTrigger, setRefreshTrigger] = reactExports.useState(0);
1993  const targetIndexRef = reactExports.useRef(0);
1994  const currentIndexRef = reactExports.useRef(0);
1995  const [displayPos, setDisplayPos] = reactExports.useState(0);
1996  const animFrameRef = reactExports.useRef(null);
1997  const isDraggingRef = reactExports.useRef(false);
1998  const dragStartYRef = reactExports.useRef(0);
1999  const dragStartTargetRef = reactExports.useRef(0);
2000  const dragMovedRef = reactExports.useRef(false);
2001  const wheelAccumulatorRef = reactExports.useRef(0);
2002  const wheelLastTimeRef = reactExports.useRef(0);
2003  const calculateStars = (track) => {
2004    var _a, _b;
2005    const tempo = ((_a = track.midiMetadata) == null ? void 0 : _a.tempo) || 120;
2006    const notesCount = ((_b = track.midiMetadata) == null ? void 0 : _b.notesCount) || 1e3;
2007    const duration = parseDurationSec(track.durationSec || track.duration);
2008    const notesPerSec = duration > 0 ? notesCount / duration : 5;
2009    if (notesPerSec > 16 || tempo >= 180) return 5;
2010    if (notesPerSec > 10 || tempo >= 150) return 4;
2011    if (notesPerSec > 6 || tempo >= 120) return 3;
2012    if (notesPerSec > 3) return 2;
2013    return 1;
2014  };
2015  reactExports.useEffect(() => {
2016    let isCurrent = true;
2017    if (currentUser == null ? void 0 : currentUser.id) {
2018      api.getUserWatchLater(currentUser.id).then((ids) => {
2019        if (isCurrent && Array.isArray(ids)) {
2020          setWatchLaterIds(new Set(ids.map(String)));
2021        }
2022      }).catch(() => {
2023      });
2024    } else {
2025      try {
2026        const guestSaved = localStorage.getItem("arcade_bookmarks_guest") || localStorage.getItem("arcade_play_later_guest");
2027        if (guestSaved) {
2028          const parsed = JSON.parse(guestSaved);
2029          if (Array.isArray(parsed) && isCurrent) {
2030            setWatchLaterIds(new Set(parsed.map(String)));
2031          }
2032        }
2033      } catch {
2034      }
2035    }
2036    return () => {
2037      isCurrent = false;
2038    };
2039  }, [currentUser == null ? void 0 : currentUser.id]);
2040  reactExports.useEffect(() => {
2041    let isCurrent = true;
2042    const fetchTracks = async () => {
2043      setIsLoadingTracks(true);
2044      try {
2045        let res = [];
2046        if (activeTab === "bookmarks") {
2047          if (currentUser == null ? void 0 : currentUser.id) {
2048            res = await api.getWatchLaterTracks(currentUser.id);
2049          } else {
2050            const guestIds = Array.from(watchLaterIds);
2051            if (guestIds.length > 0) {
2052              const trackPromises = guestIds.slice(0, 50).map((id) => api.getTrack(id).catch(() => null));
2053              const loaded = await Promise.all(trackPromises);
2054              res = loaded.filter(Boolean);
2055            } else {
2056              res = [];
2057            }
2058          }
2059        } else if (activeTab === "site") {
2060          let catParam = selectedCategory === "all" ? "" : selectedCategory;
2061          if (selectedCategory === "fast") catParam = "";
2062          const sortMode = searchQuery ? "views_desc" : "random";
2063          res = await api.getTracks(searchQuery, catParam, sortMode, 50);
2064        } else {
2065          return;
2066        }
2067        if (isCurrent && Array.isArray(res)) {
2068          const completedTracks = res.filter((t2) => t2.status === "completed");
2069          const playable = completedTracks.length > 0 ? completedTracks : res;
2070          let filtered = playable;
2071          if (activeTab === "bookmarks" && searchQuery.trim()) {
2072            const q = searchQuery.toLowerCase();
2073            filtered = filtered.filter(
2074              (t2) => t2.title && t2.title.toLowerCase().includes(q) || t2.artist && t2.artist.toLowerCase().includes(q)
2075            );
2076          }
2077          if (selectedCategory === "fast") {
2078            filtered = playable.filter((t2) => {
2079              var _a;
2080              return (((_a = t2.midiMetadata) == null ? void 0 : _a.tempo) || 120) >= 140;
2081            });
2082          } else if (selectedCategory !== "all" && activeTab === "bookmarks") {
2083            filtered = filtered.filter((t2) => {
2084              var _a, _b;
2085              const cat = typeof t2.category === "string" ? t2.category.toLowerCase() : ((_b = (_a = t2.category) == null ? void 0 : _a.label) == null ? void 0 : _b.toLowerCase()) || "";
2086              return cat.includes(selectedCategory.toLowerCase());
2087            });
2088          }
2089          const enriched = filtered.map((t2) => {
2090            var _a;
2091            return {
2092              ...t2,
2093              _cachedStars: calculateStars(t2),
2094              _cachedTempo: Math.round(((_a = t2.midiMetadata) == null ? void 0 : _a.tempo) || 120),
2095              _cachedArtist: t2.artist || ""
2096            };
2097          });
2098          setTracks(enriched);
2099          targetIndexRef.current = 0;
2100          currentIndexRef.current = 0;
2101          setDisplayPos(0);
2102          setSelectedWheelIndex(0);
2103          selectedWheelIndexRef.current = 0;
2104          if (enriched.length > 0) {
2105            pickTrackData(enriched[0], false);
2106          }
2107        }
2108      } catch (err) {
2109        console.error("Failed to fetch tracks:", err);
2110      } finally {
2111        if (isCurrent) setIsLoadingTracks(false);
2112      }
2113    };
2114    const timeoutId = setTimeout(fetchTracks, searchQuery ? 300 : 0);
2115    return () => {
2116      isCurrent = false;
2117      clearTimeout(timeoutId);
2118    };
2119  }, [searchQuery, selectedCategory, refreshTrigger, activeTab, currentUser == null ? void 0 : currentUser.id]);
2120  const playChorusPreview = reactExports.useCallback((song) => {
2121    if (previewAudioRef.current) {
2122      previewAudioRef.current.pause();
2123      previewAudioRef.current.src = "";
2124    }
2125    if (!song || !song.opusPath) {
2126      setIsPlayingPreview(false);
2127      arcadeAudio.duckBGM(false);
2128      return;
2129    }
2130    try {
2131      if (!previewAudioRef.current) {
2132        previewAudioRef.current = new Audio();
2133      }
2134      const audio = previewAudioRef.current;
2135      audio.src = song.opusPath;
2136      audio.crossOrigin = "anonymous";
2137      const cue = Math.max(0, Math.floor(song.duration * 0.35));
2138      audio.currentTime = cue;
2139      setPreviewCurrentTime(cue);
2140      audio.onloadedmetadata = () => {
2141        audio.currentTime = cue;
2142      };
2143      audio.ontimeupdate = () => {
2144        setPreviewCurrentTime(audio.currentTime);
2145        const progress = song.duration > 0 ? audio.currentTime / song.duration : 0;
2146        setPreviewProgress(progress);
2147        if (audio.currentTime >= cue + 25 || audio.currentTime >= song.duration) {
2148          audio.currentTime = cue;
2149        }
2150      };
2151      audio.onplay = () => {
2152        setIsPlayingPreview(true);
2153        arcadeAudio.duckBGM(true);
2154      };
2155      audio.onpause = () => {
2156        setIsPlayingPreview(false);
2157        arcadeAudio.duckBGM(false);
2158      };
2159      audio.onerror = () => {
2160        setIsPlayingPreview(false);
2161        arcadeAudio.duckBGM(false);
2162      };
2163      audio.play().catch(() => {
2164        setIsPlayingPreview(false);
2165        arcadeAudio.duckBGM(false);
2166      });
2167    } catch (err) {
2168      console.warn("Could not initiate preview playback:", err);
2169      setIsPlayingPreview(false);
2170      arcadeAudio.duckBGM(false);
2171    }
2172  }, []);
2173  playChorusPreviewRef.current = playChorusPreview;
2174  const pickTrackData = reactExports.useCallback((track, playAudio = true) => {
2175    var _a, _b, _c;
2176    if (!track) return;
2177    const durSec = parseDurationSec(track.durationSec || track.duration);
2178    const numId = parseInt(String(track.id));
2179    const group = numId >= 1e7 ? numId % 1e3 : Math.floor(numId / 1e3);
2180    const resolvedPianoRoll = typeof track.pianoRollPath === "string" && track.pianoRollPath ? track.pianoRollPath : track.status === "completed" ? `/storage/tracks/piano_rolls/${group}/${track.id}.json` : void 0;
2181    const resolvedOpus = typeof track.opusPath === "string" && track.opusPath ? track.opusPath : track.status === "completed" ? `/storage/tracks/opus/${group}/${track.id}.opus` : void 0;
2182    const songData = {
2183      source: "site",
2184      id: String(track.id),
2185      title: track.title || "Untitled Track",
2186      artist: track.artist || void 0,
2187      category: typeof track.category === "string" ? track.category : ((_a = track.category) == null ? void 0 : _a.label) || "General",
2188      bpm: Math.round(((_b = track.midiMetadata) == null ? void 0 : _b.tempo) || 120),
2189      duration: durSec,
2190      timeSignature: ((_c = track.midiMetadata) == null ? void 0 : _c.timeSignature) ? track.midiMetadata.timeSignature.split("/").map(Number) : [4, 4],
2191      instruments: track.instruments || [],
2192      midiPath: track.midiPath || void 0,
2193      pianoRollPath: resolvedPianoRoll,
2194      melodyPath: track.melodyPath || void 0,
2195      opusPath: resolvedOpus,
2196      difficultyStars: calculateStars(track),
2197      rawTrack: track
2198    };
2199    onSelectSongRef.current(songData);
2200    if (previewDebounceTimerRef.current) {
2201      clearTimeout(previewDebounceTimerRef.current);
2202      previewDebounceTimerRef.current = null;
2203    }
2204    if (playAudio) {
2205      previewDebounceTimerRef.current = setTimeout(() => {
2206        playChorusPreviewRef.current(songData);
2207        if (songData.pianoRollPath) {
2208          fetch(songData.pianoRollPath).then((res) => res.json()).then((data) => {
2209            if (Array.isArray(data)) {
2210              songData.pianoRollData = data;
2211              onSelectSongRef.current({ ...songData, pianoRollData: data });
2212            }
2213          }).catch(() => {
2214          });
2215        }
2216        if (songData.melodyPath) {
2217          fetch(songData.melodyPath).then((res) => res.json()).then((data) => {
2218            if (Array.isArray(data)) {
2219              songData.melodyData = data;
2220              onSelectSongRef.current({ ...songData, melodyData: data });
2221            }
2222          }).catch(() => {
2223          });
2224        }
2225      }, 140);
2226    }
2227  }, []);
2228  const togglePreview = reactExports.useCallback(() => {
2229    if (!previewAudioRef.current) {
2230      if (selectedSongRef.current) playChorusPreviewRef.current(selectedSongRef.current);
2231      return;
2232    }
2233    if (isPlayingPreview) {
2234      previewAudioRef.current.pause();
2235      setIsPlayingPreview(false);
2236      arcadeAudio.duckBGM(false);
2237    } else {
2238      previewAudioRef.current.play().then(() => {
2239        setIsPlayingPreview(true);
2240        arcadeAudio.duckBGM(true);
2241      }).catch(() => {
2242      });
2243    }
2244  }, [isPlayingPreview]);
2245  const togglePreviewRef = reactExports.useRef(togglePreview);
2246  togglePreviewRef.current = togglePreview;
2247  reactExports.useEffect(() => {
2248    return () => {
2249      if (previewDebounceTimerRef.current) {
2250        clearTimeout(previewDebounceTimerRef.current);
2251        previewDebounceTimerRef.current = null;
2252      }
2253      if (previewAudioRef.current) {
2254        previewAudioRef.current.pause();
2255        previewAudioRef.current.src = "";
2256      }
2257      arcadeAudio.duckBGM(false);
2258    };
2259  }, []);
2260  const stepAnimation = reactExports.useCallback(() => {
2261    const diff = targetIndexRef.current - currentIndexRef.current;
2262    if (Math.abs(diff) < 5e-3) {
2263      currentIndexRef.current = targetIndexRef.current;
2264      setDisplayPos(targetIndexRef.current);
2265      animFrameRef.current = null;
2266      return;
2267    }
2268    currentIndexRef.current += diff * 0.32;
2269    setDisplayPos(currentIndexRef.current);
2270    animFrameRef.current = requestAnimationFrame(stepAnimation);
2271  }, []);
2272  const setWheelTarget = reactExports.useCallback((newTarget, animate = true) => {
2273    targetIndexRef.current = newTarget;
2274    const curTracks = tracksRef.current;
2275    if (curTracks.length > 0) {
2276      const rawIdx = Math.round(newTarget);
2277      const normalizedIdx = (rawIdx % curTracks.length + curTracks.length) % curTracks.length;
2278      if (normalizedIdx !== selectedWheelIndexRef.current) {
2279        selectedWheelIndexRef.current = normalizedIdx;
2280        setSelectedWheelIndex(normalizedIdx);
2281        arcadeAudio.playWheelTick();
2282        pickTrackData(curTracks[normalizedIdx], true);
2283      }
2284    }
2285    if (animate) {
2286      if (!animFrameRef.current) {
2287        animFrameRef.current = requestAnimationFrame(stepAnimation);
2288      }
2289    } else {
2290      if (animFrameRef.current) {
2291        cancelAnimationFrame(animFrameRef.current);
2292        animFrameRef.current = null;
2293      }
2294      currentIndexRef.current = newTarget;
2295      setDisplayPos(newTarget);
2296    }
2297  }, [pickTrackData, stepAnimation]);
2298  const stepWheel = reactExports.useCallback((step) => {
2299    setWheelTarget(Math.round(targetIndexRef.current) + step, true);
2300  }, [setWheelTarget]);
2301  const stepWheelRef = reactExports.useRef(stepWheel);
2302  stepWheelRef.current = stepWheel;
2303  const setWheelTargetRef = reactExports.useRef(setWheelTarget);
2304  setWheelTargetRef.current = setWheelTarget;
2305  const handleSelectWheelTrack = reactExports.useCallback((index) => {
2306    const curTracks = tracksRef.current;
2307    if (curTracks.length === 0) return;
2308    setWheelTarget(index, true);
2309  }, [setWheelTarget]);
2310  const handleSelectWheelTrackRef = reactExports.useRef(handleSelectWheelTrack);
2311  handleSelectWheelTrackRef.current = handleSelectWheelTrack;
2312  reactExports.useEffect(() => {
2313    return () => {
2314      if (animFrameRef.current) {
2315        cancelAnimationFrame(animFrameRef.current);
2316        animFrameRef.current = null;
2317      }
2318    };
2319  }, []);
2320  reactExports.useEffect(() => {
2321    const el = wheelContainerRef.current;
2322    if (!el) return;
2323    const onNativeWheel = (e) => {
2324      e.preventDefault();
2325      e.stopPropagation();
2326      const curTracks = tracksRef.current;
2327      if (curTracks.length === 0) return;
2328      if (Math.abs(e.deltaY) < 3) return;
2329      const now = performance.now();
2330      if (now - wheelLastTimeRef.current > 160) {
2331        wheelAccumulatorRef.current = 0;
2332      }
2333      wheelLastTimeRef.current = now;
2334      wheelAccumulatorRef.current += e.deltaY;
2335      const threshold = 26;
2336      if (Math.abs(wheelAccumulatorRef.current) >= threshold) {
2337        const step = wheelAccumulatorRef.current > 0 ? 1 : -1;
2338        wheelAccumulatorRef.current = 0;
2339        stepWheelRef.current(step);
2340      }
2341    };
2342    el.addEventListener("wheel", onNativeWheel, { passive: false });
2343    return () => {
2344      el.removeEventListener("wheel", onNativeWheel);
2345    };
2346  }, []);
2347  const handlePointerDown = (e) => {
2348    if (tracks.length === 0) return;
2349    isDraggingRef.current = true;
2350    dragMovedRef.current = false;
2351    dragStartYRef.current = e.clientY;
2352    dragStartTargetRef.current = targetIndexRef.current;
2353  };
2354  const handlePointerMove = (e) => {
2355    if (!isDraggingRef.current) return;
2356    const deltaY = e.clientY - dragStartYRef.current;
2357    if (Math.abs(deltaY) > 6) {
2358      dragMovedRef.current = true;
2359      try {
2360        if (!e.currentTarget.hasPointerCapture(e.pointerId)) {
2361          e.currentTarget.setPointerCapture(e.pointerId);
2362        }
2363      } catch (_) {
2364      }
2365    }
2366    if (dragMovedRef.current) {
2367      const slotDelta = -deltaY / 60;
2368      setWheelTarget(dragStartTargetRef.current + slotDelta, true);
2369    }
2370  };
2371  const handlePointerUp = (e) => {
2372    if (!isDraggingRef.current) return;
2373    isDraggingRef.current = false;
2374    try {
2375      if (e.currentTarget.hasPointerCapture(e.pointerId)) {
2376        e.currentTarget.releasePointerCapture(e.pointerId);
2377      }
2378    } catch (_) {
2379    }
2380    if (dragMovedRef.current) {
2381      setWheelTarget(Math.round(targetIndexRef.current), true);
2382    }
2383  };
2384  const handleSwap50Songs = reactExports.useCallback(() => {
2385    arcadeAudio.playRoulette();
2386    setRefreshTrigger((v) => v + 1);
2387  }, []);
2388  const handleSwap50SongsRef = reactExports.useRef(handleSwap50Songs);
2389  handleSwap50SongsRef.current = handleSwap50Songs;
2390  const handleSpinRoulette = reactExports.useCallback(() => {
2391    const curTracks = tracksRef.current;
2392    if (curTracks.length === 0 || isSpinningRouletteRef.current) return;
2393    setIsSpinningRoulette(true);
2394    isSpinningRouletteRef.current = true;
2395    arcadeAudio.playRoulette();
2396    const extraSteps = 18 + Math.floor(Math.random() * 10);
2397    const finalTarget = Math.round(targetIndexRef.current) + extraSteps;
2398    setWheelTarget(finalTarget, true);
2399    setTimeout(() => {
2400      setIsSpinningRoulette(false);
2401      isSpinningRouletteRef.current = false;
2402      arcadeAudio.playCoin();
2403    }, 900);
2404  }, [setWheelTarget]);
2405  const handleSpinRouletteRef = reactExports.useRef(handleSpinRoulette);
2406  handleSpinRouletteRef.current = handleSpinRoulette;
2407  const setDeckViewRef = reactExports.useRef(setDeckView);
2408  setDeckViewRef.current = setDeckView;
2409  const handleToggleBookmark = reactExports.useCallback(async () => {
2410    const curSong = selectedSongRef.current;
2411    if (!curSong) return;
2412    if (curSong.source === "upload") {
2413      arcadeAudio.playBuzzer();
2414      showToast("LOCAL CARTRIDGE CANNOT BE BOOKMARKED");
2415      return;
2416    }
2417    const songId = String(curSong.id);
2418    const isSaved = watchLaterIds.has(songId);
2419    const nextSet = new Set(watchLaterIds);
2420    if (isSaved) {
2421      nextSet.delete(songId);
2422      arcadeAudio.playSelect();
2423      showToast("REMOVED FROM BOOKMARKS");
2424    } else {
2425      nextSet.add(songId);
2426      arcadeAudio.playCoin();
2427      showToast("★ SAVED TO BOOKMARKS!");
2428    }
2429    setWatchLaterIds(nextSet);
2430    try {
2431      if (currentUser == null ? void 0 : currentUser.id) {
2432        await api.toggleWatchLaterTrack(songId, currentUser.id);
2433      } else {
2434        localStorage.setItem("arcade_bookmarks_guest", JSON.stringify(Array.from(nextSet)));
2435      }
2436    } catch (err) {
2437      console.error("Failed to toggle bookmark:", err);
2438    }
2439    if (activeTab === "bookmarks" && isSaved) {
2440      setRefreshTrigger((v) => v + 1);
2441    }
2442  }, [watchLaterIds, currentUser == null ? void 0 : currentUser.id, activeTab, showToast]);
2443  const handleToggleBookmarkRef = reactExports.useRef(handleToggleBookmark);
2444  handleToggleBookmarkRef.current = handleToggleBookmark;
2445  const handleShareSong = reactExports.useCallback(() => {
2446    var _a;
2447    const curSong = selectedSongRef.current;
2448    if (!curSong) return;
2449    arcadeAudio.playCoin();
2450    let shareUrl = `${window.location.origin}/games`;
2451    if (curSong.source === "site" && curSong.id) {
2452      shareUrl += `?track=${curSong.id}`;
2453    }
2454    if ((_a = navigator == null ? void 0 : navigator.clipboard) == null ? void 0 : _a.writeText) {
2455      navigator.clipboard.writeText(shareUrl).then(() => {
2456        setIsCopiedShare(true);
2457        showToast("GAME LINK COPIED TO CLIPBOARD!");
2458        setTimeout(() => setIsCopiedShare(false), 2500);
2459      }).catch(() => {
2460        showToast("COPIED LINK TO CLIPBOARD");
2461      });
2462    } else {
2463      showToast("SHARE: " + shareUrl);
2464    }
2465  }, [showToast]);
2466  const handleOpenWatchPage = reactExports.useCallback(() => {
2467    const curSong = selectedSongRef.current;
2468    if (!curSong) return;
2469    if (curSong.source === "upload") {
2470      arcadeAudio.playBuzzer();
2471      showToast("LOCAL CARTRIDGE - NO WEB WATCH PAGE");
2472      return;
2473    }
2474    arcadeAudio.playSelect();
2475    const trackObj = curSong.rawTrack || {
2476      id: Number(curSong.id),
2477      title: curSong.title,
2478      artist: curSong.artist
2479    };
2480    const watchUrl = generateTrackUrl(trackObj);
2481    window.open(watchUrl, "_blank", "noopener,noreferrer");
2482    showToast("OPENING TRACK WATCH PAGE...");
2483  }, [showToast]);
2484  reactExports.useEffect(() => {
2485    const handleKeyDown = (e) => {
2486      var _a, _b, _c;
2487      const isInputFocused = ((_a = document.activeElement) == null ? void 0 : _a.tagName) === "INPUT";
2488      if (e.key === "Escape") {
2489        if (isInputFocused) {
2490          (_b = document.activeElement) == null ? void 0 : _b.blur();
2491        }
2492        return;
2493      }
2494      if (isInputFocused) {
2495        if (e.key === "Enter") {
2496          (_c = document.activeElement) == null ? void 0 : _c.blur();
2497        }
2498        return;
2499      }
2500      tracksRef.current;
2501      selectedWheelIndexRef.current;
2502      if (e.key === "ArrowUp" || e.key === "w" || e.key === "W") {
2503        e.preventDefault();
2504        stepWheelRef.current(-1);
2505      } else if (e.key === "ArrowDown" || e.key === "s" || e.key === "S") {
2506        e.preventDefault();
2507        stepWheelRef.current(1);
2508      } else if (e.key === "x" || e.key === "X") {
2509        e.preventDefault();
2510        handleSwap50SongsRef.current();
2511      } else if (e.key === "b" || e.key === "B" || e.key === "p" || e.key === "P") {
2512        e.preventDefault();
2513        handleToggleBookmarkRef.current();
2514      } else if (e.key === "Enter") {
2515        e.preventDefault();
2516        if (selectedSongRef.current) {
2517          if (previewAudioRef.current) previewAudioRef.current.pause();
2518          arcadeAudio.duckBGM(false);
2519          arcadeAudio.playStageStart();
2520          onProceedToCabinetRef.current();
2521        }
2522      } else if (e.key === " ") {
2523        e.preventDefault();
2524        togglePreviewRef.current();
2525      } else if (e.key === "r" || e.key === "R") {
2526        e.preventDefault();
2527        handleSpinRouletteRef.current();
2528      } else if (e.key === "t" || e.key === "T") {
2529        e.preventDefault();
2530        arcadeAudio.playWheelTick();
2531        onCycleThemeRef.current();
2532      } else if (e.key === "g" || e.key === "G") {
2533        e.preventDefault();
2534        arcadeAudio.playSelect();
2535        setDeckViewRef.current((prev) => prev === "radar" ? "vinyl" : "radar");
2536      } else if (e.key === "Tab") {
2537        e.preventDefault();
2538        const curIdx = CATEGORIES.findIndex((c) => c.key === selectedCategoryRef.current);
2539        const nextCat = CATEGORIES[(curIdx + 1) % CATEGORIES.length].key;
2540        arcadeAudio.playSelect();
2541        setSelectedCategory(nextCat);
2542      }
2543    };
2544    window.addEventListener("keydown", handleKeyDown);
2545    return () => window.removeEventListener("keydown", handleKeyDown);
2546  }, []);
2547  const parseMidiFileBuffer = async (buffer, fileName) => {
2548    setIsParsingFile(true);
2549    try {
2550      const parsed = new Midi.Midi(buffer);
2551      const duration = Math.round(parsed.duration) || 120;
2552      const tempo = parsed.header.tempos.length > 0 ? Math.round(parsed.header.tempos[0].bpm) : 120;
2553      const timeSig = parsed.header.timeSignatures.length > 0 ? [parsed.header.timeSignatures[0].timeSignature[0], parsed.header.timeSignatures[0].timeSignature[1]] : [4, 4];
2554      const instruments = [];
2555      parsed.tracks.forEach((track, idx) => {
2556        instruments.push({
2557          trackId: idx,
2558          name: track.name || track.instrument.name || `Track ${idx + 1}`,
2559          family: track.instrument.family,
2560          program: track.instrument.number !== void 0 ? track.instrument.number : 0,
2561          midiChannel: track.notes.length === 0 ? -1 : track.channel !== void 0 ? track.channel : 0,
2562          notesCount: track.notes.length
2563        });
2564      });
2565      const pianoRollData = [];
2566      let musicalTrackIdx = 0;
2567      let totalNotes = 0;
2568      parsed.tracks.forEach((track, i) => {
2569        if (track.notes.length > 0) {
2570          track.notes.forEach((n) => {
2571            totalNotes++;
2572            pianoRollData.push({
2573              t: parseFloat(n.time.toFixed(3)),
2574              n: n.midi,
2575              d: parseFloat(n.duration.toFixed(3)),
2576              v: Math.round(n.velocity * 127),
2577              i: track.name || track.instrument.name || `Track ${i + 1}`,
2578              trackIdx: musicalTrackIdx,
2579              p: track.instrument.number !== void 0 ? track.instrument.number : 0,
2580              isDrum: track.instrument.family === "drums"
2581            });
2582          });
2583          musicalTrackIdx++;
2584        }
2585      });
2586      const notesPerSec = duration > 0 ? totalNotes / duration : 5;
2587      let difficultyStars = 3;
2588      if (notesPerSec > 16) difficultyStars = 5;
2589      else if (notesPerSec > 10) difficultyStars = 4;
2590      else if (notesPerSec > 6) difficultyStars = 3;
2591      else if (notesPerSec > 3) difficultyStars = 2;
2592      else difficultyStars = 1;
2593      const cleanTitle = fileName.replace(/\.[^/.]+$/, "").replace(/[_-]/g, " ");
2594      const songData = {
2595        source: "upload",
2596        id: "custom-" + Date.now(),
2597        title: cleanTitle,
2598        bpm: tempo,
2599        duration,
2600        timeSignature: timeSig,
2601        instruments,
2602        pianoRollData,
2603        difficultyStars
2604      };
2605      arcadeAudio.playCoin();
2606      onSelectSong(songData);
2607    } catch (err) {
2608      console.error("Failed to parse local MIDI:", err);
2609      alert("Could not parse this MIDI file. Please ensure it is a valid Standard MIDI file (.mid).");
2610    } finally {
2611      setIsParsingFile(false);
2612    }
2613  };
2614  const handleFileChange = async (e) => {
2615    var _a;
2616    const file = (_a = e.target.files) == null ? void 0 : _a[0];
2617    if (!file) return;
2618    const buf = await file.arrayBuffer();
2619    parseMidiFileBuffer(buf, file.name);
2620  };
2621  const handleDrop = async (e) => {
2622    var _a;
2623    e.preventDefault();
2624    setDragOver(false);
2625    const file = (_a = e.dataTransfer.files) == null ? void 0 : _a[0];
2626    if (file && (file.name.endsWith(".mid") || file.name.endsWith(".midi"))) {
2627      const buf = await file.arrayBuffer();
2628      parseMidiFileBuffer(buf, file.name);
2629    } else {
2630      alert("Please drop a valid .mid or .midi file.");
2631    }
2632  };
2633  const handleUrlSubmit = async (e) => {
2634    var _a;
2635    e.preventDefault();
2636    setUrlError(null);
2637    const cleanUrl = urlInput.trim();
2638    if (!cleanUrl) return;
2639    setIsParsingFile(true);
2640    try {
2641      const idMatch = cleanUrl.match(/\/track\/(\d+)/) || cleanUrl.match(/\/watch\?.*v=(\d+)/) || cleanUrl.match(/\/midi(?:[a-zA-Z0-9_\-]+\/)?(\d+)_/);
2642      if (idMatch) {
2643        const trackId = idMatch[1];
2644        const trackData = await api.getTrack(trackId);
2645        if (trackData) {
2646          pickTrackData(trackData, true);
2647          setIsParsingFile(false);
2648          return;
2649        }
2650      }
2651      const res = await fetch(cleanUrl);
2652      if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
2653      const buf = await res.arrayBuffer();
2654      const fileName = ((_a = cleanUrl.split("/").pop()) == null ? void 0 : _a.split("?")[0]) || "Online Song.mid";
2655      await parseMidiFileBuffer(buf, fileName);
2656    } catch (err) {
2657      console.error("URL Ingestion error:", err);
2658      setUrlError("Unable to fetch or parse this MIDI URL. Please check the link or CORS policy.");
2659    } finally {
2660      setIsParsingFile(false);
2661    }
2662  };
2663  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-full max-h-[calc(100vh-80px)] flex flex-col justify-between overflow-hidden select-none font-mono text-white animate-in fade-in duration-300 relative", children: [
2664    toastMessage && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed top-16 left-1/2 -translate-x-1/2 z-50 px-4 py-2 rounded-xl bg-slate-950/95 border-2 border-cyan-400 text-cyan-300 text-xs font-mono font-black tracking-wider uppercase shadow-[0_0_25px_rgba(6,182,212,0.6)] animate-in fade-in slide-in-from-top-2 duration-200 flex items-center gap-2 pointer-events-none", children: [
2665      /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14, className: "text-yellow-400 animate-spin" }),
2666      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: toastMessage })
2667    ] }),
2668    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-3 px-3 py-2 bg-slate-950/80 border-2 border-cyan-500/30 rounded-xl shadow-[0_0_15px_rgba(6,182,212,0.15)] shrink-0", children: [
2669      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
2670        /* @__PURE__ */ jsxRuntimeExports.jsxs(
2671          "button",
2672          {
2673            onClick: () => {
2674              arcadeAudio.playSelect();
2675              setActiveTab("site");
2676            },
2677            className: `flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-black tracking-wider transition-all cursor-pointer ${activeTab === "site" ? "bg-gradient-to-r from-cyan-500 to-blue-600 text-white shadow-[0_0_12px_rgba(6,182,212,0.5)] scale-105" : "text-slate-400 hover:text-white hover:bg-slate-900"}`,
2678            children: [
2679              /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 14, className: activeTab === "site" ? "animate-spin" : "" }),
2680              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "ARCADE JUKEBOX" })
2681            ]
2682          }
2683        ),
2684        /* @__PURE__ */ jsxRuntimeExports.jsxs(
2685          "button",
2686          {
2687            onClick: () => {
2688              arcadeAudio.playSelect();
2689              setActiveTab("bookmarks");
2690            },
2691            className: `flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-black tracking-wider transition-all cursor-pointer ${activeTab === "bookmarks" ? "bg-gradient-to-r from-amber-500 to-yellow-600 text-slate-950 font-black shadow-[0_0_12px_rgba(245,158,11,0.6)] scale-105" : "text-slate-400 hover:text-white hover:bg-slate-900"}`,
2692            children: [
2693              /* @__PURE__ */ jsxRuntimeExports.jsx(Bookmark, { size: 14, className: watchLaterIds.size > 0 ? "fill-current text-amber-400" : "" }),
2694              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "BOOKMARKS" }),
2695              watchLaterIds.size > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-1.5 py-0.5 rounded-full text-[10px] font-mono font-black ${activeTab === "bookmarks" ? "bg-black text-amber-300" : "bg-amber-400 text-black"}`, children: watchLaterIds.size })
2696            ]
2697          }
2698        ),
2699        /* @__PURE__ */ jsxRuntimeExports.jsxs(
2700          "button",
2701          {
2702            onClick: () => {
2703              arcadeAudio.playSelect();
2704              setActiveTab("upload");
2705            },
2706            className: `flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-black tracking-wider transition-all cursor-pointer ${activeTab === "upload" ? "bg-gradient-to-r from-pink-500 to-rose-600 text-white shadow-[0_0_12px_rgba(244,63,94,0.5)] scale-105" : "text-slate-400 hover:text-white hover:bg-slate-900"}`,
2707            children: [
2708              /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 14 }),
2709              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "INSERT CARTRIDGE (.MID)" })
2710            ]
2711          }
2712        ),
2713        /* @__PURE__ */ jsxRuntimeExports.jsxs(
2714          "button",
2715          {
2716            onClick: () => {
2717              arcadeAudio.playSelect();
2718              setActiveTab("url");
2719            },
2720            className: `flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-black tracking-wider transition-all cursor-pointer ${activeTab === "url" ? "bg-gradient-to-r from-purple-500 to-indigo-600 text-white shadow-[0_0_12px_rgba(168,85,247,0.5)] scale-105" : "text-slate-400 hover:text-white hover:bg-slate-900"}`,
2721            children: [
2722              /* @__PURE__ */ jsxRuntimeExports.jsx(Link, { size: 14 }),
2723              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "CYBER TERMINAL" })
2724            ]
2725          }
2726        )
2727      ] }),
2728      (activeTab === "site" || activeTab === "bookmarks") && /* @__PURE__ */ jsxRuntimeExports.jsxs(
2729        "button",
2730        {
2731          onClick: handleSpinRoulette,
2732          disabled: isSpinningRoulette || tracks.length === 0,
2733          className: `group flex items-center gap-2 px-3 py-1.5 rounded-lg bg-gradient-to-r from-amber-500 via-pink-500 to-purple-600 text-white text-xs font-black tracking-wider shadow-[0_0_15px_rgba(236,72,153,0.5)] hover:shadow-[0_0_25px_rgba(236,72,153,0.8)] active:scale-95 transition-all cursor-pointer ${isSpinningRoulette ? "animate-pulse pointer-events-none" : ""}`,
2734          title: "Shortcut: Press [R]",
2735          children: [
2736            /* @__PURE__ */ jsxRuntimeExports.jsx(Dices, { size: 14, className: `text-yellow-300 ${isSpinningRoulette ? "animate-spin" : "group-hover:rotate-45 transition-transform"}` }),
2737            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: isSpinningRoulette ? "SPINNING..." : "ARCADE ROULETTE [R]" })
2738          ]
2739        }
2740      )
2741    ] }),
2742    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 grid grid-cols-1 lg:grid-cols-12 gap-4 my-2 min-h-0 overflow-hidden", children: [
2743      /* @__PURE__ */ jsxRuntimeExports.jsxs(
2744        "div",
2745        {
2746          className: `lg:col-span-7 flex flex-col h-full overflow-hidden rounded-2xl p-3.5 bg-slate-950/90 relative border-2 ${theme.deckBorder}`,
2747          children: [
2748            (activeTab === "site" || activeTab === "bookmarks") && selectedSong && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col justify-between overflow-hidden", children: [
2749              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center justify-between gap-3 px-3 py-1.5 rounded-lg border font-mono ${theme.hudStageBoxClass} shrink-0`, children: [
2750                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
2751                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-emerald-400 animate-ping" }),
2752                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-black tracking-wider uppercase", children: t("gameCenter.musicSelect", "MUSIC SELECT") })
2753                ] }),
2754                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
2755                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-xs font-black tracking-widest text-amber-300", children: [
2756                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "opacity-70", children: "SPEED:" }),
2757                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-white font-mono text-sm", children: selectedSong.bpm }),
2758                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-amber-400", children: "bpm" })
2759                  ] }),
2760                  /* @__PURE__ */ jsxRuntimeExports.jsxs(
2761                    "button",
2762                    {
2763                      onClick: () => {
2764                        arcadeAudio.playSelect();
2765                        setDeckView(deckView === "radar" ? "vinyl" : "radar");
2766                      },
2767                      className: "flex items-center gap-1 px-2 py-0.5 rounded border text-[9px] font-mono font-bold transition-all bg-black/60 hover:bg-black/90 active:scale-95 cursor-pointer",
2768                      style: { borderColor: `${theme.primary}60`, color: theme.primary },
2769                      title: "Toggle between Pentagon Groove Radar and Vinyl Graphic [G]",
2770                      children: [
2771                        deckView === "radar" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 11 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Radar, { size: 11 }),
2772                        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: deckView === "radar" ? "RADAR" : "VINYL" }),
2773                        /* @__P
2773URE__ */ jsxRuntimeExports.jsx("span", { className: "text-[8px] text-zinc-500", children: "[G]" })
2774                      ]
2775                    }
2776                  )
2777                ] })
2778              ] }),
2779              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `relative overflow-hidden rounded-xl p-3 my-1.5 border-4 ${theme.bannerBorderClass} ${theme.bannerBgClass} shadow-xl shrink-0 group`, children: [
2780                /* @__PURE__ */ jsxRuntimeExports.jsx(
2781                  "div",
2782                  {
2783                    className: "absolute inset-0 opacity-15 pointer-events-none",
2784                    style: {
2785                      backgroundImage: "repeating-linear-gradient(0deg, #000, #000 2px, transparent 2px, transparent 4px)",
2786                      backgroundSize: "100% 4px"
2787                    }
2788                  }
2789                ),
2790                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-1 left-1 w-1.5 h-1.5 rounded-full bg-zinc-400 border border-zinc-700 shadow-sm" }),
2791                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-zinc-400 border border-zinc-700 shadow-sm" }),
2792                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-1 left-1 w-1.5 h-1.5 rounded-full bg-zinc-400 border border-zinc-700 shadow-sm" }),
2793                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-1 right-1 w-1.5 h-1.5 rounded-full bg-zinc-400 border border-zinc-700 shadow-sm" }),
2794                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-10 flex items-center justify-between gap-3", children: [
2795                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
2796                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-0.5", children: [
2797                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-black uppercase px-2 py-0.5 rounded-full bg-black/50 border border-white/20 tracking-widest", style: { color: theme.primary }, children: selectedSong.category || theme.eraLabel }),
2798                      selectedSong && watchLaterIds.has(String(selectedSong.id)) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] font-black uppercase px-2 py-0.5 rounded-full bg-amber-500/20 text-amber-300 border border-amber-400/50 tracking-wider flex items-center gap-1", children: [
2799                        /* @__PURE__ */ jsxRuntimeExports.jsx(Bookmark, { size: 9, className: "fill-current" }),
2800                        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "SAVED" })
2801                      ] })
2802                    ] }),
2803                    /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg sm:text-xl font-black italic tracking-wide text-white drop-shadow-[0_2px_8px_rgba(0,0,0,0.9)] truncate", "data-deck-title": true, children: selectedSong.title }),
2804                    selectedSong.artist && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs font-bold mt-0.5 truncate", style: { color: `${theme.primary}ee` }, children: [
2805                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "/" }),
2806                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-300 truncate", children: selectedSong.artist })
2807                    ] })
2808                  ] }),
2809                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
2810                    /* @__PURE__ */ jsxRuntimeExports.jsx(
2811                      "button",
2812                      {
2813                        type: "button",
2814                        onClick: (e) => {
2815                          e.stopPropagation();
2816                          handleToggleBookmark();
2817                        },
2818                        className: `p-2 rounded-xl border-2 transition-all active:scale-95 flex items-center justify-center cursor-pointer ${selectedSong && watchLaterIds.has(String(selectedSong.id)) ? "bg-amber-400 text-slate-950 border-amber-300 shadow-[0_0_15px_rgba(245,158,11,0.6)]" : "bg-black/60 border-zinc-700 hover:border-amber-400/80 text-zinc-400 hover:text-amber-300"}`,
2819                        title: selectedSong && watchLaterIds.has(String(selectedSong.id)) ? "Remove from Bookmarks [B]" : "Save to Bookmarks [B]",
2820                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(Bookmark, { size: 16, className: selectedSong && watchLaterIds.has(String(selectedSong.id)) ? "fill-current" : "" })
2821                      }
2822                    ),
2823                    /* @__PURE__ */ jsxRuntimeExports.jsx(
2824                      "button",
2825                      {
2826                        type: "button",
2827                        onClick: (e) => {
2828                          e.stopPropagation();
2829                          handleShareSong();
2830                        },
2831                        className: `p-2 rounded-xl border-2 transition-all active:scale-95 flex items-center justify-center cursor-pointer ${isCopiedShare ? "bg-emerald-500 text-black border-emerald-400 shadow-[0_0_15px_rgba(16,185,129,0.6)]" : "bg-black/60 border-zinc-700 hover:border-cyan-400/80 text-zinc-400 hover:text-cyan-300"}`,
2832                        title: "Share Game Link [Copies /games?track=id]",
2833                        children: isCopiedShare ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 16, className: "stroke-[3]" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Share2, { size: 16 })
2834                      }
2835                    ),
2836                    /* @__PURE__ */ jsxRuntimeExports.jsx(
2837                      "button",
2838                      {
2839                        type: "button",
2840                        onClick: (e) => {
2841                          e.stopPropagation();
2842                          handleOpenWatchPage();
2843                        },
2844                        className: "p-2 rounded-xl border-2 border-zinc-700 hover:border-pink-400/80 bg-black/60 text-zinc-400 hover:text-pink-300 transition-all active:scale-95 flex items-center justify-center cursor-pointer",
2845                        title: "Open Track Watch & Sheet Music Page in New Tab",
2846                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { size: 16 })
2847                      }
2848                    ),
2849                    /* @__PURE__ */ jsxRuntimeExports.jsx(
2850                      "button",
2851                      {
2852                        onClick: togglePreview,
2853                        className: "shrink-0 p-2.5 rounded-xl border-2 transition-all active:scale-95 shadow-lg flex items-center justify-center cursor-pointer ml-0.5",
2854                        style: {
2855                          backgroundColor: isPlayingPreview ? `${theme.primary}30` : "rgba(0,0,0,0.6)",
2856                          borderColor: theme.primary,
2857                          color: theme.primary,
2858                          boxShadow: `0 0 15px ${theme.primary}50`
2859                        },
2860                        title: "Preview Audio [Space]",
2861                        children: isPlayingPreview ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 18, className: "fill-current" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 18, className: "fill-current ml-0.5" })
2862                      }
2863                    )
2864                  ] })
2865                ] })
2866              ] }),
2867              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "my-auto flex flex-col md:flex-row items-center justify-around gap-3 py-1 overflow-hidden", children: [
2868                deckView === "radar" ? (
2869                  /* Pentagon Groove Radar */
2870                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative flex flex-col items-center justify-center p-2 rounded-2xl bg-black/40 border border-zinc-800/80 shadow-2xl shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2871                    ArcadeGrooveRadar,
2872                    {
2873                      stats: grooveRadarStats,
2874                      theme,
2875                      size: 195
2876                    }
2877                  ) })
2878                ) : (
2879                  /* Holographic Spinning Vinyl Record */
2880                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative group cursor-pointer shrink-0", onClick: togglePreview, title: "Click to Toggle Preview [Space]", children: [
2881                    /* @__PURE__ */ jsxRuntimeExports.jsx(
2882                      "div",
2883                      {
2884                        className: `w-36 h-36 md:w-40 md:h-40 rounded-full bg-gradient-to-tr from-slate-900 via-slate-950 to-cyan-950 border-4 shadow-xl flex items-center justify-center transition-transform ${isPlayingPreview ? "animate-spin [animation-duration:4s]" : "group-hover:scale-105"}`,
2885                        style: { borderColor: theme.primary, boxShadow: `0 0 30px ${theme.primary}40` },
2886                        children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-28 h-28 md:w-32 md:h-32 rounded-full border border-white/10 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-18 h-18 md:w-20 md:h-20 rounded-full border border-pink-500/30 flex items-center justify-center bg-slate-900", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 32, style: { color: theme.primary } }) }) })
2887                      }
2888                    ),
2889                    /* @__PURE__ */ jsxRuntimeExports.jsx(
2890                      "button",
2891                      {
2892                        className: "absolute inset-0 m-auto w-10 h-10 rounded-full text-slate-950 flex items-center justify-center shadow-lg active:scale-95 transition-all cursor-pointer",
2893                        style: { backgroundColor: theme.primary },
2894                        children: isPlayingPreview ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 18, className: "fill-current" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 18, className: "fill-current ml-0.5" })
2895                      }
2896                    )
2897                  ] })
2898                ),
2899                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col gap-2 w-full max-w-sm", children: [
2900                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2 text-xs", children: [
2901                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 rounded-xl bg-slate-900/80 border border-slate-800", children: [
2902                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-slate-400 block font-bold", children: t("gameCenter.tempo", "TEMPO") }),
2903                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm font-black", style: { color: theme.primary }, children: [
2904                        "BPM ",
2905                        selectedSong.bpm
2906                      ] })
2907                    ] }),
2908                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 rounded-xl bg-slate-900/80 border border-slate-800", children: [
2909                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-slate-400 block font-bold", children: t("gameCenter.fullDuration", "FULL DURATION") }),
2910                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-black text-pink-300", children: formatArcadeDuration(selectedSong.duration) })
2911                    ] })
2912                  ] }),
2913                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2.5 rounded-xl bg-slate-900/90 border border-zinc-800 flex flex-col gap-1.5", children: [
2914                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-[10px] font-bold text-zinc-400", children: [
2915                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "uppercase tracking-wider", children: t("gameCenter.difficulty", "DIFFICULTY") }),
2916                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-500 font-mono", children: [
2917                        t("gameCenter.stars", "STARS"),
2918                        ": ",
2919                        selectedSong.difficultyStars,
2920                        "/5"
2921                      ] })
2922                    ] }),
2923                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-2.5 py-1.5 rounded-lg bg-black/60 border border-zinc-800/80", children: [
2924                      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2925                        HamieFaceIcon,
2926                        {
2927                          size: 16,
2928                          active: i < selectedSong.difficultyStars,
2929                          variant: "don",
2930                          title: `Difficulty: ${selectedSong.difficultyStars}/5`
2931                        },
2932                        i
2933                      )) }),
2934                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-black px-2 py-0.5 rounded font-mono ${selectedSong.difficultyStars >= 5 ? "bg-red-950 text-red-400 border border-red-800/60" : selectedSong.difficultyStars === 4 ? "bg-amber-950 text-amber-300 border border-amber-800/60" : selectedSong.difficultyStars === 3 ? "bg-emerald-950 text-emerald-300 border border-emerald-800/60" : "bg-blue-950 text-cyan-300 border border-cyan-800/60"}`, children: selectedSong.difficultyStars >= 5 ? "MANIAC" : selectedSong.difficultyStars === 4 ? "TRICK" : selectedSong.difficultyStars === 3 ? "STANDARD" : "BASIC" })
2935                    ] })
2936                  ] }),
2937                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 rounded-xl bg-slate-900/90 border border-zinc-800 flex flex-col gap-1", children: [
2938                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-[10px] font-bold uppercase", style: { color: theme.primary }, children: [
2939                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.previewTime", "CHORUS PREVIEW") }),
2940                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
2941                        formatArcadeDuration(previewCurrentTime),
2942                        " / 0:25"
2943                      ] })
2944                    ] }),
2945                    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-end justify-between h-6 gap-1 pt-1", children: [1, 2, 3, 4, 5, 6, 7, 8].map((band) => {
2946                      const height = isPlayingPreview ? Math.max(15, Math.floor(Math.sin(previewCurrentTime * 6 + band) * 40 + 55)) : 10;
2947                      return /* @__PURE__ */ jsxRuntimeExports.jsx(
2948                        "div",
2949                        {
2950                          className: "flex-1 rounded-t-sm transition-all duration-75",
2951                          style: {
2952                            height: `${height}%`,
2953                            backgroundImage: `linear-gradient(to top, ${theme.primary}, ${theme.secondary}, ${theme.accent})`
2954                          }
2955                        },
2956                        band
2957                      );
2958                    }) })
2959                  ] })
2960                ] })
2961              ] }),
2962              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-slate-800/80 flex items-center justify-between gap-4 shrink-0", children: [
2963                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-slate-400 flex items-center gap-2", children: [
2964                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
2965                    /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-800 border border-slate-700 font-bold text-[10px]", style: { color: theme.primary }, children: "SPACE" }),
2966                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "PREVIEW" })
2967                  ] }),
2968                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
2969                    /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-800 border border-slate-700 font-bold text-[10px]", style: { color: theme.primary }, children: "ENTER" }),
2970                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "LOCK" })
2971                  ] })
2972                ] }),
2973                /* @__PURE__ */ jsxRuntimeExports.jsxs(
2974                  "button",
2975                  {
2976                    "data-proceed-cabinet": "true",
2977                    onClick: () => {
2978                      if (previewAudioRef.current) previewAudioRef.current.pause();
2979                      arcadeAudio.duckBGM(false);
2980                      arcadeAudio.playStageStart();
2981                      onProceedToCabinet();
2982                    },
2983                    className: "px-6 py-2.5 rounded-xl text-black font-black text-xs tracking-widest uppercase shadow-xl active:scale-95 transition-all flex items-center gap-2 cursor-pointer",
2984                    style: {
2985                      backgroundImage: `linear-gradient(to right, ${theme.primary}, ${theme.secondary})`,
2986                      boxShadow: `0 0 25px ${theme.primary}60`
2987                    },
2988                    children: [
2989                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "SELECT GAME CABINET [ENTER]" }),
2990                      /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 16 })
2991                    ]
2992                  }
2993                )
2994              ] })
2995            ] }),
2996            activeTab === "bookmarks" && !selectedSong && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col items-center justify-center p-8 text-center my-auto", children: [
2997              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 rounded-full bg-amber-500/10 border-2 border-amber-400/30 flex items-center justify-center mb-3 shadow-[0_0_20px_rgba(245,158,11,0.2)]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Bookmark, { className: "w-8 h-8 text-amber-400 animate-pulse" }) }),
2998              /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-black text-amber-300 tracking-wider uppercase mb-1", children: t("gameCenter.bookmarksVault", "BOOKMARKS VAULT") }),
2999              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 max-w-sm mb-4", children: t("gameCenter.bookmarksDesc", "Bookmark your favorite tunes with [B] or the bookmark button to build your personal arcade playlist.") }),
3000              /* @__PURE__ */ jsxRuntimeExports.jsx(
3001                "button",
3002                {
3003                  onClick: () => {
3004                    arcadeAudio.playSelect();
3005                    setActiveTab("site");
3006                  },
3007                  className: "px-4 py-2 rounded-xl bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-black text-xs tracking-wider transition-all active:scale-95 shadow-[0_0_15px_rgba(6,182,212,0.4)] cursor-pointer",
3008                  children: t("gameCenter.browseJukebox", "BROWSE ARCADE JUKEBOX")
3009                }
3010              )
3011            ] }),
3012            activeTab === "upload" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col items-center justify-center p-6 border-2 border-dashed border-pink-500/40 rounded-2xl bg-slate-950/60 text-center font-mono", children: [
3013              /* @__PURE__ */ jsxRuntimeExports.jsx(
3014                "input",
3015                {
3016                  type: "file",
3017                  ref: fileInputRef,
3018                  onChange: handleFileChange,
3019                  accept: ".mid,.midi",
3020                  className: "hidden"
3021                }
3022              ),
3023              /* @__PURE__ */ jsxRuntimeExports.jsxs(
3024                "div",
3025                {
3026                  onDragOver: (e) => {
3027                    e.preventDefault();
3028                    setDragOver(true);
3029                  },
3030                  onDragLeave: () => setDragOver(false),
3031                  onDrop: handleDrop,
3032                  className: `w-full max-w-md p-6 rounded-2xl flex flex-col items-center gap-3 transition-all ${dragOver ? "bg-pink-950/40 scale-102 border border-pink-500" : "bg-slate-900/60"}`,
3033                  children: [
3034                    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 rounded-2xl bg-pink-500/10 border border-pink-500/30 text-pink-400 shadow-[0_0_20px_rgba(236,72,153,0.3)]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FileMusic, { size: 36, className: "animate-pulse" }) }),
3035                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
3036                      /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-black text-white tracking-wider mb-1", children: "DROP .MID / .MIDI CARTRIDGE" }),
3037                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-pink-400 font-bold uppercase tracking-widest", children: "AUTOPARSER READY" })
3038                    ] }),
3039                    /* @__PURE__ */ jsxRuntimeExports.jsxs(
3040                      "button",
3041                      {
3042                        onClick: () => {
3043                          var _a;
3044                          return (_a = fileInputRef.current) == null ? void 0 : _a.click();
3045                        },
3046                        disabled: isParsingFile,
3047                        className: "flex items-center gap-2 px-5 py-2.5 rounded-xl bg-gradient-to-r from-pink-500 to-rose-600 hover:from-pink-400 text-white font-black text-xs tracking-wider shadow-[0_0_20px_rgba(244,63,94,0.4)] active:scale-95 transition-all cursor-pointer",
3048                        children: [
3049                          isParsingFile ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 14 }),
3050                          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: isParsingFile ? "PARSING..." : "CHOOSE LOCAL MIDI FILE" })
3051                        ]
3052                      }
3053                    )
3054                  ]
3055                }
3056              )
3057            ] }),
3058            activeTab === "url" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full flex flex-col items-center justify-center p-6 border-2 border-amber-500/40 rounded-2xl bg-slate-950/60 font-mono", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleUrlSubmit, className: "w-full max-w-md flex flex-col gap-4", children: [
3059              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-amber-400 text-xs font-black tracking-widest uppercase", children: [
3060                /* @__PURE__ */ jsxRuntimeExports.jsx(Link, { size: 16 }),
3061                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "CYBER TERMINAL // URL INGESTION" })
3062              ] }),
3063              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [
3064                /* @__PURE__ */ jsxRuntimeExports.jsx(
3065                  "input",
3066                  {
3067                    type: "url",
3068                    required: true,
3069                    value: urlInput,
3070                    onChange: (e) => setUrlInput(e.target.value),
3071                    placeholder: "https://hamienet.com/track/1234 or https://site.com/song.mid",
3072                    className: "w-full px-4 py-2.5 rounded-xl bg-slate-900 border border-amber-500/40 text-amber-200 placeholder-slate-600 text-xs focus:outline-none focus:border-amber-400"
3073                  }
3074                ),
3075                /* @__PURE__ */ jsxRuntimeExports.jsx(
3076                  "button",
3077                  {
3078                    type: "submit",
3079                    disabled: isParsingFile || !urlInput.trim(),
3080                    className: "w-full py-2.5 rounded-xl bg-gradient-to-r from-amber-500 to-orange-600 hover:from-amber-400 text-white font-black text-xs tracking-wider shadow-[0_0_15px_rgba(245,158,11,0.4)] active:scale-95 transition-all",
3081                    children: isParsingFile ? "FETCHING..." : "LOAD URL INTO ARCADE"
3082                  }
3083                )
3084              ] }),
3085              urlError && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-400 bg-red-950/50 border border-red-500/40 px-3 py-2 rounded-lg", children: urlError })
3086            ] }) })
3087          ]
3088        }
3089      ),
3090      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "lg:col-span-5 flex flex-col h-full overflow-hidden relative select-none pr-1", children: [
3091        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 mb-2 shrink-0 px-1", children: [
3092          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full", children: [
3093            /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 text-cyan-400 w-3.5 h-3.5" }),
3094            /* @__PURE__ */ jsxRuntimeExports.jsx(
3095              "input",
3096              {
3097                type: "text",
3098                value: searchQuery,
3099                onChange: (e) => setSearchQuery(e.target.value),
3100                placeholder: "FILTER TRACKS...",
3101                className: "w-full pl-9 pr-3 py-1.5 rounded-lg bg-slate-900/90 border border-cyan-500/40 text-cyan-200 placeholder-slate-500 text-xs font-mono focus:outline-none focus:border-cyan-400 focus:shadow-[0_0_12px_rgba(6,182,212,0.4)]"
3102              }
3103            )
3104          ] }),
3105          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1 overflow-x-auto custom-scrollbar text-[10px]", children: CATEGORIES.map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3106            "button",
3107            {
3108              onClick: () => {
3109                arcadeAudio.playSelect();
3110                setSelectedCategory(cat.key);
3111              },
3112              className: `px-2 py-1 rounded font-bold whitespace-nowrap transition-all ${selectedCategory === cat.key ? "bg-cyan-500 text-slate-950 font-black shadow-[0_0_10px_rgba(6,182,212,0.6)]" : "bg-slate-900/80 text-slate-400 border border-slate-800 hover:text-cyan-300"}`,
3113              children: cat.label
3114            },
3115            cat.key
3116          )) })
3117        ] }),
3118        activeTab === "bookmarks" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
3119          "button",
3120          {
3121            onClick: () => {
3122              arcadeAudio.playRoulette();
3123              setRefreshTrigger((v) => v + 1);
3124            },
3125            disabled: isLoadingTracks,
3126            className: "w-full py-1.5 px-3 rounded-lg bg-gradient-to-r from-amber-500/20 via-yellow-500/30 to-amber-500/20 hover:from-amber-500/30 hover:via-yellow-500/40 hover:to-amber-500/30 border border-amber-400/50 hover:border-amber-300 text-amber-300 flex items-center justify-center gap-2 text-xs font-black uppercase tracking-widest transition-all active:scale-95 cursor-pointer shadow-[0_0_15px_rgba(245,158,11,0.25)] hover:shadow-[0_0_25px_rgba(245,158,11,0.45)] mb-1.5 shrink-0",
3127            title: "Refresh Bookmarks list [X]",
3128            children: [
3129              /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 13, className: isLoadingTracks ? "animate-spin text-amber-300" : "text-amber-400" }),
3130              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.refreshBookmarks", "REFRESH BOOKMARKS [X]") })
3131            ]
3132          }
3133        ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
3134          "button",
3135          {
3136            onClick: handleSwap50Songs,
3137            disabled: isLoadingTracks,
3138            className: "w-full py-1.5 px-3 rounded-lg bg-gradient-to-r from-amber-500/20 via-orange-500/30 to-amber-500/20 hover:from-amber-500/30 hover:via-orange-500/40 hover:to-amber-500/30 border border-amber-400/50 hover:border-amber-300 text-amber-300 flex items-center justify-center gap-2 text-xs font-black uppercase tracking-widest transition-all active:scale-95 cursor-pointer shadow-[0_0_15px_rgba(245,158,11,0.25)] hover:shadow-[0_0_25px_rgba(245,158,11,0.45)] mb-1.5 shrink-0",
3139            title: "Swap for 50 new random tracks [X]",
3140            children: [
3141              /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 13, className: isLoadingTracks ? "animate-spin text-amber-300" : "text-amber-400" }),
3142              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.swap50Songs", "SWAP 50 SONGS [X]") })
3143            ]
3144          }
3145        ),
3146        /* @__PURE__ */ jsxRuntimeExports.jsxs(
3147          "button",
3148          {
3149            onClick: () => stepWheel(-1),
3150            disabled: tracks.length === 0,
3151            className: "w-full py-1 rounded bg-slate-900/90 border border-cyan-500/30 hover:border-cyan-400 text-cyan-300 flex items-center justify-center gap-1 text-[10px] font-black uppercase tracking-widest shrink-0 transition-all active:scale-95 cursor-pointer mb-1 shadow-md",
3152            children: [
3153              /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }),
3154              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "▲ UP [W]" })
3155            ]
3156          }
3157        ),
3158        /* @__PURE__ */ jsxRuntimeExports.jsx(
3159          "div",
3160          {
3161            id: "arcade-song-wheel-list",
3162            ref: wheelContainerRef,
3163            onPointerDown: handlePointerDown,
3164            onPointerMove: handlePointerMove,
3165            onPointerUp: handlePointerUp,
3166            onPointerCancel: handlePointerUp,
3167            style: { perspective: 900 },
3168            className: "flex-1 relative my-1 overflow-hidden select-none touch-none cursor-grab active:cursor-grabbing flex flex-col justify-center",
3169            children: isLoadingTracks ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-2 py-10", children: [
3170              /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-6 h-6 text-cyan-400 animate-spin" }),
3171              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-cyan-300 tracking-widest uppercase animate-pulse", children: t("gameCenter.loadingWheel", "LOADING WHEEL...") })
3172            ] }) : tracks.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full flex flex-col items-center justify-center text-center p-6 border border-dashed border-slate-800 rounded-xl", children: activeTab === "bookmarks" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3173              /* @__PURE__ */ jsxRuntimeExports.jsx(Bookmark, { className: "w-8 h-8 text-amber-400/60 mb-2 animate-bounce" }),
3174              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-amber-300 font-bold mb-1", children: t("gameCenter.noBookmarks", "NO BOOKMARKED SONGS") }),
3175              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-slate-400 max-w-[220px]", children: [
3176                "Press ",
3177                /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1 py-0.5 rounded bg-slate-800 border border-slate-700 text-amber-400 font-bold", children: "[B]" }),
3178                " or tap the bookmark icon on any song to save it!"
3179              ] })
3180            ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3181              /* @__PURE__ */ jsxRuntimeExports.jsx(Music2, { className: "w-8 h-8 text-slate-600 mb-2" }),
3182              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-slate-400 font-bold", children: t("gameCenter.noTracksFound", "NO TRACKS FOUND") })
3183            ] }) }) : (
3184              // Render 11 infinite cyclic virtual slots (-5 to +5 around displayPos)
3185              [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5].map((k) => {
3186                var _a;
3187                const centerSlot = Math.round(displayPos);
3188                const slotIdx = centerSlot + k;
3189                const delta = slotIdx - displayPos;
3190                const absDelta = Math.abs(delta);
3191                const normalizedIdx = (slotIdx % tracks.length + tracks.length) % tracks.length;
3192                const track = tracks[normalizedIdx];
3193                if (!track) return null;
3194                const isSelected = absDelta < 0.45;
3195                const isAbove = delta < -0.3;
3196                const curveX = isSelected ? 0 : Math.min(32, Math.round(Math.pow(absDelta, 1.2) * 6) + Math.round(12 * Math.min(1, absDelta)));
3197                const opacity = isSelected ? 1 : Math.max(0.2, 1 - absDelta * 0.15);
3198                const zIndex = isSelected ? 30 : Math.max(1, Math.round(25 - absDelta * 4));
3199                const stars = track._cachedStars ?? calculateStars(track);
3200                const tempo = track._cachedTempo ?? Math.round(((_a = track.midiMetadata) == null ? void 0 : _a.tempo) || 120);
3201                const gradeBadge = stars >= 4 ? "AA" : stars === 3 ? "A" : "NO DATA";
3202                const kanjiBadge = stars >= 5 ? "鬼" : stars === 4 ? "激" : stars === 3 ? "踊" : "楽";
3203                const titleColor = isSelected ? "#ffffff" : isAbove ? "#ffe600" : "#00ff66";
3204                const borderedTitleStyle = {
3205                  paintOrder: "stroke fill",
3206                  WebkitTextStroke: isSelected ? "3px #000000" : "2.2px #000000",
3207                  color: titleColor,
3208                  textShadow: isSelected ? "0 0 16px rgba(0, 240, 255, 0.9), 2px 2px 0 #000, -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 0 3px 6px rgba(0,0,0,0.9)" : "2px 2px 0 #000, -1.5px -1.5px 0 #000, 1.5px -1.5px 0 #000, -1.5px 1.5px 0 #000, 0 2px 4px rgba(0,0,0,0.8)"
3209                };
3210                return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3211                  "div",
3212                  {
3213                    onClick: () => {
3214                      if (!dragMovedRef.current) {
3215                        if (isSelected) {
3216                          if (previewAudioRef.current) previewAudioRef.current.pause();
3217                          arcadeAudio.duckBGM(false);
3218                          arcadeAudio.playStageStart();
3219                          onProceedToCabinet();
3220                        } else {
3221                          setWheelTarget(slotIdx, true);
3222                        }
3223                      }
3224                    },
3225                    "data-cartridge-id": track.id,
3226                    "data-cartridge-idx": normalizedIdx,
3227                    "data-slot-k": k,
3228                    style: {
3229                      position: "absolute",
3230                      top: "50%",
3231                      left: `${6 + curveX}px`,
3232                      right: "8px",
3233                      transform: `translateY(-50%) translateY(${delta * 60}px) scaleY(${isSelected ? 1.05 : 0.95})`,
3234                      transformOrigin: "center center",
3235                      zIndex,
3236                      opacity,
3237                      ...isSelected ? {
3238                        boxShadow: `0 0 30px ${theme.cartridgeActiveGlow}, inset 0 0 16px rgba(255,255,255,0.35)`,
3239                        borderColor: "#67e8f9"
3240                      } : {}
3241                    },
3242                    className: `group flex items-center justify-between cursor-pointer select-none transition-colors duration-100 ${isSelected ? "py-2 px-3 rounded-l-full rounded-r-xl border-y-2 border-l-2 bg-gradient-to-r from-blue-700 via-cyan-600 to-blue-950/90 shadow-2xl ring-1 ring-white/60" : "py-1.5 px-2 rounded-lg hover:bg-white/10 border border-transparent"}`,
3243                    children: [
3244                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5 min-w-0 flex-1", children: [
3245                        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 flex items-center", children: isSelected ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-6 h-6 rounded-md flex items-center justify-center bg-gradient-to-tr from-cyan-400 to-white shadow-[0_0_12px_rgba(0,240,255,0.9)]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 18, className: "stroke-[3.5] text-slate-950 ml-0.5" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 h-5 rounded flex items-center justify-center bg-black/40 border border-white/10 group-hover:border-white/30 transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 14, className: `stroke-[3] ${isAbove ? "text-yellow-400" : "text-emerald-400"}` }) }) }),
3246                        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
3247                          /* @__PURE__ */ jsxRuntimeExports.jsx(
3248                            "h4",
3249                            {
3250                              className: "text-sm sm:text-base font-black italic tracking-wide truncate select-none uppercase drop-shadow-md",
3251                              style: borderedTitleStyle,
3252                              children: track.title
3253                            }
3254                          ),
3255                          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs truncate", children: [
3256                            (track._cachedArtist || track.artist) && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3257                              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `truncate max-w-[120px] sm:max-w-[180px] font-bold italic tracking-wide ${isSelected ? "text-cyan-100" : isAbove ? "text-yellow-200/80" : "text-emerald-200/80"}`, children: [
3258                                "/ ",
3259                                track._cachedArtist || track.artist
3260                              ] }),
3261                              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "opacity-40 text-white", children: "•" })
3262                            ] }),
3263                            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `font-black font-mono text-[11px] ${isSelected ? "text-white" : isAbove ? "text-yellow-300" : "text-emerald-300"}`, children: [
3264                              "BPM ",
3265                              tempo
3266                            ] })
3267                          ] })
3268                        ] })
3269                      ] }),
3270                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 shrink-0 ml-2", children: [
3271                        isSelected && /* @__PURE__ */ jsxRuntimeExports.jsx(
3272                          "button",
3273                          {
3274                            type: "button",
3275                            onClick: (e) => {
3276                              e.stopPropagation();
3277                              if (previewAudioRef.current) previewAudioRef.current.pause();
3278                              arcadeAudio.duckBGM(false);
3279                              arcadeAudio.playStageStart();
3280                              onProceedToCabinet();
3281                            },
3282                            className: "px-2 py-0.5 rounded bg-yellow-400 hover:bg-yellow-300 active:scale-95 text-black font-black text-[9px] tracking-wider uppercase animate-pulse shrink-0 shadow-md cursor-pointer transition-all",
3283                            title: "Select Game Cabinet [ENTER]",
3284                            children: "◀ PLAY"
3285                          }
3286                        ),
3287                        isSelected && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3288                          "button",
3289                          {
3290                            type: "button",
3291                            onClick: (e) => {
3292                              e.stopPropagation();
3293                              if (isPlayingPreview && previewAudioRef.current) {
3294                                previewAudioRef.current.pause();
3295                                setIsPlayingPreview(false);
3296                                arcadeAudio.duckBGM(false);
3297                              } else if (selectedSongRef.current) {
3298                                playChorusPreviewRef.current(selectedSongRef.current);
3299                              }
3300                            },
3301                            className: "flex items-end gap-0.5 h-4 px-1.5 py-0.5 rounded border border-cyan-300 bg-cyan-400/20 hover:bg-cyan-400/40 cursor-pointer transition-all active:scale-95 shrink-0",
3302                            title: isPlayingPreview ? "Pause audio preview [SPACE]" : "Play audio preview [SPACE]",
3303                            children: [
3304                              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-0.5 h-2 bg-cyan-300 ${isPlayingPreview ? "animate-pulse" : "h-1.5"}` }),
3305                              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-0.5 h-3 bg-cyan-300 ${isPlayingPreview ? "animate-pulse delay-75" : "h-2"}` }),
3306                              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-0.5 h-1.5 bg-cyan-300 ${isPlayingPreview ? "animate-pulse delay-150" : "h-1"}` })
3307                            ]
3308                          }
3309                        ),
3310                        theme.badgeStyle === "grade" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
3311                          "span",
3312                          {
3313                            className: `px-1.5 py-0.5 rounded-full font-black text-[9px] border shadow-sm shrink-0 ${gradeBadge === "NO DATA" ? "bg-blue-950/60 text-cyan-400 border-cyan-800/40 text-[8px]" : "bg-cyan-400/20 text-cyan-300 border-cyan-400/80 shadow-[0_0_8px_rgba(0,240,255,0.4)]"}`,
3314                            children: gradeBadge
3315                          }
3316                        ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
3317                          "span",
3318                          {
3319                            className: `px-1.5 py-0.5 rounded font-black text-[10px] shadow-md shrink-0 ${kanjiBadge === "鬼" ? "bg-red-600 text-white" : kanjiBadge === "激" ? "bg-emerald-500 text-black font-extrabold" : kanjiBadge === "踊" ? "bg-amber-500 text-black font-extrabold" : "bg-blue-500 text-white"}`,
3320                            children: kanjiBadge
3321                          }
3322                        ),
3323                        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-0.5 shrink-0", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3324                          HamieFaceIcon,
3325                          {
3326                            size: 11,
3327                            active: i < stars,
3328                            variant: theme.id === "sunset" ? "neon" : theme.id === "5thmix" ? "don" : "gold",
3329                            title: `Difficulty: ${stars}/5`
3330                          },
3331                          i
3332                        )) })
3333                      ] })
3334                    ]
3335                  },
3336                  slotIdx
3337                );
3338              })
3339            )
3340          }
3341        ),
3342        /* @__PURE__ */ jsxRuntimeExports.jsxs(
3343          "button",
3344          {
3345            onClick: () => stepWheel(1),
3346            disabled: tracks.length === 0,
3347            className: "w-full py-1 rounded bg-slate-900/90 border border-cyan-500/30 hover:border-cyan-400 text-cyan-300 flex items-center justify-center gap-1 text-[10px] font-black uppercase tracking-widest shrink-0 transition-all active:scale-95 cursor-pointer mt-1",
3348            children: [
3349              /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }),
3350              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
3351                "▼ ",
3352                t("gameCenter.down", "DOWN"),
3353                " [S]"
3354              ] })
3355            ]
3356          }
3357        )
3358      ] })
3359    ] }),
3360    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 px-3 py-1.5 rounded-xl bg-slate-950/90 border border-cyan-500/30 text-[11px] font-mono text-slate-400 shrink-0", children: [
3361      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3.5 flex-wrap", children: [
3362        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-cyan-400 font-bold uppercase tracking-widest flex items-center gap-1", children: [
3363          /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 12 }),
3364          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "HAMIE ARCADE:" })
3365        ] }),
3366        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-yellow-300 font-bold", children: [
3367          /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-900 border border-slate-700 text-yellow-300 font-bold text-[10px]", children: "▲ ▼ / W S" }),
3368          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.select", "SELECT") })
3369        ] }),
3370        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-cyan-300 font-bold", children: [
3371          /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-900 border border-slate-700 text-cyan-300 font-bold text-[10px]", children: "ENTER" }),
3372          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.decide", "DECIDE") })
3373        ] }),
3374        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [
3375          /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-900 border border-slate-700 text-pink-300 font-bold text-[10px]", children: "SPACE" }),
3376          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.preview", "PREVIEW") })
3377        ] }),
3378        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [
3379          /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-900 border border-slate-700 text-amber-300 font-bold text-[10px]", children: "R" }),
3380          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.roulette", "ROULETTE") })
3381        ] }),
3382        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [
3383          /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-900 border border-slate-700 text-purple-300 font-bold text-[10px]", children: "T" }),
3384          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.style", "STYLE") })
3385        ] }),
3386        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 text-amber-300 font-bold", children: [
3387          /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-900 border border-slate-700 text-amber-300 font-bold text-[10px]", children: "B" }),
3388          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.bookmarks", "BOOKMARKS") })
3389        ] }),
3390        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [
3391          /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "px-1.5 py-0.5 rounded bg-slate-900 border border-slate-700 text-emerald-300 font-bold text-[10px]", children: "X" }),
3392          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.swap", "SWAP") })
3393        ] })
3394      ] }),
3395      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-emerald-400 font-bold hidden md:flex items-center gap-1.5", children: [
3396        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse" }),
3397        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.eventMode", "EVENT MODE // ALL TRACKS UNLOCKED") })
3398      ] })
3399    ] })
3400  ] });
3401};
3402const PIANO_SPEEDS = [1, 1.2, 1.5, 2];
3403const TAIKO_DIFFICULTIES = ["kantan", "futsuu", "muzukashii", "oni"];
3404const MEOW_CHOIR_DIFFICULTIES = ["kitten", "playful", "wildcat", "meowstro"];
3405const RHYTHM_TEST_DIFFICULTIES = ["easy", "medium", "hard", "master"];
3406const GameModeSelector = ({
3407  selectedSong,
3408  onBackToSongSelect,
3409  onLaunchGame,
3410  theme = ARCADE_THEMES["5thmix"]
3411}) => {
3412  const { t } = useTranslation();
3413  const [selectedModuleId, setSelectedModuleId] = reactExports.useState("piano-roll");
3414  const [pianoSpeed, setPianoSpeed] = reactExports.useState(1.2);
3415  const [melodyOnly, setMelodyOnly] = reactExports.useState(false);
3416  const [taikoDifficulty, setTaikoDifficulty] = reactExports.useState("futsuu");
3417  const [meowChoirDifficulty, setMeowChoirDifficulty] = reactExports.useState("playful");
3418  const [rhythmDifficulty, setRhythmDifficulty] = reactExports.useState("medium");
3419  const activeModule = GAME_MODULES.find((m) => m.id === selectedModuleId) || GAME_MODULES[0];
3420  const handleLaunch = () => {
3421    arcadeAudio.playStageStart();
3422    const settings = {};
3423    if (selectedModuleId === "piano-roll") {
3424      settings.speed = pianoSpeed;
3425      settings.melodyOnly = melodyOnly;
3426    } else if (selectedModuleId === "taiko") {
3427      settings.difficulty = taikoDifficulty;
3428    } else if (selectedModuleId === "meow-choir") {
3429      settings.difficulty = meowChoirDifficulty;
3430    } else if (selectedModuleId === "rhythm-test") {
3431      settings.difficulty = rhythmDifficulty;
3432    }
3433    onLaunchGame(activeModule, settings);
3434  };
3435  reactExports.useEffect(() => {
3436    const handleKeyDown = (e) => {
3437      const moduleIds = GAME_MODULES.map((m) => m.id);
3438      const curIdx = moduleIds.indexOf(selectedModuleId);
3439      if (e.key === "ArrowLeft" || e.key === "a" || e.key === "A") {
3440        e.preventDefault();
3441        arcadeAudio.playWheelTick();
3442        const nextIdx = (curIdx - 1 + moduleIds.length) % moduleIds.length;
3443        setSelectedModuleId(moduleIds[nextIdx]);
3444      } else if (e.key === "ArrowRight" || e.key === "d" || e.key === "D") {
3445        e.preventDefault();
3446        arcadeAudio.playWheelTick();
3447        const nextIdx = (curIdx + 1) % moduleIds.length;
3448        setSelectedModuleId(moduleIds[nextIdx]);
3449      } else if (e.key === "ArrowUp" || e.key === "w" || e.key === "W") {
3450        e.preventDefault();
3451        arcadeAudio.playSelect();
3452        if (selectedModuleId === "piano-roll") {
3453          const idx = PIANO_SPEEDS.indexOf(pianoSpeed);
3454          const next = Math.min(PIANO_SPEEDS.length - 1, idx + 1);
3455          setPianoSpeed(PIANO_SPEEDS[next]);
3456        } else if (selectedModuleId === "taiko") {
3457          const idx = TAIKO_DIFFICULTIES.indexOf(taikoDifficulty);
3458          const next = Math.min(TAIKO_DIFFICULTIES.length - 1, idx + 1);
3459          setTaikoDifficulty(TAIKO_DIFFICULTIES[next]);
3460        } else if (selectedModuleId === "meow-choir") {
3461          const idx = MEOW_CHOIR_DIFFICULTIES.indexOf(meowChoirDifficulty);
3462          const next = Math.min(MEOW_CHOIR_DIFFICULTIES.length - 1, idx + 1);
3463          setMeowChoirDifficulty(MEOW_CHOIR_DIFFICULTIES[next]);
3464        } else if (selectedModuleId === "rhythm-test") {
3465          const idx = RHYTHM_TEST_DIFFICULTIES.indexOf(rhythmDifficulty);
3466          const next = Math.min(RHYTHM_TEST_DIFFICULTIES.length - 1, idx + 1);
3467          setRhythmDifficulty(RHYTHM_TEST_DIFFICULTIES[next]);
3468        }
3469      } else if (e.key === "ArrowDown" || e.key === "s" || e.key === "S") {
3470        e.preventDefault();
3471        arcadeAudio.playSelect();
3472        if (selectedModuleId === "piano-roll") {
3473          const idx = PIANO_SPEEDS.indexOf(pianoSpeed);
3474          const next = Math.max(0, idx - 1);
3475          setPianoSpeed(PIANO_SPEEDS[next]);
3476        } else if (selectedModuleId === "taiko") {
3477          const idx = TAIKO_DIFFICULTIES.indexOf(taikoDifficulty);
3478          const next = Math.max(0, idx - 1);
3479          setTaikoDifficulty(TAIKO_DIFFICULTIES[next]);
3480        } else if (selectedModuleId === "meow-choir") {
3481          const idx = MEOW_CHOIR_DIFFICULTIES.indexOf(meowChoirDifficulty);
3482          const next = Math.max(0, idx - 1);
3483          setMeowChoirDifficulty(MEOW_CHOIR_DIFFICULTIES[next]);
3484        } else if (selectedModuleId === "rhythm-test") {
3485          const idx = RHYTHM_TEST_DIFFICULTIES.indexOf(rhythmDifficulty);
3486          const next = Math.max(0, idx - 1);
3487          setRhythmDifficulty(RHYTHM_TEST_DIFFICULTIES[next]);
3488        }
3489      } else if (e.key === "Enter" || e.key === " ") {
3490        e.preventDefault();
3491        handleLaunch();
3492      } else if (e.key === "Escape") {
3493        e.preventDefault();
3494        arcadeAudio.playSelect();
3495        onBackToSongSelect();
3496      }
3497    };
3498    window.addEventListener("keydown", handleKeyDown);
3499    return () => window.removeEventListener("keydown", handleKeyDown);
3500  }, [selectedModuleId, pianoSpeed, taikoDifficulty, meowChoirDifficulty, rhythmDifficulty, onBackToSongSelect]);
3501  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-full max-h-[calc(100vh-80px)] flex flex-col justify-between overflow-hidden select-none font-mono text-white animate-in fade-in duration-300", children: [
3502    /* @__PURE__ */ jsxRuntimeExports.jsx(
3503      "div",
3504      {
3505        className: "flex items-center justify-between gap-3 px-3 py-2 rounded-xl bg-slate-950/80 border-2 shadow-lg shrink-0 transition-all duration-300",
3506        style: {
3507          borderColor: `${theme.primary}55`,
3508          boxShadow: `0 0 15px ${theme.primary}20`
3509        },
3510        children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
3511          /* @__PURE__ */ jsxRuntimeExports.jsxs(
3512            "button",
3513            {
3514              onClick: () => {
3515                arcadeAudio.playSelect();
3516                onBackToSongSelect();
3517              },
3518              className: "flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-slate-900 border border-slate-700 hover:border-cyan-400 text-slate-300 hover:text-white text-xs font-bold transition-all active:scale-95 cursor-pointer",
3519              children: [
3520                /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 14 }),
3521                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
3522                  "[ESC] ",
3523                  t("gameCenter.changeSong", "CHANGE SONG")
3524                ] })
3525              ]
3526            }
3527          ),
3528          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
3529            /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 16, className: "animate-spin", style: { color: theme.primary } }),
3530            /* @__P
3530URE__ */ jsxRuntimeExports.jsxs("div", { children: [
3531              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-bold uppercase tracking-widest block", style: { color: theme.primary }, children: [
3532                t("gameCenter.selectedTrack", "SELECTED TRACK"),
3533                " • BPM ",
3534                selectedSong.bpm
3535              ] }),
3536              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-black text-white line-clamp-1", children: selectedSong.title })
3537            ] })
3538          ] })
3539        ] })
3540      }
3541    ),
3542    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 my-2 min-h-0 overflow-hidden", children: GAME_MODULES.map((module) => {
3543      var _a;
3544      const isSelected = module.id === selectedModuleId;
3545      const isPiano = module.id === "piano-roll";
3546      const isTaiko = module.id === "taiko";
3547      const isMeowChoir = module.id === "meow-choir";
3548      const isRhythmTest = module.id === "rhythm-test";
3549      return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3550        "div",
3551        {
3552          onClick: () => {
3553            arcadeAudio.playWheelTick();
3554            setSelectedModuleId(module.id);
3555          },
3556          className: `group relative flex flex-col justify-between p-4 rounded-2xl border-2 transition-all duration-200 cursor-pointer overflow-hidden ${isSelected ? isPiano ? "border-cyan-400 bg-gradient-to-b from-cyan-950/70 via-slate-900/90 to-slate-950 shadow-[0_0_30px_rgba(6,182,212,0.4)] scale-[1.01]" : isTaiko ? "border-rose-400 bg-gradient-to-b from-rose-950/70 via-slate-900/90 to-slate-950 shadow-[0_0_30px_rgba(244,63,94,0.4)] scale-[1.01]" : isMeowChoir ? "border-purple-400 bg-gradient-to-b from-purple-950/70 via-slate-900/90 to-slate-950 shadow-[0_0_30px_rgba(168,85,247,0.4)] scale-[1.01]" : "border-amber-400 bg-gradient-to-b from-purple-950/70 via-slate-900/90 to-amber-950/50 shadow-[0_0_30px_rgba(245,158,11,0.4)] scale-[1.01]" : "border-slate-800 bg-slate-950/60 hover:border-slate-700 opacity-75 hover:opacity-100"}`,
3557          children: [
3558            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pb-3 border-b border-slate-800/80", children: [
3559              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
3560                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `p-2 rounded-xl ${isPiano ? "bg-cyan-500/20 text-cyan-300 border border-cyan-500/40" : isTaiko ? "bg-rose-500/20 text-rose-300 border border-rose-500/40" : isMeowChoir ? "bg-purple-500/20 text-purple-300 border border-purple-500/40" : "bg-amber-500/20 text-amber-300 border border-amber-500/40"}`, children: isPiano ? /* @__PURE__ */ jsxRuntimeExports.jsx(Keyboard, { size: 24 }) : isTaiko ? /* @__PURE__ */ jsxRuntimeExports.jsx(Drum, { size: 24 }) : isMeowChoir ? /* @__PURE__ */ jsxRuntimeExports.jsx(PawPrint, { size: 24 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Boxes, { size: 24 }) }),
3561                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
3562                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-slate-400 font-bold uppercase tracking-widest block", children: ((_a = module.cabinetStyle) == null ? void 0 : _a.marqueeText) || module.tagline }),
3563                  /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-black text-white tracking-wider", children: module.name })
3564                ] })
3565              ] }),
3566              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-wider ${isPiano ? "bg-cyan-950 border border-cyan-500/40 text-cyan-300" : isTaiko ? "bg-rose-950 border border-rose-500/40 text-rose-300" : isMeowChoir ? "bg-purple-950 border border-purple-500/40 text-purple-300" : "bg-amber-950 border border-amber-500/40 text-amber-300"}`, children: isPiano ? "RHYTHM SYNTH" : isTaiko ? "ARCADE DRUM" : isMeowChoir ? "KITTEN CHOIR" : "RHYTHM TEST" })
3567            ] }),
3568            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative my-2 rounded-xl overflow-hidden border-2 border-slate-700/80 bg-slate-950 aspect-video flex flex-col items-center justify-center group/preview shadow-[inset_0_0_25px_rgba(0,0,0,0.9)]", children: [
3569              isPiano && /* @__PURE__ */ jsxRuntimeExports.jsx(
3570                "img",
3571                {
3572                  src: "/assets/games/preview_pianoroll.png",
3573                  alt: "Piano Roll Rush Gameplay",
3574                  className: "w-full h-full object-cover group-hover/preview:scale-105 transition-transform duration-500"
3575                }
3576              ),
3577              isTaiko && /* @__PURE__ */ jsxRuntimeExports.jsx(
3578                "img",
3579                {
3580                  src: "/assets/games/preview_taiko.png",
3581                  alt: "Taiko no Hamie Gameplay",
3582                  className: "w-full h-full object-cover group-hover/preview:scale-105 transition-transform duration-500"
3583                }
3584              ),
3585              isMeowChoir && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-full bg-gradient-to-br from-indigo-950 via-slate-900 to-purple-950 flex flex-col items-center justify-center relative overflow-hidden group-hover/preview:scale-105 transition-transform duration-500", children: [
3586                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -top-6 -left-6 w-24 h-24 rounded-full bg-purple-500/20 blur-xl animate-pulse" }),
3587                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -bottom-6 -right-6 w-24 h-24 rounded-full bg-pink-500/20 blur-xl animate-pulse" }),
3588                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-2 left-6 w-8 h-8 rounded-full border-2 border-purple-300/80 bg-purple-400/20 shadow-[0_0_12px_rgba(192,132,252,0.6)] flex items-center justify-center text-xs animate-bounce", children: "♪" }),
3589                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-6 right-8 w-10 h-10 rounded-full border-2 border-amber-300/80 bg-amber-400/20 shadow-[0_0_12px_rgba(251,191,36,0.6)] flex items-center justify-center text-xs animate-pulse", children: "★" }),
3590                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-6 left-12 w-9 h-9 rounded-full border-2 border-pink-300/80 bg-pink-400/20 shadow-[0_0_12px_rgba(244,114,182,0.6)] flex items-center justify-center text-xs", children: "♫" }),
3591                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center z-10", children: [
3592                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-14 h-14 rounded-full bg-purple-500/20 border-2 border-purple-400 flex items-center justify-center shadow-[0_0_20px_rgba(168,85,247,0.5)]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(PawPrint, { size: 28, className: "text-purple-300 animate-pulse" }) }),
3593                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] font-black text-purple-300 tracking-wider mt-1 uppercase", children: "MEOW CHOIR" })
3594                ] })
3595              ] }),
3596              isRhythmTest && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-full bg-gradient-to-br from-purple-950 via-zinc-900 to-stone-950 flex flex-col items-center justify-center relative overflow-hidden group-hover/preview:scale-105 transition-transform duration-500", children: [
3597                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 gap-1 p-2 bg-black/40 rounded-lg border border-purple-500/30", children: [1, 1, 1, 4, 1, 1, 1, 4, 2, 1, 2, 4, 2, 1, 2, 4].map((d, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3598                  "div",
3599                  {
3600                    className: `w-3.5 h-3.5 rounded flex items-center justify-center ${i < 3 ? "bg-[#e05244]" : i === 3 ? "bg-purple-600 shadow-[0_0_6px_purple]" : "bg-zinc-700"}`,
3601                    children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1 h-1 rounded-full bg-white" })
3602                  },
3603                  i
3604                )) }),
3605                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] font-black text-amber-300 tracking-wider mt-1.5 uppercase flex items-center gap-1", children: [
3606                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "♩ = 160 BPM" }),
3607                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
3608                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "BONGO CAT" })
3609                ] })
3610              ] }),
3611              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/85 via-transparent to-black/30 pointer-events-none" }),
3612              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-2 left-3 right-3 flex items-center justify-between pointer-events-none", children: [
3613                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] font-black uppercase tracking-wider text-white drop-shadow-[0_2px_4px_rgba(0,0,0,0.9)] flex items-center gap-1.5", children: [
3614                  /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 12, className: isPiano ? "text-cyan-400" : isTaiko ? "text-rose-400" : isMeowChoir ? "text-purple-400" : "text-amber-400" }),
3615                  isPiano ? t("gameCenter.keyboardHighway", "KEYBOARD HIGHWAY // 88 KEYS") : isTaiko ? t("gameCenter.wadaikoDrum", "WADAIKO DRUM // FESTIVAL") : isMeowChoir ? t("gameCenter.meowChoirStage", "KITTEN STAGE // 4 KEYS") : "MEASURE GRID // BONGO CAT"
3616                ] }),
3617                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono font-bold px-1.5 py-0.5 rounded bg-black/60 backdrop-blur-sm border border-white/20 text-slate-300", children: t("gameCenter.preview", "PREVIEW") })
3618              ] })
3619            ] }),
3620            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-slate-800/80", children: [
3621              isPiano && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3622                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs text-slate-300", children: [
3623                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-cyan-400", children: [
3624                    /* @__PURE__ */ jsxRuntimeExports.jsx(Gauge, { size: 13 }),
3625                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.scrollSpeed", "SCROLL SPEED") })
3626                  ] }),
3627                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-cyan-300", children: [
3628                    pianoSpeed.toFixed(1),
3629                    "x"
3630                  ] })
3631                ] }),
3632                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 gap-1.5", children: PIANO_SPEEDS.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3633                  "button",
3634                  {
3635                    type: "button",
3636                    onClick: (e) => {
3637                      e.stopPropagation();
3638                      arcadeAudio.playSelect();
3639                      setSelectedModuleId(module.id);
3640                      setPianoSpeed(s);
3641                    },
3642                    className: `py-1 rounded text-xs font-bold transition-all border cursor-pointer ${pianoSpeed === s ? "bg-cyan-500 border-cyan-400 text-slate-950 font-black shadow-[0_0_10px_rgba(6,182,212,0.5)]" : "bg-slate-900 border-slate-700 text-slate-400 hover:text-white"}`,
3643                    children: [
3644                      s.toFixed(1),
3645                      "x"
3646                    ]
3647                  },
3648                  s
3649                )) })
3650              ] }),
3651              isTaiko && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3652                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs text-slate-300", children: [
3653                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-rose-400", children: [
3654                    /* @__PURE__ */ jsxRuntimeExports.jsx(Trophy, { size: 13 }),
3655                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.difficulty", "DIFFICULTY") })
3656                  ] }),
3657                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold uppercase text-white", children: taikoDifficulty })
3658                ] }),
3659                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 gap-1.5", children: TAIKO_DIFFICULTIES.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3660                  "button",
3661                  {
3662                    type: "button",
3663                    onClick: (e) => {
3664                      e.stopPropagation();
3665                      arcadeAudio.playSelect();
3666                      setSelectedModuleId(module.id);
3667                      setTaikoDifficulty(d);
3668                    },
3669                    className: `py-1 rounded text-[10px] font-bold uppercase transition-all border cursor-pointer ${taikoDifficulty === d ? "bg-rose-500 border-rose-400 text-white font-black shadow-[0_0_10px_rgba(244,63,94,0.6)]" : "bg-slate-900 border-slate-700 text-slate-400 hover:text-white"}`,
3670                    children: d === "kantan" ? "EASY" : d === "futsuu" ? "NORM" : d === "muzukashii" ? "HARD" : "ONI"
3671                  },
3672                  d
3673                )) })
3674              ] }),
3675              isMeowChoir && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3676                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs text-slate-300", children: [
3677                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-purple-400", children: [
3678                    /* @__PURE__ */ jsxRuntimeExports.jsx(Trophy, { size: 13 }),
3679                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.difficulty", "DIFFICULTY") })
3680                  ] }),
3681                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold uppercase text-white", children: meowChoirDifficulty })
3682                ] }),
3683                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 gap-1.5", children: MEOW_CHOIR_DIFFICULTIES.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3684                  "button",
3685                  {
3686                    type: "button",
3687                    onClick: (e) => {
3688                      e.stopPropagation();
3689                      arcadeAudio.playSelect();
3690                      setSelectedModuleId(module.id);
3691                      setMeowChoirDifficulty(d);
3692                    },
3693                    className: `py-1 rounded text-[10px] font-bold uppercase transition-all border cursor-pointer ${meowChoirDifficulty === d ? "bg-purple-500 border-purple-400 text-slate-950 font-black shadow-[0_0_10px_rgba(168,85,247,0.6)]" : "bg-slate-900 border-slate-700 text-slate-400 hover:text-white"}`,
3694                    children: d === "kitten" ? "KITTEN" : d === "playful" ? "PLAY" : d === "wildcat" ? "WILD" : "CHOIR"
3695                  },
3696                  d
3697                )) })
3698              ] }),
3699              isRhythmTest && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3700                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs text-slate-300", children: [
3701                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-amber-400", children: [
3702                    /* @__PURE__ */ jsxRuntimeExports.jsx(Trophy, { size: 13 }),
3703                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.difficulty", "DIFFICULTY") })
3704                  ] }),
3705                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold uppercase text-white", children: rhythmDifficulty })
3706                ] }),
3707                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 gap-1.5", children: RHYTHM_TEST_DIFFICULTIES.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3708                  "button",
3709                  {
3710                    type: "button",
3711                    onClick: (e) => {
3712                      e.stopPropagation();
3713                      arcadeAudio.playSelect();
3714                      setSelectedModuleId(module.id);
3715                      setRhythmDifficulty(d);
3716                    },
3717                    className: `py-1 rounded text-[10px] font-bold uppercase transition-all border cursor-pointer ${rhythmDifficulty === d ? "bg-amber-500 border-amber-400 text-slate-950 font-black shadow-[0_0_10px_rgba(245,158,11,0.6)]" : "bg-slate-900 border-slate-700 text-slate-400 hover:text-white"}`,
3718                    children: d === "easy" ? "EASY" : d === "medium" ? "MED" : d === "hard" ? "HARD" : "MASTER"
3719                  },
3720                  d
3721                )) })
3722              ] })
3723            ] })
3724          ]
3725        },
3726        module.id
3727      );
3728    }) }),
3729    /* @__PURE__ */ jsxRuntimeExports.jsxs(
3730      "div",
3731      {
3732        className: "flex items-center justify-between gap-4 p-3 rounded-xl bg-slate-950/95 border-2 shadow-xl shrink-0 transition-all duration-300",
3733        style: {
3734          borderColor: theme.secondary,
3735          boxShadow: `0 0 25px ${theme.secondary}40`
3736        },
3737        children: [
3738          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [
3739            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-block w-2.5 h-2.5 rounded-full bg-emerald-400 animate-ping" }),
3740            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
3741              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-black text-white uppercase tracking-wider", children: t("gameCenter.stageReady", "STAGE READY") }),
3742              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-400 font-bold truncate max-w-sm", children: [
3743                activeModule.name,
3744                " • ",
3745                selectedSong.title
3746              ] })
3747            ] })
3748          ] }),
3749          /* @__PURE__ */ jsxRuntimeExports.jsxs(
3750            "button",
3751            {
3752              onClick: handleLaunch,
3753              className: "px-8 py-3 rounded-xl text-slate-950 font-black text-xs tracking-widest uppercase active:scale-95 transition-all flex items-center justify-center gap-2 shadow-xl cursor-pointer",
3754              style: {
3755                background: `linear-gradient(135deg, ${theme.secondary}, ${theme.primary})`,
3756                boxShadow: `0 0 25px ${theme.primary}80`,
3757                color: theme.id === "max2" ? "#022c22" : "#030712"
3758              },
3759              children: [
3760                /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { size: 16, className: "fill-current text-yellow-300 animate-bounce" }),
3761                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("gameCenter.pressStart", "PRESS START [ENTER]") })
3762              ]
3763            }
3764          )
3765        ]
3766      }
3767    ),
3768    /* @__PURE__ */ jsxRuntimeExports.jsx(
3769      "div",
3770      {
3771        className: "flex items-center justify-between gap-2 px-3 py-1 rounded-xl bg-slate-950/90 border text-[11px] font-mono text-slate-400 shrink-0 transition-all duration-300",
3772        style: { borderColor: `${theme.primary}40` },
3773        children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-[10px] flex-wrap", children: [
3774          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold uppercase tracking-widest text-cyan-400", children: [
3775            t("gameCenter.cabinetControls", "CABINET CONTROLS"),
3776            ":"
3777          ] }),
3778          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "◄ ► CABINET" }),
3779          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
3780          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "▲ ▼ CONFIG" }),
3781          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
3782          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "ENTER START" }),
3783          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
3784          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "ESC BACK" })
3785        ] })
3786      }
3787    )
3788  ] });
3789};
3790const MeowChoirStage = ({
3791  score,
3792  combo,
3793  activeLaneHit,
3794  isSinging,
3795  isMissed,
3796  isFever
3797}) => {
3798  const singingNotes = reactExports.useMemo(() => {
3799    if (!isSinging) return [];
3800    return [
3801      { id: 1, symbol: "♪", x: 42, y: 55, delay: 0 },
3802      { id: 2, symbol: "♫", x: 57, y: 52, delay: 60 },
3803      { id: 3, symbol: "♪", x: 38, y: 50, delay: 120 }
3804    ];
3805  }, [isSinging]);
3806  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full h-full flex flex-col justify-between overflow-hidden select-none bg-[#0a0f29]", children: [
3807    /* @__PURE__ */ jsxRuntimeExports.jsxs(
3808      "div",
3809      {
3810        className: "absolute inset-0 z-0 pointer-events-none",
3811        style: {
3812          background: `
3813                        linear-gradient(180deg, rgba(10,14,45,0.7) 0%, rgba(15,23,68,0.2) 60%, rgba(20,30,85,0.8) 100%),
3814                        repeating-linear-gradient(
3815                            90deg,
3816                            #182357 0px,
3817                            #25368a 12px,
3818                            #3246a8 20px,
3819                            #1c2966 28px,
3820                            #121b44 36px
3821                        )
3822                    `
3823        },
3824        children: [
3825          /* @__PURE__ */ jsxRuntimeExports.jsx(
3826            "div",
3827            {
3828              className: "absolute top-0 left-1/2 -translate-x-1/2 w-3/4 h-full pointer-events-none",
3829              style: {
3830                background: "radial-gradient(ellipse 65% 55% at 50% 15%, rgba(255, 235, 180, 0.22) 0%, rgba(147, 197, 253, 0.08) 50%, transparent 100%)"
3831              }
3832            }
3833          ),
3834          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 inset-x-0 h-4 bg-gradient-to-b from-[#0b102e] to-transparent flex items-center justify-around opacity-90", children: Array.from({ length: 12 }).map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3835            "div",
3836            {
3837              className: "w-10 h-3 rounded-b-full bg-[#1b2762] border-b border-[#3b82f6]/40 shadow-sm"
3838            },
3839            i
3840          )) })
3841        ]
3842      }
3843    ),
3844    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-30 w-full pt-3 pb-1 flex flex-col items-center justify-center shrink-0 pointer-events-none", children: [
3845      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3846        "span",
3847        {
3848          className: "text-4xl sm:text-5xl font-black tracking-widest text-white transition-all transform duration-75",
3849          style: {
3850            fontFamily: '"Comic Sans MS", "Chalkboard SE", "Arial Rounded MT Bold", cursive, sans-serif',
3851            textShadow: `
3852                                -2px -2px 0 #000,
3853                                 2px -2px 0 #000,
3854                                -2px  2px 0 #000,
3855                                 2px  2px 0 #000,
3856                                 0px  4px 0 #000,
3857                                 0 6px 14px rgba(0,0,0,0.65)
3858                            `,
3859            filter: isSinging ? "brightness(1.15) scale(1.04)" : "none"
3860          },
3861          children: score
3862        }
3863      ) }),
3864      combo > 2 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3865        "div",
3866        {
3867          className: `mt-0.5 px-3 py-0.5 rounded-full text-xs font-black uppercase tracking-wider flex items-center gap-1 shadow-md transition-all ${isFever ? "bg-gradient-to-r from-amber-400 to-pink-500 text-slate-950 animate-bounce" : "bg-pink-600/90 text-white border border-pink-300/40"}`,
3868          style: {
3869            fontFamily: '"Arial Rounded MT Bold", sans-serif'
3870          },
3871          children: [
3872            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
3873              combo,
3874              " COMBO!"
3875            ] }),
3876            isFever && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "🔥 x2" })
3877          ]
3878        }
3879      )
3880    ] }),
3881    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-10 w-full h-[64%] flex items-end justify-center overflow-hidden", children: [
3882      /* @__PURE__ */ jsxRuntimeExports.jsxs(
3883        "div",
3884        {
3885          className: "absolute inset-x-0 bottom-0 h-[60%] border-t-2 border-[#5c3a1a]",
3886          style: {
3887            background: `
3888                            linear-gradient(180deg, #d49a5b 0%, #ba8145 35%, #9b6329 80%, #7d4918 100%)
3889                        `,
3890            boxShadow: "inset 0 10px 25px rgba(0,0,0,0.35)"
3891          },
3892          children: [
3893            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-full flex flex-col justify-around opacity-40 pointer-events-none", children: [
3894              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-[1px] bg-[#543011]" }),
3895              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-[1px] bg-[#543011]" }),
3896              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-[1px] bg-[#543011]" })
3897            ] }),
3898            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 flex justify-between px-12 opacity-30 pointer-events-none", children: [
3899              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-[1px] h-full bg-[#543011]" }),
3900              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-[1px] h-full bg-[#543011]" }),
3901              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-[1px] h-full bg-[#543011]" })
3902            ] }),
3903            /* @__PURE__ */ jsxRuntimeExports.jsx(
3904              "div",
3905              {
3906                className: "absolute bottom-2 left-1/2 -translate-x-1/2 w-64 h-24 rounded-full pointer-events-none opacity-45",
3907                style: {
3908                  background: "radial-gradient(ellipse at center, rgba(255,248,220,0.6) 0%, rgba(212,154,91,0) 70%)"
3909                }
3910              }
3911            )
3912          ]
3913        }
3914      ),
3915      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-lg h-full flex items-end justify-center px-4 pb-2", children: [
3916        /* @__PURE__ */ jsxRuntimeExports.jsx(
3917          "div",
3918          {
3919            className: "absolute bottom-4 left-6 sm:left-12 flex flex-col items-center transition-transform duration-100",
3920            style: {
3921              transform: isSinging && activeLaneHit === 0 ? "scale(1.08) translateY(-4px)" : "scale(1.0)"
3922            },
3923            children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3924              KittenFigure,
3925              {
3926                type: "ginger",
3927                isHeadUp: isSinging && (activeLaneHit === 0 || activeLaneHit === null),
3928                isSinging,
3929                isMissed,
3930                size: 72
3931              }
3932            )
3933          }
3934        ),
3935        /* @__PURE__ */ jsxRuntimeExports.jsx(
3936          "div",
3937          {
3938            className: "absolute bottom-4 left-[30%] sm:left-[34%] -translate-x-1/2 flex flex-col items-center z-15 transition-transform duration-100",
3939            style: {
3940              transform: isSinging && (activeLaneHit === 1 || activeLaneHit === null) ? "scale(1.1) translateY(-6px)" : "scale(1.0)"
3941            },
3942            children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3943              KittenFigure,
3944              {
3945                type: "grey",
3946                isHeadUp: isSinging,
3947                isSinging,
3948                isMissed,
3949                size: 84
3950              }
3951            )
3952          }
3953        ),
3954        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-20 flex flex-col items-center mb-1", children: [
3955          /* @__PURE__ */ jsxRuntimeExports.jsx(
3956            "div",
3957            {
3958              className: "relative w-8 h-8 rounded-full shadow-lg border border-red-700/60 transition-transform duration-100",
3959              style: {
3960                background: "radial-gradient(circle at 35% 30%, #ff7350 0%, #e11d48 70%, #9f1239 100%)",
3961                boxShadow: isSinging ? "0 0 16px rgba(244, 63, 94, 0.8), 0 4px 8px rgba(0,0,0,0.5)" : "0 4px 8px rgba(0,0,0,0.5)",
3962                transform: isSinging ? "scale(1.06)" : "scale(1.0)"
3963              },
3964              children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-1 rounded-full border border-red-300/30 opacity-70" })
3965            }
3966          ),
3967          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2.5 h-1.5 bg-neutral-700 rounded-sm -mt-0.5" }),
3968          /* @__PURE__ */ jsxRuntimeExports.jsx(
3969            "div",
3970            {
3971              className: "w-1.5 h-16 sm:h-20 bg-gradient-to-r from-neutral-800 via-neutral-600 to-neutral-900 shadow-md"
3972            }
3973          ),
3974          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-2 bg-neutral-800 rounded-sm -mb-0.5" }),
3975          /* @__PURE__ */ jsxRuntimeExports.jsx(
3976            "div",
3977            {
3978              className: "w-14 h-4 rounded-full bg-gradient-to-b from-neutral-700 via-neutral-900 to-black border border-neutral-700 shadow-xl",
3979              style: {
3980                transform: "rotateX(55deg)"
3981              }
3982            }
3983          )
3984        ] }),
3985        /* @__PURE__ */ jsxRuntimeExports.jsx(
3986          "div",
3987          {
3988            className: "absolute bottom-4 right-[30%] sm:right-[34%] translate-x-1/2 flex flex-col items-center z-15 transition-transform duration-100",
3989            style: {
3990              transform: isSinging && (activeLaneHit === 2 || activeLaneHit === null) ? "scale(1.1) translateY(-6px)" : "scale(1.0)"
3991            },
3992            children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3993              KittenFigure,
3994              {
3995                type: "white",
3996                isHeadUp: isSinging,
3997                isSinging,
3998                isMissed,
3999                size: 84
4000              }
4001            )
4002          }
4003        ),
4004        /* @__PURE__ */ jsxRuntimeExports.jsx(
4005          "div",
4006          {
4007            className: "absolute bottom-4 right-6 sm:right-12 flex flex-col items-center transition-transform duration-100",
4008            style: {
4009              transform: isSinging && activeLaneHit === 3 ? "scale(1.08) translateY(-4px)" : "scale(1.0)"
4010            },
4011            children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4012              KittenFigure,
4013              {
4014                type: "calico",
4015                isHeadUp: isSinging && (activeLaneHit === 3 || activeLaneHit === null),
4016                isSinging,
4017                isMissed,
4018                size: 72
4019              }
4020            )
4021          }
4022        ),
4023        singingNotes.map((n) => /* @__PURE__ */ jsxRuntimeExports.jsx(
4024          "div",
4025          {
4026            className: "absolute pointer-events-none text-xl sm:text-2xl font-black text-pink-300 animate-ping opacity-80",
4027            style: {
4028              left: `${n.x}%`,
4029              top: `${n.y}%`,
4030              filter: "drop-shadow(0 0 6px rgba(244,114,182,0.9))"
4031            },
4032            children: n.symbol
4033          },
4034          n.id
4035        ))
4036      ] })
4037    ] })
4038  ] });
4039};
4040const KittenFigure = ({
4041  type,
4042  isHeadUp,
4043  isSinging,
4044  isMissed,
4045  size
4046}) => {
4047  const gradId = reactExports.useMemo(() => `cat-${type}-${Math.random().toString(36).substr(2, 6)}`, [type]);
4048  const palette = reactExports.useMemo(() => {
4049    switch (type) {
4050      case "grey":
4051        return {
4052          bodyTop: "#b0c4de",
4053          bodyBottom: "#64748b",
4054          belly: "#e2e8f0",
4055          stripes: "#475569",
4056          earsInner: "#f472b6",
4057          earsInnerDark: "#db2777",
4058          nose: "#fb7185",
4059          irisTop: "#059669",
4060          irisBottom: "#34d399",
4061          bowtie: "#e11d48",
4062          collar: "#be123c",
4063          bell: "#fbbf24"
4064        };
4065      case "white":
4066        return {
4067          bodyTop: "#ffffff",
4068          bodyBottom: "#cbd5e1",
4069          belly: "#f8fafc",
4070          stripes: "#94a3b8",
4071          earsInner: "#f9a8d4",
4072          earsInnerDark: "#f472b6",
4073          nose: "#fda4af",
4074          irisTop: "#0284c7",
4075          irisBottom: "#38bdf8",
4076          bowtie: "#3b82f6",
4077          collar: "#1d4ed8",
4078          bell: "#fbbf24"
4079        };
4080      case "ginger":
4081        return {
4082          bodyTop: "#fb923c",
4083          bodyBottom: "#c2410c",
4084          belly: "#ffedd5",
4085          stripes: "#9a3412",
4086          earsInner: "#fda4af",
4087          earsInnerDark: "#f43f5e",
4088          nose: "#e11d48",
4089          irisTop: "#d97706",
4090          irisBottom: "#fcd34d",
4091          bowtie: "#8b5cf6",
4092          collar: "#6d28d9",
4093          bell: "#fbbf24"
4094        };
4095      case "calico":
4096        return {
4097          bodyTop: "#fed7aa",
4098          bodyBottom: "#b45309",
4099          belly: "#ffffff",
4100          stripes: "#78350f",
4101          earsInner: "#fda4af",
4102          earsInnerDark: "#fb7185",
4103          nose: "#e11d48",
4104          irisTop: "#059669",
4105          irisBottom: "#f59e0b",
4106          bowtie: "#10b981",
4107          collar: "#047857",
4108          bell: "#fbbf24"
4109        };
4110    }
4111  }, [type]);
4112  return /* @__PURE__ */ jsxRuntimeExports.jsx(
4113    "div",
4114    {
4115      className: "relative flex flex-col items-center group cursor-pointer",
4116      style: { width: size, height: size * 1.18 },
4117      children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
4118        "svg",
4119        {
4120          viewBox: "0 0 100 118",
4121          width: size,
4122          height: size * 1.18,
4123          className: "overflow-visible",
4124          children: [
4125            /* @__PURE__ */ jsxRuntimeExports.jsxs("defs", { children: [
4126              /* @__PURE__ */ jsxRuntimeExports.jsxs("radialGradient", { id: `${gradId}-body`, cx: "45%", cy: "35%", r: "65%", children: [
4127                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: palette.bodyTop }),
4128                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "70%", stopColor: palette.bodyTop }),
4129                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: palette.bodyBottom })
4130              ] }),
4131              /* @__PURE__ */ jsxRuntimeExports.jsxs("linearGradient", { id: `${gradId}-belly`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
4132                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "#ffffff" }),
4133                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: palette.belly })
4134              ] }),
4135              /* @__PURE__ */ jsxRuntimeExports.jsxs("linearGradient", { id: `${gradId}-ear`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
4136                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: palette.earsInner }),
4137                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: palette.earsInnerDark })
4138              ] }),
4139              /* @__PURE__ */ jsxRuntimeExports.jsxs("linearGradient", { id: `${gradId}-iris`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
4140                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: palette.irisTop }),
4141                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: palette.irisBottom })
4142              ] }),
4143              /* @__PURE__ */ jsxRuntimeExports.jsxs("radialGradient", { id: `${gradId}-bell`, cx: "35%", cy: "30%", r: "60%", children: [
4144                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "#fef08a" }),
4145                /* @__P
4145URE__ */ jsxRuntimeExports.jsx("stop", { offset: "60%", stopColor: "#eab308" }),
4146                /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "#854d0e" })
4147              ] })
4148            ] }),
4149            /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "50", cy: "110", rx: "36", ry: "6", fill: "rgba(0,0,0,0.4)", filter: "blur(1px)" }),
4150            /* @__PURE__ */ jsxRuntimeExports.jsx(
4151              "path",
4152              {
4153                d: isSinging ? "M 22 92 C 5 80 8 52 18 42 C 20 40 23 43 21 46 C 14 56 12 76 25 88 Z" : "M 22 94 C 12 85 14 68 20 60 C 22 58 24 60 22 63 C 18 70 17 80 25 90 Z",
4154                fill: `url(#${gradId}-body)`,
4155                className: "transition-all duration-150"
4156              }
4157            ),
4158            /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "50", cy: "86", rx: "29", ry: "23", fill: `url(#${gradId}-body)` }),
4159            /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "50", cy: "88", rx: "19", ry: "16", fill: `url(#${gradId}-belly)` }),
4160            /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "24", cy: "103", rx: "8", ry: "5.5", fill: palette.bodyBottom, opacity: "0.85" }),
4161            /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "76", cy: "103", rx: "8", ry: "5.5", fill: palette.bodyBottom, opacity: "0.85" }),
4162            /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
4163              /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "38", cy: "105", rx: "8.5", ry: "6", fill: `url(#${gradId}-body)` }),
4164              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "36", cy: "107", r: "1.2", fill: palette.bodyBottom, opacity: "0.6" }),
4165              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "40", cy: "107", r: "1.2", fill: palette.bodyBottom, opacity: "0.6" }),
4166              /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "62", cy: "105", rx: "8.5", ry: "6", fill: `url(#${gradId}-body)` }),
4167              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "60", cy: "107", r: "1.2", fill: palette.bodyBottom, opacity: "0.6" }),
4168              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "64", cy: "107", r: "1.2", fill: palette.bodyBottom, opacity: "0.6" })
4169            ] }),
4170            /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "transition-transform duration-100 origin-[50px_70px]", children: [
4171              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 50 71 L 40 66 C 36 64 36 78 40 76 Z", fill: palette.bowtie }),
4172              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 50 71 L 60 66 C 64 64 64 78 60 76 Z", fill: palette.bowtie }),
4173              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "50", cy: "71", r: "3.2", fill: palette.collar }),
4174              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "50", cy: "75", r: "3.2", fill: `url(#${gradId}-bell)`, stroke: "#a16207", strokeWidth: "0.5" }),
4175              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "50", cy: "76", r: "0.7", fill: "#713f12" })
4176            ] }),
4177            /* @__PURE__ */ jsxRuntimeExports.jsxs(
4178              "g",
4179              {
4180                className: "transition-transform duration-100 origin-[50px_60px]",
4181                style: {
4182                  transform: isHeadUp ? "translateY(-7px) scaleY(1.06)" : isMissed ? "translateY(3px) rotate(4deg)" : "translateY(0px)"
4183                },
4184                children: [
4185                  /* @__PURE__ */ jsxRuntimeExports.jsx(
4186                    "path",
4187                    {
4188                      d: "M 24 40 C 20 28 20 16 26 14 C 32 16 42 26 44 34 Z",
4189                      fill: `url(#${gradId}-body)`
4190                    }
4191                  ),
4192                  /* @__PURE__ */ jsxRuntimeExports.jsx(
4193                    "path",
4194                    {
4195                      d: "M 26 36 C 24 28 24 20 27 18 C 31 20 38 27 40 32 Z",
4196                      fill: `url(#${gradId}-ear)`
4197                    }
4198                  ),
4199                  /* @__PURE__ */ jsxRuntimeExports.jsx(
4200                    "path",
4201                    {
4202                      d: "M 76 40 C 80 28 80 16 74 14 C 68 16 58 26 56 34 Z",
4203                      fill: `url(#${gradId}-body)`
4204                    }
4205                  ),
4206                  /* @__PURE__ */ jsxRuntimeExports.jsx(
4207                    "path",
4208                    {
4209                      d: "M 74 36 C 76 28 76 20 73 18 C 69 20 62 27 60 32 Z",
4210                      fill: `url(#${gradId}-ear)`
4211                    }
4212                  ),
4213                  /* @__PURE__ */ jsxRuntimeExports.jsx(
4214                    "path",
4215                    {
4216                      d: "M 50 16 C 30 16 20 27 20 43 C 20 54 26 63 36 66 C 41 67.5 59 67.5 64 66 C 74 63 80 54 80 43 C 80 27 70 16 50 16 Z",
4217                      fill: `url(#${gradId}-body)`
4218                    }
4219                  ),
4220                  type === "calico" && /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 24 30 C 22 42 30 48 38 42 C 40 32 32 24 24 30 Z", fill: "#b45309", opacity: "0.85" }),
4221                  type !== "white" && type !== "calico" && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { stroke: palette.stripes, strokeWidth: "2.5", strokeLinecap: "round", opacity: "0.85", children: [
4222                    /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "50", y1: "20", x2: "50", y2: "29" }),
4223                    /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "42", y1: "22", x2: "44", y2: "30" }),
4224                    /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "58", y1: "22", x2: "56", y2: "30" })
4225                  ] }),
4226                  /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "32", cy: "49", rx: "5.5", ry: "3.5", fill: "#f43f5e", opacity: "0.35" }),
4227                  /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "68", cy: "49", rx: "5.5", ry: "3.5", fill: "#f43f5e", opacity: "0.35" }),
4228                  /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { stroke: "#ffffff", strokeWidth: "1.3", strokeLinecap: "round", opacity: "0.9", children: [
4229                    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 22 48 Q 12 46 4 45" }),
4230                    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 22 52 Q 13 54 5 57" }),
4231                    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 78 48 Q 88 46 96 45" }),
4232                    /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 78 52 Q 87 54 95 57" })
4233                  ] }),
4234                  isHeadUp ? (
4235                    // Singing / Ecstatic Happy Eyes (Crescent arcs "^ ^" with lashes)
4236                    /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { stroke: "#0f172a", strokeWidth: "2.8", strokeLinecap: "round", fill: "none", children: [
4237                      /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 33 43 Q 40 37 47 43" }),
4238                      /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 53 43 Q 60 37 67 43" }),
4239                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "28", cy: "38", r: "1.5", fill: "#fbcfe8" }),
4240                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "72", cy: "38", r: "1.5", fill: "#fbcfe8" })
4241                    ] })
4242                  ) : isMissed ? (
4243                    // Sad Embarrassed Eyes ("o o" + sweatdrop)
4244                    /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
4245                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "40", cy: "43", r: "3.5", fill: "#1e293b" }),
4246                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "60", cy: "43", r: "3.5", fill: "#1e293b" }),
4247                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "39", cy: "41.5", r: "1.2", fill: "#ffffff" }),
4248                      /* @__P
4248URE__ */ jsxRuntimeExports.jsx("circle", { cx: "59", cy: "41.5", r: "1.2", fill: "#ffffff" }),
4249                      /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 73 34 C 73 31 75 28 75 28 C 75 28 77 31 77 34 C 77 35.5 75.5 36.5 74.5 36.5 C 73.5 36.5 73 35.5 73 34 Z", fill: "#38bdf8" })
4250                    ] })
4251                  ) : (
4252                    // Gorgeous Anime Chibi Eyes with Multi-layer glints
4253                    /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
4254                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "40", cy: "43", rx: "6.5", ry: "7.5", fill: "#0f172a" }),
4255                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "60", cy: "43", rx: "6.5", ry: "7.5", fill: "#0f172a" }),
4256                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "40", cy: "44", rx: "5.5", ry: "6.2", fill: `url(#${gradId}-iris)` }),
4257                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "60", cy: "44", rx: "5.5", ry: "6.2", fill: `url(#${gradId}-iris)` }),
4258                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "40", cy: "44", rx: "3.5", ry: "4.5", fill: "#020617" }),
4259                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "60", cy: "44", rx: "3.5", ry: "4.5", fill: "#020617" }),
4260                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "42.5", cy: "41", r: "2.2", fill: "#ffffff" }),
4261                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "62.5", cy: "41", r: "2.2", fill: "#ffffff" }),
4262                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "37.5", cy: "46", r: "1.2", fill: "#ffffff", opacity: "0.8" }),
4263                      /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "57.5", cy: "46", r: "1.2", fill: "#ffffff", opacity: "0.8" })
4264                    ] })
4265                  ),
4266                  /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 47.5 49 C 48.5 48 51.5 48 52.5 49 C 52 52 50 53 50 53 C 50 53 48 52 47.5 49 Z", fill: palette.nose }),
4267                  isHeadUp ? (
4268                    // Wide Open Singing Mouth with Pink Tongue!
4269                    /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
4270                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "50", cy: "57", rx: "7.5", ry: "8.5", fill: "#881337" }),
4271                      /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "50", cy: "55", rx: "5.5", ry: "5", fill: "#4c0519" }),
4272                      /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 45 59 C 45 64 55 64 55 59 C 55 57 45 57 45 59 Z", fill: "#fda4af" })
4273                    ] })
4274                  ) : (
4275                    // Cute Feline "ω" Smile
4276                    /* @__PURE__ */ jsxRuntimeExports.jsx(
4277                      "path",
4278                      {
4279                        d: "M 44 52 Q 47 55 50 52.5 Q 53 55 56 52",
4280                        stroke: "#0f172a",
4281                        strokeWidth: "2.0",
4282                        strokeLinecap: "round",
4283                        fill: "none"
4284                      }
4285                    )
4286                  )
4287                ]
4288              }
4289            )
4290          ]
4291        }
4292      )
4293    }
4294  );
4295};
4296class MeowChoirAudioEngine {
4297  constructor() {
4298    this.ctx = null;
4299    this.masterGain = null;
4300    this.limiter = null;
4301    this.vocalGain = null;
4302    this.sfxGain = null;
4303    this.audioOffsetMs = 0;
4304    this.vocalVolume = 0.9;
4305    this.activeVoices = [];
4306    this.sustainVoices = /* @__PURE__ */ new Map();
4307    this.maxPolyphony = 6;
4308    if (typeof window !== "undefined") {
4309      const savedOffset = localStorage.getItem("meow_choir_audio_offset");
4310      if (savedOffset !== null) {
4311        this.audioOffsetMs = Number(savedOffset) || 0;
4312      }
4313      const savedVol = localStorage.getItem("meow_choir_vocal_vol");
4314      if (savedVol !== null) {
4315        this.vocalVolume = Number(savedVol) || 0.9;
4316      }
4317    }
4318  }
4319  initContext() {
4320    if (!this.ctx) {
4321      const AudioCtx = window.AudioContext || window.webkitAudioContext;
4322      if (!AudioCtx) return false;
4323      this.ctx = new AudioCtx();
4324      this.limiter = this.ctx.createDynamicsCompressor();
4325      this.limiter.threshold.setValueAtTime(-3, this.ctx.currentTime);
4326      this.limiter.knee.setValueAtTime(4, this.ctx.currentTime);
4327      this.limiter.ratio.setValueAtTime(16, this.ctx.currentTime);
4328      this.limiter.attack.setValueAtTime(3e-3, this.ctx.currentTime);
4329      this.limiter.release.setValueAtTime(0.08, this.ctx.currentTime);
4330      this.limiter.connect(this.ctx.destination);
4331      this.masterGain = this.ctx.createGain();
4332      this.masterGain.gain.setValueAtTime(0.85, this.ctx.currentTime);
4333      this.masterGain.connect(this.limiter);
4334      this.vocalGain = this.ctx.createGain();
4335      this.vocalGain.gain.setValueAtTime(this.vocalVolume, this.ctx.currentTime);
4336      this.vocalGain.connect(this.masterGain);
4337      this.sfxGain = this.ctx.createGain();
4338      this.sfxGain.gain.setValueAtTime(0.7, this.ctx.currentTime);
4339      this.sfxGain.connect(this.masterGain);
4340    }
4341    if (this.ctx && this.ctx.state === "suspended") {
4342      this.ctx.resume().catch(() => {
4343      });
4344    }
4345    return true;
4346  }
4347  getOffsetMs() {
4348    return this.audioOffsetMs;
4349  }
4350  setOffsetMs(offset) {
4351    this.audioOffsetMs = Math.max(-250, Math.min(250, offset));
4352    if (typeof window !== "undefined") {
4353      localStorage.setItem("meow_choir_audio_offset", String(this.audioOffsetMs));
4354    }
4355  }
4356  getVocalVolume() {
4357    return this.vocalVolume;
4358  }
4359  setVocalVolume(vol) {
4360    this.vocalVolume = Math.max(0, Math.min(1.2, vol));
4361    if (this.vocalGain && this.ctx) {
4362      this.vocalGain.gain.setValueAtTime(this.vocalVolume, this.ctx.currentTime);
4363    }
4364    if (typeof window !== "undefined") {
4365      localStorage.setItem("meow_choir_vocal_vol", String(this.vocalVolume));
4366    }
4367  }
4368  /**
4369   * Polyphony manager to prevent volume inflation and CPU overload.
4370   * Safely fades oldest voice without calling duplicate .stop()
4371   */
4372  managePolyphony(now) {
4373    this.activeVoices = this.activeVoices.filter((v) => v.stopTime > now && !v.isStopped);
4374    while (this.activeVoices.length >= this.maxPolyphony) {
4375      const oldest = this.activeVoices.shift();
4376      if (oldest && !oldest.isStopped) {
4377        oldest.isStopped = true;
4378        try {
4379          oldest.ampGain.gain.cancelScheduledValues(now);
4380          oldest.ampGain.gain.setValueAtTime(Math.max(1e-4, oldest.ampGain.gain.value), now);
4381          oldest.ampGain.gain.linearRampToValueAtTime(1e-4, now + 0.02);
4382        } catch (e) {
4383        }
4384      }
4385    }
4386  }
4387  /**
4388   * Synthesizes an adorable, sweet kitten "meow" tuned to the specific melody pitch.
4389   * @param midiPitch Standard MIDI note number (60 = C4, 69 = A440)
4390   * @param duration Duration of the meow in seconds (supports short taps and held meows)
4391   * @param isStarBonus If true, adds sparkle chime harmonics
4392   */
4393  playMeow(midiPitch = 60, duration = 0.35, isStarBonus = false) {
4394    this.initContext();
4395    if (!this.ctx || !this.vocalGain || this.vocalVolume <= 0.01) return;
4396    const now = this.ctx.currentTime;
4397    this.managePolyphony(now);
4398    const dur = Math.max(0.22, Math.min(2.5, duration));
4399    const clampedMidi = Math.max(55, Math.min(88, midiPitch));
4400    const baseFreq = 440 * Math.pow(2, (clampedMidi - 69) / 12);
4401    const oscSine = this.ctx.createOscillator();
4402    const oscTri = this.ctx.createOscillator();
4403    const oscHarm = this.ctx.createOscillator();
4404    const gainSine = this.ctx.createGain();
4405    const gainTri = this.ctx.createGain();
4406    const gainHarm = this.ctx.createGain();
4407    const voiceMixer = this.ctx.createGain();
4408    oscSine.type = "sine";
4409    oscTri.type = "triangle";
4410    oscHarm.type = "sine";
4411    gainSine.gain.setValueAtTime(0.7, now);
4412    gainTri.gain.setValueAtTime(0.4, now);
4413    gainHarm.gain.setValueAtTime(0.18, now);
4414    const scoopPitch = baseFreq * 0.92;
4415    const midPitch = baseFreq;
4416    const tailPitch = baseFreq * 0.96;
4417    const attackTime = Math.min(0.05, dur * 0.2);
4418    const releaseTime = Math.min(0.08, dur * 0.25);
4419    [oscSine, oscTri].forEach((osc) => {
4420      osc.frequency.setValueAtTime(scoopPitch, now);
4421      osc.frequency.linearRampToValueAtTime(midPitch, now + attackTime);
4422      osc.frequency.setValueAtTime(midPitch, now + dur - releaseTime);
4423      osc.frequency.linearRampToValueAtTime(tailPitch, now + dur);
4424    });
4425    oscHarm.frequency.setValueAtTime(scoopPitch * 2, now);
4426    oscHarm.frequency.linearRampToValueAtTime(midPitch * 2, now + attackTime);
4427    oscHarm.frequency.setValueAtTime(midPitch * 2, now + dur - releaseTime);
4428    oscHarm.frequency.linearRampToValueAtTime(tailPitch * 2, now + dur);
4429    const lfo = this.ctx.createOscillator();
4430    const lfoGain = this.ctx.createGain();
4431    const lfoGainHarm = this.ctx.createGain();
4432    const lfoTremolo = this.ctx.createGain();
4433    lfo.frequency.setValueAtTime(5.8, now);
4434    const vibDepth = baseFreq * 0.038;
4435    const attackSwellTime = Math.min(0.1, dur * 0.35);
4436    lfoGain.gain.setValueAtTime(baseFreq * 6e-3, now);
4437    lfoGain.gain.linearRampToValueAtTime(vibDepth, now + attackSwellTime);
4438    lfoGainHarm.gain.setValueAtTime(baseFreq * 2 * 6e-3, now);
4439    lfoGainHarm.gain.linearRampToValueAtTime(vibDepth * 2, now + attackSwellTime);
4440    lfoTremolo.gain.setValueAtTime(0.06, now);
4441    lfo.connect(lfoGain);
4442    lfo.connect(lfoGainHarm);
4443    lfo.connect(lfoTremolo);
4444    lfoGain.connect(oscSine.frequency);
4445    lfoGain.connect(oscTri.frequency);
4446    lfoGainHarm.connect(oscHarm.frequency);
4447    lfoTremolo.connect(voiceMixer.gain);
4448    const f1 = this.ctx.createBiquadFilter();
4449    f1.type = "bandpass";
4450    f1.Q.setValueAtTime(3.2, now);
4451    f1.frequency.setValueAtTime(680, now);
4452    f1.frequency.linearRampToValueAtTime(1020, now + dur * 0.28);
4453    f1.frequency.linearRampToValueAtTime(580, now + dur);
4454    const f2 = this.ctx.createBiquadFilter();
4455    f2.type = "bandpass";
4456    f2.Q.setValueAtTime(3.8, now);
4457    f2.frequency.setValueAtTime(1900, now);
4458    f2.frequency.linearRampToValueAtTime(2500, now + dur * 0.3);
4459    f2.frequency.linearRampToValueAtTime(1400, now + dur);
4460    const f3 = this.ctx.createBiquadFilter();
4461    f3.type = "bandpass";
4462    f3.Q.setValueAtTime(2.5, now);
4463    f3.frequency.setValueAtTime(3200, now);
4464    const ampGain = this.ctx.createGain();
4465    ampGain.gain.setValueAtTime(1e-4, now);
4466    ampGain.gain.linearRampToValueAtTime(isStarBonus ? 0.9 : 0.78, now + attackTime);
4467    ampGain.gain.setValueAtTime(isStarBonus ? 0.85 : 0.74, now + dur - releaseTime);
4468    ampGain.gain.linearRampToValueAtTime(1e-4, now + dur);
4469    oscSine.connect(gainSine);
4470    oscTri.connect(gainTri);
4471    oscHarm.connect(gainHarm);
4472    gainSine.connect(voiceMixer);
4473    gainTri.connect(voiceMixer);
4474    gainHarm.connect(voiceMixer);
4475    voiceMixer.connect(f1);
4476    voiceMixer.connect(f2);
4477    voiceMixer.connect(f3);
4478    f1.connect(ampGain);
4479    f2.connect(ampGain);
4480    f3.connect(ampGain);
4481    ampGain.connect(this.vocalGain);
4482    const stopTime = now + dur + 0.05;
4483    lfo.start(now);
4484    oscSine.start(now);
4485    oscTri.start(now);
4486    oscHarm.start(now);
4487    lfo.stop(stopTime);
4488    oscSine.stop(stopTime);
4489    oscTri.stop(stopTime);
4490    oscHarm.stop(stopTime);
4491    if (isStarBonus) {
4492      const chime = this.ctx.createOscillator();
4493      const chimeGain = this.ctx.createGain();
4494      chime.type = "sine";
4495      chime.frequency.setValueAtTime(baseFreq * 2, now);
4496      chime.frequency.linearRampToValueAtTime(baseFreq * 3, now + 0.15);
4497      chimeGain.gain.setValueAtTime(0.2, now);
4498      chimeGain.gain.linearRampToValueAtTime(1e-4, now + 0.25);
4499      chime.connect(chimeGain);
4500      chimeGain.connect(this.vocalGain);
4501      chime.start(now);
4502      chime.stop(now + 0.26);
4503    }
4504    this.activeVoices.push({
4505      oscSine,
4506      oscTri,
4507      oscHarm,
4508      lfo,
4509      ampGain,
4510      f1,
4511      f2,
4512      f3,
4513      stopTime,
4514      isStopped: false
4515    });
4516  }
4517  /**
4518   * Starts continuous sustain meow for hold notes.
4519   */
4520  startHoldMeow(lane, midiPitch = 60, isStarBonus = false) {
4521    this.initContext();
4522    if (!this.ctx || !this.vocalGain || this.vocalVolume <= 0.01) return;
4523    this.stopHoldMeow(lane);
4524    const now = this.ctx.currentTime;
4525    const clampedMidi = Math.max(55, Math.min(88, midiPitch));
4526    const baseFreq = 440 * Math.pow(2, (clampedMidi - 69) / 12);
4527    const oscSine = this.ctx.createOscillator();
4528    const oscTri = this.ctx.createOscillator();
4529    const oscHarm = this.ctx.createOscillator();
4530    const gainSine = this.ctx.createGain();
4531    const gainTri = this.ctx.createGain();
4532    const gainHarm = this.ctx.createGain();
4533    const voiceMixer = this.ctx.createGain();
4534    oscSine.type = "sine";
4535    oscTri.type = "triangle";
4536    oscHarm.type = "sine";
4537    gainSine.gain.setValueAtTime(0.7, now);
4538    gainTri.gain.setValueAtTime(0.4, now);
4539    gainHarm.gain.setValueAtTime(0.18, now);
4540    oscSine.frequency.setValueAtTime(baseFreq * 0.94, now);
4541    oscTri.frequency.setValueAtTime(baseFreq * 0.94, now);
4542    oscHarm.frequency.setValueAtTime(baseFreq * 1.88, now);
4543    oscSine.frequency.linearRampToValueAtTime(baseFreq, now + 0.045);
4544    oscTri.frequency.linearRampToValueAtTime(baseFreq, now + 0.045);
4545    oscHarm.frequency.linearRampToValueAtTime(baseFreq * 2, now + 0.045);
4546    const lfo = this.ctx.createOscillator();
4547    const lfoGain = this.ctx.createGain();
4548    const lfoGainHarm = this.ctx.createGain();
4549    const lfoTremolo = this.ctx.createGain();
4550    lfo.frequency.setValueAtTime(5.8, now);
4551    const vibDepth = baseFreq * 0.042;
4552    lfoGain.gain.setValueAtTime(baseFreq * 8e-3, now);
4553    lfoGain.gain.linearRampToValueAtTime(vibDepth, now + 0.18);
4554    lfoGainHarm.gain.setValueAtTime(baseFreq * 2 * 8e-3, now);
4555    lfoGainHarm.gain.linearRampToValueAtTime(vibDepth * 2, now + 0.18);
4556    lfoTremolo.gain.setValueAtTime(0.06, now);
4557    lfo.connect(lfoGain);
4558    lfo.connect(lfoGainHarm);
4559    lfo.connect(lfoTremolo);
4560    lfoGain.connect(oscSine.frequency);
4561    lfoGain.connect(oscTri.frequency);
4562    lfoGainHarm.connect(oscHarm.frequency);
4563    lfoTremolo.connect(voiceMixer.gain);
4564    lfo.start(now);
4565    const f1 = this.ctx.createBiquadFilter();
4566    f1.type = "bandpass";
4567    f1.Q.setValueAtTime(3.2, now);
4568    f1.frequency.setValueAtTime(950, now);
4569    const f2 = this.ctx.createBiquadFilter();
4570    f2.type = "bandpass";
4571    f2.Q.setValueAtTime(3.8, now);
4572    f2.frequency.setValueAtTime(2400, now);
4573    const f3 = this.ctx.createBiquadFilter();
4574    f3.type = "bandpass";
4575    f3.Q.setValueAtTime(2.5, now);
4576    f3.frequency.setValueAtTime(3200, now);
4577    const ampGain = this.ctx.createGain();
4578    ampGain.gain.setValueAtTime(1e-4, now);
4579    ampGain.gain.linearRampToValueAtTime(isStarBonus ? 0.9 : 0.78, now + 0.045);
4580    oscSine.connect(gainSine);
4581    oscTri.connect(gainTri);
4582    oscHarm.connect(gainHarm);
4583    gainSine.connect(voiceMixer);
4584    gainTri.connect(voiceMixer);
4585    gainHarm.connect(voiceMixer);
4586    voiceMixer.connect(f1);
4587    voiceMixer.connect(f2);
4588    voiceMixer.connect(f3);
4589    f1.connect(ampGain);
4590    f2.connect(ampGain);
4591    f3.connect(ampGain);
4592    ampGain.connect(this.vocalGain);
4593    oscSine.start(now);
4594    oscTri.start(now);
4595    oscHarm.start(now);
4596    this.sustainVoices.set(lane, {
4597      oscSine,
4598      oscTri,
4599      oscHarm,
4600      lfo,
4601      ampGain,
4602      f1,
4603      f2,
4604      f3,
4605      baseFreq,
4606      isStopped: false
4607    });
4608  }
4609  /**
4610   * Smoothly releases a hold meow with cute "-ow" decay.
4611   */
4612  stopHoldMeow(lane) {
4613    const voice = this.sustainVoices.get(lane);
4614    if (!voice || !this.ctx || voice.isStopped) return;
4615    this.sustainVoices.delete(lane);
4616    voice.isStopped = true;
4617    const now = this.ctx.currentTime;
4618    const releaseTime = 0.08;
4619    try {
4620      voice.ampGain.gain.cancelScheduledValues(now);
4621      voice.ampGain.gain.setValueAtTime(Math.max(1e-4, voice.ampGain.gain.value), now);
4622      voice.ampGain.gain.linearRampToValueAtTime(1e-4, now + releaseTime);
4623      voice.oscSine.frequency.linearRampToValueAtTime(voice.baseFreq * 0.94, now + releaseTime);
4624      voice.oscTri.frequency.linearRampToValueAtTime(voice.baseFreq * 0.94, now + releaseTime);
4625      voice.oscHarm.frequency.linearRampToValueAtTime(voice.baseFreq * 1.88, now + releaseTime);
4626      const stopAt = now + releaseTime + 0.02;
4627      voice.oscSine.stop(stopAt);
4628      voice.oscTri.stop(stopAt);
4629      voice.oscHarm.stop(stopAt);
4630      voice.lfo.stop(stopAt);
4631    } catch (e) {
4632    }
4633  }
4634  /**
4635   * Stop all sustain voices on reset or pause.
4636   */
4637  stopAllHolds() {
4638    for (const lane of Array.from(this.sustainVoices.keys())) {
4639      this.stopHoldMeow(lane);
4640    }
4641  }
4642  /**
4643   * Crisp, playful soap bubble popping sound.
4644   */
4645  playBubblePop(pitchMod = 1) {
4646    this.initContext();
4647    if (!this.ctx || !this.sfxGain) return;
4648    const now = this.ctx.currentTime;
4649    const popOsc = this.ctx.createOscillator();
4650    const popGain = this.ctx.createGain();
4651    popOsc.type = "sine";
4652    const startFreq = 480 * pitchMod;
4653    const peakFreq = 1200 * pitchMod;
4654    popOsc.frequency.setValueAtTime(startFreq, now);
4655    popOsc.frequency.linearRampToValueAtTime(peakFreq, now + 0.035);
4656    popGain.gain.setValueAtTime(0.5, now);
4657    popGain.gain.linearRampToValueAtTime(1e-4, now + 0.045);
4658    popOsc.connect(popGain);
4659    popGain.connect(this.sfxGain);
4660    popOsc.start(now);
4661    popOsc.stop(now + 0.05);
4662  }
4663  /**
4664   * Playful Nintendo DS countdown chime (3, 2, 1, MEOW!).
4665   * @param step 3, 2, 1, or 0 (0 = GO/MEOW)
4666   */
4667  playCountdownTick(step) {
4668    this.initContext();
4669    if (!this.ctx || !this.sfxGain) return;
4670    const now = this.ctx.currentTime;
4671    const osc = this.ctx.createOscillator();
4672    const gain = this.ctx.createGain();
4673    osc.type = "sine";
4674    if (step === 3) {
4675      osc.frequency.setValueAtTime(523.25, now);
4676    } else if (step === 2) {
4677      osc.frequency.setValueAtTime(659.25, now);
4678    } else if (step === 1) {
4679      osc.frequency.setValueAtTime(783.99, now);
4680    } else {
4681      osc.frequency.setValueAtTime(1046.5, now);
4682      this.playCheerNya();
4683    }
4684    const dur = step === 0 ? 0.35 : 0.22;
4685    gain.gain.setValueAtTime(0.45, now);
4686    gain.gain.linearRampToValueAtTime(1e-4, now + dur);
4687    osc.connect(gain);
4688    gain.connect(this.sfxGain);
4689    osc.start(now);
4690    osc.stop(now + dur + 0.02);
4691  }
4692  /**
4693   * Soft disappointed miss mew (short, low volume, single-shot).
4694   */
4695  playMissMew() {
4696    this.initContext();
4697    if (!this.ctx || !this.sfxGain) return;
4698    const now = this.ctx.currentTime;
4699    const osc = this.ctx.createOscillator();
4700    const gain = this.ctx.createGain();
4701    osc.type = "sine";
4702    osc.frequency.setValueAtTime(320, now);
4703    osc.frequency.linearRampToValueAtTime(160, now + 0.18);
4704    gain.gain.setValueAtTime(0.22, now);
4705    gain.gain.linearRampToValueAtTime(1e-4, now + 0.18);
4706    osc.connect(gain);
4707    gain.connect(this.sfxGain);
4708    osc.start(now);
4709    osc.stop(now + 0.2);
4710  }
4711  /**
4712   * Celebratory "Nya!" cheerful chirp for combo streaks or stage clear.
4713   */
4714  playCheerNya() {
4715    this.initContext();
4716    if (!this.ctx || !this.sfxGain) return;
4717    const now = this.ctx.currentTime;
4718    const osc = this.ctx.createOscillator();
4719    const gain = this.ctx.createGain();
4720    osc.type = "triangle";
4721    osc.frequency.setValueAtTime(700, now);
4722    osc.frequency.linearRampToValueAtTime(1300, now + 0.07);
4723    osc.frequency.linearRampToValueAtTime(950, now + 0.22);
4724    gain.gain.setValueAtTime(0.4, now);
4725    gain.gain.linearRampToValueAtTime(1e-4, now + 0.22);
4726    osc.connect(gain);
4727    gain.connect(this.sfxGain);
4728    osc.start(now);
4729    osc.stop(now + 0.24);
4730  }
4731}
4732const meowChoirAudio = new MeowChoirAudioEngine();
4733const LANE_KEYS = ["D", "F", "J", "K"];
4734const MeowChoirTouchScreen = ({
4735  notes,
4736  currentTime,
4737  isPlaying,
4738  speed = 1,
4739  approachTime: propApproachTime = 2.8,
4740  onNoteHit,
4741  onNoteMiss,
4742  onEmptyTap,
4743  lanePressStates
4744}) => {
4745  const canvasRef = reactExports.useRef(null);
4746  const containerRef = reactExports.useRef(null);
4747  const lastAudioTimeRef = reactExports.useRef(currentTime);
4748  const lastAudioTimestampRef = reactExports.useRef(performance.now());
4749  const isPlayingRef = reactExports.useRef(isPlaying);
4750  const notesRef = reactExports.useRef([]);
4751  const processedIdsRef = reactExports.useRef(/* @__PURE__ */ new Set());
4752  const particlesRef = reactExports.useRef([]);
4753  const popupsRef = reactExports.useRef([]);
4754  const lanePressRef = reactExports.useRef([false, false, false, false]);
4755  const activeHoldsRef = reactExports.useRef(/* @__PURE__ */ new Map());
4756  const getSmoothCurrentTime = reactExports.useCallback(() => {
4757    if (!isPlayingRef.current) {
4758      return lastAudioTimeRef.current;
4759    }
4760    const deltaSec = (performance.now() - lastAudioTimestampRef.current) / 1e3;
4761    return lastAudioTimeRef.current + Math.max(0, deltaSec);
4762  }, []);
4763  reactExports.useEffect(() => {
4764    if (currentTime < 0.8 || currentTime < lastAudioTimeRef.current - 1.2) {
4765      processedIdsRef.current.clear();
4766      notesRef.current = [...notes];
4767      activeHoldsRef.current.clear();
4768      meowChoirAudio.stopAllHolds();
4769    } else if (currentTime > lastAudioTimeRef.current + 1.5) {
4770      notes.forEach((n) => {
4771        if (n.t < currentTime) {
4772          processedIdsRef.current.add(n.id);
4773        }
4774      });
4775      notesRef.current = notes.filter((n) => !processedIdsRef.current.has(n.id));
4776    }
4777    lastAudioTimeRef.current = currentTime;
4778    lastAudioTimestampRef.current = performance.now();
4779  }, [currentTime, notes]);
4780  reactExports.useEffect(() => {
4781    isPlayingRef.current = isPlaying;
4782    lastAudioTimestampRef.current = performance.now();
4783    if (!isPlaying) {
4784      activeHoldsRef.current.clear();
4785      meowChoirAudio.stopAllHolds();
4786    } else {
4787      lastAudioTimeRef.current = currentTime;
4788    }
4789  }, [isPlaying, currentTime]);
4790  reactExports.useEffect(() => {
4791    lanePressRef.current = [...lanePressStates];
4792  }, [lanePressStates]);
4793  reactExports.useEffect(() => {
4794    notesRef.current = notes.filter((n) => !processedIdsRef.current.has(n.id));
4795  }, [notes]);
4796  const handleLanePress = reactExports.useCallback((lane) => {
4797    if (!isPlayingRef.current) return;
4798    lanePressRef.current[lane] = true;
4799    const canvas = canvasRef.current;
4800    const width = (canvas == null ? void 0 : canvas.clientWidth) || 400;
4801    const height = (canvas == null ? void 0 : canvas.clientHeight) || 450;
4802    const laneWidth = width / 4;
4803    const targetX = laneWidth * lane + laneWidth / 2;
4804    const targetY = height - 44;
4805    const now = getSmoothCurrentTime();
4806    const currentNotes = notesRef.current;
4807    let closestNote = null;
4808    let closestDelta = 999;
4809    let closestIdx = -1;
4810    for (let i = 0; i < currentNotes.length; i++) {
4811      const n = currentNotes[i];
4812      if (n.lane === lane && !processedIdsRef.current.has(n.id)) {
4813        const delta = Math.abs(n.t - now);
4814        if (delta < closestDelta && delta <= 0.32) {
4815          closestDelta = delta;
4816          closestNote = n;
4817          closestIdx = i;
4818        }
4819      }
4820    }
4821    if (closestNote && closestIdx >= 0) {
4822      processedIdsRef.current.add(closestNote.id);
4823      let hitType = "GOOD";
4824      let popupColor = "#34d399";
4825      let popupText = "GOOD";
4826      let pts = "+300";
4827      if (closestDelta <= 0.09) {
4828        hitType = "PURRFECT";
4829        popupColor = "#facc15";
4830        popupText = "PURRFECT!";
4831        pts = "+1000";
4832      } else if (closestDelta <= 0.18) {
4833        hitType = "PAWSOME";
4834        popupColor = "#c084fc";
4835        popupText = "PAWSOME!";
4836        pts = "+650";
4837      }
4838      const particleCount = closestNote.type === "star" ? 20 : 14;
4839      const pColor = closestNote.type === "star" ? "#facc15" : "#c084fc";
4840      for (let p = 0; p < particleCount; p++) {
4841        const angle = Math.PI * 2 * p / particleCount + (Math.random() - 0.5);
4842        const spd = 2.5 + Math.random() * 5;
4843        particlesRef.current.push({
4844          x: targetX,
4845          y: targetY,
4846          vx: Math.cos(angle) * spd,
4847          vy: Math.sin(angle) * spd - 1.5,
4848          radius: 2.5 + Math.random() * 3.5,
4849          color: pColor,
4850          alpha: 1,
4851          decay: 0.035 + Math.random() * 0.03
4852        });
4853      }
4854      popupsRef.current.push({
4855        text: popupText,
4856        color: popupColor,
4857        x: targetX,
4858        y: targetY - 26,
4859        alpha: 1,
4860        points: pts
4861      });
4862      if (closestNote.isHold && closestNote.duration >= 0.35) {
4863        activeHoldsRef.current.set(lane, {
4864          note: closestNote,
4865          lane,
4866          startTime: now,
4867          isStar: closestNote.type === "star"
4868        });
4869        meowChoirAudio.startHoldMeow(lane, closestNote.pitch, closestNote.type === "star");
4870      }
4871      onNoteHit(lane, hitType, closestNote);
4872    } else {
4873      meowChoirAudio.playBubblePop(0.85);
4874      if (onEmptyTap) onEmptyTap(lane);
4875    }
4876  }, [getSmoothCurrentTime, onNoteHit, onEmptyTap]);
4877  const handleLaneRelease = reactExports.useCallback((lane) => {
4878    lanePressRef.current[lane] = false;
4879    const activeHold = activeHoldsRef.current.get(lane);
4880    if (activeHold) {
4881      activeHoldsRef.current.delete(lane);
4882      meowChoirAudio.stopHoldMeow(lane);
4883      const now = getSmoothCurrentTime();
4884      const heldDuration = now - activeHold.startTime;
4885      const targetDuration = activeHold.note.duration;
4886      if (heldDuration >= targetDuration * 0.65) {
4887        const canvas = canvasRef.current;
4888        const width = (canvas == null ? void 0 : canvas.clientWidth) || 400;
4889        const height = (canvas == null ? void 0 : canvas.clientHeight) || 450;
4890        const laneWidth = width / 4;
4891        const targetX = laneWidth * lane + laneWidth / 2;
4892        const targetY = height - 44;
4893        popupsRef.current.push({
4894          text: "PURRFECT HOLD!",
4895          color: "#facc15",
4896          x: targetX,
4897          y: targetY - 36,
4898          alpha: 1,
4899          points: "+500"
4900        });
4901      }
4902    }
4903  }, [getSmoothCurrentTime]);
4904  const activePointerLanes = reactExports.useRef(/* @__PURE__ */ new Map());
4905  const handlePointerDown = (e) => {
4906    var _a;
4907    const rect = (_a = canvasRef.current) == null ? void 0 : _a.getBoundingClientRect();
4908    if (!rect) return;
4909    const clientX = e.clientX - rect.left;
4910    const lane = Math.min(3, Math.max(0, Math.floor(clientX / rect.width * 4)));
4911    activePointerLanes.current.set(e.pointerId, lane);
4912    handleLanePress(lane);
4913  };
4914  const handlePointerUp = (e) => {
4915    const lane = activePointerLanes.current.get(e.pointerId);
4916    if (lane !== void 0) {
4917      activePointerLanes.current.delete(e.pointerId);
4918      handleLaneRelease(lane);
4919    }
4920  };
4921  const handlePointerCancel = (e) => {
4922    const lane = activePointerLanes.current.get(e.pointerId);
4923    if (lane !== void 0) {
4924      activePointerLanes.current.delete(e.pointerId);
4925      handleLaneRelease(lane);
4926    }
4927  };
4928  reactExports.useEffect(() => {
4929    const handleKeyDown = (e) => {
4930      if (e.repeat) return;
4931      const k = e.key.toLowerCase();
4932      let lane = -1;
4933      if (k === "d" || k === "arrowleft") lane = 0;
4934      else if (k === "f" || k === "arrowdown") lane = 1;
4935      else if (k === "j" || k === "arrowup") lane = 2;
4936      else if (k === "k" || k === "arrowright") lane = 3;
4937      if (lane !== -1) {
4938        e.preventDefault();
4939        handleLanePress(lane);
4940      }
4941    };
4942    const handleKeyUp = (e) => {
4943      const k = e.key.toLowerCase();
4944      let lane = -1;
4945      if (k === "d" || k === "arrowleft") lane = 0;
4946      else if (k === "f" || k === "arrowdown") lane = 1;
4947      else if (k === "j" || k === "arrowup") lane = 2;
4948      else if (k === "k" || k === "arrowright") lane = 3;
4949      if (lane !== -1) {
4950        e.preventDefault();
4951        handleLaneRelease(lane);
4952      }
4953    };
4954    window.addEventListener("keydown", handleKeyDown);
4955    window.addEventListener("keyup", handleKeyUp);
4956    return () => {
4957      window.removeEventListener("keydown", handleKeyDown);
4958      window.removeEventListener("keyup", handleKeyUp);
4959    };
4960  }, [handleLanePress, handleLaneRelease]);
4961  reactExports.useEffect(() => {
4962    let animId;
4963    const canvas = canvasRef.current;
4964    if (!canvas) return;
4965    const ctx = canvas.getContext("2d");
4966    if (!ctx) return;
4967    const approachTime = propApproachTime;
4968    const render = () => {
4969      var _a;
4970      const dpr = window.devicePixelRatio || 1;
4971      const width = canvas.clientWidth;
4972      const height = canvas.clientHeight;
4973      if (canvas.width !== width * dpr || canvas.height !== height * dpr) {
4974        canvas.width = width * dpr;
4975        canvas.height = height * dpr;
4976      }
4977      ctx.save();
4978      ctx.scale(dpr, dpr);
4979      ctx.clearRect(0, 0, width, height);
4980      const laneWidth = width / 4;
4981      const targetY = height - 44;
4982      const ringRadius = Math.min(26, Math.max(20, laneWidth * 0.26));
4983      const now = getSmoothCurrentTime();
4984      const topTrimHeight = 12;
4985      ctx.fillStyle = "#b47b42";
4986      ctx.fillRect(0, 0, width, topTrimHeight);
4987      ctx.fillStyle = "#6e4318";
4988      ctx.fillRect(0, topTrimHeight - 2, width, 2);
4989      const pianoGrad = ctx.createLinearGradient(0, topTrimHeight, 0, height);
4990      pianoGrad.addColorStop(0, "#e2e8f0");
4991      pianoGrad.addColorStop(0.7, "#f8fafc");
4992      pianoGrad.addColorStop(1, "#ffffff");
4993      ctx.fillStyle = pianoGrad;
4994      ctx.fillRect(0, topTrimHeight, width, height - topTrimHeight);
4995      ctx.strokeStyle = "#cbd5e1";
4996      ctx.lineWidth = 1.5;
4997      for (let i = 1; i < 4; i++) {
4998        const lx = laneWidth * i;
4999        ctx.beginPath();
5000        ctx.moveTo(lx, topTrimHeight);
5001        ctx.lineTo(lx, height);
5002        ctx.stroke();
5003      }
5004      const blackKeyWidth = Math.min(22, laneWidth * 0.3);
5005      const blackKeyHeight = (height - topTrimHeight) * 0.45;
5006      [1, 2, 3].forEach((dividerLane) => {
5007        const bx = laneWidth * dividerLane - blackKeyWidth / 2;
5008        ctx.fillStyle = "rgba(0, 0, 0, 0.20)";
5009        ctx.fillRect(bx + 2, topTrimHeight + 2, blackKeyWidth, blackKeyHeight);
5010        const bkGrad = ctx.createLinearGradient(bx, topTrimHeight, bx + blackKeyWidth, topTrimHeight);
5011        bkGrad.addColorStop(0, "#1e293b");
5012        bkGrad.addColorStop(0.5, "#334155");
5013        bkGrad.addColorStop(1, "#0f172a");
5014        ctx.fillStyle = bkGrad;
5015        ctx.beginPath();
5016        ctx.roundRect(bx, topTrimHeight, blackKeyWidth, blackKeyHeight, [0, 0, 4, 4]);
5017        ctx.fill();
5018        ctx.fillStyle = "#475569";
5019        ctx.fillRect(bx, topTrimHeight + blackKeyHeight - 3, blackKeyWidth, 3);
5020      });
5021      for (let i = 0; i < 4; i++) {
5022        const targetX = laneWidth * i + laneWidth / 2;
5023        const isPressed = lanePressRef.current[i];
5024        const isHolding = activeHoldsRef.current.has(i);
5025        ctx.save();
5026        if (isPressed || isHolding) {
5027          const glowGrad = ctx.createLinearGradient(0, topTrimHeight, 0, targetY + ringRadius);
5028          glowGrad.addColorStop(0, "rgba(192, 132, 252, 0.05)");
5029          glowGrad.addColorStop(0.7, "rgba(192, 132, 252, 0.25)");
5030          glowGrad.addColorStop(1, isHolding ? "rgba(250, 204, 21, 0.45)" : "rgba(192, 132, 252, 0.50)");
5031          ctx.fillStyle = glowGrad;
5032          ctx.fillRect(laneWidth * i, topTrimHeight, laneWidth, height - topTrimHeight);
5033        }
5034        ctx.beginPath();
5035        ctx.arc(targetX, targetY + 2, ringRadius + 2, 0, Math.PI * 2);
5036        ctx.fillStyle = "rgba(0, 0, 0, 0.22)";
5037        ctx.fill();
5038        ctx.beginPath();
5039        ctx.arc(targetX, targetY, ringRadius, 0, Math.PI * 2);
5040        ctx.fillStyle = isPressed || isHolding ? "rgba(192, 132, 252, 0.65)" : "rgba(51, 65, 85, 0.40)";
5041        ctx.fill();
5042        ctx.strokeStyle = isPressed || isHolding ? "#ffffff" : "#cbd5e1";
5043        ctx.lineWidth = isPressed || isHolding ? 4 : 3;
5044        ctx.stroke();
5045        ctx.beginPath();
5046        ctx.arc(targetX, targetY, ringRadius - 5, 0, Math.PI * 2);
5047        ctx.strokeStyle = isPressed || isHolding ? "#facc15" : "rgba(255, 255, 255, 0.4)";
5048        ctx.lineWidth = 1.5;
5049        ctx.stroke();
5050        const isMobile = typeof window !== "undefined" && (isMobileDevice() || window.matchMedia && window.matchMedia("(pointer: coarse)").matches && !window.matchMedia("(pointer: fine)").matches);
5051        if (!isMobile) {
5052          const keyLetter = LANE_KEYS[i];
5053          const badgeWidth = 24;
5054          const badgeHeight = 16;
5055          const badgeX = targetX - badgeWidth / 2;
5056          const badgeY = targetY - badgeHeight / 2;
5057          ctx.save();
5058          ctx.beginPath();
5059          ctx.roundRect(badgeX, badgeY, badgeWidth, badgeHeight, 4);
5060          if (isPressed || isHolding) {
5061            ctx.fillStyle = "#facc15";
5062            ctx.fill();
5063            ctx.strokeStyle = "#ffffff";
5064            ctx.lineWidth = 1.5;
5065            ctx.stroke();
5066            ctx.fillStyle = "#0f172a";
5067            ctx.font = "black 11px monospace";
5068            ctx.textAlign = "center";
5069            ctx.textBaseline = "middle";
5070            ctx.fillText(keyLetter, targetX, targetY);
5071          } else {
5072            ctx.fillStyle = "rgba(15, 23, 42, 0.75)";
5073            ctx.fill();
5074            ctx.strokeStyle = "rgba(255, 255, 255, 0.45)";
5075            ctx.lineWidth = 1;
5076            ctx.stroke();
5077            ctx.fillStyle = "#ffffff";
5078            ctx.font = "bold 10px monospace";
5079            ctx.textAlign = "center";
5080            ctx.textBaseline = "middle";
5081            ctx.fillText(keyLetter, targetX, targetY);
5082          }
5083          ctx.restore();
5084        } else {
5085          ctx.save();
5086          ctx.beginPath();
5087          ctx.arc(targetX, targetY, isPressed || isHolding ? 7 : 4, 0, Math.PI * 2);
5088          ctx.fillStyle = isPressed || isHolding ? "#facc15" : "rgba(255, 255, 255, 0.45)";
5089          ctx.fill();
5090          ctx.restore();
5091        }
5092        if (isHolding) {
5093          const h = activeHoldsRef.current.get(i);
5094          const pCol = (h == null ? void 0 : h.isStar) ? "#facc15" : "#c084fc";
5095          for (let s = 0; s < 2; s++) {
5096            const spd = 2 + Math.random() * 3.5;
5097            const ang = -Math.PI / 2 + (Math.random() - 0.5) * 1.5;
5098            particlesRef.current.push({
5099              x: targetX + (Math.random() - 0.5) * 16,
5100              y: targetY - 4,
5101              vx: Math.cos(ang) * spd,
5102              vy: Math.sin(ang) * spd,
5103              radius: 2 + Math.random() * 2.5,
5104              color: pCol,
5105              alpha: 1,
5106              decay: 0.05
5107            });
5108          }
5109        }
5110        ctx.restore();
5111      }
5112      const notesList = notesRef.current;
5113      const scrollDistance = targetY - topTrimHeight;
5114      for (let i = 0; i < notesList.length; i++) {
5115        const note = notesList[i];
5116        const dt = note.t - now;
5117        const isProcessed = processedIdsRef.current.has(note.id);
5118        const isStar = note.type === "star";
5119        const bubbleR = ringRadius * 0.9;
5120        const bubbleX = laneWidth * note.lane + laneWidth / 2;
5121        if (dt > approachTime || dt < -0.3 && !note.isHold) continue;
5122        const progress = 1 - dt / approachTime;
5123        const bubbleY = topTrimHeight + progress * scrollDistance;
5124        if (note.isHold && note.duration >= 0.35) {
5125          const tailLengthPx = note.duration / approachTime * scrollDistance;
5126          const tailTopY = bubbleY - tailLengthPx;
5127          const isLaneHolding = activeHoldsRef.current.has(note.lane) && ((_a = activeHoldsRef.current.get(note.lane)) == null ? void 0 : _a.note.id) === note.id;
5128          if (bubbleY >= topTrimHeight && tailTopY <= targetY + 20) {
5129            ctx.save();
5130            const ribbonStartY = Math.max(topTrimHeight, tailTopY);
5131            const ribbonEndY = isLaneHolding ? targetY : Math.min(targetY + 20, bubbleY);
5132            const ribbonWidth = bubbleR * 1.25;
5133            if (ribbonEndY > ribbonStartY) {
5134              const ribGrad = ctx.createLinearGradient(0, ribbonStartY, 0, ribbonEndY);
5135              if (isStar) {
5136                ribGrad.addColorStop(0, "rgba(250, 204, 21, 0.3)");
5137                ribGrad.addColorStop(1, "rgba(250, 204, 21, 0.75)");
5138              } else {
5139                ribGrad.addColorStop(0, "rgba(168, 85, 247, 0.3)");
5140                ribGrad.addColorStop(1, "rgba(216, 180, 254, 0.75)");
5141              }
5142              ctx.beginPath();
5143              ctx.roundRect(bubbleX - ribbonWidth / 2, ribbonStartY, ribbonWidth, ribbonEndY - ribbonStartY, 8);
5144              ctx.fillStyle = ribGrad;
5145              ctx.fill();
5146              ctx.strokeStyle = isStar ? "rgba(254, 240, 138, 0.8)" : "rgba(255, 255, 255, 0.7)";
5147              ctx.lineWidth = 1.5;
5148              ctx.stroke();
5149              ctx.beginPath();
5150              ctx.moveTo(bubbleX, ribbonStartY + 4);
5151              ctx.lineTo(bubbleX, ribbonEndY - 4);
5152              ctx.strokeStyle = "rgba(255, 255, 255, 0.6)";
5153              ctx.lineWidth = 2;
5154              ctx.stroke();
5155            }
5156            ctx.restore();
5157          }
5158        }
5159        if (isProcessed && !note.isHold) continue;
5160        let bubbleAlpha = 1;
5161        if (dt < 0) {
5162          bubbleAlpha = Math.max(0, 1 + dt / 0.3);
5163        }
5164        ctx.save();
5165        ctx.globalAlpha = bubbleAlpha;
5166        ctx.beginPath();
5167        ctx.arc(bubbleX, bubbleY, bubbleR + 5, 0, Math.PI * 2);
5168        ctx.fillStyle = isStar ? "rgba(250, 204, 21, 0.40)" : "rgba(192, 132, 252, 0.45)";
5169        ctx.fill();
5170        const bGrad = ctx.createRadialGradient(
5171          bubbleX - bubbleR * 0.3,
5172          bubbleY - bubbleR * 0.3,
5173          bubbleR * 0.1,
5174          bubbleX,
5175          bubbleY,
5176          bubbleR
5177        );
5178        if (isStar) {
5179          bGrad.addColorStop(0, "#ffffff");
5180          bGrad.addColorStop(0.3, "rgba(254, 240, 138, 0.9)");
5181          bGrad.addColorStop(1, "#eab308");
5182        } else {
5183          bGrad.addColorStop(0, "#ffffff");
5184          bGrad.addColorStop(0.35, "rgba(216, 180, 254, 0.9)");
5185          bGrad.addColorStop(0.85, "#9333ea");
5186          bGrad.addColorStop(1, "#6b21a8");
5187        }
5188        ctx.beginPath();
5189        ctx.arc(bubbleX, bubbleY, bubbleR, 0, Math.PI * 2);
5190        ctx.fillStyle = bGrad;
5191        ctx.fill();
5192        ctx.beginPath();
5193        ctx.ellipse(
5194          bubbleX - bubbleR * 0.35,
5195          bubbleY - bubbleR * 0.35,
5196          bubbleR * 0.3,
5197          bubbleR * 0.15,
5198          -Math.PI / 4,
5199          0,
5200          Math.PI * 2
5201        );
5202        ctx.fillStyle = "rgba(255, 255, 255, 0.92)";
5203        ctx.fill();
5204        ctx.beginPath();
5205        ctx.arc(bubbleX, bubbleY, bubbleR, 0, Math.PI * 2);
5206        ctx.strokeStyle = isStar ? "#fef08a" : "#ffffff";
5207        ctx.lineWidth = 2;
5208        ctx.stroke();
5209        ctx.fillStyle = "#ffffff";
5210        ctx.textAlign = "center";
5211        ctx.textBaseline = "middle";
5212        ctx.font = isStar ? "bold 15px sans-serif" : "bold 17px serif";
5213        ctx.fillText(isStar ? "★" : "♪", bubbleX, bubbleY + 1);
5214        ctx.restore();
5215      }
5216      if (isPlayingRef.current) {
5217        for (let i = notesList.length - 1; i >= 0; i--) {
5218          const n = notesList[i];
5219          if (processedIdsRef.current.has(n.id)) continue;
5220          if (n.t < now - 0.25) {
5221            processedIdsRef.current.add(n.id);
5222            onNoteMiss(n.lane, n);
5223          }
5224        }
5225      }
5226      const particles = particlesRef.current;
5227      for (let i = particles.length - 1; i >= 0; i--) {
5228        const p = particles[i];
5229        p.x += p.vx;
5230        p.y += p.vy;
5231        p.vy += 0.16;
5232        p.alpha -= p.decay;
5233        if (p.alpha <= 0) {
5234          particles.splice(i, 1);
5235          continue;
5236        }
5237        ctx.save();
5238        ctx.globalAlpha = p.alpha;
5239        ctx.fillStyle = p.color;
5240        ctx.beginPath();
5241        ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
5242        ctx.fill();
5243        ctx.restore();
5244      }
5245      const popups = popupsRef.current;
5246      for (let i = popups.length - 1; i >= 0; i--) {
5247        const pop = popups[i];
5248        pop.y -= 1.1;
5249        pop.alpha -= 0.025;
5250        if (pop.alpha <= 0) {
5251          popups.splice(i, 1);
5252          continue;
5253        }
5254        ctx.save();
5255        ctx.globalAlpha = pop.alpha;
5256        ctx.textAlign = "center";
5257        ctx.textBaseline = "middle";
5258        ctx.font = "black 14px monospace";
5259        ctx.fillStyle = "#000000";
5260        ctx.fillText(pop.text, pop.x + 1, pop.y + 1);
5261        ctx.fillStyle = pop.color;
5262        ctx.fillText(pop.text, pop.x, pop.y);
5263        if (pop.points) {
5264          ctx.font = "bold 11px monospace";
5265          ctx.fillStyle = "#ffffff";
5266          ctx.fillText(pop.points, pop.x, pop.y + 13);
5267        }
5268        ctx.restore();
5269      }
5270      ctx.restore();
5271      animId = requestAnimationFrame(render);
5272    };
5273    animId = requestAnimationFrame(render);
5274    return () => {
5275      cancelAnimationFrame(animId);
5276    };
5277  }, [propApproachTime, onNoteMiss, getSmoothCurrentTime]);
5278  return /* @__PURE__ */ jsxRuntimeExports.jsx(
5279    "div",
5280    {
5281      ref: containerRef,
5282      className: "w-full h-full relative overflow-hidden select-none cursor-pointer",
5283      children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5284        "canvas",
5285        {
5286          ref: canvasRef,
5287          onPointerDown: handlePointerDown,
5288          onPointerUp: handlePointerUp,
5289          onPointerCancel: handlePointerCancel,
5290          className: "w-full h-full block touch-none"
5291        }
5292      )
5293    }
5294  );
5295};
5296const MeowChoirResultsScreen = ({
5297  isOpen,
5298  result,
5299  locale,
5300  songTitle,
5301  onPlayAgain,
5302  onBackToArcade
5303}) => {
5304  if (!isOpen || !result) return null;
5305  const grade = result.accuracy >= 95 ? "S" : result.accuracy >= 85 ? "A" : result.accuracy >= 70 ? "B" : "C";
5306  const gradeColor = grade === "S" ? "from-amber-300 via-yellow-400 to-amber-500" : grade === "A" ? "from-purple-400 via-pink-400 to-rose-400" : grade === "B" ? "from-sky-4
530600 to-blue-500" : "from-slate-400 to-zinc-500";
5307  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/85 backdrop-blur-md animate-in fade-in zoom-in-95 duration-200", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-md bg-gradient-to-b from-[#11183c] via-[#0f172a] to-[#0a0f24] border-2 border-purple-500/50 rounded-3xl p-6 shadow-[0_0_50px_rgba(168,85,247,0.35)] flex flex-col items-center text-center", children: [
5308    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-purple-900/60 border border-purple-400/40 text-purple-300 text-xs font-bold uppercase tracking-wider mb-2", children: [
5309      /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 13, className: "text-amber-300" }),
5310      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.resultsTitle })
5311    ] }),
5312    /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl sm:text-2xl font-black text-white tracking-wide mb-1", children: locale.stageClear }),
5313    songTitle && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-purple-200/70 truncate max-w-[280px] mb-4", children: songTitle }),
5314    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full bg-slate-900/80 border border-purple-500/30 rounded-2xl p-4 mb-4 flex items-center justify-around", children: [
5315      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
5316        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-slate-400 uppercase tracking-widest font-mono", children: "GRADE" }),
5317        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-6xl font-black text-transparent bg-clip-text bg-gradient-to-br ${gradeColor} drop-shadow-[0_0_15px_rgba(250,204,21,0.5)]`, children: grade })
5318      ] }),
5319      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-end", children: [
5320        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-slate-400 uppercase tracking-widest font-mono", children: locale.finalScore }),
5321        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-3xl sm:text-4xl font-black text-white tracking-wider", children: result.score.toLocaleString() }),
5322        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-amber-400 font-bold mt-1", children: [
5323          result.accuracy.toFixed(1),
5324          "% ",
5325          locale.accuracy
5326        ] })
5327      ] })
5328    ] }),
5329    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full grid grid-cols-2 gap-2 text-xs font-mono mb-6", children: [
5330      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-slate-900/60 border border-slate-800 rounded-xl p-2.5 flex items-center justify-between", children: [
5331        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400 font-bold", children: locale.purrfect }),
5332        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-white font-black", children: result.purrfectCount })
5333      ] }),
5334      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-slate-900/60 border border-slate-800 rounded-xl p-2.5 flex items-center justify-between", children: [
5335        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-purple-400 font-bold", children: locale.pawsome }),
5336        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-white font-black", children: result.pawsomeCount })
5337      ] }),
5338      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-slate-900/60 border border-slate-800 rounded-xl p-2.5 flex items-center justify-between", children: [
5339        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: locale.good }),
5340        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-white font-black", children: result.goodCount })
5341      ] }),
5342      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-slate-900/60 border border-slate-800 rounded-xl p-2.5 flex items-center justify-between", children: [
5343        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-rose-400 font-bold", children: locale.miss }),
5344        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-white font-black", children: result.missCount })
5345      ] }),
5346      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-2 bg-slate-900/60 border border-slate-800 rounded-xl p-2.5 flex items-center justify-between", children: [
5347        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-purple-300 font-bold", children: locale.maxCombo }),
5348        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-amber-400 font-black", children: [
5349          result.maxCombo,
5350          "x"
5351        ] })
5352      ] })
5353    ] }),
5354    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full flex items-center gap-3", children: [
5355      /* @__PURE__ */ jsxRuntimeExports.jsxs(
5356        "button",
5357        {
5358          type: "button",
5359          onClick: onPlayAgain,
5360          className: "flex-1 py-3 px-4 rounded-xl bg-gradient-to-r from-purple-600 via-pink-600 to-rose-600 hover:from-purple-500 hover:to-rose-500 text-white font-black text-sm uppercase tracking-wider shadow-[0_0_20px_rgba(168,85,247,0.4)] flex items-center justify-center gap-2 transition-all transform active:scale-95 cursor-pointer",
5361          children: [
5362            /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 16 }),
5363            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.playAgain })
5364          ]
5365        }
5366      ),
5367      /* @__PURE__ */ jsxRuntimeExports.jsxs(
5368        "button",
5369        {
5370          type: "button",
5371          onClick: onBackToArcade,
5372          className: "py-3 px-4 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white font-bold text-xs uppercase tracking-wider border border-slate-700 flex items-center justify-center gap-1.5 transition-all cursor-pointer",
5373          children: [
5374            /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 15 }),
5375            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.backToArcade })
5376          ]
5377        }
5378      )
5379    ] })
5380  ] }) });
5381};
5382const DIFFICULTY_SPECS = {
5383  kitten: {
5384    minInterval: 0.85,
5385    minGlobalInterval: 0.45,
5386    pitchRange: [60, 76],
5387    starChance: 0.12,
5388    speedMultiplier: 0.85,
5389    approachTime: 3.5
5390  },
5391  playful: {
5392    minInterval: 0.6,
5393    minGlobalInterval: 0.3,
5394    pitchRange: [55, 84],
5395    starChance: 0.15,
5396    speedMultiplier: 1,
5397    approachTime: 2.9
5398  },
5399  wildcat: {
5400    minInterval: 0.45,
5401    minGlobalInterval: 0.22,
5402    pitchRange: [50, 88],
5403    starChance: 0.18,
5404    speedMultiplier: 1.15,
5405    approachTime: 2.4
5406  },
5407  meowstro: {
5408    minInterval: 0.35,
5409    minGlobalInterval: 0.16,
5410    pitchRange: [48, 92],
5411    starChance: 0.22,
5412    speedMultiplier: 1.3,
5413    approachTime: 1.95
5414  }
5415};
5416function createSeededPRNG(seedInput) {
5417  let h = 2166136261;
5418  const str = String(seedInput);
5419  for (let i = 0; i < str.length; i++) {
5420    h ^= str.charCodeAt(i);
5421    h = Math.imul(h, 16777619);
5422  }
5423  let s = h >>> 0;
5424  return function() {
5425    s |= 0;
5426    s = s + 1831565813 | 0;
5427    let t = Math.imul(s ^ s >>> 15, 1 | s);
5428    t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
5429    return ((t ^ t >>> 14) >>> 0) / 4294967296;
5430  };
5431}
5432function pitchToLane(pitch, minPitch, maxPitch) {
5433  const norm = Math.max(0, Math.min(1, (pitch - minPitch) / Math.max(1, maxPitch - minPitch)));
5434  return Math.min(3, Math.max(0, Math.floor(norm * 4)));
5435}
5436function extractNotesFromData(data) {
5437  if (!data) return [];
5438  const result = [];
5439  const parseSingle = (item) => {
5440    if (!item || typeof item !== "object") return null;
5441    const t = item.t !== void 0 ? Number(item.t) : item.time !== void 0 ? Number(item.time) : item.start !== void 0 ? Number(item.start) : null;
5442    const pitch = item.n !== void 0 ? Number(item.n) : item.midi !== void 0 ? Number(item.midi) : item.pitch !== void 0 ? Number(item.pitch) : null;
5443    const duration = item.d !== void 0 ? Number(item.d) : item.duration !== void 0 ? Number(item.duration) : 0.25;
5444    if (t === null || pitch === null || isNaN(t) || isNaN(pitch)) return null;
5445    return {
5446      t: Math.max(0, t),
5447      pitch: Math.min(127, Math.max(0, Math.round(pitch))),
5448      duration: isNaN(duration) || duration <= 0 ? 0.25 : duration,
5449      instrument: String(item.i || item.instrument || ""),
5450      trackIdx: item.trackIdx !== void 0 ? Number(item.trackIdx) : item.track !== void 0 ? Number(item.track) : void 0
5451    };
5452  };
5453  if (Array.isArray(data)) {
5454    if (data.length > 0) {
5455      const first = data[0];
5456      if (first && typeof first === "object" && (Array.isArray(first.notes) || Array.isArray(first.events))) {
5457        data.forEach((track) => {
5458          const list = track.notes || track.events || [];
5459          if (Array.isArray(list)) {
5460            list.forEach((n) => {
5461              const p = parseSingle(n);
5462              if (p) {
5463                if (p.trackIdx === void 0 && track.trackIdx !== void 0) p.trackIdx = track.trackIdx;
5464                if (!p.instrument && track.name) p.instrument = track.name;
5465                result.push(p);
5466              }
5467            });
5468          }
5469        });
5470      } else {
5471        data.forEach((n) => {
5472          const p = parseSingle(n);
5473          if (p) result.push(p);
5474        });
5475      }
5476    }
5477  } else if (typeof data === "object") {
5478    if (Array.isArray(data.notes)) {
5479      data.notes.forEach((n) => {
5480        const p = parseSingle(n);
5481        if (p) result.push(p);
5482      });
5483    } else if (Array.isArray(data.tracks)) {
5484      data.tracks.forEach((track) => {
5485        const list = track.notes || track.events || [];
5486        if (Array.isArray(list)) {
5487          list.forEach((n) => {
5488            const p = parseSingle(n);
5489            if (p) result.push(p);
5490          });
5491        }
5492      });
5493    }
5494  }
5495  return result;
5496}
5497function isDrum(n) {
5498  if (n.trackIdx === 9 || n.trackIdx === 10) return true;
5499  const inst = (n.instrument || "").toLowerCase();
5500  return inst.includes("drum") || inst.includes("percussion") || inst.includes("cymbals") || inst.includes("sfx");
5501}
5502function generateMeowChoirChart(pianoData, melodyData, instruments, midiMetadata, difficulty = "playful", songDuration = 180, seedOrTrackId) {
5503  const spec = DIFFICULTY_SPECS[difficulty];
5504  const rawEvents = [];
5505  const seed = seedOrTrackId || (midiMetadata == null ? void 0 : midiMetadata.title) || (midiMetadata == null ? void 0 : midiMetadata.tempo) || songDuration || 42;
5506  const rng = createSeededPRNG(seed);
5507  const minFirstNoteTime = 3.2;
5508  const parsedMelody = extractNotesFromData(melodyData).filter((n) => n.t >= minFirstNoteTime && n.t < songDuration && !isDrum(n));
5509  let parsedPiano = extractNotesFromData(pianoData).filter((n) => n.t >= minFirstNoteTime && n.t < songDuration && !isDrum(n));
5510  if (parsedMelody.length < 15 && parsedPiano.length > 0) {
5511    const leadInstNames = ["vocal", "voice", "sing", "lead", "melody", "solo", "sax", "flute", "trumpet", "violin", "clarinet"];
5512    const leadTrackIndices = /* @__PURE__ */ new Set();
5513    const leadInstrumentNames = /* @__PURE__ */ new Set();
5514    if (Array.isArray(instruments)) {
5515      instruments.forEach((inst, idx) => {
5516        const name = String(inst.name || inst.displayName || inst.originalName || "").toLowerCase();
5517        if (leadInstNames.some((keyword) => name.includes(keyword)) && inst.midiChannel !== 9) {
5518          leadTrackIndices.add(inst.trackId !== void 0 ? inst.trackId : idx);
5519          if (inst.name) leadInstrumentNames.add(inst.name.toLowerCase());
5520          if (inst.displayName) leadInstrumentNames.add(inst.displayName.toLowerCase());
5521        }
5522      });
5523    }
5524    let filteredPianoNotes = [];
5525    if (leadTrackIndices.size > 0 || leadInstrumentNames.size > 0) {
5526      filteredPianoNotes = parsedPiano.filter((n) => {
5527        if (n.trackIdx !== void 0 && leadTrackIndices.has(n.trackIdx)) return true;
5528        if (n.instrument && leadInstrumentNames.has(n.instrument.toLowerCase())) return true;
5529        return false;
5530      });
5531    }
5532    if (filteredPianoNotes.length >= 15) {
5533      parsedPiano = filteredPianoNotes;
5534    } else {
5535      parsedPiano = parsedPiano.filter((n) => n.pitch >= 52);
5536    }
5537  }
5538  if (parsedMelody.length >= 15) {
5539    parsedMelody.sort((a, b) => a.t - b.t);
5540    const firstMelodyTime = parsedMelody[0].t;
5541    if (firstMelodyTime > minFirstNoteTime + 3 && parsedPiano.length > 0) {
5542      const introNotes = parsedPiano.filter((n) => n.t >= minFirstNoteTime && n.t < firstMelodyTime - 0.4 && n.pitch >= 55);
5543      rawEvents.push(...introNotes);
5544    }
5545    rawEvents.push(...parsedMelody);
5546  } else if (parsedPiano.length > 0) {
5547    rawEvents.push(...parsedPiano);
5548  }
5549  if (rawEvents.length === 0) {
5550    const bpm = Number(midiMetadata == null ? void 0 : midiMetadata.tempo) || 120;
5551    const beatSec = 60 / bpm;
5552    let t = minFirstNoteTime + 0.5;
5553    const scale = [60, 62, 64, 65, 67, 69, 71, 72];
5554    while (t < songDuration - 2) {
5555      const pitch = scale[Math.floor(rng() * scale.length)];
5556      const isLong = rng() < 0.25;
5557      rawEvents.push({ t, duration: isLong ? beatSec * 1.8 : beatSec * 0.7, pitch });
5558      t += beatSec * (difficulty === "kitten" ? 2 : 1);
5559    }
5560  }
5561  rawEvents.sort((a, b) => a.t - b.t);
5562  const isEasy = difficulty === "kitten" || difficulty === "playful";
5563  const melodyEvents = [];
5564  for (let i = 0; i < rawEvents.length; i++) {
5565    const ev = rawEvents[i];
5566    if (melodyEvents.length === 0) {
5567      melodyEvents.push(ev);
5568    } else {
5569      const last = melodyEvents[melodyEvents.length - 1];
5570      const isSimultaneous = Math.abs(ev.t - last.t) < 0.08;
5571      if (isSimultaneous) {
5572        if (isEasy) {
5573          if (ev.pitch > last.pitch) {
5574            melodyEvents[melodyEvents.length - 1] = {
5575              t: last.t,
5576              duration: Math.max(last.duration, ev.duration),
5577              pitch: ev.pitch
5578            };
5579          }
5580        } else {
5581          const countSimultaneous = melodyEvents.filter((m) => Math.abs(m.t - ev.t) < 0.08).length;
5582          if (countSimultaneous < 2 && Math.abs(ev.pitch - last.pitch) >= 4) {
5583            melodyEvents.push(ev);
5584          } else if (ev.pitch > last.pitch) {
5585            melodyEvents[melodyEvents.length - 1] = {
5586              t: last.t,
5587              duration: Math.max(last.duration, ev.duration),
5588              pitch: ev.pitch
5589            };
5590          }
5591        }
5592      } else {
5593        melodyEvents.push(ev);
5594      }
5595    }
5596  }
5597  let minP = 127;
5598  let maxP = 0;
5599  melodyEvents.forEach((e) => {
5600    if (e.pitch < minP) minP = e.pitch;
5601    if (e.pitch > maxP) maxP = e.pitch;
5602  });
5603  if (minP >= maxP) {
5604    minP = spec.pitchRange[0];
5605    maxP = spec.pitchRange[1];
5606  }
5607  const notes = [];
5608  const lastLaneTimes = [-999, -999, -999, -999];
5609  let lastGlobalTime = -999;
5610  let activeHoldEndTime = -999;
5611  let noteIdCounter = 1;
5612  let starCount = 0;
5613  for (let i = 0; i < melodyEvents.length; i++) {
5614    const ev = melodyEvents[i];
5615    if (isEasy) {
5616      if (ev.t < activeHoldEndTime + 0.15) {
5617        continue;
5618      }
5619      if (ev.t - lastGlobalTime < spec.minGlobalInterval) {
5620        continue;
5621      }
5622    } else {
5623      const isChord = Math.abs(ev.t - lastGlobalTime) < 0.08;
5624      if (!isChord && ev.t - lastGlobalTime < spec.minGlobalInterval) {
5625        continue;
5626      }
5627    }
5628    let lane = pitchToLane(ev.pitch, minP, maxP);
5629    if (ev.t - lastLaneTimes[lane] < spec.minInterval) {
5630      const altLanes = [(lane + 1) % 4, (lane + 3) % 4, (lane + 2) % 4];
5631      const freeLane = altLanes.find((l) => ev.t - lastLaneTimes[l] >= spec.minInterval);
5632      if (freeLane !== void 0) {
5633        lane = freeLane;
5634      } else {
5635        continue;
5636      }
5637    }
5638    lastLaneTimes[lane] = ev.t;
5639    lastGlobalTime = ev.t;
5640    const isStar = rng() < spec.starChance;
5641    if (isStar) starCount++;
5642    const isHold = ev.duration >= 0.38;
5643    const duration = isHold ? Math.min(2.2, Number(ev.duration.toFixed(3))) : 0.25;
5644    if (isEasy) {
5645      activeHoldEndTime = ev.t + (isHold ? duration : 0.25);
5646    }
5647    notes.push({
5648      id: noteIdCounter++,
5649      lane,
5650      t: Number(ev.t.toFixed(3)),
5651      duration,
5652      pitch: ev.pitch,
5653      type: isStar ? "star" : "normal",
5654      isHold
5655    });
5656  }
5657  const durationSpan = Math.max(1, songDuration);
5658  const density = Number((notes.length / durationSpan).toFixed(2));
5659  return {
5660    notes,
5661    meta: {
5662      difficulty,
5663      totalNotes: notes.length,
5664      starNotes: starCount,
5665      density,
5666      approachTime: spec.approachTime
5667    }
5668  };
5669}
5670const MEOW_CHOIR_LOCALES = {
5671  en: {
5672    gameTitle: "Meow Choir",
5673    gameSubtitle: "Kitten Stage Chorus Rhythm Adventure",
5674    tapToPlay: "TAP TO PLAY",
5675    tapToPlaySub: "Lead the kitten chorus around the mic!",
5676    keyboardHint: "Keys [D] [F] [J] [K] or Arrows",
5677    touchHint: "Tap the piano rings or purple bubbles",
5678    score: "SCORE",
5679    combo: "COMBO",
5680    purrfect: "PURRFECT!",
5681    pawsome: "PAWSOME!",
5682    good: "GOOD",
5683    miss: "MISS",
5684    fever: "CATNIP FEVER",
5685    feverActive: "FEVER x2!",
5686    paused: "PAUSED",
5687    resume: "RESUME",
5688    restart: "RESTART",
5689    backToArcade: "EXIT TO ARCADE",
5690    resultsTitle: "STAGE CLEAR!",
5691    stageClear: "MEOWVELOUS PERFORMANCE!",
5692    finalScore: "FINAL SCORE",
5693    maxCombo: "MAX COMBO",
5694    accuracy: "ACCURACY",
5695    playAgain: "SING AGAIN",
5696    difficulty: "DIFFICULTY",
5697    kitten: "KITTEN",
5698    playful: "PLAYFUL",
5699    wildcat: "WILDCAT",
5700    meowstro: "MEOWSTRO"
5701  },
5702  ja: {
5703    gameTitle: "にゃんこ合唱団",
5704    gameSubtitle: "マイクを囲んで歌う子猫たちのリズム合唱劇",
5705    tapToPlay: "タップしてスタート",
5706    tapToPlaySub: "マイクの周りで子猫たちと一緒に歌おう!",
5707    keyboardHint: "キーボード [D] [F] [J] [K] または 矢印キー",
5708    touchHint: "ピアノのリングまたは紫の音符バブルをタップ",
5709    score: "スコア",
5710    combo: "コンボ",
5711    purrfect: "パーフェクトにゃ!",
5712    pawsome: "すばらしいにゃ!",
5713    good: "いいね!",
5714    miss: "ミス…",
5715    fever: "マタタビフィーバー",
5716    feverActive: "フィーバー x2!",
5717    paused: "一時停止",
5718    resume: "つづける",
5719    restart: "やり直す",
5720    backToArcade: "ゲームセンターに戻る",
5721    resultsTitle: "ステージクリア!",
5722    stageClear: "素晴らしい合唱でしたにゃ!",
5723    finalScore: "最終スコア",
5724    maxCombo: "最大コンボ",
5725    accuracy: "正確さ",
5726    playAgain: "もう一度歌う",
5727    difficulty: "難易度",
5728    kitten: "こねこ",
5729    playful: "ごきげん",
5730    wildcat: "のらねこ",
5731    meowstro: "ニャエストロ"
5732  },
5733  "zh-CN": {
5734    gameTitle: "喵喵合唱团",
5735    gameSubtitle: "围绕麦克风齐声歌唱的猫咪舞台节奏游戏",
5736    tapToPlay: "点击开始",
5737    tapToPlaySub: "带领猫咪合唱团登台开唱!",
5738    keyboardHint: "按键 [D] [F] [J] [K] 或 方向键",
5739    touchHint: "点击琴键上的圆环或紫色音符气泡",
5740    score: "得分",
5741    combo: "连击",
5742    purrfect: "完美喵!",
5743    pawsome: "精彩喵!",
5744    good: "良好",
5745    miss: "失误",
5746    fever: "猫薄荷狂热",
5747    feverActive: "双倍狂热 x2!",
5748    paused: "已暂停",
5749    resume: "继续游戏",
5750    restart: "重新开始",
5751    backToArcade: "退出到街机厅",
5752    resultsTitle: "演出通关!",
5753    stageClear: "美妙绝伦的猫咪和声!",
5754    finalScore: "最终得分",
5755    maxCombo: "最高连击",
5756    accuracy: "准确率",
5757    playAgain: "再唱一曲",
5758    difficulty: "难度",
5759    kitten: "幼猫级",
5760    playful: "欢快级",
5761    wildcat: "野性级",
5762    meowstro: "猫界大师"
5763  },
5764  "zh-TW": {
5765    gameTitle: "喵喵合唱團",
5766    gameSubtitle: "圍繞麥克風齊聲合唱的貓咪舞台節奏遊戲",
5767    tapToPlay: "點擊開始",
5768    tapToPlaySub: "帶領小貓合唱團登台開唱!",
5769    keyboardHint: "按鍵 [D] [F] [J] [K] 或 方向鍵",
5770    touchHint: "點擊琴鍵上的圓環或紫色音符氣泡",
5771    score: "得分",
5772    combo: "連擊",
5773    purrfect: "完美喵!",
5774    pawsome: "精彩喵!",
5775    good: "良好",
5776    miss: "失誤",
5777    fever: "貓薄荷狂熱",
5778    feverActive: "雙倍狂熱 x2!",
5779    paused: "已暫停",
5780    resume: "繼續遊戲",
5781    restart: "重新開始",
5782    backToArcade: "返回街機中心",
5783    resultsTitle: "演出通關!",
5784    stageClear: "美妙絕倫的貓咪和聲!",
5785    finalScore: "最終得分",
5786    maxCombo: "最高連擊",
5787    accuracy: "準確率",
5788    playAgain: "再唱一次",
5789    difficulty: "難度",
5790    kitten: "幼貓級",
5791    playful: "歡快級",
5792    wildcat: "野性級",
5793    meowstro: "貓界大師"
5794  },
5795  ko: {
5796    gameTitle: "야옹이 합창단",
5797    gameSubtitle: "마이크 주위에서 함께 노래하는 아기고양이 리듬 합창단",
5798    tapToPlay: "탭하여 시작",
5799    tapToPlaySub: "마이크 앞에서 귀여운 고양이들과 합창해요!",
5800    keyboardHint: "키보드 [D] [F] [J] [K] 또는 방향키",
5801    touchHint: "피아노 건반의 링이나 보라색 음표 방울을 터치하세요",
5802    score: "점수",
5803    combo: "콤보",
5804    purrfect: "완벽냥!",
5805    pawsome: "대단해냥!",
5806    good: "좋아요",
5807    miss: "미스",
5808    fever: "캣닙 피버",
5809    feverActive: "피버 2배!",
5810    paused: "일시정지",
5811    resume: "계속하기",
5812    restart: "다시하기",
5813    backToArcade: "아케이드로 나가기",
5814    resultsTitle: "스테이지 클리어!",
5815    stageClear: "환상적인 야옹이 합창이었습니다!",
5816    finalScore: "최종 점수",
5817    maxCombo: "최대 콤보",
5818    accuracy: "정확도",
5819    playAgain: "다시 노래하기",
5820    difficulty: "난이도",
5821    kitten: "아기고양이",
5822    playful: "발랄냥",
5823    wildcat: "용맹냥",
5824    meowstro: "마에스트로"
5825  },
5826  es: {
5827    gameTitle: "Coro Miau",
5828    gameSubtitle: "Aventura rítmica de gatitos cantando al micrófono",
5829    tapToPlay: "TOCA PARA JUGAR",
5830    tapToPlaySub: "¡Dirige el coro de gatitos alrededor del micrófono!",
5831    keyboardHint: "Teclas [D] [F] [J] [K] o Flechas",
5832    touchHint: "Toca los aros del piano o las burbujas púrpuras",
5833    score: "PUNTUACIÓN",
5834    combo: "COMBO",
5835    purrfect: "¡MIAU-FECTO!",
5836    pawsome: "¡FANTÁSTICO!",
5837    good: "BIEN",
5838    miss: "FALLO",
5839    fever: "FIEBRE GATUNA",
5840    feverActive: "¡FIEBRE x2!",
5841    paused: "PAUSADO",
5842    resume: "CONTINUAR",
5843    restart: "REINICIAR",
5844    backToArcade: "VOLVER AL ARCADE",
5845    resultsTitle: "¡NIVEL COMPLETADO!",
5846    stageClear: "¡UNA ACTUACIÓN MIAU-RAVILLOSA!",
5847    finalScore: "PUNTOS FINALES",
5848    maxCombo: "COMBO MÁXIMO",
5849    accuracy: "PRECISIÓN",
5850    playAgain: "CANTAR OTRA VEZ",
5851    difficulty: "DIFICULTAD",
5852    kitten: "GATITO",
5853    playful: "JUGUETÓN",
5854    wildcat: "SALVAJE",
5855    meowstro: "MAESTRO"
5856  },
5857  fr: {
5858    gameTitle: "Chœur des Miaous",
5859    gameSubtitle: "Chœur de chatons chantant autour du micro",
5860    tapToPlay: "TOUCHER POUR JOUER",
5861    tapToPlaySub: "Dirigez la chorale de chatons sur scène !",
5862    keyboardHint: "Touches [D] [F] [J] [K] ou Flèches",
5863    touchHint: "Touchez les anneaux du piano ou les bulles violettes",
5864    score: "SCORE",
5865    combo: "COMBO",
5866    purrfect: "PARCHAT!",
5867    pawsome: "MIAOU-NIFIQUE!",
5868    good: "BIEN",
5869    miss: "MANQUÉ",
5870    fever: "FOLIE D’HERBE AUX CHATS",
5871    feverActive: "FIÈVRE x2!",
5872    paused: "EN PAUSE",
5873    resume: "REPRENDRE",
5874    restart: "RECOMMENCER",
5875    backToArcade: "RETOURNER À L’ARCADE",
5876    resultsTitle: "CONCERT TERMINÉ !",
5877    stageClear: "UNE HARMONIE MIAOU-VEILLEUSE !",
5878    finalScore: "SCORE FINAL",
5879    maxCombo: "COMBO MAX",
5880    accuracy: "PRÉCISION",
5881    playAgain: "RECHANTER",
5882    difficulty: "DIFFICULTÉ",
5883    kitten: "CHATON",
5884    playful: "JOUEUR",
5885    wildcat: "SAUVAGE",
5886    meowstro: "MAESTRO"
5887  },
5888  de: {
5889    gameTitle: "Miau-Chor",
5890    gameSubtitle: "Kätzchen-Bühnenchor Rhythmusspiel",
5891    tapToPlay: "ZUM STARTEN TIPPEN",
5892    tapToPlaySub: "Leite den singenden Kätzchenchor am Mikrofon!",
5893    keyboardHint: "Tasten [D] [F] [J] [K] oder Pfeiltasten",
5894    touchHint: "Tippe auf die Klavier-Ringe oder violette Notenblasen",
5895    score: "PUNKTE",
5896    combo: "KOMBO",
5897    purrfect: "SCHNURR-FEKT!",
5898    pawsome: "PFOTASTISCH!",
5899    good: "GUT",
5900    miss: "VERFEHLT",
5901    fever: "KATZENMINZE-FIEBER",
5902    feverActive: "FIEBER x2!",
5903    paused: "PAUSIERT",
5904    resume: "WEITER",
5905    restart: "NEUSTART",
5906    backToArcade: "ZURÜCK ZUR ARCADE",
5907    resultsTitle: "BÜHNE GEMEISTERT!",
5908    stageClear: "EIN SCHNURRIG WUNDERBARES KONZERT!",
5909    finalScore: "ENDPUNKTE",
5910    maxCombo: "MAX KOMBO",
5911    accuracy: "GENAUIGKEIT",
5912    playAgain: "NOCHMAL SINGEN",
5913    difficulty: "SCHWIERIGKEIT",
5914    kitten: "KÄTZCHEN",
5915    playful: "VERSPIELT",
5916    wildcat: "WILDKATZE",
5917    meowstro: "MIAUSTRO"
5918  },
5919  it: {
5920    gameTitle: "Coro Miao",
5921    gameSubtitle: "Coro di gattini che cantano al microfono",
5922    tapToPlay: "TOCCA PER GIOCARE",
5923    tapToPlaySub: "Guida il coro dei gattini sul palco!",
5924    keyboardHint: "Tasti [D] [F] [J] [K] o Frecce",
5925    touchHint: "Tocca i cerchi del pianoforte o le bolle viola",
5926    score: "PUNTEGGIO",
5927    combo: "COMBO",
5928    purrfect: "FUSA-TICO!",
5929    pawsome: "MIAO-VIGLIOSO!",
5930    good: "BENE",
5931    miss: "MANCATO",
5932    fever: "FEBBRE DI ERBA GATTA",
5933    feverActive: "FEBBRE x2!",
5934    paused: "IN PAUSA",
5935    resume: "RIPRENDI",
5936    restart: "RICOMINCIA",
5937    backToArcade: "TORNA ALL’ARCADE",
5938    resultsTitle: "SPETTACOLO COMPLETATO!",
5939    stageClear: "UN’ESIBIZIONE MIAO-RAVIGLIOSA!",
5940    finalScore: "PUNTEGGIO FINALE",
5941    maxCombo: "COMBO MASSIMA",
5942    accuracy: "PRECISIONE",
5943    playAgain: "CANTA ANCORA",
5944    difficulty: "DIFFICOLTÀ",
5945    kitten: "GATTINO",
5946    playful: "GIOCHERELLONE",
5947    wildcat: "SELVAGGIO",
5948    meowstro: "MEOWSTRO"
5949  },
5950  pt: {
5951    gameTitle: "Coro Miau",
5952    gameSubtitle: "Aventura rítmica de gatinhos cantando ao microfone",
5953    tapToPlay: "TOQUE PARA JOGAR",
5954    tapToPlaySub: "Comande o coral de gatinhos em volta do microfone!",
5955    keyboardHint: "Teclas [D] [F] [J] [K] ou Setas",
5956    touchHint: "Toque nos anéis do piano ou nas bolhas roxas",
5957    score: "PONTOS",
5958    combo: "COMBO",
5959    purrfect: "RONROM-FEITO!",
5960    pawsome: "MIAU-RAVILHOSO!",
5961    good: "BOM",
5962    miss: "ERROU",
5963    fever: "FEBRE DE ERVA-DOS-GATOS",
5964    feverActive: "FEBRE x2!",
5965    paused: "PAUSADO",
5966    resume: "CONTINUAR",
5967    restart: "REINICIAR",
5968    backToArcade: "VOLTAR AO ARCADE",
5969    resultsTitle: "SHOW CONCLUÍDO!",
5970    stageClear: "UMA APRESENTAÇÃO MIAU-RÍSSIMA!",
5971    finalScore: "PONTUAÇÃO FINAL",
5972    maxCombo: "COMBO MÁXIMO",
5973    accuracy: "PRECISÃO",
5974    playAgain: "CANTAR DE NOVO",
5975    difficulty: "DIFICULDADE",
5976    kitten: "GATINHO",
5977    playful: "BRINCALHÃO",
5978    wildcat: "SELVAGEM",
5979    meowstro: "MAESTRO"
5980  },
5981  ru: {
5982    gameTitle: "Мяу-Хор",
5983    gameSubtitle: "Ритм-хор котят, поющих у микрофона",
5984    tapToPlay: "НАЖМИТЕ ДЛЯ СТАРТА",
5985    tapToPlaySub: "Управляйте хором котят вокруг микрофона!",
5986    keyboardHint: "Клавиши [D] [F] [J] [K] или Стрелки",
5987    touchHint: "Нажимайте на кольца пианино или фиолетовые ноты",
5988    score: "СЧЁТ",
5989    combo: "КОМБО",
5990    purrfect: "МУРР-ФЕКТ!",
5991    pawsome: "МЯУ-ВЕЛИКОЛЕПНО!",
5992    good: "ХОРОШО",
5993    miss: "МИМО",
5994    fever: "КОШАЧЬЯ МЯТА",
5995    feverActive: "ФИВЕР x2!",
5996    paused: "ПАУЗА",
5997    resume: "ПРОДОЛЖИТЬ",
5998    restart: "ЗАНОВО",
5999    backToArcade: "В ИГРОВОЙ ЗАЛ",
6000    resultsTitle: "КОНЦЕРТ ОКОНЧЕН!",
6001    stageClear: "ПОТРЯСАЮЩЕЕ МЯУ-ВЫСТУПЛЕНИЕ!",
6002    finalScore: "ИТОГОВЫЙ СЧЁТ",
6003    maxCombo: "МАКС. КОМБО",
6004    accuracy: "ТОЧНОСТЬ",
6005    playAgain: "СПЕТЬ ЕЩЁ РАЗ",
6006    difficulty: "СЛОЖНОСТЬ",
6007    kitten: "КОТЁНОК",
6008    playful: "ИГРИВЫЙ",
6009    wildcat: "ДИКИЙ",
6010    meowstro: "МАЭСТРО"
6011  },
6012  id: {
6013    gameTitle: "Paduan Suara Kucing",
6014    gameSubtitle: "Petualangan ritme paduan suara anak kucing di sekitar mikrofon",
6015    tapToPlay: "KETUK UNTUK MAIN",
6016    tapToPlaySub: "Pimpin paduan suara anak kucing bernyanyi di panggung!",
6017    keyboardHint: "Tombol [D] [F] [J] [K] atau Tombol Panah",
6018    touchHint: "Ketuk cincin piano atau gelembung nada ungu",
6019    score: "SKOR",
6020    combo: "KOMBO",
6021    purrfect: "PURRFECT!",
6022    pawsome: "HEBAT!",
6023    good: "BAGUS",
6024    miss: "MELESET",
6025    fever: "DEMAM CATNIP",
6026    feverActive: "FEVER x2!",
6027    paused: "DIJEDA",
6028    resume: "LANJUTKAN",
6029    restart: "ULANGI",
6030    backToArcade: "KEMBALI KE ARCADE",
6031    resultsTitle: "PANGGUNG SELESAI!",
6032    stageClear: "PENAMPILAN KUCING YANG LUAR BIASA!",
6033    finalScore: "SKOR AKHIR",
6034    maxCombo: "KOMBO MAKSIMAL",
6035    accuracy: "AKURASI",
6036    playAgain: "NYANYI LAGI",
6037    difficulty: "TINGKAT KESULITAN",
6038    kitten: "ANAK KUCING",
6039    playful: "LINCAH",
6040    wildcat: "LIAR",
6041    meowstro: "MEOWSTRO"
6042  },
6043  ar: {
6044    gameTitle: "جوقة المواء",
6045    gameSubtitle: "مغامرة إيقاعية لفرقة قطط تغني حول الميكروفون",
6046    tapToPlay: "المس للبدء",
6047    tapToPlaySub: "قد جوقة القطط اللطيفة حول الميكروفون!",
6048    keyboardHint: "المفاتيح [D] [F] [J] [K] أو الأسهم",
6049    touchHint: "المس حلقات البيانو أو فقاعات النوتات البنفسجية",
6050    score: "النقاط",
6051    combo: "كومبو",
6052    purrfect: "مواء مثالي!",
6053    pawsome: "رائع جداً!",
6054    good: "جيد",
6055    miss: "فائت",
6056    fever: "حماس النعناع البري",
6057    feverActive: "مضاعف x2!",
6058    paused: "مؤقت",
6059    resume: "استئناف",
6060    restart: "إعادة",
6061    backToArcade: "خروج إلى الصالة",
6062    resultsTitle: "تم إتمام العرض!",
6063    stageClear: "أداء مواء مذهل ورائع!",
6064    finalScore: "النتيجة النهائية",
6065    maxCombo: "أعلى كومبو",
6066    accuracy: "الدقة",
6067    playAgain: "غناء مرة أخرى",
6068    difficulty: "المستوى",
6069    kitten: "قط صغير",
6070    playful: "مرح",
6071    wildcat: "بري",
6072    meowstro: "مايسترو"
6073  }
6074};
6075function getMeowChoirLocale(language = "en") {
6076  if (MEOW_CHOIR_LOCALES[language]) return MEOW_CHOIR_LOCALES[language];
6077  const prefix = language.split("-")[0];
6078  if (MEOW_CHOIR_LOCALES[prefix]) return MEOW_CHOIR_LOCALES[prefix];
6079  return MEOW_CHOIR_LOCALES.en;
6080}
6081const MeowChoirGame = ({
6082  data,
6083  melodyData,
6084  processedInstruments,
6085  midiMetadata,
6086  currentTime,
6087  isPlaying,
6088  duration,
6089  tempo = 120,
6090  timeSignature = [4, 4],
6091  trackId,
6092  userId,
6093  userName,
6094  onPlayPause,
6095  onSeek,
6096  isAudioLoaded = true,
6097  isLoadingAudio = false,
6098  difficulty: initialDifficulty = "playful",
6099  restartKey
6100}) => {
6101  const { i18n } = useTranslation();
6102  const locale = reactExports.useMemo(() => getMeowChoirLocale(i18n.language), [i18n.language]);
6103  const isMobile = reactExports.useMemo(() => typeof window !== "undefined" && (isMobileDevice() || window.matchMedia && window.matchMedia("(pointer: coarse)").matches && !window.matchMedia("(pointer: fine)").matches), []);
6104  const [hasStarted, setHasStarted] = reactExports.useState(false);
6105  const [countdown, setCountdown] = reactExports.useState(null);
6106  const [difficulty, setDifficulty] = reactExports.useState(initialDifficulty);
6107  const [score, setScore] = reactExports.useState(0);
6108  const [combo, setCombo] = reactExports.useState(0);
6109  const [maxCombo, setMaxCombo] = reactExports.useState(0);
6110  const [isSinging, setIsSinging] = reactExports.useState(false);
6111  const [isMissed, setIsMissed] = reactExports.useState(false);
6112  const [activeLaneHit, setActiveLaneHit] = reactExports.useState(null);
6113  const [isFever, setIsFever] = reactExports.useState(false);
6114  const [lanePressStates, setLanePressStates] = reactExports.useState([false, false, false, false]);
6115  const [isResultsOpen, setIsResultsOpen] = reactExports.useState(false);
6116  const [gameResult, setGameResult] = reactExports.useState(null);
6117  const purrfectRef = reactExports.useRef(0);
6118  const pawsomeRef = reactExports.useRef(0);
6119  const goodRef = reactExports.useRef(0);
6120  const missRef = reactExports.useRef(0);
6121  const singingTimeoutRef = reactExports.useRef(null);
6122  const countdownTimerRef = reactExports.useRef(null);
6123  const prevCurrentTimeRef = reactExports.useRef(currentTime);
6124  const chart = reactExports.useMemo(() => {
6125    return generateMeowChoirChart(
6126      data,
6127      melodyData,
6128      processedInstruments,
6129      midiMetadata,
6130      difficulty,
6131      duration,
6132      trackId
6133    );
6134  }, [data, melodyData, difficulty, duration, trackId]);
6135  reactExports.useEffect(() => {
6136    return () => {
6137      if (countdownTimerRef.current) {
6138        clearInterval(countdownTimerRef.current);
6139      }
6140      if (singingTimeoutRef.current) {
6141        clearTimeout(singingTimeoutRef.current);
6142      }
6143    };
6144  }, []);
6145  const startCountdownSequence = reactExports.useCallback(() => {
6146    if (countdownTimerRef.current) {
6147      clearInterval(countdownTimerRef.current);
6148      countdownTimerRef.current = null;
6149    }
6150    meowChoirAudio.initContext();
6151    setHasStarted(true);
6152    if (onSeek) onSeek(0);
6153    if (onPlayPause) onPlayPause(false);
6154    let count = 3;
6155    setCountdown(3);
6156    meowChoirAudio.playCountdownTick(3);
6157    countdownTimerRef.current = setInterval(() => {
6158      count--;
6159      if (count > 0) {
6160        setCountdown(count);
6161        meowChoirAudio.playCountdownTick(count);
6162      } else if (count === 0) {
6163        setCountdown(0);
6164        meowChoirAudio.playCountdownTick(0);
6165      } else {
6166        clearInterval(countdownTimerRef.current);
6167        countdownTimerRef.current = null;
6168        setCountdown(null);
6169        if (onSeek) onSeek(0);
6170        if (onPlayPause) onPlayPause(true);
6171      }
6172    }, 700);
6173  }, [onSeek, onPlayPause]);
6174  const handleReset = reactExports.useCallback(() => {
6175    setScore(0);
6176    setCombo(0);
6177    setMaxCombo(0);
6178    setIsSinging(false);
6179    setIsMissed(false);
6180    setIsFever(false);
6181    setIsResultsOpen(false);
6182    purrfectRef.current = 0;
6183    pawsomeRef.current = 0;
6184    goodRef.current = 0;
6185    missRef.current = 0;
6186    if (onSeek) onSeek(0);
6187    if (onPlayPause) onPlayPause(false);
6188    startCountdownSequence();
6189  }, [onSeek, onPlayPause, startCountdownSequence]);
6190  const handleInitialStart = reactExports.useCallback(() => {
6191    startCountdownSequence();
6192  }, [startCountdownSequence]);
6193  reactExports.useEffect(() => {
6194    if (restartKey !== void 0 && restartKey > 0) {
6195      handleReset();
6196    }
6197  }, [restartKey, handleReset]);
6198  reactExports.useEffect(() => {
6199    if (isPlaying && !hasStarted && countdown === null) {
6200      startCountdownSequence();
6201    }
6202  }, [isPlaying, hasStarted, countdown, startCountdownSequence]);
6203  reactExports.useEffect(() => {
6204    if (hasStarted && prevCurrentTimeRef.current > 1 && currentTime < 0.3 && isPlaying) {
6205      handleReset();
6206    }
6207    prevCurrentTimeRef.current = currentTime;
6208  }, [currentTime, hasStarted, isPlaying, handleReset]);
6209  const handleNoteHit = reactExports.useCallback((lane, type, note) => {
6210    setLanePressStates((prev) => {
6211      const next = [...prev];
6212      next[lane] = true;
6213      return next;
6214    });
6215    setTimeout(() => {
6216      setLanePressStates((prev) => {
6217        const next = [...prev];
6218        next[lane] = false;
6219        return next;
6220      });
6221    }, 110);
6222    let pts = 300;
6223    if (type === "PURRFECT") {
6224      pts = 1e3;
6225      purrfectRef.current++;
6226    } else if (type === "PAWSOME") {
6227      pts = 650;
6228      pawsomeRef.current++;
6229    } else {
6230      goodRef.current++;
6231    }
6232    if (note.type === "star") {
6233      pts = Math.round(pts * 1.5);
6234    }
6235    const multiplier = isFever ? 2 : 1;
6236    setScore((s) => s + pts * multiplier);
6237    setCombo((c) => {
6238      const nextC = c + 1;
6239      setMaxCombo((m) => Math.max(m, nextC));
6240      if (nextC % 20 === 0 && nextC > 0) {
6241        meowChoirAudio.playCheerNya();
6242      }
6243      return nextC;
6244    });
6245    setActiveLaneHit(lane);
6246    setIsSinging(true);
6247    if (singingTimeoutRef.current) clearTimeout(singingTimeoutRef.current);
6248    singingTimeoutRef.current = setTimeout(() => {
6249      setIsSinging(false);
6250      setActiveLaneHit(null);
6251    }, 220);
6252    meowChoirAudio.playBubblePop(note.type === "star" ? 1.25 : 1);
6253    meowChoirAudio.playMeow(note.pitch, note.duration, note.type === "star");
6254  }, [isFever]);
6255  const handleNoteMiss = reactExports.useCallback((lane, note) => {
6256    missRef.current++;
6257    setCombo(0);
6258  }, []);
6259  reactExports.useEffect(() => {
6260    if (currentTime >= duration && duration > 5 && !isResultsOpen && hasStarted && countdown === null) {
6261      const total = chart.notes.length || 1;
6262      const accuracy = Math.min(100, Math.max(0, (purrfectRef.current * 1 + pawsomeRef.current * 0.7 + goodRef.current * 0.4) / total * 100));
6263      setGameResult({
6264        score,
6265        maxCombo,
6266        purrfectCount: purrfectRef.current,
6267        pawsomeCount: pawsomeRef.current,
6268        goodCount: goodRef.current,
6269        missCount: missRef.current,
6270        totalNotes: total,
6271        accuracy
6272      });
6273      setIsResultsOpen(true);
6274      meowChoirAudio.playCheerNya();
6275      if (onPlayPause) onPlayPause(false);
6276    }
6277  }, [currentTime, duration, isResultsOpen, hasStarted, countdown, chart.notes.length, score, maxCombo, onPlayPause]);
6278  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-full flex flex-col items-center justify-center bg-[#070b1a] text-white relative select-none font-sans overflow-hidden", children: [
6279    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-md h-full flex flex-col justify-between shadow-[0_0_50px_rgba(0,0,0,0.8)] border-x sm:border-2 border-slate-800 rounded-none sm:rounded-3xl overflow-hidden bg-slate-950", children: [
6280      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-[48%] relative overflow-hidden border-b-4 border-slate-900 shadow-inner", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6281        MeowChoirStage,
6282        {
6283          score,
6284          combo,
6285          activeLaneHit,
6286          isSinging,
6287          isMissed,
6288          isFever
6289        }
6290      ) }),
6291      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-4 bg-gradient-to-b from-slate-900 via-slate-800 to-slate-950 flex items-center justify-between px-6 shrink-0 shadow-md border-y border-slate-800", children: [
6292        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1", children: [
6293          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-slate-950 border border-slate-700" }),
6294          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-slate-950 border border-slate-700" }),
6295          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-slate-950 border border-slate-700" })
6296        ] }),
6297        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-black uppercase tracking-widest text-slate-400", children: [
6298          "MEOW CHOIR • ",
6299          difficulty.toUpperCase()
6300        ] }),
6301        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1", children: [
6302          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-slate-950 border border-slate-700" }),
6303          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-slate-950 border border-slate-700" }),
6304          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-slate-950 border border-slate-700" })
6305        ] })
6306      ] }),
6307      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-[50%] relative overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6308        MeowChoirTouchScreen,
6309        {
6310          notes: chart.notes,
6311          currentTime,
6312          isPlaying: isPlaying && hasStarted && countdown === null,
6313          speed: chart.meta.density,
6314          approachTime: chart.meta.approachTime,
6315          onNoteHit: handleNoteHit,
6316          onNoteMiss: handleNoteMiss,
6317          lanePressStates
6318        }
6319      ) }),
6320      countdown !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-40 flex flex-col items-center justify-center bg-slate-950/65 backdrop-blur-[2px] animate-in fade-in duration-150 pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col items-center transform animate-in zoom-in-75 duration-200", children: countdown > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-24 h-24 rounded-full bg-gradient-to-tr from-purple-600 via-pink-500 to-amber-400 p-1 shadow-[0_0_40px_rgba(236,72,153,0.8)] flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-full rounded-full bg-slate-950 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-5xl font-black text-transparent bg-clip-text bg-gradient-to-br from-amber-300 via-pink-400 to-purple-400 font-mono", children: countdown }) }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-8 py-3 rounded-full bg-gradient-to-r from-pink-500 via-purple-600 to-amber-400 shadow-[0_0_50px_rgba(236,72,153,0.9)] flex items-center gap-2", children: [
6321        /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 24, className: "text-yellow-300 animate-spin" }),
6322        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-2xl font-black text-white tracking-widest uppercase", children: "MEOW!" }),
6323        /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 24, className: "text-yellow-300 animate-spin" })
6324      ] }) }, countdown) }),
6325      !hasStarted && countdown === null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-40 flex flex-col items-center justify-center p-4 pointer-events-none animate-in fade-in duration-200", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex flex-col items-center text-center max-w-sm px-6 py-4 rounded-3xl bg-slate-950/70 border-2 border-pink-500/50 shadow-[0_0_35px_rgba(236,72,153,0.4)] backdrop-blur-[2px] pointer-events-auto", children: [
6326        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
6327          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-block w-2.5 h-2.5 rounded-full bg-pink-400 animate-ping" }),
6328          /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-2xl sm:text-3xl font-black tracking-wider text-transparent bg-clip-text bg-gradient-to-r from-pink-400 via-purple-300 to-amber-300", children: locale.gameTitle })
6329        ] }),
6330        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-purple-200/90 mb-3", children: locale.tapToPlaySub }),
6331        /* @__PURE__ */ jsxRuntimeExports.jsxs(
6332          "button",
6333          {
6334            type: "button",
6335            onClick: handleInitialStart,
6336            className: "w-full py-3 px-6 rounded-2xl bg-gradient-to-r from-pink-500 via-purple-600 to-indigo-600 hover:from-pink-400 hover:to-indigo-500 text-white font-black text-base tracking-wider uppercase shadow-[0_0_25px_rgba(236,72,153,0.6)] transform hover:scale-105 active:scale-95 transition-all flex items-center justify-center gap-2 cursor-pointer",
6337            children: [
6338              /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 20, fill: "currentColor" }),
6339              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6340                locale.tapToPlay,
6341                " ",
6342                !isMobile ? "[Space]" : ""
6343              ] })
6344            ]
6345          }
6346        ),
6347        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 flex items-center justify-center gap-3 text-[10px] text-slate-300 font-mono", children: [
6348          !isMobile && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6349            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6350              "⌨️ ",
6351              locale.keyboardHint
6352            ] }),
6353            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" })
6354          ] }),
6355          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
6356            "📱 ",
6357            locale.touchHint
6358          ] })
6359        ] })
6360      ] }) })
6361    ] }),
6362    /* @__PURE__ */ jsxRuntimeExports.jsx(
6363      MeowChoirResultsScreen,
6364      {
6365        isOpen: isResultsOpen,
6366        result: gameResult,
6367        locale,
6368        songTitle: midiMetadata == null ? void 0 : midiMetadata.title,
6369        onPlayAgain: () => {
6370          handleReset();
6371        },
6372        onBackToArcade: () => {
6373          setIsResultsOpen(false);
6374          if (onPlayPause) onPlayPause(false);
6375        }
6376      }
6377    )
6378  ] });
6379};
6380const RhythmTestGrid = ({
6381  patterns,
6382  currentMeasureIndex,
6383  currentSubBeatOffset,
6384  isPlaying,
6385  isEditorMode = false,
6386  onMeasureClick,
6387  className = "",
6388  pageOffset = 0,
6389  inputMode = "keyboard"
6390}) => {
6391  const currentQuarterBeat = Math.max(0, Math.min(3, Math.floor(currentSubBeatOffset)));
6392  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `w-full h-full flex flex-col items-center justify-center max-w-full max-h-full select-none ${className}`, children: [
6393    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center gap-2 mb-1.5 shrink-0", children: [0, 1, 2, 3].map((beat) => {
6394      const isCurrentBeat = isPlaying && currentQuarterBeat === beat;
6395      return /* @__PURE__ */ jsxRuntimeExports.jsx(
6396        "div",
6397        {
6398          className: `h-1.5 rounded-full transition-all duration-75 flex items-center justify-center ${isCurrentBeat ? "w-7 sm:w-9 bg-amber-400 shadow-[0_0_10px_rgba(251,191,36,0.9)] scale-105" : "w-3.5 sm:w-4 bg-zinc-300 dark:bg-zinc-700 opacity-50"}`
6399        },
6400        beat
6401      );
6402    }) }),
6403    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full flex-1 min-h-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 grid-rows-4 gap-1.5 sm:gap-2 md:gap-2.5 w-full h-full max-h-full aspect-square max-w-full", children: patterns.slice(0, 16).map((pattern, idx) => {
6404      const globalMeasureIdx = pageOffset + idx;
6405      const isPast = isPlaying && globalMeasureIdx < currentMeasureIndex;
6406      const isActive = isPlaying && globalMeasureIdx === currentMeasureIndex;
6407      const isUpcoming = !isPlaying || globalMeasureIdx > currentMeasureIndex;
6408      return /* @__PURE__ */ jsxRuntimeExports.jsx(
6409        MeasureBox,
6410        {
6411          index: idx,
6412          globalMeasureIndex: globalMeasureIdx,
6413          pattern,
6414          isPast,
6415          isActive,
6416          isUpcoming,
6417          currentSubBeatOffset: isActive ? currentSubBeatOffset : -1,
6418          isEditorMode,
6419          inputMode,
6420          onClick: () => onMeasureClick == null ? void 0 : onMeasureClick(globalMeasureIdx)
6421        },
6422        globalMeasureIdx
6423      );
6424    }) }) })
6425  ] });
6426};
6427const MeasureBox = ({
6428  index,
6429  globalMeasureIndex,
6430  pattern,
6431  isPast,
6432  isActive,
6433  isUpcoming,
6434  currentSubBeatOffset,
6435  isEditorMode,
6436  inputMode,
6437  onClick
6438}) => {
6439  let boxClasses = "relative w-full h-full rounded-lg sm:rounded-xl md:rounded-2xl transition-all duration-150 flex items-center justify-center overflow-hidden ";
6440  if (isActive) {
6441    boxClasses += "bg-gradient-to-br from-purple-600 via-purple-700 to-indigo-800 shadow-[0_0_20px_rgba(168,85,247,0.7)] scale-[1.02] z-10 border-2 border-purple-300 ring-2 ring-purple-400/50";
6442  } else if (isPast) {
6443    boxClasses += "bg-[#e05244] shadow-md border border-red-400/30";
6444  } else {
6445    boxClasses += "bg-zinc-600/90 dark:bg-zinc-700/80 shadow-sm border border-zinc-500/20";
6446  }
6447  if (isEditorMode) {
6448    boxClasses += " cursor-pointer hover:opacity-85 hover:scale-[1.02] active:scale-95";
6449  }
6450  let nextTargetBeat = -1;
6451  if (isActive && pattern.beatOffsets && pattern.beatOffsets.length > 0) {
6452    for (const bOff of pattern.beatOffsets) {
6453      if (currentSubBeatOffset <= bOff + 0.35) {
6454        nextTargetBeat = bOff;
6455        break;
6456      }
6457    }
6458  }
6459  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6460    "div",
6461    {
6462      onClick,
6463      className: boxClasses,
6464      title: `Measure ${globalMeasureIndex + 1} (${pattern.dots} beat${pattern.dots === 1 ? "" : "s"})`,
6465      children: [
6466        isPast && /* @__PURE__ */ jsxRuntimeExports.jsx(
6467          "div",
6468          {
6469            className: "absolute inset-0 pointer-events-none opacity-25",
6470            style: {
6471              backgroundImage: `repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(0,0,0,0.3) 5px, rgba(0,0,0,0.3) 10px)`
6472            }
6473          }
6474        ),
6475        isActive && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 pointer-events-none bg-purple-400/15 animate-pulse" }),
6476        isActive && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-1 left-1.5 z-20 pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-1 py-0.2 rounded text-[7px] sm:text-[8px] font-mono font-black uppercase tracking-wider bg-black/60 text-amber-300 border border-amber-400/50 shadow-sm animate-pulse", children: inputMode === "keyboard" ? "SPACE / F,J" : inputMode === "touch" ? "TAP" : "CLAP" }) }),
6477        /* @__PURE__ */ jsxRuntimeExports.jsx(
6478          DotLayout,
6479          {
6480            dots: pattern.dots,
6481            isPast,
6482            isActive,
6483            currentSubBeatOffset,
6484            nextTargetBeat
6485          }
6486        ),
6487        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute bottom-0.5 right-1 sm:bottom-1 sm:right-1.5 text-[8px] sm:text-[9px] font-mono font-bold text-white/50 pointer-events-none", children: globalMeasureIndex + 1 })
6488      ]
6489    }
6490  );
6491};
6492const DotLayout = ({
6493  dots,
6494  isPast,
6495  isActive,
6496  currentSubBeatOffset,
6497  nextTargetBeat
6498}) => {
6499  if (dots === 0) {
6500    return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-400/50 text-[10px] sm:text-xs font-mono font-black tracking-widest uppercase", children: "REST" });
6501  }
6502  const renderDot = (beatIdx, beatOffset) => {
6503    const isHit = isActive && currentSubBeatOffset >= beatOffset - 0.05;
6504    const isTarget = isActive && nextTargetBeat === beatOffset;
6505    let dotClasses = "w-2.5 h-2.5 sm:w-3.5 sm:h-3.5 rounded-full relative transition-all duration-75 ";
6506    if (isActive) {
6507      if (isHit) {
6508        dotClasses += "bg-white shadow-[0_0_12px_rgba(255,255,255,1)] scale-110";
6509      } else if (isTarget) {
6510        dotClasses += "bg-amber-300 shadow-[0_0_8px_rgba(251,191,36,0.9)] scale-105";
6511      } else {
6512        dotClasses += "bg-purple-950/70 border border-purple-400/40";
6513      }
6514    } else if (isPast) {
6515      dotClasses += "bg-white shadow-[0_0_4px_rgba(255,255,255,0.4)]";
6516    } else {
6517      dotClasses += "bg-zinc-800/85 dark:bg-zinc-900/90 shadow-inner";
6518    }
6519    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex items-center justify-center", children: [
6520      isTarget && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -inset-1.5 sm:-inset-2 rounded-full border-2 border-amber-300/80 animate-ping pointer-events-none" }),
6521      isTarget && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute -inset-1 rounded-full border border-white/60 animate-pulse pointer-events-none" }),
6522      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: dotClasses })
6523    ] }, beatIdx);
6524  };
6525  if (dots === 1) {
6526    return renderDot(0, 0);
6527  }
6528  if (dots === 2) {
6529    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center gap-2.5 sm:gap-4 md:gap-5 w-full", children: [
6530      renderDot(0, 0),
6531      renderDot(1, 2)
6532    ] });
6533  }
6534  if (dots === 3) {
6535    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center gap-1.5 sm:gap-2.5", children: [
6536      renderDot(0, 0),
6537      renderDot(1, 1),
6538      renderDot(2, 2)
6539    ] });
6540  }
6541  if (dots === 4) {
6542    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 grid-rows-2 gap-x-2.5 gap-y-2.5 sm:gap-x-3.5 sm:gap-y-3.5 items-center justify-center", children: [
6543      renderDot(0, 0),
6544      renderDot(1, 1),
6545      renderDot(2, 2),
6546      renderDot(3, 3)
6547    ] });
6548  }
6549  if (dots === 8) {
6550    return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-4 grid-rows-2 gap-1 sm:gap-1.5 items-center justify-center", children: [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5].map((off, bIdx) => renderDot(bIdx, off)) });
6551  }
6552  return null;
6553};
6554const RhythmBongoCat = ({
6555  leftPawDown,
6556  rightPawDown,
6557  mood = "idle",
6558  combo = 0,
6559  className = "",
6560  onCatClick
6561}) => {
6562  const [leftImpact, setLeftImpact] = reactExports.useState(false);
6563  const [rightImpact, setRightImpact] = reactExports.useState(false);
6564  reactExports.useEffect(() => {
6565    if (leftPawDown) {
6566      setLeftImpact(true);
6567      const timer = setTimeout(() => setLeftImpact(false), 120);
6568      return () => clearTimeout(timer);
6569    }
6570  }, [leftPawDown]);
6571  reactExports.useEffect(() => {
6572    if (rightPawDown) {
6573      setRightImpact(true);
6574      const timer = setTimeout(() => setRightImpact(false), 120);
6575      return () => clearTimeout(timer);
6576    }
6577  }, [rightPawDown]);
6578  return /* @__PURE__ */ jsxRuntimeExports.jsx(
6579    "div",
6580    {
6581      onClick: onCatClick,
6582      className: `relative select-none pointer-events-auto cursor-pointer group ${className}`,
6583      title: "Bongo Cat! Tap or click to jam!",
6584      children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6585        "svg",
6586        {
6587          viewBox: "0 0 240 160",
6588          className: "w-full h-full overflow-visible",
6589          style: { filter: "drop-shadow(0 2px 8px rgba(0,0,0,0.15))" },
6590          children: [
6591            leftImpact && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "animate-in fade-in zoom-in duration-75", children: [
6592              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "72", y1: "124", x2: "60", y2: "132", stroke: "#ea580c", strokeWidth: "2.5", strokeLinecap: "round" }),
6593              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "82", y1: "128", x2: "80", y2: "140", stroke: "#f59e0b", strokeWidth: "2.5", strokeLinecap: "round" }),
6594              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "66", y1: "120", x2: "54", y2: "120", stroke: "#ea580c", strokeWidth: "2", strokeLinecap: "round" }),
6595              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "75", cy: "125", r: "3", fill: "#fbbf24" })
6596            ] }),
6597            rightImpact && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "animate-in fade-in zoom-in duration-75", children: [
6598              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "152", y1: "138", x2: "162", y2: "148", stroke: "#8b5cf6", strokeWidth: "2.5", strokeLinecap: "round" }),
6599              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "140", y1: "142", x2: "138", y2: "154", stroke: "#ec4899", strokeWidth: "2.5", strokeLinecap: "round" }),
6600              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "158", y1: "132", x2: "170", y2: "134", stroke: "#8b5cf6", strokeWidth: "2", strokeLinecap: "round" }),
6601              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "148", cy: "138", r: "3", fill: "#c084fc" })
6602            ] }),
6603            /* @__PURE__ */ jsxRuntimeExports.jsx(
6604              "line",
6605              {
6606                x1: "20",
6607                y1: "110",
6608                x2: "225",
6609                y2: "150",
6610                stroke: "currentColor",
6611                strokeWidth: "3.2",
6612                strokeLinecap: "round",
6613                className: "text-zinc-800 dark:text-zinc-200"
6614              }
6615            ),
6616            /* @__PURE__ */ jsxRuntimeExports.jsx(
6617              "path",
6618              {
6619                d: "M 55,116 C 55,60 70,30 115,30 C 160,30 178,60 178,142",
6620                fill: "#ffffff",
6621                stroke: "currentColor",
6622                strokeWidth: "3.2",
6623                strokeLinecap: "round",
6624                strokeLinejoin: "round",
6625                className: "text-zinc-800 dark:text-zinc-200"
6626              }
6627            ),
6628            /* @__PURE__ */ jsxRuntimeExports.jsx(
6629              "path",
6630              {
6631                d: "M 66,54 L 56,22 C 58,20 64,18 78,28 L 88,40",
6632                fill: "#ffffff",
6633                stroke: "currentColor",
6634                strokeWidth: "3.2",
6635                strokeLinecap: "round",
6636                strokeLinejoin: "round",
6637                className: "text-zinc-800 dark:text-zinc-200"
6638              }
6639            ),
6640            /* @__PURE__ */ jsxRuntimeExports.jsx(
6641              "path",
6642              {
6643                d: "M 64,30 L 74,34",
6644                stroke: "currentColor",
6645                strokeWidth: "2",
6646                strokeLinecap: "round",
6647                className: "text-pink-400"
6648              }
6649            ),
6650            /* @__PURE__ */ jsxRuntimeExports.jsx(
6651              "path",
6652              {
6653                d: "M 136,36 L 152,18 C 158,16 166,20 166,24 L 160,54",
6654                fill: "#ffffff",
6655                stroke: "currentColor",
6656                strokeWidth: "3.2",
6657                strokeLinecap: "round",
6658                strokeLinejoin: "round",
6659                className: "text-zinc-800 dark:text-zinc-200"
6660              }
6661            ),
6662            /* @__PURE__ */ jsxRuntimeExports.jsx(
6663              "path",
6664              {
6665                d: "M 152,28 L 158,36",
6666                stroke: "currentColor",
6667                strokeWidth: "2",
6668                strokeLinecap: "round",
6669                className: "text-pink-400"
6670              }
6671            ),
6672            mood === "idle" && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "text-zinc-800 dark:text-zinc-200", children: [
6673              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "86", cy: "62", r: "3.2", fill: "currentColor" }),
6674              /* @__P
6674URE__ */ jsxRuntimeExports.jsx("circle", { cx: "128", cy: "66", r: "3.2", fill: "currentColor" }),
6675              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 102,68 Q 106,71 110,68", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" })
6676            ] }),
6677            mood === "happy" && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "text-zinc-800 dark:text-zinc-200", children: [
6678              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 81,63 Q 86,57 91,63", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" }),
6679              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 123,67 Q 128,61 133,67", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" }),
6680              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 101,69 Q 107,76 113,69", fill: "#f43f5e", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" }),
6681              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "76", cy: "68", r: "4", fill: "#fda4af", opacity: "0.7" }),
6682              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "138", cy: "72", r: "4", fill: "#fda4af", opacity: "0.7" })
6683            ] }),
6684            mood === "focused" && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "text-zinc-800 dark:text-zinc-200", children: [
6685              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "80", y1: "58", x2: "92", y2: "64", stroke: "currentColor", strokeWidth: "2.8", strokeLinecap: "round" }),
6686              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "134", y1: "62", x2: "122", y2: "68", stroke: "currentColor", strokeWidth: "2.8", strokeLinecap: "round" }),
6687              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "86", cy: "64", r: "3", fill: "currentColor" }),
6688              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "127", cy: "68", r: "3", fill: "currentColor" }),
6689              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 102,72 L 110,72", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" })
6690            ] }),
6691            mood === "miss" && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "text-zinc-800 dark:text-zinc-200", children: [
6692              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 83,62 Q 86,58 89,62 T 86,66 T 83,62", fill: "none", stroke: "currentColor", strokeWidth: "2.4" }),
6693              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 125,66 Q 128,62 131,66 T 128,70 T 125,66", fill: "none", stroke: "currentColor", strokeWidth: "2.4" }),
6694              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 102,74 Q 106,71 110,74", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" }),
6695              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 148,46 Q 152,54 148,58 Q 144,58 144,54 Z", fill: "#38bdf8" })
6696            ] }),
6697            mood === "celebrate" && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
6698              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 81,63 Q 86,57 91,63", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", className: "text-zinc-800 dark:text-zinc-200" }),
6699              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 123,67 Q 128,61 133,67", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", className: "text-zinc-800 dark:text-zinc-200" }),
6700              /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 101,69 Q 107,76 113,69", fill: "#f43f5e", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", className: "text-zinc-800 dark:text-zinc-200" }),
6701              /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "106,10 90,36 122,34", fill: "#f59e0b", stroke: "#d97706", strokeWidth: "2" }),
6702              /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "106", cy: "10", r: "4.5", fill: "#ef4444" }),
6703              /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "93", y1: "28", x2: "119", y2: "26", stroke: "#3b82f6", strokeWidth: "2.5" })
6704            ] }),
6705            /* @__PURE__ */ jsxRuntimeExports.jsx("g", { className: "transition-all duration-75", children: leftPawDown ? (
6706              // Left Paw Pressed Down on Desk
6707              /* @__PURE__ */ jsxRuntimeExports.jsx(
6708                "path",
6709                {
6710                  d: "M 64,88 C 64,106 66,126 78,124 C 88,122 84,100 80,88 Z",
6711                  fill: "#ffffff",
6712                  stroke: "currentColor",
6713                  strokeWidth: "3.2",
6714                  strokeLinecap: "round",
6715                  strokeLinejoin: "round",
6716                  className: "text-zinc-800 dark:text-zinc-200"
6717                }
6718              )
6719            ) : (
6720              // Left Paw Raised Resting
6721              /* @__PURE__ */ jsxRuntimeExports.jsx(
6722                "path",
6723                {
6724                  d: "M 66,86 C 66,100 74,116 86,112 C 96,108 86,92 80,84 Z",
6725                  fill: "#ffffff",
6726                  stroke: "currentColor",
6727                  strokeWidth: "3.2",
6728                  strokeLinecap: "round",
6729                  strokeLinejoin: "round",
6730                  className: "text-zinc-800 dark:text-zinc-200"
6731                }
6732              )
6733            ) }),
6734            /* @__PURE__ */ jsxRuntimeExports.jsx("g", { className: "transition-all duration-75", children: rightPawDown ? (
6735              // Right Paw Pressed Down on Desk
6736              /* @__PURE__ */ jsxRuntimeExports.jsx(
6737                "path",
6738                {
6739                  d: "M 132,94 C 132,114 138,142 150,138 C 160,134 150,110 144,92 Z",
6740                  fill: "#ffffff",
6741                  stroke: "currentColor",
6742                  strokeWidth: "3.2",
6743                  strokeLinecap: "round",
6744                  strokeLinejoin: "round",
6745                  className: "text-zinc-800 dark:text-zinc-200"
6746                }
6747              )
6748            ) : (
6749              // Right Paw Raised Resting
6750              /* @__PURE__ */ jsxRuntimeExports.jsx(
6751                "path",
6752                {
6753                  d: "M 130,92 C 132,106 142,128 152,122 C 162,116 150,100 142,90 Z",
6754                  fill: "#ffffff",
6755                  stroke: "currentColor",
6756                  strokeWidth: "3.2",
6757                  strokeLinecap: "round",
6758                  strokeLinejoin: "round",
6759                  className: "text-zinc-800 dark:text-zinc-200"
6760                }
6761              )
6762            ) }),
6763            combo >= 5 && /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { className: "text-pink-500 animate-pulse", children: [
6764              /* @__PURE__ */ jsxRuntimeExports.jsx("text", { x: "180", y: "45", fontSize: "18", fill: "currentColor", children: "♪" }),
6765              combo >= 15 && /* @__PURE__ */ jsxRuntimeExports.jsx("text", { x: "195", y: "25", fontSize: "14", fill: "#8b5cf6", children: "♫" })
6766            ] })
6767          ]
6768        }
6769      )
6770    }
6771  );
6772};
6773class RhythmAudioEngine {
6774  constructor() {
6775    this.ctx = null;
6776    this.masterGain = null;
6777    this.isMuted = false;
6778    this.volume = 0.85;
6779  }
6780  init() {
6781    if (!this.ctx) {
6782      const AudioCtx = window.AudioContext || window.webkitAudioContext;
6783      if (AudioCtx) {
6784        this.ctx = new AudioCtx();
6785        this.masterGain = this.ctx.createGain();
6786        this.masterGain.gain.setValueAtTime(this.volume, this.ctx.currentTime);
6787        this.masterGain.connect(this.ctx.destination);
6788      }
6789    }
6790    if (this.ctx && this.ctx.state === "suspended") {
6791      this.ctx.resume().catch(() => {
6792      });
6793    }
6794  }
6795  getAudioContext() {
6796    this.init();
6797    return this.ctx;
6798  }
6799  setVolume(vol) {
6800    this.volume = Math.max(0, Math.min(1, vol));
6801    if (this.masterGain && this.ctx) {
6802      this.masterGain.gain.setValueAtTime(this.isMuted ? 0 : this.volume, this.ctx.currentTime);
6803    }
6804  }
6805  toggleMute() {
6806    this.isMuted = !this.isMuted;
6807    if (this.masterGain && this.ctx) {
6808      this.masterGain.gain.setValueAtTime(this.isMuted ? 0 : this.volume, this.ctx.currentTime);
6809    }
6810    return this.isMuted;
6811  }
6812  getIsMuted() {
6813    return this.isMuted;
6814  }
6815  /**
6816   * Synthesize High Bongo (crisp, higher pitch resonant drum)
6817   */
6818  playHighBongo(timeOffset = 0) {
6819    this.init();
6820    if (!this.ctx || !this.masterGain || this.isMuted) return;
6821    const now = this.ctx.currentTime + timeOffset;
6822    const osc = this.ctx.createOscillator();
6823    const gain = this.ctx.createGain();
6824    const filter = this.ctx.createBiquadFilter();
6825    osc.type = "sine";
6826    osc.frequency.setValueAtTime(380, now);
6827    osc.frequency.exponentialRampToValueAtTime(260, now + 0.12);
6828    filter.type = "bandpass";
6829    filter.frequency.setValueAtTime(420, now);
6830    filter.Q.setValueAtTime(3.5, now);
6831    gain.gain.setValueAtTime(0.9, now);
6832    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.15);
6833    osc.connect(filter);
6834    filter.connect(gain);
6835    gain.connect(this.masterGain);
6836    osc.start(now);
6837    osc.stop(now + 0.16);
6838    this.playDrumTransient(now, 1400, 0.25);
6839  }
6840  /**
6841   * Synthesize Low Bongo (warmer, deeper resonant drum)
6842   */
6843  playLowBongo(timeOffset = 0) {
6844    this.init();
6845    if (!this.ctx || !this.masterGain || this.isMuted) return;
6846    const now = this.ctx.currentTime + timeOffset;
6847    const osc = this.ctx.createOscillator();
6848    const gain = this.ctx.createGain();
6849    const filter = this.ctx.createBiquadFilter();
6850    osc.type = "sine";
6851    osc.frequency.setValueAtTime(220, now);
6852    osc.frequency.exponentialRampToValueAtTime(140, now + 0.2);
6853    filter.type = "bandpass";
6854    filter.frequency.setValueAtTime(260, now);
6855    filter.Q.setValueAtTime(3, now);
6856    gain.gain.setValueAtTime(1, now);
6857    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.22);
6858    osc.connect(filter);
6859    filter.connect(gain);
6860    gain.connect(this.masterGain);
6861    osc.start(now);
6862    osc.stop(now + 0.23);
6863    this.playDrumTransient(now, 900, 0.3);
6864  }
6865  /**
6866   * Clean wood click / transient for drum attack
6867   */
6868  playDrumTransient(now, freq, strength) {
6869    if (!this.ctx || !this.masterGain) return;
6870    const osc = this.ctx.createOscillator();
6871    const gain = this.ctx.createGain();
6872    osc.type = "triangle";
6873    osc.frequency.setValueAtTime(freq, now);
6874    osc.frequency.exponentialRampToValueAtTime(200, now + 0.02);
6875    gain.gain.setValueAtTime(strength, now);
6876    gain.gain.exponentialRampToValueAtTime(1e-4, now + 0.02);
6877    osc.connect(gain);
6878    gain.connect(this.masterGain);
6879    osc.start(now);
6880    osc.stop(now + 0.025);
6881  }
6882  /**
6883   * Metronome Tick (Accented - Beat 1)
6884   */
6885  playMetronomeAccent(timeOffset = 0) {
6886    this.init();
6887    if (!this.ctx || !this.masterGain || this.isMuted) return;
6888    const now = this.ctx.currentTime + timeOffset;
6889    const osc = this.ctx.createOscillator();
6890    const gain = this.ctx.createGain();
6891    osc.type = "sine";
6892    osc.frequency.setValueAtTime(1600, now);
6893    osc.frequency.exponentialRampToValueAtTime(800, now + 0.04);
6894    gain.gain.setValueAtTime(0.85, now);
6895    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.05);
6896    osc.connect(gain);
6897    gain.connect(this.masterGain);
6898    osc.start(now);
6899    osc.stop(now + 0.055);
6900  }
6901  /**
6902   * Metronome Tick (Regular - Beats 2, 3, 4)
6903   */
6904  playMetronomeTick(timeOffset = 0) {
6905    this.init();
6906    if (!this.ctx || !this.masterGain || this.isMuted) return;
6907    const now = this.ctx.currentTime + timeOffset;
6908    const osc = this.ctx.createOscillator();
6909    const gain = this.ctx.createGain();
6910    osc.type = "sine";
6911    osc.frequency.setValueAtTime(1e3, now);
6912    osc.frequency.exponentialRampToValueAtTime(500, now + 0.035);
6913    gain.gain.setValueAtTime(0.55, now);
6914    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.04);
6915    osc.connect(gain);
6916    gain.connect(this.masterGain);
6917    osc.start(now);
6918    osc.stop(now + 0.045);
6919  }
6920  /**
6921   * Acoustic Handclap Synthesis
6922   */
6923  playClap(timeOffset = 0) {
6924    this.init();
6925    if (!this.ctx || !this.masterGain || this.isMuted) return;
6926    const now = this.ctx.currentTime + timeOffset;
6927    const bufferSize = this.ctx.sampleRate * 0.18;
6928    const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
6929    const data = buffer.getChannelData(0);
6930    for (let i = 0; i < bufferSize; i++) {
6931      data[i] = Math.random() * 2 - 1;
6932    }
6933    const noise = this.ctx.createBufferSource();
6934    noise.buffer = buffer;
6935    const filter = this.ctx.createBiquadFilter();
6936    filter.type = "bandpass";
6937    filter.frequency.setValueAtTime(1200, now);
6938    filter.Q.setValueAtTime(1.5, now);
6939    const gain = this.ctx.createGain();
6940    gain.gain.setValueAtTime(0.3, now);
6941    gain.gain.setValueAtTime(0.6, now + 0.012);
6942    gain.gain.setValueAtTime(0.95, now + 0.024);
6943    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.16);
6944    noise.connect(filter);
6945    filter.connect(gain);
6946    gain.connect(this.masterGain);
6947    noise.start(now);
6948    noise.stop(now + 0.17);
6949  }
6950  /**
6951   * Resonant Woodblock
6952   */
6953  playWoodblock(high = false, timeOffset = 0) {
6954    this.init();
6955    if (!this.ctx || !this.masterGain || this.isMuted) return;
6956    const now = this.ctx.currentTime + timeOffset;
6957    const osc = this.ctx.createOscillator();
6958    const gain = this.ctx.createGain();
6959    osc.type = "sine";
6960    const baseFreq = high ? 920 : 640;
6961    osc.frequency.setValueAtTime(baseFreq, now);
6962    osc.frequency.exponentialRampToValueAtTime(baseFreq * 0.7, now + 0.05);
6963    gain.gain.setValueAtTime(0.7, now);
6964    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.06);
6965    osc.connect(gain);
6966    gain.connect(this.masterGain);
6967    osc.start(now);
6968    osc.stop(now + 0.065);
6969  }
6970  /**
6971   * Play sound according to active Sound Type
6972   */
6973  playBeatSound(soundType, isLeft = false, isAccent = false) {
6974    switch (soundType) {
6975      case "bongo":
6976        if (isAccent || isLeft) {
6977          this.playHighBongo();
6978        } else {
6979          this.playLowBongo();
6980        }
6981        break;
6982      case "metronome":
6983        if (isAccent) {
6984          this.playMetronomeAccent();
6985        } else {
6986          this.playMetronomeTick();
6987        }
6988        break;
6989      case "clap":
6990        this.playClap();
6991        break;
6992      case "woodblock":
6993        this.playWoodblock(isAccent || isLeft);
6994        break;
6995    }
6996  }
6997  /**
6998   * Count-In Voice Cue (Beats 1, 2, 3, 4)
6999   */
7000  playCountIn(beat) {
7001    this.init();
7002    if (!this.ctx || !this.masterGain || this.isMuted) return;
7003    const now = this.ctx.currentTime;
7004    const osc = this.ctx.createOscillator();
7005    const gain = this.ctx.createGain();
7006    osc.type = "triangle";
7007    const freq = beat === 1 ? 880 : beat === 2 ? 660 : beat === 3 ? 660 : 1100;
7008    osc.frequency.setValueAtTime(freq, now);
7009    gain.gain.setValueAtTime(0.65, now);
7010    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.12);
7011    osc.connect(gain);
7012    gain.connect(this.masterGain);
7013    osc.start(now);
7014    osc.stop(now + 0.13);
7015  }
7016  /**
7017   * Perfect / Good Judgement Chime
7018   */
7019  playHitFeedback(judgement) {
7020    this.init();
7021    if (!this.ctx || !this.masterGain || this.isMuted) return;
7022    const now = this.ctx.currentTime;
7023    if (judgement === "miss") {
7024      const osc2 = this.ctx.createOscillator();
7025      const gain2 = this.ctx.createGain();
7026      osc2.type = "sawtooth";
7027      osc2.frequency.setValueAtTime(140, now);
7028      osc2.frequency.linearRampToValueAtTime(70, now + 0.12);
7029      gain2.gain.setValueAtTime(0.25, now);
7030      gain2.gain.exponentialRampToValueAtTime(1e-3, now + 0.12);
7031      osc2.connect(gain2);
7032      gain2.connect(this.masterGain);
7033      osc2.start(now);
7034      osc2.stop(now + 0.13);
7035      return;
7036    }
7037    const baseFreq = judgement === "perfect" ? 1046.5 : 880;
7038    const osc = this.ctx.createOscillator();
7039    const gain = this.ctx.createGain();
7040    osc.type = "sine";
7041    osc.frequency.setValueAtTime(baseFreq, now);
7042    gain.gain.setValueAtTime(judgement === "perfect" ? 0.35 : 0.2, now);
7043    gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.1);
7044    osc.connect(gain);
7045    gain.connect(this.masterGain);
7046    osc.start(now);
7047    osc.stop(now + 0.11);
7048  }
7049  /**
7050   * Victory Fanfare Chord
7051   */
7052  playVictory() {
7053    this.init();
7054    if (!this.ctx || !this.masterGain || this.isMuted) return;
7055    const chords = [523.25, 659.25, 783.99, 1046.5];
7056    chords.forEach((freq, idx) => {
7057      if (!this.ctx || !this.masterGain) return;
7058      const now = this.ctx.currentTime + idx * 0.08;
7059      const osc = this.ctx.createOscillator();
7060      const gain = this.ctx.createGain();
7061      osc.type = "triangle";
7062      osc.frequency.setValueAtTime(freq, now);
7063      gain.gain.setValueAtTime(0.35, now);
7064      gain.gain.exponentialRampToValueAtTime(1e-3, now + 0.5);
7065      osc.connect(gain);
7066      gain.connect(this.masterGain);
7067      osc.start(now);
7068      osc.stop(now + 0.55);
7069    });
7070  }
7071}
7072const rhythmAudio = new RhythmAudioEngine();
7073class RhythmMicInput {
7074  constructor(initialSettings) {
7075    this.stream = null;
7076    this.ctx = null;
7077    this.sourceNode = null;
7078    this.filterNode = null;
7079    this.analyser = null;
7080    this.animationId = null;
7081    this.sensitivity = 1.3;
7082    this.threshold = 0.22;
7083    this.isRunning = false;
7084    this.lastTriggerTime = 0;
7085    this.refractoryMs = 110;
7086    this.baselineNoise = 0.02;
7087    this.onOnsetCallback = null;
7088    this.onLevelCallback = null;
7089    if ((initialSettings == null ? void 0 : initialSettings.sensitivity) !== void 0) this.sensitivity = initialSettings.sensitivity;
7090    if ((initialSettings == null ? void 0 : initialSettings.threshold) !== void 0) this.threshold = initialSettings.threshold;
7091  }
7092  async start() {
7093    var _a;
7094    if (this.isRunning) return true;
7095    if (typeof window === "undefined" || !((_a = navigator.mediaDevices) == null ? void 0 : _a.getUserMedia)) {
7096      console.warn("[RhythmMicInput] Microphone not supported on this platform");
7097      return false;
7098    }
7099    try {
7100      this.stream = await navigator.mediaDevices.getUserMedia({
7101        audio: {
7102          echoCancellation: false,
7103          noiseSuppression: false,
7104          autoGainControl: false
7105        }
7106      });
7107      const AudioCtx = window.AudioContext || window.webkitAudioContext;
7108      this.ctx = new AudioCtx();
7109      this.sourceNode = this.ctx.createMediaStreamSource(this.stream);
7110      this.filterNode = this.ctx.createBiquadFilter();
7111      this.filterNode.type = "highpass";
7112      this.filterNode.frequency.setValueAtTime(120, this.ctx.currentTime);
7113      this.filterNode.Q.setValueAtTime(1, this.ctx.currentTime);
7114      this.analyser = this.ctx.createAnalyser();
7115      this.analyser.fftSize = 512;
7116      this.analyser.smoothingTimeConstant = 0.15;
7117      this.sourceNode.connect(this.filterNode);
7118      this.filterNode.connect(this.analyser);
7119      this.isRunning = true;
7120      this.startAnalysisLoop();
7121      return true;
7122    } catch (err) {
7123      console.warn("[RhythmMicInput] Could not initialize microphone:", err);
7124      this.stop();
7125      return false;
7126    }
7127  }
7128  stop() {
7129    this.isRunning = false;
7130    if (this.animationId !== null) {
7131      cancelAnimationFrame(this.animationId);
7132      this.animationId = null;
7133    }
7134    if (this.stream) {
7135      this.stream.getTracks().forEach((track) => track.stop());
7136      this.stream = null;
7137    }
7138    if (this.sourceNode) {
7139      try {
7140        this.sourceNode.disconnect();
7141      } catch {
7142      }
7143      this.sourceNode = null;
7144    }
7145    if (this.filterNode) {
7146      try {
7147        this.filterNode.disconnect();
7148      } catch {
7149      }
7150      this.filterNode = null;
7151    }
7152    if (this.ctx) {
7153      try {
7154        this.ctx.close();
7155      } catch {
7156      }
7157      this.ctx = null;
7158    }
7159  }
7160  setSensitivity(val) {
7161    this.sensitivity = Math.max(0.2, Math.min(4, val));
7162  }
7163  setThreshold(val) {
7164    this.threshold = Math.max(0.04, Math.min(0.8, val));
7165  }
7166  getSensitivity() {
7167    return this.sensitivity;
7168  }
7169  getThreshold() {
7170    return this.threshold;
7171  }
7172  isListening() {
7173    return this.isRunning;
7174  }
7175  setOnOnset(callback) {
7176    this.onOnsetCallback = callback;
7177  }
7178  setOnLevel(callback) {
7179    this.onLevelCallback = callback;
7180  }
7181  startAnalysisLoop() {
7182    if (!this.analyser) return;
7183    const dataArray = new Uint8Array(this.analyser.frequencyBinCount);
7184    const analyze = () => {
7185      if (!this.isRunning || !this.analyser) return;
7186      this.analyser.getByteTimeDomainData(dataArray);
7187      let sumSq = 0;
7188      for (let i = 0; i < dataArray.length; i++) {
7189        const norm = (dataArray[i] - 128) / 128;
7190        sumSq += norm * norm;
7191      }
7192      const rms = Math.sqrt(sumSq / dataArray.length);
7193      const scaledLevel = Math.min(1, rms * this.sensitivity * 3.5);
7194      this.baselineNoise = this.baselineNoise * 0.96 + scaledLevel * 0.04;
7195      const isAbove = scaledLevel > this.threshold && scaledLevel - this.baselineNoise > 0.06;
7196      const now = performance.now();
7197      if (isAbove && now - this.lastTriggerTime > this.refractoryMs) {
7198        this.lastTriggerTime = now;
7199        if (this.onOnsetCallback) {
7200          this.onOnsetCallback(now, scaledLevel);
7201        }
7202      }
7203      if (this.onLevelCallback) {
7204        this.onLevelCallback(scaledLevel, isAbove);
7205      }
7206      this.animationId = requestAnimationFrame(analyze);
7207    };
7208    this.animationId = requestAnimationFrame(analyze);
7209  }
7210}
7211class RhythmInputManager {
7212  constructor() {
7213    this.onHitCallback = null;
7214    this.activeKeys = /* @__PURE__ */ new Set();
7215    this.micInput = null;
7216    this.isEnabled = true;
7217    this.lastSide = "left";
7218    this.handleKeyDown = this.handleKeyDown.bind(this);
7219    this.handleKeyUp = this.handleKeyUp.bind(this);
7220  }
7221  attach() {
7222    if (typeof window === "undefined") return;
7223    window.addEventListener("keydown", this.handleKeyDown, { passive: false });
7224    window.addEventListener("keyup", this.handleKeyUp);
7225  }
7226  detach() {
7227    if (typeof window === "undefined") return;
7228    window.removeEventListener("keydown", this.handleKeyDown);
7229    window.removeEventListener("keyup", this.handleKeyUp);
7230    this.activeKeys.clear();
7231    if (this.micInput) {
7232      this.micInput.stop();
7233      this.micInput = null;
7234    }
7235  }
7236  setOnHit(callback) {
7237    this.onHitCallback = callback;
7238  }
7239  setEnabled(enabled) {
7240    this.isEnabled = enabled;
7241    if (!enabled) {
7242      this.activeKeys.clear();
7243    }
7244  }
7245  setMicInput(mic) {
7246    this.micInput = mic;
7247    if (this.micInput) {
7248      this.micInput.setOnOnset((timestamp) => {
7249        if (!this.isEnabled) return;
7250        this.lastSide = this.lastSide === "left" ? "right" : "left";
7251        this.dispatchHit({
7252          timestamp,
7253          source: "mic",
7254          side: this.lastSide
7255        });
7256      });
7257    }
7258  }
7259  triggerManualHit(side = "center", source = "touch") {
7260    if (!this.isEnabled) return;
7261    const resolvedSide = side === "center" ? this.lastSide = this.lastSide === "left" ? "right" : "left" : side;
7262    this.dispatchHit({
7263      timestamp: performance.now(),
7264      source,
7265      side: resolvedSide
7266    });
7267  }
7268  handleKeyDown(e) {
7269    if (!this.isEnabled) return;
7270    const target = e.target;
7271    if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable)) {
7272      return;
7273    }
7274    if (e.repeat || this.activeKeys.has(e.code)) return;
7275    this.activeKeys.add(e.code);
7276    let side = "center";
7277    let handled = false;
7278    if (e.code === "KeyD" || e.code === "KeyF" || e.code === "KeyA" || e.code === "ArrowLeft") {
7279      side = "left";
7280      handled = true;
7281    } else if (e.code === "KeyJ" || e.code === "KeyK" || e.code === "KeyL" || e.code === "ArrowRight") {
7282      side = "right";
7283      handled = true;
7284    } else if (e.code === "Space" || e.code === "Enter" || e.code === "ArrowDown" || e.code === "ArrowUp") {
7285      this.lastSide = this.lastSide === "left" ? "right" : "left";
7286      side = this.lastSide;
7287      handled = true;
7288    }
7289    if (handled) {
7290      e.preventDefault();
7291      this.dispatchHit({
7292        timestamp: performance.now(),
7293        source: "keyboard",
7294        side,
7295        rawKey: e.code
7296      });
7297    }
7298  }
7299  handleKeyUp(e) {
7300    this.activeKeys.delete(e.code);
7301  }
7302  dispatchHit(event) {
7303    if (this.onHitCallback) {
7304      this.onHitCallback(event);
7305    }
7306  }
7307}
7308function getBeatOffsetsForDots(dots) {
7309  switch (dots) {
7310    case 0:
7311      return [];
7312    case 1:
7313      return [0];
7314    case 2:
7315      return [0, 2];
7316    case 3:
7317      return [0, 1, 2];
7318    case 4:
7319      return [0, 1, 2, 3];
7320    case 8:
7321      return [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5];
7322    default:
7323      return [0];
7324  }
7325}
7326const SCREENSHOT_DEFAULT_PATTERN = [
7327  1,
7328  1,
7329  1,
7330  4,
7331  1,
7332  1,
7333  1,
7334  4,
7335  2,
7336  1,
7337  2,
7338  4,
7339  2,
7340  1,
7341  2,
7342  4
7343];
7344function createPatternFromDotList(dotList, totalMeasures) {
7345  const count = totalMeasures || Math.max(16, dotList.length);
7346  const list = [];
7347  for (let i = 0; i < count; i++) {
7348    list.push(dotList[i % dotList.length]);
7349  }
7350  return list.map((dots, idx) => ({
7351    measureIndex: idx,
7352    dots,
7353    beatOffsets: getBeatOffsetsForDots(dots)
7354  }));
7355}
7356function generateRhythmPattern(difficulty = "medium", totalMeasures = 16) {
7357  const dots = [];
7358  switch (difficulty) {
7359    case "easy": {
7360      const pool = [1, 1, 2, 1, 2, 2, 1, 2];
7361      for (let i = 0; i < totalMeasures; i++) {
7362        dots.push(pool[Math.floor(Math.random() * pool.length)]);
7363      }
7364      break;
7365    }
7366    case "medium": {
7367      for (let i = 0; i < totalMeasures; i++) {
7368        const row = Math.floor(i / 4) % 2;
7369        if (row === 0) {
7370          dots.push([1, 1, 1, 4][i % 4]);
7371        } else {
7372          dots.push([2, 1, 2, 4][i % 4]);
7373        }
7374      }
7375      break;
7376    }
7377    case "hard": {
7378      const options = [2, 4, 4, 2, 4, 3, 4, 1];
7379      for (let i = 0; i < totalMeasures; i++) {
7380        if (i % 4 === 3) {
7381          dots.push(4);
7382        } else {
7383          dots.push(options[Math.floor(Math.random() * options.length)]);
7384        }
7385      }
7386      break;
7387    }
7388    case "master": {
7389      const options = [4, 4, 8, 4, 8, 2, 4, 8];
7390      for (let i = 0; i < totalMeasures; i++) {
7391        dots.push(options[Math.floor(Math.random() * options.length)]);
7392      }
7393      break;
7394    }
7395  }
7396  return createPatternFromDotList(dots, totalMeasures);
7397}
7398function convertMidiToMeasurePattern(pianoData, melodyData, bpm, songDuration = 180, difficulty = "medium") {
7399  const secPerBeat = 60 / Math.max(40, bpm || 120);
7400  const secPerMeasure = secPerBeat * 4;
7401  const totalMeasures = Math.max(16, Math.ceil(songDuration / secPerMeasure));
7402  const rawOnsets = [];
7403  const melNotes = (melodyData == null ? void 0 : melodyData.notes) || (Array.isArray(melodyData) ? melodyData : null);
7404  if (Array.isArray(melNotes) && melNotes.length > 0) {
7405    melNotes.forEach((n) => {
7406      const t = n.time !== void 0 ? Number(n.time) : n.t !== void 0 ? Number(n.t) : n.start !== void 0 ? Number(n.start) : -1;
7407      if (t >= 0 && t <= songDuration) {
7408        rawOnsets.push(t);
7409      }
7410    });
7411  }
7412  if (rawOnsets.length < 15 && pianoData) {
7413    const tracks = Array.isArray(pianoData) ? pianoData : pianoData.tracks || [pianoData];
7414    tracks.forEach((track) => {
7415      const notes = track.notes || track.events || (Array.isArray(track) ? track : []);
7416      if (Array.isArray(notes)) {
7417        notes.forEach((n) => {
7418          const t = n.time !== void 0 ? Number(n.time) : n.t !== void 0 ? Number(n.t) : n.start !== void 0 ? Number(n.start) : -1;
7419          if (t >= 0 && t <= songDuration) {
7420            rawOnsets.push(t);
7421          }
7422        });
7423      }
7424    });
7425  }
7426  if (rawOnsets.length === 0) {
7427    return generateRhythmPattern(difficulty, totalMeasures);
7428  }
7429  rawOnsets.sort((a, b) => a - b);
7430  const patterns = [];
7431  for (let m = 0; m < totalMeasures; m++) {
7432    const measureStart = m * secPerMeasure;
7433    const measureEnd = (m + 1) * secPerMeasure;
7434    const notesInMeasure = rawOnsets.filter((t) => t >
7434= measureStart && t < measureEnd);
7435    const distinctBeats = [];
7436    notesInMeasure.forEach((t) => {
7437      const relBeat = (t - measureStart) / secPerBeat;
7438      const quantizedBeat = Math.round(relBeat * 2) / 2;
7439      if (quantizedBeat >= 0 && quantizedBeat < 4) {
7440        const alreadyExists = distinctBeats.some((b) => Math.abs(b - quantizedBeat) < 0.25);
7441        if (!alreadyExists) {
7442          distinctBeats.push(quantizedBeat);
7443        }
7444      }
7445    });
7446    distinctBeats.sort((a, b) => a - b);
7447    let dots = 1;
7448    let beatOffsets = [0];
7449    const count = distinctBeats.length;
7450    if (count === 0) {
7451      dots = m === 0 ? 1 : 0;
7452      beatOffsets = dots === 0 ? [] : [0];
7453    } else if (count === 1) {
7454      dots = 1;
7455      beatOffsets = [0];
7456    } else if (count === 2) {
7457      dots = 2;
7458      beatOffsets = [0, 2];
7459    } else if (count === 3) {
7460      dots = 3;
7461      beatOffsets = [0, 1, 2];
7462    } else if (count === 4) {
7463      dots = 4;
7464      beatOffsets = [0, 1, 2, 3];
7465    } else {
7466      if (difficulty === "master" || difficulty === "hard") {
7467        dots = 8;
7468        beatOffsets = [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5];
7469      } else {
7470        dots = 4;
7471        beatOffsets = [0, 1, 2, 3];
7472      }
7473    }
7474    const targetNotes = beatOffsets.map((bOff) => ({
7475      time: measureStart + bOff * secPerBeat,
7476      measureIndex: m,
7477      beatOffset: bOff,
7478      hit: false
7479    }));
7480    patterns.push({
7481      measureIndex: m,
7482      dots,
7483      beatOffsets,
7484      targetNotes
7485    });
7486  }
7487  return patterns;
7488}
7489const RhythmTestResults = ({
7490  result,
7491  locale,
7492  onRetry,
7493  onNextPattern,
7494  onSelectSong,
7495  onChangeCabinet
7496}) => {
7497  reactExports.useEffect(() => {
7498    rhythmAudio.playVictory();
7499  }, []);
7500  let rank = "C";
7501  let rankColor = "text-zinc-400 border-zinc-500";
7502  let rankGlow = "rgba(161, 161, 170, 0.4)";
7503  let rankTitle = "Rhythm Novice";
7504  if (result.accuracy >= 98) {
7505    rank = "SSS";
7506    rankColor = "text-amber-400 border-amber-400";
7507    rankGlow = "rgba(251, 191, 36, 0.8)";
7508    rankTitle = "Grand Meowstro!";
7509  } else if (result.accuracy >= 95) {
7510    rank = "SS";
7511    rankColor = "text-yellow-400 border-yellow-400";
7512    rankGlow = "rgba(250, 204, 21, 0.7)";
7513    rankTitle = "Master of Metronome";
7514  } else if (result.accuracy >= 90) {
7515    rank = "S";
7516    rankColor = "text-emerald-400 border-emerald-400";
7517    rankGlow = "rgba(52, 211, 153, 0.6)";
7518    rankTitle = "Precision Drummer";
7519  } else if (result.accuracy >= 80) {
7520    rank = "A";
7521    rankColor = "text-cyan-400 border-cyan-400";
7522    rankGlow = "rgba(34, 211, 238, 0.5)";
7523    rankTitle = "Solid Groover";
7524  } else if (result.accuracy >= 70) {
7525    rank = "B";
7526    rankColor = "text-purple-400 border-purple-400";
7527    rankGlow = "rgba(192, 132, 252, 0.5)";
7528    rankTitle = "Rhythm Apprentice";
7529  }
7530  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-50 bg-black/85 backdrop-blur-md flex items-center justify-center p-4 animate-in fade-in duration-300", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-md bg-zinc-900 border border-zinc-700/80 rounded-2xl shadow-2xl p-6 sm:p-7 text-white text-center flex flex-col items-center overflow-hidden", children: [
7531    /* @__PURE__ */ jsxRuntimeExports.jsx(
7532      "div",
7533      {
7534        className: "absolute -top-24 left-1/2 -translate-x-1/2 w-64 h-64 rounded-full blur-3xl pointer-events-none opacity-20",
7535        style: { background: rankGlow }
7536      }
7537    ),
7538    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
7539      /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "w-5 h-5 text-amber-400" }),
7540      /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl sm:text-2xl font-black tracking-wider uppercase", children: locale.resultsTitle })
7541    ] }),
7542    /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-400 font-mono mb-4", children: [
7543      rankTitle,
7544      " • ",
7545      result.bpm,
7546      " ",
7547      locale.bpm
7548    ] }),
7549    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-36 h-24 mb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
7550      RhythmBongoCat,
7551      {
7552        leftPawDown: false,
7553        rightPawDown: false,
7554        mood: "celebrate",
7555        combo: result.maxStreak
7556      }
7557    ) }),
7558    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center gap-6 my-2", children: [
7559      /* @__PURE__ */ jsxRuntimeExports.jsxs(
7560        "div",
7561        {
7562          className: `w-20 h-20 rounded-2xl border-4 flex flex-col items-center justify-center font-black shadow-lg ${rankColor}`,
7563          style: { boxShadow: `0 0 20px ${rankGlow}` },
7564          children: [
7565            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-3xl leading-none", children: rank }),
7566            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] tracking-widest uppercase mt-0.5", children: locale.rhythmSense })
7567          ]
7568        }
7569      ),
7570      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-left font-mono", children: [
7571        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-zinc-400 uppercase", children: locale.accuracy }),
7572        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-3xl font-black text-white", children: [
7573          result.accuracy.toFixed(1),
7574          "%"
7575        ] }),
7576        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-amber-400 font-bold mt-1", children: [
7577          locale.score,
7578          ": ",
7579          result.score.toLocaleString()
7580        ] })
7581      ] })
7582    ] }),
7583    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full bg-zinc-800/80 border border-zinc-700 rounded-xl p-3 my-4 font-mono text-xs", children: [
7584      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pb-2 border-b border-zinc-700/60 mb-2", children: [
7585        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-400", children: [
7586          locale.timingOffset,
7587          ":"
7588        ] }),
7589        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-cyan-300", children: [
7590          result.avgOffsetMs >= 0 ? `+${result.avgOffsetMs.toFixed(1)}ms` : `${result.avgOffsetMs.toFixed(1)}ms`,
7591          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-400 ml-1", children: [
7592            "(",
7593            result.avgOffsetMs >= 0 ? locale.late : locale.early,
7594            ")"
7595          ] })
7596        ] })
7597      ] }),
7598      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-4 gap-2 text-center", children: [
7599        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900/60 rounded p-1.5 border border-emerald-500/20", children: [
7600          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-400 font-bold", children: "PERFECT" }),
7601          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-black text-white", children: result.perfectCount })
7602        ] }),
7603        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900/60 rounded p-1.5 border border-amber-500/20", children: [
7604          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-amber-400 font-bold", children: "GOOD" }),
7605          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-black text-white", children: result.goodCount })
7606        ] }),
7607        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900/60 rounded p-1.5 border border-rose-500/20", children: [
7608          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-rose-400 font-bold", children: "MISS" }),
7609          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-black text-white", children: result.missCount })
7610        ] }),
7611        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900/60 rounded p-1.5 border border-purple-500/20", children: [
7612          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-purple-400 font-bold", children: locale.maxStreak }),
7613          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-black text-white", children: result.maxStreak })
7614        ] })
7615      ] })
7616    ] }),
7617    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row items-center gap-2.5 w-full mt-1", children: [
7618      /* @__PURE__ */ jsxRuntimeExports.jsxs(
7619        "button",
7620        {
7621          onClick: onRetry,
7622          className: "flex-1 w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl bg-purple-600 hover:bg-purple-500 text-white font-bold transition-all shadow-lg active:scale-95 cursor-pointer",
7623          children: [
7624            /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 16 }),
7625            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.retry })
7626          ]
7627        }
7628      ),
7629      /* @__PURE__ */ jsxRuntimeExports.jsxs(
7630        "button",
7631        {
7632          onClick: onNextPattern,
7633          className: "flex-1 w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl bg-zinc-800 hover:bg-zinc-700 text-cyan-300 border border-cyan-500/30 font-bold transition-all active:scale-95 cursor-pointer",
7634          children: [
7635            /* @__PURE__ */ jsxRuntimeExports.jsx(Shuffle, { size: 16 }),
7636            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.nextPattern })
7637          ]
7638        }
7639      )
7640    ] }),
7641    (onSelectSong || onChangeCabinet) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mt-3 text-xs text-zinc-400", children: [
7642      onSelectSong && /* @__PURE__ */ jsxRuntimeExports.jsxs(
7643        "button",
7644        {
7645          onClick: onSelectSong,
7646          className: "hover:text-cyan-300 transition-colors flex items-center gap-1 cursor-pointer",
7647          children: [
7648            /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 13 }),
7649            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.changeSong })
7650          ]
7651        }
7652      ),
7653      onChangeCabinet && /* @__PURE__ */ jsxRuntimeExports.jsxs(
7654        "button",
7655        {
7656          onClick: onChangeCabinet,
7657          className: "hover:text-amber-300 transition-colors flex items-center gap-1 cursor-pointer",
7658          children: [
7659            /* @__PURE__ */ jsxRuntimeExports.jsx(Award, { size: 13 }),
7660            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.changeCabinet })
7661          ]
7662        }
7663      )
7664    ] })
7665  ] }) });
7666};
7667const RHYTHM_TEST_TRANSLATIONS = {
7668  en: {
7669    gameTitle: "Rhythm Test",
7670    gameSubtitle: "Measure Beat Pattern Training",
7671    tagline: "Follow or reproduce beat patterns measure by measure using keyboard, fingers, or microphone!",
7672    modePlayAlong: "Continuous Play-Along",
7673    modeEcho: "Call & Response (Echo)",
7674    modeSandbox: "Freeplay / Editor",
7675    inputKeyboard: "Keyboard",
7676    inputTouch: "Touch / Fingers",
7677    inputMic: "Microphone (Clap / Voice)",
7678    micCalibrate: "Calibrate Microphone",
7679    micSensitivity: "Mic Sensitivity",
7680    micThreshold: "Clap Detection Threshold",
7681    micTestClap: "Clap your hands or tap the table to test!",
7682    micActive: "Mic Active",
7683    micDenied: "Microphone access denied. Using keyboard/touch instead.",
7684    micPrompt: "Allow microphone access to play with clapping, desk taps, or voice!",
7685    micEnable: "Enable Mic",
7686    difficulty: "Difficulty",
7687    diffEasy: "Easy (Whole & Half notes)",
7688    diffMedium: "Medium (Quarter notes)",
7689    diffHard: "Hard (Mixed & Syncopated)",
7690    diffMaster: "Master (8th notes & Rapid rolls)",
7691    perfect: "PERFECT!",
7692    good: "GOOD!",
7693    ok: "OK",
7694    miss: "MISS",
7695    score: "Score",
7696    accuracy: "Accuracy",
7697    streak: "Streak",
7698    maxStreak: "Max Streak",
7699    bpm: "BPM",
7700    tapTempo: "Tap Tempo",
7701    measure: "Measure",
7702    resultsTitle: "Rhythm Test Completed!",
7703    rhythmSense: "Rhythm Aptitude",
7704    timingOffset: "Avg Timing Offset",
7705    early: "Early",
7706    late: "Late",
7707    retry: "Try Again",
7708    nextPattern: "New Pattern",
7709    changeSong: "Select Song",
7710    changeCabinet: "Change Cabinet",
7711    soundType: "Drum Sound",
7712    soundBongo: "Bongos",
7713    soundMetronome: "Metronome",
7714    soundClap: "Handclap",
7715    soundWoodblock: "Woodblock",
7716    countIn: "Count In",
7717    paused: "PAUSED",
7718    resume: "Resume",
7719    restart: "Restart",
7720    listenPhase: "LISTEN TO PATTERN",
7721    yourTurnPhase: "YOUR TURN! TAP NOW",
7722    instructions: "Tap the dots on each beat! Spacebar, touch screen, or clap into mic.",
7723    page: "Page",
7724    tapKeyHint: "Tap [SPACE] or [F / J] on the beat!",
7725    touchHint: "Tap screen on the beat!",
7726    hitNow: "HIT!",
7727    rest: "REST"
7728  },
7729  ja: {
7730    gameTitle: "リズムテスト",
7731    gameSubtitle: "小節ビートパターントレーニング",
7732    tagline: "キーボード・指タップ・マイク(拍手・声)で小節ごとのビートパターンを演奏しよう!",
7733    modePlayAlong: "連続プレイアロング",
7734    modeEcho: "コール&レスポンス(エコー)",
7735    modeSandbox: "フリープレイ / エディター",
7736    inputKeyboard: "キーボード",
7737    inputTouch: "タッチ / 指",
7738    inputMic: "マイク(手拍子 / 音声)",
7739    micCalibrate: "マイク感度調整",
7740    micSensitivity: "マイク感度",
7741    micThreshold: "拍手検知しきい値",
7742    micTestClap: "手を叩くか机をタップしてテストしてください!",
7743    micActive: "マイク稼働中",
7744    micDenied: "マイクへのアクセスが拒否されました。キーボードまたはタッチをご利用ください。",
7745    micPrompt: "手拍子や声でプレイするにはマイクの権限を許可してください!",
7746    micEnable: "マイクを有効化",
7747    difficulty: "難易度",
7748    diffEasy: "かんたん(全音符・2分音符)",
7749    diffMedium: "ふつう(4分音符)",
7750    diffHard: "むずかしい(シンコペーション)",
7751    diffMaster: "おに(8分音符・高速連打)",
7752    perfect: "パーフェクト!",
7753    good: "グッド!",
7754    ok: "OK",
7755    miss: "ミス",
7756    score: "スコア",
7757    accuracy: "精度",
7758    streak: "コンボ",
7759    maxStreak: "最大コンボ",
7760    bpm: "BPM",
7761    tapTempo: "タップテンポ",
7762    measure: "小節",
7763    resultsTitle: "リズムテスト完了!",
7764    rhythmSense: "リズム感判定",
7765    timingOffset: "平均タイミングズレ",
7766    early: "早い",
7767    late: "遅い",
7768    retry: "もう一度挑戦",
7769    nextPattern: "新しいパターン",
7770    changeSong: "曲を選ぶ",
7771    changeCabinet: "ゲーム変更",
7772    soundType: "打楽器音",
7773    soundBongo: "ボンゴ",
7774    soundMetronome: "メトロノーム",
7775    soundClap: "ハンドクラップ",
7776    soundWoodblock: "ウッドブロック",
7777    countIn: "カウントイン",
7778    paused: "一時停止中",
7779    resume: "再開",
7780    restart: "最初から",
7781    listenPhase: "お手本を聴いてね",
7782    yourTurnPhase: "あなたの番!叩いて!",
7783    instructions: "ビートのドットに合わせてタップ!スペースキー、タッチ、または手拍子で。",
7784    page: "ページ",
7785    tapKeyHint: "ビートに合わせて [SPACE] または [F / J] を叩こう!",
7786    touchHint: "ビートに合わせて画面をタップ!",
7787    hitNow: "ヒット!",
7788    rest: "休符"
7789  },
7790  "zh-CN": {
7791    gameTitle: "节奏测试",
7792    gameSubtitle: "小节节拍模式训练",
7793    tagline: "使用键盘、手指触控或麦克风(击掌/声音)逐小节挑战节拍模式!",
7794    modePlayAlong: "连续跟弹",
7795    modeEcho: "听辩复述(回音)",
7796    modeSandbox: "自由沙盒 / 编辑器",
7797    inputKeyboard: "键盘",
7798    inputTouch: "触摸 / 手指",
7799    inputMic: "麦克风(击掌 / 声音)",
7800    micCalibrate: "麦克风校准",
7801    micSensitivity: "麦克风灵敏度",
7802    micThreshold: "击掌检测阈值",
7803    micTestClap: "击掌或敲击桌面以测试!",
7804    micActive: "麦克风监听中",
7805    micDenied: "麦克风权限被拒绝,已切换至键盘/触摸模式。",
7806    micPrompt: "请允许麦克风权限,即可通过击掌或声音玩游戏!",
7807    micEnable: "启用麦克风",
7808    difficulty: "难度",
7809    diffEasy: "简单(全音符与二分音符)",
7810    diffMedium: "普通(四分音符)",
7811    diffHard: "困难(切分音与混合)",
7812    diffMaster: "大师(八分音符与极速连击)",
7813    perfect: "精准!",
7814    good: "很好!",
7815    ok: "良好",
7816    miss: "失误",
7817    score: "得分",
7818    accuracy: "准确度",
7819    streak: "连击",
7820    maxStreak: "最高连击",
7821    bpm: "BPM",
7822    tapTempo: "点打测速",
7823    measure: "小节",
7824    resultsTitle: "节奏测试完成!",
7825    rhythmSense: "节奏感评级",
7826    timingOffset: "平均节拍偏差",
7827    early: "偏早",
7828    late: "偏晚",
7829    retry: "重新挑战",
7830    nextPattern: "换个新图案",
7831    changeSong: "选择歌曲",
7832    changeCabinet: "更换机台",
7833    soundType: "打击乐音色",
7834    soundBongo: "邦戈鼓",
7835    soundMetronome: "节拍器",
7836    soundClap: "击掌声",
7837    soundWoodblock: "木鱼 / 木块",
7838    countIn: "预备拍",
7839    paused: "已暂停",
7840    resume: "ç»§ç»­",
7841    restart: "重新开始",
7842    listenPhase: "聆听演示节奏",
7843    yourTurnPhase: "轮到你了!开始敲击",
7844    instructions: "在每个圆点节拍上敲击!支持空格键、触摸屏或麦克风击掌。",
7845    page: "页",
7846    tapKeyHint: "跟随节拍敲击 [空格] 或 [F / J]!",
7847    touchHint: "跟随节拍轻点屏幕!",
7848    hitNow: "击打!",
7849    rest: "休止"
7850  },
7851  "zh-TW": {
7852    gameTitle: "節奏測試",
7853    gameSubtitle: "小節節拍模式訓練",
7854    tagline: "使用鍵盤、手指觸控或麥克風(拍手/聲音)逐小節挑戰節拍模式!",
7855    modePlayAlong: "連續跟彈",
7856    modeEcho: "聽辨複述(回音)",
7857    modeSandbox: "自由沙盒 / 編輯器",
7858    inputKeyboard: "鍵盤",
7859    inputTouch: "觸控 / 手指",
7860    inputMic: "麥克風(拍手 / 聲音)",
7861    micCalibrate: "麥克風校準",
7862    micSensitivity: "麥克風靈敏度",
7863    micThreshold: "拍手檢測閾值",
7864    micTestClap: "拍手或敲擊桌面以測試!",
7865    micActive: "麥克風監聽中",
7866    micDenied: "麥克風權限被拒絕,已切換至鍵盤/觸控模式。",
7867    micPrompt: "請允許麥克風權限,即可透過拍手或聲音玩遊戲!",
7868    micEnable: "啟用麥克風",
7869    difficulty: "難度",
7870    diffEasy: "簡單(全音符與二分音符)",
7871    diffMedium: "普通(四分音符)",
7872    diffHard: "困難(切分音與混合)",
7873    diffMaster: "大師(八分音符與極速連擊)",
7874    perfect: "精準!",
7875    good: "很好!",
7876    ok: "良好",
7877    miss: "失誤",
7878    score: "得分",
7879    accuracy: "準確度",
7880    streak: "連擊",
7881    maxStreak: "最高連擊",
7882    bpm: "BPM",
7883    tapTempo: "測速打拍",
7884    measure: "小節",
7885    resultsTitle: "節奏測試完成!",
7886    rhythmSense: "節奏感評級",
7887    timingOffset: "平均節拍偏差",
7888    early: "偏早",
7889    late: "偏晚",
7890    retry: "重新挑戰",
7891    nextPattern: "換個新圖案",
7892    changeSong: "選擇歌曲",
7893    changeCabinet: "更換機台",
7894    soundType: "打擊樂音色",
7895    soundBongo: "邦戈鼓",
7896    soundMetronome: "節拍器",
7897    soundClap: "拍手聲",
7898    soundWoodblock: "木魚 / 木塊",
7899    countIn: "預備拍",
7900    paused: "已暫停",
7901    resume: "繼續",
7902    restart: "重新開始",
7903    listenPhase: "聆聽示範節奏",
7904    yourTurnPhase: "輪到你了!開始敲擊",
7905    instructions: "在每個圓點節拍上敲擊!支援空白鍵、觸控螢幕或麥克風拍手。",
7906    page: "頁",
7907    tapKeyHint: "跟隨節拍敲擊 [空白鍵] 或 [F / J]!",
7908    touchHint: "跟隨節拍輕觸螢幕!",
7909    hitNow: "擊打!",
7910    rest: "休止"
7911  },
7912  ko: {
7913    gameTitle: "리듬 테스트",
7914    gameSubtitle: "마디 비트 패턴 트레이닝",
7915    tagline: "키보드, 화면 터치, 마이크(박수/목소리)로 마디별 비트 패턴을 연주해보세요!",
7916    modePlayAlong: "연속 플레이어롱",
7917    modeEcho: "콜 & 리스폰스 (에코)",
7918    modeSandbox: "자유 연습 / 에디터",
7919    inputKeyboard: "키보드",
7920    inputTouch: "터치 / 손가락",
7921    inputMic: "마이크 (박수 / 음성)",
7922    micCalibrate: "마이크 감도 설정",
7923    micSensitivity: "마이크 감도",
7924    micThreshold: "박수 감지 임계값",
7925    micTestClap: "박수를 치거나 책상을 두드려 테스트해보세요!",
7926    micActive: "마이크 작동 중",
7927    micDenied: "마이크 접근이 거부되었습니다. 키보드 또는 터치를 사용합니다.",
7928    micPrompt: "박수나 목소리로 플레이하려면 마이크 권한을 허용해주세요!",
7929    micEnable: "마이크 활성화",
7930    difficulty: "난이도",
7931    diffEasy: "쉬움 (온음표 & 2분음표)",
7932    diffMedium: "보통 (4분음표)",
7933    diffHard: "어려움 (싱코페이션 & 혼합)",
7934    diffMaster: "마스터 (8분음표 & 초고속 연타)",
7935    perfect: "퍼펙트!",
7936    good: "굿!",
7937    ok: "OK",
7938    miss: "미스",
7939    score: "점수",
7940    accuracy: "정확도",
7941    streak: "콤보",
7942    maxStreak: "최대 콤보",
7943    bpm: "BPM",
7944    tapTempo: "탭 템포",
7945    measure: "마디",
7946    resultsTitle: "리듬 테스트 완료!",
7947    rhythmSense: "리듬감 등급",
7948    timingOffset: "평균 타이밍 오차",
7949    early: "빠름",
7950    late: "느림",
7951    retry: "다시 도전",
7952    nextPattern: "새 패턴",
7953    changeSong: "곡 선택",
7954    changeCabinet: "기기 변경",
7955    soundType: "타악기 사운드",
7956    soundBongo: "봉고",
7957    soundMetronome: "메트로놈",
7958    soundClap: "손박수",
7959    soundWoodblock: "우드블록",
7960    countIn: "카운트 인",
7961    paused: "일시 정지",
7962    resume: "이어하기",
7963    restart: "처음부터",
7964    listenPhase: "시범 패턴을 들어보세요",
7965    yourTurnPhase: "당신의 차례! 두드리세요",
7966    instructions: "점의 타이밍에 맞춰 두드리세요! 스페이스바, 화면 터치, 마이크 박수를 지원합니다.",
7967    page: "페이지",
7968    tapKeyHint: "박자에 맞춰 [스페이스] 또는 [F / J]를 누르세요!",
7969    touchHint: "박자에 맞춰 화면을 탭하세요!",
7970    hitNow: "히트!",
7971    rest: "쉼표"
7972  },
7973  es: {
7974    gameTitle: "Test de Ritmo",
7975    gameSubtitle: "Entrenamiento de Compases y Ritmos",
7976    tagline: "¡Sigue o reproduce patrones de compás usando teclado, dedos o micrófono!",
7977    modePlayAlong: "Tocar en Continuo",
7978    modeEcho: "Llamada y Respuesta (Eco)",
7979    modeSandbox: "Modo Libre / Editor",
7980    inputKeyboard: "Teclado",
7981    inputTouch: "Táctil / Dedos",
7982    inputMic: "Micrófono (Aplauso / Voz)",
7983    micCalibrate: "Calibrar Micrófono",
7984    micSensitivity: "Sensibilidad del Mic",
7985    micThreshold: "Umbral de Detección de Aplauso",
7986    micTestClap: "¡Aplaude o golpea la mesa para probar!",
7987    micActive: "Micrófono Activo",
7988    micDenied: "Permiso de micrófono denegado. Usando teclado/táctil.",
7989    micPrompt: "¡Permite el acceso al micrófono para jugar aplaudiendo o usando tu voz!",
7990    micEnable: "Activar Micrófono",
7991    difficulty: "Dificultad",
7992    diffEasy: "Fácil (Redondas y Blancas)",
7993    diffMedium: "Medio (Negras)",
7994    diffHard: "Difícil (Sincopado y Mixto)",
7995    diffMaster: "Maestro (Corcheas y Redobles Rápidos)",
7996    perfect: "¡PERFECTO!",
7997    good: "¡BIEN!",
7998    ok: "OK",
7999    miss: "FALLO",
8000    score: "Puntuación",
8001    accuracy: "Precisión",
8002    streak: "Combo",
8003    maxStreak: "Combo Máximo",
8004    bpm: "BPM",
8005    tapTempo: "Marcar Tempo",
8006    measure: "Compás",
8007    resultsTitle: "¡Test de Ritmo Completado!",
8008    rhythmSense: "Sentido del Ritmo",
8009    timingOffset: "Desvío Promedio",
8010    early: "Adelantado",
8011    late: "Atrasado",
8012    retry: "Reintentar",
8013    nextPattern: "Nuevo Patrón",
8014    changeSong: "Elegir Canción",
8015    changeCabinet: "Cambiar Máquina",
8016    soundType: "Sonido de Percusión",
8017    soundBongo: "Bongós",
8018    soundMetronome: "Metrónomo",
8019    soundClap: "Aplauso",
8020    soundWoodblock: "Bloque de Madera",
8021    countIn: "Cuenta Inicial",
8022    paused: "EN PAUSA",
8023    resume: "Continuar",
8024    restart: "Reiniciar",
8025    listenPhase: "ESCUCHA EL PATRÓN",
8026    yourTurnPhase: "¡TU TURNO! TOCA AHORA",
8027    instructions: "¡Toca en los puntos marcados! Usa espacio, pantalla táctil o aplaude al micrófono.",
8028    page: "Página",
8029    tapKeyHint: "¡Presiona [ESPACIO] o [F / J] en el compás!",
8030    touchHint: "¡Toca la pantalla en el compás!",
8031    hitNow: "¡TOCA!",
8032    rest: "SILENCIO"
8033  },
8034  fr: {
8035    gameTitle: "Test de Rythme",
8036    gameSubtitle: "Entraînement de Mesures et Motifs Rhythmiques",
8037    tagline: "Suivez ou reproduisez les motifs mesure par mesure au clavier, au toucher ou au micro !",
8038    modePlayAlong: "Jeu Continu",
8039    modeEcho: "Appel et Réponse (Écho)",
8040    modeSandbox: "Mode Libre / Éditeur",
8041    inputKeyboard: "Clavier",
8042    inputTouch: "Tactile / Doigts",
8043    inputMic: "Microphone (Claquement / Voix)",
8044    micCalibrate: "Calibrer le Microphone",
8045    micSensitivity: "Sensibilité du Micro",
8046    micThreshold: "Seuil de Détection de Claquement",
8047    micTestClap: "Tapez dans vos mains ou sur la table pour tester !",
8048    micActive: "Microphone Actif",
8049    micDenied: "Accès micro refusé. Utilisation du clavier/tactile.",
8050    micPrompt: "Autorisez le micro pour jouer en tapant dans les mains ou avec votre voix !",
8051    micEnable: "Activer le Micro",
8052    difficulty: "Difficulté",
8053    diffEasy: "Facile (Rondes et Blanches)",
8054    diffMedium: "Moyen (Noires)",
8055    diffHard: "Difficile (Syncopes & Mixtes)",
8056    diffMaster: "Maître (Croches & Roulements Rapides)",
8057    perfect: "PARFAIT !",
8058    good: "BIEN !",
8059    ok: "OK",
8060    miss: "RATÉ",
8061    score: "Score",
8062    accuracy: "Précision",
8063    streak: "Combo",
8064    maxStreak: "Combo Max",
8065    bpm: "BPM",
8066    tapTempo: "Tap Tempo",
8067    measure: "Mesure",
8068    resultsTitle: "Test de Rythme Terminé !",
8069    rhythmSense: "Sens du Rythme",
8070    timingOffset: "Décalage Moyen",
8071    early: "En avance",
8072    late: "En retard",
8073    retry: "Réessayer",
8074    nextPattern: "Nouveau Motif",
8075    changeSong: "Choisir un Morceau",
8076    changeCabinet: "Changer de Borne",
8077    soundType: "Son de Percussion",
8078    soundBongo: "Bongos",
8079    soundMetronome: "Métronome",
8080    soundClap: "Claquement de mains",
8081    soundWoodblock: "Wood-block",
8082    countIn: "Décompte",
8083    paused: "EN PAUSE",
8084    resume: "Reprendre",
8085    restart: "Recommencer",
8086    listenPhase: "ÉCOUTEZ LE MOTIF",
8087    yourTurnPhase: "À VOUS ! TAPEZ MAINTENANT",
8088    instructions: "Tapez au rythme des points ! Espace, écran tactile ou claquements au micro.",
8089    page: "Page",
8090    tapKeyHint: "Appuyez sur [ESPACE] ou [F / J] en rythme !",
8091    touchHint: "Touchez l'écran en rythme !",
8092    hitNow: "FRAPPEZ !",
8093    rest: "PAUSE"
8094  },
8095  de: {
8096    gameTitle: "Rhythmustest",
8097    gameSubtitle: "Takt- und Beatmuster-Training",
8098    tagline: "Folge oder reproduziere Beatmuster Takt für Takt per Tastatur, Touch oder Mikrofon!",
8099    modePlayAlong: "Kontinuierliches Mitspielen",
8100    modeEcho: "Call & Response (Echo)",
8101    modeSandbox: "Freispiel / Editor",
8102    inputKeyboard: "Tastatur",
8103    inputTouch: "Touch / Finger",
8104    inputMic: "Mikrofon (Klatschen / Stimme)",
8105    micCalibrate: "Mikrofon kalibrieren",
8106    micSensitivity: "Mikrofon-Empfindlichkeit",
8107    micThreshold: "Klatsch-Schwellenwert",
8108    micTestClap: "Klatschen oder auf den Tisch klopfen zum Testen!",
8109    micActive: "Mikrofon aktiv",
8110    micDenied: "Mikrofonzugriff verweigert. Tastatur/Touch wird verwendet.",
8111    micPrompt: "Mikrofonzugriff erlauben, um mit Klatschen oder Stimme zu spielen!",
8112    micEnable: "Mikrofon aktivieren",
8113    difficulty: "Schwierigkeit",
8114    diffEasy: "Einfach (Ganze & Halbe Noten)",
8115    diffMedium: "Mittel (Viertelnoten)",
8116    diffHard: "Schwer (Synkopen & Gemischt)",
8117    diffMaster: "Meister (Achtelnoten & Wirbel)",
8118    perfect: "PERFEKT!",
8119    good: "GUT!",
8120    ok: "OK",
8121    miss: "FEHLSCHLAG",
8122    score: "Punkte",
8123    accuracy: "Genauigkeit",
8124    streak: "Combo",
8125    maxStreak: "Max. Combo",
8126    bpm: "BPM",
8127    tapTempo: "Tap Tempo",
8128    measure: "Takt",
8129    resultsTitle: "Rhythmustest abgeschlossen!",
8130    rhythmSense: "Rhythmusgefühl-Rang",
8131    timingOffset: "Durchschnittl. Abweichung",
8132    early: "Zu früh",
8133    late: "Zu spät",
8134    retry: "Nochmal versuchen",
8135    nextPattern: "Neues Muster",
8136    changeSong: "Song wählen",
8137    changeCabinet: "Automat wechseln",
8138    soundType: "Percussion-Sound",
8139    soundBongo: "Bongos",
8140    soundMetronome: "Metronom",
8141    soundClap: "Klatschen",
8142    soundWoodblock: "Holzblock",
8143    countIn: "Einzähler",
8144    paused: "PAUSIERT",
8145    resume: "Fortsetzen",
8146    restart: "Neu starten",
8147    listenPhase: "MUSTER ANHÖREN",
8148    yourTurnPhase: "DU BIST DRAN! JETZT TIPPEN",
8149    instructions: "Tippe passend zu den Punkten! Leertaste, Touchscreen oder Klatschen ins Mikrofon.",
8150    page: "Seite",
8151    tapKeyHint: "Drücke [LEERTASTE] oder [F / J] im Takt!",
8152    touchHint: "Tippe im Takt auf den Bildschirm!",
8153    hitNow: "TREFFER!",
8154    rest: "PAUSE"
8155  },
8156  it: {
8157    gameTitle: "Test del Ritmo",
8158    gameSubtitle: "Allenamento di Battute e Pattern Ritmici",
8159    tagline: "Segui o riproduci i pattern battuta per battuta usando tastiera, tocco o microfono!",
8160    modePlayAlong: "Suona Continuo",
8161    modeEcho: "Botta e Risposta (Eco)",
8162    modeSandbox: "Modalità Libera / Editor",
8163    inputKeyboard: "Tastiera",
8164    inputTouch: "Tocco / Dita",
8165    inputMic: "Microfono (Battito di mani / Voce)",
8166    micCalibrate: "Calibra Microfono",
8167    micSensitivity: "Sensibilità Microfono",
8168    micThreshold: "Soglia Rilevamento Battito",
8169    micTestClap: "Batti le mani o tocca il tavolo per testare!",
8170    micActive: "Microfono Attivo",
8171    micDenied: "Accesso al microfono negato. Utilizzo di tastiera/tocco.",
8172    micPrompt: "Consenti l'accesso al microfono per giocare battendo le mani o con la voce!",
8173    micEnable: "Attiva Microfono",
8174    difficulty: "Difficoltà",
8175    diffEasy: "Facile (Intere e Minime)",
8176    diffMedium: "Medio (Semiminime)",
8177    diffHard: "Difficile (Sincope e Misto)",
8178    diffMaster: "Maestro (Crome e Rulli Rapidi)",
8179    perfect: "PERFETTO!",
8180    good: "BENE!",
8181    ok: "OK",
8182    miss: "MANCATO",
8183    score: "Punteggio",
8184    accuracy: "Precisione",
8185    streak: "Combo",
8186    maxStreak: "Combo Max",
8187    bpm: "BPM",
8188    tapTempo: "Tap Tempo",
8189    measure: "Battuta",
8190    resultsTitle: "Test del Ritmo Completato!",
8191    rhythmSense: "Senso del Ritmo",
8192    timingOffset: "Scostamento Medio",
8193    early: "In anticipo",
8194    late: "In ritardo",
8195    retry: "Riprova",
8196    nextPattern: "Nuovo Pattern",
8197    changeSong: "Scegli Brano",
8198    changeCabinet: "Cambia Cabinato",
8199    soundType: "Suono Percussione",
8200    soundBongo: "Bongo",
8201    soundMetronome: "Metronomo",
8202    soundClap: "Battito di mani",
8203    soundWoodblock: "Woodblock",
8204    countIn: "Conteggio Iniziale",
8205    paused: "IN PAUSA",
8206    resume: "Riprendi",
8207    restart: "Ricomincia",
8208    listenPhase: "ASCOLTA IL PATTERN",
8209    yourTurnPhase: "TOCCA A TE! BATTI ORA",
8210    instructions: "Batti sui punti ritmici! Barra spaziatrice, touchscreen o battito di mani al microfono.",
8211    page: "Pagina",
8212    tapKeyHint: "Premi [SPAZIO] o [F / J] a tempo di battuta!",
8213    touchHint: "Tocca lo schermo a tempo!",
8214    hitNow: "COLPISCI!",
8215    rest: "PAUSA"
8216  },
8217  pt: {
8218    gameTitle: "Teste de Ritmo",
8219    gameSubtitle: "Treino de Compassos e Padrões Rítmicos",
8220    tagline: "Siga ou reproduza padrões rítmicos compasso a compasso usando teclado, toque ou microfone!",
8221    modePlayAlong: "Tocar Contínuo",
8222    modeEcho: "Chamada e Resposta (Eco)",
8223    modeSandbox: "Modo Livre / Editor",
8224    inputKeyboard: "Teclado",
8225    inputTouch: "Toque / Dedos",
8226    inputMic: "Microfone (Palmas / Voz)",
8227    micCalibrate: "Calibrar Microfone",
8228    micSensitivity: "Sensibilidade do Mic",
8229    micThreshold: "Limiar de Detecção de Palmas",
8230    micTestClap: "Bata palmas ou bata na mesa para testar!",
8231    micActive: "Microfone Ativo",
8232    micDenied: "Acesso ao microfone negado. Usando teclado/toque.",
8233    micPrompt: "Permita o microfone para jogar batendo palmas ou usando a voz!",
8234    micEnable: "Ativar Microfone",
8235    difficulty: "Dificuldade",
8236    diffEasy: "Fácil (Semibreves e Mínimas)",
8237    diffMedium: "Médio (Semínimas)",
8238    diffHard: "Difícil (Sincopado e Misto)",
8239    diffMaster: "Mestre (Colcheias e Rufos Rápidos)",
8240    perfect: "PERFEITO!",
8241    good: "BOM!",
8242    ok: "OK",
8243    miss: "ERRO",
8244    score: "Pontuação",
8245    accuracy: "Precisão",
8246    streak: "Combo",
8247    maxStreak: "Combo Máximo",
8248    bpm: "BPM",
8249    tapTempo: "Tap Tempo",
8250    measure: "Compasso",
8251    resultsTitle: "Teste de Ritmo Concluído!",
8252    rhythmSense: "Senso Rítmico",
8253    timingOffset: "Desvio Médio",
8254    early: "Adiantado",
8255    late: "Atrasado",
8256    retry: "Tentar Novamente",
8257    nextPattern: "Novo Padrão",
8258    changeSong: "Escolher Música",
8259    changeCabinet: "Trocar de Máquina",
8260    soundType: "Som de Percussão",
8261    soundBongo: "Bongôs",
8262    soundMetronome: "Metrônomo",
8263    soundClap: "Palmas",
8264    soundWoodblock: "Bloco de Madeira",
8265    countIn: "Contagem Inicial",
8266    paused: "PAUSADO",
8267    resume: "Retomar",
8268    restart: "Reiniciar",
8269    listenPhase: "OUÇA O PADRÃO",
8270    yourTurnPhase: "SUA VEZ! TOQUE AGORA",
8271    instructions: "Toque nos pontos de cada batida! Barra de espaço, tela sensível ou palmas no microfone.",
8272    page: "Página",
8273    tapKeyHint: "Pressione [ESPAÇO] ou [F / J] na batida!",
8274    touchHint: "Toque na tela no ritmo!",
8275    hitNow: "BATA!",
8276    rest: "PAUSA"
8277  },
8278  ru: {
8279    gameTitle: "Ритм-Тест",
8280    gameSubtitle: "Тренировка тактов и ритмических рисунков",
8281    tagline: "Повторяйте или отбивайте ритм такт за тактом с помощью клавиатуры, сенсора или микрофона!",
8282    modePlayAlong: "Непрерывная игра",
8283    modeEcho: "Эхо (Слушай и повторяй)",
8284    modeSandbox: "Свободный режим / Редактор",
8285    inputKeyboard: "Клавиатура",
8286    inputTouch: "Сенсор / Пальцы",
8287    inputMic: "Микрофон (Хлопки / Голос)",
8288    micCalibrate: "Калибровка микрофона",
8289    micSensitivity: "Чувствительность микрофона",
8290    micThreshold: "Порог срабатывания хлопка",
8291    micTestClap: "Хлопните в ладоши или постучите по столу для проверки!",
8292    micActive: "Микрофон активен",
8293    micDenied: "Доступ к микрофону отклонен. Используется клавиатура/сенсор.",
8294    micPrompt: "Разрешите доступ к микрофону, чтобы играть хлопками или голосом!",
8295    micEnable: "Включить микрофон",
8296    difficulty: "Сложность",
8297    diffEasy: "Легко (Целые и половинные ноты)",
8298    diffMedium: "Средне (Четвертные ноты)",
8299    diffHard: "Сложно (Синкопы и смешанные)",
8300    diffMaster: "Мастер (Восьмые ноты и быстрые дроби)",
8301    perfect: "ИДЕАЛЬНО!",
8302    good: "ХОРОШО!",
8303    ok: "ОК",
8304    miss: "ПРОМАХ",
8305    score: "Счет",
8306    accuracy: "Точность",
8307    streak: "Комбо",
8308    maxStreak: "Макс. комбо",
8309    bpm: "BPM",
8310    tapTempo: "Задать темп",
8311    measure: "Такт",
8312    resultsTitle: "Ритм-тест завершен!",
8313    rhythmSense: "Чувство ритма",
8314    timingOffset: "Среднее отклонение",
8315    early: "Раньше",
8316    late: "Позже",
8317    retry: "Повторить",
8318    nextPattern: "Новый паттерн",
8319    changeSong: "Выбрать трек",
8320    changeCabinet: "Сменить автомат",
8321    soundType: "Звук перкуссии",
8322    soundBongo: "Бонго",
8323    soundMetronome: "Метроном",
8324    soundClap: "Хлопок в ладоши",
8325    soundWoodblock: "Вуд-блок",
8326    countIn: "Отсчет",
8327    paused: "ПАУЗА",
8328    resume: "Продолжить",
8329    restart: "Начать заново",
8330    listenPhase: "СЛУШАЙТЕ ПАТТЕРН",
8331    yourTurnPhase: "ВАША ОЧЕРЕДЬ! НАЖИМАЙТЕ",
8332    instructions: "Нажимайте по точкам в такт! Пробел, сенсорный экран или хлопки в микрофон.",
8333    page: "Страница",
8334    tapKeyHint: "Нажимайте [ПРОБЕЛ] или [F / J] в такт!",
8335    touchHint: "Касайтесь экрана в такт!",
8336    hitNow: "УДАР!",
8337    rest: "ПАУЗА"
8338  },
8339  id: {
8340    gameTitle: "Tes Ritme",
8341    gameSubtitle: "Latihan Pola Ketukan Birama",
8342    tagline: "Ikuti atau mainkan ulang pola ketukan per birama lewat keyboard, sentuhan, atau mikrofon!",
8343    modePlayAlong: "Main Bersama Berlanjut",
8344    modeEcho: "Tanya & Jawab (Gema)",
8345    modeSandbox: "Main Bebas / Editor",
8346    inputKeyboard: "Keyboard",
8347    inputTouch: "Sentuh / Jari",
8348    inputMic: "Mikrofon (Tepuk Tangan / Suara)",
8349    micCalibrate: "Kalibrasi Mikrofon",
8350    micSensitivity: "Sensitivitas Mic",
8351    micThreshold: "Ambang Deteksi Tepukan",
8352    micTestClap: "Tepuk tangan atau ketuk meja untuk menguji!",
8353    micActive: "Mikrofon Aktif",
8354    micDenied: "Akses mikrofon ditolak. Beralih ke keyboard/sentuhan.",
8355    micPrompt: "Izinkan mikrofon untuk bermain dengan tepuk tangan atau suara!",
8356    micEnable: "Aktifkan Mikrofon",
8357    difficulty: "Tingkat Kesulitan",
8358    diffEasy: "Mudah (Ketukan Utuh & Setengah)",
8359    diffMedium: "Sedang (Ketukan Seperempat)",
8360    diffHard: "Sulit (Sinkopasi & Campuran)",
8361    diffMaster: "Master (Ketukan Seperdelapan & Cepat)",
8362    perfect: "SEMPURNA!",
8363    good: "BAGUS!",
8364    ok: "OKE",
8365    miss: "MELESET",
8366    score: "Skor",
8367    accuracy: "Akurasi",
8368    streak: "Kombo",
8369    maxStreak: "Kombo Maksimum",
8370    bpm: "BPM",
8371    tapTempo: "Tap Tempo",
8372    measure: "Birama",
8373    resultsTitle: "Tes Ritme Selesai!",
8374    rhythmSense: "Peringkat Rasa Ritme",
8375    timingOffset: "Rata-rata Selisih Waktu",
8376    early: "Terlalu Cepat",
8377    late: "Terlambat",
8378    retry: "Coba Lagi",
8379    nextPattern: "Pola Baru",
8380    changeSong: "Pilih Lagu",
8381    changeCabinet: "Ganti Permainan",
8382    soundType: "Suara Perkusi",
8383    soundBongo: "Bongo",
8384    soundMetronome: "Metronom",
8385    soundClap: "Tepuk Tangan",
8386    soundWoodblock: "Blok Kayu",
8387    countIn: "Hitungan Masuk",
8388    paused: "JEDA",
8389    resume: "Lanjutkan",
8390    restart: "Mulai Ulang",
8391    listenPhase: "DENGARKAN POLANYA",
8392    yourTurnPhase: "GILIRANMU! KETUK SEKARANG",
8393    instructions: "Ketuk sesuai titik pada setiap ketukan! Spasi, layar sentuh, atau tepukan ke mikrofon.",
8394    page: "Halaman",
8395    tapKeyHint: "Tekan [SPASI] atau [F / J] tepat pada ketukan!",
8396    touchHint: "Ketuk layar tepat pada ketukan!",
8397    hitNow: "KETUK!",
8398    rest: "ISTIRAHAT"
8399  },
8400  ar: {
8401    gameTitle: "اختبار الإيقاع",
8402    gameSubtitle: "تدريب أنماط الإيقاع لكل مقياس",
8403    tagline: "اتبع أو أعد عزف أنماط الإيقاع مقياساً بمقياس باستخدام لوحة المفاتيح أو اللمس أو الميكروفون!",
8404    modePlayAlong: "عزف مستمر متزامن",
8405    modeEcho: "نداء واستجابة (صدى)",
8406    modeSandbox: "وضع حر / محرر",
8407    inputKeyboard: "لوحة المفاتيح",
8408    inputTouch: "اللمس / الأصابع",
8409    inputMic: "الميكروفون (تصفيق / صوت)",
8410    micCalibrate: "معايرة الميكروفون",
8411    micSensitivity: "حساسية الميكروفون",
8412    micThreshold: "عتبة رصد التصفيق",
8413    micTestClap: "صفق بيديك أو انقر على الطاولة للاختبار!",
8414    micActive: "الميكروفون قيد الاستماع",
8415    micDenied: "تم رفض إذن الميكروفون. جارٍ استخدام لوحة المفاتيح/اللمس.",
8416    micPrompt: "يرجى السماح بالوصول للميكروفون للعب بالتصفيق أو الصوت!",
8417    micEnable: "تفعيل الميكروفون",
8418    difficulty: "مستوى الصعوبة",
8419    diffEasy: "سهل (علامات كاملة ونصفية)",
8420    diffMedium: "متوسط (علامات ربعية)",
8421    diffHard: "صعب (إيقاعات معقدة)",
8422    diffMaster: "محترف (علامات ثمنية ونقرات سريعة)",
8423    perfect: "ممتاز!",
8424    good: "جيد جداً!",
8425    ok: "جيد",
8426    miss: "إخفاق",
8427    score: "النقاط",
8428    accuracy: "الدقة",
8429    streak: "الضربات المتتالية",
8430    maxStreak: "أعلى تتابع",
8431    bpm: "نبضة في الدقيقة",
8432    tapTempo: "تحديد السرعة باللمس",
8433    measure: "المقياس",
8434    resultsTitle: "اكتمل اختبار الإيقاع!",
8435    rhythmSense: "تقييم الحس الإيقاعي",
8436    timingOffset: "متوسط فارق التوقيت",
8437    early: "مبكر",
8438    late: "متأخر",
8439    retry: "إعادة المحاولة",
8440    nextPattern: "نمط جديد",
8441    changeSong: "اختيار أغنية",
8442    changeCabinet: "تغيير الجهاز",
8443    soundType: "صوت الإيقاع",
8444    soundBongo: "بونغو",
8445    soundMetronome: "بندول الإيقاع",
8446    soundClap: "تصفيق اليدين",
8447    soundWoodblock: "كتلة خشبية",
8448    countIn: "عد تنازلي",
8449    paused: "موقوف مؤقتاً",
8450    resume: "متابعة",
8451    restart: "إعادة البدء",
8452    listenPhase: "استمع إلى النمط",
8453    yourTurnPhase: "دورك الآن! انقر",
8454    instructions: "انقر بدقة عند وصول النقطة! مسافة لوحة المفاتيح أو اللمس أو التصفيق.",
8455    page: "صفحة",
8456    tapKeyHint: "اضغط على [مسافة] أو [F / J] مع الإيقاع!",
8457    touchHint: "انقر على الشاشة مع الإيقاع!",
8458    hitNow: "انقر الآن!",
8459    rest: "سكتة"
8460  }
8461};
8462function getRhythmTestLocale(lang) {
8463  if (!lang) return RHYTHM_TEST_TRANSLATIONS.en;
8464  const lower = lang.toLowerCase();
8465  if (RHYTHM_TEST_TRANSLATIONS[lower]) return RHYTHM_TEST_TRANSLATIONS[lower];
8466  if (lower.startsWith("zh-cn") || lower === "zh") return RHYTHM_TEST_TRANSLATIONS["zh-CN"];
8467  if (lower.startsWith("zh-tw") || lower.startsWith("zh-hk")) return RHYTHM_TEST_TRANSLATIONS["zh-TW"];
8468  const prefix = lower.split("-")[0];
8469  return RHYTHM_TEST_TRANSLATIONS[prefix] || RHYTHM_TEST_TRANSLATIONS.en;
8470}
8471const RhythmTestGame = ({
8472  data,
8473  melodyData,
8474  currentTime = 0,
8475  isPlaying: parentIsPlaying = false,
8476  duration = 180,
8477  tempo = 120,
8478  timeSignature = [4, 4],
8479  trackId,
8480  userId,
8481  userName,
8482  onPlayPause,
8483  onSeek,
8484  onBackToSongSelect,
8485  onBackToCabinetSelect
8486}) => {
8487  const { i18n } = useTranslation();
8488  const locale = reactExports.useMemo(() => getRhythmTestLocale(i18n.language), [i18n.language]);
8489  const [playMode, setPlayMode] = reactExports.useState("play-along");
8490  const [difficulty, setDifficulty] = reactExports.useState("medium");
8491  const [bpm, setBpm] = reactExports.useState(() => Math.max(60, Math.min(240, Math.round(tempo) || 120)));
8492  const [soundType, setSoundType] = reactExports.useState("bongo");
8493  const [inputMode, setInputMode] = reactExports.useState("keyboard");
8494  const [micEnabled, setMicEnabled] = reactExports.useState(false);
8495  const [micLevel, setMicLevel] = reactExports.useState(0);
8496  const [micThreshold, setMicThreshold] = reactExports.useState(0.22);
8497  const [micSensitivity, setMicSensitivity] = reactExports.useState(1.3);
8498  const [showMicCalibration, setShowMicCalibration] = reactExports.useState(false);
8499  const [showSettingsModal, setShowSettingsModal] = reactExports.useState(false);
8500  const [showHelpModal, setShowHelpModal] = reactExports.useState(false);
8501  const [isPlaying, setIsPlaying] = reactExports.useState(false);
8502  const [countInBeat, setCountInBeat] = reactExports.useState(null);
8503  const [currentMeasureIndex, setCurrentMeasureIndex] = reactExports.useState(0);
8504  const [currentSubBeatOffset, setCurrentSubBeatOffset] = reactExports.useState(-1);
8505  const [echoPhase, setEchoPhase] = reactExports.useState("listen");
8506  const [leftPawDown, setLeftPawDown] = reactExports.useState(false);
8507  const [rightPawDown, setRightPawDown] = reactExports.useState(false);
8508  const [catMood, setCatMood] = reactExports.useState("idle");
8509  const secPerBeat = 60 / Math.max(40, bpm || 120);
8510  const secPerMeasure = secPerBeat * 4;
8511  const [patterns, setPatterns] = reactExports.useState(() => {
8512    var _a;
8513    if (data && data.length > 0 || melodyData && (((_a = melodyData.notes) == null ? void 0 : _a.length) || Array.isArray(melodyData) && melodyData.length > 0)) {
8514      return convertMidiToMeasurePattern(data, melodyData, tempo || 120, duration || 180, "medium");
8515    }
8516    return createPatternFromDotList(SCREENSHOT_DEFAULT_PATTERN);
8517  });
8518  reactExports.useEffect(() => {
8519    var _a;
8520    if (data && data.length > 0 || melodyData && (((_a = melodyData.notes) == null ? void 0 : _a.length) || Array.isArray(melodyData) && melodyData.length > 0)) {
8521      const songPatterns = convertMidiToMeasurePattern(
8522        data,
8523        melodyData,
8524        bpm,
8525        duration || 180,
8526        difficulty
8527      );
8528      setPatterns(songPatterns);
8529    } else {
8530      const totalM = Math.max(16, Math.ceil((duration || 180) / secPerMeasure));
8531      setPatterns(generateRhythmPattern(difficulty, totalM));
8532    }
8533  }, [data, melodyData, bpm, duration, difficulty, secPerMeasure]);
8534  const [score, setScore] = reactExports.useState(0);
8535  const [combo, setCombo] = reactExports.useState(0);
8536  const [maxCombo, setMaxCombo] = reactExports.useState(0);
8537  const [perfectCount, setPerfectCount] = reactExports.useState(0);
8538  const [goodCount, setGoodCount] = reactExports.useState(0);
8539  const [missCount, setMissCount] = reactExports.useState(0);
8540  const [timingOffsets, setTimingOffsets] = reactExports.useState([]);
8541  const [lastJudgement, setLastJudgement] = reactExports.useState(null);
8542  const [gameResult, setGameResult] = reactExports.useState(null);
8543  const inputManagerRef = reactExports.useRef(null);
8544  const micInputRef = reactExports.useRef(null);
8545  const scheduledNotesRef = reactExports.useRef([]);
8546  const gameStartTimeRef = reactExports.useRef(0);
8547  const animationFrameRef = reactExports.useRef(null);
8548  const isPlayingRef = reactExports.useRef(isPlaying);
8549  isPlayingRef.current = isPlaying;
8550  reactExports.useEffect(() => {
8551    const manager = new RhythmInputManager();
8552    inputManagerRef.current = manager;
8553    manager.attach();
8554    return () => {
8555      manager.detach();
8556    };
8557  }, []);
8558  const handleToggleMic = reactExports.useCallback(async () => {
8559    var _a, _b, _c;
8560    if (micEnabled) {
8561      (_a = micInputRef.current) == null ? void 0 : _a.stop();
8562      micInputRef.current = null;
8563      (_b = inputManagerRef.current) == null ? void 0 : _b.setMicInput(null);
8564      setMicEnabled(false);
8565      if (inputMode === "mic") setInputMode("keyboard");
8566    } else {
8567      const mic = new RhythmMicInput({
8568        sensitivity: micSensitivity,
8569        threshold: micThreshold
8570      });
8571      mic.setOnLevel((lvl) => {
8572        setMicLevel(lvl);
8573      });
8574      const granted = await mic.start();
8575      if (granted) {
8576        micInputRef.current = mic;
8577        (_c = inputManagerRef.current) == null ? void 0 : _c.setMicInput(mic);
8578        setMicEnabled(true);
8579        setInputMode("mic");
8580        setShowMicCalibration(true);
8581      } else {
8582        alert(locale.micDenied);
8583      }
8584    }
8585  }, [micEnabled, inputMode, micSensitivity, micThreshold, locale.micDenied]);
8586  reactExports.useEffect(() => {
8587    if (micInputRef.current) {
8588      micInputRef.current.setSensitivity(micSensitivity);
8589      micInputRef.current.setThreshold(micThreshold);
8590    }
8591  }, [micSensitivity, micThreshold]);
8592  const triggerPaw = reactExports.useCallback((side) => {
8593    if (side === "left") {
8594      setLeftPawDown(true);
8595      setTimeout(() => setLeftPawDown(false), 90);
8596    } else {
8597      setRightPawDown(true);
8598      setTimeout(() => setRightPawDown(false), 90);
8599    }
8600  }, []);
8601  const handleUserHit = reactExports.useCallback((event) => {
8602    triggerPaw(event.side);
8603    if (!isPlayingRef.current) {
8604      rhythmAudio.playBeatSound(soundType, event.side === "left", false);
8605      return;
8606    }
8607    if (playMode === "echo" && echoPhase === "listen") {
8608      rhythmAudio.playBeatSound(soundType, event.side === "left", false);
8609      return;
8610    }
8611    const now = performance.now();
8612    const activeTime = (now - gameStartTimeRef.current) / 1e3;
8613    const notes = scheduledNotesRef.current;
8614    let bestIdx = -1;
8615    let minDiff = 0.18;
8616    for (let i = 0; i < notes.length; i++) {
8617      if (notes[i].hit) continue;
8618      const diff = activeTime - notes[i].time;
8619      if (Math.abs(diff) < Math.abs(minDiff)) {
8620        minDiff = diff;
8621        bestIdx = i;
8622      }
8623    }
8624    if (bestIdx !== -1) {
8625      notes[bestIdx].hit = true;
8626      const diffMs = minDiff * 1e3;
8627      const absDiffMs = Math.abs(diffMs);
8628      setTimingOffsets((prev) => [...prev, diffMs]);
8629      if (absDiffMs <= 50) {
8630        rhythmAudio.playHitFeedback("perfect");
8631        rhythmAudio.playBeatSound(soundType, event.side === "left", true);
8632        setScore((s) => s + 300 + combo * 10);
8633        setCombo((c) => {
8634          const next = c + 1;
8635          setMaxCombo((m) => Math.max(m, next));
8636          return next;
8637        });
8638        setPerfectCount((p) => p + 1);
8639        setCatMood("happy");
8640        setLastJudgement({ text: "PERFECT", color: "text-amber-400" });
8641      } else if (absDiffMs <= 110) {
8642        rhythmAudio.playHitFeedback("good");
8643        rhythmAudio.playBeatSound(soundType, event.side === "left", false);
8644        setScore((s) => s + 150 + combo * 5);
8645        setCombo((c) => {
8646          const next = c + 1;
8647          setMaxCombo((m) => Math.max(m, next));
8648          return next;
8649        });
8650        setGoodCount((g) => g + 1);
8651        setCatMood("happy");
8652        setLastJudgement({ text: "GOOD", color: "text-emerald-400" });
8653      } else {
8654        rhythmAudio.playHitFeedback("good");
8655        rhythmAudio.playBeatSound(soundType, event.side === "left", false);
8656        setScore((s) => s + 50);
8657        setGoodCount((g) => g + 1);
8658        setCatMood("focused");
8659        setLastJudgement({ text: "OK", color: "text-cyan-400" });
8660      }
8661    } else {
8662      rhythmAudio.playBeatSound(soundType, event.side === "left", false);
8663    }
8664  }, [triggerPaw, soundType, playMode, echoPhase, combo]);
8665  reactExports.useEffect(() => {
8666    var _a;
8667    (_a = inputManagerRef.current) == null ? void 0 : _a.setOnHit(handleUserHit);
8668  }, [handleUserHit]);
8669  const buildTargetNotes = reactExports.useCallback((offsetSec = 0) => {
8670    const notes = [];
8671    patterns.forEach((pat, mIdx) => {
8672      const measureStart = offsetSec + mIdx * secPerMeasure;
8673      pat.beatOffsets.forEach((bOff) => {
8674        notes.push({
8675          time: measureStart + bOff * secPerBeat,
8676          measureIdx: mIdx,
8677          beatOffset: bOff,
8678          hit: false
8679        });
8680      });
8681    });
8682    return notes;
8683  }, [patterns, secPerMeasure, secPerBeat]);
8684  const startGame = reactExports.useCallback(() => {
8685    rhythmAudio.init();
8686    setIsPlaying(true);
8687    setGameResult(null);
8688    setScore(0);
8689    setCombo(0);
8690    setMaxCombo(0);
8691    setPerfectCount(0);
8692    setGoodCount(0);
8693    setMissCount(0);
8694    setTimingOffsets([]);
8695    setCatMood("focused");
8696    setCurrentMeasureIndex(0);
8697    setCurrentSubBeatOffset(-1);
8698    let currentLeadBeat = 1;
8699    setCountInBeat(1);
8700    rhythmAudio.playCountIn(1);
8701    const countInterval = setInterval(() => {
8702      currentLeadBeat++;
8703      if (currentLeadBeat <= 4) {
8704        setCountInBeat(currentLeadBeat);
8705        rhythmAudio.playCountIn(currentLeadBeat);
8706      } else {
8707        clearInterval(countInterval);
8708        setCountInBeat(null);
8709        if (onPlayPause) {
8710          onPlayPause(true);
8711        }
8712        const startTime = performance.now();
8713        gameStartTimeRef.current = startTime;
8714        scheduledNotesRef.current = buildTargetNotes(0);
8715        const songTotalSec = duration && duration > 5 ? duration : patterns.length * secPerMeasure;
8716        const tick = () => {
8717          const elapsedSec = (performance.now() - startTime) / 1e3;
8718          if (elapsedSec >= songTotalSec) {
8719            finishGame();
8720            return;
8721          }
8722          const mIdx = Math.min(patterns.length - 1, Math.floor(elapsedSec / secPerMeasure));
8723          const timeInMeasure = elapsedSec % secPerMeasure;
8724          const subBeat = timeInMeasure / secPerBeat;
8725          setCurrentMeasureIndex(mIdx);
8726          setCurrentSubBeatOffset(subBeat);
8727          scheduledNotesRef.current.forEach((note) => {
8728            if (!note.hit && elapsedSec - note.time > 0.16) {
8729              note.hit = true;
8730              setMissCount((m) => m + 1);
8731              setCombo(0);
8732              setCatMood("miss");
8733              rhythmAudio.playHitFeedback("miss");
8734              setLastJudgement({ text: "MISS", color: "text-rose-500" });
8735            }
8736          });
8737          if (playMode === "echo" && echoPhase === "listen") {
8738            scheduledNotesRef.current.forEach((note) => {
8739              if (Math.abs(elapsedSec - note.time) < 0.02) {
8740                triggerPaw(note.beatOffset % 2 === 0 ? "left" : "right");
8741                rhythmAudio.playBeatSound(soundType, note.beatOffset % 2 === 0, note.beatOffset === 0);
8742              }
8743            });
8744          }
8745          animationFrameRef.current = requestAnimationFrame(tick);
8746        };
8747        animationFrameRef.current = requestAnimationFrame(tick);
8748      }
8749    }, secPerBeat * 1e3);
8750  }, [secPerBeat, secPerMeasure, buildTargetNotes, playMode, echoPhase, soundType, triggerPaw, onPlayPause, duration, patterns.length]);
8751  const stopGame = reactExports.useCallback(() => {
8752    setIsPlaying(false);
8753    setCountInBeat(null);
8754    if (animationFrameRef.current !== null) {
8755      cancelAnimationFrame(animationFrameRef.current);
8756      animationFrameRef.current = null;
8757    }
8758    if (onPlayPause) {
8759      onPlayPause(false);
8760    }
8761    setCatMood("idle");
8762  }, [onPlayPause]);
8763  const finishGame = reactExports.useCallback(() => {
8764    stopGame();
8765    const totalTargets = scheduledNotesRef.current.length || 1;
8766    const totalHits = perfectCount + goodCount;
8767    const rawAccuracy = Math.max(0, Math.min(100, totalHits / totalTargets * 100));
8768    let avgOffset = 0;
8769    if (timingOffsets.length > 0) {
8770      avgOffset = timingOffsets.reduce((a, b) => a + b, 0) / timingOffsets.length;
8771    }
8772    setGameResult({
8773      score,
8774      accuracy: rawAccuracy,
8775      perfectCount,
8776      goodCount,
8777      missCount,
8778      maxStreak: maxCombo,
8779      avgOffsetMs: avgOffset,
8780      totalTargetBeats: totalTargets,
8781      bpm,
8782      difficulty
8783    });
8784  }, [stopGame, score, perfectCount, goodCount, missCount, maxCombo, timingOffsets, bpm, difficulty]);
8785  const handleMeasureClick = reactExports.useCallback((mIdx) => {
8786    if (isPlaying) return;
8787    setPatterns((prev) => {
8788      const next = [...prev];
8789      if (!next[mIdx]) return prev;
8790      const current = next[mIdx].dots;
8791      const cycle = [1, 2, 4, 3, 8, 0];
8792      const nextIdx = (cycle.indexOf(current) + 1) % cycle.length;
8793      const newDots = cycle[nextIdx];
8794      next[mIdx] = {
8795        measureIndex: mIdx,
8796        dots: newDots,
8797        beatOffsets: newDots === 0 ? [] : newDots === 1 ? [0] : newDots === 2 ? [0, 2] : newDots === 4 ? [0, 1, 2, 3] : newDots === 3 ? [0, 1, 2] : [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5]
8798      };
8799      return next;
8800    });
8801  }, [isPlaying]);
8802  const handleNextPattern = reactExports.useCallback(() => {
8803    const totalM = Math.max(16, Math.ceil((duration || 180) / secPerMeasure));
8804    setPatterns(generateRhythmPattern(difficulty, totalM));
8805    setGameResult(null);
8806  }, [difficulty, duration, secPerMeasure]);
8807  const tapTimesRef = reactExports.useRef([]);
8808  const handleTapTempo = reactExports.useCallback(() => {
8809    const now = performance.now();
8810    const taps = tapTimesRef.current;
8811    taps.push(now);
8812    if (taps.length > 1 && now - taps[taps.length - 2] > 2500) {
8813      tapTimesRef.current = [now];
8814      return;
8815    }
8816    if (taps.length > 4) taps.shift();
8817    if (taps.length >= 2) {
8818      let totalDiff = 0;
8819      for (let i = 1; i < taps.length; i++) {
8820        totalDiff += taps[i] - taps[i - 1];
8821      }
8822      const avgDiffSec = totalDiff / (taps.length - 1) / 1e3;
8823      const calculatedBpm = Math.round(60 / avgDiffSec);
8824      if (calculatedBpm >= 60 && calculatedBpm <= 240) {
8825        setBpm(calculatedBpm);
8826      }
8827    }
8828  }, []);
8829  const totalPages = Math.max(1, Math.ceil(patterns.length / 16));
8830  const currentPage = Math.min(totalPages - 1, Math.floor(currentMeasureIndex / 16));
8831  const currentDisplayPatterns = reactExports.useMemo(() => {
8832    const start = currentPage * 16;
8833    return patterns.slice(start, start + 16);
8834  }, [patterns, currentPage]);
8835  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full h-full max-h-screen bg-[#f7f7f8] dark:bg-[#0c0d12] text-zinc-900 dark:text-zinc-100 flex flex-col justify-between overflow-hidden p-2 sm:p-3 md:p-4 select-none font-sans", children: [
8836    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-[500px] mx-auto flex items-center justify-between pb-1.5 border-b border-zinc-200 dark:border-zinc-800 shrink-0", children: [
8837      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
8838        /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-lg sm:text-xl font-black tracking-tight text-zinc-900 dark:text-white", children: locale.gameTitle }),
8839        lastJudgement && isPlaying && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] sm:text-xs font-black tracking-wider uppercase px-2 py-0.5 rounded-full bg-black/10 dark:bg-white/10 ${lastJudgement.color} animate-bounce`, children: lastJudgement.text })
8840      ] }),
8841      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2.5 text-zinc-600 dark:text-zinc-400", children: [
8842        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-zinc-200/80 dark:bg-zinc-800 rounded-lg p-0.5", children: [
8843          /* @__PURE__ */ jsxRuntimeExports.jsx(
8844            "button",
8845            {
8846              onClick: () => {
8847                setInputMode("keyboard");
8848                if (micEnabled) handleToggleMic();
8849              },
8850              className: `p-1 sm:p-1.5 rounded-md transition-all ${inputMode === "keyboard" ? "bg-white dark:bg-zinc-700 text-purple-600 dark:text-purple-400 shadow-sm font-bold" : "hover:text-zinc-900"}`,
8851              title: "Keyboard Controls (Space / F,J / D,K)",
8852              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Keyboard, { size: 15 })
8853            }
8854          ),
8855          /* @__PURE__ */ jsxRuntimeExports.jsx(
8856            "button",
8857            {
8858              onClick: () => {
8859                setInputMode("touch");
8860                if (micEnabled) handleToggleMic();
8861              },
8862              className: `p-1 sm:p-1.5 rounded-md transition-all ${inputMode === "touch" ? "bg-white dark:bg-zinc-700 text-purple-600 dark:text-purple-400 shadow-sm font-bold" : "hover:text-zinc-900"}`,
8863              title: "Touch Controls (Fingers on Screen)",
8864              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Smartphone, { size: 15 })
8865            }
8866          ),
8867          /* @__PURE__ */ jsxRuntimeExports.jsx(
8868            "button",
8869            {
8870              onClick: handleToggleMic,
8871              className: `p-1 sm:p-1.5 rounded-md transition-all ${inputMode === "mic" ? "bg-purple-600 text-white shadow-sm font-bold" : "hover:text-zinc-900"}`,
8872              title: micEnabled ? "Microphone On (Clap / Voice)" : "Enable Microphone Clapping Input",
8873              children: micEnabled ? /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { size: 15, className: "text-amber-300 animate-pulse" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(MicOff, { size: 15 })
8874            }
8875          )
8876        ] }),
8877        /* @__PURE__ */ jsxRuntimeExports.jsx(
8878          "button",
8879          {
8880            onClick: () => setShowSettingsModal(true),
8881            className: "p-1 sm:p-1.5 rounded-lg hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors cursor-pointer",
8882            title: "Rhythm Test Settings",
8883            children: /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 16 })
8884          }
8885        ),
8886        onBackToSongSelect && /* @__PURE__ */ jsxRuntimeExports.jsx(
8887          "button",
8888          {
8889            onClick: onBackToSongSelect,
8890            className: "p-1 sm:p-1.5 rounded-lg hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors cursor-pointer",
8891            title: locale.changeSong,
8892            children: /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 16 })
8893          }
8894        )
8895      ] })
8896    ] }),
8897    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-[500px] mx-auto flex items-center justify-between py-1 text-[11px] font-mono shrink-0", children: [
8898      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 sm:gap-1.5", children: [
8899        /* @__PURE__ */ jsxRuntimeExports.jsx(
8900          "button",
8901          {
8902            onClick: () => setPlayMode("play-along"),
8903            className: `px-2 py-0.5 rounded font-bold transition-all ${playMode === "play-along" ? "bg-purple-600 text-white" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-white"}`,
8904            children: locale.modePlayAlong
8905          }
8906        ),
8907        /* @__PURE__ */ jsxRuntimeExports.jsx(
8908          "button",
8909          {
8910            onClick: () => setPlayMode("echo"),
8911            className: `px-2 py-0.5 rounded font-bold transition-all ${playMode === "echo" ? "bg-purple-600 text-white" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-white"}`,
8912            children: locale.modeEcho
8913          }
8914        ),
8915        /* @__PURE__ */ jsxRuntimeExports.jsx(
8916          "button",
8917          {
8918            onClick: () => setPlayMode("sandbox"),
8919            className: `px-2 py-0.5 rounded font-bold transition-all ${playMode === "sandbox" ? "bg-purple-600 text-white" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-white"}`,
8920            children: locale.modeSandbox
8921          }
8922        )
8923      ] }),
8924      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [
8925        totalPages > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-purple-600 dark:text-purple-400 font-bold flex items-center gap-1", children: [
8926          /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 11 }),
8927          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8928            locale.page,
8929            " ",
8930            currentPage + 1,
8931            "/",
8932            totalPages
8933          ] })
8934        ] }),
8935        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-500 font-bold", children: [
8936          locale.score,
8937          ": ",
8938          /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-900 dark:text-white", children: score })
8939        ] }),
8940        combo > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-amber-500 font-bold animate-pulse", children: [
8941          combo,
8942          " ",
8943          locale.streak
8944        ] })
8945      ] })
8946    ] }),
8947    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1 min-h-0 w-full flex flex-col items-center justify-center my-auto overflow-hidden py-1", children: [
8948      /* @__PURE__ */ jsxRuntimeExports.jsx(
8949        RhythmTestGrid,
8950        {
8951          patterns: currentDisplayPatterns,
8952          currentMeasureIndex,
8953          currentSubBeatOffset,
8954          isPlaying,
8955          isEditorMode: !isPlaying || playMode === "sandbox",
8956          pageOffset: currentPage * 16,
8957          inputMode,
8958          onMeasureClick: handleMeasureClick
8959        }
8960      ),
8961      countInBeat !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black/60 backdrop-blur-sm rounded-2xl z-30 animate-in fade-in duration-100", children: [
8962        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-6xl sm:text-7xl font-black text-amber-400 drop-shadow-[0_0_20px_rgba(251,191,36,0.8)] animate-ping", children: countInBeat }),
8963        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold tracking-widest text-white uppercase mt-4", children: locale.countIn })
8964      ] })
8965    ] }),
8966    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-[500px] mx-auto text-[10px] sm:text-[11px] font-mono font-bold text-zinc-500 dark:text-zinc-400 py-0.5 text-center shrink-0 flex items-center justify-center gap-1.5", children: [
8967      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "💡" }),
8968      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: inputMode === "keyboard" ? locale.tapKeyHint : inputMode === "touch" ? locale.touchHint : locale.micTestClap })
8969    ] }),
8970    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-[500px] mx-auto flex items-end justify-between pt-1 border-t border-zinc-200 dark:border-zinc-800 shrink-0", children: [
8971      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-start gap-0.5 pb-0.5", children: [
8972        /* @__PURE__ */ jsxRuntimeExports.jsxs(
8973          "div",
8974          {
8975            onClick: handleTapTempo,
8976            className: "flex items-center gap-1.5 text-base sm:text-lg font-bold font-mono text-zinc-900 dark:text-zinc-100 hover:text-purple-600 dark:hover:text-purple-400 cursor-pointer transition-colors",
8977            title: "Click repeatedly to Tap Tempo!",
8978            children: [
8979              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-lg", children: "♩" }),
8980              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "=" }),
8981              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-black text-purple-600 dark:text-purple-400", children: bpm }),
8982              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs uppercase text-zinc-500 ml-0.5", children: locale.bpm })
8983            ]
8984          }
8985        ),
8986        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 mt-0.5", children: [
8987          /* @__PURE__ */ jsxRuntimeExports.jsx(
8988            "button",
8989            {
8990              onClick: () => setBpm((b) => Math.max(60, b - 5)),
8991              className: "p-1 rounded bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 text-xs transition-colors",
8992              title: "Decrease BPM",
8993              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { size: 11 })
8994            }
8995          ),
8996          /* @__PURE__ */ jsxRuntimeExports.jsx(
8997            "button",
8998            {
8999              onClick: () => setBpm((b) => Math.min(240, b + 5)),
9000              className: "p-1 rounded bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 text-xs transition-colors",
9001              title: "Increase BPM",
9002              children: /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 11 })
9003            }
9004          ),
9005          /* @__PURE__ */ jsxRuntimeExports.jsxs(
9006            "button",
9007            {
9008              onClick: isPlaying ? stopGame : startGame,
9009              className: `ml-2 px-3 py-1 rounded-lg font-bold text-xs tracking-wider flex items-center gap-1.5 transition-all shadow-md active:scale-95 cursor-pointer ${isPlaying ? "bg-rose-500 hover:bg-rose-600 text-white" : "bg-purple-600 hover:bg-purple-500 text-white shadow-purple-500/30"}`,
9010              children: [
9011                isPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12 }),
9012                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: isPlaying ? locale.paused : "START" })
9013              ]
9014            }
9015          )
9016        ] }),
9017        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-400 font-mono mt-0.5", children: inputMode === "mic" ? "🎙️ " + locale.micActive : "⌨️ [SPACE] or [F / J]" })
9018      ] }),
9019      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-24 h-18 sm:w-28 sm:h-22 md:w-32 md:h-24 -mb-1 shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
9020        RhythmBongoCat,
9021        {
9022          leftPawDown,
9023          rightPawDown,
9024          mood: catMood,
9025          combo,
9026          onCatClick: () => {
9027            var _a;
9028            return (_a = inputManagerRef.current) == null ? void 0 : _a.triggerManualHit("center", "touch");
9029          }
9030        }
9031      ) })
9032    ] }),
9033    inputMode === "touch" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-[500px] mx-auto grid grid-cols-2 gap-2 mt-1.5 pt-1 border-t border-zinc-200 dark:border-zinc-800/80 shrink-0", children: [
9034      /* @__PURE__ */ jsxRuntimeExports.jsx(
9035        "button",
9036        {
9037          onPointerDown: (e) => {
9038            var _a;
9039            e.preventDefault();
9040            (_a = inputManagerRef.current) == null ? void 0 : _a.triggerManualHit("left", "touch");
9041          },
9042          className: "h-10 sm:h-12 rounded-xl bg-purple-500/20 active:bg-purple-600 border border-purple-400/40 text-purple-700 dark:text-purple-300 active:text-white font-bold text-xs flex items-center justify-center gap-2 select-none shadow-sm active:scale-95 transition-all",
9043          children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
9044            "🐾 ",
9045            locale.instructions ? "LEFT" : "LEFT"
9046          ] })
9047        }
9048      ),
9049      /* @__PURE__ */ jsxRuntimeExports.jsx(
9050        "button",
9051        {
9052          onPointerDown: (e) => {
9053            var _a;
9054            e.preventDefault();
9055            (_a = inputManagerRef.current) == null ? void 0 : _a.triggerManualHit("right", "touch");
9056          },
9057          className: "h-10 sm:h-12 rounded-xl bg-purple-500/20 active:bg-purple-600 border border-purple-400/40 text-purple-700 dark:text-purple-300 active:text-white font-bold text-xs flex items-center justify-center gap-2 select-none shadow-sm active:scale-95 transition-all",
9058          children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
9059            "🐾 ",
9060            locale.instructions ? "RIGHT" : "RIGHT"
9061          ] })
9062        }
9063      )
9064    ] }),
9065    micEnabled && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-14 right-4 z-20 flex items-center gap-2 px-3 py-1.5 rounded-full bg-zinc-900/90 text-white text-[11px] font-mono border border-zinc-700 shadow-xl", children: [
9066      /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { size: 12, className: "text-amber-400 animate-pulse" }),
9067      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "MIC:" }),
9068      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-16 h-2 rounded-full bg-zinc-700 overflow-hidden relative", children: [
9069        /* @__PURE__ */ jsxRuntimeExports.jsx(
9070          "div",
9071          {
9072            className: `h-full transition-all duration-75 ${micLevel > micThreshold ? "bg-amber-400" : "bg-emerald-400"}`,
9073            style: { width: `${Math.min(100, micLevel * 100)}%` }
9074          }
9075        ),
9076        /* @__PURE__ */ jsxRuntimeExports.jsx(
9077          "div",
9078          {
9079            className: "absolute top-0 bottom-0 w-0.5 bg-rose-500",
9080            style: { left: `${micThreshold * 100}%` }
9081          }
9082        )
9083      ] })
9084    ] }),
9085    gameResult && /* @__PURE__ */ jsxRuntimeExports.jsx(
9086      RhythmTestResults,
9087      {
9088        result: gameResult,
9089        locale,
9090        onRetry: startGame,
9091        onNextPattern: handleNextPattern,
9092        onSelectSong: onBackToSongSelect,
9093        onChangeCabinet: onBackToCabinetSelect
9094      }
9095    ),
9096    showSettingsModal && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-50 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 animate-in fade-in", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-sm bg-zinc-900 border border-zinc-700 rounded-2xl p-5 text-white shadow-2xl", children: [
9097      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pb-3 border-b border-zinc-800", children: [
9098        /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-base flex items-center gap-2", children: [
9099          /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 16, className: "text-purple-400" }),
9100          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Settings" })
9101        ] }),
9102        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setShowSettingsModal(false), className: "text-zinc-400 hover:text-white cursor-pointer", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 18 }) })
9103      ] }),
9104      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-4 mt-4 font-mono text-xs", children: [
9105        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
9106          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-zinc-400 mb-1.5 block font-bold", children: locale.soundType }),
9107          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 gap-2", children: ["bongo", "metronome", "clap", "woodblock"].map((type) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
9108            "button",
9109            {
9110              onClick: () => {
9111                setSoundType(type);
9112                rhythmAudio.playBeatSound(type, true, true);
9113              },
9114              className: `p-2 rounded-lg border text-center transition-all cursor-pointer ${soundType === type ? "bg-purple-600 border-purple-400 text-white font-bold" : "bg-zinc-800 border-zinc-700 text-zinc-300 hover:border-zinc-500"}`,
9115              children: [
9116                type === "bongo" && locale.soundBongo,
9117                type === "metronome" && locale.soundMetronome,
9118                type === "clap" && locale.soundClap,
9119                type === "woodblock" && locale.soundWoodblock
9120              ]
9121            },
9122            type
9123          )) })
9124        ] }),
9125        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
9126          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-zinc-400 mb-1.5 block font-bold", children: locale.difficulty }),
9127          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 gap-2", children: ["easy", "medium", "hard", "master"].map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(
9128            "button",
9129            {
9130              onClick: () => {
9131                setDifficulty(d);
9132                const totalM = Math.max(16, Math.ceil((duration || 180) / secPerMeasure));
9133                setPatterns(generateRhythmPattern(d, totalM));
9134              },
9135              className: `p-2 rounded-lg border text-center transition-all cursor-pointer ${difficulty === d ? "bg-purple-600 border-purple-400 text-white font-bold" : "bg-zinc-800 border-zinc-700 text-zinc-300 hover:border-zinc-500"}`,
9136              children: d.toUpperCase()
9137            },
9138            d
9139          )) })
9140        ] }),
9141        micEnabled && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t border-zinc-800 pt-3 flex flex-col gap-3", children: [
9142          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
9143            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-zinc-400 mb-1", children: [
9144              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.micSensitivity }),
9145              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
9146                micSensitivity.toFixed(1),
9147                "x"
9148              ] })
9149            ] }),
9150            /* @__PURE__ */ jsxRuntimeExports.jsx(
9151              "input",
9152              {
9153                type: "range",
9154                min: "0.5",
9155                max: "3.0",
9156                step: "0.1",
9157                value: micSensitivity,
9158                onChange: (e) => setMicSensitivity(parseFloat(e.target.value)),
9159                className: "w-full accent-purple-500 cursor-pointer"
9160              }
9161            )
9162          ] }),
9163          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
9164            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-zinc-400 mb-1", children: [
9165              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: locale.micThreshold }),
9166              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
9167                Math.round(micThreshold * 100),
9168                "%"
9169              ] })
9170            ] }),
9171            /* @__PURE__ */ jsxRuntimeExports.jsx(
9172              "input",
9173              {
9174                type: "range",
9175                min: "0.05",
9176                max: "0.80",
9177                step: "0.02",
9178                value: micThreshold,
9179                onChange: (e) => setMicThreshold(parseFloat(e.target.value)),
9180                className: "w-full accent-amber-500 cursor-pointer"
9181              }
9182            )
9183          ] })
9184        ] })
9185      ] }),
9186      /* @__PURE__ */ jsxRuntimeExports.jsx(
9187        "button",
9188        {
9189          onClick: () => setShowSettingsModal(false),
9190          className: "w-full mt-5 py-2.5 rounded-xl bg-purple-600 hover:bg-purple-500 text-white font-bold transition-all shadow-lg cursor-pointer",
9191          children: "CLOSE"
9192        }
9193      )
9194    ] }) }),
9195    showMicCalibration && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-50 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 animate-in fade-in", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-sm bg-zinc-900 border border-zinc-700 rounded-2xl p-6 text-white text-center shadow-2xl", children: [
9196      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 rounded-full bg-purple-500/20 text-purple-400 border border-purple-500/40 mx-auto flex items-center justify-center mb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { size: 24 }) }),
9197      /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-lg", children: locale.micCalibrate }),
9198      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 mt-2 mb-4 leading-relaxed font-mono", children: locale.micTestClap }),
9199      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-4 rounded-full bg-zinc-800 overflow-hidden relative border border-zinc-700 my-4", children: [
9200        /* @__PURE__ */ jsxRuntimeExports.jsx(
9201          "div",
9202          {
9203            className: `h-full transition-all duration-75 ${micLevel > micThreshold ? "bg-amber-400" : "bg-purple-500"}`,
9204            style: { width: `${Math.min(100, micLevel * 100)}%` }
9205          }
9206        ),
9207        /* @__PURE__ */ jsxRuntimeExports.jsx(
9208          "div",
9209          {
9210            className: "absolute top-0 bottom-0 w-1 bg-rose-500 shadow-[0_0_8px_red]",
9211            style: { left: `${micThreshold * 100}%` }
9212          }
9213        )
9214      ] }),
9215      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-3 font-mono text-xs text-left", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
9216        /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-zinc-400 block mb-1", children: [
9217          locale.micThreshold,
9218          ": ",
9219          Math.round(micThreshold * 100),
9220          "%"
9221        ] }),
9222        /* @__PURE__ */ jsxRuntimeExports.jsx(
9223          "input",
9224          {
9225            type: "range",
9226            min: "0.05",
9227            max: "0.80",
9228            step: "0.02",
9229            value: micThreshold,
9230            onChange: (e) => setMicThreshold(parseFloat(e.target.value)),
9231            className: "w-full accent-amber-500 cursor-pointer"
9232          }
9233        )
9234      ] }) }),
9235      /* @__PURE__ */ jsxRuntimeExports.jsx(
9236        "button",
9237        {
9238          onClick: () => setShowMicCalibration(false),
9239          className: "w-full mt-5 py-2.5 rounded-xl bg-purple-600 hover:bg-purple-500 text-white font-bold transition-all shadow-lg cursor-pointer",
9240          children: "READY TO PLAY"
9241        }
9242      )
9243    ] }) })
9244  ] });
9245};
9246const GameArena = ({
9247  module,
9248  selectedSong,
9249  gameSettings,
9250  currentUser,
9251  onBackToCabinetSelect,
9252  onBackToSongSelect
9253}) => {
9254  var _a;
9255  const arenaContainerRef = reactExports.useRef(null);
9256  const audioRef = reactExports.useRef(null);
9257  const effectiveOpusPath = reactExports.useMemo(() => {
9258    if (selectedSong.opusPath) return selectedSong.opusPath;
9259    if (selectedSong.source === "site" && selectedSong.id) {
9260      const numId = parseInt(selectedSong.id);
9261      const group = numId >= 1e7 ? numId % 1e3 : Math.floor(numId / 1e3);
9262      return `/storage/tracks/opus/${group}/${selectedSong.id}.opus`;
9263    }
9264    return void 0;
9265  }, [selectedSong.opusPath, selectedSong.source, selectedSong.id]);
9266  const effectivePianoRollPath = reactExports.useMemo(() => {
9267    if (selectedSong.pianoRollPath) return selectedSong.pianoRollPath;
9268    if (selectedSong.source === "site" && selectedSong.id) {
9269      const numId = parseInt(selectedSong.id);
9270      const group = numId >= 1e7 ? numId % 1e3 : Math.floor(numId / 1e3);
9271      return `/storage/tracks/piano_rolls/${group}/${selectedSong.id}.json`;
9272    }
9273    return void 0;
9274  }, [selectedSong.pianoRollPath, selectedSong.source, selectedSong.id]);
9275  const effectiveMelodyPath = reactExports.useMemo(() => {
9276    var _a2;
9277    if (selectedSong.melodyPath) return selectedSong.melodyPath;
9278    if ((_a2 = selectedSong.rawTrack) == null ? void 0 : _a2.melodyPath) return selectedSong.rawTrack.melodyPath;
9279    if (selectedSong.source === "site" && selectedSong.id) {
9280      const numId = parseInt(selectedSong.id);
9281      const group = numId >= 1e7 ? numId % 1e3 : Math.floor(numId / 1e3);
9282      return `/storage/tracks/melody/${group}/${selectedSong.id}_standard.json`;
9283    }
9284    return void 0;
9285  }, [selectedSong.melodyPath, (_a = selectedSong.rawTrack) == null ? void 0 : _a.melodyPath, selectedSong.source, selectedSong.id]);
9286  const { t } = useTranslation();
9287  const [copiedShare, setCopiedShare] = reactExports.useState(false);
9288  const normalizedTimeSignature = reactExports.useMemo(() => {
9289    if (selectedSong.timeSignature && selectedSong.timeSignature.length >= 2) {
9290      return [Number(selectedSong.timeSignature[0]) || 4, Number(selectedSong.timeSignature[1]) || 4];
9291    }
9292    return [4, 4];
9293  }, [selectedSong.timeSignature]);
9294  const [isPlaying, setIsPlaying] = reactExports.useState(false);
9295  const [isPaused, setIsPaused] = reactExports.useState(false);
9296  const [currentTime, setCurrentTime] = reactExports.useState(0);
9297  const [isFullscreen, setIsFullscreen] = reactExports.useState(false);
9298  const [isAudioLoaded, setIsAudioLoaded] = reactExports.useState(false);
9299  const [isLoadingAudio, setIsLoadingAudio] = reactExports.useState(!!effectiveOpusPath);
9300  const [audioBlocked, setAudioBlocked] = reactExports.useState(false);
9301  reactExports.useEffect(() => {
9302    if (typeof window === "undefined") return;
9303    try {
9304      const url = new URL(window.location.href);
9305      url.searchParams.set("track", selectedSong.id);
9306      url.searchParams.set("mode", module.id);
9307      window.history.replaceState(null, "", url.toString());
9308    } catch {
9309    }
9310  }, [selectedSong.id, module.id]);
9311  const handleShareGame = reactExports.useCallback(() => {
9312    arcadeAudio.playDecide();
9313    if (typeof window === "undefined") return;
9314    const shareUrl = `${window.location.origin}/games?track=${selectedSong.id}&mode=${module.id}`;
9315    if (navigator.clipboard && navigator.clipboard.writeText) {
9316      navigator.clipboard.writeText(shareUrl).then(() => {
9317        setCopiedShare(true);
9318        setTimeout(() => setCopiedShare(false), 2500);
9319      }).catch(() => {
9320      });
9321    }
9322  }, [selectedSong.id, module.id]);
9323  const [beatmapData, setBeatmapData] = reactExports.useState(selectedSong.pianoRollData || []);
9324  const [melodyBeatmap, setMelodyBeatmap] = reactExports.useState(selectedSong.melodyData || []);
9325  const [isLoadingBeatmap, setIsLoadingBeatmap] = reactExports.useState(!selectedSong.pianoRollData && !!effectivePi
9325anoRollPath);
9326  reactExports.useEffect(() => {
9327    arcadeAudio.pauseBgm();
9328    return () => {
9329      arcadeAudio.resumeBgm();
9330    };
9331  }, []);
9332  reactExports.useEffect(() => {
9333    var _a2, _b, _c;
9334    let isCurrent = true;
9335    if (!selectedSong.pianoRollData && effectivePianoRollPath) {
9336      setIsLoadingBeatmap(true);
9337      fetch(effectivePianoRollPath).then((res) => {
9338        if (!res.ok) throw new Error(`HTTP ${res.status}`);
9339        return res.json();
9340      }).then((data) => {
9341        if (!isCurrent) return;
9342        setBeatmapData(Array.isArray(data) ? data : []);
9343        setIsLoadingBeatmap(false);
9344      }).catch((err) => {
9345        console.warn("[GameArena] Could not fetch pianoRollPath:", err);
9346        if (isCurrent) setIsLoadingBeatmap(false);
9347      });
9348    } else if (selectedSong.pianoRollData) {
9349      setBeatmapData(selectedSong.pianoRollData);
9350      setIsLoadingBeatmap(false);
9351    } else {
9352      setIsLoadingBeatmap(false);
9353    }
9354    const melodyUrl = effectiveMelodyPath || selectedSong.melodyPath || ((_b = (_a2 = selectedSong.rawTrack) == null ? void 0 : _a2.quantizedScorePaths) == null ? void 0 : _b.studio_melody) || ((_c = selectedSong.rawTrack) == null ? void 0 : _c.melodyPath);
9355    if (!selectedSong.melodyData && melodyUrl) {
9356      fetch(melodyUrl).then((res) => {
9357        if (!res.ok) throw new Error(`HTTP ${res.status}`);
9358        return res.json();
9359      }).then((data) => {
9360        if (isCurrent && Array.isArray(data)) {
9361          setMelodyBeatmap(data);
9362        }
9363      }).catch(() => {
9364      });
9365    } else if (selectedSong.melodyData) {
9366      setMelodyBeatmap(selectedSong.melodyData);
9367    }
9368    return () => {
9369      isCurrent = false;
9370    };
9371  }, [selectedSong.pianoRollData, effectivePianoRollPath, selectedSong.rawTrack, selectedSong.melodyPath, selectedSong.melodyData, effectiveMelodyPath]);
9372  const derivedColorMap = reactExports.useMemo(() => {
9373    const map = {};
9374    const palette = [
9375      "#06b6d4",
9376      "#ec4899",
9377      "#8b5cf6",
9378      "#f59e0b",
9379      "#10b981",
9380      "#3b82f6",
9381      "#f43f5e",
9382      "#84cc16"
9383    ];
9384    (selectedSong.instruments || []).forEach((inst, idx) => {
9385      map[inst.name] = palette[idx % palette.length];
9386    });
9387    return map;
9388  }, [selectedSong.instruments]);
9389  const uniqueNames = reactExports.useMemo(() => {
9390    return (selectedSong.instruments || []).map((i) => i.name);
9391  }, [selectedSong.instruments]);
9392  const selectedInstruments = reactExports.useMemo(() => selectedSong.instruments || [], [selectedSong.instruments]);
9393  const meowMidiMetadata = reactExports.useMemo(() => ({
9394    title: selectedSong.title,
9395    artist: selectedSong.artist,
9396    tempo: selectedSong.bpm,
9397    timeSignature: (selectedSong.timeSignature || [4, 4]).join("/")
9398  }), [selectedSong.title, selectedSong.artist, selectedSong.bpm, selectedSong.timeSignature]);
9399  const lastTickRef = reactExports.useRef(performance.now());
9400  const isPlayingRef = reactExports.useRef(isPlaying);
9401  isPlayingRef.current = isPlaying;
9402  reactExports.useEffect(() => {
9403    if (effectiveOpusPath) return;
9404    lastTickRef.current = performance.now();
9405    let animationFrameId;
9406    const loop = (now) => {
9407      if (isPlayingRef.current && !isPaused) {
9408        const deltaSec = (now - lastTickRef.current) / 1e3;
9409        setCurrentTime((prev) => {
9410          const nextTime = prev + deltaSec;
9411          if (nextTime >= selectedSong.duration) {
9412            setIsPlaying(false);
9413            return selectedSong.duration;
9414          }
9415          return nextTime;
9416        });
9417      }
9418      lastTickRef.current = now;
9419      animationFrameId = requestAnimationFrame(loop);
9420    };
9421    animationFrameId = requestAnimationFrame(loop);
9422    return () => cancelAnimationFrame(animationFrameId);
9423  }, [effectiveOpusPath, selectedSong.duration, isPaused]);
9424  const handleAudioTimeUpdate = () => {
9425    if (audioRef.current) {
9426      setCurrentTime(audioRef.current.currentTime);
9427    }
9428  };
9429  const handleAudioLoadedMetadata = () => {
9430    setIsAudioLoaded(true);
9431    setIsLoadingAudio(false);
9432  };
9433  const handleAudioEnded = () => {
9434    setIsPlaying(false);
9435    setIsPaused(false);
9436  };
9437  const [restartKey, setRestartKey] = reactExports.useState(0);
9438  const handleTogglePlay = reactExports.useCallback((forcePlay, openPauseModal = false) => {
9439    arcadeAudio.playSelect();
9440    const targetState = forcePlay !== void 0 ? forcePlay : !isPlaying;
9441    if (targetState) {
9442      setIsPlaying(true);
9443      setIsPaused(false);
9444      if (audioRef.current) {
9445        audioRef.current.play().catch((err) => {
9446          console.warn("[GameArena] Audio playback blocked by browser autoplay policy:", err);
9447          setAudioBlocked(true);
9448        });
9449      }
9450    } else {
9451      setIsPlaying(false);
9452      if (openPauseModal) {
9453        setIsPaused(true);
9454      }
9455      if (audioRef.current) {
9456        audioRef.current.pause();
9457      }
9458    }
9459  }, [isPlaying]);
9460  const handleResume = reactExports.useCallback(() => {
9461    arcadeAudio.playSelect();
9462    setIsPaused(false);
9463    setIsPlaying(true);
9464    setAudioBlocked(false);
9465    if (audioRef.current) {
9466      audioRef.current.play().catch((err) => {
9467        console.warn("[GameArena] Audio play error on resume:", err);
9468      });
9469    }
9470  }, []);
9471  const handleSeek = reactExports.useCallback((targetTime) => {
9472    const clamped = Math.max(0, Math.min(targetTime, selectedSong.duration));
9473    setCurrentTime(clamped);
9474    if (audioRef.current) {
9475      audioRef.current.currentTime = clamped;
9476    }
9477  }, [selectedSong.duration]);
9478  const handleRestart = reactExports.useCallback(() => {
9479    arcadeAudio.playStageStart();
9480    setCurrentTime(0);
9481    setIsPaused(false);
9482    setIsPlaying(false);
9483    setAudioBlocked(false);
9484    if (audioRef.current) {
9485      audioRef.current.currentTime = 0;
9486      audioRef.current.pause();
9487    }
9488    setRestartKey((prev) => prev + 1);
9489  }, []);
9490  const handleToggleFullscreen = () => {
9491    var _a2, _b, _c;
9492    arcadeAudio.playSelect();
9493    if (!document.fullscreenElement) {
9494      (_b = (_a2 = arenaContainerRef.current) == null ? void 0 : _a2.requestFullscreen) == null ? void 0 : _b.call(_a2).then(() => setIsFullscreen(true)).catch(() => {
9495      });
9496    } else {
9497      (_c = document.exitFullscreen) == null ? void 0 : _c.call(document).then(() => setIsFullscreen(false)).catch(() => {
9498      });
9499    }
9500  };
9501  reactExports.useEffect(() => {
9502    const handleKeyDown = (e) => {
9503      const target = e.target;
9504      if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA")) return;
9505      if (e.code === "Escape" || e.code === "KeyP") {
9506        e.preventDefault();
9507        e.stopPropagation();
9508        if (isPaused) {
9509          handleResume();
9510        } else {
9511          handleTogglePlay(false, true);
9512        }
9513      } else if (e.code === "Space") {
9514        if (module.id === "rhythm-test") {
9515          return;
9516        }
9517        e.preventDefault();
9518        if (isPaused) {
9519          handleResume();
9520        } else if (isPlaying) {
9521          handleTogglePlay(false, true);
9522        } else {
9523          handleTogglePlay(true, false);
9524        }
9525      } else if (e.key === "r" || e.key === "R") {
9526        e.preventDefault();
9527        handleRestart();
9528      }
9529    };
9530    window.addEventListener("keydown", handleKeyDown);
9531    return () => window.removeEventListener("keydown", handleKeyDown);
9532  }, [isPaused, isPlaying, handleResume, handleTogglePlay, handleRestart, module.id]);
9533  const isAudioReady = (isAudioLoaded || !effectiveOpusPath) && !isLoadingBeatmap;
9534  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
9535    "div",
9536    {
9537      ref: arenaContainerRef,
9538      className: `w-full h-full max-h-[calc(100vh-56px)] flex flex-col bg-black text-white relative select-none rounded-2xl overflow-hidden border-2 border-cyan-500/50 shadow-[0_0_50px_rgba(6,182,212,0.3)] ${isFullscreen ? "fixed inset-0 z-50 h-screen w-screen rounded-none border-none" : ""}`,
9539      children: [
9540        effectiveOpusPath && /* @__PURE__ */ jsxRuntimeExports.jsx(
9541          "audio",
9542          {
9543            ref: audioRef,
9544            src: effectiveOpusPath,
9545            preload: "auto",
9546            onCanPlayThrough: handleAudioLoadedMetadata,
9547            onLoadedMetadata: handleAudioLoadedMetadata,
9548            onTimeUpdate: handleAudioTimeUpdate,
9549            onEnded: handleAudioEnded,
9550            onError: (err) => {
9551              console.warn("[GameArena] Opus audio load error:", err);
9552              setIsAudioLoaded(true);
9553              setIsLoadingAudio(false);
9554            }
9555          }
9556        ),
9557        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-30 w-full py-1.5 px-3 bg-slate-950/95 border-b-2 border-cyan-500/40 backdrop-blur-md flex items-center justify-between gap-3 font-mono text-xs", children: [
9558          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
9559            /* @__PURE__ */ jsxRuntimeExports.jsxs(
9560              "button",
9561              {
9562                onClick: handleRestart,
9563                className: "flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-slate-900 border border-slate-700 hover:border-amber-400 text-amber-300 hover:text-amber-200 transition-all active:scale-95 cursor-pointer text-xs font-mono font-bold",
9564                title: "Restart Stage [R]",
9565                children: [
9566                  /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 13 }),
9567                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden sm:inline", children: "RESTART [R]" })
9568                ]
9569              }
9570            ),
9571            /* @__PURE__ */ jsxRuntimeExports.jsxs(
9572              "button",
9573              {
9574                onClick: handleShareGame,
9575                className: `flex items-center gap-1.5 px-2.5 py-1 rounded-lg border transition-all active:scale-95 cursor-pointer ${copiedShare ? "bg-emerald-500/20 border-emerald-400 text-emerald-300 shadow-[0_0_10px_rgba(16,185,129,0.3)]" : "bg-slate-900 border-slate-700 hover:border-cyan-400 text-slate-300 hover:text-white"}`,
9576                title: "Share Game Link",
9577                children: [
9578                  copiedShare ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 13, className: "text-emerald-400" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Share2, { size: 13, className: "text-cyan-400" }),
9579                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden sm:inline text-xs font-bold font-mono", children: copiedShare ? t("gameCenter.copiedToClipboard", "COPIED!") : t("gameCenter.shareGame", "SHARE") })
9580                ]
9581              }
9582            )
9583          ] }),
9584          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-center", children: [
9585            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-3 py-1 rounded-full bg-slate-900 border border-cyan-500/40 text-cyan-300 font-bold shadow-[0_0_10px_rgba(6,182,212,0.2)]", children: [
9586              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `inline-block w-2 h-2 rounded-full ${isPlaying ? "bg-emerald-400 animate-pulse" : "bg-amber-400"}` }),
9587              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "line-clamp-1 max-w-[160px] sm:max-w-xs", children: selectedSong.title })
9588            ] }),
9589            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-amber-400 font-mono font-bold tracking-widest hidden sm:block", children: [
9590              formatArcadeDuration(currentTime),
9591              " / ",
9592              formatArcadeDuration(selectedSong.duration)
9593            ] })
9594          ] }),
9595          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
9596            /* @__PURE__ */ jsxRuntimeExports.jsxs(
9597              "button",
9598              {
9599                onClick: () => {
9600                  if (isPlaying) {
9601                    handleTogglePlay(false, true);
9602                  } else {
9603                    handleTogglePlay(true, false);
9604                  }
9605                },
9606                className: `flex items-center gap-1 px-3 py-1 rounded-lg font-black transition-all active:scale-95 ${isPlaying ? "bg-cyan-500 hover:bg-cyan-400 text-slate-950 shadow-[0_0_15px_rgba(6,182,212,0.5)]" : "bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-[0_0_15px_rgba(245,158,11,0.5)]"}`,
9607                title: isPlaying ? "Pause [Space / Esc]" : "Play [Space]",
9608                children: [
9609                  isPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 13 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 13 }),
9610                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden sm:inline", children: isPlaying ? "PAUSE" : "PLAY" })
9611                ]
9612              }
9613            ),
9614            /* @__PURE__ */ jsxRuntimeExports.jsx(
9615              "button",
9616              {
9617                onClick: handleToggleFullscreen,
9618                className: "p-1 rounded-lg bg-slate-900 border border-slate-700 hover:border-slate-500 text-slate-300 hover:text-white transition-all",
9619                title: isFullscreen ? "Exit Fullscreen" : "Fullscreen",
9620                children: isFullscreen ? /* @__PURE__ */ jsxRuntimeExports.jsx(Minimize2, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Maximize2, { size: 14 })
9621              }
9622            )
9623          ] })
9624        ] }),
9625        isLoadingBeatmap && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 z-40 bg-black/80 flex flex-col items-center justify-center gap-3 pointer-events-none", children: [
9626          /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-10 h-10 text-cyan-400 animate-spin" }),
9627          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-black tracking-widest text-cyan-300 uppercase animate-pulse", children: "TUNING RHYTHM BEATMAP..." })
9628        ] }),
9629        audioBlocked && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-14 left-1/2 -translate-x-1/2 z-40 bg-gradient-to-r from-amber-500 to-pink-500 text-slate-950 px-4 py-2 rounded-xl font-black text-xs tracking-wider shadow-[0_0_25px_rgba(245,158,11,0.8)] flex items-center gap-3 animate-bounce", children: [
9630          /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 16 }),
9631          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "BROWSER AUDIO MUTED: CLICK TO START SOUND" }),
9632          /* @__PURE__ */ jsxRuntimeExports.jsx(
9633            "button",
9634            {
9635              onClick: handleResume,
9636              className: "px-2.5 py-1 rounded bg-black text-white text-[10px] font-mono hover:bg-zinc-900 transition-colors uppercase cursor-pointer",
9637              children: "ENABLE AUDIO"
9638            }
9639          )
9640        ] }),
9641        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 w-full relative overflow-hidden bg-[#030712]", children: [
9642          module.id === "piano-roll" && /* @__PURE__ */ jsxRuntimeExports.jsx(
9643            PianoRollGame,
9644            {
9645              data: beatmapData,
9646              melodyData: melodyBeatmap,
9647              currentTime,
9648              isPlaying: isPlaying && !isPaused,
9649              hiddenTracks: /* @__PURE__ */ new Set(),
9650              colorMap: derivedColorMap,
9651              processedInstruments: selectedSong.instruments || [],
9652              uniqueNames,
9653              onPlayPause: (force) => handleTogglePlay(force),
9654              onSeek: handleSeek,
9655              toggleGameMode: () => handleTogglePlay(false),
9656              duration: selectedSong.duration,
9657              tempo: selectedSong.bpm,
9658              timeSignature: normalizedTimeSignature,
9659              trackId: selectedSong.id,
9660              userId: currentUser == null ? void 0 : currentUser.id,
9661              userName: currentUser == null ? void 0 : currentUser.username,
9662              track: selectedSong.rawTrack || {},
9663              isAudioLoaded: isAudioReady,
9664              isLoadingAudio: !isAudioReady
9665            }
9666          ),
9667          module.id === "taiko" && /* @__PURE__ */ jsxRuntimeExports.jsx(
9668            TaikoGame,
9669            {
9670              data: beatmapData,
9671              melodyData: melodyBeatmap,
9672              processedInstruments: selectedSong.instruments || [],
9673              midiMetadata: {
9674                tempo: selectedSong.bpm,
9675                timeSignature: (selectedSong.timeSignature || [4, 4]).join("/")
9676              },
9677              currentTime,
9678              isPlaying: isPlaying && !isPaused,
9679              duration: selectedSong.duration,
9680              tempo: selectedSong.bpm,
9681              timeSignature: normalizedTimeSignature,
9682              trackId: selectedSong.id,
9683              userId: currentUser == null ? void 0 : currentUser.id,
9684              userName: currentUser == null ? void 0 : currentUser.username,
9685              onPlayPause: (force) => handleTogglePlay(force),
9686              onSeek: handleSeek,
9687              theme: "dark",
9688              isAudioLoaded: isAudioReady,
9689              isLoadingAudio: !isAudioReady
9690            }
9691          ),
9692          module.id === "meow-choir" && /* @__PURE__ */ jsxRuntimeExports.jsx(
9693            MeowChoirGame,
9694            {
9695              restartKey,
9696              data: beatmapData,
9697              melodyData: melodyBeatmap,
9698              processedInstruments: selectedInstruments,
9699              midiMetadata: meowMidiMetadata,
9700              currentTime,
9701              isPlaying: isPlaying && !isPaused,
9702              duration: selectedSong.duration,
9703              tempo: selectedSong.bpm,
9704              timeSignature: normalizedTimeSignature,
9705              trackId: selectedSong.id,
9706              userId: currentUser == null ? void 0 : currentUser.id,
9707              userName: currentUser == null ? void 0 : currentUser.username,
9708              onPlayPause: (force) => handleTogglePlay(force, false),
9709              onSeek: handleSeek,
9710              isAudioLoaded: isAudioReady,
9711              isLoadingAudio: !isAudioReady,
9712              difficulty: (gameSettings == null ? void 0 : gameSettings.difficulty) || "playful"
9713            }
9714          ),
9715          module.id === "rhythm-test" && /* @__PURE__ */ jsxRuntimeExports.jsx(
9716            RhythmTestGame,
9717            {
9718              data: beatmapData,
9719              melodyData: melodyBeatmap,
9720              processedInstruments: selectedSong.instruments || [],
9721              midiMetadata: {
9722                title: selectedSong.title,
9723                artist: selectedSong.artist,
9724                tempo: selectedSong.bpm,
9725                timeSignature: (selectedSong.timeSignature || [4, 4]).join("/")
9726              },
9727              currentTime,
9728              isPlaying: isPlaying && !isPaused,
9729              duration: selectedSong.duration,
9730              tempo: selectedSong.bpm,
9731              timeSignature: normalizedTimeSignature,
9732              trackId: selectedSong.id,
9733              userId: currentUser == null ? void 0 : currentUser.id,
9734              userName: currentUser == null ? void 0 : currentUser.username,
9735              onPlayPause: (force) => handleTogglePlay(force),
9736              onSeek: handleSeek,
9737              isAudioLoaded: isAudioReady,
9738              isLoadingAudio: !isAudioReady,
9739              onBackToSongSelect,
9740              onBackToCabinetSelect
9741            }
9742          )
9743        ] }),
9744        isPaused && /* @__PURE__ */ jsxRuntimeExports.jsx(
9745          "div",
9746          {
9747            "data-arcade-pause-modal": "true",
9748            className: "absolute inset-0 z-50 bg-black/85 backdrop-blur-md flex items-center justify-center p-4 animate-in fade-in duration-150",
9749            onClick: (e) => {
9750              if (e.target === e.currentTarget) handleResume();
9751            },
9752            children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-md w-full bg-slate-950 border-4 border-cyan-400 rounded-3xl p-6 shadow-[0_0_60px_rgba(6,182,212,0.5)] flex flex-col items-center gap-5 text-center font-mono select-none", children: [
9753              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
9754                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-black text-cyan-400 tracking-[0.3em] uppercase", children: "STAGE INTERRUPTED" }),
9755                /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-4xl font-black italic tracking-widest text-transparent bg-clip-text bg-gradient-to-r from-yellow-300 via-pink-400 to-cyan-400 drop-shadow-[0_0_20px_rgba(236,72,153,0.8)] animate-pulse", children: "PAUSED" }),
9756                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-bold truncate max-w-xs mt-1", children: selectedSong.title })
9757              ] }),
9758              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full flex flex-col gap-3", children: [
9759                /* @__PURE__ */ jsxRuntimeExports.jsxs(
9760                  "button",
9761                  {
9762                    onClick: handleResume,
9763                    className: "w-full py-3 px-4 rounded-xl bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-400 hover:to-blue-500 text-slate-950 font-black text-sm tracking-widest uppercase shadow-[0_0_25px_rgba(6,182,212,0.6)] active:scale-95 transition-all flex items-center justify-center gap-2 cursor-pointer",
9764                    children: [
9765                      /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 16, className: "fill-current" }),
9766                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "RESUME PLAY [ESC / SPACE]" })
9767                    ]
9768                  }
9769                ),
9770                /* @__PURE__ */ jsxRuntimeExports.jsxs(
9771                  "button",
9772                  {
9773                    onClick: handleRestart,
9774                    className: "w-full py-2.5 px-4 rounded-xl bg-slate-900 border border-amber-500/50 hover:border-amber-400 text-amber-300 hover:text-white font-black text-xs tracking-wider uppercase transition-all active:scale-95 flex items-center justify-center gap-2 cursor-pointer",
9775                    children: [
9776                      /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 14 }),
9777                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "RESTART STAGE [R]" })
9778                    ]
9779                  }
9780                ),
9781                /* @__PURE__ */ jsxRuntimeExports.jsxs(
9782                  "button",
9783                  {
9784                    onClick: handleShareGame,
9785                    className: `w-full py-2.5 px-4 rounded-xl border font-black text-xs tracking-wider uppercase transition-all active:scale-95 flex items-center justify-center gap-2 cursor-pointer ${copiedShare ? "bg-emerald-500/20 border-emerald-400 text-emerald-300 shadow-[0_0_15px_rgba(16,185,129,0.4)]" : "bg-slate-900 border-cyan-500/50 hover:border-cyan-400 text-cyan-300 hover:text-white"}`,
9786                    children: [
9787                      copiedShare ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 14, className: "text-emerald-400" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Share2, { size: 14, className: "text-cyan-400" }),
9788                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: copiedShare ? t("gameCenter.copiedToClipboard", "LINK COPIED TO CLIPBOARD!") : t("gameCenter.shareGame", "SHARE GAME LINK") })
9789                    ]
9790                  }
9791                ),
9792                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2 mt-1", children: [
9793                  /* @__PURE__ */ jsxRuntimeExports.jsxs(
9794                    "button",
9795                    {
9796                      onClick: () => {
9797                        if (audioRef.current) audioRef.current.pause();
9798                        arcadeAudio.playSelect();
9799                        onBackToCabinetSelect();
9800                      },
9801                      className: "py-2.5 px-3 rounded-xl bg-slate-900 border border-slate-700 hover:border-cyan-400 text-slate-300 hover:text-white font-bold text-xs tracking-wider transition-all active:scale-95 flex items-center justify-center gap-1.5 cursor-pointer",
9802                      children: [
9803                        /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 13 }),
9804                        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "CABINETS" })
9805                      ]
9806                    }
9807                  ),
9808                  /* @__PURE__ */ jsxRuntimeExports.jsxs(
9809                    "button",
9810                    {
9811                      onClick: () => {
9812                        if (audioRef.current) audioRef.current.pause();
9813                        arcadeAudio.playSelect();
9814                        onBackToSongSelect();
9815                      },
9816                      className: "py-2.5 px-3 rounded-xl bg-slate-900 border border-slate-700 hover:border-pink-400 text-slate-300 hover:text-white font-bold text-xs tracking-wider transition-all active:scale-95 flex items-center justify-center gap-1.5 cursor-pointer",
9817                      children: [
9818                        /* @__PURE__ */ jsxRuntimeExports.jsx(Disc, { size: 13, className: "text-pink-400" }),
9819                        /* @__P
9819URE__ */ jsxRuntimeExports.jsx("span", { children: "SONG WHEEL" })
9820                      ]
9821                    }
9822                  )
9823                ] })
9824              ] }),
9825              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase tracking-widest pt-1 border-t border-zinc-900 w-full", children: "ESC / SPACE TO RESUME • R TO RESTART" })
9826            ] })
9827          }
9828        )
9829      ]
9830    }
9831  );
9832};
9833function HamieGameCenterPage({
9834  currentUser,
9835  onNavigate
9836}) {
9837  const [stage, setStage] = reactExports.useState("SONG");
9838  const [selectedSong, setSelectedSong] = reactExports.useState(null);
9839  const [selectedModule, setSelectedModule] = reactExports.useState(GAME_MODULES[0]);
9840  const [gameSettings, setGameSettings] = reactExports.useState({});
9841  const [enableScanlines, setEnableScanlines] = reactExports.useState(true);
9842  const [themeMode, setThemeMode] = reactExports.useState("random");
9843  const [currentThemeId, setCurrentThemeId] = reactExports.useState(() => getRandomTheme());
9844  const activeTheme = ARCADE_THEMES[currentThemeId] || ARCADE_THEMES["5thmix"];
9845  const handleCycleTheme = reactExports.useCallback(() => {
9846    arcadeAudio.playWheelTick();
9847    if (themeMode === "random") {
9848      setThemeMode("5thmix");
9849      setCurrentThemeId("5thmix");
9850    } else if (themeMode === "5thmix") {
9851      setThemeMode("max2");
9852      setCurrentThemeId("max2");
9853    } else if (themeMode === "max2") {
9854      setThemeMode("sunset");
9855      setCurrentThemeId("sunset");
9856    } else {
9857      setThemeMode("random");
9858      setCurrentThemeId(getRandomTheme());
9859    }
9860  }, [themeMode]);
9861  const handleSelectTheme = reactExports.useCallback((mode) => {
9862    arcadeAudio.playWheelTick();
9863    setThemeMode(mode);
9864    if (mode === "random") {
9865      setCurrentThemeId(getRandomTheme());
9866    } else {
9867      setCurrentThemeId(mode);
9868    }
9869  }, []);
9870  reactExports.useEffect(() => {
9871    if (typeof window === "undefined") return;
9872    const params = new URLSearchParams(window.location.search);
9873    const trackId = params.get("track") || params.get("id");
9874    const mode = params.get("mode");
9875    if (trackId) {
9876      api.getTrack(trackId).then((track) => {
9877        var _a, _b, _c;
9878        if (track) {
9879          const durSec = parseDurationSec(track.durationSec || track.duration);
9880          const numId = parseInt(String(track.id));
9881          const group = numId >= 1e7 ? numId % 1e3 : Math.floor(numId / 1e3);
9882          const resolvedPianoRoll = typeof track.pianoRollPath === "string" && track.pianoRollPath ? track.pianoRollPath : track.status === "completed" ? `/storage/tracks/piano_rolls/${group}/${track.id}.json` : void 0;
9883          const resolvedOpus = typeof track.opusPath === "string" && track.opusPath ? track.opusPath : track.status === "completed" ? `/storage/tracks/opus/${group}/${track.id}.opus` : void 0;
9884          const songData = {
9885            source: "site",
9886            id: String(track.id),
9887            title: track.title,
9888            artist: track.artist || void 0,
9889            category: typeof track.category === "string" ? track.category : ((_a = track.category) == null ? void 0 : _a.label) || "General",
9890            bpm: Math.round(((_b = track.midiMetadata) == null ? void 0 : _b.tempo) || 120),
9891            duration: durSec,
9892            timeSignature: ((_c = track.midiMetadata) == null ? void 0 : _c.timeSignature) ? track.midiMetadata.timeSignature.split("/").map(Number) : [4, 4],
9893            instruments: track.instruments || [],
9894            midiPath: track.midiPath || void 0,
9895            pianoRollPath: resolvedPianoRoll,
9896            opusPath: resolvedOpus,
9897            difficultyStars: 3,
9898            rawTrack: track
9899          };
9900          setSelectedSong(songData);
9901          if (resolvedPianoRoll) {
9902            fetch(resolvedPianoRoll).then((res) => res.json()).then((data) => {
9903              if (Array.isArray(data)) {
9904                setSelectedSong((prev) => prev ? { ...prev, pianoRollData: data } : null);
9905              }
9906            }).catch(() => {
9907            });
9908          }
9909          if (mode) {
9910            const targetModule = GAME_MODULES.find((m) => m.id === mode);
9911            if (targetModule) {
9912              setSelectedModule(targetModule);
9913              setStage("ARENA");
9914              return;
9915            }
9916          }
9917          setStage("CABINET");
9918        }
9919      }).catch(() => {
9920      });
9921    }
9922  }, []);
9923  const handleLaunchGame = (module, settings) => {
9924    setSelectedModule(module);
9925    setGameSettings(settings);
9926    setStage("ARENA");
9927  };
9928  reactExports.useEffect(() => {
9929    if (typeof window === "undefined") return;
9930    try {
9931      if (stage === "ARENA" && selectedSong) {
9932        window.history.replaceState(null, "", `/games?track=${selectedSong.id}&mode=${selectedModule.id}`);
9933      } else if (stage === "CABINET" && selectedSong) {
9934        window.history.replaceState(null, "", `/games?track=${selectedSong.id}`);
9935      } else if (stage === "SONG") {
9936        window.history.replaceState(null, "", "/games");
9937      }
9938    } catch {
9939    }
9940  }, [stage, selectedSong == null ? void 0 : selectedSong.id, selectedModule.id]);
9941  const handleQuickSwitchModule = (module) => {
9942    setSelectedModule(module);
9943    setGameSettings(module.defaultSettings || {});
9944  };
9945  reactExports.useEffect(() => {
9946    const handleGlobalKeyDown = (e) => {
9947      var _a;
9948      const targetTag = (_a = e.target) == null ? void 0 : _a.tagName;
9949      if (targetTag === "INPUT" || targetTag === "TEXTAREA") return;
9950      if (e.key === "Escape") {
9951        if (stage === "CABINET") {
9952          e.preventDefault();
9953          setStage("SONG");
9954        }
9955      } else if (e.key === "1") {
9956        e.preventDefault();
9957        setStage("SONG");
9958      } else if (e.key === "2" && selectedSong) {
9959        e.preventDefault();
9960        setStage("CABINET");
9961      } else if (e.key === "3" && selectedSong) {
9962        e.preventDefault();
9963        setStage("ARENA");
9964      }
9965    };
9966    window.addEventListener("keydown", handleGlobalKeyDown);
9967    return () => window.removeEventListener("keydown", handleGlobalKeyDown);
9968  }, [stage, selectedSong]);
9969  reactExports.useEffect(() => {
9970    arcadeAudio.enableBgm();
9971    return () => {
9972      arcadeAudio.stopBgm();
9973    };
9974  }, []);
9975  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-screen max-h-screen w-full bg-[#030712] text-white flex flex-col overflow-hidden selection:bg-cyan-500 selection:text-black", children: [
9976    /* @__PURE__ */ jsxRuntimeExports.jsx(ArcadeBackground, { enableScanlines, theme: activeTheme, isActive: stage !== "ARENA" }),
9977    /* @__PURE__ */ jsxRuntimeExports.jsx(
9978      ArcadeMarqueeHeader,
9979      {
9980        stage,
9981        selectedSong,
9982        selectedModule,
9983        onGoToStage: (targetStage) => {
9984          if (targetStage === "ARENA" && !selectedSong) return;
9985          if (targetStage === "CABINET" && !selectedSong) return;
9986          setStage(targetStage);
9987        },
9988        onSelectModule: handleQuickSwitchModule,
9989        onExit: () => {
9990          arcadeAudio.stopBgm();
9991          onNavigate("/");
9992        },
9993        enableScanlines,
9994        setEnableScanlines,
9995        theme: activeTheme,
9996        themeMode,
9997        onCycleTheme: handleCycleTheme,
9998        onSelectTheme: handleSelectTheme,
9999        onSelectMusic: () => setStage("SONG")
10000      }
10001    ),
10002    /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "relative z-10 flex-1 max-w-7xl w-full mx-auto px-4 py-2 flex flex-col overflow-hidden min-h-0 justify-between", children: [
10003      stage === "SONG" && /* @__PURE__ */ jsxRuntimeExports.jsx(
10004        ArcadeSongSelector,
10005        {
10006          currentUser,
10007          selectedSong,
10008          onSelectSong: (song) => setSelectedSong(song),
10009          onProceedToCabinet: () => setStage("CABINET"),
10010          theme: activeTheme,
10011          onCycleTheme: handleCycleTheme
10012        }
10013      ),
10014      stage === "CABINET" && selectedSong && /* @__PURE__ */ jsxRuntimeExports.jsx(
10015        GameModeSelector,
10016        {
10017          selectedSong,
10018          onBackToSongSelect: () => setStage("SONG"),
10019          onLaunchGame: handleLaunchGame,
10020          theme: activeTheme
10021        }
10022      ),
10023      stage === "ARENA" && selectedSong && /* @__PURE__ */ jsxRuntimeExports.jsx(
10024        GameArena,
10025        {
10026          module: selectedModule,
10027          selectedSong,
10028          gameSettings,
10029          currentUser,
10030          onBackToCabinetSelect: () => setStage("CABINET"),
10031          onBackToSongSelect: () => setStage("SONG")
10032        }
10033      )
10034    ] })
10035  ] });
10036}
10037export {
10038  HamieGameCenterPage as default
10039};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.