From 3f3208a2cf4fde333ffc872446d22cd9e869b1c7 Mon Sep 17 00:00:00 2001 From: Dasemu Date: Mon, 5 Jan 2026 20:13:48 +0100 Subject: [PATCH] Implement swipe-to-reveal card interaction with gravity effect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace tap/click with swipe-up gesture for role reveal - Add gravity effect: curtain falls back when released - Support both touch (mobile) and mouse (desktop) events - Real-time visual feedback while dragging - Update UI text: "Mantén levantada la cortina para ver tu rol" - Add grab/grabbing cursor for better UX This creates a more secure and immersive reveal experience that prevents accidental spoilers. --- index.html | 67 +++++++++++-- script.js | 209 +++++++++++++++++++++++++++++++++++---- styles.css | 286 ++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 515 insertions(+), 47 deletions(-) diff --git a/index.html b/index.html index 482c305..c1c120b 100644 --- a/index.html +++ b/index.html @@ -8,25 +8,77 @@ + +
+ +
+
+ +

🎭 Juego del Impostor

+

¿Podrás descubrir quién es el impostor?

+
+ + +
+

Creado por Darío Sevilla

+
+
+ + +
+

📖 Reglas del Juego

+
+
+

🎯 Objetivo

+

Los civiles deben identificar a los impostores antes de que termine el tiempo.

+
+ +
+

🎲 Preparación

+

1. Cada jugador recibe una palabra secreta

+

2. Los civiles reciben la misma palabra

+

3. Los impostores reciben una palabra diferente pero relacionada

+
+ +
+

🗣️ Partida

+

1. Por turnos, cada jugador da un sinónimo o descripción de su palabra

+

2. Intenta ser específico pero no revelar tu palabra exacta

+

3. Los impostores deben intentar pasar desapercibidos

+
+ +
+

🗳️ Votación

+

1. Tras el tiempo de juego y deliberación, vota en secreto

+

2. Los más votados son eliminados

+

3. Si todos los impostores son eliminados, ganan los civiles

+

4. Si queda algún impostor, ellos ganan

+
+
+ +
+ -
-

🎭 Juego del Impostor

+
+

⚙️ Configuración

- +
- +
- +
@@ -35,6 +87,7 @@
+
@@ -57,11 +110,11 @@

🔍 Revelación

-

Turno de: Jugador 1
Los demás, no miréis. Desliza para ver.

+

Turno de: Jugador 1
Los demás, no miréis. Mantén levantada la cortina para ver tu rol.

