Next.js 14 website with standalone output configured for Docker deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
3 KiB
TypeScript
102 lines
3 KiB
TypeScript
"use client";
|
|
|
|
import { motion, useInView, AnimatePresence } from "framer-motion";
|
|
import { useRef, useState } from "react";
|
|
import { SectionHeader } from "@/components/shared/SectionHeader";
|
|
import { faqs } from "@/lib/constants";
|
|
import { generateFAQSchema } from "@/lib/structured-data";
|
|
|
|
function FAQItem({
|
|
faq,
|
|
index,
|
|
isOpen,
|
|
onToggle,
|
|
}: {
|
|
faq: (typeof faqs)[0];
|
|
index: number;
|
|
isOpen: boolean;
|
|
onToggle: () => void;
|
|
}) {
|
|
const ref = useRef(null);
|
|
const isInView = useInView(ref, { once: true, margin: "-50px" });
|
|
|
|
return (
|
|
<motion.div
|
|
ref={ref}
|
|
className="border-b border-deep-nazar/10 last:border-0"
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ delay: index * 0.05 }}
|
|
>
|
|
<button
|
|
onClick={onToggle}
|
|
className="w-full py-5 flex items-center justify-between text-left group"
|
|
aria-expanded={isOpen}
|
|
>
|
|
<h3 className="font-display font-semibold text-base md:text-lg text-deep-nazar pr-8 group-hover:text-bosphorus transition-colors">
|
|
{faq.question}
|
|
</h3>
|
|
<motion.span
|
|
className="flex-shrink-0 w-8 h-8 rounded-full bg-bosphorus/10 flex items-center justify-center text-bosphorus"
|
|
animate={{ rotate: isOpen ? 45 : 0 }}
|
|
transition={{ duration: 0.2 }}
|
|
>
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
</motion.span>
|
|
</button>
|
|
|
|
<AnimatePresence>
|
|
{isOpen && (
|
|
<motion.div
|
|
initial={{ height: 0, opacity: 0 }}
|
|
animate={{ height: "auto", opacity: 1 }}
|
|
exit={{ height: 0, opacity: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
className="overflow-hidden"
|
|
>
|
|
<p className="pb-5 text-deep-nazar/70 leading-relaxed text-sm md:text-base">
|
|
{faq.answer}
|
|
</p>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
export function HomeFAQ() {
|
|
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
|
|
|
return (
|
|
<section className="py-20 md:py-32 section-padding bg-warm-sand">
|
|
<div className="max-w-3xl mx-auto">
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{
|
|
__html: JSON.stringify(generateFAQSchema(faqs)),
|
|
}}
|
|
/>
|
|
|
|
<SectionHeader
|
|
eyebrow="FAQ"
|
|
title="Got Questions? Good."
|
|
subtitle="We answer the ones that actually matter."
|
|
/>
|
|
|
|
<div className="bg-white rounded-3xl p-6 md:p-8 shadow-sm">
|
|
{faqs.map((faq, i) => (
|
|
<FAQItem
|
|
key={i}
|
|
faq={faq}
|
|
index={i}
|
|
isOpen={openIndex === i}
|
|
onToggle={() => setOpenIndex(openIndex === i ? null : i)}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|