222 lines
7.8 KiB
TypeScript
222 lines
7.8 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useRef, useState } from "react"
|
|
import { Check, MapPin, Clock, Shield, Zap } from "lucide-react"
|
|
|
|
const features = [
|
|
{
|
|
icon: MapPin,
|
|
title: "Locally Based",
|
|
description: "We're right here in Auckland. Real people, real support, no overseas call centres."
|
|
},
|
|
{
|
|
icon: Clock,
|
|
title: "Fast Response",
|
|
description: "We understand downtime costs money. Our team responds quickly to get you back online."
|
|
},
|
|
{
|
|
icon: Shield,
|
|
title: "Secure & Reliable",
|
|
description: "Your data is safe with us. We use enterprise-grade security for all our services."
|
|
},
|
|
{
|
|
icon: Zap,
|
|
title: "No Jargon",
|
|
description: "We explain things in plain English. Tech shouldn't be complicated."
|
|
}
|
|
]
|
|
|
|
const benefits = [
|
|
"No lock-in contracts",
|
|
"Free initial consultation",
|
|
"Transparent pricing",
|
|
"Ongoing support included",
|
|
"NZ-based data hosting",
|
|
"Small business specialists"
|
|
]
|
|
|
|
function AnimatedCounter({ end, suffix = "" }: { end: number; suffix?: string }) {
|
|
const [count, setCount] = useState(0)
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
const [isVisible, setIsVisible] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (entry.isIntersecting) {
|
|
setIsVisible(true)
|
|
}
|
|
},
|
|
{ threshold: 0.5 }
|
|
)
|
|
|
|
if (ref.current) {
|
|
observer.observe(ref.current)
|
|
}
|
|
|
|
return () => observer.disconnect()
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (!isVisible) return
|
|
|
|
const duration = 2000
|
|
const steps = 60
|
|
const increment = end / steps
|
|
let current = 0
|
|
|
|
const timer = setInterval(() => {
|
|
current += increment
|
|
if (current >= end) {
|
|
setCount(end)
|
|
clearInterval(timer)
|
|
} else {
|
|
setCount(Math.floor(current))
|
|
}
|
|
}, duration / steps)
|
|
|
|
return () => clearInterval(timer)
|
|
}, [isVisible, end])
|
|
|
|
return (
|
|
<div ref={ref} className="text-5xl md:text-6xl font-bold text-primary">
|
|
{count}{suffix}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function Features() {
|
|
const [activeFeature, setActiveFeature] = useState(0)
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setActiveFeature((prev) => (prev + 1) % features.length)
|
|
}, 4000)
|
|
return () => clearInterval(interval)
|
|
}, [])
|
|
|
|
return (
|
|
<section id="features" className="relative py-32 bg-card/30">
|
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
|
{/* Section Header */}
|
|
<div className="text-center mb-20">
|
|
<div className="inline-flex items-center gap-2 px-4 py-2 mb-6 border border-border rounded-full bg-card/50">
|
|
<span className="text-sm text-muted-foreground">Why Cloudrite?</span>
|
|
</div>
|
|
<h2 className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight mb-6">
|
|
<span className="text-balance">Tech That Actually Works</span>
|
|
</h2>
|
|
<p className="max-w-2xl mx-auto text-lg text-muted-foreground">
|
|
We're not a faceless corporation. We're your friendly neighbourhood
|
|
I.T team who genuinely cares about your business.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Features Bento Grid */}
|
|
<div className="grid lg:grid-cols-2 gap-8 mb-20">
|
|
{/* Left side - Feature showcase */}
|
|
<div className="relative bg-card border border-border rounded-3xl p-8 lg:p-12 overflow-hidden">
|
|
{/* Background decoration */}
|
|
<div className="absolute top-0 right-0 w-64 h-64 bg-primary/10 rounded-full blur-[100px]" />
|
|
|
|
{/* Feature icons row */}
|
|
<div className="flex gap-4 mb-8">
|
|
{features.map((feature, index) => {
|
|
const Icon = feature.icon
|
|
return (
|
|
<button
|
|
key={feature.title}
|
|
onClick={() => setActiveFeature(index)}
|
|
className={`w-14 h-14 rounded-xl flex items-center justify-center transition-all duration-300 ${
|
|
activeFeature === index
|
|
? "bg-primary text-primary-foreground scale-110"
|
|
: "bg-secondary text-secondary-foreground hover:bg-secondary/80"
|
|
}`}
|
|
>
|
|
<Icon className="w-6 h-6" />
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
{/* Active feature content */}
|
|
<div className="relative z-10 min-h-[200px]">
|
|
{features.map((feature, index) => (
|
|
<div
|
|
key={feature.title}
|
|
className={`transition-all duration-500 ${
|
|
activeFeature === index
|
|
? "opacity-100 translate-y-0"
|
|
: "opacity-0 translate-y-4 absolute inset-0 pointer-events-none"
|
|
}`}
|
|
>
|
|
<h3 className="text-3xl font-bold mb-4">{feature.title}</h3>
|
|
<p className="text-xl text-muted-foreground leading-relaxed">
|
|
{feature.description}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Progress bar */}
|
|
<div className="flex gap-2 mt-8">
|
|
{features.map((_, index) => (
|
|
<div key={index} className="h-1 flex-1 bg-border rounded-full overflow-hidden">
|
|
<div
|
|
className={`h-full bg-primary transition-all duration-300 ${
|
|
index === activeFeature ? "animate-progress" : index < activeFeature ? "w-full" : "w-0"
|
|
}`}
|
|
style={{
|
|
width: index === activeFeature ? "100%" : index < activeFeature ? "100%" : "0%",
|
|
transition: index === activeFeature ? "width 4s linear" : "width 0.3s ease",
|
|
}}
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right side - Benefits list */}
|
|
<div className="bg-card border border-border rounded-3xl p-8 lg:p-12">
|
|
<h3 className="text-2xl font-bold mb-8">What You Get With Us</h3>
|
|
<div className="space-y-6">
|
|
{benefits.map((benefit, index) => (
|
|
<div
|
|
key={benefit}
|
|
className="flex items-center gap-4 group"
|
|
style={{ animationDelay: `${index * 100}ms` }}
|
|
>
|
|
<div className="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center transition-all duration-300 group-hover:bg-primary group-hover:scale-110">
|
|
<Check className="w-5 h-5 text-primary transition-colors duration-300 group-hover:text-primary-foreground" />
|
|
</div>
|
|
<span className="text-lg font-medium">{benefit}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats Row */}
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 p-8 lg:p-12 bg-card border border-border rounded-3xl">
|
|
<div className="text-center">
|
|
<AnimatedCounter end={100} suffix="+" />
|
|
<p className="text-muted-foreground mt-2">Businesses Helped</p>
|
|
</div>
|
|
<div className="text-center">
|
|
<AnimatedCounter end={99} suffix="%" />
|
|
<p className="text-muted-foreground mt-2">Uptime Guarantee</p>
|
|
</div>
|
|
<div className="text-center">
|
|
<AnimatedCounter end={24} suffix="/7" />
|
|
<p className="text-muted-foreground mt-2">Support Available</p>
|
|
</div>
|
|
<div className="text-center">
|
|
<AnimatedCounter end={10} suffix="+" />
|
|
<p className="text-muted-foreground mt-2">Years Experience</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|