Two Paths CTA

Two ways to begin

Talktous,orwritefirst.Botharefine.

Two wooden boats moored off a white sand beach

Book a first conversation

Twenty minutes, free, on the phone or on video. The quickest way to find out whether this is the right room for you.

Write to us instead

If saying it out loud feels like too much for now, an email is a perfectly good first step. A practitioner replies personally within a working day.

About this block

Two Paths CTAPRO

A call to action that offers two doors: under a serif heading that settles in word by word, two soft muted cards each with a serif title, a description and a sliding-marker pill, the first topped by a photograph that drifts inside its frame as the page scrolls.

Cta99: Two Paths CTA

Two ways to begin, side by side: each path is a soft panel with an optional photograph that drifts inside its frame, a serif title, a short explanation and its own pill, with the buttons pinned to a shared baseline.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://ui.beste.co/r/cta99?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/cta99?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/cta99.tsx plus the button22 sliding-marker pill and the text1 word stagger it uses.

Quick start

The installed file exports cta99Demo alongside the block: the exact props behind the preview above. Spread it to get a working two-path call to action in one line.

tsx
import { Cta99, cta99Demo } from "@/components/beste/block/cta99";

export default function Page() {
  return <Cta99 {...cta99Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Cta99 } from "@/components/beste/block/cta99";

export default function Page() {
  return (
    <Cta99
      eyebrow="Two ways to begin"
      heading="Talk to us, or write first. Both are fine."
      paths={[
        {
          title: "Book a first conversation",
          description: "Twenty minutes, free, on the phone or on video.",
          button: { label: "Choose a time", href: "/call", tone: "dark" },
          image: { src: "/rooms/ledge.jpg", alt: "A ceramic vessel on a plaster ledge" },
        },
        {
          title: "Write to us instead",
          description: "If saying it out loud feels like too much for now, an email is a fine first step.",
          button: { label: "Send an email", href: "mailto:hello@beste.co", tone: "outline" },
        },
      ]}
    />
  );
}

Props

PropTypeDefaultDescription
eyebrowstringSmall-caps line above the heading
headingstringSection heading, rendered as an h2 through Text1
pathsPath[][]The paths, two across from md
classNamestringExtra classes for the outer <section>
ts
type Path = {
  title: string;
  description: string;
  button: ActionButton;
  image?: { src: string; alt: string };
};

type ActionButton = {
  label: string;
  href: string;
  tone?: "primary" | "light" | "dark" | "outline";
};

Behavior notes

  • The image is optional per path, which is the point of the layout: a card with a photograph and one without still finish at the same height because the copy grows with flex-1 and the button is pushed down by mt-auto.
  • Each path's button carries its own tone, so the primary route can be solid while the alternative stays as an outline without either being styled from outside.
  • Path images sit in a 16/9 frame on a bg-background/50 placeholder, so the empty frame reads as a lighter inset of the panel rather than as a grey hole while the file loads.
  • Every image owns its own scroll listener and drifts -10% to 10% against a 1.2 scale, so two cards do not move in lockstep.
  • Cards enter with a 0.15s stagger by index, so the second option arrives after the first has been read.
  • Path titles are h3 under the section h2, keeping the outline correct wherever the block sits on a page.
  • The grid is fixed at two columns from md, so a third path wraps to a new row rather than shrinking the first two.

More CTA blocks

View all CTA
PRO

cta102

Ruled Proof CTA

A call to action between two hairline rules: a serif heading that settles in word by word and an intro on the left, a sliding-marker pill with an avatar cluster, a star row and a proof line on the right.

PRO

cta105

One Line CTA

The most compact call to action in the language: a single serif line that settles in word by word with an underlined text link beneath it, and a sliding-marker pill at the other end, all between two hairline rules.

PRO

cta104

Photo Button CTA

A call to action with the text above the picture: a centred serif heading that settles in word by word and an intro, then a wide rounded photograph drifting under a light scrim and film grain with a single large sliding-marker pill at its centre.

PRO

cta83

Two-Path Closing Cards

A closing call to action offering two routes as side-by-side cards, each with an icon tile, a short kicker, a checked list on hairlines, and its own action, with the recommended route outlined in the accent.

PRO

cta29

Dual CTA

Two equal side-by-side call-to-action cards offering different paths. Perfect for segmenting audiences like developers vs designers or free vs paid.

PRO

cta80

Two Path Closing CTA

Closing section that offers two routes side by side, each a ruled column with a title, a short pitch, a dotted-rule list of what to expect and its own pill CTA, one solid and one outline.