From 730e6467d09e830fc4c2f218641dc55d6c17a838 Mon Sep 17 00:00:00 2001 From: ccadmingit1 Date: Thu, 30 Jul 2026 18:07:11 +0000 Subject: [PATCH] Initial AI-generated site --- index.html | 188 +++++++++++++ script.js | 740 +++++++++++++++++++++++++++++++++++++++++++++++++ style.css | 800 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1728 insertions(+) create mode 100644 index.html create mode 100644 script.js create mode 100644 style.css diff --git a/index.html b/index.html new file mode 100644 index 0000000..0385bc6 --- /dev/null +++ b/index.html @@ -0,0 +1,188 @@ + + + + + + משחק מירוץ סירות בנמל | משחק דפדפן חינמי + + + + + + + + + + + + + + + + +
+
+
+

מירוץ סירות בנמל

+

אספו מתנות · קבלו בוסטים · שברו שיאים!

+ +
+ +
+ +
+
+
+ ניקוד + 0 +
+
+ מרחק + 0 מ׳ +
+
+ בוסט +
+
+ 0% +
+
+
+ מתנות + 0 +
+
+ +
+ + + + +
+ +
+

איך משחקים?

+
+
+ +

שליטה פשוטה

+

חיצים למעלה ולמטה להזזת הסירה, או החלקה במובייל

+
+
+ +

אספו מתנות

+

מתנות צפות על המים — אספו לניקוד בונוס ולמילוי מד הבוסט

+
+
+ +

בוסט מהירות

+

כשהמד מלא, לחצו רווח או הקשה כפולה לזינוק קדימה!

+
+
+ +

שברו שיאים

+

התחרו בעצמכם ושפרו את השיא האישי שוב ושוב

+
+
+
+ +
+

טבלת שיאים

