page.tsx211 lines · main
| 1 | import Link from 'next/link'; |
| 2 | |
| 3 | import { DocsShell } from '../../components/shell'; |
| 4 | |
| 5 | export const metadata = { title: 'pricing' }; |
| 6 | |
| 7 | interface PlanRow { |
| 8 | label: string; |
| 9 | free: string; |
| 10 | pro: string; |
| 11 | team: string; |
| 12 | } |
| 13 | |
| 14 | // Caps are the single source of truth from apps/api/src/services/tiers.ts |
| 15 | // (TIERS). Keep this table in sync with that file if the limits move. |
| 16 | const CAPS: readonly PlanRow[] = [ |
| 17 | { label: 'price', free: '$0', pro: '$29.99 / mo', team: '$99.99 / mo' }, |
| 18 | { label: 'projects per org', free: '3', pro: '20', team: '100' }, |
| 19 | { label: 'functions per project', free: '20', pro: '200', team: '2,000' }, |
| 20 | { |
| 21 | label: 'function invocations / month', |
| 22 | free: '100,000', |
| 23 | pro: '1,000,000', |
| 24 | team: '10,000,000', |
| 25 | }, |
| 26 | { label: 'database storage', free: '1 GB', pro: '100 GB', team: '500 GB' }, |
| 27 | { |
| 28 | label: 'realtime connection-seconds / month', |
| 29 | free: '1,000,000 (~12 days)', |
| 30 | pro: '10,000,000 (~115 days)', |
| 31 | team: '100,000,000 (~1,158 days)', |
| 32 | }, |
| 33 | { |
| 34 | label: 'concurrent realtime subscriptions', |
| 35 | free: '100', |
| 36 | pro: '1,000', |
| 37 | team: '10,000', |
| 38 | }, |
| 39 | { |
| 40 | label: 'auth monthly active users', |
| 41 | free: '1,000', |
| 42 | pro: '25,000', |
| 43 | team: '250,000', |
| 44 | }, |
| 45 | { |
| 46 | label: 'support', |
| 47 | free: 'community (github)', |
| 48 | pro: 'email · 48h target', |
| 49 | team: 'priority email · 99.9% SLA', |
| 50 | }, |
| 51 | ]; |
| 52 | |
| 53 | export default function PricingPage() { |
| 54 | return ( |
| 55 | <DocsShell> |
| 56 | <h1 className="font-mono text-2xl tracking-tight">pricing</h1> |
| 57 | <p className="mt-2 font-mono text-sm text-[var(--color-text-muted)]"> |
| 58 | three tiers, one engine. start free, upgrade when a project outgrows the caps. prices |
| 59 | are in USD and exclude tax — VAT (or your local equivalent) is added at checkout for EU |
| 60 | customers, calculated from your billing country. billing runs on{' '} |
| 61 | <Link href="https://polar.sh" className="underline hover:text-[var(--color-text)]"> |
| 62 | Polar |
| 63 | </Link> |
| 64 | , which is the merchant of record. |
| 65 | </p> |
| 66 | |
| 67 | <div className="mt-8 grid gap-4 sm:grid-cols-3"> |
| 68 | <PlanCard |
| 69 | name="free" |
| 70 | price="$0" |
| 71 | blurb="for kicking the tyres, side projects, and local dogfooding. no card required." |
| 72 | /> |
| 73 | <PlanCard |
| 74 | name="pro" |
| 75 | price="$29.99" |
| 76 | period="/ mo" |
| 77 | blurb="for a production app: bigger caps, email support, and the 99.5% uptime SLA." |
| 78 | highlight |
| 79 | /> |
| 80 | <PlanCard |
| 81 | name="team" |
| 82 | price="$99.99" |
| 83 | period="/ mo" |
| 84 | blurb="for teams shipping at scale: the largest caps, priority support, and a 99.9% SLA." |
| 85 | /> |
| 86 | </div> |
| 87 | |
| 88 | <section className="mt-12"> |
| 89 | <h2 className="font-mono text-lg tracking-tight">what each tier includes</h2> |
| 90 | <div className="mt-3 overflow-x-auto"> |
| 91 | <table className="w-full border-collapse font-mono text-xs"> |
| 92 | <thead> |
| 93 | <tr className="border-b border-[var(--color-border-subtle)] text-left text-[var(--color-text-muted)]"> |
| 94 | <th className="py-2 pr-4 font-normal">limit</th> |
| 95 | <th className="py-2 pr-4 font-normal">free</th> |
| 96 | <th className="py-2 pr-4 font-normal">pro</th> |
| 97 | <th className="py-2 pr-4 font-normal">team</th> |
| 98 | </tr> |
| 99 | </thead> |
| 100 | <tbody className="text-[var(--color-text)]"> |
| 101 | {CAPS.map((row) => ( |
| 102 | <tr key={row.label} className="border-b border-[var(--color-border-subtle)]"> |
| 103 | <td className="py-2 pr-4 text-[var(--color-text-muted)]">{row.label}</td> |
| 104 | <td className="py-2 pr-4">{row.free}</td> |
| 105 | <td className="py-2 pr-4">{row.pro}</td> |
| 106 | <td className="py-2 pr-4">{row.team}</td> |
| 107 | </tr> |
| 108 | ))} |
| 109 | </tbody> |
| 110 | </table> |
| 111 | </div> |
| 112 | <p className="mt-3 font-mono text-xs text-[var(--color-text-subtle)]"> |
| 113 | most caps are <em>soft</em> — they surface on the dashboard usage widget and feed |
| 114 | metered billing rather than hard-stopping your project. concurrent subscriptions and |
| 115 | the per-org project / function counts are hard caps, enforced at subscribe and deploy |
| 116 | time. invocations, storage, and connection-seconds bill as metered overage above the |
| 117 | included amount. |
| 118 | </p> |
| 119 | </section> |
| 120 | |
| 121 | <section className="mt-10"> |
| 122 | <h2 className="font-mono text-lg tracking-tight">how to upgrade</h2> |
| 123 | <p className="mt-2 font-mono text-sm text-[var(--color-text-muted)]"> |
| 124 | upgrades happen in the dashboard — there is no sales call and no invoice to chase. |
| 125 | open your project, go to the <strong>billing</strong> page, pick Pro or Team, and |
| 126 | you'll be taken to a hosted Polar checkout. when the subscription is active the |
| 127 | project's tier flips automatically (usually within seconds). manage payment |
| 128 | method, see invoices, or cancel any time from the same billing page's customer |
| 129 | portal link. |
| 130 | </p> |
| 131 | <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]"> |
| 132 | <Link |
| 133 | href="https://briven.tech" |
| 134 | className="text-[var(--color-text-link)] underline-offset-2 hover:underline" |
| 135 | > |
| 136 | open the dashboard → billing |
| 137 | </Link>{' '} |
| 138 | to upgrade. questions about a plan? reach us via{' '} |
| 139 | <Link |
| 140 | href="https://briven.tech/contact?topic=sales" |
| 141 | className="text-[var(--color-text-link)] underline-offset-2 hover:underline" |
| 142 | > |
| 143 | the contact form |
| 144 | </Link> |
| 145 | . |
| 146 | </p> |
| 147 | </section> |
| 148 | |
| 149 | <section className="mt-10"> |
| 150 | <h2 className="font-mono text-lg tracking-tight">tax + billing notes</h2> |
| 151 | <ul className="mt-3 flex list-disc flex-col gap-2 pl-5 font-mono text-sm text-[var(--color-text-muted)]"> |
| 152 | <li> |
| 153 | prices shown are exclusive of tax. EU customers see VAT added at checkout based on |
| 154 | their billing country; valid business VAT numbers can be entered in the Polar |
| 155 | checkout for reverse-charge where applicable. |
| 156 | </li> |
| 157 | <li> |
| 158 | you're billed monthly. metered overage (extra invocations, storage, |
| 159 | connection-seconds beyond the included amount) is reconciled on the following |
| 160 | invoice. |
| 161 | </li> |
| 162 | <li> |
| 163 | free-tier projects are best-effort with no SLA; see the{' '} |
| 164 | <Link |
| 165 | href="/sla" |
| 166 | className="text-[var(--color-text-link)] underline-offset-2 hover:underline" |
| 167 | > |
| 168 | service level agreement |
| 169 | </Link>{' '} |
| 170 | for the Pro / Team uptime commitments and credit schedule. |
| 171 | </li> |
| 172 | </ul> |
| 173 | </section> |
| 174 | </DocsShell> |
| 175 | ); |
| 176 | } |
| 177 | |
| 178 | function PlanCard({ |
| 179 | name, |
| 180 | price, |
| 181 | period, |
| 182 | blurb, |
| 183 | highlight, |
| 184 | }: { |
| 185 | name: string; |
| 186 | price: string; |
| 187 | period?: string; |
| 188 | blurb: string; |
| 189 | highlight?: boolean; |
| 190 | }) { |
| 191 | return ( |
| 192 | <div |
| 193 | className={`rounded-md border p-5 ${ |
| 194 | highlight |
| 195 | ? 'border-[var(--color-primary)] bg-[var(--color-surface)]' |
| 196 | : 'border-[var(--color-border-subtle)]' |
| 197 | }`} |
| 198 | > |
| 199 | <p className="font-mono text-xs uppercase tracking-[0.12em] text-[var(--color-text-subtle)]"> |
| 200 | {name} |
| 201 | </p> |
| 202 | <p className="mt-2 font-mono text-2xl tracking-tight text-[var(--color-text)]"> |
| 203 | {price} |
| 204 | {period ? ( |
| 205 | <span className="text-sm text-[var(--color-text-subtle)]">{period}</span> |
| 206 | ) : null} |
| 207 | </p> |
| 208 | <p className="mt-3 font-mono text-xs text-[var(--color-text-muted)]">{blurb}</p> |
| 209 | </div> |
| 210 | ); |
| 211 | } |