Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b071afdfb | ||
|
|
c7fb85d281 | ||
|
|
118912dd13 | ||
|
|
0e594a5e28 | ||
|
|
a6ec72d72c | ||
|
|
e2f353d4ab | ||
|
|
e601eb04c9 | ||
|
|
6c771810f7 | ||
|
|
dbad7037d1 | ||
|
|
21362ba125 | ||
|
|
2dcdaf2b49 | ||
|
|
1fa13bbe3b | ||
|
|
a76fd8da32 | ||
|
|
634d101f2c | ||
|
|
28c9882b17 |
@@ -1573,8 +1573,10 @@ class GolfGame {
|
||||
this.heldCardFloating.classList.add('hidden');
|
||||
|
||||
if (this.pendingGameState) {
|
||||
const oldState = this.gameState;
|
||||
this.gameState = this.pendingGameState;
|
||||
this.pendingGameState = null;
|
||||
this.checkForNewPairs(oldState, this.gameState);
|
||||
this.renderGame();
|
||||
}
|
||||
}
|
||||
@@ -1789,6 +1791,10 @@ class GolfGame {
|
||||
document.body.appendChild(modal);
|
||||
this.setStatus('Hole complete');
|
||||
|
||||
// Hide bottom bar so it doesn't overlay the modal
|
||||
const bottomBar = document.getElementById('mobile-bottom-bar');
|
||||
if (bottomBar) bottomBar.classList.add('hidden');
|
||||
|
||||
// Bind next button
|
||||
const nextBtn = document.getElementById('ss-next-btn');
|
||||
nextBtn.addEventListener('click', () => {
|
||||
@@ -1918,6 +1924,10 @@ class GolfGame {
|
||||
this.clearScoresheetCountdown();
|
||||
const modal = document.getElementById('scoresheet-modal');
|
||||
if (modal) modal.remove();
|
||||
|
||||
// Restore bottom bar
|
||||
const bottomBar = document.getElementById('mobile-bottom-bar');
|
||||
if (bottomBar) bottomBar.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// --- V3_02: Dealing Animation ---
|
||||
@@ -2599,6 +2609,7 @@ class GolfGame {
|
||||
}
|
||||
|
||||
firePairCelebration(playerId, pos1, pos2) {
|
||||
this.playSound('pair');
|
||||
const elements = this.getCardElements(playerId, pos1, pos2);
|
||||
if (elements.length < 2) return;
|
||||
|
||||
@@ -3438,15 +3449,6 @@ class GolfGame {
|
||||
// Toggle game area class for border pulse
|
||||
this.gameScreen.classList.add('final-turn-active');
|
||||
|
||||
// Calculate remaining turns
|
||||
const remaining = this.countRemainingTurns();
|
||||
|
||||
// Update badge content
|
||||
const remainingEl = this.finalTurnBadge.querySelector('.final-turn-remaining');
|
||||
if (remainingEl) {
|
||||
remainingEl.textContent = remaining === 1 ? '1 turn left' : `${remaining} turns left`;
|
||||
}
|
||||
|
||||
// Show badge
|
||||
this.finalTurnBadge.classList.remove('hidden');
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
<h1><img src="golfball-logo.svg" alt="" class="golfball-logo"><span class="golfer-swing">🏌️</span><span class="kicked-ball">⚪</span> <span class="golf-title">Golf</span></h1>
|
||||
<p class="subtitle">6-Card Golf Card Game <button id="rules-btn" class="btn btn-small btn-rules">Rules</button> <button id="leaderboard-btn" class="btn btn-small leaderboard-btn">Leaderboard</button></p>
|
||||
|
||||
<div class="alpha-banner">Alpha — Things may break. Stats may be wiped.</div>
|
||||
|
||||
<!-- Auth prompt for unauthenticated users -->
|
||||
<div id="auth-prompt" class="auth-prompt">
|
||||
<p>Log in or sign up to play.</p>
|
||||
@@ -303,7 +305,6 @@
|
||||
<div id="final-turn-badge" class="final-turn-badge hidden">
|
||||
<span class="final-turn-icon">⚡</span>
|
||||
<span class="final-turn-text">FINAL TURN</span>
|
||||
<span class="final-turn-remaining"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-col header-col-right">
|
||||
|
||||
102
client/style.css
102
client/style.css
@@ -445,7 +445,21 @@ h1 {
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 40px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.alpha-banner {
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
color: rgba(255, 200, 100, 0.9);
|
||||
background: rgba(244, 164, 96, 0.1);
|
||||
border: 1px solid rgba(244, 164, 96, 0.25);
|
||||
border-radius: 20px;
|
||||
padding: 5px 16px;
|
||||
margin: 0 auto 30px;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@@ -722,8 +736,8 @@ input::placeholder {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background: rgba(0,0,0,0.35);
|
||||
padding: 6px 12px;
|
||||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.25) 0%, transparent 100%);
|
||||
font-size: 0.9rem;
|
||||
width: 100vw;
|
||||
margin-left: calc(-50vw + 50%);
|
||||
@@ -1716,7 +1730,7 @@ input::placeholder {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.game-layout {
|
||||
@@ -4926,6 +4940,7 @@ body.mobile-portrait #game-screen.active {
|
||||
overflow: hidden;
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -4952,14 +4967,15 @@ body.mobile-portrait .game-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
padding-top: calc(4px + env(safe-area-inset-top, 0px));
|
||||
padding: 6px 8px;
|
||||
padding-top: calc(6px + env(safe-area-inset-top, 0px));
|
||||
font-size: 0.75rem;
|
||||
min-height: 32px;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
gap: 4px;
|
||||
margin-bottom: 4px;
|
||||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.25) 0%, transparent 100%);
|
||||
}
|
||||
|
||||
body.mobile-portrait .header-col-left {
|
||||
@@ -5005,7 +5021,7 @@ body.mobile-portrait #leave-game-btn {
|
||||
}
|
||||
|
||||
body.mobile-portrait .mute-btn {
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.95rem;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
@@ -5023,8 +5039,9 @@ body.mobile-portrait .game-table {
|
||||
justify-content: flex-start;
|
||||
gap: 0 !important;
|
||||
flex: 1 1 0%;
|
||||
overflow: hidden;
|
||||
padding: 0 4px;
|
||||
overflow-x: clip;
|
||||
overflow-y: hidden;
|
||||
padding: 0 10px;
|
||||
min-height: 0;
|
||||
max-height: 100%;
|
||||
}
|
||||
@@ -5038,7 +5055,7 @@ body.mobile-portrait .opponents-row {
|
||||
gap: 4px 10px;
|
||||
min-height: 0 !important;
|
||||
padding: 2px 4px 6px;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -5047,7 +5064,7 @@ body.mobile-portrait .player-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: space-evenly;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
flex: 1 1 0%;
|
||||
@@ -5113,7 +5130,6 @@ body.mobile-portrait .opponent-showing {
|
||||
body.mobile-portrait .table-center {
|
||||
padding: 5px 10px;
|
||||
border-radius: 8px;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
body.mobile-portrait .deck-area {
|
||||
@@ -5367,6 +5383,66 @@ body.mobile-portrait #waiting-screen {
|
||||
max-height: 100dvh;
|
||||
}
|
||||
|
||||
/* --- Mobile: Compact scoresheet modal --- */
|
||||
body.mobile-portrait .scoresheet-content {
|
||||
padding: 14px 16px;
|
||||
max-height: 90vh;
|
||||
max-height: var(--app-height, 90vh);
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-header {
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-players {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-player-row {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-player-header {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-player-name {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-mini-card {
|
||||
width: 30px;
|
||||
height: 22px;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-columns {
|
||||
gap: 6px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-column {
|
||||
gap: 2px;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-col-score {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-scores {
|
||||
font-size: 0.7rem;
|
||||
gap: 12px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
body.mobile-portrait .ss-next-btn {
|
||||
margin-top: 10px;
|
||||
padding: 8px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* --- Mobile: Very short screens (e.g. iPhone SE) --- */
|
||||
@media (max-height: 600px) {
|
||||
body.mobile-portrait .opponents-row {
|
||||
|
||||
@@ -61,6 +61,15 @@ services:
|
||||
- "traefik.http.routers.golf.entrypoints=websecure"
|
||||
- "traefik.http.routers.golf.tls=true"
|
||||
- "traefik.http.routers.golf.tls.certresolver=letsencrypt"
|
||||
# www -> bare domain redirect
|
||||
- "traefik.http.routers.golf-www.rule=Host(`www.${DOMAIN:-golf.example.com}`)"
|
||||
- "traefik.http.routers.golf-www.entrypoints=websecure"
|
||||
- "traefik.http.routers.golf-www.tls=true"
|
||||
- "traefik.http.routers.golf-www.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.golf-www.middlewares=www-redirect"
|
||||
- "traefik.http.middlewares.www-redirect.redirectregex.regex=^https://www\\.(.+)"
|
||||
- "traefik.http.middlewares.www-redirect.redirectregex.replacement=https://$${1}"
|
||||
- "traefik.http.middlewares.www-redirect.redirectregex.permanent=true"
|
||||
- "traefik.http.services.golf.loadbalancer.server.port=8000"
|
||||
# WebSocket sticky sessions
|
||||
- "traefik.http.services.golf.loadbalancer.sticky.cookie=true"
|
||||
|
||||
Reference in New Issue
Block a user