/* ============================================================
   App shell — Promotoria Hyris
   ============================================================ */
const { useState, useEffect, useRef } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "roxo",
  "font": "Sora",
  "radius": 16,
  "density": "regular"
}/*EDITMODE-END*/;

/* Variações visuais (todas seguindo o Hyris) */
const THEMES = {
  classico: { label: "Preto + Dourado", accent: "#18D6E8", accentInk: "#04222a", gold: "#FFCB1E", bg: "#0A0A0D", bg2: "#15151B", bg3: "#1D1D25", glow: "#18d6e8" },
  roxo:     { label: "Aurora Roxa",      accent: "#9B5BFF", accentInk: "#150a28", gold: "#FFCB1E", bg: "#0B0912", bg2: "#16121F", bg3: "#201A2C", glow: "#9B5BFF" },
  dourado:  { label: "Dourado Hyris",    accent: "#FFCB1E", accentInk: "#231a00", gold: "#FFD84D", bg: "#08080A", bg2: "#141318", bg3: "#1E1C22", glow: "#FFCB1E" },
};

function applyTheme(t) {
  const th = THEMES[t.theme] || THEMES.classico;
  const r = document.documentElement;
  r.style.setProperty("--accent", th.accent);
  r.style.setProperty("--accent-ink", th.accentInk);
  r.style.setProperty("--gold", th.gold);
  r.style.setProperty("--bg", th.bg);
  r.style.setProperty("--bg2", th.bg2);
  r.style.setProperty("--bg3", th.bg3);
  r.style.setProperty("--glow", th.glow);
  r.style.setProperty("--radius", t.radius + "px");
  r.style.setProperty("--font", `"${t.font}", system-ui, sans-serif`);
  const dens = { compact: 0.82, regular: 1, comfy: 1.15 }[t.density] || 1;
  r.style.setProperty("--dens", dens);
}

