From bc43f536331bb56ba5da3ebf2e957caa843a064d Mon Sep 17 00:00:00 2001 From: chihlasm Date: Mon, 30 Mar 2026 02:13:41 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20overdrive=20landing=20page=20=E2=80=94?= =?UTF-8?q?=20live=20chat=20animation=20+=20scroll-driven=20reveals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A) Live App Preview: - Chat messages animate in with staggered timing (0.6s apart) - Typing indicator with bouncing dots appears before AI response, then fades out as the response lines arrive - Sidebar items stagger in during the entrance sequence - Creates a "show don't tell" demo moment in the hero B) Scroll-Driven Enhancements (@supports animation-timeline): - Sections use CSS scroll-driven animations instead of JS IntersectionObserver - Problem cards, feature cards, pricing cards, and step cards stagger within their parent as they enter the viewport - Social proof bar has subtle parallax drift - Falls back to existing JS-based reveal for Firefox/older browsers Accessibility: - prefers-reduced-motion removes all chat animations, shows content immediately, hides typing indicator entirely Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/pages/LandingPage.tsx | 45 ++++--- frontend/src/styles/landing.css | 210 +++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+), 15 deletions(-) diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index cafafbe1..b8073a93 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -216,25 +216,40 @@ export default function LandingPage() {
-
- You: - User can't access shared drive after password reset +
+
+ You: + User can't access shared drive after password reset +
-
- FlowPilot: - This is likely a cached credential issue. Let's check a few things: +
+
+ +
-
- FlowPilot: - 1. Run klist purge to clear Kerberos tickets +
+
+ FlowPilot: + This is likely a cached credential issue. Let's check a few things: +
-
- FlowPilot: - 2. Open Credential Manager → remove saved entries for the share +
+
+ FlowPilot: + 1. Run klist purge to clear Kerberos tickets +
-
- Auto-doc: - 3 steps captured ✓ +
+
+ FlowPilot: + 2. Open Credential Manager → remove saved entries for the share +
+
+
+
+ Auto-doc: + 3 steps captured ✓ +
diff --git a/frontend/src/styles/landing.css b/frontend/src/styles/landing.css index c2bdc99d..4e2f1dd9 100644 --- a/frontend/src/styles/landing.css +++ b/frontend/src/styles/landing.css @@ -1503,4 +1503,214 @@ .landing-cta-email-form { flex-direction: column; } +} + +/* ============================================ + OVERDRIVE: Live Chat Animation + Scroll Enhancements + ============================================ */ + +/* ── A) ANIMATED CHAT MESSAGES ── */ +/* Each chat line fades in and slides up with staggered timing. + The typing indicator appears between user message and AI response, + then hides when the AI lines appear. */ + +.landing-chat-animated { + opacity: 0; + transform: translateY(8px); + animation: landingChatReveal 0.4s ease-out both; + /* Base delay: 1.5s (after hero entrance) + stagger per line */ + animation-delay: calc(1.5s + var(--chat-index, 0) * 0.6s); +} + +/* The typing indicator (index 1) appears, then fades out when index 2 appears */ +.landing-chat-animated:nth-child(2) { + animation: landingTypingLifecycle 1.8s ease both; + animation-delay: calc(1.5s + 0.6s); +} + +@keyframes landingChatReveal { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes landingTypingLifecycle { + 0% { opacity: 0; transform: translateY(8px); } + 15% { opacity: 1; transform: translateY(0); } + 70% { opacity: 1; transform: translateY(0); } + 100% { opacity: 0; transform: translateY(0); height: 0; padding: 0; margin: 0; overflow: hidden; } +} + +/* ── Typing indicator dots ── */ +.landing-typing-indicator { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 8px; +} + +.landing-typing-indicator span { + display: block; + width: 5px; + height: 5px; + border-radius: 50%; + background: #60a5fa; + opacity: 0.4; + animation: landingTypingBounce 1.2s ease-in-out infinite; +} + +.landing-typing-indicator span:nth-child(2) { + animation-delay: 0.15s; +} + +.landing-typing-indicator span:nth-child(3) { + animation-delay: 0.3s; +} + +@keyframes landingTypingBounce { + 0%, 60%, 100% { + transform: translateY(0); + opacity: 0.4; + } + 30% { + transform: translateY(-4px); + opacity: 1; + } +} + +/* ── Sidebar items stagger in ── */ +.landing-preview-sidebar-item { + opacity: 0; + animation: landingChatReveal 0.3s ease-out both; +} + +.landing-preview-sidebar-item:nth-child(1) { animation-delay: 0.8s; } +.landing-preview-sidebar-item:nth-child(2) { animation-delay: 0.9s; } +.landing-preview-sidebar-item:nth-child(3) { animation-delay: 1.0s; } +.landing-preview-sidebar-item:nth-child(4) { animation-delay: 1.1s; } +.landing-preview-sidebar-item:nth-child(5) { animation-delay: 1.2s; } + +/* ── B) SCROLL-DRIVEN ENHANCEMENTS ── */ +/* Upgrade the basic reveal to use scroll-driven parallax where supported */ + +@supports (animation-timeline: scroll()) { + /* Sections get a subtle parallax lift as they enter viewport */ + .landing-reveal { + opacity: 0; + transform: translateY(40px); + animation: landingScrollReveal linear both; + animation-timeline: view(); + animation-range: entry 0% entry 40%; + /* Override the JS-based transition approach */ + transition: none; + } + + /* No need for the JS .visible class when scroll-driven is available */ + .landing-reveal.visible { + /* Intentionally blank — scroll-driven animation handles it */ + } + + @keyframes landingScrollReveal { + from { + opacity: 0; + transform: translateY(40px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + /* Problem cards stagger within their parent */ + .landing-problem-card { + opacity: 0; + transform: translateY(20px); + animation: landingScrollReveal linear both; + animation-timeline: view(); + animation-range: entry 0% entry 50%; + } + + .landing-problem-card:nth-child(1) { animation-range: entry 0% entry 40%; } + .landing-problem-card:nth-child(2) { animation-range: entry 5% entry 45%; } + .landing-problem-card:nth-child(3) { animation-range: entry 10% entry 50%; } + .landing-problem-card:nth-child(4) { animation-range: entry 15% entry 55%; } + + /* Feature cards stagger */ + .landing-feature-card { + opacity: 0; + transform: translateY(20px); + animation: landingScrollReveal linear both; + animation-timeline: view(); + animation-range: entry 0% entry 45%; + } + + .landing-feature-card:nth-child(1) { animation-range: entry 0% entry 35%; } + .landing-feature-card:nth-child(2) { animation-range: entry 3% entry 38%; } + .landing-feature-card:nth-child(3) { animation-range: entry 6% entry 41%; } + .landing-feature-card:nth-child(4) { animation-range: entry 9% entry 44%; } + .landing-feature-card:nth-child(5) { animation-range: entry 12% entry 47%; } + .landing-feature-card:nth-child(6) { animation-range: entry 15% entry 50%; } + + /* Pricing cards stagger */ + .landing-pricing-card { + opacity: 0; + transform: translateY(20px); + animation: landingScrollReveal linear both; + animation-timeline: view(); + animation-range: entry 0% entry 45%; + } + + .landing-pricing-card:nth-child(1) { animation-range: entry 0% entry 35%; } + .landing-pricing-card:nth-child(2) { animation-range: entry 5% entry 40%; } + .landing-pricing-card:nth-child(3) { animation-range: entry 10% entry 45%; } + + /* How it works steps stagger */ + .landing-step-card { + opacity: 0; + transform: translateY(20px); + animation: landingScrollReveal linear both; + animation-timeline: view(); + animation-range: entry 0% entry 45%; + } + + .landing-step-card:nth-child(1) { animation-range: entry 0% entry 35%; } + .landing-step-card:nth-child(2) { animation-range: entry 5% entry 40%; } + .landing-step-card:nth-child(3) { animation-range: entry 10% entry 45%; } + + /* Social proof bar parallax — slight upward drift */ + .landing-social-proof-bar { + animation: landingParallaxUp linear both; + animation-timeline: view(); + animation-range: entry 0% exit 100%; + } + + @keyframes landingParallaxUp { + from { transform: translateY(12px); } + to { transform: translateY(-12px); } + } +} + +/* ── REDUCED MOTION ── */ +@media (prefers-reduced-motion: reduce) { + .landing-chat-animated, + .landing-preview-sidebar-item { + opacity: 1; + transform: none; + animation: none; + } + + .landing-typing-indicator span { + animation: none; + opacity: 0.6; + } + + /* Hide typing indicator entirely in reduced motion */ + .landing-chat-animated:nth-child(2) { + display: none; + } } \ No newline at end of file