feat(conversion): add Interactive Luxury FAQ Accordion and Sticky Mobile Quick Connect Dock
This commit is contained in:
@@ -182,3 +182,18 @@ h1, h2, h3, h4 {
|
||||
font-size: 16px !important; /* Prevents auto-zoom on iOS */
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-only-quick-bar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.mobile-only-quick-bar {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUpBar {
|
||||
from { transform: translateY(100%); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import PillarsSection from '@/components/PillarsSection';
|
||||
import ServicesSection from '@/components/ServicesSection';
|
||||
import TestimonialsSection from '@/components/TestimonialsSection';
|
||||
import AboutSection from '@/components/AboutSection';
|
||||
import FaqSection from '@/components/FaqSection';
|
||||
import BookingForm from '@/components/BookingForm';
|
||||
import Footer from '@/components/Footer';
|
||||
import GoldSparkles from '@/components/GoldSparkles';
|
||||
import MobileQuickBar from '@/components/MobileQuickBar';
|
||||
|
||||
export default function Home() {
|
||||
useEffect(() => {
|
||||
@@ -37,8 +39,10 @@ export default function Home() {
|
||||
<ServicesSection />
|
||||
<TestimonialsSection />
|
||||
<AboutSection />
|
||||
<FaqSection />
|
||||
<BookingForm />
|
||||
<Footer />
|
||||
<MobileQuickBar />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface FaqItem {
|
||||
question: string;
|
||||
answer: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
const faqs: FaqItem[] = [
|
||||
{
|
||||
category: 'Booking & Timing',
|
||||
question: 'How far in advance should I book my event decor?',
|
||||
answer: 'We recommend booking 4 to 8 weeks in advance for weekend milestones, birthdays, and baby showers, and 2 to 4 months for weddings and large corporate galas. However, if your event is sooner, reach out! If our setup crew has availability, we gladly accommodate rush dates.',
|
||||
},
|
||||
{
|
||||
category: 'Full-Service Experience',
|
||||
question: 'Do you handle delivery, setup, and teardown?',
|
||||
answer: 'Yes! Every Wise Hearted installation is 100% full-service. We arrive on-site early to professionally assemble and style every balloon garland, backdrop panel, and floral accent. After your celebration concludes, our team returns to safely strike and remove all rental backdrop structures.',
|
||||
},
|
||||
{
|
||||
category: 'Customization & Themes',
|
||||
question: 'Can you match my exact Pinterest board, theme, or invitation colors?',
|
||||
answer: 'Absolutely! Custom color matching is our specialty. We use premium, professional-grade balloons and advanced custom double-stuffing techniques to create bespoke color palettes (e.g., custom matte terracotta, dusty rose, chrome gold, or pearl tones) that flawlessly match your invitations and venue mood board.',
|
||||
},
|
||||
{
|
||||
category: 'Outdoor Events',
|
||||
question: 'Can balloon garlands and arched backdrops be installed outdoors?',
|
||||
answer: 'Yes! We use heavy-duty commercial baseplates, reinforced framing, and specialized rigging techniques designed specifically for Southern California weather. We also advise on ideal shaded placement to keep balloons looking glossy and vibrant all day.',
|
||||
},
|
||||
{
|
||||
category: 'Service Areas',
|
||||
question: 'What areas throughout Southern California do you serve?',
|
||||
answer: 'We proudly serve the entire Antelope Valley (Palmdale, Lancaster, Quartz Hill), Santa Clarita Valley (Valencia, Stevenson Ranch, Canyon Country), San Fernando Valley, Greater Los Angeles, and surrounding counties. We are also available for select destination events.',
|
||||
},
|
||||
{
|
||||
category: 'Payment & Quotes',
|
||||
question: 'How does the quote and booking process work?',
|
||||
answer: 'It starts with submitting a custom quote request on our website. Jackie will review your date, theme, and vision, then provide a detailed design proposal. A 50% retainer and signed contract officially locks your date on our calendar, with the remaining balance due prior to event setup.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function FaqSection() {
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
||||
|
||||
const toggleFaq = (index: number) => {
|
||||
setOpenIndex(openIndex === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
id="faq"
|
||||
style={{
|
||||
padding: 'clamp(60px, 10vw, 100px) 0',
|
||||
backgroundColor: '#0A0A0A',
|
||||
position: 'relative',
|
||||
borderBottom: '1px solid var(--border-gold)',
|
||||
}}
|
||||
>
|
||||
<div className="container" style={{ maxWidth: '940px' }}>
|
||||
{/* Header */}
|
||||
<div style={{ textAlign: 'center', marginBottom: '45px', padding: '0 8px' }}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '0.85rem',
|
||||
fontWeight: 800,
|
||||
color: 'var(--color-pink-light)',
|
||||
letterSpacing: '0.15em',
|
||||
textTransform: 'uppercase',
|
||||
display: 'block',
|
||||
marginBottom: '10px',
|
||||
}}
|
||||
>
|
||||
❓ Frequently Asked Questions
|
||||
</span>
|
||||
<h2
|
||||
style={{
|
||||
fontSize: 'clamp(2rem, 3.8vw, 2.8rem)',
|
||||
fontWeight: 800,
|
||||
marginBottom: '16px',
|
||||
}}
|
||||
>
|
||||
Everything You Need <span className="gold-gradient-text">To Know</span>
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '1.05rem',
|
||||
color: '#94A3B8',
|
||||
maxWidth: '680px',
|
||||
margin: '0 auto',
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
Got questions about setup timing, color customization, or booking deposits? Here are answers to our most common client inquiries.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* FAQ Accordion List */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
|
||||
{faqs.map((faq, idx) => {
|
||||
const isOpen = openIndex === idx;
|
||||
return (
|
||||
<div
|
||||
key={idx}
|
||||
className="glass-panel"
|
||||
style={{
|
||||
border: isOpen ? '1.5px solid var(--color-gold)' : '1px solid rgba(255, 255, 255, 0.08)',
|
||||
borderRadius: '16px',
|
||||
overflow: 'hidden',
|
||||
transition: 'all 0.25s ease',
|
||||
boxShadow: isOpen ? '0 8px 25px rgba(212, 175, 55, 0.15)' : 'none',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleFaq(idx)}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '20px 24px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
color: '#FFFFFF',
|
||||
textAlign: 'left',
|
||||
cursor: 'pointer',
|
||||
gap: '16px',
|
||||
}}
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
<span style={{ color: 'var(--color-pink-light)', fontSize: '0.9rem' }}>✦</span>
|
||||
<span style={{ fontSize: '1.05rem', fontWeight: 700, color: isOpen ? 'var(--color-gold-light)' : '#F8FAFC' }}>
|
||||
{faq.question}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '1.3rem',
|
||||
fontWeight: 700,
|
||||
color: isOpen ? 'var(--color-gold)' : '#94A3B8',
|
||||
transform: isOpen ? 'rotate(45deg)' : 'rotate(0deg)',
|
||||
transition: 'transform 0.25s ease, color 0.25s ease',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
+
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div
|
||||
style={{
|
||||
padding: '0 24px 22px 42px',
|
||||
color: '#CBD5E1',
|
||||
fontSize: '0.95rem',
|
||||
lineHeight: 1.7,
|
||||
borderTop: '1px solid rgba(255, 255, 255, 0.05)',
|
||||
paddingTop: '14px',
|
||||
}}
|
||||
>
|
||||
{faq.answer}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Bottom Help Callout */}
|
||||
<div
|
||||
style={{
|
||||
marginTop: '36px',
|
||||
textAlign: 'center',
|
||||
background: 'rgba(255, 255, 255, 0.03)',
|
||||
borderRadius: '16px',
|
||||
padding: '18px 24px',
|
||||
border: '1px solid rgba(255, 255, 255, 0.08)',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#94A3B8', fontSize: '0.92rem' }}>
|
||||
Have a custom design inquiry or special venue requirement?{' '}
|
||||
</span>
|
||||
<a
|
||||
href="#contact"
|
||||
style={{
|
||||
color: 'var(--color-gold)',
|
||||
fontWeight: 800,
|
||||
textDecoration: 'none',
|
||||
marginLeft: '4px',
|
||||
}}
|
||||
>
|
||||
Chat with Jackie directly →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function MobileQuickBar() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
// Show only after scrolling down 150px
|
||||
const scrollPos = window.scrollY;
|
||||
setIsVisible(scrollPos > 150);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="mobile-only-quick-bar"
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 9999,
|
||||
padding: '10px 14px clamp(12px, 3vw, 18px)',
|
||||
background: 'rgba(8, 8, 8, 0.94)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
WebkitBackdropFilter: 'blur(20px)',
|
||||
borderTop: '1.5px solid var(--border-gold)',
|
||||
boxShadow: '0 -10px 30px rgba(0, 0, 0, 0.8), 0 0 20px rgba(212, 175, 55, 0.15)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
animation: 'slideUpBar 0.3s ease-out',
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="tel:+16619936669"
|
||||
style={{
|
||||
flex: '1',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '6px',
|
||||
background: 'rgba(255, 255, 255, 0.08)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.18)',
|
||||
color: '#FFFFFF',
|
||||
padding: '12px 10px',
|
||||
borderRadius: '50px',
|
||||
fontSize: '0.86rem',
|
||||
fontWeight: 700,
|
||||
textDecoration: 'none',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<span>📞</span> Call / Text
|
||||
</a>
|
||||
|
||||
<Link
|
||||
href="#contact"
|
||||
className="btn-gold"
|
||||
style={{
|
||||
flex: '1.3',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '6px',
|
||||
padding: '12px 14px',
|
||||
borderRadius: '50px',
|
||||
fontSize: '0.86rem',
|
||||
fontWeight: 800,
|
||||
textDecoration: 'none',
|
||||
textAlign: 'center',
|
||||
boxShadow: '0 4px 15px rgba(212, 175, 55, 0.4)',
|
||||
}}
|
||||
>
|
||||
<span>✨</span> Get Quote
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user