// Mobile simplified console for BControl v0.5
// Shown after login when viewed on a phone-sized viewport.
// Flow: pick a panel from a dropdown, fire one of three fixed commands.
// The three actions are the same for every panel, but each panel/brand
// resolves to its own command_id — matched by name below.

// VAPID base64url → Uint8Array (applicationServerKey format).
function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  const raw = atob(base64);
  const arr = new Uint8Array(raw.length);
  for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
  return arr;
}

// Subscribe this device to Web Push. Returns { ok, msg } for a hint banner.
async function bcEnablePush() {
  const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);
  const standalone = window.matchMedia('(display-mode: standalone)').matches || navigator.standalone === true;
  if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
    return { ok: false, msg: isIOS && !standalone ? 'Primero instala la app en tu inicio' : 'Notificaciones no soportadas aquí' };
  }
  try {
    const cfg = await fetch('/push/vapid-public-key').then((r) => r.json());
    if (!cfg.enabled || !cfg.key) return { ok: false, msg: 'Push no está configurado en el servidor' };
    const perm = await Notification.requestPermission();
    if (perm !== 'granted') return { ok: false, msg: 'Permiso de notificaciones denegado' };
    const reg = await navigator.serviceWorker.ready;
    let sub = await reg.pushManager.getSubscription();
    if (!sub) {
      sub = await reg.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(cfg.key),
      });
    }
    const res = await fetch('/push/subscribe', {
      method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(sub.toJSON()),
    });
    if (!res.ok) return { ok: false, msg: 'No se pudo registrar la suscripción' };
    return { ok: true, msg: 'Notificaciones activadas' };
  } catch (e) {
    if (isIOS && !standalone) return { ok: false, msg: 'Primero instala la app en tu inicio' };
    return { ok: false, msg: 'Error al activar notificaciones' };
  }
}

// Reactive "is this a phone?" hook. Mirrors the 767px CSS breakpoint.
function useIsMobile() {
  const query = '(max-width: 767px)';
  const [isMobile, setIsMobile] = React.useState(() =>
    typeof window.matchMedia === 'function' ? window.matchMedia(query).matches : false
  );
  React.useEffect(() => {
    if (typeof window.matchMedia !== 'function') return;
    const mq = window.matchMedia(query);
    const onChange = (e) => setIsMobile(e.matches);
    // Safari <14 uses addListener
    mq.addEventListener ? mq.addEventListener('change', onChange) : mq.addListener(onChange);
    return () => { mq.removeEventListener ? mq.removeEventListener('change', onChange) : mq.removeListener(onChange); };
  }, []);
  return isMobile;
}

// Resolve a panel's command list into the three canonical actions.
// Payload/name/transport differ per brand, so the classification lives in the
// DB (tcp_commands.ui_action). Keep the real command object (id) for each slot.
function matchTriadCommands(commands) {
  const list = commands || [];
  const byAction = (a) => list.find((c) => c.ui_action === a);
  return {
    reset:    byAction('reset'),
    silence:  byAction('silence'),
    ack:      byAction('ack'),
  };
}

// Fixed action slots — same icon, colour and order on every panel.
const MOBILE_ACTIONS = [
  { key: 'reset',   label: 'Reiniciar panel',  icon: 'rotate-ccw', tone: 'fire',    confirm: true  },
  { key: 'silence', label: 'Silenciar panel',  icon: 'bell-off', tone: 'trouble', confirm: false, bg: '#F0A519' }, // brighter than the shared trouble token
  { key: 'ack',     label: 'Reconocer panel',  icon: 'check',    tone: 'ok',      confirm: false },
];

