Update : added scroll candle animation nd fixed header css
This commit is contained in:
parent
6e3408ef7c
commit
b4b163634f
|
|
@ -1,6 +1,4 @@
|
|||
import { Switch, Route, Router as WouterRouter } from "wouter";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import NotFound from "@/pages/not-found";
|
||||
|
||||
import { Layout } from "@/components/Layout";
|
||||
|
|
@ -23,14 +21,11 @@ function Router() {
|
|||
|
||||
function App() {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<WouterRouter base={import.meta.env.BASE_URL.replace(/\/$/, "")}>
|
||||
<Layout>
|
||||
<Router />
|
||||
</Layout>
|
||||
</WouterRouter>
|
||||
<Toaster />
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,366 @@
|
|||
/* ─── Layout.css — Header & Footer ──────────────────────── */
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
MARQUEE / TICKER BAR
|
||||
══════════════════════════════════════════════════════════ */
|
||||
.ticker-bar {
|
||||
background: var(--brand-purple-dark);
|
||||
border-bottom: 1px solid rgba(212,162,76,0.25);
|
||||
height: 36px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ticker-track {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
animation: ticker-scroll 28s linear infinite;
|
||||
white-space: nowrap;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.ticker-track:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
@keyframes ticker-scroll {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-50%); }
|
||||
}
|
||||
|
||||
.ticker-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255,255,255,0.85);
|
||||
padding: 0 2.5rem;
|
||||
}
|
||||
|
||||
.ticker-dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: var(--brand-gold);
|
||||
flex-shrink: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
LOGO
|
||||
══════════════════════════════════════════════════════════ */
|
||||
.logo-text {
|
||||
font-family: var(--font-script);
|
||||
font-size: 2rem;
|
||||
color: #ffffff;
|
||||
transition: color 0.3s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
.logo-text:hover { color: var(--brand-gold-light); }
|
||||
|
||||
.logo-img-wrap {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 2px solid transparent;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
.logo-img-wrap:hover { border-color: var(--brand-gold); }
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
NAV ITEM WITH DROPDOWN
|
||||
══════════════════════════════════════════════════════════ */
|
||||
.nav-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
padding-bottom: 2px;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: border-color 0.3s ease, color 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--brand-gold-light);
|
||||
border-bottom-color: rgba(212,162,76,0.6);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: var(--brand-gold-light);
|
||||
border-bottom-color: var(--brand-gold);
|
||||
border-bottom-width: 2.5px;
|
||||
text-shadow:
|
||||
0 0 12px rgba(255,211,107,0.9),
|
||||
0 0 24px rgba(212,162,76,0.5);
|
||||
font-weight: 600;
|
||||
background: rgba(212,162,76,0.12);
|
||||
border-radius: 4px;
|
||||
padding: 3px 10px 5px;
|
||||
}
|
||||
|
||||
.nav-chevron {
|
||||
transition: transform 0.25s ease;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.nav-item:hover .nav-chevron {
|
||||
transform: rotate(180deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* DROPDOWN PANEL */
|
||||
.nav-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 18px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-8px);
|
||||
min-width: 200px;
|
||||
background: rgba(45, 13, 82, 0.96);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(212,162,76,0.2);
|
||||
border-radius: 8px;
|
||||
padding: 0.6rem 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s ease, transform 0.25s ease;
|
||||
box-shadow: 0 16px 40px rgba(0,0,0,0.35);
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
/* Arrow notch */
|
||||
.nav-dropdown::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid rgba(212,162,76,0.25);
|
||||
}
|
||||
|
||||
.nav-dropdown::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid rgba(45,13,82,0.96);
|
||||
}
|
||||
|
||||
.nav-item:hover .nav-dropdown {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
padding: 0.65rem 1.25rem;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.83rem;
|
||||
color: rgba(255,255,255,0.78);
|
||||
text-decoration: none;
|
||||
transition: background 0.2s ease, color 0.2s ease, padding-left 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background: rgba(212,162,76,0.12);
|
||||
color: var(--brand-gold-light);
|
||||
padding-left: 1.6rem;
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.08);
|
||||
margin: 0.4rem 0;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
MOBILE TOGGLE
|
||||
══════════════════════════════════════════════════════════ */
|
||||
.mobile-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
.mobile-toggle:hover { color: var(--brand-gold-light); }
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
MOBILE DRAWER
|
||||
══════════════════════════════════════════════════════════ */
|
||||
.mobile-menu {
|
||||
background: rgba(45, 13, 82, 0.97);
|
||||
backdrop-filter: blur(16px);
|
||||
border-bottom: 1px solid rgba(212, 162, 76, 0.2);
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.mobile-nav-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mobile-nav-link {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
padding: 0.8rem 0.5rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.07);
|
||||
transition: color 0.3s ease, padding-left 0.2s ease;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mobile-nav-link:hover {
|
||||
color: var(--brand-gold-light);
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
|
||||
.mobile-nav-link.active {
|
||||
color: var(--brand-gold);
|
||||
padding-left: 0.75rem;
|
||||
text-shadow: 0 0 10px rgba(255,211,107,0.7);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mobile-dropdown-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.25rem 0 0.5rem 1rem;
|
||||
gap: 0.1rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.07);
|
||||
}
|
||||
|
||||
.mobile-dropdown-item {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.82rem;
|
||||
color: rgba(255,255,255,0.6);
|
||||
text-decoration: none;
|
||||
padding: 0.4rem 0.5rem;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.mobile-dropdown-item:hover {
|
||||
color: var(--brand-gold-light);
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
FOOTER
|
||||
══════════════════════════════════════════════════════════ */
|
||||
.site-footer {
|
||||
background: var(--brand-purple-dark);
|
||||
color: #ffffff;
|
||||
padding: 4rem 1.5rem;
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
.footer-inner {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.footer-inner {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.footer-col { align-items: flex-start; }
|
||||
}
|
||||
|
||||
.footer-tagline {
|
||||
font-family: var(--font-serif);
|
||||
font-style: italic;
|
||||
font-size: 1.1rem;
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.footer-heading {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.25em;
|
||||
text-transform: uppercase;
|
||||
color: var(--brand-gold);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
color: rgba(255,255,255,0.75);
|
||||
font-size: 0.9rem;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
.footer-link:hover { color: var(--brand-gold-light); }
|
||||
|
||||
.social-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
transition: background 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
.social-btn:hover {
|
||||
background: var(--brand-gold);
|
||||
color: var(--brand-purple-dark);
|
||||
}
|
||||
|
||||
.footer-copy {
|
||||
font-size: 0.78rem;
|
||||
color: rgba(255,255,255,0.4);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
|
@ -1,19 +1,71 @@
|
|||
import { Link, useLocation } from "wouter";
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { Menu, X, Instagram, Facebook, Twitter } from "lucide-react";
|
||||
import AppBar from "@mui/material/AppBar";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import Box from "@mui/material/Box";
|
||||
import Container from "@mui/material/Container";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import { Menu, X, Instagram, Facebook, Twitter, ChevronDown, Flame, Sparkles, Star } from "lucide-react";
|
||||
import { ScrollCandle } from "./ScrollCandle";
|
||||
import "./Layout.css";
|
||||
|
||||
const logoPath = "/opengraph.jpg";
|
||||
|
||||
// ── Ticker messages ──────────────────────────────────────
|
||||
const TICKER_ITEMS = [
|
||||
{ icon: <Flame size={12} />, text: "Free shipping on orders over $50" },
|
||||
{ icon: <Sparkles size={12} />, text: "New Collection — Summer Solstice" },
|
||||
{ icon: <Star size={12} />, text: "Handpoured in small batches" },
|
||||
{ icon: <Flame size={12} />, text: "100% Natural Soy Wax" },
|
||||
{ icon: <Sparkles size={12} />, text: "Phthalate-free fragrance oils" },
|
||||
{ icon: <Star size={12} />, text: "Gift wrapping available at checkout" },
|
||||
];
|
||||
|
||||
// ── Nav structure with dropdowns ─────────────────────────
|
||||
const NAV_LINKS = [
|
||||
{ name: "Home", path: "/", dropdown: null },
|
||||
{
|
||||
name: "Products",
|
||||
path: "/products",
|
||||
dropdown: [
|
||||
{ label: "All Collections", path: "/products" },
|
||||
null, // divider
|
||||
{ label: "Floral Scents", path: "/products" },
|
||||
{ label: "Fresh Scents", path: "/products" },
|
||||
{ label: "Woody Scents", path: "/products" },
|
||||
{ label: "Sweet Scents", path: "/products" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "About",
|
||||
path: "/about",
|
||||
dropdown: [
|
||||
{ label: "Our Story", path: "/about" },
|
||||
{ label: "Our Process", path: "/about" },
|
||||
{ label: "Meet the Maker", path: "/about" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Contact",
|
||||
path: "/contact",
|
||||
dropdown: [
|
||||
{ label: "Send a Message", path: "/contact" },
|
||||
{ label: "Wholesale Inquiries", path: "/contact" },
|
||||
null,
|
||||
{ label: "Follow on Instagram", path: "/contact" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
const [location] = useLocation();
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [mobileOpenSection, setMobileOpenSection] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setScrolled(window.scrollY > 20);
|
||||
};
|
||||
const handleScroll = () => setScrolled(window.scrollY > 20);
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
|
|
@ -21,83 +73,181 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
setMobileMenuOpen(false);
|
||||
setMobileOpenSection(null);
|
||||
}, [location]);
|
||||
|
||||
const navLinks = [
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "About", path: "/about" },
|
||||
{ name: "Contact", path: "/contact" },
|
||||
];
|
||||
// Duplicate ticker items to create seamless loop
|
||||
const allTickerItems = [...TICKER_ITEMS, ...TICKER_ITEMS];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col font-sans bg-background text-foreground">
|
||||
<header
|
||||
className={`fixed top-0 w-full z-50 transition-all duration-500 ease-in-out ${
|
||||
scrolled
|
||||
? "bg-background/80 backdrop-blur-md shadow-[0_1px_0_0_hsl(var(--accent))]"
|
||||
: "bg-transparent"
|
||||
}`}
|
||||
<Box sx={{ minHeight: "100vh", display: "flex", flexDirection: "column" }}>
|
||||
|
||||
{/* ── Ticker Bar ── */}
|
||||
<Box
|
||||
className="ticker-bar"
|
||||
sx={{ position: "fixed", top: 0, left: 0, right: 0, zIndex: 1200 }}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-6 h-24 flex items-center justify-between">
|
||||
<Link href="/" className="flex items-center gap-4 group">
|
||||
<div className="w-12 h-12 rounded-full overflow-hidden border-2 border-transparent group-hover:border-accent transition-colors duration-300">
|
||||
<img src={logoPath} alt="Mani Candles Co. Logo" className="w-full h-full object-cover" />
|
||||
</div>
|
||||
<span className="font-script text-3xl text-primary drop-shadow-sm group-hover:text-accent transition-colors duration-300">
|
||||
Mani Candles
|
||||
<div className="ticker-track">
|
||||
{allTickerItems.map((item, i) => (
|
||||
<span className="ticker-item" key={i}>
|
||||
<span className="ticker-dot" />
|
||||
{item.icon}
|
||||
{item.text}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
{/* ── Main AppBar ── */}
|
||||
<AppBar
|
||||
position="fixed"
|
||||
sx={{
|
||||
top: 36, // sits below the ticker bar
|
||||
background: scrolled
|
||||
? "rgba(91,30,140,0.78)"
|
||||
: "rgba(91,30,140,0.58)",
|
||||
backdropFilter: "blur(22px) saturate(1.6)",
|
||||
WebkitBackdropFilter: "blur(22px) saturate(1.6)",
|
||||
boxShadow: scrolled ? "0 1px 0 0 rgba(212,162,76,0.5), 0 4px 24px rgba(58,18,96,0.35)" : "none",
|
||||
transition: "background 0.5s ease, box-shadow 0.5s ease",
|
||||
overflow: "visible",
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="xl">
|
||||
<Toolbar
|
||||
disableGutters
|
||||
sx={{ height: 76, justifyContent: "space-between", overflow: "visible" }}
|
||||
>
|
||||
{/* Logo */}
|
||||
<Link
|
||||
href="/"
|
||||
style={{ display: "flex", alignItems: "center", gap: "0.9rem", textDecoration: "none" }}
|
||||
>
|
||||
<div className="logo-img-wrap">
|
||||
<img
|
||||
src={logoPath}
|
||||
alt="Mani Candles Co. Logo"
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
/>
|
||||
</div>
|
||||
<span className="logo-text">Mani Candles</span>
|
||||
</Link>
|
||||
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
{navLinks.map((link) => (
|
||||
{/* Desktop Nav */}
|
||||
<Box
|
||||
component="nav"
|
||||
sx={{
|
||||
display: { xs: "none", md: "flex" },
|
||||
gap: 4,
|
||||
alignItems: "center",
|
||||
overflow: "visible",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{NAV_LINKS.map((link) => (
|
||||
<div className="nav-item" key={link.path + link.name}>
|
||||
<Link
|
||||
key={link.path}
|
||||
href={link.path}
|
||||
className={`font-display tracking-widest text-sm uppercase transition-colors duration-300 hover:text-accent ${
|
||||
location === link.path ? "text-accent" : "text-primary"
|
||||
}`}
|
||||
className={`nav-link${location === link.path ? " active" : ""}`}
|
||||
>
|
||||
{link.name}
|
||||
{link.dropdown && <ChevronDown size={13} className="nav-chevron" />}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<button
|
||||
className="md:hidden text-primary hover:text-accent transition-colors"
|
||||
{/* Dropdown */}
|
||||
{link.dropdown && (
|
||||
<div className="nav-dropdown">
|
||||
{link.dropdown.map((item, idx) =>
|
||||
item === null ? (
|
||||
<div key={`div-${idx}`} className="dropdown-divider" />
|
||||
) : (
|
||||
<Link key={item.label} href={item.path} className="dropdown-item">
|
||||
{item.label}
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* Mobile Toggle */}
|
||||
<IconButton
|
||||
sx={{ display: { md: "none" }, color: "#ffffff" }}
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
>
|
||||
{mobileMenuOpen ? <X size={28} /> : <Menu size={28} />}
|
||||
</button>
|
||||
</div>
|
||||
{mobileMenuOpen ? <X size={26} /> : <Menu size={26} />}
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
|
||||
{/* Mobile Drawer */}
|
||||
<AnimatePresence>
|
||||
{mobileMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: "auto" }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="md:hidden bg-background/95 backdrop-blur-md border-b border-accent/20 overflow-hidden"
|
||||
className="mobile-menu"
|
||||
>
|
||||
<div className="px-6 py-8 flex flex-col gap-6">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.path}
|
||||
href={link.path}
|
||||
className={`font-display tracking-widest text-lg uppercase transition-colors duration-300 ${
|
||||
location === link.path ? "text-accent" : "text-primary"
|
||||
}`}
|
||||
{NAV_LINKS.map((link) => (
|
||||
<div className="mobile-nav-section" key={link.name}>
|
||||
{link.dropdown ? (
|
||||
<>
|
||||
<button
|
||||
className={`mobile-nav-link${location === link.path ? " active" : ""}`}
|
||||
style={{ background: "none", border: "none", textAlign: "left", cursor: "pointer", width: "100%", display: "flex", justifyContent: "space-between", alignItems: "center" }}
|
||||
onClick={() => setMobileOpenSection(mobileOpenSection === link.name ? null : link.name)}
|
||||
>
|
||||
{link.name}
|
||||
<ChevronDown
|
||||
size={14}
|
||||
style={{
|
||||
transition: "transform 0.25s ease",
|
||||
transform: mobileOpenSection === link.name ? "rotate(180deg)" : "rotate(0deg)",
|
||||
opacity: 0.7,
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
<AnimatePresence>
|
||||
{mobileOpenSection === link.name && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: "auto" }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="mobile-dropdown-items"
|
||||
>
|
||||
{link.dropdown.map((item, i) =>
|
||||
item === null ? (
|
||||
<div key={i} style={{ height: 1, background: "rgba(255,255,255,0.07)", margin: "0.2rem 0" }} />
|
||||
) : (
|
||||
<Link key={item.label} href={item.path} className="mobile-dropdown-item">
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</header>
|
||||
</>
|
||||
) : (
|
||||
<Link
|
||||
href={link.path}
|
||||
className={`mobile-nav-link${location === link.path ? " active" : ""}`}
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</AppBar>
|
||||
|
||||
<main className="flex-grow pt-24">
|
||||
{/* ── Page Content — offset for ticker + appbar ── */}
|
||||
<Box component="main" sx={{ flexGrow: 1, pt: "112px" }}>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={location}
|
||||
|
|
@ -109,52 +259,52 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|||
{children}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</main>
|
||||
</Box>
|
||||
|
||||
<footer className="bg-primary text-primary-foreground py-16 mt-20">
|
||||
<div className="max-w-7xl mx-auto px-6 grid grid-cols-1 md:grid-cols-3 gap-12 text-center md:text-left">
|
||||
<div className="flex flex-col items-center md:items-start gap-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-full overflow-hidden bg-white p-1">
|
||||
<img src={logoPath} alt="Mani Candles Co. Logo" className="w-full h-full object-cover rounded-full" />
|
||||
{/* ── Scroll Candle ── */}
|
||||
<ScrollCandle />
|
||||
|
||||
{/* ── Footer ── */}
|
||||
<footer className="site-footer">
|
||||
<div className="footer-inner">
|
||||
<div className="footer-col">
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
|
||||
<div style={{ width: 56, height: 56, borderRadius: "50%", overflow: "hidden", background: "#fff", padding: 3 }}>
|
||||
<img
|
||||
src={logoPath}
|
||||
alt="Mani Candles Co."
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" }}
|
||||
/>
|
||||
</div>
|
||||
<span className="font-script text-4xl text-secondary">Mani Candles</span>
|
||||
<span className="font-script" style={{ fontSize: "2rem", color: "var(--brand-gold-light)" }}>
|
||||
Mani Candles
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-serif italic text-xl text-secondary/80">Handmade • Natural • Heartmade</p>
|
||||
<p className="footer-tagline">Handmade • Natural • Heartmade</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center md:items-start gap-4">
|
||||
<h4 className="font-display tracking-widest text-accent uppercase mb-2">Explore</h4>
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.path}
|
||||
href={link.path}
|
||||
className="hover:text-accent transition-colors font-sans font-light"
|
||||
>
|
||||
<div className="footer-col">
|
||||
<h4 className="footer-heading">Explore</h4>
|
||||
{NAV_LINKS.map((link) => (
|
||||
<Link key={link.path + link.name} href={link.path} className="footer-link">
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center md:items-start gap-6">
|
||||
<h4 className="font-display tracking-widest text-accent uppercase">Connect</h4>
|
||||
<div className="flex gap-4">
|
||||
<a href="#" className="w-10 h-10 rounded-full bg-secondary/10 flex items-center justify-center hover:bg-accent hover:text-primary transition-all duration-300">
|
||||
<Instagram size={20} />
|
||||
</a>
|
||||
<a href="#" className="w-10 h-10 rounded-full bg-secondary/10 flex items-center justify-center hover:bg-accent hover:text-primary transition-all duration-300">
|
||||
<Facebook size={20} />
|
||||
</a>
|
||||
<a href="#" className="w-10 h-10 rounded-full bg-secondary/10 flex items-center justify-center hover:bg-accent hover:text-primary transition-all duration-300">
|
||||
<Twitter size={20} />
|
||||
</a>
|
||||
<div className="footer-col">
|
||||
<h4 className="footer-heading">Connect</h4>
|
||||
<div style={{ display: "flex", gap: "0.75rem" }}>
|
||||
<a href="#" className="social-btn"><Instagram size={18} /></a>
|
||||
<a href="#" className="social-btn"><Facebook size={18} /></a>
|
||||
<a href="#" className="social-btn"><Twitter size={18} /></a>
|
||||
</div>
|
||||
<p className="font-sans font-light text-sm text-secondary/60 mt-4">
|
||||
<p className="footer-copy">
|
||||
© {new Date().getFullYear()} Mani Candles Co. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,291 @@
|
|||
/* ─── ScrollCandle.css ───────────────────────────────────── */
|
||||
|
||||
.scroll-candle-wrap {
|
||||
position: fixed;
|
||||
right: 28px;
|
||||
bottom: 0px;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
filter: drop-shadow(0 0 18px rgba(255, 160, 40, 0.35));
|
||||
}
|
||||
|
||||
/* ── FLAME SYSTEM ─────────────────────────────────── */
|
||||
.candle-flame-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
/* Outer glow halo */
|
||||
.flame-glow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(255, 200, 50, 0.45) 0%, transparent 70%);
|
||||
animation: glow-pulse 1.8s ease-in-out infinite alternate;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
@keyframes glow-pulse {
|
||||
0% {
|
||||
transform: translateX(-50%) scale(1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-50%) scale(1.35);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Flame outer — large orange teardrop */
|
||||
.flame-outer {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 18px;
|
||||
height: 34px;
|
||||
background: linear-gradient(to top, #ff6a00, #ffaa00, #ffe066);
|
||||
border-radius: 50% 50% 30% 30% / 60% 60% 40% 40%;
|
||||
transform-origin: bottom center;
|
||||
animation: flicker-outer 0.9s ease-in-out infinite alternate;
|
||||
box-shadow:
|
||||
0 0 8px 3px rgba(255, 140, 0, 0.6),
|
||||
0 0 18px 6px rgba(255, 100, 0, 0.4);
|
||||
}
|
||||
|
||||
@keyframes flicker-outer {
|
||||
0% {
|
||||
transform: scaleX(1) scaleY(1) rotate(-2deg);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: scaleX(0.88) scaleY(1.05) rotate(2deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scaleX(1.05) scaleY(0.95) rotate(-1deg);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: scaleX(0.92) scaleY(1.08) rotate(3deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scaleX(1.02) scaleY(1.02) rotate(-2deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Flame inner — bright white-yellow core */
|
||||
.flame-inner {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 3;
|
||||
width: 8px;
|
||||
height: 18px;
|
||||
background: linear-gradient(to top, #fff8c5, #fff, rgba(255, 255, 255, 0));
|
||||
border-radius: 50% 50% 30% 30% / 60% 60% 40% 40%;
|
||||
animation: flicker-inner 0.7s ease-in-out infinite alternate;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@keyframes flicker-inner {
|
||||
0% {
|
||||
transform: translateX(-50%) scaleX(1) scaleY(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateX(-50%) scaleX(0.85) scaleY(1.1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-50%) scaleX(1.1) scaleY(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── SMOKE PARTICLES ─────────────────────────────── */
|
||||
.smoke-wrap {
|
||||
position: absolute;
|
||||
top: -18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
pointer-events: none;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.smoke-particle {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background: rgba(200, 180, 220, 0.45);
|
||||
transform: translateX(-50%);
|
||||
animation: smoke-rise 2.4s ease-out infinite;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.smoke-particle:nth-child(2) {
|
||||
animation-delay: 0.8s;
|
||||
left: 40%;
|
||||
}
|
||||
|
||||
.smoke-particle:nth-child(3) {
|
||||
animation-delay: 1.6s;
|
||||
left: 60%;
|
||||
}
|
||||
|
||||
@keyframes smoke-rise {
|
||||
0% {
|
||||
transform: translateX(-50%) translateY(0) scale(0.6);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-50%) translateY(-22px) scale(2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── WICK ──────────────────────────────────────────── */
|
||||
.candle-wick {
|
||||
width: 2px;
|
||||
height: 8px;
|
||||
background: linear-gradient(to bottom, #3a2000, #1a0f00);
|
||||
border-radius: 1px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
/* ── CANDLE BODY ──────────────────────────────────── */
|
||||
.candle-body {
|
||||
position: relative;
|
||||
width: 22px;
|
||||
background: linear-gradient(to right,
|
||||
#e8dcc8 0%,
|
||||
#fdf6e3 25%,
|
||||
#fff9f0 50%,
|
||||
#fdf6e3 75%,
|
||||
#d9cbb5 100%);
|
||||
border-radius: 3px 3px 4px 4px;
|
||||
transition: height 0.18s linear;
|
||||
box-shadow:
|
||||
inset -3px 0 6px rgba(0, 0, 0, 0.1),
|
||||
inset 3px 0 4px rgba(255, 255, 255, 0.4);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Candle top melted rim */
|
||||
.candle-body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
height: 6px;
|
||||
background: radial-gradient(ellipse at center, #ffe8b0 0%, #e8d5a0 100%);
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ── WAX DRIPS ──────────────────────────────────────── */
|
||||
.wax-drip {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
background: linear-gradient(to bottom, rgba(253, 246, 227, 0.95), rgba(232, 220, 200, 0.6));
|
||||
border-radius: 0 0 4px 4px;
|
||||
animation: drip-grow 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.wax-drip-1 {
|
||||
left: -3px;
|
||||
height: 14px;
|
||||
animation-delay: 0s;
|
||||
animation-duration: 4.5s;
|
||||
}
|
||||
|
||||
.wax-drip-2 {
|
||||
right: -3px;
|
||||
height: 10px;
|
||||
animation-delay: 1.2s;
|
||||
animation-duration: 3.8s;
|
||||
}
|
||||
|
||||
.wax-drip-3 {
|
||||
left: 2px;
|
||||
height: 18px;
|
||||
animation-delay: 2.5s;
|
||||
animation-duration: 5.2s;
|
||||
}
|
||||
|
||||
@keyframes drip-grow {
|
||||
0% {
|
||||
height: 6px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
60% {
|
||||
height: 20px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
height: 6px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── CANDLE BASE / HOLDER ────────────────────────── */
|
||||
.candle-base {
|
||||
width: 32px;
|
||||
height: 10px;
|
||||
background: linear-gradient(to bottom, #c8a96e, #a07840);
|
||||
border-radius: 3px 3px 6px 6px;
|
||||
box-shadow:
|
||||
0 3px 8px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 2px rgba(255, 200, 100, 0.4);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Holder shine */
|
||||
.candle-base::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 5px;
|
||||
width: 8px;
|
||||
height: 3px;
|
||||
background: rgba(255, 220, 140, 0.4);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* ── SCROLL LABEL ───────────────────────────────── */
|
||||
.candle-scroll-label {
|
||||
margin-top: 6px;
|
||||
font-family: var(--font-script);
|
||||
font-size: 0.68rem;
|
||||
color: rgba(212, 162, 76, 0.7);
|
||||
letter-spacing: 0.05em;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import "./ScrollCandle.css";
|
||||
|
||||
// Space taken by flame + wick + base + label + bottom offset + header (ticker 36 + AppBar 76) + safety gap
|
||||
const NON_BODY_HEIGHT = 70 + 8 + 10 + 24 + 40 + 112 + 16;
|
||||
const CANDLE_MIN_HEIGHT = 14; // px — almost fully melted
|
||||
|
||||
export function ScrollCandle() {
|
||||
const [maxHeight, setMaxHeight] = useState(
|
||||
() => Math.max(200, window.innerHeight - NON_BODY_HEIGHT)
|
||||
);
|
||||
const [candleHeight, setCandleHeight] = useState(
|
||||
() => Math.max(200, window.innerHeight - NON_BODY_HEIGHT)
|
||||
);
|
||||
const [scrollPct, setScrollPct] = useState(0);
|
||||
|
||||
// Recalculate on resize
|
||||
useEffect(() => {
|
||||
const onResize = () => {
|
||||
const newMax = Math.max(200, window.innerHeight - NON_BODY_HEIGHT);
|
||||
setMaxHeight(newMax);
|
||||
};
|
||||
window.addEventListener("resize", onResize);
|
||||
return () => window.removeEventListener("resize", onResize);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const scrollTop = window.scrollY;
|
||||
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||
const pct = docHeight > 0 ? Math.min(scrollTop / docHeight, 1) : 0;
|
||||
|
||||
setScrollPct(pct);
|
||||
setCandleHeight(
|
||||
Math.max(CANDLE_MIN_HEIGHT, maxHeight - pct * (maxHeight - CANDLE_MIN_HEIGHT))
|
||||
);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, [maxHeight]);
|
||||
|
||||
// Flame shrinks slightly as candle melts
|
||||
const flameScale = 0.6 + (candleHeight / maxHeight) * 0.4;
|
||||
// Label changes as candle melts
|
||||
const label =
|
||||
scrollPct < 0.15 ? "just lit" :
|
||||
scrollPct < 0.45 ? "burning..." :
|
||||
scrollPct < 0.75 ? "melting..." :
|
||||
scrollPct < 0.92 ? "almost gone" :
|
||||
"★ keep scrolling";
|
||||
|
||||
return (
|
||||
<div className="scroll-candle-wrap" aria-hidden="true">
|
||||
{/* ── Flame ── */}
|
||||
<div
|
||||
className="candle-flame-wrap"
|
||||
style={{ transform: `scale(${flameScale})`, transformOrigin: "bottom center" }}
|
||||
>
|
||||
{/* Smoke */}
|
||||
<div className="smoke-wrap">
|
||||
<div className="smoke-particle" />
|
||||
<div className="smoke-particle" />
|
||||
<div className="smoke-particle" />
|
||||
</div>
|
||||
|
||||
{/* Glow halo */}
|
||||
<div className="flame-glow" />
|
||||
|
||||
{/* Outer flame */}
|
||||
<div className="flame-outer">
|
||||
{/* Inner core */}
|
||||
<div className="flame-inner" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Wick ── */}
|
||||
<div className="candle-wick" />
|
||||
|
||||
{/* ── Candle Body (shrinks on scroll) ── */}
|
||||
<div className="candle-body" style={{ height: candleHeight }}>
|
||||
<div className="wax-drip wax-drip-1" />
|
||||
<div className="wax-drip wax-drip-2" />
|
||||
<div className="wax-drip wax-drip-3" />
|
||||
</div>
|
||||
|
||||
{/* ── Base / Holder ── */}
|
||||
<div className="candle-base" />
|
||||
|
||||
{/* ── Label ── */}
|
||||
<p
|
||||
className="candle-scroll-label"
|
||||
style={{ opacity: scrollPct > 0.02 ? 1 : 0 }}
|
||||
>
|
||||
{label}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
215
src/index.css
215
src/index.css
|
|
@ -1,176 +1,85 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700&family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Great+Vibes&family=Inter:wght@300;400;500;600&display=swap');
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: hsl(var(--background));
|
||||
--color-foreground: hsl(var(--foreground));
|
||||
--color-border: hsl(var(--border));
|
||||
--color-input: hsl(var(--input));
|
||||
--color-ring: hsl(var(--ring));
|
||||
|
||||
--color-card: hsl(var(--card));
|
||||
--color-card-foreground: hsl(var(--card-foreground));
|
||||
--color-card-border: hsl(var(--card-border));
|
||||
|
||||
--color-popover: hsl(var(--popover));
|
||||
--color-popover-foreground: hsl(var(--popover-foreground));
|
||||
--color-popover-border: hsl(var(--popover-border));
|
||||
|
||||
--color-primary: hsl(var(--primary));
|
||||
--color-primary-foreground: hsl(var(--primary-foreground));
|
||||
--color-primary-border: var(--primary-border);
|
||||
|
||||
--color-secondary: hsl(var(--secondary));
|
||||
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
||||
--color-secondary-border: var(--secondary-border);
|
||||
|
||||
--color-muted: hsl(var(--muted));
|
||||
--color-muted-foreground: hsl(var(--muted-foreground));
|
||||
--color-muted-border: var(--muted-border);
|
||||
|
||||
--color-accent: hsl(var(--accent));
|
||||
--color-accent-foreground: hsl(var(--accent-foreground));
|
||||
--color-accent-border: var(--accent-border);
|
||||
|
||||
--color-destructive: hsl(var(--destructive));
|
||||
--color-destructive-foreground: hsl(var(--destructive-foreground));
|
||||
--color-destructive-border: var(--destructive-border);
|
||||
|
||||
--color-chart-1: hsl(var(--chart-1));
|
||||
--color-chart-2: hsl(var(--chart-2));
|
||||
--color-chart-3: hsl(var(--chart-3));
|
||||
--color-chart-4: hsl(var(--chart-4));
|
||||
--color-chart-5: hsl(var(--chart-5));
|
||||
|
||||
--color-sidebar: hsl(var(--sidebar));
|
||||
--color-sidebar-foreground: hsl(var(--sidebar-foreground));
|
||||
--color-sidebar-border: hsl(var(--sidebar-border));
|
||||
--color-sidebar-primary: hsl(var(--sidebar-primary));
|
||||
--color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground));
|
||||
--color-sidebar-primary-border: var(--sidebar-primary-border);
|
||||
--color-sidebar-accent: hsl(var(--sidebar-accent));
|
||||
--color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));
|
||||
--color-sidebar-accent-border: var(--sidebar-accent-border);
|
||||
--color-sidebar-ring: hsl(var(--sidebar-ring));
|
||||
|
||||
--font-sans: var(--app-font-sans);
|
||||
--font-serif: var(--app-font-serif);
|
||||
--font-display: var(--app-font-display);
|
||||
--font-script: var(--app-font-script);
|
||||
--font-mono: var(--app-font-mono);
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
/* ─── CSS Custom Properties ─────────────────────────────── */
|
||||
:root {
|
||||
--background: 272 68% 94%;
|
||||
--foreground: 272 65% 18%;
|
||||
--card: 272 68% 97%;
|
||||
--card-foreground: 272 65% 18%;
|
||||
--card-border: 272 40% 85%;
|
||||
--popover: 272 68% 97%;
|
||||
--popover-foreground: 272 65% 18%;
|
||||
--popover-border: 272 40% 85%;
|
||||
--primary: 272 65% 34%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 272 68% 87%;
|
||||
--secondary-foreground: 272 65% 25%;
|
||||
--muted: 272 40% 90%;
|
||||
--muted-foreground: 272 30% 50%;
|
||||
--accent: 38 59% 56%;
|
||||
--accent-foreground: 0 0% 100%;
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--border: 272 40% 82%;
|
||||
--input: 272 40% 82%;
|
||||
--ring: 272 65% 34%;
|
||||
--chart-1: 272 65% 34%;
|
||||
--chart-2: 38 59% 56%;
|
||||
--chart-3: 272 68% 70%;
|
||||
--chart-4: 44 100% 71%;
|
||||
--chart-5: 272 50% 55%;
|
||||
--sidebar: 272 68% 87%;
|
||||
--sidebar-foreground: 272 65% 18%;
|
||||
--sidebar-border: 272 40% 80%;
|
||||
--sidebar-primary: 272 65% 34%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 38 59% 56%;
|
||||
--sidebar-accent-foreground: 0 0% 100%;
|
||||
--sidebar-ring: 272 65% 34%;
|
||||
--app-font-sans: 'Inter', sans-serif;
|
||||
--app-font-serif: 'Cormorant Garamond', serif;
|
||||
--app-font-display: 'Cinzel', serif;
|
||||
--app-font-script: 'Great Vibes', cursive;
|
||||
--app-font-mono: Menlo, monospace;
|
||||
--radius: 0.75rem;
|
||||
|
||||
--brand-purple: #5B1E8C;
|
||||
--brand-purple-dark: #3A1260;
|
||||
--brand-purple-mid: #7B3DB8;
|
||||
--brand-lavender: #DCC6F2;
|
||||
--brand-light-lavender:#EFE4FA;
|
||||
--brand-card: #F8F2FD;
|
||||
--brand-gold: #D4A24C;
|
||||
--brand-flame-glow: #FFD36B;
|
||||
--brand-flame-core: #FFF3C4;
|
||||
--brand-gold-light: #FFD36B;
|
||||
--brand-gold-core: #FFF3C4;
|
||||
--brand-text: #2D0D52;
|
||||
--brand-muted: #7B5A9E;
|
||||
--brand-border: #D5BEF0;
|
||||
|
||||
--font-sans: 'Inter', sans-serif;
|
||||
--font-serif: 'Cormorant Garamond', serif;
|
||||
--font-display: 'Cinzel', serif;
|
||||
--font-script: 'Great Vibes', cursive;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 272 65% 10%;
|
||||
--foreground: 272 68% 94%;
|
||||
--card: 272 65% 15%;
|
||||
--card-foreground: 272 68% 94%;
|
||||
--card-border: 272 65% 25%;
|
||||
--popover: 272 65% 15%;
|
||||
--popover-foreground: 272 68% 94%;
|
||||
--popover-border: 272 65% 25%;
|
||||
--primary: 272 68% 87%;
|
||||
--primary-foreground: 272 65% 18%;
|
||||
--secondary: 272 65% 20%;
|
||||
--secondary-foreground: 272 68% 94%;
|
||||
--muted: 272 65% 20%;
|
||||
--muted-foreground: 272 40% 70%;
|
||||
--accent: 38 59% 56%;
|
||||
--accent-foreground: 0 0% 100%;
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--border: 272 65% 25%;
|
||||
--input: 272 65% 25%;
|
||||
--ring: 272 68% 87%;
|
||||
--chart-1: 272 68% 87%;
|
||||
--chart-2: 38 59% 56%;
|
||||
--chart-3: 272 68% 70%;
|
||||
--chart-4: 44 100% 71%;
|
||||
--chart-5: 272 50% 55%;
|
||||
/* ─── Reset / Base ───────────────────────────────────────── */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply font-sans antialiased bg-background text-foreground;
|
||||
font-family: var(--font-sans);
|
||||
background-color: var(--brand-light-lavender);
|
||||
color: var(--brand-text);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@apply font-serif;
|
||||
}
|
||||
font-family: var(--font-serif);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ─── Keyframe Animations ────────────────────────────────── */
|
||||
@keyframes flame-flicker {
|
||||
0%, 100% {
|
||||
transform: scale(1) translateY(0);
|
||||
opacity: 0.9;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05) translateY(-2px);
|
||||
opacity: 1;
|
||||
}
|
||||
0%, 100% { transform: scale(1) translateY(0); opacity: 0.9; }
|
||||
50% { transform: scale(1.05) translateY(-2px); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(24px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ─── Global Utility Classes ─────────────────────────────── */
|
||||
.animate-flame {
|
||||
animation: flame-flicker 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.font-script {
|
||||
font-family: var(--font-script);
|
||||
}
|
||||
|
||||
.font-display {
|
||||
font-family: var(--font-display);
|
||||
}
|
||||
|
||||
.font-serif {
|
||||
font-family: var(--font-serif);
|
||||
}
|
||||
|
|
|
|||
10
src/main.tsx
10
src/main.tsx
|
|
@ -1,5 +1,13 @@
|
|||
import { createRoot } from "react-dom/client";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import CssBaseline from "@mui/material/CssBaseline";
|
||||
import App from "./App";
|
||||
import theme from "./theme";
|
||||
import "./index.css";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { motion } from "framer-motion";
|
||||
import { Droplets, Flame, HeartHandshake } from "lucide-react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Container from "@mui/material/Container";
|
||||
import "./about.css";
|
||||
|
||||
const logoPath = "/opengraph.jpg";
|
||||
|
||||
export default function AboutPage() {
|
||||
const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
||||
|
|
@ -13,127 +15,91 @@ export default function AboutPage() {
|
|||
visible: { transition: { staggerChildren: 0.2 } },
|
||||
};
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div className="w-full min-h-screen bg-background">
|
||||
{/* Hero */}
|
||||
<section className="py-24 px-6 bg-card">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={stagger}
|
||||
className="max-w-4xl mx-auto text-center"
|
||||
>
|
||||
<motion.h1 variants={fadeInUp} className="font-serif text-5xl md:text-6xl lg:text-7xl text-primary mb-6">
|
||||
The Light Behind the Wax
|
||||
</motion.h1>
|
||||
<motion.div variants={fadeInUp} className="w-24 h-1 bg-accent mx-auto mb-8" />
|
||||
<motion.p variants={fadeInUp} className="font-sans text-lg text-muted-foreground leading-relaxed">
|
||||
Mani Candles Co. is a small handmade candle brand crafted with love, natural ingredients, and intention. Every candle is poured by hand in small batches, designed to bring warmth, luxury, and a touch of romance into your sacred spaces.
|
||||
<Box>
|
||||
{/* ── Hero ── */}
|
||||
<section className="about-hero">
|
||||
<Container maxWidth="md">
|
||||
<motion.div initial="hidden" animate="visible" variants={stagger}>
|
||||
<motion.h1 variants={fadeInUp}>The Light Behind the Wax</motion.h1>
|
||||
<div className="section-divider" />
|
||||
<motion.p variants={fadeInUp}>
|
||||
Mani Candles Co. is a small handmade candle brand crafted with love, natural ingredients, and intention.
|
||||
Every candle is poured by hand in small batches, designed to bring warmth, luxury, and a touch of romance
|
||||
into your sacred spaces.
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
{/* Brand Values */}
|
||||
<section className="py-24 px-6">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
|
||||
{/* ── Brand Values ── */}
|
||||
<section className="about-values">
|
||||
<Container maxWidth="lg">
|
||||
<div className="values-grid">
|
||||
{[
|
||||
{
|
||||
icon: <HeartHandshake size={40} />,
|
||||
title: "Handmade",
|
||||
desc: "We believe in the power of human touch. No machines, no mass production. Just careful hands pouring beautiful creations."
|
||||
},
|
||||
{
|
||||
icon: <Droplets size={40} />,
|
||||
title: "Natural",
|
||||
desc: "Pure soy wax, lead-free cotton wicks, and premium fragrance oils infused with essential oils. Clean burns only."
|
||||
},
|
||||
{
|
||||
icon: <Flame size={40} />,
|
||||
title: "Heartmade",
|
||||
desc: "A candle is more than wax and scent. It's ambiance, memory, and comfort. We put our heart into every jar."
|
||||
}
|
||||
].map((val, i) => (
|
||||
{ Icon: HeartHandshake, title: "Handmade", desc: "We believe in the power of human touch. No machines, no mass production. Just careful hands pouring beautiful creations." },
|
||||
{ Icon: Droplets, title: "Natural", desc: "Pure soy wax, lead-free cotton wicks, and premium fragrance oils infused with essential oils. Clean burns only." },
|
||||
{ Icon: Flame, title: "Heartmade", desc: "A candle is more than wax and scent. It's ambiance, memory, and comfort. We put our heart into every jar." },
|
||||
].map(({ Icon, title, desc }, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
key={title}
|
||||
className="value-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 } }
|
||||
}}
|
||||
className="text-center p-8 bg-card rounded-xl shadow-sm border border-border/50 hover:shadow-md transition-shadow"
|
||||
variants={{ hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.2, duration: 0.6 } } }}
|
||||
>
|
||||
<div className="text-accent mb-6 flex justify-center">{val.icon}</div>
|
||||
<h3 className="font-serif text-2xl text-primary mb-4">{val.title}</h3>
|
||||
<p className="font-sans text-muted-foreground">{val.desc}</p>
|
||||
<div className="value-icon"><Icon size={40} /></div>
|
||||
<h3>{title}</h3>
|
||||
<p>{desc}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
{/* Meet the Maker */}
|
||||
<section className="py-24 px-6 bg-primary text-primary-foreground relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent to-primary/50 mix-blend-overlay" />
|
||||
<div className="max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-2 gap-16 items-center relative z-10">
|
||||
{/* ── Meet the Maker ── */}
|
||||
<section className="about-maker">
|
||||
<div className="maker-gradient-overlay" />
|
||||
<Container maxWidth="lg">
|
||||
<div className="maker-grid">
|
||||
<motion.div
|
||||
className="maker-img-wrap"
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="aspect-square rounded-2xl overflow-hidden shadow-2xl relative"
|
||||
>
|
||||
<img
|
||||
src={logoPath}
|
||||
alt="Mani Candles Co. Logo Art"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 ring-1 ring-inset ring-white/20 rounded-2xl" />
|
||||
<img src={logoPath} alt="Mani Candles Co. Logo Art" />
|
||||
<div className="maker-img-ring" />
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="maker-content"
|
||||
initial={{ opacity: 0, x: 50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<h2 className="font-serif text-4xl md:text-5xl mb-6">Meet the Maker</h2>
|
||||
<div className="w-20 h-1 bg-accent mb-8" />
|
||||
<div className="font-sans space-y-6 text-primary-foreground/90 text-lg leading-relaxed">
|
||||
<p>
|
||||
What started as a quiet evening hobby quickly blossomed into a deep passion for the alchemy of scent and light.
|
||||
</p>
|
||||
<p>
|
||||
I wanted to create more than just candles; I wanted to craft experiences. The flicker of the flame, the slow release of carefully blended perfumes—these elements have the power to transform a chaotic day into a moment of pure serenity.
|
||||
</p>
|
||||
<p>
|
||||
Every Mani candle is a piece of my heart, poured in small batches to ensure that the luxury you experience is entirely personal and authentically handmade.
|
||||
</p>
|
||||
<div className="pt-6">
|
||||
<span className="font-script text-4xl text-accent">With love,</span>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Meet the Maker</h2>
|
||||
<div className="maker-divider" />
|
||||
<p>What started as a quiet evening hobby quickly blossomed into a deep passion for the alchemy of scent and light.</p>
|
||||
<p>I wanted to create more than just candles; I wanted to craft experiences. The flicker of the flame, the slow release of carefully blended perfumes—these elements have the power to transform a chaotic day into a moment of pure serenity.</p>
|
||||
<p>Every Mani candle is a piece of my heart, poured in small batches to ensure that the luxury you experience is entirely personal and authentically handmade.</p>
|
||||
<span className="maker-signature">With love,</span>
|
||||
</motion.div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
{/* Process Timeline */}
|
||||
<section className="py-24 px-6 bg-background">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={fadeInUp}
|
||||
className="text-center mb-20"
|
||||
>
|
||||
<h2 className="font-serif text-4xl text-primary mb-4">Our Process</h2>
|
||||
<p className="font-sans text-muted-foreground">The journey of a Mani Candle.</p>
|
||||
</motion.div>
|
||||
{/* ── Process Timeline ── */}
|
||||
<section className="about-process">
|
||||
<Container maxWidth="md">
|
||||
<h2>Our Process</h2>
|
||||
<p className="subtitle">The journey of a Mani Candle.</p>
|
||||
|
||||
<div className="space-y-12">
|
||||
<div className="timeline">
|
||||
{[
|
||||
{ step: "01", title: "Sourcing", desc: "We select only the finest natural soy wax and premium, phthalate-free fragrances." },
|
||||
{ step: "02", title: "Blending", desc: "Fragrances are meticulously mixed at the exact temperature for optimal throw." },
|
||||
|
|
@ -143,24 +109,22 @@ export default function AboutPage() {
|
|||
].map((item, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="timeline-item"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: i * 0.1, duration: 0.5 }}
|
||||
className="flex flex-col md:flex-row gap-6 md:gap-12 items-start md:items-center"
|
||||
>
|
||||
<div className="font-serif text-6xl text-accent/30 font-light w-24 flex-shrink-0">
|
||||
{item.step}
|
||||
</div>
|
||||
<div className="flex-grow pb-8 border-b border-border/50">
|
||||
<h3 className="font-serif text-2xl text-primary mb-2">{item.title}</h3>
|
||||
<p className="font-sans text-muted-foreground">{item.desc}</p>
|
||||
<div className="timeline-step">{item.step}</div>
|
||||
<div className="timeline-body">
|
||||
<h3>{item.title}</h3>
|
||||
<p>{item.desc}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
import { motion } from "framer-motion";
|
||||
import { Mail, MapPin, Phone, Instagram } from "lucide-react";
|
||||
import { HeartHandshake } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Container from "@mui/material/Container";
|
||||
import "./contact.css";
|
||||
|
||||
const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
||||
};
|
||||
|
||||
export default function ContactPage() {
|
||||
const [formStatus, setFormStatus] = useState<"idle" | "submitting" | "success">("idle");
|
||||
|
|
@ -8,197 +17,117 @@ export default function ContactPage() {
|
|||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setFormStatus("submitting");
|
||||
// Simulate network request
|
||||
setTimeout(() => {
|
||||
setFormStatus("success");
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
||||
setTimeout(() => setFormStatus("success"), 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full min-h-screen bg-background">
|
||||
<section className="py-24 px-6 bg-card text-center">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={fadeInUp}
|
||||
className="max-w-3xl mx-auto"
|
||||
>
|
||||
<h1 className="font-serif text-5xl md:text-6xl text-primary mb-6">Get in Touch</h1>
|
||||
<p className="font-sans text-muted-foreground text-lg mb-8">
|
||||
Whether you have a question about an order, wholesale inquiries, or simply want to share how much you love your new candle, we'd love to hear from you.
|
||||
</p>
|
||||
<div className="w-24 h-1 bg-accent mx-auto" />
|
||||
<Box>
|
||||
{/* ── Hero ── */}
|
||||
<section className="contact-hero">
|
||||
<Container maxWidth="md">
|
||||
<motion.div initial="hidden" animate="visible" variants={fadeInUp}>
|
||||
<h1>Get in Touch</h1>
|
||||
<p>Whether you have a question about an order, wholesale inquiries, or simply want to share how much you love your new candle, we'd love to hear from you.</p>
|
||||
<div className="section-divider" />
|
||||
</motion.div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<section className="py-20 px-6">
|
||||
<div className="max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-2 gap-16">
|
||||
|
||||
{/* Contact Info */}
|
||||
{/* ── Contact Body ── */}
|
||||
<section className="contact-body">
|
||||
<Container maxWidth="lg">
|
||||
<div className="contact-grid">
|
||||
{/* Info Panel */}
|
||||
<motion.div
|
||||
className="contact-info-panel"
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="flex flex-col gap-10"
|
||||
>
|
||||
<h2>Contact Information</h2>
|
||||
<div className="contact-info-list">
|
||||
{[
|
||||
{ Icon: Mail, label: "Email", value: <a href="mailto:hello@manicandles.co">hello@manicandles.co</a> },
|
||||
{ Icon: Phone, label: "Phone", value: <>Available Mon–Fri, 9am–5pm EST<br />+1 (555) 123-4567</> },
|
||||
{ Icon: MapPin, label: "Studio", value: <>New York, NY<br />(By appointment only)</> },
|
||||
].map(({ Icon, label, value }) => (
|
||||
<div key={label} className="contact-info-item">
|
||||
<div className="contact-info-icon"><Icon size={20} /></div>
|
||||
<div>
|
||||
<h2 className="font-serif text-3xl text-primary mb-8">Contact Information</h2>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-12 h-12 rounded-full bg-secondary/50 flex items-center justify-center text-primary flex-shrink-0">
|
||||
<Mail size={20} />
|
||||
<div className="contact-info-label">{label}</div>
|
||||
<div className="contact-info-value">{value}</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-display tracking-widest uppercase text-sm text-primary mb-1">Email</h4>
|
||||
<a href="mailto:hello@manicandles.co" className="font-sans text-muted-foreground hover:text-accent transition-colors">
|
||||
hello@manicandles.co
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-12 h-12 rounded-full bg-secondary/50 flex items-center justify-center text-primary flex-shrink-0">
|
||||
<Phone size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-display tracking-widest uppercase text-sm text-primary mb-1">Phone</h4>
|
||||
<p className="font-sans text-muted-foreground">
|
||||
Available Mon-Fri, 9am - 5pm EST<br/>
|
||||
+1 (555) 123-4567
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-12 h-12 rounded-full bg-secondary/50 flex items-center justify-center text-primary flex-shrink-0">
|
||||
<MapPin size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-display tracking-widest uppercase text-sm text-primary mb-1">Studio</h4>
|
||||
<p className="font-sans text-muted-foreground">
|
||||
New York, NY<br/>
|
||||
(By appointment only)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-8 bg-card border border-border rounded-xl mt-4">
|
||||
<h3 className="font-serif text-2xl text-primary mb-4">Follow Our Journey</h3>
|
||||
<p className="font-sans text-muted-foreground mb-6">
|
||||
Join our community on Instagram for behind-the-scenes peeks, styling inspiration, and launch announcements.
|
||||
</p>
|
||||
<a href="#" className="inline-flex items-center gap-2 text-primary hover:text-accent transition-colors font-medium">
|
||||
<div className="contact-social-box">
|
||||
<h3>Follow Our Journey</h3>
|
||||
<p>Join our community on Instagram for behind-the-scenes peeks, styling inspiration, and launch announcements.</p>
|
||||
<a href="#" className="contact-ig-link">
|
||||
<Instagram size={20} />
|
||||
<span>@manicandles.co</span>
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Form */}
|
||||
{/* Form Panel */}
|
||||
<motion.div
|
||||
className="contact-form-panel"
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="bg-white p-8 md:p-12 shadow-xl border border-border/50 rounded-none relative"
|
||||
>
|
||||
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-primary via-accent to-primary" />
|
||||
<h2 className="font-serif text-3xl text-primary mb-8">Send a Message</h2>
|
||||
<div className="contact-form-top-bar" />
|
||||
|
||||
{formStatus === "success" ? (
|
||||
<motion.div
|
||||
className="contact-success"
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="flex flex-col items-center justify-center text-center py-12"
|
||||
>
|
||||
<div className="w-16 h-16 bg-green-50 text-green-600 rounded-full flex items-center justify-center mb-6">
|
||||
<HeartHandshake size={32} />
|
||||
</div>
|
||||
<h3 className="font-serif text-2xl text-primary mb-2">Thank you!</h3>
|
||||
<p className="font-sans text-muted-foreground">
|
||||
Your message has been received. We will get back to you as soon as possible.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setFormStatus("idle")}
|
||||
className="mt-8 text-accent hover:text-primary transition-colors font-display uppercase tracking-widest text-sm border-b border-accent pb-1"
|
||||
>
|
||||
Send another
|
||||
</button>
|
||||
<div className="success-icon-wrap"><HeartHandshake size={32} /></div>
|
||||
<h3>Thank you!</h3>
|
||||
<p>Your message has been received. We will get back to you as soon as possible.</p>
|
||||
<button className="send-another-btn" onClick={() => setFormStatus("idle")}>Send another</button>
|
||||
</motion.div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="name" className="font-display uppercase tracking-widest text-xs text-primary">Name</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
required
|
||||
className="w-full bg-background border-b border-border py-3 px-4 focus:outline-none focus:border-accent transition-colors font-sans"
|
||||
placeholder="Your name"
|
||||
/>
|
||||
<>
|
||||
<h2>Send a Message</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-row">
|
||||
<div className="form-field">
|
||||
<label htmlFor="name" className="form-label">Name</label>
|
||||
<input id="name" type="text" className="form-input" placeholder="Your name" required />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="email" className="font-display uppercase tracking-widest text-xs text-primary">Email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
className="w-full bg-background border-b border-border py-3 px-4 focus:outline-none focus:border-accent transition-colors font-sans"
|
||||
placeholder="Your email"
|
||||
/>
|
||||
<div className="form-field">
|
||||
<label htmlFor="email" className="form-label">Email</label>
|
||||
<input id="email" type="email" className="form-input" placeholder="Your email" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="subject" className="font-display uppercase tracking-widest text-xs text-primary">Subject</label>
|
||||
<input
|
||||
id="subject"
|
||||
type="text"
|
||||
required
|
||||
className="w-full bg-background border-b border-border py-3 px-4 focus:outline-none focus:border-accent transition-colors font-sans"
|
||||
placeholder="How can we help?"
|
||||
/>
|
||||
<div className="form-field">
|
||||
<label htmlFor="subject" className="form-label">Subject</label>
|
||||
<input id="subject" type="text" className="form-input" placeholder="How can we help?" required />
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="message" className="font-display uppercase tracking-widest text-xs text-primary">Message</label>
|
||||
<textarea
|
||||
id="message"
|
||||
rows={5}
|
||||
required
|
||||
className="w-full bg-background border border-border py-3 px-4 focus:outline-none focus:border-accent transition-colors font-sans resize-none"
|
||||
placeholder="Write your message here..."
|
||||
/>
|
||||
<div className="form-field">
|
||||
<label htmlFor="message" className="form-label">Message</label>
|
||||
<textarea id="message" className="form-textarea" placeholder="Write your message here..." required />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={formStatus === "submitting"}
|
||||
className="w-full py-4 bg-primary text-primary-foreground font-display tracking-widest uppercase text-sm hover:bg-accent transition-colors disabled:opacity-70"
|
||||
>
|
||||
<button type="submit" className="form-submit" disabled={formStatus === "submitting"}>
|
||||
{formStatus === "submitting" ? "Sending..." : "Send Message"}
|
||||
</button>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<section className="py-16 text-center bg-background px-6">
|
||||
<p className="font-script text-3xl text-primary max-w-xl mx-auto">
|
||||
"May the light of our candles bring peace to your mind and warmth to your soul."
|
||||
</p>
|
||||
{/* ── Closing Quote ── */}
|
||||
<section className="contact-quote">
|
||||
<p>"May the light of our candles bring peace to your mind and warmth to your soul."</p>
|
||||
</section>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure HeartHandshake is imported
|
||||
import { HeartHandshake } from "lucide-react";
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Container from "@mui/material/Container";
|
||||
import birthdayImage from "@/assets/images/product-birthday.png";
|
||||
import eucalyptusImage from "@/assets/images/product-eucalyptus.png";
|
||||
import jasmineImage from "@/assets/images/product-jasmine.png";
|
||||
|
|
@ -8,124 +10,56 @@ import linenImage from "@/assets/images/product-linen.png";
|
|||
import oudImage from "@/assets/images/product-oud.png";
|
||||
import roseImage from "@/assets/images/product-rose.png";
|
||||
import sandalwoodImage from "@/assets/images/product-sandalwood.png";
|
||||
import "./products.css";
|
||||
|
||||
const PRODUCTS = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Lavender Dream",
|
||||
scent: "Calming French Lavender, Vanilla, & Bergamot",
|
||||
price: "$28",
|
||||
image: lavenderImage,
|
||||
category: "Floral",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Rose & Vanilla",
|
||||
scent: "Damask Rose, Sweet Vanilla bean, & Amber",
|
||||
price: "$32",
|
||||
image: roseImage,
|
||||
category: "Floral",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Eucalyptus Mint",
|
||||
scent: "Fresh Eucalyptus, Spearmint, & Cedar",
|
||||
price: "$28",
|
||||
image: eucalyptusImage,
|
||||
category: "Fresh",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Oud & Amber",
|
||||
scent: "Rich Oud Wood, Warm Amber, & Dark Musk",
|
||||
price: "$36",
|
||||
image: oudImage,
|
||||
category: "Woody",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Jasmine Night",
|
||||
scent: "Night-blooming Jasmine, Ylang Ylang, & Sandalwood",
|
||||
price: "$30",
|
||||
image: jasmineImage,
|
||||
category: "Floral",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Sandalwood Cedar",
|
||||
scent: "Indian Sandalwood, Cedarwood, & Patchouli",
|
||||
price: "$34",
|
||||
image: sandalwoodImage,
|
||||
category: "Woody",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Fresh Linen",
|
||||
scent: "Crisp Cotton, White Tea, & Lily of the Valley",
|
||||
price: "$26",
|
||||
image: linenImage,
|
||||
category: "Fresh",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Birthday Cake",
|
||||
scent: "Vanilla Buttercream, Sugar Spun, & Joy",
|
||||
price: "$28",
|
||||
image: birthdayImage,
|
||||
category: "Sweet",
|
||||
},
|
||||
{ id: 1, name: "Lavender Dream", scent: "Calming French Lavender, Vanilla, & Bergamot", price: "$28", image: lavenderImage, category: "Floral" },
|
||||
{ id: 2, name: "Rose & Vanilla", scent: "Damask Rose, Sweet Vanilla Bean, & Amber", price: "$32", image: roseImage, category: "Floral" },
|
||||
{ id: 3, name: "Eucalyptus Mint", scent: "Fresh Eucalyptus, Spearmint, & Cedar", price: "$28", image: eucalyptusImage, category: "Fresh" },
|
||||
{ id: 4, name: "Oud & Amber", scent: "Rich Oud Wood, Warm Amber, & Dark Musk", price: "$36", image: oudImage, category: "Woody" },
|
||||
{ id: 5, name: "Jasmine Night", scent: "Night-blooming Jasmine, Ylang Ylang, & Sandalwood", price: "$30", image: jasmineImage, category: "Floral" },
|
||||
{ id: 6, name: "Sandalwood Cedar", scent: "Indian Sandalwood, Cedarwood, & Patchouli", price: "$34", image: sandalwoodImage, category: "Woody" },
|
||||
{ id: 7, name: "Fresh Linen", scent: "Crisp Cotton, White Tea, & Lily of the Valley", price: "$26", image: linenImage, category: "Fresh" },
|
||||
{ id: 8, name: "Birthday Cake", scent: "Vanilla Buttercream, Sugar Spun, & Joy", price: "$28", image: birthdayImage, category: "Sweet" },
|
||||
];
|
||||
|
||||
export default function ProductsPage() {
|
||||
const [filter, setFilter] = useState("All");
|
||||
|
||||
const categories = ["All", ...Array.from(new Set(PRODUCTS.map((p) => p.category)))];
|
||||
|
||||
const filteredProducts = filter === "All" ? PRODUCTS : PRODUCTS.filter(p => p.category === filter);
|
||||
|
||||
const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.6 } },
|
||||
};
|
||||
const filteredProducts = filter === "All" ? PRODUCTS : PRODUCTS.filter((p) => p.category === filter);
|
||||
|
||||
return (
|
||||
<div className="w-full min-h-screen bg-background">
|
||||
<section className="py-24 px-6 text-center bg-card">
|
||||
<Box>
|
||||
{/* ── Hero ── */}
|
||||
<section className="products-hero">
|
||||
<Container maxWidth="md">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8 } }
|
||||
}}
|
||||
className="max-w-3xl mx-auto"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<h1 className="font-serif text-5xl md:text-6xl text-primary mb-6">Our Collection</h1>
|
||||
<p className="font-sans text-muted-foreground text-lg mb-8">
|
||||
Each candle is meticulously hand-poured with natural soy wax and premium fragrance oils, designed to elevate your everyday rituals.
|
||||
</p>
|
||||
<div className="w-24 h-1 bg-accent mx-auto" />
|
||||
<h1>Our Collection</h1>
|
||||
<p>Each candle is meticulously hand-poured with natural soy wax and premium fragrance oils, designed to elevate your everyday rituals.</p>
|
||||
<div className="section-divider" />
|
||||
</motion.div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<section className="py-12 px-6">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Filters */}
|
||||
{/* ── Products Body ── */}
|
||||
<section className="products-body">
|
||||
<Container maxWidth="xl">
|
||||
{/* Filter Bar */}
|
||||
<motion.div
|
||||
className="filter-bar"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="flex flex-wrap justify-center gap-4 mb-16"
|
||||
>
|
||||
{categories.map((cat) => (
|
||||
<button
|
||||
key={cat}
|
||||
className={`filter-btn${filter === cat ? " active" : ""}`}
|
||||
onClick={() => setFilter(cat)}
|
||||
className={`font-display tracking-widest text-sm uppercase px-6 py-2 transition-all duration-300 ${
|
||||
filter === cat
|
||||
? "text-primary border-b-2 border-accent"
|
||||
: "text-muted-foreground hover:text-primary"
|
||||
}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
|
|
@ -133,45 +67,34 @@ export default function ProductsPage() {
|
|||
</motion.div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-16">
|
||||
<div className="products-grid">
|
||||
{filteredProducts.map((product, index) => (
|
||||
<motion.div
|
||||
key={product.id}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
className="prod-card"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: { opacity: 1, y: 0, transition: { delay: (index % 4) * 0.1, duration: 0.6 } }
|
||||
}}
|
||||
className="group flex flex-col"
|
||||
transition={{ delay: (index % 4) * 0.1, duration: 0.6 }}
|
||||
>
|
||||
<div className="relative aspect-[4/5] overflow-hidden mb-6 bg-card border border-border">
|
||||
<img
|
||||
src={product.image}
|
||||
alt={product.name}
|
||||
className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-primary/30 opacity-0 group-hover:opacity-100 transition-opacity duration-500 flex items-center justify-center backdrop-blur-[2px]">
|
||||
<button className="px-8 py-3 bg-background text-primary font-display tracking-widest text-sm uppercase translate-y-8 group-hover:translate-y-0 transition-transform duration-500 hover:bg-accent hover:text-primary-foreground shadow-lg">
|
||||
Add to Cart
|
||||
</button>
|
||||
<div className="prod-img-wrap">
|
||||
<img src={product.image} alt={product.name} />
|
||||
<div className="prod-overlay">
|
||||
<button className="prod-overlay-btn">Add to Cart</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-grow flex flex-col justify-between">
|
||||
<div className="prod-info">
|
||||
<div>
|
||||
<h3 className="font-serif text-2xl text-primary mb-2">{product.name}</h3>
|
||||
<p className="font-sans text-sm text-muted-foreground mb-4 line-clamp-2">
|
||||
{product.scent}
|
||||
</p>
|
||||
<h3 className="prod-name">{product.name}</h3>
|
||||
<p className="prod-scent">{product.scent}</p>
|
||||
</div>
|
||||
<p className="font-sans font-medium text-accent text-lg">{product.price}</p>
|
||||
<p className="prod-price">{product.price}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,229 @@
|
|||
/* ─── about.css ──────────────────────────────────────────── */
|
||||
|
||||
/* HERO */
|
||||
.about-hero {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-card);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about-hero h1 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2.5rem, 6vw, 4.5rem);
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.about-hero p {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 1.1rem;
|
||||
color: var(--brand-muted);
|
||||
line-height: 1.8;
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* BRAND VALUES */
|
||||
.about-values {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-light-lavender);
|
||||
}
|
||||
|
||||
.values-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.values-grid { grid-template-columns: 1fr 1fr 1fr; }
|
||||
}
|
||||
|
||||
.value-card {
|
||||
text-align: center;
|
||||
padding: 2.5rem;
|
||||
background: var(--brand-card);
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid rgba(213,190,240,0.5);
|
||||
box-shadow: 0 2px 12px rgba(91,30,140,0.06);
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.value-card:hover {
|
||||
box-shadow: 0 6px 24px rgba(91,30,140,0.12);
|
||||
}
|
||||
|
||||
.value-icon {
|
||||
color: var(--brand-gold);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.value-card h3 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.5rem;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.value-card p {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* MEET THE MAKER */
|
||||
.about-maker {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-purple);
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.maker-gradient-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(to right, transparent, rgba(91,30,140,0.5));
|
||||
mix-blend-mode: overlay;
|
||||
}
|
||||
|
||||
.maker-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 4rem;
|
||||
align-items: center;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.maker-grid { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
|
||||
.maker-img-wrap {
|
||||
aspect-ratio: 1;
|
||||
border-radius: 1rem;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.maker-img-wrap img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.maker-img-ring {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 1rem;
|
||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.maker-content h2 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2rem, 4vw, 3rem);
|
||||
color: #ffffff;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.maker-divider {
|
||||
width: 80px;
|
||||
height: 4px;
|
||||
background: var(--brand-gold);
|
||||
border-radius: 2px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.maker-content p {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.8;
|
||||
color: rgba(255,255,255,0.88);
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.maker-signature {
|
||||
font-family: var(--font-script);
|
||||
font-size: 2.25rem;
|
||||
color: var(--brand-gold);
|
||||
margin-top: 1.5rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* PROCESS TIMELINE */
|
||||
.about-process {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-light-lavender);
|
||||
}
|
||||
|
||||
.about-process h2 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2rem, 4vw, 2.8rem);
|
||||
color: var(--brand-purple);
|
||||
text-align: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.about-process .subtitle {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
text-align: center;
|
||||
margin-bottom: 5rem;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.timeline-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-step {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 3.5rem;
|
||||
color: rgba(212,162,76,0.3);
|
||||
font-weight: 300;
|
||||
min-width: 80px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.timeline-body {
|
||||
flex: 1;
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 1px solid rgba(213,190,240,0.5);
|
||||
}
|
||||
|
||||
.timeline-body h3 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.4rem;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.timeline-body p {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
}
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
/* ─── contact.css ────────────────────────────────────────── */
|
||||
|
||||
/* HERO */
|
||||
.contact-hero {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-card);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact-hero h1 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2.5rem, 6vw, 4rem);
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.contact-hero p {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 1.05rem;
|
||||
color: var(--brand-muted);
|
||||
line-height: 1.8;
|
||||
max-width: 640px;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
/* CONTACT BODY */
|
||||
.contact-body {
|
||||
padding: 5rem 1.5rem;
|
||||
background: var(--brand-light-lavender);
|
||||
}
|
||||
|
||||
.contact-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 4rem;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.contact-grid { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
|
||||
/* INFO PANEL */
|
||||
.contact-info-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.contact-info-panel h2 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.9rem;
|
||||
color: var(--brand-purple);
|
||||
}
|
||||
|
||||
.contact-info-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.contact-info-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.contact-info-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background: rgba(220,198,242,0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--brand-purple);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contact-info-label {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.65rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.contact-info-value {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contact-info-value a {
|
||||
color: var(--brand-muted);
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-info-value a:hover {
|
||||
color: var(--brand-gold);
|
||||
}
|
||||
|
||||
/* SOCIAL BOX */
|
||||
.contact-social-box {
|
||||
padding: 2rem;
|
||||
background: var(--brand-card);
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--brand-border);
|
||||
}
|
||||
|
||||
.contact-social-box h3 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.5rem;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contact-social-box p {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
margin-bottom: 1.25rem;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contact-ig-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--brand-purple);
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-ig-link:hover {
|
||||
color: var(--brand-gold);
|
||||
}
|
||||
|
||||
/* FORM PANEL */
|
||||
.contact-form-panel {
|
||||
background: #ffffff;
|
||||
border-radius: 0px;
|
||||
padding: 3rem;
|
||||
box-shadow: 0 12px 40px rgba(91,30,140,0.1);
|
||||
border: 1px solid rgba(213,190,240,0.4);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.contact-form-top-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(to right, var(--brand-purple), var(--brand-gold), var(--brand-purple));
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.contact-form-panel h2 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.9rem;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.form-row { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
|
||||
.form-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.65rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--brand-purple);
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
padding: 0.85rem 1rem;
|
||||
background: var(--brand-light-lavender);
|
||||
border: 1px solid var(--brand-border);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.95rem;
|
||||
color: var(--brand-text);
|
||||
outline: none;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-textarea:focus {
|
||||
border-color: var(--brand-gold);
|
||||
box-shadow: 0 0 0 2px rgba(212,162,76,0.15);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
resize: none;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.form-submit {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
background: var(--brand-purple);
|
||||
color: #ffffff;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.form-submit:hover:not(:disabled) {
|
||||
background: var(--brand-gold);
|
||||
}
|
||||
|
||||
.form-submit:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* SUCCESS STATE */
|
||||
.contact-success {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.success-icon-wrap {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: #f0fdf4;
|
||||
color: #16a34a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.contact-success h3 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.75rem;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contact-success p {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
}
|
||||
|
||||
.send-another-btn {
|
||||
margin-top: 2rem;
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--brand-gold);
|
||||
padding-bottom: 3px;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--brand-gold);
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.send-another-btn:hover {
|
||||
color: var(--brand-purple);
|
||||
border-color: var(--brand-purple);
|
||||
}
|
||||
|
||||
/* CLOSING QUOTE */
|
||||
.contact-quote {
|
||||
padding: 4rem 1.5rem;
|
||||
text-align: center;
|
||||
background: var(--brand-light-lavender);
|
||||
}
|
||||
|
||||
.contact-quote p {
|
||||
font-family: var(--font-script);
|
||||
font-size: clamp(1.5rem, 3vw, 2rem);
|
||||
color: var(--brand-purple);
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
/* ─── home.css ───────────────────────────────────────────── */
|
||||
|
||||
/* HERO */
|
||||
.home-hero {
|
||||
position: relative;
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 5rem 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.home-hero-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(to bottom, rgba(220,198,242,0.4), var(--brand-light-lavender));
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.home-hero-logo-wrap {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 4px solid rgba(212,162,76,0.35);
|
||||
box-shadow: 0 0 40px rgba(212,162,76,0.25);
|
||||
position: relative;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
.home-hero-flame {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
right: -12px;
|
||||
color: var(--brand-gold);
|
||||
}
|
||||
|
||||
.home-hero h1 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2.5rem, 7vw, 5.5rem);
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 1rem;
|
||||
drop-shadow: 0 2px 8px rgba(91,30,140,0.15);
|
||||
}
|
||||
|
||||
.home-hero-tagline {
|
||||
font-family: var(--font-script);
|
||||
font-size: clamp(1.75rem, 4vw, 3rem);
|
||||
color: var(--brand-gold);
|
||||
margin-bottom: 3rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero-cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem 2.5rem;
|
||||
background: var(--brand-purple);
|
||||
color: #ffffff;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
border: 1px solid transparent;
|
||||
transition: background 0.4s ease, border-color 0.4s ease, color 0.4s ease, box-shadow 0.4s ease;
|
||||
}
|
||||
|
||||
.hero-cta:hover {
|
||||
background: transparent;
|
||||
color: var(--brand-purple);
|
||||
border-color: var(--brand-gold);
|
||||
box-shadow: 0 0 20px rgba(212,162,76,0.35);
|
||||
}
|
||||
|
||||
/* PHILOSOPHY */
|
||||
.home-philosophy {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-card);
|
||||
}
|
||||
|
||||
.home-philosophy h2 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2rem, 4vw, 3rem);
|
||||
color: var(--brand-purple);
|
||||
text-align: center;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
width: 96px;
|
||||
height: 4px;
|
||||
background: var(--brand-gold);
|
||||
margin: 0 auto 4rem;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.philosophy-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2.5rem;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.philosophy-grid { grid-template-columns: 1fr 1fr 1fr; }
|
||||
}
|
||||
|
||||
.philosophy-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 2.5rem;
|
||||
border-radius: 1rem;
|
||||
transition: background 0.4s ease;
|
||||
}
|
||||
|
||||
.philosophy-card:hover {
|
||||
background: rgba(220,198,242,0.3);
|
||||
}
|
||||
|
||||
.philosophy-icon-wrap {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: rgba(220,198,242,0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.philosophy-card h3 {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.1rem;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.philosophy-card p {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* FEATURED PRODUCTS */
|
||||
.home-products {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-light-lavender);
|
||||
}
|
||||
|
||||
.home-products-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.home-products-header {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.home-products h2 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2rem, 4vw, 3rem);
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.view-all-link {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--brand-gold);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid transparent;
|
||||
padding-bottom: 2px;
|
||||
transition: color 0.3s ease, border-color 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.view-all-link:hover {
|
||||
color: var(--brand-purple);
|
||||
border-color: var(--brand-purple);
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) { .product-grid { grid-template-columns: 1fr 1fr; } }
|
||||
@media (min-width: 1024px) { .product-grid { grid-template-columns: repeat(4, 1fr); } }
|
||||
|
||||
.product-card {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.product-img-wrap {
|
||||
position: relative;
|
||||
aspect-ratio: 1 / 1;
|
||||
overflow: hidden;
|
||||
background: var(--brand-card);
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.product-img-wrap img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.7s ease;
|
||||
}
|
||||
|
||||
.product-card:hover .product-img-wrap img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.product-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(91,30,140,0.22);
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(2px);
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.product-card:hover .product-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.product-overlay-btn {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: rgba(239,228,250,0.92);
|
||||
color: var(--brand-purple);
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transform: translateY(16px);
|
||||
transition: transform 0.5s ease, background 0.3s ease;
|
||||
}
|
||||
|
||||
.product-card:hover .product-overlay-btn {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.4rem;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-gold);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* QUOTE BANNER */
|
||||
.home-quote {
|
||||
padding: 8rem 1.5rem;
|
||||
background: var(--brand-purple);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.home-quote-texture {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url('https://www.transparenttextures.com/patterns/stardust.png');
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.home-quote blockquote {
|
||||
font-family: var(--font-serif);
|
||||
font-style: italic;
|
||||
font-size: clamp(1.4rem, 3.5vw, 2.5rem);
|
||||
color: #ffffff;
|
||||
line-height: 1.6;
|
||||
max-width: 900px;
|
||||
margin: 1.5rem auto 2rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.home-quote-divider {
|
||||
width: 64px;
|
||||
height: 1px;
|
||||
background: var(--brand-gold);
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* NEWSLETTER */
|
||||
.home-newsletter {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-card);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.home-newsletter h2 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(1.8rem, 3.5vw, 2.5rem);
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.home-newsletter p {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--brand-muted);
|
||||
margin-bottom: 2rem;
|
||||
max-width: 520px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.newsletter-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
max-width: 440px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
.newsletter-form { flex-direction: row; }
|
||||
}
|
||||
|
||||
.newsletter-input {
|
||||
flex: 1;
|
||||
padding: 0.85rem 1.25rem;
|
||||
background: var(--brand-light-lavender);
|
||||
border: 1px solid var(--brand-border);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.95rem;
|
||||
outline: none;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.newsletter-input:focus {
|
||||
border-color: var(--brand-gold);
|
||||
box-shadow: 0 0 0 2px rgba(212,162,76,0.15);
|
||||
}
|
||||
|
||||
.newsletter-btn {
|
||||
padding: 0.85rem 2rem;
|
||||
background: var(--brand-purple);
|
||||
color: #ffffff;
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.newsletter-btn:hover {
|
||||
background: var(--brand-gold);
|
||||
}
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
import { motion } from "framer-motion";
|
||||
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/product-lavender.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";
|
||||
|
||||
export default function Home() {
|
||||
const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 40 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
||||
|
|
@ -18,137 +20,77 @@ export default function Home() {
|
|||
visible: { transition: { staggerChildren: 0.2 } },
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex flex-col w-full">
|
||||
{/* Hero Section */}
|
||||
<section className="relative min-h-[90vh] flex flex-col items-center justify-center overflow-hidden px-4 py-20">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-secondary/30 to-background -z-10" />
|
||||
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={stagger}
|
||||
className="flex flex-col items-center text-center max-w-4xl mx-auto"
|
||||
>
|
||||
<motion.div variants={fadeInUp} className="mb-8 relative">
|
||||
<div className="w-32 h-32 md:w-48 md:h-48 rounded-full overflow-hidden border-4 border-accent/30 shadow-[0_0_40px_rgba(212,162,76,0.3)]">
|
||||
<img src={logoPath} alt="Mani Candles Co." className="w-full h-full object-cover" />
|
||||
<Box>
|
||||
{/* ── Hero ── */}
|
||||
<section className="home-hero">
|
||||
<div className="home-hero-bg" />
|
||||
<motion.div initial="hidden" animate="visible" variants={stagger}>
|
||||
<motion.div variants={fadeInUp} style={{ position: "relative" }}>
|
||||
<div className="home-hero-logo-wrap">
|
||||
<img src={logoPath} alt="Mani Candles Co." style={{ width: "100%", height: "100%", objectFit: "cover" }} />
|
||||
</div>
|
||||
<div className="absolute -top-4 -right-4 text-accent animate-flame">
|
||||
<Flame size={40} fill="currentColor" className="drop-shadow-[0_0_15px_rgba(255,211,107,0.8)]" />
|
||||
<div className="home-hero-flame animate-flame">
|
||||
<Flame size={40} fill="currentColor" style={{ filter: "drop-shadow(0 0 12px rgba(255,211,107,0.8))" }} />
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.h1 variants={fadeInUp} className="font-serif text-5xl md:text-7xl lg:text-8xl text-primary mb-6 drop-shadow-sm">
|
||||
Mani Candles Co.
|
||||
</motion.h1>
|
||||
<motion.h1 variants={fadeInUp}>Mani Candles Co.</motion.h1>
|
||||
|
||||
<motion.p variants={fadeInUp} className="font-script text-3xl md:text-5xl text-accent mb-12">
|
||||
<motion.span variants={fadeInUp} className="home-hero-tagline">
|
||||
Handmade • Natural • Heartmade
|
||||
</motion.p>
|
||||
</motion.span>
|
||||
|
||||
<motion.div variants={fadeInUp}>
|
||||
<Link
|
||||
href="/products"
|
||||
className="inline-flex items-center justify-center px-8 py-4 bg-primary text-primary-foreground font-display tracking-widest uppercase text-sm rounded-none border border-transparent hover:border-accent hover:bg-transparent hover:text-primary transition-all duration-500 ease-out shadow-lg hover:shadow-[0_0_20px_rgba(212,162,76,0.4)]"
|
||||
>
|
||||
Shop the Collection
|
||||
</Link>
|
||||
<Link href="/products" className="hero-cta">Shop the Collection</Link>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</section>
|
||||
|
||||
{/* Why Mani Section */}
|
||||
<section className="py-24 bg-card px-6">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
variants={stagger}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<motion.h2 variants={fadeInUp} className="font-serif text-4xl md:text-5xl text-primary mb-4">
|
||||
The Mani Philosophy
|
||||
</motion.h2>
|
||||
<motion.div variants={fadeInUp} className="w-24 h-1 bg-accent mx-auto" />
|
||||
{/* ── 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="grid grid-cols-1 md:grid-cols-3 gap-12">
|
||||
<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={fadeInUp}
|
||||
className="flex flex-col items-center text-center p-8 rounded-2xl hover:bg-secondary/20 transition-colors duration-500"
|
||||
variants={{ hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.2, duration: 0.6 } } }}
|
||||
>
|
||||
<div className="w-16 h-16 rounded-full bg-secondary/50 flex items-center justify-center text-primary mb-6">
|
||||
<Leaf size={32} />
|
||||
</div>
|
||||
<h3 className="font-display tracking-wider text-xl text-primary mb-4">Natural</h3>
|
||||
<p className="font-sans text-muted-foreground leading-relaxed">
|
||||
Crafted with pure soy wax and essential oils. Clean burns that respect your home and our earth.
|
||||
</p>
|
||||
<div className="philosophy-icon-wrap"><Icon size={32} /></div>
|
||||
<h3>{title}</h3>
|
||||
<p>{desc}</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={fadeInUp}
|
||||
className="flex flex-col items-center text-center p-8 rounded-2xl hover:bg-secondary/20 transition-colors duration-500"
|
||||
>
|
||||
<div className="w-16 h-16 rounded-full bg-secondary/50 flex items-center justify-center text-primary mb-6">
|
||||
<Sparkles size={32} />
|
||||
</div>
|
||||
<h3 className="font-display tracking-wider text-xl text-primary mb-4">Handmade</h3>
|
||||
<p className="font-sans text-muted-foreground leading-relaxed">
|
||||
Poured by hand in small batches to ensure unparalleled quality and attention to detail.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={fadeInUp}
|
||||
className="flex flex-col items-center text-center p-8 rounded-2xl hover:bg-secondary/20 transition-colors duration-500"
|
||||
>
|
||||
<div className="w-16 h-16 rounded-full bg-secondary/50 flex items-center justify-center text-primary mb-6">
|
||||
<Heart size={32} />
|
||||
</div>
|
||||
<h3 className="font-display tracking-wider text-xl text-primary mb-4">Heartmade</h3>
|
||||
<p className="font-sans text-muted-foreground leading-relaxed">
|
||||
Every candle is infused with love and intention, designed to bring warmth to your sacred spaces.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
{/* Featured Products */}
|
||||
<section className="py-24 px-6 bg-background">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={fadeInUp}
|
||||
className="flex flex-col md:flex-row justify-between items-end mb-16 gap-6"
|
||||
>
|
||||
{/* ── 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 className="font-serif text-4xl md:text-5xl text-primary mb-4">Curated Favorites</h2>
|
||||
<div className="w-24 h-1 bg-accent" />
|
||||
<h2>Curated Favorites</h2>
|
||||
<div className="section-divider" style={{ margin: "0.5rem 0 0", width: 80 }} />
|
||||
</div>
|
||||
<Link
|
||||
href="/products"
|
||||
className="font-display tracking-widest uppercase text-sm text-accent hover:text-primary transition-colors border-b border-transparent hover:border-primary pb-1"
|
||||
>
|
||||
View All Candles
|
||||
</Link>
|
||||
<Link href="/products" className="view-all-link">View All Candles</Link>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
<div className="product-grid">
|
||||
{[
|
||||
{ id: 1, name: "Lavender Dream", image: lavenderImage, price: "$28" },
|
||||
{ id: 2, name: "Rose & Vanilla", image: roseImage, price: "$32" },
|
||||
|
|
@ -157,82 +99,49 @@ export default function Home() {
|
|||
].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 } }
|
||||
}}
|
||||
className="group cursor-pointer"
|
||||
variants={{ hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.1, duration: 0.6 } } }}
|
||||
>
|
||||
<div className="relative aspect-square overflow-hidden mb-6 bg-card">
|
||||
<img
|
||||
src={product.image}
|
||||
alt={product.name}
|
||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-primary/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500 flex items-center justify-center backdrop-blur-[2px]">
|
||||
<span className="px-6 py-3 bg-background/90 text-primary font-display tracking-widest text-sm uppercase translate-y-4 group-hover:translate-y-0 transition-transform duration-500">
|
||||
Quick Add
|
||||
</span>
|
||||
<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="font-serif text-2xl text-primary mb-2">{product.name}</h3>
|
||||
<p className="font-sans text-accent">{product.price}</p>
|
||||
<h3 className="product-name">{product.name}</h3>
|
||||
<p className="product-price">{product.price}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
{/* Quote Section */}
|
||||
<section className="py-32 px-6 bg-primary relative overflow-hidden">
|
||||
<div className="absolute inset-0 opacity-10 bg-[url('https://www.transparenttextures.com/patterns/stardust.png')]" />
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={fadeInUp}
|
||||
className="max-w-4xl mx-auto text-center relative z-10"
|
||||
>
|
||||
<Flame className="text-accent mx-auto mb-8 animate-flame" size={48} />
|
||||
<p className="font-serif italic text-3xl md:text-5xl text-primary-foreground leading-relaxed mb-8">
|
||||
{/* ── 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."
|
||||
</p>
|
||||
<div className="w-16 h-[1px] bg-accent mx-auto" />
|
||||
</blockquote>
|
||||
<div className="home-quote-divider" />
|
||||
</motion.div>
|
||||
</section>
|
||||
|
||||
{/* Newsletter */}
|
||||
<section className="py-24 px-6 bg-card text-center">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={fadeInUp}
|
||||
className="max-w-2xl mx-auto"
|
||||
>
|
||||
<h2 className="font-serif text-4xl text-primary mb-4">Join Our Sanctuary</h2>
|
||||
<p className="font-sans text-muted-foreground mb-8">
|
||||
Subscribe to receive gentle updates on new collections, exclusive access, and a little light in your inbox.
|
||||
</p>
|
||||
<form className="flex flex-col sm:flex-row gap-4 max-w-md mx-auto" onSubmit={(e) => e.preventDefault()}>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Your email address"
|
||||
className="flex-grow px-6 py-3 bg-background border border-border focus:border-accent focus:outline-none focus:ring-1 focus:ring-accent font-sans rounded-none transition-all"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-8 py-3 bg-primary text-primary-foreground font-display tracking-widest text-sm uppercase hover:bg-accent transition-colors duration-300"
|
||||
>
|
||||
Subscribe
|
||||
</button>
|
||||
{/* ── 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>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
/* ─── products.css ───────────────────────────────────────── */
|
||||
|
||||
/* HERO */
|
||||
.products-hero {
|
||||
padding: 6rem 1.5rem;
|
||||
background: var(--brand-card);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.products-hero h1 {
|
||||
font-family: var(--font-serif);
|
||||
font-size: clamp(2.5rem, 6vw, 4rem);
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.products-hero p {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 1.05rem;
|
||||
color: var(--brand-muted);
|
||||
line-height: 1.8;
|
||||
max-width: 640px;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
/* FILTERS */
|
||||
.products-body {
|
||||
padding: 5rem 1.5rem;
|
||||
background: var(--brand-light-lavender);
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
padding: 0.5rem 1.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: var(--brand-muted);
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
color: var(--brand-purple);
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
color: var(--brand-purple);
|
||||
border-bottom-color: var(--brand-gold);
|
||||
}
|
||||
|
||||
/* PRODUCT GRID */
|
||||
.products-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 3rem 2rem;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) { .products-grid { grid-template-columns: 1fr 1fr; } }
|
||||
@media (min-width: 1024px) { .products-grid { grid-template-columns: repeat(4, 1fr); } }
|
||||
|
||||
.prod-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prod-img-wrap {
|
||||
position: relative;
|
||||
aspect-ratio: 4 / 5;
|
||||
overflow: hidden;
|
||||
background: var(--brand-card);
|
||||
border: 1px solid var(--brand-border);
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.prod-img-wrap img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 1s ease;
|
||||
}
|
||||
|
||||
.prod-card:hover .prod-img-wrap img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.prod-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(91,30,140,0.3);
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(2px);
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.prod-card:hover .prod-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.prod-overlay-btn {
|
||||
padding: 0.75rem 2rem;
|
||||
background: var(--brand-light-lavender);
|
||||
color: var(--brand-purple);
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transform: translateY(32px);
|
||||
transition: transform 0.5s ease, background 0.3s ease, color 0.3s ease;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.prod-card:hover .prod-overlay-btn {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.prod-overlay-btn:hover {
|
||||
background: var(--brand-gold);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.prod-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.prod-name {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.4rem;
|
||||
color: var(--brand-purple);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.prod-scent {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.85rem;
|
||||
color: var(--brand-muted);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 0.75rem;
|
||||
/* two-line clamp */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.prod-price {
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 500;
|
||||
font-size: 1.05rem;
|
||||
color: var(--brand-gold);
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import { createTheme } from "@mui/material/styles";
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: "light",
|
||||
primary: {
|
||||
main: "#5B1E8C", // deep brand purple
|
||||
light: "#DCC6F2", // lavender
|
||||
dark: "#3A1260",
|
||||
contrastText: "#ffffff",
|
||||
},
|
||||
secondary: {
|
||||
main: "#D4A24C", // brand gold / accent
|
||||
light: "#FFD36B",
|
||||
dark: "#A07830",
|
||||
contrastText: "#ffffff",
|
||||
},
|
||||
background: {
|
||||
default: "#EFE4FA", // light lavender bg
|
||||
paper: "#F8F2FD", // card bg
|
||||
},
|
||||
text: {
|
||||
primary: "#2D0D52",
|
||||
secondary: "#7B5A9E",
|
||||
},
|
||||
divider: "#D5BEF0",
|
||||
},
|
||||
typography: {
|
||||
fontFamily: "'Inter', sans-serif",
|
||||
h1: { fontFamily: "'Cormorant Garamond', serif" },
|
||||
h2: { fontFamily: "'Cormorant Garamond', serif" },
|
||||
h3: { fontFamily: "'Cormorant Garamond', serif" },
|
||||
h4: { fontFamily: "'Cormorant Garamond', serif" },
|
||||
h5: { fontFamily: "'Cormorant Garamond', serif" },
|
||||
h6: { fontFamily: "'Cormorant Garamond', serif" },
|
||||
subtitle1: { fontFamily: "'Cinzel', serif" },
|
||||
subtitle2: { fontFamily: "'Cinzel', serif" },
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 12,
|
||||
},
|
||||
components: {
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
textTransform: "none",
|
||||
borderRadius: 0,
|
||||
fontFamily: "'Cinzel', serif",
|
||||
letterSpacing: "0.15em",
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAppBar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
boxShadow: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const projectRoot = fileURLToPath(new URL(".", import.meta.url));
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(projectRoot, "src"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue