UI_REDESIGN chunk 4 was never finished: 39 app/component files still used raw Tailwind greys, kept readable in light mode only by the !important override sheet. In dark mode text-gray-600 is ~2.3:1 on surface, and the light override maps it to #94a3b8 (~2.6:1 on white) — unreadable in both. Mechanical mapping (886 classes): gray-100/200 -> ink, 300/400 -> ink-2, 500/600/700 -> ink-muted, bg 950/900/800 -> page/surface/raised, border 800 -> line, 700/600 -> line-strong, plus hover/divide/placeholder. Alpha suffixes kept (tokens are color-mix, so /60 works). bg-gray-700, text-white and marketing pages left alone. text-[9px] -> text-[10px]. Frontend unverified: no Node on the authoring box (#207). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
4.7 KiB
TypeScript
84 lines
4.7 KiB
TypeScript
import Link from "next/link";
|
|
|
|
/**
|
|
* SAAS_PLAN.md B5: page structure only — see app/terms/page.tsx for why the
|
|
* agent building this did not write real legal text. Privacy Policy needs
|
|
* the same jurisdiction-aware legal review as Terms, plus specifics this
|
|
* agent cannot respond for on the owner's behalf: what a real DPA/CCPA/GDPR
|
|
* posture looks like, and what third-party processors (OpenAI, Gemini,
|
|
* Google Maps, Firebase/GCP, Stripe once chosen) actually receive and why.
|
|
*/
|
|
|
|
const SECTIONS: { heading: string; note: string }[] = [
|
|
{
|
|
heading: "1. What data this collects",
|
|
note: "TODO(legal): account data (email, org membership), field node telemetry (location, status), radio call audio and AI-generated transcripts/entities/incident data, and usage/session logs (drb-c2-core's audit_log and user_sessions collections already exist and hold some of this today).",
|
|
},
|
|
{
|
|
heading: "2. Third parties this data is sent to, and why",
|
|
note: "TODO(legal): OpenAI (Whisper transcription), Google Gemini (incident extraction/summarization/embeddings), Google Maps (geocoding location strings extracted from transcripts), Google Cloud (Firestore + GCS storage, Firebase Auth), and — once a payment processor is chosen (SAAS_PLAN.md section 6.5, not yet decided) — that processor. Each of these is a real, already-integrated dependency, not a hypothetical one; this section needs to name them accurately, not generically.",
|
|
},
|
|
{
|
|
heading: "3. Recorded radio traffic specifically",
|
|
note: "TODO(legal): this product's core function is recording, transcribing, and storing monitored radio audio — including public-safety traffic that may name individuals, locations, and in-progress incidents. This needs explicit treatment distinct from generic 'we collect usage data' privacy boilerplate, and needs to be read alongside the same legal review flagged in Terms section 3.",
|
|
},
|
|
{
|
|
heading: "4. How long data is kept",
|
|
note: "TODO(legal): no retention enforcement exists in the product yet (no TTL, no sweep, no deletion job — see DEFERRED.md) — this section cannot promise a retention/deletion window the system doesn't actually implement.",
|
|
},
|
|
{
|
|
heading: "5. Customer and end-user rights",
|
|
note: "TODO(legal): access/export/deletion requests, and who they're directed to — org owner vs. platform operator.",
|
|
},
|
|
{
|
|
heading: "6. Cookies and session data",
|
|
note: "TODO(legal): drb_session is a client-set, non-httpOnly cookie used only for UI redirect logic (not an auth boundary — see CLAUDE.md); Firebase Auth sets its own session storage. No analytics/tracking cookies are set today.",
|
|
},
|
|
{
|
|
heading: "7. Security practices",
|
|
note: "TODO(legal): at a level appropriate for public disclosure — Firestore security rules, per-node credentials, encrypted transport. Should be reviewed against SAAS_PLAN.md's actual findings before publishing any specific claim.",
|
|
},
|
|
{
|
|
heading: "8. Changes to this policy",
|
|
note: "TODO(legal): how customers are notified.",
|
|
},
|
|
{
|
|
heading: "9. Contact",
|
|
note: "TODO(legal): real company legal identity and contact address — not yet decided (SAAS_PLAN.md section 6.6).",
|
|
},
|
|
];
|
|
|
|
export default function PrivacyPage() {
|
|
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 Privacy Policy. Every section below is a{" "}
|
|
<code className="text-yellow-100">TODO(legal)</code> marker, not actual legal text. Nothing on this page
|
|
describes a binding commitment about how data is handled.
|
|
</p>
|
|
</div>
|
|
|
|
<h1 className="text-display-sm text-white">Privacy Policy</h1>
|
|
<p className="text-ink-muted 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-ink-muted text-sm mt-2 leading-relaxed italic">{s.note}</p>
|
|
</section>
|
|
))}
|
|
</div>
|
|
|
|
<p className="text-ink-muted 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>
|
|
);
|
|
}
|