// ───────────── Journal page ───────────── const TWEAK_DEFAULTS_JOURNAL = /*EDITMODE-BEGIN*/{ "palette": "aurora", "grain": true }/*EDITMODE-END*/; const POSTS = [ { id: 1, cat: "Essay", title: "The case for a smaller agency.", excerpt: "Why we turned down 9 in 10 enquiries this year — and what that buys you as a client.", author: "Mira Rajan", date: "May 14, 2026", read: "6 min", featured: true, c1: "#1a2a4a", c2: "#0f1525" }, { id: 2, cat: "Craft", title: "On reading the brand brief twice.", excerpt: "The first read is for ego. The second is for craft. A short essay on what gets missed in week one.", author: "Theo Kassidis", date: "Apr 28, 2026", read: "4 min", c1: "#3a2418", c2: "#1f1411" }, { id: 3, cat: "Growth", title: "Paid social, lifecycle and the dollar that connects them.", excerpt: "Treating channels as one system, not many. A working model for unifying paid and CRM.", author: "Asha Tan", date: "Apr 12, 2026", read: "9 min", c1: "#0f2820", c2: "#0a1813" }, { id: 4, cat: "Studio", title: "What we read at the studio this quarter.", excerpt: "Twelve books, two podcasts and one bafflingly good newsletter. Our recommended reading list for Q1.", author: "Jules Kim", date: "Mar 30, 2026", read: "5 min", c1: "#2a1a3e", c2: "#160c22" }, { id: 5, cat: "Engineering", title: "How we shaved 800ms off MeinHaus.", excerpt: "Three boring decisions, two clever ones, and one we'd never make again.", author: "Sam Park", date: "Mar 18, 2026", read: "11 min", c1: "#3a1f1f", c2: "#1f0f0f" }, { id: 6, cat: "AI", title: "Practical AI in the studio (not theatre).", excerpt: "Where we use AI, where we don't, and what we wish people would stop pitching us.", author: "Mira Rajan", date: "Mar 04, 2026", read: "7 min", c1: "#1f3a2f", c2: "#0f1f17" }, { id: 7, cat: "Craft", title: "On grids that bend.", excerpt: "Type-driven grids, breakpoint-aware rhythm and the lie of mobile-first.", author: "Iris Romero", date: "Feb 17, 2026", read: "6 min", c1: "#0e1a30", c2: "#1a3a6a" }, { id: 8, cat: "Growth", title: "SEO in the age of the answer engine.", excerpt: "Less keyword stuffing, more authority — how we're rethinking content for a chatbot-first web.", author: "Asha Tan", date: "Feb 02, 2026", read: "10 min", c1: "#3a2a1a", c2: "#7a5a3a" }, { id: 9, cat: "Studio", title: "Notes from the Bangalore pod.", excerpt: "Three engineers, fifteen Slack threads and one shared Spotify playlist. A week in our BLR pod.", author: "Priya Singh", date: "Jan 22, 2026", read: "4 min", c1: "#2a1818", c2: "#5c2c2c" }, { id: 10, cat: "Engineering", title: "Why we still ship Webflow (sometimes).", excerpt: "A modest defense of the visual builder, written by someone who can read Next.js source.", author: "Theo Kassidis", date: "Jan 08, 2026", read: "8 min", c1: "#3e4e2a", c2: "#7a8f4d" }, ]; function Featured({ p, accent }) { return ( {/* abstract art */} {Array.from({ length: 30 }).map((_, i) => ( ))}
Featured {p.cat}

{p.title}

{p.author} · {p.date} · {p.read} read
); } function PostCard({ p, accent }) { return (
{Array.from({ length: 14 }).map((_, i) => { const cx = ((p.id * 50) + i * 31) % 400; const cy = ((p.id * 30) + i * 19) % 250; return ; })}
{p.cat}

{p.title}

{p.author} {p.date} · {p.read}
); } function JournalApp() { const t = useSiteTweaks(TWEAK_DEFAULTS_JOURNAL); const accent = PALETTES[t.palette] || PALETTES.aurora; const [cat, setCat] = useState("All"); const cats = ["All", "Essay", "Craft", "Growth", "Engineering", "AI", "Studio"]; const featured = POSTS.find(p => p.featured); const rest = POSTS.filter(p => !p.featured); const visible = cat === "All" ? rest : rest.filter(p => p.cat === cat); return (
All posts · {visible.length}
{cats.map(c => ( ))}
{visible.map((p, i) => ( ))}
Newsletter

One short note a month. No fluff.

Selected work, what we learned, and the occasional studio essay. Unsubscribe in one click.

e.preventDefault()} className="liquid-glass" style={{ borderRadius: 999, padding: "5px 5px 5px 22px", display: "flex", alignItems: "center", gap: 8, maxWidth: 460, margin: "0 auto" }}>
); } const journalRoot = ReactDOM.createRoot(document.getElementById("root")); journalRoot.render();