/* global React, Icon, Avatar, Button, StatusDot, DATA */
// ============================================================
// Messagerie in-app — store temps réel + admin chat + boîte opérateur
// ============================================================
const { useState: useMsgState, useEffect: useMsgEffect, useRef: useMsgRef, useReducer } = React;

const TempMsg = (function () {
  const threads = {
    u3: [
      { id: 1, from: "admin", text: "Salut Sofia 👋 pense à lancer le warm-up du lot du matin avant 10h.", at: "08:12", read: true },
      { id: 2, from: "u3",    text: "C'est en cours, 3/5 déjà fait 👍", at: "08:20", read: true },
      { id: 3, from: "admin", text: "Parfait, merci !", at: "08:21", read: true },
    ],
    u4: [
      { id: 4, from: "admin", text: "Tom, la story du jour est prête à publier ?", at: "10:05", read: true },
      { id: 5, from: "u4",    text: "Pas encore, je finis le warm-up d'abord", at: "10:09", read: false },
    ],
    u7: [
      { id: 6, from: "admin", text: "Yuki, ta tâche du lot du soir est en retard, tu peux la reprendre ?", at: "20:14", read: false },
    ],
  };
  const listeners = new Set();
  const emit = () => listeners.forEach((l) => l());
  const stamp = () => { const d = new Date(); return [d.getHours(), d.getMinutes()].map((n) => String(n).padStart(2, "0")).join(":"); };
  let seq = 100;
  return {
    subscribe(cb) { listeners.add(cb); return () => listeners.delete(cb); },
    get(opId) { return threads[opId] || []; },
    send(opId, from, text) {
      if (!text || !text.trim()) return;
      (threads[opId] = threads[opId] || []).push({ id: ++seq, from, text: text.trim(), at: stamp(), read: false });
      emit();
    },
    markRead(opId, viewer) {
      (threads[opId] || []).forEach((m) => {
        const fromAdmin = m.from === "admin";
        if ((viewer === "admin" && !fromAdmin) || (viewer === "op" && fromAdmin)) m.read = true;
      });
      emit();
    },
    unreadAdmin(opId) { return (threads[opId] || []).filter((m) => m.from !== "admin" && !m.read).length; },
    unreadOp(opId) { return (threads[opId] || []).filter((m) => m.from === "admin" && !m.read).length; },
    totalAdmin() { return Object.keys(threads).reduce((s, k) => s + this.unreadAdmin(k), 0); },
    last(opId) { const t = threads[opId] || []; return t[t.length - 1]; },
  };
})();

function useMsg() {
  const [, force] = useReducer((x) => x + 1, 0);
  useMsgEffect(() => TempMsg.subscribe(force), []);
  return TempMsg;
}

// signal partagé : ouvre/ferme la bulle de chat flottante (bas-droite) côté opérateur
const ChatUI = (function () {
  let open = false; const ls = new Set();
  return { sub(cb) { ls.add(cb); return () => ls.delete(cb); }, isOpen() { return open; }, set(v) { open = v; ls.forEach(f => f()); }, toggle() { open = !open; ls.forEach(f => f()); } };
})();
function useChatUI() { const [, f] = useReducer((x) => x + 1, 0); useMsgEffect(() => ChatUI.sub(f), []); return ChatUI; }

// ---- bulle de message ----
function Bubble({ m, mine }) {
  return (
    <div style={{ display: "flex", justifyContent: mine ? "flex-end" : "flex-start" }}>
      <div style={{ maxWidth: "78%", padding: "9px 13px", borderRadius: 14,
        borderBottomRightRadius: mine ? 4 : 14, borderBottomLeftRadius: mine ? 14 : 4,
        background: mine ? "var(--accent)" : "var(--surface-3)", color: mine ? "#06281c" : "var(--text)",
        fontSize: 13.5, lineHeight: 1.45 }}>
        {m.text}
        <span className="mono" style={{ display: "block", textAlign: "right", fontSize: 9.5, marginTop: 3, opacity: .7 }}>{m.at}</span>
      </div>
    </div>
  );
}

