Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions apps/sim/app/(landing)/components/cta/cta.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChipLink } from '@sim/emcn'
import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants'

/**
* Landing pre-footer CTA - the page's final conversion band. A tall, centered
Expand Down Expand Up @@ -28,10 +29,10 @@ export function Cta() {
Build your first agent today.
</h2>
<div className='flex items-center gap-1'>
<ChipLink variant='primary' href='/signup'>
<ChipLink variant='primary' href={SIGNUP_HREF}>
Get started
</ChipLink>
<ChipLink href='/demo' className='border border-[var(--border-1)]'>
<ChipLink href={DEMO_HREF} className='border border-[var(--border-1)]'>
Contact sales
</ChipLink>
</div>
Expand Down
5 changes: 3 additions & 2 deletions apps/sim/app/(landing)/components/hero-cta/hero-cta.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChipLink, cn } from '@sim/emcn'
import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants'

/**
* Hero-scale sizing shared by both CTAs - one step up from the navbar's 30px
Expand All @@ -24,11 +25,11 @@ const CTA_SIZE = 'h-[36px] px-[0.571em] text-[15px] [&>span]:[font-size:inherit]
export function HeroCta() {
return (
<div className='flex items-center gap-2 max-sm:w-full max-sm:flex-col max-sm:items-stretch'>
<ChipLink variant='primary' href='/demo' className={CTA_SIZE}>
<ChipLink variant='primary' href={DEMO_HREF} className={CTA_SIZE}>
Request a demo
</ChipLink>
<ChipLink
href='/signup'
href={SIGNUP_HREF}
prefetch={false}
className={cn(CTA_SIZE, 'border border-[var(--border-1)] max-sm:justify-center')}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
NAVBAR_GLASS_SURFACE,
useNavbarFrost,
} from '@/app/(landing)/components/navbar/components/navbar-shell'
import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants'

/**
* Mobile navigation - the `< lg` counterpart to the desktop nav clusters.
Expand Down Expand Up @@ -70,7 +71,7 @@ export function MobileNav({ stars }: MobileNavProps) {

return (
<div className='ml-auto flex items-center gap-2 lg:hidden'>
<ChipLink variant='primary' href='/signup' prefetch={false}>
<ChipLink variant='primary' href={SIGNUP_HREF} prefetch={false}>
Sign up
</ChipLink>
<button
Expand Down Expand Up @@ -166,7 +167,7 @@ export function MobileNav({ stars }: MobileNavProps) {
</ChipLink>
<ChipLink
variant='primary'
href='/demo'
href={DEMO_HREF}
fullWidth
flush
className='h-[40px] justify-center [&>span]:flex-none'
Expand Down
5 changes: 3 additions & 2 deletions apps/sim/app/(landing)/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
NavMenuChip,
SimWordmark,
} from '@/app/(landing)/components/navbar/components'
import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants'

/**
* Landing navbar.
Expand Down Expand Up @@ -87,10 +88,10 @@ export function Navbar({ stars, logoOnly = false }: NavbarProps) {
<ChipLink href='/login' prefetch={false}>
Log in
</ChipLink>
<ChipLink variant='border' href='/demo'>
<ChipLink variant='border' href={DEMO_HREF}>
Contact sales
</ChipLink>
<ChipLink variant='primary' href='/signup' prefetch={false}>
<ChipLink variant='primary' href={SIGNUP_HREF} prefetch={false}>
Sign up
</ChipLink>
</div>
Expand Down
8 changes: 8 additions & 0 deletions apps/sim/app/(landing)/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Shared landing-page CTA destinations. Every "Get started"/"Sign up" CTA
* across the marketing site funnels to signup; every "Talk to sales"/
* "Contact sales" CTA funnels to the demo-request form. Centralized here so
* no CTA can drift to the wrong destination independently.
*/
export const SIGNUP_HREF = '/signup'
export const DEMO_HREF = '/demo'
9 changes: 5 additions & 4 deletions apps/sim/app/(landing)/enterprise/enterprise.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@/app/(landing)/components/solutions-page/components'
import { SOLUTIONS_SPACING } from '@/app/(landing)/components/solutions-page/constants'
import type { SolutionsPageConfig } from '@/app/(landing)/components/solutions-page/types'
import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants'
import { EnterpriseFeatureGrid } from '@/app/(landing)/enterprise/components/enterprise-feature-grid'
import { EnterprisePlatformLoop } from '@/app/(landing)/enterprise/components/enterprise-platform-loop'
import {
Expand Down Expand Up @@ -84,7 +85,7 @@ const ENTERPRISE_CONFIG: SolutionsPageConfig = {
title: 'Build, Deploy, and Manage Enterprise AI Agents in One Workspace',
subtitle:
'Build enterprise AI agents, ship to production, and manage every version from one workspace.',
cta: { label: 'Start building', href: '/signup' },
cta: { label: 'Start building', href: SIGNUP_HREF },
cards: [
{
title: 'Build visually or with code',
Expand All @@ -111,7 +112,7 @@ const ENTERPRISE_CONFIG: SolutionsPageConfig = {
title: 'Governance and Security for Enterprise AI Agents',
subtitle:
'Security, approvals, and controls keep enterprise AI agents trusted in production.',
cta: { label: 'See security', href: '/demo' },
cta: { label: 'See security', href: DEMO_HREF },
cards: [
{
title: 'Control who can do what',
Expand All @@ -138,7 +139,7 @@ const ENTERPRISE_CONFIG: SolutionsPageConfig = {
id: 'deploy',
title: 'Deploy Enterprise Workflow Agents with Confidence',
subtitle: 'Stage, observe, and version workflow agents before they reach production.',
cta: { label: 'Explore deployment', href: '/signup' },
cta: { label: 'Explore deployment', href: SIGNUP_HREF },
cards: [
{
title: 'Stage before you ship',
Expand All @@ -162,7 +163,7 @@ const ENTERPRISE_CONFIG: SolutionsPageConfig = {
title: 'Built for Enterprise Teams',
subtitle:
'Built for the teams that own enterprise AI agents across IT, operations, and engineering.',
cta: { label: 'Talk to sales', href: '/demo' },
cta: { label: 'Talk to sales', href: DEMO_HREF },
cards: [
{
title: 'IT and platform teams',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { ReactNode } from 'react'
import { useQueryStates } from 'nuqs'
import { getUpgradeCardCta, type PlanTier, type UpgradeCardId } from '@/lib/billing/client'
import { ANNUAL_DISCOUNT_RATE, CREDIT_TIERS } from '@/lib/billing/constants'
import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants'
import {
PricingCard,
type PricingCardCta,
Expand All @@ -18,14 +19,12 @@ import {

/**
* A public visitor has no subscription, so each card's canonical label and
* variant resolves against the `free` tier. On this page every CTA funnels to
* sign-up regardless of plan.
* variant resolves against the `free` tier. On this page every self-serve CTA
* funnels to sign-up regardless of plan; the enterprise CTA routes to the
* demo-request form instead.
*/
const VISITOR_TIER: PlanTier = 'free'

/** Every CTA on the public pricing page funnels logged-out visitors to sign-up. */
const SIGNUP_HREF = '/signup'

/** This section's rows render on the public cards without their group header. */
const HEADERLESS_SECTION_TITLE = 'Credits & pricing'

Expand All @@ -52,13 +51,27 @@ function sectionsForColumn(col: number): PricingCardSection[] {
}))
}

/** The four card columns (Free, Pro, Max, Enterprise) transposed once at module load - the source data is static, so this never needs to be redone per render. */
const SECTIONS_BY_COLUMN = [0, 1, 2, 3].map(sectionsForColumn)

/** Round a dollar amount down to the annual-billing discounted price. */
function annualPrice(dollars: number): number {
return Math.round(dollars * (1 - ANNUAL_DISCOUNT_RATE))
}

/**
* Resolve a card's canonical label and variant from the free-tier matrix; the
* CTA always funnels logged-out visitors to sign-up.
* Resolve a card's canonical label and variant from the free-tier matrix. A
* `sales` intent ("Talk to sales") routes to the demo-request form, matching
* every other "Contact sales" CTA on the landing site; every other intent
* funnels logged-out visitors to sign-up.
*/
function resolveCta(card: UpgradeCardId): PricingCardCta {
const cta = getUpgradeCardCta(VISITOR_TIER, card)
return { label: cta.label, variant: cta.variant, href: SIGNUP_HREF }
return {
label: cta.label,
variant: cta.variant,
href: cta.intent === 'sales' ? DEMO_HREF : SIGNUP_HREF,
}
}

const FREE_CTA: PricingCardCta = {
Expand Down Expand Up @@ -86,12 +99,8 @@ export function PricingPlans({ heading }: PricingPlansProps) {
const setIsAnnual = (next: boolean) => setParams({ billing: next ? 'annual' : 'monthly' })

const discountPct = Math.round(ANNUAL_DISCOUNT_RATE * 100)
const proPrice = isAnnual
? Math.round(CREDIT_TIERS[0].dollars * (1 - ANNUAL_DISCOUNT_RATE))
: CREDIT_TIERS[0].dollars
const maxPrice = isAnnual
? Math.round(CREDIT_TIERS[1].dollars * (1 - ANNUAL_DISCOUNT_RATE))
: CREDIT_TIERS[1].dollars
const proPrice = isAnnual ? annualPrice(CREDIT_TIERS[0].dollars) : CREDIT_TIERS[0].dollars
const maxPrice = isAnnual ? annualPrice(CREDIT_TIERS[1].dollars) : CREDIT_TIERS[1].dollars
const priceSubtext = isAnnual
? 'per user/month, billed annually'
: 'per user/month, billed monthly'
Expand All @@ -114,30 +123,30 @@ export function PricingPlans({ heading }: PricingPlansProps) {
price='$0'
priceSubtext='Free forever'
cta={FREE_CTA}
sections={sectionsForColumn(0)}
sections={SECTIONS_BY_COLUMN[0]}
/>
<PricingCard
name='Pro'
price={`$${proPrice}`}
discountLabel={discountLabel}
priceSubtext={priceSubtext}
cta={proCta}
sections={sectionsForColumn(1)}
sections={SECTIONS_BY_COLUMN[1]}
/>
<PricingCard
name='Max'
price={`$${maxPrice}`}
discountLabel={discountLabel}
priceSubtext={priceSubtext}
cta={maxCta}
sections={sectionsForColumn(2)}
sections={SECTIONS_BY_COLUMN[2]}
/>
<PricingCard
name='Enterprise'
price='Custom'
priceSubtext='Tailored to your team'
cta={enterpriseCta}
sections={sectionsForColumn(3)}
sections={SECTIONS_BY_COLUMN[3]}
/>
</div>
</>
Expand Down
Loading