⬆️
-
DESLIZA PARA REVELAR
+
LEVANTA LA CORTINA
diff --git a/script.js b/script.js index d228155..9c941d8 100644 --- a/script.js +++ b/script.js @@ -3,6 +3,7 @@ const MAX_PLAYERS = 10; const MIN_PLAYERS = 3; const POOLS_CACHE_KEY = 'impostorWordPoolsV1'; const POOLS_MANIFEST_URL = 'word-pools/manifest.json'; +const THEME_STORAGE_KEY = 'impostorGameTheme'; // Pools embebidas con palabras de impostores [palabra_civil, palabra_impostor] const EMBEDDED_POOLS = [ @@ -286,7 +287,13 @@ function loadCurrentReveal() { const idx = state.revealOrder[state.currentReveal]; const name = state.playerNames[idx]; document.getElementById('current-player-name').textContent = name; - document.getElementById('curtain-cover').classList.remove('lifted'); + + // Resetear estado de la cortina + curtainState.isRevealed = false; + const coverEl = document.getElementById('curtain-cover'); + coverEl.style.transform = 'translateY(0)'; + coverEl.style.transition = ''; + document.getElementById('next-player-btn').style.display = 'none'; document.getElementById('start-game-btn').style.display = 'none'; } @@ -294,7 +301,12 @@ function loadCurrentReveal() { function liftCurtain() { const cover = document.getElementById('curtain-cover'); if (cover.classList.contains('lifted')) return; + + // Restablecer la transición CSS y usar la clase + cover.style.transition = ''; + cover.style.transform = ''; cover.classList.add('lifted'); + const idx = state.revealOrder[state.currentReveal]; const role = state.roles[idx]; const word = role === 'CIVIL' ? state.civilianWord : state.impostorWord; @@ -309,13 +321,102 @@ function liftCurtain() { function nextReveal() { state.currentReveal++; saveState(); loadCurrentReveal(); } -// swipe support +// Sistema de cortina con GRAVEDAD - La cortina siempre tiende a bajar +// Soporta tanto touch (móvil) como mouse (escritorio) +let curtainState = { isRevealed: false }; + (() => { const curtain = document.getElementById('curtain'); + const cover = document.getElementById('curtain-cover'); let startY = null; - curtain.addEventListener('touchstart', e => { startY = e.touches[0].clientY; }, {passive:true}); - curtain.addEventListener('touchmove', e => { if (startY === null) return; const dy = e.touches[0].clientY - startY; if (dy < -40) { liftCurtain(); startY = null; } }, {passive:true}); - curtain.addEventListener('click', liftCurtain); + let isDragging = false; + + // Función para obtener la posición Y del evento (touch o mouse) + const getY = (e) => { + return e.touches ? e.touches[0].clientY : e.clientY; + }; + + // Función de inicio (touch y mouse) + const handleStart = (e) => { + const coverEl = document.getElementById('curtain-cover'); + startY = getY(e); + isDragging = true; + if (e.type === 'mousedown') { + e.preventDefault(); // Prevenir selección de texto en escritorio + } + }; + + // Función de movimiento (touch y mouse) + const handleMove = (e) => { + if (startY === null || !isDragging) return; + const currentY = getY(e); + const dy = currentY - startY; + const coverEl = document.getElementById('curtain-cover'); + + // Calcular el desplazamiento: negativo = arriba, positivo = abajo + // Limitar el movimiento hacia arriba (no más allá de la altura de la cortina) + // y no permitir bajar más de la posición inicial (0) + const translateY = Math.max(Math.min(dy, 0), -cover.offsetHeight); + + coverEl.style.transform = `translateY(${translateY}px)`; + coverEl.style.transition = 'none'; + + // Si levanta suficiente, mostrar contenido + if (translateY < -80 && !curtainState.isRevealed) { + curtainState.isRevealed = true; + const idx = state.revealOrder[state.currentReveal]; + const role = state.roles[idx]; + const word = role === 'CIVIL' ? state.civilianWord : state.impostorWord; + document.getElementById('role-text').textContent = role; + document.getElementById('role-text').className = 'role ' + (role === 'CIVIL' ? 'civil' : 'impostor'); + document.getElementById('word-text').textContent = word; + } + + if (e.type === 'mousemove') { + e.preventDefault(); // Prevenir selección en escritorio + } + }; + + // Función de finalización (touch y mouse) + const handleEnd = (e) => { + if (!isDragging || startY === null) return; + const coverEl = document.getElementById('curtain-cover'); + + // SIEMPRE volver a bajar la cortina cuando se suelta (GRAVEDAD) + coverEl.style.transition = 'transform 0.4s ease'; + coverEl.style.transform = 'translateY(0)'; + + // Si ya se reveló el contenido, mostrar botón después de que baje + if (curtainState.isRevealed) { + setTimeout(() => { + if (state.currentReveal + 1 < state.numPlayers) { + document.getElementById('next-player-btn').style.display = 'block'; + } else { + document.getElementById('start-game-btn').style.display = 'block'; + } + }, 400); + } + + startY = null; + isDragging = false; + }; + + // Eventos touch (móvil) + curtain.addEventListener('touchstart', handleStart, {passive:true}); + curtain.addEventListener('touchmove', handleMove, {passive:true}); + curtain.addEventListener('touchend', handleEnd, {passive:true}); + curtain.addEventListener('touchcancel', handleEnd, {passive:true}); + + // Eventos mouse (escritorio) + curtain.addEventListener('mousedown', handleStart); + curtain.addEventListener('mousemove', handleMove); + curtain.addEventListener('mouseup', handleEnd); + curtain.addEventListener('mouseleave', (e) => { + // Si el mouse sale del área mientras arrastra, soltar (gravedad) + if (isDragging) { + handleEnd(e); + } + }); })(); // ---------- Timers ---------- @@ -498,24 +599,98 @@ function showScreen(id) { saveState(); } -function newMatch() { clearState(); state = { ...state, phase:'setup', timerEndAt:null, timerPhase:null, votingPool:null, isTiebreak:false, tiebreakCandidates:[] }; location.reload(); } +function newMatch() { + clearState(); + state = { ...state, phase:'welcome', timerEndAt:null, timerPhase:null, votingPool:null, isTiebreak:false, tiebreakCandidates:[] }; + saveState(); + showScreen('welcome-screen'); +} + +// ---------- Sistema de temas ---------- +function getSystemTheme() { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; +} + +function loadTheme() { + const savedTheme = localStorage.getItem(THEME_STORAGE_KEY); + return savedTheme || getSystemTheme(); +} + +function saveTheme(theme) { + localStorage.setItem(THEME_STORAGE_KEY, theme); +} + +function applyTheme(theme) { + document.documentElement.setAttribute('data-theme', theme); + const themeIcon = document.querySelector('.theme-icon'); + if (themeIcon) { + themeIcon.textContent = theme === 'dark' ? '☀️' : '🌙'; + } +} + +function toggleTheme() { + const currentTheme = document.documentElement.getAttribute('data-theme') || 'light'; + const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; + applyTheme(newTheme); + saveTheme(newTheme); +} + +// Inicializar tema +const initialTheme = loadTheme(); +applyTheme(initialTheme); + +// Event listener para el botón de tema +document.addEventListener('DOMContentLoaded', () => { + const themeToggle = document.getElementById('theme-toggle'); + if (themeToggle) { + themeToggle.addEventListener('click', toggleTheme); + } + + // Detectar cambios en el tema del sistema + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { + // Solo aplicar automáticamente si el usuario no ha seleccionado un tema manualmente + if (!localStorage.getItem(THEME_STORAGE_KEY)) { + applyTheme(e.matches ? 'dark' : 'light'); + } + }); +}); // ---------- Rehidratación ---------- (function init() { const restored = loadState(); - showScreen('setup-screen'); loadPoolsList(); if (!state.turnDirection) state.turnDirection = 'horario'; if (typeof state.startPlayer !== 'number') state.startPlayer = 0; - switch (state.phase) { - case 'setup': showScreen('setup-screen'); break; - case 'names': buildNameInputs(); showScreen('names-screen'); break; - case 'pre-reveal': renderSummary(); showScreen('pre-reveal-screen'); break; - case 'reveal': showScreen('reveal-screen'); loadCurrentReveal(); break; - case 'game': showScreen('game-screen'); resumeTimerIfNeeded(); break; - case 'deliberation': showScreen('deliberation-screen'); resumeTimerIfNeeded(); break; - case 'voting': showScreen('voting-screen'); renderVoting(); break; - case 'results': showResults(); break; - default: showScreen('setup-screen'); + + // Establecer valores por defecto en los inputs si estamos en setup + if (state.phase === 'setup' || !restored) { + const defaultPlayers = 6; + const defaultImp = defaultImpostors(defaultPlayers); + const defaultGTime = defaultGameTime(defaultPlayers); + const defaultDTime = defaultDeliberation(defaultGTime); + + document.getElementById('num-players').value = defaultPlayers; + document.getElementById('num-impostors').value = defaultImp; + document.getElementById('num-impostors').max = Math.max(1, Math.floor(defaultPlayers / 2)); + document.getElementById('game-time').value = defaultGTime; + document.getElementById('deliberation-time').value = defaultDTime; + } + + // Determinar pantalla inicial + if (!restored || state.phase === 'setup' || state.phase === 'welcome') { + // Si no hay estado guardado o estamos en setup/welcome, mostrar bienvenida + showScreen('welcome-screen'); + } else { + // Si hay una partida en curso, restaurarla + switch (state.phase) { + case 'names': buildNameInputs(); showScreen('names-screen'); break; + case 'pre-reveal': renderSummary(); showScreen('pre-reveal-screen'); break; + case 'reveal': showScreen('reveal-screen'); loadCurrentReveal(); break; + case 'game': showScreen('game-screen'); resumeTimerIfNeeded(); break; + case 'deliberation': showScreen('deliberation-screen'); resumeTimerIfNeeded(); break; + case 'voting': showScreen('voting-screen'); renderVoting(); break; + case 'results': showResults(); break; + default: showScreen('welcome-screen'); + } } })(); diff --git a/styles.css b/styles.css index 907e855..ad0abc3 100644 --- a/styles.css +++ b/styles.css @@ -1,7 +1,84 @@ +/* Variables de tema */ +:root { + /* Tema claro (por defecto) */ + --bg-gradient-start: #667eea; + --bg-gradient-end: #764ba2; + --container-bg: rgba(255, 255, 255, 0.15); + --container-border: rgba(255, 255, 255, 0.2); + --text-primary: #ffffff; + --text-secondary: rgba(255, 255, 255, 0.85); + --text-tertiary: rgba(255, 255, 255, 0.7); + --input-bg: rgba(255, 255, 255, 0.95); + --input-text: #2d3748; + --input-focus: #f5576c; + --button-gradient-start: #f093fb; + --button-gradient-end: #f5576c; + --button-secondary-start: #a8edea; + --button-secondary-end: #fed6e3; + --button-ghost-bg: rgba(255, 255, 255, 0.15); + --card-bg: rgba(255, 255, 255, 0.18); + --card-hover: rgba(255, 255, 255, 0.25); + --border-color: rgba(255, 255, 255, 0.25); + --shadow-color: rgba(31, 38, 135, 0.37); + --info-bg: rgba(0, 0, 0, 0.2); + --curtain-bg: #2d3748; + --pool-gradient: rgba(102, 126, 234, 0.4); +} + +/* Tema oscuro */ +[data-theme="dark"] { + --bg-gradient-start: #1a1a2e; + --bg-gradient-end: #16213e; + --container-bg: rgba(30, 30, 50, 0.85); + --container-border: rgba(255, 255, 255, 0.1); + --text-primary: #e0e0e0; + --text-secondary: rgba(224, 224, 224, 0.85); + --text-tertiary: rgba(224, 224, 224, 0.6); + --input-bg: rgba(40, 40, 60, 0.9); + --input-text: #e0e0e0; + --input-focus: #ff6b9d; + --button-gradient-start: #c850c0; + --button-gradient-end: #ff6b9d; + --button-secondary-start: #4158d0; + --button-secondary-end: #c850c0; + --button-ghost-bg: rgba(255, 255, 255, 0.08); + --card-bg: rgba(60, 60, 80, 0.5); + --card-hover: rgba(80, 80, 100, 0.6); + --border-color: rgba(255, 255, 255, 0.15); + --shadow-color: rgba(0, 0, 0, 0.5); + --info-bg: rgba(0, 0, 0, 0.4); + --curtain-bg: #1a1a2e; + --pool-gradient: rgba(30, 30, 50, 0.6); +} + /* Estilos móviles y UI principal */ * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; } -body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 10px; color: white; overflow: hidden; } -.container { width: 100%; max-width: 480px; max-height: 100vh; background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-radius: 16px; padding: 16px; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37); display: flex; flex-direction: column; } +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%); + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; + padding: 10px; + color: var(--text-primary); + overflow: hidden; + transition: background 0.3s ease; +} +.container { + width: 100%; + max-width: 480px; + max-height: 100vh; + background: var(--container-bg); + backdrop-filter: blur(10px); + border-radius: 20px; + padding: 20px; + box-shadow: 0 8px 32px var(--shadow-color); + border: 1px solid var(--container-border); + display: flex; + flex-direction: column; + transition: background 0.3s ease, border 0.3s ease; +} h1 { text-align: center; margin-bottom: 12px; font-size: 1.5em; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } h2 { text-align: center; margin: 8px 0; font-size: 1.2em; } .screen { display: none; animation: fadeIn 0.25s ease; flex: 1; overflow-y: auto; } @@ -9,19 +86,44 @@ h2 { text-align: center; margin: 8px 0; font-size: 1.2em; } @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .form-group { margin-bottom: 10px; } .form-group.compact { margin-bottom: 6px; } -label { display: block; margin-bottom: 4px; font-weight: 700; font-size: 0.9em; } -input { width: 100%; padding: 8px 10px; border: none; border-radius: 8px; font-size: 0.95em; background: rgba(255, 255, 255, 0.92); color: #333; } -input:focus { outline: 2px solid #f5576c; } -button { width: 100%; padding: 12px; border: none; border-radius: 10px; font-size: 1em; font-weight: 800; cursor: pointer; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; box-shadow: 0 4px 15px rgba(0,0,0,0.2); transition: transform 0.15s, box-shadow 0.15s; margin-top: 8px; } +label { display: block; margin-bottom: 4px; font-weight: 700; font-size: 0.9em; color: var(--text-secondary); } +input { + width: 100%; + padding: 10px 12px; + border: 2px solid var(--border-color); + border-radius: 10px; + font-size: 0.95em; + background: var(--input-bg); + color: var(--input-text); + transition: border-color 0.2s ease, background 0.3s ease; +} +input:focus { outline: none; border-color: var(--input-focus); } +button { + width: 100%; + padding: 14px; + border: none; + border-radius: 12px; + font-size: 1em; + font-weight: 800; + cursor: pointer; + background: linear-gradient(135deg, var(--button-gradient-start) 0%, var(--button-gradient-end) 100%); + color: white; + box-shadow: 0 4px 15px rgba(0,0,0,0.3); + transition: transform 0.15s, box-shadow 0.15s, background 0.3s ease; + margin-top: 10px; +} +button:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.4); } button:active { transform: scale(0.98); box-shadow: 0 2px 10px rgba(0,0,0,0.2); } -button.secondary { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); color: #333; } -button.ghost { background: rgba(255,255,255,0.12); color: #fff; font-weight: 600; } +button.secondary { background: linear-gradient(135deg, var(--button-secondary-start) 0%, var(--button-secondary-end) 100%); color: #fff; } +button.ghost { background: var(--button-ghost-bg); color: var(--text-primary); font-weight: 600; } .player-names-list { max-height: 280px; overflow-y: auto; margin-bottom: 8px; -webkit-overflow-scrolling: touch; } -.player-name-item { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; background: rgba(255,255,255,0.18); padding: 8px; border-radius: 8px; } -.player-name-item span { font-weight: 700; min-width: 76px; font-size: 0.9em; } +.player-name-item { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; background: var(--card-bg); padding: 10px; border-radius: 10px; border: 1px solid var(--border-color); transition: background 0.2s ease; } +.player-name-item:hover { background: var(--card-hover); } +.player-name-item span { font-weight: 700; min-width: 76px; font-size: 0.9em; color: var(--text-secondary); } .player-name-item input { flex: 1; padding: 8px; margin: 0; font-size: 0.9em; } -.curtain { position: relative; width: 100%; height: 280px; background: #2d3748; border-radius: 14px; overflow: hidden; margin: 12px 0; box-shadow: 0 4px 15px rgba(0,0,0,0.3); } -.curtain-cover { position: absolute; inset: 0; background: linear-gradient(135deg, #ffa585 0%, #ffeda0 100%); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; font-size: 1.1em; font-weight: 800; color: #333; transition: transform 0.5s ease; z-index: 10; } +.curtain { position: relative; width: 100%; height: 280px; background: var(--curtain-bg); border-radius: 16px; overflow: hidden; margin: 12px 0; box-shadow: 0 6px 20px rgba(0,0,0,0.4); cursor: grab; user-select: none; border: 2px solid var(--border-color); transition: background 0.3s ease; } +.curtain:active { cursor: grabbing; } +.curtain-cover { position: absolute; inset: 0; background: linear-gradient(135deg, #ffa585 0%, #ffeda0 100%); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; font-size: 1.1em; font-weight: 800; color: #333; transition: transform 0.5s ease; z-index: 10; user-select: none; } .curtain-cover.lifted { transform: translateY(-100%); } .curtain-icon { font-size: 2.2em; } .curtain-content { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 16px; text-align: center; } @@ -33,11 +135,12 @@ button.ghost { background: rgba(255,255,255,0.12); color: #fff; font-weight: 600 .timer.warning { color: #fbbf24; animation: pulse 1s infinite; } .timer.danger { color: #ef4444; animation: pulse 0.55s infinite; } @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } } -.info-text { text-align: center; margin: 10px 0; font-size: 0.95em; line-height: 1.5; background: rgba(0,0,0,0.2); padding: 10px; border-radius: 8px; } -.player-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 8px; margin: 12px 0; max-height: 300px; overflow-y: auto; -webkit-overflow-scrolling: touch; } -.player-item { padding: 14px 10px; background: rgba(255,255,255,0.2); border-radius: 10px; text-align: center; cursor: pointer; transition: transform 0.18s, background 0.18s, border 0.18s; font-weight: 700; font-size: 0.95em; border: 2px solid transparent; } +.info-text { text-align: center; margin: 10px 0; font-size: 0.95em; line-height: 1.5; background: var(--info-bg); padding: 12px; border-radius: 10px; color: var(--text-secondary); border: 1px solid var(--border-color); } +.player-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px; margin: 12px 0; max-height: 300px; overflow-y: auto; -webkit-overflow-scrolling: touch; } +.player-item { padding: 16px 12px; background: var(--card-bg); border-radius: 12px; text-align: center; cursor: pointer; transition: transform 0.18s, background 0.18s, border 0.18s; font-weight: 700; font-size: 0.95em; border: 2px solid var(--border-color); } +.player-item:hover { background: var(--card-hover); transform: translateY(-2px); } .player-item:active { transform: scale(0.96); } -.player-item.selected { background: #f5576c; border-color: #fff; box-shadow: 0 0 14px rgba(245, 87, 108, 0.6); } +.player-item.selected { background: var(--button-gradient-end); border-color: #fff; box-shadow: 0 0 16px rgba(245, 87, 108, 0.7); } .player-item .vote-count { display: block; font-size: 0.85em; margin-top: 4px; opacity: 0.88; } .results { background: rgba(255,255,255,0.16); border-radius: 10px; padding: 14px; margin: 10px 0; max-height: 400px; overflow-y: auto; -webkit-overflow-scrolling: touch; } .role-reveal { background: rgba(0,0,0,0.3); padding: 10px; border-radius: 8px; margin: 5px 0; border-left: 4px solid transparent; font-size: 0.9em; } @@ -79,11 +182,11 @@ button.ghost { background: rgba(255,255,255,0.12); color: #fff; font-weight: 600 } .pool-btn { - padding: 8px 6px; - border-radius: 8px; - border: 2px solid rgba(255,255,255,0.25); - background: rgba(255,255,255,0.16); - color: #fff; + padding: 10px 8px; + border-radius: 10px; + border: 2px solid var(--border-color); + background: var(--card-bg); + color: var(--text-primary); font-weight: 700; font-size: 0.85em; cursor: pointer; @@ -92,6 +195,143 @@ button.ghost { background: rgba(255,255,255,0.12); color: #fff; font-weight: 600 overflow: hidden; text-overflow: ellipsis; } -.pool-btn:hover { transform: translateY(-1px); } -.pool-btn.selected { border-color: #f093fb; background: rgba(240,147,251,0.3); box-shadow: 0 0 8px rgba(240,147,251,0.5); } +.pool-btn:hover { transform: translateY(-2px); background: var(--card-hover); } +.pool-btn.selected { border-color: var(--button-gradient-start); background: var(--button-gradient-start); color: white; box-shadow: 0 0 12px rgba(240,147,251,0.6); } + +/* Welcome screen */ +.welcome-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + height: 100%; + gap: 20px; + padding: 20px 0; +} + +.welcome-logo { + width: 140px; + height: 140px; + object-fit: contain; + filter: drop-shadow(0 8px 16px rgba(0,0,0,0.3)); + animation: float 3s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-10px); } +} + +.welcome-title { + font-size: 2em; + margin: 0; + background: linear-gradient(135deg, var(--button-gradient-start), var(--button-gradient-end)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 900; + text-shadow: none; +} + +.welcome-subtitle { + font-size: 1.1em; + color: var(--text-secondary); + margin: -10px 0 0 0; + font-weight: 500; +} + +.welcome-buttons { + display: flex; + flex-direction: column; + gap: 12px; + width: 100%; + max-width: 300px; + margin-top: 10px; +} + +.welcome-credits { + color: var(--text-tertiary); + font-size: 0.85em; + margin-top: auto; + font-weight: 500; +} + +/* Rules screen */ +.rules-content { + flex: 1; + overflow-y: auto; + padding: 10px 0; + -webkit-overflow-scrolling: touch; +} + +.rule-section { + background: var(--card-bg); + border: 2px solid var(--border-color); + border-radius: 12px; + padding: 16px; + margin-bottom: 16px; + transition: background 0.3s ease, transform 0.2s ease; +} + +.rule-section:hover { + background: var(--card-hover); + transform: translateY(-2px); +} + +.rule-section h3 { + margin: 0 0 12px 0; + color: var(--text-primary); + font-size: 1.1em; +} + +.rule-section p { + margin: 6px 0; + color: var(--text-secondary); + line-height: 1.6; + font-size: 0.95em; +} + +.rule-section p:first-of-type { + margin-top: 0; +} + +.rule-section strong { + color: var(--button-gradient-end); + font-weight: 800; +} + +/* Theme toggle button */ +.theme-toggle { + position: fixed; + top: 20px; + right: 20px; + width: 50px; + height: 50px; + border-radius: 50%; + border: 2px solid var(--border-color); + background: var(--container-bg); + backdrop-filter: blur(10px); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5em; + box-shadow: 0 4px 12px var(--shadow-color); + transition: transform 0.2s ease, background 0.3s ease, border 0.3s ease; + z-index: 1000; + margin: 0; + padding: 0; +} +.theme-toggle:hover { + transform: scale(1.1) rotate(15deg); + background: var(--card-hover); +} +.theme-toggle:active { + transform: scale(0.95); +} +.theme-icon { + transition: transform 0.3s ease; + display: inline-block; +}