import { useEffect, useState } from "react"; import Nav from "../components/Nav"; import Footer from "../components/Footer"; import NeuralBoot from "../components/NeuralBoot"; import SignupForm from "../components/SignupForm"; import LiveCounter from "../components/LiveCounter"; import USMap from "../components/USMap"; import { fetchCount } from "../lib/signup"; import { useStats } from "../lib/useStats"; const BEATS = [ { id: "about", idx: "01 / what it is", big: "Local AI is the next personal computer.", body: ( <> Not a chatbot account. Not a rented API. A model you can run on your own machine, inspect, repair, improve, and use without asking a platform to stay online. That is the line RTI exists to protect. ), img: "/beats/beat-01-local.webp", alt: "A laptop running a small AI model locally", imgMax: 505, }, { id: "principles", idx: "02 / what we want", big: "Protect lawful use. Enforce real harm.", body: ( <> People should be free to download, own, run, study, modify, and share open AI models. Fraud, cybercrime, CSAM, harassment, nonconsensual intimate deepfakes, discrimination, and sabotage should stay illegal and be enforced seriously. The red line is requiring a license just to own or run the tool. ), img: "/beats/beat-02-protect.webp", alt: "A shield protecting a growing sprout", }, { id: "local-first", idx: "03 / local first", big: "Use the computer you already own.", body: ( <> Not every AI task needs a warehouse of GPUs. For many everyday tasks, a small open model can run on a laptop, desktop, or phone people already have. Local AI will not replace every cloud model or training run, but the law should not force simple, lawful workloads back into the cloud when the task fits the device. ), img: "/beats/beat-03-devices.webp", alt: "A phone and laptop running models without the cloud", imgMax: 505, }, ]; // Mobile hero boot: its own full-strength stage under the CTA (workbench v2). // lean: 0 skips pointer physics so taps never warp it. fill leaves ~50px of // headroom around the boot inside the canvas — the breath (±14px) and scroll // jello (±34px) excursions stay inside the render area instead of clipping. const MOBILE_BOOT_OPTS = { wallMin: 0, wallGap: 12, cutoff: 0.02, fill: 0.67, nodeScale: 0.62, lean: 0, }; function useIsMobile() { const [mobile, setMobile] = useState( () => window.matchMedia("(max-width: 820px)").matches, ); useEffect(() => { const mq = window.matchMedia("(max-width: 820px)"); const on = (e: MediaQueryListEvent) => setMobile(e.matches); mq.addEventListener("change", on); return () => mq.removeEventListener("change", on); }, []); return mobile; } // Compact US-focused live stats for the endbeat: American signatures, calls, // and active states — the domestic fight, next to the state map. function EndStats() { const stats = useStats(); if (!stats) return null; let signups = 0; let calls = 0; let active = 0; for (const s of Object.values(stats.states)) { signups += s.signups; calls += s.calls; if (s.signups + s.calls > 0) active += 1; } return (
{signups.toLocaleString("en-US")} US signatures
{calls.toLocaleString("en-US")} calls logged
{active} states active
); } export default function Home() { const isMobile = useIsMobile(); const [total, setTotal] = useState(null); useEffect(() => { let alive = true; fetchCount().then((n) => { if (alive && n !== null) setTotal(n); }); // Fresh totals broadcast by the signup modal (which lives at App level). const onTotal = (e: Event) => { const n = (e as CustomEvent).detail; if (alive && typeof n === "number") setTotal(n); }; window.addEventListener("rti:total", onTotal); return () => { alive = false; window.removeEventListener("rti:total", onTotal); }; }, []); return ( <>