/* ============================================================
   COMMONS — app shell, router, page transitions
   ============================================================ */

function Footer({ go }) {
  return (
    <footer className="footer">
      <div className="shell footer-inner">
        <div className="footer-brand">
          <Logo onClick={() => go("home")} />
          <p>A warm, well-made commons for the global Punjabi diaspora.</p>
        </div>
        <div className="footer-cols">
          <div>
            <span className="kicker">Explore</span>
            <a onClick={() => go("home")}>Home</a>
            <a onClick={() => go("community")}>Community</a>
            <a onClick={() => go("read")}>Stories</a>
          </div>
          <div>
            <span className="kicker">Create</span>
            <a onClick={() => go("community")}>Start a thread</a>
            <a onClick={() => go("write")}>Write a story</a>
          </div>
          <div>
            <span className="kicker">iPanjab</span>
            <a>About</a>
            <a>Guidelines</a>
            <a>Contact</a>
          </div>
        </div>
      </div>
      <div className="shell footer-bottom">
        <span>© 2026 iPanjab</span>
        <span>Made for the diaspora, by the diaspora.</span>
      </div>
    </footer>
  );
}

/* ---------- Home: 3-column landing (profile · mixed feed · communities) ---------- */
const HOME_CSS = `
.home-grid{display:grid;grid-template-columns:240px 1fr 280px;gap:22px;align-items:start;padding:24px 0 64px}
.home-left,.home-right{position:sticky;top:88px;display:grid;gap:14px}
.home-profile{padding:20px;display:grid;justify-items:center;text-align:center;gap:2px}
.home-pf-name{font-size:18px;display:inline-flex;align-items:center;gap:4px;margin-top:8px}
.home-pf-handle{font-size:13px;color:var(--ink-faint)}
.home-pf-stats{display:flex;gap:14px;margin:12px 0 2px;font-size:12.5px;color:var(--ink-soft)}
.home-pf-stats b{color:var(--ink)}
.home-pf-view{margin-top:10px}
.home-nav{padding:8px;display:grid;gap:2px}
.home-nav-item{display:flex;align-items:center;gap:10px;text-align:left;padding:10px 12px;border-radius:9px;font-weight:600;font-size:14.5px;color:var(--ink-soft);background:none;width:100%}
.home-nav-item:hover{background:var(--paper-2);color:var(--ink)}
.home-nav-item[data-on="true"]{background:var(--accent-soft);color:var(--ink)}
.home-feed{display:grid;gap:14px}
.home-compose{display:flex;align-items:center;gap:10px;padding:12px 14px}
.home-compose-input{flex:1;text-align:left;background:var(--paper);border:1px solid var(--line);border-radius:999px;padding:10px 16px;color:var(--ink-faint);font:inherit;font-size:14px;cursor:pointer}
.home-list{display:grid;gap:14px}
.home-story{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-sm);padding:20px;cursor:pointer;animation:rise .5s var(--ease) both;transition:box-shadow .3s,transform .3s var(--ease)}
.home-story:hover{box-shadow:var(--shadow-md);transform:translateY(-2px)}
.home-story:hover .art-title{color:var(--accent)}
.home-story .home-tag{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--accent-deep);background:var(--accent-soft);padding:3px 8px;border-radius:6px;margin-left:6px}
.home-composer{padding:14px 16px;display:grid;gap:10px}
.hc-top{display:flex;align-items:center;gap:12px}
.hc-tabs{display:inline-flex;gap:2px;background:var(--paper-2);border-radius:999px;padding:3px}
.hc-tabs button{padding:6px 14px;border-radius:999px;font-weight:600;font-size:13.5px;color:var(--ink-soft);background:none}
.hc-tabs button[data-on="true"]{background:var(--card);color:var(--accent);box-shadow:var(--shadow-sm)}
.hc-title{border:none;background:none;outline:none;font:inherit;font-size:18px;font-weight:600;font-family:var(--serif);color:var(--ink);padding:2px 0}
.hc-input{border:none;background:none;outline:none;resize:none;font:inherit;font-size:17px;color:var(--ink);line-height:1.5;min-height:46px}
.hc-input::placeholder,.hc-title::placeholder{color:var(--ink-faint)}
.hc-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;border-top:1px solid var(--line);padding-top:10px}
.hc-sub{appearance:none;-webkit-appearance:none;background:var(--paper);border:1px solid var(--line);border-radius:999px;padding:6px 14px;font:inherit;font-size:13px;font-weight:600;color:var(--accent);cursor:pointer}
.hc-hint{font-size:12.5px;color:var(--ink-faint)}
.hc-post{padding:9px 24px}
@media(max-width:1040px){.home-grid{grid-template-columns:1fr 280px}.home-left{display:none}}
@media(max-width:760px){.home-grid{grid-template-columns:1fr}.home-right{display:none}.home-compose .home-soft-hide{display:none}}
`;

