update: corrected version
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import React from 'react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import { ThemeProvider } from '@/components/ThemeContext';
|
||||
|
||||
// Core Components
|
||||
import Header from '@/components/Header';
|
||||
import Footer from '@/components/Footer';
|
||||
import ScrollToTop from '@/components/ScrollToTop';
|
||||
import ProtectedRoute from '@/components/ProtectedRoute';
|
||||
import { Toaster } from '@/components/ui/toaster';
|
||||
|
||||
// Public Pages
|
||||
import Home from '@/pages/Home';
|
||||
import AboutPage from '@/pages/AboutPage';
|
||||
import OurTeamPage from '@/pages/OurTeamPage';
|
||||
import ServicesPage from '@/pages/ServicesPage';
|
||||
import ServicesHubPage from '@/pages/ServicesHubPage';
|
||||
import PaidMediaPage from '@/pages/PaidMediaPage';
|
||||
import SeoPage from '@/pages/SeoPage';
|
||||
import LlmoPage from '@/pages/LlmoPage';
|
||||
import WebDevPage from '@/pages/WebDevPage';
|
||||
import SocialMediaPage from '@/pages/SocialMediaPage';
|
||||
import BrandPage from '@/pages/BrandPage';
|
||||
import ConsultingPage from '@/pages/ConsultingPage';
|
||||
import CybersecurityPage from '@/pages/CybersecurityPage';
|
||||
import ServiceDetail from '@/pages/ServiceDetail';
|
||||
import IndustriesPage from '@/pages/IndustriesPage';
|
||||
import IndustryDetailPage from '@/pages/IndustryDetailPage';
|
||||
import ContactPage from '@/pages/ContactPage';
|
||||
|
||||
// Auth Pages
|
||||
import LoginPage from '@/pages/LoginPage';
|
||||
import ForgotPasswordPage from '@/pages/ForgotPasswordPage';
|
||||
import NewClientForm from '@/pages/NewClientForm';
|
||||
|
||||
// Portal Pages
|
||||
import PortalLayout from '@/pages/portal/PortalLayout';
|
||||
import DashboardOverview from '@/pages/portal/DashboardOverview';
|
||||
import GA4Dashboard from '@/pages/portal/GA4Dashboard';
|
||||
import GSCDashboard from '@/pages/portal/GSCDashboard';
|
||||
import GoogleAdsDashboard from '@/pages/portal/GoogleAdsDashboard';
|
||||
import MetaBusinessDashboard from '@/pages/portal/MetaBusinessDashboard';
|
||||
import SocialMediaDashboard from '@/pages/portal/SocialMediaDashboard';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<div className="min-h-screen bg-background text-foreground flex flex-col font-sans">
|
||||
<ScrollToTop />
|
||||
<Header />
|
||||
<main className="flex-grow">
|
||||
<Routes>
|
||||
{/* Public Routes */}
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<AboutPage />} />
|
||||
<Route path="/our-team" element={<OurTeamPage />} />
|
||||
|
||||
{/* Services Routes */}
|
||||
<Route path="/services" element={<ServicesHubPage />} />
|
||||
<Route path="/services/paid-media" element={<PaidMediaPage />} />
|
||||
<Route path="/services/seo" element={<SeoPage />} />
|
||||
<Route path="/services/llmo" element={<LlmoPage />} />
|
||||
<Route path="/services/web-development" element={<WebDevPage />} />
|
||||
<Route path="/services/social-media" element={<SocialMediaPage />} />
|
||||
<Route path="/services/brand" element={<BrandPage />} />
|
||||
<Route path="/services/consulting" element={<ConsultingPage />} />
|
||||
<Route path="/services/cybersecurity" element={<CybersecurityPage />} />
|
||||
<Route path="/services/all" element={<ServicesPage />} />
|
||||
<Route path="/services/:slug" element={<ServiceDetail />} />
|
||||
|
||||
{/* Industries Routes */}
|
||||
<Route path="/industries" element={<IndustriesPage />} />
|
||||
<Route path="/industries/:slug" element={<IndustryDetailPage />} />
|
||||
|
||||
{/* Contact & Auth Routes */}
|
||||
<Route path="/contact" element={<ContactPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
||||
<Route path="/new-client" element={<NewClientForm />} />
|
||||
|
||||
{/* Client Portal Routes (Protected) */}
|
||||
<Route
|
||||
path="/portal"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<PortalLayout />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
>
|
||||
<Route index element={<DashboardOverview />} />
|
||||
<Route path="analytics" element={<GA4Dashboard />} />
|
||||
<Route path="search-console" element={<GSCDashboard />} />
|
||||
<Route path="google-ads" element={<GoogleAdsDashboard />} />
|
||||
<Route path="meta-business" element={<MetaBusinessDashboard />} />
|
||||
<Route path="social-media" element={<SocialMediaDashboard />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</main>
|
||||
<Footer />
|
||||
<Toaster />
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,39 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { motion, useInView, useSpring, useReducedMotion } from 'framer-motion';
|
||||
|
||||
const AnimatedCounter = ({ from = 0, to, duration = 2, className = "", prefix = "", suffix = "" }) => {
|
||||
const ref = useRef(null);
|
||||
const inView = useInView(ref, { once: true, margin: "-10%" });
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
const [displayValue, setDisplayValue] = useState(shouldReduceMotion ? to : from);
|
||||
|
||||
const springValue = useSpring(from, {
|
||||
damping: 50,
|
||||
stiffness: 100,
|
||||
duration: duration * 1000
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldReduceMotion) return;
|
||||
|
||||
if (inView) {
|
||||
springValue.set(to);
|
||||
}
|
||||
}, [inView, to, springValue, shouldReduceMotion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldReduceMotion) return;
|
||||
|
||||
return springValue.on("change", (latest) => {
|
||||
setDisplayValue(Math.floor(latest));
|
||||
});
|
||||
}, [springValue, shouldReduceMotion]);
|
||||
|
||||
return (
|
||||
<span ref={ref} className={className}>
|
||||
{prefix}{displayValue}{suffix}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnimatedCounter;
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ImageOff } from 'lucide-react';
|
||||
|
||||
const BrandedImage = ({
|
||||
src,
|
||||
alt,
|
||||
className = '',
|
||||
imgClassName = '',
|
||||
overlayOpacity = 0.4,
|
||||
lazy = true
|
||||
}) => {
|
||||
const [error, setError] = useState(false);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
if (error || !src) {
|
||||
return (
|
||||
<div className={`relative flex items-center justify-center bg-secondary text-muted-foreground overflow-hidden ${className}`}>
|
||||
<ImageOff className="w-12 h-12 opacity-20" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`relative overflow-hidden group ${className}`}>
|
||||
<motion.img
|
||||
initial={{ scale: 1.05, filter: 'blur(10px)' }}
|
||||
animate={loaded ? { scale: 1, filter: 'blur(0px)' } : {}}
|
||||
transition={{ duration: 1.2, ease: "easeOut" }}
|
||||
src={src}
|
||||
alt={alt}
|
||||
loading={lazy ? "lazy" : "eager"}
|
||||
onLoad={() => setLoaded(true)}
|
||||
onError={() => setError(true)}
|
||||
className={`w-full h-full object-cover parallax-slow ${imgClassName}`}
|
||||
/>
|
||||
{/* Premium Red Duotone/Overlay Effect */}
|
||||
<div
|
||||
className="absolute inset-0 bg-primary mix-blend-multiply transition-opacity duration-500 group-hover:opacity-80"
|
||||
style={{ opacity: overlayOpacity }}
|
||||
/>
|
||||
{/* Subtle vignette for depth */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-black/20 pointer-events-none" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrandedImage;
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { ChevronRight, Home } from 'lucide-react';
|
||||
|
||||
const Breadcrumbs = () => {
|
||||
const location = useLocation();
|
||||
const pathnames = location.pathname.split('/').filter((x) => x);
|
||||
|
||||
// Custom formatting for breadcrumb labels
|
||||
const formatLabel = (path) => {
|
||||
const specialCases = {
|
||||
'paid-media': 'Paid Media',
|
||||
'seo': 'SEO',
|
||||
'llmo': 'LLMO/AEO/GEO',
|
||||
'web-development': 'Web Development',
|
||||
'social-media': 'Social Media',
|
||||
'brand': 'Brand',
|
||||
'consulting': 'Consulting',
|
||||
'cybersecurity': 'Cybersecurity'
|
||||
};
|
||||
if (specialCases[path]) return specialCases[path];
|
||||
|
||||
return path.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className="flex items-center space-x-1 text-sm text-muted-foreground mb-8">
|
||||
<Link to="/" className="hover:text-primary transition-colors flex items-center">
|
||||
<Home className="h-4 w-4" />
|
||||
<span className="sr-only">Home</span>
|
||||
</Link>
|
||||
|
||||
{pathnames.map((value, index) => {
|
||||
const to = `/${pathnames.slice(0, index + 1).join('/')}`;
|
||||
const isLast = index === pathnames.length - 1;
|
||||
|
||||
return (
|
||||
<React.Fragment key={to}>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
{isLast ? (
|
||||
<span className="text-foreground font-medium" aria-current="page">
|
||||
{formatLabel(value)}
|
||||
</span>
|
||||
) : (
|
||||
<Link to={to} className="hover:text-primary transition-colors">
|
||||
{formatLabel(value)}
|
||||
</Link>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Breadcrumbs;
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { CheckCircle2 } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
|
||||
const CapabilitiesTable = ({ title, capabilities }) => {
|
||||
return (
|
||||
<section className="service-section bg-secondary/30">
|
||||
<div className="mb-12 text-center md:text-left">
|
||||
<h2 className="service-heading">{title}</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-6 mx-auto md:mx-0"></div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{capabilities.map((capability, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1, duration: 0.5 }}
|
||||
>
|
||||
<Card className="h-full bg-card border-border hover:border-primary/50 transition-colors">
|
||||
<CardHeader className="pb-3 flex flex-row items-center gap-4 space-y-0">
|
||||
<CheckCircle2 className="w-6 h-6 text-primary flex-shrink-0" />
|
||||
<CardTitle className="text-lg text-heading">{capability.title || capability}</CardTitle>
|
||||
</CardHeader>
|
||||
{capability.description && (
|
||||
<CardContent>
|
||||
<p className="text-muted text-sm leading-relaxed">
|
||||
{capability.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CapabilitiesTable;
|
||||
@@ -0,0 +1,222 @@
|
||||
import React, { useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { Phone, Loader2 } from 'lucide-react';
|
||||
|
||||
const ContactForm = () => {
|
||||
const { toast } = useToast();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
fullName: '',
|
||||
companyName: '',
|
||||
phoneNumber: '',
|
||||
email: '',
|
||||
hqAddress: '',
|
||||
services: [],
|
||||
hasAgency: '',
|
||||
});
|
||||
|
||||
const dubaiPhoneNumber = "+971 55 647 0342";
|
||||
|
||||
const servicesList = [
|
||||
'Web Development',
|
||||
'SEO',
|
||||
'LLMO & GEO',
|
||||
'Social Media Management',
|
||||
'Branding',
|
||||
'Paid Advertising',
|
||||
];
|
||||
|
||||
const handleServiceChange = (service) => {
|
||||
setFormData((prev) => {
|
||||
const services = prev.services.includes(service)
|
||||
? prev.services.filter((s) => s !== service)
|
||||
: [...prev.services, service];
|
||||
return { ...prev, services };
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const requiredFields = [
|
||||
formData.fullName,
|
||||
formData.companyName,
|
||||
formData.phoneNumber,
|
||||
formData.email,
|
||||
formData.hqAddress,
|
||||
formData.hasAgency
|
||||
];
|
||||
|
||||
if (requiredFields.some(field => !field) || formData.services.length === 0) {
|
||||
toast({
|
||||
title: "Missing Information",
|
||||
description: "Please fill out all required fields marked with *",
|
||||
variant: "destructive"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
await new Promise(r => setTimeout(r, 1500));
|
||||
|
||||
toast({
|
||||
title: "Your message has been sent successfully! 🚀",
|
||||
description: "We'll analyze your requirements and get back to you shortly.",
|
||||
duration: 5000,
|
||||
className: "bg-green-600 border-green-700 text-white",
|
||||
});
|
||||
|
||||
setFormData({
|
||||
fullName: '', companyName: '', phoneNumber: '', email: '', hqAddress: '', services: [], hasAgency: '',
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: "Failed to send message",
|
||||
description: "Please try again later or contact us directly via phone.",
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="contact-form" className="py-20 bg-background relative">
|
||||
<div className="container mx-auto px-4 max-w-3xl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-card border border-border rounded-2xl p-8 md:p-12 shadow-2xl backdrop-blur-sm"
|
||||
>
|
||||
<div className="text-center mb-10">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">Start Your Growth Journey</h2>
|
||||
<p className="text-muted-foreground">Tell us about your goals and we'll help you achieve them.</p>
|
||||
<div className="mt-6 flex items-center justify-center text-primary">
|
||||
<Phone className="mr-2" size={20} />
|
||||
<a href={`tel:${dubaiPhoneNumber.replace(/[\s-]/g, '')}`} className="text-lg font-semibold hover:underline">
|
||||
{dubaiPhoneNumber}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-8">
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="fullName" className="text-foreground">Full Name <span className="text-red-500">*</span></Label>
|
||||
<Input
|
||||
id="fullName" required disabled={isSubmitting}
|
||||
value={formData.fullName} onChange={(e) => setFormData({ ...formData, fullName: e.target.value })}
|
||||
className="bg-background border-border text-foreground focus:ring-primary disabled:opacity-50"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="companyName" className="text-foreground">Company Name <span className="text-red-500">*</span></Label>
|
||||
<Input
|
||||
id="companyName" required disabled={isSubmitting}
|
||||
value={formData.companyName} onChange={(e) => setFormData({ ...formData, companyName: e.target.value })}
|
||||
className="bg-background border-border text-foreground focus:ring-primary disabled:opacity-50"
|
||||
placeholder="Acme Corp"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="phoneNumber" className="text-foreground">Phone Number <span className="text-red-500">*</span></Label>
|
||||
<Input
|
||||
id="phoneNumber" required type="tel" disabled={isSubmitting}
|
||||
value={formData.phoneNumber} onChange={(e) => setFormData({ ...formData, phoneNumber: e.target.value })}
|
||||
className="bg-background border-border text-foreground focus:ring-primary disabled:opacity-50"
|
||||
placeholder="+1 (555) 000-0000"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="text-foreground">Email Address <span className="text-red-500">*</span></Label>
|
||||
<Input
|
||||
id="email" required type="email" disabled={isSubmitting}
|
||||
value={formData.email} onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
className="bg-background border-border text-foreground focus:ring-primary disabled:opacity-50"
|
||||
placeholder="your@email.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="hqAddress" className="text-foreground">HQ Address <span className="text-red-500">*</span></Label>
|
||||
<Input
|
||||
id="hqAddress" required disabled={isSubmitting}
|
||||
value={formData.hqAddress} onChange={(e) => setFormData({ ...formData, hqAddress: e.target.value })}
|
||||
className="bg-background border-border text-foreground focus:ring-primary disabled:opacity-50"
|
||||
placeholder="123 Business Ave, Suite 100, City, Country"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Label className="text-foreground block text-base">Services Needed (Select all that apply) <span className="text-red-500">*</span></Label>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{servicesList.map((service) => (
|
||||
<div key={service} className="flex items-center space-x-3 p-3 rounded-lg border border-border bg-secondary hover:border-primary/50 transition-colors">
|
||||
<Checkbox
|
||||
id={service} disabled={isSubmitting}
|
||||
checked={formData.services.includes(service)}
|
||||
onCheckedChange={() => handleServiceChange(service)}
|
||||
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary disabled:opacity-50"
|
||||
/>
|
||||
<Label htmlFor={service} className="text-foreground cursor-pointer font-normal">
|
||||
{service}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Label className="text-foreground block text-base">Currently working with another agency? <span className="text-red-500">*</span></Label>
|
||||
<RadioGroup
|
||||
value={formData.hasAgency}
|
||||
onValueChange={(val) => setFormData({ ...formData, hasAgency: val })}
|
||||
className="flex flex-col space-y-2" required disabled={isSubmitting}
|
||||
>
|
||||
<div className="flex items-center space-x-3 p-3 rounded-lg border border-border bg-secondary">
|
||||
<RadioGroupItem value="yes" id="agency-yes" className="border-primary text-primary disabled:opacity-50" />
|
||||
<Label htmlFor="agency-yes" className="text-foreground cursor-pointer">Yes</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 p-3 rounded-lg border border-border bg-secondary">
|
||||
<RadioGroupItem value="no" id="agency-no" className="border-primary text-primary disabled:opacity-50" />
|
||||
<Label htmlFor="agency-no" className="text-foreground cursor-pointer">No</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit" disabled={isSubmitting}
|
||||
className="w-full bg-primary hover:bg-primary/90 text-primary-foreground py-6 text-lg rounded-full shadow-lg shadow-primary/25 disabled:opacity-70 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Sending Message...
|
||||
</>
|
||||
) : (
|
||||
'Submit Request'
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactForm;
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const FAQSection = ({ faqs }) => {
|
||||
return (
|
||||
<section className="service-section max-w-4xl mx-auto">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="service-heading">Frequently Asked Questions</h2>
|
||||
<div className="w-20 h-1 bg-primary mx-auto mb-6"></div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
{faqs.map((faq, index) => (
|
||||
<AccordionItem key={index} value={`item-${index}`} className="border-border">
|
||||
<AccordionTrigger className="text-left text-lg text-heading hover:text-primary font-medium px-4">
|
||||
{faq.question}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="text-muted px-4 leading-relaxed text-base">
|
||||
{faq.answer}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default FAQSection;
|
||||
@@ -0,0 +1,143 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Mail, Phone, MapPin, Linkedin, Facebook, Instagram } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ScrollReveal from './ScrollReveal';
|
||||
|
||||
const Footer = () => {
|
||||
const socialLinks = [
|
||||
{ icon: Instagram, url: 'https://www.instagram.com/aigrowthmediaservice/', label: 'Instagram' },
|
||||
{ icon: Facebook, url: 'https://www.facebook.com/growth360partners', label: 'Facebook' },
|
||||
{ icon: Linkedin, url: 'https://www.linkedin.com/company/growth360partners', label: 'LinkedIn' }
|
||||
];
|
||||
|
||||
const quickLinks = [
|
||||
{ label: 'About Us', href: '/about' },
|
||||
{ label: 'Our Team', href: '/our-team' },
|
||||
{ label: 'Portfolio', href: '/portfolio' },
|
||||
{ label: 'Contact', href: '/contact' }
|
||||
];
|
||||
|
||||
const servicesLinks = [
|
||||
{ label: 'Services Hub', href: '/services' },
|
||||
{ label: 'Paid Media', href: '/services/paid-media' },
|
||||
{ label: 'SEO', href: '/services/seo' },
|
||||
{ label: 'LLMO/AEO/GEO', href: '/services/llmo' },
|
||||
{ label: 'Web Development', href: '/services/web-development' },
|
||||
{ label: 'Social Media', href: '/services/social-media' },
|
||||
{ label: 'Brand Creation', href: '/services/brand' },
|
||||
{ label: 'Cybersecurity', href: '/services/cybersecurity' }
|
||||
];
|
||||
|
||||
return (
|
||||
<footer className="bg-black border-t border-white/10 relative overflow-hidden text-white pt-20 pb-10">
|
||||
{/* Subtle Background Glows */}
|
||||
<div className="absolute top-0 right-0 w-[700px] h-[700px] glow-red rounded-full filter blur-[120px] -translate-y-1/2 translate-x-1/3 pointer-events-none" />
|
||||
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] glow-blue rounded-full filter blur-[100px] translate-y-1/3 -translate-x-1/3 pointer-events-none" />
|
||||
|
||||
<div className="container mx-auto px-4 max-w-7xl relative z-10">
|
||||
<ScrollReveal className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-12 gap-10 lg:gap-8 mb-14">
|
||||
|
||||
{/* Company Info */}
|
||||
<div className="flex flex-col lg:col-span-4 pr-4">
|
||||
<Link to="/" className="mb-6 block">
|
||||
<img src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/35787cd5f7025697b247b5601494d350.png" alt="AIGROWTH Media Service logo" className="h-14 md:h-16 w-auto object-contain" />
|
||||
</Link>
|
||||
<p className="text-white/70 mb-8 leading-relaxed text-sm md:text-base pr-8">
|
||||
Your growth engine for digital success. We combine strategy, technology, and creativity to deliver results that compound over time.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-3">
|
||||
{socialLinks.map((social, index) => (
|
||||
<motion.a
|
||||
key={index} href={social.url} aria-label={social.label}
|
||||
whileHover={{ scale: 1.1, y: -2 }}
|
||||
className="w-10 h-10 rounded-full bg-white/10 border border-white/15 flex items-center justify-center text-white/70 hover:bg-primary hover:text-white hover:border-primary transition-all duration-300"
|
||||
>
|
||||
<social.icon size={18} />
|
||||
</motion.a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<div className="lg:col-span-2">
|
||||
<h4 className="text-white font-bold text-base mb-5 tracking-tight">Company</h4>
|
||||
<nav className="flex flex-col gap-3">
|
||||
{quickLinks.map((link, index) => (
|
||||
<Link key={index} to={link.href} className="text-white/70 hover:text-primary transition-colors duration-300 text-sm">
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-3">
|
||||
<h4 className="text-white font-bold text-base mb-5 tracking-tight">Capabilities</h4>
|
||||
<nav className="flex flex-col gap-3">
|
||||
{servicesLinks.map((link, index) => (
|
||||
<Link key={index} to={link.href} className="text-white/70 hover:text-primary transition-colors duration-300 text-sm">
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="lg:col-span-3">
|
||||
<h4 className="text-white font-bold text-base mb-5 tracking-tight">Contact</h4>
|
||||
<div className="flex flex-col gap-5 text-sm text-white/70">
|
||||
<div className="flex items-start gap-3 group">
|
||||
<MapPin size={18} className="mt-0.5 flex-shrink-0 text-primary group-hover:scale-110 transition-transform" />
|
||||
<p>Business Bay, Dubai, UAE<br/>(Headquarters)</p>
|
||||
</div>
|
||||
<div className="flex items-start gap-3 group">
|
||||
<Phone size={18} className="mt-0.5 flex-shrink-0 text-primary group-hover:scale-110 transition-transform" />
|
||||
<div className="flex flex-col gap-2">
|
||||
<div>
|
||||
<span className="text-[10px] uppercase tracking-widest text-white/45 font-bold block mb-1">Dubai</span>
|
||||
<a href="tel:+971556470342" className="hover:text-primary transition-colors">+971 55 647 0342</a>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-[10px] uppercase tracking-widest text-white/45 font-bold block mb-1 mt-2">USA Support</span>
|
||||
<a href="tel:+18323500465" className="block hover:text-primary transition-colors mb-1">+1 832-350-0465</a>
|
||||
<a href="tel:+19542354974" className="block hover:text-primary transition-colors mb-1">+1 954-235-4974</a>
|
||||
<a href="tel:+17132894577" className="block hover:text-primary transition-colors">+1 713-289-4577</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 group">
|
||||
<Mail size={18} className="flex-shrink-0 text-primary group-hover:scale-110 transition-transform" />
|
||||
<a href="mailto:info@aigrowthtechnologies.com" className="hover:text-primary transition-colors truncate">
|
||||
info@aigrowthtechnologies.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Partners & Bottom Bar */}
|
||||
<ScrollReveal delay={0.2}>
|
||||
<div className="border-t border-white/10 pt-8 flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div className="flex items-center gap-5 opacity-60 hover:opacity-100 transition-all duration-500">
|
||||
<span className="text-[10px] font-black tracking-widest uppercase text-white/50">Powered By</span>
|
||||
<img src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/a3b73073fbeb071d0e115b026561212b.png" alt="meSEO" className="h-4" />
|
||||
<img src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/fb9e42499289adaa8ac7e4f3fb56d72f.png" alt="Korrith" className="h-7" />
|
||||
<img src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/9d06cdfd1608625e82935667ceb9db7d.png" alt="Atrilyx" className="h-5" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row items-center gap-4 text-xs text-white/60">
|
||||
<p>© {new Date().getFullYear()} AI Growth Technologies. All rights reserved.</p>
|
||||
<div className="flex gap-4">
|
||||
<a href="#" className="hover:text-primary transition-colors">Privacy</a>
|
||||
<a href="#" className="hover:text-primary transition-colors">Terms</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -0,0 +1,205 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Menu, X, ChevronDown, ArrowRight } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
const Header = () => {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const [isAboutOpen, setIsAboutOpen] = useState(false);
|
||||
const [isServicesOpen, setIsServicesOpen] = useState(false);
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const dropdownRef = useRef(null);
|
||||
const servicesDropdownRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => setIsScrolled(window.scrollY > 20);
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) setIsAboutOpen(false);
|
||||
if (servicesDropdownRef.current && !servicesDropdownRef.current.contains(event.target)) setIsServicesOpen(false);
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setIsAboutOpen(false);
|
||||
setIsServicesOpen(false);
|
||||
setIsMobileMenuOpen(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
const isActive = (path) => {
|
||||
if (path === '/' && location.pathname !== '/') return false;
|
||||
return location.pathname === path || (path !== '/' && location.pathname.startsWith(path));
|
||||
};
|
||||
|
||||
const isServiceActive = () => location.pathname.startsWith('/services');
|
||||
|
||||
const navLinkClass = (active) =>
|
||||
`transition-colors duration-300 font-semibold text-sm 2xl:text-base ${active ? 'text-primary' : 'text-white/75 hover:text-white'}`;
|
||||
|
||||
return (
|
||||
<motion.header
|
||||
id="site-header"
|
||||
initial={{ y: -100 }}
|
||||
animate={{ y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-500 bg-black text-white ${
|
||||
isScrolled ? 'shadow-[0_8px_30px_rgba(0,0,0,0.24)] border-b border-white/10' : 'border-b border-transparent'
|
||||
}`}
|
||||
>
|
||||
<nav className="container mx-auto px-4 max-w-7xl">
|
||||
<div className="flex items-center justify-between h-16 md:h-20">
|
||||
{/* Logo Group */}
|
||||
<div className="flex items-center gap-4 shrink-0">
|
||||
<motion.div whileHover={{ scale: 1.02 }} transition={{ type: 'spring', stiffness: 400 }}>
|
||||
<Link to="/" className="flex items-center shrink-0 rounded-lg transition-colors">
|
||||
<img
|
||||
src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/5d6f7e4aab2acc9623d351a632c92129.png"
|
||||
alt="AIGROWTH Media Service logo"
|
||||
className="h-12 md:h-16 w-auto object-contain"
|
||||
/>
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
||||
{/* POWERED BY — partner logos remain visible on wide desktop navigation */}
|
||||
<div className="hidden xl:flex items-center gap-5 border-l border-white/15 pl-5">
|
||||
<span className="text-[10px] font-black text-white/50 tracking-widest uppercase">Powered By</span>
|
||||
<img src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/a3b73073fbeb071d0e115b026561212b.png" alt="meSEO Logo" className="h-6 w-auto object-contain opacity-70 hover:opacity-100 transition-opacity duration-300" />
|
||||
<img src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/fb9e42499289adaa8ac7e4f3fb56d72f.png" alt="Korrith Logo" className="h-10 w-auto object-contain hover:scale-105 transition-transform duration-300 opacity-90 hover:opacity-100" />
|
||||
<img src="https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/9d06cdfd1608625e82935667ceb9db7d.png" alt="Atrilyx Logo" className="h-7 w-auto object-contain hover:scale-105 transition-transform duration-300 opacity-70 hover:opacity-100" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation — visible from lg up (fixes lg→xl gap) */}
|
||||
<div className="hidden lg:flex items-center gap-5 xl:gap-7 shrink-0">
|
||||
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.1 }} className="relative" ref={servicesDropdownRef}>
|
||||
<button
|
||||
onClick={() => { setIsServicesOpen(!isServicesOpen); setIsAboutOpen(false); }}
|
||||
className={`flex items-center gap-1 ${navLinkClass(isServiceActive() || isServicesOpen)}`}
|
||||
>
|
||||
Services
|
||||
<ChevronDown className={`w-4 h-4 transition-transform duration-300 ${isServicesOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
|
||||
<AnimatePresence>
|
||||
{isServicesOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95 }} animate={{ opacity: 1, y: 0, scale: 1 }} exit={{ opacity: 0, y: 10, scale: 0.95 }} transition={{ duration: 0.2 }}
|
||||
className="absolute top-full left-1/2 -translate-x-1/2 mt-4 w-[440px] max-w-[calc(100vw-2rem)] bg-white border border-border rounded-2xl shadow-2xl shadow-black/10 overflow-hidden"
|
||||
>
|
||||
<div className="p-4 flex flex-col">
|
||||
<Link to="/services" className={`px-4 py-3 rounded-lg text-sm font-bold transition-all mb-2 ${isActive('/services') && location.pathname === '/services' ? 'bg-primary/10 text-primary' : 'text-foreground hover:bg-secondary hover:text-foreground'}`}>
|
||||
Services Hub Directory
|
||||
</Link>
|
||||
<div className="h-px bg-border mb-3 mx-2" />
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Link to="/services/paid-media" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Paid Media</Link>
|
||||
<Link to="/services/seo" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">SEO</Link>
|
||||
<Link to="/services/llmo" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">LLMO / AEO / GEO</Link>
|
||||
<Link to="/services/web-development" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Web Development</Link>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Link to="/services/social-media" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Social Media</Link>
|
||||
<Link to="/services/brand" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Brand Creation</Link>
|
||||
<Link to="/services/cybersecurity" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Cybersecurity</Link>
|
||||
<Link to="/services/consulting" className="px-4 py-2 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Consulting</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
|
||||
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 }}>
|
||||
<Link to="/industries" className={navLinkClass(isActive('/industries'))}>Industries</Link>
|
||||
</motion.div>
|
||||
|
||||
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.2 }}>
|
||||
<Link to="/portfolio" className={navLinkClass(isActive('/portfolio'))}>Portfolio</Link>
|
||||
</motion.div>
|
||||
|
||||
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.3 }} className="relative" ref={dropdownRef}>
|
||||
<button
|
||||
onClick={() => { setIsAboutOpen(!isAboutOpen); setIsServicesOpen(false); }}
|
||||
className={`flex items-center gap-1 ${navLinkClass(isActive('/about') || isActive('/our-team') || isAboutOpen)}`}
|
||||
>
|
||||
About Us
|
||||
<ChevronDown className={`w-4 h-4 transition-transform duration-300 ${isAboutOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
|
||||
<AnimatePresence>
|
||||
{isAboutOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95 }} animate={{ opacity: 1, y: 0, scale: 1 }} exit={{ opacity: 0, y: 10, scale: 0.95 }} transition={{ duration: 0.2 }}
|
||||
className="absolute top-full left-1/2 -translate-x-1/2 mt-4 w-56 bg-white border border-border rounded-2xl shadow-2xl shadow-black/10 overflow-hidden"
|
||||
>
|
||||
<div className="p-2 flex flex-col">
|
||||
<Link to="/about" className="px-4 py-3 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Our Story</Link>
|
||||
<Link to="/our-team" className="px-4 py-3 rounded-lg text-sm font-medium transition-all text-muted-foreground hover:bg-secondary hover:text-foreground">Our Team</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
|
||||
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.4 }}>
|
||||
<Link to="/contact" className={navLinkClass(isActive('/contact'))}>Contact</Link>
|
||||
</motion.div>
|
||||
|
||||
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.5 }}>
|
||||
<Button onClick={() => navigate('/contact')} className="bg-primary hover:bg-primary/90 text-white px-5 py-2.5 rounded-full shadow-lg shadow-primary/20 transition-all duration-300 font-bold tracking-wide border-0 group">
|
||||
Start Today <ArrowRight className="ml-1.5 w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
|
||||
</Button>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Nav Toggle */}
|
||||
<div className="flex items-center lg:hidden gap-2">
|
||||
<button onClick={() => navigate('/contact')} className="hidden sm:inline-flex bg-primary text-white px-4 py-2 rounded-full text-sm font-bold shadow-lg shadow-primary/20 transition-colors hover:bg-primary/90">
|
||||
Start Today
|
||||
</button>
|
||||
<button onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} aria-label="Toggle menu" className="p-2 rounded-lg transition-colors duration-300 shrink-0 text-white hover:bg-white/10">
|
||||
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Mobile Menu Content */}
|
||||
<AnimatePresence>
|
||||
{isMobileMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }} animate={{ opacity: 1, height: 'auto' }} exit={{ opacity: 0, height: 0 }} transition={{ duration: 0.3 }}
|
||||
className="lg:hidden overflow-hidden bg-black border-t border-white/10 shadow-2xl shadow-black/30 relative z-40"
|
||||
>
|
||||
<div className="flex flex-col gap-1 p-4">
|
||||
<Link to="/services" onClick={() => setIsMobileMenuOpen(false)} className="text-base font-bold py-3 px-3 rounded-lg border-b border-white/10 text-white hover:bg-white/10 hover:text-primary">Services</Link>
|
||||
<Link to="/industries" onClick={() => setIsMobileMenuOpen(false)} className="text-base font-bold py-3 px-3 rounded-lg border-b border-white/10 text-white hover:bg-white/10 hover:text-primary">Industries</Link>
|
||||
<Link to="/portfolio" onClick={() => setIsMobileMenuOpen(false)} className="text-base font-bold py-3 px-3 rounded-lg border-b border-white/10 text-white hover:bg-white/10 hover:text-primary">Portfolio</Link>
|
||||
<Link to="/about" onClick={() => setIsMobileMenuOpen(false)} className="text-base font-bold py-3 px-3 rounded-lg border-b border-white/10 text-white hover:bg-white/10 hover:text-primary">About</Link>
|
||||
<Link to="/our-team" onClick={() => setIsMobileMenuOpen(false)} className="text-base font-bold py-3 px-3 rounded-lg border-b border-white/10 text-white hover:bg-white/10 hover:text-primary">Our Team</Link>
|
||||
<Link to="/contact" onClick={() => setIsMobileMenuOpen(false)} className="text-base font-bold py-3 px-3 rounded-lg text-white hover:bg-white/10 hover:text-primary">Contact</Link>
|
||||
|
||||
<Button onClick={() => { navigate('/contact'); setIsMobileMenuOpen(false); }} className="bg-primary hover:bg-primary/90 text-white w-full rounded-full py-4 mt-4 font-bold shadow-lg shadow-primary/20 border-0">
|
||||
Start Today
|
||||
</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const HowWeWorkSection = ({ title, steps }) => {
|
||||
return (
|
||||
<section className="service-section bg-secondary/30">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="service-heading">{title}</h2>
|
||||
<div className="w-20 h-1 bg-primary mx-auto"></div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 relative">
|
||||
<div className="hidden lg:block absolute top-1/4 left-[10%] right-[10%] h-0.5 bg-border -z-10"></div>
|
||||
|
||||
{steps.map((step, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.15, duration: 0.5 }}
|
||||
className="flex flex-col items-center text-center relative group"
|
||||
>
|
||||
<div className="w-16 h-16 rounded-full bg-background border-2 border-border group-hover:border-primary flex items-center justify-center text-xl font-bold text-muted-foreground group-hover:text-primary transition-colors mb-6 shadow-lg z-10">
|
||||
0{index + 1}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-3">{step.title}</h3>
|
||||
<p className="text-muted-foreground text-sm">{step.description}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default HowWeWorkSection;
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Briefcase } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
|
||||
const IndustriesTable = ({ title, intro, industries }) => {
|
||||
return (
|
||||
<section className="service-section bg-secondary/10">
|
||||
<div className="mb-12 text-center md:text-left">
|
||||
<h2 className="service-heading">{title}</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-6 mx-auto md:mx-0"></div>
|
||||
{intro && (
|
||||
<p className="text-muted-foreground text-lg max-w-3xl leading-relaxed">
|
||||
{intro}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{industries.map((industry, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1, duration: 0.5 }}
|
||||
>
|
||||
<Card className="h-full bg-card border-border hover:border-primary/50 transition-colors">
|
||||
<CardHeader className="pb-3 flex flex-row items-center gap-4 space-y-0">
|
||||
<Briefcase className="w-6 h-6 text-primary flex-shrink-0" />
|
||||
<CardTitle className="text-lg">{industry.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="text-muted-foreground text-sm leading-relaxed list-disc list-inside ml-2">
|
||||
{industry.examples.map((example, i) => (
|
||||
<li key={i} className="mb-1">{example}</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndustriesTable;
|
||||
@@ -0,0 +1,175 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { MapPin, Phone, Globe, Building2, Mail } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Offices = () => {
|
||||
const offices = [
|
||||
{
|
||||
city: 'Dubai',
|
||||
state: '',
|
||||
country: 'UAE',
|
||||
location: 'Downtown Dubai',
|
||||
address: 'P.O. Box 123456, Dubai',
|
||||
phone: '+971 55 647 0342',
|
||||
email: 'info@aigrowthtechnologies.com',
|
||||
description: 'Our global headquarters and innovation hub driving digital excellence in the MENA region.',
|
||||
gradient: 'from-red-600 to-orange-600',
|
||||
type: 'headquarters',
|
||||
},
|
||||
{
|
||||
city: 'Houston',
|
||||
state: 'TX',
|
||||
country: 'USA',
|
||||
location: 'Downtown Houston',
|
||||
phone: '+1 832-350-0465',
|
||||
gradient: 'from-cyan-500 to-blue-600',
|
||||
type: 'partner',
|
||||
},
|
||||
{
|
||||
city: 'Miami',
|
||||
state: 'FL',
|
||||
country: 'USA',
|
||||
location: 'Brickell Avenue',
|
||||
phone: '+1 954-235-4974',
|
||||
gradient: 'from-teal-500 to-cyan-600',
|
||||
type: 'partner',
|
||||
},
|
||||
{
|
||||
city: 'New York City',
|
||||
state: 'NY',
|
||||
country: 'USA',
|
||||
location: 'Manhattan Financial District',
|
||||
phone: '+1 713-289-4577',
|
||||
gradient: 'from-blue-500 to-indigo-600',
|
||||
type: 'partner',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-20 md:py-32 bg-secondary/40 relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-tech-dots [mask-image:radial-gradient(ellipse_at_center,black,transparent_75%)] opacity-60 pointer-events-none" />
|
||||
<div className="absolute top-0 left-0 w-[500px] h-[500px] glow-red rounded-full filter blur-[120px] pointer-events-none" />
|
||||
<div className="absolute bottom-0 right-0 w-[500px] h-[500px] glow-blue rounded-full filter blur-[120px] pointer-events-none" />
|
||||
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<span className="text-primary font-semibold text-sm uppercase tracking-wider mb-4 block">
|
||||
Global Presence
|
||||
</span>
|
||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-black text-foreground mb-6">
|
||||
Our <span className="text-gradient">Global</span> Reach
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
Headquartered in Dubai with strategic partner offices across the USA, we're positioned to serve clients worldwide.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-16">
|
||||
{offices.map((office, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
whileHover={{ y: -8 }}
|
||||
className="group relative h-full"
|
||||
>
|
||||
<div className="relative h-full flex flex-col bg-white border border-border rounded-2xl p-6 hover:border-primary/40 hover:shadow-xl hover:shadow-primary/5 transition-all duration-300">
|
||||
<div className={`w-12 h-12 rounded-xl bg-gradient-to-br ${office.gradient} flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-300 shadow-lg`}>
|
||||
{office.type === 'headquarters' ? <Building2 className="text-white" size={24} /> : <MapPin className="text-white" size={24} />}
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-bold text-foreground mb-1">
|
||||
{office.city}
|
||||
</h3>
|
||||
|
||||
{office.type === 'headquarters' ? (
|
||||
<span className="inline-block bg-primary/10 text-primary text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded border border-primary/20 mb-3 self-start">Global HQ</span>
|
||||
) : (
|
||||
<span className="inline-block bg-secondary text-muted-foreground text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded border border-border mb-3 self-start">Partner Office</span>
|
||||
)}
|
||||
|
||||
{office.description && (
|
||||
<p className="text-muted-foreground text-xs mb-4 leading-relaxed">
|
||||
{office.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="space-y-3 mt-auto">
|
||||
<div className="text-muted-foreground text-xs flex flex-col gap-2">
|
||||
{office.location && (
|
||||
<div className="flex items-start gap-2">
|
||||
<MapPin size={14} className="shrink-0 text-primary mt-0.5" />
|
||||
<div className="font-medium text-foreground/80">{office.location}</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2 opacity-70 ml-5">
|
||||
<Globe size={12} />
|
||||
<span>{office.state ? `${office.state}, ` : ''}{office.country}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 text-primary text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<Phone size={14} className="shrink-0" />
|
||||
<a href={`tel:${office.phone.replace(/[\s-]/g, '')}`} className="font-semibold hover:underline truncate">{office.phone}</a>
|
||||
</div>
|
||||
{office.email && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Mail size={14} className="shrink-0" />
|
||||
<a href={`mailto:${office.email}`} className="font-medium hover:underline truncate">{office.email}</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Link to="/contact" className="mt-6">
|
||||
<Button className="w-full bg-primary hover:bg-primary/90 text-white rounded-full shadow-lg transition-all duration-300 text-xs py-2">
|
||||
Contact Office
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center"
|
||||
>
|
||||
<div className="bg-gradient-to-r from-primary to-red-500 rounded-3xl p-12 md:p-16 shadow-2xl shadow-primary/20 relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-tech-dots opacity-20 pointer-events-none" />
|
||||
<h3 className="text-3xl md:text-4xl lg:text-5xl font-black text-white mb-6 relative">
|
||||
Ready to Accelerate Your Growth?
|
||||
</h3>
|
||||
<p className="text-xl text-white/90 mb-8 max-w-2xl mx-auto relative">
|
||||
Let's build a custom growth strategy for your business. Schedule a consultation with our global team today.
|
||||
</p>
|
||||
<Link to="/contact" className="relative">
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-white text-primary hover:bg-white/90 px-8 py-6 rounded-full text-lg shadow-xl transition-all duration-300 hover:scale-105 font-bold"
|
||||
>
|
||||
Start Today
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Offices;
|
||||
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Settings, Zap, BarChart, Shield, Layout, Globe } from 'lucide-react';
|
||||
|
||||
const PlatformSection = ({ title, description, features }) => {
|
||||
const icons = [Settings, Zap, BarChart, Shield, Layout, Globe];
|
||||
|
||||
return (
|
||||
<section className="service-section">
|
||||
<div className="flex flex-col lg:flex-row gap-16 items-center">
|
||||
<motion.div
|
||||
className="lg:w-1/3"
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h2 className="service-heading text-balance">{title}</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-6"></div>
|
||||
<p className="service-subheading mb-8">
|
||||
{description}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="lg:w-2/3 grid grid-cols-1 sm:grid-cols-2 gap-4"
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
{features.map((feature, index) => {
|
||||
const Icon = icons[index % icons.length];
|
||||
return (
|
||||
<div key={index} className="flex items-start gap-4 p-5 rounded-lg border border-border bg-card/50 hover:bg-card transition-colors">
|
||||
<div className="bg-primary/10 p-3 rounded-md border border-primary/20">
|
||||
<Icon className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-foreground mb-1">{feature.title || feature}</h3>
|
||||
{feature.description && (
|
||||
<p className="text-sm text-muted-foreground">{feature.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlatformSection;
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import { isAuthenticated } from '@/lib/auth';
|
||||
|
||||
const ProtectedRoute = ({ children }) => {
|
||||
if (!isAuthenticated()) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
return children;
|
||||
};
|
||||
|
||||
export default ProtectedRoute;
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
|
||||
const ScrollReveal = ({
|
||||
children,
|
||||
className = '',
|
||||
delay = 0,
|
||||
duration = 0.6,
|
||||
yOffset = 40,
|
||||
once = true
|
||||
}) => {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
if (shouldReduceMotion) {
|
||||
return <div className={className}>{children}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: yOffset }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once, margin: "-10%" }}
|
||||
transition={{ duration, delay, ease: [0.16, 1, 0.3, 1] }}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScrollReveal;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
const ScrollToTop = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ScrollToTop;
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
|
||||
const ServiceCTA = ({ text, link = "/contact" }) => {
|
||||
return (
|
||||
<section className="py-20 bg-background border-t border-border">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<div className="max-w-3xl mx-auto bg-secondary/50 rounded-2xl p-10 md:p-16 border border-border">
|
||||
<h2 className="text-3xl md:text-4xl font-bold mb-8">{text}</h2>
|
||||
<Link to={link}>
|
||||
<Button size="lg" className="bg-primary hover:bg-primary/90 text-primary-foreground text-lg px-8 py-6 rounded-full">
|
||||
Get Started
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceCTA;
|
||||
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import Breadcrumbs from '@/components/Breadcrumbs';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const ServiceHero = ({ title, subheading, tagline, image }) => {
|
||||
return (
|
||||
<section className="relative pt-32 pb-24 md:pt-40 md:pb-32 overflow-hidden border-b border-border bg-background">
|
||||
<div className="absolute inset-0 z-0 opacity-[0.03] dark:opacity-10 pointer-events-none">
|
||||
<BrandedImage src={image} alt="" lazy={false} overlayOpacity={0.8} />
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 max-w-7xl relative z-10">
|
||||
<ScrollReveal>
|
||||
<Breadcrumbs />
|
||||
<div className="mt-8 mb-4">
|
||||
<span className="inline-block px-3 py-1 bg-primary/10 text-primary text-xs font-bold uppercase tracking-widest rounded-full">
|
||||
{tagline}
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl lg:text-7xl font-extrabold tracking-tight mb-6 max-w-4xl text-balance text-heading">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="text-lg md:text-2xl text-muted leading-relaxed max-w-3xl">
|
||||
{subheading}
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceHero;
|
||||
@@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { servicesData } from '@/lib/servicesData';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
|
||||
const Services = () => {
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.6 },
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="py-20 md:py-32 bg-slate-950 relative overflow-hidden">
|
||||
<div className="absolute inset-0 opacity-5">
|
||||
<div className="absolute top-0 left-0 w-96 h-96 bg-blue-500 rounded-full filter blur-3xl"></div>
|
||||
<div className="absolute bottom-0 right-0 w-96 h-96 bg-red-500 rounded-full filter blur-3xl"></div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 relative z-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<span className="text-blue-400 font-semibold text-sm uppercase tracking-wider mb-4 block">
|
||||
Our Services
|
||||
</span>
|
||||
<h2 className="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6">
|
||||
Full-Spectrum Digital
|
||||
<br />
|
||||
<span className="bg-gradient-to-r from-blue-400 to-red-400 bg-clip-text text-transparent">
|
||||
Marketing Solutions
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-xl text-slate-300 max-w-3xl mx-auto">
|
||||
From strategy to execution, we provide end-to-end services that drive measurable growth for your business.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
|
||||
>
|
||||
{servicesData.map((service, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
variants={itemVariants}
|
||||
whileHover={{ y: -8, scale: 1.02 }}
|
||||
className="group relative bg-slate-900/50 backdrop-blur-sm border border-slate-800 rounded-2xl hover:border-blue-500/50 transition-all duration-300 flex flex-col h-full"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-red-500/5 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
|
||||
<Link to={`/services/${service.slug}`} className="relative z-10 p-8 flex flex-col h-full">
|
||||
<div className={`w-16 h-16 rounded-xl bg-gradient-to-br ${service.gradient} flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-300 shadow-lg`}>
|
||||
<service.icon className="text-white" size={32} />
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-bold text-white mb-4 group-hover:text-blue-400 transition-colors duration-300">
|
||||
{service.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-slate-400 leading-relaxed mb-6 flex-grow">
|
||||
{service.description}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center text-blue-400 font-semibold text-sm group-hover:translate-x-2 transition-transform duration-300">
|
||||
Learn More <ArrowRight size={16} className="ml-2" />
|
||||
</div>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Services;
|
||||
@@ -0,0 +1,30 @@
|
||||
import React, { createContext, useContext, useEffect } from 'react';
|
||||
|
||||
const ThemeContext = createContext();
|
||||
|
||||
// Light theme only — the site no longer supports a dark mode.
|
||||
export const ThemeProvider = ({ children }) => {
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
root.classList.remove('dark');
|
||||
root.classList.add('light');
|
||||
// Clear any previously persisted dark preference.
|
||||
localStorage.removeItem('theme-preference');
|
||||
}, []);
|
||||
|
||||
// Kept for backward compatibility with consumers that still call useTheme.
|
||||
// toggleTheme is a no-op now.
|
||||
return (
|
||||
<ThemeContext.Provider value={{ theme: 'light', toggleTheme: () => {} }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTheme = () => {
|
||||
const context = useContext(ThemeContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useTheme must be used within a ThemeProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import * as React from "react"
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Accordion = AccordionPrimitive.Root
|
||||
|
||||
const AccordionItem = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item ref={ref} className={cn("border-b", className)} {...props} />
|
||||
))
|
||||
AccordionItem.displayName = "AccordionItem"
|
||||
|
||||
const AccordionTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
{children}
|
||||
<ChevronDown
|
||||
className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
))
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
||||
|
||||
const AccordionContent = React.forwardRef(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
{...props}>
|
||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
))
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
||||
@@ -0,0 +1,34 @@
|
||||
import * as React from "react"
|
||||
import { cva } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}) {
|
||||
return (<div className={cn(badgeVariants({ variant }), className)} {...props} />);
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
@@ -0,0 +1,47 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import React from 'react';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'h-10 w-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export { Button, buttonVariants };
|
||||
@@ -0,0 +1,50 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Card = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("rounded-xl border bg-card text-card-foreground shadow", className)}
|
||||
{...props} />
|
||||
))
|
||||
Card.displayName = "Card"
|
||||
|
||||
const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||
{...props} />
|
||||
))
|
||||
CardHeader.displayName = "CardHeader"
|
||||
|
||||
const CardTitle = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||
{...props} />
|
||||
))
|
||||
CardTitle.displayName = "CardTitle"
|
||||
|
||||
const CardDescription = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props} />
|
||||
))
|
||||
CardDescription.displayName = "CardDescription"
|
||||
|
||||
const CardContent = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
))
|
||||
CardContent.displayName = "CardContent"
|
||||
|
||||
const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center p-6 pt-0", className)}
|
||||
{...props} />
|
||||
))
|
||||
CardFooter.displayName = "CardFooter"
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
||||
@@ -0,0 +1,24 @@
|
||||
import * as React from "react"
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||
import { Check } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Checkbox = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={cn("flex items-center justify-center text-current")}
|
||||
>
|
||||
<Check className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
))
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
||||
|
||||
export { Checkbox }
|
||||
@@ -0,0 +1,19 @@
|
||||
import * as React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Input = React.forwardRef(({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Input.displayName = "Input"
|
||||
|
||||
export { Input }
|
||||
@@ -0,0 +1,19 @@
|
||||
import * as React from "react"
|
||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { cva } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const labelVariants = cva(
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
)
|
||||
|
||||
const Label = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Label.displayName = LabelPrimitive.Root.displayName
|
||||
|
||||
export { Label }
|
||||
@@ -0,0 +1,35 @@
|
||||
import * as React from "react"
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||
import { Circle } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const RadioGroup = React.forwardRef(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Root
|
||||
className={cn("grid gap-2", className)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
})
|
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
|
||||
|
||||
const RadioGroupItem = React.forwardRef(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
})
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
|
||||
|
||||
export { RadioGroup, RadioGroupItem }
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Textarea = React.forwardRef(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-foreground",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Textarea.displayName = "Textarea"
|
||||
|
||||
export { Textarea }
|
||||
@@ -0,0 +1,101 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import * as ToastPrimitives from '@radix-ui/react-toast';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { X } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
const ToastProvider = ToastPrimitives.Provider;
|
||||
|
||||
const ToastViewport = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Viewport
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
||||
|
||||
const toastVariants = cva(
|
||||
'data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-background border',
|
||||
destructive:
|
||||
'group destructive border-destructive bg-destructive text-destructive-foreground',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const Toast = React.forwardRef(({ className, variant, ...props }, ref) => {
|
||||
return (
|
||||
<ToastPrimitives.Root
|
||||
ref={ref}
|
||||
className={cn(toastVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
Toast.displayName = ToastPrimitives.Root.displayName;
|
||||
|
||||
const ToastAction = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Action
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-destructive/30 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
||||
|
||||
const ToastClose = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Close
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
|
||||
className,
|
||||
)}
|
||||
toast-close=""
|
||||
{...props}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</ToastPrimitives.Close>
|
||||
));
|
||||
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
||||
|
||||
const ToastTitle = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Title
|
||||
ref={ref}
|
||||
className={cn('text-sm font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
||||
|
||||
const ToastDescription = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Description
|
||||
ref={ref}
|
||||
className={cn('text-sm opacity-90', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
||||
|
||||
export {
|
||||
Toast,
|
||||
ToastAction,
|
||||
ToastClose,
|
||||
ToastDescription,
|
||||
ToastProvider,
|
||||
ToastTitle,
|
||||
ToastViewport,
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
Toast,
|
||||
ToastClose,
|
||||
ToastDescription,
|
||||
ToastProvider,
|
||||
ToastTitle,
|
||||
ToastViewport,
|
||||
} from '@/components/ui/toast';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import React from 'react';
|
||||
|
||||
export function Toaster() {
|
||||
const { toasts } = useToast();
|
||||
|
||||
return (
|
||||
<ToastProvider>
|
||||
{toasts.map(({ id, title, description, action, ...props }) => {
|
||||
return (
|
||||
<Toast key={id} {...props}>
|
||||
<div className="grid gap-1">
|
||||
{title && <ToastTitle>{title}</ToastTitle>}
|
||||
{description && (
|
||||
<ToastDescription>{description}</ToastDescription>
|
||||
)}
|
||||
</div>
|
||||
{action}
|
||||
<ToastClose />
|
||||
</Toast>
|
||||
);
|
||||
})}
|
||||
<ToastViewport />
|
||||
</ToastProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useState, useEffect } from "react"
|
||||
|
||||
const TOAST_LIMIT = 1
|
||||
|
||||
let count = 0
|
||||
function generateId() {
|
||||
count = (count + 1) % Number.MAX_VALUE
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
const toastStore = {
|
||||
state: {
|
||||
toasts: [],
|
||||
},
|
||||
listeners: [],
|
||||
|
||||
getState: () => toastStore.state,
|
||||
|
||||
setState: (nextState) => {
|
||||
if (typeof nextState === 'function') {
|
||||
toastStore.state = nextState(toastStore.state)
|
||||
} else {
|
||||
toastStore.state = { ...toastStore.state, ...nextState }
|
||||
}
|
||||
|
||||
toastStore.listeners.forEach(listener => listener(toastStore.state))
|
||||
},
|
||||
|
||||
subscribe: (listener) => {
|
||||
toastStore.listeners.push(listener)
|
||||
return () => {
|
||||
toastStore.listeners = toastStore.listeners.filter(l => l !== listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const toast = ({ ...props }) => {
|
||||
const id = generateId()
|
||||
|
||||
const update = (props) =>
|
||||
toastStore.setState((state) => ({
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === id ? { ...t, ...props } : t
|
||||
),
|
||||
}))
|
||||
|
||||
const dismiss = () => toastStore.setState((state) => ({
|
||||
...state,
|
||||
toasts: state.toasts.filter((t) => t.id !== id),
|
||||
}))
|
||||
|
||||
toastStore.setState((state) => ({
|
||||
...state,
|
||||
toasts: [
|
||||
{ ...props, id, dismiss },
|
||||
...state.toasts,
|
||||
].slice(0, TOAST_LIMIT),
|
||||
}))
|
||||
|
||||
return {
|
||||
id,
|
||||
dismiss,
|
||||
update,
|
||||
}
|
||||
}
|
||||
|
||||
export function useToast() {
|
||||
const [state, setState] = useState(toastStore.getState())
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = toastStore.subscribe((state) => {
|
||||
setState(state)
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const timeouts = []
|
||||
|
||||
state.toasts.forEach((toast) => {
|
||||
if (toast.duration === Infinity) {
|
||||
return
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toast.dismiss()
|
||||
}, toast.duration || 5000)
|
||||
|
||||
timeouts.push(timeout)
|
||||
})
|
||||
|
||||
return () => {
|
||||
timeouts.forEach((timeout) => clearTimeout(timeout))
|
||||
}
|
||||
}, [state.toasts])
|
||||
|
||||
return {
|
||||
toast,
|
||||
toasts: state.toasts,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const usePageTitle = (title) => {
|
||||
useEffect(() => {
|
||||
const prevTitle = document.title;
|
||||
if (title) {
|
||||
document.title = title;
|
||||
}
|
||||
return () => {
|
||||
document.title = prevTitle;
|
||||
};
|
||||
}, [title]);
|
||||
};
|
||||
@@ -0,0 +1,282 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
/* Exciting Tech-Forward Light Theme (light only) */
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222 47% 11%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222 47% 11%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
|
||||
/* Brand Red Accent #DC2626 */
|
||||
--primary: 0 84% 50%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 210 40% 96.5%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
|
||||
--muted: 220 23% 96%;
|
||||
--muted-foreground: 215 19% 38%;
|
||||
|
||||
--accent: 210 40% 96.5%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
|
||||
--destructive: 0 84% 56%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
|
||||
--border: 220 20% 90%;
|
||||
--input: 220 20% 90%;
|
||||
--ring: 0 84% 50%;
|
||||
|
||||
--radius: 0.625rem;
|
||||
--radius-card: 0.875rem;
|
||||
--radius-lg: 1.125rem;
|
||||
|
||||
--section-spacing-y: 8rem;
|
||||
|
||||
/* Custom Theme-Aware Color System for Text Visibility */
|
||||
--text-primary: 222 47% 11%;
|
||||
--text-secondary: 222 40% 18%;
|
||||
--text-muted: 215 19% 38%;
|
||||
--text-accent: 0 84% 50%;
|
||||
|
||||
--heading-color: 222 47% 11%;
|
||||
--body-text-color: 222 30% 20%;
|
||||
--muted-text-color: 215 19% 38%;
|
||||
--button-text-color: 0 0% 100%;
|
||||
}
|
||||
|
||||
/* No dark theme — site is light only. .dark mirrors light to keep stray
|
||||
dark: variants harmless if ever applied. */
|
||||
.dark {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222 47% 11%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222 47% 11%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
--primary: 0 84% 50%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 210 40% 96.5%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
--muted: 220 23% 96%;
|
||||
--muted-foreground: 215 19% 38%;
|
||||
--accent: 210 40% 96.5%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
--destructive: 0 84% 56%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--border: 220 20% 90%;
|
||||
--input: 220 20% 90%;
|
||||
--ring: 0 84% 50%;
|
||||
--text-primary: 222 47% 11%;
|
||||
--text-secondary: 222 40% 18%;
|
||||
--text-muted: 215 19% 38%;
|
||||
--text-accent: 0 84% 50%;
|
||||
--heading-color: 222 47% 11%;
|
||||
--body-text-color: 222 30% 20%;
|
||||
--muted-text-color: 215 19% 38%;
|
||||
--button-text-color: 0 0% 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
letter-spacing: -0.011em;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@apply text-foreground tracking-tight font-bold;
|
||||
font-family: 'Space Grotesk', 'Inter', -apple-system, sans-serif;
|
||||
line-height: 1.08;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p, span, label {
|
||||
}
|
||||
|
||||
p {
|
||||
@apply leading-relaxed;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Theme-Aware Utility Classes */
|
||||
.text-primary-theme {
|
||||
color: hsl(var(--text-primary));
|
||||
}
|
||||
.text-secondary-theme {
|
||||
color: hsl(var(--text-secondary));
|
||||
}
|
||||
.text-muted-theme {
|
||||
color: hsl(var(--text-muted));
|
||||
}
|
||||
.text-accent-theme {
|
||||
color: hsl(var(--text-accent));
|
||||
}
|
||||
|
||||
.text-heading {
|
||||
color: hsl(var(--heading-color));
|
||||
}
|
||||
.text-body {
|
||||
color: hsl(var(--body-text-color));
|
||||
}
|
||||
.text-muted {
|
||||
color: hsl(var(--muted-text-color));
|
||||
}
|
||||
.text-button {
|
||||
color: hsl(var(--button-text-color));
|
||||
}
|
||||
|
||||
.service-section {
|
||||
@apply py-20 md:py-32 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8;
|
||||
}
|
||||
|
||||
.service-heading {
|
||||
@apply text-4xl md:text-5xl lg:text-6xl font-extrabold tracking-tight mb-8 text-foreground;
|
||||
}
|
||||
|
||||
.service-subheading {
|
||||
@apply text-lg md:text-xl text-muted-foreground leading-relaxed max-w-3xl;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
@apply bg-card text-foreground border border-border rounded-[var(--radius-card)] p-8 hover:border-primary/50 transition-colors duration-300;
|
||||
}
|
||||
|
||||
.team-section-heading {
|
||||
@apply text-3xl md:text-4xl font-bold text-foreground mb-4;
|
||||
}
|
||||
|
||||
.team-section-divider {
|
||||
@apply h-px w-full bg-border my-16;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.font-display {
|
||||
font-family: 'Space Grotesk', 'Inter', -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.h1-theme {
|
||||
@apply text-foreground;
|
||||
}
|
||||
|
||||
.text-gradient {
|
||||
@apply bg-clip-text text-transparent bg-gradient-to-r from-primary via-red-500 to-orange-500;
|
||||
}
|
||||
|
||||
.card-hover {
|
||||
@apply transition-all duration-300 hover:-translate-y-1 hover:shadow-xl hover:shadow-primary/5 hover:border-primary/40;
|
||||
}
|
||||
|
||||
/* Tech grid background pattern */
|
||||
.bg-tech-grid {
|
||||
background-image:
|
||||
linear-gradient(to right, hsl(222 47% 11% / 0.04) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, hsl(222 47% 11% / 0.04) 1px, transparent 1px);
|
||||
background-size: 48px 48px;
|
||||
}
|
||||
|
||||
.bg-tech-dots {
|
||||
background-image: radial-gradient(hsl(222 47% 11% / 0.07) 1px, transparent 1px);
|
||||
background-size: 22px 22px;
|
||||
}
|
||||
|
||||
/* Radial mesh glow */
|
||||
.glow-red {
|
||||
background: radial-gradient(closest-side, hsl(0 84% 50% / 0.18), transparent);
|
||||
}
|
||||
.glow-orange {
|
||||
background: radial-gradient(closest-side, hsl(24 90% 55% / 0.16), transparent);
|
||||
}
|
||||
.glow-blue {
|
||||
background: radial-gradient(closest-side, hsl(217 91% 60% / 0.14), transparent);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.fade-in-up {
|
||||
animation: fadeInUp 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
.parallax-slow {
|
||||
transition: transform 1.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.parallax-slow:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.animate-float {
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
.animate-float-slow {
|
||||
animation: float 9s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-18px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: hsl(220 23% 96%);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: hsl(0 84% 50% / 0.5);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: hsl(0 84% 50% / 0.8);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
const STORAGE_KEY = 'aigrowth_client_user';
|
||||
|
||||
export const getUser = () => {
|
||||
const userStr = localStorage.getItem(STORAGE_KEY);
|
||||
if (userStr) {
|
||||
try {
|
||||
return JSON.parse(userStr);
|
||||
} catch (e) {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const isAuthenticated = () => {
|
||||
return !!getUser();
|
||||
};
|
||||
|
||||
export const login = (email, password) => {
|
||||
// Simple mock authentication
|
||||
if (!email || !password) return null;
|
||||
|
||||
const user = {
|
||||
id: '1',
|
||||
email: email,
|
||||
name: email.split('@')[0],
|
||||
role: 'client'
|
||||
};
|
||||
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(user));
|
||||
return user;
|
||||
};
|
||||
|
||||
export const logout = () => {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
export const INDUSTRIES = [
|
||||
{
|
||||
id: '01',
|
||||
name: 'Hospitality',
|
||||
slug: 'hospitality',
|
||||
description: 'Maximize direct bookings, increase RevPAR, and build lasting guest loyalty for hotels, resorts, and premium dining establishments.',
|
||||
segments: ['Boutique Hotels', 'Luxury Resorts', 'Fine Dining', 'Travel Agencies'],
|
||||
route: '/industries/hospitality',
|
||||
image: 'https://images.unsplash.com/photo-1666401565408-9b6b0741f0d6'
|
||||
},
|
||||
{
|
||||
id: '02',
|
||||
name: 'Medical & Dental',
|
||||
slug: 'medical-dental',
|
||||
description: 'Drive high-value patient acquisition and build trust through compliant, authoritative digital presence for healthcare practices.',
|
||||
segments: ['Dental Clinics', 'Med Spas', 'Specialty Medicine', 'Primary Care'],
|
||||
route: '/industries/medical-dental',
|
||||
image: 'https://images.unsplash.com/photo-1576091160550-2173dba999ef'
|
||||
},
|
||||
{
|
||||
id: '03',
|
||||
name: 'Automotive',
|
||||
slug: 'automotive',
|
||||
description: 'Accelerate inventory turnover and increase service appointments with targeted local SEO and high-converting paid media campaigns.',
|
||||
segments: ['Franchise Dealerships', 'Independent Dealers', 'Service Centers', 'Auto Groups'],
|
||||
route: '/industries/automotive',
|
||||
image: 'https://images.unsplash.com/photo-1683743407083-90b217fad319'
|
||||
},
|
||||
{
|
||||
id: '04',
|
||||
name: 'E-Commerce',
|
||||
slug: 'e-commerce',
|
||||
description: 'Scale revenue, reduce customer acquisition cost (CAC), and maximize lifetime value for direct-to-consumer and retail brands.',
|
||||
segments: ['DTC Brands', 'Apparel & Fashion', 'Consumer Goods', 'Marketplace Sellers'],
|
||||
route: '/industries/e-commerce',
|
||||
image: 'https://images.unsplash.com/photo-1674027392842-29f8354e236c'
|
||||
},
|
||||
{
|
||||
id: '05',
|
||||
name: 'Home Services',
|
||||
slug: 'home-services',
|
||||
description: 'Dominate local search and generate high-quality, exclusive leads for residential contractors and home maintenance businesses.',
|
||||
segments: ['HVAC', 'Plumbing', 'Roofing & Exteriors', 'Landscaping'],
|
||||
route: '/industries/home-services',
|
||||
image: 'https://images.unsplash.com/photo-1666401565408-9b6b0741f0d6'
|
||||
},
|
||||
{
|
||||
id: '06',
|
||||
name: 'B2B & SaaS',
|
||||
slug: 'b2b',
|
||||
description: 'Generate qualified pipeline, shorten sales cycles, and position your brand as an industry authority with content-led growth.',
|
||||
segments: ['Software as a Service (SaaS)', 'Professional Consulting', 'Manufacturing', 'Logistics'],
|
||||
route: '/industries/b2b',
|
||||
image: 'https://images.unsplash.com/photo-1691109972364-0f3b7ce93527'
|
||||
},
|
||||
{
|
||||
id: '07',
|
||||
name: 'Creators & Personal Brands',
|
||||
slug: 'creators',
|
||||
description: 'Monetize audiences, launch digital products, and transition from influencer to scalable enterprise business.',
|
||||
segments: ['Content Creators', 'Course Educators', 'Authors', 'Coaches'],
|
||||
route: '/industries/creators',
|
||||
image: 'https://images.unsplash.com/photo-1678227547309-f25998d4fc86'
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Code, Search, Brain, Share2, Palette, TrendingUp } from 'lucide-react';
|
||||
|
||||
export const servicesData = [
|
||||
{
|
||||
slug: 'web-development',
|
||||
icon: Code,
|
||||
title: 'Web Development',
|
||||
description: 'Custom websites and web applications built with cutting-edge technology for optimal performance and user experience.',
|
||||
longDescription: 'In the digital age, your website is your headquarters. We build high-performance, scalable, and secure web solutions tailored to your unique business needs. From responsive corporate websites to complex web applications, our team leverages modern frameworks like React, Next.js, and robust backend architectures to deliver exceptional user experiences.',
|
||||
benefits: [
|
||||
'Custom UI/UX Design customized to your brand identity',
|
||||
'Mobile-first responsive architecture',
|
||||
'SEO-friendly structure and code',
|
||||
'High-speed performance optimization',
|
||||
'Secure and scalable infrastructure'
|
||||
],
|
||||
gradient: 'from-blue-500 to-cyan-500',
|
||||
},
|
||||
{
|
||||
slug: 'seo',
|
||||
icon: Search,
|
||||
title: 'SEO',
|
||||
description: 'Data-driven search engine optimization strategies that improve rankings, drive organic traffic, and increase visibility.',
|
||||
longDescription: 'Visibility is the currency of the internet. Our comprehensive SEO strategies go beyond simple keyword stuffing. We conduct deep technical audits, competitive analysis, and content optimization to ensure your brand dominates the search results. We stay ahead of algorithm updates to keep your ranking secure.',
|
||||
benefits: [
|
||||
'Technical SEO and Site Health Audits',
|
||||
'Keyword Research & Competitive Gap Analysis',
|
||||
'On-Page & Off-Page Optimization',
|
||||
'Content Strategy & Marketing',
|
||||
'Local SEO for Geographic Dominance'
|
||||
],
|
||||
gradient: 'from-cyan-500 to-teal-500',
|
||||
},
|
||||
{
|
||||
slug: 'llmo-geo',
|
||||
icon: Brain,
|
||||
title: 'LLMO & GEO',
|
||||
description: 'Large Language Model Optimization and Generative Engine Optimization for AI-powered search visibility.',
|
||||
longDescription: 'The future of search is conversational. As users turn to AI tools like ChatGPT, Claude, and Bing Chat for answers, your brand needs to be part of that conversation. We optimize your digital footprint to ensure Large Language Models cite your brand as a trusted authority.',
|
||||
benefits: [
|
||||
'Entity Authority Building',
|
||||
'Structured Data & Knowledge Graph Optimization',
|
||||
'Brand Mention Analysis & Optimization',
|
||||
'AI-Ready Content Structuring',
|
||||
'Citation & Source Trust Flow Enhancement'
|
||||
],
|
||||
gradient: 'from-teal-500 to-green-500',
|
||||
},
|
||||
{
|
||||
slug: 'social-media',
|
||||
icon: Share2,
|
||||
title: 'Social Media Management',
|
||||
description: 'Strategic social media campaigns that build brand awareness, engage audiences, and drive conversions.',
|
||||
longDescription: 'Social media is where communities are built and brands are humanized. We manage your presence across all major platforms, creating engaging content, managing communities, and running targeted campaigns that turn followers into loyal customers.',
|
||||
benefits: [
|
||||
'Platform-Specific Content Strategy',
|
||||
'Community Management & Engagement',
|
||||
'Social Listening & Sentiment Analysis',
|
||||
'Influencer Partnership Management',
|
||||
'Performance Analytics & Reporting'
|
||||
],
|
||||
gradient: 'from-green-500 to-emerald-500',
|
||||
},
|
||||
{
|
||||
slug: 'branding',
|
||||
icon: Palette,
|
||||
title: 'Branding',
|
||||
description: 'Comprehensive brand identity development including logos, guidelines, and visual assets that resonate.',
|
||||
longDescription: 'Your brand is more than a logo; it’s a promise to your customers. We help define your voice, visual identity, and core values to create a cohesive brand experience that resonates with your target audience and sets you apart from the competition.',
|
||||
benefits: [
|
||||
'Logo Design & Visual Identity Systems',
|
||||
'Brand Voice & Tone Guidelines',
|
||||
'Marketing Collateral Design',
|
||||
'Rebranding & Brand Refresh',
|
||||
'Brand Strategy Workshops'
|
||||
],
|
||||
gradient: 'from-emerald-500 to-blue-500',
|
||||
},
|
||||
{
|
||||
slug: 'paid-advertising',
|
||||
icon: TrendingUp,
|
||||
title: 'Paid Advertising',
|
||||
description: 'ROI-focused advertising campaigns across Google, Facebook, Instagram, and LinkedIn that deliver results.',
|
||||
longDescription: 'Stop wasting budget on clicks that don\'t convert. Our paid media experts design hyper-targeted campaigns across search, social, and display networks. We focus on lowering your CAC (Customer Acquisition Cost) while maximizing ROAS (Return on Ad Spend).',
|
||||
benefits: [
|
||||
'PPC Campaign Management (Google/Bing)',
|
||||
'Social Media Advertising (Meta/LinkedIn/TikTok)',
|
||||
'Retargeting & Remarketing Strategies',
|
||||
'A/B Testing & Conversion Rate Optimization',
|
||||
'Detailed ROI & Attribution Reporting'
|
||||
],
|
||||
gradient: 'from-blue-500 to-red-500',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from '@/App';
|
||||
import '@/index.css';
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
);
|
||||
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
|
||||
const AboutPage = () => {
|
||||
usePageTitle("About Us | AI Growth Technologies");
|
||||
|
||||
return (
|
||||
<div className="bg-background pt-20">
|
||||
{/* Premium Hero */}
|
||||
<section className="relative min-h-[70vh] flex items-center justify-center overflow-hidden border-b border-border">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<BrandedImage
|
||||
src="https://images.unsplash.com/photo-1675023112817-52b789fd2ef0"
|
||||
alt="Abstract growth and architecture"
|
||||
lazy={false}
|
||||
overlayOpacity={0.7}
|
||||
/>
|
||||
</div>
|
||||
<div className="container mx-auto px-4 max-w-4xl relative z-10 text-center text-white">
|
||||
<ScrollReveal>
|
||||
<span className="inline-block px-4 py-2 bg-white/10 backdrop-blur-md border border-white/20 text-xs font-bold uppercase tracking-widest rounded-full mb-8">
|
||||
Our Story
|
||||
</span>
|
||||
<h1 className="text-5xl md:text-7xl font-black tracking-tight mb-8">
|
||||
Engineered for <span className="text-primary">what's next.</span>
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl text-gray-300 leading-relaxed">
|
||||
We are a team of data scientists, media buyers, and developers building the growth architecture for the AI-first internet.
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Core Values / Mission */}
|
||||
<section className="py-24 md:py-32">
|
||||
<div className="container mx-auto px-4 max-w-7xl">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-16 items-center">
|
||||
<ScrollReveal>
|
||||
<h2 className="text-4xl font-bold mb-6">The internet changed. Agencies didn't.</h2>
|
||||
<p className="text-lg text-muted-foreground leading-relaxed mb-6">
|
||||
Most marketing relies on outdated playbooks. Siloed teams, fragmented data, and a focus on vanity metrics instead of business outcomes. We built AI Growth Technologies to be the exact opposite.
|
||||
</p>
|
||||
<p className="text-lg text-muted-foreground leading-relaxed">
|
||||
Our approach integrates paid media, search optimization, generative AI visibility, and proprietary tracking technology into a single, cohesive engine designed solely to acquire customers efficiently.
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
<ScrollReveal delay={0.2} className="relative h-[400px] rounded-2xl overflow-hidden">
|
||||
<BrandedImage src="https://images.unsplash.com/photo-1551288049-bebda4e38f71" alt="Data architecture" />
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutPage;
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const BrandPage = () => {
|
||||
usePageTitle("Brand Creation & Optimization | AI Growth Technologies");
|
||||
|
||||
const capabilities = [
|
||||
{ title: "Positioning", description: "Defining your unique market stance and competitive differentiation." },
|
||||
{ title: "Naming", description: "Memorable, trademark-viable names that anchor your identity." },
|
||||
{ title: "Visual Identity", description: "Logos, color systems, and typography engineered for digital performance." },
|
||||
{ title: "Messaging", description: "Core narratives, taglines, and tone-of-voice documentation." },
|
||||
{ title: "Brand Guidelines", description: "Comprehensive rulebooks to maintain consistency at scale." },
|
||||
{ title: "Brand Optimization", description: "Modernizing legacy brands for today's digital and AI ecosystems." }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Discovery & Positioning", description: "Deep-dive workshops to uncover your core value and audience alignment." },
|
||||
{ title: "Identity & Messaging", description: "Developing the visual assets and narrative frameworks that define you." },
|
||||
{ title: "System & Guidelines", description: "Compiling a scalable brand system ready for internal and external teams." },
|
||||
{ title: "Activate & Optimize", description: "Deploying the brand across web, search, social, and paid channels." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "Do you only create new brands from scratch?", answer: "No. While we build brands from the ground up, we also specialize in Brand Optimization—refining and modernizing established brands without losing their historical equity." },
|
||||
{ question: "How does branding connect to your other services?", answer: "A strong brand system reduces customer acquisition costs. We engineer your brand's messaging and visual identity to perform natively across our SEO, Paid Media, and LLMO campaigns." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="Brands built to be recognized — and to perform."
|
||||
subheading="We create and optimize brands from positioning to identity to messaging, then build them to perform across every channel we run."
|
||||
tagline="Positioning & Identity · Messaging Systems · Built to Perform"
|
||||
image="https://images.unsplash.com/photo-1495224814653-94f36c0a31ea"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<h2 className="service-heading text-balance">What we do — A brand is a system, not a logo.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
A logo doesn't acquire customers—a coherent brand system does. We architect brands that clearly communicate why you exist, how you are different, and why your audience should care. Whether launching a new entity or optimizing an established market leader, we engineer brands to be highly recognizable and deeply integrated with performance marketing channels.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — Create & optimize" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — From insight to identity" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Let's build a brand worth remembering." /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrandPage;
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const ConsultingPage = () => {
|
||||
usePageTitle("Digital Marketing Consulting | Strategy & Audits — AI Growth Technologies");
|
||||
|
||||
const capabilities = [
|
||||
{ title: "Growth Strategy", description: "Comprehensive roadmaps aligning marketing with revenue targets." },
|
||||
{ title: "Channel Audits", description: "Deep-dives into SEO, Paid, and Social to find wasted spend and opportunities." },
|
||||
{ title: "Measurement & Analytics", description: "Designing accurate attribution and dashboard architectures." },
|
||||
{ title: "AI & LLMO Readiness", description: "Preparing your data and content for the zero-click generative search era." },
|
||||
{ title: "Team & Process", description: "Evaluating in-house capabilities and streamlining workflows." },
|
||||
{ title: "Fractional Advisory", description: "Ongoing executive-level strategic guidance and mentorship." }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Listen & Assess", description: "Understanding your business goals, current resources, and market constraints." },
|
||||
{ title: "Diagnose", description: "Rigorous auditing of your historical data, tech stack, and current channel execution." },
|
||||
{ title: "Roadmap", description: "Delivering a prioritized, step-by-step strategy based on ROI impact." },
|
||||
{ title: "Support Execution", description: "Providing ongoing advisory to ensure the plan is implemented correctly." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "Who is your consulting service for?", answer: "It's designed for marketing leaders, founders, and executives who need an objective, expert perspective before committing large budgets, or who need to audit underperforming channels." },
|
||||
{ question: "How is consulting different from managed services?", answer: "Consulting provides the roadmap, audits, and strategic oversight. Managed services execute that roadmap. You can use our consulting to guide your own in-house team, or hire us to execute the plan we build." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="Senior digital marketing strategy — before you spend the budget."
|
||||
subheading="Get clarity before you commit. We audit your channels, set a prioritized growth roadmap, and give your team the strategy to execute."
|
||||
tagline="Audits & Roadmaps · Growth Strategy · Fractional Advisory"
|
||||
image="https://images.unsplash.com/photo-1603975711481-18b7aaca4caa"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<h2 className="service-heading text-balance">What we do — Strategy first, so the spend follows a plan.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
Tactics without strategy lead to wasted budget. We provide a channel-wide, objective perspective to diagnose bottlenecks, eliminate inefficiencies, and map out the exact sequence of actions required to unlock your next phase of growth. We don't guess; we look at the data, build the blueprint, and guide you forward.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — Clarity, then momentum" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — From audit to action plan" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Get a clear-eyed read on your marketing." /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConsultingPage;
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ContactForm from '@/components/ContactForm';
|
||||
import Offices from '@/components/Offices';
|
||||
import { Phone } from 'lucide-react';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const ContactPage = () => {
|
||||
usePageTitle("Contact Us | AI Growth Technologies");
|
||||
const dubaiPhoneNumber = "+971 55 647 0342";
|
||||
|
||||
return (
|
||||
<div className="bg-background pt-20 flex flex-col lg:flex-row min-h-screen">
|
||||
|
||||
{/* Left visual side */}
|
||||
<div className="lg:w-5/12 relative hidden lg:block border-r border-border">
|
||||
<BrandedImage
|
||||
src="https://images.unsplash.com/photo-1675023112817-52b789fd2ef0"
|
||||
alt="Abstract tech visual"
|
||||
lazy={false}
|
||||
overlayOpacity={0.6}
|
||||
/>
|
||||
<div className="absolute inset-0 flex flex-col justify-end p-12 text-white">
|
||||
<h2 className="text-5xl font-black mb-4 drop-shadow-lg">Let's engineer your growth.</h2>
|
||||
<p className="text-xl text-gray-200 drop-shadow-md">Reach out to start a conversation about your digital strategy.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right content side */}
|
||||
<div className="w-full lg:w-7/12 py-12 md:py-24 px-4 sm:px-8 lg:px-16 overflow-y-auto">
|
||||
<ScrollReveal>
|
||||
<div className="bg-secondary/50 p-6 rounded-2xl border border-border flex items-center justify-between mb-12">
|
||||
<div>
|
||||
<h3 className="text-sm uppercase tracking-widest font-bold text-muted-foreground mb-1">Direct Line (Dubai)</h3>
|
||||
<a href={`tel:${dubaiPhoneNumber.replace(/[\s-]/g, '')}`} className="text-2xl font-bold text-foreground hover:text-primary transition-colors">
|
||||
{dubaiPhoneNumber}
|
||||
</a>
|
||||
</div>
|
||||
<div className="w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center text-primary">
|
||||
<Phone size={24} />
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal delay={0.1}>
|
||||
<ContactForm />
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal delay={0.2} className="mt-20 pt-12 border-t border-border">
|
||||
<Offices />
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactPage;
|
||||
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import PlatformSection from '@/components/PlatformSection';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const CybersecurityPage = () => {
|
||||
usePageTitle("Cybersecurity Services | Korrith — AI Growth Technologies");
|
||||
|
||||
const KORRITH_LOGO_URL = "https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/de2379b85c12ac09b90cae00c2763ec7.png";
|
||||
|
||||
const capabilities = [
|
||||
{ title: "Security Assessment", description: "Comprehensive penetration testing and vulnerability auditing." },
|
||||
{ title: "Website & Infra Hardening", description: "Locking down web apps, servers, and cloud environments." },
|
||||
{ title: "Vulnerability Management", description: "Continuous scanning and patching of emerging threats." },
|
||||
{ title: "Monitoring", description: "24/7 proactive surveillance of your digital attack surface." },
|
||||
{ title: "Incident Response", description: "Rapid mitigation, containment, and recovery from breaches." },
|
||||
{ title: "Data Protection & Compliance", description: "Ensuring customer data meets regulatory security standards." }
|
||||
];
|
||||
|
||||
const platformFeatures = [
|
||||
{ title: "Continuous Scanning", description: "Automated vulnerability detection across your perimeter." },
|
||||
{ title: "DDoS Mitigation", description: "Proactive traffic filtering to keep your assets online." },
|
||||
{ title: "Threat Intelligence", description: "Leveraging global threat feeds to anticipate attacks." },
|
||||
{ title: "WAF Configuration", description: "Advanced web application firewalls blocking malicious requests." }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Assess", description: "Auditing your current infrastructure to identify critical vulnerabilities." },
|
||||
{ title: "Harden", description: "Applying rigorous security controls, patching flaws, and configuring firewalls." },
|
||||
{ title: "Monitor", description: "Deploying 24/7 surveillance to detect anomalies and threat signatures." },
|
||||
{ title: "Respond & Improve", description: "Reacting instantly to alerts and continuously iterating defenses." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "What is Korrith?", answer: "Korrith is our dedicated, specialized cybersecurity practice engineered to protect the digital assets, data, and infrastructure of the brands we grow." },
|
||||
{ question: "Why does a marketing agency offer cybersecurity?", answer: "Growth creates risk. As your traffic, revenue, and visibility scale, you become a larger target. We secure what we build so a single incident doesn't wipe out years of digital equity." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="Grow fast. Stay protected. That's Korrith."
|
||||
subheading="The digital assets we help you grow are exactly what attackers target. Korrith is our cybersecurity practice, built to protect the visibility you work so hard to earn."
|
||||
tagline="Assessment & Hardening · Monitoring & Response · Powered by Korrith"
|
||||
image="https://images.unsplash.com/photo-1643101807331-21a4a3f081d5"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-8 mb-12">
|
||||
<div>
|
||||
<h2 className="service-heading text-balance">What we do — Security is part of growth, not separate from it.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-4 bg-secondary border border-border rounded-xl">
|
||||
<span className="text-[10px] text-muted-foreground font-bold uppercase tracking-widest mb-2">Powered By</span>
|
||||
<img src={KORRITH_LOGO_URL} alt="Korrith" className="h-10 w-auto" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
When you invest in marketing, you build highly valuable digital real estate. Korrith exists to ensure that investment isn't compromised. We harden your infrastructure against exploits, monitor for unauthorized access, and deploy enterprise-grade data protection—allowing you to scale with absolute confidence.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — The Korrith practice" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><PlatformSection title="Platform · Korrith — Korrith secures the growth engine." description="We utilize an advanced stack of security protocols, real-time monitors, and intelligent firewalls to shield your brand from evolving digital threats." features={platformFeatures} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — From exposure to resilience" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Protect the growth you've built." /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CybersecurityPage;
|
||||
@@ -0,0 +1,95 @@
|
||||
import React, { useState } from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Mail, ArrowLeft, ArrowRight, CheckCircle2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const ForgotPasswordPage = () => {
|
||||
const { toast } = useToast();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
usePageTitle("Reset Password | AI Growth Technologies");
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
setIsLoading(false);
|
||||
setIsSubmitted(true);
|
||||
toast({ title: "Reset Link Sent", description: "Instructions sent to your email.", className: "bg-green-900 text-white" });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pt-20 flex bg-background">
|
||||
<div className="hidden lg:block lg:w-1/2 relative border-r border-border">
|
||||
<BrandedImage src="https://images.unsplash.com/photo-1549925245-f20a1bac6454" alt="Security" overlayOpacity={0.7} lazy={false} />
|
||||
<div className="absolute inset-0 flex flex-col justify-center p-16 text-white">
|
||||
<h1 className="text-6xl font-black mb-6 drop-shadow-md">Secure Access</h1>
|
||||
<p className="text-xl text-gray-200 drop-shadow-sm">Recover your account credentials safely.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full lg:w-1/2 flex items-center justify-center p-8">
|
||||
<ScrollReveal className="w-full max-w-md">
|
||||
<div className="bg-card border border-border rounded-3xl p-10 shadow-2xl shadow-black/5 text-card-foreground">
|
||||
<Link to="/login" className="inline-flex items-center text-sm font-semibold text-muted-foreground hover:text-primary transition-colors mb-8">
|
||||
<ArrowLeft size={16} className="mr-2" /> Back to Login
|
||||
</Link>
|
||||
|
||||
{!isSubmitted ? (
|
||||
<>
|
||||
<div className="text-center mb-8">
|
||||
<div className="w-16 h-16 bg-primary/10 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
||||
<Mail className="w-8 h-8 text-primary" />
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold mb-2">Forgot Password?</h2>
|
||||
<p className="text-muted-foreground">Enter your email to receive reset instructions.</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="email" className="text-foreground">Email Address</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-4 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<Input
|
||||
id="email" type="email" placeholder="client@company.com" required
|
||||
value={email} onChange={(e) => setEmail(e.target.value)}
|
||||
className="pl-12 h-12 bg-background border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button type="submit" disabled={isLoading} className="w-full h-14 text-lg font-bold bg-primary hover:bg-primary/90 text-primary-foreground rounded-xl mt-4">
|
||||
{isLoading ? "Sending..." : <span className="flex items-center">Send Reset Link <ArrowRight className="ml-2 w-5 h-5" /></span>}
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-center py-6">
|
||||
<div className="w-20 h-20 bg-green-500/10 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||
<CheckCircle2 className="w-10 h-10 text-green-500" />
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold mb-4">Check Your Email</h2>
|
||||
<p className="text-muted-foreground mb-8 text-lg">
|
||||
We've sent a link to <span className="font-bold text-foreground">{email}</span>. Check your inbox and spam folder.
|
||||
</p>
|
||||
<Link to="/login">
|
||||
<Button className="w-full h-14 text-lg font-bold rounded-xl text-primary-foreground bg-primary">Return to Login</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPasswordPage;
|
||||
@@ -0,0 +1,182 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
ArrowRight,
|
||||
TrendingUp,
|
||||
Search,
|
||||
Sparkles,
|
||||
Code,
|
||||
Share2,
|
||||
Shield,
|
||||
Palette,
|
||||
Briefcase,
|
||||
Cpu,
|
||||
BarChart3,
|
||||
Layers,
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
import AnimatedCounter from '@/components/AnimatedCounter';
|
||||
|
||||
const Home = () => {
|
||||
usePageTitle('AI Growth Technologies - Digital Marketing Agency');
|
||||
|
||||
const services = [
|
||||
{ title: 'Paid Media', icon: TrendingUp, desc: 'Strategic advertising campaigns across Google, Meta, and other platforms to drive qualified traffic and maximize ROI.', link: '/services/paid-media' },
|
||||
{ title: 'SEO', icon: Search, desc: 'Comprehensive search engine optimization to improve organic visibility, drive sustainable traffic, and increase rankings.', link: '/services/seo' },
|
||||
{ title: 'LLMO/AI', icon: Sparkles, desc: 'Leverage advanced AI and language models to automate processes, enhance insights, and drive innovation.', link: '/services/llmo' },
|
||||
{ title: 'Web Dev', icon: Code, desc: 'Custom web development and design to create fast, scalable, and user-friendly digital experiences.', link: '/services/web-development' },
|
||||
{ title: 'Social Media', icon: Share2, desc: 'Strategic social media management and content creation to build brand presence and engage audiences.', link: '/services/social-media' },
|
||||
{ title: 'Cybersecurity', icon: Shield, desc: 'Protect your business with comprehensive security solutions, threat detection, and compliance management.', link: '/services/cybersecurity' },
|
||||
{ title: 'Brand', icon: Palette, desc: 'Build a strong brand identity with strategic positioning, design, and messaging that resonates with your audience.', link: '/services/brand' },
|
||||
{ title: 'Consulting', icon: Briefcase, desc: 'Expert strategic consulting to guide business decisions, optimize operations, and drive growth.', link: '/services/consulting' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-background">
|
||||
{/* Cinematic Light Tech Hero */}
|
||||
<section className="relative min-h-[100dvh] flex items-center justify-center pt-24 pb-32 overflow-hidden">
|
||||
{/* Tech background layers */}
|
||||
<div className="absolute inset-0 z-0 bg-background" />
|
||||
<div className="absolute inset-0 z-0 bg-tech-grid [mask-image:radial-gradient(ellipse_at_center,black,transparent_75%)]" />
|
||||
<div className="absolute top-[-10%] left-[-5%] w-[55vw] h-[55vw] max-w-[640px] max-h-[640px] glow-red rounded-full filter blur-[90px] animate-float-slow pointer-events-none" />
|
||||
<div className="absolute bottom-[-10%] right-[-5%] w-[50vw] h-[50vw] max-w-[560px] max-h-[560px] glow-orange rounded-full filter blur-[90px] animate-float pointer-events-none" />
|
||||
<div className="absolute top-[20%] right-[15%] w-[30vw] h-[30vw] max-w-[360px] max-h-[360px] glow-blue rounded-full filter blur-[100px] pointer-events-none" />
|
||||
|
||||
<div className="container mx-auto px-4 max-w-5xl relative z-10 text-center">
|
||||
<ScrollReveal yOffset={30}>
|
||||
<span className="inline-flex items-center gap-2 px-4 py-2 bg-white/70 backdrop-blur-md border border-border text-foreground text-xs sm:text-sm font-bold uppercase tracking-widest rounded-full mb-8 shadow-sm">
|
||||
<Cpu className="w-4 h-4 text-primary" />
|
||||
Digital Growth Architects
|
||||
</span>
|
||||
<h1 className="text-5xl sm:text-6xl md:text-7xl lg:text-8xl font-black text-foreground tracking-tight mb-8 text-balance">
|
||||
Growth engineered for the <span className="text-gradient">AI era.</span>
|
||||
</h1>
|
||||
<p className="text-lg sm:text-xl md:text-2xl text-muted-foreground max-w-3xl mx-auto mb-12 leading-relaxed">
|
||||
We build scalable digital ecosystems. Paid media, search, and generative AI optimization operating as a single, accountable system.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 sm:gap-6">
|
||||
<Button size="lg" className="w-full sm:w-auto h-14 px-8 text-base sm:text-lg font-bold rounded-full bg-primary hover:bg-primary/90 text-primary-foreground shadow-xl shadow-primary/25 hover:scale-105 transition-all" asChild>
|
||||
<Link to="/contact">Book a Strategy Call <ArrowRight className="ml-2 w-5 h-5" /></Link>
|
||||
</Button>
|
||||
<Button size="lg" variant="outline" className="w-full sm:w-auto h-14 px-8 text-base sm:text-lg font-bold rounded-full bg-white hover:bg-secondary text-foreground border-border transition-all" asChild>
|
||||
<Link to="/services">Explore Services</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Floating Stats Band */}
|
||||
<section className="relative z-20 -mt-20 px-4">
|
||||
<div className="container mx-auto max-w-6xl">
|
||||
<div className="bg-white border border-border rounded-2xl shadow-2xl shadow-black/5 backdrop-blur-md grid grid-cols-2 md:grid-cols-4 gap-4 py-8">
|
||||
{[
|
||||
{ val: 120, suffix: '%', label: 'Avg ROI Increase' },
|
||||
{ val: 7, label: 'Global Markets' },
|
||||
{ val: 24, label: 'Specialized Channels' },
|
||||
{ val: 99, suffix: '%', label: 'Uptime & Security' },
|
||||
].map((stat, i) => (
|
||||
<div key={i} className="text-center px-2">
|
||||
<div className="text-3xl md:text-4xl font-black text-foreground mb-1 font-display">
|
||||
<AnimatedCounter to={stat.val} suffix={stat.suffix} duration={1.5} />
|
||||
</div>
|
||||
<div className="text-[10px] sm:text-xs text-muted-foreground uppercase tracking-widest font-semibold">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Capabilities Grid */}
|
||||
<section className="py-24 md:py-32 bg-background relative">
|
||||
<div className="container mx-auto px-4 max-w-7xl">
|
||||
<ScrollReveal>
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-6 text-foreground">Capabilities that compound.</h2>
|
||||
<p className="text-lg text-muted-foreground">Every service we offer is designed to integrate and amplify the others, reducing waste and accelerating acquisition.</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
|
||||
{services.map((svc, i) => {
|
||||
const IconComponent = svc.icon;
|
||||
return (
|
||||
<ScrollReveal key={i} delay={i * 0.05}>
|
||||
<Link to={svc.link} className="block h-full outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-xl">
|
||||
<div className="card-hover bg-white text-card-foreground border border-border rounded-xl p-6 md:p-8 h-full flex flex-col">
|
||||
<div className="mb-6 inline-flex p-3 rounded-lg bg-primary/10">
|
||||
<IconComponent className="w-8 h-8 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-[1.125rem] font-semibold text-foreground mb-3">{svc.title}</h3>
|
||||
<p className="text-muted-foreground text-sm leading-relaxed flex-grow">
|
||||
{svc.desc}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
</ScrollReveal>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Proprietary Technology Feature */}
|
||||
<section className="relative py-24 md:py-32 overflow-hidden bg-secondary/50 border-y border-border">
|
||||
<div className="absolute inset-0 z-0 bg-tech-dots [mask-image:radial-gradient(ellipse_at_center,black,transparent_70%)] opacity-70" />
|
||||
<div className="absolute top-1/3 -left-20 w-[400px] h-[400px] glow-red rounded-full filter blur-[110px] pointer-events-none" />
|
||||
<div className="container mx-auto px-4 max-w-7xl relative z-10">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<ScrollReveal>
|
||||
<span className="text-primary font-bold uppercase tracking-widest mb-4 block">Proprietary Technology</span>
|
||||
<h2 className="text-4xl md:text-5xl font-black mb-6 leading-tight text-foreground">Data that tells the truth.</h2>
|
||||
<p className="text-lg md:text-xl text-muted-foreground mb-8 leading-relaxed">
|
||||
We don't rely on black-box attribution. Through our proprietary tools like meSEO and Atrilyx, we track exact user journeys—from the first generative AI query down to the closed deal.
|
||||
</p>
|
||||
<Button size="lg" className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-full font-bold px-8 h-14 shadow-lg shadow-primary/20" asChild>
|
||||
<Link to="/about">Explore Our Approach <ArrowRight className="ml-2 w-5 h-5" /></Link>
|
||||
</Button>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal delay={0.15} className="grid grid-cols-2 gap-4">
|
||||
{[
|
||||
{ icon: BarChart3, label: 'Atrilyx', desc: 'Unified media & attribution engine' },
|
||||
{ icon: Search, label: 'meSEO', desc: 'Organic + AI visibility tracking' },
|
||||
{ icon: Shield, label: 'Korrith', desc: 'Security & resilience layer' },
|
||||
{ icon: Layers, label: 'One System', desc: 'Channels that compound' },
|
||||
].map((p, i) => (
|
||||
<div key={i} className="bg-white border border-border rounded-xl p-6 shadow-sm hover:shadow-md hover:-translate-y-1 transition-all">
|
||||
<div className="w-11 h-11 rounded-lg bg-primary/10 flex items-center justify-center mb-4">
|
||||
<p.icon className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h3 className="font-bold text-foreground mb-1">{p.label}</h3>
|
||||
<p className="text-sm text-muted-foreground">{p.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Final CTA */}
|
||||
<section className="py-24 md:py-32 relative overflow-hidden bg-background">
|
||||
<div className="absolute top-0 right-0 w-[700px] h-[700px] glow-red rounded-full filter blur-[120px] pointer-events-none" />
|
||||
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] glow-orange rounded-full filter blur-[110px] pointer-events-none" />
|
||||
<div className="container mx-auto px-4 max-w-4xl text-center relative z-10">
|
||||
<ScrollReveal>
|
||||
<h2 className="text-4xl md:text-6xl font-black mb-8 text-foreground">Ready to architect your growth?</h2>
|
||||
<p className="text-lg md:text-xl text-muted-foreground mb-10 max-w-2xl mx-auto">
|
||||
Stop paying for fragmented marketing. Start investing in a unified growth system engineered for scale, resilience, and ROI.
|
||||
</p>
|
||||
<Button size="lg" className="bg-primary hover:bg-primary/90 text-primary-foreground rounded-full font-bold px-10 h-16 text-lg shadow-xl shadow-primary/25 hover:scale-105 transition-all" asChild>
|
||||
<Link to="/contact">Book Your Strategy Call <ArrowRight className="ml-2 w-5 h-5" /></Link>
|
||||
</Button>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
@@ -0,0 +1,153 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ArrowRight, ChevronRight } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { INDUSTRIES } from '@/lib/industriesData';
|
||||
import Breadcrumbs from '@/components/Breadcrumbs';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
|
||||
const IndustriesPage = () => {
|
||||
const pageTitle = "Industries We Serve | AI Growth Technologies";
|
||||
const metaDescription = "AI Growth Technologies drives growth for businesses across hospitality, medical and dental, automotive, e-commerce, home services, B2B, creators, and more.";
|
||||
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
const schemaData = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CollectionPage",
|
||||
"name": pageTitle,
|
||||
"description": metaDescription,
|
||||
"url": "https://aigrowthtechnologies.com/industries",
|
||||
"mainEntity": {
|
||||
"@type": "ItemList",
|
||||
"itemListElement": INDUSTRIES.map((ind, index) => ({
|
||||
"@type": "ListItem",
|
||||
"position": index + 1,
|
||||
"item": {
|
||||
"@type": "Thing",
|
||||
"name": ind.name,
|
||||
"description": ind.description,
|
||||
"url": `https://aigrowthtechnologies.com${ind.route}`
|
||||
}
|
||||
}))
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{pageTitle}</title>
|
||||
<meta name="description" content={metaDescription} />
|
||||
<script type="application/ld+json">{JSON.stringify(schemaData)}</script>
|
||||
</Helmet>
|
||||
|
||||
<section className="pt-32 pb-20 md:pt-40 md:pb-28">
|
||||
<div className="container mx-auto px-4 max-w-7xl">
|
||||
<ScrollReveal>
|
||||
<Breadcrumbs />
|
||||
<div className="max-w-4xl mb-16 mt-8">
|
||||
<h1 className="text-5xl md:text-6xl lg:text-7xl font-black tracking-tight mb-6 h1-theme">
|
||||
Growth built for your <span className="text-gradient">industry.</span>
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl text-muted-foreground leading-relaxed max-w-3xl">
|
||||
We apply our integrated system of paid media, SEO, and AI optimization to the specific challenges, compliance requirements, and customer journeys of your vertical.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Hero Stats */}
|
||||
<ScrollReveal delay={0.2} className="flex flex-wrap items-center gap-8 md:gap-16 border-y border-border py-12 mb-24">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-5xl font-black text-foreground">{INDUSTRIES.length}</span>
|
||||
<span className="text-xs uppercase tracking-widest text-muted-foreground font-bold mt-2">Industries</span>
|
||||
</div>
|
||||
<div className="hidden md:block w-px h-16 bg-border"></div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-5xl font-black text-foreground">24</span>
|
||||
<span className="text-xs uppercase tracking-widest text-muted-foreground font-bold mt-2">Specialisations</span>
|
||||
</div>
|
||||
<div className="hidden md:block w-px h-16 bg-border"></div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-5xl font-black text-foreground">8</span>
|
||||
<span className="text-xs uppercase tracking-widest text-muted-foreground font-bold mt-2">Services per Account</span>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="mb-24">
|
||||
<ScrollReveal><h2 className="text-3xl md:text-5xl font-bold text-foreground mb-12">Who we serve</h2></ScrollReveal>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{INDUSTRIES.map((industry, index) => (
|
||||
<ScrollReveal key={industry.id} delay={index * 0.1}>
|
||||
<Link to={industry.route} className="block h-full group">
|
||||
<div className="bg-card border border-border hover:border-primary/40 rounded-[var(--radius-lg)] overflow-hidden flex flex-col h-full transition-all duration-500 hover:-translate-y-2 hover:shadow-2xl shadow-primary/5">
|
||||
{/* Image header */}
|
||||
<div className="h-48 relative overflow-hidden">
|
||||
<BrandedImage src={industry.image} alt={industry.name} overlayOpacity={0.6} />
|
||||
<div className="absolute top-4 right-4 bg-black/50 backdrop-blur-md px-3 py-1 text-white text-xs font-bold rounded-full border border-white/10">
|
||||
{industry.id}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-8 flex flex-col flex-grow">
|
||||
<h3 className="text-3xl font-bold text-foreground mb-4 group-hover:text-primary transition-colors flex items-center justify-between">
|
||||
{industry.name}
|
||||
<ArrowRight className="w-6 h-6 opacity-0 -translate-x-4 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-300" />
|
||||
</h3>
|
||||
<p className="text-muted-foreground mb-6 flex-grow leading-relaxed">
|
||||
{industry.description}
|
||||
</p>
|
||||
<ul className="space-y-3 border-t border-border pt-6 mt-auto">
|
||||
{industry.segments.map((segment, idx) => (
|
||||
<li key={idx} className="flex items-center text-sm font-medium text-foreground/80">
|
||||
<ChevronRight className="w-4 h-4 text-primary mr-3 flex-shrink-0" />
|
||||
{segment}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</ScrollReveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ScrollReveal className="mb-24">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-8">Sectors we're adding next</h2>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{['Legal', 'Financial Services', 'Fitness & Wellness', 'Med Spa & Aesthetics', 'Education', 'Travel & Tourism'].map((sector) => (
|
||||
<Link key={sector} to="/contact" className="px-6 py-3 rounded-full bg-secondary text-secondary-foreground font-semibold hover:bg-primary hover:text-white transition-all duration-300 border border-border hover:border-primary">
|
||||
{sector}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-24 md:py-32 bg-background text-foreground relative overflow-hidden border-t border-border">
|
||||
<div className="absolute top-0 right-0 w-[600px] h-[600px] glow-red rounded-full filter blur-[120px] pointer-events-none" />
|
||||
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] glow-orange rounded-full filter blur-[110px] pointer-events-none" />
|
||||
<div className="container mx-auto px-4 max-w-4xl text-center relative z-10">
|
||||
<ScrollReveal>
|
||||
<h2 className="text-4xl md:text-6xl font-black mb-6 text-foreground">Let's grow your business.</h2>
|
||||
<p className="text-xl text-muted-foreground mb-10 max-w-2xl mx-auto">
|
||||
Ready to deploy an integrated growth system tailored to your industry's exact needs? Let's talk strategy.
|
||||
</p>
|
||||
<Button size="lg" className="bg-primary hover:bg-primary/90 text-white px-10 py-7 text-xl font-bold rounded-full border-0 shadow-2xl shadow-primary/25 hover:scale-105 transition-all" asChild>
|
||||
<Link to="/contact">Book a strategy call</Link>
|
||||
</Button>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndustriesPage;
|
||||
@@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import { useParams, Navigate, Link } from 'react-router-dom';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle';
|
||||
import { INDUSTRIES } from '@/lib/industriesData';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
import Breadcrumbs from '@/components/Breadcrumbs';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CheckCircle2, ArrowRight } from 'lucide-react';
|
||||
|
||||
const IndustryDetailPage = () => {
|
||||
const { slug } = useParams();
|
||||
const industry = INDUSTRIES.find(ind => ind.slug === slug);
|
||||
|
||||
// Hook must be called at the top level, before any early returns.
|
||||
// We use the industry name if found, otherwise a fallback string.
|
||||
usePageTitle(industry ? `${industry.name} Digital Marketing Agency | AI Growth Technologies` : "Industry Details | AI Growth Technologies");
|
||||
|
||||
if (!industry) {
|
||||
return <Navigate to="/industries" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-background">
|
||||
{/* Hero Section */}
|
||||
<section className="relative pt-32 pb-24 md:pt-40 md:pb-32 overflow-hidden border-b border-border">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<BrandedImage src={industry.image} alt={industry.name} lazy={false} overlayOpacity={0.7} />
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 max-w-7xl relative z-10 text-white">
|
||||
<ScrollReveal>
|
||||
<div className="mb-6"><Breadcrumbs className="text-gray-300" /></div>
|
||||
<span className="inline-block px-4 py-1.5 bg-primary/20 backdrop-blur-md border border-primary/50 text-white text-sm font-bold uppercase tracking-widest rounded-full mb-6">
|
||||
Industry Focus
|
||||
</span>
|
||||
<h1 className="text-5xl md:text-6xl lg:text-7xl font-black tracking-tight mb-6 max-w-4xl text-balance drop-shadow-xl">
|
||||
Growth strategy for <span className="text-primary drop-shadow-none">{industry.name}.</span>
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl text-gray-200 leading-relaxed max-w-3xl drop-shadow-md">
|
||||
{industry.description}
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-24 md:py-32 border-b border-border">
|
||||
<div className="container mx-auto px-4 max-w-7xl">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
|
||||
<ScrollReveal>
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-8">Specialized solutions for your vertical.</h2>
|
||||
<p className="text-xl text-muted-foreground leading-relaxed mb-8">
|
||||
We understand the specific compliance, operational, and customer journey challenges inherent to {industry.name}. We don't apply generic playbooks.
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
{industry.segments.map((segment, i) => (
|
||||
<div key={i} className="flex items-center gap-4 bg-secondary p-4 rounded-xl border border-border">
|
||||
<CheckCircle2 className="text-primary w-6 h-6 flex-shrink-0" />
|
||||
<span className="font-bold text-foreground">{segment}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
<ScrollReveal delay={0.2} className="relative rounded-2xl overflow-hidden shadow-2xl h-[500px]">
|
||||
<BrandedImage src={industry.image} alt={`${industry.name} solutions`} />
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-24 md:py-32 bg-background text-foreground relative overflow-hidden border-t border-border">
|
||||
<div className="absolute top-0 right-0 w-[600px] h-[600px] glow-red rounded-full filter blur-[120px] pointer-events-none" />
|
||||
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] glow-orange rounded-full filter blur-[110px] pointer-events-none" />
|
||||
<div className="container mx-auto px-4 max-w-4xl text-center relative z-10">
|
||||
<ScrollReveal>
|
||||
<h2 className="text-4xl md:text-5xl font-black mb-6 text-foreground">Dominate your market.</h2>
|
||||
<p className="text-xl text-muted-foreground mb-10 max-w-2xl mx-auto">
|
||||
Ready to deploy an integrated growth system tailored to {industry.name}? Let's talk strategy.
|
||||
</p>
|
||||
<Button size="lg" className="bg-primary hover:bg-primary/90 text-white px-10 py-7 text-xl font-bold rounded-full border-0 shadow-2xl shadow-primary/25 hover:scale-105 transition-all" asChild>
|
||||
<Link to="/contact">Book your consultation <ArrowRight className="ml-2" /></Link>
|
||||
</Button>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndustryDetailPage;
|
||||
@@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import PlatformSection from '@/components/PlatformSection';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const LlmoPage = () => {
|
||||
usePageTitle("LLMO, AEO & GEO Services | AI Search Optimization — AI Growth Technologies");
|
||||
|
||||
const capabilities = [
|
||||
{ title: "LLMO", description: "Optimizing content to train and be cited by Large Language Models." },
|
||||
{ title: "AEO", description: "Answer Engine Optimization for direct, conversational query resolution." },
|
||||
{ title: "GEO", description: "Generative Engine Optimization for AI Overviews and SGE." },
|
||||
{ title: "Entity & Structured Data", description: "Knowledge graph alignment to clarify your brand identity." },
|
||||
{ title: "AI Visibility Monitoring", description: "Tracking brand mentions across AI chat interfaces." },
|
||||
{ title: "AI Traffic Attribution", description: "Isolating referral traffic generated from AI engines." }
|
||||
];
|
||||
|
||||
const platformFeatures = [
|
||||
{ title: "Multi-engine verification", description: "Monitor visibility in ChatGPT, Gemini, and Claude." },
|
||||
{ title: "Citation monitoring", description: "Alerts when your brand is referenced as a source." },
|
||||
{ title: "Entity signals", description: "Analyzing how AI models interpret your brand connections." },
|
||||
{ title: "Structured data", description: "Validation of schema required for deep AI comprehension." },
|
||||
{ title: "AI referral attribution", description: "Tracking clicks from conversational AI interfaces." },
|
||||
{ title: "Answer tracking", description: "Monitoring sentiment and accuracy of AI-generated answers." }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Entity Auditing", description: "Analyzing how current models understand and define your brand." },
|
||||
{ title: "Semantic Structuring", description: "Re-architecting site data to feed clean information to crawlers." },
|
||||
{ title: "AEO Content Production", description: "Publishing direct, authoritative answers to industry questions." },
|
||||
{ title: "Verification", description: "Using meSEO to monitor citation frequency and traffic impact." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "What is LLMO and GEO?", answer: "Large Language Model Optimization and Generative Engine Optimization focus on structuring your brand's digital footprint so that AI tools (like ChatGPT or Google's AI Overviews) accurately cite you as the authoritative answer." },
|
||||
{ question: "How do you measure AI visibility?", answer: "Through meSEO, we track specific referral patterns, entity recognition strength, and citation frequency across major generative AI platforms." },
|
||||
{ question: "Is this replacing traditional SEO?", answer: "No, it's an evolution. Traditional SEO still drives standard search, but LLMO ensures you capture the rapidly growing segment of users who search conversationally via AI." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="Be the answer AI gives — not the link it skips."
|
||||
subheading="Search is moving inside AI. We optimize how ChatGPT, Gemini, Claude, Copilot, and AI Overviews understand and cite your brand."
|
||||
tagline="LLMO · AEO · GEO · AI Visibility Monitoring"
|
||||
image="https://images.unsplash.com/photo-1549925245-f20a1bac6454"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<h2 className="service-heading text-balance">What we do — Optimize for generative search.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
The zero-click search era is here. Users are asking complex questions and getting direct answers from AI. If your content isn't engineered for LLMs, you won't be cited. We align your digital assets with entity-based search principles to ensure your brand becomes the definitive source AI relies on.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — The AI-visibility stack" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><PlatformSection title="Platform · meSEO — Verifying AI visibility." description="We actively track how the major language models perceive, reference, and link back to your business." features={platformFeatures} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — From invisible to cited" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Claim your place in the AI answer." /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LlmoPage;
|
||||
@@ -0,0 +1,109 @@
|
||||
import React, { useState } from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import { Lock, Mail, ArrowRight, UserPlus } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { login } from '@/lib/auth';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const LoginPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [formData, setFormData] = useState({ email: '', password: '' });
|
||||
|
||||
usePageTitle("Client Portal Login | AI Growth Technologies");
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
const user = login(formData.email, formData.password);
|
||||
if (user) {
|
||||
toast({ title: "Welcome back!", description: "Successfully logged into client portal.", className: "bg-green-900 text-white" });
|
||||
navigate('/portal');
|
||||
} else {
|
||||
throw new Error("Invalid credentials");
|
||||
}
|
||||
} catch (error) {
|
||||
toast({ title: "Login Failed", description: "Please check your email and password.", variant: "destructive" });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen pt-20 flex bg-background">
|
||||
<div className="hidden lg:block lg:w-1/2 relative border-r border-border">
|
||||
<BrandedImage src="https://images.unsplash.com/photo-1549925245-f20a1bac6454" alt="Client Portal" overlayOpacity={0.7} lazy={false} />
|
||||
<div className="absolute inset-0 flex flex-col justify-center p-16 text-white">
|
||||
<h1 className="text-6xl font-black mb-6 drop-shadow-md">Client Portal</h1>
|
||||
<p className="text-xl text-gray-200 drop-shadow-sm">Access your real-time analytics, campaign performance, and strategy roadmaps.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full lg:w-1/2 flex items-center justify-center p-8">
|
||||
<ScrollReveal className="w-full max-w-md">
|
||||
<div className="bg-card border border-border rounded-3xl p-10 shadow-2xl shadow-black/5 text-card-foreground">
|
||||
<div className="text-center mb-10">
|
||||
<div className="w-16 h-16 bg-primary/10 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
||||
<Lock className="w-8 h-8 text-primary" />
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold mb-2">Welcome Back</h2>
|
||||
<p className="text-muted-foreground">Sign in to your client workspace</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="email" className="text-foreground">Email Address</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-4 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<Input
|
||||
id="email" type="email" placeholder="client@company.com" required
|
||||
value={formData.email} onChange={(e) => setFormData({...formData, email: e.target.value})}
|
||||
className="pl-12 h-12 bg-background border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="password" className="text-foreground">Password</Label>
|
||||
<Link to="/forgot-password" className="text-sm font-semibold text-primary hover:underline">Forgot password?</Link>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-4 top-3 h-5 w-5 text-muted-foreground" />
|
||||
<Input
|
||||
id="password" type="password" placeholder="••••••••" required
|
||||
value={formData.password} onChange={(e) => setFormData({...formData, password: e.target.value})}
|
||||
className="pl-12 h-12 bg-background border-border text-foreground"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button type="submit" disabled={isLoading} className="w-full h-14 text-lg font-bold bg-primary hover:bg-primary/90 text-primary-foreground rounded-xl mt-4">
|
||||
{isLoading ? "Authenticating..." : <span className="flex items-center">Sign In <ArrowRight className="ml-2 w-5 h-5" /></span>}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="mt-10 pt-6 border-t border-border text-center">
|
||||
<p className="text-muted-foreground text-sm mb-4">Don't have an account?</p>
|
||||
<Link to="/new-client">
|
||||
<Button variant="outline" className="w-full h-12 rounded-xl font-bold border-border text-foreground hover:bg-secondary">
|
||||
<UserPlus className="mr-2 w-4 h-4" /> New Client Application
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
@@ -0,0 +1,202 @@
|
||||
import React, { useState } from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import {
|
||||
User,
|
||||
Mail,
|
||||
Phone,
|
||||
Building2,
|
||||
Globe,
|
||||
MessageSquare,
|
||||
CheckCircle2,
|
||||
ArrowLeft,
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
|
||||
const NewClientForm = () => {
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
companyName: '',
|
||||
website: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
const pageTitle = 'New Client Application | AI Growth Technologies';
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
|
||||
setIsSubmitting(false);
|
||||
toast({
|
||||
title: 'Application Received!',
|
||||
description: "We've received your client application. Our team will review your details and contact you shortly to set up your account.",
|
||||
className: 'bg-green-600 border-green-700 text-white',
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
navigate('/');
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const inputClass = 'bg-background border-border text-foreground placeholder:text-muted-foreground';
|
||||
|
||||
return (
|
||||
<div className="min-h-screen py-12 px-4 sm:px-6 lg:px-8 bg-background">
|
||||
<div className="absolute inset-0 bg-tech-grid [mask-image:radial-gradient(ellipse_at_top,black,transparent_70%)] opacity-50 pointer-events-none" />
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="max-w-3xl mx-auto relative z-10"
|
||||
>
|
||||
<div className="mb-8">
|
||||
<Link to="/login" className="inline-flex items-center text-muted-foreground hover:text-primary transition-colors mb-6">
|
||||
<ArrowLeft size={16} className="mr-2" />
|
||||
Back to Login
|
||||
</Link>
|
||||
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl md:text-4xl font-black mb-4 h1-theme">
|
||||
New Client Application
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-lg max-w-2xl mx-auto">
|
||||
Interested in working with us? Please fill out the form below to create your client profile. We'll get back to you within 24 hours.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-border rounded-2xl p-6 md:p-8 shadow-xl shadow-black/5">
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
|
||||
{/* Personal Information */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-xl font-semibold text-primary flex items-center">
|
||||
<User size={20} className="mr-2" /> Personal Information
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="firstName" className="text-foreground">First Name</Label>
|
||||
<Input id="firstName" name="firstName" placeholder="John" required value={formData.firstName} onChange={handleChange} className={inputClass} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="lastName" className="text-foreground">Last Name</Label>
|
||||
<Input id="lastName" name="lastName" placeholder="Doe" required value={formData.lastName} onChange={handleChange} className={inputClass} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="text-foreground">Email Address</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-2.5 h-5 w-5 text-muted-foreground" />
|
||||
<Input id="email" name="email" type="email" placeholder="john@example.com" required value={formData.email} onChange={handleChange} className={`pl-10 ${inputClass}`} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="phone" className="text-foreground">Phone Number</Label>
|
||||
<div className="relative">
|
||||
<Phone className="absolute left-3 top-2.5 h-5 w-5 text-muted-foreground" />
|
||||
<Input id="phone" name="phone" type="tel" placeholder="+1 (555) 000-0000" value={formData.phone} onChange={handleChange} className={`pl-10 ${inputClass}`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-px bg-border my-6" />
|
||||
|
||||
{/* Company Information */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-xl font-semibold text-primary flex items-center">
|
||||
<Building2 size={20} className="mr-2" /> Company Details
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="companyName" className="text-foreground">Company Name</Label>
|
||||
<div className="relative">
|
||||
<Building2 className="absolute left-3 top-2.5 h-5 w-5 text-muted-foreground" />
|
||||
<Input id="companyName" name="companyName" placeholder="Acme Inc." required value={formData.companyName} onChange={handleChange} className={`pl-10 ${inputClass}`} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="website" className="text-foreground">Website URL</Label>
|
||||
<div className="relative">
|
||||
<Globe className="absolute left-3 top-2.5 h-5 w-5 text-muted-foreground" />
|
||||
<Input id="website" name="website" placeholder="https://example.com" value={formData.website} onChange={handleChange} className={`pl-10 ${inputClass}`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-px bg-border my-6" />
|
||||
|
||||
{/* Additional Info */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-xl font-semibold text-primary flex items-center">
|
||||
<MessageSquare size={20} className="mr-2" /> Project Details
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="message" className="text-foreground">How can we help you?</Label>
|
||||
<Textarea
|
||||
id="message"
|
||||
name="message"
|
||||
placeholder="Tell us about your goals, required services, or any specific challenges you're facing..."
|
||||
className="min-h-[120px]"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-4">
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full bg-primary hover:bg-primary/90 text-white py-6 text-lg rounded-xl shadow-lg shadow-primary/25"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<span className="flex items-center">
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ repeat: Infinity, duration: 1, ease: 'linear' }}
|
||||
className="mr-2 w-5 h-5 border-2 border-white border-t-transparent rounded-full"
|
||||
/>
|
||||
Submitting...
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center">
|
||||
Submit Application <CheckCircle2 className="ml-2" size={20} />
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
<p className="text-center text-muted-foreground text-sm mt-4">
|
||||
By submitting this form, you agree to our Terms of Service and Privacy Policy.
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewClientForm;
|
||||
@@ -0,0 +1,96 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
import BrandedImage from '@/components/BrandedImage';
|
||||
|
||||
const leadershipTeam = [
|
||||
{ name: 'Dylan Mazzei', role: 'CEO' },
|
||||
{ name: 'Reinaldo Rodriguez', role: 'COO' },
|
||||
{ name: 'Valeria Garcia', role: 'VP of Account Management' },
|
||||
];
|
||||
|
||||
const managementTeam = [
|
||||
{ name: 'Ivan Reyes', role: 'Senior Developer' },
|
||||
{ name: 'Maria DePonte', role: 'Design Specialist' },
|
||||
{ name: 'Gabriela Delfino', role: 'Account Manager' },
|
||||
];
|
||||
|
||||
const specialistsTeam = [
|
||||
{ name: 'Kanwal Hafeez', role: 'SEO | LLMO Specialist' },
|
||||
{ name: 'Sudeeepa J Bose', role: 'SEO | LLMO Specialist' },
|
||||
{ name: 'Angel Garcia', role: 'SEO | LLMO Creative Specialist' },
|
||||
{ name: 'Samuel Morales', role: 'SEO | LLMO Creative Specialist' },
|
||||
];
|
||||
|
||||
const TeamMemberCard = ({ name, role, variant = 'default' }) => (
|
||||
<div className={`card-hover bg-card border border-border rounded-[var(--radius-card)] p-8 ${variant === 'leadership' ? 'shadow-lg' : ''}`}>
|
||||
<div className="w-16 h-16 rounded-full bg-secondary text-primary flex items-center justify-center font-bold text-xl mb-6">
|
||||
{name.split(' ').map(n => n[0]).join('')}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-foreground mb-2">{name}</h3>
|
||||
<p className="text-sm font-semibold text-primary uppercase tracking-wider">{role}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const OurTeamPage = () => {
|
||||
usePageTitle("Our Team | AI Growth Technologies");
|
||||
|
||||
return (
|
||||
<div className="bg-background pt-20">
|
||||
<section className="relative min-h-[50vh] flex items-center justify-center overflow-hidden border-b border-border">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<BrandedImage src="https://images.unsplash.com/photo-1522071820081-009f0129c71c" alt="Team culture" lazy={false} overlayOpacity={0.85} />
|
||||
</div>
|
||||
<div className="container mx-auto px-4 max-w-4xl relative z-10 text-center text-white">
|
||||
<ScrollReveal>
|
||||
<h1 className="text-5xl md:text-7xl font-black tracking-tight mb-6">
|
||||
The architects behind <span className="text-primary">the engine.</span>
|
||||
</h1>
|
||||
<p className="text-xl text-gray-300 leading-relaxed max-w-2xl mx-auto">
|
||||
We combine deep technical expertise with relentless creative strategy.
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-24 md:py-32">
|
||||
<div className="container mx-auto px-4 max-w-7xl">
|
||||
|
||||
<ScrollReveal className="mb-20">
|
||||
<h2 className="text-4xl font-bold mb-12 text-center">Leadership</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{leadershipTeam.map((m, i) => (
|
||||
<TeamMemberCard key={i} name={m.name} role={m.role} variant="leadership" />
|
||||
))}
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="w-full h-px bg-border my-20"></div>
|
||||
|
||||
<ScrollReveal className="mb-20">
|
||||
<h2 className="text-4xl font-bold mb-12 text-center">Management & Specialists</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{managementTeam.map((m, i) => (
|
||||
<TeamMemberCard key={i} name={m.name} role={m.role} />
|
||||
))}
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="w-full h-px bg-border my-20"></div>
|
||||
|
||||
<ScrollReveal>
|
||||
<h2 className="text-4xl font-bold mb-12 text-center">Specialists</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{specialistsTeam.map((m, i) => (
|
||||
<TeamMemberCard key={i} name={m.name} role={m.role} />
|
||||
))}
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OurTeamPage;
|
||||
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import PlatformSection from '@/components/PlatformSection';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const PaidMediaPage = () => {
|
||||
usePageTitle("Paid Media Management & PPC Agency | Atrilyx — AI Growth Technologies");
|
||||
|
||||
const capabilities = [
|
||||
{ title: "Search & Shopping", description: "Intent-captured traffic across Google and Bing ecosystems." },
|
||||
{ title: "Paid Social", description: "Demand generation via Meta, TikTok, LinkedIn, and Pinterest." },
|
||||
{ title: "Programmatic & CTV", description: "Data-driven display and Connected TV placements at scale." },
|
||||
{ title: "Creative Production", description: "High-volume, performance-tested ad creative." },
|
||||
{ title: "Measurement & Attribution", description: "Server-side tracking to prove ROI down to the dollar." },
|
||||
{ title: "Budget Governance", description: "Algorithmic pacing to ensure optimal spend distribution." }
|
||||
];
|
||||
|
||||
const platformFeatures = [
|
||||
{ title: "Creative pipeline", description: "Streamlined asset management and variant testing." },
|
||||
{ title: "Audience architecture", description: "Dynamic segmentation across all touchpoints." },
|
||||
{ title: "Bid & budget governance", description: "Automated controls to prevent overspend and maximize ROAS." },
|
||||
{ title: "Unified reporting", description: "One dashboard for all media investments." },
|
||||
{ title: "Multi-market", description: "Seamless international and localized campaign execution." },
|
||||
{ title: "Server-side tracking", description: "Resilient data capture in a privacy-first web." }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Audit & Architecture", description: "We analyze historical data and structure a robust tracking foundation." },
|
||||
{ title: "Creative & Copy", description: "Developing ad variants engineered for algorithmic platforms." },
|
||||
{ title: "Launch & Governance", description: "Deploying campaigns with strict budget pacing and bid controls." },
|
||||
{ title: "Scale & Compounding", description: "Continuous optimization to drive down CPA and scale volume." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "What platforms do you manage?", answer: "We manage Google Ads, Microsoft Ads, Meta (Facebook/Instagram), TikTok, LinkedIn, Pinterest, as well as comprehensive Programmatic and CTV buys." },
|
||||
{ question: "How do you handle attribution?", answer: "We deploy server-side tracking and advanced modeling through the Atrilyx platform to ensure every conversion is accurately credited, even in cookieless environments." },
|
||||
{ question: "Do you produce ad creative?", answer: "Yes. Our team designs and iterates on performance-focused creative—from statics to short-form video—directly integrated into the campaign feedback loop." },
|
||||
{ question: "How is Atrilyx different from a standard agency?", answer: "We treat media as an investment portfolio. We don't just pull levers; we engineer the entire ecosystem (tracking, creative, landing pages) to guarantee business outcomes." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="Paid media that pays for itself, engineered on Atrilyx."
|
||||
subheading="We build and run performance campaigns where every impression is accountable to a business outcome. Creative, audiences, bidding, and measurement operate as one system — not four disconnected tools."
|
||||
tagline="Search & Social · Programmatic & CTV · Powered by Atrilyx"
|
||||
image="https://images.unsplash.com/photo-1674027392842-29f8354e236c"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<h2 className="service-heading text-balance">What we do — Media that behaves like an investment.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
When media is disconnected from the underlying business reality, it becomes a sinkhole. We integrate your paid media strategy directly into your growth architecture. By unifying creative production, precise audience targeting, and rigorous budget governance under the Atrilyx engine, we turn advertising into a predictable lever for scale.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — Full-funnel paid media" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><PlatformSection title="Platform · Atrilyx — Atrilyx is the engine under every campaign we run." description="Our proprietary platform bridges the gap between ad platforms and your CRM, ensuring clean data, resilient tracking, and real-time budget controls." features={platformFeatures} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — From audit to compounding returns" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Ready to make your ad spend accountable?" /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PaidMediaPage;
|
||||
@@ -0,0 +1,84 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import PlatformSection from '@/components/PlatformSection';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const SeoPage = () => {
|
||||
usePageTitle("SEO Services & Search Optimization | meSEO — AI Growth Technologies");
|
||||
|
||||
const MESEO_LOGO_URL = "https://horizons-cdn.hostinger.com/cbcecfe5-8f3e-47c5-bf6c-fcb30d4288f5/01a94c9b12f74941f3e200e28aec662e.png";
|
||||
|
||||
const capabilities = [
|
||||
{ title: "Technical SEO", description: "Architecture, crawlability, and Core Web Vitals optimization." },
|
||||
{ title: "On-Page Optimization", description: "Keyword targeting and metadata engineered for CTR." },
|
||||
{ title: "Content Strategy", description: "Pillar/cluster modeling to capture full topical authority." },
|
||||
{ title: "Local SEO", description: "Map pack dominance for multi-location enterprises." },
|
||||
{ title: "Authority Building", description: "High-trust backlink acquisition and PR placements." },
|
||||
{ title: "Behavioral Analytics", description: "Post-click user tracking powered by meSEO." }
|
||||
];
|
||||
|
||||
const platformFeatures = [
|
||||
{ title: "Cookieless site tag", description: "Privacy-first tracking of organic user journeys." },
|
||||
{ title: "AI site analysis", description: "Automated health checks and opportunity scoring." },
|
||||
{ title: "Keyword intelligence", description: "Real-time search volume and intent clustering." },
|
||||
{ title: "Competitor tracking", description: "Share of voice monitoring against your core rivals." },
|
||||
{ title: "Conversion funnels", description: "Seeing exactly where organic traffic drops off." },
|
||||
{ title: "Rank tracking", description: "Daily SERP updates across global and local markets." }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Technical Baseline", description: "Fixing indexation, site speed, and architectural roadblocks." },
|
||||
{ title: "Topic Modeling", description: "Mapping search intent to existing and missing content." },
|
||||
{ title: "Execution & PR", description: "Publishing optimized content and acquiring authoritative links." },
|
||||
{ title: "Behavioral Review", description: "Using meSEO to analyze how organic visitors navigate and convert." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "How long does it take to see SEO results?", answer: "While technical fixes can yield quick indexation bumps, a compounding organic strategy typically requires 3 to 6 months to demonstrate significant traffic and conversion lift." },
|
||||
{ question: "What makes your approach different?", answer: "We don't stop at rankings. By utilizing our meSEO platform, we track behavioral analytics to ensure the organic traffic you earn actually turns into revenue." },
|
||||
{ question: "Do you handle content writing?", answer: "Yes, our team produces high-quality, intent-matched content that appeals to both search algorithms and human readers." },
|
||||
{ question: "Is technical SEO included?", answer: "Absolutely. A strong technical foundation is non-negotiable. We handle everything from schema markup to Core Web Vitals." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="SEO that ranks — measured all the way to the conversion."
|
||||
subheading="We engineer organic visibility with technical, on-page, and content work, then prove it with meSEO — our behavioral analytics platform that tracks what visitors do after they land, not just where you rank."
|
||||
tagline="Technical & On-Page · Content & Authority · Measured in meSEO"
|
||||
image="https://images.unsplash.com/photo-1625296276703-3fbc924f07b5"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-8 mb-12">
|
||||
<div>
|
||||
<h2 className="service-heading text-balance">What we do — Rankings are the start. Behavior is the proof.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-4 bg-secondary border border-border rounded-xl">
|
||||
<span className="text-[10px] text-muted-foreground font-bold uppercase tracking-widest mb-2">Powered By</span>
|
||||
<img src={MESEO_LOGO_URL} alt="meSEO" className="h-10 w-auto" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
Traditional SEO celebrates when a keyword hits position one. We celebrate when that position one ranking results in a closed deal. By combining rigorous technical and content SEO with the deep behavioral analytics of meSEO, we optimize for the entire user journey.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — A complete search program" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><PlatformSection title="Platform · meSEO — meSEO turns SEO from a guessing game into an instrument." description="Our proprietary meSEO analytics engine provides unprecedented visibility into how organic traffic behaves, ensuring you capture maximum value from your rankings." features={platformFeatures} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — A search program that compounds" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Want SEO you can actually measure?" /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SeoPage;
|
||||
@@ -0,0 +1,99 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { useParams, Navigate, Link } from 'react-router-dom';
|
||||
import { servicesData } from '@/lib/servicesData';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CheckCircle2, ArrowLeft, ArrowRight } from 'lucide-react';
|
||||
|
||||
const ServiceDetail = () => {
|
||||
const { slug } = useParams();
|
||||
const service = servicesData.find((s) => s.slug === slug);
|
||||
|
||||
const pageTitle = service ? `${service.title} | AI Growth Technologies` : "Service Not Found | AI Growth Technologies";
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
if (!service) {
|
||||
return <Navigate to="/services" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="pt-28 md:pt-32 pb-20 min-h-screen bg-background relative overflow-hidden">
|
||||
{/* Background Effects */}
|
||||
<div className="absolute inset-0 bg-tech-grid [mask-image:radial-gradient(ellipse_at_top_right,black,transparent_70%)] opacity-60 pointer-events-none" />
|
||||
<div className="absolute top-0 right-0 w-[500px] h-[500px] glow-red rounded-full filter blur-[120px] pointer-events-none" />
|
||||
|
||||
<div className="container mx-auto px-4 max-w-7xl relative z-10">
|
||||
<Link to="/services" className="inline-flex items-center text-muted-foreground hover:text-primary mb-8 transition-colors">
|
||||
<ArrowLeft size={20} className="mr-2" /> Back to Services
|
||||
</Link>
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-12 lg:gap-16 items-start">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className={`w-20 h-20 rounded-2xl bg-gradient-to-br ${service.gradient} flex items-center justify-center mb-8 shadow-lg`}>
|
||||
<service.icon className="text-white" size={40} />
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl md:text-6xl font-black text-foreground mb-6">
|
||||
{service.title}
|
||||
</h1>
|
||||
|
||||
<p className="text-xl text-muted-foreground mb-8 leading-relaxed">
|
||||
{service.description}
|
||||
</p>
|
||||
|
||||
<div className="text-lg text-muted-foreground leading-relaxed mb-10 max-w-2xl">
|
||||
<p>{service.longDescription}</p>
|
||||
</div>
|
||||
|
||||
<Link to="/contact">
|
||||
<Button size="lg" className="bg-primary hover:bg-primary/90 text-white px-8 py-6 rounded-full text-lg shadow-lg shadow-primary/25 group">
|
||||
Get Started <ArrowRight className="ml-2 w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
||||
</Button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="bg-white border border-border rounded-3xl p-8 lg:p-10 shadow-xl shadow-black/5"
|
||||
>
|
||||
<h3 className="text-2xl font-bold text-foreground mb-8">Key Benefits</h3>
|
||||
<div className="space-y-6">
|
||||
{service.benefits.map((benefit, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.4 + (index * 0.1) }}
|
||||
className="flex items-start"
|
||||
>
|
||||
<div className="mt-1 mr-4 flex-shrink-0">
|
||||
<CheckCircle2 className="text-primary" size={24} />
|
||||
</div>
|
||||
<p className="text-lg text-foreground/80">{benefit}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-10 p-6 bg-primary/5 rounded-2xl border border-primary/20">
|
||||
<h4 className="text-primary font-semibold mb-2">Why Choose Us?</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Our {service.title.toLowerCase()} services are backed by data-driven insights and a team of dedicated experts committed to your long-term success.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceDetail;
|
||||
@@ -0,0 +1,112 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import Breadcrumbs from '@/components/Breadcrumbs';
|
||||
|
||||
const ServicesHubPage = () => {
|
||||
const pageTitle = "Services | AI Growth Technologies";
|
||||
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
const services = [
|
||||
{
|
||||
title: "Paid Media",
|
||||
tagline: "Search & Social · Programmatic & CTV · Powered by Atrilyx",
|
||||
description: "We build and run performance campaigns where every impression is accountable to a business outcome. Creative, audiences, bidding, and measurement operate as one system.",
|
||||
link: "/services/paid-media"
|
||||
},
|
||||
{
|
||||
title: "SEO",
|
||||
tagline: "Technical & On-Page · Content & Authority · Measured in meSEO",
|
||||
description: "We engineer organic visibility with technical, on-page, and content work, then prove it with meSEO — tracking what visitors do after they land, not just where you rank.",
|
||||
link: "/services/seo"
|
||||
},
|
||||
{
|
||||
title: "LLMO / AEO / GEO",
|
||||
tagline: "LLMO · AEO · GEO · AI Visibility Monitoring · Verified in meSEO",
|
||||
description: "Search is moving inside AI. We optimize how ChatGPT, Gemini, Claude, Copilot, and AI Overviews understand and cite your brand.",
|
||||
link: "/services/llmo"
|
||||
},
|
||||
{
|
||||
title: "Web Development",
|
||||
tagline: "Design & Build · Web Applications · Ongoing Maintenance",
|
||||
description: "We design and build sites and web apps that load fast, convert well, and give your SEO, LLMO, and paid media a solid foundation.",
|
||||
link: "/services/web-development"
|
||||
},
|
||||
{
|
||||
title: "Social Media",
|
||||
tagline: "International Accounts · Content & Community · Paid Amplification",
|
||||
description: "We run international social accounts end-to-end across regulated and highly competitive industries, combining localized strategy with global brand consistency.",
|
||||
link: "/services/social-media"
|
||||
},
|
||||
{
|
||||
title: "Brand Creation & Optimization",
|
||||
tagline: "Positioning & Identity · Messaging Systems · Built to Perform",
|
||||
description: "We create and optimize brands from positioning to identity to messaging, then build them to perform natively across every channel we run.",
|
||||
link: "/services/brand"
|
||||
},
|
||||
{
|
||||
title: "Digital Marketing Consulting",
|
||||
tagline: "Audits & Roadmaps · Growth Strategy · Fractional Advisory",
|
||||
description: "Senior strategy before you spend the budget. Get clarity with channel audits, roadmaps, and fractional advisory to guide your next phase of growth.",
|
||||
link: "/services/consulting"
|
||||
},
|
||||
{
|
||||
title: "Cybersecurity (Korrith)",
|
||||
tagline: "Assessment & Hardening · Monitoring & Response · Powered by Korrith",
|
||||
description: "Growth creates risk. Korrith protects the digital assets and visibility you work so hard to earn through hardening, monitoring, and incident response.",
|
||||
link: "/services/cybersecurity"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="pt-32 pb-20 md:pt-40 md:pb-28">
|
||||
<div className="container mx-auto px-4 max-w-7xl">
|
||||
<Breadcrumbs />
|
||||
|
||||
<div className="max-w-4xl mb-16">
|
||||
<h1 className="text-4xl md:text-6xl font-bold tracking-tight mb-6 h1-theme">
|
||||
Growth channels, <br className="hidden md:block"/> engineered as one system.
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl text-muted-foreground leading-relaxed">
|
||||
We run paid media, SEO, AI optimization, and a full suite of strategic growth services — not as separate tools, but as an integrated system where each makes the others stronger.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{services.map((service, index) => (
|
||||
<Card key={index} className="flex flex-col h-full hover:border-primary/50 transition-colors duration-300">
|
||||
<CardHeader>
|
||||
<Badge variant="secondary" className="w-fit mb-4 text-xs font-normal">
|
||||
{service.tagline}
|
||||
</Badge>
|
||||
<CardTitle className="text-2xl">{service.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-grow">
|
||||
<p className="text-muted-foreground text-base leading-relaxed">
|
||||
{service.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Link
|
||||
to={service.link}
|
||||
className="flex items-center text-primary font-semibold hover:text-primary/80 transition-colors group"
|
||||
>
|
||||
Explore Service
|
||||
<ArrowRight className="ml-2 w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesHubPage;
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import Services from '@/components/Services';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const ServicesPage = () => {
|
||||
const pageTitle = "Our Services | AI Growth Technologies";
|
||||
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="pt-20 bg-background">
|
||||
<Services />
|
||||
<section className="bg-zinc-950 py-20">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
className="text-3xl md:text-4xl font-bold text-white mb-6"
|
||||
>
|
||||
Custom Solutions for Your Business
|
||||
</motion.h2>
|
||||
<p className="text-gray-400 max-w-2xl mx-auto text-lg">
|
||||
Don't see exactly what you're looking for? We create custom packages tailored to your specific needs and goals.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesPage;
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import IndustriesTable from '@/components/IndustriesTable';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const SocialMediaPage = () => {
|
||||
usePageTitle("Social Media Management Agency | AI Growth Technologies");
|
||||
|
||||
const capabilities = [
|
||||
{ title: "Strategy & Planning", description: "Multi-market playbooks, brand voice definition, and content calendaring." },
|
||||
{ title: "Content Production", description: "High-quality graphic design, video editing, and localized copywriting." },
|
||||
{ title: "Community Management", description: "Proactive engagement, reputation monitoring, and customer service." },
|
||||
{ title: "Paid Amplification", description: "Targeted boosting and social ads to guarantee reach and ROI." },
|
||||
{ title: "Influencer Coordination", description: "Sourcing, vetting, and managing creator partnerships globally." },
|
||||
{ title: "Reporting", description: "Comprehensive analytics mapping social metrics to business outcomes." }
|
||||
];
|
||||
|
||||
const industries = [
|
||||
{ title: "Hospitality", examples: ["Luxury Hotels & Resorts", "Restaurant Groups", "Travel Destinations"] },
|
||||
{ title: "Medical & Clinical", examples: ["Dental Practices", "Cosmetic Surgery Clinics", "Specialized Healthcare"] },
|
||||
{ title: "Automotive", examples: ["OEM Dealerships", "Luxury Car Rentals", "Automotive Aftermarket"] },
|
||||
{ title: "E-commerce", examples: ["DTC Retail", "Fashion & Apparel", "Consumer Goods"] }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Audit & Strategy", description: "Analyzing current performance, defining the voice, and mapping out a market-specific approach." },
|
||||
{ title: "Content Engine", description: "Establishing a predictable, high-quality production pipeline tailored to each platform." },
|
||||
{ title: "Engage & Amplify", description: "Publishing content, fostering community dialogue, and deploying paid media to scale reach." },
|
||||
{ title: "Report & Refine", description: "Monthly deep-dives into what's driving actual business value, optimizing for the next cycle." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "Do you manage international social media accounts?", answer: "Yes. We specialize in cross-border social media management, handling multiple regions, languages, and localized cultural nuances to ensure brand consistency globally." },
|
||||
{ question: "Does your service include content creation?", answer: "Absolutely. We manage end-to-end content production including copywriting, visual design, video editing, and influencer coordination." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="Social media management for brands that operate across borders."
|
||||
subheading="We run international social accounts end-to-end — strategy, content, community, and paid amplification — across hospitality, healthcare, automotive, and e-commerce."
|
||||
tagline="International Accounts · Content & Community · Paid Amplification"
|
||||
image="https://images.unsplash.com/photo-1567005197762-1dcee73e230d"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<h2 className="service-heading text-balance">What we do — One consistent brand voice.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
Running social media across different regions, languages, and regulated industries requires more than just scheduling posts. It requires a localized strategy rooted in a singular global brand voice. We handle the complexities of international social media, ensuring your brand resonates authentically no matter where your audience is.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — End-to-end social" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><IndustriesTable title="Industries — Verticals we run every day" intro="We bring specialized playbooks to highly competitive and regulated industries." industries={industries} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — From handover to momentum" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Ready to hand off social to a team that gets it?" /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SocialMediaPage;
|
||||
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import ServiceHero from '@/components/ServiceHero';
|
||||
import CapabilitiesTable from '@/components/CapabilitiesTable';
|
||||
import HowWeWorkSection from '@/components/HowWeWorkSection';
|
||||
import FAQSection from '@/components/FAQSection';
|
||||
import ServiceCTA from '@/components/ServiceCTA';
|
||||
import ScrollReveal from '@/components/ScrollReveal';
|
||||
|
||||
const WebDevPage = () => {
|
||||
usePageTitle("Website Development & Maintenance | AI Growth Technologies");
|
||||
|
||||
const capabilities = [
|
||||
{ title: "Marketing Sites", description: "High-performance websites built for lead generation." },
|
||||
{ title: "Web Applications", description: "Custom logic, portals, and complex integrations." },
|
||||
{ title: "Performance & Core Web Vitals", description: "Lightning-fast load times for SEO and UX." },
|
||||
{ title: "SEO & AI Foundations", description: "Architectures designed for crawlers and LLMs." },
|
||||
{ title: "Maintenance & Security", description: "Ongoing updates, backups, and threat mitigation." },
|
||||
{ title: "Continuous Improvement", description: "A/B testing and iterative UX enhancements." }
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ title: "Discovery & UX", description: "Mapping user journeys and defining technical requirements." },
|
||||
{ title: "Design & Architecture", description: "Creating high-fidelity prototypes and scalable data structures." },
|
||||
{ title: "Development & QA", description: "Building with modern frameworks, rigorous testing, and CRM integration." },
|
||||
{ title: "Launch & Maintain", description: "Seamless deployment followed by proactive security and performance care." }
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{ question: "What technologies do you use?", answer: "We utilize modern, high-performance tech stacks including React, Next.js, Node, and headless CMS architectures, ensuring speed, security, and scalability." },
|
||||
{ question: "Do you offer ongoing maintenance?", answer: "Yes. A website is a living asset. We provide continuous maintenance, security patching, and iterative performance optimizations." },
|
||||
{ question: "How does web development integrate with SEO and Paid Media?", answer: "We build sites with tracking, Core Web Vitals, and conversion rate optimization (CRO) built-in from day one, ensuring your media spends and SEO efforts have the best possible landing environment." }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ServiceHero
|
||||
title="Websites built to convert — and maintained to stay fast."
|
||||
subheading="A website is your most important growth asset. We design and build sites and web apps that load fast, convert well, and give your SEO, LLMO, and paid media a foundation worth spending on."
|
||||
tagline="Design & Build · Web Applications · Maintenance"
|
||||
image="https://images.unsplash.com/photo-1593720213428-28a5b9e94613"
|
||||
/>
|
||||
|
||||
<ScrollReveal className="service-section border-b border-border">
|
||||
<h2 className="service-heading text-balance">What we do — A website is infrastructure, not a brochure.</h2>
|
||||
<div className="w-20 h-1 bg-primary mb-8"></div>
|
||||
<div className="prose prose-invert max-w-3xl text-muted-foreground text-lg leading-relaxed">
|
||||
<p>
|
||||
Beautiful design is meaningless if the site is slow, unsecure, or disconnected from your marketing data. We build web platforms that act as the central nervous system for your digital presence. Engineered for Core Web Vitals, conversion optimization, and deep tracking integration.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal><CapabilitiesTable title="Capabilities — Design, build, maintain" capabilities={capabilities} /></ScrollReveal>
|
||||
<ScrollReveal><HowWeWorkSection title="How we work — From brief to live and cared-for" steps={steps} /></ScrollReveal>
|
||||
<ScrollReveal><FAQSection faqs={faqs} /></ScrollReveal>
|
||||
<ScrollReveal><ServiceCTA text="Let's build a site that earns its keep." /></ScrollReveal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default WebDevPage;
|
||||
@@ -0,0 +1,139 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowUpRight, Users, Eye, MousePointer, Clock, LayoutDashboard } from 'lucide-react';
|
||||
|
||||
const StatCard = ({ title, value, change, icon: Icon, color }) => (
|
||||
<div className="bg-card border border-border rounded-xl p-6 hover:border-primary/50 transition-colors">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className={`p-2 rounded-lg bg-${color}-500/10 text-${color}-500`}>
|
||||
<Icon size={20} />
|
||||
</div>
|
||||
<span className="flex items-center text-green-500 dark:text-green-400 text-xs font-medium bg-green-500/10 px-2 py-1 rounded-full">
|
||||
+{change}% <ArrowUpRight size={12} className="ml-1" />
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-muted text-sm font-medium mb-1">{title}</h3>
|
||||
<p className="text-2xl font-bold text-heading">{value}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const DashboardOverview = () => {
|
||||
const pageTitle = "Executive Summary | Client Portal";
|
||||
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
{/* Standardized Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3 h1-theme">
|
||||
<LayoutDashboard className="text-primary" />
|
||||
Executive Summary
|
||||
</h1>
|
||||
<p className="text-muted">High-level performance metrics across all digital channels.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<StatCard
|
||||
title="Total Impressions"
|
||||
value="1.2M"
|
||||
change="12.5"
|
||||
icon={Eye}
|
||||
color="blue"
|
||||
/>
|
||||
<StatCard
|
||||
title="Total Visitors"
|
||||
value="45.2k"
|
||||
change="8.2"
|
||||
icon={Users}
|
||||
color="purple"
|
||||
/>
|
||||
<StatCard
|
||||
title="Engagement Rate"
|
||||
value="4.8%"
|
||||
change="2.4"
|
||||
icon={MousePointer}
|
||||
color="green"
|
||||
/>
|
||||
<StatCard
|
||||
title="Avg. Session"
|
||||
value="2m 45s"
|
||||
change="5.1"
|
||||
icon={Clock}
|
||||
color="orange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* Recent Activity */}
|
||||
<div className="bg-card border border-border rounded-xl p-6">
|
||||
<h3 className="text-lg font-bold text-heading mb-6">Campaign Performance Status</h3>
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{ name: 'Summer Sale Promo', status: 'Active', reach: '45k', roi: '+145%' },
|
||||
{ name: 'Brand Awareness Q3', status: 'Completed', reach: '120k', roi: '+85%' },
|
||||
{ name: 'New Product Launch', status: 'Active', reach: '22k', roi: '+112%' },
|
||||
].map((campaign, i) => (
|
||||
<div key={i} className="flex items-center justify-between p-4 bg-secondary/30 rounded-lg border border-border hover:bg-secondary/50 transition-colors">
|
||||
<div>
|
||||
<p className="font-medium text-heading">{campaign.name}</p>
|
||||
<p className="text-xs text-muted flex items-center mt-1">
|
||||
<span className={`w-2 h-2 rounded-full mr-2 ${campaign.status === 'Active' ? 'bg-green-500' : 'bg-slate-500'}`}></span>
|
||||
{campaign.status}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="font-bold text-green-600 dark:text-green-400">{campaign.roi}</p>
|
||||
<p className="text-xs text-muted">{campaign.reach} reach</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Traffic Sources Chart */}
|
||||
<div className="bg-card border border-border rounded-xl p-6 flex flex-col">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h3 className="text-lg font-bold text-heading">Weekly Traffic Overview</h3>
|
||||
<select className="bg-secondary border border-border text-xs text-body rounded-lg px-2 py-1">
|
||||
<option>Last 7 Days</option>
|
||||
<option>Last 30 Days</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex items-end justify-between gap-4 px-2 min-h-[200px]">
|
||||
{[65, 40, 85, 55, 70, 45, 60].map((h, i) => (
|
||||
<div key={i} className="w-full bg-secondary rounded-t-lg relative group">
|
||||
<div
|
||||
className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-primary/80 to-primary rounded-t-lg transition-all duration-500 group-hover:opacity-80"
|
||||
style={{ height: `${h}%` }}
|
||||
></div>
|
||||
<div className="opacity-0 group-hover:opacity-100 absolute -top-10 left-1/2 -translate-x-1/2 bg-popover text-popover-foreground text-xs font-bold py-1 px-2 rounded shadow-lg transition-opacity pointer-events-none border border-border">
|
||||
{h * 100} visits
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-between mt-4 text-xs text-muted px-2 uppercase tracking-wide">
|
||||
<span>Mon</span>
|
||||
<span>Tue</span>
|
||||
<span>Wed</span>
|
||||
<span>Thu</span>
|
||||
<span>Fri</span>
|
||||
<span>Sat</span>
|
||||
<span>Sun</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardOverview;
|
||||
@@ -0,0 +1,110 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { motion } from 'framer-motion';
|
||||
import { BarChart3, Globe, Smartphone, Clock, ArrowUpRight } from 'lucide-react';
|
||||
|
||||
const card = 'bg-white border border-border rounded-xl p-6 hover:border-primary/30 transition-colors';
|
||||
|
||||
const GA4Dashboard = () => {
|
||||
const pageTitle = 'GA4 Analytics | Client Portal';
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3 h1-theme">
|
||||
<BarChart3 className="text-orange-500" />
|
||||
Google Analytics 4
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Traffic, acquisition, and user behavior analysis.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
{[
|
||||
{ label: 'Active Users', val: '1,429', sub: 'Last 30 mins', icon: BarChart3 },
|
||||
{ label: 'Avg. Engagement', val: '2m 14s', sub: '+12% vs last week', icon: Clock },
|
||||
{ label: 'Mobile Traffic', val: '68%', sub: 'Device share', icon: Smartphone },
|
||||
{ label: 'Top Country', val: 'USA', sub: '45% of traffic', icon: Globe },
|
||||
].map((c, i) => (
|
||||
<div key={i} className={`${card} group`}>
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="p-2 bg-orange-500/10 rounded-lg text-orange-500 group-hover:text-orange-400 transition-colors">
|
||||
<c.icon size={20} />
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground font-medium px-2 py-1 bg-secondary rounded-full">Live</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold text-foreground mb-1">{c.val}</p>
|
||||
<p className="text-sm text-muted-foreground font-medium">{c.label}</p>
|
||||
<p className="text-xs text-muted-foreground/70 mt-2 flex items-center">
|
||||
<ArrowUpRight size={10} className="mr-1 text-green-500" /> {c.sub}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div className={card}>
|
||||
<h3 className="text-lg font-bold text-foreground mb-6">User Acquisition Channels</h3>
|
||||
<div className="space-y-5">
|
||||
{[
|
||||
{ channel: 'Organic Search', users: '12,405', pct: 85, color: 'bg-orange-500' },
|
||||
{ channel: 'Direct', users: '4,200', pct: 45, color: 'bg-blue-500' },
|
||||
{ channel: 'Paid Social', users: '3,150', pct: 35, color: 'bg-purple-500' },
|
||||
{ channel: 'Email', users: '1,800', pct: 20, color: 'bg-green-500' },
|
||||
{ channel: 'Referral', users: '950', pct: 10, color: 'bg-slate-400' },
|
||||
].map((item, i) => (
|
||||
<div key={i} className="relative">
|
||||
<div className="flex justify-between text-sm mb-2 z-10 relative">
|
||||
<span className="text-foreground font-medium">{item.channel}</span>
|
||||
<span className="text-muted-foreground">{item.users}</span>
|
||||
</div>
|
||||
<div className="h-2 bg-secondary rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: `${item.pct}%` }}
|
||||
transition={{ duration: 0.8, delay: i * 0.1 }}
|
||||
className={`h-full ${item.color} rounded-full`}
|
||||
></motion.div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={card}>
|
||||
<h3 className="text-lg font-bold text-foreground mb-6">Top Viewed Pages</h3>
|
||||
<div className="overflow-hidden">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr className="text-muted-foreground text-xs uppercase tracking-wider border-b border-border">
|
||||
<th className="pb-3 font-medium">Page Title</th>
|
||||
<th className="pb-3 font-medium text-right">Views</th>
|
||||
<th className="pb-3 font-medium text-right">Unique</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-sm">
|
||||
{[
|
||||
{ title: 'Home | AIGrowth', views: '45,200', unique: '32k' },
|
||||
{ title: 'Services - SEO', views: '12,800', unique: '9.5k' },
|
||||
{ title: 'Blog: Top 10 AI Tools', views: '8,400', unique: '7.2k' },
|
||||
{ title: 'Contact Us', views: '5,200', unique: '4.1k' },
|
||||
{ title: 'Case Studies', views: '3,100', unique: '2.8k' },
|
||||
].map((row, i) => (
|
||||
<tr key={i} className="border-b border-border/60 last:border-0 hover:bg-secondary/50 transition-colors group">
|
||||
<td className="py-4 text-foreground group-hover:text-primary transition-colors">{row.title}</td>
|
||||
<td className="py-4 text-right text-foreground/80">{row.views}</td>
|
||||
<td className="py-4 text-right text-muted-foreground">{row.unique}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GA4Dashboard;
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Search, TrendingUp, Eye, MousePointer2 } from 'lucide-react';
|
||||
|
||||
const card = 'bg-white border border-border rounded-xl';
|
||||
|
||||
const GSCDashboard = () => {
|
||||
const pageTitle = 'GSC Data | Client Portal';
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3 h1-theme">
|
||||
<Search className="text-green-500" />
|
||||
Google Search Console
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Organic search performance and keyword rankings.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6 mb-8">
|
||||
{[
|
||||
{ label: 'Total Clicks', val: '12.4K', icon: MousePointer2, color: 'text-blue-500', bg: 'bg-blue-500/10' },
|
||||
{ label: 'Total Impressions', val: '450K', icon: Eye, color: 'text-purple-500', bg: 'bg-purple-500/10' },
|
||||
{ label: 'Avg. CTR', val: '2.8%', icon: TrendingUp, color: 'text-green-500', bg: 'bg-green-500/10' },
|
||||
{ label: 'Avg. Position', val: '14.2', icon: Search, color: 'text-orange-500', bg: 'bg-orange-500/10' },
|
||||
].map((stat, i) => (
|
||||
<div key={i} className={`${card} p-6 text-center hover:border-primary/30 transition-colors`}>
|
||||
<div className={`mx-auto w-12 h-12 rounded-full ${stat.bg} flex items-center justify-center mb-4`}>
|
||||
<stat.icon className={stat.color} size={24} />
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-foreground mb-1">{stat.val}</p>
|
||||
<p className="text-sm text-muted-foreground">{stat.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className={`${card} overflow-hidden`}>
|
||||
<div className="p-6 border-b border-border">
|
||||
<h3 className="text-lg font-bold text-foreground">Top Performing Keywords</h3>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left">
|
||||
<thead>
|
||||
<tr className="text-muted-foreground text-xs uppercase tracking-wider bg-secondary/60">
|
||||
<th className="p-4 font-medium">Query</th>
|
||||
<th className="p-4 font-medium text-right">Clicks</th>
|
||||
<th className="p-4 font-medium text-right">Impressions</th>
|
||||
<th className="p-4 font-medium text-right">CTR</th>
|
||||
<th className="p-4 font-medium text-right">Position</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-sm divide-y divide-border">
|
||||
{[
|
||||
{ q: 'ai marketing services', c: '1,240', i: '12,500', ctr: '9.9%', p: '2.4' },
|
||||
{ q: 'digital growth agency', c: '980', i: '8,400', ctr: '11.6%', p: '1.8' },
|
||||
{ q: 'seo automation tools', c: '650', i: '15,200', ctr: '4.2%', p: '5.6' },
|
||||
{ q: 'social media management dubai', c: '420', i: '3,100', ctr: '13.5%', p: '3.1' },
|
||||
{ q: 'web development houston', c: '310', i: '2,800', ctr: '11.0%', p: '4.2' },
|
||||
{ q: 'llm optimization', c: '280', i: '9,500', ctr: '2.9%', p: '8.4' },
|
||||
].map((row, i) => (
|
||||
<tr key={i} className="hover:bg-secondary/40 transition-colors">
|
||||
<td className="p-4 text-primary font-medium">{row.q}</td>
|
||||
<td className="p-4 text-right text-foreground">{row.c}</td>
|
||||
<td className="p-4 text-right text-muted-foreground">{row.i}</td>
|
||||
<td className="p-4 text-right text-green-600">{row.ctr}</td>
|
||||
<td className="p-4 text-right text-foreground/80">{row.p}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GSCDashboard;
|
||||
@@ -0,0 +1,149 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Target, TrendingUp, MousePointerClick, CreditCard, ArrowUpRight, ArrowDownRight } from 'lucide-react';
|
||||
|
||||
const card = 'bg-white border border-border rounded-xl p-6 hover:border-primary/30 transition-colors';
|
||||
|
||||
const GoogleAdsDashboard = () => {
|
||||
const pageTitle = 'Google Ads Performance | Client Portal';
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
|
||||
<div className="mb-8 flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3 h1-theme">
|
||||
<Target className="text-blue-500" />
|
||||
Google Ads Performance
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Campaign metrics, cost analysis, and conversion tracking.</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<span className="px-3 py-1 rounded-full bg-blue-500/10 text-blue-600 text-xs font-medium border border-blue-500/20">Last 30 Days</span>
|
||||
<span className="px-3 py-1 rounded-full bg-secondary text-muted-foreground text-xs font-medium border border-border">Account: 382-992-1022</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
{[
|
||||
{ label: 'Total Cost', val: '$4,250.00', change: '+12.5%', trend: 'up', icon: CreditCard, color: 'blue' },
|
||||
{ label: 'Conversions', val: '342', change: '+8.2%', trend: 'up', icon: Target, color: 'green' },
|
||||
{ label: 'Avg. CPC', val: '$2.45', change: '-2.4%', trend: 'down', icon: MousePointerClick, color: 'purple' },
|
||||
{ label: 'Click-Through Rate', val: '4.8%', change: '+1.1%', trend: 'up', icon: TrendingUp, color: 'orange' },
|
||||
].map((stat, i) => (
|
||||
<div key={i} className={card}>
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className={`p-2 rounded-lg bg-${stat.color}-500/10 text-${stat.color}-500`}>
|
||||
<stat.icon size={20} />
|
||||
</div>
|
||||
<span className={`flex items-center text-xs font-medium px-2 py-1 rounded-full ${stat.trend === 'up' ? 'bg-green-500/10 text-green-600' : 'bg-red-500/10 text-red-500'}`}>
|
||||
{stat.change} {stat.trend === 'up' ? <ArrowUpRight size={12} className="ml-1" /> : <ArrowDownRight size={12} className="ml-1" />}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-muted-foreground text-sm font-medium mb-1">{stat.label}</h3>
|
||||
<p className="text-2xl font-bold text-foreground">{stat.val}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
|
||||
<div className="lg:col-span-2 bg-white border border-border rounded-xl p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h3 className="text-lg font-bold text-foreground">Cost vs Conversions</h3>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground"><span className="w-3 h-3 bg-blue-500 rounded-full"></span> Cost</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground"><span className="w-3 h-3 bg-green-500 rounded-full"></span> Conv.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-64 flex items-end justify-between gap-2 px-4">
|
||||
{[65, 59, 80, 81, 56, 55, 40, 70, 65, 85, 90, 75].map((h, i) => (
|
||||
<div key={i} className="w-full relative group h-full flex flex-col justify-end gap-1">
|
||||
<div className="w-full bg-green-500/80 rounded-t-sm transition-all hover:bg-green-400" style={{ height: `${h * 0.6}%` }}></div>
|
||||
<div className="w-full bg-blue-500/80 rounded-t-sm transition-all hover:bg-blue-400" style={{ height: `${h}%` }}></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-between mt-4 text-xs text-muted-foreground px-2 border-t border-border pt-2">
|
||||
<span>Jan</span><span>Feb</span><span>Mar</span><span>Apr</span><span>May</span><span>Jun</span>
|
||||
<span>Jul</span><span>Aug</span><span>Sep</span><span>Oct</span><span>Nov</span><span>Dec</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-border rounded-xl p-6">
|
||||
<h3 className="text-lg font-bold text-foreground mb-6">Top Keywords</h3>
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{ term: 'marketing agency', clicks: 1240, cost: '$1.20' },
|
||||
{ term: 'seo services', clicks: 850, cost: '$2.40' },
|
||||
{ term: 'web design company', clicks: 620, cost: '$3.10' },
|
||||
{ term: 'ppc management', clicks: 450, cost: '$4.50' },
|
||||
{ term: 'social media marketing', clicks: 320, cost: '$1.80' },
|
||||
].map((item, i) => (
|
||||
<div key={i} className="flex items-center justify-between p-3 rounded-lg bg-secondary/60 hover:bg-secondary transition-colors border border-border">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-foreground">{item.term}</p>
|
||||
<p className="text-xs text-muted-foreground">{item.clicks} clicks</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-sm font-bold text-blue-600">{item.cost}</p>
|
||||
<p className="text-xs text-muted-foreground">CPC</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-border rounded-xl overflow-hidden">
|
||||
<div className="p-6 border-b border-border">
|
||||
<h3 className="text-lg font-bold text-foreground">Active Campaigns</h3>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="bg-secondary/60 text-muted-foreground uppercase text-xs tracking-wider">
|
||||
<th className="p-4 font-medium">Campaign</th>
|
||||
<th className="p-4 font-medium">Status</th>
|
||||
<th className="p-4 font-medium text-right">Impr.</th>
|
||||
<th className="p-4 font-medium text-right">Clicks</th>
|
||||
<th className="p-4 font-medium text-right">CTR</th>
|
||||
<th className="p-4 font-medium text-right">Cost</th>
|
||||
<th className="p-4 font-medium text-right">Conv.</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border">
|
||||
{[
|
||||
{ name: 'Brand Awareness - Q4', status: 'Active', imp: '45k', clicks: '1.2k', ctr: '2.6%', cost: '$1,200', conv: 45 },
|
||||
{ name: 'Lead Gen - Service A', status: 'Active', imp: '22k', clicks: '850', ctr: '3.8%', cost: '$950', conv: 82 },
|
||||
{ name: 'Retargeting - Visitors', status: 'Active', imp: '15k', clicks: '620', ctr: '4.1%', cost: '$450', conv: 120 },
|
||||
{ name: 'Competitor - Generic', status: 'Paused', imp: '8.5k', clicks: '150', ctr: '1.7%', cost: '$250', conv: 8 },
|
||||
{ name: 'YouTube Discovery', status: 'Active', imp: '120k', clicks: '3.5k', ctr: '2.9%', cost: '$1,400', conv: 32 },
|
||||
].map((row, i) => (
|
||||
<tr key={i} className="hover:bg-secondary/40 transition-colors">
|
||||
<td className="p-4 font-medium text-blue-600">{row.name}</td>
|
||||
<td className="p-4">
|
||||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${row.status === 'Active' ? 'bg-green-500/10 text-green-600' : 'bg-yellow-500/10 text-yellow-600'}`}>
|
||||
<span className={`w-1.5 h-1.5 rounded-full mr-1.5 ${row.status === 'Active' ? 'bg-green-500' : 'bg-yellow-500'}`}></span>
|
||||
{row.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="p-4 text-right text-muted-foreground">{row.imp}</td>
|
||||
<td className="p-4 text-right text-foreground/80">{row.clicks}</td>
|
||||
<td className="p-4 text-right text-foreground/80">{row.ctr}</td>
|
||||
<td className="p-4 text-right text-foreground font-medium">{row.cost}</td>
|
||||
<td className="p-4 text-right text-green-600 font-bold">{row.conv}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoogleAdsDashboard;
|
||||
@@ -0,0 +1,153 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Layers, Users, Eye, DollarSign, ArrowUpRight, Globe } from 'lucide-react';
|
||||
|
||||
const card = 'bg-white border border-border rounded-xl p-6 hover:border-primary/30 transition-colors relative overflow-hidden';
|
||||
|
||||
const MetaBusinessDashboard = () => {
|
||||
const pageTitle = 'Meta Business Suite | Client Portal';
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
|
||||
<div className="mb-8 flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3 h1-theme">
|
||||
<Layers className="text-blue-600" />
|
||||
Meta Business Suite
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Facebook & Instagram advertising performance and audience insights.</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<span className="px-3 py-1 rounded-full bg-blue-600/10 text-blue-600 text-xs font-medium border border-blue-600/20">Active</span>
|
||||
<span className="px-3 py-1 rounded-full bg-secondary text-muted-foreground text-xs font-medium border border-border">ID: 882719201</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
{[
|
||||
{ label: 'Amount Spent', val: '$2,105.50', change: '+5.5%', icon: DollarSign, color: 'blue' },
|
||||
{ label: 'Total Reach', val: '145,200', change: '+18.2%', icon: Users, color: 'indigo' },
|
||||
{ label: 'Impressions', val: '320,500', change: '+12.4%', icon: Eye, color: 'purple' },
|
||||
{ label: 'Link Clicks', val: '4,850', change: '+3.1%', icon: Globe, color: 'pink' },
|
||||
].map((stat, i) => (
|
||||
<div key={i} className={card}>
|
||||
<div className={`absolute top-0 right-0 w-24 h-24 bg-${stat.color}-500/5 rounded-full -mr-8 -mt-8 blur-xl`}></div>
|
||||
<div className="flex justify-between items-start mb-4 relative z-10">
|
||||
<div className={`p-2 rounded-lg bg-${stat.color}-500/10 text-${stat.color}-500`}>
|
||||
<stat.icon size={20} />
|
||||
</div>
|
||||
<span className="flex items-center text-green-600 text-xs font-medium bg-green-500/10 px-2 py-1 rounded-full">
|
||||
{stat.change} <ArrowUpRight size={12} className="ml-1" />
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-muted-foreground text-sm font-medium mb-1 relative z-10">{stat.label}</h3>
|
||||
<p className="text-2xl font-bold text-foreground relative z-10">{stat.val}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
|
||||
<div className="lg:col-span-2 bg-white border border-border rounded-xl p-6">
|
||||
<h3 className="text-lg font-bold text-foreground mb-6">Platform Performance</h3>
|
||||
<div className="space-y-6">
|
||||
{[
|
||||
{ platform: 'Facebook Feed', reach: '85k', color: 'bg-blue-600', pct: 90 },
|
||||
{ platform: 'Instagram Feed', reach: '62k', color: 'bg-pink-600', pct: 70 },
|
||||
{ platform: 'Instagram Stories', reach: '45k', color: 'bg-orange-500', pct: 55 },
|
||||
{ platform: 'Audience Network', reach: '22k', color: 'bg-slate-500', pct: 35 },
|
||||
{ platform: 'Messenger', reach: '15k', color: 'bg-blue-500', pct: 25 },
|
||||
].map((item, i) => (
|
||||
<div key={i} className="relative">
|
||||
<div className="flex justify-between text-sm mb-2">
|
||||
<span className="text-foreground font-medium">{item.platform}</span>
|
||||
<span className="text-muted-foreground">{item.reach} reach</span>
|
||||
</div>
|
||||
<div className="h-2 bg-secondary rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: `${item.pct}%` }}
|
||||
transition={{ duration: 1, delay: i * 0.1 }}
|
||||
className={`h-full ${item.color} rounded-full`}
|
||||
></motion.div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-border rounded-xl p-6">
|
||||
<h3 className="text-lg font-bold text-foreground mb-6">Demographics</h3>
|
||||
<div className="flex flex-col items-center justify-center h-64 relative">
|
||||
<div className="w-40 h-40 rounded-full border-[16px] border-secondary relative flex items-center justify-center">
|
||||
<div className="absolute inset-0 border-[16px] border-blue-500 rounded-full border-r-transparent border-b-transparent rotate-45"></div>
|
||||
<div className="absolute inset-0 border-[16px] border-pink-500 rounded-full border-t-transparent border-l-transparent border-r-transparent -rotate-12"></div>
|
||||
<div className="text-center">
|
||||
<p className="text-2xl font-bold text-foreground">62%</p>
|
||||
<p className="text-xs text-muted-foreground">Mobile</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 w-full space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-2"><div className="w-3 h-3 bg-blue-500 rounded-full"></div><span className="text-muted-foreground">Men (18-34)</span></div>
|
||||
<span className="text-foreground font-bold">45%</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-2"><div className="w-3 h-3 bg-pink-500 rounded-full"></div><span className="text-muted-foreground">Women (18-34)</span></div>
|
||||
<span className="text-foreground font-bold">35%</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-2"><div className="w-3 h-3 bg-slate-400 rounded-full"></div><span className="text-muted-foreground">Other</span></div>
|
||||
<span className="text-foreground font-bold">20%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-border rounded-xl overflow-hidden">
|
||||
<div className="p-6 border-b border-border">
|
||||
<h3 className="text-lg font-bold text-foreground">Active Ad Sets</h3>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead>
|
||||
<tr className="bg-secondary/60 text-muted-foreground uppercase text-xs tracking-wider">
|
||||
<th className="p-4 font-medium">Ad Set Name</th>
|
||||
<th className="p-4 font-medium">Bid Strategy</th>
|
||||
<th className="p-4 font-medium text-right">Reach</th>
|
||||
<th className="p-4 font-medium text-right">Frequency</th>
|
||||
<th className="p-4 font-medium text-right">CPM</th>
|
||||
<th className="p-4 font-medium text-right">Cost Per Result</th>
|
||||
<th className="p-4 font-medium text-right">Amount Spent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border">
|
||||
{[
|
||||
{ name: 'US - Tech Interests - 18-45', strategy: 'Lowest Cost', reach: '45,200', freq: '1.2', cpm: '$12.50', cpr: '$4.50', spent: '$850' },
|
||||
{ name: 'Lookalike 1% - Purchasers', strategy: 'Target Cost', reach: '22,100', freq: '2.1', cpm: '$18.20', cpr: '$12.20', spent: '$620' },
|
||||
{ name: 'Retargeting - Cart Abandon', strategy: 'Lowest Cost', reach: '8,500', freq: '3.5', cpm: '$24.50', cpr: '$8.50', spent: '$450' },
|
||||
{ name: 'Broad - Brand Awareness', strategy: 'Lowest Cost', reach: '120,000', freq: '1.1', cpm: '$5.20', cpr: '$0.15', spent: '$185' },
|
||||
].map((row, i) => (
|
||||
<tr key={i} className="hover:bg-secondary/40 transition-colors">
|
||||
<td className="p-4 font-medium text-foreground">{row.name}</td>
|
||||
<td className="p-4 text-muted-foreground">{row.strategy}</td>
|
||||
<td className="p-4 text-right text-foreground/80">{row.reach}</td>
|
||||
<td className="p-4 text-right text-foreground/80">{row.freq}</td>
|
||||
<td className="p-4 text-right text-foreground/80">{row.cpm}</td>
|
||||
<td className="p-4 text-right text-blue-600 font-medium">{row.cpr}</td>
|
||||
<td className="p-4 text-right text-foreground font-bold">{row.spent}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MetaBusinessDashboard;
|
||||
@@ -0,0 +1,143 @@
|
||||
import React from 'react';
|
||||
import { Link, useLocation, useNavigate, Outlet } from 'react-router-dom';
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Share2,
|
||||
BarChart3,
|
||||
Search,
|
||||
LogOut,
|
||||
Target,
|
||||
Layers,
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { logout, getUser } from '@/lib/auth';
|
||||
|
||||
const PortalLayout = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const user = getUser();
|
||||
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
navigate('/login');
|
||||
};
|
||||
|
||||
const navGroups = [
|
||||
{
|
||||
title: 'Overview',
|
||||
items: [{ icon: LayoutDashboard, label: 'Executive Summary', path: '/portal' }],
|
||||
},
|
||||
{
|
||||
title: 'Organic Growth',
|
||||
items: [
|
||||
{ icon: BarChart3, label: 'GA4 Analytics', path: '/portal/analytics' },
|
||||
{ icon: Search, label: 'Search Console', path: '/portal/search-console' },
|
||||
{ icon: Share2, label: 'Social Media', path: '/portal/social-media' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Paid Performance',
|
||||
items: [
|
||||
{ icon: Target, label: 'Google Ads', path: '/portal/google-ads' },
|
||||
{ icon: Layers, label: 'Meta Business', path: '/portal/meta-business' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const allItems = navGroups.flatMap((g) => g.items);
|
||||
|
||||
const linkClass = (isActive) =>
|
||||
`flex items-center gap-3 px-3 py-2 rounded-lg transition-all duration-200 group ${
|
||||
isActive ? 'bg-primary text-white shadow-lg shadow-primary/20' : 'text-muted-foreground hover:bg-secondary hover:text-foreground'
|
||||
}`;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex">
|
||||
{/* Sidebar (desktop) */}
|
||||
<aside className="w-64 bg-white border-r border-border hidden lg:flex flex-col fixed top-16 md:top-20 h-[calc(100vh-64px)] md:h-[calc(100vh-80px)] overflow-y-auto">
|
||||
<div className="p-6 pb-2">
|
||||
<div className="flex items-center gap-3 mb-8 px-2 bg-secondary/60 p-3 rounded-xl border border-border">
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-primary to-red-500 flex items-center justify-center text-white font-bold shadow-lg shadow-primary/20">
|
||||
{user?.email?.[0]?.toUpperCase() || 'U'}
|
||||
</div>
|
||||
<div className="overflow-hidden">
|
||||
<p className="text-sm font-bold text-foreground truncate">Client Portal</p>
|
||||
<p className="text-xs text-muted-foreground truncate">{user?.email || 'User'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="space-y-8">
|
||||
{navGroups.map((group, groupIndex) => (
|
||||
<div key={groupIndex}>
|
||||
<h3 className="px-3 text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
|
||||
{group.title}
|
||||
</h3>
|
||||
<div className="space-y-1">
|
||||
{group.items.map((item) => {
|
||||
const isActive = location.pathname === item.path;
|
||||
return (
|
||||
<Link key={item.path} to={item.path}>
|
||||
<div className={linkClass(isActive)}>
|
||||
<item.icon size={18} className={isActive ? 'text-white' : 'text-muted-foreground group-hover:text-foreground transition-colors'} />
|
||||
<span className="font-medium text-sm">{item.label}</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="mt-auto p-6 border-t border-border">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
|
||||
onClick={handleLogout}
|
||||
>
|
||||
<LogOut size={18} className="mr-2" />
|
||||
Sign Out
|
||||
</Button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Mobile top nav (scrollable) */}
|
||||
<div className="lg:hidden fixed top-16 left-0 right-0 z-30 bg-white/90 backdrop-blur-xl border-b border-border">
|
||||
<div className="flex items-center gap-2 overflow-x-auto px-4 py-2 no-scrollbar">
|
||||
{allItems.map((item) => {
|
||||
const isActive = location.pathname === item.path;
|
||||
return (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={`flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap transition-colors ${
|
||||
isActive ? 'bg-primary text-white' : 'bg-secondary text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<item.icon size={14} />
|
||||
{item.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
|
||||
>
|
||||
<LogOut size={14} />
|
||||
Sign Out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 lg:pl-64 pt-14 lg:pt-10 p-6 lg:p-10 overflow-x-hidden bg-background min-h-[calc(100vh-64px)] md:min-h-[calc(100vh-80px)]">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PortalLayout;
|
||||
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle.js';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Share2, ThumbsUp, MessageCircle, Repeat, ArrowUpRight } from 'lucide-react';
|
||||
|
||||
const PlatformCard = ({ platform, handle, followers, growth, color }) => (
|
||||
<div className="bg-white border border-border rounded-xl p-6 flex items-center justify-between hover:border-primary/30 transition-colors">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-full bg-${color}-500/15 flex items-center justify-center text-${color}-500`}>
|
||||
<Share2 size={24} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-foreground">{platform}</h3>
|
||||
<p className="text-sm text-muted-foreground">{handle}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-xl font-bold text-foreground">{followers}</p>
|
||||
<p className="text-xs text-green-600 flex items-center justify-end">+{growth} <ArrowUpRight size={10} /></p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const SocialMediaDashboard = () => {
|
||||
const pageTitle = 'Social Media Analytics | Client Portal';
|
||||
usePageTitle(pageTitle);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3 h1-theme">
|
||||
<Share2 className="text-purple-500" />
|
||||
Social Media Analytics
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Organic growth tracking across your social channels.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
<PlatformCard platform="LinkedIn" handle="@company_inc" followers="12.5k" growth="2.4%" color="blue" />
|
||||
<PlatformCard platform="Twitter / X" handle="@company_inc" followers="8.2k" growth="1.1%" color="slate" />
|
||||
<PlatformCard platform="Instagram" handle="@company_official" followers="24.8k" growth="5.7%" color="pink" />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="lg:col-span-2 bg-white border border-border rounded-xl p-6">
|
||||
<h3 className="text-lg font-bold text-foreground mb-6">Engagement Overview</h3>
|
||||
<div className="space-y-6">
|
||||
{[
|
||||
{ label: 'Impressions', val: '450,200', width: '90%', color: 'bg-blue-600' },
|
||||
{ label: 'Engagement', val: '28,400', width: '65%', color: 'bg-purple-600' },
|
||||
{ label: 'Link Clicks', val: '12,800', width: '45%', color: 'bg-pink-600' },
|
||||
{ label: 'Shares', val: '4,200', width: '25%', color: 'bg-green-500' },
|
||||
].map((metric, i) => (
|
||||
<div key={i}>
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="text-foreground/80 font-medium text-sm">{metric.label}</span>
|
||||
<span className="text-foreground font-bold">{metric.val}</span>
|
||||
</div>
|
||||
<div className="h-3 bg-secondary rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: metric.width }}
|
||||
transition={{ duration: 1 }}
|
||||
className={`h-full ${metric.color} rounded-full`}
|
||||
></motion.div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="bg-white border border-border rounded-xl p-6 hover:border-primary/30 transition-colors">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="p-2 bg-blue-500/10 rounded-lg text-blue-500"><ThumbsUp size={20} /></div>
|
||||
<span className="text-muted-foreground text-sm">Total Likes</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold text-foreground mt-2">24.5k</p>
|
||||
</div>
|
||||
<div className="bg-white border border-border rounded-xl p-6 hover:border-primary/30 transition-colors">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="p-2 bg-green-500/10 rounded-lg text-green-500"><MessageCircle size={20} /></div>
|
||||
<span className="text-muted-foreground text-sm">Comments</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold text-foreground mt-2">3,842</p>
|
||||
</div>
|
||||
<div className="bg-white border border-border rounded-xl p-6 hover:border-primary/30 transition-colors">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="p-2 bg-purple-500/10 rounded-lg text-purple-500"><Repeat size={20} /></div>
|
||||
<span className="text-muted-foreground text-sm">Shares</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold text-foreground mt-2">1,205</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SocialMediaDashboard;
|
||||
Reference in New Issue
Block a user