Commit Graph

1435 Commits

Author SHA1 Message Date
Thomas Hallock ffae9c1bdb fix(web): move tsx to production dependencies for calendar generation
Calendar generation scripts need tsx at runtime to execute TypeScript files,
but tsx was in devDependencies which are not installed in production builds.

This caused calendar preview and PDF generation to fail in production with:
"EACCES: permission denied, mkdir '/nonexistent'" when trying to run npx tsx

Moving tsx to dependencies ensures it's available in production for:
- apps/web/scripts/generateCalendarComposite.tsx
- apps/web/scripts/generateCalendarAbacus.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 14:00:26 -06:00
Thomas Hallock 645140648a chore(abacus-react): remove debug logging and backup file 2025-11-04 13:09:01 -06:00
Thomas Hallock 06f68cc74c refactor(web): use stdin/stdout for Typst compilation
- Pipe Typst document content via stdin instead of writing .typ files
- Read PDF/SVG output from stdout instead of temp files
- Keep SVG files in temp directory (Typst requires file paths)
- Reduces disk I/O and eliminates .typ/.pdf file writes
- Add 50MB maxBuffer for large calendar PDFs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock 599a758471 feat(web): add Typst-based preview endpoint with React Suspense
- Create /api/create/calendar/preview endpoint using Typst compilation
- Refactor CalendarPreview to use useSuspenseQuery for data fetching
- Add Suspense boundary in calendar page with loading fallback
- Preview now matches PDF exactly as both use Typst rendering
- Remove post-generation SVG replacement to preserve Typst preview

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock e5ba772fde feat(abacus-react): add shared dimension calculator for consistent sizing
- Add calculateAbacusDimensions utility to AbacusUtils
- Refactor AbacusStatic to use shared dimension calculator
- Update calendar composite generator to use shared utility
- Export calculateAbacusDimensions from index and static entry points
- Ensures consistent sizing between preview and PDF generation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock 293390ae35 fix(web): generate styled-system artifacts during build
- Add @pandacss/dev to build script before Next.js build
- Ensures styled-system directory is generated in CI/CD pipeline
- Fixes "Cannot find module styled-system/css" build errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock f880cbe4bf refactor(web): use client-side React rendering for live calendar preview
- Render calendar using AbacusStatic components directly in browser
- Remove API call and React Query dependency for preview
- Show live preview that updates instantly when month/year changes
- Display generated PDF SVG after user clicks generate button
- Eliminates unnecessary server round-trip for interactive preview

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock 14a5de0dfa refactor(web): return calendar SVG preview with PDF generation
- Change generate API to return JSON instead of binary PDF
- Include base64-encoded PDF and SVG preview in response
- Update client to decode base64 PDF and trigger download
- Store SVG preview for display after generation
- Improve error handling to surface actual error messages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock 867c7ee172 feat(web): add year abacus to calendar header and make grid bolder
- Separate month name and year display in calendar header
- Render year as abacus element (15% of page width)
- Increase grid line weight from 1px to 2px
- Change grid color from light gray (#ddd) to dark (#333)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock 4f93c7d996 fix(web): use dynamic import for react-dom/server in API route
Next.js bundler flags react-dom/server imports even in API routes during static analysis.
Using dynamic import (await import()) ensures it only loads at runtime on the server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 08:55:05 -06:00
Thomas Hallock 00a8bc3e5e fix(web): move react-dom/server import to API route to satisfy Next.js
Changes:
- Scripts now export JSX elements (generateAbacusElement) or accept renderToString param
- API route imports react-dom/server (API routes are server-only, allowed)
- renderToStaticMarkup called in API route, not in imported scripts
- CLI interfaces use dynamic require() for react-dom/server when run directly

This satisfies Next.js requirement that react-dom/server not be imported in files that could be client-bundled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 08:48:14 -06:00
Thomas Hallock 9f1715f085 refactor(web): use direct function imports instead of execSync for calendar generation
Changes:
- Refactored scripts/generateCalendarAbacus.tsx to export generateAbacusSVG function
- Refactored scripts/generateCalendarComposite.tsx to export generateCalendarComposite function
- Updated API route to import and call functions directly instead of spawning subprocesses
- Removed tsx from production Dockerfile (not needed - Next.js transpiles at build time)
- Kept scripts and abacus-react package copies in Dockerfile (needed for imports)

This eliminates the need for tsx at runtime and improves performance by avoiding subprocess overhead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 08:27:41 -06:00
Thomas Hallock f9cbee8fcd fix(web): use nested SVG elements to prevent coordinate space conflicts
Fixed persistent overlap by using proper SVG nesting:

Previous approach: Extracted SVG content and used g transforms
- Problem: Inner coordinates remained in original 120x230 space
- Caused overlapping when elements had absolute positioning

New approach: Nested SVG elements with viewBox
- Each abacus rendered at natural scale=1 (120×230)
- Wrapped in <svg> with:
  - x, y: position in parent calendar
  - width, height: scaled display size
  - viewBox="0 0 120 230": maps content to display size
- Creates isolated coordinate space per abacus

This is the correct SVG pattern for embedding scaled content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 08:27:41 -06:00
Thomas Hallock 448f93c1e2 fix(web): prevent abacus overlap in composite calendar
Fixed spacing issues causing abaci to overlap:

Layout improvements:
- Calculate proper scale based on cell dimensions (CELL_WIDTH / 7, CELL_HEIGHT / 6)
- Account for natural abacus size (120×230) in scale calculation
- Use MAX_SCALE_X and MAX_SCALE_Y with 90% safety margin
- Center abacus in each cell using calculated scaled dimensions
- Add debug cell borders to visualize grid (stroke="#f0f0f0")

Math:
- ABACUS_SCALE = min(MAX_SCALE_X, MAX_SCALE_Y) × 0.9
- Position = cellCenter - (scaledSize / 2)
- This ensures abaci stay within cell boundaries

Before: Hardcoded offsets (-60, -115) caused overlaps
After: Dynamic centering based on actual scaled dimensions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 20:24:16 -06:00
Thomas Hallock 8ce8038bae feat(web): redesign monthly calendar as single composite SVG
BREAKING FIX: Monthly calendars were overflowing to multiple pages

New "page-first" design approach:
- Generate entire calendar as one composite SVG (850x1100px)
- Includes title, year abacus, weekday headers, and all day abacuses
- Typst scales the single image to fit page (width: 100%, fit: "contain")
- Impossible to overflow - it's just one scalable image

Benefits:
 Guaranteed single-page layout across all paper sizes
 No grid overflow issues
 Consistent rendering regardless of month length
 Fast generation (~97KB SVG vs multiple small files)

Implementation:
- Created generateCalendarComposite.tsx script
- Updated route to use composite for monthly, individual SVGs for daily
- Simplified generateMonthlyTypst to just scale one image

Daily calendars unchanged (intentionally multi-page, one per day).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 20:21:40 -06:00
Thomas Hallock b277a89415 feat(web): optimize monthly calendar for single-page layout
Design changes for better single-page fit:
- Reduce margins: US Letter 0.5in (was 1in), A4 1.3cm (was 2.5cm)
- Compact header: inline year abacus next to title (20% width vs 35%)
- Smaller fonts: title 18pt (was 24pt), weekdays 9pt (was 12pt)
- Tighter grid: 2pt gutter (was 4pt), added row-gutter
- Reduced vertical spacing: 0.5em (was 1.5em) after header

This ensures monthly calendars fit on one page across all paper sizes:
- US Letter (8.5" × 11")
- A4 (210mm × 297mm)
- A3 (297mm × 420mm)
- Tabloid (11" × 17")

Daily calendars continue to use one page per day (intentional multi-page).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 20:17:23 -06:00
Thomas Hallock 98cd019d4a fix: add xmlns to AbacusStatic for Typst SVG parsing
- Add xmlns="http://www.w3.org/2000/svg" to AbacusStatic root svg element
- Fixes "failed to parse SVG (missing root node)" Typst error
- React's renderToStaticMarkup doesn't add xmlns by default
- Required for standalone SVG files used outside HTML context
- Added debug logging to calendar generation route

Root cause: Typst's SVG parser requires explicit XML namespace declaration.
React assumes SVGs are embedded in HTML where xmlns is optional.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 20:10:22 -06:00
Thomas Hallock 08c6a419e2 fix(web): use AbacusStatic for calendar SVG generation
- Switch from AbacusReact to AbacusStatic in generateCalendarAbacus.tsx
- Fixes "failed to parse SVG (missing root node)" Typst error
- AbacusReact can't be rendered server-side with renderToStaticMarkup
- AbacusStatic is designed for pure server-side rendering

Root cause: AbacusReact has "use client" directive and uses hooks,
which causes renderToStaticMarkup to generate invalid/empty SVG.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 19:25:18 -06:00
Thomas Hallock 329e623212 fix(web): add dynamic export to rithmomachia page
Attempt to fix "Cannot access 'Z' before initialization" error during
static generation by forcing dynamic rendering.

Note: This doesn't fully resolve the initialization issue, but prevents
build from attempting static generation which causes the error.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 18:51:46 -06:00
Thomas Hallock 8439727b15 feat(web): improve calendar abacus preview styling
CalendarPreview changes:
- Remove showNumbers display from all abaci for cleaner calendar look
- Increase day abaci scaleFactor from 0.35 to 1.0 (fills grid squares)
- Remove year from month title (shows only month name)

CalendarConfigPanel changes:
- Replace link to /create with inline AbacusDisplayDropdown
- Add preview abacus showing current style (value=12, 2 columns)
- Users can now edit abacus styles directly in calendar page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 18:51:46 -06:00
Thomas Hallock 7ce1287525 fix(web): fix Typst PDF generation path resolution
- Change SVG image paths from absolute to relative in typstGenerator
- Execute typst compilation from tempDir with cwd option
- Fixes "file not found" error caused by Typst doubling absolute paths

Root cause: Typst treats absolute paths as relative and prepends working
directory, resulting in incorrect paths like:
/tmp/calendar-123/tmp/calendar-123/year.svg

Solution: Use relative paths ("year.svg") and run from tempDir.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 18:51:46 -06:00
Thomas Hallock 903dea2584 feat(web): add test page for AbacusStatic RSC compatibility
- Create /test-static-abacus page demonstrating pure Server Component usage
- Uses @soroban/abacus-react/static import (no client code)
- Renders 12 abacus displays with zero client-side JavaScript
- Verifies RSC export path works correctly in Next.js build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 18:51:46 -06:00
Thomas Hallock 3588d5acde feat(web): add test page for AbacusStatic Server Component
Creates /test-static-abacus route to demonstrate and verify that
AbacusStatic works correctly in React Server Components.

The page:
- Has NO "use client" directive (pure Server Component)
- Renders 12 different abacus values as static preview cards
- Uses compact mode and hideInactiveBeads for clean displays
- Includes visual confirmation that RSC rendering works

This serves as both a test and reference implementation for
using AbacusStatic in Next.js App Router Server Components.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 18:29:16 -06:00
Thomas Hallock 7228bbc2eb refactor(web): import utility functions from abacus-react
Re-export core utility functions from @soroban/abacus-react instead of
duplicating implementations:

- beadDiff.ts: Re-exports calculateBeadDiff, calculateBeadDiffFromValues,
  areStatesEqual, etc. Keeps app-specific calculateMultiStepBeadDiffs
  and validateBeadDiff.

- abacusInstructionGenerator.ts: Re-exports numberToAbacusState,
  calculateBeadChanges, and related types. Keeps app-specific
  tutorial generation logic.

- TutorialPlayer.tsx: Import calculateBeadDiffFromValues from abacus-react

- TutorialEditor.tsx: Import calculateBeadDiffFromValues from abacus-react

Eliminates ~200 lines of duplicate utility function implementations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock ff1d60a233 refactor(web): use compact prop for inline mini-abacus
Replace manual customStyles with compact={true} in AbacusTarget.tsx.

Before: 7 props + customStyles override
After: compact={true} automatically handles frame visibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock 9f7f001d74 refactor(web): use ABACUS_THEMES instead of manual style definitions
Replace manual theme style definitions with ABACUS_THEMES presets:
- MyAbacus.tsx: Use ABACUS_THEMES.light and ABACUS_THEMES.trophy
- HeroAbacus.tsx: Use ABACUS_THEMES.light
- LevelSliderDisplay.tsx: Use ABACUS_THEMES.dark

Eliminates ~60 lines of duplicate theme style code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock af0552ccd9 test: verify compose-updater automatic deployment cycle
Add test comment to trigger automatic deployment and verify that
compose-updater successfully updates the container without crashing
with the new separate project names configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:04:05 -06:00
Thomas Hallock 2b06aae394 test: trigger compose-updater deployment test
Testing that compose-updater successfully updates without crashing itself
2025-11-03 16:32:13 -06:00
Thomas Hallock 28a2d40996 fix: remove distracting parallax and wobble 3D effects
Remove all parallax hover effects and wobble physics from 3D enhancement system
as they were distracting and worsened the user experience.

Changes:
- Remove Abacus3DPhysics interface completely
- Remove physics3d prop from AbacusConfig and component
- Remove calculateParallaxOffset utility function
- Remove mouse tracking infrastructure (containerRef, mousePos, handleMouseMove)
- Update enhanced3d type to only support 'subtle' | 'realistic' (removed 'delightful')
- Update all 3D utilities to remove delightful mode support
- Remove all Delightful stories from Storybook
- Update Interactive Playground to remove parallax controls
- Change MyAbacus from delightful to realistic mode

The 3D enhancement system now focuses purely on visual improvements
(materials, lighting, textures) without any motion-based effects.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 15:41:49 -06:00
Thomas Hallock 37e330f26e feat: enable 3D enhancement on hero/open MyAbacus modes
Added delightful 3D mode to the hero and open states of MyAbacus:
- Glossy heaven beads + satin earth beads for premium feel
- Dramatic lighting for impact
- Wood grain texture on golden frame
- Hover parallax enabled for interactive depth
- Only applies to hero/open modes (not button mode)

The giant hero abacus on the home page now has satisfying
material rendering and interactive parallax effects.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 15:41:49 -06:00
Thomas Hallock 5d97673406 fix: remove wobble physics and enhance wood grain visibility
**Changes:**
- Removed wobble physics feature (was janky and distracting)
- Increased wood grain opacity from 0.15 → 0.4 (realistic) and 0.45 (delightful)
- Enhanced wood grain pattern with bolder strokes and more visible knots
- Removed getWobbleRotation utility function
- Simplified Abacus3DPhysics interface to only hoverParallax
- Updated all stories to remove wobble references
- Removed velocity tracking code from Bead component

Wood grain is now much more visible on frame elements without
affecting bead spacing or layout.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 15:41:49 -06:00
Thomas Hallock 096104b094 fix: use absolute positioning for hero abacus to eliminate scroll lag
Replace laggy JavaScript scroll tracking with proper CSS positioning:

Before:
- Used position: fixed with JavaScript scroll listener
- Calculated top position dynamically: calc(60vh - ${scrollY}px)
- Caused noticeable lag on mobile and slower browsers

After:
- Hero mode: position: absolute (scrolls naturally with document)
- Button mode: position: fixed (stays in viewport at bottom-right)
- Open mode: position: fixed (stays in viewport at center)
- Zero JavaScript scroll tracking needed

Result: Buttery smooth scrolling on all devices with zero lag.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 15:41:49 -06:00
Thomas Hallock f03d341314 fix: adjust hero abacus position to avoid covering subtitle
Move hero abacus down from 50vh to 60vh to prevent it from overlapping
the subtitle text ("master the ancient art..." etc.) on the home page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 13:24:15 -06:00
Thomas Hallock ed9a050d64 fix: resolve z-index layering and hero abacus visibility issues
Fix two critical issues with the trophy abacus system:

1. Hero abacus appearing on all pages:
   - Root cause: HomeHeroProvider now wraps all pages globally
   - Solution: Use pathname check to detect actual home page routes
   - Only show hero mode on: /, /en, /de, /ja, /hi, /es, /la

2. Z-index conflicts causing layering issues:
   - AppNavBar had hardcoded z-index: 1000 (DROPDOWN layer)
   - Should use Z_INDEX.NAV_BAR (100) for proper layering
   - Tooltip had z-index: 50, should use Z_INDEX.TOOLTIP (1000)

This ensures:
- Hero abacus only appears on home page, not all pages
- Trophy abacus (z-index 30001) appears above ALL content
- Nav bar and tooltips use correct z-index constants
- No stacking context conflicts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 13:24:15 -06:00
Thomas Hallock 423274657c fix: correct hero abacus scroll direction to flow with page content
The hero abacus was scrolling in the opposite direction of page content due to incorrect math. Fixed by subtracting scroll position instead of adding it.

Change: top: calc(50vh - ${scrollY}px) instead of calc(50vh + ${scrollY}px)

Now the abacus properly scrolls up with the page content when scrolling down.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 13:24:15 -06:00
Thomas Hallock 6620418a70 feat: add unified trophy abacus with hero mode integration
Implement a single abacus component that seamlessly transitions between three states: hero mode on the home page, compact button in the corner, and full-screen modal display.

Key features:
- Hero mode: Large white abacus at top-center of home page, interactive with beads
- Button mode: Golden mini abacus in bottom-right corner when hero scrolled away or on other pages
- Open mode: Full-screen golden abacus with blur backdrop, accessible from button
- Smooth fly-to-corner animation when scrolling past hero section
- Two-way sync between hero and trophy abacus values via HomeHeroContext
- Highest z-index layer (30000+) to appear above all content

Implementation:
- Create MyAbacus component handling all three states with smooth transitions
- Add MyAbacusContext for global open/close state management
- Move HomeHeroProvider to ClientProviders for global access
- Replace HeroAbacus with HeroSection placeholder on home page
- Fix flashcard z-index by creating proper stacking context (z-index: 1)
- Add MY_ABACUS z-index constants (30000-30001)
- Add calendar page components with correct styled-system imports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 13:24:15 -06:00
Thomas Hallock 88993f3662 fix: tolerate OpenSCAD CGAL warnings if output file is created
OpenSCAD 2021.01 (Debian stable) has CGAL geometry bugs that cause
non-zero exit status when processing complex STL operations, but it
still produces valid output files.

Instead of failing the job when OpenSCAD exits with non-zero status,
we now check if the output file was created. If it exists, we proceed
with the job. Only if the file is missing do we treat it as a failure.

This allows 3D model generation to work on production despite the
older OpenSCAD version's CGAL warnings.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 12:18:00 -06:00
Thomas Hallock d7b35d9544 fix: mark dynamic routes as force-dynamic to prevent static generation errors
Next.js build was failing because routes using headers() were being
statically generated during build time. Added `export const dynamic = 'force-dynamic'`
to:
- /api/player-stats (uses getViewerId which reads headers)
- /api/debug/active-players (uses getViewerId which reads headers)
- /opengraph-image (reads filesystem during render)

Build errors:
- Route /api/player-stats couldn't be rendered statically because it used `headers`
- Route /api/debug/active-players couldn't be rendered statically because it used `headers`
- Error occurred prerendering page "/opengraph-image"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 11:09:06 -06:00
Thomas Hallock b67cf610c5 fix: various game improvements and UI enhancements
Collection of improvements across multiple games and components:

- Complement Race: Improve sound effects timing and reliability
- Card Sorting: Enhance drag-and-drop physics and visual feedback
- Memory Quiz: Fix input phase keyboard navigation
- Rithmomachia: Update playing guide modal content and layout
- PageWithNav: Add viewport context integration
- ArcadeSession: Improve session state management and cleanup
- Global CSS: Add utility classes for 3D transforms

Documentation:
- Add Google Classroom setup guide
- Update Claude Code settings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 25880cc7e4 feat: add game preview system with mock arcade environment
Add comprehensive preview system for arcade games:

- GamePreview component: Renders any arcade game in preview mode
- MockArcadeEnvironment: Provides isolated context for previews
- MockArcadeHooks: Mock implementations of useArcadeSession, etc.
- MockGameStates: Pre-defined game states for each arcade game
- ViewportContext: Track and respond to viewport size changes

Enables rendering game components outside of arcade rooms for:
- Documentation and guides
- Marketing/showcase pages
- Testing and development
- Game selection interfaces

Mock states include setup, playing, and results phases for all
five arcade games (matching, complement-race, memory-quiz,
card-sorting, rithmomachia).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock b91b23d95f refactor: restructure /create page into hub with sub-pages
Reorganize create functionality into a central hub page:

- Move flashcard creation to /create/flashcards
- Add new creation hub at /create with options for:
  - Flashcards (existing feature)
  - Abacus 3D models (new feature)
  - Future creation tools

The hub page provides a clean navigation interface for all
content creation features.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock dafdfdd233 feat: add 3D printing support for abacus models
Add comprehensive 3D printing capabilities for generating custom
abacus models in STL format:

- OpenSCAD integration in Docker container
- API endpoint: POST /api/abacus/generate-stl
- Background job processing with status monitoring
- STL file preview with Three.js
- Interactive abacus customization page at /create/abacus
- Configurable parameters: columns, bead shapes, dimensions, colors
- Export formats: STL (for 3D printing), SCAD (for editing)

Components:
- STLPreview: Real-time 3D model viewer
- JobMonitor: Background job status tracking
- AbacusCustomizer: Interactive configuration UI

Docker: Add OpenSCAD and necessary 3D printing tools
Dependencies: Add three, @react-three/fiber, @react-three/drei

Generated models stored in public/3d-models/
Documentation: 3D_PRINTING_DOCKER.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 613301cd13 feat: add per-player stats tracking system
Implement comprehensive per-player statistics tracking across arcade games:

- Database schema: player_stats table with per-player metrics
- Migration: 0013_add_player_stats.sql for schema deployment
- Type system: Universal GameResult types supporting all game modes
  (competitive, cooperative, solo, head-to-head)
- API endpoints:
  - POST /api/player-stats/record-game - Record game results
  - GET /api/player-stats - Fetch all user's players' stats
  - GET /api/player-stats/[playerId] - Fetch specific player stats
- React hooks:
  - useRecordGameResult() - Mutation hook with cache invalidation
  - usePlayerStats() - Query hooks for fetching stats
- Game integration: Matching game now records stats on completion
- UI updates: /games page displays per-player stats in player cards

Stats tracked: games played, wins, losses, best time, accuracy,
per-game breakdowns (JSON), favorite game type, last played date.

Supports cooperative games via metadata.isTeamVictory flag where
all players share win/loss outcome.

Documentation added:
- GAME_STATS_COMPARISON.md - Cross-game analysis
- PER_PLAYER_STATS_ARCHITECTURE.md - System design
- MATCHING_GAME_STATS_INTEGRATION.md - Integration guide

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 9f51edfaa9 feat(games): add autoplay and improve carousel layout
Major improvements to the games hero carousel:
- Added smooth autoplay (4s delay, stops on interaction/hover)
- Made carousel full-width to reduce virtual scrolling artifacts
- Added horizontal padding for better buffer on edges
- Restructured layout: carousel is now full-width, rest of content
  remains constrained to max-width
- Removed containScroll setting to improve infinite loop behavior

The carousel now smoothly rotates through games automatically and
has better visual consistency at the loop edges.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 946e5d1910 feat: install embla-carousel-autoplay for games carousel
Add embla-carousel-autoplay dependency to enable smooth automatic
rotation of games in the hero carousel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 5a8c98fc10 fix(games): prevent horizontal page scroll from carousel overflow
Added overflowX: hidden to page container to clip carousel at viewport
level. This prevents the infinite carousel from making the entire page
horizontally scrollable while still allowing game cards to visually
extend beyond their container.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 876513c9cc refactor(games): remove wheel scrolling, enable overflow visible carousel
Removed non-functional wheel scrolling code and changed games carousel
to overflow: visible for a full-width effect that extends beyond the
container. Creates a more immersive, cinematic feel where game cards
bleed into the page edges.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock f80a73b35c fix(games): smooth scroll feel for carousel wheel navigation
Changed carousel wheel scrolling from discrete next/prev jumps to smooth
scrolling that follows the wheel delta. Now it feels like actually
scrolling instead of a rocket engine!

- Uses embla's internal scroll container directly
- Scales wheel delta by 0.5 for comfortable scroll speed
- Maintains horizontal scroll and shift+scroll detection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock a224abb6f6 feat(games): add horizontal scroll support to carousels
Both games and player carousels now support horizontal scrolling with:
- Mouse wheel horizontal scroll (trackpad swipe gestures)
- Shift + vertical scroll for horizontal navigation
- Prevents page scroll when interacting with carousel

Implemented with custom wheel event handlers that detect horizontal
scroll intent and call embla's scrollNext/scrollPrev methods.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 187271e515 fix(games): use specific transition properties for smooth carousel loop
Fixed carousel looping issue by replacing generic 'transition: all' with
specific transition properties. Only transition opacity on carousel items
and transform/box-shadow/border-color on cards for hover effects.

This prevents CSS transitions from interfering with embla's carousel
transform animations at the loop wrap point.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 24231e6b2e feat(games): add rotating games hero carousel
Added a hero section at the top of the games page featuring a rotating
carousel of all available arcade games. Each card shows:
- Game icon and name
- Difficulty and player count
- Description
- Category chips (Multiplayer, Memory, Soroban, etc.)
- Game-specific gradient background

Uses embla-carousel for smooth dragging and navigation dots with game
icons for quick access. Cards link directly to each game's arcade page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock ad5bb87325 refactor(games): remove redundant subtitle below nav
Removed the hero section subtitle since the page title is now in the
nav bar. The subtitle was awkwardly positioned where nobody would see it.
Added top padding to account for the fixed nav bar.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock d3fe6acbb0 fix(nav): show full navigation on /games page
Fixed AppNavBar auto-detecting /games route as needing minimal mode.
The /games page should use the full navigation bar (like /create and
/guide), not the minimal arcade nav with only a hamburger menu.

Changed route detection to only apply minimal nav to /arcade/* routes,
not /games route.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:53 -06:00
Thomas Hallock 96c760a3a5 chore: remove static icon.svg replaced by dynamic generation
The static icon.svg has been replaced by dynamic day-of-month favicon
generation via src/app/icon/route.tsx which calls scripts/generateDayIcon.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 944ad6574e feat: switch to royal color theme with transparent background
Update favicon design based on user preference:
- Ones place: Gold (#fbbf24, stroke #f59e0b)
- Tens place: Purple (#a855f7, stroke #7e22ce)
- Remove background circle for clean, transparent appearance

The royal color scheme provides better contrast and a more
sophisticated look while maintaining excellent legibility at small sizes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 0e529be789 fix: reduce padding to minimize gap below last bead
Use asymmetric padding to crop tighter to the active bead region:
- Top: 8px (some breathing room above)
- Bottom: 2px (minimal gap below last bead)
- Sides: 5px

This eliminates the visible gap of column post structure below the
last active bead while maintaining clean spacing above.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 440b492e85 fix: use nested SVG viewBox for actual cropping, not just scaling
Previous approach used transforms to scale the entire SVG, which didn't
actually crop anything - just scaled everything uniformly.

New approach uses nested SVG with viewBox to truly crop the content:
- Outer SVG: 100x100 canvas with background circle
- Inner SVG: Uses viewBox to show only the cropped region
- viewBox dimensions vary per day based on active bead positions

Results:
- Day 1 (1 bead): viewBox height ~59px (narrow vertical crop)
- Day 15 (mixed): viewBox height ~88px (medium)
- Day 31 (many): viewBox height ~104px (tall vertical crop)
- Horizontal: Always ~110px (both columns with posts)

This actually maximizes bead size by showing only the relevant vertical region.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 83090df4df fix: separate horizontal and vertical bounding box logic
Clarify that horizontal and vertical cropping use different criteria:
- Horizontal (X): Always show full width of both columns (from structural rects)
- Vertical (Y): Crop dynamically to active bead positions + heights

This ensures consistent horizontal scale (0.8) across all days, with only
vertical positioning adjusting based on where active beads are located.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 0b2f48106a fix: include column posts in favicon bounding box
Refined the cropping algorithm to include structural elements (column posts)
in the bounding box calculation, not just active beads. This ensures the
complete abacus structure is visible.

Algorithm changes:
- X range: Full width from leftmost to rightmost post (all columns)
- Y range: From top active bead to bottom active bead (tight vertical crop)

Results:
- Consistent scale of 0.8 across all days (vs original 0.48)
- Shows complete column structure with posts
- Vertical position adjusts based on active bead positions
- Maintains visual context while maximizing bead visibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 567032296a feat: dynamically crop favicon to active beads for maximum size
Implement bounding box calculation to crop the favicon SVG to show only
the active beads, maximizing their size within the 100x100 icon canvas.

Algorithm:
1. Parse rendered SVG to find all active bead positions (via regex)
2. Calculate bounding box with 15px padding
3. Compute optimal scale to fit within 96x96 (leaving border room)
4. Apply transform: translate + scale + translate to crop and center

Results:
- Day 1-9 (few beads): scale ~1.14 (2.4x larger than before)
- Day 31 (many beads): scale ~0.74 (1.5x larger than before)
- Original fixed scale: 0.48

This uses no external dependencies - just regex parsing of the rendered
SVG to extract active bead coordinates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock e1369fa275 fix: configure favicon metadata and improve bead visibility
Metadata fixes:
- Update icon path from /icon.svg to /icon to match route handler
- Ensure proper MIME type (image/svg+xml) is set in both metadata and response

Favicon visibility improvements:
- Increase AbacusReact scaleFactor from 1.0 to 1.8 for larger beads
- Add hideInactiveBeads prop to hide inactive beads
- Add hide-inactive-mode class to wrapper for CSS to take effect
- Adjust outer scale to 0.48 to fit larger abacus in 100x100 viewBox
- Reposition abacus with translate(28, -2) for proper centering

CSS cleanup:
- Strip !important declarations from generated SVG (production code policy)
- Apply same fix to OG image generator

User needs to restart dev server to clear in-memory cache.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 4d0795a9df feat: dynamic day-of-month favicon using subprocess pattern
- Create scripts/generateDayIcon.tsx for on-demand icon generation
- Route handler calls script via execSync (avoids Next.js react-dom/server restriction)
- Implement in-memory caching to minimize subprocess overhead
- Show current day (01-31) on 2-column abacus in US Central Time
- High-contrast design: blue/green beads, 2px strokes, gold border
- Document SSR pattern in .claude/CLAUDE.md for future reference

Fixes the Next.js limitation where route handlers cannot import react-dom/server
by using subprocess pattern - scripts CAN use react-dom/server, route handlers
call them via execSync and cache the results.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 712ee58e59 refactor(games): move page title to nav bar
Move 'Soroban Arcade' title from page content to navigation bar
for consistency with other app pages (create, guide).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 6333c60352 feat(i18n): update games page hero section copy
Simplified hero section messaging:
- Changed subtitle from 'Level up your mental math powers...' to
  'Classic strategy games and lightning-fast challenges'
- Removed outdated feature pills (xpBadge, streakBadge, features)
- More accurate description of current game offerings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 07:04:09 -06:00
Thomas Hallock 82c133f742 refactor(games): implement carousel, fix victories bug, add conditional stats
This commit includes several related improvements to the games page:

1. Fix victories calculation - removed incorrect division by player count
2. Implement player profile carousel using embla-carousel-react
   - Smooth infinite loop carousel with drag support
   - Navigation dots with player emojis
   - Simple opacity-based transitions (no 3D effects)
   - Disabled text selection during drag
   - Fixed gap spacing at carousel wrap point
3. Add conditional rendering - hide stats sections when user has no gameplay data
4. Update hero section - simplified design, removed excessive animations
5. Switch to PageWithNav for consistent navigation across the app

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 07:04:09 -06:00
Thomas Hallock 642ae95738 feat: install embla-carousel-react for player profile carousel
Add embla-carousel-react dependency to enable smooth carousel
functionality for displaying player profiles on the games page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 07:04:09 -06:00
Thomas Hallock 71255d3198 Configure UTC timeZone for next-intl to prevent SSR markup mismatches
- Add timeZone: 'UTC' to server-side i18n config in src/i18n/request.ts
- Add timeZone="UTC" prop to NextIntlClientProvider in src/components/ClientProviders.tsx
- Fix TypeScript type error: handle undefined cookie value with nullish coalescing
- Eliminates IntlError: ENVIRONMENT_FALLBACK warnings in development server

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 07:04:09 -06:00
Thomas Hallock f2bbd91801 Redesign OG image and favicon using AbacusReact server-side rendering
- Generate icon.svg and og-image.svg from AbacusReact component via scripts/generateAbacusIcons.tsx
- OG image: huge 4-column abacus with place-value colors, dark theme with decorative diamond shapes and math operators at 0.4/0.35 opacity for visibility
- Favicon: single-column abacus showing value 5, dark brown beads, properly centered and scaled (0.7)
- opengraph-image.tsx: read pre-generated og-image.svg instead of using react-dom/server (avoids edge runtime restriction)
- All abacus visualizations now use AbacusReact component consistently

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 07:04:09 -06:00
Thomas Hallock 9c20f12bac refactor: use AbacusReact for dynamic Open Graph image
Replaced manual HTML/CSS abacus representation in opengraph-image.tsx
with server-side rendered AbacusReact component, using the same SVG
extraction approach as icon.svg and og-image.svg.

Now all three image generation methods use the actual AbacusReact
component from @soroban/abacus-react instead of manual recreations.

Changes:
- Added renderToStaticMarkup and AbacusReact imports
- Added extractSvgContent() function to parse SVG from rendered markup
- Replaced 150+ lines of manual HTML/CSS with AbacusReact render
- Embedded extracted SVG in ImageResponse via dangerouslySetInnerHTML

Benefits:
- Consistent abacus rendering across all images
- Automatic updates when AbacusReact component changes
- Significantly less code to maintain

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 04:30:09 -06:00
Thomas Hallock b07f1c4216 fix: extract pure SVG content from AbacusReact renders
Fixed generateAbacusIcons.tsx to properly extract SVG content from
AbacusReact server-side renders, removing invalid div wrappers that
prevented the abacus from displaying in icon.svg and og-image.svg.

Changes:
- Added extractSvgContent() function to parse rendered markup
- Regenerated icon.svg with proper SVG structure
- Regenerated og-image.svg showing abacus value 123
- Removed unused React import (modern TSX doesn't need it)

The Open Graph image now correctly displays the abacus visualization
alongside the site text, fixing the issue where only text was visible.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 04:25:53 -06:00
Thomas Hallock 0922ea10b7 feat: add comprehensive metadata, SEO, and make AbacusReact SSR-compatible
- Update metadata in layout.tsx with full SEO, Open Graph, Twitter cards, PWA config
- Create sitemap.ts for dynamic sitemap generation
- Create robots.ts for search engine guidance
- Create icon.svg favicon generated from AbacusReact component
- Create opengraph-image.tsx for dynamic Open Graph image generation
- Create public/og-image.svg as fallback Open Graph image
- Make AbacusReact component SSR-compatible by:
  - Detecting server environment and conditionally using animations
  - Using regular SVG elements instead of animated ones when on server
  - Making useSpring and useDrag hooks SSR-safe
- Add generateAbacusIcons.tsx script to generate icons from real AbacusReact component

All icons now use the actual AbacusReact component rendering, ensuring consistency
between static assets and the interactive version.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 04:06:23 -06:00
Thomas Hallock fb629c44ea refactor: reorganize Harmony and Victory guide sections
Eliminated duplication between Harmony and Victory tabs:

**Harmony Tab** (focus: what ARE harmonies and how to FORM them):
- Renamed title to "Mathematical Progressions" (clearer focus)
- Updated intro to focus on patterns, not winning
- Removed "Important Rules" section (moved to Victory)
- Kept: 3 harmony types, formation strategies, quick reference

**Victory Tab** (focus: how to WIN):
- Expanded harmony victory section with placement requirements:
  - Must be in enemy territory
  - Must be in straight line
  - Must be adjacent/touching
  - Must form progression
  - Must survive one turn
- Added reference to Harmony tab for progression details
- Kept: Exhaustion victory, strategy tips

Benefits:
- Clear separation of concerns
- No duplication
- Better learning flow
- Matches SPEC.md simplified rules

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 19:38:13 -06:00
Thomas Hallock b5a96eaeb1 fix: board rotation now properly fills height in portrait mode
Fixed board rotation issues when guide is docked:
- Board now prioritizes HEIGHT when in portrait orientation
- Removed CSS constraints (maxWidth, maxHeight, aspectRatio) that were
  interfering with explicit dimensions during rotation
- Board properly fills container whether guide is docked or not
- Smooth transition between portrait and landscape orientations

Technical changes:
- Set maxWidth/maxHeight to 'none' when svgDimensions are explicit
- Set aspectRatio to 'auto' when rotating to prevent override
- Cleaned up debug logging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 19:06:41 -06:00
Thomas Hallock 56f3164155 fix(rithmomachia): handle pyramid pieces in hover error tooltip
Fixed hover error tooltip to properly check pyramid pieces by testing
all 4 pyramid faces for valid capture relations. Previously the code
was using getEffectiveValue() which returns null for pyramids, causing
the tooltip to never show.

Now for pyramid pieces, the code iterates through all 4 faces and only
shows the "No valid relation" error if NONE of the faces can form a
valid relation with the target piece.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 14:04:59 -06:00
Thomas Hallock 4ce561e785 debug: add console logging for hover error tooltip and guide dragging
Added targeted console logging to help debug two issues:
1. Capture error tooltip on hover - logs mouse events, piece detection,
   path validation, and relation checking
2. Guide dragging from docked state - logs drag events, undocking logic,
   and position calculations

All logs use string concatenation and are prefixed with [HOVER_ERROR]
or [GUIDE_DRAG] for easy filtering. Removed unrelated debug logging.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:59:06 -06:00
Thomas Hallock bf48ed1757 chore: add type definitions for react-textfit
Installed @types/react-textfit to resolve TypeScript compilation error
in PlayingGuideModal.tsx. This package provides type definitions for
the react-textfit library used in the Rithmomachia guide component.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:45:52 -06:00
Thomas Hallock 8f4a79c9b0 fix(rithmomachia): smooth guide dragging from docked state without jump
Fixed the guide modal jumping to center when dragging from docked state.

**Root Cause:**
When undocking, the code was centering the modal on the cursor:
- Position set to: `{ x: e.clientX - width/2, y: e.clientY - 20 }`
- This caused a visual jump from the docked position to cursor-centered

**Solution:**
Changed undocking logic to maintain the modal's current visual position:
- Position set to: `{ x: rect.left, y: rect.top }` (current screen position)
- dragStart set to: `{ x: e.clientX - rect.left, y: e.clientY - rect.top }` (offset)

**Result:**
- Modal stays exactly where it is visually when undocking (no jump!)
- dragStart correctly reflects the cursor offset from modal position
- Subsequent mouse movements smoothly continue the drag operation
- Single continuous drag gesture from docked to floating state

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:45:52 -06:00
Thomas Hallock bd49964186 fix(rithmomachia): validate move path before showing capture error on hover
Fixed capture error tooltip not showing by adding path validation before
checking for invalid capture relations.

The hover error now correctly:
1. Validates the move path is legal (using validateMove)
2. Only shows if path is valid AND there's no valid capture relation
3. Prevents showing errors for moves that aren't even possible

Added import for validateMove from pathValidator utils.

This ensures the "no valid relation" tooltip only appears when you're
hovering over an enemy piece that your selected piece can legally move to,
but cannot capture due to missing valid numeric relations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:38:36 -06:00
Thomas Hallock 5c2ddbef05 feat(rithmomachia): show pyramid face numbers when selected with subtle animation
Changed pyramid face numbers to display when the piece is selected for movement,
instead of on hover, with subtle fade-in and scale animation:

**PieceRenderer changes:**
- Added react-spring import for animations
- Added pyramidNumbersSpring with fade-in (opacity 0→1) and scale (0.8→1) effect
- Wrapped pyramid face numbers in animated.g with spring-driven opacity and scale
- Changed from `hovered` prop back to `selected` prop
- Uses gentle spring config: tension 200, friction 20 for smooth, non-distracting animation

**SvgPiece changes:**
- Removed hover state management (useState, onMouseEnter/Leave)
- Reverted to passing `selected` prop to PieceRenderer
- Simplified component by removing hover tracking

Pyramid face numbers now appear when the piece is selected (ready to move),
with a subtle zoom-in effect that's noticeable but not distracting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:35:53 -06:00
Thomas Hallock 339b6780f6 feat(rithmomachia): show capture error on hover instead of click
Changed "no valid relation" error to appear as a tooltip when hovering over
an enemy piece with a selected piece, instead of after clicking:

**CaptureErrorDialog changes:**
- Removed OK button (no manual dismiss needed)
- Made dialog non-interactive with pointerEvents: 'none'
- Simplified layout to center-aligned content

**BoardDisplay changes:**
- Added hoveredSquare state to track hovered enemy pieces
- Added handleSvgMouseMove to detect hovers over enemy pieces
- Added handleSvgMouseLeave to clear hover state
- Calculate showHoverError when hovering over invalid capture target
- Display inline hover error tooltip at hovered square position
- Auto-dismisses when mouse leaves (no manual dismiss needed)

This provides instant feedback about invalid captures before clicking,
improving the UX by showing errors as a preview rather than after attempting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:34:12 -06:00
Thomas Hallock b0c4523c0b feat(rithmomachia): show pyramid face numbers on hover instead of selection
Changed pyramid face numbers to appear when hovering over any pyramid piece,
making them visible to all players as public information:

- Updated SvgPiece to manage hover state with useState
- Added onMouseEnter and onMouseLeave handlers
- Changed PieceRenderer prop from `selected` to `hovered`
- Updated condition to show numbers on hover for pyramid pieces
- Removed dependency on piece selection state

Pyramid face values are not secret information, so any player can now see
them by hovering over a pyramid piece, regardless of whose turn it is.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:28:12 -06:00
Thomas Hallock 7bf2d730d3 style(rithmomachia): increase pyramid face numbers size and boldness
Made pyramid face numbers significantly larger and bolder:
- Increased font size from 0.26 to 0.35 (34.6% larger)
- Increased stroke width from 0.03 to 0.05 (66.7% thicker outline)
- Changed font to "Arial Black" for maximum boldness
- Font weight remains at 900 (maximum)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:26:22 -06:00
Thomas Hallock 94e5e6a268 style(rithmomachia): improve pyramid face numbers visibility and contrast
Enhanced visual treatment of pyramid face numbers when pieces are selected:
- Increased font size from 0.16 to 0.26 (62.5% larger)
- Repositioned to avoid edge clipping (0.12/0.88 instead of 0.05/0.95)
- Added dual-layer rendering: outline stroke + filled text with drop shadow
- Used vibrant amber colors (#fbbf24 for dark, #b45309 for light pieces)
- Added CSS transition for smooth animations
- Changed to bold Arial font (weight 900) for better readability
- High-contrast drop shadow (opacity 0.9) for visibility against yellow selection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:22:52 -06:00
Thomas Hallock 5c186f3947 feat(rithmomachia): show pyramid face numbers when selected
Pyramids now display all 4 face values around the piece when selected,
making it clear to players which numbers the pyramid can use for captures.

Implementation:
- Face numbers appear at top, right, bottom, left positions
- Only shown when pyramid piece is selected
- Clean board appearance when not selected
- Numbers scale appropriately with piece size

This permissive display helps players understand pyramid capabilities
without requiring explicit face selection (game auto-validates which
faces enable valid captures).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 12:58:08 -06:00
Thomas Hallock b314740697 feat(rithmomachia): improve guide UX and add persistence
UI/UX Improvements:
- Bust-out button now closes the modal version when opening guide in new window
- Guide button is hidden when guide is already open to avoid confusion
- Replaced undock button with intuitive drag-to-undock gesture (drag title bar away from edge)
- Guide now supports dragging even when docked, with 50px threshold before undocking
- Improved cursor feedback (grab/grabbing) for docked guide

Persistence:
- Guide modal position and size are saved to localStorage
- Docked state (docked/floating) is saved to localStorage
- Dock side preference (left/right) is saved to localStorage
- Guide reopens in the same state and position as when it was last closed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 12:33:53 -06:00
Thomas Hallock 11f674d542 feat(rithmomachia): guide defaults to docked right on open
Change guide opening behavior:
- Created handleOpenGuide() function
- Sets guideDocked=true and guideDockSide='right' when opening
- Guide now opens docked to the right side by default
- No need to drag and dock manually on first open
- Provides better initial UX with guide immediately in workspace

User can still undock or drag to left side if preferred.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 12:24:22 -06:00
Thomas Hallock 9fd54067ce feat(rithmomachia): auto-size tab labels with react-textfit
Replace ellipsized tab labels with react-textfit library:
- Install react-textfit package
- Use Textfit component for tab text labels
- Auto-scales font size (8px-14px) using binary search to fit width
- No more ellipsis (...) on narrow tabs
- Full tab labels always visible and readable
- Icon stays fixed size, only text scales

Tab labels now automatically shrink to fit available space while
remaining fully readable.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 12:23:24 -06:00
Thomas Hallock 88ca35e044 style(rithmomachia): improve divider styling and make tabs responsive
Panel Divider:
- Change from purple (rgba(139, 92, 246)) to neutral gray (#e5e7eb)
- Matches guide's light gray theme better
- Width: 2px (thinner), grows to 3px on hover
- Hover color: medium gray (#9ca3af)
- More elegant and fits the design aesthetic

Guide Tabs:
- Make tabs fully responsive with flex: '1 1 0'
- All tabs always visible and sized equally
- Remove horizontal scroll and fade indicators
- Tabs shrink/grow to fit available width
- Add text overflow handling for very narrow states
- Clean, simple design without scroll affordances

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 12:14:17 -06:00
Thomas Hallock 2a91748493 fix(rithmomachia): change undock icon to pop-out arrow
Replace confusing fullscreen-like icon (⛶) with pop-out arrow (⤴️)
for the undock button. The new icon better conveys the action of
"pop out to floating mode" rather than looking like fullscreen/maximize.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 11:48:55 -06:00
Thomas Hallock 190f8cf302 fix(rithmomachia): render guide as docked in preview panel
Fix the guide in the preview panel to render as docked (not as another
floating modal):
- Set docked={true} when guide is in panel (both preview and committed dock)
- Guide now uses relative positioning, no shadow, fills panel properly
- Only show undock button when truly docked (not during preview)
- Preview guide now visually matches the final docked appearance

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 11:23:20 -06:00
Thomas Hallock 17d2460a87 feat(rithmomachia): show real preview layout when dragging guide to dock
Replace ghost panel overlay with actual docked layout preview:
- Add onDockPreview callback to communicate preview state to parent
- Parent renders real PanelGroup layout when dockPreviewSide is set
- Guide appears in docked position showing real final layout
- Board resizes automatically to show what docked state will look like
- Dragging modal shown at 0.8 opacity during preview
- Both guide (in panel) and dragging modal visible simultaneously
- Clear preview when drag ends or moves away from edges

This provides much better visual feedback - user sees exactly what
the final docked layout will look like before releasing the drag.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 11:02:57 -06:00
Thomas Hallock c0d6526d30 feat(rithmomachia): add ghost panel preview for guide docking
Add visual feedback when dragging the guide near dockable edges:
- Ghost panel preview appears when within 100px of left/right edge
- Semi-transparent purple overlay (35% width) shows where guide will dock
- Large arrow indicator (← or →) shows docking direction
- Dashed border for clear visual feedback
- Preview clears when drag ends or moves away from edges
- Preview only shows when guide is not already docked

This makes the docking feature discoverable and provides clear
visual feedback about where the guide will be positioned.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 10:31:18 -06:00
Thomas Hallock 01247589a8 debug(rithmomachia): add console logging for guide docking
Add comprehensive console logging to debug guide docking feature:
- Log when PlayingGuideModal renders with props (isOpen, docked, handlers)
- Log when drag starts and ends
- Log mouse position and docking decision logic
- Log when handleDock/handleUndock are called
- Log state changes in RithmomachiaGame

This will help identify where the docking flow is breaking.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 10:28:33 -06:00
Thomas Hallock f457f1a1c2 feat(rithmomachia): add guide docking with resizable panels
Implement ability to dock the Rithmomachia playing guide to left or right
side of the board screen. When docked, guide and board are in separate
panels with a resizable divider between them.

Features:
- Drag guide to left/right edge (within 100px) to dock it
- Resizable divider using react-resizable-panels library
- Guide defaults to 35% width, resizable between 20-50%
- Board takes remaining space (minimum 50%)
- Undock button (⛶) returns guide to floating modal mode
- No overlap between board and guide when docked
- Dragging disabled when docked
- Styling adjusted for docked mode (no shadows, no border-radius)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 10:17:26 -06:00
Thomas Hallock 0b06a1ce00 feat(i18n): complete Old High German translations for all locales
- Translate home page (learn by doing, skills, arcade, flashcards)
- Translate games page (hero, champions, dashboard, challenges)
- Translate tutorial player (controls, navigation, steps, tooltips)
- Translate guide sections (reading numbers, structure, arithmetic)
- Translate Rithmomachia game guide (329 lines):
  - All piece types and movement rules
  - 6 capture mechanics (equality, multiple, sum, difference, product, ratio)
  - 3 harmony types (arithmetic, geometric, harmonic)
  - Complete strategy & tactics section
  - Victory conditions and tips

Uses authentic Althochdeutsch vocabulary (750-1050 AD) with proper
grammar, inflections, and period-appropriate terminology.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 10:11:14 -06:00
Thomas Hallock 81ead65680 feat: add Strategy & Tactics section to Rithmomachia guide
- Create StrategySection.tsx component with comprehensive strategy content
- Add opening principles, mid-game tactics, victory paths, common mistakes, and advanced concepts
- Include interactive board diagrams for each strategic concept
- Integrate Strategy section into PlayingGuideModal navigation (between Capture and Harmony)
- Use existing translation keys from strategy section already present in all locale files
- Add brain emoji (🧠) icon for Strategy tab

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 09:43:40 -06:00
Thomas Hallock b334a15255 feat(i18n): add Old High German (goh) language support
Add Old High German as a supported language across the entire app:

**Core i18n configuration:**
- Add 'goh' to supported locales in routing.ts
- Update Locale type in messages.ts to include 'goh'
- Add language selector label "Diutisc" with 🏰 flag emoji

**Translation files:**
- Create goh.json for all feature domains (home, games, guide, tutorial)
- Create goh.json for rithmomachia game
- Update all messages.ts files to import and export goh translations
- Currently using English text as placeholder (ready for translation)

**Bug fixes:**
- Add missing translation keys to ALL locales:
  - rithmomachia.guide.overview.boardCaption
  - rithmomachia.guide.pieces.pyramidMovement
- These keys were referenced in components but missing from translation files

Old High German will now appear in the language selector and can be selected.
All UI text currently displays in English until proper translations are added.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:52:46 -06:00
Thomas Hallock a673177bec fix(rithmomachia): improve guide modal tab navigation at narrow widths
Fix tab navigation behavior on narrow widths:
- Keep tabs horizontal at all widths (remove vertical stacking at very narrow)
- Show scroll fade indicators at all narrow widths including very narrow
- Hide scrollbar for cleaner appearance while indicators show scroll availability
- Tabs are now icon-only at very narrow widths (<250px) with proper spacing
- Add whiteSpace: nowrap to prevent tab content wrapping

This makes the tabs more usable at ultra-narrow widths (down to 150px) while
making it clear that horizontal scrolling is available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:37:01 -06:00
Thomas Hallock a270bfc0cc feat(rithmomachia): improve guide pieces section layout
Add chess analogies and simplify piece display:
- Add chess piece analogies (bishop, rook, queen, king) for each piece type
- Simplify from multiple example values to single representative value
- Include Pyramid in main pieces array with chessAnalogy "like a king"
- Larger piece visualization (80px) with cleaner layout
- Display format: value, name, movement with analogy, count

Keeps detailed pyramid explanation section below the main pieces list.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:37:01 -06:00
Thomas Hallock 2f09e620f6 chore: apply Biome auto-formatting
Auto-format changes from Biome:
- Multi-line array formatting for COLUMN_LABELS
- Type import statement formatting
- Line wrapping improvements for readability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:37:01 -06:00
Thomas Hallock 04741971b2 feat(rithmomachia): make guide modal ultra-responsive down to 150px width
The guide modal is now usable at extremely narrow widths for better screen
real estate management while playing.

Responsive breakpoints:
- Very narrow (<250px): Vertical icon-only navigation, minimal header, tiny padding
- Narrow (250-400px): Horizontal icon + first word, compact layout, scroll indicators
- Medium (400-600px): Full labels, normal spacing
- Wide (>600px): Full layout with all features

Key changes:
- Minimum width reduced from 450px to 150px
- Minimum height reduced from 600px to 300px
- Navigation switches to vertical icon-only layout when very narrow
- Header collapses: hide title, smaller buttons, minimal padding
- Bust-out button hidden when very narrow
- Content padding and font sizes scale with width
- Added fade indicators on tab scroll edges
- All sizing now responds to modal width (or window width if standalone)

Layout modes:
- Very narrow: Icon-only vertical tabs, no header text, 8px padding
- Narrow: Abbreviated labels, 12px padding, scroll indicators
- Medium: Full labels, 24px padding, standard layout

This allows the guide to be positioned as a narrow sidebar while still
being fully functional.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:24:30 -06:00
Thomas Hallock 1bcd99c949 fix(rithmomachia): fix guide modal resize drift by calculating from initial state
Resizing was calculating deltas incrementally (delta-of-delta), which caused
the modal to slide away from the cursor. Now we save the initial dimensions
and position when resize starts, and always calculate the new size/position
from that initial state.

Changes:
- Added resizeStart state to track initial dimensions and position
- handleResizeStart now saves initial state
- Mouse move calculations use resizeStart instead of incrementally updating
- Removed the buggy dragStart reset that was causing drift

Resize now feels smooth and natural - handles follow the cursor exactly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:20:07 -06:00
Thomas Hallock 4a78485d2e feat(rithmomachia): add 80% opacity to guide modal when not hovered
Guide modal now displays at 80% opacity on desktop to allow seeing the game
board behind it. Opacity increases to 100% when hovering over the modal.

Behavior:
- Desktop (≥768px): 80% opacity when not hovered, 100% when hovered
- Mobile (<768px): Always 100% opacity
- Standalone window: Always 100% opacity
- Smooth 0.2s transition between states

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:17:40 -06:00
Thomas Hallock cfac277505 fix(rithmomachia): correct board dimensions to 16x8 and restore original layout values
The board was incorrectly showing as 8x8 instead of the correct 16x8 dimensions
for Rithmomachia. This also fixes gap (5→2) and padding (20→10) values that
were incorrectly changed.

- Set BOARD_COLUMNS to 16 (was 8)
- Update COLUMN_LABELS to A-P (was A-H)
- Restore gap to 2 (was changed to 5)
- Restore padding to 10 (was changed to 20)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 07:04:04 -06:00
Thomas Hallock 2ab6ab5799 refactor(rithmomachia): update capture components to use CaptureContext
Eliminate prop drilling in all capture dialog components by using the new
CaptureContext:

- CaptureErrorDialog: 4 props → 0 props
- CaptureRelationOptions: 11 props → 1 prop (availableRelations)
- HelperSelectionOptions: 11 props → 1 prop (helpers)
- NumberBondVisualization: 15 props → 3 props (onConfirm, positions)

All components now:
- Use useCaptureContext() for shared capture state
- Use useAbacusSettings() directly instead of prop drilling
- Have cleaner, more focused interfaces

Added missing getSquarePosition import to components that need it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 0ab7a1df32 refactor(rithmomachia): use useBoardLayout and usePieceSelection in BoardDisplay
Integrate custom hooks into BoardDisplay component:
- Replace manual layout constants with useBoardLayout hook
- Extract selection logic to usePieceSelection hook
- Wrap capture components in CaptureProvider

This reduces BoardDisplay complexity and improves code organization.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock d7eb957a8d feat(rithmomachia): add CaptureContext for capture dialog state management
Add centralized context provider for capture dialog subsystem. Provides:
- Layout configuration (targetPos, cellSize, gap, padding)
- Piece references (mover, target, helper)
- Selection state (selectedRelation, closing)
- Helper functions (findValidHelpers, selectRelation, selectHelper, dismissDialog)

This eliminates 8-15 props from each capture component and provides a
single source of truth for capture state.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 275f401e3c feat(rithmomachia): add usePieceSelection hook for selection state management
Extract piece selection logic from BoardDisplay into a reusable custom hook.
Provides selectedSquare, handleSquareClick, and clearSelection.

This improves testability and reduces BoardDisplay complexity by ~100 lines.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 27f1c989d5 feat(rithmomachia): add useBoardLayout hook for centralized layout calculations
Add custom hook to provide consistent board layout values (cellSize, gap,
padding, rows, columns) throughout the rithmomachia game components.

This eliminates magic numbers and provides a single source of truth for
board dimensions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 3abc325ea2 refactor(rithmomachia): extract reusable components from SetupPhase
Extract three reusable components from SetupPhase.tsx to eliminate code
duplication and improve maintainability.

**Components Created:**
- GameRuleCard.tsx (94 lines): Reusable card component for game settings
  - Replaces 4 nearly identical card implementations
  - Supports enabled/disabled states with visual feedback
  - Accepts optional children for custom content (e.g., threshold input)
- SetupHeader.tsx (155 lines): Medieval-style ornamental header
  - Title, subtitle, decorative corners, and guide button
- StartButton.tsx (73 lines): Dramatic start button with animations

**Impact:**
- SetupPhase.tsx: 759 → 308 lines (59% reduction, -451 lines)
- Eliminated ~480 lines of duplicate JSX across 4 game rule cards
- Improved component reusability and consistency
- Cleaner, more maintainable codebase

**Testing:**
- Pre-commit checks passed (format, lint)
- TypeScript: No new errors in changed files
- Ready for manual testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 324a65992f refactor(rithmomachia): extract hooks (phase 5)
Extract useRosterWarning hook from RithmomachiaGame.tsx into dedicated
hooks/ directory, completing the refactoring with a final 95% reduction.

**Hook extracted:**
- useRosterWarning (111 lines) → hooks/useRosterWarning.ts
  - Manages player roster warnings for setup/playing phases
  - Handles player activation, kicking, and error states
  - Returns RosterWarning for GameContextNav integration

**File reduction:**
- Before Phase 5: 266 lines
- After Phase 5: 153 lines
- Lines saved this phase: 113 lines

**Final statistics:**
- Original: 3,166 lines
- Final: 153 lines
- **Total reduction: 3,013 lines (95% reduction)**

**Complete component structure:**
```
rithmomachia/
├── constants/
│   ├── board.ts
│   └── captureRelations.ts
├── utils/
│   └── boardCoordinates.ts
├── hooks/
│   └── useRosterWarning.ts
├── components/
│   ├── board/
│   │   ├── BoardDisplay.tsx
│   │   └── SvgPiece.tsx
│   ├── capture/
│   │   ├── AnimatedHelperPiece.tsx
│   │   ├── CaptureErrorDialog.tsx
│   │   ├── CaptureRelationOptions.tsx
│   │   ├── HelperSelectionOptions.tsx
│   │   └── NumberBondVisualization.tsx
│   ├── phases/
│   │   ├── SetupPhase.tsx
│   │   ├── PlayingPhase.tsx
│   │   └── ResultsPhase.tsx
│   └── RithmomachiaGame.tsx (main orchestrator)
```

All phases complete! The codebase is now highly maintainable with clear
separation of concerns.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 11364f6394 refactor(rithmomachia): extract phase components (phase 4)
Extract all game phase components from RithmomachiaGame.tsx into dedicated
phases/ directory, achieving a massive 80% reduction in main component size.

**Phase components extracted:**
- SetupPhase (747 lines) → phases/SetupPhase.tsx
- PlayingPhase (152 lines) → phases/PlayingPhase.tsx
- ResultsPhase (176 lines) → phases/ResultsPhase.tsx

**File reduction:**
- Before: 1,363 lines
- After: 266 lines
- Lines saved: 1,097 lines (80% reduction)

**Component structure:**
Each phase component has:
- TypeScript interface for props
- Proper imports ('use client', hooks, types)
- Self-contained game phase logic

**Cumulative progress:**
- Phase 1: Constants & utilities (~65 lines saved)
- Phase 2+3: Capture & board components (~1,720 lines saved)
- Phase 4: Phase components (~1,097 lines saved)
- **Total reduction: From 3,166 → 266 lines (92% reduction)**

Remaining in main file:
- useRosterWarning hook (will extract in Phase 5)
- Main RithmomachiaGame component (renders phases)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock a0a867b271 refactor(rithmomachia): extract board and capture components (phase 2+3)
Complete extraction of all capture and board components from
RithmomachiaGame.tsx into dedicated directories, reducing the main
component from 3,083 → 1,363 lines (55% reduction).

**Phase 2 (capture components):**
- Extracted CaptureErrorDialog → components/capture/
- Extracted AnimatedHelperPiece → components/capture/
- Extracted HelperSelectionOptions → components/capture/
- Extracted NumberBondVisualization → components/capture/
- Extracted CaptureRelationOptions → components/capture/

**Phase 3 (board components):**
- Extracted SvgPiece → components/board/
- Extracted BoardDisplay → components/board/

All components now have:
- TypeScript interfaces for props
- Proper imports and 'use client' directives
- Organized import statements (Biome sorted)

Lines saved: ~1,720 lines extracted to separate files
Components remaining in main file: 3 (SetupPhase, PlayingPhase, ResultsPhase)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock f0a066d8f0 refactor(rithmomachia): extract CaptureErrorDialog component (Phase 2 partial)
Extract CaptureErrorDialog from RithmomachiaGame.tsx into separate file
for better organization and maintainability.

**Changes:**
- Created components/capture/CaptureErrorDialog.tsx
  - Extracted ~105 line error dialog component
  - Added proper TypeScript interface (CaptureErrorDialogProps)
  - Self-contained with all animation logic

- Updated RithmomachiaGame.tsx
  - Added import for CaptureErrorDialog
  - Removed inline component definition
  - Net reduction: ~100 lines

**Progress so far:**
- Phase 1 (complete): Extracted constants/utilities - saved ~65 lines
- Phase 2 (partial): Extracted CaptureErrorDialog - saved ~100 lines
- Total: RithmomachiaGame.tsx reduced from 3,166 → 3,083 lines

**Remaining Phase 2 tasks:**
- Extract AnimatedHelperPiece (~80 lines)
- Extract HelperSelectionOptions (~170 lines)
- Extract NumberBondVisualization (~200 lines)
- Extract CaptureRelationOptions (~160 lines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock eace0ed529 refactor(rithmomachia): extract constants and coordinate utilities (Phase 1)
Extract duplicated code into reusable modules to improve maintainability
and reduce code size in RithmomachiaGame.tsx.

**Constants extracted:**
- constants/captureRelations.ts: Relation colors, operators, tooltips
  - RELATION_COLORS map (was duplicated 3x)
  - RELATION_OPERATORS map (was duplicated 3x)
  - Helper functions: getRelationColor(), getRelationOperator()

- constants/board.ts: Board layout constants
  - Board dimensions, column/row labels
  - Default cell size, gap, padding values

**Utilities extracted:**
- utils/boardCoordinates.ts: Board position calculations
  - parseSquare(): Convert "A1" notation to file/rank indices
  - getSquarePosition(): Calculate pixel position from square notation
  - Replaces ~20 lines of duplicated coordinate calculation code

**Changes to RithmomachiaGame.tsx:**
- Removed 3 duplicate color/operator map definitions (~50 lines)
- Replaced inline coordinate calculations with getSquarePosition()
- Net reduction: ~65 lines of duplicated code

This is Phase 1 of the rithmomachia refactoring plan. Remaining phases:
- Phase 2: Extract capture UI components (~1,500 lines)
- Phase 3: Refactor board components (container/presentation split)
- Phase 4: Extract phase components (Setup, Playing, Results)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock e9c320bb10 feat: internationalize guide page with 6 languages
Add complete i18n support for the /guide page across all 6 supported
languages (en, de, ja, hi, es, la).

Created comprehensive translation files for guide content including:
- Page metadata (nav title, hero, tabs)
- Reading Numbers section (structure, single digits, multi-digit, practice, interactive)
- Arithmetic Operations section (addition, subtraction, multiplication/division, practice tips)

Updated components to use translations:
- guide/page.tsx: Main page layout with nav and tabs
- ReadingNumbersGuide.tsx: All 5 steps with nested content
- ArithmeticOperationsGuide.tsx: All operation sections with examples

All hardcoded strings replaced with translation calls using next-intl.
Message interpolation used for dynamic content. Arrays properly handled
with t.raw() for list items.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 95b0105ca3 fix(i18n): use useMessages() for tutorial translations
- Fixed tutorial translations not displaying in user's language
- Updated homepage to use useMessages() instead of useTranslations().raw()
- Updated tutorialConverter to use translated actionDescription
- Updated ArithmeticOperationsGuide to pass tutorial translations
- Tutorial content now displays fully in selected language

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 4253964af1 feat(i18n): internationalize games page and tutorial content
- Add full translations for /games page (6 languages)
  - Created translation files: en, de, ja, hi, es, la
  - Internationalized all sections: hero, arcade, champions, dashboard, CTA
  - Implemented message interpolation for dynamic content

- Internationalize tutorial step content
  - Added step content translations to all tutorial locale files
  - Updated tutorialConverter.ts to accept and use translations
  - Modified homepage to pass tutorial translations to converter
  - Tutorial now displays in user's selected language

All translations follow established i18n patterns with co-located messages
and server-side loading for FOUC prevention.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 06:59:31 -06:00
Thomas Hallock 26d41cfd05 feat: internationalize tutorial player 2025-11-01 15:53:30 -05:00
Thomas Hallock 61f899ce99 chore: update auto-approve permissions for bash commands
Add auto-approve for additional bash commands:
- Git operations (rev-parse)
- File operations (head, tail, jq)
- Pnpm package management
- Web fetch for hub.docker.com

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 1d525c7b53 feat(arcade): add native abacus numbers support to pressure gauge
- Use native abacus numbers setting from user preferences
- Conditionally render abacus based on nativeAbacusNumbers setting
- Apply setting to PressureGauge in Complement Race game

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 5a29af78e2 docs: add database migration guide and playing guide modal spec
- Document proper process for creating database migrations with drizzle-kit
- Add warnings about NOT manually editing migration files
- Document Rithmomachia playing guide modal specifications

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 4d4d930bd3 fix(i18n): eliminate FOUC by loading messages server-side
Problem: Translation keys flashed for ~500ms before being replaced with
actual translations due to client-side async message loading.

Solution:
- Load messages server-side in layout.tsx before initial render
- Pass initialLocale and initialMessages as props to ClientProviders
- Update LocaleProvider to accept and use server-provided initial values
- Remove client-side useEffect that caused async loading delay
- Export getRequestLocale() function for server-side locale detection

Result: Zero FOUC - translations display instantly on page load while
maintaining instant language switching via changeLocale().

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 8c9d35a3b4 feat(i18n): add homepage translations for all supported languages
Add professionally translated homepage content for:
- German (de): Formal German with proper terminology
- Japanese (ja): Native Japanese with appropriate kanji/hiragana mix
- Hindi (hi): Devanagari script with formal register
- Spanish (es): Latin American Spanish
- Latin (la): Classical Latin

All translations include complete homepage structure:
- Learn by Doing section
- Skills (Read Numbers, Friends, Multiply, Mental)
- The Arcade
- Your Journey
- Create Custom Flashcards

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 40cff143c7 feat(i18n): internationalize homepage with English translations
- Replace all hardcoded strings in homepage with t() translation calls
- Create English translation file with complete homepage content:
  - Learn by Doing section
  - What You'll Learn (4 skill cards)
  - The Arcade section
  - Your Journey section
  - Create Custom Flashcards section (features and CTA)
- Add home messages to global message aggregator
- Use useTranslations('home') hook for type-safe translations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 0506360117 feat(i18n): add global language selector to navigation
- Create reusable LanguageSelector component with two variants:
  - dropdown-item: For hamburger menu (dark theme)
  - inline: For full navbar
- Add language selector to hamburger menu (after Abacus Style)
- Add language selector to full navbar (after style dropdown)
- Remove language selector from PlayingGuideModal (now redundant)
- Display language with flag emoji and native name

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock fe9bfeabf9 feat(i18n): add dynamic locale switching without page reload
- Create LocaleContext for global locale state management
- Implement changeLocale() function for instant language switching
- Update PlayingGuideModal to use changeLocale() instead of window.location.reload()
- Add language selector dropdown to Rithmomachia guide modal
- Language changes now happen instantly without page reload

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 9016b76024 feat(i18n): migrate from react-i18next to next-intl
- Install next-intl package
- Configure next-intl middleware for cookie-based locale detection
- Set up routing config with supported locales (en, de, ja, hi, es, la)
- Create request config for server-side locale resolution
- Migrate Rithmomachia guide sections to next-intl
- Update translation calls to use next-intl's useTranslations hook
- Remove old react-i18next config

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 3fcc79fe9e feat(rithmomachia): add standalone guide page route
Adds /arcade/rithmomachia/guide route that displays the playing guide
modal in standalone mode. This allows the guide to be opened in a
separate window/tab.

Route: /arcade/rithmomachia/guide
Component: PlayingGuideModal with standalone=true

Fixes 404 error on production for this route.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 21:08:51 -05:00
Thomas Hallock 79f7347d48 feat(abacus): add nativeAbacusNumbers setting to schema and UI
Adds the nativeAbacusNumbers setting that was being used by Rithmomachia
but was never committed, causing CI build failures.

Changes:
- Add nativeAbacusNumbers boolean field to abacus_settings schema
- Add database migration for the new field (default: false)
- Add UI toggle in AbacusDisplayDropdown
- Update AbacusSettingsSync to exclude nativeAbacusNumbers from
  abacus-react sync (it's app-specific, not abacus component config)

This setting allows displaying numbers as abaci throughout the app
where practical, used by arcade games like Rithmomachia.

Fixes TypeScript build errors in:
- src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx
- src/arcade-games/rithmomachia/components/RithmomachiaGame.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 20:44:16 -05:00
Thomas Hallock dae615ee72 fix(rithmomachia): add missing pyramid section keys to Japanese (ja.json)
Adds flat pyramid section translation keys to match the English structure:
- pyramidTitle, pyramidIntro
- blackPyramid/whitePyramid values
- pyramidHowItWorks with 4 rules
- pyramid example and visual example
- pyramid capture options (3 scenarios)

Fixes "returned an object instead of string" error for pyramid section
in Japanese guide.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 20:38:16 -05:00
Thomas Hallock 91154d9364 fix(rithmomachia): add missing i18next dependencies
Added i18next and react-i18next to package.json to fix Docker build failure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 20:32:01 -05:00
Thomas Hallock 14259a19a9 fix(rithmomachia): fix harmony section translation structure for hi/ja/es
Fixed nested object structures in harmony sections that were causing
"returned an object instead of string" errors.

Changes:
- Hindi (hi.json): Flattened arithmetic/geometric/harmonic objects to flat keys
- Japanese (ja.json): Flattened arithmetic/geometric/harmonic objects to flat keys
- Spanish (es.json): Flattened arithmetic/geometric/harmonic objects to flat keys
- Added missing translation keys for all languages
- Removed duplicate/old keys
- Fixed TypeScript error in PiecesSection.tsx (pyramid value type)
- Removed unused i18nInstance import from PlayingGuideModal.tsx
- Removed duplicate simpleTitle key from la.json

All harmony sections now use flat key structure matching English/German.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 4fa20f44cb fix(rithmomachia): fix modal resizing zoom issue
Fixed issue where modal would zoom away from cursor when resizing.

**Problem:** The resize logic was accumulating deltas by updating `dragStart`
with calculated offsets, causing exponential growth and the modal to zoom away.

**Solution:** Reset `dragStart` to current mouse position after each resize step.
This makes each delta calculation relative to the current position, not the
original click position.

**Changes:**
- Removed `actualDeltaX` and `actualDeltaY` tracking
- Reset `dragStart` to `{ x: e.clientX, y: e.clientY }` after each resize
- Simplified resize calculation logic

Now resizing works smoothly with cursor following the resize handle correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 85cb630add feat(rithmomachia): simplify guide language for clarity
Simplified language throughout the guide to make it super clear how to play:

**Pyramid section:**
- "Pyramids are special" instead of "multi-faced pieces"
- "Pick which value to use" instead of "declare which face value"
- Shorter, punchier rules

**Harmony section:**
- "Coolest way to win" instead of "elegant victory"
- "Equal spacing" instead of "differences are equal"
- "Multiply by same number" instead of technical descriptions
- "Tricky!" instead of "music-based, trickiest"
- Simpler rules: "Must be touching" vs "adjacent placement"
- Strategy tips use casual language: "They might not see it coming!"

**Helpers section:**
- "They don't move - they just add their number to the math"
- Much clearer explanation

**Victory section:**
- "Quick Tips" instead of "Quick Strategy Tips"
- Simpler, more direct language throughout

All changes applied to both English and German translations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock d0a8fcdea6 fix(rithmomachia): implement proper board cropping and highlighting in guide
Fixed all guide section board visualizations to use correct props:
- Replaced non-existent `cropToSquares` prop with `cropArea` object
- Added `highlightSquares` prop for yellow highlighting of piece squares
- Created `squaresToCropArea()` helper function in each section
- Updated all examples in CaptureSection (6 examples)
- Updated all examples in HarmonySection (3 examples)
- Updated pyramid example in PiecesSection
- Updated victory example in VictorySection

Board examples now properly display:
✓ Cropped to relevant squares only
✓ Yellow highlighting on squares with pieces
✓ Proper coordinate labels

This matches the original guide design shown in the reference image.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 24896957d0 feat(rithmomachia): recreate original guide modal header layout
Recreate the centered header design from the original guide:
- Centered large title "Rithmomachia Playing Guide"
- Centered subtitle "Rithmomachia – The Philosophers' Game"
- Language selector with dropdown (replaces button toggles)
- Close and bust-out buttons positioned in top-right corner
- Improved visual hierarchy with larger, more prominent title
- Better responsive sizing for mobile and desktop

This matches the original design shown in the reference image while maintaining
all modern functionality (dragging, resizing, bust-out, language switching).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 9150b0c678 feat(rithmomachia): add ratio capture example to guide
Add complete ratio capture example with visual board demonstration:
- Added ratioExample with 3 pieces showing White Triangle (20) capturing Black Circle (5) using helper Circle (4)
- Added ratio UI section with explanation and board visualization
- Added translation keys for ratio title, example, and caption in English and German
- Completes the full set of mathematical capture relations (equality, multiple, sum, difference, product, ratio)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 9fde1ef9e7 feat(rithmomachia): enhance Pyramid section with comprehensive details
Add detailed pyramid information to PiecesSection:

**Content Enhancements:**
- Clarified that Pyramids have 4 face values (perfect squares)
- Added face values with perfect square notation: "36 (6²), 25 (5²), 16 (4²), 4 (2²)"
- Added "How face selection works" section with 4 detailed rules
- Added example showing multiple capture scenarios
- Added visual board example demonstrating 3 different capture options from a single position

**Visual Improvements:**
- Cropped board showing White Pyramid at H5 with capture options
- Demonstrates flexibility of face selection (64 for multiple, 49 for equality, 25 for equality)
- All options clearly listed with mathematical relations

**Translations:**
- Added 15+ new translation keys to en.json and de.json
- Full German translations for all new pyramid content

This provides players with a complete understanding of how Pyramids work and why they're special pieces with multiple strategic uses.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock f5558563ea feat(rithmomachia): enhance Harmony section with comprehensive content
Add detailed formulas, examples, strategies, and quick reference tables to the Harmony guide section:

**Content Enhancements:**
- Added "How to check" formulas for each progression type (Arithmetic, Geometric, Harmonic)
- Added visual examples with mathematical checks for each type
- Added strategy tips for each progression type (what pieces work best)
- Added detailed rules section with 5 specific rules (Enemy Territory, Straight Line, Adjacent Placement, Survival Rule, Victory)
- Added Strategy section with 5 subsections:
  - Start with 2, Add the Third
  - Use Common Values
  - Protect the Line
  - Block Opponent's Harmonies
  - Calculate Before You Declare
- Added Quick Reference tables showing common harmonies for each type

**Visual Design:**
- Keep existing visual board examples (cropped)
- Color-coded sections: Green (Arithmetic), Yellow (Geometric), Blue (Harmonic)
- Example formulas in monospace font
- Strategy tips in colored callout boxes

**Translations:**
- Added 30+ new translation keys to en.json and de.json
- Full German translations for all new content

This merges the comprehensive old guide content with the new visual board examples, providing both detailed mathematical explanations and interactive visual demonstrations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 765525dc45 refactor(rithmomachia): extract guide sections into separate files
- Extract OverviewSection, PiecesSection, CaptureSection, HarmonySection, and VictorySection into separate files in guide-sections/ directory
- Reduce PlayingGuideModal.tsx from 1628 lines to 519 lines
- Remove unused imports from all refactored files
- Each section is now independently maintainable and testable

This refactoring makes it easier to enhance individual sections (e.g., adding comprehensive content to Harmony section).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock b7fac78829 feat(rithmomachia): add visual winning example to Victory section
- Added cropped board example showing a winning geometric progression
- White pieces 4, 8, 16 in enemy territory (rows 5-8)
- Shows Black pieces unable to break the harmony
- Example demonstrates the primary victory condition clearly
- Added title and caption translations for English and German

The visual makes it immediately clear what a winning position looks like.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 1d5f01c966 feat(rithmomachia): add visual board examples to Harmony section
- Added 3 cropped board examples showing each progression type
- Arithmetic: 6, 9, 12 in a row (9 = (6+12)/2)
- Geometric: 4, 8, 16 in a row (8² = 4×16)
- Harmonic: 6, 8, 12 in a row (2×6×12 = 8×(6+12))

- All examples show White pieces in enemy territory (rows 5-8)
- Pieces arranged in horizontal rows to demonstrate line requirement
- Added captions for each progression type
- Updated English and German translations with caption keys

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 74bc3c0dcf feat(rithmomachia): add visual board examples to Capture section
- Added 5 cropped board examples showing each capture type
- Equality: W 25 captures B 25
- Multiple: W 64 captures B 16 (64÷16=4)
- Sum: W 9 + helper 16 = B 25
- Difference: W 30 - helper 10 = B 20
- Product: W 5 × helper 5 = B 25

- Simplified capture descriptions for clarity
- Added captions to all board examples
- Updated English and German translations with caption keys

Each example shows a minimal board snippet focusing on the relevant pieces.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 55aff829f4 feat(rithmomachia): enhance Pieces section with visual examples and pyramid details
- Added example values for each piece type (Circle, Triangle, Square) with visual renderings
- Expanded Pyramid subsection showing both Black and White pyramids with their face values
- Corrected piece counts (25 total per side: 12 circles, 6 triangles, 6 squares, 1 pyramid)
- Simplified movement descriptions for clarity
- Updated English and German translations with new keys

Pyramid values:
- Black: 36, 25, 16, 4
- White: 64, 49, 36, 25

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock d42bcff0d9 feat(rithmomachia): add initial board visual to guide Overview section
- Added full initial setup array with all 48 pieces in starting positions
- Rendered board using RithmomachiaBoard component at 0.6 scale
- Added caption translation key for board visual
- Simplified language in "How to Play" steps for easier comprehension

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 4834ece98e fix(rithmomachia): Fix TypeScript errors in playing guide modal
Fix PieceRenderer props and modal scope issues:
- Update PieceRenderer usage to match correct interface (type, color, value, size)
- Move PlayingGuideModal to correct scope within RithmomachiaGame component
- Remove duplicate modal render from ResultsPhase

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 3121d8240a feat(rithmomachia): Add interactive playing guide modal
Add comprehensive draggable playing guide modal with quick navigation:
- Draggable modal on desktop, fixed on mobile
- 5 quick-access sections: Overview, Pieces, Capture, Harmony, Victory
- Integrated PieceRenderer SVGs for visual piece examples
- Responsive layout for desktop and mobile
- Modal persists during gameplay
- "How to Play" button on setup page with updated description
- "Guide" button in gameplay controls
- Complete guide content from PLAYING_GUIDE.md

Features:
- Desktop: draggable modal that can be positioned anywhere
- Mobile: full-screen responsive modal
- Quick navigation tabs for easy reference
- Visual piece examples with movement descriptions
- Mathematical capture relations explained
- Harmony progression examples with formulas
- Strategy tips and victory conditions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock e3c1f10233 docs(rithmomachia): Add concise one-page playing guide
Create quick start guide for competitive play with simple language.
Covers movement, capturing, harmonies, and winning conditions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 0471da598d refactor(rithmomachia): Update board setup to authoritative CSV layout
Replace previous board setup with traditional 24-piece formation derived
from authoritative CSV source. Update SPEC.md to reflect correct layout.

Changes:
- 24 pieces per side (7 Squares, 8 Triangles, 8 Circles, 1 Pyramid)
- Black pieces in columns A-D (left side)
- White pieces in columns M-P (right side)
- Black Pyramid at B8, White Pyramid at O2
- 8 empty columns in middle (E-L battlefield)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock 618e56358d fix(rithmomachia): Correct board setup to match reference image exactly
Applied four critical corrections to match the reference photo:

1. Black Column C (inner edge) - Fixed circle sequence:
   - C3: 9 (was 3)
   - C4: 7 (was 4)
   - C5: 5 (was 2)
   - C6: 3 (was 12)

2. White N8 - Fixed top inner triangle:
   - N8: T(9) (was T(5))

3. White Pyramid - Moved to lower position:
   - O2: Pyramid (was O7)
   - Shifted O2-O6 circles down to O3-O7

4. Black A6 - Fixed outer rim piece:
   - A6: S(120) (was T(64))
   - B6: T(64) (was C(81))
   - B7: C(81) (was S(120))

These changes ensure the layout matches the traditional Rithmomachia
reference image with large figurates on outer edges (A, P) and small
geometric units on inner edges (C, N).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 22:04:50 -05:00
Thomas Hallock 0769eaaa1d feat(rithmomachia): Update to traditional board setup with 25 pieces per side
Replaced the previous simplified setup with the classical Rithmomachia
formation from "The Philosophers' Game":

Documentation (SPEC.md):
- Section 3: Updated piece values to traditional formation
  - Black: 6 Squares, 9 Triangles, 9 Circles, 1 Pyramid
  - White: 8 Squares, 8 Triangles, 8 Circles, 1 Pyramid
  - Values encode arithmetical, geometrical, harmonic progressions
  - Large figurates on outer edges (361, 289, 225, etc.)
  - Small geometric bases inside (2-16)

- Section 4: Traditional symmetric layout
  - Black: Columns A, B, C (left side)
  - White: Columns N, O, P (right side)
  - Battlefield: Columns D-M (10 empty columns)
  - Pyramids at B8 (Black) and O7 (White)
  - Strategic philosophy: heavy squares on flanks, nimble pieces inside

Implementation (pieceSetup.ts):
- Updated createInitialBoard() to match traditional formation
- Black Pyramid faces: [36, 25, 16, 4] (6², 5², 4², 2²)
- White Pyramid faces: [64, 49, 36, 25] (8², 7², 6², 5²)
- All pieces placed according to classical symmetric pattern

Fix (RithmomachiaGame.tsx):
- Replaced WebkitBackgroundClip (unsupported in Panda CSS types)
- Use color: 'transparent' + backgroundClip: 'text' for gradient text

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 21:53:27 -05:00
Thomas Hallock 08c97620f5 feat(rithmomachia): Update harmony system to classical three-piece proportions
Updated harmony validation to match classical Rithmomachia rules using
three-piece proportions (A-M-B structure) with integer arithmetic formulas:

- SPEC.md §7: Rewrote harmony section with classical three-piece rules
  - AP (Arithmetic): 2M = A + B (middle is arithmetic mean)
  - GP (Geometric): M² = A·B (middle is geometric mean)
  - HP (Harmonic): 2AB = M(A+B) (middle is harmonic mean)
  - Added layout constraints: adjacent, equalSpacing, collinear
  - Added common harmony triads reference list

- harmonyValidator.ts: Complete rewrite for three-piece validation
  - Collinearity checking (straight line requirement)
  - Spatial middle piece detection
  - Integer proportion formulas (no division needed)
  - Layout modes: adjacent (all distance 1), equalSpacing (equal 1-2), collinear (any)
  - Fixed coordinate access: use file/rank instead of col/row

- types.ts: Updated HarmonyDeclaration.params structure
  - Changed from v/d/r/n fields to a/m/b (first/middle/last values)
  - Matches classical three-piece proportion structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 21:45:01 -05:00
Thomas Hallock a1a0374fac fix(rithmomachia): reconnect player assignment UI and fix setup layout
Restore all missing white/black player assignment functionality that was
disconnected after git rebase operations.

**Player Assignment UI (reconnected):**
- Wire up assignWhitePlayer and assignBlackPlayer from context
- Pass whitePlayerId, blackPlayerId, onAssignWhitePlayer, onAssignBlackPlayer,
  and gamePhase props to PageWithNav
- Enable split [W][B] buttons on hover for unassigned players
- Enable SWAP button on hover for assigned players
- Support observer mode (players not assigned to white/black can spectate)
- Permission system: only host (in rooms) or local players can assign

**Duplicate Indicator Fix:**
- Hide playerBadge pill when assignment UI is active to prevent showing both
  the fancy badge AND the assignment buttons
- Keep "Your turn" / "Their turn" labels (working as intended)

**Setup Layout Fix:**
- Add SetupPlayerRequirement panel when tooFew players (styled for medieval theme)
- Hide entire setup config UI when showing player requirement panel
- Prevents squishing/scrolling issues - clean swap between the two UIs
- Panel shows inactive local players, invite codes, and room history

**Navigation Banner:**
- Hide "Need two active players" banner during setup (panel handles this)
- Show banner during playing phase only

All ~10 hours of missing player management work now restored and functional.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 21:37:58 -05:00
Thomas Hallock e55f848a26 refactor(rithmomachia): make setup phase UI more compact
Reduce spacing and font sizes throughout the setup page to make better use
of screen space while maintaining readability and drama.

Changes:
- Reduce title font size: 6vh → 3.5vh
- Reduce subtitle and description font sizes
- Reduce all padding and margins (50% reduction)
- Reduce ornamental corner sizes: 8vh → 2.5vh
- Reduce border widths: 0.5vh → 0.3vh
- Reduce shadows for subtler effect
- Simplify description from 2 lines to 1 line
- Reduce start button size: 4vh → 2.5vh font
- Reduce threshold input width: 10vh → 6vh

Result: More compact, efficient use of space without sacrificing visual appeal.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 16:55:47 -05:00
Thomas Hallock b29bbeefca fix: Integrate threshold input into Point Victory card
- Remove separate threshold grid cell that broke layout
- Add threshold input inside Point Victory card with divider
- Use flexDirection: column for vertical stacking
- Add stopPropagation to prevent input clicks from toggling card
- Maintain golden theme for input border when active
- Fixes layout breakage where threshold appeared as separate cell

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 16:55:47 -05:00
Thomas Hallock 6ae4d13dc7 feat: Redesign Rithmomachia setup page with dramatic medieval theme
- Use full viewport (100%) with absolute positioning
- Deep purple gradient background with floating math symbols
- Medieval manuscript-style title card with gold ornaments
- Clickable setting cards with fancy active indicators (golden glow)
- All sizing in vh units for true responsiveness
- No scrolling, no clipping on any viewport size
- Add data attributes to all elements
- Add data attributes requirement to CLAUDE.md

Fixes checkbox clipping by making entire cards clickable.
Active state shown with golden background, border, shadow, checkmark, and corner decoration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 16:45:19 -05:00
Thomas Hallock 7c1c2d7beb fix(arcade): allow deactivating players from users who left the room
**Root Cause:**
The validation was checking if player.userId matches a current room member.
This failed when:
- User joins room with active players
- User leaves room
- Their players remain in GameModeContext as 'remote players'
- Host tries to deactivate them

**Fix:**
Remove the 'player belongs to room member' check.
Host should be able to deactivate ANY player except their own,
including orphaned players from users who have left.

**Validation now:**
- ✓ Player exists in database
- ✓ Player is not owned by the current viewer (can't deactivate your own)
- ✓ User is the room host

This allows hosts to clean up orphaned players and restore proper game state.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 16:45:19 -05:00
Thomas Hallock ee874876e4 debug: add detailed player and member logging
Add comprehensive logging to trace the player deactivation flow:

Client side:
- Log the full player object being deactivated
- Log player ID, userId, and isLocal status
- Log room members and member players

Server side:
- Log player lookup result
- Log player userId vs room member userIds comparison
- Log which member should own the player

This will help identify why 'Player does not belong to a room member' error occurs.
2025-10-30 16:45:19 -05:00
Thomas Hallock a146202205 debug: add comprehensive logging to deactivate-player endpoint
Add logging to both client and server side to debug 404 issue:

Client side (useRoomData):
- Log URL being called
- Log params being sent
- Log response status

Server side (route):
- Log when POST is received
- Log roomId and viewerId
- Log request body
- Log member validation steps
- Log success/error responses

This will help identify why the endpoint is returning 404.
2025-10-30 16:45:19 -05:00
Thomas Hallock 54bfd2fac8 fix(rithmomachia): show guest-friendly message when they can't fix too many players
When a room guest sees "too many players" but has no actions they can take:
- No local players to deactivate
- Not the host (can't kick/deactivate remote players)

**Before:** Generic message "Deactivate or kick extras:" with no buttons

**After:** Clear waiting message "Waiting for the room host to deactivate or remove extras..."

This sets proper expectations for guests who need to wait for the host to resolve the roster issue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 07:19:10 -05:00
Thomas Hallock 3628426a56 feat(arcade): add ability to deactivate remote players without kicking user
Room hosts can now deactivate individual remote players without removing the entire user from the room.

**Backend:**
- Add `setPlayerActiveStatus()` to player-manager for updating player active status
- Create `/api/arcade/rooms/:roomId/deactivate-player` endpoint (host only)
- Validates host permission, prevents self-deactivation
- Broadcasts `player-deactivated` event via socket

**Frontend:**
- Add `useDeactivatePlayer()` hook in useRoomData
- Handle `player-deactivated` socket event to update player list
- Update roster warning to show both "Deactivate" and "Kick" for remote players
  - Deactivate: Soft action, only deactivates the specific player
  - Kick: Hard action, removes entire user and all their players

**UX:**
- Host sees two options per remote player in roster warning:
  - "Deactivate [PlayerName]" (amber button)
  - "Kick [PlayerName]'s user" (red danger button)
- Gives fine-grained control over player participation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 07:14:58 -05:00
Thomas Hallock 8a11594203 feat(rithmomachia): integrate roster warning into game nav
Move roster status notice from fixed overlay into GameContextNav:

**Architecture Changes:**
- Add RosterWarning interface to GameContextNav (heading, description, actions)
- Create useRosterWarning hook in RithmomachiaGame to compute warning data
- Pass warning through PageWithNav → GameContextNav
- Remove old RosterStatusNotice component (252 lines deleted)

**UX Improvements:**
- Warning now appears in nav above player list
- Compact, non-intrusive design with left border accent
- All actions (deactivate local, kick remote) in one place
- Observers allowed (removed noLocalControl restraint)

**Benefits:**
- No z-index conflicts or overlay positioning issues
- Warning integrated with existing nav UI
- Flexible system reusable by other games
- Cleaner separation of concerns (hook for logic, nav for UI)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 04:45:15 -05:00
Thomas Hallock 709322373a fix(rithmomachia): adjust roster notice position to not overlap nav
- Move notice from top: 80px to top: 180px
- Use Z_INDEX.GAME.OVERLAY instead of hardcoded value
- Ensures game nav and player list remain visible

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 04:39:00 -05:00
Thomas Hallock 2c3732e363
Merge pull request #4 from antialias/codex/add-player-turn-indication-in-nav 2025-10-29 21:43:05 -05:00
Thomas Hallock 7c89bfef9c feat: show rithmomachia turn in nav 2025-10-29 21:37:58 -05:00
Thomas Hallock e27df45256 feat(rithmomachia): improve roster status notice UX
**Positioning:**
- Make notice fixed/centered instead of inline (no longer pushes board down)
- Add shadow and proper z-index for visibility

**Too Many Players:**
- Show ALL deactivatable local players (not just one)
- Show kick buttons for remote players if user is room host
- Organize actions into "Your players" and "Remote players" sections
- Clear visual distinction (amber for deactivate, red for kick)

**Observer Mode:**
- Remove "noLocalControl" restraint
- Allow room host and guests to observe without active players
- Only block on tooFew/tooMany, not on noLocalControl

**New Functionality:**
- Add useKickUser hook to kick remote players (host only)
- Detect if current user is room host via roomData.members
- Map player IDs to user IDs for kicking via memberPlayers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 18:15:08 -05:00
Thomas Hallock 025f0bd2d8 Add diagonal corner swipe gesture for deployment info 2025-10-29 17:31:27 -05:00
Thomas Hallock 5b56f1272b Simplify Rithmomachia roster notice 2025-10-29 17:15:02 -05:00
Thomas Hallock e7e67a023d Improve Rithmomachia player roster handling 2025-10-29 17:03:30 -05:00
Thomas Hallock dfeeb0e0db refactor(rithmomachia): redesign error notification with modern UI
Complete redesign of the capture error notification from scratch with a cleaner,
more polished appearance.

New design features:
- Dark gradient background (slate) instead of harsh red
- Horizontal layout: icon | text | button
- Warning icon (⚠) for visual context
- Slide-down animation instead of scale
- Backdrop blur effect for depth
- Subtle border glow instead of thick white border
- "OK" button instead of "Close" - more concise
- Better typography and spacing
- Modern color palette (#1e293b, #0f172a, #f1f5f9)

Result: Professional, clean notification that fits the game aesthetic without
being aggressive or condescending.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:21:04 -05:00
Thomas Hallock cda1126cb0 fix(rithmomachia): adjust error dialog sizing to prevent text clipping
Reduce font sizes and adjust container dimensions to ensure text fits properly
without clipping.

Changes:
- Increase foreignObject width from 3 to 4 cells
- Reduce main text fontSize from 0.28 to 0.18 cellSize
- Reduce button fontSize from 0.22 to 0.16 cellSize
- Reduce padding from 0.25 to 0.15/0.2 cellSize
- Add whiteSpace: 'nowrap' to prevent text wrapping
- Add width/height: 100% and boxSizing: border-box
- Add justifyContent: 'center' for proper vertical centering

Result: Clean, properly-sized error dialog with no text clipping.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:18:35 -05:00
Thomas Hallock 82a5eb2e4b refactor(rithmomachia): simplify capture error dialog to one-liner
Replace verbose error dialog with patronizing list of all failed relations
with a simple, concise message: "No valid mathematical relation" + Close button.

Changes:
- Remove moverPiece and targetPiece props from CaptureErrorDialog
- Remove all explanation generation logic
- Reduce dialog size from 5x4 cells to 3x1.6 cells
- Show single line of text instead of bulleted list
- Keep red styling and smooth animation

Result: Clean, non-patronizing feedback that simply states the fact.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:17:36 -05:00
Thomas Hallock b172440a41 feat(rithmomachia): add helpful error messages for failed captures
When a capture attempt fails because no mathematical relation works, show a
detailed error dialog explaining why each relation type can't be used. This
helps players understand the mathematical requirements for captures.

Changes to relationEngine.ts:
- Updated all relation check functions to return explanation messages on failure
- EQUAL: Shows actual values and why they don't match
- MULTIPLE/DIVISOR: Shows attempted division result
- SUM/DIFF/PRODUCT/RATIO: Shows what helper value is needed

Changes to RithmomachiaGame.tsx:
- Added CaptureErrorDialog component for displaying capture failures
- Shows red error dialog when availableRelations.length === 0
- Lists all relations and specific explanations for why each failed
- Includes Close button to dismiss the dialog

Example error messages:
- "9 ≠ 12 (values are not equal)"
- "9 does not divide 12 evenly (12÷9=1.33...)"
- "Helper 3 doesn't satisfy sum (need 3 but got 3)"
- "SUM: No friendly piece can serve as helper"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:14:14 -05:00
Thomas Hallock 774c6b0ce7 fix(rithmomachia): show actual values in tooltips for non-helper relations
For relations that don't need helpers (EQUAL, MULTIPLE, DIVISOR), show the
actual piece values in tooltips instead of "No valid helpers" message. This
makes the tooltips more informative and useful.

Changes:
- Add helperRelations array to identify which relations need helpers
- Split tooltip logic: non-helper relations show mover+target values directly
- Helper relations (SUM, DIFF, PRODUCT, RATIO) continue to show helper values

Examples:
- DIVISOR: "6 divides 12" (not "DIVISOR: No valid helpers")
- EQUAL: "9 = 9" (not "EQUAL: No valid helpers")
- MULTIPLE: "12 is multiple of 6" (not "MULTIPLE: No valid helpers")

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:06:38 -05:00
Thomas Hallock 4829e41ea1 feat(rithmomachia): cycle through valid helpers with dynamic number tooltips
When hovering over capture relation buttons, cycle through valid helper pieces
every 1.5 seconds instead of showing all triangles simultaneously. Update tooltips
to display actual numbers (e.g., "6 + 3 = 9") instead of abstract variables
(e.g., "a + h = b").

Changes:
- Add currentHelperIndex state to track which helper is currently displayed
- Add useEffect with 1.5s interval to cycle through helpers when hovering
- Modify triangle rendering to show only current helper (not all at once)
- Add getTooltipText() function that generates equations with actual piece values
- Tooltip shows concrete examples: "6 + 3 = 9" instead of "Sum: a + h = b"

User experience:
- Hover relation button → see first helper triangle with actual equation
- Every 1.5s → triangle moves to next helper, tooltip updates with new numbers
- Clearer visualization without visual clutter from multiple overlapping triangles

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 14:04:12 -05:00
Thomas Hallock be2a00e8b3 feat(rithmomachia): skip helper selection UI and auto-select first valid helper
When a capture relation is selected that requires a helper piece, automatically
select the first valid helper instead of showing the helper selection ring UI.
This streamlines the capture flow since we already show all valid helpers with
number bond triangles when hovering over relation buttons.

Changes:
- Auto-select first valid helper in handleCaptureWithRelation
- Skip Phase 2 (helper selection UI) by setting both selectedRelation and selectedHelper
- User flow: hover relation → see all valid helpers → click relation → capture animation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 13:27:21 -05:00
Thomas Hallock 976a7de949 feat(rithmomachia): use actual piece SVGs in number bond with 2.5s rotation animation
Completely revamps the number bond visualization to match user specifications:

**Number Bond Layout:**
- Operands (mover + helper) positioned at TOP (left and right)
- Result (target) positioned at BOTTOM center
- Operator symbol displayed prominently in center
- Triangle lines connect all three pieces
- Uses actual PieceRenderer SVG components instead of circles

**2.5 Second Capture Animation:**
When user clicks "✓ Capture" button:
- All three pieces begin rotating around center point
- Rotation accelerates to near-infinite speed (20π radians = 10 full rotations)
- Pieces spiral inward (radius collapses to 0)
- Helper and target pieces fade out (opacity → 0)
- Mover piece remains visible at center
- Animation duration: exactly 2.5 seconds
- After animation completes, capture is executed via onRest callback

**Technical Implementation:**
- Two-phase animation system:
  - Entrance: scale from 0 with spring physics
  - Capture: duration-based rotation/collapse with configurable easing
- Each piece offset by 120° (2π/3) for balanced rotation
- Distance calculation: `spacing * 0.7 * radius` for smooth spiral
- Mover stays at opacity 1, helper/target fade during animation
- Lines and operator hide during animation for clean visual

**State Changes:**
- Updated selectedHelper state to store full Piece objects
- Simplified handleHelperSelection (no value extraction)
- Updated handleNumberBondConfirm to use piece.id references
- Render section passes pieces instead of primitive values

This creates a dramatic, mathematically educational capture animation that
clearly shows the relationship before executing the capture.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 12:14:26 -05:00
Thomas Hallock 82d89131f0 feat(rithmomachia): add number bond visualization and helper placeholders
Enhances the helper selection flow with two new features:

1. **Low-opacity placeholders for borrowed helpers**
   - Helper pieces shown in selection ring now appear at 0.2 opacity on board
   - Indicates the piece is temporarily "borrowed" for the capture calculation
   - Placeholder remains visible at original position during selection

2. **Number bond triangle visualization**
   - After selecting a helper, shows a mathematical relationship diagram
   - Displays target (top), mover (bottom-left), helper (bottom-right) in triangle
   - Color-coded by relation type (red=SUM, blue=DIFF, green=PRODUCT, amber=RATIO)
   - Shows operator symbol between bottom values
   - Includes animated scale-in entrance with spring physics
   - "✓ Capture" button confirms and executes the capture

The capture flow is now: relation selection → helper selection → number bond
visualization → execute capture. This provides clear visual feedback about the
mathematical relationship before committing to the capture.

Technical details:
- Add NumberBondVisualization component with triangle layout
- Add selectedHelper state to track chosen helper piece with values
- Update SvgPiece to accept optional opacity prop
- Update handleHelperSelection to show number bond instead of immediate execution
- Add handleNumberBondConfirm for final capture execution
- Update rendering logic to show three phases sequentially

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 12:03:35 -05:00
Thomas Hallock cae3359587 feat(rithmomachia): add helper piece selection for mathematical captures
Implements helper piece selection UI for capture relations that require
a helper (SUM, DIFF, PRODUCT, RATIO). When a player clicks a helper-requiring
relation, eligible helper pieces animate from their board positions to form
a selection ring around the capture target.

Key changes:
- Add AnimatedHelperPiece component with react-spring animations
- Add HelperSelectionOptions component for ring layout
- Add findValidHelpers() to validate which pieces can be helpers
- Add findAvailableRelations() to filter impossible capture options
- Fix transform interpolation using react-spring's `to()` utility
- Add comprehensive console logging for position debugging

The animation properly uses `to([spring.x, spring.y], ...)` instead of
nested `.to()` calls to correctly interpolate multiple animated values.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 11:40:39 -05:00
Thomas Hallock 0a308016e9 feat(rithmomachia): enhance capture relation UI with smooth animations
- Add z-index layering (Z_INDEX.GAME.OVERLAY) for capture options to appear above other elements
- Implement bidirectional spring animations (opening: fly out + fade in, closing: retract + fade out)
- Add click-to-dismiss functionality with smooth closing animation
- Fix CSS transition conflict by limiting transitions to transform and box-shadow only
- Remove opacity from hover handlers to prevent interference with spring animation
- Add SVG overflow: visible to prevent clipping at board edges
- Add 400ms delay before unmounting to allow animation completion

Capture relation buttons now smoothly fly out from and retract back to the center
of captured pieces with synchronized fade transitions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 11:40:39 -05:00
Thomas Hallock aafb64f3e3 fix(rithmomachia): correct makeMove parameter types for capture handling
- Fix TypeScript error: pass undefined for pyramidFace parameter
- Properly structure capture data as 5th parameter
- Add target piece lookup for capture relations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 11:02:47 -05:00
Thomas Hallock d568955d6a feat(arcade): add yjs-demo collaborative game and Yjs persistence layer
- Add yjs-demo arcade game with collaborative state management
- Add Yjs persistence layer for real-time sync
- Update socket server with Yjs support
- Update Rithmomachia game component
- Add yjs type definitions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 09:46:20 -05:00
Thomas Hallock 2fc0a05f7f feat(arcade): add Rithmomachia (Battle of Numbers) game
Implement complete Rithmomachia game with:
- 8×16 board with vertical layout (BLACK left, WHITE right)
- 25 pieces per side (Circles, Triangles, Squares, Pyramid)
- SVG piece rendering with proper orientation (pieces point at opponents)
- Smooth react-spring animations for piece movement
- Mathematical capture relations (equality, sum, difference, multiple, etc.)
- Harmony victory conditions (arithmetic/geometric/harmonic progressions)
- Server-side game state validation
- Comprehensive game specification in SPEC.md

Visual improvements:
- Responsive font sizing for piece values
- Conditional text outlining for white pieces
- Pyramids displayed without numbers
- Pieces scaled appropriately for board size (56px)

Note: Infrastructure changes also register yjs-demo game (both added to registry together).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 09:34:36 -05:00
Thomas Hallock bd014bec4f fix(card-sorting): prevent ghost movements with proper optimistic updates
Fixes ghost movements when dragging cards by properly tracking which
position updates originated from this browser window/tab.

Previously used a timing-based approach (debounce) which was brittle
and didn't work reliably on slow connections.

New approach:
- Generate unique windowId for each browser tab
- Include windowId in all position updates sent to server
- Skip server position updates that contain our own windowId
- This prevents replaying our own movements when they echo back

The user should never see their own movements repeated since they
already have those positions locally applied.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 07:15:33 -05:00
Thomas Hallock 633ff12750 fix: increase server update debounce to 2000ms for low bandwidth
Fixes ghost movements when dragging cards on slow connections.
The previous 500ms timeout was too short for network round-trips
on low bandwidth, causing server updates to overwrite local positions
after drag completion.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 07:12:07 -05:00
Thomas Hallock b443ee9cdc feat: optimize card sorting for mobile displays
Mobile optimizations for Card Sorting game:

**Spectator Mode Bottom Sheet (iPhone):**
- Convert right sidebar to compact bottom sheet (120px tall)
- 3-column horizontal grid layout for stats (Time/Cards/Accuracy)
- Compact sizing: 8px padding, 10px labels, 16px values
- Dual-orientation toggle: ▲/▼ on mobile, ◀/▶ on desktop
- Emoji-only labels on mobile to save space
- Full viewport width on mobile (no wasted space)

**Results Screen:**
- Vertical layout on mobile (score panel above cards)
- Dynamic card sizing based on count (5→130%, 15→105%)
- Horizontal action buttons with equal widths
- 130px top padding to avoid mini app nav
- Compact score display (80px circle vs 160px desktop)

**Mini App Nav:**
- Prevent text wrapping with whiteSpace: nowrap
- Consistent height regardless of content length

**Bug Fixes:**
- Remove dead "Reveal Numbers" button code
- Fix checkSolution onClick handler type error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 11:45:11 -05:00
Thomas Hallock 0cced47a0f fix: delete existing user sessions before creating new ones
Fixes issue where games failed to start on iPhone with "No active session found" error.

The problem was a UNIQUE constraint on arcade_sessions.user_id - when users
tried to start a new game, the session creation would fail silently because
they already had an existing session from a previous game.

Solution: Delete any existing sessions for the user before creating a new one.
This ensures users can start new games without being blocked by stale sessions.

Error logs showed:
- "UNIQUE constraint failed: arcade_sessions.user_id"
- "Failed to fetch session"
- "Move rejected: No active session found"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 11:45:11 -05:00
Thomas Hallock d188789069 feat(card-sorting): optimize results screen for mobile
- Switch layout from side-by-side to vertical stack on mobile
- Use column-reverse to show score panel at top, cards below
- Change cards grid from 3 to 2 columns on mobile
- Make cards fluid-sized with aspect ratio instead of fixed pixels
- Reduce score circle: 120px (mobile) to 160px (desktop)
- Reduce all font sizes, padding, and gaps for mobile
- Make buttons more compact on mobile
- Fix leftover revealNumbers reference in PlayingPhase

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 07:50:56 -05:00
Thomas Hallock 15c53ea4eb fix(card-sorting): remove remaining reveal numbers references
Fix TypeScript build errors by removing leftover references to the
revealNumbers function and numbersRevealed property that were missed
in the initial refactoring.

Changes:
- Remove revealNumbers from PlayingPhaseDrag context destructuring
- Remove numbers revealed warning section from ResultsPhase

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 06:29:56 -05:00
Thomas Hallock 73cf967492 feat(card-sorting): redesign setup screen with modern UI
Complete visual overhaul of the card sorting setup screen to match
the design aesthetic of other games and the home page.

New Features:
- Beautiful gradient hero section with animated background pattern
- Floating sample abacus cards (3, 7, 12) with gentle animations
- Modern button designs with teal gradients and smooth transitions
- Four difficulty levels with personality and descriptive emojis:
  * 🌱 Gentle (5 cards) - Easy
  *  Swift (8 cards) - Medium
  * 🔥 Intense (12 cards) - Hard
  * 💎 Master (15 cards) - Expert
- Live feedback info box showing selected difficulty
- Large prominent start button with shimmer effect on hover
- Responsive design for mobile to desktop
- Compact layout that fits on screen without scrolling

Removed:
- Game mode selection (collaborative mode was not implemented)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 00:12:44 -05:00
Thomas Hallock ea5e3e838b refactor(card-sorting): remove reveal numbers feature
Remove the reveal numbers toggle that allowed players to see numeric
values on cards during gameplay. This feature added unnecessary
complexity and detracted from the core visual pattern recognition
gameplay.

Changes:
- Remove showNumbers and numbersRevealed from game state and config
- Remove REVEAL_NUMBERS move type and validation
- Remove reveal numbers button from playing phase UI
- Remove numbersRevealed from score calculation
- Clean up all references in Provider, Validator, and components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 00:12:44 -05:00
Thomas Hallock f389afa831 fix(card-sorting): use ref to track initialized state and prevent re-animation
Track whether springs have been initialized and locked at grid positions.
On re-renders (like window resize), recreate springs at grid positions
with immediate: true to prevent animation.

Changes:
- Add springsInitializedRef to track initialization
- Add gridPositionsRef to store final grid positions
- On re-render, recreate springs at grid positions with immediate: true
- Mark as initialized after locking completes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 23:11:57 -05:00
Thomas Hallock d42947eb8d fix(card-sorting): add debug logging for spring animations 2025-10-23 23:10:52 -05:00
Thomas Hallock 275cc62a52 fix(card-sorting): lock spring positions after initial animation completes
After cards animate to grid positions, set immediate: true to prevent
any further animations (including on window resize).

Changes:
- After 1000ms animation, lock positions with immediate: true
- Cards will stay locked at grid positions
- No more re-animations on resize

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 23:10:15 -05:00
Thomas Hallock a44aa5a4c2 fix(card-sorting): remove hasAnimatedRef logic causing backwards animation
The ref check was causing springs to start at grid positions instead of
initial positions. Simplified to just use empty deps arrays.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 23:10:15 -05:00
Thomas Hallock cee399ed15 fix(card-sorting): use empty deps array for useSprings to prevent recreation
Added empty dependency array to useSprings and track animation state
with ref to ensure springs are only created once and never reset.

Changes:
- Add empty deps array to useSprings (prevents recreation)
- Track animation completion with hasAnimatedRef
- If already animated, start new springs at grid positions
- Only run animation effect once

This ensures cards stay in grid positions through all re-renders.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 23:10:15 -05:00
Thomas Hallock 949d76d844 fix(card-sorting): add missing useMemo import 2025-10-23 23:04:29 -05:00
Thomas Hallock 30953b8c4a fix(card-sorting): prevent springs from reinitializing on window resize
Used refs and useMemo to ensure spring positions don't reset when
viewport dimensions change.

Changes:
- Store viewport dimensions in ref instead of state
- Memoize initial positions with empty deps array
- Use ref in rendering to get current viewport size
- Springs now maintain their animated state through resizes

Cards will stay in their organized positions even when window resizes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 23:04:01 -05:00
Thomas Hallock 4b4fbfef32 fix(card-sorting): fix results panel layout to not cover cards
Changed score panel from flex layout to fixed position on the right side
so it doesn't overlay the card animation area.

Changes:
- Position panel fixed on right side (400px wide)
- Cards now have full viewport to animate in
- Add box shadow to panel for depth
- Panel slides in from right via existing spring animation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 23:02:28 -05:00