+
+
+ מקום + ניקוד + מתנות +
+
+
+ עדיין אין שיאים — היו הראשונים! +
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..fc8856c --- /dev/null +++ b/script.js @@ -0,0 +1,740 @@ +(function() { + 'use strict'; + + // Game State + const state = { + isPlaying: false, + isPaused: false, + score: 0, + distance: 0, + gifts: 0, + boost: 0, + maxBoost: 100, + isBoostActive: false, + speed: 3, + baseSpeed: 3, + boostSpeed: 7, + playerY: 0, + playerVelocity: 0, + obstacles: [], + giftItems: [], + particles: [], + highscores: [], + lastBoostAnnounce: false + }; + + // DOM Elements + const canvas = document.getElementById('gameCanvas'); + const ctx = canvas.getContext('2d'); + const startBtn = document.getElementById('startGame'); + const overlayBtn = document.getElementById('overlayBtn'); + const gameOverlay = document.getElementById('gameOverlay'); + const overlayTitle = document.getElementById('overlayTitle'); + const overlayMessage = document.getElementById('overlayMessage'); + const finalStats = document.getElementById('finalStats'); + const scoreDisplay = document.getElementById('scoreDisplay'); + const distanceDisplay = document.getElementById('distanceDisplay'); + const giftsDisplay = document.getElementById('giftsDisplay'); + const boostBar = document.getElementById('boostBar'); + const boostValue = document.getElementById('boostValue'); + const boostBarContainer = boostBar.parentElement; + const instructionsModal = document.getElementById('instructionsModal'); + const closeInstructions = document.getElementById('closeInstructions'); + const highscoresBody = document.getElementById('highscoresBody'); + const gameStatus = document.getElementById('gameStatus'); + + // Check for reduced motion preference + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + // Focus trap for modal + let focusableElements = []; + let firstFocusable = null; + let lastFocusable = null; + + function setupFocusTrap(modal) { + focusableElements = modal.querySelectorAll( + 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' + ); + firstFocusable = focusableElements[0]; + lastFocusable = focusableElements[focusableElements.length - 1]; + } + + function handleFocusTrap(e) { + if (e.key !== 'Tab') return; + + if (e.shiftKey) { + if (document.activeElement === firstFocusable) { + e.preventDefault(); + lastFocusable.focus(); + } + } else { + if (document.activeElement === lastFocusable) { + e.preventDefault(); + firstFocusable.focus(); + } + } + } + + // Canvas setup + function resizeCanvas() { + const rect = canvas.getBoundingClientRect(); + const dpr = window.devicePixelRatio || 1; + canvas.width = rect.width * dpr; + canvas.height = rect.height * dpr; + ctx.scale(dpr, dpr); + state.playerY = rect.height / 2; + } + + function getCanvasSize() { + return { + width: canvas.getBoundingClientRect().width, + height: canvas.getBoundingClientRect().height + }; + } + + // Boat drawing + const boatSprite = { + width: 48, + height: 32, + draw(ctx, x, y, isBoost) { + ctx.save(); + ctx.translate(x, y); + + // Boat body + ctx.fillStyle = isBoost ? '#ff6b6b' : '#8b4513'; + ctx.fillRect(0, 8, 40, 16); + ctx.fillRect(-4, 12, 8, 8); + + // Boat front (pointed) + ctx.fillStyle = isBoost ? '#ff6b6b' : '#a0522d'; + ctx.beginPath(); + ctx.moveTo(40, 8); + ctx.lineTo(48, 16); + ctx.lineTo(40, 24); + ctx.closePath(); + ctx.fill(); + + // Sail + ctx.fillStyle = isBoost ? '#ffd60a' : '#ffffff'; + ctx.beginPath(); + ctx.moveTo(20, 8); + ctx.lineTo(20, -12); + ctx.lineTo(35, 8); + ctx.closePath(); + ctx.fill(); + + // Mast + ctx.fillStyle = '#654321'; + ctx.fillRect(18, -12, 4, 20); + + // Flag + ctx.fillStyle = '#f72585'; + ctx.fillRect(18, -16, 8, 6); + + // Wake effect when boosting + if (isBoost && !prefersReducedMotion) { + ctx.fillStyle = 'rgba(0, 245, 212, 0.6)'; + for (let i = 0; i < 3; i++) { + const yOffset = Math.sin(Date.now() / 100 + i) * 2; + ctx.fillRect(-8 - i * 8, 14 + yOffset, 4, 4); + } + } + + ctx.restore(); + } + }; + + // Gift item + function drawGift(ctx, x, y, type) { + ctx.save(); + ctx.translate(x, y); + + const bounce = prefersReducedMotion ? 0 : Math.sin(Date.now() / 200 + x) * 3; + ctx.translate(0, bounce); + + if (type === 'boost') { + // Lightning bolt + ctx.fillStyle = '#ffd60a'; + ctx.beginPath(); + ctx.moveTo(0, -12); + ctx.lineTo(8, -4); + ctx.lineTo(2, -4); + ctx.lineTo(8, 12); + ctx.lineTo(-4, 0); + ctx.lineTo(2, 0); + ctx.closePath(); + ctx.fill(); + } else { + // Gift box + ctx.fillStyle = '#f72585'; + ctx.fillRect(-8, -4, 16, 12); + ctx.fillStyle = '#ffd60a'; + ctx.fillRect(-2, -4, 4, 12); + ctx.fillRect(-8, 0, 16, 4); + // Bow + ctx.fillRect(-6, -10, 4, 6); + ctx.fillRect(2, -10, 4, 6); + ctx.fillRect(-2, -8, 4, 4); + } + + ctx.restore(); + } + + // Obstacle (rock) + function drawObstacle(ctx, x, y, size) { + ctx.save(); + ctx.translate(x, y); + + ctx.fillStyle = '#4a4a4a'; + ctx.beginPath(); + ctx.moveTo(-size/2, size/2); + ctx.lineTo(-size/3, -size/3); + ctx.lineTo(0, -size/2); + ctx.lineTo(size/3, -size/4); + ctx.lineTo(size/2, size/2); + ctx.closePath(); + ctx.fill(); + + // Highlights + ctx.fillStyle = '#5a5a5a'; + ctx.fillRect(-size/4, -size/4, size/4, size/4); + + ctx.restore(); + } + + // Water background + function drawWater(ctx, canvasSize) { + const { width, height } = canvasSize; + + const gradient = ctx.createLinearGradient(0, 0, 0, height); + gradient.addColorStop(0, '#0d1b2a'); + gradient.addColorStop(0.5, '#1b263b'); + gradient.addColorStop(1, '#0d1b2a'); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, width, height); + + // Animated waves (skip if reduced motion or paused) + if (!prefersReducedMotion && state.isPlaying && !state.isPaused) { + ctx.strokeStyle = 'rgba(0, 245, 212, 0.1)'; + ctx.lineWidth = 2; + for (let i = 0; i < 4; i++) { + ctx.beginPath(); + for (let x = 0; x < width; x += 20) { + const waveY = (height / 5) * (i + 1) + Math.sin((x + state.distance * 2 + i * 50) / 50) * 8; + if (x === 0) { + ctx.moveTo(x, waveY); + } else { + ctx.lineTo(x, waveY); + } + } + ctx.stroke(); + } + } + } + + // Particle system + function createParticle(x, y, color) { + if (prefersReducedMotion) return; + + state.particles.push({ + x, + y, + vx: (Math.random() - 0.5) * 4, + vy: (Math.random() - 0.5) * 4, + life: 1, + color, + size: Math.random() * 4 + 2 + }); + } + + function updateParticles() { + for (let i = state.particles.length - 1; i >= 0; i--) { + const p = state.particles[i]; + p.x += p.vx; + p.y += p.vy; + p.life -= 0.03; + if (p.life <= 0) { + state.particles.splice(i, 1); + } + } + } + + function drawParticles(ctx) { + state.particles.forEach(p => { + ctx.fillStyle = p.color; + ctx.globalAlpha = p.life; + ctx.fillRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size); + }); + ctx.globalAlpha = 1; + } + + // Spawn obstacles and gifts + function spawnObstacle(canvasSize) { + const { width, height } = canvasSize; + const maxY = height - 40; + const minY = 40; + + state.obstacles.push({ + x: width + 50, + y: Math.random() * (maxY - minY) + minY, + size: Math.random() * 20 + 30 + }); + } + + function spawnGift(canvasSize) { + const { width, height } = canvasSize; + const maxY = height - 30; + const minY = 30; + + state.giftItems.push({ + x: width + 30, + y: Math.random() * (maxY - minY) + minY, + type: Math.random() < 0.3 ? 'boost' : 'gift' + }); + } + + // Collision detection + function checkCollision(ax, ay, aw, ah, bx, by, bw, bh) { + return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by; + } + + // Update game + let lastSpawnTime = 0; + let lastGiftTime = 0; + + function update(timestamp) { + if (!state.isPlaying || state.isPaused) return; + + const canvasSize = getCanvasSize(); + const { height } = canvasSize; + + // Increase difficulty over time + state.speed = state.baseSpeed + state.distance / 1200; + const currentSpeed = state.isBoostActive ? state.boostSpeed : state.speed; + + // Update distance + state.distance += currentSpeed * 0.5; + + // Player physics + state.playerY += state.playerVelocity; + state.playerVelocity *= 0.9; + state.playerY = Math.max(24, Math.min(height - 24, state.playerY)); + + // Spawn obstacles + const spawnInterval = Math.max(800, 1500 - state.distance * 0.5); + if (timestamp - lastSpawnTime > spawnInterval) { + spawnObstacle(canvasSize); + lastSpawnTime = timestamp; + } + + // Spawn gifts + if (timestamp - lastGiftTime > 2200) { + spawnGift(canvasSize); + lastGiftTime = timestamp; + } + + // Update obstacles + for (let i = state.obstacles.length - 1; i >= 0; i--) { + state.obstacles[i].x -= currentSpeed; + + // Collision with player + if (checkCollision( + 60, state.playerY - 16, 36, 28, + state.obstacles[i].x - state.obstacles[i].size/2, + state.obstacles[i].y - state.obstacles[i].size/2, + state.obstacles[i].size, + state.obstacles[i].size + )) { + gameOver(); + return; + } + + if (state.obstacles[i].x < -50) { + state.obstacles.splice(i, 1); + state.score += 10; + } + } + + // Update gifts + for (let i = state.giftItems.length - 1; i >= 0; i--) { + state.giftItems[i].x -= currentSpeed; + + // Collision with player + if (checkCollision( + 60, state.playerY - 16, 36, 28, + state.giftItems[i].x - 12, + state.giftItems[i].y - 12, + 24, 24 + )) { + // Collect gift + const gift = state.giftItems[i]; + if (gift.type === 'boost') { + state.boost = Math.min(state.boost + 35, state.maxBoost); + state.score += 25; + } else { + state.gifts++; + state.boost = Math.min(state.boost + 15, state.maxBoost); + state.score += 50; + } + + // Particles + for (let j = 0; j < 8; j++) { + createParticle(gift.x, gift.y, gift.type === 'boost' ? '#ffd60a' : '#f72585'); + } + + state.giftItems.splice(i, 1); + updateHUD(); + continue; + } + + if (state.giftItems[i].x < -30) { + state.giftItems.splice(i, 1); + } + } + + // Boost drain + if (state.isBoostActive) { + state.boost -= 0.6; + if (state.boost <= 0) { + state.boost = 0; + state.isBoostActive = false; + } + // Boost particles + createParticle(60, state.playerY, 'rgba(0, 245, 212, 0.8)'); + } + + // Announce boost ready (only once) + const boostFull = state.boost >= state.maxBoost; + if (boostFull && !state.lastBoostAnnounce) { + gameStatus.textContent = 'בוסט מוכן! לחצו רווח להפעלה'; + state.lastBoostAnnounce = true; + } else if (!boostFull && state.lastBoostAnnounce) { + state.lastBoostAnnounce = false; + gameStatus.textContent = ''; + } + + updateParticles(); + updateHUD(); + } + + function updateHUD() { + scoreDisplay.textContent = Math.floor(state.score); + distanceDisplay.textContent = Math.floor(state.distance) + ' מ׳'; + giftsDisplay.textContent = state.gifts; + + const boostPercent = Math.floor(state.boost / state.maxBoost * 100); + boostBar.style.width = boostPercent + '%'; + boostValue.textContent = boostPercent + '%'; + boostBarContainer.setAttribute('aria-valuenow', boostPercent); + } + + // Render game + function render() { + const canvasSize = getCanvasSize(); + const { width, height } = canvasSize; + + ctx.clearRect(0, 0, width, height); + + drawWater(ctx, canvasSize); + + // Draw obstacles + state.obstacles.forEach(obs => { + drawObstacle(ctx, obs.x, obs.y, obs.size); + }); + + // Draw gifts + state.giftItems.forEach(gift => { + drawGift(ctx, gift.x, gift.y, gift.type); + }); + + // Draw player + boatSprite.draw(ctx, 60, state.playerY, state.isBoostActive); + + // Draw particles + drawParticles(ctx); + + // Boost ready indicator + if (state.boost >= state.maxBoost && state.isPlaying) { + ctx.font = 'bold 14px Heebo, sans-serif'; + ctx.fillStyle = '#ffd60a'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + ctx.fillText('⚡ בוסט מוכן! לחצו רווח ⚡', width / 2, 12); + } + } + + // Game loop + let animationId; + function gameLoop(timestamp) { + update(timestamp); + render(); + if (state.isPlaying) { + animationId = requestAnimationFrame(gameLoop); + } + } + + // Start game + function startGame() { + const canvasSize = getCanvasSize(); + + state.isPlaying = true; + state.isPaused = false; + state.score = 0; + state.distance = 0; + state.gifts = 0; + state.boost = 0; + state.isBoostActive = false; + state.speed = state.baseSpeed; + state.playerY = canvasSize.height / 2; + state.playerVelocity = 0; + state.obstacles = []; + state.giftItems = []; + state.particles = []; + state.lastBoostAnnounce = false; + + gameOverlay.classList.add('hidden'); + gameStatus.textContent = 'המשחק התחיל!'; + updateHUD(); + + lastSpawnTime = performance.now(); + lastGiftTime = performance.now(); + + animationId = requestAnimationFrame(gameLoop); + } + + // Game over + function gameOver() { + state.isPlaying = false; + cancelAnimationFrame(animationId); + + // Save highscore + const entry = { + score: Math.floor(state.score), + gifts: state.gifts, + distance: Math.floor(state.distance) + }; + + state.highscores.push(entry); + state.highscores.sort((a, b) => b.score - a.score); + state.highscores = state.highscores.slice(0, 5); + + try { + localStorage.setItem('boatRaceHighscores', JSON.stringify(state.highscores)); + } catch (e) { + // Storage might be unavailable + } + + // Show overlay + overlayTitle.textContent = 'המשחק נגמר!'; + overlayMessage.textContent = 'כל הכבוד! הגעתם למרחק ' + Math.floor(state.distance) + ' מטר'; + finalStats.hidden = false; + document.getElementById('finalScore').textContent = Math.floor(state.score); + document.getElementById('finalGifts').textContent = state.gifts; + document.getElementById('finalDistance').textContent = Math.floor(state.distance) + ' מ׳'; + overlayBtn.textContent = 'שחק שוב'; + gameOverlay.classList.remove('hidden'); + overlayBtn.focus(); + + gameStatus.textContent = 'המשחק נגמר. ניקוד סופי: ' + Math.floor(state.score); + + renderHighscores(); + } + + // Render highscores + function renderHighscores() { + if (state.highscores.length === 0) { + highscoresBody.innerHTML = '
עדיין אין שיאים — היו הראשונים!
'; + return; + } + + highscoresBody.innerHTML = state.highscores.map((entry, i) => ` +
+ #${i + 1} + ${entry.score} + ${entry.gifts} +
+ `).join(''); + } + + // Input handling + function handleKeyDown(e) { + if (e.code === 'Space') { + e.preventDefault(); + if (!state.isPlaying) { + startGame(); + } else if (state.boost >= state.maxBoost && !state.isBoostActive) { + state.isBoostActive = true; + gameStatus.textContent = 'בוסט פעיל!'; + } + } + + if (e.code === 'ArrowUp' || e.code === 'KeyW') { + e.preventDefault(); + state.playerVelocity = -7; + } + + if (e.code === 'ArrowDown' || e.code === 'KeyS') { + e.preventDefault(); + state.playerVelocity = 7; + } + + if (e.code === 'KeyP' && state.isPlaying) { + state.isPaused = !state.isPaused; + gameStatus.textContent = state.isPaused ? 'המשחק מושהה' : 'המשחק ממשיך'; + if (!state.isPaused) { + animationId = requestAnimationFrame(gameLoop); + } + } + + if (e.code === 'Escape') { + if (!instructionsModal.hidden) { + closeModal(); + } + } + } + + // Touch handling + let touchStartY = 0; + let lastTap = 0; + + function handleTouchStart(e) { + if (!state.isPlaying) { + startGame(); + return; + } + + touchStartY = e.touches[0].clientY; + + // Double tap for boost + const now = Date.now(); + if (now - lastTap < 300 && state.boost >= state.maxBoost && !state.isBoostActive) { + state.isBoostActive = true; + gameStatus.textContent = 'בוסט פעיל!'; + } + lastTap = now; + } + + function handleTouchMove(e) { + if (!state.isPlaying) return; + e.preventDefault(); + + const touchY = e.touches[0].clientY; + const diff = touchStartY - touchY; + state.playerVelocity = -diff * 0.12; + touchStartY = touchY; + } + + // Modal handling + let previousActiveElement = null; + + function openModal() { + previousActiveElement = document.activeElement; + instructionsModal.hidden = false; + setupFocusTrap(instructionsModal); + closeInstructions.focus(); + document.body.style.overflow = 'hidden'; + instructionsModal.addEventListener('keydown', handleFocusTrap); + } + + function closeModal() { + instructionsModal.hidden = true; + document.body.style.overflow = ''; + instructionsModal.removeEventListener('keydown', handleFocusTrap); + if (previousActiveElement) { + previousActiveElement.focus(); + } + } + + // Initialize + function init() { + resizeCanvas(); + + // Load highscores + try { + const saved = localStorage.getItem('boatRaceHighscores'); + if (saved) { + state.highscores = JSON.parse(saved); + } + } catch (e) { + // Storage might be unavailable + } + + renderHighscores(); + + // Event listeners + window.addEventListener('resize', resizeCanvas); + document.addEventListener('keydown', handleKeyDown); + canvas.addEventListener('touchstart', handleTouchStart, { passive: true }); + canvas.addEventListener('touchmove', handleTouchMove, { passive: false }); + + startBtn.addEventListener('click', startGame); + overlayBtn.addEventListener('click', startGame); + + // Navigation + document.querySelectorAll('.nav-btn').forEach(btn => { + btn.addEventListener('click', () => { + const action = btn.dataset.action; + if (action === 'instructions') { + openModal(); + } else if (action === 'highscores') { + document.getElementById('highscoresSection').scrollIntoView({ behavior: prefersReducedMotion ? 'auto' : 'smooth' }); + } + }); + }); + + closeInstructions.addEventListener('click', closeModal); + + instructionsModal.addEventListener('click', (e) => { + if (e.target === instructionsModal) { + closeModal(); + } + }); + + // Scroll reveal animations (skip if reduced motion) + if (!prefersReducedMotion) { + const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -40px 0px' + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('revealed'); + observer.unobserve(entry.target); + } + }); + }, observerOptions); + + document.querySelectorAll('.feature-card, .section-title').forEach(el => { + el.classList.add('reveal-on-scroll'); + observer.observe(el); + }); + + // Add reveal styles dynamically + const style = document.createElement('style'); + style.textContent = ` + .reveal-on-scroll { + opacity: 0; + transform: translateY(24px); + transition: opacity 0.5s ease, transform 0.5s ease; + } + .reveal-on-scroll.revealed { + opacity: 1; + transform: translateY(0); + } + `; + document.head.appendChild(style); + } + + // Initial render + render(); + } + + // Start + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..939e93c --- /dev/null +++ b/style.css @@ -0,0 +1,800 @@ +:root { + --color-bg: #0a0a12; + --color-water: #0d1b2a; + --color-water-light: #1b263b; + --color-primary: #00f5d4; + --color-secondary: #7b2cbf; + --color-accent: #f72585; + --color-gold: #ffd60a; + --color-text: #e8e8e8; + --color-text-muted: #b8b8b8; + --color-card-bg: #14142a; + --font-display: 'Rubik Mono One', 'Heebo', sans-serif; + --font-body: 'Heebo', sans-serif; + --space-xs: 0.5rem; + --space-sm: 1rem; + --space-md: 1.5rem; + --space-lg: 3rem; + --space-xl: 5rem; + --radius-sm: 4px; + --radius-md: 8px; +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: var(--font-body); + background: var(--color-bg); + color: var(--color-text); + line-height: 1.7; + overflow-x: hidden; + min-height: 100vh; +} + +/* Skip link */ +.skip-link { + position: absolute; + inset-inline-start: 0; + top: 0; + padding: var(--space-sm) var(--space-md); + background: var(--color-primary); + color: var(--color-bg); + font-weight: 700; + text-decoration: none; + z-index: 1000; + transform: translateY(-100%); + transition: transform 0.2s ease; +} + +.skip-link:focus { + transform: translateY(0); +} + +/* Screen reader only */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Scanlines - subtle CRT effect */ +.scanlines { + position: fixed; + inset: 0; + pointer-events: none; + z-index: 9999; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.06) 2px, + rgba(0, 0, 0, 0.06) 4px + ); +} + +/* Header */ +.site-header { + position: fixed; + top: 0; + inset-inline: 0; + z-index: 100; + padding: var(--space-sm) var(--space-md); + background: linear-gradient(to bottom, var(--color-bg) 60%, transparent); +} + +.header-content { + max-width: 960px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--space-sm); +} + +.logo { + display: flex; + align-items: center; + gap: var(--space-sm); +} + +.logo-icon { + font-size: 1.75rem; +} + +.logo-text { + font-family: var(--font-display); + font-size: 1rem; + color: var(--color-primary); + text-shadow: 0 0 20px rgba(0, 245, 212, 0.5); + letter-spacing: -0.02em; +} + +.main-nav { + display: flex; + gap: var(--space-xs); +} + +.nav-btn { + font-family: var(--font-body); + font-weight: 700; + font-size: 0.875rem; + padding: var(--space-xs) var(--space-sm); + background: transparent; + border: 2px solid var(--color-primary); + border-radius: var(--radius-sm); + color: var(--color-primary); + cursor: pointer; + transition: background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease; +} + +.nav-btn:hover { + background: var(--color-primary); + color: var(--color-bg); +} + +.nav-btn:focus-visible { + outline: 3px solid var(--color-gold); + outline-offset: 2px; +} + +/* Hero */ +.hero { + min-height: 100vh; + min-height: 100svh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + padding: var(--space-xl) var(--space-md); + padding-block-start: calc(var(--space-xl) + 60px); + position: relative; + overflow: hidden; +} + +.hero-content { + position: relative; + z-index: 2; +} + +.hero-title { + font-family: var(--font-display); + font-size: clamp(1.75rem, 6vw, 3rem); + color: var(--color-primary); + text-shadow: + 0 0 30px rgba(0, 245, 212, 0.6), + 0 0 60px rgba(0, 245, 212, 0.3); + margin-block-end: var(--space-sm); + letter-spacing: -0.02em; +} + +.hero-subtitle { + font-size: clamp(1rem, 2.5vw, 1.25rem); + color: var(--color-text-muted); + margin-block-end: var(--space-lg); + max-width: 400px; + margin-inline: auto; +} + +.pixel-btn { + font-family: var(--font-display); + font-size: clamp(0.875rem, 2vw, 1rem); + padding: var(--space-sm) var(--space-md); + background: linear-gradient(135deg, var(--color-secondary), var(--color-accent)); + border: none; + border-radius: var(--radius-sm); + color: white; + cursor: pointer; + position: relative; + overflow: hidden; + transition: transform 0.15s ease, box-shadow 0.15s ease; + box-shadow: + 0 4px 0 #4a1a6b, + 0 8px 24px rgba(123, 44, 191, 0.4); +} + +.pixel-btn:hover { + transform: translateY(-2px); + box-shadow: + 0 6px 0 #4a1a6b, + 0 12px 32px rgba(123, 44, 191, 0.5); +} + +.pixel-btn:focus-visible { + outline: 3px solid var(--color-gold); + outline-offset: 3px; +} + +.pixel-btn:active { + transform: translateY(2px); + box-shadow: + 0 2px 0 #4a1a6b, + 0 4px 12px rgba(123, 44, 191, 0.4); +} + +/* Hero decoration - harbor theme */ +.hero-decoration { + position: absolute; + bottom: 0; + inset-inline: 0; + height: 180px; + overflow: hidden; + pointer-events: none; +} + +.harbor-elements { + position: absolute; + bottom: 60px; + inset-inline: 0; + height: 120px; +} + +.lighthouse { + position: absolute; + inset-inline-end: 10%; + bottom: 0; + width: 24px; + height: 80px; + background: linear-gradient(to bottom, #fff 0%, #fff 10%, #e74c3c 10%, #e74c3c 30%, #fff 30%, #fff 50%, #e74c3c 50%, #e74c3c 70%, #fff 70%); + border-radius: 4px 4px 0 0; +} + +.lighthouse::before { + content: ''; + position: absolute; + top: -16px; + inset-inline-start: 50%; + width: 16px; + height: 16px; + background: var(--color-gold); + border-radius: 50%; + box-shadow: 0 0 20px var(--color-gold), 0 0 40px var(--color-gold); + animation: lighthouse-glow 2s ease-in-out infinite; + /* RTL-safe centering */ + margin-inline-start: -8px; +} + +.buoy { + position: absolute; + bottom: 0; + width: 12px; + height: 24px; + background: linear-gradient(to bottom, #e74c3c 50%, #fff 50%); + border-radius: 6px 6px 2px 2px; +} + +.buoy-1 { + inset-inline-start: 15%; + animation: buoy-bob 3s ease-in-out infinite; +} + +.buoy-2 { + inset-inline-start: 35%; + animation: buoy-bob 3s ease-in-out infinite 1s; +} + +@keyframes lighthouse-glow { + 0%, 100% { opacity: 0.6; } + 50% { opacity: 1; } +} + +@keyframes buoy-bob { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-6px); } +} + +.wave { + position: absolute; + bottom: 0; + inset-inline-start: 0; + width: 200%; + height: 60px; +} + +.wave-1 { + background: + radial-gradient(ellipse 60px 20px at 30px 100%, var(--color-water) 50%, transparent 50%), + radial-gradient(ellipse 60px 20px at 90px 100%, var(--color-water) 50%, transparent 50%); + background-size: 120px 60px; + animation: wave 6s linear infinite; + opacity: 0.9; +} + +.wave-2 { + background: + radial-gradient(ellipse 40px 15px at 20px 100%, var(--color-water-light) 50%, transparent 50%), + radial-gradient(ellipse 40px 15px at 60px 100%, var(--color-water-light) 50%, transparent 50%); + background-size: 80px 40px; + animation: wave 4s linear infinite reverse; + opacity: 0.5; + bottom: 10px; +} + +@keyframes wave { + 0% { transform: translateX(0); } + 100% { transform: translateX(-50%); } +} + +/* Game Container */ +.game-container { + max-width: 960px; + margin: 0 auto var(--space-xl); + padding: var(--space-md); + position: relative; +} + +.game-hud { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: var(--space-md); + margin-block-end: var(--space-sm); + padding: var(--space-sm) var(--space-md); + background: var(--color-card-bg); + border: 2px solid var(--color-water-light); + border-radius: var(--radius-md); +} + +.hud-item { + text-align: center; +} + +.hud-label { + font-family: var(--font-body); + font-weight: 700; + font-size: 0.8125rem; + color: var(--color-text); + display: block; + margin-block-end: var(--space-xs); + letter-spacing: 0.02em; +} + +.hud-value { + font-family: var(--font-display); + font-size: clamp(1rem, 3vw, 1.25rem); + color: var(--color-gold); + text-shadow: 0 0 10px rgba(255, 214, 10, 0.5); +} + +.boost-bar { + height: 28px; + background: var(--color-water); + border: 2px solid var(--color-primary); + border-radius: var(--radius-sm); + position: relative; + overflow: hidden; +} + +.boost-fill { + height: 100%; + width: 0%; + background: linear-gradient(90deg, var(--color-primary), var(--color-accent)); + transition: width 0.2s ease; + position: relative; + border-radius: var(--radius-sm); +} + +.boost-fill::after { + content: ''; + position: absolute; + inset: 0; + background: repeating-linear-gradient( + 90deg, + transparent, + transparent 6px, + rgba(255, 255, 255, 0.15) 6px, + rgba(255, 255, 255, 0.15) 12px + ); +} + +.boost-value { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + font-family: var(--font-body); + font-weight: 700; + font-size: 0.75rem; + color: var(--color-text); + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); + pointer-events: none; +} + +#gameCanvas { + width: 100%; + aspect-ratio: 16 / 9; + background: var(--color-water); + border: 3px solid var(--color-water-light); + border-radius: var(--radius-md); + display: block; +} + +.game-overlay { + position: absolute; + inset: 0; + background: rgba(10, 10, 18, 0.95); + display: flex; + justify-content: center; + align-items: center; + z-index: 10; + border-radius: var(--radius-md); +} + +.game-overlay.hidden { + display: none; +} + +.overlay-content { + text-align: center; + padding: var(--space-lg); + max-width: 90%; +} + +.overlay-content h2 { + font-family: var(--font-display); + font-size: clamp(1.25rem, 4vw, 1.75rem); + color: var(--color-primary); + margin-block-end: var(--space-sm); +} + +.overlay-content p { + color: var(--color-text-muted); + margin-block-end: var(--space-md); + font-size: 1rem; +} + +.final-stats { + background: var(--color-card-bg); + padding: var(--space-md); + margin-block-end: var(--space-md); + border: 2px solid var(--color-gold); + border-radius: var(--radius-md); +} + +.final-stats[hidden] { + display: none; +} + +.stat-row { + display: flex; + justify-content: space-between; + padding: var(--space-xs) 0; + font-family: var(--font-body); + font-weight: 700; + font-size: 0.9rem; +} + +.stat-row span:last-child { + color: var(--color-gold); +} + +/* Features */ +.features { + padding: var(--space-xl) var(--space-md); + max-width: 960px; + margin: 0 auto; +} + +.section-title { + font-family: var(--font-display); + font-size: clamp(1.125rem, 3vw, 1.5rem); + text-align: center; + color: var(--color-primary); + margin-block-end: var(--space-lg); + text-shadow: 0 0 30px rgba(0, 245, 212, 0.4); +} + +.features-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--space-md); +} + +@media (min-width: 700px) { + .features-grid { + grid-template-columns: repeat(4, 1fr); + } +} + +.feature-card { + background: var(--color-card-bg); + padding: var(--space-md); + border: 2px solid var(--color-water-light); + border-radius: var(--radius-md); + text-align: center; + transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; +} + +.feature-card:hover { + transform: translateY(-4px); + box-shadow: 0 8px 32px rgba(0, 245, 212, 0.15); + border-color: var(--color-primary); +} + +.feature-card:focus-within { + outline: 3px solid var(--color-primary); + outline-offset: 2px; +} + +.feature-icon { + font-size: 2.25rem; + margin-block-end: var(--space-sm); + display: block; + line-height: 1; +} + +.feature-card h3 { + font-family: var(--font-display); + font-size: 0.875rem; + color: var(--color-gold); + margin-block-end: var(--space-xs); +} + +.feature-card p { + color: var(--color-text-muted); + font-size: 0.9rem; + line-height: 1.6; +} + +/* Highscores */ +.highscores-section { + padding: var(--space-xl) var(--space-md); + max-width: 500px; + margin: 0 auto; +} + +.highscores-table { + background: var(--color-card-bg); + border: 3px solid var(--color-water-light); + border-radius: var(--radius-md); + overflow: hidden; +} + +.table-header { + display: grid; + grid-template-columns: 1fr 2fr 1fr; + padding: var(--space-sm); + background: var(--color-water); + font-family: var(--font-body); + font-weight: 700; + font-size: 0.875rem; + color: var(--color-primary); +} + +.table-body { + min-height: 120px; +} + +.table-row { + display: grid; + grid-template-columns: 1fr 2fr 1fr; + padding: var(--space-sm); + border-block-end: 1px solid var(--color-water-light); + font-family: var(--font-body); + font-weight: 700; + font-size: 0.875rem; + transition: background 0.15s ease; +} + +.table-row:last-child { + border-block-end: none; +} + +.table-row:hover { + background: var(--color-water); +} + +.table-row--empty { + grid-template-columns: 1fr; + text-align: center; + color: var(--color-text-muted); + font-weight: 400; + padding: var(--space-md); +} + +.table-row:first-child:not(.table-row--empty) span:first-child { + color: var(--color-gold); +} + +.table-row:nth-child(2) span:first-child { + color: #c0c0c0; +} + +.table-row:nth-child(3) span:first-child { + color: #cd7f32; +} + +/* Footer */ +.site-footer { + background: var(--color-water); + padding: var(--space-md) var(--space-md) var(--space-lg); + text-align: center; + border-block-start: 3px solid var(--color-water-light); +} + +.footer-logo { + font-family: var(--font-display); + font-size: 1rem; + color: var(--color-primary); + margin-block-end: var(--space-xs); +} + +.footer-credits { + color: var(--color-text-muted); + font-size: 0.875rem; +} + +/* Modal */ +.instructions-modal { + position: fixed; + inset: 0; + background: rgba(10, 10, 18, 0.95); + z-index: 200; + display: flex; + justify-content: center; + align-items: center; + padding: var(--space-md); +} + +.instructions-modal[hidden] { + display: none; +} + +.modal-content { + background: var(--color-card-bg); + padding: var(--space-lg); + max-width: 480px; + width: 100%; + border: 3px solid var(--color-primary); + border-radius: var(--radius-md); + position: relative; + max-height: 90vh; + overflow-y: auto; +} + +.modal-close { + position: absolute; + inset-block-start: var(--space-sm); + inset-inline-end: var(--space-sm); + background: none; + border: none; + color: var(--color-text-muted); + font-size: 1.5rem; + cursor: pointer; + padding: var(--space-xs); + line-height: 1; + transition: color 0.15s ease; +} + +.modal-close:hover, +.modal-close:focus { + color: var(--color-accent); +} + +.modal-close:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 2px; +} + +.modal-content h2 { + font-family: var(--font-display); + font-size: 1.125rem; + color: var(--color-primary); + margin-block-end: var(--space-md); +} + +.modal-content h3 { + font-family: var(--font-display); + font-size: 1rem; + color: var(--color-gold); + margin-block-start: var(--space-md); + margin-block-end: var(--space-sm); +} + +.instructions-list { + display: flex; + flex-direction: column; + gap: var(--space-sm); +} + +.instruction-item { + display: flex; + align-items: center; + gap: var(--space-sm); +} + +.instruction-item .key { + font-family: var(--font-body); + font-weight: 700; + font-size: 0.875rem; + background: var(--color-water); + padding: var(--space-xs) var(--space-sm); + border: 2px solid var(--color-primary); + border-radius: var(--radius-sm); + color: var(--color-primary); + min-width: 56px; + text-align: center; + flex-shrink: 0; +} + +.tips-list { + list-style: none; + display: flex; + flex-direction: column; + gap: var(--space-xs); +} + +.tips-list li { + padding: var(--space-xs) 0; + padding-inline-start: var(--space-sm); + border-inline-start: 3px solid var(--color-secondary); +} + +/* Responsive */ +@media (max-width: 600px) { + .game-hud { + grid-template-columns: repeat(2, 1fr); + gap: var(--space-sm); + } + + .header-content { + flex-wrap: wrap; + justify-content: center; + } + + .hero { + padding-block-start: 100px; + } + + .lighthouse { + inset-inline-end: 5%; + height: 60px; + width: 18px; + } + + .buoy { + width: 10px; + height: 20px; + } +} + +/* Reduced motion */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + + html { + scroll-behavior: auto; + } +} + +/* Focus visible for all interactive elements */ +:focus-visible { + outline: 3px solid var(--color-primary); + outline-offset: 2px; +} + +button:focus:not(:focus-visible) { + outline: none; +} \ No newline at end of file