Performance improvements and Dockerfile update

This commit is contained in:
2026-04-19 09:48:53 +00:00
parent 31f88749f9
commit e94dec0036
17 changed files with 315 additions and 295 deletions

1
.gitignore vendored
View File

@ -19,6 +19,7 @@
# production # production
/build /build
.output/
# misc # misc
.DS_Store .DS_Store

View File

@ -1,8 +1,6 @@
FROM node:22-alpine AS base FROM node:22-alpine AS base
ARG NEXT_PUBLIC_INSTAGRAM_ACCESS_TOKEN # Stage 1: Install dependencies
# Install dependencies only when needed
FROM base AS deps FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat RUN apk add --no-cache libc6-compat
@ -18,54 +16,47 @@ RUN \
fi fi
# Rebuild the source code only when needed # Stage 2: Rebuild the source code only when needed
FROM base AS builder FROM base AS builder
WORKDIR /app WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
# Next.js collects completely anonymous telemetry data about general usage. # Environment variables needed during build time
# Learn more here: https://nextjs.org/telemetry ARG INSTAGRAM_ACCESS_TOKEN
# Uncomment the following line in case you want to disable telemetry during the build. ENV INSTAGRAM_ACCESS_TOKEN=$INSTAGRAM_ACCESS_TOKEN
ENV NEXT_TELEMETRY_DISABLED=1
RUN \ RUN \
if [ -f yarn.lock ]; then yarn run build; \ if [ -f yarn.lock ]; then yarn build; \
elif [ -f package-lock.json ]; then npm run build; \ elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \ else npm run build; \
fi fi
# Production image, copy all the files and run next
# Stage 3: Production image, copy all the files and run nitro
FROM base AS runner FROM base AS runner
WORKDIR /app WORKDIR /app
ENV NODE_ENV=production ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime. # Set host to 0.0.0.0 to ensure it's accessible outside the container
ENV NEXT_TELEMETRY_DISABLED=1 ENV HOST=0.0.0.0
ENV PORT=3000
RUN addgroup --system --gid 1001 nodejs RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs RUN adduser --system --uid 1001 nitrojs
COPY --from=builder /app/public ./public # Copy the build output from the builder stage
# TanStack Start/Nitro builds into the .output directory
COPY --from=builder --chown=nitrojs:nodejs /app/.output ./.output
# Set the correct permission for prerender cache USER nitrojs
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000 EXPOSE 3000
ENV PORT=3000 # Health check to ensure the server is responding
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
# server.js is created by next build from the standalone output # server.mjs is created by nitro build
# https://nextjs.org/docs/pages/api-reference/next-config-js/output CMD ["node", ".output/server/index.mjs"]
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

View File

@ -4,5 +4,26 @@ build-production: ## Build the production docker image.
--platform linux/amd64,linux/arm64/v8 \ --platform linux/amd64,linux/arm64/v8 \
--tag registry.tanshu.com/mozimo:latest \ --tag registry.tanshu.com/mozimo:latest \
$(if $(TAG),--tag registry.tanshu.com/mozimo:$(TAG)) \ $(if $(TAG),--tag registry.tanshu.com/mozimo:$(TAG)) \
--pull \
--push \ --push \
git@github.com:tanshu/mozimo.git git@github.com:tanshu/mozimo.git
.PHONY: build-check
build-check: ## Multi-arch build without push (compile check)
@docker buildx build \
--platform linux/amd64,linux/arm64/v8 \
--tag mozimo:test \
--pull \
--progress=plain \
git@git.tanshu.com:tanshu/mozimo.git
.PHONY: build-check-local
build-check-local: ## Multi-arch build without push (compile check)
@git archive --format=tar HEAD | docker buildx build \
--platform linux/amd64 \
--tag barker:test \
--pull \
--progress=plain \
--load \
-

View File

@ -16,7 +16,7 @@ docker_image: "{{ registry }}/{{ title }}:{{ tag }}"
docker_container: "{{ title }}" docker_container: "{{ title }}"
docker_port: 3000 docker_port: 3000
instagram_token: IGQWRQSUY4b3RQWU9ZARHlVVUFDaWxJZAWFOUVllM0NxMk1zSHJ5X2JGc2dpRUxyTjBaeDNhUm0yaFZAQeUotVU9VUmFEQkJxU25CdFp3bURSZAGtnLXhCZAHVId21SWUNiNjVzc0pjZAlhPNDRxTDFzZAEQ0ZAXoyVlpUaHcZD instagram_token: IGQWRhYzlibzE4VEpNYzZA2eHEtRkVLUXJiT3NyVU9mNjlNRzM1cmd2NzY0SGxtX1ZAVVGVwb1dkbFl4VlVWaW02aFhEakQzeUp3NjBCSktRR2lQYlgwV3UwMG1seTFsWDBMSzZAHejNYdlE5RFMzU0tqbS1OVEttN0UZD
caddy_container: caddy caddy_container: caddy

View File

