diff options
Diffstat (limited to 'frontend/src/components/LandingPage.tsx')
| -rw-r--r-- | frontend/src/components/LandingPage.tsx | 262 |
1 files changed, 169 insertions, 93 deletions
diff --git a/frontend/src/components/LandingPage.tsx b/frontend/src/components/LandingPage.tsx index e7579b5..f126d6f 100644 --- a/frontend/src/components/LandingPage.tsx +++ b/frontend/src/components/LandingPage.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import React, { useState, useEffect, useRef } from 'react' import { LoadingScreen } from './LoadingScreen' import { HeartLogo } from './HeartLogo' @@ -9,7 +9,12 @@ interface LandingPageProps { 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 [velocity, setVelocity] = useState(0) + const [energy, setEnergy] = useState(0) + const [ramped, setRamped] = useState(false) const [pendingAction, setPendingAction] = useState<null | 'makimaRedirect'>(null) + const [activePanel, setActivePanel] = useState<null | 'mission' | 'makima'>(null) // Fade-in landing page content after mount useEffect(() => { @@ -17,6 +22,62 @@ export function LandingPage({ onLogin }: LandingPageProps) { return () => clearTimeout(timer) }, []) + // Ramp up stats, then keep them fluctuating near max + useEffect(() => { + const VELOCITY_MAX = 603 + const ENERGY_MAX = 32 + + let rampInterval: number | undefined + let fluctuateInterval: number | undefined + + // Ramp-up for ~2 seconds + const rampDurationMs = 2000 + const tickMs = 30 + const vStep = VELOCITY_MAX / (rampDurationMs / tickMs) + const eStep = ENERGY_MAX / (rampDurationMs / tickMs) + + rampInterval = window.setInterval(() => { + setVelocity((v) => { + const next = v + vStep + return next >= VELOCITY_MAX ? VELOCITY_MAX : next + }) + setEnergy((e) => { + const next = e + eStep + return next >= ENERGY_MAX ? ENERGY_MAX : next + }) + }, tickMs) + + const stopRamp = window.setTimeout(() => { + if (rampInterval) window.clearInterval(rampInterval) + setVelocity(VELOCITY_MAX) + setEnergy(ENERGY_MAX) + setRamped(true) + + // Fluctuate near the top + fluctuateInterval = window.setInterval(() => { + setVelocity((v) => { + const min = VELOCITY_MAX - 18 + const max = VELOCITY_MAX + const delta = (Math.random() - 0.5) * 6 // ±3 + const next = Math.max(min, Math.min(max, v + delta)) + return next + }) + setEnergy((e) => { + const min = ENERGY_MAX - 2 + const max = ENERGY_MAX + const delta = (Math.random() - 0.5) * 0.25 // ±0.125 + const next = Math.max(min, Math.min(max, e + delta)) + return next + }) + }, 220) + }, rampDurationMs + 60) + + return () => { + if (rampInterval) window.clearInterval(rampInterval) + if (fluctuateInterval) window.clearInterval(fluctuateInterval) + } + }, []) + const handleLoadingComplete = () => { if (pendingAction === 'makimaRedirect') { window.location.assign('https://makima.jp') @@ -30,129 +91,144 @@ export function LandingPage({ onLogin }: LandingPageProps) { setLoading(true) } - const scrollTo = (id: string) => { - document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' }) + const handleMission = () => { + setActivePanel((mode) => (mode === 'mission' ? null : 'mission')) + } + + const handleMakimaPanel = () => { + setActivePanel((mode) => (mode === 'makima' ? null : 'makima')) } return ( <div> {loading && <LoadingScreen onComplete={handleLoadingComplete} />} - {/* Professional floating header */} {!loading && ( - <div className={`pro-header ${showLanding ? 'fade-in' : 'hidden'}`}> - <div className="pro-header-content"> - <div className="pro-header-left"> + <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={36} - className="pro-crane-logo" + height={40} + className="brand-mark" onError={(e) => { const img = e.currentTarget as HTMLImageElement img.onerror = null img.src = '/logo/crane-logo.png' }} /> - <span className="pro-company-name">SORYU</span> </div> - - <div className="pro-header-center"> + <div className="header-center"> <HeartLogo size="header-no-rays" className="header-heart" /> </div> - <nav className="pro-header-nav"> - <button className="pro-nav-link" onClick={() => scrollTo('pro-mission')}> - Mission - </button> - <button className="pro-nav-link" onClick={() => scrollTo('pro-makima')}> - Makima - </button> - <button className="pro-nav-link pro-nav-login" onClick={handleLogin}> - Login - </button> - </nav> + <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> )} - {/* Main professional landing layout */} - <div className={`pro-landing ${showLanding && !loading ? 'fade-in' : 'hidden'}`}> - - {/* Hero section */} - <section className="pro-hero"> - <div className="pro-hero-inner"> - <div className="pro-hero-tagline-jp">そりゅう</div> - <h1 className="pro-hero-headline"> - Real‑Time Systems for<br />Mission‑Critical Observability - </h1> - <p className="pro-hero-sub"> - Low‑latency streaming infrastructure that turns live data into reliable, secure insight. - </p> - <div className="pro-hero-cta"> - <button className="pro-btn-primary" onClick={() => scrollTo('pro-mission')}> - Learn More - </button> - <button className="pro-btn-secondary" onClick={handleLogin}> - <span className="pro-btn-icon">▶</span> Launch Makima - </button> - </div> - </div> - </section> - - {/* Content grid: Mission + Makima cards */} - <section className="pro-content-grid"> - <div className="pro-card" id="pro-mission"> - <div className="pro-card-header"> - <h2 className="pro-card-title">Mission</h2> - <div className="pro-card-accent" /> + <div className={`modern-landing-page manga-style ${showLanding && !loading ? 'fade-in' : 'hidden'}`}> + {/* Background GIF fills the main body */} + <div className="background-only" aria-hidden="true"> + <img src="/background-animation.gif" alt="" className="background-gif" /> + </div> + + {/* Minimal overlay: masthead, issue badge, and CTA */} + <div className={`taisho-cover ${activePanel ? 'mission-mode' : ''}`}> + <div className="cover-backdrop" aria-hidden="true" /> + <div className="cover-content"> + {/* Masthead + Issue badge (kept) */} + <div className="masthead"> + <div className="masthead-vertical"> + <span className="jp">そりゅう</span> + <span className="en">SORYU</span> + </div> + <div className="issue-badge"><span className="led-heart" aria-hidden="true"></span>かはいい Vol.01</div> </div> - <div className="pro-card-body"> - <h3 className="pro-card-subtitle"> - Building real‑time systems for mission-critical observability and surveillance - </h3> - <p className="pro-card-text"> - We deliver low‑latency streaming & infrastructure that turns live data into - reliable, secure insight. Target selection, monitoring and full end to end observability - to make vital decisions where it matters most. - </p> + + {/* Hero area becomes Mission content when in mission mode */} + {activePanel === 'mission' ? ( + <div className="mission-screen" role="region" aria-label="Mission"> + <h1 className="mission-headline">Building real‑time systems for mission-critical observability and surveillance </h1> + <img src="/PC98Doukuusei.webp" alt="Mission montage" className="mission-image" /> + <p className="mission-paragraph"> + We deliver low‑latency streaming & infrastructure that turns live data into + reliable, secure insight. Target selection, monitoring and full end to end observability + to make vital decisions where it matters most. + </p> + </div> + ) : activePanel === 'makima' ? ( + <div className="mission-screen makima-screen" role="region" aria-label="Makima"> + <h1 className="mission-headline makima-headline">Mesh Orchestration Platform</h1> + <span className="makima-badge">Control System</span> + <img src="/logo/makima-logo.svg" alt="Makima mesh logo" className="mission-image makima-logo" /> + <p className="mission-paragraph"> + Makima is a control system for orchestrating distributed daemon meshes, + coordinating concurrent execution across distinct domains. + </p> + <p className="mission-paragraph"> + Unified command interface for spawning, monitoring, and directing + worker daemons. Real-time task coordination with overlay management. + </p> + </div> + ) : ( + <div className="hero" /> + )} + + {/* CTA row spanning full width: left Mission/MAKIMA, right Login */} + <div className="cta-area"> + <div className="cta-left"> + <button className="taisho-cta" onClick={handleMission}> + <span className="cta-text">{activePanel === 'mission' ? 'Close' : 'Mission'}</span> + </button> + <button className="taisho-cta" onClick={handleMakimaPanel}> + <span className="cta-text">{activePanel === 'makima' ? 'Close' : 'MAKIMA'}</span> + </button> + </div> + <div className="cta-right"> + <button className="taisho-cta" onClick={handleLogin}> + <span className="cta-icon">▶</span> + <span className="cta-text">Login</span> + </button> + </div> </div> </div> + </div> - <div className="pro-card" id="pro-makima"> - <div className="pro-card-header"> - <h2 className="pro-card-title">Makima</h2> - <span className="pro-card-badge">Control System</span> - <div className="pro-card-accent" /> - </div> - <div className="pro-card-body"> - <img - src="/logo/makima-logo.svg" - alt="Makima mesh logo" - className="pro-makima-logo" - /> - <h3 className="pro-card-subtitle">Mesh Orchestration Platform</h3> - <p className="pro-card-text"> - Makima is a control system for orchestrating distributed daemon meshes, - coordinating concurrent execution across distinct domains. - </p> - <p className="pro-card-text"> - Unified command interface for spawning, monitoring, and directing - worker daemons. Real-time task coordination with overlay management. - </p> + {/* Bottom stats: Velocity + Energy only (hidden in mission mode) */} + {!activePanel && ( + <div className="bottom-stats"> + <div className="rf-stats"> + <div className="rf-stat"> + <span className="label">Velocity</span> + <span className="value">{Math.round(velocity)} km/h</span> + </div> + <div className="rf-stat"> + <span className="label">Energy</span> + <span className="value">{energy.toFixed(1)} MJ</span> + </div> </div> </div> - </section> - - {/* Footer */} - <footer className="pro-footer"> - <div className="pro-footer-inner"> - <span className="pro-footer-brand">SORYU</span> - <span className="pro-footer-sep">—</span> - <span className="pro-footer-text">Real‑time systems & infrastructure</span> - </div> - </footer> + )} </div> + {/* MissionDrawer removed in favor of mission screen transformation */} </div> ) } |
