# CTA Section
*URL: https://www.grettech.com/components/cta-section*

A high-impact Call to Action block designed to drive conversions at the bottom of a page.

- Two-line headline with a gradient accent
- Primary call-to-action button
- Email, phone and website links (each optional)
- Fading dot-grid background

## Install with the shadcn CLI
```bash
npx shadcn@latest add https://www.grettech.com/api/registry/cta-section
```
With its demo: `npx shadcn@latest add https://www.grettech.com/api/registry/cta-section-demo`

**Dependencies:** npm: clsx, tailwind-merge, lucide-react, class-variance-authority; shadcn/ui: button

## Manual installation
**Step 1: Install dependencies**
Install the packages this component uses and the shadcn/ui components it builds on. It also needs the `cn` helper in `lib/utils.ts`, which `npx shadcn@latest init` adds.

```bash
npm install clsx tailwind-merge lucide-react class-variance-authority
npx shadcn@latest add button
```

**Step 2: Copy the source code**
Add these files to your project components directory.

File: `components/ui/cta-section.tsx`
```tsx
import { buttonVariants } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import { Globe, Mail, Phone } from "lucide-react"
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

const ctaSectionVariants = cva("w-full py-24", {
  variants: {
    variant: {
      default: "",
      compact: "py-12",
    },
  },
  defaultVariants: {
    variant: "default",
  },
})

const ctaContainerVariants = cva(
  "relative isolate w-full overflow-hidden rounded-3xl border border-border/60 bg-card shadow-[0_8px_30px_-12px_rgba(0,0,0,0.12)] dark:bg-card/50",
  {
    variants: {
      variant: {
        default: "",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

export interface CTASectionProps
  extends
    React.HTMLAttributes<HTMLElement>,
    VariantProps<typeof ctaSectionVariants> {
  badge?: string
  titleLine1?: string
  titleLine2?: string
  description?: string
  buttonText?: string
  buttonLink?: string
  email?: string
  phone?: string
  domain?: string
}

function ContactLink({
  href,
  icon,
  external,
  children,
}: {
  href: string
  icon: React.ReactNode
  external?: boolean
  children: React.ReactNode
}) {
  return (
    <a
      href={href}
      {...(external && { target: "_blank", rel: "noopener noreferrer" })}
      className="group inline-flex items-center gap-2.5 rounded-md text-foreground/80 transition-colors outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
    >
      <span className="flex size-8 items-center justify-center rounded-lg border border-border/60 bg-background/70 text-muted-foreground transition-colors group-hover:border-primary/40 group-hover:text-primary [&_svg]:size-4">
        {icon}
      </span>
      {children}
    </a>
  )
}

export const CTASection = React.forwardRef<HTMLElement, CTASectionProps>(
  (
    {
      badge = "Get in touch",
      titleLine1 = "Let's scale your",
      titleLine2 = "Brand Together",
      description = "We're here to help you grow your business with modern web & app solutions.",
      buttonText = "Send a mail to us",
      buttonLink = "/contact",
      email = "hello@domain.com",
      phone = "+1234567890",
      domain = "domain.com",
      variant,
      className,
      ...props
    },
    ref
  ) => {
    return (
      <section
        ref={ref}
        className={cn(ctaSectionVariants({ variant, className }))}
        {...props}
      >
        <div className="mx-auto max-w-4xl px-4 py-6 lg:px-8 lg:py-20">
          <div className={cn(ctaContainerVariants())}>
            {/* Soft primary wash */}
            <div
              aria-hidden="true"
              className="pointer-events-none absolute inset-0 bg-linear-to-tr from-primary/10 via-transparent to-primary/5"
            />

            {/* Dot grid, fading out towards the bottom */}
            <div
              aria-hidden="true"
              className="pointer-events-none absolute inset-0 bg-[radial-gradient(color-mix(in_oklab,var(--foreground)_14%,transparent)_1px,transparent_1px)] mask-[radial-gradient(ellipse_at_top,black,transparent_70%)] bg-size-[18px_18px]"
            />

            {/* Content */}
            <div className="relative flex flex-col items-center gap-5 px-6 py-14 text-center sm:px-16 md:py-16">
              {badge && (
                <p className="w-fit rounded-full border border-border/60 bg-background/70 px-4 py-1.5 text-xs font-medium tracking-wide text-muted-foreground uppercase backdrop-blur-sm">
                  {badge}
                </p>
              )}

              <h2 className="text-4xl font-bold tracking-tight text-balance text-foreground md:text-6xl">
                {titleLine1}
                {titleLine2 && (
                  <>
                    <br />
                    <span className="bg-linear-to-r from-primary to-primary/60 bg-clip-text text-transparent">
                      {titleLine2}
                    </span>
                  </>
                )}
              </h2>

              {description && (
                <p className="max-w-md text-base text-pretty text-muted-foreground md:text-lg">
                  {description}
                </p>
              )}

              {buttonText && (
                // A styled link, not <Button asChild>, so it stays a real
                // link with both Radix and Base UI versions of shadcn/ui
                <a
                  href={buttonLink}
                  className={cn(buttonVariants({ size: "lg" }), "mt-2")}
                >
                  <Mail />
                  {buttonText}
                </a>
              )}

              {(email || phone || domain) && (
                <div className="mt-8 flex w-full flex-col items-center justify-center gap-3 border-t border-border/60 pt-8 text-sm sm:flex-row sm:flex-wrap sm:gap-x-8">
                  {email && (
                    <ContactLink href={`mailto:${email}`} icon={<Mail />}>
                      {email}
                    </ContactLink>
                  )}
                  {phone && (
                    <ContactLink
                      href={`tel:${phone.replace(/[^\d+]/g, "")}`}
                      icon={<Phone />}
                    >
                      {phone}
                    </ContactLink>
                  )}
                  {domain && (
                    <ContactLink
                      href={
                        /^https?:\/\//.test(domain)
                          ? domain
                          : `https://${domain}`
                      }
                      icon={<Globe />}
                      external
                    >
                      {domain.replace(/^https?:\/\//, "")}
                    </ContactLink>
                  )}
                </div>
              )}
            </div>
          </div>
        </div>
      </section>
    )
  }
)
CTASection.displayName = "CTASection"

