297 lines
10 KiB
TypeScript
297 lines
10 KiB
TypeScript
import { motion, AnimatePresence } from "framer-motion";
|
|
import { useEffect, useState } from "react";
|
|
import { Link } from "wouter";
|
|
import { Flame, Heart, Leaf, Sparkles } from "lucide-react";
|
|
import Box from "@mui/material/Box";
|
|
import Container from "@mui/material/Container";
|
|
import lavenderImage from "@/assets/images/image-removebg-preview.png";
|
|
import roseImage from "@/assets/images/product-rose.png";
|
|
import eucalyptusImage from "@/assets/images/product-eucalyptus.png";
|
|
import oudImage from "@/assets/images/product-oud.png";
|
|
import "./home.css";
|
|
|
|
const logoPath = "/opengraph.jpg";
|
|
|
|
const fadeInUp = {
|
|
hidden: { opacity: 0, y: 40 },
|
|
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
|
};
|
|
|
|
const stagger = {
|
|
visible: { transition: { staggerChildren: 0.2 } },
|
|
};
|
|
|
|
export default function Home() {
|
|
const moods = [
|
|
{
|
|
title: "Lavender Dreams",
|
|
subtitle: "Calm your mind. Light your sanctuary.",
|
|
glow: "#7B3DB8",
|
|
},
|
|
{
|
|
title: "Rose & Vanilla",
|
|
subtitle: "Romance captured in every flame.",
|
|
glow: "#D67896",
|
|
},
|
|
{
|
|
title: "Eucalyptus Mint",
|
|
subtitle: "Freshness inspired by nature.",
|
|
glow: "#7DDCB4",
|
|
},
|
|
{
|
|
title: "Oud & Amber",
|
|
subtitle: "Luxury crafted into fragrance.",
|
|
glow: "#FFB446",
|
|
},
|
|
];
|
|
|
|
const [activeMood, setActiveMood] = useState(0);
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setActiveMood((prev) => (prev + 1) % moods.length);
|
|
}, 8000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
return (
|
|
<Box>
|
|
{/* ── Hero ── */}
|
|
<section className="arrival-hero">
|
|
|
|
<div className="arrival-bg" />
|
|
|
|
<div className="fog fog-1" />
|
|
<div className="fog fog-2" />
|
|
<div className="fog fog-3" />
|
|
|
|
<div className="embers">
|
|
{[...Array(18)].map((_, i) => (
|
|
<span
|
|
key={i}
|
|
style={{
|
|
left: `${5 + Math.random() * 90}%`,
|
|
animationDelay: `${Math.random() * 8}s`,
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<Container maxWidth="xl">
|
|
|
|
<div className="arrival-content">
|
|
|
|
<motion.div
|
|
className="arrival-copy"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 1.5 }}
|
|
>
|
|
|
|
<motion.img
|
|
src={logoPath}
|
|
alt="Mani Candles"
|
|
className="arrival-logo"
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.5, duration: 1 }}
|
|
/>
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={activeMood}
|
|
initial={{
|
|
opacity: 0,
|
|
filter: "blur(12px)",
|
|
y: 20,
|
|
}}
|
|
animate={{
|
|
opacity: 1,
|
|
filter: "blur(0px)",
|
|
y: 0,
|
|
}}
|
|
exit={{
|
|
opacity: 0,
|
|
filter: "blur(12px)",
|
|
y: -20,
|
|
}}
|
|
transition={{
|
|
duration: 1.2,
|
|
}}
|
|
>
|
|
<h1>{moods[activeMood].title}</h1>
|
|
|
|
<h2>
|
|
Light a Moment.
|
|
Create a Memory.
|
|
</h2>
|
|
|
|
<p>{moods[activeMood].subtitle}</p>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 2.8 }}
|
|
>
|
|
<Link
|
|
href="/products"
|
|
className="arrival-btn"
|
|
>
|
|
Explore Collection
|
|
</Link>
|
|
</motion.div>
|
|
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="arrival-candle-wrap"
|
|
animate={{
|
|
y: [0, -18, 0]
|
|
}}
|
|
transition={{
|
|
duration: 7,
|
|
repeat: Infinity,
|
|
ease: "easeInOut"
|
|
}}
|
|
>
|
|
<motion.div
|
|
className="arrival-glow"
|
|
animate={{
|
|
backgroundColor: moods[activeMood].glow,
|
|
}}
|
|
transition={{
|
|
duration: 3,
|
|
ease: "easeInOut",
|
|
}}
|
|
/>
|
|
|
|
<motion.img
|
|
src={lavenderImage}
|
|
alt="Luxury Candle"
|
|
className="arrival-candle"
|
|
initial={{
|
|
opacity: 0,
|
|
scale: 0.9,
|
|
filter: "blur(15px)"
|
|
}}
|
|
animate={{
|
|
opacity: 1,
|
|
scale: 1,
|
|
filter: "blur(0px)"
|
|
}}
|
|
transition={{
|
|
duration: 2.5,
|
|
delay: 1
|
|
}}
|
|
/>
|
|
|
|
</motion.div>
|
|
|
|
</div>
|
|
|
|
</Container>
|
|
|
|
<div className="scroll-indicator">
|
|
Scroll
|
|
</div>
|
|
|
|
</section>
|
|
{/* ── Philosophy ── */}
|
|
<section className="home-philosophy">
|
|
<Container maxWidth="lg">
|
|
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true, margin: "-100px" }} variants={stagger}>
|
|
<motion.h2 variants={fadeInUp}>The Mani Philosophy</motion.h2>
|
|
<div className="section-divider" />
|
|
</motion.div>
|
|
|
|
<div className="philosophy-grid">
|
|
{[
|
|
{ Icon: Leaf, title: "Natural", desc: "Crafted with pure soy wax and essential oils. Clean burns that respect your home and our earth." },
|
|
{ Icon: Sparkles, title: "Handmade", desc: "Poured by hand in small batches to ensure unparalleled quality and attention to detail." },
|
|
{ Icon: Heart, title: "Heartmade", desc: "Every candle is infused with love and intention, designed to bring warmth to your sacred spaces." },
|
|
].map(({ Icon, title, desc }, i) => (
|
|
<motion.div
|
|
key={title}
|
|
className="philosophy-card"
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true }}
|
|
variants={{ hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.2, duration: 0.6 } } }}
|
|
>
|
|
<div className="philosophy-icon-wrap"><Icon size={32} /></div>
|
|
<h3>{title}</h3>
|
|
<p>{desc}</p>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
|
|
{/* ── Featured Products ── */}
|
|
<section className="home-products">
|
|
<Container maxWidth="xl">
|
|
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp} className="home-products-header">
|
|
<div>
|
|
<h2>Curated Favorites</h2>
|
|
<div className="section-divider" style={{ margin: "0.5rem 0 0", width: 80 }} />
|
|
</div>
|
|
<Link href="/products" className="view-all-link">View All Candles</Link>
|
|
</motion.div>
|
|
|
|
<div className="product-grid">
|
|
{[
|
|
{ id: 1, name: "Lavender Dream", image: lavenderImage, price: "$28" },
|
|
{ id: 2, name: "Rose & Vanilla", image: roseImage, price: "$32" },
|
|
{ id: 3, name: "Eucalyptus Mint", image: eucalyptusImage, price: "$28" },
|
|
{ id: 4, name: "Oud & Amber", image: oudImage, price: "$36" },
|
|
].map((product, i) => (
|
|
<motion.div
|
|
key={product.id}
|
|
className="product-card"
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true }}
|
|
variants={{ hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.1, duration: 0.6 } } }}
|
|
>
|
|
<div className="product-img-wrap">
|
|
<img src={product.image} alt={product.name} />
|
|
<div className="product-overlay">
|
|
<button className="product-overlay-btn">Quick Add</button>
|
|
</div>
|
|
</div>
|
|
<h3 className="product-name">{product.name}</h3>
|
|
<p className="product-price">{product.price}</p>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
|
|
{/* ── Quote ── */}
|
|
<section className="home-quote">
|
|
<div className="home-quote-texture" />
|
|
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp}>
|
|
<Flame className="animate-flame" size={48} style={{ color: "var(--brand-gold)", margin: "0 auto 1.5rem", display: "block" }} />
|
|
<blockquote>
|
|
"A candle loses nothing by lighting another candle. It only makes the room warmer, brighter, and more beautiful."
|
|
</blockquote>
|
|
<div className="home-quote-divider" />
|
|
</motion.div>
|
|
</section>
|
|
|
|
{/* ── Newsletter ── */}
|
|
<section className="home-newsletter">
|
|
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp}>
|
|
<h2>Join Our Sanctuary</h2>
|
|
<p>Subscribe to receive gentle updates on new collections, exclusive access, and a little light in your inbox.</p>
|
|
<form className="newsletter-form" onSubmit={(e) => e.preventDefault()}>
|
|
<input type="email" className="newsletter-input" placeholder="Your email address" required />
|
|
<button type="submit" className="newsletter-btn">Subscribe</button>
|
|
</form>
|
|
</motion.div>
|
|
</section>
|
|
</Box>
|
|
);
|
|
}
|