function Composer({ onSend, placeholder, disabled }) {
  const [text, setText] = useMsgState("");
  const go = () => { if (!text.trim()) return; onSend(text); setText(""); };
  return (
    <div style={{ display: "flex", gap: 9, padding: 14, borderTop: "1px solid var(--border)", background: "var(--surface-2)" }}>
      <input value={text} onChange={(e) => setText(e.target.value)} onKeyDown={(e) => e.key === "Enter" && go()}
        placeholder={placeholder || "Écrire un message…"} disabled={disabled}
        style={{ flex: 1, padding: "10px 13px", background: "var(--bg-2)", border: "1px solid var(--border-2)", borderRadius: 10, color: "var(--text)", fontSize: 13.5, outline: "none" }} />
      <Button variant="primary" icon="send" onClick={go} disabled={disabled}>Envoyer</Button>
    </div>
  );
}

function ThreadScroll({ thread, mineFrom }) {
  const ref = useMsgRef(null);
  useMsgEffect(() => { if (ref.current) ref.current.scrollTop = ref.current.scrollHeight; }, [thread.length]);
  return (
    <div ref={ref} style={{ flex: 1, overflow: "auto", padding: 18, display: "grid", gap: 10, alignContent: "start" }}>
      {thread.length === 0 && <div style={{ textAlign: "center", color: "var(--faint)", fontSize: 13, marginTop: 30 }}>Aucun message. Démarrez la conversation 👇</div>}
      {thread.map((m) => <Bubble key={m.id} m={m} mine={m.from === mineFrom} />)}
    </div>
  );
}

// ============================================================
// ADMIN — page Messages (liste opérateurs + chat)
// ============================================================
function AdminMessages({ initialOp, onWatch }) {
  const msg = useMsg();
  const ops = DATA.realTeam().filter((o) => o.role !== "Admin");   // équipe réelle uniquement
  const [sel, setSel] = useMsgState(initialOp || ops.find((o) => o.status === "active")?.id || ops[0].id);
  useMsgEffect(() => { if (initialOp) setSel(initialOp); }, [initialOp]);
  useMsgEffect(() => { msg.markRead(sel, "admin"); }, [sel, msg.get(sel).length]);

  const op = DATA.opById(sel);
  const thread = msg.get(sel);
  const online = op.status === "active";

  return (
    <div style={{ display: "grid", gap: 0 }}>
      <div style={{ marginBottom: 18 }}>
        <h1 style={{ margin: 0, fontSize: 22, fontWeight: 700, letterSpacing: "-.01em" }}>Messages</h1>
        <p style={{ margin: "5px 0 0", color: "var(--muted)", fontSize: 13.5 }}>Envoyez un message en direct à un opérateur pendant qu'il travaille.</p>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "290px 1fr", height: "calc(100vh - 190px)", minHeight: 460,
        background: "var(--surface)", border: "1px solid var(--border)", borderRadius: "var(--r-lg)", overflow: "hidden" }} className="msg-grid">
        {/* liste conversations */}
        <div style={{ borderRight: "1px solid var(--border)", overflow: "auto", background: "var(--bg-2)" }}>
          <div className="mono" style={{ fontSize: 10, color: "var(--faint)", letterSpacing: ".12em", padding: "16px 16px 8px" }}>OPÉRATEURS</div>
          {ops.map((o) => {
            const unread = msg.unreadAdmin(o.id);
            const last = msg.last(o.id);
            const act = o.id === sel;
            return (
              <button key={o.id} onClick={() => setSel(o.id)} style={{ display: "flex", alignItems: "center", gap: 11, width: "100%", padding: "11px 14px",
                background: act ? "var(--surface)" : "transparent", border: "none", borderLeft: "2px solid " + (act ? "var(--accent)" : "transparent"), textAlign: "left", cursor: "pointer" }}>
                <div style={{ position: "relative", flex: "none" }}>
                  <Avatar user={o} size={38} />
                  <span style={{ position: "absolute", bottom: -1, right: -1, width: 11, height: 11, borderRadius: 99, background: o.status === "active" ? "var(--online)" : o.status === "suspended" ? "var(--offline)" : "var(--locked)", border: "2px solid var(--bg-2)" }} />
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 6 }}>
                    <span style={{ fontSize: 13, fontWeight: 600, color: "var(--text)" }}>{o.name}</span>
                    {last && <span className="mono" style={{ fontSize: 10, color: "var(--faint)" }}>{last.at}</span>}
                  </div>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 6, marginTop: 2 }}>
                    <span style={{ fontSize: 11.5, color: "var(--muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{last ? (last.from === "admin" ? "Vous : " : "") + last.text : "—"}</span>
                    {unread > 0 && <span className="num" style={{ flex: "none", minWidth: 18, height: 18, padding: "0 5px", borderRadius: 99, background: "var(--accent)", color: "#06281c", fontSize: 11, fontWeight: 700, display: "grid", placeItems: "center" }}>{unread}</span>}
                  </div>
                </div>
              </button>
            );
          })}
        </div>
        {/* fil */}
        <div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 11, padding: "13px 18px", borderBottom: "1px solid var(--border)" }}>
            <Avatar user={op} size={36} />
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 700 }}>{op.name}</div>
              <div style={{ fontSize: 11.5, color: online ? "var(--accent)" : "var(--faint)", display: "flex", alignItems: "center", gap: 6 }}>
                <StatusDot status={online ? "online" : "offline"} pulse={online} /> {online ? "en ligne · en train de travailler" : "hors ligne"}
              </div>
            </div>
            {online && <button onClick={() => onWatch && onWatch(sel)} title="Voir son écran en direct"
              style={{ display: "flex", alignItems: "center", gap: 7, padding: "8px 13px", borderRadius: 9, background: "var(--cyan-soft)", color: "var(--accent-2)", border: "1px solid rgba(34,211,238,.3)", fontSize: 12.5, fontWeight: 600 }}>
              {React.createElement(Icon.eye, { size: 16 })} Voir son écran</button>}
            {op.whatsapp && <a href={`https://wa.me/${op.whatsapp.replace(/[^\d]/g, "")}`} target="_blank" rel="noreferrer" title="WhatsApp"
              style={{ display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 9, background: "rgba(37,211,102,.13)", color: "#25D366", border: "1px solid rgba(37,211,102,.3)" }}>
              {React.createElement(Icon.whatsapp, { size: 17 })}</a>}
            {op.telegram && <a href={`https://t.me/${op.telegram.replace(/^@/, "")}`} target="_blank" rel="noreferrer" title="Telegram"
              style={{ display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 9, background: "rgba(42,171,238,.13)", color: "#2AABEE", border: "1px solid rgba(42,171,238,.3)" }}>
              {React.createElement(Icon.telegram, { size: 17 })}</a>}
          </div>
          <ThreadScroll thread={thread} mineFrom="admin" />
          <Composer onSend={(t) => msg.send(sel, "admin", t)} placeholder={`Message à ${op.name.split(" ")[0]}…`} />
        </div>
      </div>
    </div>
  );
}

