Animation & Responsive are updated

This commit is contained in:
akash 2025-12-09 17:30:51 +05:30
parent ce24e8e7fc
commit 3f53ee1ebd
11 changed files with 245 additions and 158 deletions

View File

@ -1,8 +1,7 @@
import Link from "next/link";
import Image from "next/image";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import InnerBanner from "@/components/InnerBanner";
import ProjectsContent from "@/components/ProjectsContent";
import { Metadata } from "next";
export const metadata: Metadata = {
@ -11,17 +10,6 @@ export const metadata: Metadata = {
};
export default function ProjectsPage() {
const categories = [
{
id: 1,
title: "Residential Real Estate",
description: "Discover our premium residential properties featuring modern architecture, luxury amenities, and prime locations. From spacious apartments to exclusive villas, find your dream home with world-class facilities and exceptional living experiences.",
image: "/assets/images/projects/residential-real-estate.jpg",
href: "/residential-real-estate",
properties: "Residential Real Estate"
}
];
return (
<div className="min-h-screen bg-gray-50 dark:bg-black">
<Header />
@ -35,63 +23,7 @@ export default function ProjectsPage() {
]}
/>
<div className="max-w-7xl mx-auto px-6 py-24">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">
Project Categories
</h2>
<p className="text-lg text-gray-600 dark:text-gray-400">
Browse through our carefully curated collection of properties
</p>
</div>
<div className="space-y-8">
{categories.map((category) => (
<div
key={category.id}
className="group bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-200 dark:border-gray-800 hover:border-primary dark:hover:border-primary shadow-lg hover:shadow-2xl transition-all duration-300"
>
<div className="grid grid-cols-1 md:grid-cols-12 gap-0">
{/* Image Section - Left */}
<div className="md:col-span-5 relative h-64 md:h-96 overflow-hidden">
<Image
src={category.image}
alt={category.title}
fill
className="object-cover group-hover:scale-110 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent md:bg-gradient-to-r md:from-transparent md:to-black/10" />
{/* Badge */}
<div className="absolute top-4 left-4 bg-primary text-white px-4 py-2 rounded-full text-sm font-semibold shadow-lg">
{category.properties}
</div>
</div>
{/* Content Section - Right */}
<div className="md:col-span-7 p-6 md:p-10 lg:p-12 flex flex-col justify-center">
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-foreground mb-4 group-hover:text-primary transition-colors">
{category.title}
</h3>
<p className="text-gray-600 dark:text-gray-400 text-sm md:text-base lg:text-lg mb-8 leading-relaxed">
{category.description}
</p>
<Link
href={category.href}
className="inline-flex items-center gap-2 px-8 py-4 bg-gradient-to-r from-primary to-blue-600 text-white rounded-xl font-semibold hover:shadow-xl transition-all duration-300 transform hover:scale-105 w-fit"
>
View More
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
</div>
</div>
</div>
))}
</div>
</div>
<ProjectsContent />
<Footer />
</div>

View File

@ -34,7 +34,13 @@ export default function About() {
<div className="max-w-7xl mx-auto px-6 relative z-10">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
{/* Text Content */}
<div className="space-y-8 z-10">
<motion.div
className="space-y-8 z-10"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.7 }}
>
<h2 className="text-4xl font-bold tracking-tight text-foreground">
Where the Sky Meets <br />
<span className="text-accent dark:text-accent">The Soil.</span>
@ -42,7 +48,9 @@ export default function About() {
<div className="space-y-6 text-lg text-gray-600 dark:text-gray-400 leading-relaxed">
<p>
At Sky and Soil, we curate exceptional living spaces that harmonize with nature. As authorized sales partners for Godrej Properties, we bring you the finest homes in Bangalore's most sought-after locations.
At Sky and Soil, we curate exceptional living spaces that harmonize with nat
ure. As authorized sales partners for Godrej Properties, we bring you the finest homes in Bangalore's most sought-after locations.
</p>
<p>
Our mission is to connect you with homes that offer not just a roof over your head, but a lifestyle grounded in luxury and elevated by nature. From North Bengaluru to Sarjapur, discover a life of abundance.
@ -55,7 +63,7 @@ export default function About() {
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
</svg>
</Link>
</div>
</motion.div>
{/* Parallax Image Container */}
<div

View File

@ -5,6 +5,7 @@ import Image from "next/image";
import { useState, ChangeEvent, FormEvent, useEffect } from "react";
import ReCAPTCHA from "react-google-recaptcha";
import axios from "axios";
import { motion } from "framer-motion";
interface FormData {
name: string;
@ -118,7 +119,13 @@ export default function ContactCTA() {
<section id="contact" className="relative py-24 bg-[#f3f1e6] dark:bg-gray-900">
<div className="relative z-10 max-w-6xl mx-auto px-6">
<div className="bg-white dark:bg-gray-800 rounded-3xl shadow-2xl overflow-hidden border border-gray-100 dark:border-gray-700 flex flex-col md:flex-row">
<motion.div
className="bg-white dark:bg-gray-800 rounded-3xl shadow-2xl overflow-hidden border border-gray-100 dark:border-gray-700 flex flex-col md:flex-row"
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.7 }}
>
{/* Left Side - Form */}
<div className="w-full md:w-1/2 p-8 md:p-12">
@ -242,7 +249,7 @@ export default function ContactCTA() {
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent md:bg-gradient-to-l" />
</div>
</div>
</motion.div>
</div>
</section>
);

View File

@ -1,6 +1,7 @@
"use client";
import { useState, useEffect } from "react";
import { motion } from "framer-motion";
import Image from "next/image";
const faqs = [
@ -215,7 +216,13 @@ export default function FAQ() {
{/* Right Column: FAQ Content */}
<div>
<div className="mb-8">
<motion.div
className="mb-8"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
>
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
Frequently Asked Questions
</h2>
@ -241,7 +248,7 @@ export default function FAQ() {
</button>
))}
</div>
</div>
</motion.div>
<div className="space-y-4">
{filteredFaqs.map((faq, index) => (

View File

@ -1,4 +1,7 @@
"use client";
import Link from "next/link";
import { motion } from "framer-motion";
export default function Hero() {
return (
@ -24,8 +27,13 @@ export default function Hero() {
{/* Location Pins (Decorative) */}
<div className="absolute inset-0 z-10 pointer-events-none hidden md:block">
{/* Pin 1: Hebbal */}
<div className="absolute top-[30%] left-[20%] animate-bounce" style={{ animationDuration: '3s' }}>
<div className="flex flex-col items-center">
<motion.div
className="absolute top-[30%] left-[20%]"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 1.2 }}
>
<div className="flex flex-col items-center animate-bounce" style={{ animationDuration: '3s' }}>
<div className="bg-white/90 backdrop-blur-md px-3 py-1 rounded-lg shadow-lg mb-2">
<span className="text-xs font-bold text-black">Hebbal</span>
</div>
@ -34,11 +42,16 @@ export default function Hero() {
</div>
<div className="h-16 w-0.5 bg-gradient-to-b from-white/50 to-transparent"></div>
</div>
</div>
</motion.div>
{/* Pin 2: Airport */}
<div className="absolute top-[20%] right-[25%] animate-bounce" style={{ animationDuration: '4s', animationDelay: '1s' }}>
<div className="flex flex-col items-center">
<motion.div
className="absolute top-[20%] right-[25%]"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 1.5 }}
>
<div className="flex flex-col items-center animate-bounce" style={{ animationDuration: '4s', animationDelay: '1s' }}>
<div className="bg-white/90 backdrop-blur-md px-3 py-1 rounded-lg shadow-lg mb-2">
<span className="text-xs font-bold text-black">Airport</span>
</div>
@ -47,11 +60,16 @@ export default function Hero() {
</div>
<div className="h-24 w-0.5 bg-gradient-to-b from-white/50 to-transparent"></div>
</div>
</div>
</motion.div>
{/* Pin 3: Whitefield */}
<div className="absolute bottom-[30%] right-[15%] animate-bounce" style={{ animationDuration: '3.5s', animationDelay: '0.5s' }}>
<div className="flex flex-col items-center">
<motion.div
className="absolute bottom-[30%] right-[15%]"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 1.8 }}
>
<div className="flex flex-col items-center animate-bounce" style={{ animationDuration: '3.5s', animationDelay: '0.5s' }}>
<div className="bg-white/90 backdrop-blur-md px-3 py-1 rounded-lg shadow-lg mb-2">
<span className="text-xs font-bold text-black">Whitefield</span>
</div>
@ -60,11 +78,16 @@ export default function Hero() {
</div>
<div className="h-12 w-0.5 bg-gradient-to-b from-white/50 to-transparent"></div>
</div>
</div>
</motion.div>
</div>
<div className="relative z-20 max-w-5xl mx-auto px-6 text-center">
<h1 className="text-5xl md:text-7xl lg:text-8xl font-bold tracking-tight text-white mb-6 animate-fade-in drop-shadow-lg">
<motion.div
className="relative z-20 max-w-5xl mx-auto px-6 text-center"
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.3 }}
>
<h1 className="text-5xl md:text-7xl lg:text-8xl font-bold tracking-tight text-white mb-6 drop-shadow-lg">
Sky and Soil <br className="hidden md:block" />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-300">
Bangalore.
@ -76,14 +99,22 @@ export default function Hero() {
</p> */}
</div>
</motion.div>
{/* Scroll Indicator */}
<div className="absolute bottom-10 left-1/2 transform -translate-x-1/2 animate-bounce z-20">
<motion.div
className="absolute bottom-10 left-1/2 transform -translate-x-1/2 z-20"
initial={{ opacity: 0 }}
animate={{ opacity: 0.8, y: [0, 10, 0] }}
transition={{
opacity: { duration: 1, delay: 1.5 },
y: { duration: 1.5, repeat: Infinity, ease: "easeInOut" }
}}
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6 text-white/80">
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</div>
</motion.div>
</section>
);
}

View File

@ -2,7 +2,7 @@
import { useState, useRef, MouseEvent } from "react";
import Image from "next/image";
import { motion } from "framer-motion";
import { FloatingHouse, RotatingKey } from "./PropertyAnimations";
export default function Lifestyle() {
@ -37,11 +37,15 @@ export default function Lifestyle() {
<div className="max-w-7xl mx-auto px-6 relative z-10">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
{/* Image Side with Zoom Effect */}
<div
<motion.div
className="order-2 lg:order-1 relative h-[600px] rounded-3xl overflow-hidden shadow-2xl group cursor-crosshair"
ref={imageRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
initial={{ opacity: 0, scale: 0.95 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.7 }}
>
<Image
src="/assets/images/home/experience.webp"
@ -59,10 +63,16 @@ export default function Lifestyle() {
Clubhouse & Amenities
</span>
</div>
</div>
</motion.div>
{/* Content Side */}
<div className="order-1 lg:order-2 space-y-8">
<motion.div
className="order-1 lg:order-2 space-y-8"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.7, delay: 0.2 }}
>
<h2 className="text-4xl md:text-5xl font-bold tracking-tight text-foreground">
Experience the <br />
<span className="text-primary">Aurora Lifestyle.</span>
@ -100,7 +110,7 @@ export default function Lifestyle() {
</li>
))}
</ul>
</div>
</motion.div>
</div>
</div>
</section>

View File

@ -0,0 +1,88 @@
"use client";
import Link from "next/link";
import Image from "next/image";
import { motion } from "framer-motion";
const categories = [
{
id: 1,
title: "Residential Real Estate",
description: "Discover our premium residential properties featuring modern architecture, luxury amenities, and prime locations. From spacious apartments to exclusive villas, find your dream home with world-class facilities and exceptional living experiences.",
image: "/assets/images/projects/residential-real-estate.jpg",
href: "/residential-real-estate",
properties: "Residential Real Estate"
}
];
export default function ProjectsContent() {
return (
<div className="max-w-7xl mx-auto px-6 py-24">
<motion.div
className="text-center mb-12"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">
Project Categories
</h2>
<p className="text-lg text-gray-600 dark:text-gray-400">
Browse through our carefully curated collection of properties
</p>
</motion.div>
<div className="space-y-8">
{categories.map((category, index) => (
<motion.div
key={category.id}
className="group bg-white dark:bg-gray-900 rounded-2xl overflow-hidden border border-gray-200 dark:border-gray-800 hover:border-primary dark:hover:border-primary shadow-lg hover:shadow-2xl transition-all duration-300"
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.7, delay: index * 0.1 }}
>
<div className="grid grid-cols-1 md:grid-cols-12 gap-0">
{/* Image Section - Left */}
<div className="md:col-span-5 relative h-64 md:h-96 overflow-hidden">
<Image
src={category.image}
alt={category.title}
fill
className="object-cover group-hover:scale-110 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent md:bg-gradient-to-r md:from-transparent md:to-black/10" />
{/* Badge */}
<div className="absolute top-4 left-4 bg-primary text-white px-4 py-2 rounded-full text-sm font-semibold shadow-lg">
{category.properties}
</div>
</div>
{/* Content Section - Right */}
<div className="md:col-span-7 p-6 md:p-10 lg:p-12 flex flex-col justify-center">
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-foreground mb-4 group-hover:text-primary transition-colors">
{category.title}
</h3>
<p className="text-gray-600 dark:text-gray-400 text-sm md:text-base lg:text-lg mb-8 leading-relaxed">
{category.description}
</p>
<Link
href={category.href}
className="inline-flex items-center gap-2 px-8 py-4 bg-gradient-to-r from-primary to-blue-600 text-white rounded-xl font-semibold hover:shadow-xl transition-all duration-300 transform hover:scale-105 w-fit"
>
View More
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
</div>
</div>
</motion.div>
))}
</div>
</div>
);
}