function Home({ go, posts }) {
  const me = (typeof window !== "undefined" && window.__USER__) || null;
  const D = (typeof window !== "undefined" && window.__DATA__) || {};
  const people = D.people || [];
  const myComms = D.myComms || [];
  const comms = (typeof COMMUNITIES !== "undefined" ? COMMUNITIES : []);
  const arts = (typeof ARTICLES !== "undefined" ? ARTICLES : []);
  const threads = posts || (typeof POSTS !== "undefined" ? POSTS : []);
  const meP = me ? (people.find(p => String(p.id) === String(me.id)) || null) : null;

  const [cmode, setCmode] = useState("post");
  const [ctext, setCtext] = useState("");
  const [ctitle, setCtitle] = useState("");
  const [csub, setCsub] = useState((comms[0] && comms[0].name) || "askpunjab");
  const [cbusy, setCbusy] = useState(false);
  const composerPublish = async () => {
    if (cbusy) return;
    if (!me) { document.dispatchEvent(new CustomEvent("ipanjab:auth")); return; }
    if (cmode === "story") {
      if (!ctitle.trim() || !ctext.trim()) return;
      setCbusy(true);
      try {
        const r = await fetch("/api/articles", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: ctitle.trim(), tag: "Story", body: ctext.trim() }) });
        if (r.ok) { location.hash = "/read"; location.reload(); } else { setCbusy(false); }
      } catch (_) { setCbusy(false); }
    } else {
      if (!ctext.trim()) return;
      setCbusy(true);
      const txt = ctext.trim();
      try {
        const r = await fetch("/api/threads", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sub: csub, title: txt.length > 140 ? txt.slice(0, 140) + "…" : txt, body: txt.length > 140 ? txt : "" }) });
        if (r.ok) { location.hash = "/community"; location.reload(); } else { setCbusy(false); }
      } catch (_) { setCbusy(false); }
    }
  };

  const feed = [];
  let i = 0, j = 0;
  while (i < threads.length || j < arts.length) {
    if (j < arts.length) feed.push({ t: "story", x: arts[j++] });
    if (i < threads.length) feed.push({ t: "post", x: threads[i++] });
  }

  const navItem = (id, label) => (
    <button className="home-nav-item" data-on={false} onClick={() => go(id)}>{label}</button>
  );

  return (
    <div className="view home">
      <style>{HOME_CSS}</style>
      <div className="shell home-grid">

        <aside className="home-left">
          {me ? (
            <div className="surface home-profile">
              <Avatar seed={me.email} label={me.name} size={64} />
              <span className="home-pf-name serif"><b>{me.name}</b>{vTick(me.name)}</span>
              <span className="home-pf-handle">{(meP && meP.handle) ? meP.handle : ("@" + String(me.email || "you").split("@")[0])}</span>
              <div className="home-pf-stats">
                <span><b>{meP ? meP.posts : 0}</b> posts</span>
                <span><b>{meP ? meP.articles : 0}</b> stories</span>
                <span><b>{meP ? meP.followers : 0}</b> followers</span>
              </div>
              <button className="btn btn-soft home-pf-view" onClick={() => go("profile")}>View profile</button>
            </div>
          ) : (
            <div className="surface home-profile">
              <Avatar seed="guest" label="iP" size={64} />
              <span className="home-pf-name serif"><b>Join iPanjab</b></span>
              <span className="home-pf-handle">Sign in to post, write and connect.</span>
              <button className="btn btn-primary home-pf-view" onClick={() => document.dispatchEvent(new CustomEvent("ipanjab:auth"))}>Log in</button>
            </div>
          )}
          <nav className="surface home-nav">
            {navItem("home", "Home")}
            {navItem("community", "Community")}
            {navItem("read", "Stories")}
            {me && navItem("inbox", "Messages")}
            {me && navItem("profile", "My profile")}
          </nav>
        </aside>

        <main className="home-feed">
          <div className="surface home-composer">
            <div className="hc-top">
              <Avatar seed={me ? me.email : "you"} label={me ? me.name : "You"} size={42} />
              <div className="hc-tabs">
                <button data-on={cmode === "post"} onClick={() => setCmode("post")}>Post</button>
                <button data-on={cmode === "story"} onClick={() => setCmode("story")}>Story</button>
                <button onClick={() => go("community")}>Community</button>
              </div>
            </div>
            {cmode === "story" && <input className="hc-title" placeholder="Story title" value={ctitle} onChange={e => setCtitle(e.target.value)} />}
            <textarea className="hc-input" rows={cmode === "story" ? 4 : 2} placeholder="What's happening, Punjab?" value={ctext} onChange={e => setCtext(e.target.value)} />
            <div className="hc-foot">
              {cmode === "post"
                ? <select className="hc-sub" value={csub} onChange={e => setCsub(e.target.value)}>{comms.map(c => <option key={c.id} value={c.name}>{c.name}</option>)}</select>
                : <span className="hc-hint">Publishing to Stories</span>}
              <button className="btn btn-primary hc-post" data-dim={cbusy || !ctext.trim() || (cmode === "story" && !ctitle.trim())} onClick={composerPublish}>{cbusy ? "Posting…" : (cmode === "story" ? "Publish" : "Post")}</button>
            </div>
          </div>

          <div className="home-list">
            {feed.map((f, idx) => f.t === "post"
              ? <PostRow key={"p" + f.x.id} post={f.x} i={idx} go={go} onOpen={(id) => go("community/" + id)} />
              : (
                <article key={"a" + f.x.id} className="home-story" style={{ "--i": idx }} onClick={() => go("read/" + f.x.id)}>
                  <span className="kicker">{f.x.kicker}<span className="home-tag">Story</span></span>
                  <h3 className="art-title serif" style={{ marginTop: 8 }}>{f.x.title}</h3>
                  <p className="art-dek">{f.x.dek}</p>
                  <div className="byline sm" style={{ marginTop: 6 }}><Avatar seed={f.x.authorU} label={f.x.author} size={24} /><span><b>{f.x.author}</b><i>{f.x.date} · {f.x.read} read</i></span></div>
                </article>
              )
            )}
          </div>
        </main>

        <aside className="home-right">
          <div className="surface side-card">
            <span className="kicker">Communities</span>
            <ul className="comm-list">
              {comms.map(c => (
                <li key={c.id}>
                  <button className="comm-item" onClick={() => go("c/" + c.name)}>
                    <span className="comm-badge" style={{ background: c.color }}>{c.name[0]}</span>
                    <span className="comm-item-txt">
                      <b>{c.name}</b>
                      <span>{c.members} {Number(c.members) === 1 ? "member" : "members"}{myComms.indexOf(c.name) >= 0 ? " · Joined" : ""}</span>
                    </span>
                    <Icon name="arrowRight" size={15} className="comm-item-go" />
                  </button>
                </li>
              ))}
            </ul>
          </div>
          <div className="surface side-card side-cta">
            <Icon name="users" size={22} />
            <b className="serif">Start a community</b>
            <p>Got a niche? Gather the people who care about it.</p>
            <button className="btn btn-soft" onClick={() => go("compose")}>Create one</button>
          </div>
        </aside>

      </div>
    </div>
  );
}

