diff options
Diffstat (limited to 'frontend/src/components/LandingPage.tsx')
| -rw-r--r-- | frontend/src/components/LandingPage.tsx | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/frontend/src/components/LandingPage.tsx b/frontend/src/components/LandingPage.tsx new file mode 100644 index 0000000..f5dc55c --- /dev/null +++ b/frontend/src/components/LandingPage.tsx @@ -0,0 +1,219 @@ +import React, { useState, useEffect } from 'react' +import { LoadingScreen } from './LoadingScreen' +import { HeartLogo } from './HeartLogo' +// Using direct PNG logo on landing header + +interface LandingPageProps { + onLogin: () => void +} + +export function LandingPage({ onLogin }: LandingPageProps) { + const [loading, setLoading] = useState(false) + const [showLanding, setShowLanding] = useState(false) + const [isStandby, setIsStandby] = useState(true) // false = LIVE, true = STDBY + const [asciiArt, setAsciiArt] = useState('') + const [isGlitching, setIsGlitching] = useState(false) + // Note: Removed VN preview and ASCII features to focus on an Art Deco/Nouveau landing. + + // Auto-fade in landing page content after component mounts + useEffect(() => { + const timer = setTimeout(() => { + setShowLanding(true) + }, 500) // Delay before fading in content + + return () => clearTimeout(timer) + }, []) + + // Removed VN dialogue preview / typing effect. + + // Load ASCII art for overlay in hero (right/bottom) + useEffect(() => { + fetch('/ascii/ascii1.txt') + .then((res) => res.text()) + .then((txt) => setAsciiArt(txt.replace(/\n+$/, ''))) + .catch(() => setAsciiArt('')) + }, []) + + // Occasionally trigger a brief glitch on the word "futurist" + useEffect(() => { + let armTimer: number | undefined + let activeTimer: number | undefined + let cancelled = false + + const arm = () => { + // Random delay between glitches: 0.8s–2s (slightly punchier) + const delay = 800 + Math.random() * 1200 + armTimer = window.setTimeout(() => { + setIsGlitching(true) + // Glitch duration: 150ms–350ms (snappier) + const dur = 150 + Math.random() * 200 + activeTimer = window.setTimeout(() => { + setIsGlitching(false) + if (!cancelled) arm() + }, dur) + }, delay) + } + + arm() + + return () => { + cancelled = true + if (armTimer) clearTimeout(armTimer) + if (activeTimer) clearTimeout(activeTimer) + } + }, []) + + + // Handle loading screen completion + const handleLoadingComplete = () => { + // Call the original onLogin immediately to transition to VN page + // Don't reset loading states as we're leaving the landing page + onLogin() + } + + // Handle login button click + const handleLogin = () => { + setLoading(true) + } + + // Removed ASCII art effects and rendering. + + return ( + <div> + {loading && ( + <LoadingScreen onComplete={handleLoadingComplete} /> + )} + + {/* Floating Header Bar - Hidden during loading */} + {!loading && ( + <div className={`floating-header ${showLanding ? 'fade-in' : 'hidden'}`}> + <div className="header-content"> + <div className="brand"> + <img + src="/logo/crane-logo-transparent.png" + alt="Soryu" + height={40} + className="brand-mark" + onError={(e) => { const img = (e.currentTarget as HTMLImageElement); img.onerror = null; img.src = '/logo/crane-logo.png'; }} + /> + </div> + <div className="header-center"> + <HeartLogo size="header-no-rays" className="header-heart" /> + </div> + + <div className="system-info"> + <div className="info-item"> + <span className="info-label">System:</span> + <span + className="info-value live-status clickable" + onClick={() => setIsStandby(!isStandby)} + title="Click to toggle between LIVE and STANDBY" + > + <span className={`status-dot ${isStandby ? 'standby' : 'live'}`}></span> + {isStandby ? 'STDBY' : 'LIVE'} + </span> + </div> + <div className="info-item"> + <span className="info-label">Version:</span> + <span className="info-value">v1.0.0</span> + </div> + </div> + </div> + </div> + )} + + <div className={`modern-landing-page manga-style ${showLanding && !loading ? 'fade-in' : 'hidden'}`}> + {/* Retro-futuristic page background */} + <div className="rf-page-bg" aria-hidden="true"> + <div className="rf-page-speedlines layer-a"></div> + <div className="rf-page-speedlines layer-b"></div> + </div> + {/* Taisho Magazine Cover Backdrop */} + <div className="taisho-cover"> + <div className="cover-backdrop" aria-hidden="true"></div> + + {/* Cover Content Grid */} + <div className="cover-content"> + {/* Vertical Masthead (magazine-style) */} + <div className="masthead"> + <div className="masthead-vertical"> + <span className="jp">そりゅう</span> + <span className="en">SORYU</span> + </div> + <div className="issue-badge">かはいい Vol.01</div> + </div> + + {/* Central Hero - full-frame retro-futuristic */} + <div className="hero"> + <div className="hero-frame taisho-modern-frame"> + <div className="hero-inner hero-fill"> + {/* Retro-futuristic racing hero content */} + <div className="rf-hero" aria-hidden="true"> + <div className="rf-speedlines layer-a"></div> + <div className="rf-speedlines layer-b"></div> + <div className="rf-hero-content"> + <div className="rf-badge">Engineering • Systems • para bellum</div> + <h2 className="rf-headline"> + Mission driven{' '} + <span + className={`glitch-word ${isGlitching ? 'is-glitching' : ''}`} + data-text="futurist" + > + futurist + </span>{' '} + technology + </h2> + <p className="rf-tagline">Avant-garde. Aesthetic Engineering. A Race of Steel</p> + <div className="rf-stats"> + <div className="rf-stat"><span className="label">Velocity</span><span className="value">0–603 km/h</span></div> + <div className="rf-stat"><span className="label">Energy</span><span className="value">32 MJ</span></div> + <div className="rf-stat"><span className="label">Flow</span><span className="value">MAX</span></div> + </div> + </div> + <div className="rf-accent-diagonal"></div> + {/* ASCII overlay (transparent, gradient text) */} + {asciiArt && ( + <div className="rf-ascii-overlay" aria-hidden="true"> + {asciiArt.split('\n').map((line, i) => ( + <div key={i} className="ascii-line"> + {Array.from(line).map((ch, j) => ( + <span + key={`${i}-${j}`} + className="ascii-char" + style={{ + '--delay': `${(i * 0.08 + j * 0.01)}s`, + } as React.CSSProperties} + > + {ch} + </span> + ))} + </div> + ))} + </div> + )} + </div> + </div> + </div> + </div> + + {/* CTA */} + <div className="cta-area"> + <button className="taisho-cta" onClick={handleLogin}> + <span className="cta-icon">▶</span> + <span className="cta-text">Enter</span> + </button> + </div> + {/* Visitor Counter */} + <div className="visit-counter" aria-label="visitor counter"> + <img + src="https://count.getloli.com/get/@soryu-landing?theme=booru-jaypee&darkmode=0" + alt="visit counter" + referrerPolicy="no-referrer-when-downgrade" + /> + </div> + </div> + </div> + </div> + </div> + ) +} |
