Ship /terms, /privacy, and /waitlist as structure, not finished pages

SAAS_PLAN.md B5/B6, narrowed: no Stripe/pricing/tier work of any kind this
pass (a mid-build correction from the business side landed while this was
in progress - the commercial model, SAAS_PLAN.md section 6.1, is still
undecided), so app/pricing and lib/billing.ts's PLANS are untouched here.
What's left of B5/B6 without that - real legal pages and a working
waitlist - still ships.

app/terms/page.tsx and app/privacy/page.tsx are section scaffolding, not
legal text. Every section is a TODO(legal) note describing what that
section needs to cover, and the page leads with a "Draft - not yet in
force" banner. This isn't caution for its own sake: DRB records, stores,
and transcribes public-safety radio traffic, and recording/rebroadcast
legality varies by state (SAAS_PLAN.md section 6.3) - an agent-generated
draft here would be actively wrong to publish, not just unpolished. Both
were pre-added to middleware.ts's PUBLIC_PATHS and ChromeSwitcher's
MARKETING_PATHS two commits ago; MarketingFooter now links both.

app/waitlist/page.tsx is a real, working form against the already-shipped
POST /waitlist - email + optional org name/note, no plan or price
mentioned anywhere on it, matching the backend route's own scope (rate
limited by source IP, not coupled to any tier). Linked from
MarketingFooter as "Request access," not from the pricing page - pricing
CTAs stay exactly as they were.

Typecheck: clean (tsc --noEmit via the WSL-native ~/drb-frontend copy).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-08-18 20:38:49 -04:00
co-authored by Claude Opus 5
parent 4842725a03
commit 1b4ed0d09c
5 changed files with 281 additions and 1 deletions
+89
View File
@@ -0,0 +1,89 @@
import Link from "next/link";
/**
* SAAS_PLAN.md B5: page structure only. The agent building this is
* explicitly instructed not to invent legal text — DRB records, stores, and
* transcribes public-safety radio traffic, and recording/rebroadcast
* legality varies by state (see SAAS_PLAN.md section 6.3), so this needs a
* human, and probably a lawyer, not a generated draft. Every section below
* is a placeholder marking what a real Terms of Service needs to cover, not
* actual terms — see the banner and every TODO(legal) marker.
*/
const SECTIONS: { heading: string; note: string }[] = [
{
heading: "1. Acceptance of terms",
note: "TODO(legal): standard acceptance clause — using the service means agreeing to these terms.",
},
{
heading: "2. What the service does and does not do",
note: "TODO(legal): describe the product (SDR ingestion, transcription, AI correlation, Discord relay) and, importantly, disclaim accuracy — AI-generated transcripts and incident summaries are not guaranteed accurate and must not be relied on as the sole source for dispatch or safety decisions.",
},
{
heading: "3. Radio recording and rebroadcast — the part that needs a lawyer",
note: "TODO(legal): this is the section that actually matters. Recording, storing, and rebroadcasting monitored radio traffic (including public-safety frequencies) has different legal treatment by state and by traffic type (encrypted vs. clear, dispatch vs. tactical). Needs jurisdiction-aware legal review before this product can be sold across state lines — do not ship this page live without it.",
},
{
heading: "4. Customer responsibilities and acceptable use",
note: "TODO(legal): who owns the hardware, who's responsible for lawful operation of the field node, prohibited uses.",
},
{
heading: "5. Data ownership and retention",
note: "TODO(legal): who owns the recorded audio/transcripts/incidents, how long they're kept, what happens on cancellation or account deletion. Note: no retention enforcement exists in the product yet either (see DEFERRED.md) — this section can't promise a retention window the system doesn't yet enforce.",
},
{
heading: "6. Payment, billing, and cancellation",
note: "TODO(legal): once a billing model and pricing exist (SAAS_PLAN.md section 6, still undecided) — refunds, proration, what happens to data on non-payment.",
},
{
heading: "7. Service availability and support",
note: "TODO(legal): whether any uptime/SLA commitment is made (today: none).",
},
{
heading: "8. Limitation of liability",
note: "TODO(legal): standard limitation-of-liability language, reviewed against the fact that this product touches public-safety-adjacent data.",
},
{
heading: "9. Changes to these terms",
note: "TODO(legal): how customers are notified of material changes.",
},
{
heading: "10. Governing law and contact",
note: "TODO(legal): governing jurisdiction, and a real company legal identity and contact address (SAAS_PLAN.md section 6.6 — not yet decided).",
},
];
export default function TermsPage() {
return (
<div className="max-w-screen-md mx-auto px-4 md:px-6 py-16 md:py-20">
<div className="bg-yellow-900/30 border border-yellow-700/50 rounded-lg px-4 py-3 mb-10">
<p className="text-yellow-200 text-sm font-mono font-semibold">
Draft — not yet in force
</p>
<p className="text-yellow-200/80 text-xs mt-1 leading-relaxed">
This page is a structural placeholder, not a real Terms of Service. Every section below is a{" "}
<code className="text-yellow-100">TODO(legal)</code> marker, not actual legal text. Nothing on this page is
binding, and no self-serve signup should be considered subject to it until an actual attorney-reviewed
version replaces this content.
</p>
</div>
<h1 className="text-display-sm text-white">Terms of Service</h1>
<p className="text-gray-500 text-sm mt-2 font-mono">Draft — last structured {new Date().getFullYear()}</p>
<div className="mt-10 space-y-8">
{SECTIONS.map((s) => (
<section key={s.heading}>
<h2 className="text-white font-semibold">{s.heading}</h2>
<p className="text-gray-500 text-sm mt-2 leading-relaxed italic">{s.note}</p>
</section>
))}
</div>
<p className="text-gray-600 text-xs font-mono mt-16">
Questions in the meantime? <Link href="/faq" className="text-indigo-400 hover:text-indigo-300 transition-colors">Check the FAQ</Link> or{" "}
<Link href="/login" className="text-indigo-400 hover:text-indigo-300 transition-colors">sign in to reach us directly</Link>.
</p>
</div>
);
}