diff --git a/drb-frontend/app/page.tsx b/drb-frontend/app/page.tsx index 4fb7b79..7d7102d 100644 --- a/drb-frontend/app/page.tsx +++ b/drb-frontend/app/page.tsx @@ -4,7 +4,6 @@ import Link from "next/link"; import { LinkButton } from "@/components/ui/Button"; import { Card } from "@/components/ui/Card"; import { Badge } from "@/components/ui/Badge"; -import { PLANS } from "@/lib/billing"; import { useAuth } from "@/components/AuthProvider"; import { LiveView } from "@/components/LiveView"; @@ -58,8 +57,8 @@ function MarketingHomePage() { bot to relay the audio live to your team.

- Get started - View pricing + Request access + Sign in
@@ -100,31 +99,21 @@ function MarketingHomePage() { - {/* Pricing teaser */} + {/* Pricing — Gate A (minutes #42/#62): no price on a public surface. */}
-
+
-

Plans for one node or a whole region

-

Start free. Upgrade when you add nodes or need longer retention.

+

Pricing is in development

+

+ We would rather talk to you about what you need than post a number we would have to + walk back. Tell us about your coverage area and we will figure it out together. +

- See full plan comparison → + More on pricing →
-
- {PLANS.map((plan) => ( - - {plan.highlighted && Most popular} -

{plan.name}

-

{plan.tagline}

-

- {plan.priceMonthlyUsd === null ? "Custom" : plan.priceMonthlyUsd === 0 ? "Free" : `$${plan.priceMonthlyUsd}`} - {plan.priceMonthlyUsd !== null && plan.priceMonthlyUsd > 0 && /mo} -

-
- ))} -
@@ -133,10 +122,10 @@ function MarketingHomePage() {

Bring your first node online

- Sign in to create an account, add a node, and start seeing incidents within minutes of your first call. + Tell us about your coverage area. We will get you a node online and you will see incidents within minutes of your first call.

- Get started + Request access
diff --git a/drb-frontend/app/pricing/page.tsx b/drb-frontend/app/pricing/page.tsx index 0f7a3d4..efa40a7 100644 --- a/drb-frontend/app/pricing/page.tsx +++ b/drb-frontend/app/pricing/page.tsx @@ -1,99 +1,45 @@ -"use client"; - -import { useState } from "react"; import Link from "next/link"; -import { PLANS, type BillingInterval } from "@/lib/billing"; import { Card } from "@/components/ui/Card"; -import { Badge } from "@/components/ui/Badge"; import { LinkButton } from "@/components/ui/Button"; -function CheckIcon() { - return ( - - - - ); -} - +/** + * Gate A (BUSINESS_MODEL.md, board minutes #42, enforced by minutes #62): + * no price may appear on a public surface until the pricing model is ratified + * and the entitlements behind it exist. The previous version of this page + * rendered the invented $0/$79/Custom catalog from lib/billing.ts with a + * below-the-fold disclaimer — a false price anchor with a footnote is worse + * than no price. Do not re-import PLANS here. Tracked: server-26#46. + */ export default function PricingPage() { - const [interval, setInterval] = useState("monthly"); - return (
-

Simple, node-based pricing

+

Pricing is in development

- Every plan includes the full incident pipeline — transcription, correlation, mapping, and the Discord relay. - Plans differ in how many nodes and seats you get, and how far back your history goes. + We are still working out what a fair price looks like for the people who actually use this. + Rather than post a number we would have to walk back, we would rather talk to you about what + you need and what it is worth.

- {/* Interval toggle */} -
- {(["monthly", "annual"] as BillingInterval[]).map((i) => ( - - ))} -
- - {/* Plan cards */} -
- {PLANS.map((plan) => { - const price = interval === "annual" ? plan.priceAnnualUsd : plan.priceMonthlyUsd; - const priceLabel = - price === null ? "Custom" : price === 0 ? "Free" : `$${interval === "annual" ? Math.round(price / 12) : price}`; - - return ( - - {plan.highlighted && Most popular} -

{plan.name}

-

{plan.tagline}

- -
- {priceLabel} - {price !== null && price > 0 && /mo} - {interval === "annual" && price !== null && price > 0 && ( -

billed ${plan.priceAnnualUsd}/year

- )} -
- -
- - {plan.priceMonthlyUsd === null ? "Contact sales" : "Get started"} - -
- -
    - {plan.features.map((f) => ( -
  • - - {f} -
  • - ))} -
-
- ); - })} -
- -

- Prices shown are sample figures for this demo build — nothing here is connected to a live payment processor. -

+ +

What you get today

+

+ The full incident pipeline — transcription, correlation into incidents, mapping, and the + Discord relay. Node counts, seats, and history length are set per account while we work out + the plan structure. +

+
+ Request access +
+

- Questions about a plan?{" "} + Questions?{" "} Check the FAQ {" "}or{" "} - sign in to talk to us. + get in touch.

diff --git a/drb-frontend/lib/billing.ts b/drb-frontend/lib/billing.ts index 8a10539..4b8db18 100644 --- a/drb-frontend/lib/billing.ts +++ b/drb-frontend/lib/billing.ts @@ -72,10 +72,26 @@ export interface UsageSummary { } // --------------------------------------------------------------------------- -// Plan catalog — this is real UI copy (safe to ship), just not wired to a -// live pricing table. In a real integration this would likely be fetched -// from the processor (Stripe Prices API) instead of hardcoded here so price -// changes don't require a frontend deploy. +// Plan catalog — NOT SAFE TO SHIP. Do not put these on a public surface. +// +// These prices are INVENTED. BUSINESS_MODEL.md §3.1 (ratified in structure by +// board minutes #42, 2026-08-23) marks $0/$79/custom as superseded, and the +// board ruled that pricing comes OFF the public site entirely — replaced with +// "Pricing in development — contact us" — rather than kept behind a +// below-the-fold disclaimer. A false price anchor with a footnote is worse +// than no price. Tracked in server-26#46 (Gate A, due 2026-09-13). +// +// Also unbacked by any implementation, and each is its own claim-vs-reality +// gap if displayed: +// - retentionDays 7/90/365 — no TTL and no deletion sweep exists anywhere +// in drb-c2-core. server-26#44 (Gate B4). +// - SSO/SAML, uptime SLA, custom data residency — no backend at all. +// There are also zero backend billing routes; createCheckoutSession() and +// createBillingPortalSession() always throw. See ADMIN_BILLING_AUDIT.md §2. +// +// If pricing is ever wired for real, fetch it from the processor (Stripe +// Prices API) rather than hardcoding here, so price changes don't require a +// frontend deploy. // --------------------------------------------------------------------------- export const PLANS: PlanDefinition[] = [