Fix render loop that was dispatching DELIVER_PASSENGER hundreds of times
per second when train remained at station, causing:
- Train stoppage despite correct answers
- Violent flashing in passenger HUD list
- Server rejecting moves: "Player does not have this passenger"
Solution:
- Add pendingDeliveryRef to track passengers with pending deliveries
- Check if delivery already dispatched before dispatching again
- Mark passenger as pending immediately before dispatch
- Clean up pending set when passengers are delivered
- Clear pending set on route changes
This mirrors the existing pendingBoardingRef pattern used for boarding.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increased spring animation config from tension:280/friction:60 to
tension:600/friction:35 for both local and ghost trains. This makes the
animations much more responsive and reduces visual lag behind the actual
game state position.
The previous slow springs caused the visual train to lag noticeably behind
the actual position, especially when answering questions (which adds momentum
and increases speed). The train would appear "stuck" even though position was
updating correctly in the game loop.
The new faster config still provides smooth interpolation to hide 100ms update
jitter, but responds quickly enough to avoid noticeable lag.
Changes:
- useTrainTransforms: Update locomotive and car spring configs to tension:600, friction:35
- GhostTrain: Update locomotive and car spring configs to match local train
Fixes:
- Train now moves immediately when answering questions
- No more "needing multiple answers" to see train movement
- Reduces perceived flashing/stuttering from render lag
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Apply the same react-spring animation treatment to the local player's train
that was previously added to ghost trains. This eliminates the low-resolution
"choppy" movement by smoothly interpolating between position updates.
Changes:
- Convert useTrainTransforms hook to use react-spring (useSpring, useSprings)
- Update TrainAndCars component to use animated.g and to() interpolation
- Animate locomotive position, rotation, and opacity
- Animate all train cars with individual springs
- Use tension:280, friction:60 config for smooth but responsive movement
Both local and ghost trains now have butter-smooth 60fps interpolated movement.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed TypeScript errors in transform interpolation by using correct react-spring
syntax: to([spring1, spring2, spring3], (a, b, c) => ...) instead of the
incorrect spring1.to((a, b, c) => ..., spring2, spring3) syntax.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Ghost trains now use react-spring animations to smoothly interpolate between
position updates (100ms intervals), eliminating the jerky/discrete movement.
Changes:
- Import useSpring, useSprings, and animated from @react-spring/web
- Convert locomotive and car positions to animated springs
- Use animated.g components for smooth transform interpolation
- Configure springs with tension:280, friction:60 for responsive smoothness
This provides buttery-smooth ghost train movement while receiving position
updates at 100ms intervals, fixing the "low resolution" appearance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed three critical multiplayer issues:
1. **Fixed interval restart bug**: Position broadcast interval was constantly restarting
because useEffect depended on `compatibleState`, which changed on every position
update. Now uses stable dependencies (`multiplayerState.gamePhase`, etc.)
2. **Increased broadcast frequency**: Changed from 200ms (5 Hz) to 100ms (10 Hz)
for smoother ghost train movement during multiplayer races
3. **Fixed position reset on reload**: Client position now syncs from server's
authoritative position when browser reloads, preventing trains from resetting
to start of track
Additional fixes:
- Used refs for `sendMove` to prevent interval recreation
- Removed unused imports (useEffect from GhostTrain, SteamTrainJourney)
- Added strategic logging for position broadcast and reception
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed all existing debug logs and added focused logging to identify why ghost
trains only update when players stop moving.
Strategic logging added:
- [POS_BROADCAST] Logs when position broadcast interval starts/stops
- [POS_BROADCAST] Throttled logging of position broadcasts (>2% change or 5s interval)
- [POS_RECEIVED] Logs when position updates are received from other players (>2% change)
This will help identify if:
1. Position broadcasts are being sent continuously during movement
2. Position updates are being received from the server
3. Updates are being processed and applied to ghost train positions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed an issue where ghost trains only updated when players stopped moving.
Root cause: clientPosition in useEffect dependency array caused the
position broadcasting interval to restart on every position change,
creating gaps in broadcasts during continuous movement.
Solution:
- Use useRef to track latest clientPosition without triggering effect
- Keep ref synced with position via separate useEffect
- Read position from ref inside interval callback
- Remove clientPosition from broadcasting useEffect dependencies
Now positions broadcast smoothly every 200ms regardless of movement state.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Ghost train cars now individually adjust opacity based on proximity to local
train, reducing visual clutter when overlapping while maintaining clarity
when separated.
Changes:
- Calculate local train car positions array in SteamTrainJourney
- Pass positions to GhostTrain for overlap detection
- Rewrite GhostTrain to render locomotive and each car separately
- Each car calculates opacity independently (0.35 when <20% from any local car, 1.0 otherwise)
- Smooth 0.3s CSS transitions between opacity states
- Overlap threshold: 20% of track length
Benefits:
- Reduced clutter when trains overlap
- Clear visibility when trains separated
- Per-car granularity for mixed scenarios
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed ReferenceError where makeMove was undefined. The correct function
is sendMove from useArcadeSession, which requires playerId and userId
parameters in addition to the move type and data.
Changes:
- Changed makeMove to sendMove
- Added playerId and userId to the move object
- Added localPlayerId guard to prevent updates before player is identified
- Updated dependency array to include localPlayerId, viewerId, and sendMove
This fixes the runtime error preventing ghost trains from working.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Clients now broadcast their train position to server every 200ms,
allowing other clients to render ghost trains at correct locations.
Added UPDATE_POSITION move type and server-side validation to sync
client-calculated positions (from momentum physics) to server state.
This fixes the issue where ghost trains were rendering but invisible
because they all had position=0 from server.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously all ghost trains used the local player's trainPosition,
causing them to render at the same location (hidden behind local train).
Now each ghost train uses its own player.position from multiplayer state,
allowing them to be visible at different positions on the track.
Also added logging to show ghost train positions for debugging.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously used `firstActivePlayer` which could show the wrong player's
name/emoji on the local train in multiplayer sessions. Now explicitly
finds the local player using `isLocal` flag.
Also updated passenger filtering to only show passengers claimed by
the local player, not the first player in the list.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement semi-transparent ghost trains to show other players' positions
in steam sprint multiplayer mode. Players can now see opponents racing
alongside them in real-time.
Changes:
- Create GhostTrain component (35% opacity with player name/score labels)
- Expose multiplayer state (players, localPlayerId) in Provider context
- Render ghost trains for all active non-local players in SteamTrainJourney
- Filter by isActive to only show currently playing opponents
Addresses multiplayer visibility gap from COMPLEMENT_RACE_MULTIPLAYER_REVIEW.md
(Priority: HIGH - "Breaks Multiplayer Experience")
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The previous fix attempted to filter by firstActivePlayer.id but was still
getting the first player with ANY id, not the first ACTIVE player.
The root cause was line 104 filtering by `p.id` (whether player has an ID)
instead of `p.isActive` (whether player is actually active).
Changes:
- Change filter from `(p) => p.id` to `(p) => p.isActive`
- Now correctly identifies the first active player
- Train shows only that player's passengers
This properly fixes the issue where inactive/disconnected players' passengers
were being displayed in the train cars.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
In multiplayer steam sprint games, the train was displaying passengers from
all players (including inactive ones) instead of only showing passengers
belonging to the first active player.
The issue was that boardedPassengers was filtering for any claimed passenger,
not checking if the claiming player was still active.
Changes to SteamTrainJourney.tsx:
- Filter boardedPassengers to only include passengers where claimedBy matches
the firstActivePlayer's ID
- Add firstActivePlayer.id to the useMemo dependency array
- Update comment to clarify filtering logic
Now the train correctly displays only the first active player's passengers
in their train cars, matching the player emoji shown as the engineer.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The interactive flashcards section at the bottom of the homepage was
hardcoding beadShape="circle" instead of respecting the app-wide abacus
configuration context that all other abaci on the site use.
Changes to InteractiveFlashcards.tsx:
- Import useAbacusConfig hook from @soroban/abacus-react
- Call useAbacusConfig() in DraggableCard component
- Use appConfig.beadShape instead of hardcoded "circle"
This ensures visual consistency across all abacus displays on the site
and respects user preferences for bead styling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
On mobile screens (base breakpoint), the level details cards were taking
up all vertical space within the 500px maxHeight constraint, pushing the
abacus completely out of view.
Solution: Hide level details on mobile (display: { base: 'none', lg: 'grid' })
so mobile users see only the slider and abacus, while desktop users see
all components. Also added minH: 0 to containers to ensure proper flex
shrinking behavior.
Changes to LevelSliderDisplay.tsx:
- Level details grid now hidden on base breakpoint, visible on lg+
- Simplified grid columns to single value since it's desktop-only
- Added minH: 0 to flex containers for proper shrinking
Tested on iPhone 14 (390px viewport).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added maxHeight constraint and reduced padding to ensure the abacus
stays visible while scrubbing the slider on mobile devices.
Changes:
- Added maxHeight: 500px on mobile (base), none on desktop (md)
- Reduced padding from 6 to 4 on mobile (base)
This ensures users can see both the slider and abacus simultaneously
on iPhone displays without scrolling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed horizontal overflow issues on small screens (iPhone 14, 390px viewport):
Changes to LevelSliderDisplay component:
- Made abacus display container stack vertically on mobile (base) and horizontal on large screens (lg)
- Made level details grid responsive: 2 cols (mobile), 3 cols (sm), 2 cols (lg)
- Removed fixed widths from detail cards, using flexible widths with min/max constraints
- Added horizontal scroll (overflow-x: auto) to abacus container as fallback
- Reduced slider thumb size on mobile: 120px x 96px (base) vs 180px x 128px (md)
- Scaled down bead in slider thumb to 75% on mobile
- Reduced emoji tick sizes: 2xl (mobile), 3xl (sm), 4xl (md)
These changes ensure the "Your Journey" slider and abacus display fit properly
on mobile devices without horizontal overflow while maintaining the desktop experience.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: Cards jumped when grabbed, especially if already rotated. The grab
point would slip during rotation instead of staying under the cursor.
Root cause: Grab offset was stored in screen space with the card already
rotated. When we later rotated this offset during drag, we were applying
rotation on top of rotation.
Solution: Convert grab offset to local (unrotated) coordinates when grabbing:
- Calculate screen-space offset from card center
- Rotate by -currentRotation to get local coordinates
- Store in grabOffsetRef
- During drag, rotate this local offset by the current rotation angle
This ensures the grab point stays perfectly under the cursor regardless of
the card's initial or current rotation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the issue where cards would slip out from under the cursor when rotating.
The grab point now stays perfectly under your cursor throughout the drag.
The problem: Simple delta positioning didn't account for rotation causing the
grab point to rotate away from the cursor position.
The solution: Properly convert between coordinate systems:
1. Calculate rotated grab offset (2D rotation matrix)
2. Determine card center: cursor position - rotated grab offset (viewport space)
3. Convert from viewport coordinates to container coordinates
4. Calculate top-left position from center for CSS translate()
Key changes:
- Pass containerRef down to DraggableCard components
- Get container bounds for viewport→container conversion
- Apply rotation matrix to grab offset
- Position card so rotated grab point aligns with cursor
Now when you grab a card by its edge and drag, the exact point you grabbed
stays glued to your cursor while the card rotates around its center.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reverted the complex rotation-compensated positioning that was causing
cards to jump to the right of the viewport on click.
The issue: The previous attempt to keep the grab point "stuck" to the cursor
while rotating was mixing screen coordinates with container-relative coords,
causing cards to teleport on pointer down.
The solution: Use simple delta positioning (cursor movement from drag start)
and let CSS handle rotation around the card center. While this means the grab
point won't stay perfectly under the cursor as the card rotates, it's much
better than cards jumping unexpectedly.
The rotation still works - cards rotate based on grab point physics - but
the positioning is now stable and predictable.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed issue where cards appeared to pivot around the grab point in
viewport space instead of rotating around their own center.
The problem: When dragging, the card would stay "pinned" at the grab
point in screen space, making it rotate around that viewport location.
The solution: As the card rotates, calculate the rotated grab offset
and reposition the card so that:
1. The grab point stays under the cursor
2. The card rotates around its own center
3. The visual result feels like the card is "stuck" to your finger
Implementation:
- Convert rotation angle to radians
- Apply 2D rotation matrix to grab offset vector
- Calculate card center position: cursor - rotated grab offset
- Convert center position to top-left for CSS translate positioning
This creates the natural feeling of grabbing and spinning a physical
card by its edge.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The previous scale factor of 5000 made rotation changes too subtle to see.
Reduced to 500 (10x more sensitive) so card rotation is clearly visible
when dragging from off-center grab points.
Also added detailed rotation logging during drag to help debug:
- Shows current rotation angle
- Shows rotation influence being applied
- Shows cross product value from physics calculation
This will help test and tune the rotation feel.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements physics-based rotation that responds to where the user grabs
the card. When you grab a card off-center and drag it, it rotates
naturally based on the grab point and drag direction.
Features:
- Calculate grab offset from card center on pointer down
- Apply rotation using cross product of grab offset and drag direction
- Rotation clamped to ±45° to prevent excessive spinning
- Final rotation preserved when card is released
- Console logging for grab point coordinates and rotation changes
Physics details:
- Cross product (grabOffset.x * deltaY - grabOffset.y * deltaX) determines
rotation direction and magnitude
- Grabbing left side + dragging right = clockwise rotation
- Grabbing right side + dragging left = counter-clockwise rotation
- Scale factor of 5000 provides smooth, realistic rotation feel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed logging issue where speed logs weren't showing during drag:
- Added separate lastLogTimeRef for logging throttle
- Logs now appear every ~200ms during drag (was never logging before)
- Velocity calculation unchanged, only logging throttle fixed
Now you can see speed values in console during drag to verify
shadow responsiveness.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
First physics enhancement - shadow changes based on how fast you drag:
- Tracks drag velocity (distance/time) during pointer move
- Shadow grows larger and darker with faster dragging
- Base: 8px offset, 24px blur, 0.3 opacity
- Fast: 32px offset, 64px blur, 0.6 opacity
- Smooth decay when released
Console logging included:
- [Shadow] logs on drag start/release
- Speed/distance/time logged during drag (throttled to ~100ms)
Test: Drag cards slowly vs fast and watch shadow change
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced complex react-spring + useDrag implementation with simple
PointerEvents-based drag and drop:
- Uses native onPointerDown/Move/Up events
- Tracks position with useState (no animation library)
- Cards stay exactly where dropped (no physics or snap-back)
- Simple scale-up effect while dragging
- Much more predictable and maintainable
Removed dependencies on:
- @react-spring/web
- @use-gesture/react
- Complex velocity tracking and decay physics
- Transform-origin calculations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed conflicting top-level config that was interfering with decay
animations. Now using explicit config objects for each property:
- x, y: decay physics with velocity
- scale, rotation: wobbly spring animations
This should fix the issue where cards were snapping back to pickup
position instead of staying where dropped.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the issue where cards were snapping back to pickup position:
- Use api.set() to immediately snap spring position to dropped location
- Then apply decay animation with momentum from that position
- Removed problematic 'from' property which doesn't work with decay
The bug was that react-spring's 'from' property is ignored when using
decay: true, causing the spring to animate from its current value rather
than the specified position. Using api.set() first ensures the spring
starts from the correct dropped position.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Made interactive flashcards a fun easter egg by:
- Changed overflow from 'hidden' to 'visible' to allow cards to be
thrown anywhere on the page, not just within container bounds
- Fixed position persistence: cards now stay exactly where dropped
instead of snapping back to pickup location
- Updated currentPositionRef immediately on drop before applying
momentum physics
Cards can now be dragged and tossed freely around the entire page
and will maintain their position after being dropped.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increased mobile scale from 2.5 to 3.5 to better utilize available screen
space on mobile devices. Screenshot review showed ample room above and
below the abacus without risk of overlapping title or scroll hint.
Final scales:
- Mobile (base): scale(3.5) - 75% larger than original
- Medium (md): scale(3.5) - 16% larger than original
- Desktop (lg): scale(4.25) - 6% larger than original
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reduced mobile scale from 2.8 to 2.5 to ensure the "Scroll to explore"
hint at the bottom is not covered by the abacus.
Final scales:
- Mobile (base): scale(2.5) - 25% larger than original, no overlap
- Medium (md): scale(3.5) - 16% larger than original
- Desktop (lg): scale(4.25) - 6% larger than original
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fine-tuned scale values based on visual feedback:
- Mobile (base): scale(2.8) - increased for better mobile visibility (40% larger than original 2)
- Medium screens (md): scale(3.5) - unchanged (16% larger than original 3)
- Desktop (lg): scale(4.25) - reduced 15% for safety (6% larger than original 4)
This balances maximum visual impact on mobile while preventing any
layout issues on larger screens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increase scale values across all breakpoints, especially on mobile:
- Mobile (base): scale(2) → scale(3) (50% increase)
- Medium screens (md): scale(3) → scale(4.5) (50% increase)
- Large screens (lg): scale(4) → scale(6) (50% increase)
The abacus now fills more of the available hero space without
clipping, improving visual impact and usability on all devices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Two small refinements to navbar branding:
- Added whiteSpace: 'nowrap' to subtitle to prevent text wrapping
- Removed abacus emoji from "Abaci One" branding for cleaner look
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed logic - the glassmorphism effect (backdrop blur, subtle backgrounds,
borders, shadows) now applies when the navbar is transparent at the top of
the homepage (isTransparent=true), not when scrolled down.
When scrolled or on other pages, the links are simpler without the extra
glassmorphism styling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced nav links (Create, Guide, Games) with floating glass pill effect
when navbar is in scrolled mode:
- Added backdrop-filter blur(8px) to blur content underneath each link
- Subtle semi-transparent backgrounds (white for inactive, purple for active)
- Added borders with purple/white tints for enhanced definition
- Soft box shadows that glow purple on hover
- Enhanced hover states with stronger purple effects
The links now have better contrast against the dark navbar while maintaining
the ethereal floating illusion. Effects are only applied when scrolled (not
when navbar is transparent on homepage hero).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the 10px gradient fade at the bottom of the navbar per user request.
Kept the border fix that removes the black line artifact when transparent.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two visual issues with the navbar:
1. Removed black border line when transparent: Changed borderBottom to
conditionally be 'none' instead of '1px solid transparent', which was
showing up as a visible line when the navbar is transparent on homepage
2. Added 10px gradient fade at bottom: Applied linear-gradient that fades
from the dark background to transparent over the last 10px, creating a
softer transition instead of a sharp cut-off
The navbar now seamlessly blends into the page content with no visual artifacts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated AppNavBar styling to blend with homepage's dark glassmorphism theme:
- Changed navbar background from white to semi-transparent dark (rgba(0,0,0,0.5))
- Added backdrop-filter blur(12px) for glassmorphism effect
- Updated border color to purple accent (rgba(139,92,246,0.2))
- Changed logo/branding text to white/light purple tones
- Updated nav link colors from gray to light gray/purple palette
- Enhanced hover states with purple highlights (rgba(196,181,253))
The navbar now seamlessly integrates with the homepage's dark theme while
maintaining transparency when the hero section is visible.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Prevents the level slider from causing horizontal overflow on mobile
devices, which was making the page width wider than intended.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed layout breakpoints from md (768px) to xl (1280px) to ensure:
- Skills section doesn't get clipped at medium viewport sizes
- Layout only switches to side-by-side when there's sufficient space
- Container min-width is responsive (100% below xl, 1400px at xl+)
This prevents the issue where content was overflowing at intermediate
viewport widths.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moved "Learn by Doing" section outside the maxW:7xl container to allow
the demo div to be 1400px wide and properly centered with mx:auto.
Remaining content stays within the 7xl constrained container.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added minW: '1400px' and removed maxW: '1200px' constraint to ensure
the "What You'll Learn" section is properly sized and centered.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increased container from 650px to 800px to ensure skill card titles
like "Friends techniques" don't wrap awkwardly across multiple lines.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed text being cut off on the right side of skill cards by:
- Adding minWidth: '0' to text container to enable proper flex shrinking
- Adding flexWrap: 'wrap' to title/badge row for better wrapping behavior
This fixes the flexbox issue where text was overflowing the card boundaries.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed width breakpoint from md to lg to match when the grid switches
to 2 columns. This prevents overflow on medium-width screens where the
container was 650px wide but still showing 1-column layout.
Now:
- Below lg: 100% width, 1 column
- At lg+: 650px width, 2 columns
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increased the "What You'll Learn" container width from 420px to 650px
to give the 2-column grid of skill cards proper breathing room and
prevent cramped layouts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed skill card abacus sizing by:
- Changing MiniAbacus to use width/height 100% instead of fixed 75px/80px
- Increased scale from 0.6 to 0.75 for better visibility
- Now properly fills the 120px/150px container set on the wrapper
This fixes the clipping issue by making the component hierarchy work correctly:
outer container (120px/150px) -> MiniAbacus (100%) -> scaled AbacusReact
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increase abacus container width from 95px/110px to 120px (mobile) and
150px (desktop) to properly accommodate 3-column abacus visualizations
without clipping.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove overflow:hidden and increase the abacus container width from
75px to responsive widths (95px mobile, 110px desktop) to properly
accommodate the abacus visualizations without clipping.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add overflow: hidden to skill card containers to properly contain
content within card bounds and prevent visual overflow issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change abacus container height to minHeight to prevent content
overflow in skill cards. This allows the container to grow as
needed while maintaining minimum dimensions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update "What You'll Learn" section to display in a responsive grid:
- One column on mobile/tablet
- Two columns on larger screens (lg breakpoint)
- Increased padding and height on two-column layout
- Added emojis to skill titles for better visual appeal
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update arcade section subtitle to highlight both single-player
challenges and multiplayer battles. Also mention the ability to
invite friends to observe games live over the network.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace "Available Now" section with "The Arcade" to better describe
the multiplayer room system. Updated subtitle to explain that users
can create or join rooms to play real-time games with friends over
the network.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Consolidate the complete levels slider UI from /levels page into a reusable
LevelSliderDisplay component. This eliminates code duplication and provides
a single source of truth for the levels progression display.
Changes:
- Created LevelSliderDisplay component (862 lines) with all levels data,
state management, animations, and UI
- Updated /levels page to use shared component (959 → 117 lines, 87.8% reduction)
- Updated homepage to use shared component (removed duplicate state/data)
- Deleted incomplete LevelsSlider component from previous commit
Component includes:
- Complete allLevels array (21 levels: 10th Kyu through 10th Dan)
- Interactive Radix slider with emoji tick marks
- StandaloneBead thumb with animated emoji transitions
- Level details for Kyu levels (Add/Sub, Multiply, Divide stats)
- Animated abacus display with speed scaling for Dan levels
- Auto-advance functionality (3s interval, pauses on hover)
- Dark theme styling
Both homepage and /levels page now use the same component with identical
behavior and appearance, eliminating maintenance overhead.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace the static emoji progression display in "Your Journey" section with
an interactive slider component adapted from /levels page. Features:
- Auto-advancing slider (3s intervals, pauses on hover)
- Interactive bead thumb with animated emoji overlay
- Animated abacus display that changes with level
- Shows 8 key milestone levels (10th Kyu through 10th Dan)
- Emoji tick marks above slider for visual navigation
- Dynamic border colors based on level category
Components:
- Created LevelsSlider component to encapsulate the UI
- Reuses existing React Spring animations from /levels page
- Uses Radix Slider for accessible interaction
- Dark theme abacus styling for consistency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Combine the interactive flashcards demonstration and the "Create
Flashcards" call-to-action into a single unified section. The card
throwing area, feature highlights, and CTA button are now part of
one cohesive pane instead of two separate sections.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add physics-based flashcard component with drag, throw, and momentum:
- Random generation of 8-15 flashcards with abacus visualizations
- Smooth drag interactions with velocity-based rotation
- Decay physics for realistic throwing and sliding
- Transform-origin pivot point based on click position
- Position persistence so cards stay where thrown
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix flashcards being positioned outside visible area by using the
container's actual dimensions instead of hardcoded pixel values.
The previous implementation used CONTAINER_WIDTH=800px and
CONTAINER_HEIGHT=500px to position cards, but the actual container
width is 100% which varies by screen size. This caused cards to be
positioned outside the visible area on smaller screens.
Changes:
- Add containerRef to get actual container dimensions
- Calculate card positions based on offsetWidth/offsetHeight
- Remove hardcoded dimension constants
- Ensure cards stay within visible bounds with proper margins
This makes the flashcard positioning responsive to any screen size.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix interactive flashcards not rendering by properly converting
react-spring animated values to CSS transforms. The x, y, rotation,
and scale spring values now use the `to` helper to create proper
CSS transform strings.
Changes:
- Import `to` helper from @react-spring/web
- Convert spring values to CSS transform using translate/rotate/scale
- Set position to absolute with left:0, top:0 as transform origin
This fixes the issue where flashcards were invisible because the
spring values weren't being properly converted to CSS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add a fun, interactive flashcard display to the homepage's flashcard
generator section. Users can drag and throw 8-15 randomly generated
flashcards around with realistic physics-based momentum.
Features:
- Drag and drop flashcards with mouse/touch
- Throw cards with velocity-based physics
- 8-15 randomly generated flashcards (100-999 range)
- Real AbacusReact components for each card
- Client-side rendering to avoid hydration errors
Technical implementation:
- Uses @use-gesture/react for drag gesture handling
- Uses @react-spring/web for smooth physics animations
- Cards generated client-side with useEffect to prevent SSR mismatch
- Each card maintains its own spring-based position and rotation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Transform the flashcard generator section into an eye-catching display
featuring five rotated flashcards spread out in a fan pattern, each
showing real AbacusReact components with different numbers.
Features:
- Five flashcards (123, 456, 789, 321, 654) rotated at different angles
- Spread out horizontally for a dynamic, fanned-out effect
- Interactive hover effects - cards lift and scale on hover
- Real AbacusReact components showing actual bead positions
- Feature boxes highlighting capabilities (formats, customization, sizes)
- Blue glow effect on hover for the entire section
- Matches the visual style of other homepage sections
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Set the tutorial panel (left side) to a fixed width of 500px on desktop
to prevent the layout from shifting when switching between tutorials
with different text lengths.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed the "What You'll Learn" panel from maxW to a fixed width (420px)
on desktop screens to prevent it from shifting when switching between
tutorials with different text lengths.
Also added minW: '0' to the tutorial panel to allow proper flex shrinking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Transform "What You'll Learn" section into an interactive experience where
users can click skill panels to see corresponding tutorials with animated
abacus demonstrations.
Changes:
- Make skill panels clickable to switch between different tutorials
- Replace static emojis with animated MiniAbacus components for each skill
- Create skill-specific tutorials: basic numbers, friends of 5,
multiplication, and mental calculation
- Add visual indication for selected panel (yellow glow effect)
- Update MiniAbacus component to cycle through custom value sequences
- Default to "Friends techniques" panel (Friends of 5 tutorial)
🤖 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>
Previously game cards were hardcoded. Now they automatically populate
from getAvailableGames() in the game registry. This means:
- Adding a new game to the registry automatically shows it on homepage
- Game metadata (icon, title, description, gradient, chips) comes from
game manifests
- No manual homepage updates needed when games are added
Changes:
- Import getAvailableGames from game registry
- Replace hardcoded GameCard components with .map() over available games
- Format maxPlayers into player count string (e.g., "1-4 players")
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added layered approach for better text readability:
- Vibrant gradient background layer (bottom)
- Dark gradient overlay from transparent (top 10%) to semi-dark (bottom 50%)
- Content with relative z-index positioning
This keeps the colorful gradients visible while ensuring all text
(titles, descriptions, tags) is clearly readable on all four cards.
Gradient goes from barely visible at top to moderately dark at bottom,
creating a natural fade that doesn't overwhelm the vibrant colors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added text shadows to all game card text elements for better readability
on bright gradient backgrounds:
- Icon emoji: subtle shadow
- Title: strong shadow for maximum contrast
- Description: medium shadow (also increased opacity to 0.95)
- Player count: medium shadow (increased opacity to 0.85)
- Tags: medium shadow
This ensures all text is clearly readable on all four gradient backgrounds
(purple, pink, cyan, and green).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed issue where only the Memory Lightning card showed its gradient
background while other cards appeared dark. The problem was that Panda
CSS's css() function doesn't properly handle raw CSS gradient strings.
Solution: Moved gradient from Panda css() to inline style prop, allowing
all four game cards to display their vibrant gradients:
- Memory Lightning: purple/violet
- Matching Pairs: pink/red
- Complement Race: blue/cyan
- Card Sorting: green/cyan
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created .claude/Z_INDEX_MANAGEMENT.md with:
- Complete z-index layering hierarchy (0-20000+)
- Stacking context rules and examples
- Current z-index audit of all 100+ hardcoded values
- Guidelines for choosing z-index values
- Migration plan to use constants file consistently
- Debugging checklist for layering issues
- Documentation of recent nav bar z-index fix
Updated .claude/CLAUDE.md to reference z-index documentation.
This provides a single source of truth for reasoning about visual
layering and prevents future z-index conflicts like the tutorial
tooltip issue.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increased navigation bar z-index from 30 to 1000 to ensure it remains
above tutorial tooltips (which use z-index 50 and 100) when scrolling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace purple gradient with solid gray.900 background to eliminate
sharp transition between hero section and page content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add isSubtitleLoaded flag to hide subtitle until client-side random
selection completes, preventing flash of wrong subtitle during hydration.
Changes:
- Add isSubtitleLoaded state to HomeHeroContext
- Set flag to true after subtitle is selected on client
- Add opacity transition to subtitle in HeroAbacus
- Matches loading pattern used for abacus value
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Store the randomly selected subtitle index in sessionStorage so it
remains consistent throughout the user's session. This eliminates
subtitle flashing on page reloads and provides a more stable UX.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove borders and extra padding around navigation elements when in
transparent mode. The borders were appearing as black and cluttering
the clean transparent overlay look. Now navigation elements show only
white text without any borders.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The thrashing was caused by a layout shift feedback loop: switching
between position sticky (takes up space) and position fixed (overlays)
caused content to shift, triggering IntersectionObserver again.
Fix: Always use position fixed so nav state changes are purely visual
(transparency, borders, colors) without any layout shifts.
Also removed unnecessary hysteresis code since the root cause is fixed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add hysteresis to IntersectionObserver to prevent rapid toggling
between transparent and opaque nav bar states when scrolling near
the threshold. Now uses different thresholds for hiding (< 10%) vs
showing (> 30%), creating a 20% buffer zone.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace unsafe global isNaN with Number.isNaN to fix linting warning
and follow best practices for type coercion checking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When the hero section is visible on the homepage, the navigation bar
now becomes transparent with a fixed position overlay, featuring
contrasting white borders around nav elements for visibility. The nav
links change to white text for better contrast against the dark hero
background.
When scrolling past the hero, the nav returns to its normal white
background with sticky positioning.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reduce gap between title and subtitle from 4 to 2, and add margin
below subtitle to increase space before the abacus for better visual
hierarchy.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously only styled column posts and reckoning bar, leaving
bright default bead colors that clashed with dark background.
Added:
- Heaven beads: light purple rgba(196, 181, 253, 0.8)
- Earth beads: medium purple rgba(167, 139, 250, 0.7)
- Both with violet strokes for definition
Beads now blend harmoniously with the dark purple gradient background.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
TypeScript error: Context type was incorrectly inferred when using
fallback React.createContext(null), causing type mismatch.
Solution: Add explicit HomeHeroContextValue type and cast both the
dynamically loaded context and the fallback to this type.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: Title, subtitle, and scaled abacus were stacking too tightly,
creating visual overlap and a messy appearance.
Solution: Reorganize with space-between flexbox layout:
- Group title + subtitle together at top with compact spacing
- Give abacus its own centered flex container with generous padding
- Separate scroll hint at bottom
- Use vertical padding and flex: 1 to ensure proper spacing
This creates clear visual separation between all sections.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add dark mode custom styles for column posts and reckoning bar:
- Semi-transparent white fills (0.3-0.4 opacity)
- Subtle stroke borders (0.2-0.25 opacity)
- Matches styling used in MiniAbacus component
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improvements:
- Increase hero abacus size: 2x→3x→4x scale for mobile/tablet/desktop
- Add better spacing between subtitle and abacus (mb: 16)
- Add z-index layering to prevent subtitle/abacus overlap
- Fix nav layout issue by adding spacer div when branding is hidden
- Remove emoji from hero title (redundant with actual abacus)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement an immersive homepage experience with a large, interactive
4-column abacus that dominates the initial viewport, creating a
"play with this" first impression. The hero smoothly transitions
to reveal the Abaci One branding in the navigation when scrolled.
**New Components:**
- `HeroAbacus`: Full-viewport interactive abacus with title/subtitle
- Auto-cycles through random 4-digit numbers
- Responsive scaling (1.5x mobile, 2x tablet, 2.5x desktop)
- Intersection Observer to track visibility
- `HomeHeroContext`: Shared state for subtitle and scroll visibility
- SSR-safe random subtitle selection (client-side only)
- Prevents hydration mismatch warnings
- Shares abacus value across hero/nav
**Navigation Updates:**
- AppNavBar conditionally shows/hides branding based on hero visibility
- Smooth fadeIn animation when branding appears after scroll
- Uses same random subtitle from context (consistent across page)
- Optional context access without breaking other pages
**Mobile Support:**
- Responsive abacus scaling for all screen sizes
- Touch-friendly interactive abacus
- Smooth animations work on all devices
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
- Add new subtitle data file with 75 three-word rhyming options
- Update AppNavBar to display "🧮 Abaci One" with random subtitle
- Implement Radix UI tooltip showing subtitle description on hover
- Use useMemo for performance (subtitle won't change on re-renders)
- Clean, minimal design with italic subtitle and help cursor
Implementation:
- Created `/src/data/abaciOneSubtitles.ts` with subtitle data structure
- Updated AppNavBar imports to include Radix Tooltip and subtitle util
- Wrapped navigation in Tooltip.Provider with 200ms delay
- Logo displays vertically with brand name and subtitle
- Tooltip shows description like "blaze through bead races" on hover
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replaced separate pl, pr, py with uniform p: '2'
- Ensures equal padding on all sides (left, right, top, bottom)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Reduced box dimensions from 200x180px to 170x150px for better fit
- Reduced grid gap from '3' to '2' for tighter layout
- Reduced box padding from '4' to '3' and gap from '2' to '1.5'
- Reduced maxW from 480px to 400px
- Changed my (margins) to py (padding) for more control
- Removed borderRight divider line between operator boxes and abacus
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Reduced my (vertical margins) from '6' to '2' to match pl (left padding)
- Ensures consistent spacing on all sides of the detail box grid
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Set fixed width (200px) and height (180px) for operator boxes to prevent shifting
- Add vertical margins (my: 6) to grid container for better spacing
- Ensures boxes stay in consistent positions when sliding through levels
- Large enough to accommodate longest content (11 digits)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Increase operator icon size from xl to 4xl for better visibility
- Center-align card layout with operator icon at top
- Extract and prominently display digit count in 2xl bold text
- Change hover effect from slide to scale for centered design
- Increase base font size from sm to md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove the Time and Pass requirement cards since we don't have actual tests
implemented. Now only showing Add/Sub, Multiply, and Divide requirements.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change from single-column flex to two-column grid layout to avoid vertical
overflow. Increased max width to 480px to accommodate the wider layout.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Parse kyu level requirements into structured sections with icons and color-coded
labels. Display in clean card layout with hover effects. Maintain consistent
font sizing across all levels.
Features:
- Parse Japanese exam data into structured sections (Add/Sub, Multiply, Divide, Time, Pass)
- Icon-based visual hierarchy (➕➖, ✖️, ➗, ⏱️, ✅)
- Color-coded labels matching level colors
- Card UI with hover effects
- Consistent sizing for better readability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Store level details exactly as provided from shuzan.jp (with Japanese
characters), then translate and format creatively in the display layer.
Changes:
- Restore verbatim data with 口, 字, 実+法, 法+商, 題
- Add formatKyuDetails() helper to translate on display
- Translate: 口→rows, 字→chars, 実+法→total, 法+商→total, 題→sets
- Use operator symbols: + / −, ×, ÷
- Maintain separation between data source and presentation
This approach keeps the original data intact while allowing creative
freedom in how we display it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive exam requirements for each Kyu level displayed on
the left side of the abacus pane:
- Create kyuLevelDetails data file with English translations
- Display level details only for Kyu levels (hidden for Dan)
- Implement responsive font sizing based on abacus size
- Center abacus for Dan levels, right-align for Kyu
- Format details in clean, readable layout with bullet points
Details include:
- Addition/Subtraction requirements (rows, characters)
- Multiplication/Division requirements (digit counts, problem counts)
- Exam time limits and passing scores
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change abacus display container from center to right-aligned layout for
better visual balance on the levels page.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update animation speed progression to be more dramatic, reaching 10ms
(0.01 seconds) at 10th Dan instead of 50ms. Speed progression now runs
from 1st Dan to 10th Dan (not from Pre-1st Dan).
Speed progression:
- Kyu levels: 500ms (constant)
- Pre-1st Dan: 500ms
- 1st Dan: 500ms
- 10th Dan: 10ms
- Linear interpolation between 1st and 10th Dan
This creates an extremely fast blur effect at the highest mastery level,
better representing the extraordinary calculation speed expected at 10th Dan.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increase abacus animation speed as Dan levels advance, creating a visual
representation of increasing mastery and speed. Animation interval changes
from 500ms at Kyu/Pre-1st Dan to 50ms at 10th Dan.
Changes:
- Kyu levels (10th-1st): constant 500ms animation interval
- Dan levels: linear interpolation from 500ms to 50ms
- Pre-1st Dan (index 10): 500ms
- 10th Dan (index 20): 50ms
- Effect now depends on currentIndex to update interval dynamically
- Add getAnimationInterval() helper for calculating speed
This creates a dramatic visual effect where the abacus becomes a blur of
movement at the highest mastery levels.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove horizontal padding (90px) from tick marks container to allow emoji
tick marks to spread across the full width of the slider. This provides
better visual distribution of level indicators.
Changes:
- Change px from '90px' to '0' on tick marks container
- Tick marks now use space-between across full slider width
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add automatic slider progression that advances one level every 3 seconds,
cycling back to the start when reaching the end. The auto-advance pauses
when the user's mouse is over the pane containing the slider and abacus,
allowing for manual exploration without interruption.
Changes:
- Add isPaneHovered state to track mouse position
- Add auto-advance effect with 3-second interval
- Pause auto-advance when isPaneHovered is true
- Add onMouseEnter/onMouseLeave handlers to pane container
- Cycle position back to 0 when reaching the last level
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move level text further down (bottom: -80px) to prevent it from overlapping
with emoji characters. Previous positioning (-60px) was too close for Dan
level emojis, causing the text to cover the characters' chins.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add visual animation to the levels page abacus that simulates ongoing
calculations by randomly changing 1-3 adjacent columns every 0.5 seconds.
This creates a more dynamic, engaging visual that shows the abacus "working"
as users explore different skill levels.
Changes:
- Add animatedDigits state to track current digit values
- Initialize random digits when level changes
- Add interval effect to update 1-3 adjacent columns every 500ms
- Use animated digits instead of static pattern for AbacusReact display
- Adjacent column grouping simulates realistic calculation movements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added mouse hover functionality to the slider so it responds as you move
your mouse across the track:
- Calculates hover position based on mouse X coordinate
- Updates slider value in real-time as you hover
- Tracks hover state with isHovering flag
- Slider thumb follows your mouse smoothly with CSS transitions
Now you can explore levels by simply hovering across the slider track,
in addition to clicking emoji tick marks or dragging the thumb.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added click handlers to emoji tick marks so you can click any emoji to jump to that level
- Added hover effects (opacity 0.6) and pointer cursor to tick marks
- Enabled pointer events on tick marks (parent has pointerEvents: 'none')
- Removed redundant "Drag or click the beads" instruction text
- Removed duplicated level info text below slider (info now only shows on slider thumb)
This simplifies the UI and makes the slider more interactive - you can now
click, drag, or hover over any emoji to explore different levels.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed slider transitions to be smooth but responsive:
- Balanced animation speeds (scale: tension 350/friction 45, emoji: 120ms)
- Added 0.3s CSS transition specifically for thumb position (left property)
- Removed complex React Spring state management that wasn't triggering re-renders
- Simplified to basic state management with CSS handling the smoothness
This gives a nice gliding effect when clicking between levels while
keeping the interface snappy and responsive.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reduced animation timing across the board to make the slider feel snappier:
- Scale factor animation: increased tension (280→400), reduced friction (60→40)
- Emoji cross-fade: reduced duration from 150ms to 80ms
- Thumb hover: reduced transition from 0.2s to 0.1s with ease-out
This addresses user feedback that the slider felt sluggish.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
feat(levels): use StandaloneBead for slider thumb and decorative tick beads
fix(levels): make slider background transparent to prevent abacus clipping
Created StandaloneBead component that integrates with the abacus style context,
replacing hardcoded SVG diamonds with proper context-aware bead rendering.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced circular slider elements with proper diamond-shaped beads that
match the authentic abacus bead style:
**Slider Thumb (Drag Handle)**:
- Diamond SVG polygon matching AbacusReact bead geometry
- 28x28px size for easy grabbing
- Two-layer design: outer diamond + inner highlight for depth
- Black stroke (0.8px) matching abacus bead styling
- Color-coded: violet for Dan levels, green for Kyu levels
- Maintains grab/grabbing cursor states
**Decorative Tick Beads**:
- All 40 level markers now use diamond shapes instead of circles
- Sized 8px (inactive) to 14px (active)
- Same color scheme and styling as main beads
- Proper stroke colors matching active/inactive states
- Drop-shadow filter for active bead glow effect
This creates a cohesive visual language connecting the interactive
slider to the abacus display, making the page feel more integrated
and true to the abacus aesthetic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced custom HTML input slider with Radix UI Slider for better
accessibility and consistency with the rest of the application.
Key changes:
- Integrated @radix-ui/react-slider for proper a11y support
- Maintained abacus-themed visual design with bead ticks
- Larger interactive area (h: '12' / 48px) for easier interaction
- Color-coded beads (green for Kyu, violet for Dan)
- Interactive thumb styled as a prominent bead with grab cursor
- Decorative beads for all 40 levels with pointer-events: none
- Smooth transitions and hover effects on thumb
- Removed custom hover handler in favor of Radix's built-in interaction
This provides a more robust and accessible slider while maintaining
the unique abacus aesthetic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced standard slider with custom abacus-themed design:
- Added bead-like circular ticks for all 40 levels
- Color-coded beads (green for Kyu, violet for Dan)
- Active bead glows and scales up (16px) with shadow effect
- Inactive beads are semi-transparent (12px)
- Increased hit target with vertical padding (py: '6')
- Added horizontal "reckoning bar" as the track
- Smooth transitions on bead state changes
This creates a more forgiving hover/drag experience and prevents
confusion from minor cursor deviations while interacting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed minimum scale factor from 1.5 to 1.2 for 30-column Dan abacuses
to prevent leftmost/rightmost columns from being clipped. Also reduced
container height from 900px to 700px to provide better visual balance
without excessive whitespace around the largest Kyu abacus.
Scale factor range is now 1.2 to 2.0, creating a 1.67x size difference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed maximum scale factor from 3.0 to 2.0 for small Kyu abacuses.
This allows for a more compact fixed-height container while still
providing appropriate visual scaling across all levels.
Scale factor range is now 1.5 to 2.0, creating a 1.33x size difference
instead of the previous 2.0x difference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed scale factor formula to use a constrained range (1.5 to 3.0)
instead of the previous unbounded formula that allowed much wider variation.
Previous formula: Math.min(2.5, 20 / digits)
- 2 columns (10 Kyu): 2.5
- 30 columns (Dan): 0.67
- Ratio: ~3.7x difference
New formula: Math.max(1.5, Math.min(3.0, 20 / digits))
- 2 columns (10 Kyu): 3.0
- 30 columns (Dan): 1.5
- Ratio: 2.0x difference
This reduces the excessive margin around Dan level abacuses while still
providing appropriate scaling for different column counts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increased the main level display container height from 700px to 900px
to provide more vertical space for the abacus display.
This prevents the top and bottom of the abacus from being clipped,
especially for levels with larger column counts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reverted the delayed column change approach (using displayedIndex state
and onRest callback) which was causing bugs. Went back to the simpler
direct approach where currentIndex immediately drives all changes.
Changes:
- Removed displayedIndex state and displayedLevel variable
- Removed onRest callback from React Spring configuration
- All UI elements now use currentLevel directly
- Kept overflow: 'hidden' on abacus container
The simpler approach provides better UX with immediate feedback while
React Spring still provides smooth scale transitions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed three layout issues with the levels page slider interaction:
1. Fixed height for level info section (160px with flexbox centering)
- Prevents slider from shifting vertically when switching between Kyu
and Dan levels (Dan levels have extra text for name + minScore)
- Keeps slider position stable during hover interaction
2. Changed abacus container overflow from 'auto' to 'visible'
- Prevents abacus from being clipped at container boundaries
- Allows full abacus display without scrollbars
3. Reduced spacing between sections for better layout balance
These changes ensure the slider stays perfectly under the mouse cursor
during hover interaction while the abacus smoothly animates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Relocate the slider control from a separate container below the level
display to inside the level pane itself, positioned above the abacus.
Changes:
- Move slider markup into main level display pane
- Position between level info and abacus display
- Remove separate slider container that was below
- Adjust spacing for better visual hierarchy
Improves UX by keeping the control close to the content it affects.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix slider hover interaction by making the entire level display pane
(including level info, abacus, and digit count) a fixed height. This
prevents the slider from moving when hovering over it.
Changes:
- Add fixed height of 700px on desktop (auto on mobile) to level pane
- Convert container to flexbox with column direction
- Make abacus display area flex: 1 to fill remaining space
- Slider now stays perfectly still under mouse during hover
This makes the hover interaction much more usable - you can now smoothly
move your mouse across the slider without it jumping away.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 08:45:17 -05:00
31 changed files with 4377 additions and 1081 deletions
* **complement-race:** increase spring animation responsiveness to reduce lag ([5bd0dad](https://github.com/antialias/soroban-abacus-flashcards/commit/5bd0dadfdf9d6d81d7db4374983e40de00effecb))
* **complement-race:** add react-spring animations to local train for smooth movement ([e5b58c8](https://github.com/antialias/soroban-abacus-flashcards/commit/e5b58c844cacce84b6118b3219b0d1f86e6f74a2))
* **complement-race:** add react-spring animations to ghost trains for smooth movement ([eb3700a](https://github.com/antialias/soroban-abacus-flashcards/commit/eb3700a57d035a142c64b60d5d1b21181d21b69f))
* **complement-race:** fix ghost train position update lag and reload position reset ([ad78a65](https://github.com/antialias/soroban-abacus-flashcards/commit/ad78a65ed7f63509602e79246e3761653ea39a15))
* **complement-race:** ensure continuous position broadcasting during train movement ([df60824](https://github.com/antialias/soroban-abacus-flashcards/commit/df60824f37f52e77e69d32c26926a24e1af88e66))
* **complement-race:** use sendMove with correct parameters for position updates ([06cd94b](https://github.com/antialias/soroban-abacus-flashcards/commit/06cd94b24cdd9dbd36fb5800c9ba7be194f7eed0))
* **complement-race:** implement position broadcasting for ghost trains ([c5fba5b](https://github.com/antialias/soroban-abacus-flashcards/commit/c5fba5b7dd0f36fd3bbe596409e01b0d3dbd4fbe))
* **complement-race:** use individual player positions for ghost trains ([00dc4b1](https://github.com/antialias/soroban-abacus-flashcards/commit/00dc4b1d06a4e1763deb16333a298145cafd9187))
* **complement-race:** use local player instead of first player for train display ([915d8a5](https://github.com/antialias/soroban-abacus-flashcards/commit/915d8a5343e70a30c7a82bed645e6628fcc08a86))
* **complement-race:** actually filter by isActive instead of just id ([ef4ca57](https://github.com/antialias/soroban-abacus-flashcards/commit/ef4ca57a6c3f35d1bddc6a70952f478058fbc6b5))
* **complement-race:** show only first active player's passengers on train ([2bfd5d2](https://github.com/antialias/soroban-abacus-flashcards/commit/2bfd5d2bda7f7d2d83c69f75600ab461fde15d92))
* **homepage:** use app-wide abacus config in interactive flashcards ([cf1be2d](https://github.com/antialias/soroban-abacus-flashcards/commit/cf1be2d1730543bd30836a87d9cbdfd2cf48360e))
* **mobile:** reduce height of Your Journey section on mobile ([8944035](https://github.com/antialias/soroban-abacus-flashcards/commit/89440355bf494e54072d2d1a1f228c33ec43d52d))
* **mobile:** optimize Your Journey section for iPhone displays ([9167fb4](https://github.com/antialias/soroban-abacus-flashcards/commit/9167fb40d68b7bdbe310b647083586434ceb6043))
* **flashcards:** store grab offset in local coordinates to prevent jump ([39d93a9](https://github.com/antialias/soroban-abacus-flashcards/commit/39d93a9e9f48a7d1ce10763cad62a600851a41d5))
* **flashcards:** keep grab point under cursor with proper coordinate conversion ([1869216](https://github.com/antialias/soroban-abacus-flashcards/commit/1869216d2fda77303c0b79d4f613c6dcdaf5324b))
* **flashcards:** correct pivot point to rotate around card center ([50fc3fd](https://github.com/antialias/soroban-abacus-flashcards/commit/50fc3fdf7f2c9b7412f6d7d890f5e0d52cb86a9b))
* **flashcards:** add grab point physics for realistic rotation ([bf37eb1](https://github.com/antialias/soroban-abacus-flashcards/commit/bf37eb1928de8d07673234e2faa1fa6268c45686))
* **flashcards:** improve shadow speed logging with separate throttling ([0f51366](https://github.com/antialias/soroban-abacus-flashcards/commit/0f51366fd56540e691df4931b6350c03043484f1))
* **flashcards:** add dynamic shadow based on drag speed ([92148a4](https://github.com/antialias/soroban-abacus-flashcards/commit/92148a4cf87e828ba2e5ec1740fb51d9667c1d73))
* **flashcards:** fix position snap-back by using api.set before decay ([30e16c8](https://github.com/antialias/soroban-abacus-flashcards/commit/30e16c8e5ac3bb25f2d54cf715dc6fb45adc4fcc))
* **flashcards:** enable unbounded drag and position persistence ([ad1ad69](https://github.com/antialias/soroban-abacus-flashcards/commit/ad1ad690f014257b5a3c3f599e794205a11d286f))
* **homepage:** adjust hero abacus scale for optimal sizing across devices ([86dee31](https://github.com/antialias/soroban-abacus-flashcards/commit/86dee31c9a51ca0712f1b4181a4899d25374d403))
* **homepage:** reduce mobile abacus scale to prevent scroll hint overlap ([b8235be](https://github.com/antialias/soroban-abacus-flashcards/commit/b8235be612c3f1dbd0da2b6cd1a935001b7dac9b))
* **navbar:** add glassmorphism effect to nav links when scrolled ([89b9072](https://github.com/antialias/soroban-abacus-flashcards/commit/89b90723b7a3fc9ed12da3ba8718fccb6ce0760f))
* **navbar:** improve theming to match homepage dark aesthetic ([284fc90](https://github.com/antialias/soroban-abacus-flashcards/commit/284fc90a53f5f4868a3e41421760ebc813be12b5))
* **homepage:** add overflow hidden to Your Journey section ([415a1fb](https://github.com/antialias/soroban-abacus-flashcards/commit/415a1fb1faa263c9d69b4e781ce22da235ca2b66))
* **homepage:** restructure layout to center 1400px wide demo section ([61403f2](https://github.com/antialias/soroban-abacus-flashcards/commit/61403f2f506557b57716a298d4dc481d7853552f))
* **homepage:** set min-width 1400px on container and remove max-width ([aa297d4](https://github.com/antialias/soroban-abacus-flashcards/commit/aa297d4ef7559473a147934766bfa3868552f58d))
* **homepage:** prevent text overflow in skill cards ([a6ac55b](https://github.com/antialias/soroban-abacus-flashcards/commit/a6ac55b7b161e0dd33a4dd5acc0df647b2a513aa))
* **homepage:** align container width breakpoint with grid columns ([422bf3d](https://github.com/antialias/soroban-abacus-flashcards/commit/422bf3d968b67e4683ac7ea7e487a84513f992f9))
* **homepage:** make MiniAbacus fill container properly ([3b5d147](https://github.com/antialias/soroban-abacus-flashcards/commit/3b5d14765dfb2d61a76f66ba3ae09695ce88bb6d))
* **homepage:** widen skill cards container to 650px ([bc1ad3a](https://github.com/antialias/soroban-abacus-flashcards/commit/bc1ad3a43a79570e1f9c61d5118d14ac4c201d71))
* **homepage:** make skills section responsive with emojis ([9ec0a71](https://github.com/antialias/soroban-abacus-flashcards/commit/9ec0a71546ee483233ed7866dae97345bf2384d7))
* **homepage:** update section title to "The Arcade" ([f47b172](https://github.com/antialias/soroban-abacus-flashcards/commit/f47b172f66bee0017c11d8f129f5b83f2ef3dcd9))
* **homepage:** use actual container dimensions for flashcard positioning ([4082a24](https://github.com/antialias/soroban-abacus-flashcards/commit/4082a246a33ea67617b762d5b7490a8c9af0ad49))
* **homepage:** set fixed width for learning panel to prevent layout shift ([dc19622](https://github.com/antialias/soroban-abacus-flashcards/commit/dc19622bbba2fead8cd9c0b2bda3a38abba0bd41))
* **homepage:** set fixed width for tutorial panel to prevent layout shift ([aba9f8a](https://github.com/antialias/soroban-abacus-flashcards/commit/aba9f8a94d50590cf94b6cd87f85b497e89045e7))
* add vibrant gradients and equal heights to game cards ([a1a135a](https://github.com/antialias/soroban-abacus-flashcards/commit/a1a135a8586e314c9d695bec6c4e58ec24e5c9cb)), closes [#4](https://github.com/antialias/soroban-abacus-flashcards/issues/4) [#00f2](https://github.com/antialias/soroban-abacus-flashcards/issues/00f2) [#667](https://github.com/antialias/soroban-abacus-flashcards/issues/667) [#764ba2](https://github.com/antialias/soroban-abacus-flashcards/issues/764ba2) [#f093](https://github.com/antialias/soroban-abacus-flashcards/issues/f093) [#f5576](https://github.com/antialias/soroban-abacus-flashcards/issues/f5576) [#43e97](https://github.com/antialias/soroban-abacus-flashcards/issues/43e97) [#38f9d7](https://github.com/antialias/soroban-abacus-flashcards/issues/38f9d7)
### Code Refactoring
* make homepage game cards dynamic from game registry ([7f51652](https://github.com/antialias/soroban-abacus-flashcards/commit/7f516526fb5f5b60c1782db5c8c3e29f05caafa7))
* **homepage:** add dark gradient overlay for better text contrast on game cards ([6410b21](https://github.com/antialias/soroban-abacus-flashcards/commit/6410b21f829810af27e42d188295630bd67d6b6b))
* **homepage:** improve text contrast on game cards with text shadows ([b6410c7](https://github.com/antialias/soroban-abacus-flashcards/commit/b6410c7c225f01f42d095ca270b8da7903cbfbb0))
* **homepage:** display gradient backgrounds on all game cards ([c6dc210](https://github.com/antialias/soroban-abacus-flashcards/commit/c6dc210bf8e3a5b4d7d6e53f2a7427d335c65322))
### Documentation
* **z-index:** add comprehensive z-index and stacking context documentation ([c89aea7](https://github.com/antialias/soroban-abacus-flashcards/commit/c89aea744478696b6f812fe53311a2dba210540f))
* **nav:** ensure nav bar appears above tutorial tooltips ([cc31564](https://github.com/antialias/soroban-abacus-flashcards/commit/cc315645de30218d1b034da3e130458fe2961a69))
### Styles
* **hero:** unify background with rest of homepage ([035d831](https://github.com/antialias/soroban-abacus-flashcards/commit/035d8312c707cbf5b0e2a725d7b1d8ff406f842d))
* **hero:** prevent SSR hydration mismatch for subtitle ([1bfde8f](https://github.com/antialias/soroban-abacus-flashcards/commit/1bfde8fb251b227ccd2528bfe1c47acffd79fa49))
* **nav:** prevent thrashing by using fixed position always ([eff44b3](https://github.com/antialias/soroban-abacus-flashcards/commit/eff44b3ad1ea0535c6965ad58012f9275cb143ec))
* **nav:** remove unnecessary borders from transparent nav ([8c2ddca](https://github.com/antialias/soroban-abacus-flashcards/commit/8c2ddca28dbdd7743227eed4d19a9a8f662a72b5))
* **hero:** prevent nav thrashing with hysteresis ([71b1b93](https://github.com/antialias/soroban-abacus-flashcards/commit/71b1b933b598c0a6a8aef1bc9f8c598c1871b2eb))
* **nav:** add transparent nav bar with borders when hero visible ([463841e](https://github.com/antialias/soroban-abacus-flashcards/commit/463841e1910f4ddb9af662f036e4efb867836a83))
### Bug Fixes
* **hero:** use Number.isNaN instead of global isNaN ([c229faf](https://github.com/antialias/soroban-abacus-flashcards/commit/c229faffac525f3eebeb12155cb5ca4dff744472))
### Styles
* **hero-abacus:** add purple bead colors for dark theme ([721dfe4](https://github.com/antialias/soroban-abacus-flashcards/commit/721dfe426db4fe259f6cdeac587d008339df769b))
* **hero:** adjust spacing between title, subtitle, and abacus ([3a3706c](https://github.com/antialias/soroban-abacus-flashcards/commit/3a3706cc6fb694c7762f065f4ab4996bb8608dc4))
* **types:** properly type HomeHeroContext in AppNavBar ([f9a7cb7](https://github.com/antialias/soroban-abacus-flashcards/commit/f9a7cb7f05dfddf291d89212a77ba1c11c00c9c7))
* **homepage:** add full-page hero abacus with scroll-based nav transition ([d8ec642](https://github.com/antialias/soroban-abacus-flashcards/commit/d8ec64280ec0c2f44f2fd9c72a93a882481f650b))
### Bug Fixes
* **homepage:** improve hero abacus sizing and layout ([230f1dc](https://github.com/antialias/soroban-abacus-flashcards/commit/230f1dcd866e5b3625e19f7400f5eae478fe7d0c))
### Styles
* **hero-abacus:** apply dark theme to match homepage styling ([e0b6a2e](https://github.com/antialias/soroban-abacus-flashcards/commit/e0b6a2e88b3ebbaae41ed54f23f9e514604d2262))
* **levels:** reduce operator box sizes and remove divider line ([29d20a6](https://github.com/antialias/soroban-abacus-flashcards/commit/29d20a6c0741e7427f2bb64bc9c3e950b1a3238a))
* **levels:** use uniform padding on operator box grid ([2818fd1](https://github.com/antialias/soroban-abacus-flashcards/commit/2818fd15cacac78de6d86ba769b9b2a02800ed1e))
* **levels:** match top/bottom margins to left padding on kyu detail boxes ([aa0bdcf](https://github.com/antialias/soroban-abacus-flashcards/commit/aa0bdcf686adcbfd1a145cf67121181d1f1194d9))
* **levels:** remove Time and Pass sections from kyu details ([d90b5d5](https://github.com/antialias/soroban-abacus-flashcards/commit/d90b5d55322e75dd28b95376614663a506c829d4))
* **levels:** use two-column grid for kyu details to prevent clipping ([fa3b73c](https://github.com/antialias/soroban-abacus-flashcards/commit/fa3b73c69169b4694201ffa19ae3f8b5a68dfe32))
* **levels:** store kyu data verbatim, add formatting layer ([53d23f1](https://github.com/antialias/soroban-abacus-flashcards/commit/53d23f19bc06459462afb76ed94d9b99d583a32d))
* **levels:** increase animation speed to 10ms for 10th Dan ([6f89d9e](https://github.com/antialias/soroban-abacus-flashcards/commit/6f89d9e274082908fc090a9c0ba310f2cb06f014))
* **levels:** progressive animation speed for Dan levels ([9dff3e7](https://github.com/antialias/soroban-abacus-flashcards/commit/9dff3e7b7b1ca46ea7f19a48135124b80c5182c0))
* **levels:** improve slider tick spacing to use full width ([1e90d6c](https://github.com/antialias/soroban-abacus-flashcards/commit/1e90d6c6207f29084a8dc96ccfbb1013a1a62271))
* **levels:** make emoji tick marks clickable and remove redundant UI ([07c783a](https://github.com/antialias/soroban-abacus-flashcards/commit/07c783a79454f50e7302b19684be6d2e5930154d))
### Bug Fixes
* **levels:** add smooth CSS transitions for slider thumb movement ([ca8cef1](https://github.com/antialias/soroban-abacus-flashcards/commit/ca8cef1c36efeb1c8c214c74f8bd383f9295be3b))
* **abacus-react:** export StandaloneBead component wired to AbacusDisplayContext ([0146ce1](https://github.com/antialias/soroban-abacus-flashcards/commit/0146ce1e67da27a24cbaa8338ba6a1a6befd6bd3))
### Performance Improvements
* **levels:** speed up slider animations for more responsive feel ([1e5467f](https://github.com/antialias/soroban-abacus-flashcards/commit/1e5467fad4e27b832300c49b4f73547dc47598b0))
* **levels:** replace slider thumb with diamond-shaped abacus beads ([0fbde53](https://github.com/antialias/soroban-abacus-flashcards/commit/0fbde53039d3ea000c6a3be492b733479e7bf47c))
* **levels:** reduce Dan scale and container height to prevent clipping ([563136f](https://github.com/antialias/soroban-abacus-flashcards/commit/563136fb79fa10b2af3a119bf0f861e3b0812b2e))
* **levels:** reduce max scale factor to allow more compact container ([ead9ee9](https://github.com/antialias/soroban-abacus-flashcards/commit/ead9ee9589aa4d7376e9385da5da53a6b444858a))
* **levels:** stabilize slider position and prevent abacus clipping ([09004dc](https://github.com/antialias/soroban-abacus-flashcards/commit/09004dc2c055031ee2f71c964ceee6f7b1d42ecd))
Z-index values are only compared within the same stacking context! Elements with `position + zIndex`, `opacity < 1`, `transform`, or `filter` create new stacking contexts where child z-indexes are relative, not global.
Before setting a z-index, always check:
1. What stacking context is this element in?
2. Am I comparing against siblings or global elements?
This document tracks z-index values and stacking contexts across the application to prevent layering conflicts and make reasoning about visual hierarchy easy.
## The Z-Index Constants System
**Location:**`src/constants/zIndex.ts`
All z-index values should be defined in this file and imported where needed:
```typescript
import{Z_INDEX}from'../constants/zIndex'
// Use it like this:
zIndex: Z_INDEX.NAV_BAR
zIndex: Z_INDEX.MODAL
zIndex: Z_INDEX.GAME_NAV.HAMBURGER_MENU
```
## Z-Index Layering Hierarchy
From lowest to highest:
| Layer | Range | Purpose | Examples |
|-------|-------|---------|----------|
| **Base Content** | 0-99 | Default page content, game elements | Background elements, game tracks, cards |
These CSS properties create new stacking contexts (z-index values are relative within them):
1.`position: fixed` or `position: sticky` with z-index
2.`position: absolute` or `position: relative` with z-index
3.`opacity` < 1
4.`transform` (any value)
5.`filter` (any value except none)
6.`isolation: isolate`
### Key Insight
**Z-index values are only compared within the same stacking context!**
If Element A creates a stacking context with `z-index: 1` and Element B is outside that context with `z-index: 999`, Element B will be on top regardless of child z-indexes inside Element A.
### Example
```tsx
// Parent creates stacking context
<divstyle={{position:'relative',zIndex: 1}}>
{/* This child's z-index is relative to parent, not global! */}
<divstyle={{position:'absolute',zIndex: 999999}}>
I'm still under elements with zIndex: 2 outside my parent!
| **AppNavBar (fixed section)** | 587 | `1000` | ❌ Should use `Z_INDEX.NAV_BAR` (100), but increased to 1000 to fix tutorial tooltip overlap | Define `TUTORIAL_TOOLTIP` in constants, set nav to proper layer |
| AppNavBar (badge) | 645 | `50` | Should use constant | Add `Z_INDEX.BADGE` |
@@ -172,6 +180,10 @@ export function useSteamJourney() {
console.log(
`🎯 DELIVERY: ${passenger.name} delivered from Car ${passenger.carIndex} to ${station.emoji}${station.name} (+${points} pts) (trainPos=${trainPosition.toFixed(1)}, carPos=${carPosition.toFixed(1)}, stationPos=${station.position})`
)
// Mark as pending BEFORE dispatch to prevent duplicate delivery attempts across frames
* **levels:** increase container height to prevent abacus clipping ([cd5c15a](https://github.com/antialias/soroban-abacus-flashcards/commit/cd5c15aeb260c568fe7ad9b6a4f51c4d6498b2b8))
* **levels:** only animate abacus, not container with background/border ([c80477d](https://github.com/antialias/soroban-abacus-flashcards/commit/c80477d24877ddada5f3f4405abbf05e1d753b5d))
* **levels:** reduce Dan scale and container height to prevent clipping ([563136f](https://github.com/antialias/soroban-abacus-flashcards/commit/563136fb79fa10b2af3a119bf0f861e3b0812b2e))
* **levels:** reduce max scale factor to allow more compact container ([ead9ee9](https://github.com/antialias/soroban-abacus-flashcards/commit/ead9ee9589aa4d7376e9385da5da53a6b444858a))
* **levels:** stabilize slider position and prevent abacus clipping ([09004dc](https://github.com/antialias/soroban-abacus-flashcards/commit/09004dc2c055031ee2f71c964ceee6f7b1d42ecd))
### Features
* **abacus-react:** export StandaloneBead component wired to AbacusDisplayContext ([0146ce1](https://github.com/antialias/soroban-abacus-flashcards/commit/0146ce1e67da27a24cbaa8338ba6a1a6befd6bd3))
* **levels:** add hover interaction and smooth React Spring transitions ([fd2b633](https://github.com/antialias/soroban-abacus-flashcards/commit/fd2b6338a84c3bbc683eff216a8da3b155749f0f))
* **levels:** redesign slider with abacus-themed beads ([f3dce84](https://github.com/antialias/soroban-abacus-flashcards/commit/f3dce84532fa706e4ec9551facde2055a060ee13))
* **levels:** replace slider thumb with diamond-shaped abacus beads ([0fbde53](https://github.com/antialias/soroban-abacus-flashcards/commit/0fbde53039d3ea000c6a3be492b733479e7bf47c))
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.