summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-04-28 17:35:34 +0100
committerGitHub <noreply@github.com>2026-04-28 17:35:34 +0100
commit636694182fe9381479f2e9062229dda3838c5421 (patch)
treebf77510030692e50d58790d33163e740b050f51b
parentd513f93c84ae985738e0f696fcb72fa1153046ef (diff)
downloadsoryu-636694182fe9381479f2e9062229dda3838c5421.tar.gz
soryu-636694182fe9381479f2e9062229dda3838c5421.zip
feat: Redesign homepage with professional PC-98 styling (#96)
* feat: soryu-co/soryu - soryu: Redesign landing page layout and header for professional style * feat: soryu-co/soryu - soryu: Restyle CSS for professional landing page * feat: soryu-co/soryu - soryu: Integrate and polish landing page styles with component
-rw-r--r--frontend/index.html2
-rw-r--r--frontend/src/components/LandingPage.tsx262
-rw-r--r--frontend/src/styles/mobile.css108
-rw-r--r--frontend/src/styles/pc98.css323
4 files changed, 525 insertions, 170 deletions
diff --git a/frontend/index.html b/frontend/index.html
index 095310b..0014d94 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -6,7 +6,7 @@
<title>soryu.co</title>
<link rel="icon" type="image/png" href="/logo/crane-logo-transparent.png" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=DotGothic16&display=swap" rel="stylesheet">
+ <link href="https://fonts.googleapis.com/css2?family=DotGothic16&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
</head>
<body>
<div id="root"></div>
diff --git a/frontend/src/components/LandingPage.tsx b/frontend/src/components/LandingPage.tsx
index f126d6f..e7579b5 100644
--- a/frontend/src/components/LandingPage.tsx
+++ b/frontend/src/components/LandingPage.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useRef } from 'react'
+import { useState, useEffect } from 'react'
import { LoadingScreen } from './LoadingScreen'
import { HeartLogo } from './HeartLogo'
@@ -9,12 +9,7 @@ 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(() => {
@@ -22,62 +17,6 @@ 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')
@@ -91,144 +30,129 @@ export function LandingPage({ onLogin }: LandingPageProps) {
setLoading(true)
}
- const handleMission = () => {
- setActivePanel((mode) => (mode === 'mission' ? null : 'mission'))
- }
-
- const handleMakimaPanel = () => {
- setActivePanel((mode) => (mode === 'makima' ? null : 'makima'))
+ const scrollTo = (id: string) => {
+ document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
}
return (
<div>
{loading && <LoadingScreen onComplete={handleLoadingComplete} />}
+ {/* Professional floating header */}
{!loading && (
- <div className={`floating-header ${showLanding ? 'fade-in' : 'hidden'}`}>
- <div className="header-content">
- <div className="brand">
+ <div className={`pro-header ${showLanding ? 'fade-in' : 'hidden'}`}>
+ <div className="pro-header-content">
+ <div className="pro-header-left">
<img
src="/logo/crane-logo-transparent.png"
alt="Soryu"
- height={40}
- className="brand-mark"
+ height={36}
+ className="pro-crane-logo"
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="header-center">
+
+ <div className="pro-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>
+ <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>
</div>
)}
- <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>
+ {/* 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>
-
- {/* 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>
+ </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>
+ <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 &amp; 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>
</div>
- </div>
- {/* 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 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>
</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 &amp; infrastructure</span>
+ </div>
+ </footer>
</div>
- {/* MissionDrawer removed in favor of mission screen transformation */}
</div>
)
}
diff --git a/frontend/src/styles/mobile.css b/frontend/src/styles/mobile.css
index c1c524d..7d1f282 100644
--- a/frontend/src/styles/mobile.css
+++ b/frontend/src/styles/mobile.css
@@ -51,4 +51,112 @@
.makima-inline-icon { width: 16px; height: 16px; margin-left: 6px; }
.makima-list { gap: 4px; }
.makima-list li { font-size: 12px; line-height: 1.45; }
+
+ /* ================== Professional Landing Page – Mobile ================== */
+
+ /* Compact header */
+ .pro-header-content {
+ padding: 0.45rem 1rem;
+ }
+
+ .pro-company-name {
+ font-size: 0.85rem;
+ letter-spacing: 0.12em;
+ }
+
+ .pro-crane-logo {
+ height: 28px;
+ }
+
+ /* Hide center heart on small screens to save space */
+ .pro-header-center {
+ display: none;
+ }
+
+ /* Full-width nav buttons */
+ .pro-header-nav {
+ gap: 0.15rem;
+ }
+
+ .pro-nav-link {
+ font-size: 0.7rem;
+ padding: 0.3rem 0.5rem;
+ }
+
+ .pro-nav-login {
+ margin-left: 0.25rem;
+ }
+
+ /* Hero – reduce heading size and padding */
+ .pro-hero {
+ min-height: 85vh;
+ padding: 5rem 1.25rem 3rem;
+ }
+
+ .pro-hero-headline {
+ font-size: clamp(1.4rem, 5.5vw, 2rem);
+ }
+
+ .pro-hero-sub {
+ font-size: 0.9rem;
+ margin-bottom: 2rem;
+ }
+
+ .pro-hero-tagline-jp {
+ font-size: 0.75rem;
+ letter-spacing: 0.35em;
+ }
+
+ .pro-hero-cta {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .pro-btn-primary,
+ .pro-btn-secondary {
+ width: 100%;
+ text-align: center;
+ justify-content: center;
+ }
+
+ /* Stack cards to single column */
+ .pro-content-grid {
+ grid-template-columns: 1fr;
+ padding: 0 1rem 3rem;
+ gap: 1.5rem;
+ }
+
+ .pro-card-header {
+ padding: 1rem 1.25rem 0.6rem;
+ }
+
+ .pro-card-body {
+ padding: 0.6rem 1.25rem 1.25rem;
+ }
+
+ .pro-card-title {
+ font-size: 1rem;
+ }
+
+ .pro-card-subtitle {
+ font-size: 0.88rem;
+ }
+
+ .pro-card-text {
+ font-size: 0.8rem;
+ }
+
+ .pro-makima-logo {
+ width: 48px;
+ height: 48px;
+ }
+
+ /* Footer compact */
+ .pro-footer {
+ padding: 1.25rem 1rem;
+ }
+
+ .pro-footer-inner {
+ font-size: 0.7rem;
+ }
}
diff --git a/frontend/src/styles/pc98.css b/frontend/src/styles/pc98.css
index 7ec0d1c..e591cdc 100644
--- a/frontend/src/styles/pc98.css
+++ b/frontend/src/styles/pc98.css
@@ -4680,3 +4680,326 @@ button:focus-visible {
.capacity-fill.high { background: #ffcc66; }
.capacity-fill.full { background: #ff4466; }
.refresh-indicator { font-size: 11px; color: rgba(255, 255, 255, 0.3); margin-left: auto; }
+
+/* ================== Professional Landing Page ================== */
+
+/* ── Header ── */
+
+.pro-header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ background: rgba(10, 14, 28, 0.85);
+ border-bottom: 1px solid rgba(0, 200, 255, 0.15);
+}
+
+.pro-header-content {
+ max-width: 1100px;
+ margin: 0 auto;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.6rem 1.5rem;
+}
+
+.pro-header-left {
+ display: flex;
+ align-items: center;
+ gap: 0.65rem;
+}
+
+.pro-crane-logo {
+ display: block;
+}
+
+.pro-company-name {
+ font-family: var(--font-family, 'IBM Plex Mono', 'MS Gothic', monospace);
+ font-size: 1rem;
+ font-weight: 600;
+ letter-spacing: 0.18em;
+ color: #e0e8f0;
+}
+
+.pro-header-center {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.pro-header-nav {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+}
+
+.pro-nav-link {
+ background: none;
+ border: 1px solid transparent;
+ color: #8fa8c8;
+ font-family: var(--font-family, 'IBM Plex Mono', 'MS Gothic', monospace);
+ font-size: 0.8rem;
+ letter-spacing: 0.08em;
+ padding: 0.35rem 0.75rem;
+ cursor: pointer;
+ border-radius: 3px;
+ transition: color 0.2s, border-color 0.2s;
+}
+
+.pro-nav-link:hover {
+ color: #00c8ff;
+ border-color: rgba(0, 200, 255, 0.3);
+}
+
+.pro-nav-login {
+ color: #00c8ff;
+ border-color: rgba(0, 200, 255, 0.35);
+ margin-left: 0.5rem;
+}
+
+.pro-nav-login:hover {
+ background: rgba(0, 200, 255, 0.08);
+ border-color: #00c8ff;
+}
+
+/* ── Landing Container ── */
+
+.pro-landing {
+ min-height: 100vh;
+ background: #080c1a;
+ color: #c8d4e0;
+ font-family: var(--font-family, 'IBM Plex Mono', 'MS Gothic', monospace);
+ overflow-x: hidden;
+}
+
+/* ── Hero ── */
+
+.pro-hero {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ padding: 6rem 1.5rem 4rem;
+ text-align: center;
+ position: relative;
+}
+
+.pro-hero::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 60% 50% at 50% 40%, rgba(0, 100, 180, 0.08) 0%, transparent 100%),
+ radial-gradient(ellipse 40% 30% at 50% 60%, rgba(0, 200, 255, 0.04) 0%, transparent 100%);
+ pointer-events: none;
+}
+
+.pro-hero-inner {
+ position: relative;
+ max-width: 700px;
+}
+
+.pro-hero-tagline-jp {
+ font-size: 0.85rem;
+ letter-spacing: 0.5em;
+ color: rgba(0, 200, 255, 0.45);
+ margin-bottom: 1.5rem;
+ text-transform: uppercase;
+}
+
+.pro-hero-headline {
+ font-size: clamp(1.8rem, 4vw, 2.8rem);
+ font-weight: 600;
+ line-height: 1.25;
+ color: #e8f0fa;
+ margin: 0 0 1.25rem;
+ letter-spacing: -0.01em;
+}
+
+.pro-hero-sub {
+ font-size: 1rem;
+ line-height: 1.7;
+ color: #8fa8c8;
+ margin: 0 auto 2.5rem;
+ max-width: 520px;
+}
+
+.pro-hero-cta {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.pro-btn-primary,
+.pro-btn-secondary {
+ font-family: var(--font-family, 'IBM Plex Mono', 'MS Gothic', monospace);
+ font-size: 0.85rem;
+ letter-spacing: 0.06em;
+ padding: 0.65rem 1.5rem;
+ border-radius: 3px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.pro-btn-primary {
+ background: rgba(0, 200, 255, 0.1);
+ color: #00c8ff;
+ border: 1px solid rgba(0, 200, 255, 0.35);
+}
+
+.pro-btn-primary:hover {
+ background: rgba(0, 200, 255, 0.18);
+ border-color: #00c8ff;
+}
+
+.pro-btn-secondary {
+ background: transparent;
+ color: #8fa8c8;
+ border: 1px solid rgba(140, 170, 200, 0.25);
+}
+
+.pro-btn-secondary:hover {
+ color: #e0e8f0;
+ border-color: rgba(140, 170, 200, 0.5);
+}
+
+.pro-btn-icon {
+ margin-right: 0.35rem;
+ font-size: 0.75rem;
+}
+
+/* ── Content Grid ── */
+
+.pro-content-grid {
+ max-width: 1100px;
+ margin: 0 auto;
+ padding: 0 1.5rem 5rem;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 440px), 1fr));
+ gap: 2rem;
+}
+
+.pro-card {
+ background: rgba(12, 18, 36, 0.7);
+ border: 1px solid rgba(0, 200, 255, 0.12);
+ border-radius: 4px;
+ overflow: hidden;
+ scroll-margin-top: 5rem;
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
+}
+
+.pro-card:hover {
+ border-color: rgba(0, 200, 255, 0.28);
+ box-shadow: 0 0 20px rgba(0, 200, 255, 0.06);
+}
+
+.pro-card-header {
+ padding: 1.25rem 1.5rem 0.75rem;
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ flex-wrap: wrap;
+}
+
+.pro-card-title {
+ font-size: 1.1rem;
+ font-weight: 600;
+ letter-spacing: 0.12em;
+ color: #e0e8f0;
+ margin: 0;
+ text-transform: uppercase;
+}
+
+.pro-card-badge {
+ font-size: 0.65rem;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ padding: 0.2rem 0.5rem;
+ background: rgba(0, 200, 255, 0.1);
+ border: 1px solid rgba(0, 200, 255, 0.25);
+ border-radius: 2px;
+ color: #00c8ff;
+}
+
+.pro-card-accent {
+ flex: 1;
+ height: 1px;
+ background: linear-gradient(90deg, rgba(0, 200, 255, 0.3), transparent);
+}
+
+.pro-card-body {
+ padding: 0.75rem 1.5rem 1.5rem;
+}
+
+.pro-card-subtitle {
+ font-size: 0.95rem;
+ font-weight: 500;
+ color: #c0d0e0;
+ margin: 0 0 0.75rem;
+ line-height: 1.5;
+}
+
+.pro-card-text {
+ font-size: 0.85rem;
+ line-height: 1.7;
+ color: #7a90a8;
+ margin: 0 0 0.6rem;
+}
+
+.pro-card-text:last-child {
+ margin-bottom: 0;
+}
+
+.pro-makima-logo {
+ display: block;
+ width: 64px;
+ height: 64px;
+ margin-bottom: 1rem;
+ opacity: 0.75;
+}
+
+/* ── Footer ── */
+
+.pro-footer {
+ border-top: 1px solid rgba(0, 200, 255, 0.1);
+ padding: 1.5rem;
+ text-align: center;
+}
+
+.pro-footer-inner {
+ font-size: 0.75rem;
+ color: #4a6080;
+ letter-spacing: 0.08em;
+}
+
+.pro-footer-brand {
+ letter-spacing: 0.18em;
+ color: #5a7898;
+}
+
+.pro-footer-sep {
+ margin: 0 0.5rem;
+ opacity: 0.4;
+}
+
+.pro-footer-text {
+ color: #4a6080;
+}
+
+/* ── Shared transitions ── */
+
+.pro-landing.hidden,
+.pro-header.hidden {
+ opacity: 0;
+ pointer-events: none;
+}
+
+.pro-landing.fade-in,
+.pro-header.fade-in {
+ opacity: 1;
+ transition: opacity 0.8s ease;
+}