View File

@ -3,6 +3,7 @@
import { useState, useRef } from "react";
import Link from "next/link";
import { properties } from "@/data/properties";
import { motion } from "framer-motion";
type Category = "Apartments" | "Premium Homes" | "Luxury";
@ -29,14 +30,20 @@ export default function Properties({ layout = "slider" }: PropertiesProps) {
return (
<section id="projects" className="py-24 bg-white dark:bg-black overflow-hidden">
<div className="max-w-7xl mx-auto px-6">
<div className="text-center mb-12">
<motion.div
className="text-center mb-12"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
>
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
Our Signature Projects
</h2>
<p className="text-lg text-gray-600 dark:text-gray-400">
Discover a home that complements your lifestyle.
</p>
</div>
</motion.div>
{/* Tabs */}
<div className="flex justify-center mb-12">
@ -92,12 +99,16 @@ export default function Properties({ layout = "slider" }: PropertiesProps) {
if (activeTab !== "All" && index > 0) return null;
return (
<div
<motion.div
key={property.id}
className={activeTab === "All"
? "flex-shrink-0 snap-center w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)]"
: "flex-shrink-0 w-full max-w-md mx-auto snap-center"
}
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 0.6, delay: index * 0.15 }}
>
<Link
href={`/residential-real-estate/${property.slug}`}
@ -144,7 +155,7 @@ export default function Properties({ layout = "slider" }: PropertiesProps) {
</div>
</div>
</Link>
</div>
</motion.div>
);
})}
</div>

