summaryrefslogtreecommitdiff
path: root/frontend/src/components/LandingPage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/LandingPage.tsx')
-rw-r--r--frontend/src/components/LandingPage.tsx219
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>
+ )
+}