Compare commits

...

6 Commits

3 changed files with 610 additions and 70 deletions

318
app.js
View File

@@ -221,8 +221,7 @@ function initGame() {
const startBtn = document.getElementById('startGameBtn');
const scoreEl = document.getElementById('scoreVal');
const overlay = document.getElementById('gameOverlay');
const submitBtn = document.getElementById('submitScoreBtn');
const nameInput = document.getElementById('playerName');
const submitBtn = document.getElementById('saveScoreBtn');
const leaderboardList = document.getElementById('leaderboardList');
const timerDisplay = document.getElementById('timerDisplay');
const timeBtns = document.querySelectorAll('.time-btn');
@@ -253,27 +252,202 @@ function initGame() {
});
});
// Leaderboard logic
let leaderboard = JSON.parse(localStorage.getItem('trae_leaderboard') || '[]');
const updateLeaderboardUI = () => {
leaderboardList.innerHTML = '';
leaderboard.sort((a, b) => b.score - a.score).slice(0, 10).forEach((entry, idx) => {
const dateStr = new Date(entry.date).toLocaleDateString();
const durationStr = entry.duration ? `${entry.duration}s` : 'N/A';
const div = document.createElement('div');
div.className = 'leader-item';
div.innerHTML = `
<span class="leader-rank">#${idx + 1}</span>
<span class="leader-name">${entry.name}</span>
<span class="leader-score">${entry.score}</span>
<span class="leader-duration">${durationStr}</span>
<span class="leader-date">${dateStr}</span>
`;
leaderboardList.appendChild(div);
const playersData = JSON.parse(localStorage.getItem('trae_players') || '{}');
let currentUser = null;
const generateSecretCode = () => {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
let code = '';
for (let i = 0; i < 6; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
}
return code;
};
window.registerPlayer = () => {
const username = document.getElementById('regUsername').value.trim() || 'Anonymous';
const pin = generateSecretCode();
if (playersData[username]) {
document.getElementById('regStatus').innerHTML = `<span style="color: #ff4444;">Username already exists! Your PIN: ${playersData[username].code}</span>`;
return;
}
playersData[username] = {
name: username,
code: pin,
games: {},
lastPlayed: new Date().toISOString()
};
localStorage.setItem('trae_players', JSON.stringify(playersData));
currentUser = username;
document.getElementById('regUsername').value = '';
document.getElementById('regStatus').innerHTML = '';
document.getElementById('regStatus').parentElement.querySelector('.input-group').style.display = 'none';
document.getElementById('currentUserInfo').style.display = 'block';
document.getElementById('currentUserDisplay').innerText = username;
document.getElementById('currentUserPin').innerText = pin;
updateLeaderboardUI();
};
window.logoutUser = () => {
currentUser = null;
document.getElementById('currentUserInfo').style.display = 'none';
document.getElementById('regUsername').value = '';
document.getElementById('regStatus').innerHTML = '';
document.getElementById('regStatus').parentElement.querySelector('.input-group').style.display = 'flex';
updateAllLoginPrompts();
};
window.submitScore = (gameType, score) => {
if (!currentUser) {
alert('Please register first to save scores');
return;
}
const player = playersData[currentUser];
if (!player.games[gameType] || player.games[gameType].score < score) {
player.games[gameType] = {
score: score,
date: new Date().toISOString()
};
player.lastPlayed = new Date().toISOString();
localStorage.setItem('trae_players', JSON.stringify(playersData));
updateLeaderboardUI();
alert(`Score saved! ${currentUser}: ${score} in ${gameType}`);
} else {
alert(`Score not saved. Your best for ${gameType} is ${player.games[gameType].score}`);
}
};
const updateAllLoginPrompts = () => {
const prompts = document.querySelectorAll('[id$="LoginPrompt"]');
prompts.forEach(p => {
p.style.display = currentUser ? 'none' : 'block';
});
};
const updateAllOverlays = () => {
if (currentUser) {
document.getElementById('currentUserDisplay').innerText = currentUser;
document.getElementById('currentUserPin').innerText = playersData[currentUser].code;
document.getElementById('currentUserInfo').style.display = 'block';
document.getElementById('regStatus').parentElement.querySelector('.input-group').style.display = 'none';
}
updateAllLoginPrompts();
};
window.copySecretCode = (code) => {
navigator.clipboard.writeText(code).then(() => {
const btn = event.target;
const originalText = btn.innerText;
btn.innerText = 'Copied!';
btn.style.background = 'var(--trae-green)';
setTimeout(() => {
btn.innerText = code;
btn.style.background = '';
}, 1500);
});
};
// Leaderboard logic
let currentTab = 'total';
const updateLeaderboardUI = () => {
leaderboardList.innerHTML = '';
let displayData = [];
if (currentTab === 'total') {
Object.values(playersData).forEach(player => {
const totalScore = Object.values(player.games).reduce((sum, val) => sum + val.score, 0);
displayData.push({
name: player.name,
code: player.code,
totalScore,
game: 'Total',
score: totalScore,
date: player.lastPlayed
});
});
displayData.sort((a, b) => b.totalScore - a.totalScore);
} else {
Object.values(playersData).forEach(player => {
if (player.games[currentTab]) {
displayData.push({
name: player.name,
code: player.code,
game: currentTab,
score: player.games[currentTab].score,
date: player.games[currentTab].date
});
}
});
displayData.sort((a, b) => b.score - a.score);
}
displayData.slice(0, 50).forEach((entry, idx) => {
const dateStr = new Date(entry.date).toLocaleDateString();
const div = document.createElement('div');
div.className = 'leader-item';
if (currentTab === 'total') {
div.innerHTML = `
<span class="leader-rank">#${idx + 1}</span>
<span class="leader-name">${entry.name}</span>
<span class="leader-game">${entry.game}</span>
<span class="leader-score">${entry.totalScore}</span>
<span class="leader-date">${dateStr}</span>
`;
} else {
const gameLabels = {
gift: 'Gift Catcher',
traoom: 'Traoom',
neonpuzzle: 'Neon Puzzle',
rhythm: 'Rhythm Beat',
arena: 'Cosmic Arena',
tetris: 'Crystal Tetris',
platformer: 'Aurora Jumper'
};
div.innerHTML = `
<span class="leader-rank">#${idx + 1}</span>
<span class="leader-name">${entry.name}</span>
<span class="leader-game">${gameLabels[currentTab]}</span>
<span class="leader-score">${entry.score}</span>
<span class="leader-code" onclick="copySecretCode('${entry.code}')">${entry.code}</span>
<span class="leader-date">${dateStr}</span>
`;
}
leaderboardList.appendChild(div);
});
if (displayData.length === 0) {
const emptyMsg = document.createElement('div');
emptyMsg.className = 'leader-item';
emptyMsg.style.justifyContent = 'center';
emptyMsg.innerHTML = `<span style="color: #888;">No scores yet for ${currentTab === 'total' ? 'Total' : currentTab}!</span>`;
leaderboardList.appendChild(emptyMsg);
}
};
const tabButtons = document.querySelectorAll('.tab-btn');
tabButtons.forEach(btn => {
btn.addEventListener('click', () => {
tabButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentTab = btn.dataset.tab;
updateLeaderboardUI();
});
});
document.getElementById('registerBtn')?.addEventListener('click', registerPlayer);
updateAllOverlays();
updateLeaderboardUI();
const startTimer = () => {
timeLeft = selectedDuration;
timerDisplay.innerText = timeLeft + 's';
@@ -301,20 +475,23 @@ function initGame() {
gameActive = false;
if (timerInterval) clearInterval(timerInterval);
document.body.style.overflow = '';
document.getElementById('overlayTitle').innerText = 'GAME OVER';
document.getElementById('gameFinalScore').innerText = score;
overlay.classList.remove('hidden');
document.getElementById('overlayTitle').innerText = `GAME OVER - SCORE: ${score}`;
};
submitBtn.addEventListener('click', () => {
const name = nameInput.value.trim() || 'Anonymous';
leaderboard.push({ name, score, date: new Date().toISOString(), duration: selectedDuration });
localStorage.setItem('trae_leaderboard', JSON.stringify(leaderboard));
updateLeaderboardUI();
submitScore('gift', score);
overlay.classList.add('hidden');
startBtn.style.display = 'block';
document.body.style.overflow = '';
});
document.getElementById('playAgainBtn').addEventListener('click', () => {
overlay.classList.add('hidden');
startBtn.click();
});
const resize = () => {
canvas.width = canvas.parentElement.clientWidth;
canvas.height = canvas.parentElement.clientHeight;
@@ -737,8 +914,7 @@ function initGame() {
// Export/Import Leaderboard
window.exportLeaderboard = () => {
const leaderboard = JSON.parse(localStorage.getItem('trae_leaderboard') || '[]');
const dataStr = JSON.stringify(leaderboard, null, 2);
const dataStr = JSON.stringify(playersData, null, 2);
const blob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
@@ -756,9 +932,13 @@ window.importLeaderboard = (event) => {
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
if (Array.isArray(data)) {
localStorage.setItem('trae_leaderboard', JSON.stringify(data));
location.reload();
if (typeof data === 'object' && data !== null) {
Object.keys(data).forEach(name => {
playersData[name] = data[name];
});
localStorage.setItem('trae_players', JSON.stringify(playersData));
updateLeaderboardUI();
alert(`Imported ${Object.keys(data).length} player(s)!`);
}
} catch (err) {
alert('Invalid file format');
@@ -772,7 +952,6 @@ function initTraoom() {
const canvas = document.getElementById('traoomCanvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startTraoomBtn');
const restartBtn = document.getElementById('restartTraoomBtn');
const killEl = document.getElementById('killVal');
const timeEl = document.getElementById('traoomTime');
const waveEl = document.getElementById('waveVal');
@@ -1189,8 +1368,29 @@ function initTraoom() {
mouse.y = touch.clientY - rect.top;
}, { passive: false });
document.getElementById('saveTraoomScoreBtn').addEventListener('click', () => {
submitScore('traoom', kills);
overlay.classList.add('hidden');
startBtn.style.display = 'block';
});
document.getElementById('playTraoomAgainBtn').addEventListener('click', () => {
overlay.classList.add('hidden');
startBtn.click();
});
document.getElementById('savePuzzleScoreBtn').addEventListener('click', () => {
submitScore('neonpuzzle', moves);
overlay.classList.add('hidden');
startBtn.style.display = 'block';
});
document.getElementById('playPuzzleAgainBtn').addEventListener('click', () => {
overlay.classList.add('hidden');
startBtn.click();
});
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
window.addEventListener('resize', resize);
resize();
@@ -1201,7 +1401,6 @@ function initNeonPuzzle() {
const canvas = document.getElementById('neonpuzzleCanvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startPuzzleBtn');
const nextBtn = document.getElementById('nextPuzzleBtn');
const levelEl = document.getElementById('puzzleLevel');
const movesEl = document.getElementById('puzzleMoves');
const overlay = document.getElementById('neonpuzzleOverlay');
@@ -1378,7 +1577,6 @@ function initNeonPuzzle() {
});
startBtn.addEventListener('click', startGame);
nextBtn.addEventListener('click', nextLevel);
window.addEventListener('resize', resize);
resize();
@@ -1389,7 +1587,6 @@ function initRhythmBeat() {
const canvas = document.getElementById('rhythmCanvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startRhythmBtn');
const restartBtn = document.getElementById('restartRhythmBtn');
const scoreEl = document.getElementById('rhythmScore');
const comboEl = document.getElementById('rhythmCombo');
const streakEl = document.getElementById('rhythmStreak');
@@ -1574,8 +1771,18 @@ function initRhythmBeat() {
}
});
document.getElementById('saveRhythmScoreBtn').addEventListener('click', () => {
submitScore('rhythm', score);
overlay.classList.add('hidden');
startBtn.style.display = 'block';
});
document.getElementById('playRhythmAgainBtn').addEventListener('click', () => {
overlay.classList.add('hidden');
startBtn.click();
});
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
window.addEventListener('resize', resize);
resize();
@@ -1586,7 +1793,6 @@ function initCosmicArena() {
const canvas = document.getElementById('arenaCanvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startArenaBtn');
const restartBtn = document.getElementById('restartArenaBtn');
const hpEl = document.getElementById('arenaHP');
const killsEl = document.getElementById('arenaKills');
const waveEl = document.getElementById('arenaWave');
@@ -1849,8 +2055,18 @@ function initCosmicArena() {
});
});
document.getElementById('saveArenaScoreBtn').addEventListener('click', () => {
submitScore('arena', kills);
overlay.classList.add('hidden');
startBtn.style.display = 'block';
});
document.getElementById('playArenaAgainBtn').addEventListener('click', () => {
overlay.classList.add('hidden');
startBtn.click();
});
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
window.addEventListener('resize', resize);
resize();
@@ -1861,7 +2077,6 @@ function initTetris() {
const canvas = document.getElementById('tetrisCanvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startTetrisBtn');
const restartBtn = document.getElementById('restartTetrisBtn');
const scoreEl = document.getElementById('tetrisScore');
const linesEl = document.getElementById('tetrisLines');
const levelEl = document.getElementById('tetrisLevel');
@@ -2100,8 +2315,18 @@ function initTetris() {
draw();
});
document.getElementById('saveTetrisScoreBtn').addEventListener('click', () => {
submitScore('tetris', score);
overlay.classList.add('hidden');
startBtn.style.display = 'block';
});
document.getElementById('playTetrisAgainBtn').addEventListener('click', () => {
overlay.classList.add('hidden');
startBtn.click();
});
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
window.addEventListener('resize', resize);
resize();
@@ -2112,7 +2337,6 @@ function initPlatformer() {
const canvas = document.getElementById('platformerCanvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startPlatformerBtn');
const restartBtn = document.getElementById('restartPlatformerBtn');
const coinsEl = document.getElementById('coinsVal');
const timeEl = document.getElementById('platformerTime');
const overlay = document.getElementById('platformerOverlay');
@@ -2347,8 +2571,18 @@ function initPlatformer() {
window.addEventListener('keydown', (e) => keys[e.code] = true);
window.addEventListener('keyup', (e) => keys[e.code] = false);
document.getElementById('savePlatformerScoreBtn').addEventListener('click', () => {
submitScore('platformer', collected * 10 + Math.max(0, 100 - parseInt(formatTime(gameTime).replace(':', ''))));
overlay.classList.add('hidden');
startBtn.style.display = 'block';
});
document.getElementById('playPlatformerAgainBtn').addEventListener('click', () => {
overlay.classList.add('hidden');
startBtn.click();
});
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
window.addEventListener('resize', resize);
resize();

View File

@@ -19,6 +19,7 @@
<div class="logo">TRAE <span>AURORA</span></div>
<ul class="nav-links">
<li><a href="#hero">Home</a></li>
<li><a href="#registration">Register</a></li>
<li><a href="#game">Gift Catcher</a></li>
<li><a href="#traoom">Traoom</a></li>
<li><a href="#neonpuzzle">Puzzle</a></li>
@@ -146,6 +147,25 @@
</div>
</section>
<!-- Registration Section -->
<section id="registration" class="glass-section">
<div class="section-card glass">
<h2>🎮 Player Registration</h2>
<p>Register once to save your scores across all games</p>
<div class="input-group" style="justify-content: center; max-width: 400px; margin: 0 auto;">
<input type="text" id="regUsername" placeholder="Username" maxlength="15" style="flex: 1;">
<button id="registerBtn" class="btn-primary">Register</button>
</div>
<p id="regStatus" style="text-align: center; margin-top: 15px; font-weight: bold;"></p>
<div id="currentUserInfo" class="user-info" style="display: none; text-align: center; margin-top: 15px;">
<p>🎉 Your Name: <strong id="currentUserDisplay"></strong></p>
<p style="font-size: 0.8rem; color: #888;">Your PIN: <strong id="currentUserPin"></strong></p>
<p style="font-size: 0.7rem; color: #666; margin-top: 5px;">Use this Name + PIN for all games</p>
<button onclick="logoutUser()" class="btn-secondary" style="margin-top: 10px; background: #ff4444;">Logout</button>
</div>
</div>
</section>
<!-- Game Section -->
<section id="game" class="game-section">
<div class="section-card glass">
@@ -162,10 +182,14 @@
</div>
<div id="gameOverlay" class="game-overlay hidden">
<h3 id="overlayTitle">GAME OVER</h3>
<div class="input-group">
<input type="text" id="playerName" placeholder="Enter your name" maxlength="15">
<button id="submitScoreBtn" class="btn-primary">Save Score</button>
<p id="finalScore">Final Score: <span id="gameFinalScore">0</span></p>
<div id="saveScoreButtons" class="input-group">
<button id="saveScoreBtn" class="btn-primary">Save Score</button>
<button id="playAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
</div>
<p id="loginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
</p>
</div>
<button id="startGameBtn" class="btn-primary">Start Mission</button>
</div>
@@ -175,7 +199,17 @@
<canvas id="gameCanvas"></canvas>
</div>
<div class="leaderboard glass">
<h3>Leaderboard</h3>
<div class="leaderboard-header">
<h3>Global Leaderboard</h3>
<button class="tab-btn active" data-tab="total">Total</button>
<button class="tab-btn" data-tab="gift">Gift Catcher</button>
<button class="tab-btn" data-tab="traoom">Traoom</button>
<button class="tab-btn" data-tab="neonpuzzle">Neon Puzzle</button>
<button class="tab-btn" data-tab="rhythm">Rhythm Beat</button>
<button class="tab-btn" data-tab="arena">Cosmic Arena</button>
<button class="tab-btn" data-tab="tetris">Crystal Tetris</button>
<button class="tab-btn" data-tab="platformer">Aurora Jumper</button>
</div>
<div id="leaderboardList"></div>
<div class="export-buttons">
<button class="export-btn" onclick="exportLeaderboard()">Download Scores</button>
@@ -202,7 +236,13 @@
<h3 id="traoomOverlayTitle">GAME OVER</h3>
<p id="traoomOverlayScore">Bugs Fixed: <span id="finalKills">0</span></p>
<p id="traoomOverlayTime">Time Survived: <span id="finalTime">0:00</span></p>
<button id="restartTraoomBtn" class="btn-primary">Play Again</button>
<div class="input-group">
<button id="saveTraoomScoreBtn" class="btn-primary">Save Score</button>
<button id="playTraoomAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
</div>
<p id="traoomLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
</p>
</div>
<button id="startTraoomBtn" class="btn-primary">Start Bug Hunt</button>
</div>
@@ -228,7 +268,13 @@
<div id="neonpuzzleOverlay" class="game-overlay hidden">
<h3 id="puzzleOverlayTitle">LEVEL COMPLETE</h3>
<p id="puzzleOverlayScore">Moves: <span id="finalMoves">0</span></p>
<button id="nextPuzzleBtn" class="btn-primary">Next Level</button>
<div class="input-group">
<button id="savePuzzleScoreBtn" class="btn-primary">Save Score</button>
<button id="playPuzzleAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
</div>
<p id="puzzleLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
</p>
</div>
<button id="startPuzzleBtn" class="btn-primary">Start Puzzle</button>
</div>
@@ -255,7 +301,13 @@
<div id="rhythmOverlay" class="game-overlay hidden">
<h3 id="rhythmOverlayTitle">BEAT OVER</h3>
<p id="rhythmOverlayScore">Final Score: <span id="finalRhythmScore">0</span></p>
<button id="restartRhythmBtn" class="btn-primary">Play Again</button>
<div class="input-group">
<button id="saveRhythmScoreBtn" class="btn-primary">Save Score</button>
<button id="playRhythmAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
</div>
<p id="rhythmLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
</p>
</div>
<button id="startRhythmBtn" class="btn-primary">Start Rhythm</button>
</div>
@@ -282,7 +334,13 @@
<div id="arenaOverlay" class="game-overlay hidden">
<h3 id="arenaOverlayTitle">GAME OVER</h3>
<p id="arenaOverlayScore">Enemies Defeated: <span id="finalArenaKills">0</span></p>
<button id="restartArenaBtn" class="btn-primary">Play Again</button>
<div class="input-group">
<button id="saveArenaScoreBtn" class="btn-primary">Save Score</button>
<button id="playArenaAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
</div>
<p id="arenaLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
</p>
</div>
<button id="startArenaBtn" class="btn-primary">Enter Arena</button>
</div>
@@ -309,7 +367,13 @@
<div id="tetrisOverlay" class="game-overlay hidden">
<h3 id="tetrisOverlayTitle">GAME OVER</h3>
<p id="tetrisOverlayScore">Final Score: <span id="finalTetrisScore">0</span></p>
<button id="restartTetrisBtn" class="btn-primary">Play Again</button>
<div class="input-group">
<button id="saveTetrisScoreBtn" class="btn-primary">Save Score</button>
<button id="playTetrisAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
</div>
<p id="tetrisLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
</p>
</div>
<button id="startTetrisBtn" class="btn-primary">Start Game</button>
</div>
@@ -336,7 +400,13 @@
<h3 id="platformerOverlayTitle">LEVEL COMPLETE</h3>
<p id="platformerOverlayScore">Coins: <span id="finalCoins">0</span></p>
<p id="platformerOverlayTime">Time: <span id="finalPlatformerTime">0:00</span></p>
<button id="restartPlatformerBtn" class="btn-primary">Play Again</button>
<div class="input-group">
<button id="savePlatformerScoreBtn" class="btn-primary">Save Score</button>
<button id="playPlatformerAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
</div>
<p id="platformerLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
</p>
</div>
<button id="startPlatformerBtn" class="btn-primary">Start Game</button>
</div>

272
style.css
View File

@@ -35,7 +35,7 @@ body {
}
.app-container {
max-width: 1200px;
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
width: 100%;
@@ -913,30 +913,158 @@ body {
text-align: left;
}
.leaderboard h3 {
font-family: var(--font-accent);
text-align: center;
.leaderboard-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
margin-bottom: 20px;
}
.leaderboard-header h3 {
font-family: var(--font-accent);
color: var(--aurora-cyan);
margin: 0;
}
.tab-btn {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
color: var(--text-primary);
padding: 8px 16px;
border-radius: 12px;
cursor: pointer;
font-family: var(--font-accent);
font-size: 0.85rem;
transition: all 0.3s ease;
}
.tab-btn:hover {
background: rgba(0, 255, 102, 0.2);
border-color: var(--trae-green);
}
.tab-btn.active {
background: var(--trae-green);
color: var(--bg-dark);
border-color: var(--trae-green);
}
.leader-item {
display: flex;
justify-content: space-between;
padding: 10px 20px;
padding: 12px 20px;
border-bottom: 1px solid var(--glass-border);
font-family: var(--font-accent);
align-items: center;
transition: background 0.3s ease;
}
.leader-item:hover {
background: rgba(0, 255, 102, 0.1);
}
.leader-item:last-child {
border: none;
}
.leader-rank { color: var(--trae-green); }
.leader-name { flex: 1; margin-left: 20px; }
.leader-score { color: var(--aurora-cyan); }
.leader-duration { color: #ff9900; font-size: 0.85rem; margin-left: 10px; }
.leader-date { color: #888; font-size: 0.8rem; margin-left: 10px; }
.leader-rank {
color: var(--trae-green);
font-weight: bold;
min-width: 40px;
}
.leader-name {
flex: 1;
margin-left: 20px;
min-width: 0;
}
.leader-score {
color: var(--aurora-cyan);
font-weight: bold;
min-width: 80px;
text-align: center;
}
.leader-game {
color: #ff6600;
font-size: 0.8rem;
padding: 4px 10px;
border-radius: 8px;
background: rgba(255, 102, 0, 0.1);
margin-right: 10px;
}
.leader-code {
color: #7000ff;
font-size: 0.75rem;
margin-left: 10px;
padding: 4px 10px;
border-radius: 6px;
background: rgba(112, 0, 255, 0.1);
cursor: pointer;
transition: all 0.3s ease;
}
.leader-code:hover {
background: rgba(112, 0, 255, 0.2);
}
.leader-duration {
color: #ff9900;
font-size: 0.85rem;
margin-left: 10px;
}
.leader-date {
color: #888;
font-size: 0.8rem;
margin-left: 10px;
}
.code-hint {
color: #888;
font-size: 0.8rem;
text-align: center;
margin-top: 10px;
font-style: italic;
}
.pin-explainer {
color: var(--aurora-cyan);
font-size: 0.75rem;
text-align: center;
margin-top: 8px;
padding: 8px 12px;
border-radius: 8px;
background: rgba(0, 255, 255, 0.1);
border: 1px solid rgba(0, 255, 255, 0.3);
}
.export-buttons {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 20px;
}
.export-btn {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
color: var(--text-primary);
padding: 10px 20px;
border-radius: 12px;
cursor: pointer;
font-family: var(--font-accent);
transition: all 0.3s ease;
}
.export-btn:hover {
background: rgba(0, 255, 102, 0.2);
border-color: var(--trae-green);
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.9); }
@@ -1039,24 +1167,132 @@ footer span, .glm-link, .author-link, .gemini-link {
.forge-input-group { flex-direction: column; }
.btn-secondary { margin-left: 0; margin-top: 15px; }
.app-container { padding: 0 10px; }
.hero h1 { font-size: 2rem; }
.app-icons { gap: 15px; }
.app-icon-inner { width: 60px; height: 60px; font-size: 30px; }
.app-label { font-size: 0.75rem; }
.game-container {
padding: 15px;
gap: 20px;
}
.section-card {
padding: 20px;
}
.game-container, .traoom-container, .neonpuzzle-container, .rhythm-container, .arena-container, .platformer-container {
height: 50vh;
min-height: 350px;
}
canvas {
width: 100%;
height: 100%;
}
.game-overlay {
padding: 25px 20px;
margin: 10px;
}
.input-group {
flex-direction: column;
gap: 10px;
}
.input-group input {
width: 100%;
padding: 10px;
font-size: 0.9rem;
}
.btn-primary, .export-btn {
padding: 10px 15px;
font-size: 0.9rem;
width: 100%;
}
.leaderboard-header {
flex-direction: column;
align-items: stretch;
}
.tab-btn {
padding: 8px 10px;
font-size: 0.75rem;
}
.leader-item {
flex-wrap: wrap;
padding: 10px;
gap: 5px;
}
.leader-game, .leader-code {
display: none;
}
.game-timer {
top: 10px;
right: 10px;
font-size: 1rem;
}
.game-ui > div {
flex-direction: column;
gap: 5px;
font-size: 0.8rem;
}
.traoom-container {
height: 60vh;
min-height: 400px;
height: 50vh;
min-height: 350px;
}
.traoom-stats {
flex-direction: column;
gap: 8px;
left: 10px;
top: 10px;
gap: 5px;
left: 8px;
top: 8px;
}
.traoom-stat {
padding: 6px 12px;
padding: 5px 10px;
font-size: 0.75rem;
}
.traoom-controls p, .neonpuzzle-controls p, .rhythm-container p, .arena-container p {
font-size: 0.75rem;
}
footer p {
font-size: 0.85rem;
}
.traoom-controls p {
font-size: 0.8rem;
.export-buttons {
flex-direction: column;
}
}
@media (max-width: 480px) {
.hero h1 { font-size: 1.5rem; }
.app-icons { gap: 10px; }
.app-icon-inner { width: 50px; height: 50px; font-size: 24px; }
.app-label { font-size: 0.7rem; }
.game-container, .traoom-container, .neonpuzzle-container, .rhythm-container, .arena-container, .platformer-container {
height: 45vh;
min-height: 300px;
}
.game-overlay h3 {
font-size: 1.2rem;
}
.leaderboard-header h3 {
font-size: 1.2rem;
}
}