```

**Step 3: Framework Adjustments (Optional)**
If you are manually copying this code into a Vite or Remix project (without using the CLI), make sure to remove the `"use client";` directive from the very top of the file.

## Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| `badge` | `string` | `"Get in touch"` | No | Small label above the headline. Pass an empty string to hide it. |
| `titleLine1` | `string` | `"Let's scale your"` | No | First line of the headline. |
| `titleLine2` | `string` | `"Brand Together"` | No | Second line of the headline, in the gradient accent. Pass an empty string to hide it. |
| `description` | `string` | `"We're here to help you grow your business with modern web & app solutions."` | No | Text under the headline. Pass an empty string to hide it. |
| `buttonText` | `string` | `"Send a mail to us"` | No | Label of the call-to-action button. Pass an empty string to hide the button. |
| `buttonLink` | `string` | `"/contact"` | No | Where the call-to-action button links to. |
| `email` | `string` | `"hello@domain.com"` | No | Contact email shown below. Pass an empty string to hide it. |
| `phone` | `string` | `"+1234567890"` | No | Contact phone shown below. Pass an empty string to hide it. |
| `domain` | `string` | `"domain.com"` | No | Website shown below (with or without https://). Pass an empty string to hide it. |
| `variant` | `"default" \| "compact"` | `"default"` | No | "compact" halves the section's vertical padding. |
| `className` | `string` | - | No | Extra classes for the root <section>. |
| `...props` | `React.HTMLAttributes<HTMLElement>` | - | No | Any other section attributes (id, style, etc.). |

## Usage
```tsx
"use client"

import { CTASection } from "@/components/ui/cta-section"

export default function CTASectionDemo() {
  return (
    <div className="relative w-full overflow-hidden rounded-xl border border-dashed bg-background">
      <CTASection
        badge="Get in touch"
        titleLine1="Let's scale your"
        titleLine2="Brand Together"
        description="We're here to help you grow your business with modern web & app solutions."
        buttonText="Send a mail to us"
        buttonLink="/contact"
        email="hello@grettech.com"
        phone="+1 (555) 123-4567"
        domain="grettech.com"
      />
    </div>
  )
}

```
