diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 90f6dc26..0562734a 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -3,6 +3,8 @@ import Link from 'next/link' import { useEffect, useState } from 'react' import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react' +import { HeroAbacus } from '@/components/HeroAbacus' +import { HomeHeroProvider } from '@/contexts/HomeHeroContext' import { PageWithNav } from '@/components/PageWithNav' import { TutorialPlayer } from '@/components/tutorial/TutorialPlayer' import { getTutorialForEditor } from '@/utils/tutorialConverter' @@ -73,664 +75,517 @@ export default function HomePage() { } return ( - -
- {/* Hero Section */} -
- {/* Background pattern */} -
+ + +
+ {/* Hero Section with Large Interactive Abacus */} + -
-
- {/* Main headline */} -

- A structured path to soroban fluency -

- - {/* Subtitle */} -

- Designed for self-directed learning. Start where you are, practice the skills you - need, play games that reinforce concepts. -

- - {/* Dev status badge */} -
- 🏗️ Curriculum system in active development -
- - {/* Visual learning journey */} -
- {[ - { icon: '📖', label: 'Learn' }, - { icon: '→', label: '', isArrow: true }, - { icon: '✏️', label: 'Practice' }, - { icon: '→', label: '', isArrow: true }, - { icon: '🎮', label: 'Play' }, - { icon: '→', label: '', isArrow: true }, - { icon: '🎯', label: 'Master' }, - ].map((step, i) => ( -
-
- {step.icon} -
- {step.label && ( -
{step.label}
- )} -
- ))} -
- - {/* Primary CTAs */} -
- + {/* Learn by Doing Section - with inline tutorial demo */} +
+
+

- 📚 Start Learning - - - 🎮 Practice Through Games - + Learn by Doing +

+

+ Interactive tutorials teach you step-by-step. Try this example right now: +

-
-
-
- {/* Main content container */} -
- {/* Learn by Doing Section - with inline tutorial demo */} -
-
-

- Learn by Doing -

-

- Interactive tutorials teach you step-by-step. Try this example right now: -

-
- - {/* Live demo and learning objectives */} -
+ {/* Live demo and learning objectives */}
- {/* Tutorial on the left */} -
- -
- - {/* What you'll learn on the right */}
-

+ +

+ + {/* What you'll learn on the right */} +
- What You'll Learn - -
- {[ - { - icon: '🔢', - title: 'Read and set numbers', - desc: 'Master abacus number representation from zero to thousands', - example: '0-9999', - badge: 'Foundation', - }, - { - icon: '🤝', - title: 'Friends techniques', - desc: 'Add and subtract using complement pairs and mental shortcuts', - example: '5 = 2+3', - badge: 'Core', - }, - { - icon: '✖️➗', - title: 'Multiply & divide', - desc: 'Fluent multi-digit calculations with advanced techniques', - example: '12×34', - badge: 'Advanced', - }, - { - icon: '🧠', - title: 'Mental calculation', - desc: 'Visualize and compute without the physical tool (Anzan)', - example: 'Speed math', - badge: 'Expert', - }, - ].map((skill, i) => ( -
-
-
- {i === 0 ? : skill.icon} -
-
-
-
- {skill.title} +

+ What You'll Learn +

+
+ {[ + { + icon: '🔢', + title: 'Read and set numbers', + desc: 'Master abacus number representation from zero to thousands', + example: '0-9999', + badge: 'Foundation', + }, + { + icon: '🤝', + title: 'Friends techniques', + desc: 'Add and subtract using complement pairs and mental shortcuts', + example: '5 = 2+3', + badge: 'Core', + }, + { + icon: '✖️➗', + title: 'Multiply & divide', + desc: 'Fluent multi-digit calculations with advanced techniques', + example: '12×34', + badge: 'Advanced', + }, + { + icon: '🧠', + title: 'Mental calculation', + desc: 'Visualize and compute without the physical tool (Anzan)', + example: 'Speed math', + badge: 'Expert', + }, + ].map((skill, i) => ( +
+
+
+ {i === 0 ? : skill.icon} +
+
+
+
+ {skill.title} +
+
+ {skill.badge} +
+
+
+ {skill.desc}
- {skill.badge} + {skill.example}
-
- {skill.desc} -
-
- {skill.example} -
-
- ))} + ))} +
-
-
+ - {/* Current Offerings Section */} -
-
-

+
+

+ Available Now +

+

+ Foundation tutorials and reinforcement games ready to use +

+
+ +
+ + + + +
+

+ + {/* For Kids & Families Section */} +
+
+

+ For Kids & Families +

+

+ Simple enough for kids to start on their own, structured enough for parents to + trust +

+
+ +
+ + +
+
+ + {/* Progression Visualization */} +
+
+

+ Your Journey +

+

+ Progress from beginner to master +

+
+ + - Available Now - -

- Foundation tutorials and reinforcement games ready to use -

-
- -
- - - - -
- - - {/* For Kids & Families Section */} -
-
-

- For Kids & Families -

-

- Simple enough for kids to start on their own, structured enough for parents to trust -

