593 lines
20 KiB
TypeScript
593 lines
20 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo, useState } from "react";
|
|
import { motion } from "framer-motion";
|
|
import PaymentButton from "@/components/stripe/PaymentButton";
|
|
|
|
type ServicesAndPricingProps = {
|
|
lang: "es" | "en";
|
|
onContact: () => void;
|
|
};
|
|
|
|
type ServiceCard = {
|
|
id: string;
|
|
icon: string;
|
|
title: string;
|
|
for: string;
|
|
description: string[];
|
|
sections?: { title: string; items: string[] }[];
|
|
setup: string;
|
|
monthly: number | null;
|
|
term: string;
|
|
features: string[];
|
|
optional: string[];
|
|
cta: string;
|
|
popular?: boolean;
|
|
subtext?: string;
|
|
};
|
|
|
|
const fadeUp = {
|
|
hidden: { opacity: 0, y: 24 },
|
|
visible: { opacity: 1, y: 0 },
|
|
};
|
|
|
|
const content = {
|
|
es: {
|
|
sectionTitle: "Servicios y Precios",
|
|
sectionEyebrow: "Servicios y Precios",
|
|
headline: "Convertimos tu web en un vendedor inteligente que trabaja 24/7",
|
|
subheadline:
|
|
"En 2026, los clientes esperan respuestas instantáneas, reservas sin fricción y experiencias personalizadas. Si tu web no puede hacerlo, están yendo a tu competencia.",
|
|
yearlyLabel: "Pago anual",
|
|
yearlyDesc: "Paga anual y ahorra 15%",
|
|
saveLabel: "Descuento 15%",
|
|
setupLabel: "Instalación",
|
|
fromLabel: "Desde",
|
|
perMonth: "/mes",
|
|
billedAnnually: "(facturado anualmente)",
|
|
examplesTitle: "Casos de uso reales en Costa del Sol",
|
|
examplesCta: "Descubre tu plan ideal",
|
|
fairUseNote: "Todos los planes incluyen un uso generoso de IA para un negocio típico. Se aplica una",
|
|
fairUseLink: "política de uso justo",
|
|
fairUseEnd: ", sin cargos sorpresa.",
|
|
fairUseTitle: "Uso justo",
|
|
fairUseQuestion: 'Qué significa "uso justo"?',
|
|
close: "Cerrar",
|
|
fairUseBody: [
|
|
"Cada plan incluye suficientes mensajes de IA, llamadas y conversaciones de WhatsApp para un negocio típico de tu tamaño.",
|
|
"Si tu uso se mantiene muy por encima de lo normal (por ejemplo, porque añades nuevas marcas, locales o campañas), haremos lo siguiente:",
|
|
],
|
|
fairUseList: [
|
|
"Te avisaremos primero.",
|
|
"Te enviaremos un informe sencillo de uso.",
|
|
"Te propondremos opciones (seguir igual con algunos límites o subir de plan / añadir más capacidad).",
|
|
],
|
|
fairUseEndBody: "Nunca aplicamos cargos extra sin hablar contigo antes.",
|
|
popular: "Más popular",
|
|
},
|
|
en: {
|
|
sectionTitle: "Services & Pricing",
|
|
sectionEyebrow: "Services & Pricing",
|
|
headline: "Turn your website into a 24/7 intelligent salesperson",
|
|
subheadline:
|
|
"In 2026, customers expect instant answers, frictionless bookings, and personalized experiences. If your site can't deliver, they're going to your competitors.",
|
|
yearlyLabel: "Pay yearly",
|
|
yearlyDesc: "Pay yearly - Save 15%",
|
|
saveLabel: "Save 15%",
|
|
setupLabel: "Setup",
|
|
fromLabel: "From",
|
|
perMonth: "/month",
|
|
billedAnnually: "(billed annually)",
|
|
examplesTitle: "Real use cases on the Costa del Sol",
|
|
examplesCta: "Find your ideal plan",
|
|
fairUseNote: "All plans include generous AI usage for a typical business. A",
|
|
fairUseLink: "fair use policy",
|
|
fairUseEnd: " applies, no surprise charges.",
|
|
fairUseTitle: "Fair use",
|
|
fairUseQuestion: 'What does "fair use" mean?',
|
|
close: "Close",
|
|
fairUseBody: [
|
|
"Each plan includes enough AI messages, calls and WhatsApp conversations for a typical business of your size.",
|
|
"If your usage stays well above normal (e.g. you add new brands, locations or campaigns), we will:",
|
|
],
|
|
fairUseList: [
|
|
"Notify you first.",
|
|
"Send you a simple usage report.",
|
|
"Propose options (continue as-is with some limits, or upgrade / add capacity).",
|
|
],
|
|
fairUseEndBody: "We never apply extra charges without speaking to you first.",
|
|
popular: "Most popular",
|
|
},
|
|
} as const;
|
|
|
|
const serviceCards: Record<"es" | "en", ServiceCard[]> = {
|
|
es: [
|
|
{
|
|
id: "starter",
|
|
icon: "🤖",
|
|
title: "Empleado IA - Chat",
|
|
for: "Restaurantes, clínicas, negocios que quieren su primer empleado IA",
|
|
description: [
|
|
"Un empleado virtual que trabaja 24/7, 365 días al año. Responde preguntas, toma reservas y captura leads mientras tú duermes.",
|
|
],
|
|
sections: [
|
|
{
|
|
title: "Un empleado IA que:",
|
|
items: [
|
|
"Responde 24/7 (también a las 3 AM)",
|
|
"Habla español, inglés, francés, alemán...",
|
|
"Toma reservas y citas automáticas",
|
|
"Nunca se enferma, nunca se va de vacaciones",
|
|
],
|
|
},
|
|
],
|
|
setup: "900 €",
|
|
monthly: 299,
|
|
term: "6-12 meses",
|
|
features: [
|
|
"🤖 Chat IA en tu web",
|
|
"🌍 Hasta 3 idiomas",
|
|
"📅 Reservas automáticas",
|
|
"📊 Dashboard de clientes",
|
|
"📱 Notificaciones WhatsApp/SMS",
|
|
"📱 Notificaciones en tiempo real",
|
|
],
|
|
optional: [],
|
|
cta: "Empezar",
|
|
},
|
|
{
|
|
id: "site",
|
|
icon: "📞",
|
|
title: "Empleado IA - Chat + Voz",
|
|
for: "Negocios que no quieren perder llamadas",
|
|
description: [
|
|
"Tu empleado IA ahora también contesta el teléfono. Disponible 24/7 para responder, cualificar yivar llamadas a tu móvil o agendar directamente.",
|
|
],
|
|
sections: [
|
|
{
|
|
title: "Todo lo del plan Chat, más:",
|
|
items: [
|
|
"📞 Contesta llamadas entrantes",
|
|
"🎯 Cualifica leads por ti",
|
|
"📋 Envía resúmenes post-llamada",
|
|
"🔗 Integración con tu calendario",
|
|
],
|
|
},
|
|
],
|
|
setup: "2.500 €",
|
|
monthly: 599,
|
|
term: "6-12 meses",
|
|
features: [
|
|
"✅ Todo lo de Chat IA",
|
|
"📞 Contesta llamadas",
|
|
"🌍 Hasta 5 idiomas",
|
|
"📅 Sincroniza con tu calendario",
|
|
"📋 Resúmenes de conversaciones",
|
|
],
|
|
optional: [],
|
|
cta: "Empezar",
|
|
popular: true,
|
|
},
|
|
{
|
|
id: "growth",
|
|
icon: "🚀",
|
|
title: "Empresa IA",
|
|
for: "Cadenas, grupos o negocios con alto volumen",
|
|
description: [
|
|
"IA para toda tu operación: múltiples ubicaciones, CRM, analytics avanzado y consultoría mensual para maximizar resultados.",
|
|
],
|
|
sections: [
|
|
{
|
|
title: "Incluye:",
|
|
items: [
|
|
"Todo de Chat + Voz",
|
|
"Múltiples ubicaciones",
|
|
"CRM completo",
|
|
"Analytics avanzado",
|
|
"Consultoría mensual",
|
|
],
|
|
},
|
|
],
|
|
setup: "5.000 €",
|
|
monthly: 1499,
|
|
term: "12 meses",
|
|
features: [
|
|
"✅ Todo de Chat + Voz",
|
|
"🏢 Múltiples ubicaciones",
|
|
"🔗 Integraciones CRM",
|
|
"📈 Analytics avanzado",
|
|
"👤 Account manager dedicado",
|
|
],
|
|
optional: [],
|
|
cta: "Hablemos",
|
|
},
|
|
{
|
|
id: "enterprise",
|
|
icon: "🎯",
|
|
title: "Enterprise / A medida",
|
|
for: "Operaciones a escala con necesidades custom",
|
|
description: [
|
|
"Todo adaptado a tu operación: multimarca, white-label, account manager dedicado y SLA de rendimiento.",
|
|
],
|
|
setup: "Personalizado",
|
|
monthly: null,
|
|
term: "Definimos el alcance contigo",
|
|
features: [
|
|
"Todo a medida",
|
|
"Multimarca y white-label",
|
|
"Account manager dedicado",
|
|
"SLA de rendimiento",
|
|
],
|
|
optional: [],
|
|
cta: "Contactar",
|
|
subtext: "Hablemos",
|
|
},
|
|
],
|
|
en: [
|
|
{
|
|
id: "starter",
|
|
icon: "🤖",
|
|
title: "AI Employee - Chat",
|
|
for: "Restaurants, clinics, businesses wanting their first AI employee",
|
|
description: [
|
|
"A virtual employee that works 24/7, 365 days a year. Answers questions, takes bookings and captures leads while you sleep.",
|
|
],
|
|
sections: [
|
|
{
|
|
title: "An AI employee that:",
|
|
items: [
|
|
"Responds 24/7 (even at 3 AM)",
|
|
"Speaks Spanish, English, French, German...",
|
|
"Takes bookings and appointments automatically",
|
|
"Never gets sick, never goes on vacation",
|
|
],
|
|
},
|
|
],
|
|
setup: "€900",
|
|
monthly: 299,
|
|
term: "6-12 months",
|
|
features: [
|
|
"🤖 AI Chat on your website",
|
|
"🌍 Up to 3 languages",
|
|
"📅 Automated bookings",
|
|
"📊 Customer dashboard",
|
|
"📱 Real-time notifications",
|
|
],
|
|
optional: [],
|
|
cta: "Get Started",
|
|
},
|
|
{
|
|
id: "site",
|
|
icon: "📞",
|
|
title: "AI Employee - Chat + Voice",
|
|
for: "Businesses that can't miss calls",
|
|
description: [
|
|
"Your AI employee now also answers the phone. Available 24/7 to respond, qualify, and forward calls to your mobile or book directly in your calendar.",
|
|
],
|
|
sections: [
|
|
{
|
|
title: "Everything in Chat plan, plus:",
|
|
items: [
|
|
"📞 Answers incoming calls",
|
|
"🎯 Qualifies leads for you",
|
|
"📋 Post-call summaries",
|
|
"🔗 Calendar integration",
|
|
],
|
|
},
|
|
],
|
|
setup: "€2,500",
|
|
monthly: 599,
|
|
term: "6-12 months",
|
|
features: [
|
|
"✅ Everything in Chat AI",
|
|
"📞 Answers calls",
|
|
"🌍 Up to 5 languages",
|
|
"📅 Syncs with your calendar",
|
|
"📋 Conversation summaries",
|
|
],
|
|
optional: [],
|
|
cta: "Get Started",
|
|
popular: true,
|
|
},
|
|
{
|
|
id: "growth",
|
|
icon: "🚀",
|
|
title: "AI Company",
|
|
for: "Chains, groups or high-volume businesses",
|
|
description: [
|
|
"AI for your entire operation: multiple locations, CRM, advanced analytics and monthly consulting to maximize results.",
|
|
],
|
|
sections: [
|
|
{
|
|
title: "Includes:",
|
|
items: [
|
|
"Everything in Chat + Voice",
|
|
"Multiple locations",
|
|
"Full CRM",
|
|
"Advanced analytics",
|
|
"Monthly consulting",
|
|
],
|
|
},
|
|
],
|
|
setup: "€5,000",
|
|
monthly: 1499,
|
|
term: "12 months",
|
|
features: [
|
|
"✅ Everything in Chat + Voice",
|
|
"🏢 Multiple locations",
|
|
"🔗 CRM integrations",
|
|
"📈 Advanced analytics",
|
|
"👤 Dedicated account manager",
|
|
],
|
|
optional: [],
|
|
cta: "Let's talk",
|
|
},
|
|
{
|
|
id: "enterprise",
|
|
icon: "🎯",
|
|
title: "Enterprise / Custom",
|
|
for: "Large-scale operations with custom needs",
|
|
description: [
|
|
"Everything tailored: multi-brand, white-label, dedicated account manager, performance SLA.",
|
|
],
|
|
setup: "Custom",
|
|
monthly: null,
|
|
term: "Let us scope it",
|
|
features: [
|
|
"Everything tailored",
|
|
"Multi-brand & white-label",
|
|
"Dedicated account manager",
|
|
"Performance SLAs",
|
|
],
|
|
optional: [],
|
|
cta: "Contact us",
|
|
subtext: "Let's talk",
|
|
},
|
|
],
|
|
};
|
|
|
|
const examples = {
|
|
es: [
|
|
{ title: "🍽️ Restaurantes", text: "Reservas a las 3 AM, respuestas sobre alérgenos, sugerencias de platos." },
|
|
{ title: "🏠 Inmobiliarias", text: "IA responde dudas, agenda visitas, cualifica leads." },
|
|
{ title: "🚗 Rent a Car", text: "Cotiza y reserva en 3 idiomas sin intervención humana." },
|
|
],
|
|
en: [
|
|
{ title: "🍽️ Restaurants", text: "Reservations at 3 AM, allergy answers, dish suggestions." },
|
|
{ title: "🏠 Real Estate", text: "AI answers questions, books viewings, qualifies leads." },
|
|
{ title: "🚗 Rent a Car", text: "Quote and book in 3 languages with no human intervention." },
|
|
],
|
|
};
|
|
|
|
export default function ServicesAndPricing({
|
|
lang,
|
|
onContact,
|
|
}: ServicesAndPricingProps) {
|
|
const [yearly, setYearly] = useState(false);
|
|
const [showFairUse, setShowFairUse] = useState(false);
|
|
|
|
const t = content[lang];
|
|
const cards = serviceCards[lang];
|
|
|
|
const formatMonthly = (value: number) => {
|
|
const price = yearly ? Math.round(value * 0.85) : value;
|
|
return `${price} €${t.perMonth}`;
|
|
};
|
|
|
|
return (
|
|
<section
|
|
id="services"
|
|
className="mx-auto w-full max-w-6xl px-6 py-20"
|
|
>
|
|
<motion.div
|
|
variants={fadeUp}
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, amount: 0.3 }}
|
|
transition={{ duration: 0.7 }}
|
|
>
|
|
<p className="text-sm font-semibold uppercase tracking-[0.2em] text-white/70">
|
|
{t.sectionEyebrow}
|
|
</p>
|
|
<h2 className="mt-4 text-3xl font-bold sm:text-4xl">
|
|
{t.sectionTitle}
|
|
</h2>
|
|
<p className="mt-4 max-w-3xl text-base text-white/85">
|
|
{t.subheadline}
|
|
</p>
|
|
</motion.div>
|
|
|
|
<div className="mt-6 flex flex-col items-center justify-between gap-4 rounded-2xl border border-white/15 bg-white/10 p-5 text-center backdrop-blur sm:flex-row">
|
|
<div>
|
|
<p className="text-sm font-semibold text-white/90">{t.yearlyLabel}</p>
|
|
<p className="mt-1 text-sm text-white/75">{t.yearlyDesc}</p>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={() => setYearly((prev) => !prev)}
|
|
className="flex items-center gap-3 rounded-full border border-white/25 bg-white/10 px-4 py-2 text-sm font-semibold text-white"
|
|
>
|
|
<span>{t.saveLabel}</span>
|
|
<span
|
|
className={`relative block h-6 w-11 rounded-full transition ${
|
|
yearly ? "bg-brand-pink" : "bg-white/20"
|
|
}`}
|
|
>
|
|
<span
|
|
className={`absolute left-1 top-1 h-4 w-4 rounded-full bg-white transition ${
|
|
yearly ? "translate-x-5" : ""
|
|
}`}
|
|
/>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div className="mt-10 grid gap-6 lg:grid-cols-2 xl:grid-cols-4">
|
|
{cards.map((card, index) => (
|
|
<motion.div
|
|
key={card.id}
|
|
variants={fadeUp}
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, amount: 0.2 }}
|
|
transition={{ duration: 0.5, delay: index * 0.08 }}
|
|
className={`flex flex-col rounded-2xl border p-6 backdrop-blur transition hover:border-white/30 ${
|
|
card.popular
|
|
? "border-brand-pink bg-white/12 shadow-purple-soft"
|
|
: "border-white/15 bg-white/8"
|
|
}`}
|
|
>
|
|
{card.popular && (
|
|
<span className="mb-3 inline-flex w-fit rounded-full bg-brand-pink/25 px-3 py-0.5 text-xs font-semibold text-white">
|
|
{t.popular}
|
|
</span>
|
|
)}
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-2xl">{card.icon}</span>
|
|
<h3 className="text-lg font-semibold">{card.title}</h3>
|
|
</div>
|
|
<p className="mt-2 text-sm text-white/70">{card.for}</p>
|
|
|
|
<div className="mt-4 space-y-2 text-sm text-white/90">
|
|
{card.description.map((p, i) => (
|
|
<p key={i}>{p}</p>
|
|
))}
|
|
</div>
|
|
|
|
{card.sections?.map((section) => (
|
|
<div key={section.title} className="mt-4">
|
|
<p className="text-xs font-semibold text-white/85">
|
|
{section.title}
|
|
</p>
|
|
<ul className="mt-2 space-y-1 text-xs text-white/80">
|
|
{section.items.map((item) => (
|
|
<li key={item}>• {item}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
))}
|
|
|
|
<div className="mt-6 rounded-xl border border-white/15 bg-white/5 px-4 py-3">
|
|
<p className="text-xs text-white/70">
|
|
{t.setupLabel}: <span className="font-semibold text-white">{card.setup}</span>
|
|
</p>
|
|
<p className="mt-1 text-xl font-bold text-brand-pink">
|
|
{card.monthly === null
|
|
? card.subtext
|
|
: card.monthly !== null
|
|
? `${t.fromLabel} ${formatMonthly(card.monthly)}`
|
|
: null}
|
|
</p>
|
|
<p className="mt-1 text-xs text-white/60">
|
|
{card.term}
|
|
{card.monthly !== null && yearly && (
|
|
<span className="block">{t.billedAnnually}</span>
|
|
)}
|
|
</p>
|
|
</div>
|
|
|
|
<ul className="mt-4 space-y-1.5 text-sm text-white/85">
|
|
{card.features.map((f) => (
|
|
<li key={f}>✓ {f}</li>
|
|
))}
|
|
</ul>
|
|
{card.optional.length > 0 && (
|
|
<p className="mt-3 text-xs text-white/60">
|
|
{card.optional.join(" · ")}
|
|
</p>
|
|
)}
|
|
|
|
{/* Enterprise/Custom plan: contact form, others: Stripe payment */}
|
|
{card.id === "enterprise" ? (
|
|
<button
|
|
onClick={onContact}
|
|
className="mt-auto mt-6 w-full rounded-lg bg-brand-pink py-2.5 text-sm font-semibold text-white transition hover:bg-[#ff7bc0]"
|
|
>
|
|
{card.cta}
|
|
</button>
|
|
) : (
|
|
<PaymentButton
|
|
planId={card.id}
|
|
planType="monthly"
|
|
label={card.cta}
|
|
variant="primary"
|
|
/>
|
|
)}
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-16 rounded-2xl border border-white/15 bg-white/10 p-8 backdrop-blur">
|
|
<h3 className="text-xl font-semibold">{t.examplesTitle}</h3>
|
|
<div className="mt-6 grid gap-4 md:grid-cols-3">
|
|
{examples[lang].map((ex) => (
|
|
<div
|
|
key={ex.title}
|
|
className="rounded-xl border border-white/10 bg-white/5 p-4"
|
|
>
|
|
<p className="text-lg">{ex.title}</p>
|
|
<p className="mt-2 text-sm text-white/80">"{ex.text}"</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="mt-6 flex justify-center">
|
|
<button
|
|
onClick={onContact}
|
|
className="rounded-lg bg-brand-pink px-6 py-3 text-sm font-semibold text-white transition hover:bg-[#ff7bc0]"
|
|
>
|
|
{t.examplesCta}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="mt-6 text-center text-xs text-white/70">
|
|
{t.fairUseNote}{" "}
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowFairUse(true)}
|
|
className="font-semibold text-white underline decoration-white/60 underline-offset-2"
|
|
>
|
|
{t.fairUseLink}
|
|
</button>
|
|
{t.fairUseEnd}
|
|
</p>
|
|
|
|
{showFairUse && (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
<div
|
|
className="absolute inset-0 bg-black/60"
|
|
onClick={() => setShowFairUse(false)}
|
|
/>
|
|
<div className="relative z-10 w-full max-w-lg rounded-2xl border border-white/20 bg-[#4a3572] p-8 text-white shadow-xl">
|
|
<div className="flex items-start justify-between gap-4">
|
|
<div>
|
|
<p className="text-xs uppercase tracking-widest text-white/70">
|
|
{t.fairUseTitle}
|
|
</p>
|
|
<h3 className="mt-2 text-xl font-bold">{t.fairUseQuestion}</h3>
|
|
</div>
|
|
<button
|
|
onClick={() => setShowFairUse(false)}
|
|
className="rounded-full border border-white/40 px-3 py-1 text-xs font-semibold text-white/80 hover:bg-white/10"
|
|
>
|
|
{t.close}
|
|
</button>
|
|
</div>
|
|
<div className="mt-6 space-y-3 text-sm text-white/85">
|
|
{t.fairUseBody.map((p) => (
|
|
<p key={p}>{p}</p>
|
|
))}
|
|
<ol className="list-decimal space-y-2 pl-5">
|
|
{t.fairUseList.map((item) => (
|
|
<li key={item}>{item}</li>
|
|
))}
|
|
</ol>
|
|
<p className="font-medium">{t.fairUseEndBody}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|