// announcement.jsx — server-driven workspace announcements.
//
// Two surfaces:
//
//   1. AnnouncementHost — auto-mounted near the bottom of <body>. On
//      app load (and on focus + every 60s) it asks the server for
//      GET /api/announcements/active, which returns every pending
//      announcement for this user. The host renders a FULL-SCREEN,
//      non-dismissible overlay for the first one; tapping "Got it"
//      POSTs an ack, then the next pending announcement immediately
//      takes the screen. Per-user ack lives in the DB, so the same
//      user on a different device also sees each announcement once
//      and only once.
//
//   2. AnnouncementsAdminView — owner / workspace-admin only page.
//      Lists every announcement past + present, with read receipts
//      (X of Y). Lets the owner publish a new one (multiple can be
//      active at the same time — users work through them in order)
//      and pick the audience: "All users" or "Specific users"
//      (multi-select picker).
//
// The host file is loaded once globally; the admin view is exposed
// via window.AnnouncementsAdminView and routed by app.jsx into the
// shell sidebar (Announcements).

(function () {
  const R = window.React;

  // ── Full-screen blocking host ───────────────────────────────
  // Seconds the user must wait before "Noted" becomes clickable. The
  // intent is to make sure the message is at least visible long enough
  // to be read — not just tab-tab-enter dismissed.
  const NOTED_COUNTDOWN_SECS = 10;

  function AnnouncementHost() {
    // Queue of pending announcements (oldest first) and current index.
    // Multiple announcements can be active at once; the user works
    // through them one at a time. When the user acks the current one
    // we advance `idx`; when idx passes the end of the queue we clear
    // it and the overlay disappears until reload() finds more.
    const [queue, setQueue]     = R.useState([]);
    const [idx, setIdx]         = R.useState(0);
    const [acking, setAcking]   = R.useState(false);
    const [secs, setSecs]       = R.useState(NOTED_COUNTDOWN_SECS);

    const reload = R.useCallback(async () => {
      try {
        const u = window.api && window.api.getUser && window.api.getUser();
        if (!u || !u.id) return;
        // External collaborators (ws_role = 'guest') are not the
        // intended audience for internal HR / company announcements,
        // so we suppress the modal client-side. Backend still scopes
        // queries by workspace but this avoids targeted-message
        // accidents leaking to outsiders.
        if (u.wsRole === "guest") return;
        const r = await window.api.announcements.active();
        // Prefer the new array shape; fall back to the single-row
        // shape from older server builds so a stale backend doesn't
        // break the host.
        const list = Array.isArray(r && r.announcements)
          ? r.announcements
          : (r && r.announcement ? [r.announcement] : []);
        setQueue(list);
        setIdx(0);
      } catch {}
    }, []);

    // Initial load.
    R.useEffect(() => { reload(); }, [reload]);
    // Re-check on login, on tab refocus, and every 60s in case the owner
    // publishes a new one mid-session.
    R.useEffect(() => {
      function onAuth() { reload(); }
      function onFocus() { if (document.visibilityState === "visible") reload(); }
      window.addEventListener("flowboard:authed", onAuth);
      document.addEventListener("visibilitychange", onFocus);
      const id = setInterval(() => { if (document.visibilityState === "visible") reload(); }, 60_000);
      return () => {
        window.removeEventListener("flowboard:authed", onAuth);
        document.removeEventListener("visibilitychange", onFocus);
        clearInterval(id);
      };
    }, [reload]);

    const pending = (idx >= 0 && idx < queue.length) ? queue[idx] : null;

    // Reset + tick the countdown whenever the visible announcement
    // changes (initial appear OR advance to next in queue).
    R.useEffect(() => {
      if (!pending) return;
      setSecs(NOTED_COUNTDOWN_SECS);
      const id = setInterval(() => {
        setSecs(s => (s > 0 ? s - 1 : 0));
      }, 1000);
      return () => clearInterval(id);
    }, [pending && pending.id]);

    if (!pending) return null;

    async function ack() {
      if (acking || secs > 0) return;
      setAcking(true);
      try {
        await window.api.announcements.ack(pending.id);
        // Advance to the next announcement (if any). When we run off
        // the end of the queue, clearing it makes the overlay vanish.
        if (idx + 1 < queue.length) {
          setIdx(idx + 1);
        } else {
          setQueue([]); setIdx(0);
        }
        setAcking(false);
      }
      catch (e) { setAcking(false); }
    }
    const ready    = secs <= 0;
    const total    = queue.length;
    const position = idx + 1;
    const remaining = total - idx; // including the current one

    // Full-screen overlay. No "X" close, no escape-to-close. The user
    // MUST click "Got it" to dismiss — that's the whole point.
    return window.ReactDOM.createPortal(
      <div style={S.overlay} role="alertdialog" aria-modal="true">
        <div style={S.card}>
          {total > 1 && (
            <div style={S.counter} aria-hidden="true">
              {position} of {total}
            </div>
          )}
          {pending.icon && <div style={S.icon} aria-hidden="true">{pending.icon}</div>}
          <h1 style={S.title}>{pending.title}</h1>
          {pending.body && (
            <div style={S.body}>
              {String(pending.body).split(/\n+/).map((p, i) => (
                <p key={i} style={S.bodyPara}>{p}</p>
              ))}
            </div>
          )}
          <button
            style={{ ...S.cta, ...(ready ? null : S.ctaDisabled) }}
            onClick={ack}
            disabled={acking || !ready}
            aria-live="polite">
            {acking
              ? "Saving…"
              : ready
                ? (remaining > 1 ? "Got it · Next" : "Got it")
                : `Got it (${secs}s)`}
          </button>
          <div style={S.foot}>
            {ready
              ? (remaining > 1
                  ? `${remaining - 1} more announcement${remaining - 1 === 1 ? "" : "s"} after this.`
                  : "This must be acknowledged to continue.")
              : "Please take a moment to read this before continuing."}
          </div>
        </div>
      </div>,
      document.body
    );
  }

  const S = {
    overlay: {
      position: "fixed", inset: 0, zIndex: 99800,
      background: "linear-gradient(135deg, rgba(15,23,41,.85), rgba(35,15,80,.82))",
      backdropFilter: "blur(6px)",
      display: "flex", alignItems: "center", justifyContent: "center",
      padding: 24,
    },
    card: {
      width: "min(560px, 100%)",
      background: "#fff",
      borderRadius: 18,
      padding: "32px 30px 22px",
      boxShadow: "0 40px 100px rgba(0,0,0,.45)",
      textAlign: "center",
      animation: "fbann-pop .35s cubic-bezier(.22,.9,.36,1.18) both",
    },
    icon: { fontSize: 56, lineHeight: 1, marginBottom: 12 },
    title: {
      margin: "4px 0 14px", fontSize: 22, fontWeight: 800,
      color: "#0f1729", letterSpacing: "-0.01em",
    },
    body: { textAlign: "left", marginBottom: 16 },
    bodyPara: {
      margin: "6px 0", fontSize: 14.5, lineHeight: 1.6, color: "#3b4254",
    },
    cta: {
      display: "inline-block",
      background: "#0073ea", color: "#fff",
      border: 0, borderRadius: 10,
      padding: "12px 32px",
      fontSize: 14.5, fontWeight: 800, cursor: "pointer",
      letterSpacing: ".02em",
      marginTop: 6,
      transition: "background .15s ease, opacity .15s ease",
      minWidth: 160,
    },
    ctaDisabled: {
      background: "#aebdd2", cursor: "not-allowed", opacity: 0.85,
    },
    foot: { marginTop: 12, fontSize: 11.5, color: "#8a90a0" },
    counter: {
      display: "inline-block",
      fontSize: 11, fontWeight: 700, letterSpacing: ".06em",
      color: "#5b6478", background: "#eef1f7",
      padding: "3px 10px", borderRadius: 99,
      marginBottom: 12,
    },
  };

  (function injectKeyframes() {
    if (document.getElementById("fbann-css")) return;
    const st = document.createElement("style"); st.id = "fbann-css";
    st.textContent = "@keyframes fbann-pop{0%{opacity:0;transform:scale(.92)}100%{opacity:1;transform:scale(1)}}";
    document.head.appendChild(st);
  })();

  function mount() {
    if (document.getElementById("fb-ann-host")) return;
    const node = document.createElement("div"); node.id = "fb-ann-host";
    document.body.appendChild(node);
    try {
      if (window.ReactDOM.createRoot) window.ReactDOM.createRoot(node).render(R.createElement(AnnouncementHost));
      else window.ReactDOM.render(R.createElement(AnnouncementHost), node);
    } catch (e) { console.warn("[announcement] mount failed:", e && e.message); }
  }
  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", mount);
  else mount();

  // ── Admin view (owner/admin) ────────────────────────────────
  function AnnouncementsAdminView() {
    const [items, setItems]   = R.useState(null);
    const [err, setErr]       = R.useState("");
    const [openCreate, setOC] = R.useState(false);
    const [openId, setOpenId] = R.useState(null);

    const reload = R.useCallback(async () => {
      try {
        const r = await window.api.announcements.list();
        setItems(r.items || []);
      } catch (e) {
        setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load");
      }
    }, []);
    R.useEffect(() => { reload(); }, [reload]);

    function ago(iso) {
      if (!iso) return "";
      const d = new Date(String(iso).replace(" ", "T"));
      return d.toLocaleString();
    }

    return (
      <div className="home-root" style={{ padding: 18, maxWidth: 980, margin: "0 auto" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 10 }}>
          <h1 style={{ margin: 0, fontSize: 20, fontWeight: 800, color: "var(--ink-strong, #0f1729)" }}>Announcements</h1>
          <div style={{ flex: 1 }}/>
          <button className="btn btn-primary" onClick={() => setOC(true)}>+ New announcement</button>
        </div>
        <div style={{ fontSize: 12.5, color: "var(--ink-muted)", marginBottom: 14 }}>
          Multiple can be active at once. Users see them one after the other, oldest first.
        </div>
        {err && <div style={{ color: "#c0223a", fontSize: 12.5 }}>{err}</div>}
        {items == null ? (
          <div style={{ padding: 24, color: "var(--ink-muted)", textAlign: "center" }}>Loading…</div>
        ) : items.length === 0 ? (
          <div style={{ padding: 28, color: "var(--ink-muted)", textAlign: "center", border: "1px dashed var(--border-row,#eaecf1)", borderRadius: 10 }}>
            No announcements yet.
          </div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {items.map(a => {
              const pct = a.total > 0 ? Math.round((a.read / a.total) * 100) : 0;
              return (
                <div key={a.id}
                     onClick={() => setOpenId(a.id)}
                     style={{
                       cursor: "pointer", padding: "14px 16px",
                       borderRadius: 12, border: "1px solid var(--border-row,#eaecf1)",
                       background: a.active ? "linear-gradient(180deg,#fff,#f4f8ff)" : "#fff",
                       display: "flex", alignItems: "center", gap: 14,
                     }}>
                  <div style={{ fontSize: 26 }} aria-hidden="true">{a.icon || "📢"}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <div style={{ fontSize: 14, fontWeight: 700, color: "var(--ink-strong,#0f1729)" }}>{a.title}</div>
                      {a.active === 1 && (
                        <span style={{ background: "#0073ea", color: "#fff", fontSize: 10.5, fontWeight: 700, padding: "1px 8px", borderRadius: 99 }}>ACTIVE</span>
                      )}
                    </div>
                    <div style={{ fontSize: 11.5, color: "var(--ink-muted)", marginTop: 3 }}>
                      {a.audience_kind === "all" ? "All users" : "Selected users"} · {ago(a.created_at)}
                    </div>
                  </div>
                  <div style={{ textAlign: "right", minWidth: 110 }}>
                    <div style={{ fontSize: 13, fontWeight: 700, color: "var(--ink-strong,#0f1729)" }}>{a.read} / {a.total}</div>
                    <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>{pct}% read</div>
                    <div style={{ height: 4, marginTop: 4, background: "#eef0f5", borderRadius: 99, overflow: "hidden" }}>
                      <div style={{ width: pct + "%", height: "100%", background: "#0073ea" }}/>
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        )}

        {openCreate && (
          <CreateAnnouncementModal onClose={() => setOC(false)} onSaved={() => { setOC(false); reload(); }}/>
        )}
        {openId && (
          <AnnouncementDetailModal id={openId} onClose={() => { setOpenId(null); reload(); }}/>
        )}
      </div>
    );
  }

  // Create modal: title, body, icon, audience (all / individuals).
  function CreateAnnouncementModal({ onClose, onSaved }) {
    const PEOPLE = (typeof window.PEOPLE !== "undefined" && Array.isArray(window.PEOPLE)) ? window.PEOPLE : [];
    const [title, setTitle]    = R.useState("");
    const [icon, setIcon]      = R.useState("📢");
    const [body, setBody]      = R.useState("");
    const [audience, setAud]   = R.useState("all"); // 'all' | 'individuals'
    const [pickedIds, setPick] = R.useState([]);
    const [busy, setBusy]      = R.useState(false);
    const [err, setErr]        = R.useState("");

    function togglePick(id) {
      setPick(arr => arr.includes(id) ? arr.filter(x => x !== id) : [...arr, id]);
    }
    async function save() {
      if (!title.trim()) { setErr("Title is required"); return; }
      if (audience === "individuals" && pickedIds.length === 0) { setErr("Pick at least one user"); return; }
      setBusy(true); setErr("");
      try {
        await window.api.announcements.create({
          title: title.trim(), body: body.trim() || null,
          icon: icon || null,
          audience_kind: audience,
          audience_user_ids: audience === "individuals" ? pickedIds : null,
        });
        onSaved();
      } catch (e) {
        setErr((e && e.body && (e.body.message || e.body.error)) || "Could not save");
        setBusy(false);
      }
    }
    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 520 }}>
          <div className="modal-header"><div style={{ fontWeight: 700 }}>New announcement</div>
            <button className="modal-close" onClick={onClose}>×</button>
          </div>
          <div className="modal-body" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            <div style={{ fontSize: 11.5, color: "var(--ink-muted)", padding: "6px 8px", background: "#fff8ec", border: "1px solid #fadc9b", borderRadius: 6 }}>
              This is queued alongside any other active announcements. Every user in the audience sees a full-screen modal on their next load and works through the queue one by one, tapping <b>Got it</b> on each.
            </div>
            <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
              <span style={S2.k}>Title</span>
              <input value={title} onChange={e => setTitle(e.target.value)} style={S2.in} placeholder="e.g. Please check out or start a break at lunch"/>
            </label>
            <div style={{ display: "grid", gridTemplateColumns: "80px 1fr", gap: 10 }}>
              <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <span style={S2.k}>Icon</span>
                <input value={icon} onChange={e => setIcon(e.target.value)} style={S2.in} maxLength={4}/>
              </label>
              <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <span style={S2.k}>Audience</span>
                <select value={audience} onChange={e => setAud(e.target.value)} style={S2.in}>
                  <option value="all">All workspace users</option>
                  <option value="individuals">Specific users</option>
                </select>
              </label>
            </div>
            <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
              <span style={S2.k}>Body (newlines become paragraphs)</span>
              <textarea value={body} onChange={e => setBody(e.target.value)} rows={5}
                        style={{ ...S2.in, resize: "vertical", fontFamily: "inherit" }}
                        placeholder="Explain what you need everyone to know or do."/>
            </label>
            {audience === "individuals" && (
              <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <span style={S2.k}>Pick users</span>
                <div style={{ maxHeight: 200, overflow: "auto", border: "1px solid var(--bd,#d8dce4)", borderRadius: 7, padding: 8 }}>
                  {PEOPLE.map(p => (
                    <label key={p.id} style={{ display: "flex", alignItems: "center", gap: 8, padding: "4px 6px", cursor: "pointer", borderRadius: 6 }}>
                      <input type="checkbox" checked={pickedIds.includes(p.id)} onChange={() => togglePick(p.id)}/>
                      <span style={{ fontSize: 13, color: "var(--ink-strong,#0f1729)" }}>{p.name}</span>
                    </label>
                  ))}
                </div>
                <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>{pickedIds.length} selected</div>
              </div>
            )}
            {err && <div style={{ color: "#c0223a", fontSize: 12 }}>{err}</div>}
          </div>
          <div className="modal-footer">
            <button className="btn" onClick={onClose} disabled={busy}>Cancel</button>
            <button className="btn btn-primary" onClick={save} disabled={busy || !title.trim()}>{busy ? "Publishing…" : "Publish"}</button>
          </div>
        </div>
      </div>,
      document.body
    );
  }

  // Detail modal: full body + read receipts + deactivate.
  function AnnouncementDetailModal({ id, onClose }) {
    const [data, setData]   = R.useState(null);
    const [tab, setTab]     = R.useState("readers");
    const [busy, setBusy]   = R.useState(false);
    const [err, setErr]     = R.useState("");

    R.useEffect(() => {
      (async () => {
        try { setData(await window.api.announcements.get(id)); }
        catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load"); }
      })();
    }, [id]);

    async function deactivate() {
      if (!window.confirm("Deactivate this announcement? Users with the modal still up will keep seeing it until they tap Got it; new loads will skip it.")) return;
      setBusy(true);
      try { await window.api.announcements.deactivate(id); onClose(); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not deactivate"); setBusy(false); }
    }
    async function remove() {
      if (!window.confirm("Delete this announcement permanently? Read receipts will be discarded.")) return;
      setBusy(true);
      try { await window.api.announcements.remove(id); onClose(); }
      catch (e) {
        const msg = (e && e.body && (e.body.message || e.body.error))
          || (e && e.message)
          || "Could not delete";
        setErr(msg);
        setBusy(false);
      }
    }

    // While the detail is loading (or if the fetch errored), still
    // render the modal frame so the user gets visible feedback that
    // their click did something. Previously we returned null here,
    // which made it look like nothing was happening when the request
    // was slow / blocked / failed silently.
    if (!data) {
      return window.ReactDOM.createPortal(
        <div className="modal-backdrop" onMouseDown={onClose}>
          <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 480 }}>
            <div className="modal-header">
              <div style={{ fontWeight: 700 }}>Announcement</div>
              <button className="modal-close" onClick={onClose}>×</button>
            </div>
            <div className="modal-body" style={{ padding: 28, textAlign: "center" }}>
              {err
                ? <div style={{ color: "#c0223a", fontSize: 13 }}>{err}</div>
                : <div style={{ color: "var(--ink-muted)", fontSize: 13 }}>Loading announcement…</div>}
            </div>
            <div className="modal-footer">
              <button className="btn" onClick={onClose}>Close</button>
            </div>
          </div>
        </div>,
        document.body
      );
    }
    const a = data.announcement || {};
    const readers = data.readers || [], pending = data.pending || [];
    const pct = a.total > 0 ? Math.round((a.read / a.total) * 100) : 0;

    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 620 }}>
          <div className="modal-header" style={{ alignItems: "center" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <span style={{ fontSize: 22 }}>{a.icon || "📢"}</span>
              <div>
                <div style={{ fontSize: 14.5, fontWeight: 800 }}>{a.title}</div>
                <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                  {a.audience_kind === "all" ? "All users" : "Specific users"} ·
                  {" "}{a.active === 1 ? <span style={{ color: "#0073ea", fontWeight: 700 }}>Active</span> : "Deactivated"}
                </div>
              </div>
            </div>
            <button className="modal-close" onClick={onClose}>×</button>
          </div>
          <div className="modal-body" style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {a.body && (
              <div style={{ background: "#f6f8fc", border: "1px solid var(--border-row,#eaecf1)", padding: 12, borderRadius: 10 }}>
                {String(a.body).split(/\n+/).map((p, i) => (
                  <p key={i} style={{ margin: "4px 0", fontSize: 13.5, lineHeight: 1.55, color: "#3b4254" }}>{p}</p>
                ))}
              </div>
            )}
            <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
              <div style={{ fontSize: 18, fontWeight: 800, color: "#0073ea" }}>{a.read} / {a.total}</div>
              <div style={{ flex: 1 }}>
                <div style={{ height: 6, background: "#eef0f5", borderRadius: 99, overflow: "hidden" }}>
                  <div style={{ width: pct + "%", height: "100%", background: "#0073ea" }}/>
                </div>
                <div style={{ fontSize: 11.5, color: "var(--ink-muted)", marginTop: 4 }}>{pct}% read</div>
              </div>
            </div>
            <div style={{ display: "flex", gap: 8, borderBottom: "1px solid var(--border-row,#eaecf1)" }}>
              <button onClick={() => setTab("readers")} style={S2.tab(tab === "readers")}>Read ({readers.length})</button>
              <button onClick={() => setTab("pending")} style={S2.tab(tab === "pending")}>Pending ({pending.length})</button>
            </div>
            <div style={{ maxHeight: 260, overflow: "auto" }}>
              {(tab === "readers" ? readers : pending).map(u => (
                <div key={u.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "6px 4px", borderBottom: "1px solid var(--border-row,#f1f3f7)" }}>
                  <div style={{ width: 26, height: 26, borderRadius: "50%", background: u.color || "#e7f0fe", color: "#0b5cad", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 800, overflow: "hidden" }}>
                    {u.avatar ? <img src={u.avatar} style={{ width: 26, height: 26, objectFit: "cover" }}/> : ((u.name || "?").split(/\s+/).map(s => s[0]).slice(0,2).join("").toUpperCase())}
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, color: "var(--ink-strong,#0f1729)", fontWeight: 600 }}>{u.name}</div>
                    <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>{u.email}</div>
                  </div>
                  {tab === "readers" && (
                    <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>{u.acked_at ? new Date(String(u.acked_at).replace(" ", "T")).toLocaleString() : ""}</div>
                  )}
                </div>
              ))}
              {(tab === "readers" ? readers : pending).length === 0 && (
                <div style={{ padding: 16, color: "var(--ink-muted)", textAlign: "center", fontSize: 13 }}>—</div>
              )}
            </div>
            {err && <div style={{ color: "#c0223a", fontSize: 12 }}>{err}</div>}
          </div>
          <div className="modal-footer">
            {a.active === 1
              ? <button className="btn" style={{ background: "#fff", color: "#c0223a", borderColor: "#f3c0c4" }} onClick={deactivate} disabled={busy}>Deactivate</button>
              : <button className="btn" style={{ background: "#fff", color: "#c0223a", borderColor: "#f3c0c4" }} onClick={remove} disabled={busy}>Delete</button>}
            <button className="btn btn-primary" onClick={onClose}>Close</button>
          </div>
        </div>
      </div>,
      document.body
    );
  }

  const S2 = {
    k:  { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: ".04em", color: "var(--ink-muted)" },
    in: { padding: "8px 10px", borderRadius: 7, border: "1px solid var(--bd, #d8dce4)", fontSize: 13, background: "#fff" },
    tab: (on) => ({
      padding: "8px 12px", border: 0, background: "none",
      borderBottom: on ? "2px solid #0073ea" : "2px solid transparent",
      color: on ? "var(--ink-strong,#0f1729)" : "var(--ink-muted)",
      fontWeight: 700, fontSize: 12.5, cursor: "pointer",
    }),
  };

  Object.assign(window, { AnnouncementHost, AnnouncementsAdminView });
})();