/* ---------- Notificações: helpers ---------- */
// extrai ids de membros mencionados (por @usuario e por @grupo)
function collectMentioned(text, members, groups) {
  const ids = new Set();
  const tokens = (String(text || "").match(/@([\w.]+)/g) || []).map((s) => s.slice(1).toLowerCase());
  tokens.forEach((tok) => {
    const m = members.find((x) => x.username.toLowerCase() === tok || x.display.toLowerCase() === tok);
    if (m) ids.add(m.id);
    const g = (groups || []).find((gr) => gr.key.toLowerCase() === tok || (gr.aliases || []).some((a) => a.toLowerCase() === tok));
    if (g) {
      members.forEach((mem) => {
        if (!g.roles || !g.roles.length || g.roles.includes(mem.role)) ids.add(mem.id);
      });
    }
  });
  return ids;
}
// adiciona uma notificação para cada alvo (menos o autor), com teto de 40
function addNotifs(notifications, targetIds, makeNotif, exceptId) {
  const next = Object.assign({}, notifications || {});
  targetIds.forEach((id) => {
    if (id === exceptId) return;
    const arr = next[id] ? next[id].slice() : [];
    arr.unshift(makeNotif(id));
    next[id] = arr.slice(0, 40);
  });
  return next;
}
function fmtNotifTime(ts) {
  const diff = Date.now() - ts;
  const m = Math.floor(diff / 60000);
  if (m < 1) return "agora";
  if (m < 60) return "há " + m + " min";
  const h = Math.floor(m / 60);
  if (h < 24) return "há " + h + "h";
  const d = Math.floor(h / 24);
  if (d < 30) return "há " + d + (d === 1 ? " dia" : " dias");
  const mo = Math.floor(d / 30);
  return "há " + mo + (mo === 1 ? " mês" : " meses");
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  useEffect(() => { applyTheme(t); }, [t]);

  const [state, setState] = useState(null);
  const applyingRemote = useRef(false);
  const booted = useRef(false);

  // conecta ao Supabase (ou offline) e recebe mudanças em tempo real
  useEffect(() => {
    if (booted.current) return;
    booted.current = true;
    PRStore.init((remote) => { applyingRemote.current = true; setState(remote); });
  }, []);

  // salva no Supabase quando o estado muda LOCALMENTE (não quando veio de fora)
  useEffect(() => {
    if (!state) return;
    if (applyingRemote.current) { applyingRemote.current = false; return; }
    PRStore.push(state);
  }, [state]);

  const [push, toastNode] = useToasts();

  // navegação
  const [view, setView] = useState("forum");
  const [manageTab, setManageTab] = useState("cats"); // cats | roles
  const [openPostId, setOpenPostId] = useState(null);

  // modais
  const [postEditor, setPostEditor] = useState(null);   // {post} | {new:true} | null
  const [memberEditor, setMemberEditor] = useState(null); // {member} | {new:true} | null
  const [catEditor, setCatEditor] = useState(null);     // {category} | {new:true} | null
  const [roleEditor, setRoleEditor] = useState(null);   // {role} | {new:true} | null
  const [groupEditor, setGroupEditor] = useState(null); // {group} | {new:true} | null
  const [confirm, setConfirm] = useState(null);
  const [notifOpen, setNotifOpen] = useState(false);

  // tela de carregamento — DEPOIS de todos os hooks (regras do React)
  if (!state) {
    return (
      <div className="pr-boot">
        <img src="assets/logo.png" alt="Hyris" />
        <span>Conectando ao painel…</span>
      </div>
    );
  }

  // sincroniza cargos e grupos do estado com PR.* ANTES de renderizar os filhos
  PR.applyRoles(state.roles);
  PR.applyGroups(state.mentionGroups);

  const currentUser = state.members.find((m) => m.id === state.sessionUserId);

  /* ---------- auth ---------- */
  function login(id) { setState((s) => ({ ...s, sessionUserId: id })); setView("forum"); }
  function logout() { setState((s) => ({ ...s, sessionUserId: null })); setOpenPostId(null); }

  if (!currentUser) {
    return <><LoginView members={state.members} onLogin={login} /><TweaksUI t={t} setTweak={setTweak} />{toastNode}</>;
  }

  /* ---------- posts ---------- */
  function createPost(data) {
    const post = { id: PR.uid("p"), authorId: currentUser.id, createdAt: PR.NOW, replies: [], ...data };
    setState((s) => {
      const mentioned = collectMentioned(post.body, s.members, s.mentionGroups);
      const notifications = addNotifs(s.notifications, [...mentioned], () => ({
        id: PR.uid("n"), type: "mention", postId: post.id,
        title: "Você foi mencionado",
        body: `${currentUser.display} mencionou você em "${post.title}"`,
        ts: Date.now(), read: false,
      }), currentUser.id);
      return { ...s, posts: [post, ...s.posts], notifications };
    });
    push("Postagem publicada");
  }
  function updatePost(id, data) {
    setState((s) => ({ ...s, posts: s.posts.map((p) => p.id === id ? { ...p, ...data } : p) }));
    push("Postagem atualizada");
  }
  function deletePost(id) {
    setConfirm({
      title: "Remover postagem", danger: true, confirmLabel: "Remover",
      message: "Tem certeza que deseja remover esta postagem? Esta ação não pode ser desfeita.",
      onConfirm: () => { setState((s) => ({ ...s, posts: s.posts.filter((p) => p.id !== id) })); setOpenPostId(null); setView("forum"); push("Postagem removida"); },
    });
  }
  function togglePin(id) {
    setState((s) => ({ ...s, posts: s.posts.map((p) => p.id === id ? { ...p, pinned: !p.pinned } : p) }));
  }
  function toggleResolved(id) {
    let nowResolved = false;
    setState((s) => ({ ...s, posts: s.posts.map((p) => { if (p.id === id) { nowResolved = !p.resolved; return { ...p, resolved: !p.resolved }; } return p; }) }));
    push(nowResolved ? "Marcado como resolvido" : "Post reaberto");
  }
  function addReply(postId, body) {
    const reply = { id: PR.uid("r"), authorId: currentUser.id, body, createdAt: PR.NOW };
    setState((s) => {
      const post = s.posts.find((p) => p.id === postId);
      let notifications = s.notifications;
      if (post) {
        // avisa o autor do tópico (se não for você mesmo)
        if (post.authorId !== currentUser.id) {
          notifications = addNotifs(notifications, [post.authorId], () => ({
            id: PR.uid("n"), type: "reply", postId,
            title: "Nova resposta no seu tópico",
            body: `${currentUser.display} respondeu em "${post.title}"`,
            ts: Date.now(), read: false,
          }), currentUser.id);
        }
        // avisa quem foi mencionado na resposta
        const mentioned = collectMentioned(body, s.members, s.mentionGroups);
        notifications = addNotifs(notifications, [...mentioned], () => ({
          id: PR.uid("n"), type: "mention", postId,
          title: "Você foi mencionado",
          body: `${currentUser.display} mencionou você numa resposta em "${post.title}"`,
          ts: Date.now(), read: false,
        }), currentUser.id);
      }
      return { ...s, posts: s.posts.map((p) => p.id === postId ? { ...p, replies: [...p.replies, reply] } : p), notifications };
    });
    push("Resposta enviada");
  }
  function deleteReply(postId, replyId) {
    setState((s) => ({ ...s, posts: s.posts.map((p) => p.id === postId ? { ...p, replies: p.replies.filter((r) => r.id !== replyId) } : p) }));
    push("Resposta removida");
  }
  function toggleReaction(postId, emoji) {
    setState((s) => ({ ...s, posts: s.posts.map((p) => {
      if (p.id !== postId) return p;
      const reactions = { ...(p.reactions || {}) };
      const arr = reactions[emoji] ? reactions[emoji].slice() : [];
      const i = arr.indexOf(currentUser.id);
      if (i >= 0) arr.splice(i, 1); else arr.push(currentUser.id);
      if (arr.length) reactions[emoji] = arr; else delete reactions[emoji];
      return { ...p, reactions };
    }) }));
  }

  /* ---------- categorias ---------- */
  function saveCategory(data) {
    if (catEditor && catEditor.category) {
      const id = catEditor.category.id;
      setState((s) => ({ ...s, categories: s.categories.map((c) => c.id === id ? { ...c, ...data } : c) }));
      push("Categoria atualizada");
    } else {
      const baseId = data.label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "cat";
      let id = baseId, n = 2;
      while (state.categories.some((c) => c.id === id)) id = baseId + "-" + (n++);
      setState((s) => ({ ...s, categories: [...s.categories, { id, ...data }] }));
      push("Categoria criada");
    }
  }
  function removeCategory(cat) {
    const count = state.posts.filter((p) => p.category === cat.id).length;
    setConfirm({
      title: "Remover categoria", danger: true, confirmLabel: "Remover",
      message: count > 0
        ? `A categoria "${cat.label}" tem ${count} post(s). Remover a categoria também apaga esses posts. Continuar?`
        : `Remover a categoria "${cat.label}"?`,
      onConfirm: () => {
        setState((s) => ({ ...s, categories: s.categories.filter((c) => c.id !== cat.id), posts: s.posts.filter((p) => p.category !== cat.id) }));
        push("Categoria removida");
      },
    });
  }

  /* ---------- membros ---------- */
  function handleMemberUpdate(id, payload) {
    if (payload === "__edit__") { setMemberEditor({ member: state.members.find((m) => m.id === id) }); return; }
    setState((s) => ({ ...s, members: s.members.map((m) => m.id === id ? { ...m, ...payload } : m) }));
  }
  function saveMember(data) {
    if (memberEditor && memberEditor.member) {
      const id = memberEditor.member.id;
      setState((s) => ({ ...s, members: s.members.map((m) => m.id === id ? { ...m, ...data } : m) }));
      push("Membro atualizado");
    } else {
      const m = { id: PR.uid("m"), joined: new Date(PR.NOW).toISOString().slice(0, 10), ...data };
      setState((s) => ({ ...s, members: [...s.members, m] }));
      push("Membro adicionado");
    }
  }
  function removeMember(id) {
    const m = state.members.find((x) => x.id === id);
    setConfirm({
      title: "Remover membro", danger: true, confirmLabel: "Remover",
      message: `Remover ${m.display} da promotoria? O acesso dele ao painel será revogado.`,
      onConfirm: () => { setState((s) => ({ ...s, members: s.members.filter((x) => x.id !== id) })); push("Membro removido"); },
    });
  }

  /* ---------- cargos ---------- */
  function saveRole(data) {
    if (roleEditor && roleEditor.role) {
      const id = roleEditor.role.id;
      setState((s) => ({ ...s, roles: s.roles.map((r) => r.id === id ? { ...r, ...data } : r) }));
      push("Cargo atualizado");
    } else {
      const base = data.label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "cargo";
      let id = base, n = 2;
      while (state.roles.some((r) => r.id === id)) id = base + "-" + (n++);
      const level = Math.max(0, ...state.roles.map((r) => r.level)) + 1;
      setState((s) => ({ ...s, roles: [...s.roles, { id, level, ...data }] }));
      push("Cargo criado");
    }
  }
  function removeRole(role) {
    const count = state.members.filter((m) => m.role === role.id).length;
    const fallback = state.roles.filter((r) => r.id !== role.id).sort((a, b) => a.level - b.level)[0];
    setConfirm({
      title: "Remover cargo", danger: true, confirmLabel: "Remover",
      message: count > 0
        ? `O cargo "${role.label}" tem ${count} membro(s). Eles serão movidos para "${fallback ? fallback.label : "—"}". Continuar?`
        : `Remover o cargo "${role.label}"?`,
      onConfirm: () => {
        setState((s) => ({
          ...s,
          roles: s.roles.filter((r) => r.id !== role.id),
          members: s.members.map((m) => m.role === role.id ? { ...m, role: fallback.id, color: PR.ROLES[fallback.id] ? PR.ROLES[fallback.id].color : m.color } : m),
          categories: s.categories.map((c) => ({ ...c, perms: Object.fromEntries(Object.entries(c.perms).map(([k, v]) => [k, v.filter((x) => x !== role.id)])) })),
          mentionGroups: s.mentionGroups.map((g) => g.roles ? { ...g, roles: g.roles.filter((x) => x !== role.id) } : g),
        }));
        push("Cargo removido");
      },
    });
  }
  function moveRole(id, dir) {
    // reordena por nível (dir -1 = subir = mais poder)
    setState((s) => {
      const sorted = s.roles.slice().sort((a, b) => b.level - a.level); // topo = maior poder
      const i = sorted.findIndex((r) => r.id === id);
      const j = i + dir;
      if (j < 0 || j >= sorted.length) return s;
      const tmp = sorted[i]; sorted[i] = sorted[j]; sorted[j] = tmp;
      // renumera níveis: topo recebe o maior
      const n = sorted.length;
      const relevel = sorted.map((r, idx) => ({ ...r, level: n - idx }));
      return { ...s, roles: relevel };
    });
  }

  /* ---------- grupos de menção ---------- */
  function saveGroup(data) {
    if (groupEditor && groupEditor.group) {
      const key = groupEditor.group.key;
      setState((s) => ({ ...s, mentionGroups: s.mentionGroups.map((g) => g.key === key ? { ...g, ...data, aliases: g.aliases } : g) }));
      push("Grupo atualizado");
    } else {
      const base = data.label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "") || "grupo";
      let key = base, n = 2;
      while (state.mentionGroups.some((g) => g.key === key)) key = base + (n++);
      setState((s) => ({ ...s, mentionGroups: [...s.mentionGroups, { key, aliases: [key], ...data }] }));
      push("Grupo criado");
    }
  }
  function removeGroup(group) {
    setConfirm({
      title: "Remover grupo", danger: true, confirmLabel: "Remover",
      message: `Remover o grupo de menção @${group.key}? Quem já usou em posts antigos vai ver só o texto.`,
      onConfirm: () => { setState((s) => ({ ...s, mentionGroups: s.mentionGroups.filter((g) => g.key !== group.key) })); push("Grupo removido"); },
    });
  }

  /* ---------- notificações ---------- */
  const myNotifs = (state.notifications && state.notifications[currentUser.id]) || [];
  const unreadCount = myNotifs.filter((n) => !n.read).length;
  function toggleNotifs() {
    const willOpen = !notifOpen;
    setNotifOpen(willOpen);
    if (willOpen && unreadCount > 0) {
      setState((s) => {
        const mine = ((s.notifications && s.notifications[currentUser.id]) || []).map((n) => ({ ...n, read: true }));
        return { ...s, notifications: Object.assign({}, s.notifications, { [currentUser.id]: mine }) };
      });
    }
  }
  function openNotif(n) {
    setNotifOpen(false);
    if (n.postId && state.posts.some((p) => p.id === n.postId)) { setView("forum"); setOpenPostId(n.postId); }
  }
  function clearNotifs() {
    setState((s) => Object.assign({}, s, { notifications: Object.assign({}, s.notifications, { [currentUser.id]: [] }) }));
  }

  /* ---------- nav config ---------- */
  const nav = [
    { id: "forum", label: "Fórum", icon: "feed", show: true },
    { id: "categories", label: "Categorias & Cargos", icon: "folder", show: PR.can(currentUser.role, "members.manage") },
    { id: "members", label: "Membros", icon: "users", show: PR.can(currentUser.role, "members.view") },
    { id: "creds", label: "Credenciais", icon: "key", show: PR.can(currentUser.role, "creds.manage") },
  ].filter((n) => n.show);

  const openPost = openPostId ? state.posts.find((p) => p.id === openPostId) : null;

  return (
    <div className="pr-shell">
      {/* Sidebar */}
      <aside className="pr-sidebar">
        <div className="pr-brand">
          <img src="assets/logo.png" alt="Hyris" className="pr-brand-logo" />
          <div className="pr-brand-text">
            <strong>Promotoria</strong>
            <span>Hyris · Eventos</span>
          </div>
        </div>

        <nav className="pr-nav">
          {nav.map((n) => (
            <button key={n.id} className={"pr-navitem" + (view === n.id ? " active" : "")}
              onClick={() => { setView(n.id); setOpenPostId(null); }}>
              <Icon name={n.icon} size={19} /> {n.label}
            </button>
          ))}
        </nav>

        <div className="pr-sidebar-foot">
          <div className="pr-userchip">
            <Avatar member={currentUser} size={38} />
            <div className="pr-userchip-info">
              <strong>{currentUser.display}</strong>
              <RoleBadge role={currentUser.role} size="sm" />
            </div>
            <button className="pr-iconbtn" title="Sair" onClick={logout}><Icon name="logout" size={17} /></button>
          </div>
        </div>
      </aside>

      {/* Sino de notificações (topo direito) */}
      <button className="pr-topbell" title="Notificações" onClick={toggleNotifs}>
        <Icon name="bell" size={19} />
        {unreadCount > 0 && <span className="pr-bell-badge">{unreadCount > 9 ? "9+" : unreadCount}</span>}
      </button>

      {notifOpen && (
        <>
          <div className="pr-notif-scrim" onClick={() => setNotifOpen(false)}></div>
          <div className="pr-notif-pop">
            <div className="pr-notif-head">
              <span><Icon name="bell" size={16} /> Notificações</span>
              {myNotifs.length > 0 && <button className="pr-notif-clear" onClick={clearNotifs}>Limpar</button>}
            </div>
            <div className="pr-notif-list">
              {myNotifs.length === 0 && <div className="pr-notif-empty"><Icon name="bell" size={26} /><p>Nenhuma notificação ainda.</p></div>}
              {myNotifs.map((n) => (
                <button key={n.id} className="pr-notif-item" onClick={() => openNotif(n)}>
                  <span className={"pr-notif-icon " + n.type}><Icon name={n.type === "reply" ? "reply" : "bell"} size={16} /></span>
                  <span className="pr-notif-txt">
                    <strong>{n.title}</strong>
                    <span>{n.body}</span>
                    <em>{fmtNotifTime(n.ts)}</em>
                  </span>
                </button>
              ))}
            </div>
          </div>
        </>
      )}

      {/* Conteúdo */}
      <main className="pr-main">
        {view === "forum" && !openPost && (
          <ForumView posts={state.posts} members={state.members} categories={state.categories} currentUser={currentUser}
            onOpen={(id) => { setOpenPostId(id); setView("forum"); }} onNew={() => setPostEditor({ new: true })}
            onEdit={(p) => setPostEditor({ post: p })} onTogglePin={togglePin} onDelete={deletePost} onToggleResolved={toggleResolved} />
        )}
        {view === "forum" && openPost && (
          <PostDetail post={openPost} members={state.members} categories={state.categories} currentUser={currentUser}
            onBack={() => setOpenPostId(null)}
            onEdit={(p) => setPostEditor({ post: p })}
            onDelete={deletePost} onReply={addReply} onTogglePin={togglePin} onDeleteReply={deleteReply} onToggleReaction={toggleReaction} onToggleResolved={toggleResolved} />
        )}
        {view === "categories" && (
          <div className="pr-page" style={{ paddingBottom: 0 }}>
            <div className="pr-managehead">
              <h2 className="pr-pagetitle">Categorias & Cargos</h2>
              <div className="pr-subtabs">
                <button className={"pr-subtab" + (manageTab === "cats" ? " active" : "")} onClick={() => setManageTab("cats")}>
                  <Icon name="folder" size={16} /> Categorias
                </button>
                <button className={"pr-subtab" + (manageTab === "roles" ? " active" : "")} onClick={() => setManageTab("roles")}>
                  <Icon name="shield" size={16} /> Cargos & Menções
                </button>
              </div>
            </div>
          </div>
        )}
        {view === "categories" && manageTab === "cats" && (
          <CategoriesView categories={state.categories} posts={state.posts} currentUser={currentUser}
            onAdd={() => setCatEditor({ new: true })} onEdit={(c) => setCatEditor({ category: c })} onRemove={removeCategory} />
        )}
        {view === "categories" && manageTab === "roles" && (
          <RolesMentionsView roles={state.roles} groups={state.mentionGroups} members={state.members} posts={state.posts} currentUser={currentUser}
            onAddRole={() => setRoleEditor({ new: true })} onEditRole={(r) => setRoleEditor({ role: r })} onRemoveRole={removeRole} onMoveRole={moveRole}
            onAddGroup={() => setGroupEditor({ new: true })} onEditGroup={(g) => setGroupEditor({ group: g })} onRemoveGroup={removeGroup} />
        )}
        {view === "members" && (
          <MembersView members={state.members} currentUser={currentUser}
            onAdd={() => setMemberEditor({ new: true })} onUpdate={handleMemberUpdate} onRemove={removeMember} />
        )}
        {view === "creds" && (
          <CredentialsView members={state.members} currentUser={currentUser} onUpdate={handleMemberUpdate} onToast={push} />
        )}
      </main>

      {/* Modais */}
      {postEditor && (
        <PostEditor post={postEditor.post} currentUser={currentUser} members={state.members} categories={state.categories}
          onSave={(data) => postEditor.post ? updatePost(postEditor.post.id, data) : createPost(data)}
          onClose={() => setPostEditor(null)} />
      )}
      {catEditor && (
        <CategoryEditor category={catEditor.category} onSave={saveCategory} onClose={() => setCatEditor(null)} />
      )}
      {roleEditor && (
        <RoleEditor role={roleEditor.role} onSave={saveRole} onClose={() => setRoleEditor(null)} />
      )}
      {groupEditor && (
        <GroupEditor group={groupEditor.group} roles={state.roles.slice().sort((a, b) => b.level - a.level)} onSave={saveGroup} onClose={() => setGroupEditor(null)} />
      )}
      {memberEditor && (
        <MemberEditor member={memberEditor.member} onSave={saveMember} onClose={() => setMemberEditor(null)} />
      )}
      {confirm && <Confirm {...confirm} onClose={() => setConfirm(null)} />}

      <TweaksUI t={t} setTweak={setTweak} />
      {toastNode}
    </div>
  );
}

/* ---------- Painel de Tweaks ---------- */
function TweaksUI({ t, setTweak }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Tema visual" />
      <TweakRadio label="Estilo" value={t.theme}
        options={[{ value: "classico", label: "Preto+Ouro" }, { value: "roxo", label: "Roxo" }, { value: "dourado", label: "Dourado" }]}
        onChange={(v) => setTweak("theme", v)} />
      <TweakSection label="Tipografia & forma" />
      <TweakRadio label="Fonte" value={t.font}
        options={["Sora", "Space Grotesk", "Plus Jakarta Sans"]}
        onChange={(v) => setTweak("font", v)} />
      <TweakSlider label="Arredondamento" value={t.radius} min={6} max={24} step={2} unit="px"
        onChange={(v) => setTweak("radius", v)} />
      <TweakRadio label="Densidade" value={t.density}
        options={[{ value: "compact", label: "Compacto" }, { value: "regular", label: "Padrão" }, { value: "comfy", label: "Amplo" }]}
        onChange={(v) => setTweak("density", v)} />
    </TweaksPanel>
  );
}

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