// Shared UI primitives + tweak defaults. Requires React + tweaks-panel.jsx. const { useState, useEffect, useRef, useMemo } = React; window.SHARED_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "cyan", "motion": "subtle", "grain": true }/*EDITMODE-END*/; window.useTime = function useTime() { const [now, setNow] = useState(() => new Date()); useEffect(() => { const id = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(id); }, []); return now; }; window.useReveal = function useReveal() { const ref = useRef(null); const [shown, setShown] = useState(false); const [armed, setArmed] = useState(false); useEffect(() => { if (!ref.current) return; const el = ref.current; if (typeof IntersectionObserver === "undefined") { setShown(true); return; } setArmed(true); let done = false; const reveal = () => { if (!done) { done = true; setShown(true); } }; const io = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { reveal(); io.disconnect(); } }, { threshold: 0.05, rootMargin: "0px 0px -10px 0px" } ); io.observe(el); requestAnimationFrame(() => { const r = el.getBoundingClientRect(); if (r.top < window.innerHeight && r.bottom > 0) reveal(); }); const t = setTimeout(reveal, 1200); return () => { io.disconnect(); clearTimeout(t); }; }, []); return [ref, shown, armed]; }; window.Reveal = function Reveal({ children, delay = 0, className = "", as: Tag = "div" }) { const [ref, shown, armed] = window.useReveal(); const cls = ["reveal", armed ? "is-armed" : "", shown ? "is-shown" : "", className].filter(Boolean).join(" "); return {children}; }; window.Caret = function Caret() { return