Remove all flush-edge styling (negative margins, half-pills, border
removal). Restore original padding, justify-content, and pill shapes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zero padding was breaking game layout. Keep 12px padding for layout
stability and use margin-left: -12px / margin-right: -12px on the
edge items to push them flush against screen edges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zero horizontal padding on bottom bar, remove border on flush side,
use half-rounded pill shape so they sit against the screen edges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reverts flush-edge pill styling and restores horizontal padding to prevent
clipping. Rules drawer is now a sibling of bottom-bar, not inside game-layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows "S" (standard) or "!" (house rules) in the mobile bottom bar.
Tapping opens a drawer with the full active rules list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
markKnocker() was called before opponent areas were rebuilt by
innerHTML='', so the is-knocker class and OUT badge were immediately
destroyed. Move markKnocker to after opponent areas are created.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Makes the red border on the knocker's area more visible, especially
for opponents on mobile where the area is small.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Final results modal: keep BY POINTS and BY HOLES side-by-side on
mobile, compact spacing, buttons side-by-side, bottom padding for
mobile bar overlay.
Turn shake: delay 5s before first shake, 300ms every 2s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Less aggressive draw hint: waits 5 seconds before first shake,
then shakes for 300ms every 2 seconds with slightly less movement.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Position the button centered beneath the held card instead of to the
right side. Reset writing-mode to horizontal and add width:auto.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce overlap offset from 1.15 to 0.65 so the held card sits at the
DRAW/DISCARD label level rather than up in the opponents area.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Position the held card a full card height above the deck (1.15x offset)
so it sits in the space between opponents and the draw/discard piles.
All three position calculations (app.js x2, card-animations.js) are
synced so draw animations land at the correct held position.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap each pile in a .pile-wrapper with a small label above it.
Fix direct child selectors that broke with the new wrapper nesting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Increase top padding from 5px to 20px so the deck/discard sit lower,
giving the held card more breathing room from the opponents row above.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update getHoldingRect() in card-animations.js and the second held card
positioning path in app.js to use the same reduced overlap offset on
mobile portrait. All three places that compute the held position now
use 0.15 on mobile-portrait vs 0.35 on desktop/landscape.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce held card overlap offset from 0.35 to 0.15 on mobile portrait
so it doesn't cover the second row of opponents. Increase bottom
padding on opponents row from 6px to 12px for more breathing room.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The status was set without a type class in renderGame(), overriding
the styled version from updateStatusFromGameState() on every state
update. Now the purple background shows consistently for opponent turns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove margin-top from base rules/leaderboard/matchmaking styles so
desktop and landscape layouts are unaffected. The 50px top margin is
now only applied via the mobile-portrait override.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Override width:100% from .btn base class with width:auto on both
back buttons. Add padding-bottom and border-bottom to leaderboard
header to match rules page styling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Position the button absolutely on the left side of the header,
vertically centered with the title. Remove mobile fixed-position
override that placed it in the top bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents auth bar from overlapping back buttons. Back buttons
align to start instead of stretching full width.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove game-screen padding and replace solid dark header background
with subtle dark-to-transparent gradient matching mobile treatment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The desktop #game-screen.active had padding:10px that was never
overridden in the mobile portrait styles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces background:none with a dark-to-transparent gradient so the
status message and mute button are visible against the green felt.
Reverts mute button circle in favor of the gradient approach.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The desktop game-header background was still showing on mobile,
creating a visible dark band with padding around the status bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove left/right/top padding from notification bar so it spans edge
to edge, and increase bottom margin from 4px to 8px for more breathing
room before the opponents row.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace margin:auto on table-center with space-evenly on player-row
so the draw pile and player cards are equally spaced vertically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Increase horizontal padding on game-table (4px to 10px) and header
(8px to 12px) to prevent edge clipping. Change opponents-row overflow
to visible so dealer chips aren't cut off.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The swap animation defers state updates to pendingGameState, which
bypassed checkForNewPairs entirely. Now pair detection runs when the
deferred state is applied after the swap animation completes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sound was gated behind element check which may fail during swap
animation when card DOM elements are replaced by overlays.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bottom bar is hidden when the hole results modal opens and restored
when dismissed. Also adds mobile-specific compact styles for the
scoresheet: smaller cards, tighter spacing, reduced padding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
firePairCelebration was only doing the visual animation but not playing
the pair sound. The sound was only played during score tallying.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove position:fixed from the bottom bar and make it a flex-shrink:0
child of the game screen flex column. This guarantees the game layout
gets exactly the remaining viewport height with no overlap, regardless
of how the browser calculates viewport units.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set --app-height CSS custom property from window.innerHeight via JS,
which is the only reliable way to get the actual visible viewport on
Chrome Android. Falls back to 100vh if JS hasn't loaded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use position:fixed with inset:0 for the game screen instead of
height-based sizing. This bypasses the Chrome Android 100vh bug where
vh includes space behind the dynamic URL bar. Also adds
-webkit-fill-available fallback on body.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 100vh fallback before 100dvh, max-height constraints on every flex
container in the layout chain, and explicit flex-basis 0% to prevent
Chrome from letting flex children grow beyond viewport bounds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces fixed 120px width with calc((100% - 20px) / 3) so 3 opponents
always fit per row regardless of viewport width.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Also fix opponent areas shifting between rows on mobile by giving them
a fixed 120px width so name/score changes don't cause reflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduces from 38px to 22px and pulls offset from -10px to -4px so it
no longer overlaps the bottom bar buttons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add bottom padding to game-table to reserve space for the fixed bottom
bar, and overflow:hidden on player-row so content respects flex bounds.
Also centers draw pile with equal spacing and adds dealer chip clearance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove transform: scale(0.75) from the bar container which broke the
full-width layout and margin-auto pinning. Instead shrink font sizes
and padding on individual buttons and round info by ~25%.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bumps z-index from 500 to 900 so the bottom bar stays above side panel
drawers (600) but below card animation overlays (1000). Scales the bar
to 75% in mobile portrait to reduce visual footprint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduces card sizes from 72×101 to 64×90 on mobile portrait to prevent
overlap with bottom bar when opponents wrap to 2 rows. Increases
horizontal gap between opponent hands from 4px to 10px for better
readability of player chips.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace onclick attributes with data-action/data-id attributes and
use event delegation. CSP script-src 'self' blocks inline handlers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin panel gets "Copy Link" button on active invites that copies
a signup URL with ?invite= param. Client auto-opens signup form
with invite code pre-filled when visiting that link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Forgot password form in auth modal with email input
- Reset password form handles token from email link
- /reset-password route serves index.html for SPA
- EMAIL_FROM env var in docker-compose
- Success/error feedback for both flows
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hole indicator: margin-right auto, pill background, brighter text (0.9 opacity)
- End Game: margin-left auto, pinned to right edge
- Standings/Scores stay centered between them
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add round info and leave button to mobile-bottom-bar HTML
- Hide .round-info and #leave-game-btn from header on mobile
- Style round info as subtle text, leave button as red-tinted pill
- Slim down bottom bar: smaller gaps/padding to fit 4 items
- Sync round numbers and leave text via JS (renderGame + bindEvents)
- Frees up header space, reduces mobile crowding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove excessive 44px bottom padding on game-table (was eating vertical space)
- Tighten opponents-row: reduce gap 6px->4px, side padding 8px->4px
- Reduce opponent-area side padding 5px->4px
- Allow opponent areas to shrink (remove flex-shrink: 0)
- 3 opponents now fit in ~367px, well within 412px Pixel 10 Pro
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Position fixed bottom:0 so buttons are always at the very bottom
- Remove dark background that was picking up the green felt color
- Add bottom padding to game-table so player cards aren't hidden behind bar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Redesign bottom bar buttons as pill-shaped with subtle glass border
- Active state: warm gradient fill with glow shadow
- Tap feedback: scale(0.95) press effect
- Drawer panels: iOS-style cubic-bezier spring curve, drop shadow
- Backdrop transition matches drawer timing
- Darker bottom bar background with softer border for depth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change opponents-row from nowrap to flex-wrap: wrap (max 3+2 layout)
- Opponent cards: 32x45px -> 35x49px, font 0.6 -> 0.65rem
- Short screen: 26x36px -> 29x40px, font 0.45 -> 0.5rem
- 3 opponents at 35px fits 369px, well within 375px iPhone width
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix animation overlay cards rendering at wrong size: base .card CSS
(clamp 65px min) was overriding the inline dimensions set by JS.
Add !important to .draw-anim-front/.draw-anim-back width/height: 100%
so overlays always match their parent container size.
- Size opponent swap held card to match opponent card dimensions instead
of defaulting to deck size (looked oversized on mobile)
- Shrink dealer chip on mobile (38px -> 20px) to fit opponent areas
- Make header more compact: smaller fonts, tighter gaps, nowrap on badges
- Bump deck/discard to 72x101px to match player card size on mobile
- Add spacing between header/opponents, and between deck area/player cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add renderGame() guard during deal animation to prevent DOM destruction
mid-animation causing cards to pile up at wrong positions
- Push lobby content below fixed auth-bar (padding 15px -> 50px top)
- Scale player card font-size to 1.5rem/1.3rem for readable text on mobile
- Add full mobile portrait layout: bottom drawers, compact header, responsive
card grid sizing, safe-area insets, and mobile detection via matchMedia
- Add cardFontSize() helper for consistent proportional font scaling
- Add mobile bottom bar with drawer toggles for standings/scores
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add _intentionalClose flag to suppress error when server closes
WebSocket after game_ended broadcast. Clean transition to lobby.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Enforce invite codes on registration (INVITE_ONLY=true by default)
- Bootstrap admin account for first-time setup
- Require authentication for WebSocket connections and room creation
- Add Glicko-2 rating system with multiplayer pairwise comparisons
- Add Redis-backed matchmaking queue with expanding rating window
- Auto-start matched games with standard rules after countdown
- Add "Find Game" button and matchmaking UI to client
- Add rating column to leaderboard
- Scale down docker-compose.prod.yml for 512MB droplet
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove scale(1.15) size jump on held card, keep gold border/glow highlight.
Set animation card font-size proportionally to card width so text matches
across deck, hand, and opponent card sizes. Animate font-size during swaps
so text scales smoothly as cards travel between different-sized positions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only standard-rules games now count toward leaderboard stats. Games
with any house rule variant are marked "Unranked" in the active rules
bar, and a notice appears in the lobby when house rules are selected.
Also fixes game_logger duplicate options dicts (now uses dataclasses.asdict,
capturing all options including previously missing ones) and refactors
duplicated achievement-checking logic into shared helpers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Client-side scoring (points badge and score tally animation) ignored
house rules that modify pair behavior. Extract shared
calculateColumnScores() helper that mirrors server logic for
eagle_eye, negative_pairs_keep_value, wolfpack, four_of_a_kind,
and one_eyed_jacks rules. Server now sends scoring_rules flags
in game state.
Also fix opponent flip animation card font-size matching.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename "New Variants" to "Game Variants", fix descriptions that
contradicted game mechanics (impossible card scenarios, misleading
value assessments), and clarify Underdog Bonus catch-up intent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Cut reveal/tally/celebration timings by ~50% for snappier round end
- Add dealAnimationInProgress flag to suppress flip prompts during deal
- Stop deck/discard pulse animation when round ends
- Update CLAUDE.md with animation race condition documentation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix discard pile "do-si-do" race condition when CPU draws from discard
- Add isDrawAnimating flag for opponent draw animations
- Skip STEP 2 (discard detection) when draw from discard detected
- Fix deal animation using wrong rect (was using whole player area)
- Add player area highlight when it's their turn (green glow)
- Clear opponent animation flags when your_turn message received
- Hide discard pile during draw-from-discard animation
- Add comprehensive debug logging for animation flags
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- client/ANIMATIONS.md: Full documentation of the CardAnimations API, timing config, CSS rules, and common patterns
- CLAUDE.md: Project context for AI assistants with architecture overview and development guidelines
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>