function NotificationsPage({ go }) {
  const src = (typeof window !== "undefined" && window.__DATA__ && window.__DATA__.notifs) || [];
  const [items] = useState(() => src.map(n => ({ ...n })));
  useEffect(() => {
    fetch("/api/notifications/read", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" }).catch(() => {});
  }, []);
  const open = (n) => {
    if (n.id) fetch("/api/notifications/read", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: n.id }) }).catch(() => {});
    go(n.link || "community");
  };
  return (
    <div className="view notif-page">
      <div className="shell notif-shell">
        <span className="kicker">iPanjab</span>
        <h1 className="serif notif-page-title">Notifications</h1>
        {items.length === 0 ? (
          <div className="pf-empty">
            <Icon name="bell" size={28} />
            <b className="serif">You are all caught up</b>
            <p>When people reply to your posts or comment on your stories, you will see it here.</p>
            <button className="btn btn-primary" onClick={() => go("community")}>Go to community</button>
          </div>
        ) : (
          <div className="notif-page-list">
            {items.map((n, i) => (
              <button key={n.id || i} className="notif-row" data-unread={n.unread} style={{ "--i": i }} onClick={() => open(n)}>
                <Avatar seed={n.seed} label={n.who} size={42} />
                <div className="notif-row-main">
                  <p><b>{n.who}</b> {n.txt}</p>
                  <span>{n.time}</span>
                </div>
                {n.unread && <span className="notif-row-dot" />}
              </button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function PeoplePage({ go }) {
  const all = (typeof window !== "undefined" && window.__DATA__ && window.__DATA__.people) || [];
  const [q, setQ] = useState("");
  const list = all.filter(p => !q || (p.name || "").toLowerCase().includes(q.toLowerCase()) || (p.handle || "").toLowerCase().includes(q.toLowerCase()));
  return (
    <div className="view people-page">
      <section className="comm-banner">
        <div className="shell comm-banner-inner">
          <div>
            <span className="kicker" style={{ "--i": 0 }}>iPanjab Members</span>
            <h1 className="comm-title serif" style={{ "--i": 1 }}>Find your people.</h1>
          </div>
        </div>
      </section>
      <div className="shell people-wrap">
        <div className="people-search">
          <Icon name="search" size={18} />
          <input placeholder="Search members by name or handle" value={q} onChange={e => setQ(e.target.value)} />
        </div>
        {list.length === 0 ? (
          <div className="empty">No members found.</div>
        ) : (
          <div className="people-grid">
            {list.map((p, i) => (
              <button key={p.id} className="person-card" style={{ "--i": i }} onClick={() => go("profile/u" + p.id)}>
                <Avatar seed={p.handle} label={p.name} size={52} />
                <div className="person-main">
                  <b>{p.name}</b>
                  <span className="person-handle">{p.handle}</span>
                  <span className="person-meta">{p.posts} posts, {p.articles} stories, {p.joined}</span>
                </div>
                <Icon name="arrowRight" size={16} className="person-go" />
              </button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function SearchPage({ go }) {
  const D = (typeof window !== "undefined" && window.__DATA__) || {};
  const me = (typeof window !== "undefined" && window.__USER__) || null;
  const people = D.people || [], posts = D.posts || [], arts = D.articles || [];
  const [q, setQ] = useState("");
  const ql = q.trim().toLowerCase();
  const match = (s) => (s || "").toLowerCase().includes(ql);
  const ppl = (ql && me) ? people.filter(p => match(p.name) || match(p.handle)).slice(0, 10) : [];
  const pst = ql ? posts.filter(p => match(p.title) || match(p.body) || match(p.sub)).slice(0, 10) : [];
  const sty = ql ? arts.filter(a => match(a.title) || match(a.dek) || match(a.author)).slice(0, 10) : [];
  const coms = ql ? (D.communities || []).filter(c => match(c.name) || match(c.desc)).slice(0, 8) : [];
  const total = ppl.length + pst.length + sty.length + coms.length;
  return (
    <div className="view search-page">
      <div className="shell search-shell">
        <span className="kicker">iPanjab</span>
        <h1 className="serif notif-page-title">Search</h1>
        <div className="people-search">
          <Icon name="search" size={18} />
          <input autoFocus placeholder="Search people, posts and stories" value={q} onChange={e => setQ(e.target.value)} />
        </div>
        {!ql && <div className="empty">Start typing to search across members, posts and stories.</div>}
        {ql && total === 0 && <div className="empty">No results for that search.</div>}
        {coms.length > 0 && (
          <div className="search-sec">
            <span className="kicker">Communities</span>
            <div className="search-list">
              {coms.map(c => (
                <button key={c.id} className="search-row" onClick={() => go("c/" + c.name)}>
                  <span className="comm-badge" style={{ background: c.color }}>{c.name[0]}</span>
                  <div className="search-row-main"><b>{c.name}</b><span>{c.members} members - {c.desc}</span></div>
                </button>
              ))}
            </div>
          </div>
        )}
        {ppl.length > 0 && (
          <div className="search-sec">
            <span className="kicker">People</span>
            <div className="people-grid">
              {ppl.map(p => (
                <button key={p.id} className="person-card" onClick={() => go("profile/u" + p.id)}>
                  <Avatar seed={p.handle} label={p.name} size={46} />
                  <div className="person-main"><b>{p.name}</b><span className="person-handle">{p.handle}</span></div>
                  <Icon name="arrowRight" size={16} className="person-go" />
                </button>
              ))}
            </div>
          </div>
        )}
        {pst.length > 0 && (
          <div className="search-sec">
            <span className="kicker">Posts</span>
            <div className="search-list">
              {pst.map(p => (
                <button key={p.id} className="search-row" onClick={() => go("community/" + p.id)}>
                  <Icon name="chat" size={17} />
                  <div className="search-row-main"><b>{p.title}</b><span>{p.sub} - {p.author}</span></div>
                </button>
              ))}
            </div>
          </div>
        )}
        {sty.length > 0 && (
          <div className="search-sec">
            <span className="kicker">Stories</span>
            <div className="search-list">
              {sty.map(a => (
                <button key={a.id} className="search-row" onClick={() => go("read/" + a.id)}>
                  <Icon name="book" size={17} />
                  <div className="search-row-main"><b>{a.title}</b><span>{a.author}</span></div>
                </button>
              ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function InboxPage({ sub, go }) {
  const me = (typeof window !== "undefined" && window.__USER__) || null;
  const [convos, setConvos] = React.useState([]);
  const [peer, setPeer] = React.useState(sub && sub[0] === "u" ? sub.slice(1) : null);
  const [msgs, setMsgs] = React.useState([]);
  const [draft, setDraft] = React.useState("");
  const loadConvos = () => fetch("/api/dm").then(r => r.json()).then(j => setConvos(j.conversations || [])).catch(() => {});
  React.useEffect(() => { loadConvos(); }, []);
  React.useEffect(() => {
    setPeer(sub && sub[0] === "u" ? sub.slice(1) : null);
  }, [sub]);
  React.useEffect(() => {
    if (!peer) { setMsgs([]); return; }
    fetch("/api/dm/with?id=" + peer).then(r => r.json()).then(j => setMsgs(j.messages || [])).catch(() => {});
  }, [peer]);
  const people = (typeof window !== "undefined" && window.__DATA__ && window.__DATA__.people) || [];
  const peerName = (() => {
    const c = convos.find(x => String(x.peer) === String(peer));
    if (c && c.peer_name) return c.peer_name;
    const p = people.find(x => String(x.id) === String(peer));
    return p ? p.name : "Member";
  })();
  const send = async () => {
    if (!draft.trim() || !peer) return;
    const body = draft.trim(); setDraft("");
    try {
      const r = await fetch("/api/dm", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ to: peer, body }) });
      const j = await r.json();
      if (r.ok && j.message) { setMsgs(m => [...m, j.message]); loadConvos(); }
    } catch (_) {}
  };
  if (!me) {
    return <div className="view inbox"><div className="shell"><div className="pf-empty" style={{ marginTop: 80 }}><Icon name="chat" size={28} /><b className="serif">Sign in to message</b><p>Log in to start conversations with the sangat.</p><button className="btn btn-primary" onClick={() => go("community")}>Back to community</button></div></div></div>;
  }
  return (
    <div className="view inbox">
      <div className="shell inbox-shell">
        <h1 className="serif notif-page-title">Messages</h1>
        <div className="inbox-grid">
          <div className="inbox-list">
            {convos.length === 0 && <div className="empty">No conversations yet. Open a member and tap Message.</div>}
            {convos.map(c => (
              <button key={c.peer} className="inbox-convo" data-on={String(c.peer) === String(peer)} onClick={() => go("inbox/u" + c.peer)}>
                <Avatar seed={c.peer_email || c.peer_name || "x"} label={c.peer_name} size={40} />
                <div className="inbox-convo-main"><b>{c.peer_name || "Member"}</b><span>{c.last_body}</span></div>
                {Number(c.unread) > 0 && <span className="inbox-badge">{c.unread}</span>}
              </button>
            ))}
          </div>
          <div className="inbox-thread">
            {!peer ? (
              <div className="empty">Select a conversation to start chatting.</div>
            ) : (
              <React.Fragment>
                <div className="inbox-thread-head" onClick={() => go("profile/u" + peer)} style={{ cursor: "pointer" }} title="View profile"><Avatar seed={peer} label={peerName} size={34} /><b>{peerName}</b></div>
                <div className="inbox-msgs">
                  {msgs.length === 0 && <div className="empty">Say hello to {peerName}.</div>}
                  {msgs.map(m => (<div key={m.id} className={"inbox-msg " + (String(m.sender_id) === String(me.id) ? "mine" : "theirs")}>{m.body}</div>))}
                </div>
                <div className="inbox-compose">
                  <input placeholder="Write a message..." value={draft} onChange={e => setDraft(e.target.value)} onKeyDown={e => { if (e.key === "Enter") send(); }} />
                  <button className="btn btn-primary" data-dim={!draft.trim()} onClick={send}>Send</button>
                </div>
              </React.Fragment>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

const TWEAK_DEFAULTS = {
  accent: "#E8482A",
  dark: false,
  radius: 14,
  headline: "editorial",
  motion: true,
};

function App() {
  const [route, setRoute] = useState(() => (location.hash.replace(/^#\/?/, "") || "home"));
  const [favs, setFavs] = useState(() => new Set(EVENTS.filter(e => e.fav).map(e => e.id)));
  const [posts, setPosts] = useState(POSTS);
  const addPost = (p) => setPosts(prev => [p, ...prev]);
  const [trans, setTrans] = useState(false);
  const scrollerRef = useRef(null);
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  useEffect(() => {
    const root = document.documentElement;
    root.style.setProperty("--accent", t.accent);
    root.style.setProperty("--accent-deep", `color-mix(in oklch, ${t.accent} 76%, black)`);
    root.style.setProperty("--accent-soft", `color-mix(in oklch, ${t.accent} 15%, var(--card))`);
    root.setAttribute("data-theme", t.dark ? "dark" : "light");
    root.style.setProperty("--radius", t.radius + "px");
    root.style.setProperty("--radius-sm", Math.round(t.radius * 0.64) + "px");
    root.style.setProperty("--radius-lg", Math.round(t.radius * 1.6) + "px");
    root.style.setProperty("--serif", t.headline === "modern"
      ? '"Hanken Grotesk", system-ui, sans-serif'
      : '"Newsreader", Georgia, "Times New Roman", serif');
    root.classList.toggle("no-motion", !t.motion);
  }, [t.accent, t.dark, t.radius, t.headline, t.motion]);

  const go = (r) => {
    if (r === route) { window.scrollTo({ top: 0, behavior: "smooth" }); return; }
    setTrans(true);
    setTimeout(() => {
      setRoute(r);
      location.hash = "/" + r;
      window.scrollTo(0, 0);
      requestAnimationFrame(() => setTrans(false));
    }, 220);
  };

  useEffect(() => {
    const onHash = () => {
      const r = location.hash.replace(/^#\/?/, "") || "home";
      setRoute(r);
    };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);

  const toggleFav = (id) => setFavs(prev => {
    const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n;
  });

  const [top, sub] = route.split("/");
  let view;
  if (top === "home") view = <Home go={go} posts={posts} />;
  else if (top === "community" && sub) view = <PostThread id={sub} go={go} posts={posts} />;
  else if (top === "community") view = <Community go={go} posts={posts} />;
  else if (top === "c") view = <CommunityPage name={sub} go={go} posts={posts} />;
  else if (top === "compose") view = <NewPost go={go} addPost={addPost} editId={sub || null} />;
  else if (top === "read" && sub) view = <Reader id={sub} go={go} />;
  else if (top === "read") view = <Read go={go} />;
  else if (top === "write") view = <StoryCompose go={go} editId={sub || null} />;
  else if (top === "notifications") view = <NotificationsPage go={go} />;
  else if (top === "people") view = <PeoplePage go={go} />;
  else if (top === "inbox") view = <InboxPage sub={sub} go={go} />;
  else if (top === "search") view = <SearchPage go={go} />;
  else if (top === "profile") view = <Profile sub={sub} go={go} favs={favs} toggleFav={toggleFav} posts={posts} />;
  else if (top === "settings") view = <Settings go={go} tweaks={t} setTweak={setTweak} />;
  else view = <Home go={go} posts={posts} />;

  return (
    <React.Fragment>
      <Nav route={route} go={go} />
      <main className={"stage" + (trans ? " leaving" : " entering")} key={route} ref={scrollerRef}>
        {view}
      </main>
      <Footer go={go} />
      <TweaksPanel title="Tweaks">
        <TweakSection label="Color" />
        <TweakColor label="Accent" value={t.accent}
          options={["#E8482A", "#2C4A6E", "#5E7355", "#6B3F5E", "#B98326"]}
          onChange={(v) => setTweak("accent", v)} />
        <TweakToggle label="Dark mode" value={t.dark} onChange={(v) => setTweak("dark", v)} />
        <TweakSection label="Shape & type" />
        <TweakSlider label="Corner radius" value={t.radius} min={2} max={24} unit="px"
          onChange={(v) => setTweak("radius", v)} />
        <TweakRadio label="Headlines" value={t.headline}
          options={["editorial", "modern"]}
          onChange={(v) => setTweak("headline", v)} />
        <TweakSection label="Motion" />
        <TweakToggle label="Animations" value={t.motion} onChange={(v) => setTweak("motion", v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
