CTA

CTA 03 — Signature Closer

Minimal closing band over a masked interactive mesh backdrop: oversized Swiss headline, a bordered action rail with primary CTA and risk-reversal caption, and a three-row hairline micro-proof ledger.

$ npx shadcn@latest add @ludusvibe/cta-03
Open in v0

Code

Installed to components/sections/cta-03.tsx — it's yours to edit.

"use client"

import * as React from "react"
import { motion } from "motion/react"
import { ArrowRight, Star } from "lucide-react"

import { buttonVariants } from "@/components/ui/button"
import { Grain } from "@/components/ludus/grain"
import { MeshBackdrop } from "@/components/ludus/mesh-backdrop"
import { fadeUp, stagger, viewport } from "@/lib/motion"
import { cn } from "@/lib/utils"

type Cta = { label: string; href: string }

type ProofItem = { label: string; value: string }

export type Cta03Props = {
  index?: string
  label?: string
  note?: string
  title?: React.ReactNode
  description?: string
  primaryCta?: Cta
  secondaryCta?: Cta
  proof?: ProofItem[]
  rating?: string
  /** Single risk-reversal line under the primary CTA. */
  reassurance?: string
  /** Heading element — use "h1" when this is the page's main heading. */
  as?: "h1" | "h2"
  className?: string
}

const DEFAULT_PROOF: ProofItem[] = [
  { label: "Setup time", value: "Under 10 minutes" },
  { label: "Guarantee", value: "30-day money-back" },
]

export function Cta03({
  index = "06",
  label = "Next step",
  note = "2 minutes to start",
  title = "Stop guessing. Start recovering revenue.",
  description = "Connect your funnel today and see your first leak report by tomorrow morning. If it doesn't pay for itself, you don't pay.",
  primaryCta = { label: "Start free — see your leaks", href: "#" },
  secondaryCta = { label: "Talk to a founder", href: "#" },
  proof = DEFAULT_PROOF,
  rating = "4.9 from 2,400+ teams",
  reassurance = "No credit card required · Cancel anytime",
  as: Heading = "h2",
  className,
}: Cta03Props) {
  return (
    <section className={cn("relative overflow-hidden bg-background", className)}>
      <MeshBackdrop
        intensity={0.5}
        interactive
        className="[mask-image:linear-gradient(to_top,black,transparent_70%)]"
      />
      <Grain opacity={0.04} />

      <motion.div
        initial="hidden"
        whileInView="visible"
        viewport={viewport}
        variants={stagger(0.09)}
        className="relative mx-auto max-w-6xl px-6 py-20 md:py-28"
      >
        {/* Swiss index bar */}
        <motion.div
          variants={fadeUp}
          className="flex items-baseline justify-between border-b pb-3 text-xs tracking-[0.2em] text-muted-foreground uppercase"
        >
          <span>
            <span className="text-primary">{index}</span> — {label}
          </span>
          <span className="hidden sm:block">{note}</span>
        </motion.div>

        {/* Final word */}
        <div className="mt-12 grid grid-cols-12 gap-x-8 gap-y-12 md:mt-16">
          <motion.div variants={fadeUp} className="col-span-12 lg:col-span-8">
            <Heading className="text-[clamp(2.5rem,7vw,5rem)] leading-[0.98] font-medium tracking-tight text-balance">
              {title}
            </Heading>
          </motion.div>

          <motion.div
            variants={fadeUp}
            className="col-span-12 flex flex-col gap-6 lg:col-span-4 lg:border-l lg:pl-8"
          >
            <p className="max-w-[42ch] text-pretty text-muted-foreground">{description}</p>

            <div className="flex flex-col gap-3">
              <a
                href={primaryCta.href}
                className={cn(buttonVariants({ size: "lg" }), "group w-full justify-between")}
              >
                {primaryCta.label}
                <ArrowRight className="transition-transform group-hover:translate-x-0.5" />
              </a>
              <p className="text-xs tracking-wide text-muted-foreground/70 uppercase">
                {reassurance}
              </p>
              <a
                href={secondaryCta.href}
                className={cn(
                  buttonVariants({ size: "lg", variant: "ghost" }),
                  "w-fit px-0 hover:bg-transparent hover:underline"
                )}
              >
                {secondaryCta.label} →
              </a>
            </div>

            {/* Micro-proof ledger */}
            <ul className="divide-y border-t text-sm">
              {proof.map((item) => (
                <li key={item.label} className="flex items-baseline justify-between gap-4 py-2.5">
                  <span className="text-muted-foreground">{item.label}</span>
                  <span className="text-right font-medium">{item.value}</span>
                </li>
              ))}
              <li className="flex items-baseline justify-between gap-4 py-2.5">
                <span className="text-muted-foreground">Rating</span>
                <span className="flex items-center gap-1.5 font-medium">
                  <Star className="size-3.5 fill-current text-primary" aria-hidden />
                  {rating}
                </span>
              </li>
            </ul>
          </motion.div>
        </div>
      </motion.div>
    </section>
  )
}