// Compact read-only notification triad for the mobile banner.
// Tapping a badge is a no-op here (filtering lives in the full view) — surface that via onTap.
function MobileNotifTriad({ counts, onTap }) {
  const items = [
    { icon: 'fire',    count: counts.alarm,       sev: BC.fire },
    { icon: 'warning', count: counts.trouble,     sev: BC.trouble },
    { icon: 'shield',  count: counts.supervisory, sev: BC.supervisory },
  ];
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 2 }}>
      {items.map((it) => (
        <button key={it.icon} className="bc-btn" onClick={onTap}
          style={{ position: 'relative', width: 38, height: 38, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', border: 'none', background: 'transparent' }}>
          <Icon name={it.icon} size={20} />
          {it.count > 0 && (
            <span style={{ position: 'absolute', top: 2, right: 2, minWidth: 17, height: 17, padding: '0 4px', borderRadius: 9, background: it.sev.bar, color: '#fff', fontSize: 10, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 0 0 2px ' + BC.brand }}>
              {it.count >= 100 ? '99+' : it.count}
            </span>
          )}
        </button>
      ))}
    </div>
  );
}

// Custom panel dropdown ("drop box") — shows status dot + alarm badge per panel.
function MobilePanelPicker({ clients, selected, setSelected, newAlarmsByClient }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const handler = (e) => { if (!ref.current?.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', handler);
    document.addEventListener('touchstart', handler);
    return () => { document.removeEventListener('mousedown', handler); document.removeEventListener('touchstart', handler); };
  }, [open]);

  const current = clients.find((c) => c.client_id === selected);
  const dotFor = (p) => p.alive_status === 'connected' ? BC.ok.dot : p.alive_status === 'pending' ? BC.pending.dot : BC.fire.dot;

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button className="bc-btn" onClick={() => setOpen((o) => !o)}
        style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px', minHeight: 60, background: BC.surface, border: '1px solid ' + (open ? BC.brand : BC.border), borderRadius: 10, color: BC.ink, textAlign: 'left', boxShadow: BC.shadow }}>
        {current ? (
          <>
            <span style={{ width: 10, height: 10, borderRadius: 10, background: dotFor(current), flex: '0 0 10px' }} />
            <span style={{ flex: 1, minWidth: 0, fontSize: 15, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{current.description || current.client_id}</span>
            {(newAlarmsByClient[current.client_id] || 0) > 0 && (
              <span style={{ minWidth: 22, height: 20, padding: '0 6px', borderRadius: 10, background: BC.fire.bar, color: '#fff', fontSize: 11, fontWeight: 700, fontFamily: BC.mono, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{newAlarmsByClient[current.client_id]}</span>
            )}
          </>
        ) : (
          <span style={{ flex: 1, fontSize: 15, color: BC.inkFaint }}>Seleccione un panel…</span>
        )}
        <Icon name="chev-down" size={18} color={BC.inkMuted} style={{ transition: 'transform .15s', transform: open ? 'rotate(180deg)' : 'rotate(0deg)' }} />
      </button>
      {open && (
        <div className="bc-slide-up bc-scroll" style={{ position: 'absolute', left: 0, right: 0, top: 'calc(100% + 6px)', zIndex: 300, background: BC.surface, border: '1px solid ' + BC.border, borderRadius: 10, boxShadow: BC.shadowLg, maxHeight: '50vh', overflowY: 'auto', overflowX: 'hidden' }}>
          {clients.length === 0 && (
            <div style={{ padding: '18px 16px', fontSize: 13, color: BC.inkFaint, fontStyle: 'italic' }}>No hay paneles asignados.</div>
          )}
          {clients.map((p) => {
            const active = p.client_id === selected;
            const alarms = newAlarmsByClient[p.client_id] || 0;
            return (
              <button key={p.client_id} className="bc-btn bc-nav-item" onClick={() => { setSelected(p.client_id); setOpen(false); }}
                style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 12, padding: '13px 16px', minHeight: 52, border: 'none', borderBottom: '1px solid ' + BC.divider, background: active ? BC.brandTint : 'transparent', color: BC.ink, textAlign: 'left' }}>
                <span style={{ width: 9, height: 9, borderRadius: 9, background: dotFor(p), flex: '0 0 9px' }} />
                <span style={{ flex: 1, minWidth: 0, fontSize: 14, fontWeight: active ? 600 : 500, color: active ? BC.brand : BC.ink, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.description || p.client_id}</span>
                {alarms > 0 && (
                  <span style={{ minWidth: 20, height: 18, padding: '0 5px', borderRadius: 9, background: BC.fire.bar, color: '#fff', fontSize: 10, fontWeight: 700, fontFamily: BC.mono, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{alarms}</span>
                )}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

function MobileView({ user, clients, events, counts, selected, setSelected, commandsByClient, onSendCommand, lastCmdResult, onLogout, onResetNotifications, liveState, newAlarmsByClient, onExitMobile, panelOrder }) {
  const sortedClients = React.useMemo(() => {
    const order = new Map((panelOrder || []).map((id, i) => [id, i]));
    return [...clients].sort((a, b) => {
      const ai = order.has(a.client_id) ? order.get(a.client_id) : Infinity;
      const bi = order.has(b.client_id) ? order.get(b.client_id) : Infinity;
      return ai - bi;
    });
  }, [clients, panelOrder]);
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [confirmKey, setConfirmKey] = React.useState(null); // action key awaiting a 2nd tap
  const [evtToasts, setEvtToasts] = React.useState([]);     // stacked arriving events, newest first
  const [notifHint, setNotifHint] = React.useState(null);   // transient hint banner message
  const [confirmExit, setConfirmExit] = React.useState(false); // "enter full view?" prompt
  const menuRef = React.useRef(null);
  const lastEvtId = React.useRef(null);
  const evtMounted = React.useRef(false);
  const notifHintTimer = React.useRef(null);

  function showHint(msg) {
    setNotifHint(msg);
    clearTimeout(notifHintTimer.current);
    notifHintTimer.current = setTimeout(() => setNotifHint(null), 3000);
  }
  React.useEffect(() => () => clearTimeout(notifHintTimer.current), []);

  async function handleEnablePush() {
    showHint('Activando notificaciones…');
    const r = await bcEnablePush();
    showHint(r.msg);
  }

  const panelName = (cid) => (clients.find((c) => c.client_id === cid)?.description) || cid;

  // Ask for OS notification permission once (best-effort, for background pushes).
  React.useEffect(() => {
    try { if (window.Notification && Notification.permission === 'default') Notification.requestPermission(); } catch (_) {}
  }, []);

  // Notify on every newly arriving event while on the simplified view.
  React.useEffect(() => {
    const top = (events || [])[0];
    if (!evtMounted.current) { evtMounted.current = true; lastEvtId.current = top ? top.id : null; return; }
    if (!top || top.id === lastEvtId.current) return;
    lastEvtId.current = top.id;
    // Stack: new toast goes on top of any still showing (cap the stack).
    setEvtToasts((prev) => [top, ...prev.filter((e) => e.id !== top.id)].slice(0, 4));
    try {
      if (document.hidden && window.Notification && Notification.permission === 'granted') {
        new Notification(`${classifyLabel(top.cls)} · ${panelName(top.client_id)}`, { body: top.message || '' });
      }
    } catch (_) {}
    // Independent per-toast timer — must NOT be cleared on the next event,
    // otherwise stacked toasts would never auto-dismiss.
    setTimeout(() => setEvtToasts((prev) => prev.filter((e) => e.id !== top.id)), 15000);
  }, [events]);

  React.useEffect(() => {
    if (!menuOpen) return;
    const handler = (e) => { if (!menuRef.current?.contains(e.target)) setMenuOpen(false); };
    document.addEventListener('mousedown', handler);
    document.addEventListener('touchstart', handler);
    return () => { document.removeEventListener('mousedown', handler); document.removeEventListener('touchstart', handler); };
  }, [menuOpen]);

  const panel = clients.find((c) => c.client_id === selected);
  const role = user.role;
  const rawCommands = panel ? commandsByClient[panel.client_id] : null;
  const loadingCommands = panel && rawCommands === undefined;
  const triad = matchTriadCommands(rawCommands);

  // Reset the pending confirm whenever the panel changes.
  React.useEffect(() => { setConfirmKey(null); }, [selected]);

  function gate(cmd) {
    if (!panel) return { disabled: true, reason: 'Elija un panel' };
    if (loadingCommands) return { disabled: true, reason: 'Cargando…' };
    if (!cmd) return { disabled: true, reason: 'No disponible' };
    if (role === 'viewer') return { disabled: true, reason: 'Solo lectura' };
    if (panel.status !== 'connected') return { disabled: true, reason: 'Panel desconectado' };
    if (cmd.admin_only && role !== 'admin') return { disabled: true, reason: 'Solo administradores' };
    return { disabled: false };
  }

  function handleAction(action, cmd) {
    if (action.confirm && confirmKey !== action.key) {
      setConfirmKey(action.key);
      setTimeout(() => setConfirmKey((k) => (k === action.key ? null : k)), 4000);
      return;
    }
    setConfirmKey(null);
    onSendCommand(cmd);
  }

  return (
    <div className="bc-mobile-root" style={{ display: 'flex', flexDirection: 'column', background: BC.bg }}>
      {/* ── Banner: logo + notifications badge + menu (padded for the status bar / notch) ── */}
      <div style={{ flex: '0 0 auto', minHeight: 56, paddingTop: 'env(safe-area-inset-top)', paddingRight: 6, paddingLeft: 12, background: BC.brand, display: 'flex', alignItems: 'center', color: '#fff', borderBottom: '1px solid ' + BC.brandDeep }}>
        <img src="/frontend/logo.png" alt="Logo" style={{ height: 34, objectFit: 'contain' }} />
        <div style={{ flex: 1 }} />
        <MobileNotifTriad counts={counts} onTap={() => showHint('Disponible solo en la vista completa')} />
        <div ref={menuRef} style={{ position: 'relative', marginLeft: 4 }}>
          <button className="bc-btn" onClick={() => setMenuOpen((o) => !o)} aria-label="Menú"
            style={{ width: 40, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center', border: 'none', background: menuOpen ? 'rgba(255,255,255,.2)' : 'transparent', borderRadius: 8, color: '#fff' }}>
            <div style={{ width: 30, height: 30, borderRadius: 15, background: '#fff', color: BC.brand, display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: 13 }}>
              {(user.username || '?').slice(0, 1).toUpperCase()}
            </div>
          </button>
          {menuOpen && (
            <div className="bc-slide-up" style={{ position: 'fixed', right: 8, top: 'calc(60px + env(safe-area-inset-top))', zIndex: 400, background: BC.surface, border: '1px solid ' + BC.border, borderRadius: 10, boxShadow: BC.shadowLg, minWidth: 230, overflow: 'hidden', color: BC.ink }}>
              <div style={{ padding: '12px 14px', borderBottom: '1px solid ' + BC.divider, background: BC.surfaceAlt }}>
                <div style={{ fontSize: 14, fontWeight: 600 }}>{user.username}</div>
                <div style={{ fontSize: 11, color: BC.inkSubtle, fontFamily: BC.mono, textTransform: 'uppercase', letterSpacing: '.06em' }}>{user.role}</div>
              </div>
              <button className="bc-btn bc-nav-item" onClick={() => { setMenuOpen(false); handleEnablePush(); }}
                style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', border: 'none', background: 'transparent', fontSize: 14, color: BC.ink, textAlign: 'left' }}>
                <Icon name="bell" size={16} color={BC.inkMuted} /> Activar notificaciones
              </button>
              <button className="bc-btn bc-nav-item" onClick={() => { setMenuOpen(false); onResetNotifications && onResetNotifications(); }}
                style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', border: 'none', borderTop: '1px solid ' + BC.divider, background: 'transparent', fontSize: 14, color: BC.ink, textAlign: 'left' }}>
                <Icon name="check" size={16} color={BC.inkMuted} /> Limpiar notificaciones
              </button>
              <button className="bc-btn bc-nav-item" onClick={() => { setMenuOpen(false); setConfirmExit(true); }}
                style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', border: 'none', borderTop: '1px solid ' + BC.divider, background: 'transparent', fontSize: 14, color: BC.ink, textAlign: 'left' }}>
                <Icon name="list" size={16} color={BC.inkMuted} /> Vista completa
              </button>
              <button className="bc-btn bc-nav-item" onClick={() => { setMenuOpen(false); onLogout(); }}
                style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', border: 'none', borderTop: '1px solid ' + BC.divider, background: 'transparent', fontSize: 14, color: BC.fire.fg, textAlign: 'left', fontWeight: 600 }}>
                <Icon name="logout" size={16} /> Cerrar sesión
              </button>
            </div>
          )}
        </div>
      </div>

      {/* ── "Enter full view?" confirmation ── */}
      {confirmExit && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 500, background: 'rgba(15,23,42,.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}
          onClick={() => setConfirmExit(false)}>
          <div className="bc-slide-up" onClick={(e) => e.stopPropagation()}
            style={{ width: '100%', maxWidth: 340, background: BC.surface, borderRadius: 14, boxShadow: BC.shadowLg, padding: 20, color: BC.ink }}>
            <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 8 }}>¿Entrar a la vista completa?</div>
            <div style={{ fontSize: 13, color: BC.inkSubtle, lineHeight: 1.4, marginBottom: 20 }}>
              Podrás volver a la vista simplificada en cualquier momento desde el menú de usuario.
            </div>
            <div style={{ display: 'flex', gap: 10 }}>
              <button className="bc-btn" onClick={() => setConfirmExit(false)}
                style={{ flex: 1, padding: '10px 14px', borderRadius: 8, border: '1px solid ' + BC.border, background: BC.surface, color: BC.ink, fontSize: 13, fontWeight: 600, fontFamily: 'inherit' }}>
                Cancelar
              </button>
              <button className="bc-btn" onClick={() => { setConfirmExit(false); onExitMobile && onExitMobile(); }}
                style={{ flex: 1, padding: '10px 14px', borderRadius: 8, border: 'none', background: BC.brand, color: '#fff', fontSize: 13, fontWeight: 600, fontFamily: 'inherit' }}>
                Entrar
              </button>
            </div>
          </div>
        </div>
      )}

      {/* ── Transient hint banner (badge tap / push activation) ── */}
      {notifHint && (
        <div className="bc-slide-up" onClick={() => setNotifHint(null)}
          style={{ position: 'fixed', top: 'calc(64px + env(safe-area-inset-top))', left: 12, right: 12, zIndex: 360, display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px', background: BC.ink, color: '#fff', borderRadius: 10, fontSize: 12, boxShadow: BC.shadowLg, cursor: 'pointer' }}>
          <Icon name="bell" size={15} color="#fff" />
          {notifHint}
        </div>
      )}

      {/* ── Incoming-event notifications (stacked, newest on top) ── */}
      {evtToasts.length > 0 && (
        <div style={{ position: 'fixed', bottom: 'calc(40px + env(safe-area-inset-bottom))', left: 12, right: 12, zIndex: 350, display: 'flex', flexDirection: 'column', gap: 8 }}>
          {evtToasts.map((ev) => {
            const c = classifyColor(ev.cls);
            const cat = classifyCategory(ev.cls);
            return (
              <div key={ev.id} className="bc-slide-up" onClick={() => setEvtToasts((prev) => prev.filter((e) => e.id !== ev.id))}
                style={{ display: 'flex', alignItems: 'flex-start', gap: 10, padding: '12px 14px', background: BC.surface, borderRadius: 12, border: '1px solid ' + c.bar, borderLeft: '4px solid ' + c.bar, boxShadow: BC.shadowLg, cursor: 'pointer' }}>
                <span style={{ width: 34, height: 34, borderRadius: 8, flex: '0 0 34px', display: 'flex', alignItems: 'center', justifyContent: 'center', background: c.bg, color: c.fg }}>
                  <Icon name={cat === 'alarm' ? 'fire' : cat === 'trouble' ? 'warning' : cat === 'supervisory' ? 'shield' : 'bell'} size={18} color={c.fg} />
                </span>
                <span style={{ flex: 1, minWidth: 0 }}>
                  <span style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11, fontFamily: BC.mono, fontWeight: 700, color: c.fg, letterSpacing: '.04em' }}>
                    <span style={{ flex: '0 0 auto' }}>{classifyLabel(ev.cls)}</span>
                    <span style={{ minWidth: 0, flex: '1 1 auto', color: BC.inkSubtle, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>· {panelName(ev.client_id)}</span>
                    {ev.timestamp && <span style={{ flex: '0 0 auto', marginLeft: 'auto', color: BC.inkFaint, fontWeight: 500 }}>{relTime(ev.timestamp)}</span>}
                  </span>
                  <span style={{ fontSize: 13, color: BC.ink, marginTop: 3, overflow: 'hidden', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical' }}>{ev.message}</span>
                </span>
                <Icon name="close" size={15} color={BC.inkFaint} />
              </div>
            );
          })}
        </div>
      )}

      {/* ── Body ── */}
      <div className="bc-scroll" style={{ flex: 1, overflowY: 'auto', padding: '18px 16px 28px' }}>
        <div style={{ maxWidth: 460, margin: '0 auto' }}>
          <div style={{ fontSize: 11, fontWeight: 700, color: BC.inkSubtle, letterSpacing: '.09em', textTransform: 'uppercase', marginBottom: 8 }}>Panel</div>
          <MobilePanelPicker clients={sortedClients} selected={selected} setSelected={setSelected} newAlarmsByClient={newAlarmsByClient} />

          <div style={{ fontSize: 11, fontWeight: 700, color: BC.inkSubtle, letterSpacing: '.09em', textTransform: 'uppercase', margin: '22px 0 8px' }}>Comando</div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            {MOBILE_ACTIONS.map((action) => {
              const cmd = triad[action.key];
              const g = gate(cmd);
              const tone = BC[action.tone];
              const awaitingConfirm = confirmKey === action.key;
              return (
                <button key={action.key} className="bc-btn bc-cmd-card" disabled={g.disabled}
                  onClick={() => handleAction(action, cmd)}
                  style={{ position: 'relative', width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, padding: '24px 14px', minHeight: 164, borderRadius: 14, border: 'none', background: action.bg || tone.bar, boxShadow: awaitingConfirm ? BC.shadowLg + ', inset 0 0 0 3px rgba(255,255,255,.6)' : BC.shadowLg, color: '#fff', textAlign: 'center', opacity: g.disabled ? 0.5 : 1 }}>
                  <span style={{ width: 64, height: 64, borderRadius: 14, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(255,255,255,.22)' }}>
                    <Icon name={awaitingConfirm ? 'warning' : action.icon} size={34} color="#fff" strokeWidth={2.5} />
                  </span>
                  <span style={{ minWidth: 0 }}>
                    <span style={{ display: 'block', fontSize: 17, fontWeight: 800, lineHeight: 1.25, letterSpacing: '.01em' }}>{awaitingConfirm ? '¿Confirmar reinicio?' : action.label}</span>
                    {awaitingConfirm && <span style={{ display: 'block', fontSize: 10, fontFamily: BC.mono, letterSpacing: '.04em', marginTop: 4, opacity: .85 }}>Toque de nuevo para ejecutar</span>}
                  </span>
                  {cmd?.admin_only && !awaitingConfirm && (
                    <span style={{ position: 'absolute', top: 10, right: 10, fontSize: 9, padding: '2px 6px', borderRadius: 3, background: 'rgba(255,255,255,.28)', color: '#fff', fontFamily: BC.mono, letterSpacing: '.04em', fontWeight: 700 }}>ADM</span>
                  )}
                </button>
              );
            })}
          </div>

          {/* Result toast */}
          {lastCmdResult && (
            <div style={{ marginTop: 18, display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, padding: '10px 14px', borderRadius: 10, background: lastCmdResult.ok ? BC.ok.bg : BC.fire.bg, color: lastCmdResult.ok ? BC.ok.fg : BC.fire.fg, border: '1px solid ' + (lastCmdResult.ok ? BC.ok.bar : BC.fire.bar) }}>
              <Icon name={lastCmdResult.ok ? 'check' : 'warning'} size={16} />{lastCmdResult.msg}
            </div>
          )}
        </div>
      </div>

      {/* ── Status strip (padded for the home indicator) ── */}
      <div style={{ flex: '0 0 auto', minHeight: 30, paddingBottom: 'env(safe-area-inset-bottom)', background: BC.surfaceAlt, borderTop: '1px solid ' + BC.border, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, fontSize: 11, fontFamily: BC.mono, color: BC.inkSubtle }}>
        <span style={{ width: 7, height: 7, borderRadius: 7, background: liveState === 'live' ? BC.ok.dot : liveState === 'reconnecting' ? BC.pending.dot : BC.fire.dot }} />
        {clients.filter((c) => c.alive_status === 'connected').length} de {clients.length} paneles conectados · BControl {APP_VERSION}
      </div>
    </div>
  );
}

Object.assign(window, { useIsMobile, matchTriadCommands, MobileView });
