All checks were successful
Dev CI / Backend — Lint (push) Successful in 10m23s
Dev CI / Backend — Unit Tests (push) Successful in 10m17s
Dev CI / Frontend — Lint & Type-check (push) Successful in 11m3s
Dev CI / Frontend — Unit Tests (push) Successful in 10m33s
Dev CI / Notify Failure (push) Has been skipped
629 lines
27 KiB
TypeScript
629 lines
27 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useRef } from 'react';
|
|
import { useTranslations } from 'next-intl';
|
|
import { motion, useInView } from 'framer-motion';
|
|
import {
|
|
Mail,
|
|
Phone,
|
|
MapPin,
|
|
Clock,
|
|
Send,
|
|
MessageSquare,
|
|
Headphones,
|
|
Building2,
|
|
CheckCircle2,
|
|
Loader2,
|
|
Shield,
|
|
Zap,
|
|
BookOpen,
|
|
ArrowRight,
|
|
type LucideIcon,
|
|
} from 'lucide-react';
|
|
import { Link } from '@/i18n/navigation';
|
|
import { LandingHeader, LandingFooter } from '@/components/layout';
|
|
import { sendContactForm } from '@/lib/api/auth';
|
|
|
|
type MethodKey = 'email' | 'phone' | 'chat' | 'support';
|
|
type SubjectKey = 'demo' | 'pricing' | 'partnership' | 'support' | 'press' | 'careers' | 'other';
|
|
|
|
const METHODS: { key: MethodKey; icon: LucideIcon; color: string }[] = [
|
|
{ key: 'email', icon: Mail, color: 'from-blue-500 to-cyan-500' },
|
|
{ key: 'phone', icon: Phone, color: 'from-green-500 to-emerald-500' },
|
|
{ key: 'chat', icon: MessageSquare, color: 'from-purple-500 to-pink-500' },
|
|
{ key: 'support', icon: Headphones, color: 'from-orange-500 to-red-500' },
|
|
];
|
|
|
|
const SUBJECTS: SubjectKey[] = ['demo', 'pricing', 'partnership', 'support', 'press', 'careers', 'other'];
|
|
|
|
export default function ContactPage() {
|
|
const t = useTranslations('marketing.contact');
|
|
const [formData, setFormData] = useState({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
company: '',
|
|
phone: '',
|
|
subject: '',
|
|
message: '',
|
|
});
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
const heroRef = useRef(null);
|
|
const formRef = useRef(null);
|
|
const contactRef = useRef(null);
|
|
const afterSubmitRef = useRef(null);
|
|
const quickAccessRef = useRef(null);
|
|
|
|
const isHeroInView = useInView(heroRef, { once: true });
|
|
const isFormInView = useInView(formRef, { once: true });
|
|
const isContactInView = useInView(contactRef, { once: true });
|
|
const isAfterSubmitInView = useInView(afterSubmitRef, { once: true });
|
|
const isQuickAccessInView = useInView(quickAccessRef, { once: true });
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
setIsSubmitting(true);
|
|
|
|
try {
|
|
await sendContactForm({
|
|
firstName: formData.firstName,
|
|
lastName: formData.lastName,
|
|
email: formData.email,
|
|
company: formData.company || undefined,
|
|
phone: formData.phone || undefined,
|
|
subject: formData.subject,
|
|
message: formData.message,
|
|
});
|
|
setIsSubmitted(true);
|
|
} catch (err: any) {
|
|
setError(err.message || t('form.genericError'));
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
const handleChange = (
|
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
|
) => {
|
|
setFormData((prev) => ({
|
|
...prev,
|
|
[e.target.name]: e.target.value,
|
|
}));
|
|
};
|
|
|
|
const containerVariants = {
|
|
hidden: { opacity: 0, y: 50 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
transition: {
|
|
duration: 0.6,
|
|
staggerChildren: 0.1,
|
|
},
|
|
},
|
|
};
|
|
|
|
const itemVariants = {
|
|
hidden: { opacity: 0, y: 20 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
transition: { duration: 0.5 },
|
|
},
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white">
|
|
<LandingHeader activePage="contact" />
|
|
|
|
{/* Hero Section */}
|
|
<section ref={heroRef} className="relative pt-32 pb-20 bg-gradient-to-br from-brand-navy to-brand-navy/95 overflow-hidden">
|
|
<div className="absolute inset-0 opacity-10">
|
|
<div className="absolute top-20 left-20 w-96 h-96 bg-brand-turquoise rounded-full blur-3xl" />
|
|
<div className="absolute bottom-20 right-20 w-96 h-96 bg-brand-green rounded-full blur-3xl" />
|
|
</div>
|
|
|
|
<div className="relative z-10 max-w-7xl mx-auto px-6 lg:px-8">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={isHeroInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.8 }}
|
|
className="text-center"
|
|
>
|
|
<motion.div
|
|
initial={{ scale: 0.8, opacity: 0 }}
|
|
animate={isHeroInView ? { scale: 1, opacity: 1 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.2 }}
|
|
className="inline-flex items-center space-x-2 bg-white/10 backdrop-blur-sm px-4 py-2 rounded-full mb-8 border border-white/20"
|
|
>
|
|
<Mail className="w-5 h-5 text-brand-turquoise" />
|
|
<span className="text-white/90 text-sm font-medium">{t('badge')}</span>
|
|
</motion.div>
|
|
|
|
<h1 className="text-4xl lg:text-6xl font-bold text-white mb-6 leading-tight">
|
|
{t('title1')}
|
|
<br />
|
|
<span className="text-transparent bg-clip-text bg-gradient-to-r from-brand-turquoise to-brand-green">
|
|
{t('title2')}
|
|
</span>
|
|
</h1>
|
|
|
|
<p className="text-xl text-white/80 mb-10 max-w-3xl mx-auto leading-relaxed">
|
|
{t('intro')}
|
|
</p>
|
|
</motion.div>
|
|
</div>
|
|
|
|
{/* Wave */}
|
|
<div className="absolute bottom-0 left-0 right-0">
|
|
<svg className="w-full h-16" viewBox="0 0 1440 60" preserveAspectRatio="none">
|
|
<path
|
|
d="M0,30 C240,50 480,10 720,30 C960,50 1200,10 1440,30 L1440,60 L0,60 Z"
|
|
fill="white"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Contact Methods */}
|
|
<section ref={contactRef} className="py-16 bg-gray-50">
|
|
<motion.div
|
|
variants={containerVariants}
|
|
initial="hidden"
|
|
animate={isContactInView ? 'visible' : 'hidden'}
|
|
className="max-w-7xl mx-auto px-6 lg:px-8"
|
|
>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
{METHODS.map((method) => {
|
|
const IconComponent = method.icon;
|
|
return (
|
|
<motion.div
|
|
key={method.key}
|
|
variants={itemVariants}
|
|
className="bg-white p-6 rounded-2xl shadow-lg border border-gray-100"
|
|
>
|
|
<div
|
|
className={`w-12 h-12 rounded-xl bg-gradient-to-br ${method.color} flex items-center justify-center mb-4`}
|
|
>
|
|
<IconComponent className="w-6 h-6 text-white" />
|
|
</div>
|
|
<h3 className="text-lg font-bold text-brand-navy mb-1">{t(`methods.${method.key}.title`)}</h3>
|
|
<p className="text-gray-500 text-sm mb-2">{t(`methods.${method.key}.description`)}</p>
|
|
<p className="text-brand-turquoise font-medium">{t(`methods.${method.key}.value`)}</p>
|
|
</motion.div>
|
|
);
|
|
})}
|
|
</div>
|
|
</motion.div>
|
|
</section>
|
|
|
|
{/* Contact Form & Info */}
|
|
<section ref={formRef} className="py-20">
|
|
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
|
{/* Form */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: -50 }}
|
|
animate={isFormInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.8 }}
|
|
>
|
|
<h2 className="text-3xl font-bold text-brand-navy mb-6">{t('form.title')}</h2>
|
|
<p className="text-gray-600 mb-8">
|
|
{t('form.description')}
|
|
</p>
|
|
|
|
{isSubmitted ? (
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.9 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
className="bg-green-50 border border-green-200 rounded-2xl p-8 text-center"
|
|
>
|
|
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<CheckCircle2 className="w-8 h-8 text-green-600" />
|
|
</div>
|
|
<h3 className="text-2xl font-bold text-green-800 mb-2">{t('form.successTitle')}</h3>
|
|
<p className="text-green-700 mb-6">
|
|
{t('form.successBody')}
|
|
</p>
|
|
<button
|
|
onClick={() => {
|
|
setIsSubmitted(false);
|
|
setFormData({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
company: '',
|
|
phone: '',
|
|
subject: '',
|
|
message: '',
|
|
});
|
|
}}
|
|
className="text-brand-turquoise font-medium hover:underline"
|
|
>
|
|
{t('form.sendAnother')}
|
|
</button>
|
|
</motion.div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<label htmlFor="firstName" className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('form.firstName')} *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="firstName"
|
|
name="firstName"
|
|
required
|
|
value={formData.firstName}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder={t('form.firstNamePlaceholder')}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="lastName" className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('form.lastName')} *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="lastName"
|
|
name="lastName"
|
|
required
|
|
value={formData.lastName}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder={t('form.lastNamePlaceholder')}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('form.email')} *
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
required
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder={t('form.emailPlaceholder')}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('form.phone')}
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
id="phone"
|
|
name="phone"
|
|
value={formData.phone}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder={t('form.phonePlaceholder')}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="company" className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('form.company')}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="company"
|
|
name="company"
|
|
value={formData.company}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder={t('form.companyPlaceholder')}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="subject" className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('form.subject')} *
|
|
</label>
|
|
<select
|
|
id="subject"
|
|
name="subject"
|
|
required
|
|
value={formData.subject}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
>
|
|
<option value="">{t('subjects.placeholder')}</option>
|
|
{SUBJECTS.map((key) => (
|
|
<option key={key} value={key}>
|
|
{t(`subjects.${key}`)}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('form.message')} *
|
|
</label>
|
|
<textarea
|
|
id="message"
|
|
name="message"
|
|
required
|
|
rows={5}
|
|
value={formData.message}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all resize-none"
|
|
placeholder={t('form.messagePlaceholder')}
|
|
/>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={isSubmitting}
|
|
className="w-full px-8 py-4 bg-brand-turquoise text-white rounded-lg hover:bg-brand-turquoise/90 transition-all font-semibold text-lg flex items-center justify-center space-x-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{isSubmitting ? (
|
|
<>
|
|
<Loader2 className="w-5 h-5 animate-spin" />
|
|
<span>{t('form.submitting')}</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Send className="w-5 h-5" />
|
|
<span>{t('form.submit')}</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
</form>
|
|
)}
|
|
</motion.div>
|
|
|
|
{/* Offices */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: 50 }}
|
|
animate={isFormInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.8, delay: 0.2 }}
|
|
>
|
|
<h2 className="text-3xl font-bold text-brand-navy mb-6">{t('office.title')}</h2>
|
|
<p className="text-gray-600 mb-8">
|
|
{t('office.subtitle')}
|
|
</p>
|
|
|
|
<div className="space-y-6">
|
|
<div className="bg-white p-6 rounded-2xl border-2 border-brand-turquoise shadow-lg">
|
|
<div className="flex items-start space-x-4">
|
|
<div className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0 bg-brand-turquoise">
|
|
<Building2 className="w-6 h-6 text-white" />
|
|
</div>
|
|
<div className="flex-1">
|
|
<div className="flex items-center space-x-2 mb-2">
|
|
<h3 className="text-xl font-bold text-brand-navy">{t('office.city')}</h3>
|
|
<span className="px-2 py-1 bg-brand-turquoise/10 text-brand-turquoise text-xs font-medium rounded-full">
|
|
{t('office.hqBadge')}
|
|
</span>
|
|
</div>
|
|
<div className="space-y-2 text-gray-600">
|
|
<div className="flex items-center space-x-2">
|
|
<MapPin className="w-4 h-4 text-gray-400" />
|
|
<span>{t('office.address')}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<span className="text-gray-400 ml-6">{t('office.postalCode')}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<Phone className="w-4 h-4 text-gray-400" />
|
|
<a href={`tel:${t('office.phone').replace(/\s/g, '')}`} className="hover:text-brand-turquoise">
|
|
{t('office.phone')}
|
|
</a>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<Mail className="w-4 h-4 text-gray-400" />
|
|
<a href={`mailto:${t('office.email')}`} className="hover:text-brand-turquoise">
|
|
{t('office.email')}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Hours */}
|
|
<div className="mt-8 bg-gray-50 p-6 rounded-2xl">
|
|
<div className="flex items-center space-x-3 mb-4">
|
|
<Clock className="w-6 h-6 text-brand-turquoise" />
|
|
<h3 className="text-lg font-bold text-brand-navy">{t('hours.title')}</h3>
|
|
</div>
|
|
<div className="space-y-2 text-gray-600">
|
|
<div className="flex justify-between">
|
|
<span>{t('hours.weekdays')}</span>
|
|
<span className="font-medium">{t('hours.weekdaysHours')}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>{t('hours.saturday')}</span>
|
|
<span className="font-medium">{t('hours.saturdayHours')}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>{t('hours.sunday')}</span>
|
|
<span className="font-medium text-gray-400">{t('hours.closed')}</span>
|
|
</div>
|
|
</div>
|
|
<p className="mt-4 text-sm text-gray-500">
|
|
{t('hours.supportNote')}
|
|
</p>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Section 1 : After submit */}
|
|
<section ref={afterSubmitRef} className="py-16 bg-white">
|
|
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 40 }}
|
|
animate={isAfterSubmitInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.8 }}
|
|
className="relative bg-gradient-to-br from-brand-navy to-brand-navy/90 rounded-3xl overflow-hidden p-8 lg:p-12"
|
|
>
|
|
<div className="absolute inset-0 opacity-10 pointer-events-none">
|
|
<div className="absolute -top-10 -left-10 w-64 h-64 bg-brand-turquoise rounded-full blur-3xl" />
|
|
<div className="absolute -bottom-10 -right-10 w-64 h-64 bg-brand-green rounded-full blur-3xl" />
|
|
</div>
|
|
|
|
<div className="relative z-10">
|
|
<div className="flex items-center space-x-3 mb-2">
|
|
<div className="p-2 bg-brand-turquoise/20 rounded-lg">
|
|
<Mail className="w-5 h-5 text-brand-turquoise" />
|
|
</div>
|
|
<span className="text-brand-turquoise font-semibold uppercase tracking-widest text-xs">
|
|
{t('afterSubmit.badge')}
|
|
</span>
|
|
</div>
|
|
<h2 className="text-2xl lg:text-3xl font-bold text-white mb-8">
|
|
{t('afterSubmit.title')}
|
|
</h2>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{/* Commitment */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={isAfterSubmitInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.2 }}
|
|
className="bg-white/10 backdrop-blur-sm rounded-2xl p-6 border border-white/20"
|
|
>
|
|
<div className="flex items-center space-x-3 mb-4">
|
|
<div className="w-10 h-10 bg-brand-turquoise/30 rounded-xl flex items-center justify-center flex-shrink-0">
|
|
<CheckCircle2 className="w-5 h-5 text-brand-turquoise" />
|
|
</div>
|
|
<h3 className="text-lg font-bold text-white">{t('afterSubmit.commitmentTitle')}</h3>
|
|
</div>
|
|
<p className="text-white/80 leading-relaxed">
|
|
{t('afterSubmit.commitmentBody1')}
|
|
<span className="text-brand-turquoise font-semibold">
|
|
{t('afterSubmit.commitmentHighlight')}
|
|
</span>
|
|
</p>
|
|
</motion.div>
|
|
|
|
{/* Security */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={isAfterSubmitInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.35 }}
|
|
className="bg-white/10 backdrop-blur-sm rounded-2xl p-6 border border-white/20"
|
|
>
|
|
<div className="flex items-center space-x-3 mb-4">
|
|
<div className="w-10 h-10 bg-brand-green/30 rounded-xl flex items-center justify-center flex-shrink-0">
|
|
<Shield className="w-5 h-5 text-brand-green" />
|
|
</div>
|
|
<h3 className="text-lg font-bold text-white">{t('afterSubmit.securityTitle')}</h3>
|
|
</div>
|
|
<p className="text-white/80 leading-relaxed">
|
|
{t('afterSubmit.securityBody1')}
|
|
<Link href="/privacy" className="text-brand-turquoise font-semibold hover:underline">
|
|
{t('afterSubmit.privacyLink')}
|
|
</Link>
|
|
{t('afterSubmit.securityBody2')}
|
|
</p>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Section 2: Quick access */}
|
|
<section ref={quickAccessRef} className="py-16 bg-gray-50">
|
|
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={isQuickAccessInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.7 }}
|
|
>
|
|
<div className="text-center mb-10">
|
|
<span className="text-brand-turquoise font-semibold uppercase tracking-widest text-xs">
|
|
{t('quickAccess.badge')}
|
|
</span>
|
|
<h2 className="text-2xl lg:text-3xl font-bold text-brand-navy mt-2">
|
|
{t('quickAccess.title')}
|
|
</h2>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl mx-auto">
|
|
{/* Instant pricing */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: -30 }}
|
|
animate={isQuickAccessInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.15 }}
|
|
whileHover={{ y: -4 }}
|
|
className="bg-white rounded-2xl shadow-lg border border-gray-100 p-8 flex flex-col"
|
|
>
|
|
<div className="w-14 h-14 bg-gradient-to-br from-brand-turquoise to-cyan-400 rounded-2xl flex items-center justify-center mb-6 flex-shrink-0">
|
|
<Zap className="w-7 h-7 text-white" />
|
|
</div>
|
|
<h3 className="text-xl font-bold text-brand-navy mb-3">{t('quickAccess.pricingTitle')}</h3>
|
|
<p className="text-gray-600 leading-relaxed flex-1 mb-6">
|
|
{t('quickAccess.pricingBody1')}
|
|
<span className="font-semibold text-brand-navy">{t('quickAccess.pricingHighlight')}</span>
|
|
{t('quickAccess.pricingBody2')}
|
|
</p>
|
|
<Link
|
|
href="/dashboard"
|
|
className="inline-flex items-center justify-center space-x-2 px-6 py-3 bg-brand-turquoise text-white rounded-xl font-semibold hover:bg-brand-turquoise/90 transition-all group"
|
|
>
|
|
<span>{t('quickAccess.pricingCta')}</span>
|
|
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
|
</Link>
|
|
</motion.div>
|
|
|
|
{/* Wiki */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: 30 }}
|
|
animate={isQuickAccessInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.25 }}
|
|
whileHover={{ y: -4 }}
|
|
className="bg-white rounded-2xl shadow-lg border border-gray-100 p-8 flex flex-col"
|
|
>
|
|
<div className="w-14 h-14 bg-gradient-to-br from-brand-navy to-brand-navy/80 rounded-2xl flex items-center justify-center mb-6 flex-shrink-0">
|
|
<BookOpen className="w-7 h-7 text-brand-turquoise" />
|
|
</div>
|
|
<h3 className="text-xl font-bold text-brand-navy mb-3">{t('quickAccess.wikiTitle')}</h3>
|
|
<p className="text-gray-600 leading-relaxed flex-1 mb-6">
|
|
{t('quickAccess.wikiBody1')}
|
|
<span className="font-semibold text-brand-navy">{t('quickAccess.wikiHighlight')}</span>
|
|
{t('quickAccess.wikiBody2')}
|
|
</p>
|
|
<Link
|
|
href="/dashboard/wiki"
|
|
className="inline-flex items-center justify-center space-x-2 px-6 py-3 border-2 border-brand-navy text-brand-navy rounded-xl font-semibold hover:bg-brand-navy hover:text-white transition-all group"
|
|
>
|
|
<span>{t('quickAccess.wikiCta')}</span>
|
|
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
|
</Link>
|
|
</motion.div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
|
|
<LandingFooter />
|
|
</div>
|
|
);
|
|
}
|