// delafolla — main app // React via global, JSX via Babel. const { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } = React; // ---------- Tweak defaults (host can rewrite on disk) ---------- const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "theme": "cream", "density": "comfortable", "asteriskSpeed": 12, "showCursorPreview": true } /*EDITMODE-END*/; // ---------- Asterisk (literal * glyph in Akira Expanded, blue) ---------- const Asterisk = ({ size = 40, color = "var(--blue)", spin = false, speed = 8, style = {} }) => ; // Alias used by some call sites. const Star = Asterisk; // ---------- Live clock (Madrid time) ---------- const useClock = () => { const [now, setNow] = useState(() => new Date()); useEffect(() => { const t = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(t); }, []); return now; }; const formatClock = (d) => { const tz = "Europe/Madrid"; const time = new Intl.DateTimeFormat("en-GB", { timeZone: tz, hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false }).format(d); return time; }; // ---------- Custom blue * cursor (follows mouse with smooth lerp) ---------- const CursorFollower = ({ mouse }) => { const ref = useRef(null); const posRef = useRef({ x: -1000, y: -1000 }); const [variant, setVariant] = useState("idle"); // idle | big | hidden const [onBlue, setOnBlue] = useState(false); useEffect(() => { let raf = 0; const tick = () => { const p = posRef.current; const dx = (mouse.x - p.x) * 0.32; const dy = (mouse.y - p.y) * 0.32; p.x += dx;p.y += dy; if (ref.current) ref.current.style.transform = `translate(${p.x}px, ${p.y}px) translate(-50%, -50%)`; raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [mouse.x, mouse.y]); useEffect(() => { const onOver = (e) => { const t = e.target; if (!t || !t.closest) return; setOnBlue(!!t.closest(".about")); // Over the list rows the project preview already follows the cursor — // hide the * cursor there so they don't overlap. if (t.closest(".row")) setVariant("hidden");else if (t.closest("button, a, .chip, [data-cursor='big']")) setVariant("big");else setVariant("idle"); }; document.addEventListener("pointerover", onOver); return () => document.removeEventListener("pointerover", onOver); }, []); return (