Added a 28px × 64px grab tab that extends to the right of the resize
handle divider, providing a larger visual target when the sidebar is
collapsed or near-collapsed.
**Changes:**
- Thin 8px divider remains the draggable area
- Visual grab tab (28px × 64px) extends to right with:
- Rounded corners on top-right and bottom-right (borderRadius)
- Vertical knurled texture for grip appearance
- Drop shadow for depth
- Positioned absolutely, centered vertically
- Tab is visual decoration only (pointerEvents: none)
- Divider remains fully draggable
**Note:** Tab is not draggable itself (library limitation - child elements
outside parent bounds don't trigger drag). The 8px divider is the
interactive area. Tab provides visual affordance when sidebar is collapsed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Root Cause:**
Server was generating ALL pages during SSR and passing them as initialData,
which pre-populated the entire loadedPages Map. This bypassed the
virtualization system because pagesToFetch was always empty.
**Changes:**
1. **page.tsx** - Only generate first 3 pages on server
- Changed generateWorksheetPreview() to include startPage/endPage params
- Server now generates min(3, totalPages) instead of all pages
- Added clear comments explaining SSR vs virtualization split
2. **WorksheetPreview.tsx** - Cleaned up debug logging
- Removed all console.log statements added during investigation
- Virtualization logic unchanged (was already correct)
**Result:**
- Initial page load: Fast (only 3 pages)
- Scrolling: Progressive loading with visible network activity
- 100-page worksheet: Loads incrementally as user scrolls
- Memory usage: Only loaded pages in memory, not entire set
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented proactive duplicate risk warnings directly in the page selector
UI to guide users toward valid configurations before they encounter issues.
**Features:**
- Color-coded page buttons based on duplicate risk
- Green/brand: Safe (< 50% of space)
- Yellow: Caution (50-80% of space)
- Red: Danger (> 80% of space)
- Warning indicator dots on risky page counts
- Radix UI tooltips on hover explaining the risk
- Live problem space calculation based on current config
**Visual Indicators:**
- Border colors change to yellow/red for risky page counts
- Background tints match risk level when selected
- Small dot in top-right corner for at-risk buttons
- Hover shows detailed tooltip with:
- Total problems requested vs available
- Duplicate risk level
- Actionable recommendations
**Implementation:**
- OrientationPanel.tsx: Added problem space validation logic
- Passes digitRange, pAnyStart, operator, mode from form state
- Uses estimateUniqueProblemSpace() for real-time calculation
- Tooltip messages formatted with recommendations
- Skips validation for mastery+mixed mode (consistent with banner)
**Example:**
1-digit 100% regrouping with 15 problems/page:
- Page 1: Green (15 of 45 available)
- Page 2: Yellow warning (30 of 45)
- Page 3: Red danger (45 of 45 - duplicates inevitable)
Tooltip on page 3: "🚫 Too many duplicates: 45 problems requested, only
~45 unique available. Consider: • Reduce to 1 pages • Increase digit range
• Lower regrouping %"
**Next:** Problem space indicator in config panel showing live estimate
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem Generation Improvements:
- Implement hybrid approach: generate-all + shuffle for small problem spaces (< 10k), retry-based for large spaces
- Add countRegroupingOperations() for accurate difficulty sorting
- Support progressive difficulty in generate-all mode by sorting problems before sampling
- Fix problem space estimation for 1-digit problems (was 6075, now correctly returns 45 for 100% regrouping)
Duplicate Warning System:
- Add validateProblemSpace() utility to estimate unique problems and warn users
- Create dismissable warning banner with visual styling (yellow bg, 15px rounded corners, drop shadow)
- Position warning banner as floating element alongside page indicator and action button
- Auto-reset dismissed state when worksheet config changes
- Skip validation for complex mastery+mixed mode
Component Refactoring:
- Extract WorksheetPreviewContext for shared state (warnings, theme, form state)
- Extract DuplicateWarningBanner component with context integration
- Move floating elements (warning banner, page indicator) to PreviewCenter for correct positioning
- Fix scroll container structure: preview-center is positioned container, nested div handles scrolling
- Export page data from WorksheetPreview via onPageDataReady callback
Technical Details:
- FloatingPageIndicator changed from position: sticky to position: absolute
- Warning banner positioned at top: 24px, right: 20px to avoid action button overlap
- Remove maxW constraint on scroll container to allow full width flex
- Server logs now show: "0 retries, generate-all method" for small problem spaces
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement comprehensive skill customization system allowing teachers to:
- Configure existing default skills with custom difficulty settings
- Create entirely new custom skills from scratch
- Visualize skills in mastery progression context with directional edges
- Interact with difficulty space using 2D plot with hover tooltips
Database Schema:
- custom_skills table: Stores user-created skills
- skill_customizations table: Stores modifications to default skills
- Both tables track regrouping config, display rules, and metadata
API Endpoints:
- POST /api/worksheets/skills/custom - Create custom skill
- GET /api/worksheets/skills/custom - List custom skills
- PUT /api/worksheets/skills/custom/[id] - Update custom skill
- DELETE /api/worksheets/skills/custom/[id] - Delete custom skill
- POST /api/worksheets/skills/[skillId]/customize - Save customization
- GET /api/worksheets/skills/customizations - List customizations
Components:
- DifficultyPlot2D: Interactive 2D visualization of difficulty space
- Regrouping Intensity (x-axis) × Scaffolding Level (y-axis)
- Dual mode: Default presets vs Mastery progression skills
- Directional edges showing skill progression sequence
- Hover tooltips with skill details
- Click to select configuration
- Visual legend explaining elements
- SkillConfigurationModal: Modal for skill configuration
- Name and description fields
- Digit range slider
- 2D difficulty plot integration
- Shows mastery progression context when editing
- Real-time configuration summary
- MasteryModePanel Integration:
- "Configure Skill" button for existing skills
- "Create Custom Skill" button for new skills
- Passes mastery progression to modal for context
Visual Design:
- Purple theme (#9333ea) for mastery progression skills
- Green theme (#10b981) for current configuration
- Dashed arrows with triangular arrow heads
- Numbered skill circles with hover tooltips
- Compact legend in top-right corner
Technical Features:
- PlotPoint interface for custom skill plotting
- Conditional snapping to either presets or custom points
- Vector math for arrow head calculations
- Z-ordering: edges before points
- Event propagation control for hover interactions
- Storybook examples for both components
Bug Fixes:
- Fix page indicator stuck on page 1 in WorksheetPreview
- Changed from threshold-based to most-visible-page tracking
- Works correctly for both scroll directions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes to shared worksheet viewer (/worksheets/shared/[id]):
- Move Download, Share, Edit actions from banner to floating action menu
- Main button shows "Edit" in read-only mode (most common action)
- Download and Share available in dropdown menu
- Simplified banner to show only read-only indicator
- Made banner light/dark mode ready with theme-aware colors
Changes to PreviewCenter component:
- Added onShare and onEdit props for read-only mode
- Main button adapts: "Edit" for read-only, "Download" for edit mode
- Dropdown menu adapts based on mode (Download/Share vs Share/Upload)
- Floating action button now always visible
Changes to WorksheetPreview component:
- Fixed virtualization to show all pages when initialData provided
- Shared worksheets now display all pages immediately (no lazy loading)
- Interactive editor still uses virtualization for performance
- Added debug logging for page visibility and theme changes
Bug fixes:
- Fixed page virtualization preventing multiple pages from showing
- Made shouldVirtualize persistent across re-renders
- Added comprehensive logging for debugging theme and page issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace prop drilling pattern with React Context for worksheet configuration
state and leverage existing ThemeContext for app-wide theme concerns.
Context Architecture:
- Created WorksheetConfigContext for worksheet-specific state (formState, onChange, operator)
- Use existing global ThemeContext for theme/isDark instead of prop drilling
- Clear separation of concerns: worksheet state vs app-wide theme
Components Refactored:
- DigitRangeSection: removed isDark prop, uses useTheme()
- RegroupingFrequencyPanel: removed ALL props (formState, onChange, isDark), uses both contexts
- SmartModeControls: removed isDark prop, uses useTheme() internally
- DifficultyPresetDropdown: uses both contexts
- MakeEasierHarderButtons: uses useTheme()
- OverallDifficultySlider: uses useTheme()
- ConfigPanel: wraps children with WorksheetConfigProvider
- ManualModeControls: removed isDark from child component calls
Benefits:
- Eliminated 10+ prop declarations and prop-passing statements
- RegroupingFrequencyPanel went from 3 props to 0 props
- Better testability with context providers
- Cleaner component APIs
- Proper separation of worksheet vs app-wide state
Documentation:
- Added PROP_DRILLING_AUDIT.md with comprehensive analysis
- Documents before/after patterns and best practices
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The problem: When in mastery mode, layout toggles (Problem Numbers and
Cell Borders) appeared to work but had no effect on the worksheet preview.
Root cause: Mastery mode was using hardcoded recommendedScaffolding from
skill definitions, completely overriding user's displayRules choices.
Solution: Merge user's displayRules with skill's recommended scaffolding,
giving user's layout choices (problemNumbers, cellBorders) priority while
still respecting the skill's pedagogical scaffolding recommendations for
other display options.
Changes:
- validation.ts: Override layout options with user choices in mastery mode
- Remove debug logging from OrientationPanel, AdditionWorksheetClient,
preview API route, and typstGenerator
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move the "Progressive difficulty" toggle to appear immediately after
the "Operator" section in the worksheet configuration panel, before
the "Mode Selector". This creates a more logical flow:
1. Student Name
2. Digit Range
3. Operator
4. Progressive Difficulty
5. Mode Selector
6. Mode-specific controls
Previously, Progressive Difficulty appeared after Mode Selector, which
broke the logical grouping of basic settings before mode selection.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove the sd-sub-borrow skill as it was redundant with the existing
"Two-digit with ones place borrowing" skill which naturally covers
problems like 52-17, 43-18, etc.
The skill was problematic because:
- digitRange: { min: 1, max: 1 } constrained both operands to single digits
- But the description "13-7, 15-8" implied 2-digit minus 1-digit
- This contradiction made it impossible to generate appropriate problems
- Students were seeing either 0% or 100% of the intended pattern
Rather than fix the complex asymmetric digit range logic, we're removing
the skill entirely. The progression now flows:
- sd-sub-no-borrow (single-digit without borrowing)
- td-sub-no-borrow (two-digit without borrowing)
- td-sub-ones-borrow (two-digit with ones place borrowing)
This provides a cleaner, more natural progression.
Changes:
- Remove sd-sub-borrow skill definition from skills.ts
- Remove 'sd-sub-borrow' from SkillId type union
- Update td-sub-no-borrow prerequisites to reference sd-sub-no-borrow
- Remove sd-sub-borrow from skillMigration.ts mapping
- Remove generateTeensMinusSingles() function from problemGenerator.ts
- Revert generateOnesOnlyBorrow() to standard logic
Also includes previous fixes:
- Fix AllSkillsModal tab button types to prevent modal closing
- Add operator-specific display rules for mixed mode
- Add borrowNotation and borrowingHints to displayRules schema
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:** Using Unicode characters '+' and '−' (U+2212) as operator flags
causes bugs when developers use the wrong character (ASCII '-' vs Unicode '−').
**Solution:** Changed operator discriminator values to alphanumeric strings:
- Addition: '+' → 'add'
- Subtraction: '−' → 'sub'
**Files changed:**
- types.ts: Updated AdditionProblem and SubtractionProblem operator types
- All operator assignments changed: operator: 'add' / operator: 'sub'
- All operator comparisons changed: === 'add' / === 'sub'
**Benefits:**
- No more Unicode character confusion
- Easier to type and read
- Less error-prone for future developers
- Consistent with WorksheetOperator type ('addition', 'subtraction', 'mixed')
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL FIX: The validateWorksheetConfig() function only checked for mode === 'smart',
causing mastery mode configs to fall through to the manual mode path. This meant:
- Frontend sent mode: 'mastery' with displayRules
- Validation function created a manual mode config instead
- displayRules were lost, replaced with boolean flags
- Ten-frames didn't render because flags defaulted to false
Changes:
- validation.ts: Check for both 'smart' OR 'mastery' when using displayRules
- validation.ts: Preserve mode as 'smart' | 'mastery' instead of forcing 'smart'
- validation.ts: Include currentStepId for mastery progression tracking
- types.ts: Import and include AdditionConfigV4Mastery in WorksheetFormState type
This was the third place mastery mode needed to be handled:
1. ✅ config-schemas.ts - Zod schema (added in previous commit)
2. ✅ typstGenerator.ts - Problem enrichment (added in previous commit)
3. ✅ validation.ts - Config validation (this commit)
Tests: All 14 ten-frames tests passing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two critical bugs preventing ten-frames from rendering:
1. **Mastery mode not handled** (typstGenerator.ts:61)
- Code only checked for 'smart' | 'manual' modes
- Mastery mode fell into manual path, tried to use boolean flags that don't exist
- Resulted in all display options being `undefined`
- Fix: Check for both 'smart' OR 'mastery' modes (both use displayRules)
2. **Typst array membership syntax** (already fixed in previous commit)
- Used `(i in array)` which doesn't work in Typst
- Changed to `array.contains(i)`
Added comprehensive unit tests (tenFrames.test.ts):
- Problem analysis tests (regrouping detection)
- Display rule evaluation tests
- Full Typst template generation tests
- Mastery mode specific tests
- All 14 tests now passing
Added debug logging to trace display rules resolution:
- displayRules.ts: Shows rule evaluation per problem
- typstGenerator.ts: Shows enriched problems and Typst data
- Helps diagnose future issues
The issue was that mastery mode (which uses displayRules like smart mode)
was being treated as manual mode (which uses boolean flags), resulting in
undefined display options.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Two critical fixes for worksheet grading:
1. **Fix OpenAI Responses API parameters**
- Move `verbosity` from top-level to `text.verbosity`
- API was rejecting requests with 400 error
- Confirmed against GPT-5 Responses API documentation
2. **Surface actual grading errors in UI**
- Add `error_message` column to worksheet_attempts table
- Store actual API/grading errors in database
- Display real error messages instead of generic "too blurry" text
- Users now see OpenAI API errors, validation failures, etc.
Changes:
- Updated gradeWorksheet.ts API call structure
- Created migration 0020 for error_message column
- Updated processAttempt.ts to save error messages
- Updated API route to return errorMessage field
- Updated results page to display actual errors
Now when grading fails, users see helpful error messages like:
"Unsupported parameter: 'verbosity'..." instead of just "too blurry"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Recover all changes from stash including:
- Linter/formatter updates across codebase
- Settings permission updates for git checkout
This commit captures the complete state of work that was
stashed during the previous session's git operations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL PEDAGOGICAL FIX: The difficulty progression was removing scaffolding
at the same time regrouping was being introduced, which is backwards!
The Problem:
- Beginner: 0% regrouping, 100% scaffolding ✓
- Early Learner: 25% regrouping, 100% scaffolding ✓
- Intermediate: 75% regrouping, 50% scaffolding ✗ WRONG!
- Advanced/Expert: 90% regrouping, 0% scaffolding ✗ WRONG!
Students were losing scaffolds (answer boxes, place value colors, ten-frames)
exactly when they needed them most - during intensive regrouping practice.
The Fix:
Added new "Practice" difficulty profile between Early Learner and Intermediate:
- Beginner: 0% regrouping, 100% scaffolding (learn structure)
- Early Learner: 25% regrouping, 100% scaffolding (introduce regrouping)
- **Practice: 75% regrouping, 100% scaffolding** ← NEW! (master WITH support)
- Intermediate: 75% regrouping, 50% scaffolding (begin removing support)
- Advanced/Expert: 90% regrouping, 0% scaffolding (full mastery)
Practice Profile Details:
- regrouping: { pAllStart: 0.25, pAnyStart: 0.75 } (same as Intermediate)
- carryBoxes: 'whenRegrouping' (show borrow/carry boxes when needed)
- answerBoxes: 'always' (keep guiding placement during intensive practice)
- placeValueColors: 'always' (keep visual support)
- tenFrames: 'whenRegrouping' (visual aid for regrouping)
Pedagogical Rationale:
Students need a "plateau phase" where they practice regrouping frequently
WITH full scaffolding support before we start removing training wheels.
This is especially critical for subtraction borrowing:
- First encounter borrowing with low frequency (Early Learner)
- Then practice borrowing intensively WITH scaffolds (Practice)
- Then gradually remove scaffolds as mastery develops (Intermediate → Expert)
Impact:
- Teachers selecting "Practice" mode get frequent regrouping with full support
- Smart difficulty progression no longer removes scaffolds prematurely
- Addresses user feedback: "we start turning off scaffolding for subtraction
as soon as we introduce regrouping, which defeats the whole point"
Updated DIFFICULTY_PROGRESSION:
['beginner', 'earlyLearner', 'practice', 'intermediate', 'advanced', 'expert']
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete borrowing hints visualization with smooth bezier curve:
- Curve starts near "1" in borrow box (dx: 0.9, dy: 0.15)
- Smooth quadratic bezier with control point for natural flow
- Ends at arrowhead position (0.24, 0.70 relative)
- Small arrowhead (0.35x font size) at (0.96, 0.62)
- White curve on colored backgrounds, gray on plain
- Typst bezier syntax: vertex with relative control point
Visual guide shows "n − 1" with arrow pointing to borrowed 10s box.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moved the progressive difficulty checkbox outside mode-specific sections
so it's available for both Smart and Manual modes.
Previously: Only available in Manual mode
Now: Available in both modes as a shared setting
The checkbox is now in its own card section below mode-specific controls,
making it clear that it applies regardless of which mode is selected.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed from per-item mode badges to a cleaner grouped format:
Before:
carry boxes [always] · answer boxes [when regrouping] · ten-frames [2+ regroups]
After:
Always: carry boxes, answer boxes • When needed: ten-frames
This reduces cognitive load by:
- Grouping scaffolding aids by frequency instead of individual labels
- Using simple "Always" vs "When needed" instead of detailed conditions
- Eliminating repetitive badge styling that added visual noise
- Making the summary more scannable at a glance
Users get the key information (what's enabled, when it shows) without
needing to parse multiple conditional badges per item.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Display conditional scaffolding modes with color-coded badges:
- Green 'always' - unconditionally shown
- Yellow 'when regrouping' - shown for problems with any regrouping
- Orange '2+ regroups' - shown for problems with multiple regroups
- Blue '3+ digits' - shown for problems with 3+ digit operands
Example output:
carry boxes [always] · answer boxes [when regrouping] · ten-frames [2+ regroups]
This makes it immediately clear to users when each scaffolding aid
will appear in the generated worksheets, replacing the previous
text-only list that didn't communicate the conditional nature.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Bug: getScaffoldingSummary() only checked for 'always' or 'whenRegrouping',
missing 'whenMultipleRegroups' and 'when3PlusDigits' modes.
This caused configs with tenFrames: 'whenMultipleRegroups' to show
'no scaffolding' in the summary, even though ten-frames would appear
on problems with multiple regroups.
Fix: Changed from whitelist (=== 'always' || === 'whenRegrouping')
to blacklist (!== 'never') approach. Now correctly identifies any mode
that enables scaffolding, including all conditional modes.
Added comprehensive console logging to debug the issue:
- ConfigPanel.tsx: getScaffoldingSummary() logs each rule evaluation
- typstGenerator.ts: logs config mode and first problem display options
- displayRules.ts: logs rule resolution for each problem
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Show preset description in collapsed dropdown state
- Show 'Settings modified from preset' message for custom configs
- Remove redundant preset button grid below 2D plot
- Cleaner, more compact UI with all preset selection in one place
- Maintains full two-way binding between dropdown and sliders
- dateFormatting.ts: getDefaultDate() for consistent date formatting
- layoutCalculations.ts: getDefaultColsForProblemsPerPage() and calculateDerivedState()
- Pure functions, easy to test
- Removes 32 lines from main component
Fixes production error "Cannot read properties of undefined (reading 'carryBoxes')"
that occurred when users tried to adjust difficulty settings.
Root cause: displayRules was undefined for new users or users with old V1 config
in database. Difficulty adjustment buttons accessed displayRules.carryBoxes without
checking if displayRules existed first.
Changes:
- AdditionWorksheetClient: Initialize displayRules with defaults when missing
- ConfigPanel: Use null-coalescing operators instead of non-null assertions
- ConfigPanel: Add error logging when required fields are missing
- NEW: WorksheetErrorBoundary component to catch all errors in worksheet page
- page.tsx: Wrap client component with error boundary
This ensures users see helpful error messages instead of blank pages,
and never need to open the browser console to understand what went wrong.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**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>
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>
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>
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>
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>
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>
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>
- 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>
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>
- Stack room code and share link vertically on left
- Place square QR button on right, spanning both rows
- Show mini QR code (40px) in button instead of emoji
- Fix popover z-index to appear above dropdown menu (z: 10000)
- Reduce button padding for more compact appearance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated the game theme system and game cards to have vibrant, eye-catching
gradients and consistent heights:
Theme System Changes:
- Updated all game themes from pastel to vibrant gradients
- Blue: Vibrant cyan (#4facfe to #00f2fe)
- Purple: Vibrant purple (#667eea to #764ba2)
- Pink: Vibrant pink (#f093fb to #f5576c)
- Green: Vibrant green/teal (#43e97b to #38f9d7)
- Plus updated indigo, teal, orange, yellow, red, gray themes
Game Manifest Updates:
- Memory Lightning: now uses purple theme
- Matching Pairs: now uses pink theme
- Complement Race: continues using blue (cyan) theme
- Card Sorting: now uses green theme
Homepage Game Cards:
- Added height: '100%' and flexbox to make all cards equal height
- Cards now stretch uniformly regardless of content length
- Maintains responsive hover effects and text readability overlays
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change <p> tag to <div> tag to fix HTML nesting violation. The
<p> tag was containing <DecompositionWithReasons> which renders
a <div>, causing a hydration error. In HTML, <p> cannot contain
block-level elements like <div>.
Fixed: apps/web/src/components/tutorial/TutorialPlayer.tsx:1386
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create a centralized theme system to prevent inconsistent game card
styling. All games now use standard color presets instead of manually
specifying gradients.
## Changes
**New Theme System:**
- Created `/src/lib/arcade/game-themes.ts` with 10 standard themes
- All themes use Tailwind 100-200 colors for consistent pastel appearance
- Exported `getGameTheme()` helper and `GAME_THEMES` constants via SDK
- Added comprehensive documentation in `.claude/GAME_THEMES.md`
**Migrated All Games:**
- card-sorting: Uses `getGameTheme('teal')`
- memory-quiz: Uses `getGameTheme('blue')`
- matching: Uses `getGameTheme('purple')`
- complement-race: Uses `getGameTheme('blue')`
**Benefits:**
- ✅ Prevents future styling inconsistencies
- ✅ One-line theme setup instead of three properties
- ✅ TypeScript autocomplete for available themes
- ✅ Centralized maintenance - update all games by changing theme definition
- ✅ Clear documentation prevents mistakes
**Before:**
```typescript
color: 'teal',
gradient: 'linear-gradient(135deg, #99f6e4, #5eead4)', // Manual, error-prone
borderColor: 'teal.200',
```
**After:**
```typescript
...getGameTheme('teal') // Simple, consistent, discoverable
```
This fixes the root cause where card-sorting needed manual gradient
adjustments - now all games automatically get professional styling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace outdated "flashcard generator" landing page with comprehensive
platform showcase highlighting all three pillars: arcade games,
interactive learning, and flashcard creation.
**New Home Page Structure:**
- Compact hero with 3 CTAs: Play Games, Learn, Create
- 4 arcade game cards with player counts and mode tags
- Two-column feature sections for Learning & Flashcards
- Multiplayer features grid (4 cards)
- Stats banner: 4 games, 8 max players, 3 learning modes, 4+ formats
**Visual Design:**
- Smaller, denser components to fit more content
- Information-rich showcase vs marketing fluff
- Purple gradient hero matching guide branding
- Responsive grid layouts for all screen sizes
**Result:**
Home page now accurately represents the full platform:
multiplayer arcade games + interactive tutorials + flashcard tools.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>