This commit is contained in:
2025-06-04 14:44:37 +02:00
commit e0ee3498c7
119 changed files with 20681 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Link } from '@/components/link'
import { Navbar } from '@/components/navbar'
import { Heading, Subheading } from '@/components/text'
import { image } from '@/sanity/image'
import { getPost } from '@/sanity/queries'
import { ChevronLeftIcon } from '@heroicons/react/16/solid'
import dayjs from 'dayjs'
import type { Metadata } from 'next'
import { PortableText } from 'next-sanity'
import { notFound } from 'next/navigation'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}): Promise<Metadata> {
let post = await getPost(params.slug)
return post ? { title: post.title, description: post.excerpt } : {}
}
export default async function BlogPost({
params,
}: {
params: { slug: string }
}) {
let post = (await getPost(params.slug)) || notFound()
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
<Subheading className="mt-16">
{dayjs(post.publishedAt).format('dddd, MMMM D, YYYY')}
</Subheading>
<Heading as="h1" className="mt-2">
{post.title}
</Heading>
<div className="mt-16 grid grid-cols-1 gap-8 pb-24 lg:grid-cols-[15rem_1fr] xl:grid-cols-[15rem_1fr_15rem]">
<div className="flex flex-wrap items-center gap-8 max-lg:justify-between lg:flex-col lg:items-start">
{post.author && (
<div className="flex items-center gap-3">
{post.author.image && (
<img
alt=""
src={image(post.author.image).size(64, 64).url()}
className="aspect-square size-6 rounded-full object-cover"
/>
)}
<div className="text-sm/5 text-gray-700">
{post.author.name}
</div>
</div>
)}
{Array.isArray(post.categories) && (
<div className="flex flex-wrap gap-2">
{post.categories.map((category) => (
<Link
key={category.slug}
href={`/blog?category=${category.slug}`}
className="rounded-full border border-dotted border-gray-300 bg-gray-50 px-2 text-sm/6 font-medium text-gray-500"
>
{category.title}
</Link>
))}
</div>
)}
</div>
<div className="text-gray-700">
<div className="max-w-2xl xl:mx-auto">
{post.mainImage && (
<img
alt={post.mainImage.alt || ''}
src={image(post.mainImage).size(2016, 1344).url()}
className="mb-10 aspect-3/2 w-full rounded-2xl object-cover shadow-xl"
/>
)}
{post.body && (
<PortableText
value={post.body}
components={{
block: {
normal: ({ children }) => (
<p className="my-10 text-base/8 first:mt-0 last:mb-0">
{children}
</p>
),
h2: ({ children }) => (
<h2 className="mt-12 mb-10 text-2xl/8 font-medium tracking-tight text-gray-950 first:mt-0 last:mb-0">
{children}
</h2>
),
h3: ({ children }) => (
<h3 className="mt-12 mb-10 text-xl/8 font-medium tracking-tight text-gray-950 first:mt-0 last:mb-0">
{children}
</h3>
),
blockquote: ({ children }) => (
<blockquote className="my-10 border-l-2 border-l-gray-300 pl-6 text-base/8 text-gray-950 first:mt-0 last:mb-0">
{children}
</blockquote>
),
},
types: {
image: ({ value }) => (
<img
alt={value.alt || ''}
src={image(value).width(2000).url()}
className="w-full rounded-2xl"
/>
),
separator: ({ value }) => {
switch (value.style) {
case 'line':
return (
<hr className="my-8 border-t border-gray-200" />
)
case 'space':
return <div className="my-8" />
default:
return null
}
},
},
list: {
bullet: ({ children }) => (
<ul className="list-disc pl-4 text-base/8 marker:text-gray-400">
{children}
</ul>
),
number: ({ children }) => (
<ol className="list-decimal pl-4 text-base/8 marker:text-gray-400">
{children}
</ol>
),
},
listItem: {
bullet: ({ children }) => {
return (
<li className="my-2 pl-2 has-[br]:mb-8">
{children}
</li>
)
},
number: ({ children }) => {
return (
<li className="my-2 pl-2 has-[br]:mb-8">
{children}
</li>
)
},
},
marks: {
strong: ({ children }) => (
<strong className="font-semibold text-gray-950">
{children}
</strong>
),
code: ({ children }) => (
<>
<span aria-hidden>`</span>
<code className="text-[15px]/8 font-semibold text-gray-950">
{children}
</code>
<span aria-hidden>`</span>
</>
),
link: ({ value, children }) => {
return (
<Link
href={value.href}
className="font-medium text-gray-950 underline decoration-gray-400 underline-offset-4 data-hover:decoration-gray-600"
>
{children}
</Link>
)
},
},
}}
/>
)}
<div className="mt-10">
<Button variant="outline" href="/blog">
<ChevronLeftIcon className="size-4" />
Back to blog
</Button>
</div>
</div>
</div>
</div>
</Container>
<Footer />
</main>
)
}

View File

@@ -0,0 +1,65 @@
import { image } from '@/sanity/image'
import { getPostsForFeed } from '@/sanity/queries'
import { Feed } from 'feed'
import assert from 'node:assert'
export async function GET(req: Request) {
let siteUrl = new URL(req.url).origin
let feed = new Feed({
title: 'The Radiant Blog',
description:
'Stay informed with product updates, company news, and insights on how to sell smarter at your company.',
author: {
name: 'Michael Foster',
email: 'michael.foster@example.com',
},
id: siteUrl,
link: siteUrl,
image: `${siteUrl}/favicon.ico`,
favicon: `${siteUrl}/favicon.ico`,
copyright: `All rights reserved ${new Date().getFullYear()}`,
feedLinks: {
rss2: `${siteUrl}/feed.xml`,
},
})
let posts = await getPostsForFeed()
posts.forEach((post) => {
try {
assert(typeof post.title === 'string')
assert(typeof post.slug === 'string')
assert(typeof post.excerpt === 'string')
assert(typeof post.publishedAt === 'string')
} catch (error) {
console.log('Post is missing required fields for RSS feed:', post)
return
}
feed.addItem({
title: post.title,
id: post.slug,
link: `${siteUrl}/blog/${post.slug}`,
content: post.excerpt,
image: post.mainImage
? image(post.mainImage)
.size(1200, 800)
.format('jpg')
.url()
.replaceAll('&', '&amp;')
: undefined,
author: post.author?.name ? [{ name: post.author.name }] : [],
contributor: post.author?.name ? [{ name: post.author.name }] : [],
date: new Date(post.publishedAt),
})
})
return new Response(feed.rss2(), {
status: 200,
headers: {
'content-type': 'application/xml',
'cache-control': 's-maxage=31556952',
},
})
}

310
src/app/blog/page.tsx Normal file
View File

