"use client"; import { useId, useState, type CSSProperties, type ElementType, type PointerEvent } from "react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; export type PricingPrismPlan = { name: string; text?: string; /** Price text, so any currency works: "₹499", "Free". */ monthly: string; /** Leave out if the plan costs the same either way. */ yearly?: string; features: string[]; /** Button text. */ cta?: string; /** Makes the button a link. */ href?: string; /** Keeps the spectrum edge on all the time. */ featured?: boolean; /** Small label on the card, such as "Popular". */ badge?: string; }; type Period = "monthly" | "yearly"; type PricingPrismProps = { plans: PricingPrismPlan[]; title?: string; /** Small line above the title. */ eyebrow?: string; subtitle?: string; /** Called when a plan button without href is pressed. */ onSelect?: (plan: PricingPrismPlan, period: Period) => void; /** Words for the switch and the price ending. */ labels?: { monthly: string; yearly: string; perMonth: string; perYear: string }; /** Small note beside the yearly switch, such as "Save 20%". */ saving?: string; /** Card colour. */ tile?: string; /** Spectrum colours. Repeat the first at the end for a smooth ring. */ colors?: string[]; /** h2 by default. */ headingAs?: ElementType; /** Set padding and colours here. */ className?: string; }; const LABELS = { monthly: "Monthly", yearly: "Yearly", perMonth: "/mo", perYear: "/yr" }; const COLORS = ["#ff6b6b", "#ffc857", "#5eead4", "#38bdf8", "#a78bfa", "#f472b6", "#ff6b6b"]; const RING = "linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)"; const SPOT = "radial-gradient(15rem at var(--pp-x) var(--pp-y), #000, transparent)"; /* PricingPrism: pricing cards with a monthly / yearly switch. A spectrum splits around the pointer and runs along the card edge, like light through glass. */ export function PricingPrism({ plans, title, eyebrow, subtitle, onSelect, labels = LABELS, saving, tile = "#15122a", colors = COLORS, headingAs: Heading = "h2", className = "bg-[#0b0a14] px-5 py-16 text-white", }: PricingPrismProps) { const reduce = useReducedMotion(); const id = useId(); const [period, setPeriod] = useState("monthly"); const hasYearly = plans.some((plan) => plan.yearly); const spectrum = `conic-gradient(from var(--pp-a) at var(--pp-x) var(--pp-y), ${colors.join(", ")})`; const track = (e: PointerEvent) => { const box = e.currentTarget.getBoundingClientRect(); const x = (e.clientX - box.left) / box.width; e.currentTarget.style.setProperty("--pp-x", `${x * 100}%`); e.currentTarget.style.setProperty("--pp-y", `${((e.clientY - box.top) / box.height) * 100}%`); e.currentTarget.style.setProperty("--pp-a", `${x * 200}deg`); }; return (
{title && (
{eyebrow &&

{eyebrow}

} {title} {subtitle &&

{subtitle}

}
)} {hasYearly && (
{(["monthly", "yearly"] as const).map((option) => ( ))}
{saving && ( {saving} )}
)}
    {plans.map((plan, i) => { const yearly = period === "yearly" && !!plan.yearly; const price = yearly ? plan.yearly! : plan.monthly; const unit = yearly ? labels.perYear : labels.perMonth; const button = "block w-full rounded-xl px-4 py-2.5 text-center text-sm font-semibold transition-opacity hover:opacity-85"; const look = plan.featured ? { backgroundColor: "#fff", color: "#0b0a14" } : { boxShadow: "inset 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent)" }; return (

    {plan.name}

    {plan.badge && {plan.badge}}
    {plan.text &&

    {plan.text}

    }

    {price} {/\d/.test(price) && {unit}}

      {plan.features.map((feature) => (
    • ✓ {feature}
    • ))}
    {plan.href ? ( {plan.cta ?? "Choose plan"} ) : ( )}
    ); })}
); }