import React, { useState, useEffect, createContext, useContext } from 'react';
import {
ShoppingBag, Menu, X, Star, ChevronRight, ChevronDown,
MapPin, Clock, Phone, Mail, Instagram, Facebook,
Check, ArrowRight, Heart, Sparkles, Plus, Minus, Trash2
} from 'lucide-react';
// --- CONTEXT & STATE MANAGEMENT ---
const CartContext = createContext();
const CartProvider = ({ children }) => {
const [cart, setCart] = useState([]);
const [isCartOpen, setIsCartOpen] = useState(false);
const addToCart = (product, quantity = 1) => {
setCart(prev => {
const existing = prev.find(item => item.id === product.id);
if (existing) {
return prev.map(item =>
item.id === product.id ? { ...item, quantity: item.quantity + quantity } : item
);
}
return [...prev, { ...product, quantity }];
});
setIsCartOpen(true);
};
const removeFromCart = (id) => {
setCart(prev => prev.filter(item => item.id !== id));
};
const updateQuantity = (id, delta) => {
setCart(prev => prev.map(item => {
if (item.id === id) {
const newQty = item.quantity + delta;
return newQty > 0 ? { ...item, quantity: newQty } : item;
}
return item;
}));
};
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
const subtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
return (
<CartContext.Provider value={{
cart, addToCart, removeFromCart, updateQuantity,
isCartOpen, setIsCartOpen, totalItems, subtotal
}}>
{children}
</CartContext.Provider>
);
};
const useCart = () => useContext(CartContext);
// --- MOCK DATA ---
const PRODUCTS = [
{
id: 'c1',
name: 'Classic Chocolate Chunk',
tagline: 'Gooey perfection in every bite',
price: 3.99,
category: 'Classic',
rating: 4.9,
reviews: 328,
description: 'Thick, buttery dough loaded with oversized chunks of semi-sweet belgian chocolate, baked until gold on the outside and warm and molten inside.',
allergens: ['Milk', 'Eggs', 'Wheat', 'Soy'],
calories: '480 kcal',
image: 'https://images.unsplash.com/photo-1499636136210-6f4ee915583e?auto=format&fit=crop&q=80&w=600',
popular: true
},
{
id: 'c2',
name: 'Biscoff Butter Crumble',
tagline: 'Warm spiced speculoos bliss',
price: 4.50,
category: 'Stuffed',
rating: 5.0,
reviews: 215,
description: 'Spiced cookie dough filled with a molten cookie butter core, topped with crushed Biscoff cookies and a caramelized drizzle.',
allergens: ['Milk', 'Eggs', 'Wheat', 'Soy'],
calories: '520 kcal',
popular: true
},
{
id: 'c3',
name: 'Birthday Cake Swirl',
tagline: 'Celebration wrapped in sugar',
price: 4.25,
category: 'Classic',
rating: 4.8,
reviews: 184,
description: 'Rich vanilla cake batter dough folded with colorful rainbow sprinkles, stuffed with sweet cream cheese frosting and topped with cake crumbs.',
allergens: ['Milk', 'Eggs', 'Wheat'],
calories: '460 kcal',
popular: false
},
{
id: 'c4',
name: 'Campfire S’mores Overload',
tagline: 'Toasted gooey marshmallow dream',
price: 4.75,
category: 'Stuffed',
rating: 4.9,
reviews: 412,
description: 'Graham cracker enriched dough packed with milk chocolate chunks, stuffed with a giant gooey marshmallow, and flame-torched on top.',
allergens: ['Milk', 'Eggs', 'Wheat', 'Soy'],
calories: '510 kcal',
popular: true
},
{
id: 'c5',
name: 'Red Velvet Cream Cheese',
tagline: 'Decadent, velvet smooth perfection',
price: 4.50,
category: 'Stuffed',
rating: 4.7,
reviews: 156,
description: 'Deep cocoa red velvet dough baked warm with a silky vanilla cream cheese center and sprinkled with dark chocolate chips.',
allergens: ['Milk', 'Eggs', 'Wheat'],
calories: '490 kcal',
popular: false
},
{
id: 'c6',
name: 'Peanut Butter Lava',
tagline: 'Rich, nutty, and irresistibly gooey',
price: 4.25,
category: 'Stuffed',
rating: 4.9,
reviews: 290,
description: 'Salted peanut butter dough overflowing with a molten peanut butter fudge center and finished with crushed roasted peanuts.',
allergens: ['Peanuts', 'Milk', 'Eggs', 'Wheat'],
calories: '540 kcal',
popular: true
},
{
id: 'c7',
name: 'Plant-Based Dark Choc Fudge',
tagline: '100% Vegan, 100% Indulgent',
price: 4.50,
category: 'Vegan',
rating: 4.8,
reviews: 98,
description: 'Rich, fudgy chocolate dough mixed with 70% dark chocolate chunks and sea salt flakes. Absolutely zero animal products, maximum decadence.',
allergens: ['Wheat', 'Soy'],
calories: '430 kcal',
popular: false
},
{
id: 'c8',
name: 'Gluten-Free Cinnamon Roll',
tagline: 'Warm cinnamon swirl goodness',
price: 4.75,
category: 'Gluten-Free',
rating: 4.6,
reviews: 87,
description: 'Crafted with almond and oat flour, swirled with brown sugar and cinnamon, drizzled with sweet cream glaze.',
allergens: ['Milk', 'Eggs', 'Tree Nuts (Almond)'],
calories: '410 kcal',
popular: false
}
];
const TESTIMONIALS = [
{
id: 1,
name: 'Sarah Jenkins',
role: 'Cookie Enthusiast',
quote: 'The Biscoff Butter Crumble literally melted my heart. You haven’t lived until you eat one of these warm!',
rating: 5,
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&q=80&w=150'
},
{
id: 2,
name: 'Marcus Chen',
role: 'Food Blogger',
quote: 'Crispy outer shell, thick and gooey inside. Crumble Cookie sets the gold standard for artisanal treats.',
rating: 5,
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&q=80&w=150'
},
{
id: 3,
name: 'Emily Watson',
role: 'Event Coordinator',
quote: 'We ordered 200 custom cookies for a corporate event. They arrived warm, fresh, and were gone in 10 minutes!',
rating: 5,
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&q=80&w=150'
}
];
const LOCATIONS = [
{
city: 'Downtown Flagship',
address: '452 Sweet Briar Lane, Suite 100',
hours: 'Mon-Sun: 8am - 10pm',
phone: '(555) 234-5678'
},
{
city: 'Uptown Bakery & Cafe',
address: '881 Gourmet Boulevard',
hours: 'Mon-Sun: 7am - 11pm',
phone: '(555) 876-5432'
}
];
const FAQS = [
{
q: 'How should I reheat my cookies?',
a: 'For that fresh-out-of-the-oven experience, pop your cookie in a preheated oven at 350°F (175°C) for 3 to 5 minutes, or microwave for 10-12 seconds!'
},
{
q: 'Do you offer gluten-free and vegan options?',
a: 'Yes! We always have at least one dedicated Vegan and one Gluten-Free flavor on our menu. They are baked using strict separation procedures, though processed in a facility that handles gluten.'
},
{
q: 'How long do the cookies stay fresh?',
a: 'Our cookies stay soft and fresh for up to 5 days in an airtight container at room temperature. You can also freeze them for up to 3 months!'
},
{
q: 'How far in advance should I place catering orders?',
a: 'For small event boxes (2-4 dozen), 24-hour notice is appreciated. For large events or custom orders, please contact us at least 5 business days in advance.'
}
];
// --- NAVIGATION COMPONENT ---
const Navbar = ({ activeTab, setActiveTab }) => {
const [isScrolled, setIsScrolled] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const { totalItems, setIsCartOpen } = useCart();
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const navLinks = [
{ name: 'Home', id: 'home' },
{ name: 'Menu', id: 'menu' },
{ name: 'About Us', id: 'about' },
{ name: 'Locations', id: 'locations' },
{ name: 'Catering', id: 'catering' },
{ name: 'FAQ', id: 'faq' },
{ name: 'Contact', id: 'contact' }
];
const handleNavClick = (id) => {
setActiveTab(id);
setMobileMenuOpen(false);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return (
<nav className={fixed top-0 left-0 right-0 z-40 transition-all duration-300 ${ isScrolled ? 'bg-[#FFF8EC]/95 backdrop-blur-md shadow-md py-3' : 'bg-transparent py-5' }}>
{/* Brand Logo */}
<button
onClick={() => handleNavClick('home')}
className="flex items-center gap-2 group text-left focus:outline-none"
>
<div className="w-10 h-10 rounded-full bg-[#E8A857] flex items-center justify-center text-[#3D2817] font-bold text-xl shadow-inner group-hover:scale-105 transition-transform">
🍪
</div>
<div>
<span className="text-2xl font-black tracking-tight text-[#3D2817] block leading-none font-[#Fredoka]">
Crumble
</span>
<span className="text-xs uppercase tracking-widest text-[#8B5A2B] font-bold">
Cookie Co.
</span>
</div>
</button>
{/* Desktop Navigation Links */}
<div className="hidden md:flex items-center space-x-8">
{navLinks.map(link => (
<button
key={link.id}
onClick={() => handleNavClick(link.id)}
className={`text-sm font-bold tracking-wide transition-colors hover:text-[#8B5A2B] ${
activeTab === link.id ? 'text-[#8B5A2B] border-b-2 border-[#E8A857] pb-1' : 'text-[#3D2817]/80'
}`}
>
{link.name}
</button>
))}
</div>
{/* Right CTA / Cart */}
<div className="hidden md:flex items-center space-x-4">
<button
onClick={() => setIsCartOpen(true)}
className="relative p-2 text-[#3D2817] hover:text-[#8B5A2B] transition-colors focus:outline-none"
aria-label="Shopping Cart"
>
<ShoppingBag className="w-6 h-6" />
{totalItems > 0 && (
<span className="absolute -top-1 -right-1 bg-[#8B5A2B] text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center shadow">
{totalItems}
</span>
)}
</button>
<button
onClick={() => handleNavClick('menu')}
className="bg-[#E8A857] hover:bg-[#d99746] text-[#3D2817] px-5 py-2.5 rounded-full font-bold text-sm transition-all transform hover:-translate-y-0.5 shadow-md"
>
Order Fresh Now
</button>
</div>
{/* Mobile Hamburger Button */}
<div className="flex md:hidden items-center space-x-3">
<button
onClick={() => setIsCartOpen(true)}
className="relative p-2 text-[#3D2817]"
>
<ShoppingBag className="w-6 h-6" />
{totalItems > 0 && (
<span className="absolute -top-1 -right-1 bg-[#8B5A2B] text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center">
{totalItems}
</span>
)}
</button>
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="p-2 text-[#3D2817] focus:outline-none"
>
{mobileMenuOpen ? <X className="w-7 h-7" /> : <Menu className="w-7 h-7" />}
</button>
</div>
</div>
{/* Mobile Drawer */}
{mobileMenuOpen && (
<div className="md:hidden bg-[#FFF8EC] border-b border-[#8B5A2B]/10 px-4 pt-2 pb-6 space-y-3 shadow-lg animate-fadeIn">
{navLinks.map(link => (
<button
key={link.id}
onClick={() => handleNavClick(link.id)}
className={`block w-full text-left px-3 py-2 rounded-lg text-base font-bold ${
activeTab === link.id ? 'bg-[#E8A857]/20 text-[#8B5A2B]' : 'text-[#3D2817]'
}`}
>
{link.name}
</button>
))}
<button
onClick={() => handleNavClick('menu')}
className="w-full mt-4 bg-[#E8A857] text-[#3D2817] py-3 rounded-full font-bold text-center shadow"
>
Order Fresh Now
</button>
</div>
)}
</nav>
);
};
// --- CART DRAWER COMPONENT ---
const CartDrawer = () => {
const { cart, isCartOpen, setIsCartOpen, removeFromCart, updateQuantity, subtotal } = useCart();
if (!isCartOpen) return null;
return (
<div
className="absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity"
onClick={() => setIsCartOpen(false)}
/>
<div className="fixed inset-y-0 right-0 max-w-full flex pl-10">
<div className="w-screen max-w-md bg-[#FFF8EC] shadow-2xl flex flex-col">
{/* Header */}
<div className="p-6 bg-[#3D2817] text-[#FFF8EC] flex items-center justify-between">
<div className="flex items-center gap-2">
<ShoppingBag className="w-5 h-5 text-[#E8A857]" />
<h2 className="text-xl font-bold tracking-wide">Your Fresh Box</h2>
</div>
<button
onClick={() => setIsCartOpen(false)}
className="p-1 hover:bg-white/10 rounded-full transition-colors"
>
<X className="w-6 h-6" />
</button>
</div>
{/* Cart Items List */}
<div className="flex-1 overflow-y-auto p-6 space-y-4">
{cart.length === 0 ? (
<div className="text-center py-16">
<div className="text-6xl mb-4">🍪</div>
<h3 className="text-lg font-bold text-[#3D2817]">Your box is currently empty</h3>
<p className="text-sm text-[#8B5A2B] mt-1">Fill it up with warm, gooey goodness!</p>
</div>
) : (
cart.map(item => (
<div key={item.id} className="flex gap-4 p-3 bg-white rounded-2xl shadow-sm border border-[#8B5A2B]/10 items-center">
<img
src={item.image}
alt={item.name}
className="w-20 h-20 object-cover rounded-xl flex-shrink-0"
/>
<div className="flex-1 min-w-0">
<h4 className="font-bold text-[#3D2817] text-sm truncate">{item.name}</h4>
<p className="text-xs text-[#8B5A2B] font-semibold">${item.price.toFixed(2)} each</p>
<div className="flex items-center gap-3 mt-2">
<div className="flex items-center border border-gray-200 rounded-lg bg-[#FFF8EC]">
<button
onClick={() => updateQuantity(item.id, -1)}
className="p-1 text-[#3D2817] hover:text-[#8B5A2B]"
>
<Minus className="w-3.5 h-3.5" />
</button>
<span className="px-2 text-xs font-bold text-[#3D2817]">{item.quantity}</span>
<button
onClick={() => updateQuantity(item.id, 1)}
className="p-1 text-[#3D2817] hover:text-[#8B5A2B]"
>
<Plus className="w-3.5 h-3.5" />
</button>
</div>
<button
onClick={() => removeFromCart(item.id)}
className="text-red-400 hover:text-red-600 p-1"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
<div className="text-right font-bold text-[#3D2817] text-sm">
${(item.price * item.quantity).toFixed(2)}
</div>
</div>
))
)}
</div>
{/* Footer Checkout Info */}
{cart.length > 0 && (
<div className="p-6 bg-white border-t border-[#8B5A2B]/10 space-y-4">
<div className="space-y-1.5 text-sm">
<div className="flex justify-between text-gray-600">
<span>Subtotal</span>
<span className="font-semibold text-[#3D2817]">${subtotal.toFixed(2)}</span>
</div>
<div className="flex justify-between text-gray-600">
<span>Estimated Taxes & Shipping</span>
<span className="font-semibold text-[#3D2817]">$4.99</span>
</div>
<div className="flex justify-between text-base font-black text-[#3D2817] pt-2 border-t">
<span>Total</span>
<span className="text-[#8B5A2B]">${(subtotal + 4.99).toFixed(2)}</span>
</div>
</div>
<button
onClick={() => alert("Mock Checkout Complete! Your cookies will be baked fresh!")}
className="w-full bg-[#E8A857] hover:bg-[#d99746] text-[#3D2817] font-extrabold py-3.5 rounded-full shadow-lg transition-transform transform active:scale-95 flex items-center justify-center gap-2"
>
Checkout Now
<ArrowRight className="w-4 h-4" />
</button>
</div>
)}
</div>
</div>
</div>
);
};
// --- PRODUCT CARD COMPONENT ---
const ProductCard = ({ product, onSelectProduct }) => {
const { addToCart } = useCart();
return (
<div
className="relative overflow-hidden rounded-2xl cursor-pointer aspect-square mb-4 bg-[#FFF8EC]"
onClick={() => onSelectProduct(product)}
>

{product.popular && (
Best Seller
)}
{product.category}
<div className="flex items-center gap-1 mb-1 text-amber-500 text-xs font-bold">
<Star className="w-3.5 h-3.5 fill-current" />
<span>{product.rating}</span>
<span className="text-gray-400 font-normal">({product.reviews})</span>
</div>
<h3
onClick={() => onSelectProduct(product)}
className="font-black text-lg text-[#3D2817] cursor-pointer hover:text-[#8B5A2B] transition-colors leading-snug"
>
{product.name}
</h3>
<p className="text-xs text-[#8B5A2B] font-medium mt-1 line-clamp-2">
{product.tagline}
</p>
</div>
<div className="flex items-center justify-between mt-5 pt-3 border-t border-gray-100">
<span className="text-lg font-black text-[#3D2817]">${product.price.toFixed(2)}</span>
<button
onClick={() => addToCart(product)}
className="bg-[#FFF8EC] hover:bg-[#E8A857] text-[#3D2817] border border-[#E8A857] hover:border-transparent font-bold text-xs px-4 py-2 rounded-full transition-all flex items-center gap-1.5 shadow-sm"
>
<Plus className="w-3.5 h-3.5" />
Add
</button>
</div>
</div>
);
};
// --- PAGES ---
// 1. HOME PAGE
const HomePage = ({ setActiveTab, onSelectProduct }) => {
const { addToCart } = useCart();
return (
{/* HERO SECTION */}
<section className="relative pt-32 pb-20 md:pt-40 md:pb-28 overflow-hidden bg-gradient-to-b from-[#FFF8EC] to-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<div className="grid md:grid-cols-2 gap-12 items-center">
<div className="space-y-6 text-center md:text-left">
<span className="inline-flex items-center gap-2 bg-[#E8A857]/20 border border-[#E8A857]/40 text-[#8B5A2B] px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-widest">
<Sparkles className="w-3.5 h-3.5" /> Baked Fresh Daily
</span>
<h1 className="text-4xl sm:text-6xl font-black text-[#3D2817] leading-none tracking-tight">
Baked Thick. <br />
Baked Fresh. <br />
<span className="text-[#8B5A2B]">Baked to Crumble.</span>
</h1>
<p className="text-base sm:text-lg text-gray-600 max-w-lg mx-auto md:mx-0 font-medium">
Gourmet oversized cookies baked warm all day long. Soft, melt-in-your-mouth centers with golden crispy edges.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center md:justify-start gap-4 pt-2">
<button
onClick={() => setActiveTab('menu')}
className="w-full sm:w-auto bg-[#E8A857] hover:bg-[#d99746] text-[#3D2817] font-extrabold px-8 py-4 rounded-full shadow-lg hover:shadow-xl transition-all transform hover:-translate-y-1 text-center"
>
Order Warm Cookies
</button>
<button
onClick={() => setActiveTab('about')}
className="w-full sm:w-auto border-2 border-[#8B5A2B] text-[#8B5A2B] hover:bg-[#8B5A2B] hover:text-white font-bold px-8 py-4 rounded-full transition-all text-center"
>
Our Story
</button>
</div>
{/* Trust Badges */}
<div className="pt-6 flex items-center justify-center md:justify-start gap-6 text-xs font-bold text-gray-500">
<div className="flex items-center gap-1.5">
<Check className="w-4 h-4 text-[#8B5A2B]" /> 100% Real Butter
</div>
<div className="flex items-center gap-1.5">
<Check className="w-4 h-4 text-[#8B5A2B]" /> Zero Preservatives
</div>
</div>
</div>
{/* Hero Image / Visual */}
<div className="relative">
<div className="absolute -inset-4 bg-[#E8A857]/30 rounded-full blur-3xl -z-10" />
<div className="relative rounded-3xl overflow-hidden shadow-2xl border-4 border-white transform rotate-2 hover:rotate-0 transition-transform duration-500">
<img
src="https://images.unsplash.com/photo-1499636136210-6f4ee915583e?auto=format&fit=crop&q=80&w=800"
alt="Gooey warm chocolate chunk cookie pull"
className="w-full h-[400px] sm:h-[480px] object-cover"
/>
<div className="absolute bottom-4 left-4 right-4 bg-white/90 backdrop-blur-md p-4 rounded-2xl shadow-lg flex items-center justify-between">
<div>
<p className="font-black text-[#3D2817] text-sm">Classic Chocolate Chunk</p>
<p className="text-xs text-[#8B5A2B]">Molten Belgian dark chocolate</p>
</div>
<button
onClick={() => addToCart(PRODUCTS[0])}
className="bg-[#8B5A2B] text-white px-3.5 py-2 rounded-xl text-xs font-bold hover:bg-[#3D2817] transition-colors"
>
Quick Add
</button>
</div>
</div>
</div>
</div>
</div>
</section>
{/* FEATURED FLAVORS CAROUSEL / GRID */}
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex flex-col md:flex-row md:items-end justify-between mb-8">
<div>
<span className="text-xs font-bold text-[#8B5A2B] tracking-widest uppercase">Fresh From The Oven</span>
<h2 className="text-3xl font-black text-[#3D2817] mt-1">This Week’s Lineup</h2>
</div>
<button
onClick={() => setActiveTab('menu')}
className="mt-4 md:mt-0 text-[#8B5A2B] font-bold text-sm flex items-center gap-1 hover:gap-2 transition-all"
>
Explore All Flavors <ChevronRight className="w-4 h-4" />
</button>
</div>
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-6">
{PRODUCTS.slice(0, 4).map(product => (
<ProductCard key={product.id} product={product} onSelectProduct={onSelectProduct} />
))}
</div>
</section>
{/* HOW IT WORKS */}
<section className="bg-[#FFF8EC] py-16 border-y border-[#8B5A2B]/10">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<span className="text-xs font-bold text-[#8B5A2B] tracking-widest uppercase">Simple & Sweet</span>
<h2 className="text-3xl font-black text-[#3D2817] mt-1 mb-12">How To Get Your Crumble Fix</h2>
<div className="grid md:grid-cols-3 gap-8">
<div className="bg-white p-8 rounded-3xl shadow-sm space-y-3 relative">
<div className="w-14 h-14 bg-[#E8A857]/20 text-[#8B5A2B] rounded-2xl font-black text-xl flex items-center justify-center mx-auto">
1
</div>
<h3 className="font-bold text-lg text-[#3D2817]">Pick Your Flavors</h3>
<p className="text-xs text-gray-600">Browse our weekly rotating lineup of warm classic and stuffed oversized cookies.</p>
</div>
<div className="bg-white p-8 rounded-3xl shadow-sm space-y-3 relative">
<div className="w-14 h-14 bg-[#E8A857]/20 text-[#8B5A2B] rounded-2xl font-black text-xl flex items-center justify-center mx-auto">
2
</div>
<h3 className="font-bold text-lg text-[#3D2817]">Baked To Order</h3>
<p className="text-xs text-gray-600">Our bakers prepare your warm treats in small batches using premium natural ingredients.</p>
</div>
<div className="bg-white p-8 rounded-3xl shadow-sm space-y-3 relative">
<div className="w-14 h-14 bg-[#E8A857]/20 text-[#8B5A2B] rounded-2xl font-black text-xl flex items-center justify-center mx-auto">
3
</div>
<h3 className="font-bold text-lg text-[#3D2817]">Warm Delivery or Pickup</h3>
<p className="text-xs text-gray-600">Get your pink-accent box delivered straight to your door or pick up warm in store.</p>
</div>
</div>
</div>
</section>
{/* TESTIMONIALS */}
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center max-w-2xl mx-auto mb-12">
<span className="text-xs font-bold text-[#8B5A2B] tracking-widest uppercase">Real Cookie Lovers</span>
<h2 className="text-3xl font-black text-[#3D2817] mt-1">What Our Fans Say</h2>
</div>
<div className="grid md:grid-cols-3 gap-6">
{TESTIMONIALS.map(t => (
<div key={t.id} className="bg-white p-6 rounded-3xl border border-[#8B5A2B]/10 shadow-sm flex flex-col justify-between">
<div>
<div className="flex gap-1 text-amber-400 mb-3">
{[...Array(t.rating)].map((_, i) => (
<Star key={i} className="w-4 h-4 fill-current" />
))}
</div>
<p className="text-sm text-gray-700 italic">"{t.quote}"</p>
</div>
<div className="flex items-center gap-3 mt-6 pt-4 border-t border-gray-100">
<img src={t.avatar} alt={t.name} className="w-10 h-10 rounded-full object-cover" />
<div>
<h4 className="text-xs font-bold text-[#3D2817]">{t.name}</h4>
<p className="text-[10px] text-gray-500">{t.role}</p>
</div>
</div>
</div>
))}
</div>
</section>
{/* CATERING BANNER */}
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="bg-[#3D2817] rounded-3xl p-8 md:p-12 text-white flex flex-col md:flex-row items-center justify-between gap-8 relative overflow-hidden shadow-xl">
<div className="space-y-4 max-w-xl z-10 text-center md:text-left">
<span className="bg-[#E8A857] text-[#3D2817] text-xs font-black uppercase px-3 py-1 rounded-full">
Events & Weddings
</span>
<h2 className="text-3xl font-black text-[#FFF8EC]">Planning a Special Event?</h2>
<p className="text-sm text-gray-300">
Bring the ultimate cookie bar to your wedding, corporate party, or birthday. Custom boxes, mini cookies, and catering trays available!
</p>
<button
onClick={() => setActiveTab('catering')}
className="inline-block bg-[#E8A857] hover:bg-[#d99746] text-[#3D2817] font-bold px-6 py-3 rounded-full text-sm transition-transform transform hover:scale-105"
>
Request Catering Quote
</button>
</div>
<div className="w-full md:w-1/3 h-48 md:h-64 rounded-2xl overflow-hidden shadow-md z-10">
<img
src="https://images.unsplash.com/photo-1558961363-fa8fdf82db35?auto=format&fit=crop&q=80&w=600"
alt="Cookie catering box"
className="w-full h-full object-cover"
/>
</div>
</div>
</section>
</div>
);
};
// 2. MENU / SHOP PAGE
const MenuPage = ({ onSelectProduct }) => {
const [activeFilter, setActiveFilter] = useState('All');
const categories = ['All', 'Classic', 'Stuffed', 'Vegan', 'Gluten-Free'];
const filteredProducts = activeFilter === 'All'
? PRODUCTS
: PRODUCTS.filter(p => p.category === activeFilter);
return (
Our Cookie Menu
Baked in small batches with premium French butter and real Belgian chocolate.
{/* Filter Tabs */}
<div className="flex flex-wrap justify-center gap-2 mb-10">
{categories.map(cat => (
<button
key={cat}
onClick={() => setActiveFilter(cat)}
className={`px-5 py-2.5 rounded-full text-xs font-bold transition-all ${
activeFilter === cat
? 'bg-[#8B5A2B] text-white shadow-md'
: 'bg-white text-[#3D2817] border border-[#8B5A2B]/10 hover:bg-[#FFF8EC]'
}`}
>
{cat}
</button>
))}
</div>
{/* Product Grid */}
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-6">
{filteredProducts.map(product => (
<ProductCard key={product.id} product={product} onSelectProduct={onSelectProduct} />
))}
</div>
</div>
);
};
// 3. PRODUCT DETAIL PAGE / MODAL VIEW
const ProductDetailPage = ({ product, onBack, setActiveTab }) => {
const { addToCart } = useCart();
const [quantity, setQuantity] = useState(1);
if (!product) return null;
return (
← Back to Menu
<div className="bg-white rounded-3xl p-6 md:p-10 border border-[#8B5A2B]/10 shadow-xl grid md:grid-cols-2 gap-10">
<div className="rounded-2xl overflow-hidden bg-[#FFF8EC]">
<img
src={product.image}
alt={product.name}
className="w-full h-96 object-cover"
/>
</div>
<div className="flex flex-col justify-between space-y-6">
<div>
<span className="text-xs font-bold uppercase tracking-widest text-[#8B5A2B] bg-[#FFF8EC] px-3 py-1 rounded-full">
{product.category}
</span>
<h1 className="text-3xl font-black text-[#3D2817] mt-3">{product.name}</h1>
<p className="text-2xl font-bold text-[#8B5A2B] mt-2">${product.price.toFixed(2)}</p>
<p className="text-sm text-gray-600 mt-4 leading-relaxed">{product.description}</p>
<div className="mt-6 space-y-2 border-t pt-4 border-gray-100">
<div className="flex justify-between text-xs">
<span className="font-bold text-[#3D2817]">Allergens:</span>
<span className="text-gray-500">{product.allergens.join(', ')}</span>
</div>
<div className="flex justify-between text-xs">
<span className="font-bold text-[#3D2817]">Calories:</span>
<span className="text-gray-500">{product.calories}</span>
</div>
</div>
</div>
<div className="space-y-4 pt-4 border-t border-gray-100">
<div className="flex items-center gap-4">
<div className="flex items-center border border-gray-300 rounded-full px-3 py-1.5 bg-[#FFF8EC]">
<button onClick={() => setQuantity(Math.max(1, quantity - 1))} className="p-1 text-[#3D2817]">
<Minus className="w-4 h-4" />
</button>
<span className="px-4 font-bold text-sm text-[#3D2817]">{quantity}</span>
<button onClick={() => setQuantity(quantity + 1)} className="p-1 text-[#3D2817]">
<Plus className="w-4 h-4" />
</button>
</div>
<button
onClick={() => {
addToCart(product, quantity);
}}
className="flex-1 bg-[#E8A857] hover:bg-[#d99746] text-[#3D2817] font-extrabold py-3.5 rounded-full shadow-lg transition-transform active:scale-95 text-sm"
>
Add To Box — ${(product.price * quantity).toFixed(2)}
</button>
</div>
</div>
</div>
</div>
</div>
);
};
// 4. ABOUT US PAGE
const AboutPage = () => (
Our Story
Born From Pure Obsession
<div className="grid md:grid-cols-2 gap-10 items-center">
<img
src="https://images.unsplash.com/photo-1556911220-e15b29be8c8f?auto=format&fit=crop&q=80&w=600"
alt="Baker preparing cookie dough"
className="rounded-3xl shadow-lg object-cover h-96 w-full"
/>
<div className="space-y-4 text-gray-700 text-sm leading-relaxed">
<h2 className="text-2xl font-black text-[#3D2817]">Thick, Gooey, Unapologetic.</h2>
<p>
Crumble Cookie started in a small home kitchen with one simple mission: build the ultimate cookie. No thin, dry, store-bought wafers — we wanted heavy, thick, gooey-centered cookies loaded with highest quality ingredients.
</p>
<p>
Every dough batch is chilled for 24 hours to intensify flavors, then baked warm in our specialized convection ovens throughout the day.
</p>
</div>
</div>
);
// 5. LOCATIONS PAGE
const LocationsPage = () => (
Find A Bakery Near You
Stop by for a warm cookie straight out of the oven.
<div className="grid md:grid-cols-2 gap-6">
{LOCATIONS.map((loc, idx) => (
<div key={idx} className="bg-white p-6 rounded-3xl border border-[#8B5A2B]/10 shadow-sm space-y-4">
<h3 className="text-xl font-black text-[#3D2817]">{loc.city}</h3>
<div className="space-y-2 text-xs text-gray-600">
<p className="flex items-center gap-2"><MapPin className="w-4 h-4 text-[#8B5A2B]" /> {loc.address}</p>
<p className="flex items-center gap-2"><Clock className="w-4 h-4 text-[#8B5A2B]" /> {loc.hours}</p>
<p className="flex items-center gap-2"><Phone className="w-4 h-4 text-[#8B5A2B]" /> {loc.phone}</p>
</div>
<button className="w-full mt-2 bg-[#FFF8EC] text-[#8B5A2B] hover:bg-[#8B5A2B] hover:text-white font-bold text-xs py-2.5 rounded-full transition-colors border border-[#8B5A2B]/20">
Get Directions
</button>
</div>
))}
</div>
);
// 6. CATERING PAGE
const CateringPage = () => {
const [submitted, setSubmitted] = useState(false);
return (
Cookie Catering & Events
Make your special day unforgettable with warm, custom cookies.
{submitted ? (
<div className="bg-white p-8 rounded-3xl shadow-md text-center space-y-3">
<div className="text-5xl">🎉</div>
<h2 className="text-2xl font-bold text-[#3D2817]">Inquiry Received!</h2>
<p className="text-xs text-gray-600">Our catering coordinator will reach out to you within 24 hours.</p>
</div>
) : (
<form onSubmit={(e) => { e.preventDefault(); setSubmitted(true); }} className="bg-white p-8 rounded-3xl border border-[#8B5A2B]/10 shadow-lg space-y-4">
<div className="grid md:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Your Name</label>
<input required type="text" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none focus:border-[#8B5A2B]" placeholder="Jane Doe" />
</div>
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Email Address</label>
<input required type="email" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none focus:border-[#8B5A2B]" placeholder="jane@example.com" />
</div>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Event Date</label>
<input required type="date" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none focus:border-[#8B5A2B]" />
</div>
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Estimated Quantity (Cookies)</label>
<input required type="number" min="24" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none focus:border-[#8B5A2B]" placeholder="e.g. 50" />
</div>
</div>
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Event Details & Requests</label>
<textarea rows="4" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none focus:border-[#8B5A2B]" placeholder="Tell us about your event, preferred flavors, custom box preferences..."></textarea>
</div>
<button type="submit" className="w-full bg-[#E8A857] hover:bg-[#d99746] text-[#3D2817] font-extrabold py-3.5 rounded-full text-sm shadow">
Submit Catering Request
</button>
</form>
)}
</div>
);
};
// 7. FAQ PAGE
const FAQPage = () => {
const [openIdx, setOpenIdx] = useState(null);
return (
Frequently Asked Questions
Everything you need to know about our warm treats.
<div className="space-y-3">
{FAQS.map((faq, idx) => (
<div key={idx} className="bg-white rounded-2xl border border-[#8B5A2B]/10 overflow-hidden">
<button
onClick={() => setOpenIdx(openIdx === idx ? null : idx)}
className="w-full p-5 text-left font-bold text-sm text-[#3D2817] flex justify-between items-center hover:bg-[#FFF8EC]/50"
>
<span>{faq.q}</span>
<ChevronDown className={`w-4 h-4 transition-transform ${openIdx === idx ? 'rotate-180' : ''}`} />
</button>
{openIdx === idx && (
<div className="p-5 pt-0 text-xs text-gray-600 border-t border-gray-100 bg-white">
{faq.a}
</div>
)}
</div>
))}
</div>
</div>
);
};
// 8. CONTACT PAGE
const ContactPage = () => {
const [sent, setSent] = useState(false);
return (
Say Hello
Have a question or feedback? We’d love to hear from you!
{sent ? (
<div className="bg-white p-8 rounded-3xl shadow-md text-center space-y-2">
<div className="text-4xl">📬</div>
<h2 className="text-xl font-bold text-[#3D2817]">Message Sent!</h2>
<p className="text-xs text-gray-600">We'll respond to your email as soon as possible.</p>
</div>
) : (
<form onSubmit={(e) => { e.preventDefault(); setSent(true); }} className="bg-white p-8 rounded-3xl border border-[#8B5A2B]/10 shadow-lg space-y-4">
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Name</label>
<input required type="text" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none" />
</div>
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Email</label>
<input required type="email" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none" />
</div>
<div>
<label className="block text-xs font-bold text-[#3D2817] mb-1">Message</label>
<textarea required rows="4" className="w-full bg-[#FFF8EC] border border-gray-200 rounded-xl p-3 text-xs focus:outline-none"></textarea>
</div>
<button type="submit" className="w-full bg-[#E8A857] hover:bg-[#d99746] text-[#3D2817] font-extrabold py-3.5 rounded-full text-sm shadow">
Send Message
</button>
</form>
)}
</div>
);
};
// --- FOOTER COMPONENT ---
const Footer = ({ setActiveTab }) => (
<div className="space-y-4">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-full bg-[#E8A857] flex items-center justify-center text-[#3D2817] font-bold text-lg">
🍪
</div>
<span className="text-xl font-black text-[#FFF8EC]">Crumble Cookie</span>
</div>
<p className="text-xs text-gray-400 leading-relaxed">
Artisanal gourmet cookies, baked fresh in small batches daily. Satisfying your sweet cravings one warm cookie at a time.
</p>
</div>
<div>
<h4 className="text-xs font-bold tracking-widest text-[#E8A857] uppercase mb-4">Quick Links</h4>
<ul className="space-y-2 text-xs font-medium text-gray-300">
{['home', 'menu', 'about', 'locations', 'catering', 'faq', 'contact'].map(tab => (
<li key={tab}>
<button
onClick={() => { setActiveTab(tab); window.scrollTo({ top: 0, behavior: 'smooth' }); }}
className="hover:text-[#E8A857] capitalize transition-colors"
>
{tab}
</button>
</li>
))}
</ul>
</div>
<div>
<h4 className="text-xs font-bold tracking-widest text-[#E8A857] uppercase mb-4">Store Hours</h4>
<div className="text-xs text-gray-300 space-y-1">
<p>Monday - Thursday: 8am - 10pm</p>
<p>Friday - Saturday: 8am - 11pm</p>
<p>Sunday: 9am - 9pm</p>
</div>
</div>
<div>
<h4 className="text-xs font-bold tracking-widest text-[#E8A857] uppercase mb-4">Get 10% Off</h4>
<p className="text-xs text-gray-400 mb-3">Join our Cookie Club for exclusive discounts and weekly flavor updates.</p>
<form onSubmit={(e) => { e.preventDefault(); alert("Subscribed!"); }} className="flex gap-2">
<input
type="email"
placeholder="Your email"
className="bg-[#FFF8EC]/10 border border-[#FFF8EC]/20 rounded-full px-4 py-2 text-xs text-white placeholder-gray-400 focus:outline-none flex-1"
/>
<button className="bg-[#E8A857] text-[#3D2817] font-bold px-4 py-2 rounded-full text-xs hover:bg-[#d99746]">
Join
</button>
</form>
</div>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-12 pt-6 border-t border-white/10 text-center text-[10px] text-gray-400">
© {new Date().getFullYear()} Crumble Cookie Co. All rights reserved. Crafted with care.
</div>
);
// --- MAIN APPLICATION COMPONENT ---
export default function App() {
const [activeTab, setActiveTab] = useState('home');
const [selectedProduct, setSelectedProduct] = useState(null);
const handleSelectProduct = (product) => {
setSelectedProduct(product);
setActiveTab('product-detail');
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return (
<CartDrawer />
<main>
{activeTab === 'home' && <HomePage setActiveTab={setActiveTab} onSelectProduct={handleSelectProduct} />}
{activeTab === 'menu' && <MenuPage onSelectProduct={handleSelectProduct} />}
{activeTab === 'product-detail' && <ProductDetailPage product={selectedProduct} onBack={() => setActiveTab('menu')} setActiveTab={setActiveTab} />}
{activeTab === 'about' && <AboutPage />}
{activeTab === 'locations' && <LocationsPage />}
{activeTab === 'catering' && <CateringPage />}
{activeTab === 'faq' && <FAQPage />}
{activeTab === 'contact' && <ContactPage />}
</main>
<Footer setActiveTab={setActiveTab} />
</div>
</CartProvider>
);
}
import React, { useState, useEffect, createContext, useContext } from 'react';
import {
ShoppingBag, Menu, X, Star, ChevronRight, ChevronDown,
MapPin, Clock, Phone, Mail, Instagram, Facebook,
Check, ArrowRight, Heart, Sparkles, Plus, Minus, Trash2
} from 'lucide-react';
// --- CONTEXT & STATE MANAGEMENT ---
const CartContext = createContext();
const CartProvider = ({ children }) => {
const [cart, setCart] = useState([]);
const [isCartOpen, setIsCartOpen] = useState(false);
const addToCart = (product, quantity = 1) => {
setCart(prev => {
const existing = prev.find(item => item.id === product.id);
if (existing) {
return prev.map(item =>
item.id === product.id ? { ...item, quantity: item.quantity + quantity } : item
);
}
return [...prev, { ...product, quantity }];
});
setIsCartOpen(true);
};
const removeFromCart = (id) => {
setCart(prev => prev.filter(item => item.id !== id));
};
const updateQuantity = (id, delta) => {
setCart(prev => prev.map(item => {
if (item.id === id) {
const newQty = item.quantity + delta;
return newQty > 0 ? { ...item, quantity: newQty } : item;
}
return item;
}));
};
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
const subtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
return (
<CartContext.Provider value={{
cart, addToCart, removeFromCart, updateQuantity,
isCartOpen, setIsCartOpen, totalItems, subtotal
}}>
{children}
</CartContext.Provider>
);
};
const useCart = () => useContext(CartContext);
// --- MOCK DATA ---
const PRODUCTS = [
{
id: 'c1',
name: 'Classic Chocolate Chunk',
tagline: 'Gooey perfection in every bite',
price: 3.99,
category: 'Classic',
rating: 4.9,
reviews: 328,
description: 'Thick, buttery dough loaded with oversized chunks of semi-sweet belgian chocolate, baked until gold on the outside and warm and molten inside.',
allergens: ['Milk', 'Eggs', 'Wheat', 'Soy'],
calories: '480 kcal',
image: 'https://images.unsplash.com/photo-1499636136210-6f4ee915583e?auto=format&fit=crop&q=80&w=600',
popular: true
},
{
id: 'c2',
name: 'Biscoff Butter Crumble',
tagline: 'Warm spiced speculoos bliss',
price: 4.50,
category: 'Stuffed',
rating: 5.0,
reviews: 215,
description: 'Spiced cookie dough filled with a molten cookie butter core, topped with crushed Biscoff cookies and a caramelized drizzle.',
allergens: ['Milk', 'Eggs', 'Wheat', 'Soy'],
calories: '520 kcal',
popular: true
},
{
id: 'c3',
name: 'Birthday Cake Swirl',
tagline: 'Celebration wrapped in sugar',
price: 4.25,
category: 'Classic',
rating: 4.8,
reviews: 184,
description: 'Rich vanilla cake batter dough folded with colorful rainbow sprinkles, stuffed with sweet cream cheese frosting and topped with cake crumbs.',
allergens: ['Milk', 'Eggs', 'Wheat'],
calories: '460 kcal',
popular: false
},
{
id: 'c4',
name: 'Campfire S’mores Overload',
tagline: 'Toasted gooey marshmallow dream',
price: 4.75,
category: 'Stuffed',
rating: 4.9,
reviews: 412,
description: 'Graham cracker enriched dough packed with milk chocolate chunks, stuffed with a giant gooey marshmallow, and flame-torched on top.',
allergens: ['Milk', 'Eggs', 'Wheat', 'Soy'],
calories: '510 kcal',
popular: true
},
{
id: 'c5',
name: 'Red Velvet Cream Cheese',
tagline: 'Decadent, velvet smooth perfection',
price: 4.50,
category: 'Stuffed',
rating: 4.7,
reviews: 156,
description: 'Deep cocoa red velvet dough baked warm with a silky vanilla cream cheese center and sprinkled with dark chocolate chips.',
allergens: ['Milk', 'Eggs', 'Wheat'],
calories: '490 kcal',
popular: false
},
{
id: 'c6',
name: 'Peanut Butter Lava',
tagline: 'Rich, nutty, and irresistibly gooey',
price: 4.25,
category: 'Stuffed',
rating: 4.9,
reviews: 290,
description: 'Salted peanut butter dough overflowing with a molten peanut butter fudge center and finished with crushed roasted peanuts.',
allergens: ['Peanuts', 'Milk', 'Eggs', 'Wheat'],
calories: '540 kcal',
popular: true
},
{
id: 'c7',
name: 'Plant-Based Dark Choc Fudge',
tagline: '100% Vegan, 100% Indulgent',
price: 4.50,
category: 'Vegan',
rating: 4.8,
reviews: 98,
description: 'Rich, fudgy chocolate dough mixed with 70% dark chocolate chunks and sea salt flakes. Absolutely zero animal products, maximum decadence.',
allergens: ['Wheat', 'Soy'],
calories: '430 kcal',
popular: false
},
{
id: 'c8',
name: 'Gluten-Free Cinnamon Roll',
tagline: 'Warm cinnamon swirl goodness',
price: 4.75,
category: 'Gluten-Free',
rating: 4.6,
reviews: 87,
description: 'Crafted with almond and oat flour, swirled with brown sugar and cinnamon, drizzled with sweet cream glaze.',
allergens: ['Milk', 'Eggs', 'Tree Nuts (Almond)'],
calories: '410 kcal',
popular: false
}
];
const TESTIMONIALS = [
{
id: 1,
name: 'Sarah Jenkins',
role: 'Cookie Enthusiast',
quote: 'The Biscoff Butter Crumble literally melted my heart. You haven’t lived until you eat one of these warm!',
rating: 5,
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&q=80&w=150'
},
{
id: 2,
name: 'Marcus Chen',
role: 'Food Blogger',
quote: 'Crispy outer shell, thick and gooey inside. Crumble Cookie sets the gold standard for artisanal treats.',
rating: 5,
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&q=80&w=150'
},
{
id: 3,
name: 'Emily Watson',
role: 'Event Coordinator',
quote: 'We ordered 200 custom cookies for a corporate event. They arrived warm, fresh, and were gone in 10 minutes!',
rating: 5,
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&q=80&w=150'
}
];
const LOCATIONS = [
{
city: 'Downtown Flagship',
address: '452 Sweet Briar Lane, Suite 100',
hours: 'Mon-Sun: 8am - 10pm',
phone: '(555) 234-5678'
},
{
city: 'Uptown Bakery & Cafe',
address: '881 Gourmet Boulevard',
hours: 'Mon-Sun: 7am - 11pm',
phone: '(555) 876-5432'
}
];
const FAQS = [
{
q: 'How should I reheat my cookies?',
a: 'For that fresh-out-of-the-oven experience, pop your cookie in a preheated oven at 350°F (175°C) for 3 to 5 minutes, or microwave for 10-12 seconds!'
},
{
q: 'Do you offer gluten-free and vegan options?',
a: 'Yes! We always have at least one dedicated Vegan and one Gluten-Free flavor on our menu. They are baked using strict separation procedures, though processed in a facility that handles gluten.'
},
{
q: 'How long do the cookies stay fresh?',
a: 'Our cookies stay soft and fresh for up to 5 days in an airtight container at room temperature. You can also freeze them for up to 3 months!'
},
{
q: 'How far in advance should I place catering orders?',
a: 'For small event boxes (2-4 dozen), 24-hour notice is appreciated. For large events or custom orders, please contact us at least 5 business days in advance.'
}
];
// --- NAVIGATION COMPONENT ---
const Navbar = ({ activeTab, setActiveTab }) => {
const [isScrolled, setIsScrolled] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const { totalItems, setIsCartOpen } = useCart();
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const navLinks = [
{ name: 'Home', id: 'home' },
{ name: 'Menu', id: 'menu' },
{ name: 'About Us', id: 'about' },
{ name: 'Locations', id: 'locations' },
{ name: 'Catering', id: 'catering' },
{ name: 'FAQ', id: 'faq' },
{ name: 'Contact', id: 'contact' }
];
const handleNavClick = (id) => {
setActiveTab(id);
setMobileMenuOpen(false);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return (
<nav className={
fixed top-0 left-0 right-0 z-40 transition-all duration-300 ${ isScrolled ? 'bg-[#FFF8EC]/95 backdrop-blur-md shadow-md py-3' : 'bg-transparent py-5' }}>);
};
// --- CART DRAWER COMPONENT ---
const CartDrawer = () => {
const { cart, isCartOpen, setIsCartOpen, removeFromCart, updateQuantity, subtotal } = useCart();
if (!isCartOpen) return null;
return (
<div
className="absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity"
onClick={() => setIsCartOpen(false)}
/>
);
};
// --- PRODUCT CARD COMPONENT ---
const ProductCard = ({ product, onSelectProduct }) => {
const { addToCart } = useCart();
return (
<div
className="relative overflow-hidden rounded-2xl cursor-pointer aspect-square mb-4 bg-[#FFF8EC]"
onClick={() => onSelectProduct(product)}
>
{product.popular && (
Best Seller
)}
{product.category}
);
};
// --- PAGES ---
// 1. HOME PAGE
const HomePage = ({ setActiveTab, onSelectProduct }) => {
const { addToCart } = useCart();
return (
);
};
// 2. MENU / SHOP PAGE
const MenuPage = ({ onSelectProduct }) => {
const [activeFilter, setActiveFilter] = useState('All');
const categories = ['All', 'Classic', 'Stuffed', 'Vegan', 'Gluten-Free'];
const filteredProducts = activeFilter === 'All'
? PRODUCTS
: PRODUCTS.filter(p => p.category === activeFilter);
return (
Our Cookie Menu
Baked in small batches with premium French butter and real Belgian chocolate.
);
};
// 3. PRODUCT DETAIL PAGE / MODAL VIEW
const ProductDetailPage = ({ product, onBack, setActiveTab }) => {
const { addToCart } = useCart();
const [quantity, setQuantity] = useState(1);
if (!product) return null;
return (
← Back to Menu
);
};
// 4. ABOUT US PAGE
const AboutPage = () => (
Born From Pure Obsession
// 5. LOCATIONS PAGE
const LocationsPage = () => (
Find A Bakery Near You
Stop by for a warm cookie straight out of the oven.
// 6. CATERING PAGE
const CateringPage = () => {
const [submitted, setSubmitted] = useState(false);
return (
Cookie Catering & Events
Make your special day unforgettable with warm, custom cookies.
);
};
// 7. FAQ PAGE
const FAQPage = () => {
const [openIdx, setOpenIdx] = useState(null);
return (
Frequently Asked Questions
Everything you need to know about our warm treats.
);
};
// 8. CONTACT PAGE
const ContactPage = () => {
const [sent, setSent] = useState(false);
return (
Say Hello
Have a question or feedback? We’d love to hear from you!
);
};
// --- FOOTER COMPONENT ---
const Footer = ({ setActiveTab }) => (
// --- MAIN APPLICATION COMPONENT ---
export default function App() {
const [activeTab, setActiveTab] = useState('home');
const [selectedProduct, setSelectedProduct] = useState(null);
const handleSelectProduct = (product) => {
setSelectedProduct(product);
setActiveTab('product-detail');
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return (
);
}