// Shared shell for the public, login-free legal pages (/privacy, /terms). These must be // reachable unauthenticated — Google's OAuth consent screen links to them and reviewers // fetch them directly — so they render outside the authenticated App tree (see main.tsx), // with no react-query provider: the operator contact is fetched with a plain fetch below. import { useEffect, useState } from "react"; const LAST_UPDATED = "June 14, 2026"; // Contact shown on the legal pages: the instance operator's configured admin email, from the // public /auth/config. One shared fetch across the page; null when the operator set none. let contactPromise: Promise | null = null; function fetchOperatorContact(): Promise { if (!contactPromise) { contactPromise = fetch("/auth/config") .then((r) => (r.ok ? r.json() : null)) .then((c) => (c?.operator_contact as string | undefined) ?? null) .catch(() => null); } return contactPromise; } function useOperatorContact(): string | null { const [contact, setContact] = useState(null); useEffect(() => { let alive = true; fetchOperatorContact().then((c) => alive && setContact(c)); return () => { alive = false; }; }, []); return contact; } // The operator's contact rendered as a mailto link, or a neutral fallback phrase when unset. export function ContactEmail() { const email = useOperatorContact(); if (!email) return <>your instance's administrator; return ( {email} ); } export function LegalLink({ href, children }: { href: string; children: React.ReactNode }) { return ( {children} ); } export default function LegalLayout({ title, children, }: { title: string; children: React.ReactNode; }) { const contact = useOperatorContact(); return (
Siftlode

{title}

Last updated: {LAST_UPDATED}

{children}
); } export function H2({ children }: { children: React.ReactNode }) { return

{children}

; }