View File

@ -7,6 +7,7 @@ import InnerBanner from "@/components/InnerBanner";
import PropertyFilters, { FilterState } from "@/components/PropertyFilters";
import PropertyCard from "@/components/PropertyCard";
import { properties } from "@/data/properties";
import { motion } from "framer-motion";
export default function PropertiesClient() {
const [filteredProperties, setFilteredProperties] = useState(properties);
@ -79,15 +80,30 @@ export default function PropertiesClient() {
<PropertyFilters onFilterChange={handleFilterChange} />
<div className="max-w-7xl mx-auto px-6 py-12">
<h2 className="text-3xl font-bold text-foreground mb-8">Browse Our Properties</h2>
<motion.h2
className="text-3xl font-bold text-foreground mb-8"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
>
Browse Our Properties
</motion.h2>
{/* Property Grid */}
{filteredProperties.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredProperties.map((property) => (
<PropertyCard key={property.id} property={property} />
))}
</div>
{filteredProperties.map((property, index) => (
<motion.div
key={property.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<PropertyCard property={property} />
</motion.div>
))} </div>
) : (
<div className="text-center py-20">
<svg className="w-20 h-20 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">

View File

@ -2,6 +2,7 @@
import { useRef } from "react";
import { GrowingBuilding } from "./PropertyAnimations";
import { motion } from "framer-motion";
export default function Testimonials() {
const scrollContainerRef = useRef<HTMLDivElement>(null);
@ -81,7 +82,13 @@ export default function Testimonials() {
</div>
<div className="max-w-7xl mx-auto px-6 relative z-10">
<div className="text-center mb-16">
<motion.div
className="text-center mb-16"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
>
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
Stories of Satisfaction
</h2>
@ -92,7 +99,7 @@ export default function Testimonials() {
<span className="text-lg font-semibold text-foreground">5.0</span>
<span className="text-gray-500"> Based on {testimonials.length} reviews</span>
</div>
</div>
</motion.div>
{/* Slider Container */}
<div className="relative group">

View File

@ -1,13 +1,10 @@
"use client";
import { useState, useEffect, useRef } from "react";
import Image from "next/image";
import { FloatingHouse, RotatingKey, GrowingBuilding } from "./PropertyAnimations";
import { motion } from "framer-motion";
export default function WhyChooseUs() {
const [visibleCards, setVisibleCards] = useState<boolean[]>([false, false, false, false]);
const sectionRef = useRef<HTMLDivElement>(null);
const features = [
{
title: "Prime Locations",
@ -31,40 +28,8 @@ export default function WhyChooseUs() {
},
];
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Trigger cards to appear one by one with delay
features.forEach((_, index) => {
setTimeout(() => {
setVisibleCards((prev) => {
const newVisible = [...prev];
newVisible[index] = true;
return newVisible;
});
}, index * 150); // 150ms delay between each card
});
}
});
},
{ threshold: 0.2 }
);
if (sectionRef.current) {
observer.observe(sectionRef.current);
}
return () => {
if (sectionRef.current) {
observer.unobserve(sectionRef.current);
}
};
}, []);
return (
<section ref={sectionRef} className="py-24 bg-secondary dark:bg-gray-900/50 relative overflow-hidden">
<section className="py-24 bg-secondary dark:bg-gray-900/50 relative overflow-hidden">
{/* Decorative Animations */}
<div className="absolute top-20 left-10 opacity-50 hidden lg:block">
@ -78,26 +43,31 @@ export default function WhyChooseUs() {
</div>
<div className="max-w-7xl mx-auto px-6 relative z-10">
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4 animate-fade-in">
<motion.div
className="text-center mb-16"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
>
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-4">
Why Sky and Soil?
</h2>
<p className="text-lg text-gray-600 dark:text-gray-400 max-w-2xl mx-auto animate-slide-up">
<p className="text-lg text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
We bridge the gap between your dreams and reality with premium properties and unmatched service.
</p>
</div>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{features.map((feature, index) => (
<div
<motion.div
key={index}
className={`bg-white dark:bg-gray-800 p-8 rounded-2xl shadow-sm hover:shadow-xl transition-all duration-500 flex flex-col items-center text-center group transform ${visibleCards[index]
? 'translate-y-0 opacity-100'
: 'translate-y-10 opacity-0'
}`}
style={{
transitionDelay: `${index * 100}ms`
}}
className="bg-white dark:bg-gray-800 p-8 rounded-2xl shadow-sm hover:shadow-xl transition-all duration-500 flex flex-col items-center text-center group"
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 0.5, delay: index * 0.15 }}
whileHover={{ y: -10, transition: { duration: 0.3 } }}
>
<div className="mb-6 p-4 bg-blue-50 dark:bg-blue-900/30 rounded-full group-hover:scale-110 group-hover:rotate-6 transition-all duration-300 relative w-20 h-20 flex items-center justify-center">
<div className="relative w-[80%] h-[80%]">
@ -115,7 +85,7 @@ export default function WhyChooseUs() {
<p className="text-gray-600 dark:text-gray-400 leading-relaxed">
{feature.description}
</p>
</div>
</motion.div>
))}
</div>
</div>