From 679127a4f4685aa20fbf55fbd78c3a2e6832dabb Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Sun, 16 Nov 2025 18:26:26 +0000
Subject: Landing page redesign: minimalist layout, Mission/Contact buttons,
 Login redirect; update title and favicon

Remove top level backend as it is used. Will be part of this monorepo, but structured differently
---
 frontend/src/components/LandingPage.tsx | 237 +++++++++++++++-----------------
 1 file changed, 109 insertions(+), 128 deletions(-)

(limited to 'frontend/src/components/LandingPage.tsx')

diff --git a/frontend/src/components/LandingPage.tsx b/frontend/src/components/LandingPage.tsx
index f5dc55c..0489794 100644
--- a/frontend/src/components/LandingPage.tsx
+++ b/frontend/src/components/LandingPage.tsx
@@ -1,7 +1,6 @@
 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
@@ -11,80 +10,95 @@ 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.
+  const [velocity, setVelocity] = useState(0)
+  const [energy, setEnergy] = useState(0)
+  const [ramped, setRamped] = useState(false)
+  const [pendingAction, setPendingAction] = useState<null | 'makima' | 'mission'>(null)
 
-  // Auto-fade in landing page content after component mounts
+  // Fade-in landing page content after mount
   useEffect(() => {
-    const timer = setTimeout(() => {
-      setShowLanding(true)
-    }, 500) // Delay before fading in content
-
+    const timer = setTimeout(() => setShowLanding(true), 500)
     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"
+  // Ramp up stats, then keep them fluctuating near max
   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()
+    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 () => {
-      cancelled = true
-      if (armTimer) clearTimeout(armTimer)
-      if (activeTimer) clearTimeout(activeTimer)
+      if (rampInterval) window.clearInterval(rampInterval)
+      if (fluctuateInterval) window.clearInterval(fluctuateInterval)
     }
   }, [])
 
-
-  // 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
+    if (pendingAction === 'makima') {
+      window.location.assign('https://makima.jp')
+      return
+    }
     onLogin()
   }
 
-  // Handle login button click
-  const handleLogin = () => {
+  const handleMakima = () => {
+    setPendingAction('makima')
     setLoading(true)
   }
 
-  // Removed ASCII art effects and rendering.
+  const handleMission = () => {
+    // Placeholder action for now
+    setPendingAction('mission')
+  }
 
   return (
     <div>
-      {loading && (
-        <LoadingScreen onComplete={handleLoadingComplete} />
-      )}
-      
-      {/* Floating Header Bar - Hidden during loading */}
+      {loading && <LoadingScreen onComplete={handleLoadingComplete} />}
+
       {!loading && (
         <div className={`floating-header ${showLanding ? 'fade-in' : 'hidden'}`}>
           <div className="header-content">
@@ -94,7 +108,11 @@ export function LandingPage({ onLogin }: LandingPageProps) {
                 alt="Soryu"
                 height={40}
                 className="brand-mark"
-                onError={(e) => { const img = (e.currentTarget as HTMLImageElement); img.onerror = null; img.src = '/logo/crane-logo.png'; }}
+                onError={(e) => {
+                  const img = e.currentTarget as HTMLImageElement
+                  img.onerror = null
+                  img.src = '/logo/crane-logo.png'
+                }}
               />
             </div>
             <div className="header-center">
@@ -121,20 +139,17 @@ export function LandingPage({ onLogin }: LandingPageProps) {
           </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>
+        {/* Background GIF fills the main body */}
+        <div className="background-only" aria-hidden="true">
+          <img src="/background-animation.gif" alt="" className="background-gif" />
         </div>
-        {/* Taisho Magazine Cover Backdrop */}
-        <div className="taisho-cover">
-          <div className="cover-backdrop" aria-hidden="true"></div>
 
-          {/* Cover Content Grid */}
+        {/* Minimal overlay: masthead, issue badge, and CTA */}
+        <div className="taisho-cover">
           <div className="cover-content">
-            {/* Vertical Masthead (magazine-style) */}
+            {/* Masthead + Issue badge (kept) */}
             <div className="masthead">
               <div className="masthead-vertical">
                 <span className="jp">そりゅう</span>
@@ -143,73 +158,39 @@ export function LandingPage({ onLogin }: LandingPageProps) {
               <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>
+            {/* Empty hero area to preserve grid; background sits behind */}
+            <div className="hero" />
+
+            {/* CTA row spanning full width: left Mission/Contact, right Login */}
+            <div className="cta-area">
+              <div className="cta-left">
+                <button className="taisho-cta" onClick={handleMission}>
+                  <span className="cta-text">Mission</span>
+                </button>
+                <button className="taisho-cta" onClick={() => {/* placeholder contact */}}>
+                  <span className="cta-text">Contact</span>
+                </button>
+              </div>
+              <div className="cta-right">
+                <button className="taisho-cta" onClick={handleMakima}>
+                  <span className="cta-icon">▶</span>
+                  <span className="cta-text">Login</span>
+                </button>
               </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>
+        {/* Bottom stats: Velocity + Energy only */}
+        <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>
-            {/* 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 className="rf-stat">
+              <span className="label">Energy</span>
+              <span className="value">{energy.toFixed(1)} MJ</span>
             </div>
           </div>
         </div>
-- 
cgit v1.2.3

