# Cta 2

> Centered CTA with radial glow pulse and staggered reveal — hero-spotlight DNA for catalog close.

A shadcn/ui block from the [@nusaiba](https://nusaiba.dev) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/nusaiba/cta-2).

- Page, with a live preview: https://shadcn.exchange/r/nusaiba/cta-2
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/nusaiba/cta-2-light-1eec4bb2e8dea1f4.png
- Registry item: https://nusaiba.dev/r/base-nova/cta-2.json
- Categories: cta
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://nusaiba.dev/r/base-nova/cta-2.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `motion`
- Registry items: `@nusaiba/foundation`, `button`

## Files

### `registry/blocks/cta/2/cta-motion-section.tsx`

```tsx
"use client";

import Link from "next/link";
import { useRef } from "react";
import { ArrowRightIcon } from "lucide-react";
import { motion, useInView, useReducedMotion } from "motion/react";
import { buttonVariants } from "@/components/ui/button";
import { motionStagger, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { SectionShell } from "@/components/layout-contract";

const spring = { type: "spring" as const, duration: 0.3, bounce: 0 };

export function CtaMotionSection() {
	const reduceMotion = useReducedMotion();
	const ref = useRef<HTMLElement>(null);
	const inView = useInView(ref, motionViewport);

	const itemVariants = reduceMotion
		? { hidden: { opacity: 1 }, visible: { opacity: 1 } }
		: {
				hidden: { opacity: 0, filter: "blur(4px)", y: 8 },
				visible: { opacity: 1, filter: "blur(0px)", y: 0, transition: spring },
			};

	return (
		<SectionShell spacingMode="section" className="relative overflow-hidden" ref={ref}>
			<div
				aria-hidden="true"
				className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(ellipse_at_center,theme(--color-primary/.08),transparent_65%)]"
			/>
			<motion.div
				animate={inView ? "visible" : "hidden"}
				className="relative mx-auto max-w-2xl text-center"
				initial="hidden"
				variants={{
					hidden: {},
					visible: { transition: { staggerChildren: motionStagger.item, delayChildren: 0.04 } },
				}}
			>
				<motion.div
					animate={
						reduceMotion
							? undefined
							: { opacity: [0.4, 0.85, 0.4], scale: [1, 1.02, 1] }
					}
					aria-hidden
					className="pointer-events-none absolute inset-x-8 top-1/2 -z-10 h-48 -translate-y-1/2 rounded-full bg-primary/10 blur-3xl md:inset-x-16"
					transition={{
						duration: 4,
						repeat: Number.POSITIVE_INFINITY,
						ease: "easeInOut",
					}}
				/>
				<motion.p
					className="font-medium text-primary text-xs uppercase tracking-[0.22em]"
					variants={itemVariants}
				>
					Nusaiba
				</motion.p>
				<motion.h2
					className="mt-3 text-balance font-medium text-2xl tracking-tight md:text-4xl lg:text-5xl"
					variants={itemVariants}
				>
					Motion sections you can actually install
				</motion.h2>
				<motion.p
					className="mx-auto mt-4 max-w-md text-pretty text-muted-foreground text-sm md:text-base"
					variants={itemVariants}
				>
					Open registry blocks, Pro catalog with a token, and copy prompts on every
					page — built for vibe coders who ship.
				</motion.p>
				<motion.div className="mt-8" variants={itemVariants}>
					<Link
						className={cn(
							buttonVariants({ size: "lg" }),
							"active:scale-[0.96] transition-transform",
						)}
						href="/blocks"
					>
						Browse catalog
						<ArrowRightIcon data-icon="inline-end" />
					</Link>
				</motion.div>
			</motion.div>
		</SectionShell>
	);
}
```
