Compare commits
19 Commits
version_5_
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f83f105ce | |||
|
|
ec07521e6f | ||
|
|
dd2fb07049 | ||
| 2c34a642b7 | |||
|
|
f75f10a447 | ||
| 40cc266c9c | |||
|
|
77d5ba721a | ||
| 40cd0dd55b | |||
|
|
1dd53b266d | ||
| ca77bbff42 | |||
|
|
598978467c | ||
| f18a94ea3f | |||
|
|
aebd0911fd | ||
| bf880a6f34 | |||
|
|
b4227ecc8b | ||
| 89ffb05621 | |||
| f65a66bb7a | |||
| 7282580819 | |||
| c6d88dd4b7 |
@@ -17,7 +17,12 @@ import ContactSection from './HomePage/sections/Contact';
|
||||
|
||||
import FaqSection from './HomePage/sections/Faq';
|
||||
import CertificationsSection from './HomePage/sections/Certifications';
|
||||
import ClientLogosSection from './HomePage/sections/ClientLogos';export default function HomePage(): React.JSX.Element {
|
||||
import ClientLogosSection from './HomePage/sections/ClientLogos';
|
||||
import FloatingCtaSection from './HomePage/sections/FloatingCta';
|
||||
import GuaranteeSection from './HomePage/sections/Guarantee';
|
||||
import BackgroundMusicSection from './HomePage/sections/BackgroundMusic';
|
||||
import NewsletterSection from './HomePage/sections/Newsletter';
|
||||
import PricingSection from './HomePage/sections/Pricing';export default function HomePage(): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<HeroSection />
|
||||
@@ -35,9 +40,14 @@ import ClientLogosSection from './HomePage/sections/ClientLogos';export default
|
||||
<ClientLogosSection />
|
||||
|
||||
<SocialProofSection />
|
||||
<PricingSection />
|
||||
<FaqSection />
|
||||
<GuaranteeSection />
|
||||
|
||||
<ContactSection />
|
||||
<NewsletterSection />
|
||||
<FloatingCtaSection />
|
||||
<BackgroundMusicSection />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
46
src/pages/HomePage/sections/BackgroundMusic.tsx
Normal file
46
src/pages/HomePage/sections/BackgroundMusic.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { Volume2, VolumeX } from 'lucide-react';
|
||||
|
||||
export default function BackgroundMusicSection() {
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const audioRef = useRef<HTMLAudioElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (audioRef.current) {
|
||||
audioRef.current.volume = 0.15;
|
||||
audioRef.current.play().then(() => {
|
||||
setIsPlaying(true);
|
||||
}).catch(() => {
|
||||
setIsPlaying(false);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const togglePlay = () => {
|
||||
if (audioRef.current) {
|
||||
if (isPlaying) {
|
||||
audioRef.current.pause();
|
||||
} else {
|
||||
audioRef.current.play();
|
||||
}
|
||||
setIsPlaying(!isPlaying);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div data-webild-section="background-music" id="background-music" className="fixed bottom-4 right-4 z-50">
|
||||
<audio
|
||||
ref={audioRef}
|
||||
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
|
||||
loop
|
||||
/>
|
||||
<button
|
||||
onClick={togglePlay}
|
||||
className="w-10 h-10 rounded-full bg-background/90 backdrop-blur border border-black/10 flex items-center justify-center text-foreground hover:bg-background transition-colors shadow-lg"
|
||||
aria-label="Toggle background music"
|
||||
>
|
||||
{isPlaying ? <Volume2 size={18} /> : <VolumeX size={18} />}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +1,19 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "contact" section.
|
||||
// Created by add_section_from_catalog (ContactCenter).
|
||||
|
||||
import React from 'react';
|
||||
import ContactCta from '@/components/sections/contact/ContactCta';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
import ContactCenter from '@/components/sections/contact/ContactCenter';
|
||||
|
||||
export default function ContactSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="contact" data-section="contact">
|
||||
<SectionErrorBoundary name="contact">
|
||||
<ContactCta
|
||||
tag="Visit Us"
|
||||
text="Ready to taste the difference? Stop by our bakery or pre-order online for the freshest loaves."
|
||||
primaryButton={{
|
||||
text: "Visit Our Shop",
|
||||
href: "#",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "Call to Order",
|
||||
href: "#",
|
||||
}}
|
||||
textAnimation="slide-up"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
<div data-webild-section="contact" data-section="contact" id="contact">
|
||||
<ContactCenter
|
||||
inputPlaceholder="Email Address"
|
||||
textAnimation="slide-up"
|
||||
title="Reserve Your Loaf"
|
||||
tag="Pre-Order"
|
||||
description="Our small-batch sourdough sells out quickly. Enter your email below to secure your fresh-baked bread."
|
||||
buttonText="Secure My Loaf"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
15
src/pages/HomePage/sections/FloatingCta.tsx
Normal file
15
src/pages/HomePage/sections/FloatingCta.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import Button from "@/components/ui/Button";
|
||||
|
||||
export default function FloatingCta() {
|
||||
return (
|
||||
<div data-webild-section="floating-cta" className="fixed bottom-4 left-1/2 -translate-x-1/2 w-[calc(100%-2rem)] max-w-md z-50 pointer-events-none">
|
||||
<div className="card pointer-events-auto w-full p-4 flex items-center justify-between gap-4 shadow-2xl rounded-xl border border-black/5">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-bold text-foreground">Ready to order?</span>
|
||||
<span className="text-xs text-accent">Get in touch today</span>
|
||||
</div>
|
||||
<Button text="Contact Us" variant="primary" href="#contact" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
12
src/pages/HomePage/sections/Guarantee.tsx
Normal file
12
src/pages/HomePage/sections/Guarantee.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ShieldCheck } from "lucide-react";
|
||||
|
||||
export default function GuaranteeSection() {
|
||||
return (
|
||||
<section data-webild-section="guarantee" className="w-full bg-background pt-16 pb-8 flex justify-center">
|
||||
<div className="inline-flex items-center gap-3 px-6 py-3 rounded-full bg-card border border-black/5 shadow-sm">
|
||||
<ShieldCheck className="w-6 h-6 text-accent" />
|
||||
<span className="text-sm font-medium text-foreground">100% Satisfaction Guarantee — Love your loaf or it's on us.</span>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
38
src/pages/HomePage/sections/Newsletter.tsx
Normal file
38
src/pages/HomePage/sections/Newsletter.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import TextAnimation from '@/components/ui/TextAnimation';
|
||||
import Input from '@/components/ui/Input';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Tag from '@/components/ui/Tag';
|
||||
|
||||
export default function NewsletterSection() {
|
||||
return (
|
||||
<div data-webild-section="newsletter" id="newsletter" className="py-24 bg-background">
|
||||
<div className="w-content-width mx-auto text-center flex flex-col items-center">
|
||||
<Tag text="Industry Insights" className="mb-6" />
|
||||
<TextAnimation
|
||||
text="Join Our Artisan Community"
|
||||
variant="slide-up"
|
||||
tag="h2"
|
||||
gradientText={false}
|
||||
className="text-4xl font-bold text-foreground mb-4"
|
||||
/>
|
||||
<p className="text-lg text-accent mb-8 max-w-2xl">
|
||||
Not ready to order yet? Subscribe to our newsletter for baking tips, behind-the-scenes stories, and exclusive early access to seasonal loaves.
|
||||
</p>
|
||||
<form className="flex flex-col sm:flex-row gap-4 justify-center w-full max-w-md" onSubmit={(e) => e.preventDefault()}>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Enter your email address"
|
||||
className="flex-1"
|
||||
required
|
||||
/>
|
||||
<Button
|
||||
text="Subscribe"
|
||||
variant="primary"
|
||||
className="w-full sm:w-auto"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
120
src/pages/HomePage/sections/Pricing.tsx
Normal file
120
src/pages/HomePage/sections/Pricing.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Check } from "lucide-react";
|
||||
import Button from "@/components/ui/Button";
|
||||
import Card from "@/components/ui/Card";
|
||||
import Tag from "@/components/ui/Tag";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ScrollReveal from "@/components/ui/ScrollReveal";
|
||||
|
||||
export default function PricingSection() {
|
||||
const plans = [
|
||||
{
|
||||
name: "Single Loaf",
|
||||
price: "$12",
|
||||
period: "/loaf",
|
||||
description: "Perfect for trying our signature 48-hour sourdough.",
|
||||
features: [
|
||||
"1 Classic Sourdough Loaf",
|
||||
"Freshly baked same-day",
|
||||
"In-store pickup",
|
||||
],
|
||||
buttonText: "Order Now",
|
||||
highlighted: false,
|
||||
},
|
||||
{
|
||||
name: "Weekly Subscription",
|
||||
price: "$40",
|
||||
period: "/month",
|
||||
description: "Never run out of fresh, artisan bread for your family.",
|
||||
features: [
|
||||
"1 Loaf per week (4 total)",
|
||||
"Priority baking schedule",
|
||||
"Free local delivery",
|
||||
"Early access to seasonal flavors",
|
||||
],
|
||||
buttonText: "Subscribe",
|
||||
highlighted: true,
|
||||
highlightText: "Most Popular",
|
||||
},
|
||||
{
|
||||
name: "Wholesale",
|
||||
price: "Custom",
|
||||
period: "",
|
||||
description: "For cafes and restaurants serving quality to their guests.",
|
||||
features: [
|
||||
"Bulk pricing available",
|
||||
"Custom loaf sizes & shapes",
|
||||
"Daily morning delivery",
|
||||
"Dedicated account manager",
|
||||
],
|
||||
buttonText: "Request Quote",
|
||||
highlighted: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="pricing" data-webild-section="pricing" className="relative w-full bg-background">
|
||||
<div className="w-content-width mx-auto">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<ScrollReveal variant="fade">
|
||||
<Tag text="Pricing & Tiers" className="mb-4" />
|
||||
</ScrollReveal>
|
||||
<TextAnimation
|
||||
text="Choose Your Bread Journey"
|
||||
variant="fade-blur"
|
||||
tag="h2"
|
||||
className="text-4xl md:text-5xl font-bold text-foreground mb-6"
|
||||
gradientText={false}
|
||||
/>
|
||||
<ScrollReveal variant="fade" delay={0.1}>
|
||||
<p className="text-lg text-accent max-w-content-width mx-auto">
|
||||
Simple, transparent pricing for individuals, families, and local businesses. Evaluate the best fit for your needs.
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 items-center">
|
||||
{plans.map((plan, index) => (
|
||||
<ScrollReveal variant="fade" key={index} delay={index * 0.1} className="h-full">
|
||||
<Card className={`relative h-full flex flex-col p-8 ${plan.highlighted ?'border-2 border-[var(--primary-cta)] shadow-xl md:scale-105 z-10' : ''}`}>
|
||||
{plan.highlighted && (
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
<span className="bg-foreground text-background text-xs font-bold px-4 py-1.5 rounded-full uppercase tracking-wider whitespace-nowrap">
|
||||
{plan.highlightText}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-8">
|
||||
<h3 className="text-xl font-semibold text-foreground mb-2">{plan.name}</h3>
|
||||
<div className="flex items-baseline gap-1 mb-4">
|
||||
<span className="text-4xl font-bold text-foreground">{plan.price}</span>
|
||||
{plan.period && <span className="text-accent">{plan.period}</span>}
|
||||
</div>
|
||||
<p className="text-accent text-sm">{plan.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex-grow mb-8">
|
||||
<ul className="space-y-4">
|
||||
{plan.features.map((feature, fIndex) => (
|
||||
<li key={fIndex} className="flex items-start gap-3">
|
||||
<Check className="w-5 h-5 text-foreground shrink-0" />
|
||||
<span className="text-foreground text-sm">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
text={plan.buttonText}
|
||||
variant={plan.highlighted ? 'primary' : 'secondary'}
|
||||
className="w-full justify-center"
|
||||
href="#contact"
|
||||
/>
|
||||
</Card>
|
||||
</ScrollReveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,58 +1,149 @@
|
||||
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
||||
// file as the canonical source for the "testimonials" section.
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck — generated by catalog-eject; runtime-correct but TS strict-mode false-positives on inlined catalog body
|
||||
import { Star } from "lucide-react";
|
||||
import { cls } from "@/lib/utils";
|
||||
import Button from "@/components/ui/Button";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
||||
import ScrollReveal from "@/components/ui/ScrollReveal";
|
||||
|
||||
import React from 'react';
|
||||
import TestimonialMarqueeOverlayCards from '@/components/sections/testimonial/TestimonialMarqueeOverlayCards';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
const testimonials = [
|
||||
{
|
||||
name: "Claire Miller",
|
||||
role: "Food Blogger",
|
||||
company: "EatLocal",
|
||||
rating: 5,
|
||||
result: "Featured their sourdough in a post that drove 10k+ new readers.",
|
||||
imageSrc: "https://picsum.photos/seed/1052007197/1200/800"
|
||||
},
|
||||
{
|
||||
name: "John Doe",
|
||||
role: "Local Resident",
|
||||
company: "Neighbor",
|
||||
rating: 5,
|
||||
result: "Subscribed for 2 years straight, saving 15% on weekly artisan loaves.",
|
||||
imageSrc: "https://picsum.photos/seed/1232930319/1200/800"
|
||||
},
|
||||
{
|
||||
name: "Sarah Smith",
|
||||
role: "Chef",
|
||||
company: "Bistro",
|
||||
rating: 5,
|
||||
result: "Boosted our lunch sandwich sales by 30% after switching to their bread.",
|
||||
imageSrc: "https://picsum.photos/seed/274719499/1200/800"
|
||||
},
|
||||
{
|
||||
name: "Mike Ross",
|
||||
role: "Teacher",
|
||||
company: "Public School",
|
||||
rating: 5,
|
||||
result: "Ordered 50+ loaves for our school fundraiser, raising over $500.",
|
||||
imageSrc: "https://picsum.photos/seed/1477195051/1200/800"
|
||||
},
|
||||
{
|
||||
name: "Jane Wilson",
|
||||
role: "Designer",
|
||||
company: "Studio",
|
||||
rating: 5,
|
||||
result: "Their reliable catering delivery has flawlessly served 12 of our client events.",
|
||||
imageSrc: "https://picsum.photos/seed/681714153/1200/800"
|
||||
}
|
||||
];
|
||||
|
||||
type Testimonial = {
|
||||
name: string;
|
||||
role: string;
|
||||
company: string;
|
||||
rating: number;
|
||||
result?: string;
|
||||
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
||||
|
||||
const TestimonialsInline = () => {
|
||||
const duplicated = [...testimonials, ...testimonials, ...testimonials, ...testimonials];
|
||||
|
||||
export default function TestimonialsSection(): React.JSX.Element {
|
||||
return (
|
||||
<div id="testimonials" data-section="testimonials">
|
||||
<SectionErrorBoundary name="testimonials">
|
||||
<TestimonialMarqueeOverlayCards
|
||||
tag="Testimonials"
|
||||
title="Loved by Locals"
|
||||
description="See why our neighbors make us part of their daily routine."
|
||||
testimonials={[
|
||||
{
|
||||
name: "Claire Miller",
|
||||
role: "Food Blogger",
|
||||
company: "EatLocal",
|
||||
rating: 5,
|
||||
imageSrc: "https://images.pexels.com/photos/16771767/pexels-photo-16771767.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
|
||||
},
|
||||
{
|
||||
name: "John Doe",
|
||||
role: "Local Resident",
|
||||
company: "Neighbor",
|
||||
rating: 5,
|
||||
imageSrc: "https://images.pexels.com/photos/34699447/pexels-photo-34699447.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
|
||||
},
|
||||
{
|
||||
name: "Sarah Smith",
|
||||
role: "Chef",
|
||||
company: "Bistro",
|
||||
rating: 5,
|
||||
imageSrc: "https://images.pexels.com/photos/8429811/pexels-photo-8429811.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
|
||||
},
|
||||
{
|
||||
name: "Mike Ross",
|
||||
role: "Teacher",
|
||||
company: "Public School",
|
||||
rating: 5,
|
||||
imageSrc: "https://images.pexels.com/photos/6563916/pexels-photo-6563916.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
|
||||
},
|
||||
{
|
||||
name: "Jane Wilson",
|
||||
role: "Designer",
|
||||
company: "Studio",
|
||||
rating: 5,
|
||||
imageSrc: "https://images.pexels.com/photos/24863757/pexels-photo-24863757.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
|
||||
},
|
||||
]}
|
||||
textAnimation="fade"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
<section aria-label="Testimonials section" className="pt-20 pb-10">
|
||||
<div className="flex flex-col gap-8 md:gap-10">
|
||||
<div className="flex flex-col items-center w-content-width mx-auto gap-2">
|
||||
<div className="px-3 py-1 mb-1 text-sm card rounded w-fit">
|
||||
<p>{"Testimonials"}</p>
|
||||
</div>
|
||||
|
||||
<TextAnimation
|
||||
text={"Loved by Locals"}
|
||||
variant={"fade"}
|
||||
gradientText={true}
|
||||
tag="h2"
|
||||
className="md:max-w-8/10 text-6xl 2xl:text-7xl leading-[1.15] font-semibold text-center text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={"See why our neighbors make us part of their daily routine."}
|
||||
variant={"fade"}
|
||||
gradientText={false}
|
||||
tag="p"
|
||||
className="md:max-w-7/10 text-lg md:text-xl leading-snug text-center text-balance"
|
||||
/>
|
||||
|
||||
{(undefined || undefined) && (
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="primary" />}
|
||||
{undefined && <Button text={undefined.text} href={undefined.href} variant="secondary" animationDelay={0.1} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ScrollReveal variant="slide-up">
|
||||
<div className="w-content-width mx-auto overflow-hidden mask-fade-x-medium">
|
||||
<div className="flex w-max animate-marquee-horizontal" style={{ animationDuration: "60s" }}>
|
||||
{duplicated.map((testimonial, i) => (
|
||||
<div key={i} className="relative shrink-0 w-60 md:w-75 2xl:w-80 aspect-4/5 mb-10 mr-3 md:mr-5 rounded overflow-hidden">
|
||||
<ImageOrVideo
|
||||
imageSrc={testimonial.imageSrc}
|
||||
videoSrc={testimonial.videoSrc}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
<div className="absolute inset-x-4 bottom-4 xl:inset-x-5 xl:bottom-5 2xl:inset-x-6 2xl:bottom-6 flex flex-col gap-1 xl:gap-2 2xl:gap-3 p-4 xl:p-5 2xl:p-6 card rounded backdrop-blur-sm">
|
||||
<div className="flex gap-1.5 mb-1">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Star
|
||||
key={index}
|
||||
className={cls(
|
||||
"size-5 text-accent",
|
||||
index < testimonial.rating ? "fill-accent" : "fill-transparent"
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<span className="text-2xl font-semibold leading-snug truncate">{testimonial.name}</span>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<span className="text-base leading-snug truncate">{testimonial.role}</span>
|
||||
<span className="text-base leading-snug truncate">{testimonial.company}</span>
|
||||
</div>
|
||||
{testimonial.result && (
|
||||
<p className="text-sm text-accent mt-2 line-clamp-3">
|
||||
"{testimonial.result}"
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default function TestimonialsSection() {
|
||||
return (
|
||||
<div data-webild-section="testimonials" data-section="testimonials" id="testimonials">
|
||||
<TestimonialsInline />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user