# Cta 3

> Split layout with registry preview panel and light parallax — hero-1 asymmetry for install conversion.

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

- Page, with a live preview: https://shadcn.exchange/r/nusaiba/cta-3
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/nusaiba/cta-3-light-b3df1ddb386a5561.png
- Registry item: https://nusaiba.dev/r/base-nova/cta-3.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-3.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`, `@nusaiba/gallery-visuals`, `button`

## Files

### `registry/blocks/cta/3/cta-section.tsx`

```tsx
"use client";

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

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

export function CtaSplitVisualSection() {
	const reduceMotion = useReducedMotion();
	const ref = useRef<HTMLElement>(null);
	const inView = useInView(ref, motionViewport);
	const { scrollYProgress } = useScroll({
		target: ref,
		offset: ["start end", "end start"],
	});
	const imageY = useTransform(scrollYProgress, [0, 1], reduceMotion ? [0, 0] : [12, -12]);

	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" ref={ref}>
			<ContentRail maxWidth="max-w-5xl" className="grid items-center gap-10 lg:grid-cols-2 lg:gap-14">
				<motion.div
					animate={inView ? "visible" : "hidden"}
					className="max-w-lg"
					initial="hidden"
					variants={{
						hidden: {},
						visible: { transition: { staggerChildren: motionStagger.item, delayChildren: 0.04 } },
					}}
				>
					<motion.p
						className="font-medium text-primary text-xs uppercase tracking-[0.22em]"
						variants={itemVariants}
					>
						Install
					</motion.p>
					<motion.h2
						className="mt-3 text-balance font-medium text-3xl tracking-tight md:text-4xl"
						variants={itemVariants}
					>
						Copy blocks into your repo
					</motion.h2>
					<motion.p
						className="mt-4 text-pretty text-muted-foreground text-sm leading-relaxed md:text-base"
						variants={itemVariants}
					>
						Preview live on Nusaiba, install with one CLI command, and keep every file in
						your project.
					</motion.p>
					<motion.div
						className="mt-8 flex flex-col gap-3 sm:flex-row"
						variants={itemVariants}
					>
						<Link
							className={cn(buttonVariants(), "active:scale-[0.96] transition-transform")}
							href="/blocks"
						>
							Browse blocks
							<ArrowRightIcon data-icon="inline-end" />
						</Link>
						<Link
							className={cn(
								buttonVariants({ variant: "outline" }),
								"active:scale-[0.96] transition-transform",
							)}
							href="/pricing"
						>
							View pricing
						</Link>
					</motion.div>
				</motion.div>

				<motion.div
					className="relative"
					initial={reduceMotion ? false : { opacity: 0, filter: "blur(4px)", y: 12 }}
					transition={spring}
					viewport={motionViewport}
					whileInView={reduceMotion ? undefined : { opacity: 1, filter: "blur(0px)", y: 0 }}
				>
					<div className="overflow-hidden rounded-2xl p-1 shadow-[0_8px_30px_rgba(0,0,0,0.06)]">
						<motion.div className="relative aspect-[4/3] overflow-hidden rounded-xl" style={{ y: imageY }}>
							<Image
								alt="Registry preview with motion blocks"
								className={cn(
									"object-cover object-top",
									"outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10",
								)}
								fill
								priority={false}
								sizes="(max-width: 1024px) 100vw, 50vw"
								src="/images/visuals/beams.png"
							/>
						</motion.div>
					</div>
				</motion.div>
			</ContentRail>
		</SectionShell>
	);
}
```
