Animation & Responsive are updated
This commit is contained in:
parent
ce24e8e7fc
commit
3f53ee1ebd
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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) => (
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
88
src/components/ProjectsContent.tsx
Normal file
88
src/components/ProjectsContent.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user