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>
- Update version from 2.0.1 to 3.1.1 in pyproject.toml and server/main.py
- Add V3_17_MOBILE_PORTRAIT_LAYOUT.md documenting all mobile improvements:
responsive layout, animation sizing fixes, compact header, bottom drawers
- Add V3_17 entry to V3 master plan
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>
Tighten should_knock_early() so AI no longer knocks with projected
scores of 12-14. New range: max_acceptable 5-9 (was 8-18), with
scaled knock_chance by score quality and an exception when all
opponents show 25+ visible points.
Fix 5 pre-existing test failures:
- test_event_replay: use game.current_player() instead of hardcoding
"p1", since dealer logic makes p2 go first
- game.py: include current_player_idx in round_started event so state
replay knows the correct starting player
- test_house_rules: rename test_rule_config → run_rule_config so
pytest doesn't collect it as a test fixture
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Break the 666-line choose_swap_or_discard into 8 focused sub-functions,
extract named constants for ~15 magic numbers, add column/pair utility
functions (iter_columns, project_score, count_hidden, hidden_positions),
and extract _log_cpu_action helper to reduce logging boilerplate in
process_cpu_turn. No behavior changes - validated with simulate.py 500.
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>
- Add GameLogger service for move logging to PostgreSQL
- Add moves table to event_store.py for AI decision analysis
- Update main.py to initialize GameLogger in lifespan
- Update game_analyzer.py to query PostgreSQL instead of SQLite
- Add VDD documentation V2_08_GAME_LOGGING.md
Replaces SQLite game_log.py with unified PostgreSQL backend.
See docs/v2/V2_08_GAME_LOGGING.md for architecture and API.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AI decision safety checks documentation
- Add simulation testing commands
- Update architecture with services/, stores/, and new files
- Add PostgreSQL to dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prevent CPU players from swapping 8+ value cards (8, 9, 10, J, Q) into
face-down positions, which is statistically bad since expected hidden
card value is ~4.5.
Fixes applied:
- Add value threshold (7) to unpredictability random swap path
- Restrict comeback bonus to cards with value < 8
- Reduce speculative wolfpack Jack bonus from 6x to 2x aggression
- Add safety filter to remove hidden positions for 8+ cards
- Fix endgame logic to discard 8+ instead of forcing swap into hidden
- Skip hidden positions in denial candidate list for 8+ cards
- Add swapped_high_into_unknown tracking to SimulationStats
Reduces "swapped 8+ into unknown" dumb moves from ~85 per 200 games
to ~6 per 500 games (0.054% rate, down from ~2%).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>