Merge pull request #2 from ABasral/fix/align

design changes
This commit is contained in:
Bharti1205
2025-08-19 02:20:34 +05:30
committed by GitHub
5 changed files with 578 additions and 436 deletions

View File

@ -7,9 +7,9 @@ import Section from "@/components/Section";
import Button from "@/components/Button";
import BannerSlider from "@/components/BannerSlider";
import Image from "next/image";
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { AnimatePresence } from "framer-motion";
import { useInstagram } from "@/hooks/useInstagram";
import { useState, useEffect, useRef, useCallback } from "react";
import ProductCategory from "@/components/ProductCategory";
import SocialMedia from "@/components/SocialMedia";
// Premium animation variants
const fadeInUp = {
@ -46,14 +46,6 @@ const categories = [
"Dragees",
"Gelatos",
];
const categoryImages = [
"/categories/c1.svg",
"/categories/c2.svg",
"/categories/c3.svg",
"/categories/c4.svg",
"/categories/c5.svg",
"/categories/c6.svg",
];
const partnerImages = [
"/partners/p1.svg",
@ -127,14 +119,18 @@ export default function Home() {
return () => clearInterval(timer);
}, []);
const { posts, loading, error, isTokenExpired } = useInstagram();
// Memoize expensive components
const BrandStorySection = useMemo(
() => (
return (
<div className="min-h-screen bg-white font-renner">
<Header />
<BannerSlider />
{/* Brand Story */}
<Section background="white" id="about">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 md:gap-12 lg:gap-16 items-center">
{/* Left Column - Text */}
<div className="grid grid-cols-1 lg:grid-cols-2 items-center">
{/* Brand Story - Left Text */}
<motion.div
initial="initial"
whileInView="animate"
@ -143,7 +139,7 @@ export default function Home() {
className="space-y-6 md:space-y-8 order-2 lg:order-1 px-4 md:px-0" // ← Added mobile padding
>
<motion.h2
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-moneta text-gradient"
className="text-3xl md:text-4xl lg:text-5xl font-bold text-[#3C2A21] font-moneta text-gradient p-4 md:p-0"
style={{
fontWeight: 400,
fontSize: "clamp(32px, 5vw, 58px)",
@ -208,21 +204,17 @@ export default function Home() {
</Button>
</motion.div>
</motion.div>
{/* Right Column - Image */}
{/* Brand Story - Right Image */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="relative order-1 lg:order-2 w-screen md:w-auto -mx-4 md:mx-0" // ← Made full-width on mobile
className="relative order-1 lg:order-2 w-screen md:w-auto -mx-4 md:mx-0"
>
<motion.div
className="relative w-full h-[300px] md:h-[400px] lg:h-[500px] rounded-none md:rounded-xl overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500" // ← Removed rounded corners on mobile
whileHover={{
scale: 1.02,
transition: { duration: 0.3 },
}}
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 } }}
>
<Image
src="/bst/bs1.svg"
@ -232,40 +224,21 @@ export default function Home() {
draggable={false}
quality={85}
/>
{/* Premium overlay for depth */}
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
</div>
</Section>
),
[]
);
return (
<div className="min-h-screen bg-white font-renner">
<Header />
<BannerSlider />
{BrandStorySection}
{/* Chocolate Tempering Section */}
<Section background="white">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 md:gap-12 lg:gap-16 items-center">
{/* Left Column - Image */}
{/* Chocolate Tempering - Left Video */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="relative order-1 lg:order-1 w-screen md:w-auto -mx-4 md:mx-0" // ← Added for mobile edge-to-edge
className="relative order-3 lg:order-3 w-screen md:w-auto -mx-4 md:mx-0 py-4 md:py-0"
>
<motion.div
className="relative w-full h-[400px] md:h-[500px] lg:h-[700px] rounded-none md:rounded-xl overflow-hidden shadow-premium hover:shadow-premium-hover transition-all duration-500" // ← Changed to rounded-none for mobile
whileHover={{
scale: 1.02,
transition: { duration: 0.3 },
}}
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 } }}
>
<video
src="/bst/v1.mp4"
@ -276,20 +249,18 @@ export default function Home() {
playsInline
preload="auto"
aria-label="Chocolate pouring from metallic spout - Mozimo chocolate tempering process"
style={{ display: "block" }}
/>
{/* Premium overlay for depth */}
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</motion.div>
{/* Right Column - Text */}
{/* Chocolate Tempering - Right Text */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="space-y-4 md:space-y-6 order-2 lg:order-2 px-4 md:px-0" // ← Added px-4 for mobile padding
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">
<motion.h2
@ -316,6 +287,7 @@ export default function Home() {
letterSpacing: "0%",
fontStyle: "italic",
color: "#703133",
fontFamily: "Renner*",
}}
whileHover={{ scale: 1.02 }}
transition={{ duration: 0.3 }}
@ -324,7 +296,6 @@ export default function Home() {
</motion.h3>
</div>
{/* Premium decorative line */}
<motion.div
className="w-100 h-px bg-gradient-to-r from-transparent via-[#8B4513] to-transparent"
initial={{ scaleX: 0 }}
@ -442,318 +413,9 @@ export default function Home() {
</motion.div>
</Section>
{/* Product Categories Section */}
<Section background="white" id="categories">
<div
ref={categoriesSectionRef}
className="grid grid-cols-1 lg:grid-cols-2 gap-0 items-stretch min-h-[400px] md:min-h-[500px]"
>
{/* Left Column - Categories List */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
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"
>
<ul className="space-y-4 md:space-y-6">
{categories.map((category, idx) => (
<motion.li
key={category}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: idx * 0.1 }}
viewport={{ once: true }}
>
<button
className={`group flex items-center gap-4 transition-all duration-700 ease-out w-full text-left font-moneta hover:scale-105
${
idx === currentCategory
? "text-[#703133] font-normal"
: "text-[#703133] opacity-60 font-light"
}
`}
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: idx === currentCategory ? 400 : 300,
fontSize:
idx === currentCategory
? "clamp(22px, 5vw, 64px)"
: "clamp(18px, 4vw, 56px)",
lineHeight: "120%",
letterSpacing: "0%",
transition: "all 0.7s cubic-bezier(0.25,0.46,0.45,0.94)",
}}
onClick={() => setCurrentCategory(idx)}
onMouseEnter={() => setCurrentCategory(idx)}
tabIndex={-1}
>
{/* Show only on mobile */}
<div className="block lg:hidden w-10 h-10 rounded-full overflow-hidden shrink-0">
<img
src={categoryImages[idx]}
alt={category}
className="w-full h-full object-cover"
draggable={false}
/>
</div>
{/* Category name */}
<span className="text-left">{category}</span>
</button>
</motion.li>
))}
</ul>
</motion.div>
{/* Right Column - Large Category Image (desktop only) */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="relative hidden lg:flex items-center justify-center h-full min-h-[300px] md:min-h-[400px] bg-white order-1 lg:order-2"
>
<AnimatePresence mode="wait">
<motion.img
key={currentCategory}
src={categoryImages[currentCategory]}
alt={categories[currentCategory]}
initial={{ opacity: 0, scale: 1.05 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.7, ease: [0.25, 0.46, 0.45, 0.94] }}
className="w-full h-full object-cover rounded-2xl shadow-premium hover:shadow-premium-hover transition-all duration-500 img-premium"
draggable={false}
style={{ maxHeight: "600px", maxWidth: "100%" }}
/>
</AnimatePresence>
</motion.div>
</div>
</Section>
<ProductCategory />
{/* Social Media Call to Action */}
<Section background="cream" padding="md">
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-center mb-8 md:mb-12"
>
<h2
className="text-3xl md:text-4xl lg:text-5xl font-moneta text-[#703133] mb-6 md:mb-8 text-gradient"
style={{
fontWeight: 400,
fontSize: "clamp(28px, 5vw, 48px)",
lineHeight: "120%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
>
Follow us on Instagram
</h2>
</motion.div>
<div className="flex overflow-x-auto no-scrollbar snap-x snap-mandatory gap-4 px-4 sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:gap-6 md:gap-8 max-w-7xl mx-auto">
{loading ? (
<>
{[1, 2, 3].map((i) => (
<motion.div
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"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: i * 0.1 }}
viewport={{ once: true }}
/>
))}
</>
) : error ? (
isTokenExpired ? (
<div className="col-span-full text-center py-12 sm:col-span-2 lg:col-span-3">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="max-w-md mx-auto"
>
<div className="w-16 h-16 bg-gradient-to-br from-amber-200 to-orange-300 rounded-full mx-auto mb-4 flex items-center justify-center shadow-premium">
<span className="text-2xl">🔗</span>
</div>
<h3 className="text-xl font-semibold text-[#703133] mb-2">
Instagram Connection Expired
</h3>
<p className="text-gray-600 mb-4">
Our Instagram connection needs to be refreshed. We&apos;re
working on getting it back up!
</p>
<div className="text-sm text-gray-500">
In the meantime, follow us @mozimo_chocolate
</div>
</motion.div>
</div>
) : (
<>
{/* Left Block - Chocolate Spread Jar */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2 snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto"
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">
<motion.div
className="text-center"
whileHover={{ scale: 1.05 }}
transition={{ duration: 0.3 }}
>
<div className="w-32 h-32 bg-gradient-to-br from-amber-200 to-orange-300 rounded-full mx-auto mb-4 flex items-center justify-center shadow-premium">
<span className="text-4xl">🍫</span>
</div>
<div className="bg-white rounded-lg p-4 mx-4 shadow-premium">
<div className="font-semibold text-[#703133] text-lg">
MOZIMO
</div>
<div className="text-sm text-gray-600">
SINGLE ORIGIN HAZELNUT SPREAD 45%
</div>
</div>
</motion.div>
</div>
</motion.div>
{/* Middle Block - Magazine Article */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2 snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto"
whileHover={{ scale: 1.02 }}
>
<div className="p-4 md:p-6">
<div className="bg-red-600 h-32 rounded-lg mb-4 flex items-center justify-center shadow-premium">
<span className="text-white text-2xl">🍫</span>
</div>
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="bg-gray-200 h-20 rounded-lg flex items-center justify-center shadow-premium">
<span className="text-lg">🍰</span>
</div>
<div className="bg-gray-200 h-20 rounded-lg flex items-center justify-center shadow-premium">
<span className="text-lg">👨🍳</span>
</div>
</div>
<div className="text-xs text-gray-700 space-y-2 leading-relaxed">
<p>
<strong>By RUPALI DEAN</strong>
</p>
<p>
<strong>
MOZIMO&apos;S CHOCOLATE PIE BY PRIYANKA GUPTA
</strong>
</p>
<p>
isolat[ing] beans to not only single ori- but also to
single farms so as to express unique complexity.
</p>
</div>
</div>
</motion.div>
{/* Right Block - World Chocolate Day */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2 snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto"
whileHover={{ scale: 1.02 }}
>
<div className="p-4 md:p-6 text-center">
<div className="mb-4">
<h3 className="text-2xl font-bold text-[#8B4513] mb-2 text-gradient">
World Chocolate Day
</h3>
<p className="text-gray-600">
Celebrating the art of chocolate making
</p>
</div>
<div className="bg-gradient-to-br from-amber-100 to-orange-100 rounded-lg p-4 shadow-premium">
<div className="text-4xl mb-2">🍫</div>
<p className="text-sm text-gray-700">
Join us in celebrating the world&apos;s favorite treat
</p>
</div>
</div>
</motion.div>
</>
)
) : (
posts?.slice(0, 3).map((post, index) => (
<motion.div
key={post.id}
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={
index === 0
? fadeInLeft
: index === 1
? fadeInUp
: fadeInRight
}
className="snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2"
whileHover={{ scale: 1.02 }}
>
<a
href={post.permalink}
target="_blank"
rel="noopener noreferrer"
className="block"
>
<div className="relative h-64 md:h-96">
<Image
src={post.mediaUrl}
alt={post.caption || "Instagram post"}
fill
className="object-cover img-premium"
draggable={false}
quality={85}
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "/bst/bs1.svg";
}}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/30 via-transparent to-transparent" />
{post.mediaType === "VIDEO" && (
<div className="absolute top-4 right-4 bg-black/50 rounded-full p-2">
<svg
className="w-4 h-4 text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M8 5v10l8-5-8-5z" />
</svg>
</div>
)}
<div className="absolute bottom-4 left-4 right-4">
<div className="glass rounded-lg p-3 border border-white/20">
<p className="text-sm text-gray-800 line-clamp-2">
{post.caption?.slice(0, 100)}...
</p>
</div>
</div>
</div>
</a>
</motion.div>
))
)}
</div>
</Section>
<SocialMedia />
{/* In the Spotlight Section */}
<Section background="white">
@ -785,7 +447,6 @@ export default function Home() {
animate={{ x: `-${spotlightIndex * 152}px` }}
transition={{ type: "spring", stiffness: 80, damping: 18 }}
>
{/* Create 12 tiles (3 sets of 4 logos) */}
{Array(12)
.fill(0)
.map((_, i) => {
@ -795,7 +456,10 @@ export default function Home() {
<motion.div
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"
style={{ boxShadow: "0 2px 12px 0 rgba(60,42,33,0.10)" }}
style={{
boxShadow: "0 4px 20px 0 rgba(60,42,33,0.15)", // Increased blur and opacity
margin: "8px", // Added margin to give space for shadow
}}
whileHover={{
scale: 1.1,
rotate: 5,

View File

@ -1,14 +1,14 @@
'use client';
"use client";
import { useState, useEffect, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import Image from 'next/image';
import { useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import Image from "next/image";
const banners = [
{ id: 1, src: '/banners/b1.svg', alt: 'Mozimo Chocolate Banner 1' },
{ id: 2, src: '/banners/b2.svg', alt: 'Mozimo Chocolate Banner 2' },
{ id: 3, src: '/banners/b3.svg', alt: 'Mozimo Chocolate Banner 3' },
{ id: 4, src: '/banners/b4.svg', alt: 'Mozimo Chocolate Banner 4' },
{ id: 1, src: "/banners/b1.svg", alt: "Mozimo Chocolate Banner 1" },
{ id: 2, src: "/banners/b3.svg", alt: "Mozimo Chocolate Banner 2" },
{ id: 3, src: "/banners/b2.svg", alt: "Mozimo Chocolate Banner 3" },
{ id: 4, src: "/banners/b4.svg", alt: "Mozimo Chocolate Banner 4" },
];
export default function BannerSlider() {
@ -17,7 +17,7 @@ export default function BannerSlider() {
const [showArrows, setShowArrows] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// Auto-advance slider
useEffect(() => {
const timer = setInterval(() => {
@ -37,7 +37,7 @@ export default function BannerSlider() {
const goToPrevious = () => {
setDirection(-1);
setCurrentIndex((prevIndex) =>
setCurrentIndex((prevIndex) =>
prevIndex === 0 ? banners.length - 1 : prevIndex - 1
);
};
@ -49,7 +49,7 @@ export default function BannerSlider() {
const slideVariants = {
enter: (direction: number) => ({
x: direction > 0 ? '100%' : '-100%',
x: direction > 0 ? "100%" : "-100%",
opacity: 1,
}),
center: {
@ -57,9 +57,9 @@ export default function BannerSlider() {
opacity: 1,
},
exit: (direction: number) => ({
x: direction < 0 ? '100%' : '-100%',
x: direction < 0 ? "100%" : "-100%",
opacity: 1,
})
}),
};
const swipeConfidenceThreshold = 10000;
@ -76,7 +76,7 @@ export default function BannerSlider() {
};
return (
<div
<div
ref={containerRef}
className="relative w-full h-[calc(100vh-80px)] md:h-screen overflow-hidden bg-gradient-to-br from-[#1a0f0a] to-[#2d1810]"
onMouseEnter={() => {
@ -105,7 +105,7 @@ export default function BannerSlider() {
exit="exit"
transition={{
x: { type: "spring", stiffness: 300, damping: 30 },
duration: 0.4
duration: 0.4,
}}
drag="x"
dragConstraints={{ left: 0, right: 0 }}
@ -128,41 +128,43 @@ export default function BannerSlider() {
className="object-cover img-premium"
draggable={false}
priority={currentIndex === 0}
loading={currentIndex === 0 ? 'eager' : 'lazy'}
loading={currentIndex === 0 ? "eager" : "lazy"}
sizes="100vw"
quality={85}
/>
{/* Premium overlay with gradient */}
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
</motion.div>
</AnimatePresence>
{/* Premium Shop Now Button */}
<div className="absolute bottom-16 md:bottom-20 left-1/2 transform -translate-x-1/2 z-10">
<motion.button
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
whileHover={{
scale: 1.05,
y: -2,
transition: { duration: 0.3 }
}}
whileTap={{ scale: 0.95 }}
className="glass text-[#3C2A21] px-8 md:px-12 py-4 md:py-5 rounded-full font-semibold text-base md:text-lg shadow-premium hover:shadow-premium-hover transition-all duration-300 font-renner border border-white/20 hover:border-white/40"
style={{
fontWeight: 300,
fontSize: '16px',
lineHeight: '100%',
letterSpacing: '0%',
fontFamily: 'Renner'
}}
>
<span className="relative z-10">Shop Now</span>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full hover:translate-x-full transition-transform duration-700 ease-out rounded-full" />
</motion.button>
</div>
{currentIndex === 0 && (
<div className="absolute bottom-16 md:bottom-20 left-1/2 transform -translate-x-1/2 z-10">
<motion.button
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
whileHover={{
scale: 1.05,
y: -2,
transition: { duration: 0.3 },
}}
whileTap={{ scale: 0.95 }}
className="bg-white text-[#3C2A21] px-8 md:px-12 py-4 md:py-5 rounded-full font-semibold text-base md:text-lg shadow-premium hover:shadow-premium-hover transition-all duration-300 font-renner border border-white/20 hover:border-white/40"
style={{
fontWeight: 300,
fontFamily: "Renner*",
fontSize: "22px",
lineHeight: "100%",
letterSpacing: "0%",
}}
>
<span className="relative z-10">Shop Now</span>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full hover:translate-x-full transition-transform duration-700 ease-out rounded-full" />
</motion.button>
</div>
)}
{/* Premium Navigation Arrows - Hidden on mobile */}
<AnimatePresence>
@ -179,8 +181,18 @@ export default function BannerSlider() {
whileTap={{ scale: 0.95 }}
aria-label="Previous banner"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 19l-7-7 7-7"
/>
</svg>
</motion.button>
@ -195,8 +207,18 @@ export default function BannerSlider() {
whileTap={{ scale: 0.95 }}
aria-label="Next banner"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
</motion.button>
</>
@ -212,8 +234,18 @@ export default function BannerSlider() {
whileTap={{ scale: 0.95 }}
aria-label="Previous banner"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 19l-7-7 7-7"
/>
</svg>
</motion.button>
@ -224,22 +256,32 @@ export default function BannerSlider() {
whileTap={{ scale: 0.95 }}
aria-label="Next banner"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
</motion.button>
</div>
{/* Premium Dots Indicator */}
<div className="absolute bottom-6 md:bottom-8 left-1/2 transform -translate-x-1/2 z-20 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) => (
<motion.button
key={index}
onClick={() => goToSlide(index)}
className={`w-2 h-2 md:w-3 md:h-3 rounded-full transition-all duration-300 ${
index === currentIndex
? 'bg-white scale-110 shadow-glow'
: 'bg-white/50 hover:bg-white/70 hover:scale-110'
? "bg-white scale-110 shadow-glow"
: "bg-white/50 hover:bg-white/70 hover:scale-110"
}`}
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
@ -260,4 +302,4 @@ export default function BannerSlider() {
</div>
</div>
);
}
}

View File

@ -60,7 +60,7 @@ export default function Header() {
<>
{/* Mobile Header - Only visible on mobile */}
<motion.header
className="fixed top-0 z-[60] w-full md:hidden"
className="fixed top-0 z-[60] w-full md:hidden p-1 "
initial={false}
animate={{
width: "100%",
@ -75,7 +75,7 @@ export default function Header() {
ease: [0.25, 0.46, 0.45, 0.94],
}}
>
<nav className="relative bg-white border border-white/20 shadow-premium w-full h-full flex items-center px-4">
<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 */}
<div className="absolute left-4 top-1/2 -translate-y-1/2">
<motion.button
@ -136,7 +136,7 @@ export default function Header() {
fill
className="object-contain drop-shadow-lg"
priority
sizes="40px"
sizes="60px"
quality={95}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWGRkqGx0f/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRmknyJckliyjqTzSlT54b6bk+h0R//2Q=="
@ -215,7 +215,7 @@ export default function Header() {
{/* Desktop Header - Only visible on desktop */}
<motion.header
className="fixed top-0 z-50 hidden md:block"
className="fixed top-0 z-50 hidden md:block "
initial={false}
animate={{
width: isScrolled ? "100%" : "1440px",
@ -242,9 +242,9 @@ export default function Header() {
href="#about"
className="text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group"
style={{
fontWeight: 100,
fontFamily: "Renner",
fontSize: "18px",
fontWeight: 300,
fontFamily: "Renner*",
fontSize: "22px",
lineHeight: "100%",
letterSpacing: "0%",
}}
@ -257,9 +257,9 @@ export default function Header() {
href="#shop"
className="text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group"
style={{
fontWeight: 100,
fontFamily: "Renner",
fontSize: "18px",
fontWeight: 300,
fontFamily: "Renner*",
fontSize: "22px",
lineHeight: "100%",
letterSpacing: "0%",
}}
@ -309,9 +309,9 @@ export default function Header() {
<motion.button
className="flex items-center space-x-1 text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group"
style={{
fontWeight: 100,
fontFamily: "Renner",
fontSize: "18px",
fontWeight: 300,
fontFamily: "Renner*",
fontSize: "22px",
lineHeight: "100%",
letterSpacing: "0%",
}}
@ -427,7 +427,7 @@ export default function Header() {
className="block text-lg font-renner text-gray-800 hover:text-[#8B4513] transition-all duration-300 py-3 border-b border-gray-100"
style={{
fontWeight: 300,
fontFamily: "Renner",
fontFamily: "Renner*",
fontSize: "18px",
lineHeight: "100%",
letterSpacing: "0%",
@ -442,7 +442,7 @@ export default function Header() {
className="block text-lg font-renner text-gray-800 hover:text-[#8B4513] transition-all duration-300 py-3 border-b border-gray-100"
style={{
fontWeight: 300,
fontFamily: "Renner",
fontFamily: "Renner*",
fontSize: "18px",
lineHeight: "100%",
letterSpacing: "0%",
@ -457,7 +457,7 @@ export default function Header() {
className="block text-lg font-renner text-gray-800 hover:text-[#8B4513] transition-all duration-300 py-3 border-b border-gray-100"
style={{
fontWeight: 300,
fontFamily: "Renner",
fontFamily: "Renner*",
fontSize: "18px",
lineHeight: "100%",
letterSpacing: "0%",

View File

@ -0,0 +1,152 @@
"use client";
import { useState, useRef, useCallback, useMemo } from "react";
import { AnimatePresence } from "framer-motion";
import { motion } from "framer-motion";
import Section from "@/components/Section";
import Image from "next/image";
export default function ProductCategory() {
const [currentCategory, setCurrentCategory] = useState(0);
const categoriesSectionRef = useRef<HTMLDivElement>(null);
const fadeInLeft = {
initial: { opacity: 0, x: -30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInRight = {
initial: { opacity: 0, x: 30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const categories = [
"Bars",
"Barks",
"Pralines",
"Spreads",
"Dragees",
"Gelatos",
];
const categoryImages = [
"/categories/c1.svg",
"/categories/c2.svg",
"/categories/c3.svg",
"/categories/c4.svg",
"/categories/c5.svg",
"/categories/c6.svg",
];
// Memoize category change handler for better performance
const handleCategoryChange = useCallback((idx: number) => {
setCurrentCategory(idx);
}, []);
// Memoize category buttons to prevent unnecessary re-renders
const categoryButtons = useMemo(() => {
return categories.map((category, idx) => (
<motion.li
key={category}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: idx * 0.05 }} // Reduced delay
viewport={{ once: true }}
>
<button
className={`group flex items-center gap-4 transition-all duration-200 ease-out w-full text-left font-moneta hover:scale-105
${
idx === currentCategory
? "text-[#703133] font-normal"
: "text-[#703133] opacity-60 font-light"
}
`}
style={{
fontFamily: "MonetaSans-Regular",
fontWeight: idx === currentCategory ? 400 : 300,
fontSize:
idx === currentCategory
? "clamp(22px, 5vw, 64px)"
: "clamp(18px, 4vw, 56px)",
lineHeight: "120%",
letterSpacing: "0%",
transition: "all 0.2s cubic-bezier(0.4, 0.0, 0.2, 1)", // Even faster transition
}}
onClick={() => handleCategoryChange(idx)}
onMouseEnter={() => handleCategoryChange(idx)}
onTouchStart={() => handleCategoryChange(idx)} // Add this for mobile touch
tabIndex={-1}
>
{/* Show only on mobile */}
<div className="block lg:hidden w-16 h-16 rounded-full overflow-hidden shrink-0">
<Image
src={categoryImages[idx]}
alt={category}
width={56}
height={56}
className="w-full h-full object-cover"
draggable={false}
quality={85}
sizes="(max-width: 375px) 40px, (max-width: 640px) 44px, (max-width: 768px) 48px, 56px"
/>
</div>
{/* Category name */}
<span className="text-left">{category}</span>
</button>
</motion.li>
));
}, [currentCategory, handleCategoryChange]);
// Memoize the category image component
const categoryImageComponent = useMemo(
() => (
<AnimatePresence mode="wait">
<motion.img
key={currentCategory}
src={categoryImages[currentCategory]}
alt={categories[currentCategory]}
initial={{ opacity: 0, scale: 1.01 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.99 }}
transition={{ duration: 0.25, ease: [0.4, 0.0, 0.2, 1] }} // Super fast transition
className="w-full h-full object-cover rounded-2xl shadow-premium hover:shadow-premium-hover transition-all duration-200 img-premium" // Faster shadow
draggable={false}
style={{ maxHeight: "600px", maxWidth: "100%" }}
/>
</AnimatePresence>
),
[currentCategory]
);
return (
<div className="min-h-screen bg-white font-renner">
{/* Product Categories Section */}
<Section background="white" id="categories">
<div
ref={categoriesSectionRef}
className="grid grid-cols-1 lg:grid-cols-2 gap-0 items-stretch min-h-[400px] md:min-h-[500px]"
>
{/* Left Column - Categories List */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
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"
>
<ul className="space-y-4 md:space-y-6">{categoryButtons}</ul>
</motion.div>
{/* Right Column - Large Category Image (desktop only) */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="relative hidden lg:flex items-center justify-center h-full min-h-[300px] md:min-h-[400px] bg-white order-1 lg:order-2"
>
{categoryImageComponent}
</motion.div>
</div>
</Section>
</div>
);
}

View File

@ -0,0 +1,284 @@
"use client";
import { useEffect, useRef } from "react";
import { motion } from "framer-motion";
import Image from "next/image";
import Section from "./Section";
import { useInstagram } from "@/hooks/useInstagram";
export default function SocialMedia() {
const { posts, loading, error, isTokenExpired } = useInstagram();
const scrollRef = useRef<HTMLDivElement>(null);
const fadeInUp = {
initial: { opacity: 0, y: 30 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInLeft = {
initial: { opacity: 0, x: -30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
const fadeInRight = {
initial: { opacity: 0, x: 30 },
animate: { opacity: 1, x: 0 },
transition: { duration: 0.8, ease: [0.25, 0.46, 0.45, 0.94] },
};
useEffect(() => {
// Only auto-scroll on mobile (<768px)
if (window.innerWidth >= 768) return;
const container = scrollRef.current;
if (!container) return;
let scrollAmount = 0;
const cardWidth = container.firstElementChild?.clientWidth || 300;
const scrollStep = cardWidth + 16; // 16px gap between items
let direction = 1; // 1 = right, -1 = left
const interval = setInterval(() => {
if (!container) return;
scrollAmount += scrollStep * direction;
// Reverse direction if hitting ends
if (
scrollAmount >= container.scrollWidth - container.clientWidth ||
scrollAmount <= 0
) {
direction *= -1;
}
container.scrollTo({
left: scrollAmount,
behavior: "smooth",
});
}, 3000); // scroll every 3s
return () => clearInterval(interval);
}, []);
return (
<Section background="cream" padding="md">
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="text-center mb-8 md:mb-12"
>
<h2
className="text-3xl md:text-4xl lg:text-5xl font-moneta text-[#703133] mb-6 md:mb-8 text-gradient"
style={{
fontWeight: 400,
fontSize: "clamp(28px, 5vw, 48px)",
lineHeight: "120%",
letterSpacing: "0%",
fontFamily: "MonetaSans-Regular",
}}
>
Follow us on Instagram
</h2>
</motion.div>
<div
ref={scrollRef}
className="flex overflow-x-auto no-scrollbar snap-x snap-mandatory gap-4 px-4 sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:gap-6 md:gap-8 max-w-7xl mx-auto"
>
{loading ? (
<>
{[1, 2, 3].map((i) => (
<motion.div
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"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: i * 0.1 }}
viewport={{ once: true }}
/>
))}
</>
) : error ? (
isTokenExpired ? (
<div className="col-span-full text-center py-12 sm:col-span-2 lg:col-span-3">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="max-w-md mx-auto"
>
<div className="w-16 h-16 bg-gradient-to-br from-amber-200 to-orange-300 rounded-full mx-auto mb-4 flex items-center justify-center shadow-premium">
<span className="text-2xl">🔗</span>
</div>
<h3 className="text-xl font-semibold text-[#703133] mb-2">
Instagram Connection Expired
</h3>
<p className="text-gray-600 mb-4">
Our Instagram connection needs to be refreshed. We&apos;re
working on getting it back up!
</p>
<div className="text-sm text-gray-500">
In the meantime, follow us @mozimo_chocolate
</div>
</motion.div>
</div>
) : (
<>
{/* Left Block - Chocolate Spread Jar */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInLeft}
className="bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2 snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto"
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">
<motion.div
className="text-center"
whileHover={{ scale: 1.05 }}
transition={{ duration: 0.3 }}
>
<div className="w-32 h-32 bg-gradient-to-br from-amber-200 to-orange-300 rounded-full mx-auto mb-4 flex items-center justify-center shadow-premium">
<span className="text-4xl">🍫</span>
</div>
<div className="bg-white rounded-lg p-4 mx-4 shadow-premium">
<div className="font-semibold text-[#703133] text-lg">
MOZIMO
</div>
<div className="text-sm text-gray-600">
SINGLE ORIGIN HAZELNUT SPREAD 45%
</div>
</div>
</motion.div>
</div>
</motion.div>
{/* Middle Block - Magazine Article */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInUp}
className="bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2 snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto"
whileHover={{ scale: 1.02 }}
>
<div className="p-4 md:p-6">
<div className="bg-red-600 h-32 rounded-lg mb-4 flex items-center justify-center shadow-premium">
<span className="text-white text-2xl">🍫</span>
</div>
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="bg-gray-200 h-20 rounded-lg flex items-center justify-center shadow-premium">
<span className="text-lg">🍰</span>
</div>
<div className="bg-gray-200 h-20 rounded-lg flex items-center justify-center shadow-premium">
<span className="text-lg">👨🍳</span>
</div>
</div>
<div className="text-xs text-gray-700 space-y-2 leading-relaxed">
<p>
<strong>By RUPALI DEAN</strong>
</p>
<p>
<strong>
MOZIMO&apos;S CHOCOLATE PIE BY PRIYANKA GUPTA
</strong>
</p>
<p>
isolat[ing] beans to not only single ori- but also to
single farms so as to express unique complexity.
</p>
</div>
</div>
</motion.div>
{/* Right Block - World Chocolate Day */}
<motion.div
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={fadeInRight}
className="bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2 snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto"
whileHover={{ scale: 1.02 }}
>
<div className="p-4 md:p-6 text-center">
<div className="mb-4">
<h3 className="text-2xl font-bold text-[#8B4513] mb-2 text-gradient">
World Chocolate Day
</h3>
<p className="text-gray-600">
Celebrating the art of chocolate making
</p>
</div>
<div className="bg-gradient-to-br from-amber-100 to-orange-100 rounded-lg p-4 shadow-premium">
<div className="text-4xl mb-2">🍫</div>
<p className="text-sm text-gray-700">
Join us in celebrating the world&apos;s favorite treat
</p>
</div>
</div>
</motion.div>
</>
)
) : (
posts?.slice(0, 3).map((post, index) => (
<motion.div
key={post.id}
initial="initial"
whileInView="animate"
viewport={{ once: true, margin: "-50px" }}
variants={
index === 0 ? fadeInLeft : index === 1 ? fadeInUp : fadeInRight
}
className="snap-start w-[calc(80vw-1rem)] flex-shrink-0 sm:w-auto bg-white rounded-lg shadow-premium overflow-hidden hover:shadow-premium-hover transition-all duration-500 transform hover:-translate-y-2"
whileHover={{ scale: 1.02 }}
>
<a
href={post.permalink}
target="_blank"
rel="noopener noreferrer"
className="block"
>
<div className="relative h-64 md:h-96">
<Image
src={post.mediaUrl}
alt={post.caption || "Instagram post"}
fill
className="object-cover img-premium"
draggable={false}
quality={85}
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "/bst/bs1.svg";
}}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/30 via-transparent to-transparent" />
{post.mediaType === "VIDEO" && (
<div className="absolute top-4 right-4 bg-black/50 rounded-full p-2">
<svg
className="w-4 h-4 text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M8 5v10l8-5-8-5z" />
</svg>
</div>
)}
<div className="absolute bottom-4 left-4 right-4">
<div className="glass rounded-lg p-3 border border-white/20">
<p className="text-sm text-gray-800 line-clamp-2">
{post.caption?.slice(0, 100)}...
</p>
</div>
</div>
</div>
</a>
</motion.div>
))
)}
</div>
</Section>
);
}