Add precision mode threshold system for know-your-world magnifier:
**Precision Mode Threshold System:**
- Constant PRECISION_MODE_THRESHOLD = 20 px/px
- Caps zoom when not in pointer lock to prevent exceeding threshold
- Shows clickable notice when threshold reached
- Activates pointer lock for precision control
**Pixel Grid Visualization:**
- Shows gold grid overlay aligned with crosshair
- Each grid cell = 1 screen pixel of mouse movement on main map
- Fades in from 70% to 100% of threshold (14-20 px/px)
- Fades out from 100% to 130% of threshold (20-26 px/px)
- Visible in both normal and precision modes
**Visual "Disabled" State:**
- Magnifier dims (60% brightness, 50% saturation) when at threshold
- Indicates zoom is capped until precision mode activated
- Returns to normal appearance in precision mode
**User Experience:**
- Below 14 px/px: Normal magnifier
- 14-20 px/px: Grid fades in as warning
- At 20 px/px: Full grid, dimmed magnifier, "Click here (not map) for precision mode"
- Click magnifier label (not map) to activate pointer lock
- In precision mode: Grid fades out (20-26 px/px), magnifier returns to normal
- e.stopPropagation() prevents accidental region clicks
**Debug Mode:**
- SHOW_MAGNIFIER_DEBUG_INFO flag (dev only)
- Shows technical info: zoom level and px/px ratio
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix MapData import in GameInfoPanel.tsx (import from types, not maps)
- Fix MapData import in test files
- Fix currentRegionName type (handle undefined with ?? null)
These errors were blocking the Docker build in CI.
Fixes production errors where Socket.IO server failed with "Unexpected token 'export'"
when loading map data from @svg-maps/world and @svg-maps/usa packages.
**Changes:**
- **maps.ts**: Use dynamic `import()` for ES modules instead of JSON workaround
- Async loading via `ensureMapSourcesLoaded()` works in both browser and Node.js
- Sync Proxy exports (`WORLD_MAP`, `USA_MAP`) for client components
- Async functions (`getMapData()`, `getFilteredMapData()`) for server-side use
- Remove JSON data files (world.json, usa.json) - now load directly from npm packages
- **Client components**: Use sync map exports instead of async functions
- MapRenderer, PlayingPhase, ResultsPhase, SetupPhase, StudyPhase, MapRenderer.stories
- Import `WORLD_MAP`/`USA_MAP` or use `getFilteredMapDataSync()`
- No more Promise handling needed in React components
- **Page component**: Disable SSR to prevent build-time errors
- Use Next.js `dynamic()` import with `ssr: false`
- Prevents race condition during static generation
- **tsconfig.server.json**: Include maps.ts and related files for server compilation
**Testing:**
- ✅ Docker build succeeds
- ✅ Socket.IO server loads: "Socket server module loaded successfully"
- ✅ Zero ES module errors in production build
- ✅ Maps load correctly from @svg-maps packages via dynamic imports
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:** Pointer lock was activating on MapRenderer's container,
but PlayingPhase was trying to manage it, so state never updated.
**Solution:** Move all pointer lock management into MapRenderer:
- Add pointerLocked state in MapRenderer
- Add pointer lock event listeners in MapRenderer
- Add click handler to request lock on MapRenderer container
- Add "Enable Precision Controls" overlay in MapRenderer
- Remove all pointer lock code from PlayingPhase
Now pointer lock state tracks correctly and custom cursor will render.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increase z-index from 1000 to 10000 to ensure the overlay
appears above all other content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add extensive console logging to debug cursor visibility:
**PlayingPhase:**
- Log when pointer lock listeners attach/detach
- Log pointer lock state changes with element details
- Log container click events with conditions
- Log pointerLocked state changes
**MapRenderer:**
- Log custom cursor render conditions on every render
- Log when cursor should be visible
- Log cursor position when rendering
This will help diagnose why custom cursor isn't visible.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major refactoring of precision controls for Know Your World game:
**New Flow:**
- Pointer lock requested once when PlayingPhase mounts (user click)
- Active for entire game session until unmount
- Simpler: use movementX/movementY with adaptive multiplier
**PlayingPhase.tsx:**
- Add pointer lock management with event listeners
- Show "Enable Precision Controls" overlay on mount
- Request pointer lock on first click (user gesture)
- Release on unmount (game ends)
- Pass pointerLocked prop to MapRenderer
**MapRenderer.tsx:**
- Accept pointerLocked as prop (don't manage internally)
- Remove complex cursor dampening (dual refs: raw vs dampened)
- Simplified: apply multiplier to movementX/movementY based on region size
- Sub-pixel regions (<1px): 3% speed
- Tiny regions (1-5px): 10% speed
- Small regions (5-15px): 25% speed
- Larger regions: 100% speed
- Remove precision mode state (not needed)
- Remove cooldown logic (not needed)
- Remove click capture handler (not needed)
- Update cursor style and mouse handlers to use pointerLocked
**Benefits:**
- Much simpler code (removed ~100 lines of complexity)
- No lag when changing direction (no interpolation)
- Pointer lock active entire session (can't drift off map)
- Movement multiplier clearer than dampening
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement automatic cursor dampening, super zoom on hover, and quick-escape to make sub-pixel regions (Gibraltar 0.08px, Jersey 0.82px) clickable. Fix crosshair accuracy to match dampened cursor position, add excluded region visualization (gray pre-labeled), and increase unfound region contrast (0.3→0.7 opacity).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add intelligent magnifying glass feature with smooth animations:
Magnifier Features:
- Appears when 7+ regions overlap in 50×50px box OR region <8px
- Adaptive zoom (8×-24×) based on region density and size
- Smooth zoom/opacity animations using react-spring
- Dynamic positioning to avoid covering cursor
- Visual indicator box on main map shows magnified area
- Crosshair shows exact cursor position in magnified view
Implementation Details:
- Uses react-spring for smooth zoom and fade transitions
- Position calculated per quadrant (opposite corner from cursor)
- Zoom formula: base 8× + density factor + size factor
- Animated SVG viewBox for seamless zooming
- Dashed blue indicator rectangle tracks magnified region
UI/UX Improvements:
- Remove duplicate turn indicator (use player avatar dock)
- Hide arrow labels feature behind flag (disabled by default)
- Add Storybook stories for map renderer with tuning controls
This makes clicking tiny island nations and crowded regions much easier!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add continent-based filtering for the world map to make the game more playable by reducing visual clutter:
Continent Selection:
- Add continent selector to setup screen (only for world map)
- 7 continents + "All" option: Africa, Asia, Europe, North America, South America, Oceania, Antarctica
- Each continent button shows emoji and name
Map Filtering:
- Filter world map regions by selected continent using ISO 3166-1 country codes
- Calculate bounding box for filtered regions
- Automatically crop and scale viewBox to show only selected continent
- Add 10% padding around continent bounding box for better visibility
Technical Implementation:
- Create continents.ts with comprehensive country-to-continent mappings (256 countries)
- Add getFilteredMapData() function to filter regions and adjust viewBox
- Add calculateBoundingBox() to compute min/max coordinates from SVG paths
- Add selectedContinent field to game state and config (persisted to database)
- Add SET_CONTINENT move type and validator
- Update all map rendering components (StudyPhase, PlayingPhase, MapRenderer)
Benefits:
- Solves "too many small countries" problem on world map
- Allows focused study of specific geographic regions
- Dynamic viewBox adjustment provides optimal zoom level
- Maintains full world map option for comprehensive play
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add new arcade game for testing geography knowledge:
Game Features:
- 4 phases: Setup, Study, Playing, Results
- 3 multiplayer modes: Cooperative, Race, Turn-Based
- 2 maps: World countries, USA states
- Configurable study mode (0, 30, 60, or 120 seconds)
- Return to Setup and New Game options in game menu
- Small region labels with arrows for improved visibility
Map Rendering:
- 8-color deterministic palette with hash-based assignment
- Opacity-based states (20-27% unfound, 100% found)
- Enhanced label visibility with text shadows
- Smart bounding box calculation for small regions
- Supports both easy (outlines always visible) and hard (outlines on hover/found) difficulty
Game Modes:
- Cooperative: All players work together to find all regions
- Race: First to click gets the point
- Turn-Based: Players take turns finding regions
Study Phase:
- Optional timed study period before quiz starts
- Shows all region labels for memorization
- Countdown timer with skip option
Dependencies:
- Add @svg-maps/world and @svg-maps/usa packages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>