initial commit

This commit is contained in:
ABasral
2025-07-30 17:27:56 +05:30
parent 9e66f2e54b
commit 3aa7eb5a85
39 changed files with 6441 additions and 753 deletions
+263
View File
@@ -0,0 +1,263 @@
'use client';
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' },
];
export default function BannerSlider() {
const [currentIndex, setCurrentIndex] = useState(0);
const [direction, setDirection] = useState(0);
const [showArrows, setShowArrows] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// Auto-advance slider
useEffect(() => {
const timer = setInterval(() => {
if (!isHovered) {
setDirection(1);
setCurrentIndex((prevIndex) => (prevIndex + 1) % banners.length);
}
}, 4000);
return () => clearInterval(timer);
}, [isHovered]);
const goToSlide = (index: number) => {
setDirection(index > currentIndex ? 1 : -1);
setCurrentIndex(index);
};
const goToPrevious = () => {
setDirection(-1);
setCurrentIndex((prevIndex) =>
prevIndex === 0 ? banners.length - 1 : prevIndex - 1
);
};
const goToNext = () => {
setDirection(1);
setCurrentIndex((prevIndex) => (prevIndex + 1) % banners.length);
};
const slideVariants = {
enter: (direction: number) => ({
x: direction > 0 ? '100%' : '-100%',
opacity: 1,
}),
center: {
x: 0,
opacity: 1,
},
exit: (direction: number) => ({
x: direction < 0 ? '100%' : '-100%',
opacity: 1,
})
};
const swipeConfidenceThreshold = 10000;
const swipePower = (offset: number, velocity: number) => {
return Math.abs(offset) * velocity;
};
const paginate = (newDirection: number) => {
if (newDirection > 0) {
goToNext();
} else {
goToPrevious();
}
};
return (
<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={() => {
setShowArrows(true);
setIsHovered(true);
}}
onMouseLeave={() => {
setShowArrows(false);
setIsHovered(false);
}}
>
{/* Premium Background Pattern */}
<div className="absolute inset-0 opacity-5">
<div className="absolute inset-0 bg-gradient-to-br from-amber-200/20 to-orange-200/20" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_50%,rgba(255,255,255,0.1)_1px,transparent_1px)] bg-[length:20px_20px]" />
</div>
{/* Banner Images with Premium Animations */}
<AnimatePresence initial={false} custom={direction} mode="popLayout">
<motion.div
key={`${currentIndex}-${direction}`}
custom={direction}
variants={slideVariants}
initial="enter"
animate="center"
exit="exit"
transition={{
x: { type: "spring", stiffness: 300, damping: 30 },
duration: 0.4
}}
drag="x"
dragConstraints={{ left: 0, right: 0 }}
dragElastic={0.7}
onDragEnd={(e, { offset, velocity }) => {
const swipe = swipePower(offset.x, velocity.x);
if (swipe < -swipeConfidenceThreshold) {
paginate(1);
} else if (swipe > swipeConfidenceThreshold) {
paginate(-1);
}
}}
className="absolute inset-0"
>
<Image
src={banners[currentIndex].src}
alt={banners[currentIndex].alt}
fill
className="object-cover img-premium"
draggable={false}
priority={currentIndex === 0}
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>
{/* Premium Navigation Arrows - Hidden on mobile */}
<AnimatePresence>
{showArrows && (
<>
<motion.button
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
onClick={goToPrevious}
className="hidden md:block absolute left-6 top-1/2 transform -translate-y-1/2 z-20 glass text-white p-4 rounded-full backdrop-blur-sm transition-all duration-300 border border-white/20 hover:border-white/40 shadow-premium hover:shadow-premium-hover"
whileHover={{ scale: 1.1, rotate: -5 }}
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>
</motion.button>
<motion.button
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }}
transition={{ duration: 0.3 }}
onClick={goToNext}
className="hidden md:block absolute right-6 top-1/2 transform -translate-y-1/2 z-20 glass text-white p-4 rounded-full backdrop-blur-sm transition-all duration-300 border border-white/20 hover:border-white/40 shadow-premium hover:shadow-premium-hover"
whileHover={{ scale: 1.1, rotate: 5 }}
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>
</motion.button>
</>
)}
</AnimatePresence>
{/* 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">
<motion.button
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"
whileHover={{ scale: 1.1, rotate: -5 }}
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>
</motion.button>
<motion.button
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"
whileHover={{ scale: 1.1, rotate: 5 }}
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>
</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">
{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'
}`}
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
aria-label={`Go to banner ${index + 1}`}
/>
))}
</div>
{/* Premium Progress Bar */}
<div className="absolute bottom-0 left-0 right-0 h-1 bg-white/10 z-20">
<motion.div
className="h-full bg-gradient-to-r from-[#8B4513] to-[#DAA520]"
initial={{ width: 0 }}
animate={{ width: "100%" }}
transition={{ duration: 4, ease: "linear" }}
key={currentIndex}
/>
</div>
</div>
);
}
+76
View File
@@ -0,0 +1,76 @@
'use client';
import { motion } from 'framer-motion';
import { ReactNode } from 'react';
interface ButtonProps {
children: ReactNode;
variant?: 'primary' | 'secondary' | 'white';
size?: 'sm' | 'md' | 'lg';
className?: string;
onClick?: () => void;
href?: string;
}
export default function Button({
children,
variant = 'primary',
size = 'md',
className = '',
onClick,
href
}: ButtonProps) {
const baseClasses = 'font-renner rounded-full transition-all duration-500 inline-flex items-center justify-center relative overflow-hidden shadow-premium hover:shadow-premium-hover';
const variantClasses = {
primary: 'bg-gradient-to-r from-[#8B4513] to-[#A0522D] text-white hover:from-[#3C2A21] hover:to-[#8B4513] shadow-glow hover:shadow-glow',
secondary: 'bg-gradient-to-r from-[#DAA520] to-[#CD7F32] text-white hover:from-[#B8860B] hover:to-[#DAA520] shadow-glow hover:shadow-glow',
white: 'bg-white text-[#3C2A21] shadow-premium hover:shadow-premium-hover hover:bg-gray-50 border border-gray-200 hover:border-[#8B4513]'
};
const sizeClasses = {
sm: 'px-3 py-2 text-sm md:px-4 md:py-2 md:text-sm',
md: 'px-4 py-2 text-sm md:px-6 md:py-3 md:text-base',
lg: 'px-6 py-3 text-base md:px-8 md:py-4 md:text-lg'
};
const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`;
const buttonContent = (
<motion.div
className={classes}
style={{
fontWeight: 300,
fontSize: 'clamp(14px, 2.5vw, 16px)',
lineHeight: '100%',
letterSpacing: '0%'
}}
whileHover={{
scale: 1.05,
y: -3,
transition: { duration: 0.3, ease: "easeOut" }
}}
whileTap={{
scale: 0.98,
transition: { duration: 0.1 }
}}
onClick={onClick}
>
{/* Shimmer effect overlay */}
<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" />
{/* Button content */}
<span className="relative z-10">{children}</span>
</motion.div>
);
if (href) {
return (
<a href={href} className="inline-block">
{buttonContent}
</a>
);
}
return buttonContent;
}
+284
View File
@@ -0,0 +1,284 @@
'use client';
import { motion } from 'framer-motion';
export default function Footer() {
return (
<footer className="bg-[#F8F6F5] text-[#703133] font-renner text-base relative overflow-hidden">
{/* Premium background pattern */}
<div className="absolute inset-0 opacity-5">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_25%_25%,rgba(139,69,19,0.1)_1px,transparent_1px)] bg-[length:40px_40px]" />
</div>
<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 */}
<motion.div
className="space-y-4 sm:col-span-2 lg:col-span-1"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
>
{/* MOZIMO Logo */}
<div className="flex items-center space-x-2">
<div className="relative">
{/* Cocoa pod icon */}
<div className="absolute -top-2 -left-1 w-4 h-4 text-[#8B4513]">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
</div>
{/* MOZIMO Text */}
<div className="text-center">
<div className="text-xs text-[#8B4513] font-samantha" style={{ fontFamily: 'Samantha Signature' }}>
HAND CRAFTED
</div>
<div className="text-xl md:text-2xl font-bold text-[#703133] font-moneta" style={{ fontFamily: 'MonetaSans-Regular' }}>
MOZIMO
</div>
<div className="text-xs text-[#8B4513] font-renner">
CHOCOLATES
</div>
</div>
</div>
</div>
{/* Tagline */}
<p className="text-sm leading-relaxed opacity-80 font-renner" style={{
fontWeight: 300,
fontSize: '14px',
lineHeight: '160%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}>
India&apos;s Premier European style bean-to-bar chocolate experience.
</p>
</motion.div>
{/* Column 2: Get in touch */}
<motion.div
className="space-y-4"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
viewport={{ once: true }}
>
<h3 className="font-semibold text-lg font-renner" style={{
fontWeight: 600,
fontSize: '18px',
lineHeight: '120%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}>Get in touch</h3>
<div className="space-y-2 text-sm font-renner" style={{
fontWeight: 300,
fontSize: '14px',
lineHeight: '160%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}>
<motion.div
className="flex items-center space-x-2"
initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: 0.2 }}
viewport={{ once: true }}
whileHover={{ x: 5 }}
>
<svg className="w-4 h-4 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<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>
<span className="text-xs md:text-sm">0172-4045414</span>
</motion.div>
<motion.div
className="flex items-start space-x-2"
initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: 0.3 }}
viewport={{ once: true }}
whileHover={{ x: 5 }}
>
<svg className="w-4 h-4 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clipRule="evenodd" />
</svg>
<span className="text-xs md:text-sm">SCO 8, Inner Market, 9-D, Sector 9, Chandigarh, 160009</span>
</motion.div>
</div>
</motion.div>
{/* Column 3: Know more */}
<motion.div
className="space-y-4"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
>
<h3 className="font-semibold text-lg font-renner" style={{
fontWeight: 600,
fontSize: '18px',
lineHeight: '120%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}>Know more</h3>
<ul className="space-y-2 text-sm font-renner" style={{
fontWeight: 300,
fontSize: '14px',
lineHeight: '160%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}>
{['Privacy Policy', 'Refund & Return Policy', 'Terms & Conditions', 'Shipping Policy'].map((link, index) => (
<motion.li
key={link}
initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: 0.2 + index * 0.1 }}
viewport={{ once: true }}
>
<a
href="#"
className="hover:opacity-80 transition-all duration-300 relative group text-xs md:text-sm"
>
{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>
</a>
</motion.li>
))}
</ul>
</motion.div>
{/* Column 4: FAQ, Store, Ordering, Social Media */}
<motion.div
className="space-y-4 sm:col-span-2 lg:col-span-1"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
>
{/* Individual links */}
<div className="space-y-2 text-sm font-renner" style={{
fontWeight: 300,
fontSize: '14px',
lineHeight: '160%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}>
{['FAQ', 'Locate our store', 'Bulk Ordering'].map((link, index) => (
<motion.div
key={link}
initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: 0.3 + index * 0.1 }}
viewport={{ once: true }}
>
<a
href="#"
className="hover:opacity-80 transition-all duration-300 relative group text-xs md:text-sm"
>
{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>
</a>
</motion.div>
))}
</div>
{/* Find Us On */}
<div className="pt-4">
<h3 className="font-semibold text-lg font-renner mb-3" style={{
fontWeight: 600,
fontSize: '18px',
lineHeight: '120%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}>Find Us On</h3>
<div className="flex space-x-3 md:space-x-4">
{/* Facebook */}
<motion.a
href="#"
className="hover:opacity-80 transition-all duration-300 p-2 rounded-full hover:bg-white/20 backdrop-blur-sm"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4, delay: 0.4 }}
viewport={{ once: true }}
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
aria-label="Facebook"
>
<svg className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<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>
</motion.a>
{/* Instagram */}
<motion.a
href="#"
className="hover:opacity-80 transition-all duration-300 p-2 rounded-full hover:bg-white/20 backdrop-blur-sm"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4, delay: 0.5 }}
viewport={{ once: true }}
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
aria-label="Instagram"
>
<svg className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<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>
</motion.a>
{/* YouTube */}
<motion.a
href="#"
className="hover:opacity-80 transition-all duration-300 p-2 rounded-full hover:bg-white/20 backdrop-blur-sm"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4, delay: 0.6 }}
viewport={{ once: true }}
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
aria-label="YouTube"
>
<svg className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<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>
</motion.a>
{/* LinkedIn */}
<motion.a
href="#"
className="hover:opacity-80 transition-all duration-300 p-2 rounded-full hover:bg-white/20 backdrop-blur-sm"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4, delay: 0.7 }}
viewport={{ once: true }}
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
aria-label="LinkedIn"
>
<svg className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<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>
</motion.a>
</div>
</div>
</motion.div>
</div>
{/* Bottom Bar */}
<motion.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"
style={{
fontWeight: 300,
fontSize: '14px',
lineHeight: '160%',
letterSpacing: '0%',
fontFamily: 'Renner*'
}}
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.6, delay: 0.4 }}
viewport={{ once: true }}
>
<p className="text-xs md:text-sm">&copy; 2024 Mozimo. All rights reserved.</p>
</motion.div>
</footer>
);
}
+411
View File
@@ -0,0 +1,411 @@
'use client';
import { motion, AnimatePresence } from 'framer-motion';
import Image from 'next/image';
import { useState, useEffect } from 'react';
export default function Header() {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
useEffect(() => {
const handleScroll = () => {
const scrollPosition = window.scrollY;
setIsScrolled(scrollPosition > 100);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Close mobile menu when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as Element;
if (!target.closest('.mobile-menu') && !target.closest('.hamburger-button')) {
setIsMobileMenuOpen(false);
}
};
if (isMobileMenuOpen) {
document.addEventListener('click', handleClickOutside);
}
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [isMobileMenuOpen]);
// Prevent body scroll when mobile menu is open
useEffect(() => {
if (isMobileMenuOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
}
return () => {
document.body.style.overflow = 'unset';
};
}, [isMobileMenuOpen]);
const toggleMobileMenu = () => {
setIsMobileMenuOpen(!isMobileMenuOpen);
};
return (
<>
{/* Mobile Header - Only visible on mobile */}
<motion.header
className="fixed top-0 z-[60] w-full md:hidden"
initial={false}
animate={{
width: '100%',
left: '0%',
x: '0%',
top: isScrolled ? '0px' : '12px',
borderRadius: '0px',
height: isScrolled ? '64px' : '60px',
}}
transition={{
duration: 0.5,
ease: [0.25, 0.46, 0.45, 0.94]
}}
>
<nav
className={`glass border border-white/20 shadow-premium w-full h-full flex justify-between items-center rounded-none`}
>
<div className="flex justify-between items-center w-full px-4">
{/* Mobile Hamburger Menu */}
<motion.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"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
onClick={toggleMobileMenu}
aria-label="Toggle mobile menu"
>
<div className="w-6 h-6 flex flex-col justify-center items-center">
<motion.span
className="w-6 h-0.5 bg-current block transition-all duration-300"
animate={{
rotate: isMobileMenuOpen ? 45 : 0,
y: isMobileMenuOpen ? 6 : 0,
}}
/>
<motion.span
className="w-6 h-0.5 bg-current block mt-1 transition-all duration-300"
animate={{
opacity: isMobileMenuOpen ? 0 : 1,
}}
/>
<motion.span
className="w-6 h-0.5 bg-current block mt-1 transition-all duration-300"
animate={{
rotate: isMobileMenuOpen ? -45 : 0,
y: isMobileMenuOpen ? -6 : 0,
}}
/>
</div>
</motion.button>
{/* Center Logo */}
<motion.div
className="flex items-center justify-center absolute left-1/2 transform -translate-x-1/2"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<motion.div
className="relative w-10 h-10"
animate={{
scale: isScrolled ? 1.1 : 1,
}}
transition={{ duration: 0.5, ease: "easeInOut" }}
whileHover={{
scale: 1.15,
rotate: 5,
transition: { duration: 0.3 }
}}
>
<Image
src="/logo/logo.svg"
alt="Mozimo Logo"
fill
className="object-contain drop-shadow-lg"
priority
sizes="40px"
quality={95}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWGRkqGx0f/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRmknyJckliyjqTzSlT54b6bk+h0R//2Q=="
/>
</motion.div>
</motion.div>
{/* Mobile Right Side - Empty space to balance layout */}
<div className="w-10 h-10"></div>
</div>
</nav>
</motion.header>
{/* Desktop Header - Only visible on desktop */}
<motion.header
className="fixed top-0 z-50 hidden md:block"
initial={false}
animate={{
width: isScrolled ? '100%' : '1440px',
left: isScrolled ? '0%' : '50%',
x: isScrolled ? '0%' : '-50%',
top: isScrolled ? '0px' : '24px',
borderRadius: isScrolled ? '0px' : '15px',
height: isScrolled ? '64px' : '80px',
}}
transition={{
duration: 0.5,
ease: [0.25, 0.46, 0.45, 0.94]
}}
>
<nav
className={`glass border border-white/20 shadow-premium w-full h-full flex justify-between items-center ${
isScrolled ? 'rounded-none' : 'rounded-[15px]'
}`}
>
<div className="flex justify-between items-center w-full px-20">
{/* Left Navigation */}
<div className="flex items-center space-x-16">
<motion.a
href="#about"
className="text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group"
style={{
fontWeight: 100,
fontFamily: 'Renner',
fontSize: '18px',
lineHeight: '100%',
letterSpacing: '0%'
}}
whileHover={{ scale: 1.02 }}
>
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>
</motion.a>
<motion.a
href="#shop"
className="text-gray-800 hover:text-[#8B4513] transition-all duration-300 font-renner relative group"
style={{
fontWeight: 100,
fontFamily: 'Renner',
fontSize: '18px',
lineHeight: '100%',
letterSpacing: '0%'
}}
whileHover={{ scale: 1.02 }}
>
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>
</motion.a>
</div>
{/* Center Logo */}
<motion.div
className="flex items-center justify-center absolute left-1/2 transform -translate-x-1/2"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<motion.div
className="relative w-12 h-12"
animate={{
scale: isScrolled ? 1.1 : 1,
}}
transition={{ duration: 0.5, ease: "easeInOut" }}
whileHover={{
scale: 1.15,
rotate: 5,
transition: { duration: 0.3 }
}}
>
<Image
src="/logo/logo.svg"
alt="Mozimo Logo"
fill
className="object-contain drop-shadow-lg"
priority
sizes="48px"
quality={95}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWGRkqGx0f/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRmknyJckliyjqTzSlT54b6bk+h0R//2Q=="
/>
</motion.div>
</motion.div>
{/* Right Icons */}
<div className="flex items-center space-x-8">
{/* More Dropdown */}
<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',
lineHeight: '100%',
letterSpacing: '0%'
}}
whileHover={{ scale: 1.02 }}
aria-label="More options"
>
<span>More</span>
<motion.svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
animate={{ rotate: 0 }}
whileHover={{ rotate: 180 }}
transition={{ duration: 0.3 }}
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</motion.svg>
</motion.button>
<motion.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"
whileHover={{ scale: 1.1, rotate: 5 }}
whileTap={{ scale: 0.95 }}
aria-label="User account"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} 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>
</motion.button>
<motion.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"
whileHover={{ scale: 1.1, rotate: -5 }}
whileTap={{ scale: 0.95 }}
aria-label="Search"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</motion.button>
<motion.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"
whileHover={{ scale: 1.1, y: -2 }}
whileTap={{ scale: 0.95 }}
aria-label="Shopping bag"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" />
</svg>
</motion.button>
</div>
</div>
</nav>
</motion.header>
{/* Mobile Menu Overlay - Only visible on mobile */}
<AnimatePresence>
{isMobileMenuOpen && (
<motion.div
className="mobile-menu fixed inset-0 z-50 bg-black/50 backdrop-blur-sm md:hidden"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
<motion.div
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 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3, delay: 0.1 }}
>
<div className="px-6 py-8 space-y-6">
{/* Mobile Navigation Links */}
<div className="space-y-4">
<motion.a
href="#about"
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',
fontSize: '18px',
lineHeight: '100%',
letterSpacing: '0%'
}}
onClick={() => setIsMobileMenuOpen(false)}
whileHover={{ x: 10 }}
>
About us
</motion.a>
<motion.a
href="#shop"
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',
fontSize: '18px',
lineHeight: '100%',
letterSpacing: '0%'
}}
onClick={() => setIsMobileMenuOpen(false)}
whileHover={{ x: 10 }}
>
Shop
</motion.a>
<motion.a
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"
style={{
fontWeight: 300,
fontFamily: 'Renner',
fontSize: '18px',
lineHeight: '100%',
letterSpacing: '0%'
}}
onClick={() => setIsMobileMenuOpen(false)}
whileHover={{ x: 10 }}
>
Categories
</motion.a>
</div>
{/* Mobile Action Buttons */}
<div className="flex space-x-4 pt-4">
<motion.button
className="flex-1 p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm border border-gray-200"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
aria-label="Search"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</motion.button>
<motion.button
className="flex-1 p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm border border-gray-200"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
aria-label="Shopping bag"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" />
</svg>
</motion.button>
<motion.button
className="flex-1 p-3 text-gray-800 hover:text-[#8B4513] transition-all duration-300 relative min-h-[40px] flex items-center justify-center rounded-full hover:bg-white/20 backdrop-blur-sm border border-gray-200"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
aria-label="User account"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} 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>
</motion.button>
</div>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</>
);
}
+57
View File
@@ -0,0 +1,57 @@
'use client';
import { motion } from 'framer-motion';
import { ReactNode } from 'react';
interface SectionProps {
children: ReactNode;
className?: string;
background?: 'white' | 'cream' | 'dark';
padding?: 'sm' | 'md' | 'lg' | 'xl';
id?: string;
}
export default function Section({
children,
className = '',
background = 'white',
padding = 'lg',
id
}: SectionProps) {
const backgroundClasses = {
white: 'bg-white',
cream: 'bg-[#F8F6F5]',
dark: 'bg-[#3C2A21]'
};
const paddingClasses = {
sm: 'py-6 md:py-8',
md: 'py-8 md:py-12',
lg: 'py-12 md:py-16 lg:py-20',
xl: 'py-16 md:py-24 lg:py-32'
};
const classes = `${backgroundClasses[background]} ${paddingClasses[padding]} ${className} section-premium`;
return (
<section id={id} className={classes}>
<motion.div
initial={{ opacity: 0, y: 60 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{
duration: 1.2,
ease: [0.25, 0.46, 0.45, 0.94],
delay: 0.1
}}
viewport={{ once: true, margin: "-100px" }}
className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative"
>
{/* Premium section decoration */}
<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}
</motion.div>
</section>
);
}