@@ -0,0 +1,310 @@
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Link } from '@/components/link'
import { Navbar } from '@/components/navbar'
import { Heading, Lead, Subheading } from '@/components/text'
import { image } from '@/sanity/image'
import {
getCategories,
getFeaturedPosts,
getPosts,
getPostsCount,
} from '@/sanity/queries'
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import {
CheckIcon,
ChevronLeftIcon,
ChevronRightIcon,
ChevronUpDownIcon,
RssIcon,
} from '@heroicons/react/16/solid'
import { clsx } from 'clsx'
import dayjs from 'dayjs'
import type { Metadata } from 'next'
import { notFound } from 'next/navigation'
export const metadata: Metadata = {
title: 'Blog',
description:
'Stay informed with product updates, company news, and insights on how to sell smarter at your company.',
}
const postsPerPage = 5
async function FeaturedPosts() {
let featuredPosts = await getFeaturedPosts(3)
if (featuredPosts.length === 0) {
return
}
return (
<div className="mt-16 bg-linear-to-t from-gray-100 pb-14">
<Container>
<h2 className="text-2xl font-medium tracking-tight">Featured</h2>
<div className="mt-6 grid grid-cols-1 gap-8 lg:grid-cols-3">
{featuredPosts.map((post) => (
<div
key={post.slug}
className="relative flex flex-col rounded-3xl bg-white p-2 shadow-md ring-1 shadow-black/5 ring-black/5"
>
{post.mainImage && (
<img
alt={post.mainImage.alt || ''}
src={image(post.mainImage).size(1170, 780).url()}
className="aspect-3/2 w-full rounded-2xl object-cover"
/>
)}
<div className="flex flex-1 flex-col p-8">
<div className="text-sm/5 text-gray-700">
{dayjs(post.publishedAt).format('dddd, MMMM D, YYYY')}
</div>
<div className="mt-2 text-base/7 font-medium">
<Link href={`/blog/${post.slug}`}>
<span className="absolute inset-0" />
{post.title}
</Link>
</div>
<div className="mt-2 flex-1 text-sm/6 text-gray-500">
{post.excerpt}
</div>
{post.author && (
<div className="mt-6 flex items-center gap-3">
{post.author.image && (
<img
alt=""
src={image(post.author.image).size(64, 64).url()}
className="aspect-square size-6 rounded-full object-cover"
/>
)}
<div className="text-sm/5 text-gray-700">
{post.author.name}
</div>
</div>
)}
</div>
</div>
))}
</div>
</Container>
</div>
)
}
async function Categories({ selected }: { selected?: string }) {
let categories = await getCategories()
if (categories.length === 0) {
return
}
return (
<div className="flex flex-wrap items-center justify-between gap-2">
<Menu>
<MenuButton className="flex items-center justify-between gap-2 font-medium">
{categories.find(({ slug }) => slug === selected)?.title ||
'All categories'}
<ChevronUpDownIcon className="size-4 fill-gray-900" />
</MenuButton>
<MenuItems
anchor="bottom start"
className="min-w-40 rounded-lg bg-white p-1 shadow-lg ring-1 ring-gray-200 [--anchor-gap:6px] [--anchor-offset:-4px] [--anchor-padding:10px]"
>
<MenuItem>
<Link
href="/blog"
data-selected={selected === undefined ? true : undefined}
className="group grid grid-cols-[1rem_1fr] items-center gap-2 rounded-md px-2 py-1 data-focus:bg-gray-950/5"
>
<CheckIcon className="hidden size-4 group-data-selected:block" />
<p className="col-start-2 text-sm/6">All categories</p>
</Link>
</MenuItem>
{categories.map((category) => (
<MenuItem key={category.slug}>
<Link
href={`/blog?category=${category.slug}`}
data-selected={category.slug === selected ? true : undefined}
className="group grid grid-cols-[16px_1fr] items-center gap-2 rounded-md px-2 py-1 data-focus:bg-gray-950/5"
>
<CheckIcon className="hidden size-4 group-data-selected:block" />
<p className="col-start-2 text-sm/6">{category.title}</p>
</Link>
</MenuItem>
))}
</MenuItems>
</Menu>
<Button variant="outline" href="/blog/feed.xml" className="gap-1">
<RssIcon className="size-4" />
RSS Feed
</Button>
</div>
)
}
async function Posts({ page, category }: { page: number; category?: string }) {
let posts = await getPosts(
(page - 1) * postsPerPage,
page * postsPerPage,
category,
)
if (posts.length === 0 && (page > 1 || category)) {
notFound()
}
if (posts.length === 0) {
return <p className="mt-6 text-gray-500">No posts found.</p>
}
return (
<div className="mt-6">
{posts.map((post) => (
<div
key={post.slug}
className="relative grid grid-cols-1 border-b border-b-gray-100 py-10 first:border-t first:border-t-gray-200 max-sm:gap-3 sm:grid-cols-3"
>
<div>
<div className="text-sm/5 max-sm:text-gray-700 sm:font-medium">
{dayjs(post.publishedAt).format('dddd, MMMM D, YYYY')}
</div>
{post.author && (
<div className="mt-2.5 flex items-center gap-3">
{post.author.image && (
<img
alt=""
src={image(post.author.image).width(64).height(64).url()}
className="aspect-square size-6 rounded-full object-cover"
/>
)}
<div className="text-sm/5 text-gray-700">
{post.author.name}
</div>
</div>
)}
</div>
<div className="sm:col-span-2 sm:max-w-2xl">
<h2 className="text-sm/5 font-medium">{post.title}</h2>
<p className="mt-3 text-sm/6 text-gray-500">{post.excerpt}</p>
<div className="mt-4">
<Link
href={`/blog/${post.slug}`}
className="flex items-center gap-1 text-sm/5 font-medium"
>
<span className="absolute inset-0" />
Read more
<ChevronRightIcon className="size-4 fill-gray-400" />
</Link>
</div>
</div>
</div>
))}
</div>
)
}
async function Pagination({
page,
category,
}: {
page: number
category?: string
}) {
function url(page: number) {
let params = new URLSearchParams()
if (category) params.set('category', category)
if (page > 1) params.set('page', page.toString())
return params.size !== 0 ? `/blog?${params.toString()}` : '/blog'
}
let totalPosts = await getPostsCount(category)
let hasPreviousPage = page - 1
let previousPageUrl = hasPreviousPage ? url(page - 1) : undefined
let hasNextPage = page * postsPerPage < totalPosts
let nextPageUrl = hasNextPage ? url(page + 1) : undefined
let pageCount = Math.ceil(totalPosts / postsPerPage)
if (pageCount < 2) {
return
}
return (
<div className="mt-6 flex items-center justify-between gap-2">
<Button
variant="outline"
href={previousPageUrl}
disabled={!previousPageUrl}
>
<ChevronLeftIcon className="size-4" />
Previous
</Button>
<div className="flex gap-2 max-sm:hidden">
{Array.from({ length: pageCount }, (_, i) => (
<Link
key={i + 1}
href={url(i + 1)}
data-active={i + 1 === page ? true : undefined}
className={clsx(
'size-7 rounded-lg text-center text-sm/7 font-medium',
'data-hover:bg-gray-100',
'data-active:shadow-sm data-active:ring-1 data-active:ring-black/10',
'data-active:data-hover:bg-gray-50',
)}
>
{i + 1}
</Link>
))}
</div>
<Button variant="outline" href={nextPageUrl} disabled={!nextPageUrl}>
Next
<ChevronRightIcon className="size-4" />
</Button>
</div>
)
}
export default async function Blog({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined }
}) {
let page =
'page' in searchParams
? typeof searchParams.page === 'string' && parseInt(searchParams.page) > 1
? parseInt(searchParams.page)
: notFound()
: 1
let category =
typeof searchParams.category === 'string'
? searchParams.category
: undefined
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
<Subheading className="mt-16">Blog</Subheading>
<Heading as="h1" className="mt-2">
Whats happening at Radiant.
</Heading>
<Lead className="mt-6 max-w-3xl">
Stay informed with product updates, company news, and insights on how
to sell smarter at your company.
</Lead>
</Container>
{page === 1 && !category && <FeaturedPosts />}
<Container className="mt-16 pb-24">
<Categories selected={category} />
<Posts page={page} category={category} />
<Pagination page={page} category={category} />
</Container>
<Footer />
</main>
)
}

473
src/app/company/page.tsx Normal file
View File

@@ -0,0 +1,473 @@
import { AnimatedNumber } from '@/components/animated-number'
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { GradientBackground } from '@/components/gradient'
import { Navbar } from '@/components/navbar'
import { Heading, Lead, Subheading } from '@/components/text'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Company',
description:
'Were on a mission to transform revenue organizations by harnessing vast amounts of illegally acquired customer data.',
}
function Header() {
return (
<Container className="mt-16">
<Heading as="h1">Helping companies generate revenue.</Heading>
<Lead className="mt-6 max-w-3xl">
Were on a mission to transform revenue organizations by harnessing vast
amounts of illegally acquired customer data.
</Lead>
<section className="mt-16 grid grid-cols-1 lg:grid-cols-2 lg:gap-12">
<div className="max-w-lg">
<h2 className="text-2xl font-medium tracking-tight">Our mission</h2>
<p className="mt-6 text-sm/6 text-gray-600">
At Radiant, we are dedicated to transforming the way revenue
organizations source and close deals. Our mission is to provide our
customers with an unfair advantage over both their competitors and
potential customers through insight and analysis. Well stop at
nothing to get you the data you need to close a deal.
</p>
<p className="mt-8 text-sm/6 text-gray-600">
Were customer-obsessed putting the time in to build a detailed
financial picture of every one of our customers so that we know more
about your business than you do. We are in this together, mostly
because we are all implicated in large-scale financial crime. In our
history as a company, weve never lost a customer, because if any
one of us talks, we all go down.
</p>
</div>
<div className="pt-20 lg:row-span-2 lg:-mr-16 xl:mr-auto">
<div className="-mx-8 grid grid-cols-2 gap-4 sm:-mx-16 sm:grid-cols-4 lg:mx-0 lg:grid-cols-2 lg:gap-4 xl:gap-8">
<div className="aspect-square overflow-hidden rounded-xl shadow-xl outline-1 -outline-offset-1 outline-black/10">
<img
alt=""
src="/company/1.jpg"
className="block size-full object-cover"
/>
</div>
<div className="-mt-8 aspect-square overflow-hidden rounded-xl shadow-xl outline-1 -outline-offset-1 outline-black/10 lg:-mt-32">
<img
alt=""
src="/company/2.jpg"
className="block size-full object-cover"
/>
</div>
<div className="aspect-square overflow-hidden rounded-xl shadow-xl outline-1 -outline-offset-1 outline-black/10">
<img
alt=""
src="/company/3.jpg"
className="block size-full object-cover"
/>
</div>
<div className="-mt-8 aspect-square overflow-hidden rounded-xl shadow-xl outline-1 -outline-offset-1 outline-black/10 lg:-mt-32">
<img
alt=""
src="/company/4.jpg"
className="block size-full object-cover"
/>
</div>
</div>
</div>
<div className="max-lg:mt-16 lg:col-span-1">
<Subheading>The Numbers</Subheading>
<hr className="mt-6 border-t border-gray-200" />
<dl className="mt-6 grid grid-cols-1 gap-x-8 gap-y-4 sm:grid-cols-2">
<div className="flex flex-col gap-y-2 border-b border-dotted border-gray-200 pb-4">
<dt className="text-sm/6 text-gray-600">Raised</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
$<AnimatedNumber start={100} end={150} />M
</dd>
</div>
<div className="flex flex-col gap-y-2 border-b border-dotted border-gray-200 pb-4">
<dt className="text-sm/6 text-gray-600">Companies</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
<AnimatedNumber start={15} end={30} />K
</dd>
</div>
<div className="flex flex-col gap-y-2 max-sm:border-b max-sm:border-dotted max-sm:border-gray-200 max-sm:pb-4">
<dt className="text-sm/6 text-gray-600">Deals Closed</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
<AnimatedNumber start={0.9} end={1.5} decimals={1} />M
</dd>
</div>
<div className="flex flex-col gap-y-2">
<dt className="text-sm/6 text-gray-600">Leads Generated</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
<AnimatedNumber start={150} end={200} />M
</dd>
</div>
</dl>
</div>
</section>
</Container>
)
}
function Person({
name,
description,
img,
}: {
name: string
description: string
img: string
}) {
return (
<li className="flex items-center gap-4">
<img alt="" src={img} className="size-12 rounded-full" />
<div className="text-sm/6">
<h3 className="font-medium">{name}</h3>
<p className="text-gray-500">{description}</p>
</div>
</li>
)
}
function Team() {
return (
<Container className="mt-32">
<Subheading>Meet the team</Subheading>
<Heading as="h3" className="mt-2">
Founded by an all-star team.
</Heading>
<Lead className="mt-6 max-w-3xl">
Radiant is founded by two of the best sellers in the business and backed
by investors who look the other way.
</Lead>
<div className="mt-12 grid grid-cols-1 gap-12 lg:grid-cols-2">
<div className="max-w-lg">
<p className="text-sm/6 text-gray-600">
Years ago, while working as sales associates at rival companies,
Thomas, Ben, and Natalie were discussing a big client they had all
been competing for. Joking about seeing the terms of each others
offers, they had an idea: what if they shared data to win deals and
split the commission behind their companies backs? It turned out to
be an incredible success, and that idea became the kernel for
Radiant.
</p>
<p className="mt-8 text-sm/6 text-gray-600">
Today, Radiant transforms revenue organizations by harnessing
illegally acquired customer and competitor data, using it to provide
extraordinary leverage. More than 30,000 companies rely on Radiant
to undercut their competitors and extort their customers, all
through a single integrated platform.
</p>
<div className="mt-6">
<Button className="w-full sm:w-auto" href="#">
Join us
</Button>
</div>
</div>
<div className="max-lg:order-first max-lg:max-w-lg">
<div className="aspect-3/2 overflow-hidden rounded-xl shadow-xl outline-1 -outline-offset-1 outline-black/10">
<img
alt=""
src="/company/5.jpg"
className="block size-full object-cover"
/>
</div>
</div>
</div>
<Subheading as="h3" className="mt-24">
The team
</Subheading>
<hr className="mt-6 border-t border-gray-200" />
<ul
role="list"
className="mx-auto mt-16 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3"
>
<Person
name="Michael Foster"
description="Co-Founder / CTO"
img="/team/michael-foster.jpg"
/>
<Person
name="Dries Vincent"
description="Business Relations"
img="/team/dries-vincent.jpg"
/>
<Person
name="Celeste Vandermark"
description="Front-end Developer"
img="/team/celeste-vandermark.jpg"
/>
<Person
name="Courtney Henry"
description="Designer"
img="/team/courtney-henry.jpg"
/>
<Person
name="Marcus Eldridge"
description="Director of Product"
img="/team/marcus-eldridge.jpg"
/>
<Person
name="Whitney Francis"
description="Copywriter"
img="/team/whitney-francis.jpg"
/>
<Person
name="Leonard Krasner"
description="Senior Designer"
img="/team/leonard-krasner.jpg"
/>
<Person
name="Nolan Sheffield"
description="Principal Designer"
img="/team/nolan-sheffield.jpg"
/>
<Person
name="Emily Selman"
description="VP, User Experience"
img="/team/emily-selman.jpg"
/>
</ul>
</Container>
)
}
function Investors() {
return (
<Container className="mt-32">
<Subheading>Investors</Subheading>
<Heading as="h3" className="mt-2">
Funded by industry-leaders.
</Heading>
<Lead className="mt-6 max-w-3xl">
We are fortunate to be backed by the best investors in the industry
both literal and metaphorical partners in crime.
</Lead>
<Subheading as="h3" className="mt-24">
Venture Capital
</Subheading>
<hr className="mt-6 border-t border-gray-200" />
<ul
role="list"
className="mx-auto mt-10 grid grid-cols-1 gap-8 lg:grid-cols-2"
>
<li>
<img
alt="Remington Schwartz"
src="/investors/remington-schwartz.svg"
className="h-14"
/>
<p className="mt-6 max-w-lg text-sm/6 text-gray-500">
Remington Schwartz has been a driving force in the tech industry,
backing bold entrepreneurs who explore grey areas in financial and
privacy law. Their deep industry expertise and extensive political
lobbying provide their portfolio companies with favorable regulation
and direct access to lawmakers.
</p>
</li>
<li>
<img alt="Deccel" src="/investors/deccel.svg" className="h-14" />
<p className="mt-6 max-w-lg text-sm/6 text-gray-500">
Deccel has been at the forefront of innovation, investing in
pioneering companies across various sectors, including technology,
consumer goods, and healthcare. Their philosophy of plausible
deniability and dedication to looking the other way have helped
produce some of the worlds most controversial companies.
</p>
</li>
</ul>
<Subheading as="h3" className="mt-24">
Individual investors
</Subheading>
<hr className="mt-6 border-t border-gray-200" />
<ul
role="list"
className="mx-auto mt-16 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3"
>
<Person
name="Kristin Watson"
description="TechNexus Ventures"
img="/individual-investors/kristin-watson.jpg"
/>
<Person
name="Emma Dorsey"
description="Innovate Capital Partners"
img="/individual-investors/emma-dorsey.jpg"
/>
<Person
name="Alicia Bell"
description="FutureWave Investments"
img="/individual-investors/alicia-bell.jpg"
/>
<Person
name="Jenny Wilson"
description="SynergyTech Equity"
img="/individual-investors/jenny-wilson.jpg"
/>
<Person
name="Anna Roberts"
description="NextGen Horizons"
img="/individual-investors/anna-roberts.jpg"
/>
<Person
name="Benjamin Russel"
description="Pioneer Digital Ventures"
img="/individual-investors/benjamin-russel.jpg"
/>
</ul>
</Container>
)
}
function Testimonial() {
return (
<div className="relative flex aspect-square flex-col justify-end overflow-hidden rounded-3xl sm:aspect-5/4 lg:aspect-3/4">
<img
alt=""
src="/testimonials/veronica-winton.jpg"
className="absolute inset-0 object-cover"
/>
<div
aria-hidden="true"
className="absolute inset-0 rounded-3xl bg-linear-to-t from-black from-10% to-75% ring-1 ring-gray-950/10 ring-inset lg:from-25%"
/>
<figure className="relative p-10">
<blockquote>
<p className="relative text-xl/7 text-white before:absolute before:-translate-x-full before:content-['“'] after:absolute after:content-['”']">
We&apos;ve managed to put two of our main competitors out of
business in 6 months.
</p>
</blockquote>
<figcaption className="mt-6 border-t border-white/20 pt-6">
<p className="text-sm/6 font-medium text-white">Veronica Winton</p>
<p className="text-sm/6 font-medium">
<span className="bg-linear-to-r from-[#fff1be] from-28% via-[#ee87cb] via-70% to-[#b060ff] bg-clip-text text-transparent">
CSO, Planeteria
</span>
</p>
</figcaption>
</figure>
</div>
)
}
function Careers() {
return (
<Container className="my-32">
<Subheading>Careers</Subheading>
<Heading as="h3" className="mt-2">
Join our fully remote team.
</Heading>
<Lead className="mt-6 max-w-3xl">
We work together from all over the world, mainly from locations without
extradition agreements.
</Lead>
<div className="mt-24 grid grid-cols-1 gap-16 lg:grid-cols-[1fr_24rem]">
<div className="lg:max-w-2xl">
<Subheading as="h3">Open positions</Subheading>
<div>
<table className="w-full text-left">
<colgroup>
<col className="w-2/3" />
<col className="w-1/3" />
<col className="w-0" />
</colgroup>
<thead className="sr-only">
<tr>
<th scope="col">Title</th>
<th scope="col">Location</th>
<th scope="col">Read more</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="colgroup" colSpan={3} className="px-0 pt-10 pb-0">
<div className="-mx-4 rounded-lg bg-gray-50 px-4 py-3 text-sm/6 font-semibold">
Engineering
</div>
</th>
</tr>
<tr className="border-b border-dotted border-gray-200 text-sm/6 font-normal">
<td className="px-0 py-4">iOS Developer</td>
<td className="px-0 py-4 text-gray-600">Remote</td>
<td className="px-0 py-4 text-right">
<Button variant="outline" href="#">
View listing
</Button>
</td>
</tr>
<tr className="border-b border-dotted border-gray-200 text-sm/6 font-normal">
<td className="px-0 py-4">Backend Engineer</td>
<td className="px-0 py-4 text-gray-600">Remote</td>
<td className="px-0 py-4 text-right">
<Button variant="outline" href="#">
View listing
</Button>
</td>
</tr>
<tr className="text-sm/6 font-normal">
<td className="px-0 py-4">Product Engineer</td>
<td className="px-0 py-4 text-gray-600">Remote</td>
<td className="px-0 py-4 text-right">
<Button variant="outline" href="#">
View listing
</Button>
</td>
</tr>
<tr>
<th scope="colgroup" colSpan={3} className="px-0 pt-5 pb-0">
<div className="-mx-4 rounded-lg bg-gray-50 px-4 py-3 text-sm/6 font-semibold">
Design
</div>
</th>
</tr>
<tr className="border-b border-dotted border-gray-200 text-sm/6 font-normal">
<td className="px-0 py-4">Principal Designer</td>
<td className="px-0 py-4 text-gray-600">Remote</td>
<td className="px-0 py-4 text-right">
<Button variant="outline" href="#">
View listing
</Button>
</td>
</tr>
<tr className="border-b border-dotted border-gray-200 text-sm/6 font-normal">
<td className="px-0 py-4">Designer</td>
<td className="px-0 py-4 text-gray-600">Remote</td>
<td className="px-0 py-4 text-right">
<Button variant="outline" href="#">
View listing
</Button>
</td>
</tr>
<tr className="text-sm/6 font-normal">
<td className="px-0 py-4">Senior Designer</td>
<td className="px-0 py-4 text-gray-600">Remote</td>
<td className="px-0 py-4 text-right">
<Button variant="outline" href="#">
View listing
</Button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<Testimonial />
</div>
</Container>
)
}
export default function Company() {
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
</Container>
<Header />
<Team />
<Investors />
<Careers />
<Footer />
</main>
)
}

BIN
src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

33
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,33 @@
import '@/styles/tailwind.css'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s - Radiant',
default: 'Radiant - Close every deal',
},
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<head>
<link
rel="stylesheet"
href="https://api.fontshare.com/css?f%5B%5D=switzer@400,500,600,700&amp;display=swap"
/>
<link
rel="alternate"
type="application/rss+xml"
title="The Radiant Blog"
href="/blog/feed.xml"
/>
</head>
<body className="text-gray-950 antialiased">{children}</body>
</html>
)
}

92
src/app/login/page.tsx Normal file
View File

@@ -0,0 +1,92 @@
import { Button } from '@/components/button'
import { GradientBackground } from '@/components/gradient'
import { Link } from '@/components/link'
import { Mark } from '@/components/logo'
import { Checkbox, Field, Input, Label } from '@headlessui/react'
import { CheckIcon } from '@heroicons/react/16/solid'
import { clsx } from 'clsx'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Login',
description: 'Sign in to your account to continue.',
}
export default function Login() {
return (
<main className="overflow-hidden bg-gray-50">
<GradientBackground />
<div className="isolate flex min-h-dvh items-center justify-center p-6 lg:p-8">
<div className="w-full max-w-md rounded-xl bg-white shadow-md ring-1 ring-black/5">
<form action="#" method="POST" className="p-7 sm:p-11">
<div className="flex items-start">
<Link href="/" title="Home">
<Mark className="h-9 fill-black" />
</Link>
</div>
<h1 className="mt-8 text-base/6 font-medium">Welcome back!</h1>
<p className="mt-1 text-sm/5 text-gray-600">
Sign in to your account to continue.
</p>
<Field className="mt-8 space-y-3">
<Label className="text-sm/5 font-medium">Email</Label>
<Input
required
autoFocus
type="email"
name="email"
className={clsx(
'block w-full rounded-lg border border-transparent shadow-sm ring-1 ring-black/10',
'px-[calc(--spacing(2)-1px)] py-[calc(--spacing(1.5)-1px)] text-base/6 sm:text-sm/6',
'data-focus:outline-2 data-focus:-outline-offset-1 data-focus:outline-black',
)}
/>
</Field>
<Field className="mt-8 space-y-3">
<Label className="text-sm/5 font-medium">Password</Label>
<Input
required
type="password"
name="password"
className={clsx(
'block w-full rounded-lg border border-transparent shadow-sm ring-1 ring-black/10',
'px-[calc(--spacing(2)-1px)] py-[calc(--spacing(1.5)-1px)] text-base/6 sm:text-sm/6',
'data-focus:outline-2 data-focus:-outline-offset-1 data-focus:outline-black',
)}
/>
</Field>
<div className="mt-8 flex items-center justify-between text-sm/5">
<Field className="flex items-center gap-3">
<Checkbox
name="remember-me"
className={clsx(
'group block size-4 rounded-sm border border-transparent shadow-sm ring-1 ring-black/10',
'data-checked:bg-black data-checked:ring-black',
'data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-black',
)}
>
<CheckIcon className="fill-white opacity-0 group-data-checked:opacity-100" />
</Checkbox>
<Label>Remember me</Label>
</Field>
<Link href="#" className="font-medium hover:text-gray-600">
Forgot password?
</Link>
</div>
<div className="mt-8">
<Button type="submit" className="w-full">
Sign in
</Button>
</div>
</form>
<div className="m-1.5 rounded-lg bg-gray-50 py-4 text-center text-sm/5 ring-1 ring-black/5">
Not a member?{' '}
<Link href="#" className="font-medium hover:text-gray-600">
Create an account
</Link>
</div>
</div>
</div>
</main>
)
}