// ============================================================
// OPÉRATEUR — cloche + boîte de réception + toast live
// ============================================================
function OperatorBell({ user }) {
  const msg = useMsg();
  const chat = useChatUI();
  const [toast, setToast] = useMsgState(null);
  const prev = useMsgRef(msg.unreadOp(user.id));
  const unread = msg.unreadOp(user.id);

  useMsgEffect(() => {
    if (unread > prev.current && !chat.isOpen()) {
      const last = msg.last(user.id);
      setToast(last);
      const t = setTimeout(() => setToast(null), 5000);
      prev.current = unread;
      return () => clearTimeout(t);
    }
    prev.current = unread;
  }, [unread]);

  return (
    <>
      <button onClick={() => chat.set(true)} title="Messages" style={{ position: "relative", background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 10, width: 38, height: 38, display: "grid", placeItems: "center", color: chat.isOpen() ? "var(--accent)" : "var(--muted)" }}>
        {React.createElement(Icon.bell, { size: 18 })}
        {unread > 0 && <span className="num" style={{ position: "absolute", top: -6, right: -6, minWidth: 18, height: 18, padding: "0 4px", borderRadius: 99, background: "var(--accent)", color: "#06281c", fontSize: 10.5, fontWeight: 700, display: "grid", placeItems: "center", border: "2px solid var(--bg)" }}>{unread}</span>}
      </button>

      {toast && (
        <div onClick={() => { chat.set(true); setToast(null); }} style={{ position: "fixed", top: 70, right: 22, zIndex: 90, width: 320, maxWidth: "90vw",
          background: "var(--surface)", border: "1px solid var(--accent-line)", borderRadius: 13, boxShadow: "var(--shadow-lg)", padding: 14, cursor: "pointer", animation: "toastIn .26s ease" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 7 }}>
            <span style={{ width: 22, height: 22, borderRadius: 7, background: "linear-gradient(135deg,var(--accent),var(--accent-2))", display: "grid", placeItems: "center", color: "#06281c" }}>{React.createElement(Icon.send, { size: 13 })}</span>
            <span style={{ fontSize: 12.5, fontWeight: 700 }}>Message · ton manager</span>
            <span className="mono" style={{ marginLeft: "auto", fontSize: 10, color: "var(--faint)" }}>{toast.at}</span>
          </div>
          <div style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.45 }}>{toast.text}</div>
        </div>
      )}
    </>
  );
}

// ============================================================
// Bulle de chat flottante (bas-droite) — opérateur ET superviseur
// ============================================================
function FloatingChat({ opId, as, z = 80 }) {
  const msg = useMsg();
  const chat = useChatUI();
  const controlled = as === opId; // côté opérateur : piloté par la cloche
  const [localOpen, setLocalOpen] = useMsgState(false);
  const open = controlled ? chat.isOpen() : localOpen;
  const setOpen = controlled ? (v) => chat.set(typeof v === "function" ? v(chat.isOpen()) : v) : setLocalOpen;
  const thread = msg.get(opId);
  const isAdmin = as === "admin";
  const unread = isAdmin ? msg.unreadAdmin(opId) : msg.unreadOp(opId);
  const peer = isAdmin ? DATA.opById(opId) : null;

  useMsgEffect(() => { if (open) msg.markRead(opId, isAdmin ? "admin" : "op"); }, [open, thread.length]);

  return (
    <div style={{ position: "fixed", right: 22, bottom: 22, zIndex: z, display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 12 }}>
      {open && (
        <div style={{ width: 348, maxWidth: "90vw", height: 440, display: "flex", flexDirection: "column",
          background: "var(--surface)", border: "1px solid var(--border-2)", borderRadius: 16, boxShadow: "var(--shadow-lg)", overflow: "hidden", animation: "toastIn .2s ease" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }}>
            {isAdmin
              ? <Avatar user={peer} size={32} />
              : <div style={{ width: 32, height: 32, borderRadius: 9, background: "linear-gradient(135deg,var(--accent),var(--accent-2))", display: "grid", placeItems: "center", color: "#06281c" }}>{React.createElement(Icon.shield, { size: 17 })}</div>}
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13.5, fontWeight: 700 }}>{isAdmin ? peer.name : "Agence Tampro"}</div>
              <div style={{ fontSize: 10.5, color: "var(--accent)", display: "flex", alignItems: "center", gap: 5 }}><StatusDot status="online" pulse /> {isAdmin ? "opérateur · en ligne" : "admin · en ligne"}</div>
            </div>
            <button onClick={() => setOpen(false)} style={{ background: "none", border: "none", color: "var(--muted)", display: "grid", padding: 4 }}>{React.createElement(Icon.chevDown, { size: 20 })}</button>
          </div>
          <ThreadScroll thread={thread} mineFrom={as} />
          <Composer onSend={(t) => msg.send(opId, as, t)} placeholder={isAdmin ? `Guider ${peer.name.split(" ")[0]}…` : "Répondre à l'agence…"} />
        </div>
      )}
      <button onClick={() => setOpen(o => !o)} style={{ position: "relative", width: 56, height: 56, borderRadius: 99, border: "none",
        background: open ? "var(--surface-3)" : "linear-gradient(135deg,var(--accent),var(--accent-2))", color: open ? "var(--text)" : "#06281c",
        display: "grid", placeItems: "center", boxShadow: "0 10px 28px rgba(0,224,138,.32)", cursor: "pointer", transition: "transform .14s" }}
        onMouseEnter={e => e.currentTarget.style.transform = "scale(1.06)"} onMouseLeave={e => e.currentTarget.style.transform = "none"}>
        {React.createElement(Icon[open ? "chevDown" : "send"], { size: 23 })}
        {!open && unread > 0 && <span className="num" style={{ position: "absolute", top: -2, right: -2, minWidth: 22, height: 22, padding: "0 5px", borderRadius: 99, background: "var(--danger)", color: "#fff", fontSize: 11.5, fontWeight: 700, display: "grid", placeItems: "center", border: "2px solid var(--bg)" }}>{unread}</span>}
      </button>
    </div>
  );
}

// ============================================================
// Supervision live — voir l'écran de l'opérateur + le guider
// ============================================================
const WATCH_SCRIPT = [
  ["Tap", "(0.51, 0.34)"], ["Ouvrir Instagram", "com.burbn.instagram"], ["Scroll ↓", "feed"],
  ["Like", "post #3"], ["Swipe ←", "story suivante"], ["Saisie texte", "« trop bien 🔥 »"],
  ["Tap", "(0.27, 0.78)"], ["Home", "—"],
];
function WatchOverlay({ opId, onClose }) {
  const op = DATA.opById(opId);
  const dev = DATA.DEVICES.find(d => d.operator && d.operator.id === opId && (d.status === "online" || d.status === "busy"))
           || DATA.DEVICES.find(d => d.operator && d.operator.id === opId)
           || DATA.DEVICES.find(d => d.status === "online");
  const [feed, setFeed] = useMsgState([{ label: "Session ouverte", meta: "vous regardez en direct", at: now() }]);
  const [assist, setAssist] = useMsgState(false);
  const [busyBtn, setBusyBtn] = useMsgState(null);
  const [fps, setFps] = useMsgState(dev ? Math.min(20, Math.max(1, dev.fps || 8)) : 8);
  const assistRef = React.useRef(false); assistRef.current = assist;
  const swipeRef = React.useRef(null);
  const pushFeed = (label, meta) => setFeed(f => [{ label, meta, at: now() }, ...f].slice(0, 24));
  const act = (label, btnId, meta) => { if (btnId) { setBusyBtn(btnId); setTimeout(() => setBusyBtn(null), 360); } pushFeed(label, meta); };
  const onDown = (e) => { swipeRef.current = { x: e.clientX, y: e.clientY }; };
  const onUp = (e) => {
    const s = swipeRef.current; swipeRef.current = null; if (!s) return;
    const dx = e.clientX - s.x, dy = e.clientY - s.y;
    if (Math.abs(dx) < 22 && Math.abs(dy) < 22) return;
    const dir = Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? "→" : "←") : (dy > 0 ? "↓" : "↑");
    act(`Swipe ${dir} (vous)`, null, "geste écran");
  };

  useMsgEffect(() => {
    const k = (e) => e.key === "Escape" && onClose();
    window.addEventListener("keydown", k);
    let i = 0;
    const t = setInterval(() => {
      if (assistRef.current) return; // vous avez la main : l'opérateur n'agit plus
      const [label, meta] = WATCH_SCRIPT[i % WATCH_SCRIPT.length]; i++;
      setFeed(f => [{ label, meta, at: now() }, ...f].slice(0, 24));
    }, 2600);
    return () => { clearInterval(t); window.removeEventListener("keydown", k); };
  }, []);

  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 110, background: "rgba(4,6,11,.82)", backdropFilter: "blur(8px)", animation: "appfade .18s ease", display: "flex", flexDirection: "column" }}>
      {/* top bar */}
      <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 22px", borderBottom: "1px solid var(--border)", background: "var(--bg-2)" }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: "5px 11px", borderRadius: 99, background: "rgba(255,92,108,.14)", border: "1px solid rgba(255,92,108,.35)", color: "var(--danger)", fontSize: 12, fontWeight: 700 }}>
          <span style={{ width: 8, height: 8, borderRadius: 99, background: "var(--danger)", animation: "streamPulse 1.1s infinite" }} /> LIVE
        </span>
        <Avatar user={op} size={36} />
        <div style={{ flex: 1, lineHeight: 1.25 }}>
          <div style={{ fontSize: 15, fontWeight: 700 }}>Supervision · {op.name}</div>
          <div className="mono" style={{ fontSize: 11, color: "var(--faint)" }}>vous regardez son écran en direct{dev ? ` · ${dev.tag} (${dev.name})` : ""}</div>
        </div>
        <button onClick={() => setAssist(a => !a)} style={{ display: "flex", alignItems: "center", gap: 7, padding: "9px 14px", borderRadius: 9, fontSize: 13, fontWeight: 600,
          background: assist ? "var(--accent)" : "var(--surface-2)", color: assist ? "#06281c" : "var(--text)", border: "1px solid " + (assist ? "transparent" : "var(--border-2)") }}>
          {React.createElement(Icon[assist ? "unlock" : "eye"], { size: 16 })}{assist ? "Contrôle partagé actif" : "Assister (prendre la main)"}
        </button>
        <button onClick={onClose} style={{ display: "flex", alignItems: "center", gap: 7, padding: "9px 14px", borderRadius: 9, background: "var(--surface-2)", border: "1px solid var(--border-2)", color: "var(--text)", fontSize: 13, fontWeight: 600 }}>
          {React.createElement(Icon.x, { size: 16 })} Fermer
        </button>
      </div>

      {/* corps */}
      <div style={{ flex: 1, display: "grid", gridTemplateColumns: assist ? "1fr 408px" : "1fr 340px", gap: 0, minHeight: 0 }}>
        <div style={{ display: "grid", placeItems: "center", padding: 24, overflow: "auto" }}>
          {dev
            ? <div style={{ display: "grid", placeItems: "center", gap: 14 }}>
                <div style={{ position: "relative" }}>
                  <Phone device={dev} onTap={assist ? (p) => act("Tap (vous)", null, `(${p.x}, ${p.y})`) : undefined} onDown={assist ? onDown : undefined} onUp={assist ? onUp : undefined} />
                  {assist && <div style={{ position: "absolute", inset: 0, borderRadius: 52, border: "2px solid var(--accent)", boxShadow: "0 0 0 4px var(--accent-soft)", pointerEvents: "none" }} />}
                </div>
                <div className="mono" style={{ fontSize: 11.5, color: assist ? "var(--accent)" : "var(--faint)", display: "flex", alignItems: "center", gap: 8 }}>
                  <span style={{ width: 6, height: 6, borderRadius: 99, background: assist ? "var(--accent)" : "var(--faint)", animation: "streamPulse 1.2s infinite" }} />
                  {assist ? "contrôle partagé — clic = tap, glisser = swipe · vos actions sont transmises" : "lecture seule — vous observez sans intervenir"}
                </div>
              </div>
            : <div style={{ color: "var(--faint)", textAlign: "center" }}>{React.createElement(Icon.phone, { size: 36 })}<p>Aucun device actif pour cet opérateur.</p></div>}
        </div>

        {/* panneau droit : télécommande (assist) ou feed (lecture) */}
        {assist
          ? <div style={{ borderLeft: "1px solid var(--border)", background: "var(--bg-2)", display: "flex", flexDirection: "column", minHeight: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "14px 16px", borderBottom: "1px solid var(--border)" }}>
                {React.createElement(Icon.apps, { size: 16, color: "var(--accent)" })}
                <span style={{ fontSize: 12, fontWeight: 700, letterSpacing: ".05em", textTransform: "uppercase", color: "var(--text)" }}>Télécommande</span>
                <span className="mono" style={{ marginLeft: "auto", fontSize: 11, color: "var(--accent)" }}>● vous pilotez</span>
              </div>
              <div style={{ flex: 1, overflow: "auto", padding: 14 }}>
                <Remote act={act} busyBtn={busyBtn} dead={false} actions={feed} fps={fps} setFps={setFps} />
              </div>
            </div>
          : <div style={{ borderLeft: "1px solid var(--border)", background: "var(--bg-2)", display: "flex", flexDirection: "column", minHeight: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "14px 16px", borderBottom: "1px solid var(--border)" }}>
                {React.createElement(Icon.list, { size: 16, color: "var(--muted)" })}
                <span style={{ fontSize: 12, fontWeight: 700, letterSpacing: ".05em", textTransform: "uppercase", color: "var(--muted)" }}>Ce qu'il fait</span>
                <span className="mono" style={{ marginLeft: "auto", fontSize: 11, color: "var(--accent)" }}>● live</span>
              </div>
              <div style={{ flex: 1, overflow: "auto", padding: "8px 12px" }}>
                {feed.map((a, i) => (
                  <div key={i} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 4px", fontSize: 12.5, opacity: i === 0 ? 1 : Math.max(.4, 0.92 - i * 0.05) }}>
                    <span className="num" style={{ fontSize: 10.5, color: "var(--faint)", flex: "none" }}>{a.at}</span>
                    <span style={{ width: 5, height: 5, borderRadius: 99, background: "var(--accent)", flex: "none" }} />
                    <span style={{ fontWeight: 600 }}>{a.label}</span>
                    {a.meta && <span className="mono" style={{ fontSize: 10.5, color: "var(--faint)", marginLeft: "auto" }}>{a.meta}</span>}
                  </div>
                ))}
              </div>
            </div>}
      </div>

      {/* bulle de chat pour le guider */}
      <FloatingChat opId={opId} as="admin" z={130} />
    </div>
  );
}

Object.assign(window, { TempMsg, useMsg, AdminMessages, OperatorBell, FloatingChat, WatchOverlay });
