init
This commit is contained in:
200
src/app/blog/[slug]/page.tsx
Normal file
200
src/app/blog/[slug]/page.tsx
Normal 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>
|
||||
)
|
||||
}
|
65
src/app/blog/feed.xml/route.ts
Normal file
65
src/app/blog/feed.xml/route.ts
Normal 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('&', '&')
|
||||
: 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
310
src/app/blog/page.tsx
Normal 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">
|
||||
What’s 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
473
src/app/company/page.tsx
Normal 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:
|
||||
'We’re 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">
|
||||
We’re 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. We’ll 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">
|
||||
We’re 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, we’ve 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 other’s
|
||||
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 world’s 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'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
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
33
src/app/layout.tsx
Normal 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&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
92
src/app/login/page.tsx
Normal 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
211
src/app/page.tsx
Normal 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, you’ll know which companies your leads are talking to and exactly how much they’re 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="It’s 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
516
src/app/pricing/page.tsx
Normal 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'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 business—and 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 company’s 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
|
||||
you’re 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>
|
||||
)
|
||||
}
|
19
src/app/studio/[[...tool]]/page.tsx
Normal file
19
src/app/studio/[[...tool]]/page.tsx
Normal 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} />
|
||||
}
|
Reference in New Issue
Block a user