fix drone
continuous-integration/drone Build is failing
Details
continuous-integration/drone Build is failing
Details
This commit is contained in:
parent
acca584f0d
commit
ede4237281
|
|
@ -1,9 +1,9 @@
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.dockerignore
|
.dockerignore
|
||||||
README.md
|
README.md
|
||||||
.env
|
.env
|
||||||
81
.drone.yml
81
.drone.yml
|
|
@ -1,41 +1,40 @@
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: Build Image
|
type: docker
|
||||||
|
name: Build Image
|
||||||
steps:
|
|
||||||
- name: Build Docker Image
|
steps:
|
||||||
image: plugins/docker
|
- name: Build Docker Image
|
||||||
settings:
|
image: plugins/docker
|
||||||
username:
|
settings:
|
||||||
from_secret: docker_username
|
username:
|
||||||
password:
|
from_secret: docker_username
|
||||||
from_secret: docker_password
|
password:
|
||||||
repo: git.midastix.com/ci-docker-repo-user/manicandles
|
from_secret: docker_password
|
||||||
registry: git.midastix.com
|
repo: git.midastix.com/ci-docker-repo-user/manicandles
|
||||||
|
registry: git.midastix.com
|
||||||
when:
|
|
||||||
branch:
|
when:
|
||||||
- main
|
branch:
|
||||||
event:
|
- main
|
||||||
- push
|
event:
|
||||||
|
- push
|
||||||
- name: Deploy Image
|
|
||||||
image: appleboy/drone-ssh
|
- name: Deploy image
|
||||||
|
image: appleboy/drone-ssh
|
||||||
settings:
|
settings:
|
||||||
host: server1.midastix.com
|
host: server1.midastix.com
|
||||||
username: root
|
username: root
|
||||||
key:
|
key:
|
||||||
from_secret: server_ssh_pkey
|
from_secret: server_ssh_pkey
|
||||||
port: 22
|
port: 22
|
||||||
command_timeout: 5m
|
command_timeout: 5m
|
||||||
|
script:
|
||||||
script:
|
- echo "Deploying ManiCandles"
|
||||||
- echo "Deploying ManiCandles"
|
- sudo /opt/deployable/manicandles/docker/deploy.sh
|
||||||
- sudo /opt/deployable/manicandles/docker/deploy.sh
|
- echo "Completed Deployment"
|
||||||
- echo "Deployment Completed"
|
|
||||||
|
when:
|
||||||
when:
|
branch:
|
||||||
branch:
|
- main
|
||||||
- main
|
event:
|
||||||
event:
|
- push
|
||||||
- push
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"arcanis.vscode-zipfs"
|
"arcanis.vscode-zipfs"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/.yarn": true,
|
"**/.yarn": true,
|
||||||
"**/.pnp.*": true
|
"**/.pnp.*": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# This file is automatically generated by @yarnpkg/sdks.
|
# This file is automatically generated by @yarnpkg/sdks.
|
||||||
# Manual changes might be lost!
|
# Manual changes might be lost!
|
||||||
|
|
||||||
integrations:
|
integrations:
|
||||||
- vscode
|
- vscode
|
||||||
|
|
|
||||||
36
Dockerfile
36
Dockerfile
|
|
@ -1,19 +1,19 @@
|
||||||
FROM node:20-alpine AS build
|
FROM node:20-alpine AS build
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM nginx:alpine
|
FROM nginx:alpine
|
||||||
|
|
||||||
COPY --from=build /app/dist /usr/share/nginx/html
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
64
src/App.tsx
64
src/App.tsx
|
|
@ -1,32 +1,32 @@
|
||||||
import { Switch, Route, Router as WouterRouter } from "wouter";
|
import { Switch, Route, Router as WouterRouter } from "wouter";
|
||||||
import NotFound from "@/pages/not-found";
|
import NotFound from "@/pages/not-found";
|
||||||
|
|
||||||
import { Layout } from "@/components/Layout";
|
import { Layout } from "@/components/Layout";
|
||||||
import Home from "@/pages/home";
|
import Home from "@/pages/home";
|
||||||
import ProductsPage from "@/pages/ProductsPage";
|
import ProductsPage from "@/pages/ProductsPage";
|
||||||
import AboutPage from "@/pages/AboutPage";
|
import AboutPage from "@/pages/AboutPage";
|
||||||
import ContactPage from "@/pages/ContactPage";
|
import ContactPage from "@/pages/ContactPage";
|
||||||
|
|
||||||
function Router() {
|
function Router() {
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/" component={Home} />
|
<Route path="/" component={Home} />
|
||||||
<Route path="/products" component={ProductsPage} />
|
<Route path="/products" component={ProductsPage} />
|
||||||
<Route path="/about" component={AboutPage} />
|
<Route path="/about" component={AboutPage} />
|
||||||
<Route path="/contact" component={ContactPage} />
|
<Route path="/contact" component={ContactPage} />
|
||||||
<Route component={NotFound} />
|
<Route component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<WouterRouter base={import.meta.env.BASE_URL.replace(/\/$/, "")}>
|
<WouterRouter base={import.meta.env.BASE_URL.replace(/\/$/, "")}>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Router />
|
<Router />
|
||||||
</Layout>
|
</Layout>
|
||||||
</WouterRouter>
|
</WouterRouter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
||||||
|
|
@ -1,366 +1,366 @@
|
||||||
/* ─── Layout.css — Header & Footer ──────────────────────── */
|
/* ─── Layout.css — Header & Footer ──────────────────────── */
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════
|
||||||
MARQUEE / TICKER BAR
|
MARQUEE / TICKER BAR
|
||||||
══════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════ */
|
||||||
.ticker-bar {
|
.ticker-bar {
|
||||||
background: var(--brand-purple-dark);
|
background: var(--brand-purple-dark);
|
||||||
border-bottom: 1px solid rgba(212,162,76,0.25);
|
border-bottom: 1px solid rgba(212,162,76,0.25);
|
||||||
height: 36px;
|
height: 36px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticker-track {
|
.ticker-track {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
animation: ticker-scroll 28s linear infinite;
|
animation: ticker-scroll 28s linear infinite;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticker-track:hover {
|
.ticker-track:hover {
|
||||||
animation-play-state: paused;
|
animation-play-state: paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ticker-scroll {
|
@keyframes ticker-scroll {
|
||||||
0% { transform: translateX(0); }
|
0% { transform: translateX(0); }
|
||||||
100% { transform: translateX(-50%); }
|
100% { transform: translateX(-50%); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticker-item {
|
.ticker-item {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.6rem;
|
gap: 0.6rem;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.62rem;
|
font-size: 0.62rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: rgba(255,255,255,0.85);
|
color: rgba(255,255,255,0.85);
|
||||||
padding: 0 2.5rem;
|
padding: 0 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticker-dot {
|
.ticker-dot {
|
||||||
width: 5px;
|
width: 5px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════
|
||||||
LOGO
|
LOGO
|
||||||
══════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════ */
|
||||||
.logo-text {
|
.logo-text {
|
||||||
font-family: var(--font-script);
|
font-family: var(--font-script);
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
.logo-text:hover { color: var(--brand-gold-light); }
|
.logo-text:hover { color: var(--brand-gold-light); }
|
||||||
|
|
||||||
.logo-img-wrap {
|
.logo-img-wrap {
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
transition: border-color 0.3s ease;
|
transition: border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
.logo-img-wrap:hover { border-color: var(--brand-gold); }
|
.logo-img-wrap:hover { border-color: var(--brand-gold); }
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════
|
||||||
NAV ITEM WITH DROPDOWN
|
NAV ITEM WITH DROPDOWN
|
||||||
══════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════ */
|
||||||
.nav-item {
|
.nav-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
transition: border-color 0.3s ease, color 0.3s ease;
|
transition: border-color 0.3s ease, color 0.3s ease;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: none;
|
background: none;
|
||||||
border-left: none;
|
border-left: none;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:hover {
|
.nav-link:hover {
|
||||||
color: var(--brand-gold-light);
|
color: var(--brand-gold-light);
|
||||||
border-bottom-color: rgba(212,162,76,0.6);
|
border-bottom-color: rgba(212,162,76,0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active {
|
.nav-link.active {
|
||||||
color: var(--brand-gold-light);
|
color: var(--brand-gold-light);
|
||||||
border-bottom-color: var(--brand-gold);
|
border-bottom-color: var(--brand-gold);
|
||||||
border-bottom-width: 2.5px;
|
border-bottom-width: 2.5px;
|
||||||
text-shadow:
|
text-shadow:
|
||||||
0 0 12px rgba(255,211,107,0.9),
|
0 0 12px rgba(255,211,107,0.9),
|
||||||
0 0 24px rgba(212,162,76,0.5);
|
0 0 24px rgba(212,162,76,0.5);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
background: rgba(212,162,76,0.12);
|
background: rgba(212,162,76,0.12);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 3px 10px 5px;
|
padding: 3px 10px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-chevron {
|
.nav-chevron {
|
||||||
transition: transform 0.25s ease;
|
transition: transform 0.25s ease;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:hover .nav-chevron {
|
.nav-item:hover .nav-chevron {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DROPDOWN PANEL */
|
/* DROPDOWN PANEL */
|
||||||
.nav-dropdown {
|
.nav-dropdown {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% + 18px);
|
top: calc(100% + 18px);
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%) translateY(-8px);
|
transform: translateX(-50%) translateY(-8px);
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
background: rgba(45, 13, 82, 0.96);
|
background: rgba(45, 13, 82, 0.96);
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(16px);
|
||||||
-webkit-backdrop-filter: blur(16px);
|
-webkit-backdrop-filter: blur(16px);
|
||||||
border: 1px solid rgba(212,162,76,0.2);
|
border: 1px solid rgba(212,162,76,0.2);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 0.6rem 0;
|
padding: 0.6rem 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity 0.2s ease, transform 0.25s ease;
|
transition: opacity 0.2s ease, transform 0.25s ease;
|
||||||
box-shadow: 0 16px 40px rgba(0,0,0,0.35);
|
box-shadow: 0 16px 40px rgba(0,0,0,0.35);
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Arrow notch */
|
/* Arrow notch */
|
||||||
.nav-dropdown::before {
|
.nav-dropdown::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -7px;
|
top: -7px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
border-left: 7px solid transparent;
|
border-left: 7px solid transparent;
|
||||||
border-right: 7px solid transparent;
|
border-right: 7px solid transparent;
|
||||||
border-bottom: 7px solid rgba(212,162,76,0.25);
|
border-bottom: 7px solid rgba(212,162,76,0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-dropdown::after {
|
.nav-dropdown::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -6px;
|
top: -6px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
border-left: 7px solid transparent;
|
border-left: 7px solid transparent;
|
||||||
border-right: 7px solid transparent;
|
border-right: 7px solid transparent;
|
||||||
border-bottom: 7px solid rgba(45,13,82,0.96);
|
border-bottom: 7px solid rgba(45,13,82,0.96);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:hover .nav-dropdown {
|
.nav-item:hover .nav-dropdown {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transform: translateX(-50%) translateY(0);
|
transform: translateX(-50%) translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.6rem;
|
gap: 0.6rem;
|
||||||
padding: 0.65rem 1.25rem;
|
padding: 0.65rem 1.25rem;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 0.83rem;
|
font-size: 0.83rem;
|
||||||
color: rgba(255,255,255,0.78);
|
color: rgba(255,255,255,0.78);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: background 0.2s ease, color 0.2s ease, padding-left 0.2s ease;
|
transition: background 0.2s ease, color 0.2s ease, padding-left 0.2s ease;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item:hover {
|
.dropdown-item:hover {
|
||||||
background: rgba(212,162,76,0.12);
|
background: rgba(212,162,76,0.12);
|
||||||
color: var(--brand-gold-light);
|
color: var(--brand-gold-light);
|
||||||
padding-left: 1.6rem;
|
padding-left: 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-divider {
|
.dropdown-divider {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: rgba(255,255,255,0.08);
|
background: rgba(255,255,255,0.08);
|
||||||
margin: 0.4rem 0;
|
margin: 0.4rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════
|
||||||
MOBILE TOGGLE
|
MOBILE TOGGLE
|
||||||
══════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════ */
|
||||||
.mobile-toggle {
|
.mobile-toggle {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
.mobile-toggle:hover { color: var(--brand-gold-light); }
|
.mobile-toggle:hover { color: var(--brand-gold-light); }
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════
|
||||||
MOBILE DRAWER
|
MOBILE DRAWER
|
||||||
══════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════ */
|
||||||
.mobile-menu {
|
.mobile-menu {
|
||||||
background: rgba(45, 13, 82, 0.97);
|
background: rgba(45, 13, 82, 0.97);
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(16px);
|
||||||
border-bottom: 1px solid rgba(212, 162, 76, 0.2);
|
border-bottom: 1px solid rgba(212, 162, 76, 0.2);
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-nav-section {
|
.mobile-nav-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-nav-link {
|
.mobile-nav-link {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 0.8rem 0.5rem;
|
padding: 0.8rem 0.5rem;
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.07);
|
border-bottom: 1px solid rgba(255,255,255,0.07);
|
||||||
transition: color 0.3s ease, padding-left 0.2s ease;
|
transition: color 0.3s ease, padding-left 0.2s ease;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-nav-link:hover {
|
.mobile-nav-link:hover {
|
||||||
color: var(--brand-gold-light);
|
color: var(--brand-gold-light);
|
||||||
padding-left: 0.75rem;
|
padding-left: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-nav-link.active {
|
.mobile-nav-link.active {
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
padding-left: 0.75rem;
|
padding-left: 0.75rem;
|
||||||
text-shadow: 0 0 10px rgba(255,211,107,0.7);
|
text-shadow: 0 0 10px rgba(255,211,107,0.7);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-dropdown-items {
|
.mobile-dropdown-items {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0.25rem 0 0.5rem 1rem;
|
padding: 0.25rem 0 0.5rem 1rem;
|
||||||
gap: 0.1rem;
|
gap: 0.1rem;
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.07);
|
border-bottom: 1px solid rgba(255,255,255,0.07);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-dropdown-item {
|
.mobile-dropdown-item {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
color: rgba(255,255,255,0.6);
|
color: rgba(255,255,255,0.6);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 0.4rem 0.5rem;
|
padding: 0.4rem 0.5rem;
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-dropdown-item:hover {
|
.mobile-dropdown-item:hover {
|
||||||
color: var(--brand-gold-light);
|
color: var(--brand-gold-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════
|
||||||
FOOTER
|
FOOTER
|
||||||
══════════════════════════════════════════════════════════ */
|
══════════════════════════════════════════════════════════ */
|
||||||
.site-footer {
|
.site-footer {
|
||||||
background: var(--brand-purple-dark);
|
background: var(--brand-purple-dark);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
padding: 4rem 1.5rem;
|
padding: 4rem 1.5rem;
|
||||||
margin-top: 5rem;
|
margin-top: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-inner {
|
.footer-inner {
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 3rem;
|
gap: 3rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.footer-inner {
|
.footer-inner {
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-col {
|
.footer-col {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.footer-col { align-items: flex-start; }
|
.footer-col { align-items: flex-start; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-tagline {
|
.footer-tagline {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
color: rgba(255,255,255,0.7);
|
color: rgba(255,255,255,0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-heading {
|
.footer-heading {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
letter-spacing: 0.25em;
|
letter-spacing: 0.25em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-link {
|
.footer-link {
|
||||||
color: rgba(255,255,255,0.75);
|
color: rgba(255,255,255,0.75);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
.footer-link:hover { color: var(--brand-gold-light); }
|
.footer-link:hover { color: var(--brand-gold-light); }
|
||||||
|
|
||||||
.social-btn {
|
.social-btn {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: rgba(255,255,255,0.08);
|
background: rgba(255,255,255,0.08);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: background 0.3s ease, color 0.3s ease;
|
transition: background 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
.social-btn:hover {
|
.social-btn:hover {
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
color: var(--brand-purple-dark);
|
color: var(--brand-purple-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-copy {
|
.footer-copy {
|
||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
color: rgba(255,255,255,0.4);
|
color: rgba(255,255,255,0.4);
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,310 +1,310 @@
|
||||||
import { Link, useLocation } from "wouter";
|
import { Link, useLocation } from "wouter";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import AppBar from "@mui/material/AppBar";
|
import AppBar from "@mui/material/AppBar";
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import { Menu, X, Instagram, Facebook, Twitter, ChevronDown, Flame, Sparkles, Star } from "lucide-react";
|
import { Menu, X, Instagram, Facebook, Twitter, ChevronDown, Flame, Sparkles, Star } from "lucide-react";
|
||||||
import { ScrollCandle } from "./ScrollCandle";
|
import { ScrollCandle } from "./ScrollCandle";
|
||||||
import "./Layout.css";
|
import "./Layout.css";
|
||||||
|
|
||||||
const logoPath = "/opengraph.jpg";
|
const logoPath = "/opengraph.jpg";
|
||||||
|
|
||||||
// ── Ticker messages ──────────────────────────────────────
|
// ── Ticker messages ──────────────────────────────────────
|
||||||
const TICKER_ITEMS = [
|
const TICKER_ITEMS = [
|
||||||
{ icon: <Flame size={12} />, text: "Free shipping on orders over $50" },
|
{ icon: <Flame size={12} />, text: "Free shipping on orders over $50" },
|
||||||
{ icon: <Sparkles size={12} />, text: "New Collection — Summer Solstice" },
|
{ icon: <Sparkles size={12} />, text: "New Collection — Summer Solstice" },
|
||||||
{ icon: <Star size={12} />, text: "Handpoured in small batches" },
|
{ icon: <Star size={12} />, text: "Handpoured in small batches" },
|
||||||
{ icon: <Flame size={12} />, text: "100% Natural Soy Wax" },
|
{ icon: <Flame size={12} />, text: "100% Natural Soy Wax" },
|
||||||
{ icon: <Sparkles size={12} />, text: "Phthalate-free fragrance oils" },
|
{ icon: <Sparkles size={12} />, text: "Phthalate-free fragrance oils" },
|
||||||
{ icon: <Star size={12} />, text: "Gift wrapping available at checkout" },
|
{ icon: <Star size={12} />, text: "Gift wrapping available at checkout" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ── Nav structure with dropdowns ─────────────────────────
|
// ── Nav structure with dropdowns ─────────────────────────
|
||||||
const NAV_LINKS = [
|
const NAV_LINKS = [
|
||||||
{ name: "Home", path: "/", dropdown: null },
|
{ name: "Home", path: "/", dropdown: null },
|
||||||
{
|
{
|
||||||
name: "Products",
|
name: "Products",
|
||||||
path: "/products",
|
path: "/products",
|
||||||
dropdown: [
|
dropdown: [
|
||||||
{ label: "All Collections", path: "/products" },
|
{ label: "All Collections", path: "/products" },
|
||||||
null, // divider
|
null, // divider
|
||||||
{ label: "Floral Scents", path: "/products" },
|
{ label: "Floral Scents", path: "/products" },
|
||||||
{ label: "Fresh Scents", path: "/products" },
|
{ label: "Fresh Scents", path: "/products" },
|
||||||
{ label: "Woody Scents", path: "/products" },
|
{ label: "Woody Scents", path: "/products" },
|
||||||
{ label: "Sweet Scents", path: "/products" },
|
{ label: "Sweet Scents", path: "/products" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "About",
|
name: "About",
|
||||||
path: "/about",
|
path: "/about",
|
||||||
dropdown: [
|
dropdown: [
|
||||||
{ label: "Our Story", path: "/about" },
|
{ label: "Our Story", path: "/about" },
|
||||||
{ label: "Our Process", path: "/about" },
|
{ label: "Our Process", path: "/about" },
|
||||||
{ label: "Meet the Maker", path: "/about" },
|
{ label: "Meet the Maker", path: "/about" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Contact",
|
name: "Contact",
|
||||||
path: "/contact",
|
path: "/contact",
|
||||||
dropdown: [
|
dropdown: [
|
||||||
{ label: "Send a Message", path: "/contact" },
|
{ label: "Send a Message", path: "/contact" },
|
||||||
{ label: "Wholesale Inquiries", path: "/contact" },
|
{ label: "Wholesale Inquiries", path: "/contact" },
|
||||||
null,
|
null,
|
||||||
{ label: "Follow on Instagram", path: "/contact" },
|
{ label: "Follow on Instagram", path: "/contact" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function Layout({ children }: { children: React.ReactNode }) {
|
export function Layout({ children }: { children: React.ReactNode }) {
|
||||||
const [location] = useLocation();
|
const [location] = useLocation();
|
||||||
const [scrolled, setScrolled] = useState(false);
|
const [scrolled, setScrolled] = useState(false);
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
const [mobileOpenSection, setMobileOpenSection] = useState<string | null>(null);
|
const [mobileOpenSection, setMobileOpenSection] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => setScrolled(window.scrollY > 20);
|
const handleScroll = () => setScrolled(window.scrollY > 20);
|
||||||
window.addEventListener("scroll", handleScroll);
|
window.addEventListener("scroll", handleScroll);
|
||||||
return () => window.removeEventListener("scroll", handleScroll);
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
setMobileMenuOpen(false);
|
setMobileMenuOpen(false);
|
||||||
setMobileOpenSection(null);
|
setMobileOpenSection(null);
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
// Duplicate ticker items to create seamless loop
|
// Duplicate ticker items to create seamless loop
|
||||||
const allTickerItems = [...TICKER_ITEMS, ...TICKER_ITEMS];
|
const allTickerItems = [...TICKER_ITEMS, ...TICKER_ITEMS];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ minHeight: "100vh", display: "flex", flexDirection: "column" }}>
|
<Box sx={{ minHeight: "100vh", display: "flex", flexDirection: "column" }}>
|
||||||
|
|
||||||
{/* ── Ticker Bar ── */}
|
{/* ── Ticker Bar ── */}
|
||||||
<Box
|
<Box
|
||||||
className="ticker-bar"
|
className="ticker-bar"
|
||||||
sx={{ position: "fixed", top: 0, left: 0, right: 0, zIndex: 1200 }}
|
sx={{ position: "fixed", top: 0, left: 0, right: 0, zIndex: 1200 }}
|
||||||
>
|
>
|
||||||
<div className="ticker-track">
|
<div className="ticker-track">
|
||||||
{allTickerItems.map((item, i) => (
|
{allTickerItems.map((item, i) => (
|
||||||
<span className="ticker-item" key={i}>
|
<span className="ticker-item" key={i}>
|
||||||
<span className="ticker-dot" />
|
<span className="ticker-dot" />
|
||||||
{item.icon}
|
{item.icon}
|
||||||
{item.text}
|
{item.text}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* ── Main AppBar ── */}
|
{/* ── Main AppBar ── */}
|
||||||
<AppBar
|
<AppBar
|
||||||
position="fixed"
|
position="fixed"
|
||||||
sx={{
|
sx={{
|
||||||
top: 36, // sits below the ticker bar
|
top: 36, // sits below the ticker bar
|
||||||
background: scrolled
|
background: scrolled
|
||||||
? "rgba(91,30,140,0.78)"
|
? "rgba(91,30,140,0.78)"
|
||||||
: "rgba(91,30,140,0.58)",
|
: "rgba(91,30,140,0.58)",
|
||||||
backdropFilter: "blur(22px) saturate(1.6)",
|
backdropFilter: "blur(22px) saturate(1.6)",
|
||||||
WebkitBackdropFilter: "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",
|
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",
|
transition: "background 0.5s ease, box-shadow 0.5s ease",
|
||||||
overflow: "visible",
|
overflow: "visible",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
<Toolbar
|
<Toolbar
|
||||||
disableGutters
|
disableGutters
|
||||||
sx={{ height: 76, justifyContent: "space-between", overflow: "visible" }}
|
sx={{ height: 76, justifyContent: "space-between", overflow: "visible" }}
|
||||||
>
|
>
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
style={{ display: "flex", alignItems: "center", gap: "0.9rem", textDecoration: "none" }}
|
style={{ display: "flex", alignItems: "center", gap: "0.9rem", textDecoration: "none" }}
|
||||||
>
|
>
|
||||||
<div className="logo-img-wrap">
|
<div className="logo-img-wrap">
|
||||||
<img
|
<img
|
||||||
src={logoPath}
|
src={logoPath}
|
||||||
alt="Mani Candles Co. Logo"
|
alt="Mani Candles Co. Logo"
|
||||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="logo-text">Mani Candles</span>
|
<span className="logo-text">Mani Candles</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Desktop Nav */}
|
{/* Desktop Nav */}
|
||||||
<Box
|
<Box
|
||||||
component="nav"
|
component="nav"
|
||||||
sx={{
|
sx={{
|
||||||
display: { xs: "none", md: "flex" },
|
display: { xs: "none", md: "flex" },
|
||||||
gap: 4,
|
gap: 4,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
overflow: "visible",
|
overflow: "visible",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{NAV_LINKS.map((link) => (
|
{NAV_LINKS.map((link) => (
|
||||||
<div className="nav-item" key={link.path + link.name}>
|
<div className="nav-item" key={link.path + link.name}>
|
||||||
<Link
|
<Link
|
||||||
href={link.path}
|
href={link.path}
|
||||||
className={`nav-link${location === link.path ? " active" : ""}`}
|
className={`nav-link${location === link.path ? " active" : ""}`}
|
||||||
>
|
>
|
||||||
{link.name}
|
{link.name}
|
||||||
{link.dropdown && <ChevronDown size={13} className="nav-chevron" />}
|
{link.dropdown && <ChevronDown size={13} className="nav-chevron" />}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Dropdown */}
|
{/* Dropdown */}
|
||||||
{link.dropdown && (
|
{link.dropdown && (
|
||||||
<div className="nav-dropdown">
|
<div className="nav-dropdown">
|
||||||
{link.dropdown.map((item, idx) =>
|
{link.dropdown.map((item, idx) =>
|
||||||
item === null ? (
|
item === null ? (
|
||||||
<div key={`div-${idx}`} className="dropdown-divider" />
|
<div key={`div-${idx}`} className="dropdown-divider" />
|
||||||
) : (
|
) : (
|
||||||
<Link key={item.label} href={item.path} className="dropdown-item">
|
<Link key={item.label} href={item.path} className="dropdown-item">
|
||||||
{item.label}
|
{item.label}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Mobile Toggle */}
|
{/* Mobile Toggle */}
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{ display: { md: "none" }, color: "#ffffff" }}
|
sx={{ display: { md: "none" }, color: "#ffffff" }}
|
||||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
>
|
>
|
||||||
{mobileMenuOpen ? <X size={26} /> : <Menu size={26} />}
|
{mobileMenuOpen ? <X size={26} /> : <Menu size={26} />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
{/* Mobile Drawer */}
|
{/* Mobile Drawer */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{mobileMenuOpen && (
|
{mobileMenuOpen && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, height: 0 }}
|
initial={{ opacity: 0, height: 0 }}
|
||||||
animate={{ opacity: 1, height: "auto" }}
|
animate={{ opacity: 1, height: "auto" }}
|
||||||
exit={{ opacity: 0, height: 0 }}
|
exit={{ opacity: 0, height: 0 }}
|
||||||
className="mobile-menu"
|
className="mobile-menu"
|
||||||
>
|
>
|
||||||
{NAV_LINKS.map((link) => (
|
{NAV_LINKS.map((link) => (
|
||||||
<div className="mobile-nav-section" key={link.name}>
|
<div className="mobile-nav-section" key={link.name}>
|
||||||
{link.dropdown ? (
|
{link.dropdown ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className={`mobile-nav-link${location === link.path ? " active" : ""}`}
|
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" }}
|
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)}
|
onClick={() => setMobileOpenSection(mobileOpenSection === link.name ? null : link.name)}
|
||||||
>
|
>
|
||||||
{link.name}
|
{link.name}
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
size={14}
|
size={14}
|
||||||
style={{
|
style={{
|
||||||
transition: "transform 0.25s ease",
|
transition: "transform 0.25s ease",
|
||||||
transform: mobileOpenSection === link.name ? "rotate(180deg)" : "rotate(0deg)",
|
transform: mobileOpenSection === link.name ? "rotate(180deg)" : "rotate(0deg)",
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{mobileOpenSection === link.name && (
|
{mobileOpenSection === link.name && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, height: 0 }}
|
initial={{ opacity: 0, height: 0 }}
|
||||||
animate={{ opacity: 1, height: "auto" }}
|
animate={{ opacity: 1, height: "auto" }}
|
||||||
exit={{ opacity: 0, height: 0 }}
|
exit={{ opacity: 0, height: 0 }}
|
||||||
className="mobile-dropdown-items"
|
className="mobile-dropdown-items"
|
||||||
>
|
>
|
||||||
{link.dropdown.map((item, i) =>
|
{link.dropdown.map((item, i) =>
|
||||||
item === null ? (
|
item === null ? (
|
||||||
<div key={i} style={{ height: 1, background: "rgba(255,255,255,0.07)", margin: "0.2rem 0" }} />
|
<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">
|
<Link key={item.label} href={item.path} className="mobile-dropdown-item">
|
||||||
{item.label}
|
{item.label}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Link
|
<Link
|
||||||
href={link.path}
|
href={link.path}
|
||||||
className={`mobile-nav-link${location === link.path ? " active" : ""}`}
|
className={`mobile-nav-link${location === link.path ? " active" : ""}`}
|
||||||
>
|
>
|
||||||
{link.name}
|
{link.name}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
||||||
{/* ── Page Content — offset for ticker + appbar ── */}
|
{/* ── Page Content — offset for ticker + appbar ── */}
|
||||||
<Box component="main" sx={{ flexGrow: 1, pt: "112px" }}>
|
<Box component="main" sx={{ flexGrow: 1, pt: "112px" }}>
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
<motion.div
|
<motion.div
|
||||||
key={location}
|
key={location}
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, y: -20 }}
|
exit={{ opacity: 0, y: -20 }}
|
||||||
transition={{ duration: 0.5, ease: "easeOut" }}
|
transition={{ duration: 0.5, ease: "easeOut" }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* ── Scroll Candle ── */}
|
{/* ── Scroll Candle ── */}
|
||||||
<ScrollCandle />
|
<ScrollCandle />
|
||||||
|
|
||||||
{/* ── Footer ── */}
|
{/* ── Footer ── */}
|
||||||
<footer className="site-footer">
|
<footer className="site-footer">
|
||||||
<div className="footer-inner">
|
<div className="footer-inner">
|
||||||
<div className="footer-col">
|
<div className="footer-col">
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
|
||||||
<div style={{ width: 56, height: 56, borderRadius: "50%", overflow: "hidden", background: "#fff", padding: 3 }}>
|
<div style={{ width: 56, height: 56, borderRadius: "50%", overflow: "hidden", background: "#fff", padding: 3 }}>
|
||||||
<img
|
<img
|
||||||
src={logoPath}
|
src={logoPath}
|
||||||
alt="Mani Candles Co."
|
alt="Mani Candles Co."
|
||||||
style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" }}
|
style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="font-script" style={{ fontSize: "2rem", color: "var(--brand-gold-light)" }}>
|
<span className="font-script" style={{ fontSize: "2rem", color: "var(--brand-gold-light)" }}>
|
||||||
Mani Candles
|
Mani Candles
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="footer-tagline">Handmade • Natural • Heartmade</p>
|
<p className="footer-tagline">Handmade • Natural • Heartmade</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="footer-col">
|
<div className="footer-col">
|
||||||
<h4 className="footer-heading">Explore</h4>
|
<h4 className="footer-heading">Explore</h4>
|
||||||
{NAV_LINKS.map((link) => (
|
{NAV_LINKS.map((link) => (
|
||||||
<Link key={link.path + link.name} href={link.path} className="footer-link">
|
<Link key={link.path + link.name} href={link.path} className="footer-link">
|
||||||
{link.name}
|
{link.name}
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="footer-col">
|
<div className="footer-col">
|
||||||
<h4 className="footer-heading">Connect</h4>
|
<h4 className="footer-heading">Connect</h4>
|
||||||
<div style={{ display: "flex", gap: "0.75rem" }}>
|
<div style={{ display: "flex", gap: "0.75rem" }}>
|
||||||
<a href="#" className="social-btn"><Instagram size={18} /></a>
|
<a href="#" className="social-btn"><Instagram size={18} /></a>
|
||||||
<a href="#" className="social-btn"><Facebook size={18} /></a>
|
<a href="#" className="social-btn"><Facebook size={18} /></a>
|
||||||
<a href="#" className="social-btn"><Twitter size={18} /></a>
|
<a href="#" className="social-btn"><Twitter size={18} /></a>
|
||||||
</div>
|
</div>
|
||||||
<p className="footer-copy">
|
<p className="footer-copy">
|
||||||
© {new Date().getFullYear()} Mani Candles Co. All rights reserved.
|
© {new Date().getFullYear()} Mani Candles Co. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,291 +1,291 @@
|
||||||
/* ─── ScrollCandle.css ───────────────────────────────────── */
|
/* ─── ScrollCandle.css ───────────────────────────────────── */
|
||||||
|
|
||||||
.scroll-candle-wrap {
|
.scroll-candle-wrap {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 28px;
|
right: 28px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
filter: drop-shadow(0 0 18px rgba(255, 160, 40, 0.35));
|
filter: drop-shadow(0 0 18px rgba(255, 160, 40, 0.35));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── FLAME SYSTEM ─────────────────────────────────── */
|
/* ── FLAME SYSTEM ─────────────────────────────────── */
|
||||||
.candle-flame-wrap {
|
.candle-flame-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Outer glow halo */
|
/* Outer glow halo */
|
||||||
.flame-glow {
|
.flame-glow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: radial-gradient(circle, rgba(255, 200, 50, 0.45) 0%, transparent 70%);
|
background: radial-gradient(circle, rgba(255, 200, 50, 0.45) 0%, transparent 70%);
|
||||||
animation: glow-pulse 1.8s ease-in-out infinite alternate;
|
animation: glow-pulse 1.8s ease-in-out infinite alternate;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes glow-pulse {
|
@keyframes glow-pulse {
|
||||||
0% {
|
0% {
|
||||||
transform: translateX(-50%) scale(1);
|
transform: translateX(-50%) scale(1);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: translateX(-50%) scale(1.35);
|
transform: translateX(-50%) scale(1.35);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flame outer — large orange teardrop */
|
/* Flame outer — large orange teardrop */
|
||||||
.flame-outer {
|
.flame-outer {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
background: linear-gradient(to top, #ff6a00, #ffaa00, #ffe066);
|
background: linear-gradient(to top, #ff6a00, #ffaa00, #ffe066);
|
||||||
border-radius: 50% 50% 30% 30% / 60% 60% 40% 40%;
|
border-radius: 50% 50% 30% 30% / 60% 60% 40% 40%;
|
||||||
transform-origin: bottom center;
|
transform-origin: bottom center;
|
||||||
animation: flicker-outer 0.9s ease-in-out infinite alternate;
|
animation: flicker-outer 0.9s ease-in-out infinite alternate;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 0 8px 3px rgba(255, 140, 0, 0.6),
|
0 0 8px 3px rgba(255, 140, 0, 0.6),
|
||||||
0 0 18px 6px rgba(255, 100, 0, 0.4);
|
0 0 18px 6px rgba(255, 100, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes flicker-outer {
|
@keyframes flicker-outer {
|
||||||
0% {
|
0% {
|
||||||
transform: scaleX(1) scaleY(1) rotate(-2deg);
|
transform: scaleX(1) scaleY(1) rotate(-2deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
25% {
|
25% {
|
||||||
transform: scaleX(0.88) scaleY(1.05) rotate(2deg);
|
transform: scaleX(0.88) scaleY(1.05) rotate(2deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
transform: scaleX(1.05) scaleY(0.95) rotate(-1deg);
|
transform: scaleX(1.05) scaleY(0.95) rotate(-1deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
75% {
|
75% {
|
||||||
transform: scaleX(0.92) scaleY(1.08) rotate(3deg);
|
transform: scaleX(0.92) scaleY(1.08) rotate(3deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: scaleX(1.02) scaleY(1.02) rotate(-2deg);
|
transform: scaleX(1.02) scaleY(1.02) rotate(-2deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flame inner — bright white-yellow core */
|
/* Flame inner — bright white-yellow core */
|
||||||
.flame-inner {
|
.flame-inner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 4px;
|
bottom: 4px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
background: linear-gradient(to top, #fff8c5, #fff, rgba(255, 255, 255, 0));
|
background: linear-gradient(to top, #fff8c5, #fff, rgba(255, 255, 255, 0));
|
||||||
border-radius: 50% 50% 30% 30% / 60% 60% 40% 40%;
|
border-radius: 50% 50% 30% 30% / 60% 60% 40% 40%;
|
||||||
animation: flicker-inner 0.7s ease-in-out infinite alternate;
|
animation: flicker-inner 0.7s ease-in-out infinite alternate;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes flicker-inner {
|
@keyframes flicker-inner {
|
||||||
0% {
|
0% {
|
||||||
transform: translateX(-50%) scaleX(1) scaleY(1);
|
transform: translateX(-50%) scaleX(1) scaleY(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
transform: translateX(-50%) scaleX(0.85) scaleY(1.1);
|
transform: translateX(-50%) scaleX(0.85) scaleY(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: translateX(-50%) scaleX(1.1) scaleY(0.9);
|
transform: translateX(-50%) scaleX(1.1) scaleY(0.9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── SMOKE PARTICLES ─────────────────────────────── */
|
/* ── SMOKE PARTICLES ─────────────────────────────── */
|
||||||
.smoke-wrap {
|
.smoke-wrap {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -18px;
|
top: -18px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smoke-particle {
|
.smoke-particle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: rgba(200, 180, 220, 0.45);
|
background: rgba(200, 180, 220, 0.45);
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
animation: smoke-rise 2.4s ease-out infinite;
|
animation: smoke-rise 2.4s ease-out infinite;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smoke-particle:nth-child(2) {
|
.smoke-particle:nth-child(2) {
|
||||||
animation-delay: 0.8s;
|
animation-delay: 0.8s;
|
||||||
left: 40%;
|
left: 40%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smoke-particle:nth-child(3) {
|
.smoke-particle:nth-child(3) {
|
||||||
animation-delay: 1.6s;
|
animation-delay: 1.6s;
|
||||||
left: 60%;
|
left: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes smoke-rise {
|
@keyframes smoke-rise {
|
||||||
0% {
|
0% {
|
||||||
transform: translateX(-50%) translateY(0) scale(0.6);
|
transform: translateX(-50%) translateY(0) scale(0.6);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
20% {
|
20% {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: translateX(-50%) translateY(-22px) scale(2);
|
transform: translateX(-50%) translateY(-22px) scale(2);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── WICK ──────────────────────────────────────────── */
|
/* ── WICK ──────────────────────────────────────────── */
|
||||||
.candle-wick {
|
.candle-wick {
|
||||||
width: 2px;
|
width: 2px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
background: linear-gradient(to bottom, #3a2000, #1a0f00);
|
background: linear-gradient(to bottom, #3a2000, #1a0f00);
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── CANDLE BODY ──────────────────────────────────── */
|
/* ── CANDLE BODY ──────────────────────────────────── */
|
||||||
.candle-body {
|
.candle-body {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 22px;
|
width: 22px;
|
||||||
background: linear-gradient(to right,
|
background: linear-gradient(to right,
|
||||||
#e8dcc8 0%,
|
#e8dcc8 0%,
|
||||||
#fdf6e3 25%,
|
#fdf6e3 25%,
|
||||||
#fff9f0 50%,
|
#fff9f0 50%,
|
||||||
#fdf6e3 75%,
|
#fdf6e3 75%,
|
||||||
#d9cbb5 100%);
|
#d9cbb5 100%);
|
||||||
border-radius: 3px 3px 4px 4px;
|
border-radius: 3px 3px 4px 4px;
|
||||||
transition: height 0.18s linear;
|
transition: height 0.18s linear;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
inset -3px 0 6px rgba(0, 0, 0, 0.1),
|
inset -3px 0 6px rgba(0, 0, 0, 0.1),
|
||||||
inset 3px 0 4px rgba(255, 255, 255, 0.4);
|
inset 3px 0 4px rgba(255, 255, 255, 0.4);
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Candle top melted rim */
|
/* Candle top melted rim */
|
||||||
.candle-body::before {
|
.candle-body::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -3px;
|
top: -3px;
|
||||||
left: -2px;
|
left: -2px;
|
||||||
right: -2px;
|
right: -2px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
background: radial-gradient(ellipse at center, #ffe8b0 0%, #e8d5a0 100%);
|
background: radial-gradient(ellipse at center, #ffe8b0 0%, #e8d5a0 100%);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── WAX DRIPS ──────────────────────────────────────── */
|
/* ── WAX DRIPS ──────────────────────────────────────── */
|
||||||
.wax-drip {
|
.wax-drip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
width: 5px;
|
width: 5px;
|
||||||
background: linear-gradient(to bottom, rgba(253, 246, 227, 0.95), rgba(232, 220, 200, 0.6));
|
background: linear-gradient(to bottom, rgba(253, 246, 227, 0.95), rgba(232, 220, 200, 0.6));
|
||||||
border-radius: 0 0 4px 4px;
|
border-radius: 0 0 4px 4px;
|
||||||
animation: drip-grow 4s ease-in-out infinite;
|
animation: drip-grow 4s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wax-drip-1 {
|
.wax-drip-1 {
|
||||||
left: -3px;
|
left: -3px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
animation-delay: 0s;
|
animation-delay: 0s;
|
||||||
animation-duration: 4.5s;
|
animation-duration: 4.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wax-drip-2 {
|
.wax-drip-2 {
|
||||||
right: -3px;
|
right: -3px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
animation-delay: 1.2s;
|
animation-delay: 1.2s;
|
||||||
animation-duration: 3.8s;
|
animation-duration: 3.8s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wax-drip-3 {
|
.wax-drip-3 {
|
||||||
left: 2px;
|
left: 2px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
animation-delay: 2.5s;
|
animation-delay: 2.5s;
|
||||||
animation-duration: 5.2s;
|
animation-duration: 5.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes drip-grow {
|
@keyframes drip-grow {
|
||||||
0% {
|
0% {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
60% {
|
60% {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── CANDLE BASE / HOLDER ────────────────────────── */
|
/* ── CANDLE BASE / HOLDER ────────────────────────── */
|
||||||
.candle-base {
|
.candle-base {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
background: linear-gradient(to bottom, #c8a96e, #a07840);
|
background: linear-gradient(to bottom, #c8a96e, #a07840);
|
||||||
border-radius: 3px 3px 6px 6px;
|
border-radius: 3px 3px 6px 6px;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 3px 8px rgba(0, 0, 0, 0.3),
|
0 3px 8px rgba(0, 0, 0, 0.3),
|
||||||
inset 0 1px 2px rgba(255, 200, 100, 0.4);
|
inset 0 1px 2px rgba(255, 200, 100, 0.4);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Holder shine */
|
/* Holder shine */
|
||||||
.candle-base::after {
|
.candle-base::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
background: rgba(255, 220, 140, 0.4);
|
background: rgba(255, 220, 140, 0.4);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── SCROLL LABEL ───────────────────────────────── */
|
/* ── SCROLL LABEL ───────────────────────────────── */
|
||||||
.candle-scroll-label {
|
.candle-scroll-label {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
font-family: var(--font-script);
|
font-family: var(--font-script);
|
||||||
font-size: 0.68rem;
|
font-size: 0.68rem;
|
||||||
color: rgba(212, 162, 76, 0.7);
|
color: rgba(212, 162, 76, 0.7);
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
@ -1,99 +1,99 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import "./ScrollCandle.css";
|
import "./ScrollCandle.css";
|
||||||
|
|
||||||
// Space taken by flame + wick + base + label + bottom offset + header (ticker 36 + AppBar 76) + safety gap
|
// 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 NON_BODY_HEIGHT = 70 + 8 + 10 + 24 + 40 + 112 + 16;
|
||||||
const CANDLE_MIN_HEIGHT = 14; // px — almost fully melted
|
const CANDLE_MIN_HEIGHT = 14; // px — almost fully melted
|
||||||
//
|
//
|
||||||
export function ScrollCandle() {
|
export function ScrollCandle() {
|
||||||
const [maxHeight, setMaxHeight] = useState(
|
const [maxHeight, setMaxHeight] = useState(
|
||||||
() => Math.max(200, window.innerHeight - NON_BODY_HEIGHT)
|
() => Math.max(200, window.innerHeight - NON_BODY_HEIGHT)
|
||||||
);
|
);
|
||||||
const [candleHeight, setCandleHeight] = useState(
|
const [candleHeight, setCandleHeight] = useState(
|
||||||
() => Math.max(200, window.innerHeight - NON_BODY_HEIGHT)
|
() => Math.max(200, window.innerHeight - NON_BODY_HEIGHT)
|
||||||
);
|
);
|
||||||
const [scrollPct, setScrollPct] = useState(0);
|
const [scrollPct, setScrollPct] = useState(0);
|
||||||
|
|
||||||
// Recalculate on resize
|
// Recalculate on resize
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onResize = () => {
|
const onResize = () => {
|
||||||
const newMax = Math.max(200, window.innerHeight - NON_BODY_HEIGHT);
|
const newMax = Math.max(200, window.innerHeight - NON_BODY_HEIGHT);
|
||||||
setMaxHeight(newMax);
|
setMaxHeight(newMax);
|
||||||
};
|
};
|
||||||
window.addEventListener("resize", onResize);
|
window.addEventListener("resize", onResize);
|
||||||
return () => window.removeEventListener("resize", onResize);
|
return () => window.removeEventListener("resize", onResize);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const scrollTop = window.scrollY;
|
const scrollTop = window.scrollY;
|
||||||
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
const pct = docHeight > 0 ? Math.min(scrollTop / docHeight, 1) : 0;
|
const pct = docHeight > 0 ? Math.min(scrollTop / docHeight, 1) : 0;
|
||||||
|
|
||||||
setScrollPct(pct);
|
setScrollPct(pct);
|
||||||
setCandleHeight(
|
setCandleHeight(
|
||||||
Math.max(CANDLE_MIN_HEIGHT, maxHeight - pct * (maxHeight - CANDLE_MIN_HEIGHT))
|
Math.max(CANDLE_MIN_HEIGHT, maxHeight - pct * (maxHeight - CANDLE_MIN_HEIGHT))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("scroll", handleScroll, { passive: true });
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||||
return () => window.removeEventListener("scroll", handleScroll);
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
}, [maxHeight]);
|
}, [maxHeight]);
|
||||||
|
|
||||||
// Flame shrinks slightly as candle melts
|
// Flame shrinks slightly as candle melts
|
||||||
const flameScale = 0.6 + (candleHeight / maxHeight) * 0.4;
|
const flameScale = 0.6 + (candleHeight / maxHeight) * 0.4;
|
||||||
// Label changes as candle melts
|
// Label changes as candle melts
|
||||||
const label =
|
const label =
|
||||||
scrollPct < 0.15 ? "just lit" :
|
scrollPct < 0.15 ? "just lit" :
|
||||||
scrollPct < 0.45 ? "burning..." :
|
scrollPct < 0.45 ? "burning..." :
|
||||||
scrollPct < 0.75 ? "melting..." :
|
scrollPct < 0.75 ? "melting..." :
|
||||||
scrollPct < 0.92 ? "almost gone" :
|
scrollPct < 0.92 ? "almost gone" :
|
||||||
"★ keep scrolling";
|
"★ keep scrolling";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="scroll-candle-wrap" aria-hidden="true">
|
<div className="scroll-candle-wrap" aria-hidden="true">
|
||||||
{/* ── Flame ── */}
|
{/* ── Flame ── */}
|
||||||
<div
|
<div
|
||||||
className="candle-flame-wrap"
|
className="candle-flame-wrap"
|
||||||
style={{ transform: `scale(${flameScale})`, transformOrigin: "bottom center" }}
|
style={{ transform: `scale(${flameScale})`, transformOrigin: "bottom center" }}
|
||||||
>
|
>
|
||||||
{/* Smoke */}
|
{/* Smoke */}
|
||||||
<div className="smoke-wrap">
|
<div className="smoke-wrap">
|
||||||
<div className="smoke-particle" />
|
<div className="smoke-particle" />
|
||||||
<div className="smoke-particle" />
|
<div className="smoke-particle" />
|
||||||
<div className="smoke-particle" />
|
<div className="smoke-particle" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Glow halo */}
|
{/* Glow halo */}
|
||||||
<div className="flame-glow" />
|
<div className="flame-glow" />
|
||||||
|
|
||||||
{/* Outer flame */}
|
{/* Outer flame */}
|
||||||
<div className="flame-outer">
|
<div className="flame-outer">
|
||||||
{/* Inner core */}
|
{/* Inner core */}
|
||||||
<div className="flame-inner" />
|
<div className="flame-inner" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Wick ── */}
|
{/* ── Wick ── */}
|
||||||
<div className="candle-wick" />
|
<div className="candle-wick" />
|
||||||
|
|
||||||
{/* ── Candle Body (shrinks on scroll) ── */}
|
{/* ── Candle Body (shrinks on scroll) ── */}
|
||||||
<div className="candle-body" style={{ height: candleHeight }}>
|
<div className="candle-body" style={{ height: candleHeight }}>
|
||||||
<div className="wax-drip wax-drip-1" />
|
<div className="wax-drip wax-drip-1" />
|
||||||
<div className="wax-drip wax-drip-2" />
|
<div className="wax-drip wax-drip-2" />
|
||||||
<div className="wax-drip wax-drip-3" />
|
<div className="wax-drip wax-drip-3" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Base / Holder ── */}
|
{/* ── Base / Holder ── */}
|
||||||
<div className="candle-base" />
|
<div className="candle-base" />
|
||||||
|
|
||||||
{/* ── Label ── */}
|
{/* ── Label ── */}
|
||||||
<p
|
<p
|
||||||
className="candle-scroll-label"
|
className="candle-scroll-label"
|
||||||
style={{ opacity: scrollPct > 0.02 ? 1 : 0 }}
|
style={{ opacity: scrollPct > 0.02 ? 1 : 0 }}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
170
src/index.css
170
src/index.css
|
|
@ -1,85 +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 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');
|
||||||
|
|
||||||
/* ─── CSS Custom Properties ─────────────────────────────── */
|
/* ─── CSS Custom Properties ─────────────────────────────── */
|
||||||
:root {
|
:root {
|
||||||
--brand-purple: #5B1E8C;
|
--brand-purple: #5B1E8C;
|
||||||
--brand-purple-dark: #3A1260;
|
--brand-purple-dark: #3A1260;
|
||||||
--brand-purple-mid: #7B3DB8;
|
--brand-purple-mid: #7B3DB8;
|
||||||
--brand-lavender: #DCC6F2;
|
--brand-lavender: #DCC6F2;
|
||||||
--brand-light-lavender:#EFE4FA;
|
--brand-light-lavender:#EFE4FA;
|
||||||
--brand-card: #F8F2FD;
|
--brand-card: #F8F2FD;
|
||||||
--brand-gold: #D4A24C;
|
--brand-gold: #D4A24C;
|
||||||
--brand-gold-light: #FFD36B;
|
--brand-gold-light: #FFD36B;
|
||||||
--brand-gold-core: #FFF3C4;
|
--brand-gold-core: #FFF3C4;
|
||||||
--brand-text: #2D0D52;
|
--brand-text: #2D0D52;
|
||||||
--brand-muted: #7B5A9E;
|
--brand-muted: #7B5A9E;
|
||||||
--brand-border: #D5BEF0;
|
--brand-border: #D5BEF0;
|
||||||
|
|
||||||
--font-sans: 'Inter', sans-serif;
|
--font-sans: 'Inter', sans-serif;
|
||||||
--font-serif: 'Cormorant Garamond', serif;
|
--font-serif: 'Cormorant Garamond', serif;
|
||||||
--font-display: 'Cinzel', serif;
|
--font-display: 'Cinzel', serif;
|
||||||
--font-script: 'Great Vibes', cursive;
|
--font-script: 'Great Vibes', cursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Reset / Base ───────────────────────────────────────── */
|
/* ─── Reset / Base ───────────────────────────────────────── */
|
||||||
*, *::before, *::after {
|
*, *::before, *::after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
background-color: var(--brand-light-lavender);
|
background-color: var(--brand-light-lavender);
|
||||||
color: var(--brand-text);
|
color: var(--brand-text);
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Keyframe Animations ────────────────────────────────── */
|
/* ─── Keyframe Animations ────────────────────────────────── */
|
||||||
@keyframes flame-flicker {
|
@keyframes flame-flicker {
|
||||||
0%, 100% { transform: scale(1) translateY(0); opacity: 0.9; }
|
0%, 100% { transform: scale(1) translateY(0); opacity: 0.9; }
|
||||||
50% { transform: scale(1.05) translateY(-2px); opacity: 1; }
|
50% { transform: scale(1.05) translateY(-2px); opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadeInUp {
|
@keyframes fadeInUp {
|
||||||
from { opacity: 0; transform: translateY(24px); }
|
from { opacity: 0; transform: translateY(24px); }
|
||||||
to { opacity: 1; transform: translateY(0); }
|
to { opacity: 1; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Global Utility Classes ─────────────────────────────── */
|
/* ─── Global Utility Classes ─────────────────────────────── */
|
||||||
.animate-flame {
|
.animate-flame {
|
||||||
animation: flame-flicker 2s ease-in-out infinite alternate;
|
animation: flame-flicker 2s ease-in-out infinite alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-script {
|
.font-script {
|
||||||
font-family: var(--font-script);
|
font-family: var(--font-script);
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-display {
|
.font-display {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-serif {
|
.font-serif {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
src/main.tsx
26
src/main.tsx
|
|
@ -1,13 +1,13 @@
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { ThemeProvider } from "@mui/material/styles";
|
import { ThemeProvider } from "@mui/material/styles";
|
||||||
import CssBaseline from "@mui/material/CssBaseline";
|
import CssBaseline from "@mui/material/CssBaseline";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<App />
|
<App />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,130 +1,130 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Droplets, Flame, HeartHandshake } from "lucide-react";
|
import { Droplets, Flame, HeartHandshake } from "lucide-react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import "./about.css";
|
import "./about.css";
|
||||||
|
|
||||||
const logoPath = "/opengraph.jpg";
|
const logoPath = "/opengraph.jpg";
|
||||||
|
|
||||||
const fadeInUp = {
|
const fadeInUp = {
|
||||||
hidden: { opacity: 0, y: 30 },
|
hidden: { opacity: 0, y: 30 },
|
||||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
const stagger = {
|
const stagger = {
|
||||||
visible: { transition: { staggerChildren: 0.2 } },
|
visible: { transition: { staggerChildren: 0.2 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AboutPage() {
|
export default function AboutPage() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{/* ── Hero ── */}
|
{/* ── Hero ── */}
|
||||||
<section className="about-hero">
|
<section className="about-hero">
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
<motion.div initial="hidden" animate="visible" variants={stagger}>
|
<motion.div initial="hidden" animate="visible" variants={stagger}>
|
||||||
<motion.h1 variants={fadeInUp}>The Light Behind the Wax</motion.h1>
|
<motion.h1 variants={fadeInUp}>The Light Behind the Wax</motion.h1>
|
||||||
<div className="section-divider" />
|
<div className="section-divider" />
|
||||||
<motion.p variants={fadeInUp}>
|
<motion.p variants={fadeInUp}>
|
||||||
Mani Candles Co. is a small handmade candle brand crafted with love, natural ingredients, and intention.
|
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
|
Every candle is poured by hand in small batches, designed to bring warmth, luxury, and a touch of romance
|
||||||
into your sacred spaces.
|
into your sacred spaces.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Brand Values ── */}
|
{/* ── Brand Values ── */}
|
||||||
<section className="about-values">
|
<section className="about-values">
|
||||||
<Container maxWidth="lg">
|
<Container maxWidth="lg">
|
||||||
<div className="values-grid">
|
<div className="values-grid">
|
||||||
{[
|
{[
|
||||||
{ 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: 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: 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." },
|
{ 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) => (
|
].map(({ Icon, title, desc }, i) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={title}
|
key={title}
|
||||||
className="value-card"
|
className="value-card"
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
whileInView="visible"
|
whileInView="visible"
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
variants={{ hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.2, duration: 0.6 } } }}
|
variants={{ hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.2, duration: 0.6 } } }}
|
||||||
>
|
>
|
||||||
<div className="value-icon"><Icon size={40} /></div>
|
<div className="value-icon"><Icon size={40} /></div>
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<p>{desc}</p>
|
<p>{desc}</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Meet the Maker ── */}
|
{/* ── Meet the Maker ── */}
|
||||||
<section className="about-maker">
|
<section className="about-maker">
|
||||||
<div className="maker-gradient-overlay" />
|
<div className="maker-gradient-overlay" />
|
||||||
<Container maxWidth="lg">
|
<Container maxWidth="lg">
|
||||||
<div className="maker-grid">
|
<div className="maker-grid">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="maker-img-wrap"
|
className="maker-img-wrap"
|
||||||
initial={{ opacity: 0, x: -50 }}
|
initial={{ opacity: 0, x: -50 }}
|
||||||
whileInView={{ opacity: 1, x: 0 }}
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
>
|
>
|
||||||
<img src={logoPath} alt="Mani Candles Co. Logo Art" />
|
<img src={logoPath} alt="Mani Candles Co. Logo Art" />
|
||||||
<div className="maker-img-ring" />
|
<div className="maker-img-ring" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="maker-content"
|
className="maker-content"
|
||||||
initial={{ opacity: 0, x: 50 }}
|
initial={{ opacity: 0, x: 50 }}
|
||||||
whileInView={{ opacity: 1, x: 0 }}
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
>
|
>
|
||||||
<h2>Meet the Maker</h2>
|
<h2>Meet the Maker</h2>
|
||||||
<div className="maker-divider" />
|
<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>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>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>
|
<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>
|
<span className="maker-signature">With love,</span>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Process Timeline ── */}
|
{/* ── Process Timeline ── */}
|
||||||
<section className="about-process">
|
<section className="about-process">
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
<h2>Our Process</h2>
|
<h2>Our Process</h2>
|
||||||
<p className="subtitle">The journey of a Mani Candle.</p>
|
<p className="subtitle">The journey of a Mani Candle.</p>
|
||||||
|
|
||||||
<div className="timeline">
|
<div className="timeline">
|
||||||
{[
|
{[
|
||||||
{ step: "01", title: "Sourcing", desc: "We select only the finest natural soy wax and premium, phthalate-free fragrances." },
|
{ 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." },
|
{ step: "02", title: "Blending", desc: "Fragrances are meticulously mixed at the exact temperature for optimal throw." },
|
||||||
{ step: "03", title: "Pouring", desc: "Each jar is wicked and poured entirely by hand, ensuring an even, smooth finish." },
|
{ step: "03", title: "Pouring", desc: "Each jar is wicked and poured entirely by hand, ensuring an even, smooth finish." },
|
||||||
{ step: "04", title: "Curing", desc: "Candles rest and cure for weeks to let the wax and fragrance bind perfectly." },
|
{ step: "04", title: "Curing", desc: "Candles rest and cure for weeks to let the wax and fragrance bind perfectly." },
|
||||||
{ step: "05", title: "Packaging", desc: "Lovingly labeled, boxed, and prepared to illuminate your home." },
|
{ step: "05", title: "Packaging", desc: "Lovingly labeled, boxed, and prepared to illuminate your home." },
|
||||||
].map((item, i) => (
|
].map((item, i) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={i}
|
key={i}
|
||||||
className="timeline-item"
|
className="timeline-item"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ delay: i * 0.1, duration: 0.5 }}
|
transition={{ delay: i * 0.1, duration: 0.5 }}
|
||||||
>
|
>
|
||||||
<div className="timeline-step">{item.step}</div>
|
<div className="timeline-step">{item.step}</div>
|
||||||
<div className="timeline-body">
|
<div className="timeline-body">
|
||||||
<h3>{item.title}</h3>
|
<h3>{item.title}</h3>
|
||||||
<p>{item.desc}</p>
|
<p>{item.desc}</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,133 +1,133 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Mail, MapPin, Phone, Instagram } from "lucide-react";
|
import { Mail, MapPin, Phone, Instagram } from "lucide-react";
|
||||||
import { HeartHandshake } from "lucide-react";
|
import { HeartHandshake } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import "./contact.css";
|
import "./contact.css";
|
||||||
|
|
||||||
const fadeInUp = {
|
const fadeInUp = {
|
||||||
hidden: { opacity: 0, y: 30 },
|
hidden: { opacity: 0, y: 30 },
|
||||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ContactPage() {
|
export default function ContactPage() {
|
||||||
const [formStatus, setFormStatus] = useState<"idle" | "submitting" | "success">("idle");
|
const [formStatus, setFormStatus] = useState<"idle" | "submitting" | "success">("idle");
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setFormStatus("submitting");
|
setFormStatus("submitting");
|
||||||
setTimeout(() => setFormStatus("success"), 1500);
|
setTimeout(() => setFormStatus("success"), 1500);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{/* ── Hero ── */}
|
{/* ── Hero ── */}
|
||||||
<section className="contact-hero">
|
<section className="contact-hero">
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
<motion.div initial="hidden" animate="visible" variants={fadeInUp}>
|
<motion.div initial="hidden" animate="visible" variants={fadeInUp}>
|
||||||
<h1>Get in Touch</h1>
|
<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>
|
<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" />
|
<div className="section-divider" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Contact Body ── */}
|
{/* ── Contact Body ── */}
|
||||||
<section className="contact-body">
|
<section className="contact-body">
|
||||||
<Container maxWidth="lg">
|
<Container maxWidth="lg">
|
||||||
<div className="contact-grid">
|
<div className="contact-grid">
|
||||||
{/* Info Panel */}
|
{/* Info Panel */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="contact-info-panel"
|
className="contact-info-panel"
|
||||||
initial={{ opacity: 0, x: -30 }}
|
initial={{ opacity: 0, x: -30 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
>
|
>
|
||||||
<h2>Contact Information</h2>
|
<h2>Contact Information</h2>
|
||||||
<div className="contact-info-list">
|
<div className="contact-info-list">
|
||||||
{[
|
{[
|
||||||
{ Icon: Mail, label: "Email", value: <a href="mailto:hello@manicandles.co">hello@manicandles.co</a> },
|
{ 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: 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)</> },
|
{ Icon: MapPin, label: "Studio", value: <>New York, NY<br />(By appointment only)</> },
|
||||||
].map(({ Icon, label, value }) => (
|
].map(({ Icon, label, value }) => (
|
||||||
<div key={label} className="contact-info-item">
|
<div key={label} className="contact-info-item">
|
||||||
<div className="contact-info-icon"><Icon size={20} /></div>
|
<div className="contact-info-icon"><Icon size={20} /></div>
|
||||||
<div>
|
<div>
|
||||||
<div className="contact-info-label">{label}</div>
|
<div className="contact-info-label">{label}</div>
|
||||||
<div className="contact-info-value">{value}</div>
|
<div className="contact-info-value">{value}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="contact-social-box">
|
<div className="contact-social-box">
|
||||||
<h3>Follow Our Journey</h3>
|
<h3>Follow Our Journey</h3>
|
||||||
<p>Join our community on Instagram for behind-the-scenes peeks, styling inspiration, and launch announcements.</p>
|
<p>Join our community on Instagram for behind-the-scenes peeks, styling inspiration, and launch announcements.</p>
|
||||||
<a href="#" className="contact-ig-link">
|
<a href="#" className="contact-ig-link">
|
||||||
<Instagram size={20} />
|
<Instagram size={20} />
|
||||||
<span>@manicandles.co</span>
|
<span>@manicandles.co</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Form Panel */}
|
{/* Form Panel */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="contact-form-panel"
|
className="contact-form-panel"
|
||||||
initial={{ opacity: 0, x: 30 }}
|
initial={{ opacity: 0, x: 30 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
>
|
>
|
||||||
<div className="contact-form-top-bar" />
|
<div className="contact-form-top-bar" />
|
||||||
|
|
||||||
{formStatus === "success" ? (
|
{formStatus === "success" ? (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="contact-success"
|
className="contact-success"
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
>
|
>
|
||||||
<div className="success-icon-wrap"><HeartHandshake size={32} /></div>
|
<div className="success-icon-wrap"><HeartHandshake size={32} /></div>
|
||||||
<h3>Thank you!</h3>
|
<h3>Thank you!</h3>
|
||||||
<p>Your message has been received. We will get back to you as soon as possible.</p>
|
<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>
|
<button className="send-another-btn" onClick={() => setFormStatus("idle")}>Send another</button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<h2>Send a Message</h2>
|
<h2>Send a Message</h2>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<div className="form-field">
|
<div className="form-field">
|
||||||
<label htmlFor="name" className="form-label">Name</label>
|
<label htmlFor="name" className="form-label">Name</label>
|
||||||
<input id="name" type="text" className="form-input" placeholder="Your name" required />
|
<input id="name" type="text" className="form-input" placeholder="Your name" required />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-field">
|
<div className="form-field">
|
||||||
<label htmlFor="email" className="form-label">Email</label>
|
<label htmlFor="email" className="form-label">Email</label>
|
||||||
<input id="email" type="email" className="form-input" placeholder="Your email" required />
|
<input id="email" type="email" className="form-input" placeholder="Your email" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-field">
|
<div className="form-field">
|
||||||
<label htmlFor="subject" className="form-label">Subject</label>
|
<label htmlFor="subject" className="form-label">Subject</label>
|
||||||
<input id="subject" type="text" className="form-input" placeholder="How can we help?" required />
|
<input id="subject" type="text" className="form-input" placeholder="How can we help?" required />
|
||||||
</div>
|
</div>
|
||||||
<div className="form-field">
|
<div className="form-field">
|
||||||
<label htmlFor="message" className="form-label">Message</label>
|
<label htmlFor="message" className="form-label">Message</label>
|
||||||
<textarea id="message" className="form-textarea" placeholder="Write your message here..." required />
|
<textarea id="message" className="form-textarea" placeholder="Write your message here..." required />
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className="form-submit" disabled={formStatus === "submitting"}>
|
<button type="submit" className="form-submit" disabled={formStatus === "submitting"}>
|
||||||
{formStatus === "submitting" ? "Sending..." : "Send Message"}
|
{formStatus === "submitting" ? "Sending..." : "Send Message"}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Closing Quote ── */}
|
{/* ── Closing Quote ── */}
|
||||||
<section className="contact-quote">
|
<section className="contact-quote">
|
||||||
<p>"May the light of our candles bring peace to your mind and warmth to your soul."</p>
|
<p>"May the light of our candles bring peace to your mind and warmth to your soul."</p>
|
||||||
</section>
|
</section>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,100 +1,100 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import birthdayImage from "@/assets/images/product-birthday.png";
|
import birthdayImage from "@/assets/images/product-birthday.png";
|
||||||
import eucalyptusImage from "@/assets/images/product-eucalyptus.png";
|
import eucalyptusImage from "@/assets/images/product-eucalyptus.png";
|
||||||
import jasmineImage from "@/assets/images/product-jasmine.png";
|
import jasmineImage from "@/assets/images/product-jasmine.png";
|
||||||
import lavenderImage from "@/assets/images/product-lavender.png";
|
import lavenderImage from "@/assets/images/product-lavender.png";
|
||||||
import linenImage from "@/assets/images/product-linen.png";
|
import linenImage from "@/assets/images/product-linen.png";
|
||||||
import oudImage from "@/assets/images/product-oud.png";
|
import oudImage from "@/assets/images/product-oud.png";
|
||||||
import roseImage from "@/assets/images/product-rose.png";
|
import roseImage from "@/assets/images/product-rose.png";
|
||||||
import sandalwoodImage from "@/assets/images/product-sandalwood.png";
|
import sandalwoodImage from "@/assets/images/product-sandalwood.png";
|
||||||
import "./products.css";
|
import "./products.css";
|
||||||
|
|
||||||
const PRODUCTS = [
|
const PRODUCTS = [
|
||||||
{ id: 1, name: "Lavender Dream", scent: "Calming French Lavender, Vanilla, & Bergamot", price: "$28", image: lavenderImage, category: "Floral" },
|
{ 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: 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: 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: 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: 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: 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: 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: 8, name: "Birthday Cake", scent: "Vanilla Buttercream, Sugar Spun, & Joy", price: "$28", image: birthdayImage, category: "Sweet" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function ProductsPage() {
|
export default function ProductsPage() {
|
||||||
const [filter, setFilter] = useState("All");
|
const [filter, setFilter] = useState("All");
|
||||||
const categories = ["All", ...Array.from(new Set(PRODUCTS.map((p) => p.category)))];
|
const categories = ["All", ...Array.from(new Set(PRODUCTS.map((p) => p.category)))];
|
||||||
const filteredProducts = filter === "All" ? PRODUCTS : PRODUCTS.filter((p) => p.category === filter);
|
const filteredProducts = filter === "All" ? PRODUCTS : PRODUCTS.filter((p) => p.category === filter);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{/* ── Hero ── */}
|
{/* ── Hero ── */}
|
||||||
<section className="products-hero">
|
<section className="products-hero">
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
>
|
>
|
||||||
<h1>Our Collection</h1>
|
<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>
|
<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" />
|
<div className="section-divider" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Products Body ── */}
|
{/* ── Products Body ── */}
|
||||||
<section className="products-body">
|
<section className="products-body">
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
{/* Filter Bar */}
|
{/* Filter Bar */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="filter-bar"
|
className="filter-bar"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ delay: 0.2 }}
|
transition={{ delay: 0.2 }}
|
||||||
>
|
>
|
||||||
{categories.map((cat) => (
|
{categories.map((cat) => (
|
||||||
<button
|
<button
|
||||||
key={cat}
|
key={cat}
|
||||||
className={`filter-btn${filter === cat ? " active" : ""}`}
|
className={`filter-btn${filter === cat ? " active" : ""}`}
|
||||||
onClick={() => setFilter(cat)}
|
onClick={() => setFilter(cat)}
|
||||||
>
|
>
|
||||||
{cat}
|
{cat}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Grid */}
|
{/* Grid */}
|
||||||
<div className="products-grid">
|
<div className="products-grid">
|
||||||
{filteredProducts.map((product, index) => (
|
{filteredProducts.map((product, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={product.id}
|
key={product.id}
|
||||||
className="prod-card"
|
className="prod-card"
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, margin: "-50px" }}
|
viewport={{ once: true, margin: "-50px" }}
|
||||||
transition={{ delay: (index % 4) * 0.1, duration: 0.6 }}
|
transition={{ delay: (index % 4) * 0.1, duration: 0.6 }}
|
||||||
>
|
>
|
||||||
<div className="prod-img-wrap">
|
<div className="prod-img-wrap">
|
||||||
<img src={product.image} alt={product.name} />
|
<img src={product.image} alt={product.name} />
|
||||||
<div className="prod-overlay">
|
<div className="prod-overlay">
|
||||||
<button className="prod-overlay-btn">Add to Cart</button>
|
<button className="prod-overlay-btn">Add to Cart</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="prod-info">
|
<div className="prod-info">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="prod-name">{product.name}</h3>
|
<h3 className="prod-name">{product.name}</h3>
|
||||||
<p className="prod-scent">{product.scent}</p>
|
<p className="prod-scent">{product.scent}</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="prod-price">{product.price}</p>
|
<p className="prod-price">{product.price}</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,229 +1,229 @@
|
||||||
/* ─── about.css ──────────────────────────────────────────── */
|
/* ─── about.css ──────────────────────────────────────────── */
|
||||||
|
|
||||||
/* HERO */
|
/* HERO */
|
||||||
.about-hero {
|
.about-hero {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-hero h1 {
|
.about-hero h1 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2.5rem, 6vw, 4.5rem);
|
font-size: clamp(2.5rem, 6vw, 4.5rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-hero p {
|
.about-hero p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
max-width: 680px;
|
max-width: 680px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BRAND VALUES */
|
/* BRAND VALUES */
|
||||||
.about-values {
|
.about-values {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
}
|
}
|
||||||
|
|
||||||
.values-grid {
|
.values-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.values-grid { grid-template-columns: 1fr 1fr 1fr; }
|
.values-grid { grid-template-columns: 1fr 1fr 1fr; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-card {
|
.value-card {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 2.5rem;
|
padding: 2.5rem;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
border: 1px solid rgba(213,190,240,0.5);
|
border: 1px solid rgba(213,190,240,0.5);
|
||||||
box-shadow: 0 2px 12px rgba(91,30,140,0.06);
|
box-shadow: 0 2px 12px rgba(91,30,140,0.06);
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-card:hover {
|
.value-card:hover {
|
||||||
box-shadow: 0 6px 24px rgba(91,30,140,0.12);
|
box-shadow: 0 6px 24px rgba(91,30,140,0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-icon {
|
.value-icon {
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-card h3 {
|
.value-card h3 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-card p {
|
.value-card p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MEET THE MAKER */
|
/* MEET THE MAKER */
|
||||||
.about-maker {
|
.about-maker {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-purple);
|
background: var(--brand-purple);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-gradient-overlay {
|
.maker-gradient-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: linear-gradient(to right, transparent, rgba(91,30,140,0.5));
|
background: linear-gradient(to right, transparent, rgba(91,30,140,0.5));
|
||||||
mix-blend-mode: overlay;
|
mix-blend-mode: overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-grid {
|
.maker-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 4rem;
|
gap: 4rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.maker-grid { grid-template-columns: 1fr 1fr; }
|
.maker-grid { grid-template-columns: 1fr 1fr; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-img-wrap {
|
.maker-img-wrap {
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-img-wrap img {
|
.maker-img-wrap img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-img-ring {
|
.maker-img-ring {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.2);
|
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-content h2 {
|
.maker-content h2 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2rem, 4vw, 3rem);
|
font-size: clamp(2rem, 4vw, 3rem);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-divider {
|
.maker-divider {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-content p {
|
.maker-content p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
color: rgba(255,255,255,0.88);
|
color: rgba(255,255,255,0.88);
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maker-signature {
|
.maker-signature {
|
||||||
font-family: var(--font-script);
|
font-family: var(--font-script);
|
||||||
font-size: 2.25rem;
|
font-size: 2.25rem;
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PROCESS TIMELINE */
|
/* PROCESS TIMELINE */
|
||||||
.about-process {
|
.about-process {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-process h2 {
|
.about-process h2 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2rem, 4vw, 2.8rem);
|
font-size: clamp(2rem, 4vw, 2.8rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-process .subtitle {
|
.about-process .subtitle {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 5rem;
|
margin-bottom: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline {
|
.timeline {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 3rem;
|
gap: 3rem;
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-item {
|
.timeline-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.timeline-item {
|
.timeline-item {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 3rem;
|
gap: 3rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-step {
|
.timeline-step {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 3.5rem;
|
font-size: 3.5rem;
|
||||||
color: rgba(212,162,76,0.3);
|
color: rgba(212,162,76,0.3);
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-body {
|
.timeline-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-bottom: 2rem;
|
padding-bottom: 2rem;
|
||||||
border-bottom: 1px solid rgba(213,190,240,0.5);
|
border-bottom: 1px solid rgba(213,190,240,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-body h3 {
|
.timeline-body h3 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-body p {
|
.timeline-body p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,316 +1,316 @@
|
||||||
/* ─── contact.css ────────────────────────────────────────── */
|
/* ─── contact.css ────────────────────────────────────────── */
|
||||||
|
|
||||||
/* HERO */
|
/* HERO */
|
||||||
.contact-hero {
|
.contact-hero {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-hero h1 {
|
.contact-hero h1 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2.5rem, 6vw, 4rem);
|
font-size: clamp(2.5rem, 6vw, 4rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-hero p {
|
.contact-hero p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
margin: 0 auto 2rem;
|
margin: 0 auto 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CONTACT BODY */
|
/* CONTACT BODY */
|
||||||
.contact-body {
|
.contact-body {
|
||||||
padding: 5rem 1.5rem;
|
padding: 5rem 1.5rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-grid {
|
.contact-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 4rem;
|
gap: 4rem;
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.contact-grid { grid-template-columns: 1fr 1fr; }
|
.contact-grid { grid-template-columns: 1fr 1fr; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* INFO PANEL */
|
/* INFO PANEL */
|
||||||
.contact-info-panel {
|
.contact-info-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 2.5rem;
|
gap: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-panel h2 {
|
.contact-info-panel h2 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.9rem;
|
font-size: 1.9rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-list {
|
.contact-info-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-item {
|
.contact-info-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-icon {
|
.contact-info-icon {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: rgba(220,198,242,0.5);
|
background: rgba(220,198,242,0.5);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-label {
|
.contact-info-label {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.65rem;
|
font-size: 0.65rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-value {
|
.contact-info-value {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-value a {
|
.contact-info-value a {
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info-value a:hover {
|
.contact-info-value a:hover {
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SOCIAL BOX */
|
/* SOCIAL BOX */
|
||||||
.contact-social-box {
|
.contact-social-box {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
border: 1px solid var(--brand-border);
|
border: 1px solid var(--brand-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-social-box h3 {
|
.contact-social-box h3 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-social-box p {
|
.contact-social-box p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-ig-link {
|
.contact-ig-link {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-ig-link:hover {
|
.contact-ig-link:hover {
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FORM PANEL */
|
/* FORM PANEL */
|
||||||
.contact-form-panel {
|
.contact-form-panel {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
padding: 3rem;
|
padding: 3rem;
|
||||||
box-shadow: 0 12px 40px rgba(91,30,140,0.1);
|
box-shadow: 0 12px 40px rgba(91,30,140,0.1);
|
||||||
border: 1px solid rgba(213,190,240,0.4);
|
border: 1px solid rgba(213,190,240,0.4);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-form-top-bar {
|
.contact-form-top-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background: linear-gradient(to right, var(--brand-purple), var(--brand-gold), var(--brand-purple));
|
background: linear-gradient(to right, var(--brand-purple), var(--brand-gold), var(--brand-purple));
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-form-panel h2 {
|
.contact-form-panel h2 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.9rem;
|
font-size: 1.9rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.form-row { grid-template-columns: 1fr 1fr; }
|
.form-row { grid-template-columns: 1fr 1fr; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field {
|
.form-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-label {
|
.form-label {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.65rem;
|
font-size: 0.65rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input,
|
.form-input,
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.85rem 1rem;
|
padding: 0.85rem 1rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
border: 1px solid var(--brand-border);
|
border: 1px solid var(--brand-border);
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
color: var(--brand-text);
|
color: var(--brand-text);
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input:focus,
|
.form-input:focus,
|
||||||
.form-textarea:focus {
|
.form-textarea:focus {
|
||||||
border-color: var(--brand-gold);
|
border-color: var(--brand-gold);
|
||||||
box-shadow: 0 0 0 2px rgba(212,162,76,0.15);
|
box-shadow: 0 0 0 2px rgba(212,162,76,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
resize: none;
|
resize: none;
|
||||||
height: 140px;
|
height: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-submit {
|
.form-submit {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background: var(--brand-purple);
|
background: var(--brand-purple);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.3s ease;
|
transition: background 0.3s ease;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-submit:hover:not(:disabled) {
|
.form-submit:hover:not(:disabled) {
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-submit:disabled {
|
.form-submit:disabled {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SUCCESS STATE */
|
/* SUCCESS STATE */
|
||||||
.contact-success {
|
.contact-success {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 4rem 0;
|
padding: 4rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-icon-wrap {
|
.success-icon-wrap {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #f0fdf4;
|
background: #f0fdf4;
|
||||||
color: #16a34a;
|
color: #16a34a;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-success h3 {
|
.contact-success h3 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-success p {
|
.contact-success p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.send-another-btn {
|
.send-another-btn {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 1px solid var(--brand-gold);
|
border-bottom: 1px solid var(--brand-gold);
|
||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.send-another-btn:hover {
|
.send-another-btn:hover {
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
border-color: var(--brand-purple);
|
border-color: var(--brand-purple);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CLOSING QUOTE */
|
/* CLOSING QUOTE */
|
||||||
.contact-quote {
|
.contact-quote {
|
||||||
padding: 4rem 1.5rem;
|
padding: 4rem 1.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-quote p {
|
.contact-quote p {
|
||||||
font-family: var(--font-script);
|
font-family: var(--font-script);
|
||||||
font-size: clamp(1.5rem, 3vw, 2rem);
|
font-size: clamp(1.5rem, 3vw, 2rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,386 +1,386 @@
|
||||||
/* ─── home.css ───────────────────────────────────────────── */
|
/* ─── home.css ───────────────────────────────────────────── */
|
||||||
|
|
||||||
/* HERO */
|
/* HERO */
|
||||||
.home-hero {
|
.home-hero {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 90vh;
|
min-height: 90vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 5rem 1.5rem;
|
padding: 5rem 1.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero-bg {
|
.home-hero-bg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: linear-gradient(to bottom, rgba(220,198,242,0.4), var(--brand-light-lavender));
|
background: linear-gradient(to bottom, rgba(220,198,242,0.4), var(--brand-light-lavender));
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero-logo-wrap {
|
.home-hero-logo-wrap {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
height: 140px;
|
height: 140px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 4px solid rgba(212,162,76,0.35);
|
border: 4px solid rgba(212,162,76,0.35);
|
||||||
box-shadow: 0 0 40px rgba(212,162,76,0.25);
|
box-shadow: 0 0 40px rgba(212,162,76,0.25);
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 auto 2rem;
|
margin: 0 auto 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero-flame {
|
.home-hero-flame {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -12px;
|
top: -12px;
|
||||||
right: -12px;
|
right: -12px;
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero h1 {
|
.home-hero h1 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2.5rem, 7vw, 5.5rem);
|
font-size: clamp(2.5rem, 7vw, 5.5rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
drop-shadow: 0 2px 8px rgba(91,30,140,0.15);
|
drop-shadow: 0 2px 8px rgba(91,30,140,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero-tagline {
|
.home-hero-tagline {
|
||||||
font-family: var(--font-script);
|
font-family: var(--font-script);
|
||||||
font-size: clamp(1.75rem, 4vw, 3rem);
|
font-size: clamp(1.75rem, 4vw, 3rem);
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-cta {
|
.hero-cta {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 1rem 2.5rem;
|
padding: 1rem 2.5rem;
|
||||||
background: var(--brand-purple);
|
background: var(--brand-purple);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: background 0.4s ease, border-color 0.4s ease, color 0.4s ease, box-shadow 0.4s ease;
|
transition: background 0.4s ease, border-color 0.4s ease, color 0.4s ease, box-shadow 0.4s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-cta:hover {
|
.hero-cta:hover {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
border-color: var(--brand-gold);
|
border-color: var(--brand-gold);
|
||||||
box-shadow: 0 0 20px rgba(212,162,76,0.35);
|
box-shadow: 0 0 20px rgba(212,162,76,0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PHILOSOPHY */
|
/* PHILOSOPHY */
|
||||||
.home-philosophy {
|
.home-philosophy {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-philosophy h2 {
|
.home-philosophy h2 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2rem, 4vw, 3rem);
|
font-size: clamp(2rem, 4vw, 3rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-divider {
|
.section-divider {
|
||||||
width: 96px;
|
width: 96px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
margin: 0 auto 4rem;
|
margin: 0 auto 4rem;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.philosophy-grid {
|
.philosophy-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 2.5rem;
|
gap: 2.5rem;
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.philosophy-grid { grid-template-columns: 1fr 1fr 1fr; }
|
.philosophy-grid { grid-template-columns: 1fr 1fr 1fr; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.philosophy-card {
|
.philosophy-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 2.5rem;
|
padding: 2.5rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
transition: background 0.4s ease;
|
transition: background 0.4s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.philosophy-card:hover {
|
.philosophy-card:hover {
|
||||||
background: rgba(220,198,242,0.3);
|
background: rgba(220,198,242,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.philosophy-icon-wrap {
|
.philosophy-icon-wrap {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: rgba(220,198,242,0.5);
|
background: rgba(220,198,242,0.5);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.philosophy-card h3 {
|
.philosophy-card h3 {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.1em;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.philosophy-card p {
|
.philosophy-card p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FEATURED PRODUCTS */
|
/* FEATURED PRODUCTS */
|
||||||
.home-products {
|
.home-products {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-products-header {
|
.home-products-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
margin-bottom: 4rem;
|
margin-bottom: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.home-products-header {
|
.home-products-header {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-products h2 {
|
.home-products h2 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2rem, 4vw, 3rem);
|
font-size: clamp(2rem, 4vw, 3rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-all-link {
|
.view-all-link {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
transition: color 0.3s ease, border-color 0.3s ease;
|
transition: color 0.3s ease, border-color 0.3s ease;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-all-link:hover {
|
.view-all-link:hover {
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
border-color: var(--brand-purple);
|
border-color: var(--brand-purple);
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-grid {
|
.product-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) { .product-grid { grid-template-columns: 1fr 1fr; } }
|
@media (min-width: 600px) { .product-grid { grid-template-columns: 1fr 1fr; } }
|
||||||
@media (min-width: 1024px) { .product-grid { grid-template-columns: repeat(4, 1fr); } }
|
@media (min-width: 1024px) { .product-grid { grid-template-columns: repeat(4, 1fr); } }
|
||||||
|
|
||||||
.product-card {
|
.product-card {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-img-wrap {
|
.product-img-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-img-wrap img {
|
.product-img-wrap img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
transition: transform 0.7s ease;
|
transition: transform 0.7s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card:hover .product-img-wrap img {
|
.product-card:hover .product-img-wrap img {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-overlay {
|
.product-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(91,30,140,0.22);
|
background: rgba(91,30,140,0.22);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
backdrop-filter: blur(2px);
|
backdrop-filter: blur(2px);
|
||||||
transition: opacity 0.5s ease;
|
transition: opacity 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card:hover .product-overlay {
|
.product-card:hover .product-overlay {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-overlay-btn {
|
.product-overlay-btn {
|
||||||
padding: 0.75rem 1.5rem;
|
padding: 0.75rem 1.5rem;
|
||||||
background: rgba(239,228,250,0.92);
|
background: rgba(239,228,250,0.92);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.18em;
|
letter-spacing: 0.18em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transform: translateY(16px);
|
transform: translateY(16px);
|
||||||
transition: transform 0.5s ease, background 0.3s ease;
|
transition: transform 0.5s ease, background 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card:hover .product-overlay-btn {
|
.product-card:hover .product-overlay-btn {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-name {
|
.product-name {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-price {
|
.product-price {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* QUOTE BANNER */
|
/* QUOTE BANNER */
|
||||||
.home-quote {
|
.home-quote {
|
||||||
padding: 8rem 1.5rem;
|
padding: 8rem 1.5rem;
|
||||||
background: var(--brand-purple);
|
background: var(--brand-purple);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-quote-texture {
|
.home-quote-texture {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background-image: url('https://www.transparenttextures.com/patterns/stardust.png');
|
background-image: url('https://www.transparenttextures.com/patterns/stardust.png');
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-quote blockquote {
|
.home-quote blockquote {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: clamp(1.4rem, 3.5vw, 2.5rem);
|
font-size: clamp(1.4rem, 3.5vw, 2.5rem);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: 1.5rem auto 2rem;
|
margin: 1.5rem auto 2rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-quote-divider {
|
.home-quote-divider {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NEWSLETTER */
|
/* NEWSLETTER */
|
||||||
.home-newsletter {
|
.home-newsletter {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-newsletter h2 {
|
.home-newsletter h2 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(1.8rem, 3.5vw, 2.5rem);
|
font-size: clamp(1.8rem, 3.5vw, 2.5rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-newsletter p {
|
.home-newsletter p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
max-width: 520px;
|
max-width: 520px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsletter-form {
|
.newsletter-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
max-width: 440px;
|
max-width: 440px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 480px) {
|
@media (min-width: 480px) {
|
||||||
.newsletter-form { flex-direction: row; }
|
.newsletter-form { flex-direction: row; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsletter-input {
|
.newsletter-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0.85rem 1.25rem;
|
padding: 0.85rem 1.25rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
border: 1px solid var(--brand-border);
|
border: 1px solid var(--brand-border);
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color 0.3s ease;
|
transition: border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsletter-input:focus {
|
.newsletter-input:focus {
|
||||||
border-color: var(--brand-gold);
|
border-color: var(--brand-gold);
|
||||||
box-shadow: 0 0 0 2px rgba(212,162,76,0.15);
|
box-shadow: 0 0 0 2px rgba(212,162,76,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsletter-btn {
|
.newsletter-btn {
|
||||||
padding: 0.85rem 2rem;
|
padding: 0.85rem 2rem;
|
||||||
background: var(--brand-purple);
|
background: var(--brand-purple);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.3s ease;
|
transition: background 0.3s ease;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsletter-btn:hover {
|
.newsletter-btn:hover {
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,147 +1,147 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Link } from "wouter";
|
import { Link } from "wouter";
|
||||||
import { Flame, Heart, Leaf, Sparkles } from "lucide-react";
|
import { Flame, Heart, Leaf, Sparkles } from "lucide-react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import lavenderImage from "@/assets/images/product-lavender.png";
|
import lavenderImage from "@/assets/images/product-lavender.png";
|
||||||
import roseImage from "@/assets/images/product-rose.png";
|
import roseImage from "@/assets/images/product-rose.png";
|
||||||
import eucalyptusImage from "@/assets/images/product-eucalyptus.png";
|
import eucalyptusImage from "@/assets/images/product-eucalyptus.png";
|
||||||
import oudImage from "@/assets/images/product-oud.png";
|
import oudImage from "@/assets/images/product-oud.png";
|
||||||
import "./home.css";
|
import "./home.css";
|
||||||
|
|
||||||
const logoPath = "/opengraph.jpg";
|
const logoPath = "/opengraph.jpg";
|
||||||
|
|
||||||
const fadeInUp = {
|
const fadeInUp = {
|
||||||
hidden: { opacity: 0, y: 40 },
|
hidden: { opacity: 0, y: 40 },
|
||||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
const stagger = {
|
const stagger = {
|
||||||
visible: { transition: { staggerChildren: 0.2 } },
|
visible: { transition: { staggerChildren: 0.2 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{/* ── Hero ── */}
|
{/* ── Hero ── */}
|
||||||
<section className="home-hero">
|
<section className="home-hero">
|
||||||
<div className="home-hero-bg" />
|
<div className="home-hero-bg" />
|
||||||
<motion.div initial="hidden" animate="visible" variants={stagger}>
|
<motion.div initial="hidden" animate="visible" variants={stagger}>
|
||||||
<motion.div variants={fadeInUp} style={{ position: "relative" }}>
|
<motion.div variants={fadeInUp} style={{ position: "relative" }}>
|
||||||
<div className="home-hero-logo-wrap">
|
<div className="home-hero-logo-wrap">
|
||||||
<img src={logoPath} alt="Mani Candles Co." style={{ width: "100%", height: "100%", objectFit: "cover" }} />
|
<img src={logoPath} alt="Mani Candles Co." style={{ width: "100%", height: "100%", objectFit: "cover" }} />
|
||||||
</div>
|
</div>
|
||||||
<div className="home-hero-flame animate-flame">
|
<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))" }} />
|
<Flame size={40} fill="currentColor" style={{ filter: "drop-shadow(0 0 12px rgba(255,211,107,0.8))" }} />
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.h1 variants={fadeInUp}>Mani Candles Co.</motion.h1>
|
<motion.h1 variants={fadeInUp}>Mani Candles Co.</motion.h1>
|
||||||
|
|
||||||
<motion.span variants={fadeInUp} className="home-hero-tagline">
|
<motion.span variants={fadeInUp} className="home-hero-tagline">
|
||||||
Handmade • Natural • Heartmade
|
Handmade • Natural • Heartmade
|
||||||
</motion.span>
|
</motion.span>
|
||||||
|
|
||||||
<motion.div variants={fadeInUp}>
|
<motion.div variants={fadeInUp}>
|
||||||
<Link href="/products" className="hero-cta">Shop the Collection</Link>
|
<Link href="/products" className="hero-cta">Shop the Collection</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Philosophy ── */}
|
{/* ── Philosophy ── */}
|
||||||
<section className="home-philosophy">
|
<section className="home-philosophy">
|
||||||
<Container maxWidth="lg">
|
<Container maxWidth="lg">
|
||||||
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true, margin: "-100px" }} variants={stagger}>
|
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true, margin: "-100px" }} variants={stagger}>
|
||||||
<motion.h2 variants={fadeInUp}>The Mani Philosophy</motion.h2>
|
<motion.h2 variants={fadeInUp}>The Mani Philosophy</motion.h2>
|
||||||
<div className="section-divider" />
|
<div className="section-divider" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="philosophy-grid">
|
<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: 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: 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." },
|
{ 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) => (
|
].map(({ Icon, title, desc }, i) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={title}
|
key={title}
|
||||||
className="philosophy-card"
|
className="philosophy-card"
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
whileInView="visible"
|
whileInView="visible"
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
variants={{ hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.2, duration: 0.6 } } }}
|
variants={{ hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.2, duration: 0.6 } } }}
|
||||||
>
|
>
|
||||||
<div className="philosophy-icon-wrap"><Icon size={32} /></div>
|
<div className="philosophy-icon-wrap"><Icon size={32} /></div>
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<p>{desc}</p>
|
<p>{desc}</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Featured Products ── */}
|
{/* ── Featured Products ── */}
|
||||||
<section className="home-products">
|
<section className="home-products">
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp} className="home-products-header">
|
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp} className="home-products-header">
|
||||||
<div>
|
<div>
|
||||||
<h2>Curated Favorites</h2>
|
<h2>Curated Favorites</h2>
|
||||||
<div className="section-divider" style={{ margin: "0.5rem 0 0", width: 80 }} />
|
<div className="section-divider" style={{ margin: "0.5rem 0 0", width: 80 }} />
|
||||||
</div>
|
</div>
|
||||||
<Link href="/products" className="view-all-link">View All Candles</Link>
|
<Link href="/products" className="view-all-link">View All Candles</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="product-grid">
|
<div className="product-grid">
|
||||||
{[
|
{[
|
||||||
{ id: 1, name: "Lavender Dream", image: lavenderImage, price: "$28" },
|
{ id: 1, name: "Lavender Dream", image: lavenderImage, price: "$28" },
|
||||||
{ id: 2, name: "Rose & Vanilla", image: roseImage, price: "$32" },
|
{ id: 2, name: "Rose & Vanilla", image: roseImage, price: "$32" },
|
||||||
{ id: 3, name: "Eucalyptus Mint", image: eucalyptusImage, price: "$28" },
|
{ id: 3, name: "Eucalyptus Mint", image: eucalyptusImage, price: "$28" },
|
||||||
{ id: 4, name: "Oud & Amber", image: oudImage, price: "$36" },
|
{ id: 4, name: "Oud & Amber", image: oudImage, price: "$36" },
|
||||||
].map((product, i) => (
|
].map((product, i) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={product.id}
|
key={product.id}
|
||||||
className="product-card"
|
className="product-card"
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
whileInView="visible"
|
whileInView="visible"
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
variants={{ hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.1, duration: 0.6 } } }}
|
variants={{ hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { delay: i * 0.1, duration: 0.6 } } }}
|
||||||
>
|
>
|
||||||
<div className="product-img-wrap">
|
<div className="product-img-wrap">
|
||||||
<img src={product.image} alt={product.name} />
|
<img src={product.image} alt={product.name} />
|
||||||
<div className="product-overlay">
|
<div className="product-overlay">
|
||||||
<button className="product-overlay-btn">Quick Add</button>
|
<button className="product-overlay-btn">Quick Add</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="product-name">{product.name}</h3>
|
<h3 className="product-name">{product.name}</h3>
|
||||||
<p className="product-price">{product.price}</p>
|
<p className="product-price">{product.price}</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Quote ── */}
|
{/* ── Quote ── */}
|
||||||
<section className="home-quote">
|
<section className="home-quote">
|
||||||
<div className="home-quote-texture" />
|
<div className="home-quote-texture" />
|
||||||
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp}>
|
<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" }} />
|
<Flame className="animate-flame" size={48} style={{ color: "var(--brand-gold)", margin: "0 auto 1.5rem", display: "block" }} />
|
||||||
<blockquote>
|
<blockquote>
|
||||||
"A candle loses nothing by lighting another candle. It only makes the room warmer, brighter, and more beautiful."
|
"A candle loses nothing by lighting another candle. It only makes the room warmer, brighter, and more beautiful."
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<div className="home-quote-divider" />
|
<div className="home-quote-divider" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Newsletter ── */}
|
{/* ── Newsletter ── */}
|
||||||
<section className="home-newsletter">
|
<section className="home-newsletter">
|
||||||
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp}>
|
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }} variants={fadeInUp}>
|
||||||
<h2>Join Our Sanctuary</h2>
|
<h2>Join Our Sanctuary</h2>
|
||||||
<p>Subscribe to receive gentle updates on new collections, exclusive access, and a little light in your inbox.</p>
|
<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()}>
|
<form className="newsletter-form" onSubmit={(e) => e.preventDefault()}>
|
||||||
<input type="email" className="newsletter-input" placeholder="Your email address" required />
|
<input type="email" className="newsletter-input" placeholder="Your email address" required />
|
||||||
<button type="submit" className="newsletter-btn">Subscribe</button>
|
<button type="submit" className="newsletter-btn">Subscribe</button>
|
||||||
</form>
|
</form>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</section>
|
</section>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,173 +1,173 @@
|
||||||
/* ─── products.css ───────────────────────────────────────── */
|
/* ─── products.css ───────────────────────────────────────── */
|
||||||
|
|
||||||
/* HERO */
|
/* HERO */
|
||||||
.products-hero {
|
.products-hero {
|
||||||
padding: 6rem 1.5rem;
|
padding: 6rem 1.5rem;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.products-hero h1 {
|
.products-hero h1 {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: clamp(2.5rem, 6vw, 4rem);
|
font-size: clamp(2.5rem, 6vw, 4rem);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.products-hero p {
|
.products-hero p {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
margin: 0 auto 2rem;
|
margin: 0 auto 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FILTERS */
|
/* FILTERS */
|
||||||
.products-body {
|
.products-body {
|
||||||
padding: 5rem 1.5rem;
|
padding: 5rem 1.5rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-bar {
|
.filter-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin-bottom: 4rem;
|
margin-bottom: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-btn {
|
.filter-btn {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
padding: 0.5rem 1.5rem;
|
padding: 0.5rem 1.5rem;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 0.3s ease, border-color 0.3s ease;
|
transition: color 0.3s ease, border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-btn:hover {
|
.filter-btn:hover {
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-btn.active {
|
.filter-btn.active {
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
border-bottom-color: var(--brand-gold);
|
border-bottom-color: var(--brand-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PRODUCT GRID */
|
/* PRODUCT GRID */
|
||||||
.products-grid {
|
.products-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 3rem 2rem;
|
gap: 3rem 2rem;
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) { .products-grid { grid-template-columns: 1fr 1fr; } }
|
@media (min-width: 640px) { .products-grid { grid-template-columns: 1fr 1fr; } }
|
||||||
@media (min-width: 1024px) { .products-grid { grid-template-columns: repeat(4, 1fr); } }
|
@media (min-width: 1024px) { .products-grid { grid-template-columns: repeat(4, 1fr); } }
|
||||||
|
|
||||||
.prod-card {
|
.prod-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-img-wrap {
|
.prod-img-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
aspect-ratio: 4 / 5;
|
aspect-ratio: 4 / 5;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--brand-card);
|
background: var(--brand-card);
|
||||||
border: 1px solid var(--brand-border);
|
border: 1px solid var(--brand-border);
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-img-wrap img {
|
.prod-img-wrap img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
transition: transform 1s ease;
|
transition: transform 1s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-card:hover .prod-img-wrap img {
|
.prod-card:hover .prod-img-wrap img {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-overlay {
|
.prod-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(91,30,140,0.3);
|
background: rgba(91,30,140,0.3);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
backdrop-filter: blur(2px);
|
backdrop-filter: blur(2px);
|
||||||
transition: opacity 0.5s ease;
|
transition: opacity 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-card:hover .prod-overlay {
|
.prod-card:hover .prod-overlay {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-overlay-btn {
|
.prod-overlay-btn {
|
||||||
padding: 0.75rem 2rem;
|
padding: 0.75rem 2rem;
|
||||||
background: var(--brand-light-lavender);
|
background: var(--brand-light-lavender);
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.18em;
|
letter-spacing: 0.18em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transform: translateY(32px);
|
transform: translateY(32px);
|
||||||
transition: transform 0.5s ease, background 0.3s ease, color 0.3s ease;
|
transition: transform 0.5s ease, background 0.3s ease, color 0.3s ease;
|
||||||
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-card:hover .prod-overlay-btn {
|
.prod-card:hover .prod-overlay-btn {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-overlay-btn:hover {
|
.prod-overlay-btn:hover {
|
||||||
background: var(--brand-gold);
|
background: var(--brand-gold);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-info {
|
.prod-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-name {
|
.prod-name {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
color: var(--brand-purple);
|
color: var(--brand-purple);
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-scent {
|
.prod-scent {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
color: var(--brand-muted);
|
color: var(--brand-muted);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
/* two-line clamp */
|
/* two-line clamp */
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prod-price {
|
.prod-price {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
color: var(--brand-gold);
|
color: var(--brand-gold);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
126
src/theme.ts
126
src/theme.ts
|
|
@ -1,63 +1,63 @@
|
||||||
import { createTheme } from "@mui/material/styles";
|
import { createTheme } from "@mui/material/styles";
|
||||||
|
|
||||||
const theme = createTheme({
|
const theme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
mode: "light",
|
mode: "light",
|
||||||
primary: {
|
primary: {
|
||||||
main: "#5B1E8C", // deep brand purple
|
main: "#5B1E8C", // deep brand purple
|
||||||
light: "#DCC6F2", // lavender
|
light: "#DCC6F2", // lavender
|
||||||
dark: "#3A1260",
|
dark: "#3A1260",
|
||||||
contrastText: "#ffffff",
|
contrastText: "#ffffff",
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: "#D4A24C", // brand gold / accent
|
main: "#D4A24C", // brand gold / accent
|
||||||
light: "#FFD36B",
|
light: "#FFD36B",
|
||||||
dark: "#A07830",
|
dark: "#A07830",
|
||||||
contrastText: "#ffffff",
|
contrastText: "#ffffff",
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
default: "#EFE4FA", // light lavender bg
|
default: "#EFE4FA", // light lavender bg
|
||||||
paper: "#F8F2FD", // card bg
|
paper: "#F8F2FD", // card bg
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
primary: "#2D0D52",
|
primary: "#2D0D52",
|
||||||
secondary: "#7B5A9E",
|
secondary: "#7B5A9E",
|
||||||
},
|
},
|
||||||
divider: "#D5BEF0",
|
divider: "#D5BEF0",
|
||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
fontFamily: "'Inter', sans-serif",
|
fontFamily: "'Inter', sans-serif",
|
||||||
h1: { fontFamily: "'Cormorant Garamond', serif" },
|
h1: { fontFamily: "'Cormorant Garamond', serif" },
|
||||||
h2: { fontFamily: "'Cormorant Garamond', serif" },
|
h2: { fontFamily: "'Cormorant Garamond', serif" },
|
||||||
h3: { fontFamily: "'Cormorant Garamond', serif" },
|
h3: { fontFamily: "'Cormorant Garamond', serif" },
|
||||||
h4: { fontFamily: "'Cormorant Garamond', serif" },
|
h4: { fontFamily: "'Cormorant Garamond', serif" },
|
||||||
h5: { fontFamily: "'Cormorant Garamond', serif" },
|
h5: { fontFamily: "'Cormorant Garamond', serif" },
|
||||||
h6: { fontFamily: "'Cormorant Garamond', serif" },
|
h6: { fontFamily: "'Cormorant Garamond', serif" },
|
||||||
subtitle1: { fontFamily: "'Cinzel', serif" },
|
subtitle1: { fontFamily: "'Cinzel', serif" },
|
||||||
subtitle2: { fontFamily: "'Cinzel', serif" },
|
subtitle2: { fontFamily: "'Cinzel', serif" },
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
MuiButton: {
|
MuiButton: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
fontFamily: "'Cinzel', serif",
|
fontFamily: "'Cinzel', serif",
|
||||||
letterSpacing: "0.15em",
|
letterSpacing: "0.15em",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiAppBar: {
|
MuiAppBar: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
boxShadow: "none",
|
boxShadow: "none",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
const projectRoot = fileURLToPath(new URL(".", import.meta.url));
|
const projectRoot = fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(projectRoot, "src"),
|
"@": path.resolve(projectRoot, "src"),
|
||||||
},
|
},
|
||||||
dedupe: ["react", "react-dom"],
|
dedupe: ["react", "react-dom"],
|
||||||
},
|
},
|
||||||
root: projectRoot,
|
root: projectRoot,
|
||||||
build: {
|
build: {
|
||||||
outDir: path.resolve(projectRoot, "dist"),
|
outDir: path.resolve(projectRoot, "dist"),
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
port: 4173,
|
port: 4173,
|
||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue