Add final results modal, active rules display, and UI improvements
- Add big final results modal at game end with rankings and share button - Add active rules bar showing enabled variants during gameplay - Increase spacing between player cards and opponents row - Add Wolfpack bonus rule (2 pairs of Jacks = -5 pts) - Change joker options to radio buttons (None/Standard/Lucky Swing/Eagle-Eye) - Update Eagle-Eye jokers: +2 pts unpaired, -4 pts paired - Add card flip animation on discard pile - Redesign waiting room layout with side-by-side columns - Style card backs with red Bee-style diamond crosshatch pattern - Compact standings panel to show top 4 per category - Various CSS polish and responsive improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
349
client/app.js
349
client/app.js
@@ -62,6 +62,15 @@ class GolfGame {
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.2);
|
||||
oscillator.start(ctx.currentTime);
|
||||
oscillator.stop(ctx.currentTime + 0.2);
|
||||
} else if (type === 'flip') {
|
||||
// Sharp quick click for card flips
|
||||
oscillator.type = 'square';
|
||||
oscillator.frequency.setValueAtTime(1800, ctx.currentTime);
|
||||
oscillator.frequency.exponentialRampToValueAtTime(600, ctx.currentTime + 0.02);
|
||||
gainNode.gain.setValueAtTime(0.12, ctx.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.025);
|
||||
oscillator.start(ctx.currentTime);
|
||||
oscillator.stop(ctx.currentTime + 0.025);
|
||||
} else if (type === 'shuffle') {
|
||||
// Multiple quick sounds to simulate shuffling
|
||||
for (let i = 0; i < 8; i++) {
|
||||
@@ -102,6 +111,7 @@ class GolfGame {
|
||||
|
||||
// Waiting room elements
|
||||
this.displayRoomCode = document.getElementById('display-room-code');
|
||||
this.copyRoomCodeBtn = document.getElementById('copy-room-code');
|
||||
this.playersList = document.getElementById('players-list');
|
||||
this.hostSettings = document.getElementById('host-settings');
|
||||
this.waitingMessage = document.getElementById('waiting-message');
|
||||
@@ -111,21 +121,15 @@ class GolfGame {
|
||||
this.initialFlipsSelect = document.getElementById('initial-flips');
|
||||
this.flipOnDiscardCheckbox = document.getElementById('flip-on-discard');
|
||||
this.knockPenaltyCheckbox = document.getElementById('knock-penalty');
|
||||
this.jokerModeSelect = document.getElementById('joker-mode');
|
||||
// House Rules - Point Modifiers
|
||||
this.superKingsCheckbox = document.getElementById('super-kings');
|
||||
this.luckySevensCheckbox = document.getElementById('lucky-sevens');
|
||||
this.tenPennyCheckbox = document.getElementById('ten-penny');
|
||||
// House Rules - Bonuses/Penalties
|
||||
this.knockBonusCheckbox = document.getElementById('knock-bonus');
|
||||
this.underdogBonusCheckbox = document.getElementById('underdog-bonus');
|
||||
this.tiedShameCheckbox = document.getElementById('tied-shame');
|
||||
this.blackjackCheckbox = document.getElementById('blackjack');
|
||||
// House Rules - Gameplay Twists
|
||||
this.queensWildCheckbox = document.getElementById('queens-wild');
|
||||
this.fourOfAKindCheckbox = document.getElementById('four-of-a-kind');
|
||||
this.eagleEyeCheckbox = document.getElementById('eagle-eye');
|
||||
this.eagleEyeLabel = document.getElementById('eagle-eye-label');
|
||||
this.wolfpackCheckbox = document.getElementById('wolfpack');
|
||||
this.startGameBtn = document.getElementById('start-game-btn');
|
||||
this.leaveRoomBtn = document.getElementById('leave-room-btn');
|
||||
this.addCpuBtn = document.getElementById('add-cpu-btn');
|
||||
@@ -157,6 +161,9 @@ class GolfGame {
|
||||
this.gameButtons = document.getElementById('game-buttons');
|
||||
this.nextRoundBtn = document.getElementById('next-round-btn');
|
||||
this.newGameBtn = document.getElementById('new-game-btn');
|
||||
this.leaveGameBtn = document.getElementById('leave-game-btn');
|
||||
this.activeRulesBar = document.getElementById('active-rules-bar');
|
||||
this.activeRulesList = document.getElementById('active-rules-list');
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
@@ -174,6 +181,13 @@ class GolfGame {
|
||||
this.cancelCpuBtn.addEventListener('click', () => { this.playSound('click'); this.hideCpuSelect(); });
|
||||
this.addSelectedCpusBtn.addEventListener('click', () => { this.playSound('success'); this.addSelectedCpus(); });
|
||||
this.muteBtn.addEventListener('click', () => this.toggleSound());
|
||||
this.leaveGameBtn.addEventListener('click', () => { this.playSound('click'); this.leaveGame(); });
|
||||
|
||||
// Copy room code to clipboard
|
||||
this.copyRoomCodeBtn.addEventListener('click', () => {
|
||||
this.playSound('click');
|
||||
this.copyRoomCode();
|
||||
});
|
||||
|
||||
// Enter key handlers
|
||||
this.playerNameInput.addEventListener('keypress', (e) => {
|
||||
@@ -188,20 +202,6 @@ class GolfGame {
|
||||
e.target.value = e.target.value.toUpperCase();
|
||||
});
|
||||
|
||||
// Eagle Eye only works with Standard Jokers (need 2 to pair them)
|
||||
const updateEagleEyeVisibility = () => {
|
||||
const isStandardJokers = this.jokerModeSelect.value === 'standard';
|
||||
if (isStandardJokers) {
|
||||
this.eagleEyeLabel.classList.remove('hidden');
|
||||
} else {
|
||||
this.eagleEyeLabel.classList.add('hidden');
|
||||
this.eagleEyeCheckbox.checked = false;
|
||||
}
|
||||
};
|
||||
this.jokerModeSelect.addEventListener('change', updateEagleEyeVisibility);
|
||||
// Check initial state
|
||||
updateEagleEyeVisibility();
|
||||
|
||||
// Update deck recommendation when deck selection changes
|
||||
this.numDecksSelect.addEventListener('change', () => {
|
||||
const playerCount = this.currentPlayers ? this.currentPlayers.length : 0;
|
||||
@@ -322,6 +322,15 @@ class GolfGame {
|
||||
this.showScoreboard(data.final_scores, true, data.rankings);
|
||||
break;
|
||||
|
||||
case 'game_ended':
|
||||
// Host ended the game or player was kicked
|
||||
this.ws.close();
|
||||
this.showLobby();
|
||||
if (data.reason) {
|
||||
this.showError(data.reason);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
this.showError(data.message);
|
||||
break;
|
||||
@@ -358,6 +367,26 @@ class GolfGame {
|
||||
this.showLobby();
|
||||
}
|
||||
|
||||
copyRoomCode() {
|
||||
if (!this.roomCode) return;
|
||||
|
||||
navigator.clipboard.writeText(this.roomCode).then(() => {
|
||||
// Show brief visual feedback
|
||||
const originalText = this.copyRoomCodeBtn.textContent;
|
||||
this.copyRoomCodeBtn.textContent = '✓';
|
||||
setTimeout(() => {
|
||||
this.copyRoomCodeBtn.textContent = originalText;
|
||||
}, 1500);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy room code:', err);
|
||||
// Fallback: select the text for manual copy
|
||||
const range = document.createRange();
|
||||
range.selectNode(this.displayRoomCode);
|
||||
window.getSelection().removeAllRanges();
|
||||
window.getSelection().addRange(range);
|
||||
});
|
||||
}
|
||||
|
||||
startGame() {
|
||||
const decks = parseInt(this.numDecksSelect.value);
|
||||
const rounds = parseInt(this.numRoundsSelect.value);
|
||||
@@ -367,14 +396,14 @@ class GolfGame {
|
||||
const flip_on_discard = this.flipOnDiscardCheckbox.checked;
|
||||
const knock_penalty = this.knockPenaltyCheckbox.checked;
|
||||
|
||||
// Joker mode
|
||||
const joker_mode = this.jokerModeSelect.value;
|
||||
// Joker mode (radio buttons)
|
||||
const joker_mode = document.querySelector('input[name="joker-mode"]:checked').value;
|
||||
const use_jokers = joker_mode !== 'none';
|
||||
const lucky_swing = joker_mode === 'lucky-swing';
|
||||
const eagle_eye = joker_mode === 'eagle-eye';
|
||||
|
||||
// House Rules - Point Modifiers
|
||||
const super_kings = this.superKingsCheckbox.checked;
|
||||
const lucky_sevens = this.luckySevensCheckbox.checked;
|
||||
const ten_penny = this.tenPennyCheckbox.checked;
|
||||
|
||||
// House Rules - Bonuses/Penalties
|
||||
@@ -382,11 +411,7 @@ class GolfGame {
|
||||
const underdog_bonus = this.underdogBonusCheckbox.checked;
|
||||
const tied_shame = this.tiedShameCheckbox.checked;
|
||||
const blackjack = this.blackjackCheckbox.checked;
|
||||
|
||||
// House Rules - Gameplay Twists
|
||||
const queens_wild = this.queensWildCheckbox.checked;
|
||||
const four_of_a_kind = this.fourOfAKindCheckbox.checked;
|
||||
const eagle_eye = this.eagleEyeCheckbox.checked;
|
||||
const wolfpack = this.wolfpackCheckbox.checked;
|
||||
|
||||
this.send({
|
||||
type: 'start_game',
|
||||
@@ -398,15 +423,13 @@ class GolfGame {
|
||||
use_jokers,
|
||||
lucky_swing,
|
||||
super_kings,
|
||||
lucky_sevens,
|
||||
ten_penny,
|
||||
knock_bonus,
|
||||
underdog_bonus,
|
||||
tied_shame,
|
||||
blackjack,
|
||||
queens_wild,
|
||||
four_of_a_kind,
|
||||
eagle_eye
|
||||
eagle_eye,
|
||||
wolfpack
|
||||
});
|
||||
}
|
||||
|
||||
@@ -549,7 +572,7 @@ class GolfGame {
|
||||
if (this.gameState.waiting_for_initial_flip) {
|
||||
if (card.face_up) return;
|
||||
|
||||
this.playSound('card');
|
||||
this.playSound('flip');
|
||||
const requiredFlips = this.gameState.initial_flips || 2;
|
||||
|
||||
if (this.selectedCards.includes(position)) {
|
||||
@@ -594,6 +617,22 @@ class GolfGame {
|
||||
this.leaveRoom();
|
||||
}
|
||||
|
||||
leaveGame() {
|
||||
if (this.isHost) {
|
||||
// Host ending game affects everyone
|
||||
if (confirm('End game for all players?')) {
|
||||
this.send({ type: 'end_game' });
|
||||
}
|
||||
} else {
|
||||
// Regular player just leaves
|
||||
if (confirm('Leave this game?')) {
|
||||
this.send({ type: 'leave_game' });
|
||||
this.ws.close();
|
||||
this.showLobby();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UI Helpers
|
||||
showScreen(screen) {
|
||||
this.lobbyScreen.classList.remove('active');
|
||||
@@ -630,6 +669,28 @@ class GolfGame {
|
||||
this.drawnCard = null;
|
||||
this.selectedCards = [];
|
||||
this.waitingForFlip = false;
|
||||
// Update leave button text based on role
|
||||
this.leaveGameBtn.textContent = this.isHost ? 'End Game' : 'Leave';
|
||||
// Update active rules bar
|
||||
this.updateActiveRulesBar();
|
||||
}
|
||||
|
||||
updateActiveRulesBar() {
|
||||
if (!this.gameState || !this.gameState.active_rules) {
|
||||
this.activeRulesBar.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
const rules = this.gameState.active_rules;
|
||||
if (rules.length === 0) {
|
||||
this.activeRulesBar.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeRulesList.innerHTML = rules
|
||||
.map(rule => `<span class="rule-tag">${rule}</span>`)
|
||||
.join('');
|
||||
this.activeRulesBar.classList.remove('hidden');
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
@@ -825,6 +886,15 @@ class GolfGame {
|
||||
// Update discard pile
|
||||
if (this.gameState.discard_top) {
|
||||
const discardCard = this.gameState.discard_top;
|
||||
const cardKey = `${discardCard.rank}-${discardCard.suit}`;
|
||||
|
||||
// Animate if discard changed
|
||||
if (this.lastDiscardKey && this.lastDiscardKey !== cardKey) {
|
||||
this.discard.classList.add('card-flip-in');
|
||||
setTimeout(() => this.discard.classList.remove('card-flip-in'), 400);
|
||||
}
|
||||
this.lastDiscardKey = cardKey;
|
||||
|
||||
this.discard.classList.add('has-card', 'card-front');
|
||||
this.discard.classList.remove('card-back', 'red', 'black', 'joker');
|
||||
|
||||
@@ -839,6 +909,7 @@ class GolfGame {
|
||||
} else {
|
||||
this.discard.classList.remove('has-card', 'card-front', 'red', 'black', 'joker');
|
||||
this.discardContent.innerHTML = '';
|
||||
this.lastDiscardKey = null;
|
||||
}
|
||||
|
||||
// Update deck/discard clickability and visual state
|
||||
@@ -864,9 +935,10 @@ class GolfGame {
|
||||
}
|
||||
|
||||
const displayName = player.name.length > 12 ? player.name.substring(0, 11) + '…' : player.name;
|
||||
const showingScore = this.calculateShowingScore(player.cards);
|
||||
|
||||
div.innerHTML = `
|
||||
<h4>${displayName}${player.all_face_up ? ' ✓' : ''}</h4>
|
||||
<h4>${displayName}${player.all_face_up ? ' ✓' : ''}<span class="opponent-showing">${showingScore}</span></h4>
|
||||
<div class="card-grid">
|
||||
${player.cards.map(card => this.renderCard(card, false, false)).join('')}
|
||||
</div>
|
||||
@@ -960,34 +1032,48 @@ class GolfGame {
|
||||
updateStandings() {
|
||||
if (!this.gameState || !this.standingsList) return;
|
||||
|
||||
// Sort players by total score (lowest is best in golf)
|
||||
const sorted = [...this.gameState.players].sort((a, b) => a.total_score - b.total_score);
|
||||
// Sort by total points (lowest wins) - top 4
|
||||
const byPoints = [...this.gameState.players].sort((a, b) => a.total_score - b.total_score).slice(0, 4);
|
||||
// Sort by holes won (most wins) - top 4
|
||||
const byHoles = [...this.gameState.players].sort((a, b) => b.rounds_won - a.rounds_won).slice(0, 4);
|
||||
|
||||
this.standingsList.innerHTML = '';
|
||||
|
||||
sorted.forEach((player, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'standing-row';
|
||||
|
||||
if (index === 0 && player.total_score < sorted[sorted.length - 1]?.total_score) {
|
||||
div.classList.add('leader');
|
||||
// Build points ranking
|
||||
let pointsRank = 0;
|
||||
let prevPoints = null;
|
||||
const pointsHtml = byPoints.map((p, i) => {
|
||||
if (p.total_score !== prevPoints) {
|
||||
pointsRank = i;
|
||||
prevPoints = p.total_score;
|
||||
}
|
||||
if (player.id === this.playerId) {
|
||||
div.classList.add('you');
|
||||
const medal = pointsRank === 0 ? '🥇' : pointsRank === 1 ? '🥈' : pointsRank === 2 ? '🥉' : '4.';
|
||||
const name = p.name.length > 8 ? p.name.substring(0, 7) + '…' : p.name;
|
||||
return `<div class="rank-row ${pointsRank === 0 ? 'leader' : ''}"><span class="rank-pos">${medal}</span><span class="rank-name">${name}</span><span class="rank-val">${p.total_score}pt</span></div>`;
|
||||
}).join('');
|
||||
|
||||
// Build holes won ranking
|
||||
let holesRank = 0;
|
||||
let prevHoles = null;
|
||||
const holesHtml = byHoles.map((p, i) => {
|
||||
if (p.rounds_won !== prevHoles) {
|
||||
holesRank = i;
|
||||
prevHoles = p.rounds_won;
|
||||
}
|
||||
const medal = p.rounds_won === 0 ? '-' :
|
||||
holesRank === 0 ? '🥇' : holesRank === 1 ? '🥈' : holesRank === 2 ? '🥉' : '4.';
|
||||
const name = p.name.length > 8 ? p.name.substring(0, 7) + '…' : p.name;
|
||||
return `<div class="rank-row ${holesRank === 0 && p.rounds_won > 0 ? 'leader' : ''}"><span class="rank-pos">${medal}</span><span class="rank-name">${name}</span><span class="rank-val">${p.rounds_won}W</span></div>`;
|
||||
}).join('');
|
||||
|
||||
const displayName = player.name.length > 12
|
||||
? player.name.substring(0, 11) + '…'
|
||||
: player.name;
|
||||
|
||||
div.innerHTML = `
|
||||
<span class="standing-pos">${index + 1}.</span>
|
||||
<span class="standing-name">${displayName}</span>
|
||||
<span class="standing-score">${player.total_score} pts</span>
|
||||
`;
|
||||
|
||||
this.standingsList.appendChild(div);
|
||||
});
|
||||
this.standingsList.innerHTML = `
|
||||
<div class="standings-section">
|
||||
<div class="standings-title">By Score</div>
|
||||
${pointsHtml}
|
||||
</div>
|
||||
<div class="standings-section">
|
||||
<div class="standings-title">By Holes</div>
|
||||
${holesHtml}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderCard(card, clickable, selected) {
|
||||
@@ -1043,16 +1129,20 @@ class GolfGame {
|
||||
this.scoreTable.appendChild(tr);
|
||||
});
|
||||
|
||||
// Show rankings announcement
|
||||
this.showRankingsAnnouncement(rankings, isFinal);
|
||||
// Show rankings announcement only for final results
|
||||
const existingAnnouncement = document.getElementById('rankings-announcement');
|
||||
if (existingAnnouncement) existingAnnouncement.remove();
|
||||
|
||||
if (isFinal) {
|
||||
// Show big final results modal instead of side panel stuff
|
||||
this.showFinalResultsModal(rankings, scores);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show game buttons
|
||||
this.gameButtons.classList.remove('hidden');
|
||||
|
||||
if (isFinal) {
|
||||
this.nextRoundBtn.classList.add('hidden');
|
||||
this.newGameBtn.classList.remove('hidden');
|
||||
} else if (this.isHost) {
|
||||
if (this.isHost) {
|
||||
this.nextRoundBtn.classList.remove('hidden');
|
||||
this.newGameBtn.classList.add('hidden');
|
||||
} else {
|
||||
@@ -1065,6 +1155,8 @@ class GolfGame {
|
||||
// Remove existing announcement if any
|
||||
const existing = document.getElementById('rankings-announcement');
|
||||
if (existing) existing.remove();
|
||||
const existingVictory = document.getElementById('double-victory-banner');
|
||||
if (existingVictory) existingVictory.remove();
|
||||
|
||||
if (!rankings) return;
|
||||
|
||||
@@ -1109,13 +1201,20 @@ class GolfGame {
|
||||
return `<div class="rank-row ${holesRank === 0 && p.rounds_won > 0 ? 'leader' : ''}"><span class="rank-pos">${medal}</span><span class="rank-name">${name}</span><span class="rank-val">${p.rounds_won}W</span></div>`;
|
||||
}).join('');
|
||||
|
||||
const doubleVictoryHtml = isDoubleVictory
|
||||
? `<div class="double-victory">DOUBLE VICTORY! ${pointsLeader.name}</div>`
|
||||
: '';
|
||||
// If double victory, show banner above the left panel (standings)
|
||||
if (isDoubleVictory) {
|
||||
const victoryBanner = document.createElement('div');
|
||||
victoryBanner.id = 'double-victory-banner';
|
||||
victoryBanner.className = 'double-victory';
|
||||
victoryBanner.textContent = `DOUBLE VICTORY! ${pointsLeader.name}`;
|
||||
const standingsPanel = document.getElementById('standings-panel');
|
||||
if (standingsPanel) {
|
||||
standingsPanel.insertBefore(victoryBanner, standingsPanel.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
announcement.innerHTML = `
|
||||
<h3>${title}</h3>
|
||||
${doubleVictoryHtml}
|
||||
<div class="rankings-columns">
|
||||
<div class="ranking-section">
|
||||
<h4>Points (Low Wins)</h4>
|
||||
@@ -1131,6 +1230,118 @@ class GolfGame {
|
||||
// Insert before the scoreboard
|
||||
this.scoreboard.insertBefore(announcement, this.scoreboard.firstChild);
|
||||
}
|
||||
|
||||
showFinalResultsModal(rankings, scores) {
|
||||
// Hide side panels
|
||||
const standingsPanel = document.getElementById('standings-panel');
|
||||
const scoreboard = document.getElementById('scoreboard');
|
||||
if (standingsPanel) standingsPanel.classList.add('hidden');
|
||||
if (scoreboard) scoreboard.classList.add('hidden');
|
||||
|
||||
// Remove existing modal if any
|
||||
const existing = document.getElementById('final-results-modal');
|
||||
if (existing) existing.remove();
|
||||
|
||||
// Determine winners
|
||||
const pointsLeader = rankings.by_points[0];
|
||||
const holesLeader = rankings.by_holes_won[0];
|
||||
const isDoubleVictory = pointsLeader && holesLeader &&
|
||||
pointsLeader.name === holesLeader.name &&
|
||||
holesLeader.rounds_won > 0;
|
||||
|
||||
// Build points ranking
|
||||
let pointsRank = 0;
|
||||
let prevPoints = null;
|
||||
const pointsHtml = rankings.by_points.map((p, i) => {
|
||||
if (p.total !== prevPoints) {
|
||||
pointsRank = i;
|
||||
prevPoints = p.total;
|
||||
}
|
||||
const medal = pointsRank === 0 ? '🥇' : pointsRank === 1 ? '🥈' : pointsRank === 2 ? '🥉' : `${pointsRank + 1}.`;
|
||||
return `<div class="final-rank-row ${pointsRank === 0 ? 'winner' : ''}"><span class="rank-pos">${medal}</span><span class="rank-name">${p.name}</span><span class="rank-val">${p.total} pts</span></div>`;
|
||||
}).join('');
|
||||
|
||||
// Build holes ranking
|
||||
let holesRank = 0;
|
||||
let prevHoles = null;
|
||||
const holesHtml = rankings.by_holes_won.map((p, i) => {
|
||||
if (p.rounds_won !== prevHoles) {
|
||||
holesRank = i;
|
||||
prevHoles = p.rounds_won;
|
||||
}
|
||||
const medal = p.rounds_won === 0 ? '-' :
|
||||
holesRank === 0 ? '🥇' : holesRank === 1 ? '🥈' : holesRank === 2 ? '🥉' : `${holesRank + 1}.`;
|
||||
return `<div class="final-rank-row ${holesRank === 0 && p.rounds_won > 0 ? 'winner' : ''}"><span class="rank-pos">${medal}</span><span class="rank-name">${p.name}</span><span class="rank-val">${p.rounds_won} wins</span></div>`;
|
||||
}).join('');
|
||||
|
||||
// Build share text
|
||||
const shareText = this.buildShareText(rankings, isDoubleVictory);
|
||||
|
||||
// Create modal
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'final-results-modal';
|
||||
modal.className = 'final-results-modal';
|
||||
modal.innerHTML = `
|
||||
<div class="final-results-content">
|
||||
<h2>🏌️ Final Results</h2>
|
||||
${isDoubleVictory ? `<div class="double-victory-banner">🏆 DOUBLE VICTORY: ${pointsLeader.name} 🏆</div>` : ''}
|
||||
<div class="final-rankings">
|
||||
<div class="final-ranking-section">
|
||||
<h3>By Points (Low Wins)</h3>
|
||||
${pointsHtml}
|
||||
</div>
|
||||
<div class="final-ranking-section">
|
||||
<h3>By Holes Won</h3>
|
||||
${holesHtml}
|
||||
</div>
|
||||
</div>
|
||||
<div class="final-actions">
|
||||
<button class="btn btn-primary" id="share-results-btn">📋 Copy Results</button>
|
||||
<button class="btn btn-secondary" id="close-results-btn">New Game</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Bind button events
|
||||
document.getElementById('share-results-btn').addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(shareText).then(() => {
|
||||
const btn = document.getElementById('share-results-btn');
|
||||
btn.textContent = '✓ Copied!';
|
||||
setTimeout(() => btn.textContent = '📋 Copy Results', 2000);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('close-results-btn').addEventListener('click', () => {
|
||||
modal.remove();
|
||||
this.leaveRoom();
|
||||
});
|
||||
}
|
||||
|
||||
buildShareText(rankings, isDoubleVictory) {
|
||||
let text = '🏌️ Golf Card Game Results\n';
|
||||
text += '═══════════════════════\n\n';
|
||||
|
||||
if (isDoubleVictory) {
|
||||
text += `🏆 DOUBLE VICTORY: ${rankings.by_points[0].name}!\n\n`;
|
||||
}
|
||||
|
||||
text += '📊 By Points (Low Wins):\n';
|
||||
rankings.by_points.forEach((p, i) => {
|
||||
const medal = i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : `${i + 1}.`;
|
||||
text += `${medal} ${p.name}: ${p.total} pts\n`;
|
||||
});
|
||||
|
||||
text += '\n⛳ By Holes Won:\n';
|
||||
rankings.by_holes_won.forEach((p, i) => {
|
||||
const medal = p.rounds_won === 0 ? '-' : i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : `${i + 1}.`;
|
||||
text += `${medal} ${p.name}: ${p.rounds_won} wins\n`;
|
||||
});
|
||||
|
||||
text += '\nPlayed at golf.game';
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize game when page loads
|
||||
|
||||
@@ -38,151 +38,164 @@
|
||||
|
||||
<!-- Waiting Room Screen -->
|
||||
<div id="waiting-screen" class="screen">
|
||||
<h2>Room: <span id="display-room-code"></span></h2>
|
||||
|
||||
<div class="players-list">
|
||||
<h3>Players</h3>
|
||||
<ul id="players-list"></ul>
|
||||
<div class="room-code-banner">
|
||||
<span class="room-code-label">ROOM CODE</span>
|
||||
<span class="room-code-value" id="display-room-code"></span>
|
||||
<button class="room-code-copy" id="copy-room-code" title="Copy to clipboard">📋</button>
|
||||
</div>
|
||||
|
||||
<div id="host-settings" class="settings hidden">
|
||||
<h3>Game Settings</h3>
|
||||
<div class="form-group">
|
||||
<label>CPU Players</label>
|
||||
<div class="cpu-controls">
|
||||
<button id="remove-cpu-btn" class="btn btn-small btn-secondary">- CPU</button>
|
||||
<button id="add-cpu-btn" class="btn btn-small btn-primary">+ CPU</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="num-decks">Number of Decks</label>
|
||||
<select id="num-decks">
|
||||
<option value="1">1 Deck (2-4 players)</option>
|
||||
<option value="2">2 Decks (4-6 players)</option>
|
||||
<option value="3">3 Decks (5-6 players)</option>
|
||||
</select>
|
||||
<p id="deck-recommendation" class="recommendation hidden">Strongly recommended: 2+ decks for 4+ players to avoid running out of cards</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="num-rounds">Number of Holes</label>
|
||||
<select id="num-rounds">
|
||||
<option value="9" selected>9 Holes (Front Nine)</option>
|
||||
<option value="18">18 Holes (Full Round)</option>
|
||||
<option value="3">3 Holes (Quick Game)</option>
|
||||
<option value="1">1 Hole</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="initial-flips">Starting Cards Revealed</label>
|
||||
<select id="initial-flips">
|
||||
<option value="2" selected>2 cards (Standard)</option>
|
||||
<option value="1">1 card</option>
|
||||
<option value="0">None (Blind start)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Variants</label>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="flip-on-discard">
|
||||
<span>Flip card when discarding from deck</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="knock-penalty">
|
||||
<span>+10 penalty if you go out but don't have lowest</span>
|
||||
</label>
|
||||
<div class="waiting-layout">
|
||||
<div class="waiting-left-col">
|
||||
<div class="players-list">
|
||||
<h3>Players</h3>
|
||||
<ul id="players-list"></ul>
|
||||
</div>
|
||||
<button id="leave-room-btn" class="btn btn-danger">Leave Room</button>
|
||||
</div>
|
||||
|
||||
<details class="house-rules-section">
|
||||
<summary>House Rules</summary>
|
||||
|
||||
<div class="house-rules-category">
|
||||
<h4>Jokers</h4>
|
||||
<div class="form-group compact">
|
||||
<select id="joker-mode">
|
||||
<option value="none">No Jokers</option>
|
||||
<option value="standard">Standard (2 per deck, -2 each)</option>
|
||||
<option value="lucky-swing">Lucky Swing (1 joker in all decks, -5 pts)</option>
|
||||
<div id="host-settings" class="settings hidden">
|
||||
<h3>Game Settings</h3>
|
||||
<div class="basic-settings-row">
|
||||
<div class="form-group">
|
||||
<label>CPU Players</label>
|
||||
<div class="cpu-controls">
|
||||
<button id="remove-cpu-btn" class="btn btn-small btn-danger">(-) Delete</button>
|
||||
<button id="add-cpu-btn" class="btn btn-small btn-success">(+) Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="num-decks">Decks</label>
|
||||
<select id="num-decks">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="num-rounds">Holes</label>
|
||||
<select id="num-rounds">
|
||||
<option value="9" selected>9</option>
|
||||
<option value="18">18</option>
|
||||
<option value="3">3</option>
|
||||
<option value="1">1</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="initial-flips">Cards Revealed</label>
|
||||
<select id="initial-flips">
|
||||
<option value="2" selected>2 cards</option>
|
||||
<option value="1">1 card</option>
|
||||
<option value="0">None</option>
|
||||
</select>
|
||||
<label class="checkbox-label eagle-eye-option hidden" id="eagle-eye-label">
|
||||
<input type="checkbox" id="eagle-eye">
|
||||
<span>Eagle Eye</span>
|
||||
<span class="rule-desc">Paired jokers score -8</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<p id="deck-recommendation" class="recommendation hidden">Recommended: 2+ decks for 4+ players</p>
|
||||
<details class="advanced-options-section">
|
||||
<summary>Advanced Options</summary>
|
||||
|
||||
<div class="house-rules-category">
|
||||
<h4>Point Modifiers</h4>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="super-kings">
|
||||
<span>Super Kings</span>
|
||||
<span class="rule-desc">Kings worth -2 instead of 0</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="lucky-sevens">
|
||||
<span>Lucky Sevens</span>
|
||||
<span class="rule-desc">7s worth 0 instead of 7</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="ten-penny">
|
||||
<span>Ten Penny</span>
|
||||
<span class="rule-desc">10s worth 1 (like Ace)</span>
|
||||
</label>
|
||||
<div class="advanced-options-grid">
|
||||
<!-- Left Column: Variants & Jokers -->
|
||||
<div class="options-column">
|
||||
<div class="options-category">
|
||||
<h4>Variants</h4>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="flip-on-discard">
|
||||
<span>Flip on Discard</span>
|
||||
<span class="rule-desc">Flip card when discarding from deck</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="knock-penalty">
|
||||
<span>Knock Penalty</span>
|
||||
<span class="rule-desc">+10 if you go out but don't have lowest</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-category">
|
||||
<h4>Jokers</h4>
|
||||
<div class="radio-group">
|
||||
<label class="radio-label">
|
||||
<input type="radio" name="joker-mode" value="none" checked>
|
||||
<span>None</span>
|
||||
</label>
|
||||
<label class="radio-label">
|
||||
<input type="radio" name="joker-mode" value="standard">
|
||||
<span>Standard</span>
|
||||
<span class="rule-desc">2 per deck, -2 pts / 0 paired</span>
|
||||
</label>
|
||||
<label class="radio-label">
|
||||
<input type="radio" name="joker-mode" value="lucky-swing">
|
||||
<span>Lucky Swing</span>
|
||||
<span class="rule-desc">1-2-3 decks - 1 Joker, -5 pt</span>
|
||||
</label>
|
||||
<label class="radio-label">
|
||||
<input type="radio" name="joker-mode" value="eagle-eye">
|
||||
<span>Eagle-Eyed</span>
|
||||
<span class="rule-desc">★ = +2 pts, -4 pts paired</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-category">
|
||||
<h4>Point Modifiers</h4>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label inline">
|
||||
<input type="checkbox" id="super-kings">
|
||||
<span>Super Kings</span>
|
||||
<span class="rule-desc">K = -2 pts</span>
|
||||
</label>
|
||||
<label class="checkbox-label inline">
|
||||
<input type="checkbox" id="ten-penny">
|
||||
<span>Ten Penny</span>
|
||||
<span class="rule-desc">10 = 1 pt</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="house-rules-category">
|
||||
<h4>Bonuses & Penalties</h4>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="knock-bonus">
|
||||
<span>Knock Out Bonus</span>
|
||||
<span class="rule-desc">-5 for going out first</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="underdog-bonus">
|
||||
<span>Underdog Bonus</span>
|
||||
<span class="rule-desc">-3 for lowest score each hole</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="tied-shame">
|
||||
<span>Tied Shame</span>
|
||||
<span class="rule-desc">+5 if you tie with someone</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="blackjack">
|
||||
<span>Blackjack</span>
|
||||
<span class="rule-desc">Exact 21 becomes 0</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column: Bonuses & Gameplay -->
|
||||
<div class="options-column">
|
||||
<div class="options-category">
|
||||
<h4>Bonuses & Penalties</h4>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="knock-bonus">
|
||||
<span>Knock Out Bonus</span>
|
||||
<span class="rule-desc">-5 for going out first</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="underdog-bonus">
|
||||
<span>Underdog Bonus</span>
|
||||
<span class="rule-desc">-3 for lowest score each hole</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="tied-shame">
|
||||
<span>Tied Shame</span>
|
||||
<span class="rule-desc">+5 if you tie with someone</span>
|
||||
</label>
|
||||
<label class="checkbox-label inline">
|
||||
<input type="checkbox" id="blackjack">
|
||||
<span>Blackjack</span>
|
||||
<span class="rule-desc">21 pts = 0 pts</span>
|
||||
</label>
|
||||
<label class="checkbox-label inline">
|
||||
<input type="checkbox" id="wolfpack">
|
||||
<span>Wolfpack</span>
|
||||
<span class="rule-desc">2 pairs of Jacks = -5 pts</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="house-rules-category">
|
||||
<h4>Gameplay Twists</h4>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="queens-wild">
|
||||
<span>Queens Wild</span>
|
||||
<span class="rule-desc">Queens pair with any rank</span>
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="four-of-a-kind">
|
||||
<span>Four of a Kind</span>
|
||||
<span class="rule-desc">4 matching cards all score 0</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<button id="start-game-btn" class="btn btn-primary">Start Game</button>
|
||||
<button id="start-game-btn" class="btn btn-primary">Start Game</button>
|
||||
</div>
|
||||
|
||||
<p id="waiting-message" class="info">Waiting for host to start the game...</p>
|
||||
</div>
|
||||
|
||||
<p id="waiting-message" class="info">Waiting for host to start the game...</p>
|
||||
|
||||
<button id="leave-room-btn" class="btn btn-danger">Leave Room</button>
|
||||
</div>
|
||||
|
||||
<!-- Game Screen -->
|
||||
@@ -193,7 +206,15 @@
|
||||
<div class="round-info">Hole <span id="current-round">1</span>/<span id="total-rounds">9</span></div>
|
||||
<div class="turn-info" id="turn-info">Your turn</div>
|
||||
<div class="score-info">Showing: <span id="your-score">0</span></div>
|
||||
<button id="mute-btn" class="mute-btn" title="Toggle sound">🔊</button>
|
||||
<div class="header-buttons">
|
||||
<button id="leave-game-btn" class="btn btn-small btn-danger">Leave</button>
|
||||
<button id="mute-btn" class="mute-btn" title="Toggle sound">🔊</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="active-rules-bar" class="active-rules-bar hidden">
|
||||
<span class="rules-label">Rules:</span>
|
||||
<span id="active-rules-list" class="rules-list"></span>
|
||||
</div>
|
||||
|
||||
<div class="game-table">
|
||||
@@ -203,7 +224,7 @@
|
||||
<div class="table-center">
|
||||
<div class="deck-area">
|
||||
<div id="deck" class="card card-back">
|
||||
<span>DECK</span>
|
||||
<span>?</span>
|
||||
</div>
|
||||
<div id="discard" class="card">
|
||||
<span id="discard-content"></span>
|
||||
@@ -228,7 +249,7 @@
|
||||
|
||||
<!-- Left panel: Standings -->
|
||||
<div id="standings-panel" class="side-panel left-panel">
|
||||
<h4>Standings</h4>
|
||||
<h4>Current Standings</h4>
|
||||
<div id="standings-list" class="standings-list"></div>
|
||||
</div>
|
||||
|
||||
|
||||
701
client/style.css
701
client/style.css
@@ -6,8 +6,8 @@
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background-color: #1a472a;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Cg fill='%23224d32' fill-opacity='0.4'%3E%3Cpath d='M15 5c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v8c0 1.1-.9 2-2 2h-6c-1.1 0-2-.9-2-2V5zm0 40c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v8c0 1.1-.9 2-2 2h-6c-1.1 0-2-.9-2-2v-8z'/%3E%3Cpath d='M35 25c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v8c0 1.1-.9 2-2 2h-6c-1.1 0-2-.9-2-2v-8z'/%3E%3Ccircle cx='10' cy='30' r='3'/%3E%3Ccircle cx='50' cy='10' r='3'/%3E%3Ccircle cx='50' cy='50' r='3'/%3E%3Cpath d='M30 18l3 5h-6l3-5zm0 24l3 5h-6l3-5z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
/* Dark emerald pool table felt */
|
||||
background: linear-gradient(180deg, #0a4528 0%, #0d5030 50%, #0a4528 100%);
|
||||
min-height: 100vh;
|
||||
color: #fff;
|
||||
}
|
||||
@@ -61,11 +61,153 @@ body {
|
||||
|
||||
/* Waiting Screen */
|
||||
#waiting-screen {
|
||||
max-width: 500px;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Desktop: side-by-side layout */
|
||||
.waiting-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 220px 1fr;
|
||||
gap: 25px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.waiting-left-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.waiting-left-col .players-list {
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.waiting-left-col .players-list h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#waiting-screen .settings {
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#waiting-screen .settings h3 {
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
/* Basic settings in a row */
|
||||
.basic-settings-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 15px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.basic-settings-row .form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.basic-settings-row .form-group label {
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.basic-settings-row select {
|
||||
width: 100%;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.basic-settings-row .cpu-controls {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.basic-settings-row .cpu-controls .btn {
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
#waiting-message {
|
||||
grid-column: 1 / -1;
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
/* Mobile: stack vertically */
|
||||
@media (max-width: 700px) {
|
||||
.waiting-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.basic-settings-row {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Room Code Banner */
|
||||
.room-code-banner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: linear-gradient(135deg, rgba(244, 164, 96, 0.9) 0%, rgba(230, 140, 70, 0.95) 100%);
|
||||
padding: 10px 15px;
|
||||
border-radius: 0 0 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.room-code-label {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: rgba(26, 71, 42, 0.8);
|
||||
}
|
||||
|
||||
.room-code-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
font-family: 'Courier New', monospace;
|
||||
letter-spacing: 0.2em;
|
||||
color: #1a472a;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.room-code-copy {
|
||||
background: rgba(26, 71, 42, 0.2);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.room-code-copy:hover {
|
||||
background: rgba(26, 71, 42, 0.3);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.room-code-copy:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.room-code-copy.copied {
|
||||
background: rgba(26, 71, 42, 0.4);
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
@@ -153,6 +295,11 @@ input::placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: #27ae60;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: 8px 16px;
|
||||
font-size: 0.9rem;
|
||||
@@ -270,6 +417,41 @@ input::placeholder {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.radio-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.radio-label input[type="radio"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.radio-label .rule-desc {
|
||||
width: auto;
|
||||
margin-left: 0;
|
||||
opacity: 0.6;
|
||||
font-weight: 400;
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.radio-label .rule-desc::before {
|
||||
content: "— ";
|
||||
}
|
||||
|
||||
/* Settings */
|
||||
.settings {
|
||||
background: rgba(0,0,0,0.2);
|
||||
@@ -307,8 +489,16 @@ input::placeholder {
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.game-header .mute-btn {
|
||||
.game-header .header-buttons {
|
||||
justify-self: end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#leave-game-btn {
|
||||
padding: 6px 12px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.mute-btn {
|
||||
@@ -327,6 +517,39 @@ input::placeholder {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
/* Active Rules Bar */
|
||||
.active-rules-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 6px 20px;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
font-size: 0.8rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.active-rules-bar .rules-label {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.active-rules-bar .rules-list {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.active-rules-bar .rule-tag {
|
||||
background: rgba(244, 164, 96, 0.25);
|
||||
color: #f4a460;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Card Styles */
|
||||
.card {
|
||||
width: clamp(65px, 5.5vw, 100px);
|
||||
@@ -349,10 +572,19 @@ input::placeholder {
|
||||
}
|
||||
|
||||
.card-back {
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||||
border: 3px solid #1a252f;
|
||||
/* Bee-style diamond grid pattern - red with white crosshatch */
|
||||
background-color: #c41e3a;
|
||||
background-image:
|
||||
linear-gradient(45deg, rgba(255,255,255,0.25) 25%, transparent 25%),
|
||||
linear-gradient(-45deg, rgba(255,255,255,0.25) 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.25) 75%),
|
||||
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.25) 75%);
|
||||
background-size: 8px 8px;
|
||||
background-position: 0 0, 0 4px, 4px -4px, -4px 0;
|
||||
border: 3px solid #8b1528;
|
||||
color: #fff;
|
||||
font-size: 0.8rem;
|
||||
font-size: clamp(1.8rem, 2.5vw, 3rem);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.card-front {
|
||||
@@ -402,7 +634,7 @@ input::placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
gap: 25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -510,10 +742,16 @@ input::placeholder {
|
||||
|
||||
.deck-area {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.deck-area .card {
|
||||
width: clamp(80px, 7vw, 120px);
|
||||
height: clamp(112px, 9.8vw, 168px);
|
||||
font-size: clamp(2.4rem, 3.2vw, 4rem);
|
||||
}
|
||||
|
||||
#discard {
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: 2px dashed rgba(255,255,255,0.3);
|
||||
@@ -537,6 +775,29 @@ input::placeholder {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Card flip animation for discard pile */
|
||||
.card-flip-in {
|
||||
animation: cardFlipIn 0.4s ease-out;
|
||||
}
|
||||
|
||||
@keyframes cardFlipIn {
|
||||
0% {
|
||||
transform: scale(1.3) rotateY(90deg);
|
||||
opacity: 0.5;
|
||||
box-shadow: 0 0 30px rgba(244, 164, 96, 0.8);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15) rotateY(0deg);
|
||||
opacity: 1;
|
||||
box-shadow: 0 0 25px rgba(244, 164, 96, 0.6);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotateY(0deg);
|
||||
opacity: 1;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
#drawn-card-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -547,6 +808,11 @@ input::placeholder {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#drawn-card-area .card {
|
||||
width: clamp(80px, 7vw, 120px);
|
||||
height: clamp(112px, 9.8vw, 168px);
|
||||
font-size: clamp(2.4rem, 3.2vw, 4rem);
|
||||
}
|
||||
|
||||
#drawn-card-area .btn {
|
||||
white-space: nowrap;
|
||||
@@ -585,6 +851,19 @@ input::placeholder {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.opponent-showing {
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.opponent-area .card-grid {
|
||||
@@ -678,9 +957,9 @@ input::placeholder {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
background: linear-gradient(145deg, rgba(15, 50, 35, 0.92) 0%, rgba(8, 30, 20, 0.95) 100%);
|
||||
border-radius: 14px;
|
||||
padding: 16px 18px;
|
||||
width: 235px;
|
||||
border-radius: 16px;
|
||||
padding: 18px 20px;
|
||||
width: 263px;
|
||||
z-index: 100;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(244, 164, 96, 0.25);
|
||||
@@ -699,69 +978,81 @@ input::placeholder {
|
||||
}
|
||||
|
||||
.side-panel > h4 {
|
||||
font-size: 0.9rem;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 14px;
|
||||
color: #f4a460;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2em;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
|
||||
border-bottom: 1px solid rgba(244, 164, 96, 0.2);
|
||||
padding-bottom: 10px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
/* Standings list */
|
||||
.standings-list {
|
||||
font-size: 0.95rem;
|
||||
/* Standings list - two sections, top 4 each */
|
||||
.standings-section {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.standings-list .standing-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.standings-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.standings-title {
|
||||
font-size: 0.7rem;
|
||||
color: rgba(255,255,255,0.5);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
padding-bottom: 3px;
|
||||
margin-bottom: 3px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.standings-list .rank-row {
|
||||
display: grid;
|
||||
grid-template-columns: 22px 1fr 36px;
|
||||
gap: 4px;
|
||||
font-size: 0.8rem;
|
||||
padding: 2px 0;
|
||||
align-items: center;
|
||||
padding: 6px 8px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.standings-list .standing-row.leader {
|
||||
background: rgba(244, 164, 96, 0.2);
|
||||
border-left: 3px solid #f4a460;
|
||||
.standings-list .rank-pos {
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.standings-list .standing-row.you {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.standings-list .standing-pos {
|
||||
font-weight: 700;
|
||||
color: #f4a460;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.standings-list .standing-name {
|
||||
flex: 1;
|
||||
.standings-list .rank-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.standings-list .standing-score {
|
||||
font-weight: 600;
|
||||
opacity: 0.9;
|
||||
.standings-list .rank-val {
|
||||
text-align: right;
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.standings-list .rank-row.leader {
|
||||
color: #f4a460;
|
||||
}
|
||||
|
||||
.standings-list .rank-row.leader .rank-val {
|
||||
color: #f4a460;
|
||||
}
|
||||
|
||||
/* Score table */
|
||||
.side-panel table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.9rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.side-panel th,
|
||||
.side-panel td {
|
||||
padding: 7px 5px;
|
||||
padding: 8px 6px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
@@ -769,7 +1060,7 @@ input::placeholder {
|
||||
.side-panel th {
|
||||
font-weight: 600;
|
||||
background: rgba(0,0,0,0.25);
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
@@ -790,62 +1081,62 @@ input::placeholder {
|
||||
}
|
||||
|
||||
.game-buttons {
|
||||
margin-top: 10px;
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.game-buttons .btn {
|
||||
font-size: 0.7rem;
|
||||
padding: 8px 10px;
|
||||
font-size: 0.8rem;
|
||||
padding: 10px 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Rankings Announcement */
|
||||
.rankings-announcement {
|
||||
background: linear-gradient(135deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.25) 100%);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid rgba(244, 164, 96, 0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rankings-announcement h3 {
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.95rem;
|
||||
text-align: center;
|
||||
margin: 0 0 8px 0;
|
||||
margin: 0 0 10px 0;
|
||||
color: #f4a460;
|
||||
}
|
||||
|
||||
.rankings-announcement h4 {
|
||||
font-size: 0.7rem;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
margin: 0 0 5px 0;
|
||||
margin: 0 0 6px 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.rankings-columns {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ranking-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rank-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.7rem;
|
||||
padding: 2px 0;
|
||||
gap: 2px;
|
||||
font-size: 0.8rem;
|
||||
padding: 3px 0;
|
||||
gap: 3px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
@@ -855,7 +1146,7 @@ input::placeholder {
|
||||
}
|
||||
|
||||
.rank-pos {
|
||||
width: 16px;
|
||||
width: 18px;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -869,7 +1160,7 @@ input::placeholder {
|
||||
}
|
||||
|
||||
.rank-val {
|
||||
font-size: 0.6rem;
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.9;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
@@ -881,11 +1172,11 @@ input::placeholder {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #f4a460 50%, #ffd700 100%);
|
||||
color: #1a472a;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 8px;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 10px;
|
||||
animation: victoryPulse 1s ease-in-out infinite alternate;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,0.3);
|
||||
}
|
||||
@@ -1198,15 +1489,15 @@ input::placeholder {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
/* House Rules Section */
|
||||
.house-rules-section {
|
||||
/* Advanced Options Section */
|
||||
.advanced-options-section {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
border-radius: 8px;
|
||||
margin: 15px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.house-rules-section summary {
|
||||
.advanced-options-section summary {
|
||||
padding: 12px 15px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
@@ -1218,69 +1509,103 @@ input::placeholder {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.house-rules-section summary::-webkit-details-marker {
|
||||
.advanced-options-section summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.house-rules-section summary::before {
|
||||
.advanced-options-section summary::before {
|
||||
content: "▸";
|
||||
font-size: 0.8rem;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.house-rules-section[open] summary::before {
|
||||
.advanced-options-section[open] summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.house-rules-section summary:hover {
|
||||
.advanced-options-section summary:hover {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.house-rules-category {
|
||||
padding: 12px 15px;
|
||||
/* Two-column grid for options */
|
||||
.advanced-options-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.options-column {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.options-column:first-child {
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.options-category {
|
||||
padding: 8px 0;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.house-rules-category h4 {
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 10px;
|
||||
.options-category:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.options-category h4 {
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.9;
|
||||
color: #f4a460;
|
||||
}
|
||||
|
||||
.house-rules-category .checkbox-group {
|
||||
gap: 6px;
|
||||
.options-category .checkbox-group {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.house-rules-category .checkbox-label {
|
||||
font-size: 0.85rem;
|
||||
padding: 5px 0;
|
||||
.options-category .checkbox-label {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
padding: 3px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Inline checkbox labels - description on same line */
|
||||
.checkbox-label.inline {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.checkbox-label.inline .rule-desc {
|
||||
width: auto;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.checkbox-label.inline .rule-desc::before {
|
||||
content: "— ";
|
||||
}
|
||||
|
||||
/* Rule description */
|
||||
.rule-desc {
|
||||
width: 100%;
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.7;
|
||||
margin-left: 22px;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.6;
|
||||
margin-left: 30px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
/* Compact form group for house rules */
|
||||
.house-rules-category .form-group.compact {
|
||||
/* Compact form group for options */
|
||||
.options-category .form-group.compact {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.house-rules-category .form-group.compact select {
|
||||
.options-category .form-group.compact select {
|
||||
width: 100%;
|
||||
font-size: 0.8rem;
|
||||
padding: 6px 8px;
|
||||
font-size: 0.75rem;
|
||||
padding: 5px 6px;
|
||||
}
|
||||
|
||||
/* Eagle Eye option under joker dropdown */
|
||||
.eagle-eye-option {
|
||||
margin-top: 8px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* Disabled checkbox styling */
|
||||
@@ -1292,3 +1617,191 @@ input::placeholder {
|
||||
.checkbox-label input:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Mobile: stack columns */
|
||||
@media (max-width: 500px) {
|
||||
.advanced-options-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.options-column:first-child {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Final Results Modal */
|
||||
.final-results-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 300;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.final-results-content {
|
||||
background: linear-gradient(145deg, #1a472a 0%, #0d3320 100%);
|
||||
border-radius: 20px;
|
||||
padding: 30px 40px;
|
||||
max-width: 550px;
|
||||
width: 90%;
|
||||
text-align: center;
|
||||
box-shadow:
|
||||
0 20px 60px rgba(0, 0, 0, 0.6),
|
||||
0 0 80px rgba(244, 164, 96, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
border: 2px solid rgba(244, 164, 96, 0.3);
|
||||
animation: modalSlideIn 0.4s ease;
|
||||
}
|
||||
|
||||
@keyframes modalSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.final-results-content h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 20px;
|
||||
color: #f4a460;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.double-victory-banner {
|
||||
background: linear-gradient(135deg, #ffd700 0%, #f4a460 50%, #ffd700 100%);
|
||||
color: #1a472a;
|
||||
padding: 12px 20px;
|
||||
border-radius: 10px;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
animation: victoryPulse 1s ease-in-out infinite alternate;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.final-rankings {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.final-ranking-section {
|
||||
flex: 1;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.final-ranking-section h3 {
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.final-rank-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 10px;
|
||||
margin-bottom: 4px;
|
||||
border-radius: 6px;
|
||||
font-size: 1rem;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.final-rank-row:nth-child(2) {
|
||||
background: linear-gradient(90deg, rgba(244, 164, 96, 0.3) 0%, rgba(244, 164, 96, 0.1) 100%);
|
||||
font-weight: 600;
|
||||
color: #f4a460;
|
||||
}
|
||||
|
||||
.final-rank-row:nth-child(3) {
|
||||
background: rgba(192, 192, 192, 0.15);
|
||||
}
|
||||
|
||||
.final-rank-row:nth-child(4) {
|
||||
background: rgba(205, 127, 50, 0.12);
|
||||
}
|
||||
|
||||
.final-rank-row .rank-pos {
|
||||
width: 28px;
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.final-rank-row .rank-name {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.final-rank-row .rank-val {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.final-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.final-actions .btn {
|
||||
min-width: 140px;
|
||||
padding: 14px 24px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.final-actions .btn-primary {
|
||||
box-shadow: 0 4px 15px rgba(244, 164, 96, 0.4);
|
||||
}
|
||||
|
||||
/* Mobile adjustments for final results modal */
|
||||
@media (max-width: 500px) {
|
||||
.final-results-content {
|
||||
padding: 20px 25px;
|
||||
}
|
||||
|
||||
.final-results-content h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.final-rankings {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.final-rank-row {
|
||||
font-size: 0.9rem;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.final-actions .btn {
|
||||
min-width: 120px;
|
||||
padding: 12px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user