Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion website-next/app/(content)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { ReactNode } from "react";
export default function ContentLayout({ children }: { children: ReactNode }) {
return (
<div className="px-5 py-8 sm:px-12">
<div className="mx-auto max-w-5xl">{children}</div>
<div className="mx-auto max-w-7xl">{children}</div>
</div>
);
}
61 changes: 57 additions & 4 deletions website-next/app/(content)/pricing/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,66 @@
import type { Metadata } from "next";

import { Typography } from "@/src/design-system/Typography";
import { ClosingCta } from "@/src/components/pricing/ClosingCta";
import { CompareTable } from "@/src/components/pricing/CompareTable";
import { PlanSelector } from "@/src/components/pricing/PlanSelector";
import { PricingFaq } from "@/src/components/pricing/PricingFaq";
import { RegulatedBand } from "@/src/components/pricing/RegulatedBand";
import { OutlineButton, SolidButton } from "@/src/design-system/Button";

export const metadata: Metadata = {
title: "Pricing",
description:
"Explore pricing for the ChilliCream GraphQL Platform. Compare plans and find the right option for your team, from open source to enterprise.",
"Nitro pricing: start free on the shared cloud, pay as you go at $20/mo, run a dedicated single-tenant instance from $400, or self-host. Compare every plan.",
keywords: [
"ChilliCream pricing",
"Nitro pricing",
"GraphQL platform pricing",
"GraphQL plans",
"dedicated instance",
"self-hosted",
"schema registry pricing",
"GraphQL observability pricing",
],
openGraph: {
title: "Nitro Pricing",
description:
"Start free on the shared cloud, pay as you go at $20/mo, run a dedicated single-tenant instance from $400, or self-host on your own infrastructure.",
},
};

