Updates Mar6

This commit is contained in:
belviskhoremk
2026-03-06 23:05:33 +00:00
parent f2a0fd1260
commit d07111a4f2
22 changed files with 2390 additions and 479 deletions

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react'
import { Link } from 'react-router-dom'
import {
Sparkles, Bot, Globe, Code, Database, Shield, Zap, ArrowRight,
Check, MessageSquare, Upload, Play, ChevronRight, Star, Users,
Check, MessageSquare, Upload, Play, ChevronRight, Star,
FileText, Cpu, Lock, Download, Menu, X
} from 'lucide-react'
@@ -20,11 +20,20 @@ function useInView(options?: IntersectionObserverInit) {
)
observer.observe(el)
return () => observer.disconnect()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return { ref, isInView }
}
// ─── Static conversation data (outside component to avoid re-creation) ─────────
const CHAT_CONVERSATION = [
{ role: 'user', text: 'What are your return policies?' },
{ role: 'bot', text: 'Based on your company documents, we offer a 30-day return policy for all items in original condition. Refunds are processed within 5-7 business days.' },
{ role: 'user', text: 'Can I return sale items?' },
{ role: 'bot', text: 'According to Section 4.2 of your policy, sale items can be exchanged within 14 days but are not eligible for refunds. Would you like to know more?' },
]
// ─── Animated Counter ──────────────────────────────────────────────────────────
const AnimatedCounter: React.FC<{ end: number; suffix?: string; label: string; isInView: boolean }> = ({ end, suffix = '', label, isInView }) => {
const [count, setCount] = useState(0)
@@ -58,28 +67,21 @@ const FloatingChatPreview: React.FC = () => {
const [isTyping, setIsTyping] = useState(false)
const [step, setStep] = useState(0)
const conversation = [
{ role: 'user', text: 'What are your return policies?' },
{ role: 'bot', text: 'Based on your company documents, we offer a 30-day return policy for all items in original condition. Refunds are processed within 5-7 business days.' },
{ role: 'user', text: 'Can I return sale items?' },
{ role: 'bot', text: 'According to Section 4.2 of your policy, sale items can be exchanged within 14 days but are not eligible for refunds. Would you like to know more?' },
]
useEffect(() => {
if (step >= conversation.length) {
if (step >= CHAT_CONVERSATION.length) {
const timeout = setTimeout(() => { setMessages([]); setStep(0) }, 4000)
return () => clearTimeout(timeout)
}
const timeout = setTimeout(() => {
if (conversation[step].role === 'bot') {
if (CHAT_CONVERSATION[step].role === 'bot') {
setIsTyping(true)
setTimeout(() => {
setIsTyping(false)
setMessages(prev => [...prev, conversation[step]])
setMessages(prev => [...prev, CHAT_CONVERSATION[step]])
setStep(s => s + 1)
}, 1500)
} else {
setMessages(prev => [...prev, conversation[step]])
setMessages(prev => [...prev, CHAT_CONVERSATION[step]])
setStep(s => s + 1)
}
}, step === 0 ? 1500 : 1000)
@@ -240,12 +242,12 @@ export const LandingPage: React.FC = () => {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const [scrolled, setScrolled] = useState(false)
const features = useInView()
const howItWorks = useInView()
const stats = useInView()
const testimonials = useInView()
const pricing = useInView()
const cta = useInView()
const { ref: featuresRef, isInView: featuresInView } = useInView()
const { ref: howItWorksRef, isInView: howItWorksInView } = useInView()
const { ref: statsRef, isInView: statsInView } = useInView()
const { ref: testimonialsRef, isInView: testimonialsInView } = useInView()
const { ref: pricingRef, isInView: pricingInView } = useInView()
const { ref: ctaRef, isInView: ctaInView } = useInView()
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 20)
@@ -385,32 +387,32 @@ export const LandingPage: React.FC = () => {
</section>
{/* ── Trusted By / Stats ── */}
<section ref={stats.ref} className="py-16 border-t border-b border-gray-100 bg-gray-50/50">
<section ref={statsRef} className="py-16 border-t border-b border-gray-100 bg-gray-50/50">
<div className="max-w-5xl mx-auto px-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
<AnimatedCounter end={2500} suffix="+" label="Chatbots Created" isInView={stats.isInView} />
<AnimatedCounter end={500} suffix="+" label="Companies" isInView={stats.isInView} />
<AnimatedCounter end={10} suffix="M+" label="Messages Processed" isInView={stats.isInView} />
<AnimatedCounter end={99} suffix="%" label="Uptime" isInView={stats.isInView} />
<AnimatedCounter end={2500} suffix="+" label="Chatbots Created" isInView={statsInView} />
<AnimatedCounter end={500} suffix="+" label="Companies" isInView={statsInView} />
<AnimatedCounter end={10} suffix="M+" label="Messages Processed" isInView={statsInView} />
<AnimatedCounter end={99} suffix="%" label="Uptime" isInView={statsInView} />
</div>
</div>
</section>
{/* ── Features ── */}
<section id="features" ref={features.ref} className="py-20 md:py-28">
<section id="features" ref={featuresRef} className="py-20 md:py-28">
<div className="max-w-6xl mx-auto px-6">
<div className="text-center mb-16">
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${features.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}>
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${featuresInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}>
Features
</div>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${features.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${featuresInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Everything you need to build
</h2>
<p className={`text-gray-500 max-w-lg mx-auto transition-all duration-700 delay-100
${features.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<p className={`text-gray-500 max-w-lg mx-auto transition-all duration-700 delay-100
${featuresInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
From document upload to deployment we handle the heavy lifting so you can focus on your business.
</p>
</div>
@@ -424,24 +426,24 @@ export const LandingPage: React.FC = () => {
{ icon: <Sparkles className="w-6 h-6" />, title: 'Premium AI Models', desc: 'Access GPT-4o, Claude 3.5 Sonnet, Gemini 1.5 Pro, and open-source models.', color: 'bg-yellow-100 text-yellow-700' },
{ icon: <Shield className="w-6 h-6" />, title: 'Data Isolation', desc: 'Each company gets its own isolated vector database. Your data is never mixed with others.', color: 'bg-red-100 text-red-600' },
].map((f, i) => (
<FeatureCard key={f.title} {...f} delay={i * 100} isInView={features.isInView} />
<FeatureCard key={f.title} {...f} delay={i * 100} isInView={featuresInView} />
))}
</div>
</div>
</section>
{/* ── How It Works ── */}
<section ref={howItWorks.ref} className="py-20 md:py-28 bg-gray-50/70 relative">
<section ref={howItWorksRef} className="py-20 md:py-28 bg-gray-50/70 relative">
<div className="absolute inset-0 bg-dots" />
<div className="relative max-w-5xl mx-auto px-6">
<div className="text-center mb-16">
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${howItWorks.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}>
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${howItWorksInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}>
How It Works
</div>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${howItWorks.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${howItWorksInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Three steps to your AI chatbot
</h2>
</div>
@@ -452,13 +454,13 @@ export const LandingPage: React.FC = () => {
<Step num={1} icon={<Upload className="w-7 h-7 text-white" />}
title="Upload Documents" desc="Drag and drop your PDFs, DOCX, CSV, Excel, or text files. We handle parsing and chunking automatically."
isInView={howItWorks.isInView} delay={0} />
isInView={howItWorksInView} delay={0} />
<Step num={2} icon={<Cpu className="w-7 h-7 text-white" />}
title="Configure & Train" desc="Choose your AI model, customize the system prompt, and let our RAG engine build your knowledge base."
isInView={howItWorks.isInView} delay={200} />
isInView={howItWorksInView} delay={200} />
<Step num={3} icon={<Play className="w-7 h-7 text-white" />}
title="Deploy Anywhere" desc="Publish to the marketplace, embed on your website, or export the full source code for self-hosting."
isInView={howItWorks.isInView} delay={400} />
isInView={howItWorksInView} delay={400} />
</div>
</div>
</section>
@@ -534,17 +536,17 @@ async def chat(message: str):
</section>
{/* ── Testimonials ── */}
<section ref={testimonials.ref} className="py-20 md:py-28 bg-gray-50/70 relative">
<section ref={testimonialsRef} className="py-20 md:py-28 bg-gray-50/70 relative">
<div className="absolute inset-0 bg-grid opacity-30" />
<div className="relative max-w-6xl mx-auto px-6">
<div className="text-center mb-16">
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${testimonials.isInView ? 'opacity-100' : 'opacity-0'}`}>
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${testimonialsInView ? 'opacity-100' : 'opacity-0'}`}>
Testimonials
</div>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${testimonials.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${testimonialsInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Loved by builders
</h2>
</div>
@@ -553,36 +555,36 @@ async def chat(message: str):
<TestimonialCard
quote="We had our support chatbot live in under an hour. The code export feature meant we could customize everything to match our brand perfectly."
name="Sarah Chen" role="CTO" company="TechFlow"
isInView={testimonials.isInView} delay={0}
isInView={testimonialsInView} delay={0}
/>
<TestimonialCard
quote="The data isolation gives us confidence to use this with sensitive healthcare data. Each client's information stays completely separate."
name="Marcus Johnson" role="Head of Engineering" company="MedAssist"
isInView={testimonials.isInView} delay={150}
isInView={testimonialsInView} delay={150}
/>
<TestimonialCard
quote="No other platform lets you export the full source code. That was the dealbreaker for us — we needed to own the entire stack."
name="Elena Kowalski" role="VP of Product" company="DataBridge"
isInView={testimonials.isInView} delay={300}
isInView={testimonialsInView} delay={300}
/>
</div>
</div>
</section>
{/* ── Pricing Teaser ── */}
<section ref={pricing.ref} className="py-20 md:py-28">
<section ref={pricingRef} className="py-20 md:py-28">
<div className="max-w-4xl mx-auto px-6 text-center">
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${pricing.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${pricingInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Start free, scale as you grow
</h2>
<p className={`text-gray-500 mb-10 transition-all duration-700 delay-100
${pricing.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<p className={`text-gray-500 mb-10 transition-all duration-700 delay-100
${pricingInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Build unlimited chatbots for free. Upgrade to publish and unlock premium features.
</p>
<div className={`flex flex-wrap justify-center gap-x-6 gap-y-3 mb-10 transition-all duration-700 delay-200
${pricing.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<div className={`flex flex-wrap justify-center gap-x-6 gap-y-3 mb-10 transition-all duration-700 delay-200
${pricingInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{[
{ feature: 'Free forever plan', included: true },
{ feature: 'Unlimited chatbot creation', included: true },
@@ -603,15 +605,15 @@ async def chat(message: str):
))}
</div>
<Link to="/pricing" className={`text-primary-600 font-semibold hover:text-primary-700 text-sm inline-flex items-center gap-1.5
transition-all duration-700 delay-300 hover:gap-2.5 ${pricing.isInView ? 'opacity-100' : 'opacity-0'}`}>
<Link to="/pricing" className={`text-primary-600 font-semibold hover:text-primary-700 text-sm inline-flex items-center gap-1.5
transition-all duration-700 delay-300 hover:gap-2.5 ${pricingInView ? 'opacity-100' : 'opacity-0'}`}>
View full pricing <ArrowRight className="w-4 h-4" />
</Link>
</div>
</section>
{/* ── CTA ── */}
<section ref={cta.ref} className="relative py-20 md:py-24 overflow-hidden">
<section ref={ctaRef} className="relative py-20 md:py-24 overflow-hidden">
{/* Gradient background */}
<div className="absolute inset-0 bg-gradient-to-br from-primary-600 via-primary-700 to-indigo-800" />
<div className="absolute inset-0 bg-grid opacity-10" />
@@ -620,16 +622,16 @@ async def chat(message: str):
<div className="absolute bottom-10 right-10 w-60 h-60 bg-white/5 rounded-full blur-2xl animate-float-delayed" />
<div className="relative max-w-2xl mx-auto px-6 text-center">
<h2 className={`text-3xl md:text-4xl font-extrabold text-white mb-5 tracking-tight transition-all duration-700
${cta.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<h2 className={`text-3xl md:text-4xl font-extrabold text-white mb-5 tracking-tight transition-all duration-700
${ctaInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Ready to build your first chatbot?
</h2>
<p className={`text-primary-100 mb-10 text-lg transition-all duration-700 delay-100
${cta.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<p className={`text-primary-100 mb-10 text-lg transition-all duration-700 delay-100
${ctaInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Join hundreds of companies using Contexta to power their AI experiences.
</p>
<div className={`flex flex-col sm:flex-row items-center justify-center gap-4 transition-all duration-700 delay-200
${cta.isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<div className={`flex flex-col sm:flex-row items-center justify-center gap-4 transition-all duration-700 delay-200
${ctaInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<Link to="/signup" className="bg-white text-primary-700 px-8 py-3.5 rounded-xl font-semibold
hover:bg-primary-50 transition-all inline-flex items-center gap-2 shadow-lg shadow-black/10
hover:-translate-y-0.5 w-full sm:w-auto justify-center text-base">