-
- -
- - -
-
- - {/* Progression Visualization */} -
-
-

- Your Journey -

-

Progress from beginner to master

-
- - - {/* Subtle arrow indicator */} -
- → -
- -
- {( - [ - { level: '10 Kyu', label: 'Beginner', color: 'colors.green.400', emoji: '🧒' }, - { - level: '5 Kyu', - label: 'Intermediate', - color: 'colors.blue.400', - emoji: '🧑', + {/* Subtle arrow indicator */} +
( -
+ })} + > + → +
+ +
+ {( + [ + { + level: '10 Kyu', + label: 'Beginner', + color: 'colors.green.400', + emoji: '🧒', + }, + { + level: '5 Kyu', + label: 'Intermediate', + color: 'colors.blue.400', + emoji: '🧑', + }, + { + level: '1 Kyu', + label: 'Advanced', + color: 'colors.violet.400', + emoji: '🧔', + }, + { level: 'Dan', label: 'Master', color: 'colors.amber.400', emoji: '🧙' }, + ] as const + ).map((stage, i) => (
- {stage.emoji} -
-
- {stage.level} -
-
- {stage.label} -
- {i < 3 && (
- → + {stage.emoji}
- )} -
- ))} -
-
- Click to learn about the official Japanese ranking system → -
- -
+
+ {stage.level} +
+
+ {stage.label} +
+ {i < 3 && ( +
+ → +
+ )} +
+ ))} +
+
+ Click to learn about the official Japanese ranking system → +
+ + - {/* Additional Tools Section */} -
-
-

- Additional Tools -

-
+ {/* Additional Tools Section */} +
+
+

+ Additional Tools +

+
-
- - -
-
+
+ + +
+
+
- -
+ + ) } diff --git a/apps/web/src/components/AppNavBar.tsx b/apps/web/src/components/AppNavBar.tsx index 75a70c98..b9794c28 100644 --- a/apps/web/src/components/AppNavBar.tsx +++ b/apps/web/src/components/AppNavBar.tsx @@ -4,7 +4,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import * as Tooltip from '@radix-ui/react-tooltip' import Link from 'next/link' import { usePathname, useRouter } from 'next/navigation' -import React, { useMemo, useState } from 'react' +import React, { useContext, useMemo, useState } from 'react' import { css } from '../../styled-system/css' import { container, hstack } from '../../styled-system/patterns' import { Z_INDEX } from '../constants/zIndex' @@ -12,6 +12,24 @@ import { useFullscreen } from '../contexts/FullscreenContext' import { getRandomSubtitle } from '../data/abaciOneSubtitles' import { AbacusDisplayDropdown } from './AbacusDisplayDropdown' +// Import HomeHeroContext for optional usage +import type { Subtitle } from '../data/abaciOneSubtitles' + +// HomeHeroContext - imported dynamically to avoid circular deps +let HomeHeroContextModule: any = null +try { + HomeHeroContextModule = require('../contexts/HomeHeroContext') +} catch { + // Context not available +} + +const HomeHeroContext = HomeHeroContextModule?.HomeHeroContext || React.createContext(null) + +// Use HomeHeroContext without requiring it +function useOptionalHomeHero(): { subtitle: Subtitle; isHeroVisible: boolean } | null { + return useContext(HomeHeroContext) +} + interface AppNavBarProps { variant?: 'full' | 'minimal' navSlot?: React.ReactNode @@ -516,8 +534,16 @@ export function AppNavBar({ variant = 'full', navSlot }: AppNavBarProps) { const isArcadePage = pathname?.startsWith('/arcade') const { isFullscreen, toggleFullscreen, exitFullscreen } = useFullscreen() + // Try to get home hero context (if on homepage) + const homeHero = useOptionalHomeHero() + // Select a random subtitle once on mount (performance: won't change on re-renders) - const subtitle = useMemo(() => getRandomSubtitle(), []) + // Use homeHero subtitle if available, otherwise generate one + const fallbackSubtitle = useMemo(() => getRandomSubtitle(), []) + const subtitle = homeHero?.subtitle || fallbackSubtitle + + // Show branding unless we're on homepage with visible hero + const showBranding = !homeHero || !homeHero.isHeroVisible // Auto-detect variant based on context const actualVariant = variant === 'full' && (isGamePage || isArcadePage) ? 'minimal' : variant @@ -552,68 +578,72 @@ export function AppNavBar({ variant = 'full', navSlot }: AppNavBarProps) { >
- {/* Logo */} - .brand-name': { color: 'brand.900' } }, - })} - > - .brand-name': { color: 'brand.900' } }, + opacity: 0, + animation: 'fadeIn 0.3s ease-out forwards', })} > - 🧮 Abaci One - - - - - {subtitle.text} - - - - - {subtitle.description} - + 🧮 Abaci One + + + + - - - - + > + {subtitle.text} + + + + + {subtitle.description} + + + + + + )}
{/* Navigation Links */} @@ -635,6 +665,24 @@ export function AppNavBar({ variant = 'full', navSlot }: AppNavBarProps) {
+ + {/* Keyframes for fade-in animation */} +