@ -9,8 +9,6 @@ const compat = new FlatCompat({
baseDirectory: __dirname, baseDirectory: __dirname,
}); });
const eslintConfig = [ const eslintConfig = [{}];
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig; export default eslintConfig;

View File

@ -1,6 +1,9 @@
import { createRootRoute, Outlet, HeadContent, Scripts } from '@tanstack/react-router'; import { createRootRoute, Outlet, HeadContent, Scripts } from '@tanstack/react-router';
import { LazyMotion } from 'framer-motion';
import appCss from './globals.css?url'; import appCss from './globals.css?url';
const loadFeatures = () => import('framer-motion').then(res => res.domAnimation);
export const Route = createRootRoute({ export const Route = createRootRoute({
head: () => ({ head: () => ({
meta: [ meta: [
@ -68,7 +71,9 @@ function RootLayout() {
Since the Inter Google Font is loaded via the stylesheet above, it will automatically apply Since the Inter Google Font is loaded via the stylesheet above, it will automatically apply
if configured in Tailwind CSS. */} if configured in Tailwind CSS. */}
<body> <body>
<Outlet /> <LazyMotion features={loadFeatures} strict>
<Outlet />
</LazyMotion>
<Scripts /> <Scripts />
</body> </body>
</html> </html>

View File

@ -1,9 +1,11 @@
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { motion } from "framer-motion"; import { m } from "framer-motion";
import Header from "@/components/Header"; import Header from "@/components/Header";
import Footer from "@/components/Footer"; // Footer is lazy loaded
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
import { lazy, Suspense } from "react";
const Footer = lazy(() => import("@/components/Footer"));
export const Route = createFileRoute('/about')({ export const Route = createFileRoute('/about')({
component: AboutPage, component: AboutPage,
}); });
@ -43,14 +45,14 @@ function AboutPage() {
{/* Hero Section */} {/* Hero Section */}
<section className="pt-32 md:pt-40 pb-16 md:pb-24"> <section className="pt-32 md:pt-40 pb-16 md:pb-24">
<div className="w-full"> <div className="w-full">
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp} variants={fadeInUp}
className="text-center space-y-8" className="text-center space-y-8"
> >
<motion.h1 <m.h1
className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#3C2A21] font-samantha" className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#3C2A21] font-samantha"
style={{ style={{
fontFamily: "MonetaSans-Regular", fontFamily: "MonetaSans-Regular",
@ -64,9 +66,9 @@ function AboutPage() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Welcome to the World of Mozimo Magic Welcome to the World of Mozimo Magic
</motion.h1> </m.h1>
<motion.div <m.div
className="w-full px-8 md:px-16 lg:px-24" className="w-full px-8 md:px-16 lg:px-24"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -85,17 +87,17 @@ function AboutPage() {
> >
Indulge in the Luxurious. Immerse in the Captivating. Savor the Fresh. At Mozimo, we create a chocolate experience that transports you to a world of opulence, with mesmerizing aromas and visuals, and delights your taste buds with the vibrant flavors of freshly crafted chocolates. Join us on a journey of pure luxury, captivating moments, and a fresh perspective on the art of chocolate-making. Indulge in the Luxurious. Immerse in the Captivating. Savor the Fresh. At Mozimo, we create a chocolate experience that transports you to a world of opulence, with mesmerizing aromas and visuals, and delights your taste buds with the vibrant flavors of freshly crafted chocolates. Join us on a journey of pure luxury, captivating moments, and a fresh perspective on the art of chocolate-making.
</p> </p>
</motion.div> </m.div>
{/* Hero Image */} {/* Hero Image */}
<motion.div <m.div
className="relative w-full mt-12" className="relative w-full mt-12"
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4 }} transition={{ duration: 0.8, delay: 0.4 }}
viewport={{ once: true }} viewport={{ once: true }}
> >
<motion.div <m.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 rounded-lg" className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 rounded-lg"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
> >
@ -106,9 +108,9 @@ function AboutPage() {
className="w-full h-full object-cover img-premium" className="w-full h-full object-cover img-premium"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/10 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/10 via-transparent to-transparent" />
</motion.div> </m.div>
</motion.div> </m.div>
</motion.div> </m.div>
</div> </div>
</section> </section>
@ -118,14 +120,14 @@ function AboutPage() {
<div className="w-full"> <div className="w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{/* Founders Image */} {/* Founders Image */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft} variants={fadeInLeft}
className="order-2 lg:order-1" className="order-2 lg:order-1"
> >
<motion.div <m.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500" className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
> >
@ -136,18 +138,18 @@ function AboutPage() {
className="w-full h-full object-cover img-premium" className="w-full h-full object-cover img-premium"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
</motion.div> </m.div>
{/* Founders Text */} {/* Founders Text */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight} variants={fadeInRight}
className="space-y-6 md:space-y-8 order-1 lg:order-2 px-8 md:px-16 lg:px-24" className="space-y-6 md:space-y-8 order-1 lg:order-2 px-8 md:px-16 lg:px-24"
> >
<motion.h2 <m.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha" className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha"
style={{ style={{
fontFamily: "MonetaSans-Regular", fontFamily: "MonetaSans-Regular",
@ -161,9 +163,9 @@ function AboutPage() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Our Founders Our Founders
</motion.h2> </m.h2>
<motion.h3 <m.h3
className="text-2xl md:text-3xl font-semibold text-[#8B4513] font-renner" className="text-2xl md:text-3xl font-semibold text-[#8B4513] font-renner"
style={{ style={{
fontWeight: 500, fontWeight: 500,
@ -176,9 +178,9 @@ function AboutPage() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Priyanka and Amritanshu. Priyanka and Amritanshu.
</motion.h3> </m.h3>
<motion.p <m.p
className="text-gray-700 leading-relaxed font-renner" className="text-gray-700 leading-relaxed font-renner"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -193,8 +195,8 @@ function AboutPage() {
viewport={{ once: true }} viewport={{ once: true }}
> >
are the proud founders of Mozimo, a bean-to-bar chocolate shop that celebrates the rich and distinctive flavors of single origin cocoa beans. Their journey with Mozimo has been a passionate pursuit of creating exceptional chocolate that tantalizes the taste buds and tells the unique story of each cocoa bean&apos;s origin. are the proud founders of Mozimo, a bean-to-bar chocolate shop that celebrates the rich and distinctive flavors of single origin cocoa beans. Their journey with Mozimo has been a passionate pursuit of creating exceptional chocolate that tantalizes the taste buds and tells the unique story of each cocoa bean&apos;s origin.
</motion.p> </m.p>
</motion.div> </m.div>
</div> </div>
</div> </div>
</section> </section>
@ -204,14 +206,14 @@ function AboutPage() {
<div className="w-full"> <div className="w-full">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{/* Italian Chocolate Text */} {/* Italian Chocolate Text */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft} variants={fadeInLeft}
className="order-2 lg:order-1 space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24" className="order-2 lg:order-1 space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
> >
<motion.h2 <m.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21]" className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21]"
style={{ style={{
fontFamily: "MonetaSans-Regular", fontFamily: "MonetaSans-Regular",
@ -225,8 +227,8 @@ function AboutPage() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Capturing the essence of Capturing the essence of
</motion.h2> </m.h2>
<motion.h2 <m.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha" className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-samantha"
style={{ style={{
fontFamily: "Samantha Signature", fontFamily: "Samantha Signature",
@ -240,9 +242,9 @@ function AboutPage() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Italian Chocolate Italian Chocolate
</motion.h2> </m.h2>
<motion.p <m.p
className="text-gray-700 leading-relaxed font-renner" className="text-gray-700 leading-relaxed font-renner"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -257,18 +259,18 @@ function AboutPage() {
viewport={{ once: true }} viewport={{ once: true }}
> >
With over 15 years of hospitality experience, these avid travelers scoured the globe for the finest cocoa beans and techniques. Deep in remote cocoa farms, they cultivated relationships with farmers committed to sustainability. Carefully selecting beans, they honor each harvest&apos;s stories. In their cozy workshop, they roast, crack, and refine beans with modern techniques, capturing their essence in every Mozimo chocolate bar. With over 15 years of hospitality experience, these avid travelers scoured the globe for the finest cocoa beans and techniques. Deep in remote cocoa farms, they cultivated relationships with farmers committed to sustainability. Carefully selecting beans, they honor each harvest&apos;s stories. In their cozy workshop, they roast, crack, and refine beans with modern techniques, capturing their essence in every Mozimo chocolate bar.
</motion.p> </m.p>
</motion.div> </m.div>
{/* Cocoa Farm Image */} {/* Cocoa Farm Image */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight} variants={fadeInRight}
className="order-1 lg:order-2" className="order-1 lg:order-2"
> >
<motion.div <m.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500" className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
> >
@ -279,8 +281,8 @@ function AboutPage() {
className="w-full h-full object-cover img-premium" className="w-full h-full object-cover img-premium"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
</motion.div> </m.div>
</div> </div>
</div> </div>
</section> </section>
@ -289,14 +291,14 @@ function AboutPage() {
<section className="py-16 md:py-24 bg-white"> <section className="py-16 md:py-24 bg-white">
<div className="w-full"> <div className="w-full">
{/* Two Images Side by Side */} {/* Two Images Side by Side */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={staggerContainer} variants={staggerContainer}
className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12 mb-12 md:mb-16" className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12 mb-12 md:mb-16"
> >
<motion.div <m.div
variants={fadeInLeft} variants={fadeInLeft}
className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500" className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
@ -308,9 +310,9 @@ function AboutPage() {
className="w-full h-full object-cover img-premium" className="w-full h-full object-cover img-premium"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
<motion.div <m.div
variants={fadeInRight} variants={fadeInRight}
className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500" className="relative w-full h-[300px] md:h-[400px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
@ -322,18 +324,18 @@ function AboutPage() {
className="w-full h-full object-cover img-premium" className="w-full h-full object-cover img-premium"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
</motion.div> </m.div>
{/* Text Content */} {/* Text Content */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp} variants={fadeInUp}
className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24" className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
> >
<motion.p <m.p
className="text-gray-700 leading-relaxed font-renner" className="text-gray-700 leading-relaxed font-renner"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -348,9 +350,9 @@ function AboutPage() {
viewport={{ once: true }} viewport={{ once: true }}
> >
Under the mentorship of the master chocolatier Gabriele Rinaudo, Priyanka and Amritanshu dedicated themselves to mastering the intricate art of chocolate making. With unwavering determination and a thirst for knowledge, they immersed themselves in the world of cocoa, learning the nuances of sourcing the finest ingredients and perfecting the delicate techniques that transform raw beans into exquisite chocolate creations. Under the mentorship of the master chocolatier Gabriele Rinaudo, Priyanka and Amritanshu dedicated themselves to mastering the intricate art of chocolate making. With unwavering determination and a thirst for knowledge, they immersed themselves in the world of cocoa, learning the nuances of sourcing the finest ingredients and perfecting the delicate techniques that transform raw beans into exquisite chocolate creations.
</motion.p> </m.p>
<motion.p <m.p
className="text-gray-700 leading-relaxed font-renner" className="text-gray-700 leading-relaxed font-renner"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -365,15 +367,15 @@ function AboutPage() {
viewport={{ once: true }} viewport={{ once: true }}
> >
In addition to their training under Gabriele Rinaudo, they embarked on a transformative journey to Italy to further enrich their understanding of the art of chocolate making. This immersive experience in Italy not only broadened their knowledge but also deepened their appreciation for the timeless artistry and dedication that define the world of chocolate making. In addition to their training under Gabriele Rinaudo, they embarked on a transformative journey to Italy to further enrich their understanding of the art of chocolate making. This immersive experience in Italy not only broadened their knowledge but also deepened their appreciation for the timeless artistry and dedication that define the world of chocolate making.
</motion.p> </m.p>
</motion.div> </m.div>
</div> </div>
</section> </section>
{/* Vision and Passion Section */} {/* Vision and Passion Section */}
<section className="py-16 md:py-24 bg-[#F5E6D3]"> <section className="py-16 md:py-24 bg-[#F5E6D3]">
<div className="w-full"> <div className="w-full">
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -381,7 +383,7 @@ function AboutPage() {
className="text-center space-y-8 md:space-y-12" className="text-center space-y-8 md:space-y-12"
> >
{/* Founders Outdoor Image */} {/* Founders Outdoor Image */}
<motion.div <m.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500" className="relative w-full h-[400px] md:h-[500px] lg:h-[600px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
> >
@ -392,10 +394,10 @@ function AboutPage() {
className="w-full h-full object-cover img-premium" className="w-full h-full object-cover img-premium"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
{/* Vision and Passion Text */} {/* Vision and Passion Text */}
<motion.div <m.div
className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24" className="w-full space-y-6 md:space-y-8 px-8 md:px-16 lg:px-24"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -414,12 +416,14 @@ function AboutPage() {
> >
Passionate about chocolate as a medium for creativity and exploration, they view it not just as a confection but as a canvas for artistic expression. Constantly seeking inspiration, they push the boundaries of chocolate-making, committed to crafting sensory experiences that delight the taste buds, eyes, and soul. Proud to be part of the global craft chocolate revolution, they aim to inspire others while championing cocoa diversity and sustainability. Back in the workshop, cacao beans are roasted, cracked, and refined with modern techniques, crafting each bar of Mozimo chocolate into a masterpiece, capturing the essence of its origins. Passionate about chocolate as a medium for creativity and exploration, they view it not just as a confection but as a canvas for artistic expression. Constantly seeking inspiration, they push the boundaries of chocolate-making, committed to crafting sensory experiences that delight the taste buds, eyes, and soul. Proud to be part of the global craft chocolate revolution, they aim to inspire others while championing cocoa diversity and sustainability. Back in the workshop, cacao beans are roasted, cracked, and refined with modern techniques, crafting each bar of Mozimo chocolate into a masterpiece, capturing the essence of its origins.
</p> </p>
</motion.div> </m.div>
</motion.div> </m.div>
</div> </div>
</section> </section>
<Footer /> <Suspense fallback={<div className="h-[300px] w-full skeleton bg-gray-100 animate-pulse" />}>
<Footer />
</Suspense>
</div> </div>
); );
} }

View File

@ -1,14 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'; import { createFileRoute } from '@tanstack/react-router';
import { motion } from "framer-motion"; import { m } from "framer-motion";
import Header from "@/components/Header"; import Header from "@/components/Header";
import Footer from "@/components/Footer"; // Footer is lazy loaded
import Section from "@/components/Section"; import Section from "@/components/Section";
import Button from "@/components/Button"; import Button from "@/components/Button";
import BannerSlider from "@/components/BannerSlider"; import BannerSlider from "@/components/BannerSlider";
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
import { useState, useEffect, useRef, useCallback } from "react"; import { useState, useEffect, useRef, useCallback, lazy, Suspense } from "react";
import ProductCategory from "@/components/ProductCategory"; const ProductCategory = lazy(() => import("@/components/ProductCategory"));
import SocialMedia from "@/components/SocialMedia"; const Footer = lazy(() => import("@/components/Footer"));
export const Route = createFileRoute('/')({ export const Route = createFileRoute('/')({
component: Home, component: Home,
@ -17,30 +17,35 @@ export const Route = createFileRoute('/')({
// Premium animation variants // Premium animation variants
const fadeInUp = { const fadeInUp = {
initial: { opacity: 0, y: 30 }, initial: { opacity: 0, y: 30 },
animate: { opacity: 1, y: 0 }, animate: { opacity: 1, y: 0, transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] as const } },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
}; };
const fadeInLeft = { const fadeInLeft = {
initial: { opacity: 0, x: -30 }, initial: { opacity: 0, x: -30 },
animate: { opacity: 1, x: 0 }, animate: { opacity: 1, x: 0, transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] as const } },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
}; };
const fadeInRight = { const fadeInRight = {
initial: { opacity: 0, x: 30 }, initial: { opacity: 0, x: 30 },
animate: { opacity: 1, x: 0 }, animate: { opacity: 1, x: 0, transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] as const } },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
}; };
const staggerContainer = { const staggerContainer = {
initial: { opacity: 0 },
animate: { animate: {
opacity: 1,
transition: { transition: {
staggerChildren: 0.2, staggerChildren: 0.2,
delayChildren: 0.1,
}, },
}, },
}; };
const itemUp = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0, transition: { duration: 0.6 } }
};
const categories = [ const categories = [
"Bars", "Bars",
"Barks", "Barks",
@ -134,14 +139,14 @@ function Home() {
<Section background="white" id="about"> <Section background="white" id="about">
<div className="grid grid-cols-1 lg:grid-cols-2 items-center"> <div className="grid grid-cols-1 lg:grid-cols-2 items-center">
{/* Brand Story - Left Text */} {/* Brand Story - Left Text */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft} variants={fadeInLeft}
className="space-y-6 md:space-y-8 order-2 lg:order-1 px-4 md:px-0" // ← Added mobile padding className="space-y-6 md:space-y-8 order-2 lg:order-1 px-4 md:px-0" // ← Added mobile padding
> >
<motion.h2 <m.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-moneta text-gradient p-4 md:p-0" className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-moneta text-gradient p-4 md:p-0"
style={{ style={{
fontWeight: 400, fontWeight: 400,
@ -155,7 +160,7 @@ function Home() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Brand Story Brand Story
</motion.h2> </m.h2>
<div <div
className="space-y-4 md:space-y-6 text-gray-700 leading-relaxed font-renner" className="space-y-4 md:space-y-6 text-gray-700 leading-relaxed font-renner"
style={{ style={{
@ -166,7 +171,7 @@ function Home() {
fontFamily: "Renner*", fontFamily: "Renner*",
}} }}
> >
<motion.p <m.p
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }} transition={{ duration: 0.6, delay: 0.1 }}
@ -175,8 +180,8 @@ function Home() {
Mozimo&apos;s journey is a passionate pursuit of crafting Mozimo&apos;s journey is a passionate pursuit of crafting
exceptional chocolate that celebrates the origin of each cocoa exceptional chocolate that celebrates the origin of each cocoa
bean. bean.
</motion.p> </m.p>
<motion.p <m.p
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }} transition={{ duration: 0.6, delay: 0.2 }}
@ -184,8 +189,8 @@ function Home() {
> >
We meticulously roast, crack, winnow, and refine beans in-house, We meticulously roast, crack, winnow, and refine beans in-house,
using modern techniques to highlight their natural flavors. using modern techniques to highlight their natural flavors.
</motion.p> </m.p>
<motion.p <m.p
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }} transition={{ duration: 0.6, delay: 0.3 }}
@ -193,9 +198,9 @@ function Home() {
> >
Each chocolate is a masterpiece, capturing the essence of cocoa Each chocolate is a masterpiece, capturing the essence of cocoa
in its purest form. in its purest form.
</motion.p> </m.p>
</div> </div>
<motion.div <m.div
className="pt-4" className="pt-4"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -205,17 +210,17 @@ function Home() {
<Button variant="white" size="lg" className="btn-premium"> <Button variant="white" size="lg" className="btn-premium">
Discover more Discover more
</Button> </Button>
</motion.div> </m.div>
</motion.div> </m.div>
{/* Brand Story - Right Image */} {/* Brand Story - Right Image */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight} variants={fadeInRight}
className="relative order-1 lg:order-2 w-screen md:w-auto -mx-4 md:mx-0" className="relative order-1 lg:order-2 w-screen md:w-auto -mx-4 md:mx-0"
> >
<motion.div <m.div
className="relative w-full h-[300px] md:h-[400px] lg:h-[500px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-bottom-left" className="relative w-full h-[300px] md:h-[400px] lg:h-[500px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-bottom-left"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
> >
@ -227,18 +232,18 @@ function Home() {
draggable={false} draggable={false}
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
</motion.div> </m.div>
{/* Chocolate Tempering - Left Video */} {/* Chocolate Tempering - Left Video */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft} variants={fadeInLeft}
className="relative order-3 lg:order-3 w-screen md:w-auto -mx-4 md:mx-0 py-4 md:py-0" className="relative order-3 lg:order-3 w-screen md:w-auto -mx-4 md:mx-0 py-4 md:py-0"
> >
<motion.div <m.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[700px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-top-right" className="relative w-full h-[400px] md:h-[500px] lg:h-[700px] overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500 clip-diagonal-top-right"
whileHover={{ scale: 1.02, transition: { duration: 0.3 } }} whileHover={{ scale: 1.02, transition: { duration: 0.3 } }}
> >
@ -253,11 +258,11 @@ function Home() {
aria-label="Chocolate pouring from metallic spout - Mozimo chocolate tempering process" aria-label="Chocolate pouring from metallic spout - Mozimo chocolate tempering process"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
</motion.div> </m.div>
{/* Chocolate Tempering - Right Text */} {/* Chocolate Tempering - Right Text */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -265,7 +270,7 @@ function Home() {
className="space-y-4 md:space-y-6 order-4 lg:order-4 px-4 md:px-0 ml-4 md:ml-8 lg:ml-12" className="space-y-4 md:space-y-6 order-4 lg:order-4 px-4 md:px-0 ml-4 md:ml-8 lg:ml-12"
> >
<div className="space-y-3 md:space-y-4"> <div className="space-y-3 md:space-y-4">
<motion.h2 <m.h2
className="text-2xl md:text-3xl lg:text-4xl font-bold text-[#3C2A21] font-renner text-gradient" className="text-2xl md:text-3xl lg:text-4xl font-bold text-[#3C2A21] font-renner text-gradient"
style={{ style={{
fontWeight: 200, fontWeight: 200,
@ -279,8 +284,8 @@ function Home() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Discover the delicate art of our Discover the delicate art of our
</motion.h2> </m.h2>
<motion.h3 <m.h3
className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#8B4513] font-samantha" className="text-4xl md:text-5xl lg:text-7xl font-bold text-[#8B4513] font-samantha"
style={{ style={{
fontFamily: "Samantha Signature", fontFamily: "Samantha Signature",
@ -295,10 +300,10 @@ function Home() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Chocolate Tempering Chocolate Tempering
</motion.h3> </m.h3>
</div> </div>
<motion.div <m.div
className="w-100 h-px bg-gradient-to-r from-transparent via-[#8B4513] to-transparent" className="w-100 h-px bg-gradient-to-r from-transparent via-[#8B4513] to-transparent"
initial={{ scaleX: 0 }} initial={{ scaleX: 0 }}
whileInView={{ scaleX: 1 }} whileInView={{ scaleX: 1 }}
@ -306,7 +311,7 @@ function Home() {
viewport={{ once: true }} viewport={{ once: true }}
/> />
<motion.p <m.p
className="text-gray-700 leading-relaxed font-renner" className="text-gray-700 leading-relaxed font-renner"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -325,14 +330,14 @@ function Home() {
perfectly tempered chocolate: velvety smooth, exquisitely rich, perfectly tempered chocolate: velvety smooth, exquisitely rich,
and artfully balanced. Each bite offers a symphony of nuanced and artfully balanced. Each bite offers a symphony of nuanced
cocoa flavors, melting luxuriously! cocoa flavors, melting luxuriously!
</motion.p> </m.p>
</motion.div> </m.div>
</div> </div>
</Section> </Section>
{/* Our Collections Section */} {/* Our Collections Section */}
<Section background="cream" id="shop"> <Section background="cream" id="shop">
<motion.h2 <m.h2
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -349,9 +354,9 @@ function Home() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
Our Collections Our Collections
</motion.h2> </m.h2>
<motion.div <m.div
className="flex overflow-x-auto no-scrollbar snap-x snap-mandatory gap-4 px-4 sm:px-0 sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:gap-6 md:gap-8" className="flex overflow-x-auto no-scrollbar snap-x snap-mandatory gap-4 px-4 sm:px-0 sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:gap-6 md:gap-8"
variants={staggerContainer} variants={staggerContainer}
initial="initial" initial="initial"
@ -375,7 +380,7 @@ function Home() {
alt: "Gift Collection - Mozimo packaged chocolate products and gift items", alt: "Gift Collection - Mozimo packaged chocolate products and gift items",
}, },
].map((item) => ( ].map((item) => (
<motion.div <m.div
key={item.title} key={item.title}
variants={fadeInUp} variants={fadeInUp}
className="snap-start w-[calc(50%)] flex-shrink-0 sm:w-auto sm:flex-shrink sm:min-w-0 group flex flex-col items-center justify-end transition-all duration-500 hover:-translate-y-2" className="snap-start w-[calc(50%)] flex-shrink-0 sm:w-auto sm:flex-shrink sm:min-w-0 group flex flex-col items-center justify-end transition-all duration-500 hover:-translate-y-2"
@ -391,9 +396,8 @@ function Home() {
width={320} width={320}
height={400} height={400}
layout="constrained" layout="constrained"
className="object-contain img-premium group-hover:scale-105 transition-transform duration-500" className="object-contain img-premium group-hover:scale-105 transition-transform duration-500 max-h-[380px] w-full"
draggable={false} draggable={false}
style={{ maxHeight: "380px", width: "100%" }}
/> />
<div className="p-4 md:p-6 text-center mb-2 w-full"> <div className="p-4 md:p-6 text-center mb-2 w-full">
@ -410,18 +414,20 @@ function Home() {
{item.title} {item.title}
</h3> </h3>
</div> </div>
</motion.div> </m.div>
))} ))}
</motion.div> </m.div>
</Section> </Section>
<ProductCategory /> <Suspense fallback={<div className="h-[500px] w-full skeleton bg-gray-100 animate-pulse" />}>
<ProductCategory />
</Suspense>
{/* Social Media Call to Action */} {/* Social Media Call to Action */}
<SocialMedia /> {/* <SocialMedia /> */}
{/* In the Spotlight Section */} {/* In the Spotlight Section */}
<Section background="white"> <Section background="white">
<motion.h2 <m.h2
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -438,10 +444,10 @@ function Home() {
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
In the Spotlight In the Spotlight
</motion.h2> </m.h2>
<div className="w-full flex justify-center items-center py-8 md:py-12"> <div className="w-full flex justify-center items-center py-8 md:py-12">
<div className="w-full max-w-[700px] px-4 overflow-hidden"> <div className="w-full max-w-[700px] px-4 overflow-hidden">
<motion.div <m.div
className="flex gap-8 py-6" className="flex gap-8 py-6"
animate={{ x: `-${spotlightIndex * 152}px` }} animate={{ x: `-${spotlightIndex * 152}px` }}
transition={{ type: "spring", stiffness: 80, damping: 18 }} transition={{ type: "spring", stiffness: 80, damping: 18 }}
@ -452,7 +458,7 @@ function Home() {
const imgIdx = i % partnerImages.length; const imgIdx = i % partnerImages.length;
const img = partnerImages[imgIdx]; const img = partnerImages[imgIdx];
return ( return (
<motion.div <m.div
key={i} key={i}
className="w-24 h-24 md:w-32 md:h-32 lg:w-36 lg:h-36 bg-white rounded-full flex items-center justify-center shadow-premium hover:shadow-premium-hover flex-shrink-0 transition-all duration-300" className="w-24 h-24 md:w-32 md:h-32 lg:w-36 lg:h-36 bg-white rounded-full flex items-center justify-center shadow-premium hover:shadow-premium-hover flex-shrink-0 transition-all duration-300"
style={{ style={{
@ -474,15 +480,17 @@ function Home() {
className="w-16 h-16 md:w-20 md:h-20 lg:w-20 lg:h-20 object-contain img-premium" className="w-16 h-16 md:w-20 md:h-20 lg:w-20 lg:h-20 object-contain img-premium"
draggable={false} draggable={false}
/> />
</motion.div> </m.div>
); );
})} })}
</motion.div> </m.div>
</div> </div>
</div> </div>
</Section> </Section>
<Footer /> <Suspense fallback={<div className="h-[300px] w-full skeleton bg-gray-100 animate-pulse" />}>
<Footer />
</Suspense>
</div> </div>
); );
} }

View File

@ -1,5 +1,5 @@
import { useState, useEffect, useRef } from "react"; import { useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion"; import { m, AnimatePresence } from "framer-motion";
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
const banners = [ const banners = [
@ -95,7 +95,7 @@ export default function BannerSlider() {
{/* Banner Images with Premium Animations */} {/* Banner Images with Premium Animations */}
<AnimatePresence initial={false} custom={direction} mode="popLayout"> <AnimatePresence initial={false} custom={direction} mode="popLayout">
<motion.div <m.div
key={`${currentIndex}-${direction}`} key={`${currentIndex}-${direction}`}
custom={direction} custom={direction}
variants={slideVariants} variants={slideVariants}
@ -130,13 +130,13 @@ export default function BannerSlider() {
{/* Premium overlay with gradient */} {/* Premium overlay with gradient */}
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div> </m.div>
</AnimatePresence> </AnimatePresence>
{/* Premium Shop Now Button */} {/* Premium Shop Now Button */}
{currentIndex === 0 && ( {currentIndex === 0 && (
<div className="absolute bottom-16 md:bottom-20 left-1/2 transform -translate-x-1/2 z-10"> <div className="absolute bottom-16 md:bottom-20 left-1/2 transform -translate-x-1/2 z-10">
<motion.a <m.a
href="https://shop.mozimo.in/" href="https://shop.mozimo.in/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -159,7 +159,7 @@ export default function BannerSlider() {
}} }}
> >
<span className="relative z-10">Shop Now</span> <span className="relative z-10">Shop Now</span>
</motion.a> </m.a>
</div> </div>
)} )}
@ -167,7 +167,7 @@ export default function BannerSlider() {
<AnimatePresence> <AnimatePresence>
{showArrows && ( {showArrows && (
<> <>
<motion.button <m.button
initial={{ opacity: 0, x: -20 }} initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }} exit={{ opacity: 0, x: -20 }}
@ -191,9 +191,9 @@ export default function BannerSlider() {
d="M15 19l-7-7 7-7" d="M15 19l-7-7 7-7"
/> />
</svg> </svg>
</motion.button> </m.button>
<motion.button <m.button
initial={{ opacity: 0, x: 20 }} initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }} exit={{ opacity: 0, x: 20 }}
@ -217,14 +217,14 @@ export default function BannerSlider() {
d="M9 5l7 7-7 7" d="M9 5l7 7-7 7"
/> />
</svg> </svg>
</motion.button> </m.button>
</> </>
)} )}
</AnimatePresence> </AnimatePresence>
{/* Mobile Navigation Arrows */} {/* Mobile Navigation Arrows */}
<div className="md:hidden absolute inset-x-0 top-1/2 transform -translate-y-1/2 z-20 flex justify-between items-center px-4"> <div className="md:hidden absolute inset-x-0 top-1/2 transform -translate-y-1/2 z-20 flex justify-between items-center px-4">
<motion.button <m.button
onClick={goToPrevious} onClick={goToPrevious}
className="glass text-white p-3 rounded-full backdrop-blur-sm transition-all duration-300 border border-white/20 hover:border-white/40 shadow-premium" className="glass text-white p-3 rounded-full backdrop-blur-sm transition-all duration-300 border border-white/20 hover:border-white/40 shadow-premium"
whileHover={{ scale: 1.1, rotate: -5 }} whileHover={{ scale: 1.1, rotate: -5 }}
@ -244,9 +244,9 @@ export default function BannerSlider() {
d="M15 19l-7-7 7-7" d="M15 19l-7-7 7-7"
/> />
</svg> </svg>
</motion.button> </m.button>
<motion.button <m.button
onClick={goToNext} onClick={goToNext}
className="glass text-white p-3 rounded-full backdrop-blur-sm transition-all duration-300 border border-white/20 hover:border-white/40 shadow-premium" className="glass text-white p-3 rounded-full backdrop-blur-sm transition-all duration-300 border border-white/20 hover:border-white/40 shadow-premium"
whileHover={{ scale: 1.1, rotate: 5 }} whileHover={{ scale: 1.1, rotate: 5 }}
@ -266,13 +266,13 @@ export default function BannerSlider() {
d="M9 5l7 7-7 7" d="M9 5l7 7-7 7"
/> />
</svg> </svg>
</motion.button> </m.button>
</div> </div>
{/* Premium Dots Indicator */} {/* Premium Dots Indicator */}
<div className="absolute bottom-6 md:bottom-8 left-1/2 transform -translate-x-1/2 z-20 hidden md:flex space-x-2 md:space-x-3"> <div className="absolute bottom-6 md:bottom-8 left-1/2 transform -translate-x-1/2 z-20 hidden md:flex space-x-2 md:space-x-3">
{banners.map((_, index) => ( {banners.map((_, index) => (
<motion.button <m.button
key={index} key={index}
onClick={() => goToSlide(index)} onClick={() => goToSlide(index)}
className={`w-2 h-2 md:w-3 md:h-3 rounded-full transition-all duration-300 ${ className={`w-2 h-2 md:w-3 md:h-3 rounded-full transition-all duration-300 ${
@ -289,7 +289,7 @@ export default function BannerSlider() {
{/* Premium Progress Bar */} {/* Premium Progress Bar */}
<div className="absolute bottom-0 left-0 right-0 h-1 bg-white/10 z-20"> <div className="absolute bottom-0 left-0 right-0 h-1 bg-white/10 z-20">
<motion.div <m.div
className="h-full bg-gradient-to-r from-[#8B4513] to-[#DAA520]" className="h-full bg-gradient-to-r from-[#8B4513] to-[#DAA520]"
initial={{ width: 0 }} initial={{ width: 0 }}
animate={{ width: "100%" }} animate={{ width: "100%" }}

View File

@ -1,4 +1,4 @@
import { motion } from "framer-motion"; import { m } from "framer-motion";
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
export default function DesktopFooter() { export default function DesktopFooter() {
@ -11,7 +11,7 @@ export default function DesktopFooter() {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 md:py-12 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8 relative z-10"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 md:py-12 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8 relative z-10">
{/* Column 1: Brand Identity */} {/* Column 1: Brand Identity */}
<motion.div <m.div
className="space-y-4 sm:col-span-2 lg:col-span-1" className="space-y-4 sm:col-span-2 lg:col-span-1"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -41,10 +41,10 @@ export default function DesktopFooter() {
India&apos;s Premier European style bean-to-bar chocolate India&apos;s Premier European style bean-to-bar chocolate
experience. experience.
</p> </p>
</motion.div> </m.div>
{/* Column 2: Get in touch */} {/* Column 2: Get in touch */}
<motion.div <m.div
className="space-y-4" className="space-y-4"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -73,7 +73,7 @@ export default function DesktopFooter() {
fontFamily: "Renner*", fontFamily: "Renner*",
}} }}
> >
<motion.div <m.div
className="flex items-center space-x-2" className="flex items-center space-x-2"
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -89,8 +89,8 @@ export default function DesktopFooter() {
<path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" /> <path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" />
</svg> </svg>
<span className="text-xs md:text-sm">0172-4045414</span> <span className="text-xs md:text-sm">0172-4045414</span>
</motion.div> </m.div>
<motion.div <m.div
className="flex items-start space-x-2" className="flex items-start space-x-2"
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -112,12 +112,12 @@ export default function DesktopFooter() {
<span className="text-xs md:text-sm"> <span className="text-xs md:text-sm">
SCO 8, Inner Market, 9-D, Sector 9, Chandigarh, 160009 SCO 8, Inner Market, 9-D, Sector 9, Chandigarh, 160009
</span> </span>
</motion.div> </m.div>
</div> </div>
</motion.div> </m.div>
{/* Column 3: Know more */} {/* Column 3: Know more */}
<motion.div <m.div
className="space-y-4" className="space-y-4"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -152,7 +152,7 @@ export default function DesktopFooter() {
"Terms & Conditions", "Terms & Conditions",
"Shipping Policy", "Shipping Policy",
].map((link, index) => ( ].map((link, index) => (
<motion.li <m.li
key={link} key={link}
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -166,13 +166,13 @@ export default function DesktopFooter() {
{link} {link}
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span> <span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span>
</a> </a>
</motion.li> </m.li>
))} ))}
</ul> </ul>
</motion.div> </m.div>
{/* Column 4: FAQ, Store, Ordering, Social Media */} {/* Column 4: FAQ, Store, Ordering, Social Media */}
<motion.div <m.div
className="space-y-4 sm:col-span-2 lg:col-span-1" className="space-y-4 sm:col-span-2 lg:col-span-1"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -191,7 +191,7 @@ export default function DesktopFooter() {
}} }}
> >
{["FAQ", "Locate our store", "Bulk Ordering"].map((link, index) => ( {["FAQ", "Locate our store", "Bulk Ordering"].map((link, index) => (
<motion.div <m.div
key={link} key={link}
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -205,7 +205,7 @@ export default function DesktopFooter() {
{link} {link}
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span> <span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span>
</a> </a>
</motion.div> </m.div>
))} ))}
</div> </div>
@ -225,7 +225,7 @@ export default function DesktopFooter() {
</h3> </h3>
<div className="flex space-x-3 md:space-x-4"> <div className="flex space-x-3 md:space-x-4">
{/* Facebook */} {/* Facebook */}
<motion.a <m.a
href="https://www.facebook.com/share/16vPmvcQV7/?mibextid=wwXIfr" href="https://www.facebook.com/share/16vPmvcQV7/?mibextid=wwXIfr"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -245,9 +245,9 @@ export default function DesktopFooter() {
> >
<path d="M22.675 0h-21.35C.595 0 0 .592 0 1.326v21.348C0 23.408.595 24 1.325 24h11.495v-9.294H9.692v-3.622h3.128V8.413c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.797.143v3.24l-1.918.001c-1.504 0-1.797.715-1.797 1.763v2.313h3.587l-.467 3.622h-3.12V24h6.116C23.406 24 24 23.408 24 22.674V1.326C24 .592 23.406 0 22.675 0" /> <path d="M22.675 0h-21.35C.595 0 0 .592 0 1.326v21.348C0 23.408.595 24 1.325 24h11.495v-9.294H9.692v-3.622h3.128V8.413c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.797.143v3.24l-1.918.001c-1.504 0-1.797.715-1.797 1.763v2.313h3.587l-.467 3.622h-3.12V24h6.116C23.406 24 24 23.408 24 22.674V1.326C24 .592 23.406 0 22.675 0" />
</svg> </svg>
</motion.a> </m.a>
{/* Instagram */} {/* Instagram */}
<motion.a <m.a
href="https://www.instagram.com/mozimo.choc?igsh=bTVzbGo0enV3b3Jn" href="https://www.instagram.com/mozimo.choc?igsh=bTVzbGo0enV3b3Jn"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -267,9 +267,9 @@ export default function DesktopFooter() {
> >
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 1.366.062 2.633.334 3.608 1.308.974.974 1.246 2.241 1.308 3.608.058 1.266.069 1.646.069 4.85s-.012 3.584-.07 4.85c-.062 1.366-.334 2.633-1.308 3.608-.974.974-2.241 1.246-3.608 1.308-1.266.058-1.646.069-4.85.069s-3.584-.012-4.85-.07c-1.366-.062-2.633-.334-3.608-1.308-.974-.974-1.246-2.241-1.308-3.608C2.175 15.647 2.163 15.267 2.163 12s.012-3.584.07-4.85c.062-1.366.334-2.633 1.308-3.608C4.515 2.567 5.782 2.295 7.148 2.233 8.414 2.175 8.794 2.163 12 2.163zm0-2.163C8.741 0 8.332.012 7.052.07 5.771.128 4.659.334 3.678 1.315c-.98.98-1.187 2.092-1.245 3.373C2.012 5.668 2 6.077 2 12c0 5.923.012 6.332.07 7.612.058 1.281.265 2.393 1.245 3.373.98.98 2.092 1.187 3.373 1.245C8.332 23.988 8.741 24 12 24s3.668-.012 4.948-.07c1.281-.058 2.393-.265 3.373-1.245.98-.98 1.187-2.092 1.245-3.373.058-1.28.07-1.689.07-7.612 0-5.923-.012-6.332-.07-7.612-.058-1.281-.265-2.393-1.245-3.373-.98-.98-2.092-1.187-3.373-1.245C15.668.012 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zm0 10.162a3.999 3.999 0 1 1 0-7.998 3.999 3.999 0 0 1 0 7.998zm6.406-11.845a1.44 1.44 0 1 0 0 2.88 1.44 1.44 0 0 0 0-2.88z" /> <path d="M12 2.163c3.204 0 3.584.012 4.85.07 1.366.062 2.633.334 3.608 1.308.974.974 1.246 2.241 1.308 3.608.058 1.266.069 1.646.069 4.85s-.012 3.584-.07 4.85c-.062 1.366-.334 2.633-1.308 3.608-.974.974-2.241 1.246-3.608 1.308-1.266.058-1.646.069-4.85.069s-3.584-.012-4.85-.07c-1.366-.062-2.633-.334-3.608-1.308-.974-.974-1.246-2.241-1.308-3.608C2.175 15.647 2.163 15.267 2.163 12s.012-3.584.07-4.85c.062-1.366.334-2.633 1.308-3.608C4.515 2.567 5.782 2.295 7.148 2.233 8.414 2.175 8.794 2.163 12 2.163zm0-2.163C8.741 0 8.332.012 7.052.07 5.771.128 4.659.334 3.678 1.315c-.98.98-1.187 2.092-1.245 3.373C2.012 5.668 2 6.077 2 12c0 5.923.012 6.332.07 7.612.058 1.281.265 2.393 1.245 3.373.98.98 2.092 1.187 3.373 1.245C8.332 23.988 8.741 24 12 24s3.668-.012 4.948-.07c1.281-.058 2.393-.265 3.373-1.245.98-.98 1.187-2.092 1.245-3.373.058-1.28.07-1.689.07-7.612 0-5.923-.012-6.332-.07-7.612-.058-1.281-.265-2.393-1.245-3.373-.98-.98-2.092-1.187-3.373-1.245C15.668.012 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zm0 10.162a3.999 3.999 0 1 1 0-7.998 3.999 3.999 0 0 1 0 7.998zm6.406-11.845a1.44 1.44 0 1 0 0 2.88 1.44 1.44 0 0 0 0-2.88z" />
</svg> </svg>
</motion.a> </m.a>
{/* YouTube */} {/* YouTube */}
<motion.a <m.a
href="https://www.youtube.com/@MozimoChocolates" href="https://www.youtube.com/@MozimoChocolates"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -289,9 +289,9 @@ export default function DesktopFooter() {
> >
<path d="M23.498 6.186a2.994 2.994 0 0 0-2.107-2.117C19.163 3.5 12 3.5 12 3.5s-7.163 0-9.391.569A2.994 2.994 0 0 0 .502 6.186C0 8.413 0 12 0 12s0 3.587.502 5.814a2.994 2.994 0 0 0 2.107 2.117C4.837 20.5 12 20.5 12 20.5s7.163 0 9.391-.569a2.994 2.994 0 0 0 2.107-2.117C24 15.587 24 12 24 12s0-3.587-.502-5.814zM9.545 15.568V8.432l6.545 3.568-6.545 3.568z" /> <path d="M23.498 6.186a2.994 2.994 0 0 0-2.107-2.117C19.163 3.5 12 3.5 12 3.5s-7.163 0-9.391.569A2.994 2.994 0 0 0 .502 6.186C0 8.413 0 12 0 12s0 3.587.502 5.814a2.994 2.994 0 0 0 2.107 2.117C4.837 20.5 12 20.5 12 20.5s7.163 0 9.391-.569a2.994 2.994 0 0 0 2.107-2.117C24 15.587 24 12 24 12s0-3.587-.502-5.814zM9.545 15.568V8.432l6.545 3.568-6.545 3.568z" />
</svg> </svg>
</motion.a> </m.a>
{/* LinkedIn */} {/* LinkedIn */}
<motion.a <m.a
href="https://www.linkedin.com/company/mozimochocolates/" href="https://www.linkedin.com/company/mozimochocolates/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -311,14 +311,14 @@ export default function DesktopFooter() {
> >
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.761 0 5-2.239 5-5v-14c0-2.761-2.239-5-5-5zm-11 19h-3v-10h3v10zm-1.5-11.268c-.966 0-1.75-.784-1.75-1.75s.784-1.75 1.75-1.75 1.75.784 1.75 1.75-.784 1.75-1.75 1.75zm15.5 11.268h-3v-5.604c0-1.337-.025-3.063-1.868-3.063-1.868 0-2.154 1.459-2.154 2.967v5.7h-3v-10h2.881v1.367h.041c.401-.761 1.381-1.563 2.841-1.563 3.039 0 3.6 2.001 3.6 4.601v5.595z" /> <path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.761 0 5-2.239 5-5v-14c0-2.761-2.239-5-5-5zm-11 19h-3v-10h3v10zm-1.5-11.268c-.966 0-1.75-.784-1.75-1.75s.784-1.75 1.75-1.75 1.75.784 1.75 1.75-.784 1.75-1.75 1.75zm15.5 11.268h-3v-5.604c0-1.337-.025-3.063-1.868-3.063-1.868 0-2.154 1.459-2.154 2.967v5.7h-3v-10h2.881v1.367h.041c.401-.761 1.381-1.563 2.841-1.563 3.039 0 3.6 2.001 3.6 4.601v5.595z" />
</svg> </svg>
</motion.a> </m.a>
</div> </div>
</div> </div>
</motion.div> </m.div>
</div> </div>
{/* Bottom Bar */} {/* Bottom Bar */}
<motion.div <m.div
className="border-t border-[#e5e5e5] mt-6 md:mt-8 pt-6 md:pt-8 text-center text-sm font-renner relative z-10" className="border-t border-[#e5e5e5] mt-6 md:mt-8 pt-6 md:pt-8 text-center text-sm font-renner relative z-10"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -335,7 +335,7 @@ export default function DesktopFooter() {
<p className="text-xs md:text-sm"> <p className="text-xs md:text-sm">
&copy; 2024 Mozimo. All rights reserved. &copy; 2024 Mozimo. All rights reserved.
</p> </p>
</motion.div> </m.div>
</footer> </footer>
); );
} }

View File

@ -1,4 +1,4 @@
import { motion, AnimatePresence } from "framer-motion"; import { m, AnimatePresence, useScroll, useMotionValueEvent } from "framer-motion";
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useLocation } from "@tanstack/react-router"; import { useLocation } from "@tanstack/react-router";
@ -8,15 +8,11 @@ export default function Header() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const pathname = useLocation({ select: (loc) => loc.pathname }); const pathname = useLocation({ select: (loc) => loc.pathname });
useEffect(() => { const { scrollY } = useScroll();
const handleScroll = () => {
const scrollPosition = window.scrollY;
setIsScrolled(scrollPosition > 100);
};
window.addEventListener("scroll", handleScroll); useMotionValueEvent(scrollY, "change", (latest: number) => {
return () => window.removeEventListener("scroll", handleScroll); setIsScrolled(latest > 100);
}, []); });
// Close mobile menu when clicking outside // Close mobile menu when clicking outside
useEffect(() => { useEffect(() => {
@ -59,7 +55,7 @@ export default function Header() {
return ( return (
<> <>
{/* Mobile Header - Only visible on mobile */} {/* Mobile Header - Only visible on mobile */}
<motion.header <m.header
className="fixed top-0 z-[60] w-full md:hidden p-1 " className="fixed top-0 z-[60] w-full md:hidden p-1 "
initial={false} initial={false}
animate={{ animate={{
@ -78,7 +74,7 @@ export default function Header() {
<nav className="relative bg-white border border-white/20 shadow-premium w-full h-full flex items-center px-4 rounded-[15px]"> <nav className="relative bg-white border border-white/20 shadow-premium w-full h-full flex items-center px-4 rounded-[15px]">
{/* Left: Hamburger Menu */} {/* Left: Hamburger Menu */}
<div className="absolute left-4 top-1/2 -translate-y-1/2"> <div className="absolute left-4 top-1/2 -translate-y-1/2">
<motion.button <m.button
className="hamburger-button p-2 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm" className="hamburger-button p-2 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm"
whileHover={{ scale: 1.1 }} whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -86,20 +82,20 @@ export default function Header() {
aria-label="Toggle mobile menu" aria-label="Toggle mobile menu"
> >
<div className="w-6 h-6 flex flex-col justify-center items-center"> <div className="w-6 h-6 flex flex-col justify-center items-center">
<motion.span <m.span
className="w-6 h-0.5 bg-current block transition-all duration-300" className="w-6 h-0.5 bg-current block transition-all duration-300"
animate={{ animate={{
rotate: isMobileMenuOpen ? 45 : 0, rotate: isMobileMenuOpen ? 45 : 0,
y: isMobileMenuOpen ? 6 : 0, y: isMobileMenuOpen ? 6 : 0,
}} }}
/> />
<motion.span <m.span
className="w-6 h-0.5 bg-current block mt-1 transition-all duration-300" className="w-6 h-0.5 bg-current block mt-1 transition-all duration-300"
animate={{ animate={{
opacity: isMobileMenuOpen ? 0 : 1, opacity: isMobileMenuOpen ? 0 : 1,
}} }}
/> />
<motion.span <m.span
className="w-6 h-0.5 bg-current block mt-1 transition-all duration-300" className="w-6 h-0.5 bg-current block mt-1 transition-all duration-300"
animate={{ animate={{
rotate: isMobileMenuOpen ? -45 : 0, rotate: isMobileMenuOpen ? -45 : 0,
@ -107,18 +103,18 @@ export default function Header() {
}} }}
/> />
</div> </div>
</motion.button> </m.button>
</div> </div>
{/* Center: Logo */} {/* Center: Logo */}
<div className="absolute left-1/2 transform -translate-x-1/2"> <div className="absolute left-1/2 transform -translate-x-1/2">
<motion.div <m.div
className="flex items-center justify-center" className="flex items-center justify-center"
initial={{ opacity: 0, y: -20 }} initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
> >
<motion.div <m.div
className="relative" className="relative"
animate={{ animate={{
width: isScrolled ? 40 : 60, // shrink on scroll width: isScrolled ? 40 : 60, // shrink on scroll
@ -137,13 +133,13 @@ export default function Header() {
layout="fullWidth" layout="fullWidth"
className="w-full h-full object-contain drop-shadow-lg" className="w-full h-full object-contain drop-shadow-lg"
/> />
</motion.div> </m.div>
</motion.div> </m.div>
</div> </div>
{/* Right: Action Buttons */} {/* Right: Action Buttons */}
<div className="absolute right-4 top-1/2 -translate-y-1/2 flex items-center gap-1"> <div className="absolute right-4 top-1/2 -translate-y-1/2 flex items-center gap-1">
<motion.button <m.button
className="p-2" className="p-2"
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -162,9 +158,9 @@ export default function Header() {
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/> />
</svg> </svg>
</motion.button> </m.button>
<motion.button <m.button
className="p-2" className="p-2"
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -183,9 +179,9 @@ export default function Header() {
d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"
/> />
</svg> </svg>
</motion.button> </m.button>
<motion.button <m.button
className="p-2" className="p-2"
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -204,13 +200,13 @@ export default function Header() {
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
/> />
</svg> </svg>
</motion.button> </m.button>
</div> </div>
</nav> </nav>
</motion.header> </m.header>
{/* Desktop Header - Only visible on desktop */} {/* Desktop Header - Only visible on desktop */}
<motion.header <m.header
className="fixed top-0 z-50 hidden md:block " className="fixed top-0 z-50 hidden md:block "
initial={false} initial={false}
animate={{ animate={{
@ -234,7 +230,7 @@ export default function Header() {
<div className="flex justify-between items-center w-full px-20"> <div className="flex justify-between items-center w-full px-20">
{/* Left Navigation */} {/* Left Navigation */}
<div className="flex items-center space-x-16"> <div className="flex items-center space-x-16">
<motion.a <m.a
href={pathname === "/about" ? "/" : "/about"} href={pathname === "/about" ? "/" : "/about"}
className="text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group" className="text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group"
style={{ style={{
@ -248,8 +244,8 @@ export default function Header() {
> >
{pathname === "/about" ? "Home" : "About us"} {pathname === "/about" ? "Home" : "About us"}
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span> <span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span>
</motion.a> </m.a>
<motion.a <m.a
href="https://shop.mozimo.in/" href="https://shop.mozimo.in/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -265,11 +261,11 @@ export default function Header() {
> >
Shop Shop
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span> <span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span>
</motion.a> </m.a>
</div> </div>
{/* Center Logo */} {/* Center Logo */}
<motion.div <m.div
className="relative" className="relative"
animate={{ animate={{
width: isScrolled ? 75 : 96, // shrink logo when scrolled width: isScrolled ? 75 : 96, // shrink logo when scrolled
@ -289,12 +285,12 @@ export default function Header() {
layout="fullWidth" layout="fullWidth"
className="w-full h-full object-contain drop-shadow-lg" className="w-full h-full object-contain drop-shadow-lg"
/> />
</motion.div> </m.div>
{/* Right Icons */} {/* Right Icons */}
<div className="flex items-center space-x-8"> <div className="flex items-center space-x-8">
{/* More Dropdown */} {/* More Dropdown */}
<motion.button <m.button
className="flex items-center space-x-1 text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group" className="flex items-center space-x-1 text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -307,7 +303,7 @@ export default function Header() {
aria-label="More options" aria-label="More options"
> >
<span>More</span> <span>More</span>
<motion.svg <m.svg
className="w-4 h-4" className="w-4 h-4"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
@ -322,10 +318,10 @@ export default function Header() {
strokeWidth={2} strokeWidth={2}
d="M19 9l-7 7-7-7" d="M19 9l-7 7-7-7"
/> />
</motion.svg> </m.svg>
</motion.button> </m.button>
<motion.button <m.button
className="p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm" className="p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm"
whileHover={{ scale: 1.1, rotate: 5 }} whileHover={{ scale: 1.1, rotate: 5 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -344,8 +340,8 @@ export default function Header() {
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
/> />
</svg> </svg>
</motion.button> </m.button>
<motion.button <m.button
className="p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm" className="p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm"
whileHover={{ scale: 1.1, rotate: -5 }} whileHover={{ scale: 1.1, rotate: -5 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -364,8 +360,8 @@ export default function Header() {
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/> />
</svg> </svg>
</motion.button> </m.button>
<motion.button <m.button
className="p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm" className="p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-w-[40px] min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm"
whileHover={{ scale: 1.1, y: -2 }} whileHover={{ scale: 1.1, y: -2 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -384,23 +380,23 @@ export default function Header() {
d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"
/> />
</svg> </svg>
</motion.button> </m.button>
</div> </div>
</div> </div>
</nav> </nav>
</motion.header> </m.header>
{/* Mobile Menu Overlay - Only visible on mobile */} {/* Mobile Menu Overlay - Only visible on mobile */}
<AnimatePresence> <AnimatePresence>
{isMobileMenuOpen && ( {isMobileMenuOpen && (
<motion.div <m.div
className="mobile-menu fixed inset-0 z-50 bg-black/50 backdrop-blur-sm md:hidden" className="mobile-menu fixed inset-0 z-50 bg-black/50 backdrop-blur-sm md:hidden"
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
> >
<motion.div <m.div
className="absolute top-[72px] left-0 right-0 bg-white/95 backdrop-blur-md border-t border-white/20 shadow-premium" className="absolute top-[72px] left-0 right-0 bg-white/95 backdrop-blur-md border-t border-white/20 shadow-premium"
initial={{ opacity: 0, y: -20 }} initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
@ -410,7 +406,7 @@ export default function Header() {
<div className="px-6 py-8 space-y-6"> <div className="px-6 py-8 space-y-6">
{/* Mobile Navigation Links */} {/* Mobile Navigation Links */}
<div className="space-y-4"> <div className="space-y-4">
<motion.a <m.a
href={pathname === "/about" ? "/" : "/about"} href={pathname === "/about" ? "/" : "/about"}
className="block text-lg font-renner text-gray-800 hover:text-[#8B4513] transition-all duration-300 py-3 border-b border-gray-100" className="block text-lg font-renner text-gray-800 hover:text-[#8B4513] transition-all duration-300 py-3 border-b border-gray-100"
style={{ style={{
@ -424,8 +420,8 @@ export default function Header() {
whileHover={{ x: 10 }} whileHover={{ x: 10 }}
> >
{pathname === "/about" ? "Home" : "About us"} {pathname === "/about" ? "Home" : "About us"}
</motion.a> </m.a>
<motion.a <m.a
href="https://shop.mozimo.in/" href="https://shop.mozimo.in/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -441,8 +437,8 @@ export default function Header() {
whileHover={{ x: 10 }} whileHover={{ x: 10 }}
> >
Shop Shop
</motion.a> </m.a>
<motion.a <m.a
href="#categories" href="#categories"
className="block text-lg font-renner text-gray-800 hover:text-[#8B4513] transition-all duration-300 py-3 border-b border-gray-100" className="block text-lg font-renner text-gray-800 hover:text-[#8B4513] transition-all duration-300 py-3 border-b border-gray-100"
style={{ style={{
@ -456,11 +452,11 @@ export default function Header() {
whileHover={{ x: 10 }} whileHover={{ x: 10 }}
> >
Categories Categories
</motion.a> </m.a>
</div> </div>
</div> </div>
</motion.div> </m.div>
</motion.div> </m.div>
)} )}
</AnimatePresence> </AnimatePresence>
</> </>

View File

@ -1,4 +1,4 @@
import { motion } from "framer-motion"; import { m } from "framer-motion";
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
export default function MobileFooter() { export default function MobileFooter() {
@ -11,7 +11,7 @@ export default function MobileFooter() {
<div className="px-4 py-8 flex flex-col gap-8 relative z-10"> <div className="px-4 py-8 flex flex-col gap-8 relative z-10">
{/* Logo at top */} {/* Logo at top */}
<motion.div <m.div
className="flex" className="flex"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -26,12 +26,12 @@ export default function MobileFooter() {
height={80} height={80}
layout="fixed" layout="fixed"
/> />
</motion.div> </m.div>
{/* Two column layout */} {/* Two column layout */}
<div className="grid grid-cols-2 gap-6"> <div className="grid grid-cols-2 gap-6">
{/* Left Column - Contact Info */} {/* Left Column - Contact Info */}
<motion.div <m.div
className="space-y-4" className="space-y-4"
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -60,7 +60,7 @@ export default function MobileFooter() {
fontFamily: "Renner*", fontFamily: "Renner*",
}} }}
> >
<motion.div <m.div
className="flex items-center space-x-2" className="flex items-center space-x-2"
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -76,8 +76,8 @@ export default function MobileFooter() {
<path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" /> <path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" />
</svg> </svg>
<span className="text-xs md:text-sm">0172-4045414</span> <span className="text-xs md:text-sm">0172-4045414</span>
</motion.div> </m.div>
<motion.div <m.div
className="flex items-start space-x-2" className="flex items-start space-x-2"
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -99,7 +99,7 @@ export default function MobileFooter() {
<span className="text-xs md:text-sm"> <span className="text-xs md:text-sm">
SCO 8, Inner Market, 9-D, Sector 9, Chandigarh, 160009 SCO 8, Inner Market, 9-D, Sector 9, Chandigarh, 160009
</span> </span>
</motion.div> </m.div>
</div> </div>
{/* Find Us On */} {/* Find Us On */}
@ -118,7 +118,7 @@ export default function MobileFooter() {
</h3> </h3>
<div className="flex"> <div className="flex">
{/* Social icons with original animations */} {/* Social icons with original animations */}
<motion.a <m.a
href="https://www.facebook.com/share/16vPmvcQV7/?mibextid=wwXIfr" href="https://www.facebook.com/share/16vPmvcQV7/?mibextid=wwXIfr"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -138,8 +138,8 @@ export default function MobileFooter() {
> >
<path d="M22.675 0h-21.35C.595 0 0 .592 0 1.326v21.348C0 23.408.595 24 1.325 24h11.495v-9.294H9.692v-3.622h3.128V8.413c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.797.143v3.24l-1.918.001c-1.504 0-1.797.715-1.797 1.763v2.313h3.587l-.467 3.622h-3.12V24h6.116C23.406 24 24 23.408 24 22.674V1.326C24 .592 23.406 0 22.675 0" /> <path d="M22.675 0h-21.35C.595 0 0 .592 0 1.326v21.348C0 23.408.595 24 1.325 24h11.495v-9.294H9.692v-3.622h3.128V8.413c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.797.143v3.24l-1.918.001c-1.504 0-1.797.715-1.797 1.763v2.313h3.587l-.467 3.622h-3.12V24h6.116C23.406 24 24 23.408 24 22.674V1.326C24 .592 23.406 0 22.675 0" />
</svg> </svg>
</motion.a> </m.a>
<motion.a <m.a
href="https://www.instagram.com/mozimo.choc?igsh=bTVzbGo0enV3b3Jn" href="https://www.instagram.com/mozimo.choc?igsh=bTVzbGo0enV3b3Jn"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -159,8 +159,8 @@ export default function MobileFooter() {
> >
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 1.366.062 2.633.334 3.608 1.308.974.974 1.246 2.241 1.308 3.608.058 1.266.069 1.646.069 4.85s-.012 3.584-.07 4.85c-.062 1.366-.334 2.633-1.308 3.608-.974.974-2.241 1.246-3.608 1.308-1.266.058-1.646.069-4.85.069s-3.584-.012-4.85-.07c-1.366-.062-2.633-.334-3.608-1.308-.974-.974-1.246-2.241-1.308-3.608C2.175 15.647 2.163 15.267 2.163 12s.012-3.584.07-4.85c.062-1.366.334-2.633 1.308-3.608C4.515 2.567 5.782 2.295 7.148 2.233 8.414 2.175 8.794 2.163 12 2.163zm0-2.163C8.741 0 8.332.012 7.052.07 5.771.128 4.659.334 3.678 1.315c-.98.98-1.187 2.092-1.245 3.373C2.012 5.668 2 6.077 2 12c0 5.923.012 6.332.07 7.612.058 1.281.265 2.393 1.245 3.373.98.98 2.092 1.187 3.373 1.245C8.332 23.988 8.741 24 12 24s3.668-.012 4.948-.07c1.281-.058 2.393-.265 3.373-1.245.98-.98 1.187-2.092 1.245-3.373.058-1.28.07-1.689.07-7.612 0-5.923-.012-6.332-.07-7.612-.058-1.281-.265-2.393-1.245-3.373-.98-.98-2.092-1.187-3.373-1.245C15.668.012 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zm0 10.162a3.999 3.999 0 1 1 0-7.998 3.999 3.999 0 0 1 0 7.998zm6.406-11.845a1.44 1.44 0 1 0 0 2.88 1.44 1.44 0 0 0 0-2.88z" /> <path d="M12 2.163c3.204 0 3.584.012 4.85.07 1.366.062 2.633.334 3.608 1.308.974.974 1.246 2.241 1.308 3.608.058 1.266.069 1.646.069 4.85s-.012 3.584-.07 4.85c-.062 1.366-.334 2.633-1.308 3.608-.974.974-2.241 1.246-3.608 1.308-1.266.058-1.646.069-4.85.069s-3.584-.012-4.85-.07c-1.366-.062-2.633-.334-3.608-1.308-.974-.974-1.246-2.241-1.308-3.608C2.175 15.647 2.163 15.267 2.163 12s.012-3.584.07-4.85c.062-1.366.334-2.633 1.308-3.608C4.515 2.567 5.782 2.295 7.148 2.233 8.414 2.175 8.794 2.163 12 2.163zm0-2.163C8.741 0 8.332.012 7.052.07 5.771.128 4.659.334 3.678 1.315c-.98.98-1.187 2.092-1.245 3.373C2.012 5.668 2 6.077 2 12c0 5.923.012 6.332.07 7.612.058 1.281.265 2.393 1.245 3.373.98.98 2.092 1.187 3.373 1.245C8.332 23.988 8.741 24 12 24s3.668-.012 4.948-.07c1.281-.058 2.393-.265 3.373-1.245.98-.98 1.187-2.092 1.245-3.373.058-1.28.07-1.689.07-7.612 0-5.923-.012-6.332-.07-7.612-.058-1.281-.265-2.393-1.245-3.373-.98-.98-2.092-1.187-3.373-1.245C15.668.012 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zm0 10.162a3.999 3.999 0 1 1 0-7.998 3.999 3.999 0 0 1 0 7.998zm6.406-11.845a1.44 1.44 0 1 0 0 2.88 1.44 1.44 0 0 0 0-2.88z" />
</svg> </svg>
</motion.a> </m.a>
<motion.a <m.a
href="https://www.youtube.com/@MozimoChocolates" href="https://www.youtube.com/@MozimoChocolates"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -180,8 +180,8 @@ export default function MobileFooter() {
> >
<path d="M23.498 6.186a2.994 2.994 0 0 0-2.107-2.117C19.163 3.5 12 3.5 12 3.5s-7.163 0-9.391.569A2.994 2.994 0 0 0 .502 6.186C0 8.413 0 12 0 12s0 3.587.502 5.814a2.994 2.994 0 0 0 2.107 2.117C4.837 20.5 12 20.5 12 20.5s7.163 0 9.391-.569a2.994 2.994 0 0 0 2.107-2.117C24 15.587 24 12 24 12s0-3.587-.502-5.814zM9.545 15.568V8.432l6.545 3.568-6.545 3.568z" /> <path d="M23.498 6.186a2.994 2.994 0 0 0-2.107-2.117C19.163 3.5 12 3.5 12 3.5s-7.163 0-9.391.569A2.994 2.994 0 0 0 .502 6.186C0 8.413 0 12 0 12s0 3.587.502 5.814a2.994 2.994 0 0 0 2.107 2.117C4.837 20.5 12 20.5 12 20.5s7.163 0 9.391-.569a2.994 2.994 0 0 0 2.107-2.117C24 15.587 24 12 24 12s0-3.587-.502-5.814zM9.545 15.568V8.432l6.545 3.568-6.545 3.568z" />
</svg> </svg>
</motion.a> </m.a>
<motion.a <m.a
href="https://www.linkedin.com/company/mozimochocolates/" href="https://www.linkedin.com/company/mozimochocolates/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -201,14 +201,14 @@ export default function MobileFooter() {
> >
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.761 0 5-2.239 5-5v-14c0-2.761-2.239-5-5-5zm-11 19h-3v-10h3v10zm-1.5-11.268c-.966 0-1.75-.784-1.75-1.75s.784-1.75 1.75-1.75 1.75.784 1.75 1.75-.784 1.75-1.75 1.75zm15.5 11.268h-3v-5.604c0-1.337-.025-3.063-1.868-3.063-1.868 0-2.154 1.459-2.154 2.967v5.7h-3v-10h2.881v1.367h.041c.401-.761 1.381-1.563 2.841-1.563 3.039 0 3.6 2.001 3.6 4.601v5.595z" /> <path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.761 0 5-2.239 5-5v-14c0-2.761-2.239-5-5-5zm-11 19h-3v-10h3v10zm-1.5-11.268c-.966 0-1.75-.784-1.75-1.75s.784-1.75 1.75-1.75 1.75.784 1.75 1.75-.784 1.75-1.75 1.75zm15.5 11.268h-3v-5.604c0-1.337-.025-3.063-1.868-3.063-1.868 0-2.154 1.459-2.154 2.967v5.7h-3v-10h2.881v1.367h.041c.401-.761 1.381-1.563 2.841-1.563 3.039 0 3.6 2.001 3.6 4.601v5.595z" />
</svg> </svg>
</motion.a> </m.a>
{/* Other social icons... */} {/* Other social icons... */}
</div> </div>
</div> </div>
</motion.div> </m.div>
{/* Right Column - Know more and other links */} {/* Right Column - Know more and other links */}
<motion.div <m.div
className="space-y-4" className="space-y-4"
initial={{ opacity: 0, x: 10 }} initial={{ opacity: 0, x: 10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -247,7 +247,7 @@ export default function MobileFooter() {
"Locate our store", "Locate our store",
"Bulk Ordering", "Bulk Ordering",
].map((link, index) => ( ].map((link, index) => (
<motion.li <m.li
key={link} key={link}
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -261,15 +261,15 @@ export default function MobileFooter() {
{link} {link}
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span> <span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#8B4513] to-[#DAA520] transition-all duration-300 group-hover:w-full"></span>
</a> </a>
</motion.li> </m.li>
))} ))}
</ul> </ul>
</div> </div>
</motion.div> </m.div>
</div> </div>
{/* Copyright - original styling preserved */} {/* Copyright - original styling preserved */}
<motion.div <m.div
className="border-t border-[#e5e5e5] mt-6 md:mt-8 pt-6 md:pt-8 text-center text-sm font-renner relative z-10" className="border-t border-[#e5e5e5] mt-6 md:mt-8 pt-6 md:pt-8 text-center text-sm font-renner relative z-10"
style={{ style={{
fontWeight: 300, fontWeight: 300,
@ -286,7 +286,7 @@ export default function MobileFooter() {
<p className="text-xs md:text-sm"> <p className="text-xs md:text-sm">
&copy; 2024 Mozimo. All rights reserved. &copy; 2024 Mozimo. All rights reserved.
</p> </p>
</motion.div> </m.div>
</div> </div>
</footer> </footer>
); );

View File

@ -1,5 +1,5 @@
import { useState, useCallback, useMemo, useEffect } from "react"; import { useState, useCallback, useMemo, useEffect } from "react";
import { LazyMotion, domAnimation, m } from "framer-motion"; import { m } from "framer-motion";
import Section from "@/components/Section"; import Section from "@/components/Section";
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
@ -94,7 +94,7 @@ import { Image } from "@unpic/react";
return ( return (
<Section background="white" id="categories"> <Section background="white" id="categories">
<LazyMotion features={domAnimation}>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-0 items-stretch min-h-[400px] md:min-h-[500px]"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-0 items-stretch min-h-[400px] md:min-h-[500px]">
{/* Left */} {/* Left */}
<div className="flex flex-col justify-center h-full bg-white px-4 md:px-8 py-6 md:py-12 order-2 lg:order-1"> <div className="flex flex-col justify-center h-full bg-white px-4 md:px-8 py-6 md:py-12 order-2 lg:order-1">
@ -123,7 +123,7 @@ import { Image } from "@unpic/react";
</div> </div>
</div> </div>
</div> </div>
</LazyMotion>
</Section> </Section>
); );
} }

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { motion } from 'framer-motion'; import { m } from 'framer-motion';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
interface SectionProps { interface SectionProps {
@ -35,7 +35,7 @@ export default function Section({
return ( return (
<section id={id} className={classes}> <section id={id} className={classes}>
<motion.div <m.div
initial={{ opacity: 0, y: 60 }} initial={{ opacity: 0, y: 60 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ transition={{
@ -50,7 +50,7 @@ export default function Section({
<div className="absolute top-0 left-1/2 transform -translate-x-1/2 w-16 md:w-24 h-1 bg-gradient-to-r from-transparent via-[#8B4513]/30 to-transparent opacity-0 hover:opacity-100 transition-opacity duration-500" /> <div className="absolute top-0 left-1/2 transform -translate-x-1/2 w-16 md:w-24 h-1 bg-gradient-to-r from-transparent via-[#8B4513]/30 to-transparent opacity-0 hover:opacity-100 transition-opacity duration-500" />
{children} {children}
</motion.div> </m.div>
</section> </section>
); );
} }

View File

@ -1,5 +1,5 @@
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { motion } from "framer-motion"; import { m } from "framer-motion";
import { Image } from "@unpic/react"; import { Image } from "@unpic/react";
import Section from "./Section"; import Section from "./Section";
import { useInstagram } from "@/hooks/useInstagram"; import { useInstagram } from "@/hooks/useInstagram";
@ -60,7 +60,7 @@ export default function SocialMedia() {
return ( return (
<Section background="cream" padding="md"> <Section background="cream" padding="md">
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -79,7 +79,7 @@ export default function SocialMedia() {
> >
Follow us on Instagram Follow us on Instagram
</h2> </h2>
</motion.div> </m.div>
<div <div
ref={scrollRef} ref={scrollRef}
@ -88,7 +88,7 @@ export default function SocialMedia() {
{loading ? ( {loading ? (
<> <>
{[1, 2, 3].map((i) => ( {[1, 2, 3].map((i) => (
<motion.div <m.div
key={i} key={i}
className="bg-white rounded-lg shadow-premium h-64 md:h-96 skeleton snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto" className="bg-white rounded-lg shadow-premium h-64 md:h-96 skeleton snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
@ -101,7 +101,7 @@ export default function SocialMedia() {
) : error ? ( ) : error ? (
isTokenExpired ? ( isTokenExpired ? (
<div className="col-span-full text-center py-12 sm:col-span-2 lg:col-span-3"> <div className="col-span-full text-center py-12 sm:col-span-2 lg:col-span-3">
<motion.div <m.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
@ -121,12 +121,12 @@ export default function SocialMedia() {
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
In the meantime, follow us @mozimo_chocolate In the meantime, follow us @mozimo_chocolate
</div> </div>
</motion.div> </m.div>
</div> </div>
) : ( ) : (
<> <>
{/* Left Block - Chocolate Spread Jar */} {/* Left Block - Chocolate Spread Jar */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -135,7 +135,7 @@ export default function SocialMedia() {
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
> >
<div className="relative h-64 md:h-96 bg-gradient-to-br from-amber-50 to-orange-50 flex items-center justify-center"> <div className="relative h-64 md:h-96 bg-gradient-to-br from-amber-50 to-orange-50 flex items-center justify-center">
<motion.div <m.div
className="text-center" className="text-center"
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
@ -151,12 +151,12 @@ export default function SocialMedia() {
SINGLE ORIGIN HAZELNUT SPREAD 45% SINGLE ORIGIN HAZELNUT SPREAD 45%
</div> </div>
</div> </div>
</motion.div> </m.div>
</div> </div>
</motion.div> </m.div>
{/* Middle Block - Magazine Article */} {/* Middle Block - Magazine Article */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -191,10 +191,10 @@ export default function SocialMedia() {
</p> </p>
</div> </div>
</div> </div>
</motion.div> </m.div>
{/* Right Block - World Chocolate Day */} {/* Right Block - World Chocolate Day */}
<motion.div <m.div
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
viewport={{ once: true, margin: "-50px" }} viewport={{ once: true, margin: "-50px" }}
@ -218,12 +218,12 @@ export default function SocialMedia() {
</p> </p>
</div> </div>
</div> </div>
</motion.div> </m.div>
</> </>
) )
) : ( ) : (
posts?.slice(0, 3).map((post, index) => ( posts?.slice(0, 3).map((post, index) => (
<motion.div <m.div
key={post.id} key={post.id}
initial="initial" initial="initial"
whileInView="animate" whileInView="animate"
@ -273,7 +273,7 @@ export default function SocialMedia() {
</div> </div>
</div> </div>
</a> </a>
</motion.div> </m.div>
)) ))
)} )}
</div> </div>

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@ -13,15 +13,10 @@
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }