// ============================================
// SOCIAL do Carom3D (carom3d-re/notes/social.md; docs/social.md)
// ============================================
// Painel recolhível (botão "Social" no canto) com:
//   - Canal: os "servidores × canais" do original (região × faixa), lista de quem está no canal e o chat
//     do canal com os comandos (/to /re /pf /where /join /joinid /in /ex /vl) e o chat rápido (macros).
//   - Amigos: pedidos, quem está online e onde, cochichar, convidar para a sala, bloqueados, opções.
//   - Recados: as conversas (cochicho de quem estava fora fica guardado).
// Ficha do jogador (/PF) em qualquer nick: window.MastershotSocial.openCard(nick).
// Convites, cochichos e pedidos de amizade chegam pela caixa privada (inbox) e aparecem como avisos.
// O quadro de avisos do admin: faixa (<AnnouncementBar/>) e popup uma vez por aviso.

const SOC_CHANNEL_KEY = "mastershot.social.channel";
const SOC_OPEN_KEY = "mastershot.social.open";
const SOC_ANN_SEEN_KEY = "mastershot.social.annSeen";
const SOC_REGION_OF = { BR: "SA", AR: "SA", CL: "SA", CO: "SA", PE: "SA", UY: "SA", PY: "SA", BO: "SA", EC: "SA", VE: "SA",
    US: "NA", CA: "NA", MX: "NA", PT: "EU", ES: "EU", FR: "EU", DE: "EU", IT: "EU", GB: "EU", NL: "EU", BE: "EU", PL: "EU",
    JP: "AS", KR: "AS", CN: "AS", TW: "AS", HK: "AS", SG: "AS", TH: "AS", VN: "AS", PH: "AS", ID: "AS", MY: "AS", IN: "AS" };
const SOC_TIER_NAMES = { livre: "Livre", iniciante: "Iniciante", avancado: "Avançado", expert: "Expert", vip: "VIP" };

const socApi = () => window.MastershotSocialApi;
// sons do Carom3D (assets/Sounds/carom3d): cochicho, mensagem (recado/convite/amizade)
function socSound(name) {
    try {
        const S = window.MastershotSettings;
        const v = Number(S?.get?.("audioMaster") ?? 0.7) * Number(S?.get?.("audioSfx") ?? 0.7);
        if (!(v > 0)) return;
        const a = new Audio(`assets/Sounds/carom3d/${name}.mp3`);
        a.volume = Math.min(1, v); a.play().catch(() => {});
    } catch {}
}
const socT = (s, v) => window.t(s, v);
const socChannelName = (id) => {
    if (!id) return "";
    const [region, tier] = id.split("-");
    return `${socT(window.MastershotData?.REGION_NAMES?.[region] || region)} · ${socT(SOC_TIER_NAMES[tier] || tier)}`;
};
const socErr = (code, extra = {}) => ({
    not_found: socT("Jogador não encontrado."),
    refused: socT("{nick} não está recebendo isso agora.", { nick: extra.nick || socT("O jogador") }),
    self: socT("Esse é você."),
    flood: socT("Chat bloqueado por {n} s (mensagens demais).", { n: extra.wait || 10 }),
    empty: socT("Mensagem vazia."),
    offline: socT("{nick} está offline.", { nick: extra.nick || socT("O jogador") }),
    busy: socT("{nick} está jogando agora.", { nick: extra.nick || socT("O jogador") }),
    not_in_room: socT("Você não está numa sala."),
    not_in_channel: socT("Entre num canal para falar."),
    channel_level: socT("Seu nível não dá acesso a este canal (níveis {a}–{b}).", { a: extra.minLevel, b: extra.maxLevel >= 1000 ? "∞" : extra.maxLevel }),
    channel_full: socT("Canal lotado."),
    limit: socT("Limite atingido."),
    no_request: socT("Não há pedido de amizade dessa pessoa."),
    unauthorized: socT("Entre na sua conta."),
    offline_api: socT("Social indisponível agora."),
    muted: socT("Você está sem chat no momento (moderação)."),
}[code] || socT("Não foi possível agora. Tente de novo."));
const socGuildErr = (code) => ({
    in_guild: socT("Você já está numa guilda."), level: socT("Criar guilda pede nível 5."), bad_name: socT("O nome precisa ter de 3 a 20 caracteres."),
    bad_tag: socT("A tag precisa ter de 2 a 4 letras ou números."), taken: socT("Nome ou tag já em uso."), not_officer: socT("Só líder e oficiais podem fazer isso."),
    not_leader: socT("Só o líder pode fazer isso."), leader_must_transfer: socT("Passe a liderança antes de sair (ou desfaça a guilda)."),
    target_in_guild: socT("Essa pessoa já está numa guilda."), full: socT("A guilda está cheia (50)."), no_invite: socT("Esse convite não existe mais."),
    no_request: socT("Esse pedido não existe mais."), not_member: socT("Essa pessoa não é da guilda."), no_guild: socT("Você não está numa guilda."),
}[code] || socErr(code));