export default function Page() {
return <Typography variant="body">TODO: Pricing page</Typography>;
export default function PricingPage() {
return (
<>
<Hero />
<PlanSelector />
<CompareTable />
<PricingFaq />
<RegulatedBand />
<ClosingCta />
</>
);
}

function Hero() {
return (
<section className="pt-10 pb-14 text-center sm:pt-16 sm:pb-20">
<p className="text-cc-nav-label font-mono text-xs tracking-[0.18em] uppercase">
Nitro pricing
</p>
<h1 className="font-heading text-cc-heading sm:text-h2 mt-5 text-4xl font-semibold text-balance">
Pricing that scales with your platform.
</h1>
<p className="text-cc-ink mx-auto mt-6 max-w-2xl text-base text-pretty sm:text-lg">
Start free on the shared cloud. Pay as you go as traffic grows, run a
dedicated single-tenant instance when you need your own region and
isolation, or self-host on your own infrastructure.
</p>
<div className="mt-9 flex flex-wrap items-center justify-center gap-3">
<SolidButton href="/get-started">Start for Free</SolidButton>
<OutlineButton href="/services/support/contact">
Talk to Sales
</OutlineButton>
</div>
</section>
);
}
2 changes: 1 addition & 1 deletion website-next/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function Home() {
<NitroPricing />
<GrabADrink />
<div className="px-5 py-8 sm:px-12">
<div className="mx-auto flex max-w-6xl flex-col gap-12">
<div className="mx-auto flex max-w-7xl flex-col gap-12">
<FromOurBlog />
</div>
</div>
Expand Down
20 changes: 15 additions & 5 deletions website-next/src/components/Offering.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,24 @@ export function Offering({
// must match the number of section cells rendered.
const rowCount = 2 + (Icon ? 1 : 0) + (description ? 1 : 0) + (price ? 1 : 0);

// The popular card gets a rainbow gradient border (the gradient paints the
// border-box layer, the opaque surface fill paints the padding-box layer on
// top), so the rainbow shows only on the edge and the interior stays solid.
const cardStyle: CSSProperties = popular
? {
gridRow: `span ${rowCount}`,
border: "1.5px solid transparent",
background:
"linear-gradient(var(--color-cc-surface), var(--color-cc-surface)) padding-box, linear-gradient(140deg, #16b9e4 0%, #7c92c6 50%, #f0786a 100%) border-box",
}
: { gridRow: `span ${rowCount}` };

return (
<div
className={`relative grid h-full grid-rows-subgrid gap-0 rounded-3xl border p-6 sm:p-7 ${
popular
? "bg-cc-card-bg border-cc-accent"
: "border-cc-card-border bg-cc-card-bg/60"
className={`relative grid h-full grid-rows-subgrid gap-0 rounded-3xl p-6 sm:p-7 ${
popular ? "" : "border-cc-card-border bg-cc-card-bg/60 border"
}`}
style={{ gridRow: `span ${rowCount}` }}
style={cardStyle}
>
{popular && <PopularBadge />}

Expand Down
4 changes: 2 additions & 2 deletions website-next/src/components/home/BuildYourWay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ const PRODUCTS = [
*/
export function BuildYourWay() {
return (
<section className="mx-auto max-w-6xl px-5 py-8 sm:px-12">
<div className="mx-auto max-w-6xl overflow-hidden rounded-3xl shadow-[0_24px_50px_rgba(0,0,0,0.4)]">
<section className="mx-auto max-w-7xl px-5 py-8 sm:px-12">
<div className="mx-auto max-w-7xl overflow-hidden rounded-3xl shadow-[0_24px_50px_rgba(0,0,0,0.4)]">
{/* Pitch region (top of the card). */}
<div
className="px-8 pt-12 pb-12 text-[#0b1018] sm:px-12 sm:pt-14 lg:px-16"
Expand Down
2 changes: 1 addition & 1 deletion website-next/src/components/home/FusionFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const SCRIM =
*/
export function FusionFlow() {
return (
<section className="mx-auto max-w-6xl px-5 sm:px-12">
<section className="mx-auto max-w-7xl px-5 sm:px-12">
{/* Built apart / queried together */}
<div className="py-16 text-center sm:py-20">
<h2 className="font-heading text-cc-heading text-h2 sm:text-h1 leading-[1.05] font-semibold text-balance">
Expand Down
2 changes: 1 addition & 1 deletion website-next/src/components/home/HomeHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function position(
*/
export function HomeHero() {
return (
<section className="relative isolate mx-auto flex min-h-[34rem] max-w-6xl flex-col items-center justify-center px-5 py-20 text-center sm:min-h-[40rem] sm:px-12 lg:min-h-[46rem]">
<section className="relative isolate mx-auto flex min-h-[34rem] max-w-7xl flex-col items-center justify-center px-5 py-20 text-center sm:min-h-[40rem] sm:px-12 lg:min-h-[46rem]">
{/* Decorative scatter, behind the headline. Trimmed corner composition on
phones, full scatter from the `sm` breakpoint up. */}
<div
Expand Down
2 changes: 1 addition & 1 deletion website-next/src/components/home/LogoCloud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { SwissLifeLogo } from "@/src/icons/SwissLifeLogo";
*/
export function LogoCloud() {
return (
<section className="mx-auto max-w-6xl px-5 py-12 text-center sm:px-12 sm:py-16">
<section className="mx-auto max-w-7xl px-5 py-12 text-center sm:px-12 sm:py-16">
<p className="text-cc-nav-label font-mono text-xs tracking-[0.2em] uppercase">
Trusted by Enterprises
</p>
Expand Down
123 changes: 47 additions & 76 deletions website-next/src/components/home/NitroPricing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,33 @@ import type { ComponentType } from "react";

import { Offering } from "@/src/components/Offering";
import { OfferingGrid } from "@/src/components/OfferingGrid";
import type { TierId } from "@/src/components/pricing/pricingData";
import { TIERS } from "@/src/components/pricing/pricingData";
import { OutlineButton } from "@/src/design-system/Button";
import { DripBrewer } from "@/src/icons/DripBrewer";
import { FrenchPress } from "@/src/icons/FrenchPress";
import { PourOver } from "@/src/icons/PourOver";

interface Plan {
readonly Icon: ComponentType<{ readonly className?: string }>;
readonly name: string;
readonly description: string;
readonly price: string;
readonly priceNote: string;
readonly features: readonly string[];
readonly cta: string;
readonly ctaHref: string;
readonly popular?: boolean;
}
// Coffee-brew icon per cloud tier, lightest brew to strongest.
const ICONS: Partial<
Record<TierId, ComponentType<{ readonly className?: string }>>
> = {
free: FrenchPress,
payg: DripBrewer,
dedicated: PourOver,
};

const PLANS: readonly Plan[] = [
{
Icon: FrenchPress,
name: "Shared Instance",
description: "Shared resources, fully managed",
price: "Free",
priceNote: "pay-as-you-go",
features: [
"Multi-tenant cloud region",
"1 Schema · 3 Environments",
"Up to 5M ops / month included",
"Community Slack support",
"Pay only for what you use after",
],
cta: "Start for Free",
ctaHref: "https://nitro.chillicream.com",
},
{
Icon: DripBrewer,
name: "Dedicated Instance",
description: "Dedicated resources, fully managed",
price: "$400",
priceNote: "per month",
features: [
"Single-tenant cloud region",
"Unlimited schemas",
"BYOC region · private networking",
"99.95% SLA · email + private chat",
"SSO, audit log, role-based access",
],
cta: "Talk to Us",
ctaHref: "/services/support/contact",
popular: true,
},
{
Icon: PourOver,
name: "Self-Hosted",
description: "Self managed",
price: "Custom",
priceNote: "talk to us",
features: [
"Run on your own infrastructure",
"Air-gapped & on-prem supported",
"Priority engineering support",
"Long-term release channel",
"Custom training & onboarding",
],
cta: "Talk to Us",
ctaHref: "/services/support/contact",
},
];
const CLOUD_TIERS = TIERS.filter((tier) => tier.id !== "self");
const SELF_HOSTED = TIERS.find((tier) => tier.id === "self");

/**
* Nitro pricing: three plans framed as coffee brews (French Press, Drip
* Brewer, Pour-Over). The middle plan is highlighted as the popular pick. Cards
* stack on small screens and sit side by side from large up.
* Nitro pricing: the three cloud tiers (Free, Pay as you go, Dedicated) framed
* as coffee brews, with Dedicated highlighted as the popular pick, and a
* self-hosted option below. All data comes from the shared pricing module.
*/
export function NitroPricing() {
return (
<section className="mx-auto max-w-6xl px-5 py-16 sm:px-12 sm:py-24">
<section className="mx-auto max-w-7xl px-5 py-16 sm:px-12 sm:py-24">
<h2 className="font-heading text-cc-heading text-h4 sm:text-h3 text-center font-semibold">
Brew it your Way
</h2>
Expand All @@ -88,20 +39,40 @@ export function NitroPricing() {
</p>

<OfferingGrid columns="mt-14 md:grid-cols-3">
{PLANS.map((plan) => (
{CLOUD_TIERS.map((tier) => (
<Offering
key={plan.name}
Icon={plan.Icon}
title={plan.name}
description={plan.description}
price={plan.price}
priceNote={plan.priceNote}
perks={plan.features}
popular={plan.popular}
callToAction={{ title: plan.cta, link: plan.ctaHref }}
key={tier.id}
Icon={ICONS[tier.id]}
title={tier.name}
description={tier.tagline}
price={tier.price}
priceNote={tier.priceNote}
perks={tier.features}
popular={tier.popular}
callToAction={{ title: tier.cta, link: tier.ctaHref }}
/>
))}
</OfferingGrid>

{SELF_HOSTED && (
<div className="border-cc-card-border bg-cc-card-bg/60 mt-6 flex flex-col gap-5 rounded-3xl border p-6 sm:flex-row sm:items-center sm:justify-between sm:p-8">
<div>
<h3 className="font-heading text-cc-heading text-h6 font-semibold">
{SELF_HOSTED.name}
</h3>
<p className="text-cc-ink mt-2 text-sm text-pretty">
{SELF_HOSTED.tagline} Run on your own infrastructure, air-gapped
or on-prem, with configurable retention and priority support.
</p>
</div>
<OutlineButton
href={SELF_HOSTED.ctaHref}
className="shrink-0 sm:w-auto"
>
{SELF_HOSTED.cta}
</OutlineButton>
</div>
)}
</section>
);
}
2 changes: 1 addition & 1 deletion website-next/src/components/home/ProtocolCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function ProtocolTag({ label }: { readonly label: string }) {
*/
export function ProtocolCards() {
return (
<section className="mx-auto max-w-6xl px-5 pb-16 sm:px-12 sm:pb-20">
<section className="mx-auto max-w-7xl px-5 pb-16 sm:px-12 sm:pb-20">
{/* Protocol chip box. The gradient runs left→right so the centered connector
line coming down from the Fusion section meets the midpoint color
(#66be77) for a seamless join. */}
Expand Down
2 changes: 1 addition & 1 deletion website-next/src/components/home/StackDiagrams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function StackRow({ title, diagram, reverse }: StackRowProps) {
*/
export function StackDiagrams() {
return (
<section className="mx-auto flex max-w-6xl flex-col gap-20 px-5 py-16 sm:px-12 sm:py-24 lg:gap-28">
<section className="mx-auto flex max-w-7xl flex-col gap-20 px-5 py-16 sm:px-12 sm:py-24 lg:gap-28">
<StackRow title="Platform" diagram={<Hub className="h-auto w-full" />} />
<StackRow
title="Agentic Era"
Expand Down
24 changes: 24 additions & 0 deletions website-next/src/components/pricing/ClosingCta.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Meta, StoryObj } from "@storybook/nextjs-vite";

import { ClosingCta } from "./ClosingCta";

const meta = {
title: "Components/Pricing/ClosingCta",
component: ClosingCta,
parameters: { layout: "fullscreen" },
decorators: [
(Story) => (
<div className="cc-content-dark p-10">
<div className="mx-auto max-w-5xl">
<Story />
</div>
</div>
),
],
} satisfies Meta<typeof ClosingCta>;

export default meta;
type Story = StoryObj<typeof meta>;

// The closing call to action with the spectrum hairline and teal glow.
export const Default: Story = {};
45 changes: 45 additions & 0 deletions website-next/src/components/pricing/ClosingCta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { OutlineButton, SolidButton } from "@/src/design-system/Button";

/**
* The closing call to action: a bordered band with a spectrum hairline and a
* soft teal glow, restating the free offer and pointing to sign-up and docs.
*/
export function ClosingCta() {
return (
<section className="mt-24 mb-10 sm:mt-28">
<div className="border-cc-card-border bg-cc-card-bg relative overflow-hidden rounded-3xl border p-10 text-center sm:p-16">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 h-px"
style={{
background:
"linear-gradient(90deg, transparent, #16b9e4 30%, #7c92c6 50%, #f0786a 70%, transparent)",
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute -top-32 left-1/2 h-64 w-[40rem] max-w-full -translate-x-1/2 opacity-50 blur-3xl"
style={{
background:
"radial-gradient(50% 50% at 50% 50%, rgba(94,234,212,0.12), transparent 70%)",
}}
/>
<h2 className="font-heading text-cc-heading text-h3 sm:text-h2 relative font-semibold text-balance">
Start free. Scale when you do.
</h2>
<p className="text-cc-ink relative mx-auto mt-5 max-w-xl text-base text-pretty sm:text-lg">
1M operations, 2 GB of ingest, schemas and environments, and the full
Nitro control plane, free on the shared cloud. Upgrade only when you
outgrow it.
</p>
<div className="relative mt-8 flex flex-wrap items-center justify-center gap-3">
<SolidButton href="/get-started">Start for free</SolidButton>
<OutlineButton href="/docs">Read the docs</OutlineButton>
</div>
<p className="text-cc-nav-label relative mt-6 font-mono text-xs">
No credit card. Free forever on the shared cloud.
</p>
</div>
</section>
);
}
Loading
Loading