From 4ad6ca7a87a54315799085bcb15cfb791115d0a2 Mon Sep 17 00:00:00 2001 From: Pramod-Munoli Date: Thu, 30 Oct 2025 22:48:26 +0530 Subject: [PATCH] feat: enhance piano tiles gameplay with modern UI and features --- projects/pianotiles/index.html | 550 ++++++---------------------- projects/pianotiles/script.js | 578 ++++++++++++++++++++++++++++++ projects/pianotiles/styles.css | 633 +++++++++++++++++++++++++++++++++ 3 files changed, 1311 insertions(+), 450 deletions(-) create mode 100644 projects/pianotiles/script.js create mode 100644 projects/pianotiles/styles.css diff --git a/projects/pianotiles/index.html b/projects/pianotiles/index.html index a5acbe6..34a6254 100644 --- a/projects/pianotiles/index.html +++ b/projects/pianotiles/index.html @@ -2,469 +2,119 @@ - + Piano Tiles - + + - -
-
-
Score: 0
- -
- -
-
-
-
-
-
- - - - -
- - - \ No newline at end of file diff --git a/projects/pianotiles/script.js b/projects/pianotiles/script.js new file mode 100644 index 0000000..0918f13 --- /dev/null +++ b/projects/pianotiles/script.js @@ -0,0 +1,578 @@ + + // Game variables + let score = 0; + let highScore = 0; + let gameActive = false; + let gamePaused = false; + let gameMode = 'classic'; + let tileSpeed = 0.4; // Very slow initial speed + let speedIncrement = 0.005; // Much smaller speed increase + let tileRows = []; + let lastTileTime = 0; + let tileInterval = 3000; // Very long interval between tiles (3 seconds) + let animationId; + let audioContext; + let currentTheme = 'light'; + let combo = 0; + let lastComboTime = 0; + + // Statistics tracking + let gameStats = { + totalGames: 0, + allTimeHigh: 0, + totalScore: 0, + totalTiles: 0, + classicHigh: 0, + arcadeHigh: 0, + scoreHistory: [] + }; + + // DOM elements + const gameArea = document.getElementById('game-area'); + const scoreDisplay = document.getElementById('score-display'); + const highScoreDisplay = document.getElementById('high-score'); + const speedFill = document.getElementById('speed-fill'); + const comboDisplay = document.getElementById('combo-display'); + const pauseButton = document.getElementById('pause-button'); + const startScreen = document.getElementById('start-screen'); + const gameOverScreen = document.getElementById('game-over-screen'); + const pauseScreen = document.getElementById('pause-screen'); + const modeSelection = document.getElementById('mode-selection'); + const statsScreen = document.getElementById('stats-screen'); + const finalScoreDisplay = document.getElementById('final-score'); + const highestScoreDisplay = document.getElementById('highest-score-display'); + const newHighScoreDisplay = document.getElementById('new-high-score'); + const modeSelectButton = document.getElementById('mode-select-button'); + const backButton = document.getElementById('back-button'); + const restartButton = document.getElementById('restart-button'); + const menuButton = document.getElementById('menu-button'); + const menuButtonPause = document.getElementById('menu-button-pause'); + const resumeButton = document.getElementById('resume-button'); + const themeToggle = document.getElementById('theme-toggle'); + const statsButton = document.getElementById('stats-button'); + const backFromStats = document.getElementById('back-from-stats'); + const clearDataButton = document.getElementById('clear-data-button'); + + // Load saved data from localStorage + function loadGameData() { + // Load game statistics + const savedStats = localStorage.getItem('pianoTilesStats'); + if (savedStats) { + gameStats = JSON.parse(savedStats); + updateStatsDisplay(); + } + + // Load theme preference + const savedTheme = localStorage.getItem('pianoTilesTheme'); + if (savedTheme) { + currentTheme = savedTheme; + document.documentElement.setAttribute('data-theme', currentTheme); + updateThemeToggle(); + } + + // Update high score display + highScore = gameStats.allTimeHigh; + highScoreDisplay.textContent = highScore; + } + + // Save game data to localStorage + function saveGameData() { + localStorage.setItem('pianoTilesStats', JSON.stringify(gameStats)); + localStorage.setItem('pianoTilesTheme', currentTheme); + } + + // Save current game score + function saveScore() { + // Update statistics + gameStats.totalGames++; + gameStats.totalScore += score; + gameStats.totalTiles += score; + + // Update mode-specific high scores + if (gameMode === 'classic' && score > gameStats.classicHigh) { + gameStats.classicHigh = score; + } else if (gameMode === 'arcade' && score > gameStats.arcadeHigh) { + gameStats.arcadeHigh = score; + } + + // Update all-time high score + if (score > gameStats.allTimeHigh) { + gameStats.allTimeHigh = score; + highScore = score; + highScoreDisplay.textContent = highScore; + } + + // Add to score history (keep last 10 scores) + const scoreEntry = { + score: score, + mode: gameMode, + date: new Date().toISOString() + }; + gameStats.scoreHistory.unshift(scoreEntry); + if (gameStats.scoreHistory.length > 10) { + gameStats.scoreHistory = gameStats.scoreHistory.slice(0, 10); + } + + // Save to localStorage + saveGameData(); + updateStatsDisplay(); + } + + // Update statistics display + function updateStatsDisplay() { + document.getElementById('total-games').textContent = gameStats.totalGames; + document.getElementById('all-time-high').textContent = gameStats.allTimeHigh; + + // Calculate average score + const averageScore = gameStats.totalGames > 0 + ? Math.round(gameStats.totalScore / gameStats.totalGames) + : 0; + document.getElementById('average-score').textContent = averageScore; + + document.getElementById('total-tiles').textContent = gameStats.totalTiles; + + // Determine best mode + let bestMode = '-'; + if (gameStats.classicHigh > 0 || gameStats.arcadeHigh > 0) { + if (gameStats.classicHigh >= gameStats.arcadeHigh) { + bestMode = `Classic (${gameStats.classicHigh})`; + } else { + bestMode = `Arcade (${gameStats.arcadeHigh})`; + } + } + document.getElementById('best-mode').textContent = bestMode; + + // Update score history + const historyList = document.getElementById('history-list'); + historyList.innerHTML = ''; + + if (gameStats.scoreHistory.length === 0) { + historyList.innerHTML = '
No scores yet
'; + } else { + gameStats.scoreHistory.forEach(entry => { + const item = document.createElement('div'); + item.className = 'history-item'; + + const date = new Date(entry.date); + const timeString = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + + item.innerHTML = ` + ${entry.score} pts - ${entry.mode.charAt(0).toUpperCase() + entry.mode.slice(1)} + ${timeString} + `; + historyList.appendChild(item); + }); + } + } + + // Initialize audio context + function initAudio() { + if (!audioContext) { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + } + } + + // Play a piano tone + function playTone(frequency = 440, duration = 200) { + if (!audioContext) return; + + const oscillator = audioContext.createOscillator(); + const gainNode = audioContext.createGain(); + + oscillator.connect(gainNode); + gainNode.connect(audioContext.destination); + + oscillator.frequency.value = frequency; + oscillator.type = 'sine'; + + gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); + gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration / 1000); + + oscillator.start(audioContext.currentTime); + oscillator.stop(audioContext.currentTime + duration / 1000); + } + + // Create enhanced particle effect + function createParticle(x, y) { + // Create multiple particles for better effect + for (let i = 0; i < 6; i++) { + const particle = document.createElement('div'); + particle.className = 'particle'; + particle.style.left = `${x}px`; + particle.style.top = `${y}px`; + + const angle = (Math.PI * 2 * i) / 6; + const velocity = 50 + Math.random() * 50; + const tx = Math.cos(angle) * velocity; + const ty = Math.sin(angle) * velocity; + + particle.style.setProperty('--tx', `${tx}px`); + particle.style.setProperty('--ty', `${ty}px`); + particle.style.animation = 'particle-animation 0.6s ease-out forwards'; + + document.body.appendChild(particle); + + setTimeout(() => { + document.body.removeChild(particle); + }, 600); + } + } + + // Create score popup animation + function createScorePopup(x, y) { + const popup = document.createElement('div'); + popup.className = 'score-popup'; + popup.textContent = '+1'; + popup.style.left = `${x}px`; + popup.style.top = `${y}px`; + + document.body.appendChild(popup); + + setTimeout(() => { + document.body.removeChild(popup); + }, 1000); + } + + // Show combo display + function showCombo() { + const now = Date.now(); + if (now - lastComboTime < 2000) { + combo++; + if (combo > 1) { + comboDisplay.textContent = `${combo}x COMBO!`; + comboDisplay.classList.remove('show'); + void comboDisplay.offsetWidth; // Trigger reflow + comboDisplay.classList.add('show'); + } + } else { + combo = 1; + } + lastComboTime = now; + } + + // Update speed indicator + function updateSpeedIndicator() { + const maxSpeed = 2.0; + const percentage = Math.min((tileSpeed / maxSpeed) * 100, 100); + speedFill.style.width = `${percentage}%`; + } + + // Create a new tile row + function createTileRow() { + const row = document.createElement('div'); + row.className = 'tile-row'; + row.style.top = '-25vh'; + + // Random position for the active tile (0-3) + const activeTileIndex = Math.floor(Math.random() * 4); + + for (let i = 0; i < 4; i++) { + const tile = document.createElement('div'); + tile.className = 'tile'; + + if (i === activeTileIndex) { + tile.classList.add('active'); + + // Add both click and touch event listeners for better responsiveness + const handleTileClick = (e) => { + e.preventDefault(); + if (!gameActive || gamePaused || tile.classList.contains('clicked')) return; + + tile.classList.add('clicked'); + score++; + scoreDisplay.textContent = score; + + // Show combo + showCombo(); + + // Create particle effect at touch position + const rect = tile.getBoundingClientRect(); + const x = e.clientX || (e.touches && e.touches[0].clientX) || rect.left + rect.width / 2; + const y = e.clientY || (e.touches && e.touches[0].clientY) || rect.top + rect.height / 2; + createParticle(x, y); + createScorePopup(x, y); + + // Play a tone based on the tile position + const baseFrequency = 261.63; // C4 + const frequency = baseFrequency * Math.pow(2, (3 - i) / 12); + playTone(frequency, 150); + + // Update speed indicator + updateSpeedIndicator(); + + // Increase speed based on game mode (very gradual now) + if (gameMode === 'classic' && score % 50 === 0) { // Every 50 points + tileSpeed += speedIncrement; + } else if (gameMode === 'arcade' && score % 40 === 0) { // Every 40 points + tileSpeed += speedIncrement * 1.5; + } + }; + + // Multiple event listeners for better touch responsiveness + tile.addEventListener('click', handleTileClick); + tile.addEventListener('touchstart', handleTileClick, { passive: false }); + tile.addEventListener('mousedown', handleTileClick); + } + + row.appendChild(tile); + } + + gameArea.appendChild(row); + tileRows.push({ + element: row, + position: -25, + clicked: false + }); + } + + // Update tile positions + function updateTiles(timestamp) { + if (!gameActive || gamePaused) return; + + // Create new tiles at intervals based on game mode + let currentTileInterval = tileInterval; + + if (gameMode === 'arcade') { + currentTileInterval = 2500; + } + + if (timestamp - lastTileTime > currentTileInterval) { + createTileRow(); + lastTileTime = timestamp; + + // Very gradual decrease in interval for arcade mode + if (gameMode === 'arcade' && currentTileInterval > 2000) { + tileInterval -= 30; + } + } + + // Update existing tiles + for (let i = tileRows.length - 1; i >= 0; i--) { + const row = tileRows[i]; + row.position += tileSpeed; + row.element.style.top = `${row.position}vh`; + + // Check if any active tile was missed + if (row.position > 0 && !row.clicked) { + const activeTile = row.element.querySelector('.tile.active:not(.clicked)'); + if (activeTile) { + activeTile.classList.add('missed'); + combo = 0; // Reset combo on miss + endGame(); + return; + } + } + + // Remove tiles that are off screen + if (row.position > 100) { + gameArea.removeChild(row.element); + tileRows.splice(i, 1); + } + } + + animationId = requestAnimationFrame(updateTiles); + } + + // Start the game + function startGame(mode = 'classic') { + initAudio(); + gameMode = mode; + score = 0; + combo = 0; + tileSpeed = gameMode === 'arcade' ? 0.5 : 0.4; // Very slow initial speeds + tileInterval = gameMode === 'arcade' ? 2500 : 3000; // Very long initial intervals + lastTileTime = 0; + scoreDisplay.textContent = score; + gameActive = true; + gamePaused = false; + + // Reset speed indicator + updateSpeedIndicator(); + + // Clear any existing tiles + tileRows.forEach(row => { + gameArea.removeChild(row.element); + }); + tileRows = []; + + // Hide screens + startScreen.classList.add('hidden'); + gameOverScreen.classList.add('hidden'); + modeSelection.classList.add('hidden'); + statsScreen.classList.add('hidden'); + pauseScreen.classList.add('hidden'); + newHighScoreDisplay.classList.add('hidden'); + + // Hide stats button during gameplay + statsButton.classList.add('hidden'); + + // Show pause button + pauseButton.classList.remove('hidden'); + + // Start the game loop + animationId = requestAnimationFrame(updateTiles); + } + + // End the game + function endGame() { + gameActive = false; + gamePaused = false; + cancelAnimationFrame(animationId); + + // Hide pause button + pauseButton.classList.add('hidden'); + + // Save the score + saveScore(); + + // Check for new high score + if (score > highScore) { + newHighScoreDisplay.classList.remove('hidden'); + } else { + newHighScoreDisplay.classList.add('hidden'); + } + + // Show game over screen with highest score + finalScoreDisplay.textContent = `Final Score: ${score}`; + highestScoreDisplay.textContent = `Highest Score: ${gameStats.allTimeHigh}`; + gameOverScreen.classList.remove('hidden'); + + // Play game over sound + playTone(200, 500); + } + + // Pause the game + function pauseGame() { + if (!gameActive || gamePaused) return; + + gamePaused = true; + pauseScreen.classList.remove('hidden'); + } + + // Resume the game + function resumeGame() { + if (!gameActive || !gamePaused) return; + + gamePaused = false; + pauseScreen.classList.add('hidden'); + animationId = requestAnimationFrame(updateTiles); + } + + // Toggle theme + function toggleTheme() { + currentTheme = currentTheme === 'light' ? 'dark' : 'light'; + document.documentElement.setAttribute('data-theme', currentTheme); + updateThemeToggle(); + saveGameData(); + } + + // Update theme toggle button + function updateThemeToggle() { + const icon = currentTheme === 'light' ? 'πŸŒ™' : 'β˜€οΈ'; + if (themeToggle) themeToggle.textContent = icon; + } + + // Clear all data + function clearAllData() { + if (confirm('Are you sure you want to clear all game data? This cannot be undone.')) { + gameStats = { + totalGames: 0, + allTimeHigh: 0, + totalScore: 0, + totalTiles: 0, + classicHigh: 0, + arcadeHigh: 0, + scoreHistory: [] + }; + highScore = 0; + highScoreDisplay.textContent = highScore; + saveGameData(); + updateStatsDisplay(); + alert('All data has been cleared.'); + } + } + + // Show main menu + function showMainMenu() { + // Show stats button only on main menu + statsButton.classList.remove('hidden'); + + // Hide other screens + startScreen.classList.remove('hidden'); + modeSelection.classList.add('hidden'); + statsScreen.classList.add('hidden'); + gameOverScreen.classList.add('hidden'); + pauseScreen.classList.add('hidden'); + } + + // Event listeners with better touch handling + modeSelectButton.addEventListener('click', () => { + startScreen.classList.add('hidden'); + modeSelection.classList.remove('hidden'); + }); + + backButton.addEventListener('click', () => { + modeSelection.classList.add('hidden'); + startScreen.classList.remove('hidden'); + }); + + restartButton.addEventListener('click', () => startGame(gameMode)); + menuButton.addEventListener('click', () => { + gameOverScreen.classList.add('hidden'); + showMainMenu(); + }); + + menuButtonPause.addEventListener('click', () => { + gameActive = false; + gamePaused = false; + cancelAnimationFrame(animationId); + pauseButton.classList.add('hidden'); + pauseScreen.classList.add('hidden'); + showMainMenu(); + }); + + pauseButton.addEventListener('click', pauseGame); + resumeButton.addEventListener('click', resumeGame); + + themeToggle.addEventListener('click', toggleTheme); + + statsButton.addEventListener('click', () => { + startScreen.classList.add('hidden'); + statsScreen.classList.remove('hidden'); + updateStatsDisplay(); + }); + + backFromStats.addEventListener('click', () => { + statsScreen.classList.add('hidden'); + startScreen.classList.remove('hidden'); + }); + + clearDataButton.addEventListener('click', clearAllData); + + // Game mode buttons + document.querySelectorAll('.mode-button[data-mode]').forEach(button => { + button.addEventListener('click', () => { + const mode = button.getAttribute('data-mode'); + startGame(mode); + }); + }); + + // Prevent scrolling on mobile + document.addEventListener('touchmove', function(e) { + if (gameActive) { + e.preventDefault(); + } + }, { passive: false }); + + // Prevent zooming on mobile + document.addEventListener('gesturestart', function(e) { + e.preventDefault(); + }); + + // Prevent context menu on long press + document.addEventListener('contextmenu', function(e) { + e.preventDefault(); + }); + + // Load game data on start + loadGameData(); diff --git a/projects/pianotiles/styles.css b/projects/pianotiles/styles.css new file mode 100644 index 0000000..0e26991 --- /dev/null +++ b/projects/pianotiles/styles.css @@ -0,0 +1,633 @@ + + :root { + --bg-color: #f5f5f5; + --tile-color: #333; + --tile-active-color: #4CAF50; + --text-color: #333; + --game-bg: #fff; + --button-bg: #4CAF50; + --button-text: white; + --score-bg: rgba(255, 255, 255, 0.9); + --game-over-bg: rgba(0, 0, 0, 0.8); + --primary-color: #2196F3; + --accent-color: #FFC107; + } + + [data-theme="dark"] { + --bg-color: #121212; + --tile-color: #e0e0e0; + --tile-active-color: #8BC34A; + --text-color: #f5f5f5; + --game-bg: #1e1e1e; + --button-bg: #8BC34A; + --button-text: #121212; + --score-bg: rgba(30, 30, 30, 0.9); + --game-over-bg: rgba(0, 0, 0, 0.9); + } + + * { + margin: 0; + padding: 0; + box-sizing: border-box; + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; + } + + body { + font-family: 'Arial', sans-serif; + background-color: var(--bg-color); + color: var(--text-color); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + transition: background-color 0.3s, color 0.3s; + overflow: hidden; + position: relative; + touch-action: manipulation; + } + + header { + position: absolute; + top: 0; + width: 100%; + padding: 1rem; + display: flex; + justify-content: center; + align-items: center; + z-index: 10; + } + + .stats-button { + background: var(--score-bg); + border: none; + cursor: pointer; + font-size: 1.5rem; + color: var(--text-color); + padding: 0.5rem; + transition: transform 0.2s, opacity 0.2s; + min-width: 44px; + min-height: 44px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + position: absolute; + top: 1rem; + left: 1rem; + z-index: 100; + } + + .stats-button:hover, .stats-button:active { + transform: scale(1.1); + opacity: 0.8; + } + + main { + width: 100%; + max-width: 400px; + height: 100vh; + max-height: 600px; + position: relative; + overflow: hidden; + border-radius: 10px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + } + + #game-container { + width: 100%; + height: 100%; + background-color: var(--game-bg); + position: relative; + overflow: hidden; + } + + #game-area { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + } + + .tile-row { + display: flex; + width: 100%; + height: 25vh; + position: absolute; + } + + .tile { + width: 25%; + height: 100%; + border: 1px solid rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: background-color 0.15s ease, transform 0.1s ease; + position: relative; + overflow: hidden; + -webkit-tap-highlight-color: transparent; + } + + .tile.active { + background-color: var(--tile-color); + transform: scale(1); + box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2); + } + + .tile.active:hover { + background-color: #555; + transform: scale(0.98); + } + + .tile.active:active, .tile.active.clicked { + background-color: var(--tile-active-color); + transform: scale(0.95); + box-shadow: 0 0 30px rgba(76, 175, 80, 0.6); + } + + .tile.missed { + background-color: #F44336; + animation: shake 0.3s ease-in-out; + } + + @keyframes shake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-5px); } + 75% { transform: translateX(5px); } + } + + /* Improved Score Display */ + .game-ui { + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: flex-start; + z-index: 5; + pointer-events: none; + } + + .score-container { + background: linear-gradient(135deg, var(--score-bg), rgba(255, 255, 255, 0.95)); + padding: 12px 20px; + border-radius: 25px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + align-items: flex-start; + min-width: 120px; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); + } + + .score-label { + font-size: 0.8rem; + opacity: 0.7; + margin-bottom: 2px; + text-transform: uppercase; + letter-spacing: 1px; + } + + .score-value { + font-size: 1.8rem; + font-weight: bold; + color: var(--primary-color); + line-height: 1; + } + + .high-score-container { + background: linear-gradient(135deg, var(--accent-color), #FFD54F); + padding: 12px 20px; + border-radius: 25px; + box-shadow: 0 4px 15px rgba(255, 193, 7, 0.3); + display: flex; + flex-direction: column; + align-items: flex-end; + min-width: 120px; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); + } + + .high-score-label { + font-size: 0.8rem; + opacity: 0.9; + margin-bottom: 2px; + text-transform: uppercase; + letter-spacing: 1px; + color: #333; + } + + .high-score-value { + font-size: 1.4rem; + font-weight: bold; + color: #333; + line-height: 1; + } + + /* Combo Display */ + .combo-display { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 3rem; + font-weight: bold; + color: var(--accent-color); + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); + opacity: 0; + pointer-events: none; + z-index: 20; + } + + .combo-display.show { + animation: comboPopup 0.8s ease-out; + } + + @keyframes comboPopup { + 0% { + opacity: 0; + transform: translate(-50%, -50%) scale(0.5); + } + 50% { + opacity: 1; + transform: translate(-50%, -50%) scale(1.2); + } + 100% { + opacity: 0; + transform: translate(-50%, -50%) scale(1); + } + } + + /* Speed Indicator */ + .speed-indicator { + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + background: var(--score-bg); + padding: 8px 16px; + border-radius: 20px; + font-size: 0.9rem; + font-weight: bold; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + gap: 8px; + backdrop-filter: blur(10px); + } + + .speed-bar { + width: 60px; + height: 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + overflow: hidden; + } + + .speed-fill { + height: 100%; + background: linear-gradient(90deg, #4CAF50, #FFC107, #F44336); + width: 10%; + transition: width 0.3s ease; + } + + /* Improved Pause Button - Moved to Bottom Left */ + #pause-button { + position: absolute; + bottom: 20px; + left: 20px; + background: linear-gradient(135deg, #FF5722, #F44336); + border: none; + width: 50px; + height: 50px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 15; + font-size: 1.3rem; + transition: transform 0.2s, box-shadow 0.2s, background 0.2s; + box-shadow: 0 4px 15px rgba(244, 67, 54, 0.3); + color: white; + } + + #pause-button:hover { + transform: scale(1.1); + box-shadow: 0 6px 20px rgba(244, 67, 54, 0.4); + background: linear-gradient(135deg, #F44336, #E91E63); + } + + #pause-button:active { + transform: scale(0.95); + box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3); + } + + /* Enhanced Particles */ + .particle { + position: absolute; + width: 12px; + height: 12px; + background: radial-gradient(circle, var(--tile-active-color), rgba(76, 175, 80, 0.6)); + border-radius: 50%; + pointer-events: none; + z-index: 100; + box-shadow: 0 0 10px rgba(76, 175, 80, 0.8); + } + + @keyframes particle-animation { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 100% { + transform: translate(var(--tx), var(--ty)) scale(0); + opacity: 0; + } + } + + /* Score Animation */ + .score-popup { + position: absolute; + font-size: 1.5rem; + font-weight: bold; + color: var(--tile-active-color); + pointer-events: none; + z-index: 50; + animation: scoreFloat 1s ease-out forwards; + } + + @keyframes scoreFloat { + 0% { + opacity: 1; + transform: translateY(0) scale(1); + } + 100% { + opacity: 0; + transform: translateY(-50px) scale(1.5); + } + } + + #start-screen, #game-over-screen, #mode-selection, #stats-screen, #pause-screen { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: var(--game-over-bg); + color: white; + z-index: 10; + padding: 2rem; + overflow-y: auto; + } + + h1 { + font-size: 2.5rem; + margin-bottom: 1rem; + text-align: center; + } + + #game-over-screen h2 { + font-size: 2rem; + margin-bottom: 1rem; + } + + #final-score { + font-size: 1.5rem; + margin-bottom: 2rem; + } + + #highest-score-display { + font-size: 1.2rem; + margin-bottom: 2rem; + color: #FFC107; + font-weight: bold; + } + + .game-modes { + display: flex; + flex-direction: column; + gap: 1rem; + width: 100%; + max-width: 300px; + margin-bottom: 2rem; + } + + .mode-button { + background-color: var(--button-bg); + color: var(--button-text); + border: none; + padding: 14px 24px; + font-size: 1rem; + border-radius: 30px; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s, background-color 0.2s; + display: flex; + align-items: center; + justify-content: space-between; + min-height: 48px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + position: relative; + overflow: hidden; + } + + .mode-button::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background-color: rgba(255, 255, 255, 0.3); + transform: translate(-50%, -50%); + transition: width 0.3s, height 0.3s; + } + + .mode-button:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); + background-color: var(--button-bg); + } + + .mode-button:active { + transform: translateY(0); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + } + + .mode-button:active::before { + width: 300px; + height: 300px; + } + + .hidden { + display: none !important; + } + + footer { + position: absolute; + bottom: 10px; + text-align: center; + font-size: 0.8rem; + opacity: 0.7; + pointer-events: none; + } + + .stats-container { + background-color: rgba(255, 255, 255, 0.1); + border-radius: 10px; + padding: 1.5rem; + width: 100%; + max-width: 350px; + margin-bottom: 1rem; + } + + .stat-item { + display: flex; + justify-content: space-between; + margin-bottom: 0.8rem; + font-size: 1rem; + } + + .stat-label { + opacity: 0.8; + } + + .stat-value { + font-weight: bold; + } + + .score-history { + background-color: rgba(255, 255, 255, 0.1); + border-radius: 10px; + padding: 1rem; + width: 100%; + max-width: 350px; + max-height: 200px; + overflow-y: auto; + } + + .score-history h3 { + margin-bottom: 0.5rem; + text-align: center; + } + + .history-item { + display: flex; + justify-content: space-between; + padding: 0.5rem 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + font-size: 0.9rem; + } + + .history-item:last-child { + border-bottom: none; + } + + .clear-data-button { + background-color: #F44336; + color: white; + border: none; + padding: 10px 16px; + border-radius: 20px; + cursor: pointer; + font-size: 0.9rem; + margin-top: 1rem; + transition: background-color 0.3s, transform 0.2s; + min-height: 44px; + } + + .clear-data-button:hover { + background-color: #D32F2F; + transform: translateY(-1px); + } + + .clear-data-button:active { + transform: translateY(0); + } + + .theme-toggle { + background: var(--score-bg); + border: none; + cursor: pointer; + font-size: 1.5rem; + color: var(--text-color); + padding: 0.5rem; + transition: transform 0.2s, opacity 0.2s; + min-width: 44px; + min-height: 44px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + margin-top: 1rem; + } + + .theme-toggle:hover, .theme-toggle:active { + transform: scale(1.1); + opacity: 0.8; + } + + @media (max-width: 480px) { + main { + max-width: 100%; + max-height: 100vh; + border-radius: 0; + } + + h1 { + font-size: 2rem; + } + + .game-modes { + max-width: 250px; + } + + header { + padding: 0.5rem; + padding-top: 1rem; + } + + #pause-button { + bottom: 1rem; + left: 1rem; + z-index: 15; + } + + .score-container, .high-score-container { + min-width: 100px; + padding: 10px 15px; + } + + .score-value { + font-size: 1.5rem; + } + + .high-score-value { + font-size: 1.2rem; + } + + .speed-indicator { + bottom: 80px; /* Adjusted to avoid overlap with pause button */ + } + } + + /* Touch feedback improvements */ + @media (hover: none) { + .tile.active:hover { + background-color: var(--tile-color); + transform: scale(1); + } + }