// ---------------------------------------------------------------------------
// Estado compartilhado (um só por página) — as telas assinam com useSocial()
// ---------------------------------------------------------------------------
const SocialStore = (() => {
    let s = {
        active: false, me: null, inboxKey: null, options: { noWhispers: false, noInvites: false },
        friends: [], blocks: [], unread: 0,
        channels: [], channel: null, users: [], messages: [],
        convs: [], thread: null,                // thread = { with, messages }
        open: false, tab: "channel",
        status: "menu", room: null,             // situação para o pulso: 'menu' | 'lobby' | 'room'
        lastWhisper: null, toasts: [], card: null, invite: null,
        announcements: [],
        guild: null, guildInvites: [], guildRequests: [], guildMessages: [],   // a guilda (my_guild) e o chat dela
    };
    const subs = new Set();
    const set = (patch) => { s = { ...s, ...(typeof patch === "function" ? patch(s) : patch) }; subs.forEach((f) => f(s)); };
    let navigate = null, unsubInbox = null, unsubChat = null, unsubGuild = null, guildKey = null, beat = null, usersTimer = null, friendsTimer = null, toastSeq = 0;
    const seenDm = new Set();

    const sys = (text, kind = "system") => set((x) => ({ messages: [...x.messages, { id: "s" + (++toastSeq), kind, body: text, at: new Date().toISOString() }].slice(-150) }));
    function toast(t) {
        const id = ++toastSeq;
        set((x) => ({ toasts: [...x.toasts, { id, ...t }].slice(-4) }));
        if (!t.sticky) setTimeout(() => dismiss(id), t.ms || 6000);
        return id;
    }
    const dismiss = (id) => set((x) => ({ toasts: x.toasts.filter((t) => t.id !== id) }));

    async function refreshMe() {
        const api = socApi(); if (!api) return;
        const r = await api.me();
        set({ me: r.me, inboxKey: r.inboxKey, options: r.options, friends: r.friends || [], blocks: r.blocks || [], unread: r.unread || 0 });
        return r;
    }
    async function heartbeat() {
        const api = socApi(); if (!api || !s.active) return;
        try {
            const r = await api.heartbeat(s.channel, s.room ? "room" : s.status, s.room?.id, s.room?.name);
            if (r && r.ok === false) {
                // perdeu o acesso (nível) ou lotou: sai do canal e avisa
                sys(socErr(r.error, r));
                if (s.channel) leaveChannel();
            } else if (r) set({ unread: r.unread || 0 });
        } catch (e) { /* rede: tenta no próximo pulso */ }
    }
    async function loadUsers() {
        const api = socApi(); if (!api || !s.channel) return;
        try { set({ users: await api.channelUsers(s.channel) || [] }); } catch {}
    }
    async function loadChannels() {
        const api = socApi(); if (!api) return;
        try { set({ channels: await api.channels() || [] }); } catch {}
    }
    function leaveChannel() {
        unsubChat?.(); unsubChat = null;
        set({ channel: null, users: [], messages: [] });
        try { localStorage.removeItem(SOC_CHANNEL_KEY); } catch {}
    }
    async function joinChannel(id) {
        const api = socApi(); if (!api || !id) return false;
        const r = await api.heartbeat(id, s.room ? "room" : s.status, s.room?.id, s.room?.name).catch((e) => ({ ok: false, error: /unauthorized/.test(e.message) ? "unauthorized" : "offline_api" }));
        if (!r || r.ok === false) { toast({ kind: "error", text: socErr(r?.error, r || {}) }); return false; }
        unsubChat?.();
        set({ channel: id, users: [], messages: [] });
        try { localStorage.setItem(SOC_CHANNEL_KEY, id); } catch {}
        unsubChat = api.subscribeChannelChat(id, (m) => {
            if (s.blocks.some((b) => b.id === m.userId)) return;
            set((x) => x.messages.some((y) => y.id === m.id) ? {} : { messages: [...x.messages, m].slice(-150) });
        });
        try {
            const hist = await api.chatHistory(id) || [];
            set((x) => ({ messages: [...hist, ...x.messages.filter((m) => !hist.some((h) => h.id === m.id))].slice(-150) }));
        } catch {}
        loadUsers();
        return true;
    }
    function defaultChannel(me) {
        const region = SOC_REGION_OF[(me?.country || "").toUpperCase()] || "ALL";
        return `${region}-livre`;
    }

    function onDm(p) {
        if (seenDm.has(p.id)) return;
        seenDm.add(p.id);
        const mine = !!p.mine;
        const other = mine ? p.to : p.from;
        set((x) => ({
            messages: [...x.messages, { id: "dm" + p.id, kind: mine ? "whisper-out" : "whisper-in", nick: other.nick, country: other.country, userId: other.id, body: p.body, at: p.at }].slice(-150),
            lastWhisper: mine ? x.lastWhisper : other.nick,
        }));
        if (s.thread && s.thread.with?.id === other.id) {
            set((x) => ({ thread: { ...x.thread, messages: [...x.thread.messages, { id: p.id, mine, body: p.body, at: p.at }] } }));
            if (!mine) socApi()?.thread(other.id).catch(() => {});   // marca como lido
        } else if (!mine) {
            set((x) => ({ unread: x.unread + 1 }));
            if (!(s.open && s.tab === "channel")) toast({ kind: "dm", title: socT("Cochicho de {nick}", { nick: other.nick }), text: p.body, onClick: () => openThread(other) });
            socSound("cochicho");
        }
        if (s.tab === "memos" && s.open) loadConvs();
    }
    function onFriend(p) {
        socSound("mensagem");
        if (p.kind === "request") {
            toast({ kind: "friend", sticky: true, title: socT("Pedido de amizade"), text: socT("{nick} quer ser seu amigo.", { nick: p.from.nick }),
                actions: [[socT("Aceitar"), () => friendAction(p.from.nick, "accept")], [socT("Recusar"), () => friendAction(p.from.nick, "decline")]] });
        } else if (p.kind === "accepted") {
            toast({ kind: "friend", text: socT("{nick} aceitou seu pedido de amizade.", { nick: p.from.nick }) });
        }
        refreshMe().catch(() => {});
    }
    // ---- guilda (canal da guilda, /TOG, "Player %s has logged in") ----
    async function refreshGuild() {
        const api = socApi(); if (!api) return;
        try {
            const r = await api.myGuild();
            set({ guild: r.guild || null, guildInvites: r.invites || [], guildRequests: r.requests || [] });
            if (r.chatKey !== guildKey) {
                unsubGuild?.(); unsubGuild = null; guildKey = r.chatKey || null;
                set({ guildMessages: [] });
                if (guildKey) {
                    unsubGuild = api.subscribeGuild(guildKey, {
                        msg: (m) => set((x) => x.guildMessages.some((y) => y.id === m.id) ? {} : { guildMessages: [...x.guildMessages, m].slice(-150) }),
                        online: (p) => { if (p.who?.id !== s.me?.id) { toast({ kind: "guild", text: socT("{nick} entrou no jogo.", { nick: p.who.nick }), ms: 4000 }); socSound("guilda"); } },
                        notice: (p) => { set((x) => ({ guild: x.guild ? { ...x.guild, notice: p.notice } : x.guild })); toast({ kind: "guild", title: socT("Aviso da guilda"), text: p.notice || socT("(aviso apagado)") }); },
                        member: () => refreshGuild(),
                    });
                    const hist = await api.chatHistory("guild:" + r.guild.id).catch(() => []);
                    set({ guildMessages: hist || [] });
                }
            }
        } catch (e) { console.warn("[guilda]", e.message); }
    }
    async function guildAction(action, target) {
        try {
            const r = await socApi().guildAction(action, target);
            if (r?.ok === false) { toast({ kind: "error", text: socGuildErr(r.error) }); return r; }
            await refreshGuild();
            return r;
        } catch (e) { toast({ kind: "error", text: socErr("x") }); }
    }
    function onGuild(p) {
        socSound("mensagem");
        if (p.kind === "invite") toast({ kind: "guild", sticky: true, title: socT("Convite de guilda"),
            text: socT("{nick} convidou você para a guilda [{tag}] {name}.", { nick: p.from.nick, tag: p.guild.tag, name: p.guild.name }),
            actions: [[socT("Entrar"), () => guildAction("accept", p.guild.id)], [socT("Recusar"), () => guildAction("decline", p.guild.id)]] });
        else if (p.kind === "request") toast({ kind: "guild", text: socT("{nick} pediu para entrar na guilda.", { nick: p.from.nick }), onClick: () => SocialStore.setOpen(true, "guild") });
        else if (p.kind === "approved") toast({ kind: "guild", text: socT("Você entrou na guilda [{tag}] {name}.", { tag: p.guild.tag, name: p.guild.name }) });
        else if (p.kind === "kicked") toast({ kind: "error", text: socT("Você foi removido da guilda.") });
        refreshGuild();
    }
    // ---- torneio (LG_MATCHREADY) e sanções ----
    function onTournament(p) {
        socSound("mensagem");
        const go = () => navigate?.("tournaments", { id: p.tournament?.id });
        if (p.kind === "ready") toast({ kind: "friend", sticky: true, title: socT("Torneio"),
            text: socT("{nick} abriu a sala da sua partida em {name}.", { nick: p.from?.nick || "", name: p.tournament?.name || "" }),
            actions: [[socT("Jogar"), () => enterRoomById(p.room.id)], [socT("Ver"), go]] });
        else if (p.kind === "started") toast({ kind: "friend", title: socT("Torneio"), text: socT("{name} começou.", { name: p.tournament.name }), onClick: go });
        else if (p.kind === "round") toast({ kind: "friend", title: socT("Torneio"), text: socT("{name}: rodada {n} liberada.", { name: p.tournament.name, n: p.round }), onClick: go });
        else if (p.kind === "finished") toast({ kind: "friend", title: socT("Torneio"), text: socT("{name} terminou.", { name: p.tournament.name }), onClick: go });
    }
    function onSanction(p) {
        const until = p.until ? new Date(p.until).toLocaleString() : null;
        const msg = p.kind === "mute" ? socT("Você está sem chat até {d}.", { d: until })
            : p.kind === "suspend" ? socT("Sua conta está suspensa das salas e do chat até {d}.", { d: until })
            : socT("Sua conta foi banida.");
        window.MastershotAlert({ title: socT("Moderação"), message: msg + (p.reason ? "\n" + p.reason : "") });
    }

    function onInvite(p) {
        // "%s invited you. [Room : %s] Do you want to join?" — some sozinho em 30 s
        set({ invite: { ...p, until: Date.now() + 30000 } });
        socSound("mensagem");
        setTimeout(() => { if (s.invite && s.invite.until <= Date.now()) set({ invite: null }); }, 30500);
    }

    async function start() {
        if (s.active) return;
        const api = socApi();
        if (!api || !window.MastershotAuth?.isAuthenticated?.()) return;
        set({ active: true });
        try {
            const r = await refreshMe();
            if (!r?.me?.country) {
                const cc = await api.detectCountry();
                if (cc) { await api.setCountry(cc).catch(() => {}); refreshMe().catch(() => {}); }
            }
            unsubInbox = api.subscribeInbox(r.inboxKey, { dm: onDm, friend: onFriend, invite: onInvite, guild: onGuild, tournament: onTournament, sanction: onSanction });
            refreshGuild();
            let saved = null; try { saved = localStorage.getItem(SOC_CHANNEL_KEY); } catch {}
            if (!(await joinChannel(saved || defaultChannel(r.me))) && saved) await joinChannel(defaultChannel(r.me));
        } catch (e) {
            console.warn("[social]", e.message);
        }
        loadChannels();
        loadAnnouncements();
        beat = setInterval(heartbeat, 30000);
        usersTimer = setInterval(() => { if (s.open && s.tab === "channel") loadUsers(); }, 20000);
        friendsTimer = setInterval(() => { if (s.open && s.tab === "friends") refreshMe().catch(() => {}); }, 45000);
    }
    function stop() {
        unsubInbox?.(); unsubChat?.(); unsubGuild?.(); unsubInbox = unsubChat = unsubGuild = null; guildKey = null;
        clearInterval(beat); clearInterval(usersTimer); clearInterval(friendsTimer);
        set({ active: false, me: null, inboxKey: null, friends: [], blocks: [], channel: null, users: [], messages: [], convs: [], thread: null, unread: 0 });
    }

    async function loadAnnouncements() {
        try {
            const list = await socApi()?.announcements() || [];
            set({ announcements: list });
            let seen = []; try { seen = JSON.parse(localStorage.getItem(SOC_ANN_SEEN_KEY) || "[]"); } catch {}
            const pop = list.find((a) => a.kind === "popup" && !seen.includes(a.id));
            if (pop) {
                try { localStorage.setItem(SOC_ANN_SEEN_KEY, JSON.stringify([...seen, pop.id].slice(-50))); } catch {}
                window.MastershotAlert({ title: socT("Aviso"), message: pop.body });
            }
        } catch {}
    }
    async function loadConvs() {
        try { set({ convs: await socApi().conversations() || [] }); } catch {}
    }
    async function openThread(who) {
        set({ open: true, tab: "memos" });
        try {
            const r = await socApi().thread(who.id || who.nick);
            if (r?.ok) {
                set({ thread: { with: r.with, messages: r.messages } });
                refreshMe().catch(() => {});
            }
        } catch {}
    }
    async function whisper(target, body) {
        try {
            const r = await socApi().whisper(target, body);
            if (!r?.ok) { sys(socErr(r?.error, { ...r, nick: target })); return false; }
            onDm({ id: r.id, mine: true, from: s.me, to: r.to, body, at: new Date().toISOString() });
            if (!r.online) sys(socT("{nick} está offline: a mensagem ficou como recado.", { nick: r.to.nick }));
            return true;
        } catch (e) { sys(socErr(/unauthorized/.test(e.message) ? "unauthorized" : /muted/.test(e.message) ? "muted" : "x")); return false; }
    }
    async function friendAction(target, action) {
        try {
            const r = await socApi().friend(target, action);
            if (!r?.ok) toast({ kind: "error", text: socErr(r?.error, { nick: target }) });
            else if (action === "request") toast({ kind: "friend", text: r.state === "friend" ? socT("Agora vocês são amigos.") : socT("Pedido enviado a {nick}.", { nick: target }) });
            await refreshMe();
            if (s.card && (s.card.nick === target || s.card.id === target)) openCard(s.card.id);
            return r;
        } catch (e) { toast({ kind: "error", text: socErr("x") }); }
    }
    async function block(target, on) {
        try {
            const r = await socApi().block(target, on);
            if (!r?.ok) { toast({ kind: "error", text: socErr(r?.error, { nick: target }) }); return; }
            sys(on ? socT("{nick} foi bloqueado.", { nick: r.nick }) : socT("{nick} foi desbloqueado.", { nick: r.nick }));
            await refreshMe();
            if (on) set((x) => ({ messages: x.messages.filter((m) => m.nick !== r.nick) }));
            if (s.card && s.card.nick === r.nick) openCard(s.card.id);
        } catch { toast({ kind: "error", text: socErr("x") }); }
    }
    // /ACCUSE e o botão Denunciar: vai com o que a pessoa escreveu nas últimas 24 h
    async function report(target, reason) {
        if (!reason) {
            reason = await window.MastershotPrompt({ title: socT("Denunciar {nick}", { nick: target }), message: socT("Conte o que aconteceu (opcional). O histórico do chat vai junto."), maxLength: 300 });
            if (reason == null) return;
        }
        try {
            const r = await socApi().report(target, reason);
            toast(r?.ok ? { kind: "friend", text: r.already ? socT("Você já denunciou {nick}; a moderação vai analisar.", { nick: target }) : socT("Denúncia enviada. Obrigado.") }
                        : { kind: "error", text: r?.error === "limit" ? socT("Denúncias demais hoje.") : socErr(r?.error, { nick: target }) });
        } catch { toast({ kind: "error", text: socErr("x") }); }
    }
    async function invite(target) {
        if (!s.room) { toast({ kind: "error", text: socErr("not_in_room") }); return; }
        try {
            const r = await socApi().invite(target, s.room.id);
            toast(r?.ok ? { kind: "friend", text: socT("Convite enviado a {nick}.", { nick: target }) } : { kind: "error", text: socErr(r?.error, { ...r, nick: target }) });
        } catch { toast({ kind: "error", text: socErr("x") }); }
    }
    async function openCard(target) {
        set({ card: { loading: true } });
        try {
            const r = await socApi().card(target);
            set({ card: r?.ok ? r : { error: socErr(r?.error) } });
        } catch { set({ card: { error: socErr("x") } }); }
    }
    async function enterRoomById(roomId, password) {
        const Lobby = window.MastershotLobby;
        try {
            const room = await Lobby.getRoom(roomId);
            if (!room) { sys(socT("A sala não existe mais.")); return; }
            set({ open: false, invite: null });
            if (password) room._password = password;
            await window.MastershotEnterRoom(room, navigate);
        } catch (e) { sys(socT("Não foi possível entrar: {msg}", { msg: e.message })); }
    }

    // ---- o que se digita no chat ----
    async function send(text) {
        const api = socApi();
        const c = api.parseChatCommand(text);
        switch (c.cmd) {
            case "say": {
                if (!c.body) return;
                if (!s.channel) { sys(socErr("not_in_channel")); return; }
                const r = await api.chatSend(s.channel, c.body).catch((e) => ({ ok: false, error: /muted/.test(e.message) ? "muted" : "x" }));
                if (!r?.ok) sys(socErr(r?.error, r || {}));
                return;
            }
            case "to": await whisper(c.target, c.body); return;
            case "re":
                if (!s.lastWhisper) { sys(socT("Ninguém cochichou com você ainda.")); return; }
                await whisper(s.lastWhisper, c.body); return;
            case "pf": openCard(c.target); return;
            case "where": case "joinid": {
                const r = await api.where(c.target).catch(() => null);
                if (!r?.ok) { sys(socErr(r?.error || "x")); return; }
                if (c.cmd === "joinid") {
                    if (r.online && r.roomId) enterRoomById(r.roomId);
                    else sys(socT("{nick} não está numa sala.", { nick: r.nick }));
                    return;
                }
                sys(!r.online ? socT("{nick} está offline.", { nick: r.nick })
                    : r.status === "playing" ? socT("{nick} está jogando{room}.", { nick: r.nick, room: r.roomName ? " — " + r.roomName : "" })
                    : r.roomName ? socT("{nick} está na sala {room}.", { nick: r.nick, room: r.roomName })
                    : r.channel ? socT("{nick} está no canal {channel}.", { nick: r.nick, channel: socChannelName(r.channel) })
                    : socT("{nick} está online.", { nick: r.nick }));
                return;
            }
            case "join": {
                const list = await window.MastershotLobby.listOpenRooms().catch(() => []);
                const byName = (n) => list.find((r) => (r.name || "").toLowerCase() === n.toLowerCase());
                let room = byName(c.text), pass = null;
                if (!room && c.text.includes(" ")) { const i = c.text.lastIndexOf(" "); room = byName(c.text.slice(0, i).trim()); pass = c.text.slice(i + 1); }
                if (!room) { sys(socT("Sala não encontrada: {name}", { name: c.text })); return; }
                enterRoomById(room.id, pass);
                return;
            }
            case "in": block(c.target, true); return;
            case "ex": block(c.target, false); return;
            case "friend": friendAction(c.target, "request"); return;
            case "invite": invite(c.target); return;
            case "vl":
                window.MastershotSettings?.set("audioMaster", c.value / 100);
                sys(socT("Volume: {n}", { n: c.value }));
                return;
            case "tog": {   // chat da guilda
                if (!s.guild) { sys(socT("Você não está numa guilda.")); return; }
                const r = await api.chatSend("guild:" + s.guild.id, c.body).catch((e) => ({ ok: false, error: /muted/.test(e.message) ? "muted" : "x" }));
                if (!r?.ok) sys(socErr(r?.error, r || {}));
                else sys(socT("[Guilda] {msg}", { msg: c.body }), "guild");
                return;
            }
            case "accuse": report(c.target, c.reason); return;
            case "help":
                for (const [u, d] of api.COMMAND_HELP) sys(`${u} — ${socT(d)}`, "help");
                return;
            case "usage": {
                const h = api.COMMAND_HELP.find(([u]) => u.startsWith("/" + c.of) || u.includes(" /" + c.of));
                sys(h ? socT("Uso: {u}", { u: h[0] }) : socT("Faltou um argumento."));
                return;
            }
            default: sys(socT("Comando desconhecido: /{name} — digite /? para ver os comandos.", { name: c.name }));
        }
    }

    return {
        get: () => s, set, subscribe: (f) => { subs.add(f); return () => subs.delete(f); },
        setNavigate: (n) => { navigate = n; },
        start, stop, joinChannel, leaveChannel, loadChannels, loadUsers, loadConvs, refreshMe, openThread, whisper, friendAction, block,
        invite, openCard, enterRoomById, send, toast, dismiss, sys,
        setOpen: (open, tab) => { set((x) => ({ open, tab: tab || x.tab })); try { localStorage.setItem(SOC_OPEN_KEY, open ? "1" : "0"); } catch {} },
        setRoom: (room) => { set({ room: room ? { id: room.id, name: room.name } : null }); heartbeat(); },
        setStatus: (status) => { set({ status }); },
        setOptions: async (opts) => { try { await socApi().setOptions(opts); await refreshMe(); } catch {} },
        sendQuick: (cat, n) => send(socApi().quickCode(cat, n)),
        loadAnnouncements, refreshGuild, guildAction, report,
        guildSend: async (text) => {
            const r = await socApi().chatSend("guild:" + s.guild.id, text).catch((e) => ({ ok: false, error: /muted/.test(e.message) ? "muted" : "x" }));
            if (!r?.ok) toast({ kind: "error", text: socErr(r?.error, r || {}) });
        },
    };
})();

function useSocial() {
    const [st, setSt] = React.useState(SocialStore.get());
    React.useEffect(() => SocialStore.subscribe(setSt), []);
    return st;
}

// ---------------------------------------------------------------------------
// Peças
// ---------------------------------------------------------------------------
function SocNick({ nick, country, level, id, vip, guild, onClick }) {
    return (
        <button className={"soc-nick" + (vip ? " vip" : "")} onClick={onClick || (() => SocialStore.openCard(id || nick))} title={socT("Ver ficha")}>
            <CountryFlag code={country} size={14}/>
            {guild?.tag && <span className="soc-gtag" style={{ color: guild.color || "#c9a55c" }}>[{guild.tag}]</span>}
            <span>{vip ? "★ " : ""}{nick}</span>
            {level != null && <span className="soc-lv">{level}</span>}
        </button>
    );
}

function socQuickText(body) {
    const q = socApi()?.quickPhrase(body);
    return q ? socT(q.text) : null;
}

function SocMessage({ m }) {
    const time = m.at ? new Date(m.at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "";
    if (m.kind === "system" || m.kind === "help" || m.kind === "guild") return <div className={"soc-msg soc-sys" + (m.kind === "help" ? " soc-help" : m.kind === "guild" ? " soc-guild" : "")}>{m.body}</div>;
    if (m.kind === "whisper-in" || m.kind === "whisper-out") {
        return (
            <div className="soc-msg soc-whisper" title={time}>
                <span className="soc-wtag">{m.kind === "whisper-in" ? socT("de") : socT("para")}</span>
                <SocNick nick={m.nick} country={m.country} id={m.userId}/>
                <span className="soc-body">{m.body}</span>
                {m.kind === "whisper-in" && <button className="soc-re" onClick={() => document.dispatchEvent(new CustomEvent("soc:reply", { detail: m.nick }))}>↩</button>}
            </div>
        );
    }
    const quick = m.kind === "quick" ? socQuickText(m.body) : null;
    return (
        <div className="soc-msg" title={time}>
            <SocNick nick={m.nick} country={m.country} level={m.level} id={m.userId} vip={m.vip}/>
            <span className={"soc-body" + (quick ? " soc-quick" : "")}>{quick ? "⚡ " + quick : m.body}</span>
        </div>
    );
}

function SocQuickMenu({ onPick, onClose }) {
    const [cat, setCat] = React.useState(1);
    const QUICK = socApi()?.QUICK_CHAT || [];
    return (
        <div className="soc-quick-menu" onMouseLeave={onClose}>
            <div className="soc-quick-cats">
                {QUICK.map(([name], i) => (
                    <button key={i} className={cat === i + 1 ? "on" : ""} onMouseEnter={() => setCat(i + 1)} onClick={() => setCat(i + 1)}>{socT(name)}</button>
                ))}
            </div>
            <div className="soc-quick-list">
                {(QUICK[cat - 1]?.[1] || []).map((p, j) => (
                    <button key={j} onClick={() => { onPick(cat, j + 1); onClose(); }}>{socT(p)}</button>
                ))}
            </div>
        </div>
    );
}

function SocInput({ placeholder, onSend, quick = true }) {
    const [text, setText] = React.useState("");
    const [qOpen, setQOpen] = React.useState(false);
    const ref = React.useRef(null);
    React.useEffect(() => {
        const h = (e) => { setText(`/to ${e.detail} `); ref.current?.focus(); };
        document.addEventListener("soc:reply", h);
        return () => document.removeEventListener("soc:reply", h);
    }, []);
    const submit = (e) => { e.preventDefault(); const t = text.trim(); if (!t) return; setText(""); onSend(t); };
    return (
        <form className="soc-input" onSubmit={submit}>
            {quick && <button type="button" className="soc-qbtn" title={socT("Chat rápido")} onClick={() => setQOpen((v) => !v)}>⚡</button>}
            {qOpen && <SocQuickMenu onPick={(c, n) => SocialStore.sendQuick(c, n)} onClose={() => setQOpen(false)}/>}
            <input ref={ref} value={text} maxLength={300} placeholder={placeholder} onChange={(e) => setText(e.target.value)}/>
            <button type="submit" className="soc-send">{socT("Enviar")}</button>
        </form>
    );
}

function socWhereText(u) {
    if (!u.online) return socT("Offline");
    if (u.status === "playing") return socT("Jogando") + (u.roomName ? ` · ${u.roomName}` : "");
    if (u.roomName) return socT("Na sala {room}", { room: u.roomName });
    if (u.channel) return socChannelName(u.channel);
    return socT("Online");
}

// ---- aba Canal ----
function SocChannelTab({ st }) {
    const [picking, setPicking] = React.useState(!st.channel);
    const [showUsers, setShowUsers] = React.useState(() => { try { return localStorage.getItem("mastershot.social.users") !== "0"; } catch { return true; } });
    const listRef = React.useRef(null);
    React.useEffect(() => { const el = listRef.current; if (el) el.scrollTop = el.scrollHeight; }, [st.messages.length, picking]);
    React.useEffect(() => { if (picking) SocialStore.loadChannels(); else SocialStore.loadUsers(); }, [picking]);
    const toggleUsers = () => { setShowUsers((v) => { try { localStorage.setItem("mastershot.social.users", v ? "0" : "1"); } catch {} return !v; }); };

    if (picking) {
        const byRegion = {};
        for (const c of st.channels) (byRegion[c.region] = byRegion[c.region] || []).push(c);
        return (
            <div className="soc-pane">
                <div className="soc-sub">{socT("Escolha um canal")}</div>
                <div className="soc-scroll">
                    {Object.entries(byRegion).map(([region, list]) => (
                        <div key={region} className="soc-region">
                            <div className="soc-region-name">{socT(window.MastershotData?.REGION_NAMES?.[region] || region)}</div>
                            {list.map((c) => (
                                <button key={c.id} className={"soc-chan" + (c.id === st.channel ? " on" : "")} disabled={!c.allowed || c.online >= c.capacity}
                                    onClick={async () => { if (await SocialStore.joinChannel(c.id)) setPicking(false); }}>
                                    <span>{socT(SOC_TIER_NAMES[c.tier] || c.tier)}</span>
                                    <span className="soc-chan-lv">{c.maxLevel >= 1000 ? (c.minLevel > 1 ? socT("Nv {n}+", { n: c.minLevel }) : socT("Todos")) : socT("Nv {a}–{b}", { a: c.minLevel, b: c.maxLevel })}</span>
                                    <span className="soc-chan-n">{!c.allowed ? "🔒" : c.online >= c.capacity ? socT("Lotado") : `${c.online}/${c.capacity}`}</span>
                                </button>
                            ))}
                        </div>
                    ))}
                </div>
                {st.channel && <button className="soc-link" onClick={() => setPicking(false)}>← {socT("Voltar ao canal")}</button>}
            </div>
        );
    }
    return (
        <div className="soc-pane">
            <div className="soc-chanbar">
                <button className="soc-chan-cur" onClick={() => setPicking(true)} title={socT("Trocar de canal")}>{socChannelName(st.channel)} ▾</button>
                <button className="soc-users-toggle" onClick={toggleUsers}>{socT("{n} no canal", { n: st.users.length })} {showUsers ? "▴" : "▾"}</button>
            </div>
            {showUsers && (
                <div className="soc-users">
                    {st.users.map((u) => (
                        <div key={u.id} className="soc-user">
                            <SocNick nick={u.nick} country={u.country} level={u.level} id={u.id} vip={u.vip} guild={u.guild}/>
                            <span className="soc-where">{u.status === "playing" ? "🎱" : u.roomName ? "🚪" : ""}</span>
                        </div>
                    ))}
                </div>
            )}
            <div className="soc-msgs" ref={listRef}>
                {st.messages.length === 0 && <div className="soc-empty">{socT("Ninguém falou ainda. Digite /? para ver os comandos.")}</div>}
                {st.messages.map((m) => <SocMessage key={m.id} m={m}/>)}
            </div>
            <SocInput placeholder={socT("Mensagem ou /comando")} onSend={(t) => SocialStore.send(t)}/>
        </div>
    );
}

// ---- aba Amigos ----
function SocFriendsTab({ st }) {
    const [nick, setNick] = React.useState("");
    const [showBlocks, setShowBlocks] = React.useState(false);
    React.useEffect(() => { SocialStore.refreshMe().catch(() => {}); }, []);
    const incoming = st.friends.filter((f) => f.state === "incoming");
    const friends = st.friends.filter((f) => f.state === "friend").sort((a, b) => (b.online - a.online) || a.nick.localeCompare(b.nick));
    const outgoing = st.friends.filter((f) => f.state === "outgoing");
    return (
        <div className="soc-pane">
            <form className="soc-add" onSubmit={(e) => { e.preventDefault(); const n = nick.trim(); if (n) { SocialStore.friendAction(n, "request"); setNick(""); } }}>
                <input value={nick} placeholder={socT("Adicionar amigo (nick)")} onChange={(e) => setNick(e.target.value)}/>
                <button type="submit">+</button>
            </form>
            <div className="soc-scroll">
                {incoming.length > 0 && <div className="soc-sub">{socT("Pedidos")}</div>}
                {incoming.map((f) => (
                    <div key={f.id} className="soc-friend">
                        <SocNick nick={f.nick} country={f.country} level={f.level} id={f.id} vip={f.vip} guild={f.guild}/>
                        <div className="soc-actions">
                            <button onClick={() => SocialStore.friendAction(f.nick, "accept")}>{socT("Aceitar")}</button>
                            <button className="ghost" onClick={() => SocialStore.friendAction(f.nick, "decline")}>{socT("Recusar")}</button>
                        </div>
                    </div>
                ))}
                <div className="soc-sub">{socT("Amigos ({n})", { n: friends.length })}</div>
                {friends.length === 0 && <div className="soc-empty">{socT("Nenhum amigo ainda. Adicione pelo nick ou pela ficha do jogador.")}</div>}
                {friends.map((f) => (
                    <div key={f.id} className={"soc-friend" + (f.online ? " online" : "")}>
                        <span className="soc-dot"/>
                        <div className="soc-friend-main">
                            <SocNick nick={f.nick} country={f.country} level={f.level} id={f.id} vip={f.vip} guild={f.guild}/>
                            <div className="soc-where-text">{socWhereText(f)}</div>
                        </div>
                        <div className="soc-actions">
                            <button title={socT("Cochichar")} onClick={() => SocialStore.openThread(f)}>✉</button>
                            {st.room && f.online && f.status !== "playing" && <button title={socT("Convidar para a sala")} onClick={() => SocialStore.invite(f.nick)}>＋🚪</button>}
                            {f.online && f.roomId && !st.room && <button title={socT("Entrar na sala dele")} onClick={() => SocialStore.enterRoomById(f.roomId)}>→</button>}
                        </div>
                    </div>
                ))}
                {outgoing.length > 0 && <div className="soc-sub">{socT("Aguardando resposta")}</div>}
                {outgoing.map((f) => (
                    <div key={f.id} className="soc-friend">
                        <SocNick nick={f.nick} country={f.country} level={f.level} id={f.id} vip={f.vip} guild={f.guild}/>
                        <div className="soc-actions"><button className="ghost" onClick={() => SocialStore.friendAction(f.nick, "remove")}>{socT("Cancelar")}</button></div>
                    </div>
                ))}
                <div className="soc-sub">{socT("Privacidade")}</div>
                <label className="soc-opt"><input type="checkbox" checked={!!st.options.noWhispers} onChange={(e) => SocialStore.setOptions({ noWhispers: e.target.checked })}/> {socT("Não receber cochichos (amigos passam)")}</label>
                <label className="soc-opt"><input type="checkbox" checked={!!st.options.noInvites} onChange={(e) => SocialStore.setOptions({ noInvites: e.target.checked })}/> {socT("Não receber convites (amigos passam)")}</label>
                <button className="soc-link" onClick={() => setShowBlocks((v) => !v)}>{socT("Bloqueados ({n})", { n: st.blocks.length })} {showBlocks ? "▴" : "▾"}</button>
                {showBlocks && st.blocks.map((b) => (
                    <div key={b.id} className="soc-friend">
                        <span className="soc-blocked">{b.nick}</span>
                        <div className="soc-actions"><button className="ghost" onClick={() => SocialStore.block(b.nick, false)}>{socT("Desbloquear")}</button></div>
                    </div>
                ))}
            </div>
        </div>
    );
}

// ---- aba Recados ----
function SocMemosTab({ st }) {
    const listRef = React.useRef(null);
    React.useEffect(() => { if (!st.thread) SocialStore.loadConvs(); }, [st.thread]);
    React.useEffect(() => { const el = listRef.current; if (el) el.scrollTop = el.scrollHeight; }, [st.thread?.messages?.length]);
    if (st.thread) {
        const w = st.thread.with;
        return (
            <div className="soc-pane">
                <div className="soc-chanbar">
                    <button className="soc-link" onClick={() => SocialStore.set({ thread: null })}>←</button>
                    <SocNick nick={w.nick} country={w.country} level={w.level} id={w.id}/>
                    <span className="soc-where-text">{socWhereText(w)}</span>
                </div>
                <div className="soc-msgs" ref={listRef}>
                    {st.thread.messages.length === 0 && <div className="soc-empty">{socT("Comece a conversa.")}</div>}
                    {st.thread.messages.map((m) => (
                        <div key={m.id} className={"soc-bubble" + (m.mine ? " mine" : "")}>
                            <span>{m.body}</span>
                            <small>{new Date(m.at).toLocaleString([], { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" })}</small>
                        </div>
                    ))}
                </div>
                <SocInput quick={false} placeholder={socT("Mensagem para {nick}", { nick: w.nick })} onSend={(t) => SocialStore.whisper(w.nick, t)}/>
            </div>
        );
    }
    return (
        <div className="soc-pane">
            <div className="soc-scroll">
                {st.convs.length === 0 && <div className="soc-empty">{socT("Nenhum recado. Cochichos para quem está fora ficam guardados aqui.")}</div>}
                {st.convs.map((c) => (
                    <button key={c.id} className="soc-conv" onClick={() => SocialStore.openThread(c)}>
                        <span className="soc-conv-top"><CountryFlag code={c.country} size={14}/> <b>{c.nick}</b>{c.unread > 0 && <span className="soc-badge">{c.unread}</span>}</span>
                        <span className="soc-conv-last">{c.mine ? socT("Você: ") : ""}{c.last}</span>
                    </button>
                ))}
            </div>
        </div>
    );
}

// ---- aba Guilda ----
const SOC_ROLE = { leader: "Líder", officer: "Oficial", member: "Membro" };
function SocGuildTab({ st }) {
    const g = st.guild;
    const [form, setForm] = React.useState({ name: "", tag: "", color: "#c9a55c" });
    const [search, setSearch] = React.useState("");
    const [list, setList] = React.useState(null);
    const [nick, setNick] = React.useState("");
    const [view, setView] = React.useState("chat");   // chat | members
    const listRef = React.useRef(null);
    React.useEffect(() => { SocialStore.refreshGuild(); }, []);
    React.useEffect(() => { if (!g) socApi()?.guildList(search || null).then(setList).catch(() => setList([])); }, [g?.id, search]);
    React.useEffect(() => { const el = listRef.current; if (el) el.scrollTop = el.scrollHeight; }, [st.guildMessages.length, view]);
    const officer = g && (g.myRole === "leader" || g.myRole === "officer");

    if (!g) {
        return (
            <div className="soc-pane">
                <div className="soc-scroll">
                    {st.guildInvites.length > 0 && <div className="soc-sub">{socT("Convites")}</div>}
                    {st.guildInvites.map((i) => (
                        <div key={i.id} className="soc-friend">
                            <div className="soc-friend-main"><b style={{ color: i.color }}>[{i.tag}]</b> {i.name}<div className="soc-where-text">{socT("de {nick}", { nick: i.from || "?" })}</div></div>
                            <div className="soc-actions">
                                <button onClick={() => SocialStore.guildAction("accept", i.id)}>{socT("Entrar")}</button>
                                <button className="ghost" onClick={() => SocialStore.guildAction("decline", i.id)}>{socT("Recusar")}</button>
                            </div>
                        </div>
                    ))}
                    <div className="soc-sub">{socT("Criar guilda")}</div>
                    <form className="soc-gform" onSubmit={async (e) => { e.preventDefault(); const r = await socApi().guildCreate(form.name.trim(), form.tag.trim(), form.color).catch(() => ({ ok: false }));
                        if (r?.ok === false) SocialStore.toast({ kind: "error", text: socGuildErr(r.error) }); else SocialStore.refreshGuild(); }}>
                        <input value={form.name} maxLength={20} placeholder={socT("Nome (3–20)")} onChange={(e) => setForm({ ...form, name: e.target.value })}/>
                        <input value={form.tag} maxLength={4} placeholder={socT("Tag")} style={{ width: 70, textTransform: "uppercase" }} onChange={(e) => setForm({ ...form, tag: e.target.value })}/>
                        <input type="color" value={form.color} onChange={(e) => setForm({ ...form, color: e.target.value })} title={socT("Cor")}/>
                        <button type="submit">{socT("Criar")}</button>
                    </form>
                    <div className="soc-where-text" style={{ marginTop: 4 }}>{socT("Pede nível 5. Até 50 membros.")}</div>
                    <div className="soc-sub">{socT("Guildas")}</div>
                    <input className="soc-gsearch" value={search} placeholder={socT("Buscar pelo nome ou tag")} onChange={(e) => setSearch(e.target.value)}/>
                    {list === null && <div className="soc-empty">{socT("Carregando…")}</div>}
                    {list && list.length === 0 && <div className="soc-empty">{socT("Nenhuma guilda ainda.")}</div>}
                    {list && list.map((x, i) => (
                        <div key={x.id} className="soc-friend">
                            <span className="soc-rank">{i + 1}</span>
                            <div className="soc-friend-main"><b style={{ color: x.color }}>[{x.tag}]</b> {x.name}
                                <div className="soc-where-text">{socT("{n} membros · {p} pontos na temporada", { n: x.members, p: Number(x.points).toLocaleString() })}</div></div>
                            <div className="soc-actions"><button className="ghost" onClick={() => SocialStore.guildAction("apply", x.id).then((r) => r?.ok && SocialStore.toast({ kind: "guild", text: socT("Pedido enviado.") }))}>{socT("Pedir para entrar")}</button></div>
                        </div>
                    ))}
                </div>
            </div>
        );
    }
    const online = g.members.filter((m) => m.online).length;
    return (
        <div className="soc-pane">
            <div className="soc-ghead">
                <div><b style={{ color: g.color }}>[{g.tag}]</b> {g.name}</div>
                <div className="soc-where-text">{socT("{o}/{n} online · {p} pontos na temporada", { o: online, n: g.members.length, p: Number(g.seasonPoints).toLocaleString() })} · {socT(SOC_ROLE[g.myRole] || g.myRole)}</div>
                {g.notice && <div className="soc-gnotice">📌 {g.notice}</div>}
                <div className="soc-gtabs">
                    <button className={view === "chat" ? "on" : ""} onClick={() => setView("chat")}>{socT("Chat")}</button>
                    <button className={view === "members" ? "on" : ""} onClick={() => setView("members")}>{socT("Membros")}{st.guildRequests.length ? ` (${st.guildRequests.length})` : ""}</button>
                </div>
            </div>
            {view === "chat" ? (<>
                <div className="soc-msgs" ref={listRef}>
                    {st.guildMessages.length === 0 && <div className="soc-empty">{socT("O chat da guilda é só dos membros. Fora daqui, /tog manda mensagem para ela.")}</div>}
                    {st.guildMessages.map((m) => <SocMessage key={m.id} m={m}/>)}
                </div>
                <SocInput placeholder={socT("Mensagem para a guilda")} onSend={(t) => SocialStore.guildSend(t)}/>
            </>) : (
                <div className="soc-scroll">
                    {officer && st.guildRequests.length > 0 && <div className="soc-sub">{socT("Pedidos para entrar")}</div>}
                    {officer && st.guildRequests.map((r) => (
                        <div key={r.id} className="soc-friend">
                            <SocNick nick={r.nick} country={r.country} level={r.level} id={r.id}/>
                            <div className="soc-actions">
                                <button onClick={() => SocialStore.guildAction("approve", r.nick)}>{socT("Aceitar")}</button>
                                <button className="ghost" onClick={() => SocialStore.guildAction("reject", r.nick)}>{socT("Recusar")}</button>
                            </div>
                        </div>
                    ))}
                    {officer && (
                        <form className="soc-add" style={{ padding: "8px 0" }} onSubmit={(e) => { e.preventDefault(); if (nick.trim()) { SocialStore.guildAction("invite", nick.trim()).then((r) => r?.ok && SocialStore.toast({ kind: "guild", text: socT("Convite enviado a {nick}.", { nick }) })); setNick(""); } }}>
                            <input value={nick} placeholder={socT("Convidar (nick)")} onChange={(e) => setNick(e.target.value)}/>
                            <button type="submit">+</button>
                        </form>
                    )}
                    <div className="soc-sub">{socT("Membros ({n})", { n: g.members.length })}</div>
                    {g.members.map((m) => (
                        <div key={m.id} className={"soc-friend" + (m.online ? " online" : "")}>
                            <span className="soc-dot"/>
                            <div className="soc-friend-main">
                                <SocNick nick={m.nick} country={m.country} level={m.level} id={m.id} vip={m.vip}/>
                                <div className="soc-where-text">{socT(SOC_ROLE[m.role])} · {socWhereText(m)}</div>
                            </div>
                            {m.id !== st.me?.id && (
                                <div className="soc-actions">
                                    {g.myRole === "leader" && m.role === "member" && <button className="ghost" title={socT("Promover a oficial")} onClick={() => SocialStore.guildAction("promote", m.nick)}>▲</button>}
                                    {g.myRole === "leader" && m.role === "officer" && <button className="ghost" title={socT("Rebaixar a membro")} onClick={() => SocialStore.guildAction("demote", m.nick)}>▼</button>}
                                    {g.myRole === "leader" && <button className="ghost" title={socT("Passar a liderança")} onClick={async () => { if (await window.MastershotConfirm({ title: socT("Passar a liderança"), message: socT("{nick} vira o líder e você vira oficial.", { nick: m.nick }) })) SocialStore.guildAction("transfer", m.nick); }}>♛</button>}
                                    {(g.myRole === "leader" || (g.myRole === "officer" && m.role === "member")) && <button className="ghost" title={socT("Expulsar")} onClick={() => SocialStore.guildAction("kick", m.nick)}>✕</button>}
                                </div>
                            )}
                        </div>
                    ))}
                    <div className="soc-sub">{socT("Guilda")}</div>
                    {officer && <button className="soc-link" onClick={async () => { const n = await window.MastershotPrompt({ title: socT("Aviso da guilda"), message: socT("Aparece para todos os membros. Vazio apaga."), maxLength: 300 }); if (n != null) SocialStore.guildAction("notice", n); }}>📌 {socT("Escrever o aviso")}</button>}
                    {g.myRole === "leader"
                        ? <button className="soc-link" style={{ color: "#ff9b90" }} onClick={async () => { if (await window.MastershotConfirm({ title: socT("Desfazer a guilda"), message: socT("A guilda some para todos. Não dá para desfazer."), danger: true })) SocialStore.guildAction("disband"); }}>{socT("Desfazer a guilda")}</button>
                        : <button className="soc-link" style={{ color: "#ff9b90" }} onClick={async () => { if (await window.MastershotConfirm({ title: socT("Sair da guilda"), message: g.name, danger: true })) SocialStore.guildAction("leave"); }}>{socT("Sair da guilda")}</button>}
                </div>
            )}
        </div>
    );
}

// ---- ficha do jogador (/PF) ----
function SocPlayerCard({ card, st }) {
    if (!card) return null;
    const close = () => SocialStore.set({ card: null });
    const modeName = (id) => (window.MastershotData?.GAME_MODES || []).find((m) => m.id === id)?.name || id;
    return (
        <div className="soc-card-ov" onClick={(e) => { if (e.target === e.currentTarget) close(); }}>
            <div className="soc-card" role="dialog" aria-modal="true">
                <button className="soc-x" onClick={close} aria-label={socT("Fechar")}>✕</button>
                {card.loading && <div className="soc-empty">{socT("Carregando…")}</div>}
                {card.error && <div className="soc-empty">{card.error}</div>}
                {card.ok && (<>
                    <div className="soc-card-head">
                        <RankBadge level={card.level} size={52}/>
                        <div>
                            <div className="soc-card-nick"><CountryFlag code={card.country} size={18}/> {card.nick}{card.vip && <span className="soc-vip">★ VIP</span>}</div>
                            <div className="soc-card-sub">{socT("Nível {n}", { n: card.level })} · {rankName(rankForLevel(card.level))}</div>
                            {card.guild && <div className="soc-card-sub"><span className="soc-gtag" style={{ color: card.guild.color }}>[{card.guild.tag}]</span> {card.guildName}</div>}
                            <div className={"soc-card-where" + (card.online ? " on" : "")}>● {socWhereText(card)}</div>
                        </div>
                    </div>
                    <div className="soc-card-xp">
                        <div className="soc-card-xpbar"><i style={{ width: `${Math.min(100, card.xpNext ? (100 * card.xp) / card.xpNext : 0)}%` }}/></div>
                        <span>{socT("{xp}/{next} XP para o próximo nível", { xp: (card.xp || 0).toLocaleString(), next: (card.xpNext || 0).toLocaleString() })}</span>
                    </div>
                    <div className="soc-card-stats">
                        <div><b>{card.games}</b><span>{socT("Partidas")}</span></div>
                        <div><b>{card.wins}–{card.losses}{card.draws ? `–${card.draws}` : ""}</b><span>{card.draws ? socT("V–D–E") : socT("V–D")} ({card.winRate}%)</span></div>
                        <div><b>{card.longestStreak}</b><span>{socT("Maior sequência")}</span></div>
                        <div><b>{card.elo}</b><span>{socT("Rating")}</span></div>
                    </div>
                    {card.modes?.length > 0 && (
                        <table className="soc-card-modes">
                            <thead><tr><th>{socT("Modo")}</th><th>{socT("Partidas")}</th><th>{socT("Vitórias")}</th><th>{socT("Rating")}</th></tr></thead>
                            <tbody>{card.modes.map((m) => <tr key={m.mode}><td>{modeName(m.mode)}</td><td>{m.games}</td><td>{m.wins}</td><td>{m.elo}</td></tr>)}</tbody>
                        </table>
                    )}
                    {card.extra && <div className="soc-card-extra">{card.extra}</div>}
                    <div className="soc-card-since">{socT("Joga desde {d}", { d: new Date(card.since).toLocaleDateString() })}{card.drops != null ? " · " + socT("Abandonos: {n}", { n: card.drops }) : ""}
                        {card.tournaments?.joined ? " · " + socT("Torneios: {j} ({w} títulos)", { j: card.tournaments.joined, w: card.tournaments.won }) : ""}</div>
                    {!card.self && st.active && (
                        <div className="soc-card-actions">
                            <button onClick={() => { close(); SocialStore.openThread(card); }}>✉ {socT("Cochichar")}</button>
                            {card.friend === "none" && !card.blocked && <button onClick={() => SocialStore.friendAction(card.nick, "request")}>＋ {socT("Adicionar amigo")}</button>}
                            {card.friend === "incoming" && <button onClick={() => SocialStore.friendAction(card.nick, "accept")}>✓ {socT("Aceitar amizade")}</button>}
                            {card.friend === "outgoing" && <button disabled>{socT("Pedido enviado")}</button>}
                            {card.friend === "friend" && <button className="ghost" onClick={() => SocialStore.friendAction(card.nick, "remove")}>{socT("Desfazer amizade")}</button>}
                            {st.room && card.online && <button onClick={() => SocialStore.invite(card.nick)}>🚪 {socT("Convidar")}</button>}
                            {card.online && card.roomId && !st.room && <button onClick={() => { close(); SocialStore.enterRoomById(card.roomId); }}>→ {socT("Ir para a sala")}</button>}
                            <button className="ghost danger" onClick={() => SocialStore.block(card.nick, !card.blocked)}>{card.blocked ? socT("Desbloquear") : socT("Bloquear")}</button>
                            {st.guild && ["leader", "officer"].includes(st.guild.myRole) && !card.guild && <button onClick={() => SocialStore.guildAction("invite", card.nick)}>⚑ {socT("Convidar para a guilda")}</button>}
                            <button className="ghost" onClick={() => SocialStore.report(card.nick)}>⚠ {socT("Denunciar")}</button>
                            {card.canModerate && window.MastershotModeration && <button className="ghost" onClick={() => { close(); window.MastershotModeration.openUser(card.nick); }}>🛡 {socT("Moderar")}</button>}
                        </div>
                    )}
                </>)}
            </div>
        </div>
    );
}

// ---- convite recebido ----
function SocInvitePopup({ inv }) {
    const [, tick] = React.useState(0);
    React.useEffect(() => { const t = setInterval(() => tick((n) => n + 1), 1000); return () => clearInterval(t); }, []);
    if (!inv) return null;
    const left = Math.max(0, Math.ceil((inv.until - Date.now()) / 1000));
    const modeName = (window.MastershotData?.GAME_MODES || []).find((m) => m.id === inv.room.mode)?.name || inv.room.mode;
    return (
        <div className="soc-invite">
            <div className="soc-invite-title">{socT("Convite")}</div>
            <div className="soc-invite-text">
                {socT("{nick} convidou você para a sala {room}.", { nick: inv.from.nick, room: inv.room.name })}
                <div className="soc-invite-meta">{modeName}{inv.room.bet > 0 ? ` · 🪙 ${Number(inv.room.bet).toLocaleString()}` : ""}</div>
            </div>
            <div className="soc-actions">
                <button onClick={() => SocialStore.enterRoomById(inv.room.id)}>{socT("Entrar")} ({left})</button>
                <button className="ghost" onClick={() => SocialStore.set({ invite: null })}>{socT("Recusar")}</button>
            </div>
        </div>
    );
}

// ---- o painel ----
const SOC_HIDDEN_ON = new Set(["title", "login", "postLogin", "game"]);
function SocialDock({ navigate, screen }) {
    const st = useSocial();
    const [authed, setAuthed] = React.useState(() => !!window.MastershotAuth?.isAuthenticated?.());
    React.useEffect(() => window.MastershotAuth?.onChange?.((x) => setAuthed(!!x.authed)), []);
    React.useEffect(() => { SocialStore.setNavigate(navigate); }, [navigate]);
    React.useEffect(() => {
        if (!authed) { SocialStore.stop(); return; }
        const go = () => SocialStore.start();
        if (window.MastershotSocialApi) go(); else window.addEventListener("mastershot:lobby-ready", go, { once: true });
    }, [authed]);
    React.useEffect(() => { SocialStore.setStatus(screen === "online" ? "lobby" : "menu"); }, [screen]);
    React.useEffect(() => {
        const h = (e) => { if (e.key === "Escape" && SocialStore.get().open && !SocialStore.get().card) SocialStore.setOpen(false); };
        addEventListener("keydown", h); return () => removeEventListener("keydown", h);
    }, []);
    if (!authed || !st.active) return null;
    const hidden = SOC_HIDDEN_ON.has(screen);
    const pending = st.friends.filter((f) => f.state === "incoming").length;
    const badge = st.unread + pending;
    return (
        <div className="soc-root">
            {!hidden && !st.open && (
                <button className="soc-fab" onClick={() => SocialStore.setOpen(true)} title={socT("Social")}>
                    💬 <span>{socT("Social")}</span>{badge > 0 && <span className="soc-badge">{badge}</span>}
                </button>
            )}
            {!hidden && st.open && (
                <aside className="soc-drawer">
                    <header>
                        <div className="soc-tabs">
                            {[["channel", socT("Canal")], ["friends", socT("Amigos")], ["guild", socT("Guilda")], ["memos", socT("Recados")]].map(([id, label]) => (
                                <button key={id} className={st.tab === id ? "on" : ""} onClick={() => SocialStore.setOpen(true, id)}>
                                    {label}
                                    {id === "friends" && pending > 0 && <span className="soc-badge">{pending}</span>}
                                    {id === "memos" && st.unread > 0 && <span className="soc-badge">{st.unread}</span>}
                                    {id === "guild" && (st.guildInvites.length + st.guildRequests.length) > 0 && <span className="soc-badge">{st.guildInvites.length + st.guildRequests.length}</span>}
                                </button>
                            ))}
                        </div>
                        <button className="soc-x" onClick={() => SocialStore.setOpen(false)} aria-label={socT("Fechar")}>✕</button>
                    </header>
                    {st.tab === "channel" && <SocChannelTab st={st}/>}
                    {st.tab === "friends" && <SocFriendsTab st={st}/>}
                    {st.tab === "guild" && <SocGuildTab st={st}/>}
                    {st.tab === "memos" && <SocMemosTab st={st}/>}
                </aside>
            )}
            <div className="soc-toasts">
                {st.toasts.map((t) => (
                    <div key={t.id} className={"soc-toast " + (t.kind || "")} onClick={() => { if (t.onClick) { t.onClick(); SocialStore.dismiss(t.id); } }}>
                        {t.title && <b>{t.title}</b>}
                        <span>{t.text}</span>
                        {t.actions && <div className="soc-actions">{t.actions.map(([l, f], i) => <button key={i} className={i ? "ghost" : ""} onClick={(e) => { e.stopPropagation(); f(); SocialStore.dismiss(t.id); }}>{l}</button>)}</div>}
                        {t.sticky && !t.actions && <button className="soc-x" onClick={(e) => { e.stopPropagation(); SocialStore.dismiss(t.id); }}>✕</button>}
                    </div>
                ))}
            </div>
            <SocInvitePopup inv={st.invite}/>
            <SocPlayerCard card={st.card} st={st}/>
        </div>
    );
}

// Faixa do quadro de avisos (lobby e menu principal); some ao fechar, por aviso
function AnnouncementBar() {
    const st = useSocial();
    const [closed, setClosed] = React.useState(() => { try { return JSON.parse(localStorage.getItem("mastershot.social.annClosed") || "[]"); } catch { return []; } });
    React.useEffect(() => { if (!st.announcements.length) SocialStore.loadAnnouncements(); }, []);
    const list = st.announcements.filter((a) => a.kind === "banner" && !closed.includes(a.id));
    if (!list.length) return null;
    const a = list[0];
    const close = () => { const c = [...closed, a.id].slice(-50); setClosed(c); try { localStorage.setItem("mastershot.social.annClosed", JSON.stringify(c)); } catch {} };
    return (
        <div className="soc-ann">
            <span className="soc-ann-tag">{socT("Aviso")}</span>
            <span className="soc-ann-text">{a.body}</span>
            <button onClick={close} aria-label={socT("Fechar")}>✕</button>
        </div>
    );
}

// Admin: o quadro de avisos (criar, editar, desligar, apagar)
function AnnouncementsAdmin({ onClose }) {
    const [list, setList] = React.useState(null);
    const [draft, setDraft] = React.useState({ id: null, body: "", kind: "banner", active: true, endsAt: "" });
    const [err, setErr] = React.useState(null);
    const load = async () => { try { setList(await socApi().adminAnnouncements() || []); } catch (e) { setErr(e.message); } };
    React.useEffect(() => { load(); }, []);
    const save = async (patch = {}) => {
        const d = { ...draft, ...patch };
        try {
            const r = await socApi().adminAnnouncementSave({ id: d.id, body: d.body, kind: d.kind, active: d.active,
                endsAt: d.endsAt ? new Date(d.endsAt).toISOString() : null, remove: !!patch.remove });
            if (r?.ok === false) { setErr(socErr(r.error)); return; }
            setDraft({ id: null, body: "", kind: "banner", active: true, endsAt: "" }); setErr(null);
            await load(); SocialStore.loadAnnouncements();
        } catch (e) { setErr(e.message); }
    };
    return (
        <div className="soc-card-ov" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
            <div className="soc-card" style={{ width: "min(560px, calc(100vw - 24px))" }}>
                <button className="soc-x" onClick={onClose}>✕</button>
                <div className="soc-card-nick">{socT("Quadro de Avisos")}</div>
                <div className="soc-card-sub" style={{ marginBottom: 12 }}>{socT("Faixa: aparece no lobby e no menu. Popup: abre uma vez para cada jogador.")}</div>
                <textarea value={draft.body} maxLength={500} rows={3} placeholder={socT("Texto do aviso")}
                    onChange={(e) => setDraft({ ...draft, body: e.target.value })}
                    style={{ width: "100%", background: "rgba(0,0,0,.4)", border: "1px solid rgba(201,165,92,.25)", borderRadius: 10, color: "#eee", padding: 10, font: "inherit", fontSize: 13 }}/>
                <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", margin: "8px 0 4px", fontSize: 12 }}>
                    <label><input type="radio" checked={draft.kind === "banner"} onChange={() => setDraft({ ...draft, kind: "banner" })}/> {socT("Faixa")}</label>
                    <label><input type="radio" checked={draft.kind === "popup"} onChange={() => setDraft({ ...draft, kind: "popup" })}/> {socT("Popup")}</label>
                    <label>{socT("Até")} <input type="datetime-local" value={draft.endsAt} onChange={(e) => setDraft({ ...draft, endsAt: e.target.value })}
                        style={{ background: "rgba(0,0,0,.4)", color: "#eee", border: "1px solid rgba(201,165,92,.25)", borderRadius: 6, padding: 3 }}/></label>
                    <div className="soc-card-actions" style={{ margin: 0, marginLeft: "auto" }}>
                        {draft.id && <button className="ghost" onClick={() => setDraft({ id: null, body: "", kind: "banner", active: true, endsAt: "" })}>{socT("Cancelar")}</button>}
                        <button onClick={() => save()} disabled={!draft.body.trim()}>{draft.id ? socT("Salvar") : socT("Publicar")}</button>
                    </div>
                </div>
                {err && <div className="soc-sys" style={{ color: "#ff9b90" }}>{err}</div>}
                <div className="soc-sub" style={{ paddingLeft: 0 }}>{socT("Avisos")}</div>
                {list === null && <div className="soc-empty">{socT("Carregando…")}</div>}
                {list && list.length === 0 && <div className="soc-empty">{socT("Nenhum aviso.")}</div>}
                {list && list.map((a) => (
                    <div key={a.id} className="soc-friend" style={{ alignItems: "flex-start" }}>
                        <div className="soc-friend-main">
                            <div style={{ fontSize: 13, opacity: a.active ? 1 : .5 }}>{a.body}</div>
                            <div className="soc-where-text">{a.kind === "popup" ? socT("Popup") : socT("Faixa")}{a.endsAt ? " · " + socT("até {d}", { d: new Date(a.endsAt).toLocaleString() }) : ""}{a.active ? "" : " · " + socT("desligado")}</div>
                        </div>
                        <div className="soc-actions">
                            <button className="ghost" onClick={() => setDraft({ id: a.id, body: a.body, kind: a.kind, active: a.active, endsAt: a.endsAt ? new Date(a.endsAt).toISOString().slice(0, 16) : "" })}>{socT("Editar")}</button>
                            <button className="ghost" onClick={() => save({ id: a.id, body: a.body, kind: a.kind, active: !a.active, endsAt: a.endsAt ? new Date(a.endsAt).toISOString().slice(0, 16) : "" })}>{a.active ? socT("Desligar") : socT("Ligar")}</button>
                            <button className="ghost danger" onClick={() => save({ id: a.id, remove: true })}>✕</button>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

(function socInjectStyles() {
    if (document.getElementById("soc-styles")) return;
    const st = document.createElement("style");
    st.id = "soc-styles";
    st.textContent = `
    .soc-root { font-family: var(--font-body, 'Sora', sans-serif); }
    .soc-fab { position: fixed; right: 18px; bottom: 18px; z-index: 9000; display: inline-flex; align-items: center; gap: 8px;
        padding: 10px 16px; border-radius: 999px; cursor: pointer; font-weight: 700; font-size: 12px; line-height: 1; font-family: inherit; letter-spacing: .08em; text-transform: uppercase;
        color: var(--gold-400, #e2c178); background: rgba(6,13,9,.9); border: 1px solid rgba(201,165,92,.4); box-shadow: 0 8px 24px rgba(0,0,0,.45); }
    .soc-fab:hover { filter: brightness(1.12); }
    .soc-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
        background: #e0524a; color: #fff; font-weight: 800; font-size: 10px; line-height: 1; font-family: inherit; margin-left: 6px; }
    .soc-drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 9000; width: min(380px, 100vw); display: flex; flex-direction: column;
        background: linear-gradient(180deg, rgba(13,30,20,.97), rgba(6,13,9,.98)); border-left: 1px solid rgba(201,165,92,.22);
        box-shadow: -12px 0 40px rgba(0,0,0,.5); color: #eee; animation: soc-in .18s ease; }
    @keyframes soc-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
    .soc-drawer header { display: flex; align-items: center; gap: 8px; padding: 10px 10px 8px 12px; border-bottom: 1px solid rgba(255,255,255,.07); }
    .soc-tabs { display: flex; gap: 4px; flex: 1; }
    .soc-tabs button { flex: 1; padding: 8px 6px; border-radius: 999px; border: 1px solid transparent; background: none; color: rgba(241,227,182,.55);
        font-weight: 700; font-size: 11px; line-height: 1; font-family: inherit; letter-spacing: .1em; text-transform: uppercase; cursor: pointer; }
    .soc-tabs button.on { color: var(--gold-900, #2a1e06); background: var(--gold-500, #d7ae4f); }
    .soc-x { background: none; border: 0; color: rgba(255,255,255,.55); font-size: 15px; cursor: pointer; padding: 4px 6px; }
    .soc-pane { flex: 1; min-height: 0; display: flex; flex-direction: column; }
    .soc-scroll { flex: 1; overflow-y: auto; padding: 6px 12px 12px; }
    .soc-sub { font-weight: 700; font-size: 10px; line-height: 1; font-family: inherit; color: var(--ms-eyebrow, #a8894a); letter-spacing: .24em; text-transform: uppercase; padding: 12px 12px 6px; }
    .soc-scroll .soc-sub { padding-left: 0; padding-right: 0; }
    .soc-empty { color: rgba(241,227,182,.45); font-size: 12px; padding: 18px 12px; text-align: center; line-height: 1.5; }
    .soc-link { background: none; border: 0; color: var(--gold-400, #e2c178); font-weight: 600; font-size: 12px; line-height: 1.4; font-family: inherit; cursor: pointer; padding: 8px 12px; text-align: left; }
    .soc-region { margin-bottom: 10px; }
    .soc-region-name { font-size: 11px; color: rgba(241,227,182,.6); margin: 6px 0 4px; }
    .soc-chan { width: 100%; display: grid; grid-template-columns: 1fr auto 64px; gap: 8px; align-items: center; padding: 8px 10px; margin-bottom: 4px;
        border-radius: 12px; border: 1px solid rgba(255,255,255,.07); background: rgba(255,255,255,.025); color: #f1e3b6; font-weight: 600; font-size: 12px; line-height: 1; font-family: inherit; cursor: pointer; text-align: left; }
    .soc-chan:hover:not(:disabled) { border-color: rgba(201,165,92,.45); }
    .soc-chan.on { border-color: var(--gold-500, #d7ae4f); background: rgba(215,174,79,.08); }
    .soc-chan:disabled { opacity: .45; cursor: not-allowed; }
    .soc-chan-lv { font-size: 10px; color: rgba(241,227,182,.5); font-weight: 500; }
    .soc-chan-n { font-size: 11px; color: rgba(241,227,182,.7); text-align: right; }
    .soc-chanbar { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,.06); }
    .soc-chan-cur { background: rgba(201,165,92,.1); border: 1px solid rgba(201,165,92,.3); color: #f1e3b6; border-radius: 999px; padding: 6px 10px; font-weight: 600; font-size: 12px; line-height: 1; font-family: inherit; cursor: pointer; }
    .soc-users-toggle { margin-left: auto; background: none; border: 0; color: rgba(241,227,182,.6); font-weight: 600; font-size: 11px; line-height: 1; font-family: inherit; cursor: pointer; }
    .soc-users { max-height: 28%; overflow-y: auto; padding: 4px 12px 8px; display: flex; flex-wrap: wrap; gap: 2px 10px; border-bottom: 1px solid rgba(255,255,255,.06); }
    .soc-user { display: inline-flex; align-items: center; gap: 2px; }
    .soc-where { font-size: 10px; }
    .soc-nick { display: inline-flex; align-items: center; gap: 5px; background: none; border: 0; padding: 0; color: #f3d98a; font-weight: 700; font-size: 12px; line-height: 1.4; font-family: inherit; cursor: pointer; }
    .soc-nick:hover span:first-of-type { text-decoration: underline; }
    .soc-nick.vip span:first-of-type { color: #ffd87a; text-shadow: 0 0 8px rgba(255,216,122,.35); }
    .soc-vip { font-weight: 800; font-size: 10px; line-height: 1; font-family: inherit; color: #2a1e06; background: var(--gold-500, #d7ae4f); border-radius: 5px; padding: 3px 6px; margin-left: 4px; }
    .soc-gtag { font-weight: 800; font-size: 11px; }
    .soc-ghead { padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,.07); font-size: 14px; color: #f1e3b6; }
    .soc-gnotice { margin-top: 6px; font-size: 12px; color: #f3e3ad; background: rgba(201,165,92,.1); border-radius: 8px; padding: 6px 8px; }
    .soc-gtabs { display: flex; gap: 6px; margin-top: 8px; }
    .soc-gtabs button { background: none; border: 1px solid rgba(255,255,255,.12); border-radius: 999px; color: rgba(241,227,182,.7); font-weight: 600; font-size: 11px; line-height: 1; font-family: inherit; padding: 5px 10px; cursor: pointer; }
    .soc-gtabs button.on { color: var(--gold-900, #2a1e06); background: var(--gold-500, #d7ae4f); border-color: transparent; }
    .soc-gform { display: flex; gap: 6px; align-items: center; }
    .soc-gform input { min-width: 0; flex: 1; background: rgba(0,0,0,.3); border: 1px solid rgba(255,255,255,.08); border-radius: 999px; color: #eee; font-weight: 500; font-size: 13px; line-height: 1; font-family: inherit; padding: 8px 9px; }
    .soc-gform input[type=color] { flex: 0 0 34px; padding: 2px; height: 32px; }
    .soc-gform button { border-radius: 999px; border: 0; cursor: pointer; font-weight: 700; font-size: 11.5px; line-height: 1; font-family: inherit; padding: 9px 13px; color: #2a1e06; background: var(--gold-500, #d7ae4f); }
    .soc-gsearch { width: 100%; box-sizing: border-box; background: rgba(0,0,0,.3); border: 1px solid rgba(255,255,255,.08); border-radius: 999px; color: #eee; font-weight: 500; font-size: 13px; line-height: 1; font-family: inherit; padding: 8px 10px; margin-bottom: 6px; }
    .soc-rank { width: 22px; text-align: center; font-weight: 800; color: var(--gold-400, #e2c178); }
    .soc-toast.guild { border-color: rgba(120,200,255,.5); }
    .soc-guild { color: #9fd0ff; }
    .soc-lv { font-size: 9px; font-weight: 800; color: #1b1406; background: #c9a55c; border-radius: 4px; padding: 1px 3px; line-height: 1.2; }
    .soc-msgs { flex: 1; overflow-y: auto; padding: 8px 12px; display: flex; flex-direction: column; gap: 4px; }
    .soc-msg { font-size: 12.5px; line-height: 1.45; color: #e9e3d2; word-break: break-word; }
    .soc-msg .soc-nick { margin-right: 6px; }
    .soc-body.soc-quick { color: #ffd87a; font-style: italic; }
    .soc-sys { color: #9fd3a8; font-size: 12px; }
    .soc-help { color: #c9c2ae; font-family: ui-monospace, monospace; font-size: 11px; }
    .soc-whisper { color: #f2a6d4; }
    .soc-whisper .soc-nick { color: #f7b8de; }
    .soc-wtag { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; margin-right: 5px; opacity: .8; }
    .soc-re { background: none; border: 0; color: #f2a6d4; cursor: pointer; margin-left: 4px; }
    .soc-input { position: relative; display: flex; gap: 6px; padding: 10px 12px; border-top: 1px solid rgba(255,255,255,.07); }
    .soc-input input, .soc-add input { flex: 1; min-width: 0; background: rgba(0,0,0,.3); border: 1px solid rgba(255,255,255,.08); border-radius: 999px; color: #eee;
        font-weight: 500; font-size: 13px; line-height: 1; font-family: inherit; padding: 9px 10px; }
    .soc-send, .soc-add button, .soc-actions button, .soc-card-actions button { border-radius: 999px; border: 1px solid transparent; cursor: pointer;
        font-weight: 700; font-size: 11.5px; line-height: 1; font-family: inherit; padding: 8px 13px; color: #2a1e06; background: var(--gold-500, #d7ae4f); }
    .soc-actions button.ghost, .soc-card-actions button.ghost { color: var(--gold-400, #e2c178); background: transparent; border-color: rgba(201,165,92,.3); }
    .soc-card-actions button.danger { color: #f0a49c; border-color: rgba(212,87,76,.45); }
    .soc-actions button:disabled, .soc-card-actions button:disabled { opacity: .55; cursor: default; }
    .soc-qbtn { background: rgba(201,165,92,.12); border: 1px solid rgba(201,165,92,.3); border-radius: 999px; color: #ffd87a; cursor: pointer; padding: 0 10px; }
    .soc-quick-menu { position: absolute; left: 12px; right: 12px; bottom: 54px; display: grid; grid-template-columns: 112px 1fr; max-height: 280px;
        background: #0b1710; border: 1px solid rgba(201,165,92,.3); border-radius: 14px; overflow: hidden; box-shadow: 0 12px 30px rgba(0,0,0,.5); }
    .soc-quick-cats { display: flex; flex-direction: column; border-right: 1px solid rgba(255,255,255,.07); overflow-y: auto; }
    .soc-quick-cats button, .soc-quick-list button { background: none; border: 0; color: #e9e3d2; text-align: left; font-weight: 600; font-size: 12px; line-height: 1.3; font-family: inherit; padding: 8px 10px; cursor: pointer; }
    .soc-quick-cats button.on { background: rgba(201,165,92,.16); color: #ffd87a; }
    .soc-quick-list { overflow-y: auto; }
    .soc-quick-list button:hover { background: rgba(255,255,255,.06); }
    .soc-add { display: flex; gap: 6px; padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,.06); }
    .soc-friend { display: flex; align-items: center; gap: 8px; padding: 7px 0; border-bottom: 1px solid rgba(255,255,255,.04); }
    .soc-friend-main { flex: 1; min-width: 0; }
    .soc-friend .soc-actions { margin-left: auto; display: flex; gap: 4px; }
    .soc-friend .soc-actions button { padding: 6px 8px; }
    .soc-dot { width: 8px; height: 8px; border-radius: 50%; background: #555; flex-shrink: 0; }
    .soc-friend.online .soc-dot { background: var(--win, #6fc28e); box-shadow: 0 0 6px rgba(111,194,142,.6); }
    .soc-where-text { font-size: 11px; color: rgba(241,227,182,.5); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .soc-opt { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #d6d0bd; padding: 4px 0; }
    .soc-blocked { flex: 1; color: rgba(255,255,255,.6); font-size: 12px; }
    .soc-conv { width: 100%; display: flex; flex-direction: column; gap: 3px; text-align: left; padding: 9px 4px; background: none; border: 0;
        border-bottom: 1px solid rgba(255,255,255,.05); color: #eee; cursor: pointer; font: inherit; }
    .soc-conv-top { display: flex; align-items: center; gap: 6px; font-size: 13px; }
    .soc-conv-last { font-size: 12px; color: rgba(241,227,182,.55); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .soc-bubble { max-width: 82%; align-self: flex-start; background: rgba(255,255,255,.07); border-radius: 12px 12px 12px 4px; padding: 7px 10px;
        font-size: 13px; line-height: 1.4; display: flex; flex-direction: column; gap: 2px; word-break: break-word; }
    .soc-bubble.mine { align-self: flex-end; background: rgba(201,165,92,.2); border-radius: 12px 12px 4px 12px; }
    .soc-bubble small { font-size: 9.5px; opacity: .5; }
    .soc-toasts { position: fixed; right: 18px; bottom: 72px; z-index: 9050; display: flex; flex-direction: column; gap: 8px; width: min(320px, calc(100vw - 32px)); }
    .soc-toast { background: rgba(11,23,16,.97); border: 1px solid rgba(201,165,92,.35); border-radius: 14px; padding: 10px 12px; color: #eee; font-size: 12.5px;
        line-height: 1.4; display: flex; flex-direction: column; gap: 4px; box-shadow: 0 10px 28px rgba(0,0,0,.5); cursor: default; position: relative; animation: soc-in .18s ease; }
    .soc-toast b { color: #ffd87a; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; }
    .soc-toast.dm { border-color: rgba(242,166,212,.55); cursor: pointer; }
    .soc-toast.dm b { color: #f7b8de; }
    .soc-toast.error { border-color: rgba(224,82,74,.6); }
    .soc-toast .soc-actions { display: flex; gap: 6px; margin-top: 4px; }
    .soc-toast .soc-x { position: absolute; top: 4px; right: 4px; }
    .soc-invite { position: fixed; left: 50%; top: 18%; transform: translateX(-50%); z-index: 9060; width: min(380px, calc(100vw - 32px));
        background: linear-gradient(180deg,#10231a,#0a1711); border: 1px solid rgba(201,165,92,.4); border-radius: 18px; padding: 16px 18px;
        box-shadow: 0 24px 60px rgba(0,0,0,.6); color: #eee; animation: soc-in .18s ease; }
    .soc-invite-title { color: #ffd87a; font-weight: 800; font-size: 11px; line-height: 1; font-family: inherit; letter-spacing: .16em; text-transform: uppercase; margin-bottom: 8px; }
    .soc-invite-text { font-size: 14px; line-height: 1.45; }
    .soc-invite-meta { font-size: 12px; color: rgba(241,227,182,.6); margin-top: 4px; }
    .soc-invite .soc-actions { display: flex; gap: 8px; margin-top: 14px; }
    .soc-invite .soc-actions button { flex: 1; padding: 10px; }
    .soc-card-ov { position: fixed; inset: 0; z-index: 9080; display: flex; align-items: center; justify-content: center; background: rgba(3,8,5,.6); backdrop-filter: blur(5px); }
    .soc-card { position: relative; width: min(440px, calc(100vw - 24px)); max-height: calc(100vh - 32px); overflow-y: auto;
        background: linear-gradient(180deg,#10231a,#0a1711); border: 1px solid rgba(201,165,92,.25); border-radius: 20px; padding: 20px; color: #eee; box-shadow: 0 24px 70px rgba(0,0,0,.55); }
    .soc-card > .soc-x { position: absolute; top: 10px; right: 10px; }
    .soc-card-head { display: flex; gap: 14px; align-items: center; }
    .soc-card-nick { display: flex; align-items: center; gap: 8px; font-family: var(--font-display); font-size: 22px; font-weight: 600; color: var(--ms-title, #f3e6c6); }
    .soc-card-sub { font-size: 12px; color: rgba(241,227,182,.7); margin-top: 3px; }
    .soc-card-where { font-size: 12px; color: rgba(255,255,255,.4); margin-top: 3px; }
    .soc-card-where.on { color: var(--win, #6fc28e); }
    .soc-card-xp { margin: 14px 0 4px; font-size: 11px; color: rgba(241,227,182,.55); }
    .soc-card-xpbar { height: 4px; border-radius: 2px; background: rgba(255,255,255,.08); overflow: hidden; margin-bottom: 5px; }
    .soc-card-xpbar i { display: block; height: 100%; background: var(--gold-500, #d7ae4f); }
    .soc-card-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 12px 0; }
    .soc-card-stats div { background: rgba(255,255,255,.025); border: 1px solid rgba(255,255,255,.07); border-radius: 12px; padding: 8px 6px; text-align: center; }
    .soc-card-stats b { display: block; font-family: var(--font-display); font-size: 18px; font-weight: 600; color: var(--ms-title, #f3e6c6); }
    .soc-card-stats span { font-size: 10px; color: rgba(241,227,182,.55); }
    .soc-card-modes { width: 100%; border-collapse: collapse; font-size: 12px; margin: 4px 0 10px; }
    .soc-card-modes th { text-align: left; font-size: 10px; color: rgba(241,227,182,.5); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; padding: 4px; }
    .soc-card-modes td { padding: 5px 4px; border-top: 1px solid rgba(255,255,255,.05); }
    .soc-card-since, .soc-card-extra { font-size: 11px; color: rgba(241,227,182,.45); }
    .soc-card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
    .soc-ann { display: flex; align-items: center; gap: 10px; padding: 8px 12px; margin: 0 0 12px; border-radius: 10px;
        background: linear-gradient(90deg, rgba(201,165,92,.18), rgba(201,165,92,.05)); border: 1px solid rgba(201,165,92,.35); color: #f3e3ad; font-size: 13px; }
    .soc-ann-tag { font-weight: 800; font-size: 10px; line-height: 1; font-family: inherit; letter-spacing: .14em; text-transform: uppercase; color: #2a1e06; background: #e0b84a; border-radius: 5px; padding: 4px 6px; }
    .soc-ann-text { flex: 1; }
    .soc-ann button { background: none; border: 0; color: rgba(255,255,255,.5); cursor: pointer; }
    @media (max-width: 640px) { .soc-fab { right: 12px; bottom: 12px; padding: 9px 12px; } .soc-fab span:first-of-type { display: none; } .soc-toasts { right: 12px; bottom: 64px; } }
    `;
    document.head.appendChild(st);
})();

Object.assign(window, { SocialDock, AnnouncementBar, AnnouncementsAdmin, MastershotSocial: SocialStore });