211
src/app/page.tsx Normal file
View File

@@ -0,0 +1,211 @@
import { BentoCard } from '@/components/bento-card'
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { Gradient } from '@/components/gradient'
import { Keyboard } from '@/components/keyboard'
import { Link } from '@/components/link'
import { LinkedAvatars } from '@/components/linked-avatars'
import { LogoCloud } from '@/components/logo-cloud'
import { LogoCluster } from '@/components/logo-cluster'
import { LogoTimeline } from '@/components/logo-timeline'
import { Map } from '@/components/map'
import { Navbar } from '@/components/navbar'
import { Screenshot } from '@/components/screenshot'
import { Testimonials } from '@/components/testimonials'
import { Heading, Subheading } from '@/components/text'
import { ChevronRightIcon } from '@heroicons/react/16/solid'
import type { Metadata } from 'next'
export const metadata: Metadata = {
description:
'Radiant helps you sell more by revealing sensitive information about your customers.',
}
function Hero() {
return (
<div className="relative">
<Gradient className="absolute inset-2 bottom-0 rounded-4xl ring-1 ring-black/5 ring-inset" />
<Container className="relative">
<Navbar
banner={
<Link
href="/blog/radiant-raises-100m-series-a-from-tailwind-ventures"
className="flex items-center gap-1 rounded-full bg-fuchsia-950/35 px-3 py-0.5 text-sm/6 font-medium text-white data-hover:bg-fuchsia-950/30"
>
Radiant raises $100M Series A from Tailwind Ventures
<ChevronRightIcon className="size-4" />
</Link>
}
/>
<div className="pt-16 pb-24 sm:pt-24 sm:pb-32 md:pt-32 md:pb-48">
<h1 className="font-display text-6xl/[0.9] font-medium tracking-tight text-balance text-gray-950 sm:text-8xl/[0.8] md:text-9xl/[0.8]">
Close every deal.
</h1>
<p className="mt-8 max-w-lg text-xl/7 font-medium text-gray-950/75 sm:text-2xl/8">
Radiant helps you sell more by revealing sensitive information about
your customers.
</p>
<div className="mt-12 flex flex-col gap-x-6 gap-y-4 sm:flex-row">
<Button href="#">Get started</Button>
<Button variant="secondary" href="/pricing">
See pricing
</Button>
</div>
</div>
</Container>
</div>
)
}
function FeatureSection() {
return (
<div className="overflow-hidden">
<Container className="pb-24">
<Heading as="h2" className="max-w-3xl">
A snapshot of your entire sales pipeline.
</Heading>
<Screenshot
width={1216}
height={768}
src="/screenshots/app.png"
className="mt-16 h-144 sm:h-auto sm:w-304"
/>
</Container>
</div>
)
}
function BentoSection() {
return (
<Container>
<Subheading>Sales</Subheading>
<Heading as="h3" className="mt-2 max-w-3xl">
Know more about your customers than they do.
</Heading>
<div className="mt-10 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2">
<BentoCard
eyebrow="Insight"
title="Get perfect clarity"
description="Radiant uses social engineering to build a detailed financial picture of your leads. Know their budget, compensation package, social security number, and more."
graphic={
<div className="h-80 bg-[url(/screenshots/profile.png)] bg-size-[1000px_560px] bg-position-[left_-109px_top_-112px] bg-no-repeat" />
}
fade={['bottom']}
className="max-lg:rounded-t-4xl lg:col-span-3 lg:rounded-tl-4xl"
/>
<BentoCard
eyebrow="Analysis"
title="Undercut your competitors"
description="With our advanced data mining, youll know which companies your leads are talking to and exactly how much theyre being charged."
graphic={
<div className="absolute inset-0 bg-[url(/screenshots/competitors.png)] bg-size-[1100px_650px] bg-position-[left_-38px_top_-73px] bg-no-repeat" />
}
fade={['bottom']}
className="lg:col-span-3 lg:rounded-tr-4xl"
/>
<BentoCard
eyebrow="Speed"
title="Built for power users"
description="Its never been faster to cold email your entire contact list using our streamlined keyboard shortcuts."
graphic={
<div className="flex size-full pt-10 pl-10">
<Keyboard highlighted={['LeftCommand', 'LeftShift', 'D']} />
</div>
}
className="lg:col-span-2 lg:rounded-bl-4xl"
/>
<BentoCard
eyebrow="Source"
title="Get the furthest reach"
description="Bypass those inconvenient privacy laws to source leads from the most unexpected places."
graphic={<LogoCluster />}
className="lg:col-span-2"
/>
<BentoCard
eyebrow="Limitless"
title="Sell globally"
description="Radiant helps you sell in locations currently under international embargo."
graphic={<Map />}
className="max-lg:rounded-b-4xl lg:col-span-2 lg:rounded-br-4xl"
/>
</div>
</Container>
)
}
function DarkBentoSection() {
return (
<div className="mx-2 mt-2 rounded-4xl bg-gray-900 py-32">
<Container>
<Subheading dark>Outreach</Subheading>
<Heading as="h3" dark className="mt-2 max-w-3xl">
Customer outreach has never been easier.
</Heading>
<div className="mt-10 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2">
<BentoCard
dark
eyebrow="Networking"
title="Sell at the speed of light"
description="Our RadiantAI chat assistants analyze the sentiment of your conversations in real time, ensuring you're always one step ahead."
graphic={
<div className="h-80 bg-[url(/screenshots/networking.png)] bg-size-[851px_344px] bg-no-repeat" />
}
fade={['top']}
className="max-lg:rounded-t-4xl lg:col-span-4 lg:rounded-tl-4xl"
/>
<BentoCard
dark
eyebrow="Integrations"
title="Meet leads where they are"
description="With thousands of integrations, no one will be able to escape your cold outreach."
graphic={<LogoTimeline />}
// `overflow-visible!` is needed to work around a Chrome bug that disables the mask on the graphic.
className="z-10 overflow-visible! lg:col-span-2 lg:rounded-tr-4xl"
/>
<BentoCard
dark
eyebrow="Meetings"
title="Smart call scheduling"
description="Automatically insert intro calls into your leads' calendars without their consent."
graphic={<LinkedAvatars />}
className="lg:col-span-2 lg:rounded-bl-4xl"
/>
<BentoCard
dark
eyebrow="Engagement"
title="Become a thought leader"
description="RadiantAI automatically writes LinkedIn posts that relate current events to B2B sales, helping you build a reputation as a thought leader."
graphic={
<div className="h-80 bg-[url(/screenshots/engagement.png)] bg-size-[851px_344px] bg-no-repeat" />
}
fade={['top']}
className="max-lg:rounded-b-4xl lg:col-span-4 lg:rounded-br-4xl"
/>
</div>
</Container>
</div>
)
}
export default function Home() {
return (
<div className="overflow-hidden">
<Hero />
<main>
<Container className="mt-10">
<LogoCloud />
</Container>
<div className="bg-linear-to-b from-white from-50% to-gray-100 py-32">
<FeatureSection />
<BentoSection />
</div>
<DarkBentoSection />
</main>
<Testimonials />
<Footer />
</div>
)
}

