Components
A modern two-column scrollspy section highlighting AppVibed’s key advantages. Includes sticky navigation, Intersection Observer tracking, smooth scrolling, and gradient-accented UI cards.
Loading preview...
"use client";
import * as React from "react";
import { useState, useEffect, useRef } from "react";
import { TrendingDown, CheckCircle, X } from "lucide-react";
export default function WhyAppVibed() {
const [activeStep, setActiveStep] = useState(0);
const whyStepRefs = useRef<(HTMLDivElement | null)[]>([]);
// Scroll tracking with IntersectionObserver
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const index = whyStepRefs.current.findIndex(ref => ref === entry.target);
if (index !== -1) setActiveStep(index);
}
});
},
{ threshold: 0.5, rootMargin: "-20% 0px -20% 0px" }
);
whyStepRefs.current.forEach(ref => ref && observer.observe(ref));
return () => observer.disconnect();
}, []);
const steps = [
{
title: "No Monthly Fees",
description: "No matter how many employees or users you have",
detailTitle: "No Monthly Fees, Ever",
subheading: "Pay once, own forever",
paragraph:
"Stop paying per seat, per user, or per month. With AppVibed, you pay once and own the app forever—no matter if you have 5 users or 5,000.",
comparison: [
{ icon: <X className="w-5 h-5 text-red-500" />, text: "$25/user/month = $3,000/year for 10 users" },
{ icon: <CheckCircle className="w-5 h-5 text-green-500" />, text: "One-time payment. Unlimited users forever." },
],
},
{
title: "Fully Functional Templates",
description: "No breaking templates or missing features",
detailTitle: "Fully Functional Templates",
subheading: "Everything works, beautifully",
paragraph:
"Every template includes all working logic, flows, and design elements—ready to use immediately.",
comparison: [
{ icon: <X className="w-5 h-5 text-red-500" />, text: "Limited demo templates with locked features" },
{ icon: <CheckCircle className="w-5 h-5 text-green-500" />, text: "Complete, production-ready templates." },
],
},
{
title: "Your Brand, Your Way",
description: "Fully customizable and branded for your company",
detailTitle: "Your Brand, Your Way",
subheading: "Make it yours",
paragraph:
"Edit text, colors, and logos directly—no code required. Perfect for agencies and SaaS founders.",
comparison: [
{ icon: <X className="w-5 h-5 text-red-500" />, text: "Locked brand and style system" },
{ icon: <CheckCircle className="w-5 h-5 text-green-500" />, text: "Your brand. Your visuals. Your control." },
],
},
{
title: "Human Support",
description: "Real humans to help you onboard and launch",
detailTitle: "Human Support",
subheading: "We’re here to help",
paragraph:
"You’re never alone. AppVibed provides onboarding calls and one-on-one help to deploy your product.",
comparison: [
{ icon: <X className="w-5 h-5 text-red-500" />, text: "Chatbots and ticket queues only" },
{ icon: <CheckCircle className="w-5 h-5 text-green-500" />, text: "Friendly team available to guide you live." },
],
},
];
return (
<section className="py-20 px-4 bg-gradient-to-br from-white via-blue-50/30 to-purple-50/30 dark:from-gray-950 dark:via-blue-950/10 dark:to-purple-950/10">
<div className="max-w-7xl mx-auto">
<div className="grid lg:grid-cols-2 gap-12 lg:gap-16 items-start">
{/* Left Column */}
<div className="lg:sticky lg:top-20 space-y-8">
<div className="text-center lg:text-left">
<h2 className="text-4xl md:text-5xl font-bold mb-4 text-gray-900 dark:text-white">
Why{" "}
<span className="bg-gradient-to-r from-orange-500 to-pink-600 bg-clip-text text-transparent">
AppVibed
</span>
?
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Own your business tools outright. No subscriptions, no limits, no compromises.
</p>
</div>
{/* Steps Navigation */}
<div className="space-y-6">
{steps.map((step, i) => (
<div
key={i}
role="button"
tabIndex={0}
aria-label={`Navigate to ${step.title} section`}
className={`flex items-start gap-4 p-6 rounded-2xl transition-all duration-300 cursor-pointer ${
activeStep === i
? "bg-orange-50 dark:bg-orange-900/20 border-2 border-orange-500 shadow-lg shadow-orange-500/20"
: "bg-white/50 dark:bg-gray-800/50 border-2 border-transparent hover:border-orange-300 dark:hover:border-orange-700"
}`}
onClick={() =>
whyStepRefs.current[i]?.scrollIntoView({
behavior: "smooth",
block: "center",
})
}
>
<div
className={`flex-shrink-0 w-12 h-12 rounded-xl flex items-center justify-center text-2xl font-bold transition-all duration-300 ${
activeStep === i
? "bg-gradient-to-br from-orange-500 to-pink-600 text-white shadow-lg"
: "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
}`}
>
{i + 1}
</div>
<div className="flex-1">
<h3
className={`text-lg font-bold mb-2 transition-colors duration-300 ${
activeStep === i
? "text-orange-600 dark:text-orange-400"
: "text-gray-900 dark:text-white"
}`}
>
{step.title}
</h3>
<p className="text-gray-600 dark:text-gray-300 text-sm leading-relaxed">
{step.description}
</p>
</div>
</div>
))}
</div>
</div>
{/* Right Column */}
<div className="space-y-16">
{steps.map((step, i) => (
<div key={i} ref={(el) => (whyStepRefs.current[i] = el)} className="scroll-mt-24">
<div className="bg-white dark:bg-gray-800 rounded-3xl p-8 md:p-12 shadow-xl border border-gray-200 dark:border-gray-700">
<div className="flex items-center gap-4 mb-6">
<div className="w-16 h-16 bg-gradient-to-br from-red-500 to-orange-600 rounded-2xl flex items-center justify-center shadow-lg">
<TrendingDown className="h-8 w-8 text-white" />
</div>
<div>
<h3 className="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white">
{step.detailTitle}
</h3>
<p className="text-orange-600 dark:text-orange-400 font-semibold">{step.subheading}</p>
</div>
</div>
<p className="text-lg text-gray-600 dark:text-gray-300 leading-relaxed mb-6">
{step.paragraph}
</p>
<div className="bg-gradient-to-br from-red-50 to-orange-50 dark:from-red-900/20 dark:to-orange-900/20 rounded-2xl p-6 border border-red-200 dark:border-red-800">
<h4 className="font-bold text-gray-900 dark:text-white mb-4">Comparison</h4>
<div className="space-y-3">
{step.comparison.map((item, j) => (
<div key={j} className="flex items-center gap-3">
{item.icon}
<span className="text-gray-700 dark:text-gray-300">{item.text}</span>
</div>
))}
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</section>
);
}
Loading preview...