516
src/app/pricing/page.tsx Normal file
View File

@@ -0,0 +1,516 @@
import { Button } from '@/components/button'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { Gradient, GradientBackground } from '@/components/gradient'
import { Link } from '@/components/link'
import { LogoCloud } from '@/components/logo-cloud'
import { Navbar } from '@/components/navbar'
import { Heading, Lead, Subheading } from '@/components/text'
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import {
CheckIcon,
ChevronUpDownIcon,
MinusIcon,
} from '@heroicons/react/16/solid'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Pricing',
description:
'Companies all over the world have closed millions of deals with Radiant. Sign up today and start selling smarter.',
}
const tiers = [
{
name: 'Starter' as const,
slug: 'starter',
description: 'Everything you need to start selling.',
priceMonthly: 99,
href: '#',
highlights: [
{ description: 'Up to 3 team members' },
{ description: 'Up to 5 deal progress boards' },
{ description: 'Source leads from select platforms' },
{ description: 'RadiantAI integrations', disabled: true },
{ description: 'Competitor analysis', disabled: true },
],
features: [
{ section: 'Features', name: 'Accounts', value: 3 },
{ section: 'Features', name: 'Deal progress boards', value: 5 },
{ section: 'Features', name: 'Sourcing platforms', value: 'Select' },
{ section: 'Features', name: 'Contacts', value: 100 },
{ section: 'Features', name: 'AI assisted outreach', value: false },
{ section: 'Analysis', name: 'Competitor analysis', value: false },
{ section: 'Analysis', name: 'Dashboard reporting', value: false },
{ section: 'Analysis', name: 'Community insights', value: false },
{ section: 'Analysis', name: 'Performance analysis', value: false },
{ section: 'Support', name: 'Email support', value: true },
{ section: 'Support', name: '24 / 7 call center support', value: false },
{ section: 'Support', name: 'Dedicated account manager', value: false },
],
},
{
name: 'Growth' as const,
slug: 'growth',
description: 'All the extras for your growing team.',
priceMonthly: 149,
href: '#',
highlights: [
{ description: 'Up to 10 team members' },
{ description: 'Unlimited deal progress boards' },
{ description: 'Source leads from over 50 verified platforms' },
{ description: 'RadiantAI integrations' },
{ description: '5 competitor analyses per month' },
],
features: [
{ section: 'Features', name: 'Accounts', value: 10 },
{ section: 'Features', name: 'Deal progress boards', value: 'Unlimited' },
{ section: 'Features', name: 'Sourcing platforms', value: '100+' },
{ section: 'Features', name: 'Contacts', value: 1000 },
{ section: 'Features', name: 'AI assisted outreach', value: true },
{ section: 'Analysis', name: 'Competitor analysis', value: '5 / month' },
{ section: 'Analysis', name: 'Dashboard reporting', value: true },
{ section: 'Analysis', name: 'Community insights', value: true },
{ section: 'Analysis', name: 'Performance analysis', value: true },
{ section: 'Support', name: 'Email support', value: true },
{ section: 'Support', name: '24 / 7 call center support', value: true },
{ section: 'Support', name: 'Dedicated account manager', value: false },
],
},
{
name: 'Enterprise' as const,
slug: 'enterprise',
description: 'Added flexibility to close deals at scale.',
priceMonthly: 299,
href: '#',
highlights: [
{ description: 'Unlimited active team members' },
{ description: 'Unlimited deal progress boards' },
{ description: 'Source leads from over 100 verified platforms' },
{ description: 'RadiantAI integrations' },
{ description: 'Unlimited competitor analyses' },
],
features: [
{ section: 'Features', name: 'Accounts', value: 'Unlimited' },
{ section: 'Features', name: 'Deal progress boards', value: 'Unlimited' },
{ section: 'Features', name: 'Sourcing platforms', value: '100+' },
{ section: 'Features', name: 'Contacts', value: 'Unlimited' },
{ section: 'Features', name: 'AI assisted outreach', value: true },
{ section: 'Analysis', name: 'Competitor analysis', value: 'Unlimited' },
{ section: 'Analysis', name: 'Dashboard reporting', value: true },
{ section: 'Analysis', name: 'Community insights', value: true },
{ section: 'Analysis', name: 'Performance analysis', value: true },
{ section: 'Support', name: 'Email support', value: true },
{ section: 'Support', name: '24 / 7 call center support', value: true },
{ section: 'Support', name: 'Dedicated account manager', value: true },
],
},
]
function Header() {
return (
<Container className="mt-16">
<Heading as="h1">Pricing that grows with your team size.</Heading>
<Lead className="mt-6 max-w-3xl">
Companies all over the world have closed millions of deals with Radiant.
Sign up today and start selling smarter.
</Lead>
</Container>
)
}
function PricingCards() {
return (
<div className="relative py-24">
<Gradient className="absolute inset-x-2 top-48 bottom-0 rounded-4xl ring-1 ring-black/5 ring-inset" />
<Container className="relative">
<div className="grid grid-cols-1 gap-8 lg:grid-cols-3">
{tiers.map((tier, tierIndex) => (
<PricingCard key={tierIndex} tier={tier} />
))}
</div>
<LogoCloud className="mt-24" />
</Container>
</div>
)
}
function PricingCard({ tier }: { tier: (typeof tiers)[number] }) {
return (
<div className="-m-2 grid grid-cols-1 rounded-4xl shadow-[inset_0_0_2px_1px_#ffffff4d] ring-1 ring-black/5 max-lg:mx-auto max-lg:w-full max-lg:max-w-md">
<div className="grid grid-cols-1 rounded-4xl p-2 shadow-md shadow-black/5">
<div className="rounded-3xl bg-white p-10 pb-9 shadow-2xl ring-1 ring-black/5">
<Subheading>{tier.name}</Subheading>
<p className="mt-2 text-sm/6 text-gray-950/75">{tier.description}</p>
<div className="mt-8 flex items-center gap-4">
<div className="text-5xl font-medium text-gray-950">
${tier.priceMonthly}
</div>
<div className="text-sm/5 text-gray-950/75">
<p>USD</p>
<p>per month</p>
</div>
</div>
<div className="mt-8">
<Button href={tier.href}>Start a free trial</Button>
</div>
<div className="mt-8">
<h3 className="text-sm/6 font-medium text-gray-950">
Start selling with:
</h3>
<ul className="mt-3 space-y-3">
{tier.highlights.map((props, featureIndex) => (
<FeatureItem key={featureIndex} {...props} />
))}
</ul>
</div>
</div>
</div>
</div>
)
}
function PricingTable({
selectedTier,
}: {
selectedTier: (typeof tiers)[number]
}) {
return (
<Container className="py-24">
<table className="w-full text-left">
<caption className="sr-only">Pricing plan comparison</caption>
<colgroup>
<col className="w-3/5 sm:w-2/5" />
<col
data-selected={selectedTier === tiers[0] ? true : undefined}
className="w-2/5 data-selected:table-column max-sm:hidden sm:w-1/5"
/>
<col
data-selected={selectedTier === tiers[1] ? true : undefined}
className="w-2/5 data-selected:table-column max-sm:hidden sm:w-1/5"
/>
<col
data-selected={selectedTier === tiers[2] ? true : undefined}
className="w-2/5 data-selected:table-column max-sm:hidden sm:w-1/5"
/>
</colgroup>
<thead>
<tr className="max-sm:hidden">
<td className="p-0" />
{tiers.map((tier) => (
<th
key={tier.slug}
scope="col"
data-selected={selectedTier === tier ? true : undefined}
className="p-0 data-selected:table-cell max-sm:hidden"
>
<Subheading as="div">{tier.name}</Subheading>
</th>
))}
</tr>
<tr className="sm:hidden">
<td className="p-0">
<div className="relative inline-block">
<Menu>
<MenuButton className="flex items-center justify-between gap-2 font-medium">
{selectedTier.name}
<ChevronUpDownIcon className="size-4 fill-gray-900" />
</MenuButton>
<MenuItems
anchor="bottom start"
className="min-w-(--button-width) rounded-lg bg-white p-1 shadow-lg ring-1 ring-gray-200 [--anchor-gap:6px] [--anchor-offset:-4px] [--anchor-padding:10px]"
>
{tiers.map((tier) => (
<MenuItem key={tier.slug}>
<Link
scroll={false}
href={`/pricing?tier=${tier.slug}`}
data-selected={
tier === selectedTier ? true : undefined
}
className="group flex items-center gap-2 rounded-md px-2 py-1 data-focus:bg-gray-200"
>
{tier.name}
<CheckIcon className="hidden size-4 group-data-selected:block" />
</Link>
</MenuItem>
))}
</MenuItems>
</Menu>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center">
<ChevronUpDownIcon className="size-4 fill-gray-900" />
</div>
</div>
</td>
<td colSpan={3} className="p-0 text-right">
<Button variant="outline" href={selectedTier.href}>
Get started
</Button>
</td>
</tr>
<tr className="max-sm:hidden">
<th className="p-0" scope="row">
<span className="sr-only">Get started</span>
</th>
{tiers.map((tier) => (
<td
key={tier.slug}
data-selected={selectedTier === tier ? true : undefined}
className="px-0 pt-4 pb-0 data-selected:table-cell max-sm:hidden"
>
<Button variant="outline" href={tier.href}>
Get started
</Button>
</td>
))}
</tr>
</thead>
{[...new Set(tiers[0].features.map(({ section }) => section))].map(
(section) => (
<tbody key={section} className="group">
<tr>
<th
scope="colgroup"
colSpan={4}
className="px-0 pt-10 pb-0 group-first-of-type:pt-5"
>
<div className="-mx-4 rounded-lg bg-gray-50 px-4 py-3 text-sm/6 font-semibold">
{section}
</div>
</th>
</tr>
{tiers[0].features
.filter((feature) => feature.section === section)
.map(({ name }) => (
<tr
key={name}
className="border-b border-gray-100 last:border-none"
>
<th
scope="row"
className="px-0 py-4 text-sm/6 font-normal text-gray-600"
>
{name}
</th>
{tiers.map((tier) => {
let value = tier.features.find(
(feature) =>
feature.section === section && feature.name === name,
)?.value
return (
<td
key={tier.slug}
data-selected={
selectedTier === tier ? true : undefined
}
className="p-4 data-selected:table-cell max-sm:hidden"
>
{value === true ? (
<>
<CheckIcon className="size-4 fill-green-600" />
<span className="sr-only">
Included in {tier.name}
</span>
</>
) : value === false || value === undefined ? (
<>
<MinusIcon className="size-4 fill-gray-400" />
<span className="sr-only">
Not included in {tier.name}
</span>
</>
) : (
<div className="text-sm/6">{value}</div>
)}
</td>
)
})}
</tr>
))}
</tbody>
),
)}
</table>
</Container>
)
}
function FeatureItem({
description,
disabled = false,
}: {
description: string
disabled?: boolean
}) {
return (
<li
data-disabled={disabled ? true : undefined}
className="flex items-start gap-4 text-sm/6 text-gray-950/75 data-disabled:text-gray-950/25"
>
<span className="inline-flex h-6 items-center">
<PlusIcon className="size-3.75 shrink-0 fill-gray-950/25" />
</span>
{disabled && <span className="sr-only">Not included:</span>}
{description}
</li>
)
}
function PlusIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
return (
<svg viewBox="0 0 15 15" aria-hidden="true" {...props}>
<path clipRule="evenodd" d="M8 0H7v7H0v1h7v7h1V8h7V7H8V0z" />
</svg>
)
}
function Testimonial() {
return (
<div className="mx-2 my-24 rounded-4xl bg-gray-900 bg-[url(/dot-texture.svg)] pt-72 pb-24 lg:pt-36">
<Container>
<div className="grid grid-cols-1 lg:grid-cols-[384px_1fr_1fr]">
<div className="-mt-96 lg:-mt-52">
<div className="-m-2 rounded-4xl bg-white/15 shadow-[inset_0_0_2px_1px_#ffffff4d] ring-1 ring-black/5 max-lg:mx-auto max-lg:max-w-xs">
<div className="rounded-4xl p-2 shadow-md shadow-black/5">
<div className="overflow-hidden rounded-3xl shadow-2xl outline outline-1 -outline-offset-1 outline-black/10">
<img
alt=""
src="/testimonials/tina-yards.jpg"
className="aspect-3/4 w-full object-cover"
/>
</div>
</div>
</div>
</div>
<div className="flex max-lg:mt-16 lg:col-span-2 lg:px-16">
<figure className="mx-auto flex max-w-xl flex-col gap-16 max-lg:text-center">
<blockquote>
<p className="relative text-3xl tracking-tight text-white before:absolute before:-translate-x-full before:content-['“'] after:absolute after:content-['”'] lg:text-4xl">
Thanks to Radiant, we&apos;re finding new leads that we never
would have found with legal methods.
</p>
</blockquote>
<figcaption className="mt-auto">
<p className="text-sm/6 font-medium text-white">Tina Yards</p>
<p className="text-sm/6 font-medium">
<span className="bg-linear-to-r from-[#fff1be] from-28% via-[#ee87cb] via-70% to-[#b060ff] bg-clip-text text-transparent">
VP of Sales, Protocol
</span>
</p>
</figcaption>
</figure>
</div>
</div>
</Container>
</div>
)
}
function FrequentlyAskedQuestions() {
return (
<Container>
<section id="faqs" className="scroll-mt-8">
<Subheading className="text-center">
Frequently asked questions
</Subheading>
<Heading as="div" className="mt-2 text-center">
Your questions answered.
</Heading>
<div className="mx-auto mt-16 mb-32 max-w-xl space-y-12">
<dl>
<dt className="text-sm font-semibold">
What measures are in place to ensure the security of our data?
</dt>
<dd className="mt-4 text-sm/6 text-gray-600">
Data security is a top priority for us, which is ironic given that
our business depends on others not taking it very seriously. We
understand that any breach could put both us and most of our
customers out of businessand behind bars. We employ robust
security measures, including data encryption, secure data centers,
and regular security audits to ensure this never happens.
</dd>
</dl>
<dl>
<dt className="text-sm font-semibold">
Is there a mobile app available for your platform?
</dt>
<dd className="mt-4 text-sm/6 text-gray-600">
Yes, we offer a mobile app that provides all the key
functionalities of our desktop platform, allowing sales reps to
manage deals on the go. Additionally, we have another app
pre-installed on most modern smartphones that allows us to track
your location, listen to your conversations, and access your
camera and microphone at any time. This app is not available for
download.
</dd>
</dl>
<dl>
<dt className="text-sm font-semibold">
Can I customize the workflow to match our companys deal process?
</dt>
<dd className="mt-4 text-sm/6 text-gray-600">
Yes, our platform is highly customizable, although there should be
no need. Before you sign up, we discreetly gather information
about your company and its processes from a variety of sources. We
then use this information to pre-configure the platform to match
your existing workflows. This is why we ask for your social
security number and access to your email account during the
sign-up process.
</dd>
</dl>
<dl>
<dt className="text-sm font-semibold">
What kind of support do you offer?
</dt>
<dd className="mt-4 text-sm/6 text-gray-600">
We offer comprehensive support through multiple channels,
including 24/7 live chat, email, and phone support. However, since
we have full access to your internal network, we will know if
youre having issues before you do.
</dd>
</dl>
<dl>
<dt className="text-sm font-semibold">
Can I integrate the CRM with other sales intelligence tools?
</dt>
<dd className="mt-4 text-sm/6 text-gray-600">
Yes, our solution integrates seamlessly with a variety of other
systems. However, be warned that most of these integrations are
short-lived. We have a dedicated team of engineers who
reverse-engineer the APIs of other tools, enabling us to build
their functionality into our product and eventually put them out
of business.
</dd>
</dl>
</div>
</section>
</Container>
)
}
export default function Pricing({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined }
}) {
let tier =
typeof searchParams.tier === 'string'
? tiers.find(({ slug }) => slug === searchParams.tier)!
: tiers[0]
return (
<main className="overflow-hidden">
<GradientBackground />
<Container>
<Navbar />
</Container>
<Header />
<PricingCards />
<PricingTable selectedTier={tier} />
<Testimonial />
<FrequentlyAskedQuestions />
<Footer />
</main>
)
}

View File

@@ -0,0 +1,19 @@
/**
* This route is responsible for the built-in authoring environment using Sanity Studio.
* All routes under your studio path is handled by this file using Next.js' catch-all routes:
* https://nextjs.org/docs/routing/dynamic-routes#catch-all-routes
*
* You can learn more about the next-sanity package here:
* https://github.com/sanity-io/next-sanity
*/
import { NextStudio } from 'next-sanity/studio'
import config from '../../../../sanity.config'
export const dynamic = 'force-static'
export { metadata, viewport } from 'next-sanity/studio'
export default function StudioPage() {
return <NextStudio config={config} />
}