/* Real, backend-wired page implementations.
 * All forms use UNCONTROLLED inputs + FormData on submit to bypass any
 * React state weirdness that was preventing typing.
 * Overrides the placeholders defined in pages.jsx. */

const { useState: useIS, useEffect: useIE, useMemo: useIM, useCallback: useIC, useRef: useIR } = React;

// ---------- Loading skeleton ---------------------------------
function PageSkeleton() {
  return (
    <div className="skeleton">
      <div className="skeleton-bar w-1-3 h-lg" />
      <div className="skeleton-bar w-3-4 h-xl" />
      <div className="skeleton-bar w-1-2" />
      <div style={{ height: 8 }} />
      <div className="skeleton-row">
        <div className="skeleton-card" />
        <div className="skeleton-card" />
        <div className="skeleton-card" />
      </div>
      <div className="skeleton-bar w-full" />
      <div className="skeleton-bar w-3-4" />
      <div className="skeleton-bar w-1-2" />
    </div>
  );
}

// ---------- Shared header -----------------------------------
function PH({ eyebrow, title, sub, right }) {
  return (
    <div className="page-head">
      <div style={{ flex: 1, minWidth: 0 }}>
        <div className="page-eyebrow">/ {eyebrow}</div>
        <h1 className="page-title">{title}<span style={{ color: 'var(--green)' }}>.</span></h1>
        {sub && <p className="page-sub">{sub}</p>}
      </div>
      {right}
    </div>
  );
}

function StatusChip({ status }) {
  const map = {
    0: { label: 'ABERTA', cls: '' },
    1: { label: 'RESOLVIDA', cls: '' },
    2: { label: 'PENDENTE', cls: 'is-warn' },
    3: { label: 'SNOOZED', cls: 'is-info' },
  };
  const m = map[status] ?? { label: String(status), cls: 'is-info' };
  return <span className={`chip ${m.cls}`}>{m.label}</span>;
}

function fmtDate(iso) {
  if (!iso) return '—';
  try { return new Date(iso).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); }
  catch { return iso; }
}

function ConvLink({ display_id }) {
  if (!display_id) return null;
  return (
    <a href={`https://igreenchat.app/app/accounts/1/conversations/${display_id}`} target="_blank" rel="noreferrer"
       className="mono" style={{ color: 'var(--green)', textDecoration: 'none', letterSpacing: '0.06em' }}>
      #{display_id} ↗
    </a>
  );
}

function ConversationRow({ row }) {
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: '90px 1fr 110px 140px 140px', gap: 12, alignItems: 'center',
      padding: '10px 4px', borderTop: '1px solid rgba(255,255,255,0.03)',
    }}>
      <ConvLink display_id={row.display_id} />
      <div style={{ fontSize: 12.5, color: 'rgba(255,255,255,0.75)' }}>
        {row.contact_name || 'contato'}
        {' · '}<span className="mono" style={{ color: 'rgba(255,255,255,0.4)' }}>#{row.contact_id ?? '—'}</span>
        {(row.team_name || row.team_id) ? <> · <span className="mono" style={{ color: 'rgba(255,255,255,0.4)' }}>{row.team_name || `team ${row.team_id}`}</span></> : null}
      </div>
      <StatusChip status={row.status} />
      <span className="mono" style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)' }}>{fmtDate(row.last_activity_at ?? row.updated_at)}</span>
      <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.35)' }}>criada {fmtDate(row.created_at)}</span>
    </div>
  );
}

// ---------- Multi-select compacto, visual igual ao <select> nativo --------
function CCMultiPicker({ placeholder = 'Selecione', options, selectedKeys, onChange }) {
  const [open, setOpen] = useIS(false);
  const [search, setSearch] = useIS('');
  const ref = useIR(null);

  useIE(() => {
    if (!open) return;
    function onDown(e) {
      if (ref.current && !ref.current.contains(e.target)) setOpen(false);
    }
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  function toggle(key) {
    const set = new Set(selectedKeys);
    if (set.has(key)) set.delete(key); else set.add(key);
    onChange(Array.from(set));
  }

  const q = search.trim().toLowerCase();
  const filtered = q
    ? options.filter(o => (o.label || '').toLowerCase().includes(q) || (o.key || '').toLowerCase().includes(q))
    : options;

  const triggerLabel = (() => {
    if (selectedKeys.length === 0) return placeholder;
    if (selectedKeys.length === 1) {
      const found = options.find(o => o.key === selectedKeys[0]);
      return found ? found.label : selectedKeys[0];
    }
    return `${selectedKeys.length} selecionadas`;
  })();

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button type="button" onClick={() => setOpen(o => !o)} style={{
        ...inp, display: 'flex', alignItems: 'center', gap: 8,
        cursor: 'pointer', textAlign: 'left', userSelect: 'none',
      }}>
        <span style={{ flex: 1, color: selectedKeys.length ? 'var(--text)' : 'rgba(255,255,255,0.4)' }}>
          {triggerLabel}
        </span>
        <span style={{ opacity: 0.6, fontSize: 13 }}>{open ? '▴' : '▾'}</span>
      </button>

      {open && (
        <div style={{
          position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4,
          background: 'rgba(15,23,42,0.97)', backdropFilter: 'blur(10px)',
          border: 'none', borderRadius: 8,
          boxShadow: '0 24px 60px -10px rgba(0,0,0,0.65)',
          zIndex: 60, overflow: 'hidden',
        }}>
          <div style={{ padding: 8, borderBottom: '1px solid rgba(255,255,255,0.03)' }}>
            <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Buscar..."
              style={{ ...inp, padding: '7px 10px', fontSize: 12.5, width: '100%' }} autoFocus />
          </div>
          <div style={{ maxHeight: 240, overflowY: 'auto' }}>
            {filtered.length === 0 ? (
              <div style={{ padding: '14px 12px', color: 'rgba(255,255,255,0.45)', fontSize: 12 }}>Nada encontrado.</div>
            ) : filtered.map(opt => {
              const checked = selectedKeys.includes(opt.key);
              return (
                <button key={opt.key} type="button" onClick={() => toggle(opt.key)}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10,
                    width: '100%', padding: '8px 12px',
                    background: checked ? 'rgba(34,197,94,0.06)' : 'transparent',
                    border: 'none', color: 'inherit', textAlign: 'left', cursor: 'pointer',
                    fontSize: 12.5,
                  }}>
                  <span style={{
                    width: 14, height: 14, borderRadius: 4,
                    border: '1px solid ' + (checked ? 'var(--green)' : 'transparent'),
                    background: checked ? 'var(--green)' : 'transparent',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    color: '#06170d', fontSize: 10, fontWeight: 700, flexShrink: 0,
                  }}>{checked ? '✓' : ''}</span>
                  <span style={{ flex: 1 }}>{opt.label}</span>
                </button>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

// ---------- Single-select searchable picker (single-team / single-family) -
function CCSinglePicker({ placeholder = 'Selecione', options, value, onChange, emptyLabel = 'Nada disponível', searchPlaceholder = 'Buscar por nome ou ID…' }) {
  const [open, setOpen] = useIS(false);
  const [search, setSearch] = useIS('');
  const ref = useIR(null);

  useIE(() => {
    if (!open) return;
    function onDown(e) {
      if (ref.current && !ref.current.contains(e.target)) setOpen(false);
    }
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  const q = search.trim().toLowerCase();
  const filtered = q
    ? options.filter(o =>
        (o.label || '').toLowerCase().includes(q)
        || (o.sub || '').toLowerCase().includes(q)
        || String(o.key ?? '').toLowerCase().includes(q))
    : options;

  const current = options.find(o => String(o.key) === String(value));

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button type="button" onClick={() => setOpen(o => !o)} style={{
        ...inp, display: 'flex', alignItems: 'center', gap: 8,
        cursor: 'pointer', textAlign: 'left', userSelect: 'none',
      }}>
        <span style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                       color: current ? 'var(--text)' : 'rgba(255,255,255,0.4)' }}>
          {current ? current.label : placeholder}
        </span>
        {current?.sub && (
          <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.45)' }}>{current.sub}</span>
        )}
        <span style={{ opacity: 0.6, fontSize: 13 }}>{open ? '▴' : '▾'}</span>
      </button>

      {open && (
        <div style={{
          position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4,
          background: 'rgba(15,23,42,0.97)', backdropFilter: 'blur(10px)',
          border: 'none', borderRadius: 8,
          boxShadow: '0 24px 60px -10px rgba(0,0,0,0.65)',
          zIndex: 60, overflow: 'hidden',
        }}>
          <div style={{ padding: 8, borderBottom: '1px solid rgba(255,255,255,0.03)' }}>
            <input value={search} onChange={e => setSearch(e.target.value)} placeholder={searchPlaceholder}
              style={{ ...inp, padding: '7px 10px', fontSize: 12.5, width: '100%' }} autoFocus />
          </div>
          <div style={{ maxHeight: 240, overflowY: 'auto' }}>
            {options.length === 0 ? (
              <div style={{ padding: '14px 12px', color: 'rgba(255,255,255,0.45)', fontSize: 12 }}>{emptyLabel}</div>
            ) : filtered.length === 0 ? (
              <div style={{ padding: '14px 12px', color: 'rgba(255,255,255,0.45)', fontSize: 12 }}>Nada encontrado para "{search}".</div>
            ) : filtered.map(opt => {
              const isSel = String(opt.key) === String(value);
              return (
                <button key={opt.key} type="button"
                  onClick={() => { onChange(opt.key); setOpen(false); setSearch(''); }}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10,
                    width: '100%', padding: '8px 12px',
                    background: isSel ? 'rgba(34,197,94,0.10)' : 'transparent',
                    border: 'none', color: 'inherit', textAlign: 'left', cursor: 'pointer',
                    fontSize: 12.5,
                  }}>
                  <span style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{opt.label}</span>
                  {opt.sub && <span className="mono" style={{ fontSize: 9.5, color: 'rgba(255,255,255,0.45)' }}>{opt.sub}</span>}
                </button>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

// ---------- Uncontrolled search (uses ref + setState on input event) ----
function SearchInput({ name, placeholder, onValue, defaultValue = '' }) {
  const ref = useIR(null);
  return (
    <input
      ref={ref}
      name={name}
      type="text"
      defaultValue={defaultValue}
      placeholder={placeholder}
      onInput={(e) => onValue(e.currentTarget.value)}
      style={{ ...inp, flex: 1 }}
      autoComplete="off"
    />
  );
}

// =============================================================
// at-conv-origem — Localizar conversa  (uncontrolled)
// =============================================================
function PageConvOrigemReal() {
  const inputRef = useIR(null);
  const [loading, setLoading] = useIS(false);
  const [error, setError] = useIS(null);
  const [result, setResult] = useIS(null);

  async function submit(e) {
    e.preventDefault();
    const q = inputRef.current?.value?.trim() ?? '';
    if (!q) return;
    setLoading(true); setError(null);
    try {
      const data = await window.CCApi.me.find(q);
      setResult(data);
    } catch (err) { setError(err.message); setResult(null); }
    finally { setLoading(false); }
  }

  return (
    <div className="page">
      <PH eyebrow="PORTAL DO GESTOR · OPERAÇÃO" title="Localizar conversa"
          sub="Digite id da conversa, nome ou telefone do cliente" />

      <form onSubmit={submit} className="card card-topline" style={{ padding: 16, display: 'flex', gap: 10, alignItems: 'center' }}>
        <Icon name="search" size={14} />
        <input ref={inputRef} name="q" type="text" autoComplete="off"
          placeholder="Ex.: 12345  ·  Maria Souza  ·  11988887777"
          style={{ flex: 1, background: 'transparent', border: 'none', outline: 'none', color: 'var(--text)', fontSize: 14 }} />
        <button type="submit" disabled={loading} style={btnPrimary(loading)}>
          {loading ? 'Buscando…' : 'Buscar'}
        </button>
      </form>

      {error && <div className="chip is-warn" style={{ marginTop: 14 }}>{error}</div>}

      {result && (
        <div className="card" style={{ padding: 0, marginTop: 14, overflow: 'hidden' }}>
          <div style={{ padding: '10px 16px', borderBottom: '1px solid rgba(255,255,255,0.03)', display: 'flex', alignItems: 'center', gap: 10 }}>
            <span style={{ color: 'var(--green)' }}>●</span>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.22em' }}>
              / RESULTADO · MODO {String(result.mode ?? 'auto').toUpperCase()}
            </span>
            <span style={{ flex: 1 }} />
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>{result.count ?? (result.items?.length ?? 0)} CONVERSAS</span>
          </div>
          {(result.items ?? []).length === 0 ? (
            <div style={{ padding: 22, color: 'rgba(255,255,255,0.5)', fontSize: 13 }}>
              Nada encontrado entre as suas conversas.
            </div>
          ) : (
            <div style={{ padding: '8px 16px 14px' }}>
              {result.items.map((r) => <ConversationRow key={r.id} row={r} />)}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// =============================================================
// at-backlog — Meu backlog
// =============================================================
function PageBacklogReal() {
  const [data, setData] = useIS(null);
  const [tickets, setTickets] = useIS(null);
  const [error, setError] = useIS(null);
  const [loading, setLoading] = useIS(true);

  async function load() {
    setLoading(true); setError(null);
    try {
      const [conv, tk] = await Promise.all([
        window.CCApi.me.backlog().catch(e => ({ items: [], _err: e.message })),
        window.CCApi.me.ticketsCount().catch(e => ({ total: 0, byStatus: {}, _err: e.message })),
      ]);
      setData(conv);
      setTickets(tk);
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }
  useIE(() => { load(); }, []);

  const items = data?.items ?? [];
  const openCount = items.filter(x => x.status === 0).length;
  const pendingCount = items.filter(x => x.status === 2).length;
  const ticketTotal = tickets?.total ?? 0;

  function ageDays(iso) {
    if (!iso) return null;
    return Math.floor((Date.now() - new Date(iso).getTime()) / 86400000);
  }

  return (
    <div className="page">
      <PH eyebrow="PORTAL DO GESTOR · OPERAÇÃO" title="Meu backlog"
          sub="Ordenado por data de criação — conversas mais antigas primeiro. Priorize de cima pra baixo."
          right={<button onClick={load} disabled={loading} style={btnGhost()}>{loading ? '…' : 'Atualizar'}</button>} />

      {error && <div className="chip is-warn">{error}</div>}

      {loading && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loading && <div style={{ display: 'flex', gap: 10, marginBottom: 10, flexWrap: 'wrap' }}>
        <span className="chip" style={{ fontSize: 11 }}>Abertas <strong style={{ color: 'var(--green)', marginLeft: 4 }}>{openCount}</strong></span>
        <span className="chip" style={{ fontSize: 11 }}>Pendentes <strong style={{ color: '#f59e0b', marginLeft: 4 }}>{pendingCount}</strong></span>
        <span className="chip" style={{ fontSize: 11 }}>Tickets <strong style={{ marginLeft: 4 }}>{ticketTotal}</strong></span>
        <span className="chip" style={{ fontSize: 11 }}>Total <strong style={{ marginLeft: 4 }}>{items.length + ticketTotal}</strong></span>
      </div>}

      {!loading && items.length > 0 && (
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div style={{
            display: 'grid', gridTemplateColumns: '36px 90px 1fr 100px 90px 60px',
            gap: '0 12px', padding: '6px 16px', fontSize: 9, letterSpacing: '0.18em',
            color: 'rgba(255,255,255,0.35)', borderBottom: '1px solid rgba(255,255,255,0.03)',
          }} className="mono">
            <span>#</span><span>ID</span><span>CONTATO</span><span>STATUS</span><span>CRIADA</span><span>DIAS</span>
          </div>
          {items.map((r, i) => {
            const days = ageDays(r.created_at);
            const urgent = days != null && days >= 3;
            return (
              <div key={r.id} style={{
                display: 'grid', gridTemplateColumns: '36px 90px 1fr 100px 90px 60px',
                gap: '0 12px', padding: '5px 16px', alignItems: 'center',
                fontSize: 12, borderTop: i ? '1px solid rgba(255,255,255,0.04)' : 'none',
                background: urgent ? 'rgba(239,68,68,0.06)' : 'transparent',
              }}>
                <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.3)' }}>{i + 1}</span>
                <a href={`https://igreenchat.app/app/accounts/1/conversations/${r.display_id}`}
                   target="_blank" rel="noreferrer" className="mono"
                   style={{ color: 'var(--green)', textDecoration: 'none', fontSize: 11 }}>
                  #{r.display_id}
                </a>
                <span style={{ color: 'rgba(255,255,255,0.75)', fontSize: 12, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {r.contact_name || '—'}
                </span>
                <StatusChip status={r.status} />
                <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.45)' }}>
                  {fmtDate(r.created_at)}
                </span>
                <span className="mono" style={{
                  fontSize: 11, fontWeight: 600,
                  color: urgent ? '#ef4444' : days != null && days >= 1 ? '#f59e0b' : 'rgba(255,255,255,0.5)',
                }}>
                  {days != null ? `${days}d` : '—'}
                </span>
              </div>
            );
          })}
        </div>
      )}

      {!loading && items.length === 0 && (
        <div className="card" style={{ padding: 24, color: 'rgba(255,255,255,0.5)', fontSize: 13 }}>
          Nenhuma conversa no backlog.
        </div>
      )}
    </div>
  );
}

// =============================================================
// gs-cad-criar — Criar cadastro (FormData)
// =============================================================
function PageCriarReal() {
  // form state (controlled — global Field collision já resolvida em pages-impl)
  const [email, setEmail] = useIS('');
  const [firstName, setFirstName] = useIS('');
  const [lastName, setLastName] = useIS('');
  const [nameAutoFilled, setNameAutoFilled] = useIS(true);
  const [ticketRole, setTicketRole] = useIS('agent');
  const [ticketLevel, setTicketLevel] = useIS('n1');
  const [familyKey, setFamilyKey] = useIS('');
  const [selectedTeamIds, setSelectedTeamIds] = useIS([]);
  const [subfamilyKeys, setSubfamilyKeys] = useIS([]);
  const [secondaryFamilyKeys, setSecondaryFamilyKeys] = useIS([]);
  const [showHorarios, setShowHorarios] = useIS(false);
  const [horarioEntrada, setHorarioEntrada] = useIS('');
  const [horarioSaida, setHorarioSaida] = useIS('');
  const [almocoInicio, setAlmocoInicio] = useIS('');
  const [almocoFim, setAlmocoFim] = useIS('');
  const [pausa1Inicio, setPausa1Inicio] = useIS('');
  const [pausa1Fim, setPausa1Fim] = useIS('');
  const [pausa2Inicio, setPausa2Inicio] = useIS('');
  const [pausa2Fim, setPausa2Fim] = useIS('');

  // submission state
  const [submitting, setSubmitting] = useIS(false);
  const [error, setError] = useIS(null);
  const [result, setResult] = useIS(null);
  const [copiedKey, setCopiedKey] = useIS(null);

  // server data
  const [families, setFamilies] = useIS([]);
  const [allTeams, setAllTeams] = useIS([]);
  const [teamFamiliesMap, setTeamFamiliesMap] = useIS({});

  // filas dropdown
  const [filasOpen, setFilasOpen] = useIS(false);
  const [filasSearch, setFilasSearch] = useIS('');
  const filasRef = useIR(null);

  useIE(() => {
    let cancelled = false;
    Promise.all([
      window.CCApi.admin.listFamilies().catch(() => ({ items: [] })),
      window.CCApi.admin.listTeams().catch(() => ({ items: [] })),
      window.CCApi.admin.listTeamFamilies().catch(() => ({ items: [] })),
    ]).then(([fams, tms, tfs]) => {
      if (cancelled) return;
      // Mantém todos os tipos no state (a UI filtra abaixo conforme o campo).
      setFamilies((fams.items ?? []).filter(f => f.family_key !== 'familias_all'));
      setAllTeams((tms.items ?? []).map(t => ({ id: Number(t.id), name: t.name })));
      const map = {};
      for (const r of (tfs.items ?? [])) {
        const k = r.family_key;
        const id = Number(r.team_id);
        if (!k || !Number.isFinite(id)) continue;
        if (!map[k]) map[k] = [];
        map[k].push(id);
      }
      setTeamFamiliesMap(map);
    });
    return () => { cancelled = true; };
  }, []);

  // Auto-preenche nome a partir do email (fulano.detal@... → Fulano Detal)
  useIE(() => {
    if (!nameAutoFilled) return;
    const local = email.split('@')[0] ?? '';
    if (!local) { setFirstName(''); setLastName(''); return; }
    const parts = local.split('.').map(p => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase());
    setFirstName(parts[0] || '');
    setLastName(parts.slice(1).join(' '));
  }, [email, nameAutoFilled]);

  // Pré-seleciona times da família ao trocar de família.
  useIE(() => {
    if (!familyKey) { setSelectedTeamIds([]); return; }
    setSelectedTeamIds(teamFamiliesMap[familyKey] ?? []);
  }, [familyKey, teamFamiliesMap]);

  // Reseta sub-famílias quando troca de família (subs são filhas de UMA main específica).
  useIE(() => { setSubfamilyKeys([]); }, [familyKey]);

  // Click fora fecha dropdown.
  useIE(() => {
    if (!filasOpen) return;
    function onDown(e) {
      if (filasRef.current && !filasRef.current.contains(e.target)) setFilasOpen(false);
    }
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [filasOpen]);

  function toggleTeam(teamId) {
    setSelectedTeamIds(prev =>
      prev.includes(teamId) ? prev.filter(id => id !== teamId) : [...prev, teamId]
    );
  }

  function resetForm() {
    setEmail(''); setFirstName(''); setLastName('');
    setTicketRole('agent'); setTicketLevel('n1');
    setFamilyKey(''); setSelectedTeamIds([]);
    setSubfamilyKeys([]); setSecondaryFamilyKeys([]);
    setError(null); setResult(null); setCopiedKey(null);
    setFilasOpen(false); setFilasSearch('');
  }

  function copyText(text, key) {
    if (!text) return;
    navigator.clipboard.writeText(text).then(() => {
      setCopiedKey(key);
      setTimeout(() => setCopiedKey(k => k === key ? null : k), 1800);
    }).catch(() => {});
  }

  async function submit(e) {
    e.preventDefault();
    if (!familyKey) { setError('Selecione uma família.'); return; }
    setSubmitting(true); setError(null); setResult(null);
    try {
      const payload = {
        email: email.trim().toLowerCase(),
        first_name: firstName.trim(),
        last_name: lastName.trim(),
        ticket_role: ticketRole,
        ticket_level: ticketLevel,
        family_key: familyKey,
        team_ids: selectedTeamIds,
        subfamily_keys: subfamilyKeys,
        secondary_family_keys: secondaryFamilyKeys,
        horario_entrada: horarioEntrada || null,
        horario_saida: horarioSaida || null,
        almoco_inicio: almocoInicio || null,
        almoco_fim: almocoFim || null,
        pausa_1_inicio: pausa1Inicio || null,
        pausa_1_fim: pausa1Fim || null,
        pausa_2_inicio: pausa2Inicio || null,
        pausa_2_fim: pausa2Fim || null,
      };
      const out = await window.CCApi.admin.createUser(payload);
      setResult(out);
    } catch (err) { setError(err.detail?.message || err.detail?.error || err.message); }
    finally { setSubmitting(false); }
  }

  const filteredTeams = filasSearch.trim()
    ? allTeams.filter(t => (t.name || '').toLowerCase().includes(filasSearch.trim().toLowerCase()))
    : allTeams;
  const familyTeamSet = new Set(teamFamiliesMap[familyKey] ?? []);
  const familyLabel = (families.find(f => f.family_key === familyKey) || {}).family_label || '';
  const countFamily = selectedTeamIds.filter(id => familyTeamSet.has(id)).length;
  const countExtras = selectedTeamIds.length - countFamily;

  const cadastrarBtn = {
    padding: '14px 20px', borderRadius: 12,
    background: 'linear-gradient(135deg, hsl(145 55% 38%), hsl(160 50% 30%))',
    border: 'none',
    color: '#06170d',
    fontSize: 15, fontWeight: 600, fontFamily: 'inherit',
    cursor: submitting ? 'wait' : 'pointer',
    boxShadow: '0 14px 36px -12px rgba(34,197,94,0.55)',
    width: '100%',
    opacity: submitting ? 0.7 : 1,
    letterSpacing: 0.2,
  };
  const linkBtnStyle = {
    padding: '7px 10px', borderRadius: 7,
    background: 'transparent', border: 'none',
    color: 'rgba(255,255,255,0.55)',
    fontSize: 11.5, fontFamily: 'inherit', cursor: 'pointer',
    letterSpacing: 0.2,
  };
  const miniCopyBtnStyle = {
    padding: '4px 9px', borderRadius: 6,
    background: 'rgba(255,255,255,0.04)', border: 'none',
    color: 'rgba(255,255,255,0.65)',
    fontSize: 11, fontFamily: 'inherit', cursor: 'pointer', lineHeight: 1.2,
  };
  const passwordTextStyle = {
    fontFamily: '"Geist Mono", ui-monospace, monospace',
    color: 'var(--green)', fontWeight: 600, textTransform: 'none',
    letterSpacing: 0.5, fontSize: 15, userSelect: 'all',
  };
  const emailTextStyle = {
    fontFamily: '"Geist Mono", ui-monospace, monospace',
    fontSize: 13.5, color: 'var(--text)', textTransform: 'none', userSelect: 'all',
  };
  const filasTriggerStyle = {
    ...inp, display: 'flex', alignItems: 'center', gap: 8,
    cursor: 'pointer', textAlign: 'left', userSelect: 'none',
    background: 'rgba(255,255,255,0.03)',
  };

  function RowCopy({ label, value, valueStyle, copyKey }) {
    return (
      <div style={{
        display: 'grid', gridTemplateColumns: '140px 1fr auto', gap: 12,
        padding: '9px 0', borderTop: '1px solid rgba(255,255,255,0.03)', alignItems: 'center',
      }}>
        <span className="mono" style={{ fontSize: 10.5, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.22em' }}>{label}</span>
        <span style={valueStyle}>{value}</span>
        <button type="button" onClick={() => copyText(value, copyKey)} style={miniCopyBtnStyle}>
          {copiedKey === copyKey ? '✓ copiado' : '⎘ copiar'}
        </button>
      </div>
    );
  }

  // Helper text dinâmico abaixo do trigger das filas
  let filasLegenda;
  if (selectedTeamIds.length === 0) {
    filasLegenda = 'Nenhuma fila selecionada ainda.';
  } else if (familyKey && familyTeamSet.size > 0) {
    if (countFamily === familyTeamSet.size && countExtras === 0) {
      filasLegenda = `${countFamily} time${countFamily === 1 ? '' : 's'} de ${familyLabel} pré-selecionado${countFamily === 1 ? '' : 's'}.`;
    } else {
      filasLegenda = `${countFamily} de ${familyTeamSet.size} times da ${familyLabel}` + (countExtras > 0 ? ` + ${countExtras} extra${countExtras === 1 ? '' : 's'}` : '') + ' selecionados.';
    }
  } else {
    filasLegenda = `${selectedTeamIds.length} time${selectedTeamIds.length === 1 ? '' : 's'} selecionado${selectedTeamIds.length === 1 ? '' : 's'}.`;
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · CADASTROS" title="Criar cadastro"
          sub="Onboarding completo: Chatwoot + Supabase Auth + ticket maps + famílias + times." />

      <form onSubmit={submit} className="card card-topline" style={{ padding: 26, display: 'flex', flexDirection: 'column', gap: 18 }}>
        <Field label="Email Corporativo">
          <input required type="email" value={email} onChange={e => setEmail(e.target.value)}
            placeholder="joao@igreenenergy.com.br" style={inp} autoComplete="off" />
        </Field>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
          <Field label="Nome">
            <input required type="text" value={firstName} onChange={e => { setFirstName(e.target.value); setNameAutoFilled(false); }}
              placeholder="João" style={inp} autoComplete="off" />
          </Field>
          <Field label="Sobrenome">
            <input required type="text" value={lastName} onChange={e => { setLastName(e.target.value); setNameAutoFilled(false); }}
              placeholder="Silva" style={inp} autoComplete="off" />
          </Field>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>
          <Field label="Função">
            <select value={ticketRole} onChange={e => setTicketRole(e.target.value)} style={inp}>
              <option value="agent">Agente</option>
              <option value="administrator">Administrador</option>
            </select>
          </Field>
          <Field label="Nível">
            <select value={ticketLevel} onChange={e => setTicketLevel(e.target.value)} style={inp}>
              <option value="n1">N1</option>
              <option value="n2">N2</option>
            </select>
          </Field>
          <Field label="Família">
            <select required value={familyKey} onChange={e => setFamilyKey(e.target.value)} style={inp}>
              <option value="">Selecione</option>
              {families
                // Mains + ouvidoria (special, mas operacionalmente vira família principal).
                .filter(f => f.is_active !== false && (
                  (f.kind ?? 'main') === 'main' || f.family_key === 'ouvidoria'
                ))
                .map(f => <option key={f.family_key} value={f.family_key}>{f.family_label}</option>)}
            </select>
          </Field>
        </div>

        <div ref={filasRef} style={{ position: 'relative' }}>
          <Field label="Filas que vai atender">
            {familyKey === 'ouvidoria' ? (
              <div style={{
                padding: '12px 14px', borderRadius: 8,
                background: 'rgba(59,130,246,0.06)',
                border: 'none',
                fontSize: 12.5, color: 'rgba(219,234,254,0.85)', lineHeight: 1.55,
              }}>
                Para ouvidoria não é necessário selecionar filas do Chatwoot — a gestão da ouvidoria acontece por tickets.
              </div>
            ) : (
              <>
                <div style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.45)', marginTop: -2, marginBottom: 6 }}>
                  Selecione a família para marcar automaticamente os times dela. Ajuste manualmente se precisar.
                </div>
                <button type="button" onClick={() => setFilasOpen(o => !o)} style={filasTriggerStyle}>
                  <span style={{ flex: 1, color: selectedTeamIds.length ? 'var(--text)' : 'rgba(255,255,255,0.4)' }}>
                    {selectedTeamIds.length === 0
                      ? 'Selecione as filas...'
                      : `${selectedTeamIds.length} fila${selectedTeamIds.length === 1 ? '' : 's'} selecionada${selectedTeamIds.length === 1 ? '' : 's'}`}
                  </span>
                  <span style={{ opacity: 0.6, fontSize: 13 }}>{filasOpen ? '▴' : '▾'}</span>
                </button>
                <span style={{ fontSize: 11, color: 'rgba(255,255,255,0.4)', marginTop: 6 }}>{filasLegenda}</span>
              </>
            )}
          </Field>

          {filasOpen && familyKey !== 'ouvidoria' && (
            <div style={{
              position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4,
              background: 'rgba(15,23,42,0.97)', backdropFilter: 'blur(10px)',
              border: 'none', borderRadius: 8,
              boxShadow: '0 24px 60px -10px rgba(0,0,0,0.65)',
              zIndex: 60, overflow: 'hidden',
            }}>
              <div style={{ padding: 8, borderBottom: '1px solid rgba(255,255,255,0.03)' }}>
                <input
                  value={filasSearch}
                  onChange={e => setFilasSearch(e.target.value)}
                  placeholder="Buscar fila..."
                  style={{ ...inp, padding: '7px 10px', fontSize: 12.5, width: '100%' }}
                  autoFocus
                />
              </div>
              <div style={{ maxHeight: 240, overflowY: 'auto' }}>
                {filteredTeams.length === 0 ? (
                  <div style={{ padding: '14px 12px', color: 'rgba(255,255,255,0.45)', fontSize: 12 }}>
                    Nenhuma fila.
                  </div>
                ) : filteredTeams.map(team => {
                  const checked = selectedTeamIds.includes(team.id);
                  const isFamilyTeam = familyTeamSet.has(team.id);
                  return (
                    <button
                      key={team.id} type="button" onClick={() => toggleTeam(team.id)}
                      style={{
                        display: 'flex', alignItems: 'center', gap: 10,
                        width: '100%', padding: '8px 12px',
                        background: checked ? 'rgba(34,197,94,0.06)' : 'transparent',
                        border: 'none', color: 'inherit', textAlign: 'left', cursor: 'pointer',
                        fontSize: 12.5,
                      }}>
                      <span style={{
                        width: 14, height: 14, borderRadius: 4,
                        border: '1px solid ' + (checked ? 'var(--green)' : 'transparent'),
                        background: checked ? 'var(--green)' : 'transparent',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        color: '#06170d', fontSize: 10, fontWeight: 700, flexShrink: 0,
                      }}>{checked ? '✓' : ''}</span>
                      <span style={{ flex: 1 }}>{team.name}</span>
                      {isFamilyTeam && (
                        <span className="mono" style={{ fontSize: 9, color: 'var(--green)', letterSpacing: '0.22em' }}>FAMÍLIA</span>
                      )}
                    </button>
                  );
                })}
              </div>
            </div>
          )}
        </div>

        <div>
          <button type="button" onClick={() => setShowHorarios(o => !o)} style={{
            display: 'flex', alignItems: 'center', gap: 8, width: '100%',
            padding: '10px 12px', borderRadius: 8, cursor: 'pointer',
            background: 'rgba(255,255,255,0.02)', border: 'none',
            color: 'rgba(255,255,255,0.6)', fontSize: 12,
          }}>
            <span style={{ color: 'rgba(255,255,255,0.35)', fontSize: 11, transition: 'transform 150ms', transform: showHorarios ? 'rotate(90deg)' : 'rotate(0deg)' }}>›</span>
            <span className="mono" style={{ letterSpacing: '0.18em', fontSize: 10 }}>HORÁRIOS</span>
            <span style={{ color: 'rgba(255,255,255,0.3)', fontSize: 11 }}>(opcional)</span>
            {horarioEntrada && <span style={{ marginLeft: 'auto', color: 'var(--green)', fontSize: 11 }}>{horarioEntrada} – {horarioSaida || '?'}</span>}
          </button>
          {showHorarios && (
            <div style={{
              marginTop: 8, padding: '14px 12px', borderRadius: 8,
              background: 'rgba(255,255,255,0.015)', border: 'none',
              display: 'flex', flexDirection: 'column', gap: 10,
              animation: 'page-enter 300ms ease both',
            }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                <Field label="Entrada"><input type="time" value={horarioEntrada} onChange={e => setHorarioEntrada(e.target.value)} style={inp} /></Field>
                <Field label="Saída"><input type="time" value={horarioSaida} onChange={e => setHorarioSaida(e.target.value)} style={inp} /></Field>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                <Field label="Almoço início"><input type="time" value={almocoInicio} onChange={e => setAlmocoInicio(e.target.value)} style={inp} /></Field>
                <Field label="Almoço fim"><input type="time" value={almocoFim} onChange={e => setAlmocoFim(e.target.value)} style={inp} /></Field>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                <Field label="Pausa 1 início"><input type="time" value={pausa1Inicio} onChange={e => setPausa1Inicio(e.target.value)} style={inp} /></Field>
                <Field label="Pausa 1 fim"><input type="time" value={pausa1Fim} onChange={e => setPausa1Fim(e.target.value)} style={inp} /></Field>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                <Field label="Pausa 2 início"><input type="time" value={pausa2Inicio} onChange={e => setPausa2Inicio(e.target.value)} style={inp} /></Field>
                <Field label="Pausa 2 fim"><input type="time" value={pausa2Fim} onChange={e => setPausa2Fim(e.target.value)} style={inp} /></Field>
              </div>
            </div>
          )}
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
          <Field label="Sub-famílias (opcional)">
            <CCMultiPicker
              placeholder={familyKey ? 'Selecione' : 'Selecione uma família primeiro'}
              options={families
                .filter(f => f.kind === 'subfamily' && f.parent_family_key === familyKey && f.is_active !== false)
                .map(f => ({ key: f.family_key, label: f.family_label }))}
              selectedKeys={subfamilyKeys}
              onChange={setSubfamilyKeys}
            />
          </Field>
          <Field label="Famílias secundárias (opcional)">
            <CCMultiPicker
              placeholder="Selecione"
              options={families
                .filter(f => (f.kind ?? 'main') === 'main' && f.family_key !== familyKey && f.is_active !== false)
                .map(f => ({ key: f.family_key, label: f.family_label }))}
              selectedKeys={secondaryFamilyKeys}
              onChange={setSecondaryFamilyKeys}
            />
          </Field>
        </div>

        {error && <div className="chip is-warn" style={{ alignSelf: 'flex-start' }}>{error}</div>}

        <button type="submit" disabled={submitting} style={cadastrarBtn}>
          {submitting ? 'Provisionando...' : 'Cadastrar'}
        </button>
      </form>

      {result && (
        <div className="card" style={{ padding: 22, marginTop: 16 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14, flexWrap: 'wrap' }}>
            <div className="mono" style={{ fontSize: 10, color: 'var(--green)', letterSpacing: '0.32em' }}>/ CADASTRO CRIADO</div>
            <span style={{ flex: 1 }} />
            <button type="button"
              onClick={() => copyText(`EMAIL: ${result.email}\nSENHA: ${result.password}`, 'both')}
              style={linkBtnStyle}>
              {copiedKey === 'both' ? '✓ copiado' : '⎘ copiar email e senha'}
            </button>
            <button type="button" onClick={resetForm}
              style={{ ...linkBtnStyle, color: 'rgba(255,255,255,0.78)' }}>
              ← cadastrar outro
            </button>
          </div>
          <RowCopy label="EMAIL" value={result.email} valueStyle={emailTextStyle} copyKey="email" />
          <RowCopy label="SENHA" value={result.password} valueStyle={passwordTextStyle} copyKey="password" />
          <KV k="Nome" v={result.display_name} />
          <KV k="Família" v={result.family_key} />
          <KV k="Role" v={result.ticket_role} />
          <KV k="Nível" v={(result.ticket_level || '').toUpperCase()} />
          <details style={{ marginTop: 14 }}>
            <summary className="mono" style={{ fontSize: 11, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.22em', cursor: 'pointer' }}>RELATÓRIO COMPLETO</summary>
            <pre style={{ fontSize: 11, color: 'rgba(255,255,255,0.7)', overflow: 'auto', marginTop: 8 }}>{JSON.stringify(result.report, null, 2)}</pre>
          </details>
        </div>
      )}
    </div>
  );
}

// =============================================================
// gs-cad-reset — Reset de senha (com busca de colaborador)
// =============================================================
function PageResetReal() {
  const [submitting, setSubmitting] = useIS(false);
  const [error, setError] = useIS(null);
  const [result, setResult] = useIS(null);
  const [showCustom, setShowCustom] = useIS(false);
  const [copiedKey, setCopiedKey] = useIS(null);
  const [customPassword, setCustomPassword] = useIS('');

  // user picker state
  const [users, setUsers] = useIS([]);
  const [families, setFamilies] = useIS([]);
  const [assigneeFamilies, setAssigneeFamilies] = useIS(new Map());
  const [filter, setFilter] = useIS('');
  const [familyFilter, setFamilyFilter] = useIS('');
  const [selected, setSelected] = useIS(null);
  const [loadingDir, setLoadingDir] = useIS(true);

  useIE(() => {
    let cancelled = false;
    Promise.all([
      window.CCApi.admin.listUsers().catch(() => ({ items: [] })),
      window.CCApi.admin.listFamilies().catch(() => ({ items: [] })),
      window.CCApi.admin.listAssigneeFamilies().catch(() => ({ items: [] })),
    ]).then(([u, f, af]) => {
      if (cancelled) return;
      setUsers(u.items ?? []);
      setFamilies((f.items ?? []).filter(x => x.family_key !== 'familias_all'));
      const m = new Map();
      for (const r of (af.items ?? [])) {
        m.set(Number(r.assignee_id), new Set(r.family_keys ?? []));
      }
      setAssigneeFamilies(m);
      setLoadingDir(false);
    });
    return () => { cancelled = true; };
  }, []);

  const filteredUsers = useIM(() => {
    let list = users;
    if (familyFilter) {
      list = list.filter(u => {
        const fams = assigneeFamilies.get(Number(u.assignee_id));
        return fams && fams.has(familyFilter);
      });
    }
    const q = filter.trim().toLowerCase();
    if (q) {
      list = list.filter(u =>
        (u.email || '').toLowerCase().includes(q) ||
        (u.display_name || '').toLowerCase().includes(q) ||
        String(u.assignee_id || '').includes(q));
    }
    return list.slice(0, 200);
  }, [users, filter, familyFilter, assigneeFamilies]);

  async function submit(e) {
    e.preventDefault();
    if (!selected) { setError('Selecione um colaborador.'); return; }
    setSubmitting(true); setError(null); setResult(null);
    try {
      const email = String(selected.email || '').trim().toLowerCase();
      if (!email) throw new Error('Colaborador selecionado não tem email cadastrado.');
      const custom = showCustom ? customPassword.trim() : '';
      if (showCustom && custom.length === 0) {
        throw new Error('Modo manual ativado mas senha vazia — preencha ou volte pra senha automática.');
      }
      const out = await window.CCApi.admin.resetPassword(email, custom ? { password: custom } : {});
      setResult(out);
    } catch (e) { setError(e.detail?.message || e.detail?.error || e.message); }
    finally { setSubmitting(false); }
  }

  function resetForAnother() {
    setResult(null);
    setError(null);
    setShowCustom(false);
    setCopiedKey(null);
    setCustomPassword('');
    setSelected(null);
    setFilter('');
    setFamilyFilter('');
  }

  function copyText(text, key) {
    if (!text) return;
    navigator.clipboard.writeText(text)
      .then(() => {
        setCopiedKey(key);
        setTimeout(() => setCopiedKey(k => k === key ? null : k), 1800);
      })
      .catch(() => {});
  }

  const linkBtnStyle = {
    padding: '7px 10px', borderRadius: 7,
    background: 'transparent',
    border: 'none',
    color: 'rgba(255,255,255,0.55)',
    fontSize: 11.5, fontFamily: 'inherit', cursor: 'pointer',
    letterSpacing: 0.2,
  };

  const miniCopyBtnStyle = {
    padding: '4px 9px', borderRadius: 6,
    background: 'rgba(255,255,255,0.04)',
    border: 'none',
    color: 'rgba(255,255,255,0.65)',
    fontSize: 11, fontFamily: 'inherit', cursor: 'pointer',
    lineHeight: 1.2,
  };

  // Monospace SEM uppercase — `.mono` herda text-transform: uppercase do CSS,
  // o que confundia quem tirava print da senha (lia tudo em caixa alta).
  const passwordTextStyle = {
    fontFamily: '"Geist Mono", ui-monospace, monospace',
    color: 'var(--green)',
    fontWeight: 600,
    textTransform: 'none',
    letterSpacing: 0.5,
    fontSize: 15,
    userSelect: 'all',
  };

  const emailTextStyle = {
    fontFamily: '"Geist Mono", ui-monospace, monospace',
    fontSize: 13.5,
    color: 'var(--text)',
    textTransform: 'none',
    userSelect: 'all',
  };

  function RowCopy({ label, value, valueStyle, copyKey }) {
    return (
      <div style={{
        display: 'grid', gridTemplateColumns: '140px 1fr auto', gap: 12,
        padding: '9px 0', borderTop: '1px solid rgba(255,255,255,0.03)', alignItems: 'center',
      }}>
        <span className="mono" style={{ fontSize: 10.5, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.22em' }}>{label}</span>
        <span style={valueStyle}>{value}</span>
        <button
          type="button"
          onClick={() => copyText(value, copyKey)}
          style={miniCopyBtnStyle}
          title={`Copiar ${label.toLowerCase()}`}>
          {copiedKey === copyKey ? '✓ copiado' : '⎘ copiar'}
        </button>
      </div>
    );
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · CADASTROS" title="Reset de senha"
          sub="Reseta a senha do colaborador em todos os sistemas Conexão Green." />

      {!selected && (
        <div className="card" style={{ padding: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10, flexWrap: 'wrap' }}>
            <Icon name="search" size={13} />
            <SearchInput name="resetPickerFilter" placeholder="Filtrar por nome, email ou assignee_id…" onValue={setFilter} />
            <select
              value={familyFilter}
              onChange={e => setFamilyFilter(e.target.value)}
              style={{ ...inp, maxWidth: 240, padding: '7px 9px', fontSize: 12.5 }}
              title="Filtrar por família">
              <option value="">Todas as famílias</option>
              {families.map(f => (
                <option key={f.family_key} value={f.family_key}>
                  {f.family_label} {f.count != null ? `· ${f.count}` : ''}
                </option>
              ))}
            </select>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>
              {loadingDir ? 'CARREGANDO…' : `${filteredUsers.length} EXIBIDOS`}
            </span>
          </div>
          {loadingDir ? (
            <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>
          ) : filteredUsers.length === 0 ? (
            <div style={{ padding: '24px 12px', color: 'rgba(255,255,255,0.45)', fontSize: 12.5 }}>
              Nenhum colaborador encontrado com os filtros atuais.
            </div>
          ) : (
            <div style={{ maxHeight: 320, overflow: 'auto' }}>
              {filteredUsers.map(u => {
                const isAdmin = u.ticket_role === 'administrator';
                const isSuper = !!u.is_super_admin;
                const roleLabel = isSuper ? 'DEV' : (isAdmin ? 'ADMIN' : 'AGENTE');
                const lvl = (u.ticket_level || '').toUpperCase();
                const fams = assigneeFamilies.get(Number(u.assignee_id));
                const famLabel = fams && fams.size > 0
                  ? [...fams].map(fk => (families.find(f => f.family_key === fk) || {}).family_label || fk).join(' · ')
                  : '—';
                return (
                  <button key={u.user_id} type="button" onClick={() => { setSelected(u); setError(null); }}
                    style={{
                      display: 'grid', gridTemplateColumns: '56px minmax(0,1.2fr) minmax(0,1.4fr) minmax(0,1.2fr) 84px 52px', gap: 10, alignItems: 'center',
                      width: '100%', padding: '7px 8px', borderRadius: 6,
                      background: 'transparent', border: '1px solid transparent',
                      textAlign: 'left', color: 'inherit', cursor: 'pointer',
                    }}
                    onMouseEnter={e => { e.currentTarget.style.background = 'rgba(255,255,255,0.04)'; }}
                    onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}>
                    <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.45)' }}>#{u.assignee_id}</span>
                    <span style={{ fontSize: 12.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 6 }}>
                      {u.display_name}
                      {isSuper && <span style={{ color: '#d8b4fe', display: 'inline-flex' }}><Icon name="shield" size={11} /></span>}
                    </span>
                    <span style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.55)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u.email}</span>
                    <span className="mono" style={{ fontSize: 9.5, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.14em', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={famLabel}>{famLabel}</span>
                    <span className="mono" style={{
                      justifySelf: 'start',
                      padding: '2px 8px', borderRadius: 999,
                      fontSize: 9.5, letterSpacing: '0.16em',
                      background: isSuper ? 'rgba(168,85,247,0.14)' : (isAdmin ? 'rgba(34,197,94,0.12)' : 'rgba(255,255,255,0.05)'),
                      border: '1px solid ' + (isSuper ? 'rgba(168,85,247,0.40)' : (isAdmin ? 'rgba(34,197,94,0.32)' : 'transparent')),
                      color: isSuper ? '#d8b4fe' : (isAdmin ? 'var(--green)' : 'rgba(255,255,255,0.65)'),
                      whiteSpace: 'nowrap',
                    }}>{roleLabel}</span>
                    <span className="mono" style={{
                      justifySelf: 'start',
                      padding: '2px 8px', borderRadius: 999,
                      fontSize: 9.5, letterSpacing: '0.16em',
                      background: 'rgba(255,255,255,0.04)',
                      border: 'none',
                      color: 'rgba(255,255,255,0.65)',
                      whiteSpace: 'nowrap',
                    }}>{lvl || '—'}</span>
                  </button>
                );
              })}
            </div>
          )}
        </div>
      )}

      {selected && !result && (
        <form onSubmit={submit} key={selected.user_id} className="card card-topline" style={{ padding: 22, display: 'grid', gap: 14, gridTemplateColumns: '1fr 1fr', marginTop: 14 }}>
          <div style={{ gridColumn: 'span 2', display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <div className="mono" style={{ fontSize: 10, color: 'var(--green)', letterSpacing: '0.32em' }}>/ COLABORADOR SELECIONADO</div>
            <span style={{ flex: 1 }} />
            <button type="button" onClick={() => { setSelected(null); setError(null); }} style={linkBtnStyle}>
              ← trocar
            </button>
          </div>
          <Field label="Nome" style={{ gridColumn: 'span 1' }}>
            <input value={selected.display_name || ''} readOnly style={{ ...inp, opacity: 0.85 }} />
          </Field>
          <Field label="E-mail" style={{ gridColumn: 'span 1' }}>
            <input value={selected.email || ''} readOnly style={{ ...inp, opacity: 0.85, fontFamily: '"Geist Mono", ui-monospace, monospace', fontSize: 13, textTransform: 'none' }} />
          </Field>

          {showCustom && (
            <Field label="Senha manual" style={{ gridColumn: 'span 2' }}>
              <input
                key="custom-pw"
                name="password"
                type="text"
                value={customPassword}
                onChange={e => setCustomPassword(e.target.value)}
                placeholder="Mínimo 6 caracteres"
                style={inp}
                autoComplete="off"
                minLength={6}
                autoFocus
              />
            </Field>
          )}

          <div style={{ gridColumn: 'span 2', display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
            <button type="submit" disabled={submitting} style={btnPrimary(submitting)}>
              {submitting ? 'Resetando…' : (showCustom ? 'Resetar com senha definida' : 'Resetar (gerar automática)')}
            </button>
            <button
              type="button"
              onClick={() => { setShowCustom(s => !s); setError(null); }}
              style={linkBtnStyle}>
              {showCustom ? '× voltar pra senha automática' : '+ definir senha manualmente'}
            </button>
            {error && <span className="chip is-warn">{error}</span>}
          </div>
        </form>
      )}

      {result && (() => {
        const supaOk = result.supabase === 'ok';
        const cwOk = result.chatwoot === 'ok';
        const bothOk = supaOk && cwOk;
        const statusLabel = bothOk
          ? '✓ Sincronizado em todos os sistemas Conexão Green (Chatwoot, Dashboard, Tickets, Suporte ao Gestor)'
          : (!supaOk && cwOk) ? '⚠ Atualizado só no Chatwoot'
          : (supaOk && !cwOk) ? '⚠ Atualizado só nos sistemas internos'
          : '✗ Falha em todos os sistemas';
        const statusColor = bothOk ? 'var(--green)' : 'rgba(245,158,11,0.95)';
        const statusBorder = bothOk ? 'rgba(34,197,94,0.32)' : 'rgba(245,158,11,0.35)';
        return (
          <div className="card" style={{ padding: 22, marginTop: 16 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14, flexWrap: 'wrap' }}>
              <div className="mono" style={{ fontSize: 10, color: 'var(--green)', letterSpacing: '0.32em' }}>/ SENHA REDEFINIDA</div>
              <span className="chip" style={{ color: statusColor, borderColor: statusBorder }}>{statusLabel}</span>
              <span style={{ flex: 1 }} />
              <button
                type="button"
                onClick={() => copyText(`EMAIL: ${result.email}\nNOVA SENHA: ${result.password}`, 'both')}
                style={linkBtnStyle}>
                {copiedKey === 'both' ? '✓ copiado' : '⎘ copiar email e senha'}
              </button>
              <button
                type="button"
                onClick={resetForAnother}
                style={{ ...linkBtnStyle, color: 'rgba(255,255,255,0.78)' }}>
                ← resetar outra pessoa
              </button>
            </div>
            <RowCopy label="EMAIL" value={result.email} valueStyle={emailTextStyle} copyKey="email" />
            <RowCopy label="NOVA SENHA" value={result.password} valueStyle={passwordTextStyle} copyKey="password" />
          </div>
        );
      })()}
    </div>
  );
}

// =============================================================
// gs-cad-role — Alterar role  (filter uncontrolled via onInput)
// =============================================================
function PageRoleReal() {
  const [users, setUsers] = useIS([]);
  const [filter, setFilter] = useIS('');
  const [selected, setSelected] = useIS(null);
  const [busy, setBusy] = useIS(false);
  const [error, setError] = useIS(null);
  const [ok, setOk] = useIS(null);
  const [loadingDir, setLoadingDir] = useIS(true);
  const formRef = useIR(null);
  const actorIsSuper = !!(window.__identity && window.__identity.isSuperAdmin);
  const targetIsSuper = !!(selected && selected.is_super_admin);
  const protectedTarget = targetIsSuper && !actorIsSuper;

  useIE(() => { window.CCApi.admin.listUsers().then(r => { setUsers(r.items ?? []); setLoadingDir(false); }).catch(e => { setError(e.message); setLoadingDir(false); }); }, []);

  const filtered = useIM(() => {
    const q = filter.trim().toLowerCase();
    if (!q) return users.slice(0, 100);
    return users.filter(u =>
      (u.email || '').toLowerCase().includes(q) ||
      (u.display_name || '').toLowerCase().includes(q) ||
      String(u.assignee_id || '').includes(q)).slice(0, 100);
  }, [users, filter]);

  async function apply(e) {
    e.preventDefault();
    if (!selected) return;
    const newRole = new FormData(e.currentTarget).get('ticket_role') || 'agent';
    setBusy(true); setError(null); setOk(null);
    try {
      const r = await window.CCApi.admin.setRole(selected.user_id, newRole);
      const label = r.ticket_role === 'administrator' ? 'Administrador' : 'Agente';
      setOk(`Permissão de ${selected.email} agora é ${label}.`);
      setUsers(u => u.map(x => x.user_id === selected.user_id ? { ...x, ticket_role: newRole } : x));
      setSelected(s => ({ ...s, ticket_role: newRole }));
    } catch (e) { setError(e.detail?.error || e.message); }
    finally { setBusy(false); }
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · CADASTROS" title="Alterar permissão"
          sub="Promover ou rebaixar colaborador entre Agente e Administrador." />
      {loadingDir && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}
      {!loadingDir && <UserPickerPlus users={filtered} onFilter={setFilter} selected={selected} onSelect={setSelected} />}

      {selected && (
        <form ref={formRef} onSubmit={apply} key={selected.user_id} className="card card-topline" style={{ padding: 22, marginTop: 14, display: 'grid', gap: 12, gridTemplateColumns: 'minmax(0, 1.4fr) minmax(0, 1fr) auto' }}>
          <Field label="Colaborador">
            <input value={`${selected.display_name} · ${selected.email}`} readOnly style={{ ...inp, opacity: 0.85 }} />
          </Field>
          <Field label="Nova permissão">
            <select name="ticket_role" defaultValue={selected.ticket_role || 'agent'} style={{ ...inp, opacity: protectedTarget ? 0.55 : 1 }} disabled={protectedTarget}>
              <option value="agent">Agente</option>
              <option value="administrator">Administrador</option>
            </select>
          </Field>
          <div style={{ display: 'flex', alignItems: 'flex-end' }}>
            <button type="submit" disabled={busy || protectedTarget} style={btnPrimary(busy || protectedTarget)}>
              {busy ? '…' : 'Aplicar'}
            </button>
          </div>
          {protectedTarget && (
            <div className="chip is-info" style={{ gridColumn: '1 / -1' }}>
              <Icon name="lock" size={11} /> Desenvolvedor — apenas outro desenvolvedor pode alterar.
            </div>
          )}
          {ok && <div className="chip" style={{ gridColumn: '1 / -1', color: 'var(--green)' }}>{ok}</div>}
          {error && <div className="chip is-warn" style={{ gridColumn: '1 / -1' }}>{error}</div>}
        </form>
      )}
    </div>
  );
}

// =============================================================
// gs-cad-nivel — Alterar nível
// =============================================================
function PageNivelReal() {
  const [users, setUsers] = useIS([]);
  const [filter, setFilter] = useIS('');
  const [selected, setSelected] = useIS(null);
  const [busy, setBusy] = useIS(false);
  const [error, setError] = useIS(null);
  const [ok, setOk] = useIS(null);
  const [loadingDir, setLoadingDir] = useIS(true);
  const actorIsSuper = !!(window.__identity && window.__identity.isSuperAdmin);
  const targetIsSuper = !!(selected && selected.is_super_admin);
  const protectedTarget = targetIsSuper && !actorIsSuper;

  useIE(() => { window.CCApi.admin.listUsers().then(r => { setUsers(r.items ?? []); setLoadingDir(false); }).catch(e => { setError(e.message); setLoadingDir(false); }); }, []);

  const filtered = useIM(() => {
    const q = filter.trim().toLowerCase();
    if (!q) return users.slice(0, 100);
    return users.filter(u =>
      (u.email || '').toLowerCase().includes(q) ||
      (u.display_name || '').toLowerCase().includes(q) ||
      String(u.assignee_id || '').includes(q)).slice(0, 100);
  }, [users, filter]);

  async function apply(e) {
    e.preventDefault();
    if (!selected) return;
    const newLevel = new FormData(e.currentTarget).get('ticket_level') || 'n1';
    setBusy(true); setError(null); setOk(null);
    try {
      const r = await window.CCApi.admin.setLevel(selected.user_id, newLevel);
      setOk(`Nível de ${selected.email} agora é ${r.ticket_level.toUpperCase()}.`);
      setUsers(u => u.map(x => x.user_id === selected.user_id ? { ...x, ticket_level: newLevel } : x));
      setSelected(s => ({ ...s, ticket_level: newLevel }));
    } catch (e) { setError(e.detail?.error || e.message); }
    finally { setBusy(false); }
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · CADASTROS" title="Alterar nível"
          sub="Promover entre N1 e N2 conforme avaliação técnica." />
      {loadingDir && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}
      {!loadingDir && <UserPickerPlus users={filtered} onFilter={setFilter} selected={selected} onSelect={setSelected} />}

      {selected && (
        <form onSubmit={apply} key={selected.user_id} className="card card-topline" style={{ padding: 22, marginTop: 14, display: 'grid', gap: 12, gridTemplateColumns: 'minmax(0, 1.4fr) minmax(0, 1fr) auto' }}>
          <Field label="Colaborador">
            <input value={`${selected.display_name} · ${selected.email}`} readOnly style={{ ...inp, opacity: 0.85 }} />
          </Field>
          <Field label="Novo nível">
            <select name="ticket_level" defaultValue={selected.ticket_level || 'n1'} style={{ ...inp, opacity: protectedTarget ? 0.55 : 1 }} disabled={protectedTarget}>
              <option value="n1">N1</option>
              <option value="n2">N2</option>
            </select>
          </Field>
          <div style={{ display: 'flex', alignItems: 'flex-end' }}>
            <button type="submit" disabled={busy || protectedTarget} style={btnPrimary(busy || protectedTarget)}>
              {busy ? '…' : 'Aplicar'}
            </button>
          </div>
          {protectedTarget && (
            <div className="chip is-info" style={{ gridColumn: '1 / -1' }}>
              <Icon name="lock" size={11} /> Desenvolvedor — apenas outro desenvolvedor pode alterar.
            </div>
          )}
          {ok && <div className="chip" style={{ gridColumn: '1 / -1', color: 'var(--green)' }}>{ok}</div>}
          {error && <div className="chip is-warn" style={{ gridColumn: '1 / -1' }}>{error}</div>}
        </form>
      )}
    </div>
  );
}

// =============================================================
// gs-cad-desligar — Excluir cadastro (soft offboarding)
// Espelha a skill remover-colaborador. Não deleta o agente Chatwoot —
// reseta senha + remove times/inbox + soft-delete em dash_assignee_families
// + hard-delete em ticket_user_* + hard-delete auth.users.
// =============================================================
function PageDesligarReal() {
  const [users, setUsers] = useIS([]);
  const [filter, setFilter] = useIS('');
  const [selected, setSelected] = useIS(null);
  const [confirmText, setConfirmText] = useIS('');
  const [busy, setBusy] = useIS(false);
  const [error, setError] = useIS(null);
  const [result, setResult] = useIS(null);
  const [loadingDir, setLoadingDir] = useIS(true);
  const actorIsSuper = !!(window.__identity && window.__identity.isSuperAdmin);
  const targetIsSuper = !!(selected && selected.is_super_admin);
  const protectedTarget = targetIsSuper && !actorIsSuper;

  useIE(() => { window.CCApi.admin.listUsers().then(r => { setUsers(r.items ?? []); setLoadingDir(false); }).catch(e => { setError(e.message); setLoadingDir(false); }); }, []);

  const filtered = useIM(() => {
    const q = filter.trim().toLowerCase();
    if (!q) return users.slice(0, 100);
    return users.filter(u =>
      (u.email || '').toLowerCase().includes(q) ||
      (u.display_name || '').toLowerCase().includes(q) ||
      String(u.assignee_id || '').includes(q)).slice(0, 100);
  }, [users, filter]);

  const expectedConfirm = selected ? `DESLIGAR ${selected.display_name || selected.email || ''}`.trim() : '';
  const canSubmit = !!selected && !protectedTarget && confirmText.trim() === expectedConfirm && !busy;

  async function execute() {
    if (!canSubmit) return;
    setBusy(true); setError(null); setResult(null);
    try {
      const r = await window.CCApi.admin.offboardUser(selected.assignee_id, selected.email);
      setResult(r);
      // Remove o cara da lista local (já tá desligado)
      setUsers(prev => prev.filter(u => u.assignee_id !== selected.assignee_id));
    } catch (e) {
      setError(e.detail?.message || e.detail?.error || e.message);
    } finally {
      setBusy(false);
    }
  }

  function startOver() {
    setSelected(null); setConfirmText(''); setError(null); setResult(null);
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · CADASTROS" title="Excluir cadastro"
          sub="Soft offboarding: revoga acessos sem deletar o agente — preserva todo o histórico." />

      {loadingDir && !selected && !result && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}
      {!loadingDir && !selected && !result && (
        <UserPickerPlus users={filtered} onFilter={setFilter} selected={null} onSelect={setSelected} />
      )}

      {selected && !result && (
        <div className="card card-topline" style={{ padding: 22, marginTop: 14, display: 'grid', gap: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <div className="mono" style={{ fontSize: 10, color: 'var(--danger)', letterSpacing: '0.32em' }}>/ CONFIRMAR DESLIGAMENTO</div>
            <span style={{ flex: 1 }} />
            <button type="button" onClick={() => { setSelected(null); setConfirmText(''); setError(null); }}
                    style={{ padding: '7px 10px', borderRadius: 7, background: 'transparent', border: 'none', color: 'rgba(255,255,255,0.55)', fontSize: 11.5, fontFamily: 'inherit', cursor: 'pointer' }}>
              ← trocar
            </button>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            <Field label="Colaborador">
              <input value={`${selected.display_name || '—'} · #${selected.assignee_id}`} readOnly
                     style={{ ...inp, opacity: 0.85 }} />
            </Field>
            <Field label="E-mail">
              <input value={selected.email || ''} readOnly
                     style={{ ...inp, opacity: 0.85, fontFamily: '"Geist Mono", ui-monospace, monospace', fontSize: 13, textTransform: 'none' }} />
            </Field>
          </div>

          <div style={{
            padding: 14, marginTop: 4, borderRadius: 8,
            background: 'rgba(239,68,68,0.06)', border: 'none',
          }}>
            <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.28em', color: 'rgba(239,68,68,0.95)', marginBottom: 6 }}>
              ATENÇÃO
            </div>
            <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.65)', lineHeight: 1.7 }}>
              Reseta senha, remove de todos os times e inbox, desatribui conversas abertas, marca como desligado e apaga mappings de tickets. O histórico de mensagens no Chatwoot é preservado.
            </div>
          </div>

          {protectedTarget && (
            <div className="chip is-info" style={{ alignSelf: 'flex-start' }}>
              <Icon name="lock" size={11} /> Desenvolvedor — só outro super admin pode desligar.
            </div>
          )}

          <div>
            <div style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.55)', marginBottom: 6 }}>
              Digite <strong style={{ color: 'var(--danger)', fontFamily: '"Geist Mono", monospace' }}>{expectedConfirm}</strong> para liberar o botão
            </div>
            <input
              value={confirmText}
              onChange={e => setConfirmText(e.target.value)}
              placeholder={expectedConfirm}
              style={{ ...inp, fontFamily: '"Geist Mono", ui-monospace, monospace', fontSize: 13, width: '100%' }}
              autoComplete="off"
              disabled={protectedTarget}
            />
          </div>

          {!busy && (
            <div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
              <button type="button" onClick={execute} disabled={!canSubmit}
                      style={{
                        padding: '10px 18px', borderRadius: 10, border: '1px solid rgba(239,68,68,0.40)',
                        background: canSubmit ? 'linear-gradient(135deg, #ef4444, #b91c1c)' : 'rgba(239,68,68,0.18)',
                        color: canSubmit ? '#fff' : 'rgba(255,255,255,0.45)',
                        fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
                        cursor: canSubmit ? 'pointer' : 'not-allowed',
                        boxShadow: canSubmit ? '0 12px 28px -12px rgba(239,68,68,0.55)' : 'none',
                      }}>
                Desligar colaborador
              </button>
              {error && <span className="chip is-warn">{error}</span>}
            </div>
          )}
          {busy && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '14px 0' }}>
              <span className="dot-pulse" />
              <span style={{ fontSize: 13, color: 'rgba(255,255,255,0.7)' }}>Executando offboarding... isso pode levar alguns segundos.</span>
            </div>
          )}
        </div>
      )}

      {result && (
        <div className="card card-topline" style={{ padding: 22, marginTop: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
            <span style={{ fontSize: 20 }}>✓</span>
            <div>
              <div style={{ fontSize: 15, fontWeight: 500 }}>Colaborador desligado com sucesso</div>
              <div style={{ fontSize: 12.5, color: 'rgba(255,255,255,0.55)', marginTop: 4 }}>
                {result.report?.name || result.report?.email || ''} foi removido dos times, inbox e filas de atribuição. As resoluções e o histórico de conversas continuam disponíveis para consulta. O colaborador não aparecerá mais no Chatwoot nem no Dashboard do Suporte ao Cliente.
              </div>
            </div>
          </div>
          <button type="button" onClick={startOver}
                  style={{ padding: '8px 14px', borderRadius: 7, background: 'rgba(255,255,255,0.04)', border: 'none', color: 'rgba(255,255,255,0.7)', fontSize: 12, cursor: 'pointer' }}>
            Desligar outro colaborador
          </button>
        </div>
      )}
    </div>
  );
}

function UserPickerPlus({ users, onFilter, selected, onSelect }) {
  return (
    <div className="card" style={{ padding: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
        <Icon name="search" size={13} />
        <SearchInput name="userPickerFilter" placeholder="Filtrar por nome, email ou assignee_id…" onValue={onFilter} />
        <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>{users.length} EXIBIDOS</span>
      </div>
      <div style={{ maxHeight: 280, overflow: 'auto' }}>
        {users.map(u => {
          const isSel = selected?.user_id === u.user_id;
          const isAdmin = u.ticket_role === 'administrator';
          const isSuper = !!u.is_super_admin;
          const roleLabel = isSuper ? 'DEV' : (isAdmin ? 'ADMIN' : 'AGENTE');
          const lvl = (u.ticket_level || '').toUpperCase();
          return (
            <button key={u.user_id} type="button" onClick={() => onSelect(u)}
              style={{
                display: 'grid', gridTemplateColumns: '56px minmax(0,1.2fr) minmax(0,1.4fr) 88px 52px', gap: 10, alignItems: 'center',
                width: '100%', padding: '7px 8px', borderRadius: 6,
                background: isSel ? 'rgba(34,197,94,0.10)' : 'transparent',
                border: '1px solid ' + (isSel ? 'rgba(34,197,94,0.22)' : 'transparent'),
                textAlign: 'left', color: 'inherit', cursor: 'pointer',
              }}>
              <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.45)' }}>#{u.assignee_id}</span>
              <span style={{ fontSize: 12.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 6 }}>
                {u.display_name}
                {isSuper && <span style={{ color: '#d8b4fe', display: 'inline-flex' }}><Icon name="shield" size={11} /></span>}
              </span>
              <span style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.55)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u.email}</span>
              <span className="mono" style={{
                justifySelf: 'start',
                padding: '2px 8px', borderRadius: 999,
                fontSize: 9.5, letterSpacing: '0.16em',
                background: isSuper ? 'rgba(168,85,247,0.14)' : (isAdmin ? 'rgba(34,197,94,0.12)' : 'rgba(255,255,255,0.05)'),
                border: '1px solid ' + (isSuper ? 'rgba(168,85,247,0.40)' : (isAdmin ? 'rgba(34,197,94,0.32)' : 'transparent')),
                color: isSuper ? '#d8b4fe' : (isAdmin ? 'var(--green)' : 'rgba(255,255,255,0.65)'),
                whiteSpace: 'nowrap',
              }}>{roleLabel}</span>
              <span className="mono" style={{
                justifySelf: 'start',
                padding: '2px 8px', borderRadius: 999,
                fontSize: 9.5, letterSpacing: '0.16em',
                background: 'rgba(255,255,255,0.04)',
                border: 'none',
                color: 'rgba(255,255,255,0.65)',
                whiteSpace: 'nowrap',
              }}>{lvl || '—'}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

// =============================================================
// gs-fam-col — Colaborador × Família  (FormData)
// =============================================================
function FamilyOrgCard({ m, tier }) {
  const name = m.assignee_name || `Assignee #${m.assignee_id}`;
  const initials = (name.split(/[\s.@_-]/).filter(Boolean).slice(0, 2).map(s => s[0]?.toUpperCase()).join('')) || 'CC';
  const lvl = m.ticket_level ? String(m.ticket_level).toUpperCase() : null;
  return (
    <div className={`org-card org-card-${tier}`}>
      <div className="org-card-head">
        <div className="org-card-avatar">{initials}</div>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div className="org-card-name" title={name}>{name}</div>
          {m.assignee_email && <div className="org-card-email" title={m.assignee_email}>{m.assignee_email}</div>}
        </div>
      </div>
      <div className="org-card-foot">
        <span className="mono org-pill">#{m.assignee_id}</span>
        {tier === 'admin'
          ? <span className="mono org-pill org-pill-admin">ADMIN</span>
          : <span className="mono org-pill">AGENT</span>}
        {lvl === 'N2' && <span className="mono org-pill org-pill-n2">N2</span>}
        {lvl === 'N1' && <span className="mono org-pill org-pill-n1">N1</span>}
      </div>
    </div>
  );
}

function FamilyOrgChart({ family, parentLabel, members }) {
  const admins = members.filter(m => m.ticket_role === 'administrator');
  const agents = members.filter(m => m.ticket_role !== 'administrator');
  return (
    <div className="org-chart">
      <div className="org-node-root">
        <div className="org-root-tag mono">/ FAMÍLIA</div>
        <div className="org-root-title">{family?.family_label || family?.family_key || '—'}</div>
        <div className="org-root-meta mono">
          <span>{members.length} membros</span>
          <span className="org-dot">·</span>
          <span>{admins.length} admin</span>
          <span className="org-dot">·</span>
          <span>{agents.length} agentes</span>
        </div>
        {family?.parent_family_key && (
          <div className="org-root-parent mono">↳ subfamília de {parentLabel || family.parent_family_key}</div>
        )}
      </div>

      {members.length === 0 && (
        <div className="org-empty">Sem membros nesta família ainda.</div>
      )}

      {admins.length > 0 && (
        <>
          <div className="org-trunk" />
          <div className="org-tier">
            <div className="org-tier-label mono">ADMINISTRADORES · {admins.length}</div>
            <div className="org-row">
              {admins.map(a => <FamilyOrgCard key={a.assignee_id} m={a} tier="admin" />)}
            </div>
          </div>
        </>
      )}

      {agents.length > 0 && (
        <>
          <div className="org-trunk" />
          <div className="org-tier">
            <div className="org-tier-label mono">AGENTES · {agents.length}</div>
            <div className="org-row org-row-agents">
              {agents.map(a => <FamilyOrgCard key={a.assignee_id} m={a} tier="agent" />)}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

function PageFamColReal() {
  const [families, setFamilies] = useIS([]);
  const [selected, setSelected] = useIS('');
  const [members, setMembers] = useIS([]);
  const [loading, setLoading] = useIS(false);
  const [loadingInit, setLoadingInit] = useIS(true);
  const [error, setError] = useIS(null);
  const [addOpen, setAddOpen] = useIS(false);
  const [directory, setDirectory] = useIS([]);
  const [view, setView] = useIS('org');
  const [orphans, setOrphans] = useIS([]);

  useIE(() => {
    window.CCApi.admin.listFamilies()
      .then(r => { setFamilies(r.items ?? []); setLoadingInit(false); })
      .catch(e => { setError(e.message); setLoadingInit(false); });
    window.CCApi.admin.listUsers()
      .then(r => setDirectory(r.items ?? []))
      .catch(() => {});
    window.CCApi.admin.listOrphanAssignees()
      .then(r => setOrphans(r.items ?? []))
      .catch(() => {});
  }, []);

  useIE(() => {
    if (selected || families.length === 0) return;
    const sorted = families
      .filter(f => (f.kind === 'main' || f.kind === 'special') && f.family_key !== 'familias_all')
      .sort((a, b) => (a.family_label || '').localeCompare(b.family_label || ''));
    const def = sorted[0] || families[0];
    if (def) setSelected(def.family_key);
  }, [families, selected]);

  const dirByAssignee = useIM(() => {
    const m = new Map();
    for (const u of directory) {
      if (u.assignee_id != null) m.set(String(u.assignee_id), u);
    }
    return m;
  }, [directory]);

  const enrichedMembers = useIM(() => members.map(m => {
    const d = dirByAssignee.get(String(m.assignee_id));
    return {
      ...m,
      ticket_role: d?.ticket_role ?? null,
      ticket_level: d?.ticket_level ?? null,
    };
  }), [members, dirByAssignee]);

  const selectedFamily = useIM(() => families.find(f => f.family_key === selected) || null, [families, selected]);
  const parentLabel = useIM(() => {
    if (!selectedFamily?.parent_family_key) return null;
    return families.find(f => f.family_key === selectedFamily.parent_family_key)?.family_label
        || selectedFamily.parent_family_key;
  }, [selectedFamily, families]);

  const loadMembers = useIC(async (key) => {
    if (!key) return;
    setLoading(true); setError(null);
    try {
      const r = await window.CCApi.admin.listFamilyMembers(key);
      setMembers(r.items ?? []);
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, []);

  useIE(() => { if (selected) loadMembers(selected); }, [selected, loadMembers]);
  // Trocar de família = fechar o form "+ Adicionar" e limpar erro pendente.
  useIE(() => { setAddOpen(false); setError(null); }, [selected]);

  const [addSearch, setAddSearch] = useIS('');

  const addCandidates = useIM(() => {
    if (!addOpen || !selected) return [];
    const currentIds = new Set(members.map(m => m.assignee_id));
    const candidates = directory.filter(u => !currentIds.has(Number(u.assignee_id)) && u.assignee_id);
    const q = addSearch.trim().toLowerCase();
    if (!q) return candidates.slice(0, 50);
    return candidates.filter(u =>
      (u.display_name || '').toLowerCase().includes(q) ||
      (u.email || '').toLowerCase().includes(q) ||
      String(u.assignee_id).includes(q)
    ).slice(0, 50);
  }, [addOpen, selected, members, directory, addSearch]);

  async function addMemberFromPick(user) {
    const assigneeId = Number(user.assignee_id);
    const name = user.display_name || '';
    const email = user.email || '';
    const familyLabel = families.find(f => f.family_key === selected)?.family_label || selected;
    const motivo = await window.CCMotivo({
      title: `Adicionar à família ${familyLabel}`,
      message: `Vincular ${name || '#' + assigneeId} à família "${familyLabel}".`,
      hint: 'Será registrado como ticket.',
      confirmLabel: 'Vincular',
      tone: 'primary',
    });
    if (!motivo) return;
    try {
      await window.CCApi.admin.addFamilyMember(selected, {
        assignee_id: assigneeId,
        assignee_name: name || undefined,
        assignee_email: email || undefined,
        motivo,
      });
      window.CCApi.support.createTicket({
        title: `${name || '#' + assigneeId} adicionado à família ${familyLabel}`,
        description: motivo,
        type: 'outro',
        priority: 'low',
      }).catch(() => {});
      setAddOpen(false);
      setAddSearch('');
      loadMembers(selected);
    } catch (e) { setError(e.detail?.message || e.detail?.error || e.message); }
  }

  async function removeMember(assigneeId) {
    const target = members.find(m => m.assignee_id === assigneeId);
    const who = target?.assignee_name || `assignee #${assigneeId}`;
    const motivo = await window.CCMotivo({
      title: 'Remover colaborador da família',
      message: `Remover ${who} da família ${selected}.`,
      hint: 'Será registrado para auditoria.',
      confirmLabel: 'Remover',
      cancelLabel: 'Cancelar',
      tone: 'danger',
    });
    if (!motivo) return;
    try {
      await window.CCApi.admin.removeFamilyMember(selected, assigneeId, { motivo });
      loadMembers(selected);
    } catch (e) { setError(e.detail?.message || e.detail?.error || e.message); }
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · FAMÍLIAS" title="Colaboradores × Família"
          sub="Lista, adiciona e remove vínculos. Toda mudança aqui afeta roteamento e dashboards." />

      {loadingInit && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loadingInit && <React.Fragment>
      <OrphanAssigneesPanel orphans={orphans} families={families}
        onLink={async (familyKey, assigneeId, name, email) => {
          const label = families.find(f => f.family_key === familyKey)?.family_label || familyKey;
          await window.CCApi.admin.addFamilyMember(familyKey, { assignee_id: assigneeId, assignee_name: name, assignee_email: email });
        }}
        onAfterLink={() => {
          window.CCApi.admin.listOrphanAssignees().then(r => setOrphans(r.items ?? [])).catch(() => {});
        }}
      />

      <div className="page-sidebar-grid" style={{ display: 'grid', gridTemplateColumns: '240px minmax(0, 1fr)', gap: 14 }}>
        <div className="card card-topline" style={{ padding: 14 }}>
          {(() => {
            const labelByKey = Object.fromEntries(families.map(f => [f.family_key, f.family_label]));
            // Ouvidoria é tipo "special" no backend mas operacionalmente é uma família principal
            // — então a gente agrupa visualmente junto com as mains.
            const mains = families.filter(f =>
              f.kind === 'main' || (f.kind === 'special' && f.family_key !== 'familias_all')
            ).sort((a, b) => (a.family_label || '').localeCompare(b.family_label || ''));
            const groups = [
              { title: 'PRINCIPAIS',   items: mains, accent: 'var(--green)' },
              { title: 'SUB-FAMÍLIAS', items: families.filter(f => f.kind === 'subfamily'), accent: 'rgba(245,158,11,0.85)' },
              { title: 'TODAS',        items: families.filter(f => f.family_key === 'familias_all'), accent: 'rgba(59,130,246,0.85)' },
              { title: 'SEM CATÁLOGO', items: families.filter(f => !f.kind), accent: 'rgba(255,255,255,0.5)' },
            ];
            return groups.map((g, gi) => g.items.length === 0 ? null : (
              <div key={g.title} style={{ marginBottom: 10 }}>
                <div className="mono" style={{
                  fontSize: 9.5, color: g.accent, letterSpacing: '0.28em',
                  padding: '0 4px 6px',
                  borderTop: gi === 0 ? 'none' : '1px dashed rgba(255,255,255,0.06)',
                  marginTop: gi === 0 ? 0 : 10,
                  paddingTop: gi === 0 ? 0 : 10,
                  display: 'flex', alignItems: 'center', gap: 6,
                }}>
                  <span>/ {g.title}</span>
                  <span style={{ flex: 1 }} />
                  <span style={{ color: 'rgba(255,255,255,0.35)', letterSpacing: '0.18em' }}>{g.items.length}</span>
                </div>
                {g.items.map(f => {
                  const isSelected = selected === f.family_key;
                  return (
                    <button key={f.family_key} type="button" onClick={() => setSelected(f.family_key)}
                      style={{
                        display: 'flex', alignItems: 'center', gap: 10,
                        width: '100%', padding: '7px 10px', borderRadius: 6,
                        background: isSelected ? 'rgba(34,197,94,0.10)' : 'transparent',
                        border: '1px solid ' + (isSelected ? 'rgba(34,197,94,0.22)' : 'transparent'),
                        textAlign: 'left', color: 'inherit', cursor: 'pointer', marginBottom: 2,
                      }}>
                      <span style={{ flex: 1, minWidth: 0 }}>
                        <span style={{ fontSize: 12.5, display: 'block' }}>{f.family_label}</span>
                        {f.kind === 'subfamily' && f.parent_family_key && (
                          <span style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', display: 'block', marginTop: 1 }}>
                            ↳ de {labelByKey[f.parent_family_key] || f.parent_family_key}
                          </span>
                        )}
                      </span>
                      <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>{f.count}</span>
                    </button>
                  );
                })}
              </div>
            ));
          })()}
        </div>

        <div className="card" style={{ padding: 16 }}>
          {!selected
            ? <div style={{ color: 'rgba(255,255,255,0.5)', fontSize: 13 }}>Selecione uma família à esquerda.</div>
            : (
              <>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
                  <span className="mono" style={{ fontSize: 10, color: 'var(--green)', letterSpacing: '0.32em' }}>/ {selected.toUpperCase()}</span>
                  <span style={{ flex: 1 }} />
                  <div className="tabs-seg">
                    <button type="button" className={view === 'lista' ? 'is-active' : ''} onClick={() => setView('lista')}>Lista</button>
                    <button type="button" className={view === 'org' ? 'is-active' : ''} onClick={() => setView('org')}>Organograma</button>
                  </div>
                  <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>{members.length} MEMBROS</span>
                  {view === 'lista' && (
                    <button type="button" onClick={() => setAddOpen(o => !o)} style={btnGhost()}>{addOpen ? '× Cancelar' : '+ Adicionar'}</button>
                  )}
                </div>

                {view === 'lista' && addOpen && (
                  <div style={{ marginBottom: 12, padding: 10, background: 'rgba(255,255,255,0.03)', borderRadius: 8, border: 'none' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
                      <Icon name="search" size={12} />
                      <input value={addSearch} onChange={e => setAddSearch(e.target.value)}
                        placeholder="Buscar por nome, email ou ID..."
                        style={{ ...inp, flex: 1, fontSize: 12 }} autoFocus autoComplete="off" />
                      <span className="mono" style={{ fontSize: 9.5, color: 'rgba(255,255,255,0.35)' }}>{addCandidates.length}</span>
                    </div>
                    <div style={{ maxHeight: 200, overflowY: 'auto' }}>
                      {addCandidates.length === 0 && (
                        <div style={{ padding: 12, color: 'rgba(255,255,255,0.4)', fontSize: 12, textAlign: 'center' }}>Nenhum colaborador encontrado</div>
                      )}
                      {addCandidates.map(u => (
                        <button key={u.user_id || u.assignee_id} type="button" onClick={() => addMemberFromPick(u)}
                          style={{
                            display: 'grid', gridTemplateColumns: '42px minmax(0,1fr) minmax(0,1fr)', gap: 8, alignItems: 'center',
                            width: '100%', padding: '6px 6px', borderRadius: 5, textAlign: 'left',
                            background: 'transparent', border: '1px solid transparent', color: 'inherit', cursor: 'pointer',
                          }}
                          onMouseEnter={e => { e.currentTarget.style.background = 'rgba(34,197,94,0.06)'; e.currentTarget.style.borderColor = 'rgba(34,197,94,0.15)'; }}
                          onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.borderColor = 'transparent'; }}>
                          <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>#{u.assignee_id}</span>
                          <span style={{ fontSize: 12, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u.display_name || '—'}</span>
                          <span style={{ fontSize: 11, color: 'rgba(255,255,255,0.45)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u.email || ''}</span>
                        </button>
                      ))}
                    </div>
                  </div>
                )}

                {loading && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

                {!loading && view === 'lista' && members.map(m => (
                  <div key={m.assignee_id} style={{
                    display: 'grid', gridTemplateColumns: '50px minmax(0,1fr) minmax(0,1fr) auto', gap: 8, alignItems: 'center',
                    padding: '8px 4px', borderTop: '1px solid rgba(255,255,255,0.03)',
                  }}>
                    <span className="mono" style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)' }}>#{m.assignee_id}</span>
                    <span style={{ fontSize: 12.5 }}>{m.assignee_name}</span>
                    <span style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.55)' }}>{m.assignee_email}</span>
                    <button type="button" onClick={() => removeMember(m.assignee_id)} style={btnDanger()}>Remover</button>
                  </div>
                ))}

                {!loading && view === 'org' && (
                  <FamilyOrgChart family={selectedFamily} parentLabel={parentLabel} members={enrichedMembers} />
                )}
              </>
            )}
        </div>
      </div>
      </React.Fragment>}

      {error && <div className="chip is-warn" style={{ marginTop: 12 }}>{error}</div>}
    </div>
  );
}

// =============================================================
// gs-fam-time — Time × Família
// =============================================================
function TeamOrgCard({ team, teamId, onRemove }) {
  const name = team?.name || `Time #${teamId}`;
  return (
    <div className="org-card org-card-team">
      <button type="button" className="org-card-remove" onClick={onRemove} title="Desvincular time">×</button>
      <div className="org-card-head">
        <div className="org-card-avatar org-card-avatar-team">
          <Icon name="users" size={13} />
        </div>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div className="org-card-name" title={name}>{name}</div>
          {team?.description && (
            <div className="org-card-email" title={team.description}>{team.description}</div>
          )}
        </div>
      </div>
      <div className="org-card-foot">
        <span className="mono org-pill">#{teamId}</span>
        {team?.allow_auto_assign && <span className="mono org-pill org-pill-auto">AUTO</span>}
      </div>
    </div>
  );
}

function TeamFamilyOrgChart({ family, parentLabel, teams, teamIds, onRemove }) {
  const teamMap = useIM(() => new Map(teams.map(t => [t.id, t])), [teams]);
  return (
    <div className="org-chart">
      <div className="org-node-root">
        <div className="org-root-tag mono">/ FAMÍLIA</div>
        <div className="org-root-title">{family?.family_label || family?.family_key || '—'}</div>
        <div className="org-root-meta mono">
          <span>{teamIds.length} {teamIds.length === 1 ? 'time' : 'times'}</span>
          {typeof family?.count === 'number' && (
            <>
              <span className="org-dot">·</span>
              <span>{family.count} colaboradores</span>
            </>
          )}
        </div>
        {family?.parent_family_key && (
          <div className="org-root-parent mono">↳ subfamília de {parentLabel || family.parent_family_key}</div>
        )}
      </div>

      {teamIds.length === 0 ? (
        <div className="org-empty">Nenhum time vinculado a esta família.</div>
      ) : (
        <>
          <div className="org-trunk" />
          <div className="org-tier">
            <div className="org-tier-label mono">TIMES · {teamIds.length}</div>
            <div className="org-row">
              {teamIds.map(tid => (
                <TeamOrgCard key={tid} team={teamMap.get(tid)} teamId={tid} onRemove={() => onRemove(tid)} />
              ))}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

function OrphanAssigneesPanel({ orphans, families, onLink, onAfterLink }) {
  const [open, setOpen] = useIS(false);
  const [sel, setSel] = useIS({});
  const [busyId, setBusyId] = useIS(null);
  const [err, setErr] = useIS(null);

  const mainFamilies = useIM(() =>
    families
      .filter(f => (f.kind === 'main' || f.kind === 'special') && f.family_key !== 'familias_all')
      .sort((a, b) => (a.family_label || '').localeCompare(b.family_label || '')),
    [families]
  );

  const inp = { font: 'inherit', fontSize: 12, padding: '7px 8px', background: 'rgba(255,255,255,0.04)', border: 'none', borderRadius: 7, color: 'var(--text)', outline: 'none', cursor: 'pointer' };

  useIE(() => {
    if (!open) return;
    function onKey(e) { if (e.key === 'Escape') setOpen(false); }
    document.addEventListener('keydown', onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = prev; };
  }, [open]);

  async function link(assigneeId) {
    const famKey = sel[assigneeId];
    if (!famKey) return;
    const o = orphans.find(x => x.assignee_id === assigneeId);
    if (!o) return;
    setBusyId(assigneeId); setErr(null);
    try {
      await onLink(famKey, assigneeId, o.assignee_name, o.assignee_email);
      setSel(prev => { const n = { ...prev }; delete n[assigneeId]; return n; });
      onAfterLink?.();
    } catch (e) {
      setErr(e?.detail?.error || e?.message || 'Falha ao vincular');
    } finally {
      setBusyId(null);
    }
  }

  if (orphans.length === 0) {
    return (
      <div className="orphan-zone">
        <div className="orphan-bar orphan-bar-ok">
          <span className="orphan-bar-ic"><Icon name="shield" size={12} /></span>
          <span>Todos os colaboradores estão vinculados a uma família.</span>
        </div>
      </div>
    );
  }

  return (
    <div className="orphan-zone">
      <button type="button" className="orphan-bar" onClick={() => setOpen(true)}>
        <span className="orphan-bar-ic"><Icon name="bell" size={13} /></span>
        <span className="orphan-bar-text">
          <strong>{orphans.length}</strong> {orphans.length === 1 ? 'colaborador sem família' : 'colaboradores sem família'}
        </span>
        <span className="orphan-bar-hint">abrir lista</span>
        <span className="orphan-bar-arrow">↗</span>
      </button>

      {open && (
        <div className="orphan-modal-backdrop" onClick={() => setOpen(false)}>
          <div className="orphan-modal" onClick={(e) => e.stopPropagation()}>
            <div className="orphan-modal-head">
              <span className="orphan-bar-ic"><Icon name="bell" size={14} /></span>
              <div className="orphan-modal-title">
                <strong>{orphans.length}</strong> {orphans.length === 1 ? 'colaborador sem família' : 'colaboradores sem família'}
              </div>
              <button type="button" className="orphan-modal-close" onClick={() => setOpen(false)} aria-label="Fechar">×</button>
            </div>
            <div className="orphan-modal-body">
              <div className="orphan-help mono">/ VINCULE CADA COLABORADOR A UMA FAMÍLIA PRINCIPAL</div>
              {err && <div className="chip is-warn" style={{ marginBottom: 8 }}>{err}</div>}
              {orphans.map(o => {
                const isBusy = busyId === o.assignee_id;
                const canLink = Boolean(sel[o.assignee_id]);
                return (
                  <div key={o.assignee_id} className="orphan-row">
                    <div className="orphan-ic"><Icon name="userPlus" size={13} /></div>
                    <div style={{ minWidth: 0 }}>
                      <div className="orphan-name" title={o.assignee_name}>{o.assignee_name || '—'}</div>
                      <div className="orphan-sub mono">
                        <span>#{o.assignee_id}</span>
                        {o.assignee_email && <><span className="orphan-dot">·</span><span>{o.assignee_email}</span></>}
                      </div>
                    </div>
                    <select value={sel[o.assignee_id] || ''} onChange={e => setSel(s => ({ ...s, [o.assignee_id]: e.target.value }))}
                      style={inp} className="orphan-sel">
                      <option value="">Família…</option>
                      {mainFamilies.map(f => <option key={f.family_key} value={f.family_key}>{f.family_label}</option>)}
                    </select>
                    <span />
                    <button type="button" disabled={!canLink || isBusy} onClick={() => link(o.assignee_id)}
                      style={{
                        padding: '7px 14px', borderRadius: 7, fontSize: 12, fontWeight: 500, cursor: canLink && !isBusy ? 'pointer' : 'not-allowed',
                        background: canLink ? 'linear-gradient(135deg, var(--green), var(--green-dark))' : 'rgba(255,255,255,0.04)',
                        border: canLink ? 'none' : '1px solid rgba(255,255,255,0.08)',
                        color: canLink ? '#06170d' : 'rgba(255,255,255,0.35)',
                        opacity: isBusy ? 0.6 : 1,
                      }}>
                      {isBusy ? '…' : 'Vincular'}
                    </button>
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function OrphanTeamsPanel({ orphans, families, onLink, onAfterLink }) {
  const [open, setOpen] = useIS(false);
  const [sel, setSel] = useIS({});
  const [busyId, setBusyId] = useIS(null);
  const [err, setErr] = useIS(null);

  const mainFamilies = useIM(() =>
    families
      .filter(f => (f.kind === 'main' || f.kind === 'special') && f.family_key !== 'familias_all')
      .sort((a, b) => (a.family_label || '').localeCompare(b.family_label || '')),
    [families]
  );
  const subByParent = useIM(() => {
    const m = new Map();
    for (const f of families) {
      if (f.kind === 'subfamily' && f.parent_family_key) {
        if (!m.has(f.parent_family_key)) m.set(f.parent_family_key, []);
        m.get(f.parent_family_key).push(f);
      }
    }
    return m;
  }, [families]);

  useIE(() => {
    if (!open) return;
    function onKey(e) { if (e.key === 'Escape') setOpen(false); }
    document.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [open]);

  function patch(teamId, p) {
    setSel(s => ({ ...s, [teamId]: { ...(s[teamId] || {}), ...p } }));
  }

  async function link(teamId) {
    const s = sel[teamId] || {};
    const targets = [s.main, s.sub].filter(Boolean);
    if (!targets.length) return;
    setBusyId(teamId); setErr(null);
    try {
      for (const fam of targets) await onLink(fam, teamId);
      setSel(prev => { const n = { ...prev }; delete n[teamId]; return n; });
      onAfterLink?.();
    } catch (e) {
      setErr(e?.detail?.error || e?.message || 'Falha ao vincular');
    } finally {
      setBusyId(null);
    }
  }

  if (orphans.length === 0) {
    return (
      <div className="orphan-zone">
        <div className="orphan-bar orphan-bar-ok">
          <span className="orphan-bar-ic"><Icon name="shield" size={12} /></span>
          <span>Todos os times estão vinculados a pelo menos uma família.</span>
        </div>
      </div>
    );
  }

  return (
    <div className="orphan-zone">
      <button type="button" className="orphan-bar" onClick={() => setOpen(true)}>
        <span className="orphan-bar-ic"><Icon name="bell" size={13} /></span>
        <span className="orphan-bar-text">
          <strong>{orphans.length}</strong> {orphans.length === 1 ? 'time sem família' : 'times sem família'}
        </span>
        <span className="orphan-bar-hint">abrir lista</span>
        <span className="orphan-bar-arrow">↗</span>
      </button>

      {open && (
        <div className="orphan-modal-backdrop" onClick={() => setOpen(false)}>
          <div className="orphan-modal" onClick={(e) => e.stopPropagation()}>
            <div className="orphan-modal-head">
              <span className="orphan-bar-ic"><Icon name="bell" size={14} /></span>
              <div className="orphan-modal-title">
                <strong>{orphans.length}</strong> {orphans.length === 1 ? 'time sem família' : 'times sem família'}
              </div>
              <button type="button" className="orphan-modal-close" onClick={() => setOpen(false)} aria-label="Fechar">×</button>
            </div>
            <div className="orphan-modal-body">
              <div className="orphan-help mono">
                / VINCULE CADA TIME A UMA FAMÍLIA · OPCIONALMENTE TAMBÉM A UMA SUB-FAMÍLIA
              </div>
              {err && <div className="chip is-warn" style={{ marginBottom: 8 }}>{err}</div>}
              {orphans.map(t => {
                const s = sel[t.id] || {};
                const subs = s.main ? (subByParent.get(s.main) || []) : [];
                const canLink = Boolean(s.main || s.sub);
                const isBusy = busyId === t.id;
                return (
                  <div key={t.id} className="orphan-row">
                    <div className="orphan-ic"><Icon name="users" size={13} /></div>
                    <div style={{ minWidth: 0 }}>
                      <div className="orphan-name" title={t.name}>{t.name || `Time #${t.id}`}</div>
                      <div className="orphan-sub mono">
                        <span>#{t.id}</span>
                        {t.description && <><span className="orphan-dot">·</span><span title={t.description}>{t.description}</span></>}
                        {t.allow_auto_assign && <><span className="orphan-dot">·</span><span style={{ color: '#d8b4fe' }}>AUTO</span></>}
                      </div>
                    </div>
                    <select value={s.main || ''} onChange={(e) => patch(t.id, { main: e.target.value, sub: '' })} style={inp} className="orphan-sel">
                      <option value="">Família…</option>
                      {mainFamilies.map(f => <option key={f.family_key} value={f.family_key}>{f.family_label}</option>)}
                    </select>
                    <select value={s.sub || ''} onChange={(e) => patch(t.id, { sub: e.target.value })}
                            disabled={!s.main || subs.length === 0}
                            style={{ ...inp, opacity: (!s.main || subs.length === 0) ? 0.45 : 1 }} className="orphan-sel">
                      <option value="">
                        {!s.main ? 'escolha família primeiro' : (subs.length ? 'Sub-família (opcional)' : 'sem sub-famílias')}
                      </option>
                      {subs.map(f => <option key={f.family_key} value={f.family_key}>{f.family_label}</option>)}
                    </select>
                    <button type="button" disabled={!canLink || isBusy} onClick={() => link(t.id)} style={btnPrimary(isBusy)}>
                      {isBusy ? '…' : 'Vincular'}
                    </button>
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function PageFamTimeReal() {
  const [items, setItems] = useIS([]);
  const [teams, setTeams] = useIS([]);
  const [families, setFamilies] = useIS([]);
  const [selected, setSelected] = useIS('');
  const [view, setView] = useIS('org');
  const [error, setError] = useIS(null);
  const [busy, setBusy] = useIS(false);
  const [addOpen, setAddOpen] = useIS(false);
  const [loadingInit, setLoadingInit] = useIS(true);

  async function load() {
    try {
      const [tf, t, f] = await Promise.all([
        window.CCApi.admin.listTeamFamilies(),
        window.CCApi.admin.listTeams().catch(() => ({ items: [] })),
        window.CCApi.admin.listFamilies(),
      ]);
      setItems(tf.items ?? []);
      setTeams(t.items ?? []);
      setFamilies(f.items ?? []);
    } catch (e) { setError(e.message); }
    setLoadingInit(false);
  }
  useIE(() => { load(); }, []);

  useIE(() => {
    if (selected || families.length === 0) return;
    const sorted = families
      .filter(f => (f.kind === 'main' || f.kind === 'special') && f.family_key !== 'familias_all')
      .sort((a, b) => (a.family_label || '').localeCompare(b.family_label || ''));
    const def = sorted[0] || families[0];
    if (def) setSelected(def.family_key);
  }, [families, selected]);

  const teamsByFamily = useIM(() => {
    const m = new Map();
    for (const i of items) {
      if (!m.has(i.family_key)) m.set(i.family_key, []);
      m.get(i.family_key).push(Number(i.team_id));
    }
    return m;
  }, [items]);

  const selectedFamily = useIM(() => families.find(f => f.family_key === selected) || null, [families, selected]);
  const parentLabel = useIM(() => {
    if (!selectedFamily?.parent_family_key) return null;
    return families.find(f => f.family_key === selectedFamily.parent_family_key)?.family_label
        || selectedFamily.parent_family_key;
  }, [selectedFamily, families]);

  const teamIdsForSelected = useIM(() => teamsByFamily.get(selected) || [], [teamsByFamily, selected]);
  const teamMap = useIM(() => new Map(teams.map(t => [t.id, t])), [teams]);
  const availableTeams = useIM(() => {
    const set = new Set(teamIdsForSelected);
    return teams.filter(t => !set.has(t.id)).sort((a, b) => (a.name || '').localeCompare(b.name || ''));
  }, [teams, teamIdsForSelected]);

  const linkedTeamIds = useIM(() => {
    const s = new Set();
    for (const i of items) s.add(Number(i.team_id));
    return s;
  }, [items]);
  const orphanTeams = useIM(() =>
    teams
      .filter(t => !linkedTeamIds.has(t.id))
      .sort((a, b) => (a.name || '').localeCompare(b.name || '')),
    [teams, linkedTeamIds]
  );

  const [pickedTeamId, setPickedTeamId] = useIS(null);
  // Trocar de família = cancelar qualquer "+ Vincular time" em aberto e limpar o pick.
  useIE(() => { setPickedTeamId(null); setAddOpen(false); setError(null); }, [selected]);
  // Limpar pickedTeamId também quando o usuário cancela manualmente (fecha o form).
  useIE(() => { if (!addOpen) setPickedTeamId(null); }, [addOpen]);

  async function addTeam(e) {
    e.preventDefault();
    if (!selected) return;
    const teamId = Number(pickedTeamId || 0);
    if (!Number.isFinite(teamId) || teamId <= 0) { setError('Selecione um time válido.'); return; }
    setBusy(true); setError(null);
    try {
      await window.CCApi.admin.addTeamFamily(selected, teamId);
      setPickedTeamId(null);
      setAddOpen(false);
      load();
    } catch (e) { setError(e.detail?.error || e.message); }
    finally { setBusy(false); }
  }

  async function removeTeam(teamId) {
    const t = teamMap.get(teamId);
    const ok = await window.CCConfirm({
      title: 'Desvincular time',
      message: `Desvincular "${t?.name || `team #${teamId}`}" da família ${selected}?`,
      confirmLabel: 'Desvincular',
      cancelLabel: 'Cancelar',
      tone: 'danger',
    });
    if (!ok) return;
    try { await window.CCApi.admin.removeTeamFamily(selected, teamId); load(); }
    catch (e) { setError(e.detail?.error || e.message); }
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · FAMÍLIAS" title="Times × Família"
          sub="Define quais times do Chatwoot atendem cada família. Usado pelo roteamento de conversas." />

      {loadingInit && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loadingInit && <React.Fragment>
      <OrphanTeamsPanel
        orphans={orphanTeams}
        families={families}
        onLink={(familyKey, teamId) => window.CCApi.admin.addTeamFamily(familyKey, teamId)}
        onAfterLink={load}
      />

      <div className="page-sidebar-grid" style={{ display: 'grid', gridTemplateColumns: '240px minmax(0, 1fr)', gap: 14 }}>
        <div className="card card-topline" style={{ padding: 14 }}>
          {(() => {
            const labelByKey = Object.fromEntries(families.map(f => [f.family_key, f.family_label]));
            const mains = families.filter(f =>
              f.kind === 'main' || (f.kind === 'special' && f.family_key !== 'familias_all')
            ).sort((a, b) => (a.family_label || '').localeCompare(b.family_label || ''));
            const groups = [
              { title: 'PRINCIPAIS',   items: mains, accent: 'var(--green)' },
              { title: 'SUB-FAMÍLIAS', items: families.filter(f => f.kind === 'subfamily'), accent: 'rgba(245,158,11,0.85)' },
              { title: 'TODAS',        items: families.filter(f => f.family_key === 'familias_all'), accent: 'rgba(59,130,246,0.85)' },
              { title: 'SEM CATÁLOGO', items: families.filter(f => !f.kind), accent: 'rgba(255,255,255,0.5)' },
            ];
            return groups.map((g, gi) => g.items.length === 0 ? null : (
              <div key={g.title} style={{ marginBottom: 10 }}>
                <div className="mono" style={{
                  fontSize: 9.5, color: g.accent, letterSpacing: '0.28em',
                  padding: '0 4px 6px',
                  borderTop: gi === 0 ? 'none' : '1px dashed rgba(255,255,255,0.06)',
                  marginTop: gi === 0 ? 0 : 10,
                  paddingTop: gi === 0 ? 0 : 10,
                  display: 'flex', alignItems: 'center', gap: 6,
                }}>
                  <span>/ {g.title}</span>
                  <span style={{ flex: 1 }} />
                  <span style={{ color: 'rgba(255,255,255,0.35)', letterSpacing: '0.18em' }}>{g.items.length}</span>
                </div>
                {g.items.map(f => {
                  const isSelected = selected === f.family_key;
                  const tCount = (teamsByFamily.get(f.family_key) || []).length;
                  return (
                    <button key={f.family_key} type="button" onClick={() => setSelected(f.family_key)}
                      style={{
                        display: 'flex', alignItems: 'center', gap: 10,
                        width: '100%', padding: '7px 10px', borderRadius: 6,
                        background: isSelected ? 'rgba(34,197,94,0.10)' : 'transparent',
                        border: '1px solid ' + (isSelected ? 'rgba(34,197,94,0.22)' : 'transparent'),
                        textAlign: 'left', color: 'inherit', cursor: 'pointer', marginBottom: 2,
                      }}>
                      <span style={{ flex: 1, minWidth: 0 }}>
                        <span style={{ fontSize: 12.5, display: 'block' }}>{f.family_label}</span>
                        {f.kind === 'subfamily' && f.parent_family_key && (
                          <span style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', display: 'block', marginTop: 1 }}>
                            ↳ de {labelByKey[f.parent_family_key] || f.parent_family_key}
                          </span>
                        )}
                      </span>
                      <span className="mono" style={{ fontSize: 10, color: tCount > 0 ? 'var(--green)' : 'rgba(255,255,255,0.35)' }}>{tCount}</span>
                    </button>
                  );
                })}
              </div>
            ));
          })()}
        </div>

        <div className="card" style={{ padding: 16 }}>
          {!selected
            ? <div style={{ color: 'rgba(255,255,255,0.5)', fontSize: 13 }}>Selecione uma família à esquerda.</div>
            : (
              <>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
                  <span className="mono" style={{ fontSize: 10, color: 'var(--green)', letterSpacing: '0.32em' }}>/ {selected.toUpperCase()}</span>
                  <span style={{ flex: 1 }} />
                  <div className="tabs-seg">
                    <button type="button" className={view === 'org' ? 'is-active' : ''} onClick={() => setView('org')}>Organograma</button>
                    <button type="button" className={view === 'lista' ? 'is-active' : ''} onClick={() => setView('lista')}>Lista</button>
                  </div>
                  <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>{teamIdsForSelected.length} TIMES</span>
                  <button type="button" onClick={() => setAddOpen(o => !o)} style={btnGhost()}>{addOpen ? '× Cancelar' : '+ Vincular time'}</button>
                </div>

                {addOpen && (
                  <form onSubmit={addTeam} style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 8, marginBottom: 12, padding: 10, background: 'rgba(255,255,255,0.03)', borderRadius: 8 }}>
                    <CCSinglePicker
                      placeholder={availableTeams.length ? 'Buscar time pelo nome ou ID…' : 'Todos os times já vinculados'}
                      searchPlaceholder="Digite parte do nome ou o ID…"
                      emptyLabel="Todos os times já estão vinculados a esta família."
                      options={availableTeams.map(t => ({ key: t.id, label: t.name || `Time #${t.id}`, sub: `#${t.id}` }))}
                      value={pickedTeamId}
                      onChange={setPickedTeamId}
                    />
                    <button type="submit" disabled={busy || !pickedTeamId} style={btnPrimary(busy || !pickedTeamId)}>{busy ? '…' : 'Vincular'}</button>
                  </form>
                )}

                {view === 'org' && (
                  <TeamFamilyOrgChart
                    family={selectedFamily}
                    parentLabel={parentLabel}
                    teams={teams}
                    teamIds={teamIdsForSelected}
                    onRemove={removeTeam}
                  />
                )}

                {view === 'lista' && (
                  <div className="fam-team-list">
                    {teamIdsForSelected.length === 0 ? (
                      <div className="org-empty" style={{ marginTop: 4 }}>Nenhum time vinculado.</div>
                    ) : teamIdsForSelected.map(tid => {
                      const t = teamMap.get(tid);
                      return (
                        <div key={tid} className="fam-team-row">
                          <div className="fam-team-ic"><Icon name="users" size={13} /></div>
                          <div style={{ minWidth: 0 }}>
                            <div className="fam-team-name" title={t?.name}>{t?.name || `Time #${tid}`}</div>
                            {t?.description && <div className="fam-team-desc" title={t.description}>{t.description}</div>}
                          </div>
                          <span className="mono fam-team-id">#{tid}</span>
                          {t?.allow_auto_assign && <span className="mono org-pill org-pill-auto">AUTO</span>}
                          <button type="button" onClick={() => removeTeam(tid)} style={btnDanger()}>Remover</button>
                        </div>
                      );
                    })}
                  </div>
                )}
              </>
            )}
        </div>
      </div>
      </React.Fragment>}

      {error && <div className="chip is-warn" style={{ marginTop: 12 }}>{error}</div>}
    </div>
  );
}

// =============================================================
// gs-fam-forn — Família × Fornecedora (read-only)
// Hierarquia: 1 família atende N fornecedoras de energia.
// =============================================================
function FamilyFornecedoraCard({ row }) {
  return (
    <div className="org-card org-card-fam">
      <div className="org-card-head">
        <div className="org-card-avatar org-card-avatar-fam"
             style={{ background: 'rgba(168,85,247,0.10)', borderColor: 'rgba(168,85,247,0.30)', color: '#d8b4fe' }}>
          <Icon name="plug" size={13} />
        </div>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div className="org-card-name" title={row.fornecedora_label}>{row.fornecedora_label || row.fornecedora_key}</div>
          <div className="org-card-email mono">{row.fornecedora_key}</div>
        </div>
      </div>
      <div className="org-card-foot">
        <span className="mono org-pill" style={{ color: '#d8b4fe', borderColor: 'rgba(168,85,247,0.32)' }}>FORNECEDORA</span>
      </div>
    </div>
  );
}

function FamilyFornecedoraOrgChart({ family, rows }) {
  const isOuvidoria = family?.key === 'ouvidoria';
  const isAll = family?.key === 'familias_all';
  return (
    <div className="org-chart">
      <div className="org-node-root org-node-fornecedora">
        <div className="org-root-tag mono">
          / {isAll ? 'TODAS AS FAMÍLIAS' : isOuvidoria ? 'OUVIDORIA' : 'FAMÍLIA'}
        </div>
        <div className="org-root-title">{family?.label || family?.key || '—'}</div>
        <div className="org-root-meta mono">
          <span>{family?.key}</span>
          <span className="org-dot">·</span>
          <span>{rows.length} {rows.length === 1 ? 'fornecedora' : 'fornecedoras'}</span>
        </div>
      </div>

      {rows.length === 0 ? (
        <div className="org-empty">Sem fornecedoras mapeadas.</div>
      ) : (
        <>
          <div className="org-trunk org-trunk-fornecedora" />
          <div className="org-tier">
            <div className="org-tier-label mono">FORNECEDORAS · {rows.length}</div>
            <div className="org-row">
              {rows.map(r => <FamilyFornecedoraCard key={`${r.family_key}::${r.fornecedora_key}`} row={r} />)}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

function PageFamFornReal() {
  const [items, setItems] = useIS([]);
  const [error, setError] = useIS(null);
  const [selected, setSelected] = useIS('');
  const [view, setView] = useIS('org');
  const [loadingInit, setLoadingInit] = useIS(true);

  useIE(() => {
    window.CCApi.admin.listFornecedoraFamilies()
      .then(r => { setItems(r.items ?? []); setLoadingInit(false); })
      .catch(e => { setError(e.message); setLoadingInit(false); });
  }, []);

  const familyGroups = useIM(() => {
    const m = new Map();
    for (const r of items) {
      if (!m.has(r.family_key)) m.set(r.family_key, { key: r.family_key, label: r.family_label, rows: [] });
      m.get(r.family_key).rows.push(r);
    }
    for (const g of m.values()) {
      g.rows.sort((a, b) => (a.fornecedora_label || a.fornecedora_key || '').localeCompare(b.fornecedora_label || b.fornecedora_key || ''));
    }
    return [...m.values()].sort((a, b) => (a.label || a.key || '').localeCompare(b.label || b.key || ''));
  }, [items]);

  const selectedFamily = useIM(() => familyGroups.find(g => g.key === selected) || null, [familyGroups, selected]);

  useIE(() => {
    if (selected || familyGroups.length === 0) return;
    const def = familyGroups.find(g => g.key === 'ouvidoria') || familyGroups[0];
    if (def) setSelected(def.key);
  }, [familyGroups, selected]);

  return (
    <div className="page">
      <PH eyebrow="GESTOR · FAMÍLIAS" title="Fornecedoras × Família"
          sub="Quais fornecedoras de energia cada família atende. Somente leitura." />

      {error && <div className="chip is-warn" style={{ marginBottom: 10 }}>{error}</div>}

      {loadingInit && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loadingInit && <div className="page-sidebar-grid" style={{ display: 'grid', gridTemplateColumns: '240px minmax(0,1fr)', gap: 14 }}>
        <div className="card card-topline" style={{ padding: 14 }}>
          <div className="mono" style={{
            fontSize: 9.5, color: 'var(--green)', letterSpacing: '0.28em',
            padding: '0 4px 8px', display: 'flex', alignItems: 'center', gap: 6,
          }}>
            <span>/ FAMÍLIAS</span>
            <span style={{ flex: 1 }} />
            <span style={{ color: 'rgba(255,255,255,0.35)' }}>{familyGroups.length}</span>
          </div>
          {familyGroups.length === 0 ? (
            <div style={{ color: 'rgba(255,255,255,0.45)', fontSize: 12, padding: '6px 4px' }}>Nenhuma família com fornecedoras mapeadas.</div>
          ) : familyGroups.map(g => {
            const isSelected = selected === g.key;
            return (
              <button key={g.key} type="button" onClick={() => setSelected(g.key)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 10,
                  width: '100%', padding: '7px 10px', borderRadius: 6,
                  background: isSelected ? 'rgba(34,197,94,0.10)' : 'transparent',
                  border: '1px solid ' + (isSelected ? 'rgba(34,197,94,0.22)' : 'transparent'),
                  textAlign: 'left', color: 'inherit', cursor: 'pointer', marginBottom: 2,
                }}>
                <span style={{ flex: 1, minWidth: 0 }}>
                  <span style={{ fontSize: 12.5, display: 'block' }}>{g.label || g.key}</span>
                  <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', display: 'block', marginTop: 1 }}>{g.key}</span>
                </span>
                <span className="mono" style={{ fontSize: 10, color: g.rows.length ? 'var(--green)' : 'rgba(255,255,255,0.35)' }}>{g.rows.length}</span>
              </button>
            );
          })}
        </div>

        <div className="card" style={{ padding: 16 }}>
          {!selected
            ? <div style={{ color: 'rgba(255,255,255,0.5)', fontSize: 13 }}>Selecione uma família à esquerda.</div>
            : (
              <>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
                  <span className="mono" style={{ fontSize: 10, color: 'var(--green)', letterSpacing: '0.32em' }}>/ {selected.toUpperCase()}</span>
                  <span style={{ flex: 1 }} />
                  <div className="tabs-seg">
                    <button type="button" className={view === 'org' ? 'is-active' : ''} onClick={() => setView('org')}>Organograma</button>
                    <button type="button" className={view === 'lista' ? 'is-active' : ''} onClick={() => setView('lista')}>Lista</button>
                  </div>
                  <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>{selectedFamily?.rows.length ?? 0} FORNECEDORAS</span>
                </div>

                {view === 'org' && (
                  <FamilyFornecedoraOrgChart family={selectedFamily} rows={selectedFamily?.rows ?? []} />
                )}

                {view === 'lista' && (
                  <div className="fam-team-list">
                    {(selectedFamily?.rows ?? []).length === 0 ? (
                      <div className="org-empty" style={{ marginTop: 4 }}>Sem fornecedoras mapeadas.</div>
                    ) : selectedFamily.rows.map(r => (
                      <div key={`${r.family_key}::${r.fornecedora_key}`} className="fam-team-row">
                        <div className="fam-team-ic fam-team-ic-fam"
                             style={{ background: 'rgba(168,85,247,0.10)', borderColor: 'rgba(168,85,247,0.28)', color: '#d8b4fe' }}>
                          <Icon name="plug" size={13} />
                        </div>
                        <div style={{ minWidth: 0 }}>
                          <div className="fam-team-name" title={r.fornecedora_label}>{r.fornecedora_label || r.fornecedora_key}</div>
                          <div className="fam-team-desc">{r.fornecedora_key}</div>
                        </div>
                        <span className="mono fam-team-id">read-only</span>
                      </div>
                    ))}
                  </div>
                )}
              </>
            )}
        </div>
      </div>}
    </div>
  );
}

// =============================================================
// Helpers

// =============================================================
function Field({ label, children, style }) {
  return (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 6, ...style }}>
      <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.22em' }}>{label.toUpperCase()}</span>
      {children}
    </label>
  );
}

function KV({ k, v }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '160px 1fr', gap: 12, padding: '5px 0', borderTop: '1px solid rgba(255,255,255,0.03)' }}>
      <span className="mono" style={{ fontSize: 10.5, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.22em' }}>{k.toUpperCase()}</span>
      <span style={{ fontSize: 13 }}>{v}</span>
    </div>
  );
}

const inp = {
  padding: '9px 11px', borderRadius: 7,
  background: 'rgba(255,255,255,0.04)', border: 'none',
  color: 'var(--text)', fontSize: 13.5, outline: 'none',
  pointerEvents: 'auto', userSelect: 'text', WebkitUserSelect: 'text',
};

function btnPrimary(disabled) {
  return {
    padding: '9px 14px', borderRadius: 7,
    border: 'none',
    background: 'linear-gradient(180deg, rgba(34,197,94,0.18), rgba(34,197,94,0.08))',
    color: '#dcfce7', cursor: disabled ? 'wait' : 'pointer',
    fontSize: 12.5, fontWeight: 500,
    opacity: disabled ? 0.7 : 1,
  };
}
function btnGhost() {
  return {
    padding: '7px 12px', borderRadius: 7,
    border: 'none',
    background: 'rgba(255,255,255,0.03)',
    color: 'rgba(255,255,255,0.78)', cursor: 'pointer',
    fontSize: 11.5,
  };
}
function btnDanger() {
  return {
    padding: '5px 10px', borderRadius: 6,
    border: 'none',
    background: 'rgba(239,68,68,0.10)',
    color: '#fecaca', cursor: 'pointer',
    fontSize: 11,
  };
}

// =============================================================
// gs-fam-cat — Catálogo de famílias (CRUD)
// =============================================================
function CatalogFamilyNode({ family, subs }) {
  const isSpecial = family.kind === 'special';
  const isInactive = family.is_active === false;
  return (
    <div className={`cat-tree ${isInactive ? 'is-inactive' : ''}`}>
      <div className={`cat-tree-root ${isSpecial ? 'is-special' : ''}`}>
        <div className="cat-tree-tag mono">
          {isSpecial ? '/ ESPECIAL · regras únicas' : '/ PRINCIPAL'}
          {isInactive && <span className="cat-tree-inactive"> · INATIVA</span>}
        </div>
        <div className="cat-tree-title">{family.family_label}</div>
        <div className="cat-tree-key mono">{family.family_key}</div>
        <div className="cat-tree-meta mono">
          <span>ordem {family.display_order ?? '—'}</span>
          <span className="org-dot">·</span>
          <span>{subs.length} {subs.length === 1 ? 'sub-família' : 'sub-famílias'}</span>
        </div>
      </div>
      {subs.length > 0 && (
        <>
          <div className="cat-tree-trunk" />
          <div className="cat-tree-tier mono">SUB-FAMÍLIAS · {subs.length}</div>
          <div className="cat-tree-row">
            {subs.map(s => (
              <div key={s.family_key} className={`cat-sub ${s.is_active === false ? 'is-inactive' : ''}`}>
                <div className="cat-sub-label">{s.family_label}</div>
                <div className="cat-sub-key mono">{s.family_key}</div>
                {s.is_active === false && <span className="mono cat-sub-tag">INATIVA</span>}
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function CatalogOrgChart({ items }) {
  const subsByParent = useIM(() => {
    const m = new Map();
    for (const i of items) {
      if (i.kind === 'subfamily' && i.parent_family_key) {
        if (!m.has(i.parent_family_key)) m.set(i.parent_family_key, []);
        m.get(i.parent_family_key).push(i);
      }
    }
    for (const arr of m.values()) {
      arr.sort((a, b) => (a.display_order ?? 999) - (b.display_order ?? 999) || (a.family_label || '').localeCompare(b.family_label || ''));
    }
    return m;
  }, [items]);

  const sortFn = (a, b) => (a.display_order ?? 999) - (b.display_order ?? 999) || (a.family_label || '').localeCompare(b.family_label || '');
  // Ouvidoria entra junto com as principais (operacionalmente é uma família principal).
  // familias_all fica numa seção própria "TODAS".
  const todas = items.filter(i => i.family_key === 'familias_all').sort(sortFn);
  const mains = items
    .filter(i => (i.kind === 'main' || i.family_key === 'ouvidoria') && i.family_key !== 'familias_all')
    .sort(sortFn);
  const knownKeys = new Set(items.map(i => i.family_key));
  const orphanSubs = items.filter(i =>
    i.kind === 'subfamily' && (!i.parent_family_key || !knownKeys.has(i.parent_family_key))
  ).sort(sortFn);
  const uncat = items.filter(i => !i.kind).sort(sortFn);

  if (items.length === 0) {
    return <div className="org-empty">Catálogo vazio.</div>;
  }

  return (
    <div className="cat-org">
      {todas.length > 0 && (
        <div className="cat-org-section">
          <div className="cat-org-section-label mono cat-org-section-todas">
            <span>/ TODAS</span>
            <span className="cat-org-section-count">{todas.length}</span>
          </div>
          <div className="cat-org-grid">
            {todas.map(t => <CatalogFamilyNode key={t.family_key} family={t} subs={subsByParent.get(t.family_key) || []} />)}
          </div>
        </div>
      )}

      {mains.length > 0 && (
        <div className="cat-org-section">
          <div className="cat-org-section-label mono">
            <span>/ FAMÍLIAS PRINCIPAIS</span>
            <span className="cat-org-section-count">{mains.length}</span>
          </div>
          <div className="cat-org-grid">
            {mains.map(m => <CatalogFamilyNode key={m.family_key} family={m} subs={subsByParent.get(m.family_key) || []} />)}
          </div>
        </div>
      )}

      {orphanSubs.length > 0 && (
        <div className="cat-org-section">
          <div className="cat-org-section-label mono cat-org-section-warn">
            <span>/ SUB-FAMÍLIAS ÓRFÃS</span>
            <span className="cat-org-section-count">{orphanSubs.length}</span>
          </div>
          <div className="cat-org-grid">
            {orphanSubs.map(s => (
              <div key={s.family_key} className="cat-tree is-orphan">
                <div className="cat-tree-root is-orphan-root">
                  <div className="cat-tree-tag mono">/ SUB SEM MÃE</div>
                  <div className="cat-tree-title">{s.family_label}</div>
                  <div className="cat-tree-key mono">{s.family_key}</div>
                  {s.parent_family_key && (
                    <div className="cat-tree-meta mono">parent: {s.parent_family_key} (não existe)</div>
                  )}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {uncat.length > 0 && (
        <div className="cat-org-section">
          <div className="cat-org-section-label mono cat-org-section-muted">
            <span>/ SEM TIPO DEFINIDO</span>
            <span className="cat-org-section-count">{uncat.length}</span>
          </div>
          <div className="cat-org-grid">
            {uncat.map(u => (
              <div key={u.family_key} className="cat-tree is-uncat">
                <div className="cat-tree-root is-uncat-root">
                  <div className="cat-tree-tag mono">/ KIND=NULL</div>
                  <div className="cat-tree-title">{u.family_label}</div>
                  <div className="cat-tree-key mono">{u.family_key}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function PageFamCatReal() {
  const [items, setItems] = useIS([]);
  const [loading, setLoading] = useIS(true);
  const [error, setError] = useIS(null);
  const [view, setView] = useIS('org');

  async function load() {
    setLoading(true); setError(null);
    try {
      const r = await window.CCApi.admin.listFamilyCatalog();
      setItems(r.items ?? []);
    } catch (e) { setError(e.detail?.message || e.message); }
    finally { setLoading(false); }
  }

  useIE(() => { load(); }, []);

  function kindBadge(row) {
    if (row.family_key === 'familias_all') return <span className="chip is-info">TODAS</span>;
    if (row.family_key === 'ouvidoria') return <span className="chip" style={{ color: 'var(--green)', borderColor: 'rgba(34,197,94,0.32)' }}>PRINCIPAL</span>;
    if (row.kind === 'main') return <span className="chip" style={{ color: 'var(--green)', borderColor: 'rgba(34,197,94,0.32)' }}>PRINCIPAL</span>;
    if (row.kind === 'subfamily') return <span className="chip is-warn">SUB</span>;
    return <span className="chip">—</span>;
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · FAMÍLIAS" title="Catálogo de famílias"
          sub="Visualização do que é família principal ou sub-família. Define o que aparece em cada dropdown nos cadastros."
          right={
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <div className="tabs-seg">
                <button type="button" className={view === 'org' ? 'is-active' : ''} onClick={() => setView('org')}>Organograma</button>
                <button type="button" className={view === 'lista' ? 'is-active' : ''} onClick={() => setView('lista')}>Lista</button>
              </div>
            </div>
          } />

      {error && <div className="chip is-warn">{error}</div>}

      {view === 'org' && (
        loading
          ? <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>
          : <CatalogOrgChart items={items} />
      )}

      {view === 'lista' && (
        <div className="card card-topline" style={{ padding: 0, overflow: 'hidden' }}>
          <div style={{
            display: 'grid', gridTemplateColumns: '1fr 200px 90px 1fr 70px', gap: 12,
            padding: '12px 18px', borderBottom: '1px solid rgba(255,255,255,0.03)',
            background: 'rgba(255,255,255,0.02)',
          }}>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.22em' }}>LABEL</span>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.22em' }}>KEY</span>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.22em' }}>TIPO</span>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.22em' }}>FAMÍLIA-MÃE</span>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.22em', textAlign: 'right' }}>ORDEM</span>
          </div>
          {loading
            ? <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>
            : items.length === 0
              ? <div style={{ padding: 22, color: 'rgba(255,255,255,0.5)' }}>Catálogo vazio.</div>
              : items.map(r => (
                  <div key={r.family_key} style={{
                    display: 'grid', gridTemplateColumns: '1fr 200px 90px 1fr 70px', gap: 12,
                    padding: '11px 18px', borderTop: '1px solid rgba(255,255,255,0.03)',
                    alignItems: 'center',
                    opacity: r.is_active === false ? 0.45 : 1,
                  }}>
                    <span style={{ fontSize: 13 }}>
                      {r.family_label}
                      {r.is_active === false && <span className="mono" style={{ fontSize: 9, marginLeft: 8, color: 'var(--danger)' }}>INATIVA</span>}
                    </span>
                    <span style={{ fontFamily: '"Geist Mono", monospace', fontSize: 11.5, color: 'rgba(255,255,255,0.62)' }}>{r.family_key}</span>
                    {kindBadge(r)}
                    <span style={{ fontSize: 12, color: r.parent_family_key ? 'var(--text)' : 'rgba(255,255,255,0.3)' }}>
                      {r.parent_family_key || '—'}
                    </span>
                    <span className="mono" style={{ fontSize: 10.5, color: 'rgba(255,255,255,0.5)', textAlign: 'right' }}>{r.display_order}</span>
                  </div>
                ))}
        </div>
      )}

    </div>
  );
}

// =============================================================
// gs-link-sac — Dashboard externo (preview inspirado no login dash-clone)
// =============================================================
function LinkSACClock() {
  const [t, setT] = useIS(() => new Date());
  useIE(() => {
    const id = setInterval(() => setT(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const p = (n) => String(n).padStart(2, '0');
  return `${p(t.getHours())}:${p(t.getMinutes())}:${p(t.getSeconds())}`;
}

function LinkSACPreviewStat({ label, value, trend, accent, spark }) {
  const positive = trend && !trend.startsWith('-');
  return (
    <div className="link-sac-stat" style={{
      position: 'relative', borderRadius: 10, padding: '10px 12px',
      background: 'linear-gradient(145deg, rgba(30,41,59,0.6), rgba(15,23,42,0.55))',
      border: 'none',
      backdropFilter: 'blur(4px)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <span className="mono" style={{ fontSize: 9, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.5)' }}>{label}</span>
        {trend && (
          <span className="mono" style={{ fontSize: 9, color: positive ? '#10b981' : '#ef4444' }}>{trend}</span>
        )}
      </div>
      <div style={{
        marginTop: 4,
        fontSize: 19, fontWeight: 600, letterSpacing: '-0.02em', lineHeight: 1.15,
        color: accent ? 'var(--green)' : 'var(--text)',
      }}>{value}</div>
      <svg viewBox="0 0 100 16" preserveAspectRatio="none" style={{ marginTop: 6, width: '100%', height: 14 }} aria-hidden="true">
        <path d={spark} stroke={accent ? '#22c55e' : 'rgba(52,211,153,0.6)'} strokeWidth="1.2" fill="none" />
      </svg>
    </div>
  );
}

function PageLinkSACReal() {
  const DASHBOARD_URL = 'https://dashboardsac.igreendata.com/';
  const clock = LinkSACClock();
  // Wave path estática mas com 2 curvas defasadas — dá movimento sem JS extra.
  return (
    <div className="page">
      <PH eyebrow="GESTOR · OPERAÇÃO" title="Dashboard - Suporte ao cliente"
          sub="Prévia das métricas operacionais; o painel completo abre em nova aba." />

      <div className="card card-topline link-sac-preview" style={{ padding: 22 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.5)' }}>
            GRÁFICOS SAC
          </div>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.16em', color: 'rgba(255,255,255,0.55)' }}>
            <span style={{ color: 'var(--green)' }}>●</span> {clock}
          </div>
        </div>

        <div style={{ position: 'relative', height: 160, borderRadius: 10, overflow: 'hidden',
          background: 'linear-gradient(180deg, rgba(34,197,94,0.06), rgba(34,197,94,0) 70%)',
          border: 'none' }}>
          <svg viewBox="0 0 600 160" preserveAspectRatio="none" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} aria-hidden="true">
            <defs>
              <linearGradient id="lsg" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0%" stopColor="rgba(34,197,94,0.35)" />
                <stop offset="100%" stopColor="rgba(34,197,94,0)" />
              </linearGradient>
            </defs>
            <path d="M0 120 Q 50 60 100 80 T 200 90 T 300 60 T 400 75 T 500 50 T 600 90 L 600 160 L 0 160 Z"
                  fill="url(#lsg)" />
            <path d="M0 120 Q 50 60 100 80 T 200 90 T 300 60 T 400 75 T 500 50 T 600 90"
                  stroke="#22c55e" strokeWidth="2" fill="none" />
            <path d="M0 140 Q 50 110 100 120 T 200 130 T 300 110 T 400 120 T 500 100 T 600 125"
                  stroke="rgba(52,211,153,0.4)" strokeWidth="1.2" fill="none" />
            {[100, 200, 300, 400, 500].map((x, i) => (
              <line key={i} x1={x} y1={0} x2={x} y2={160} stroke="rgba(255,255,255,0.04)" strokeWidth="1" />
            ))}
          </svg>
        </div>

        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 8 }}>
          <span className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.4)' }}>
            T − 00:05:00
          </span>
          <span className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.55)' }}>
            <span style={{ color: 'var(--green)' }}>●</span> VIVO
          </span>
          <span className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.4)' }}>
            AGORA
          </span>
        </div>

        <div style={{
          marginTop: 14,
          display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 10,
        }}>
          <LinkSACPreviewStat label="ABERTAS/BACKLOG" value="68" trend="+4"
            spark="M0 12 L15 10 L30 11 L45 5 L60 8 L75 6 L90 4 L100 5" />
          <LinkSACPreviewStat label="RESOLVIDAS HOJE" value="124" trend="+12" accent
            spark="M0 14 L15 12 L30 9 L45 7 L60 5 L75 4 L90 3 L100 2" />
          <LinkSACPreviewStat label="TMA" value="02:41" trend="-0:08"
            spark="M0 4 L15 6 L30 5 L45 8 L60 10 L75 9 L90 11 L100 12" />
          <LinkSACPreviewStat label="CSAT" value="4.72" trend="+0.04" accent
            spark="M0 10 L15 8 L30 9 L45 6 L60 7 L75 5 L90 4 L100 4" />
        </div>
      </div>

      <div className="card" style={{
        padding: 22, marginTop: 14, display: 'flex', gap: 18, alignItems: 'center',
        justifyContent: 'space-between', flexWrap: 'wrap',
      }}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <h3 style={{ margin: 0, fontSize: 17, fontWeight: 500, letterSpacing: '-0.01em' }}>
            Painel SAC · ao vivo<span style={{ color: 'var(--green)' }}>.</span>
          </h3>
          <p style={{ margin: '6px 0 0', fontSize: 12.5, color: 'rgba(255,255,255,0.6)', lineHeight: 1.55 }}>
            O dashboard completo é mantido fora da Central. Abre em nova aba com filas, SLA e canais detalhados.
          </p>
          <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.4)', marginTop: 8 }}>
            {DASHBOARD_URL.replace(/^https?:\/\//, '').replace(/\/$/, '')} · NOVA ABA
          </div>
        </div>
        <a href={DASHBOARD_URL} target="_blank" rel="noreferrer"
           className="btn-cta"
           style={{ textDecoration: 'none', flexShrink: 0 }}>
          <Icon name="link" size={14} /> Abrir Dashboard SAC ↗
        </a>
      </div>
    </div>
  );
}

// =============================================================
// gs-atrb-relacao — Atribuição automática (skill gerenciar-atribuicao-automatica)
// =============================================================
function PageAtribuicaoReal() {
  const [items, setItems] = useIS([]);
  const [loading, setLoading] = useIS(true);
  const [error, setError] = useIS(null);
  const [ok, setOk] = useIS(null);
  const [filter, setFilter] = useIS('');
  // Default: meetcall — atualmente é a única família onde o gerenciamento de atribuição automática está ativo no n8n.
  const [familyKey, setFamilyKey] = useIS('familia_meet_call');
  const [modal, setModal] = useIS(null); // { row, mode: 'temporary'|'permanent' }
  const [busy, setBusy] = useIS(false);
  const [tip, setTip] = useIS(null); // { x, y, text }
  function showTip(e, text) {
    const rect = e.currentTarget.getBoundingClientRect();
    setTip({ x: rect.left + rect.width / 2, y: rect.top, text });
  }
  function hideTip() { setTip(null); }
  const tipHandlers = (text) => ({
    onMouseEnter: (e) => showTip(e, text),
    onMouseLeave: hideTip,
    onFocus: (e) => showTip(e, text),
    onBlur: hideTip,
  });

  async function load() {
    setError(null);
    try {
      const r = await window.CCApi.admin.listAttribution();
      setItems(r.items ?? []);
    } catch (e) { setError(e.detail?.message || e.message); }
    finally { setLoading(false); }
  }
  useIE(() => { load(); }, []);

  // Re-classifica localmente a cada 30s pra mostrar quando uma pausa temporária expira sem precisar reload manual.
  const [, force] = useIS(0);
  useIE(() => {
    const t = setInterval(() => force((n) => n + 1), 30_000);
    return () => clearInterval(t);
  }, []);

  function classifyLocal(row) {
    const now = Date.now();
    if (row.atrb_ignorar_sempre) return 'permanent';
    if (row.atrb_ignorar_linha) {
      const until = row.atrb_ignorar_linha_ate ? new Date(row.atrb_ignorar_linha_ate).getTime() : 0;
      if (!until || until > now) return 'temporary';
      return 'active';
    }
    return 'active';
  }

  const familyOptions = useIM(() => {
    const seen = new Map();
    for (const r of items) {
      for (const f of r.families || []) {
        if (f.family_key === 'familias_all') continue;
        if (!seen.has(f.family_key)) seen.set(f.family_key, f.family_label || f.family_key);
      }
    }
    return Array.from(seen.entries())
      .map(([key, label]) => ({ key, label }))
      .sort((a, b) => (a.label || '').localeCompare(b.label || '', 'pt-BR'));
  }, [items]);

  const grouped = useIM(() => {
    const q = filter.trim().toLowerCase();
    const matchesText = (r) => !q
      || (r.assignee_name || '').toLowerCase().includes(q)
      || (r.assignee_email || '').toLowerCase().includes(q)
      || String(r.assignee_id).includes(q);
    const matchesFamily = (r) => !familyKey || (r.families || []).some((f) => f.family_key === familyKey);
    const buckets = { active: [], temporary: [], permanent: [] };
    for (const r of items) {
      if (!matchesText(r) || !matchesFamily(r)) continue;
      buckets[classifyLocal(r)].push(r);
    }
    return buckets;
  }, [items, filter, familyKey]);

  async function unblock(row, scope) {
    setBusy(true); setError(null); setOk(null);
    try {
      await window.CCApi.admin.unblockAttribution(row.assignee_id, scope);
      setOk(`${row.assignee_name || row.assignee_email} reativado.`);
      await load();
    } catch (e) { setError(e.detail?.message || e.detail?.error || e.message); }
    finally { setBusy(false); }
  }

  function openBlock(row, mode) {
    setModal({ row, mode, untilLocal: defaultUntilLocal() });
    setOk(null); setError(null);
  }
  function closeModal() { if (!busy) setModal(null); }

  async function confirmBlock() {
    if (!modal) return;
    const { row, mode } = modal;
    setBusy(true); setError(null); setOk(null);
    try {
      const payload = { mode };
      if (mode === 'temporary') {
        if (!modal.untilLocal) throw new Error('Data/hora inválida.');
        const iso = toIsoBrt(modal.untilLocal);
        const dt = new Date(iso);
        if (Number.isNaN(dt.getTime())) throw new Error('Data/hora inválida.');
        if (dt.getTime() <= Date.now()) throw new Error('A data limite precisa ser no futuro.');
        payload.until = iso;
      }
      const r = await window.CCApi.admin.blockAttribution(row.assignee_id, payload);
      const label = mode === 'permanent' ? 'permanentemente' : `até ${formatPtBr(new Date(payload.until))}`;
      const extra = r?.unassign?.unassigned != null ? ` · ${r.unassign.unassigned} conversa(s) desatribuída(s)` : '';
      setOk(`${row.assignee_name || row.assignee_email} pausado ${label}${extra}.`);
      setModal(null);
      await load();
    } catch (e) {
      setError(e.detail?.message || e.detail?.error || e.message);
    } finally { setBusy(false); }
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · ATRIBUIÇÃO AUTOMÁTICA" title="Relação de atribuição automática"
          sub="Quem está ativo, em pausa temporária ou em pausa permanente. Pausar também desatribui as conversas abertas no Chatwoot." />

      <div style={{
        display: 'flex', alignItems: 'flex-start', gap: 12,
        padding: '12px 14px', marginBottom: 12,
        borderRadius: 10,
        background: 'linear-gradient(145deg, rgba(245,158,11,0.10), rgba(245,158,11,0.03))',
        border: 'none',
      }}>
        <span style={{
          width: 26, height: 26, borderRadius: 8, flexShrink: 0,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          background: 'rgba(245,158,11,0.18)', color: '#fbbf24',
        }}>
          <Icon name="bell" size={13} />
        </span>
        <div style={{ fontSize: 12.5, lineHeight: 1.55, color: '#fde68a' }}>
          <strong style={{ color: '#fef3c7' }}>Atenção:</strong>{' '}
          o gerenciamento de atribuição automática está ativo <strong>apenas para a família Meet Call</strong> no momento.
          Pausas/bloqueios em outras famílias ficam registrados no banco mas não têm efeito no roteamento de atribuição para outras famílias.
        </div>
      </div>

      <div className="card" style={{ padding: 14, marginBottom: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: '1 1 260px' }}>
            <Icon name="search" size={13} />
            <SearchInput name="atrbFilter" placeholder="Filtrar por nome, email ou assignee_id…" onValue={setFilter} />
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.22em' }}>FAMÍLIA</span>
            <select value={familyKey} onChange={(e) => setFamilyKey(e.target.value)} style={{ ...inp, minWidth: 220 }}>
              <option value="">Todas as famílias</option>
              {familyOptions.map((f) => (
                <option key={f.key} value={f.key}>{f.label}</option>
              ))}
            </select>
          </div>
          <button type="button" onClick={() => { setLoading(true); load(); }} style={btnGhost()}>Recarregar</button>
        </div>
      </div>

      {error && <div className="chip is-warn" style={{ marginBottom: 10 }}>{error}</div>}
      {ok && <div className="chip" style={{ color: 'var(--green)', marginBottom: 10 }}>{ok}</div>}

      {loading
        ? <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>
        : (
          <>
            <div className="atrb-stats">
              <AtrbStat icon="shield" tone="safe"
                count={grouped.active.length} label="Ativos" />
              <AtrbStat icon="history" tone="warn"
                count={grouped.temporary.length} label="Pausa temporária" />
              <AtrbStat icon="ban" tone="danger"
                count={grouped.permanent.length} label="Pausa permanente" />
            </div>

            <div className="atrb-grid">
              <AtrbColumn tone="safe" title="Ativos" count={grouped.active.length}
                rows={grouped.active} emptyText="Ninguém ativo neste filtro."
                renderActions={(r) => (
                  <>
                    <button className="atrb-ic-btn" data-tone="warn" type="button"
                      aria-label="Pausar até um horário"
                      {...tipHandlers('Pausar até um horário')}
                      onClick={() => openBlock(r, 'temporary')} disabled={busy}>
                      <Icon name="history" size={13} />
                    </button>
                    <button className="atrb-ic-btn" data-tone="danger" type="button"
                      aria-label="Pausar por tempo indeterminado"
                      {...tipHandlers('Pausar por tempo indeterminado')}
                      onClick={() => openBlock(r, 'permanent')} disabled={busy}>
                      <Icon name="ban" size={13} />
                    </button>
                  </>
                )} />
              <AtrbColumn tone="warn" title="Pausa temporária" count={grouped.temporary.length}
                rows={grouped.temporary} emptyText="Ninguém em pausa temporária."
                renderSub={(r) => r.atrb_ignorar_linha_ate
                  ? `até ${formatPtBr(new Date(r.atrb_ignorar_linha_ate))}`
                  : null}
                renderActions={(r) => (
                  <button className="atrb-ic-btn" data-tone="safe" type="button"
                    aria-label="Liberar agora"
                    {...tipHandlers('Liberar agora (cancelar a pausa)')}
                    onClick={() => unblock(r, 'temporary')} disabled={busy}>
                    <Icon name="rotate" size={13} />
                  </button>
                )} />
              <AtrbColumn tone="danger" title="Pausa permanente" count={grouped.permanent.length}
                rows={grouped.permanent} emptyText="Ninguém em pausa permanente."
                renderActions={(r) => (
                  <button className="atrb-ic-btn" data-tone="safe" type="button"
                    aria-label="Reativar"
                    {...tipHandlers('Reativar (voltar a atribuir)')}
                    onClick={() => unblock(r, 'permanent')} disabled={busy}>
                    <Icon name="rotate" size={13} />
                  </button>
                )} />
            </div>
          </>
        )}

      {modal && (
        <AtrbModal
          modal={modal}
          busy={busy}
          onClose={closeModal}
          onConfirm={confirmBlock}
          onChangeUntil={(v) => setModal((m) => m ? { ...m, untilLocal: v } : m)}
        />
      )}

      {tip && (
        <div className="atrb-tip" style={{ left: tip.x, top: tip.y }}>
          {tip.text}
        </div>
      )}
    </div>
  );
}

const ATRB_TONES = {
  safe:   { color: 'var(--green)',          bg: 'rgba(34,197,94,0.10)',  border: 'rgba(34,197,94,0.28)',  tint: 'rgba(34,197,94,0.06)'  },
  warn:   { color: 'rgba(245,158,11,0.95)', bg: 'rgba(245,158,11,0.10)', border: 'rgba(245,158,11,0.32)', tint: 'rgba(245,158,11,0.06)' },
  danger: { color: '#d8b4fe',               bg: 'rgba(168,85,247,0.12)', border: 'rgba(168,85,247,0.36)', tint: 'rgba(168,85,247,0.06)' },
};
function toneVars(tone) {
  const t = ATRB_TONES[tone] || ATRB_TONES.safe;
  return { '--atrb-color': t.color, '--atrb-bg': t.bg, '--atrb-border': t.border, '--atrb-tint': t.tint };
}
function atrbInitials(name, fallback) {
  const src = (name || fallback || '').trim();
  if (!src) return '·';
  const parts = src.split(/[\s.@_-]+/).filter(Boolean);
  if (parts.length === 0) return src.slice(0, 2).toUpperCase();
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}

function AtrbStat({ icon, tone, count, label }) {
  return (
    <div className="atrb-stat" style={toneVars(tone)}>
      <span className="atrb-stat-ic"><Icon name={icon} size={16} /></span>
      <div>
        <div className="atrb-stat-num">{count}</div>
        <div className="atrb-stat-lbl">{label}</div>
      </div>
    </div>
  );
}

function AtrbColumn({ tone, title, count, rows, renderActions, renderSub, emptyText }) {
  const styleVars = toneVars(tone);
  return (
    <div className="atrb-col" style={styleVars}>
      <div className="atrb-col-head">
        <span className="atrb-col-dot" />
        <span className="atrb-col-title">{title}</span>
        <span className="atrb-col-count">{count}</span>
      </div>
      <div className="atrb-col-body">
        {rows.length === 0
          ? <div className="atrb-col-empty">{emptyText}</div>
          : rows.map((r) => (
              <div key={r.assignee_id} className="atrb-row" title={r.assignee_email}>
                <span className="atrb-avatar">{atrbInitials(r.assignee_name, r.assignee_email)}</span>
                <div className="atrb-row-text">
                  <span className="atrb-row-name">{r.assignee_name || r.assignee_email}</span>
                  <span className="atrb-row-sub">
                    {renderSub ? (renderSub(r) || r.assignee_email) : r.assignee_email}
                  </span>
                </div>
                <span className="atrb-row-actions">{renderActions(r)}</span>
              </div>
            ))}
      </div>
    </div>
  );
}

function AtrbModal({ modal, busy, onClose, onConfirm, onChangeUntil }) {
  useIE(() => {
    function onKey(e) { if (e.key === 'Escape' && !busy) onClose(); }
    document.addEventListener('keydown', onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = prev; };
  }, [busy]);

  const { row, mode } = modal;
  const isTemp = mode === 'temporary';
  return (
    <div className="orphan-modal-backdrop" onClick={onClose}>
      <div className="orphan-modal" onClick={(e) => e.stopPropagation()} style={{ borderColor: isTemp ? 'rgba(245,158,11,0.32)' : 'rgba(239,68,68,0.40)' }}>
        <div className="orphan-modal-head">
          <span className="orphan-bar-ic" style={{ background: isTemp ? 'rgba(245,158,11,0.18)' : 'rgba(239,68,68,0.18)', color: isTemp ? '#fbbf24' : '#fecaca' }}>
            <Icon name={isTemp ? 'history' : 'ban'} size={14} />
          </span>
          <div className="orphan-modal-title">
            {isTemp ? 'Pausar temporariamente' : 'Pausar permanentemente'} — <strong>{row.assignee_name || row.assignee_email}</strong>
          </div>
          <button type="button" className="orphan-modal-close" onClick={onClose} aria-label="Fechar" disabled={busy}>×</button>
        </div>
        <div className="orphan-modal-body">
          <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.75)', lineHeight: 1.55, marginBottom: 14 }}>
            {isTemp
              ? <>Atribuição pausada até a data/hora abaixo. O sistema libera automaticamente quando o prazo passar. Conversas abertas serão desatribuídas no Chatwoot.</>
              : <>Atribuição pausada por tempo indeterminado. Conversas abertas serão desatribuídas no Chatwoot. Use "Reativar" para liberar depois.</>}
          </div>

          {isTemp && (
            <Field label="Liberar em">
              <input
                type="datetime-local"
                value={modal.untilLocal}
                onChange={(e) => onChangeUntil(e.target.value)}
                style={inp}
              />
            </Field>
          )}

          <div style={{ marginTop: 14, fontSize: 11.5, color: 'rgba(255,255,255,0.5)' }}>
            Famílias afetadas: {row.families?.length ? row.families.map((f) => f.family_key).join(', ') : '—'}
          </div>

          <div style={{ display: 'flex', gap: 10, marginTop: 18 }}>
            <button type="button" onClick={onConfirm} style={isTemp ? btnPrimary(busy) : btnDanger()} disabled={busy}>
              {busy ? '…' : (isTemp ? 'Pausar' : 'Pausar permanentemente')}
            </button>
            <button type="button" onClick={onClose} style={btnGhost()} disabled={busy}>Cancelar</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// Helpers de data para o modal de pausa temporária.
function defaultUntilLocal() {
  // Default: amanhã 07:00 horário local do navegador.
  const d = new Date();
  d.setDate(d.getDate() + 1);
  d.setHours(7, 0, 0, 0);
  return localInput(d);
}
function localInput(d) {
  // 'YYYY-MM-DDTHH:mm' no horário local (input datetime-local).
  const pad = (n) => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
function toIsoBrt(localInput) {
  // O input do <input type="datetime-local"> é "YYYY-MM-DDTHH:mm" sem timezone.
  // A skill `gerenciar-atribuicao-automatica` espera que esse horário seja
  // interpretado como BRT (UTC-3) — então só anexamos o offset.
  if (!localInput || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(localInput)) return null;
  return `${localInput}:00-03:00`;
}
function formatPtBr(d) {
  try {
    return new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }).format(d);
  } catch { return d.toISOString(); }
}

// =============================================================
// Relatório de Resoluções (gs-rel-resol) — porting da app standalone
// relatorio-v2-sac. Backend faz tudo via job em memória; aqui só
// UI + polling + alarme sonoro + export CSV.
// =============================================================

function RR_pad(n) { return String(n).padStart(2, '0'); }
function RR_toInputDate(d) {
  return `${d.getFullYear()}-${RR_pad(d.getMonth()+1)}-${RR_pad(d.getDate())}`;
}
function RR_fromInputDate(s) {
  const [y,m,d] = String(s).split('-').map(Number);
  return new Date(y, m-1, d);
}
function RR_daysInMonth(y, m) { return new Date(y, m+1, 0).getDate(); }
function RR_firstWeekday(y, m) { return new Date(y, m, 1).getDay(); }
function RR_monthGrid(y, m) {
  const total = RR_daysInMonth(y, m);
  const start = RR_firstWeekday(y, m);
  const cells = [];
  for (let i = 0; i < start; i++) cells.push(null);
  for (let d = 1; d <= total; d++) cells.push(d);
  while (cells.length % 7 !== 0) cells.push(null);
  return cells;
}
function RR_formatBrasilia(iso) {
  if (!iso) return '-';
  try {
    return new Date(iso).toLocaleString('pt-BR', {
      timeZone: 'America/Sao_Paulo',
      day: '2-digit', month: '2-digit', year: 'numeric',
      hour: '2-digit', minute: '2-digit', second: '2-digit',
    });
  } catch { return iso; }
}

function PageRelResolReal() {
  const today = useIM(() => new Date(), []);
  const todayStr = useIM(() => RR_toInputDate(today), [today]);
  const minDateStr = '2025-09-15';
  const MAX_PERIOD_DAYS = 40;

  const [startDate, setStartDate] = useIS(todayStr);
  const [endDate, setEndDate] = useIS(todayStr);

  const [families, setFamilies] = useIS([]);
  const [assignees, setAssignees] = useIS([]);
  const [familyAssigneeIds, setFamilyAssigneeIds] = useIS({});
  const [selectedFamilyKey, setSelectedFamilyKey] = useIS('');
  const [selectedAssigneeIds, setSelectedAssigneeIds] = useIS([]);

  const [loadingFilters, setLoadingFilters] = useIS(true);
  const [filtersError, setFiltersError] = useIS(null);

  const [loading, setLoading] = useIS(false);
  const [progressMessage, setProgressMessage] = useIS('Iniciando…');
  const [data, setData] = useIS([]);
  const [hasSearched, setHasSearched] = useIS(false);
  const [reportError, setReportError] = useIS(null);
  const [exporting, setExporting] = useIS(false);
  const [periodLimitOpen, setPeriodLimitOpen] = useIS(false);

  const [pickerOpen, setPickerOpen] = useIS(null); // 'start' | 'end' | null
  const [viewYear, setViewYear] = useIS(today.getFullYear());
  const [viewMonth, setViewMonth] = useIS(today.getMonth());
  const [familyMenuOpen, setFamilyMenuOpen] = useIS(false);
  const [assigneeMenuOpen, setAssigneeMenuOpen] = useIS(false);

  const pickerRef = useIR(null);
  const familyMenuRef = useIR(null);
  const assigneeMenuRef = useIR(null);
  const pollIntervalRef = useIR(null);
  const lastLoadingRef = useIR(false);

  const minDate = RR_fromInputDate(minDateStr);
  const maxDate = today;

  const family = families.find(f => f.key === selectedFamilyKey) || null;
  const displayedAssignees = selectedFamilyKey
    ? assignees.filter(a => (familyAssigneeIds[selectedFamilyKey] || []).includes(a.id))
    : assignees;
  const isCustomAssigneeMode = selectedAssigneeIds.length > 0;
  const isFamilyMode = !!selectedFamilyKey && !isCustomAssigneeMode;

  const selectedAssigneesLabel =
    selectedAssigneeIds.length === 0
      ? 'Todos os colaboradores'
      : selectedAssigneeIds.length === 1
        ? (assignees.find(a => a.id === selectedAssigneeIds[0])?.name || '1 colaborador')
        : `${selectedAssigneeIds.length} colaboradores`;

  // ---------- Alarme sonoro (Web Audio API — copy literal do standalone) ---------
  const playAlarm = useIC(() => {
    try {
      const Ctx = window.AudioContext || window.webkitAudioContext;
      if (!Ctx) return;
      const ctx = new Ctx();
      const now = ctx.currentTime;
      const tone = (freq, start, dur) => {
        const osc = ctx.createOscillator();
        const gain = ctx.createGain();
        osc.type = 'square';
        osc.frequency.value = freq;
        osc.connect(gain);
        gain.connect(ctx.destination);
        gain.gain.setValueAtTime(0.0001, now + start);
        gain.gain.exponentialRampToValueAtTime(0.5, now + start + 0.05);
        gain.gain.exponentialRampToValueAtTime(0.0001, now + start + dur);
        osc.start(now + start);
        osc.stop(now + start + dur + 0.02);
      };
      tone(900, 0, 0.28);
      tone(700, 0.35, 0.28);
      tone(1000, 0.7, 0.36);
    } catch {}
  }, []);

  // ---------- Polling de job ----------
  const stopPolling = useIC(() => {
    if (pollIntervalRef.current) {
      clearInterval(pollIntervalRef.current);
      pollIntervalRef.current = null;
    }
  }, []);

  const startPolling = useIC((jobId) => {
    stopPolling();
    const tick = async () => {
      try {
        const r = await window.CCApi.admin.getResolutionsJob(jobId);
        const job = r?.job;
        if (!job) return;
        if (job.progressMessage) setProgressMessage(job.progressMessage);
        if (job.status === 'done') {
          stopPolling();
          setData(job.result || []);
          setLoading(false);
        } else if (job.status === 'error') {
          stopPolling();
          setReportError(job.error || 'Falha ao gerar o relatório.');
          setLoading(false);
        }
      } catch (e) {
        // ignora — pode ser blip de rede; próximo tick tenta de novo
      }
    };
    tick(); // primeiro fire imediato
    pollIntervalRef.current = setInterval(tick, 5000);
  }, [stopPolling]);

  // ---------- Carregar filtros + retomar job ativo ----------
  useIE(() => {
    let cancelled = false;
    (async () => {
      try {
        setLoadingFilters(true);
        setFiltersError(null);
        const options = await window.CCApi.admin.listResolutionsOptions();
        if (cancelled) return;
        setFamilies(options.families || []);
        setAssignees(options.assignees || []);
        setFamilyAssigneeIds(options.familyAssigneeIds || {});
      } catch (e) {
        if (!cancelled) setFiltersError('Não foi possível carregar Famílias e Colaboradores.');
      } finally {
        if (!cancelled) setLoadingFilters(false);
      }

      try {
        const r = await window.CCApi.admin.getActiveResolutionsJob();
        if (cancelled) return;
        const job = r?.job;
        if (job?.jobId) {
          setLoading(true);
          setHasSearched(true);
          setProgressMessage(job.progressMessage || 'Retomando relatório em andamento…');
          if (job.params?.startDate) setStartDate(job.params.startDate);
          if (job.params?.endDate) setEndDate(job.params.endDate);
          startPolling(job.jobId);
        }
      } catch {}
    })();
    return () => {
      cancelled = true;
      stopPolling();
    };
  // eslint-disable-next-line
  }, []);

  // Disparar alarme quando loading vira false depois de ter sido true
  useIE(() => {
    if (lastLoadingRef.current && !loading && hasSearched) playAlarm();
    lastLoadingRef.current = loading;
  }, [loading, hasSearched, playAlarm]);

  // Outside click pra fechar menus
  useIE(() => {
    function onClick(e) {
      const target = e.target;
      if (pickerOpen && pickerRef.current && !pickerRef.current.contains(target)) setPickerOpen(null);
      if (familyMenuOpen && familyMenuRef.current && !familyMenuRef.current.contains(target)) setFamilyMenuOpen(false);
      if (assigneeMenuOpen && assigneeMenuRef.current && !assigneeMenuRef.current.contains(target)) setAssigneeMenuOpen(false);
    }
    document.addEventListener('mousedown', onClick);
    return () => document.removeEventListener('mousedown', onClick);
  }, [pickerOpen, familyMenuOpen, assigneeMenuOpen]);

  // ---------- Handlers ----------
  function openPicker(which) {
    setFamilyMenuOpen(false);
    setAssigneeMenuOpen(false);
    setPickerOpen(which);
    const base = which === 'start' ? RR_fromInputDate(startDate) : RR_fromInputDate(endDate);
    setViewYear(base.getFullYear());
    setViewMonth(base.getMonth());
  }
  function moveMonth(offset) {
    const candidate = new Date(viewYear, viewMonth + offset, 1);
    const minMonth = new Date(minDate.getFullYear(), minDate.getMonth(), 1);
    const maxMonth = new Date(maxDate.getFullYear(), maxDate.getMonth(), 1);
    let target = candidate;
    if (candidate < minMonth) target = minMonth;
    if (candidate > maxMonth) target = maxMonth;
    setViewYear(target.getFullYear());
    setViewMonth(target.getMonth());
  }
  function isOutsideRange(y, m, d) {
    const dt = new Date(y, m, d);
    return dt < minDate || dt > maxDate;
  }
  function isSelected(which, y, m, d) {
    const s = which === 'start' ? startDate : endDate;
    const dt = RR_fromInputDate(s);
    return dt.getFullYear() === y && dt.getMonth() === m && dt.getDate() === d;
  }
  function chooseDay(d) {
    const chosen = RR_toInputDate(new Date(viewYear, viewMonth, d));
    if (pickerOpen === 'start') setStartDate(chosen);
    if (pickerOpen === 'end') setEndDate(chosen);
    setPickerOpen(null);
  }

  function toggleAssignee(id) {
    if (selectedFamilyKey) setSelectedFamilyKey('');
    setSelectedAssigneeIds(curr => curr.includes(id) ? curr.filter(x => x !== id) : [...curr, id]);
  }

  async function handleSearch() {
    if (!startDate || !endDate) return;
    const start = RR_fromInputDate(startDate);
    const end = RR_fromInputDate(endDate);
    const startUtc = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
    const endUtc = Date.UTC(end.getFullYear(), end.getMonth(), end.getDate());
    const days = Math.floor((endUtc - startUtc) / 86_400_000) + 1;
    if (days > MAX_PERIOD_DAYS) { setPeriodLimitOpen(true); return; }

    setReportError(null);
    setLoading(true);
    setHasSearched(true);
    setData([]);
    setProgressMessage('Iniciando…');

    try {
      const fam = families.find(f => f.key === selectedFamilyKey) || null;
      const useFamilyFilter = !!selectedFamilyKey && selectedAssigneeIds.length === 0;
      const payload = {
        startDate,
        endDate,
        assigneeIds: selectedAssigneeIds,
        familySourceKeys: useFamilyFilter ? (fam?.sourceKeys || []) : [],
      };
      const r = await window.CCApi.admin.startResolutionsReport(payload);
      if (r?.jobId) startPolling(r.jobId);
      else throw new Error('Backend não retornou jobId');
    } catch (e) {
      setReportError(e?.message || 'Erro ao iniciar relatório.');
      setLoading(false);
    }
  }

  async function exportCSV() {
    if (data.length === 0 || exporting) return;
    setExporting(true);
    await new Promise(r => setTimeout(r, 0));
    const headers = [
      'ID Evento','ID Conversa','ID Usuário','Contact ID','Código do Cliente',
      'CPF/CNPJ','Telefone','Colaborador','Fornecedora','Time','Familia',
      'Tabulação','Data Início Conversa','Data Resolução','Nota CSAT','Nota CES',
      'Comentario','Comentário 2','Link conversa',
    ];
    const cell = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
    const rows = [
      headers.join(','),
      ...data.map(row => [
        cell(row.id), cell(row.conversation_id), cell(row.user_id), cell(row.contact_id),
        cell(row.codigo_cliente), cell(row.cpf_cnpj), cell(row.phone_number),
        cell(row.colaborador), cell(row.fornecedora), cell(row.time),
        cell(row.familia ?? row.Familia), cell(row.tabulacao),
        cell(RR_formatBrasilia(row.conversation_started_at ?? '')),
        cell(RR_formatBrasilia(row.event_start_time)),
        cell(row.nota_csat), cell(row.nota_ces),
        cell(row.comentario), cell(row.comentario2),
        cell(`https://igreenchat.app/app/accounts/1/conversations/${row.conversation_id}`),
      ].join(',')),
    ];
    const blob = new Blob([rows.join('\n')], { type: 'text/csv' });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.setAttribute('hidden', '');
    a.setAttribute('href', url);
    a.setAttribute('download', `relatorio_resolucoes_colaboradores_${startDate}_${endDate}.csv`);
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    setExporting(false);
  }

  // ---------- Render ----------
  return (
    <section className="page-shell cc-resol-root rise">
      <PH
        eyebrow="GESTOR / OPERAÇÃO"
        title="Relatório de resoluções"
        sub="Conversas resolvidas no período, com CSAT / CES e enriquecimento de fornecedora & cliente."
      />

      <div className="cc-resol-filters-card">
        <div className="cc-resol-filters-grid">
          {/* Período */}
          <div ref={pickerRef} className="cc-resol-field cc-resol-field-period">
            <label className="cc-resol-label">Selecione o período</label>
            <div className="cc-resol-period-input">
              <div className="cc-resol-period-slot" onClick={() => !loading && openPicker('start')}>
                <Icon name="layers" size={14} />
                <span className="mono">{startDate}</span>
              </div>
              <span className="cc-resol-period-sep">até</span>
              <div className="cc-resol-period-slot" onClick={() => !loading && openPicker('end')}>
                <Icon name="layers" size={14} />
                <span className="mono">{endDate}</span>
              </div>
            </div>
            {pickerOpen && (
              <div className={`cc-resol-calendar-pop ${pickerOpen === 'end' ? 'is-right' : 'is-left'}`}>
                <div className="cc-resol-cal-head">
                  <button className="cc-resol-cal-nav" onClick={() => moveMonth(-1)}>‹</button>
                  <div className="cc-resol-cal-title">
                    {new Date(viewYear, viewMonth, 1).toLocaleString('pt-BR', { month: 'long', year: 'numeric' })}
                  </div>
                  <button className="cc-resol-cal-nav" onClick={() => moveMonth(1)}>›</button>
                </div>
                <div className="cc-resol-cal-weekdays">
                  {['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'].map((d,i) => <div key={i}>{d}</div>)}
                </div>
                <div className="cc-resol-cal-grid">
                  {RR_monthGrid(viewYear, viewMonth).map((cell, i) => {
                    if (cell === null) return <div key={i} className="cc-resol-cal-empty" />;
                    const sel = isSelected(pickerOpen, viewYear, viewMonth, cell);
                    const dis = isOutsideRange(viewYear, viewMonth, cell);
                    return (
                      <button key={i}
                              disabled={dis}
                              onClick={() => chooseDay(cell)}
                              className={`cc-resol-cal-day${sel ? ' is-sel' : ''}${dis ? ' is-dis' : ''}`}>
                        {cell}
                      </button>
                    );
                  })}
                </div>
              </div>
            )}
          </div>

          {/* Família */}
          <div ref={familyMenuRef} className="cc-resol-field">
            <label className="cc-resol-label">Família</label>
            <button type="button" className="cc-resol-select-btn"
                    disabled={loading || loadingFilters}
                    onClick={() => { setFamilyMenuOpen(v => !v); setAssigneeMenuOpen(false); }}>
              <span className="cc-resol-select-text">{family?.label || 'Família'}</span>
              <span className={`cc-resol-select-caret${familyMenuOpen ? ' is-open' : ''}`}>▾</span>
            </button>
            {familyMenuOpen && (
              <div className="cc-resol-dropdown-pop">
                <button className="cc-resol-dd-item" onClick={() => {
                  setSelectedFamilyKey(''); setSelectedAssigneeIds([]); setFamilyMenuOpen(false);
                }}>
                  <span className="cc-resol-dd-check">{!selectedFamilyKey ? '✓' : ' '}</span>
                  Todas
                </button>
                <div className="cc-resol-dd-list">
                  {families.map(item => (
                    <button key={item.key} className="cc-resol-dd-item" onClick={() => {
                      setSelectedFamilyKey(item.key); setSelectedAssigneeIds([]); setFamilyMenuOpen(false);
                    }}>
                      <span className="cc-resol-dd-check">{selectedFamilyKey === item.key ? '✓' : ' '}</span>
                      <span className="cc-resol-dd-label">{item.label}</span>
                    </button>
                  ))}
                </div>
              </div>
            )}
          </div>

          {/* Colaboradores */}
          <div ref={assigneeMenuRef} className="cc-resol-field">
            <label className="cc-resol-label">Colaboradores</label>
            <button type="button" className="cc-resol-select-btn"
                    disabled={loading || loadingFilters}
                    onClick={() => { setAssigneeMenuOpen(v => !v); setFamilyMenuOpen(false); }}>
              <Icon name="users" size={13} />
              <span className="cc-resol-select-text" style={{ marginLeft: 6 }}>{selectedAssigneesLabel}</span>
              <span className={`cc-resol-select-caret${assigneeMenuOpen ? ' is-open' : ''}`}>▾</span>
            </button>
            {assigneeMenuOpen && (
              <div className="cc-resol-dropdown-pop is-wide">
                <div className="cc-resol-dd-list">
                  {displayedAssignees.map(a => {
                    const checked = selectedAssigneeIds.includes(a.id);
                    return (
                      <button key={a.id} className="cc-resol-dd-item" onClick={() => toggleAssignee(a.id)}>
                        <span className={`cc-resol-dd-checkbox${checked ? ' is-on' : ''}`}>{checked ? '✓' : ''}</span>
                        <span className="cc-resol-dd-label">{a.name}</span>
                      </button>
                    );
                  })}
                  {displayedAssignees.length === 0 && (
                    <div className="cc-resol-dd-empty">Nenhum colaborador para a família selecionada.</div>
                  )}
                </div>
                <div className="cc-resol-dd-foot">
                  <button onClick={() => setSelectedAssigneeIds([])} className="cc-resol-dd-clear">Limpar</button>
                </div>
              </div>
            )}
          </div>

          {/* Botão pesquisar */}
          <div className="cc-resol-field cc-resol-field-search">
            <span className="cc-resol-label">&nbsp;</span>
            <button onClick={handleSearch}
                    disabled={loading || loadingFilters}
                    className="cc-resol-search-btn">
              <Icon name={loading ? 'rotate' : 'search'} size={16} />
              <span>{loading ? 'Buscando…' : loadingFilters ? 'Carregando filtros…' : 'Pesquisar'}</span>
            </button>
          </div>
        </div>

        <div className="cc-resol-mode-row">
          <span className="cc-resol-mode-badge">
            <span className="cc-resol-mode-dot" />
            {isCustomAssigneeMode
              ? `Modo: Colaboradores (${selectedAssigneeIds.length})`
              : isFamilyMode
                ? `Modo: Família (${family?.label})`
                : 'Modo: Todos'}
          </span>
        </div>
        {filtersError && <div className="cc-resol-err">{filtersError}</div>}
        {reportError && <div className="cc-resol-err">{reportError}</div>}
      </div>

      {hasSearched && !loading && (
        <div className="cc-resol-kpi-card rise">
          <div className="cc-resol-kpi-head">
            <div>
              <div className="cc-resol-kpi-title">Resumo do Período</div>
              <span className="cc-resol-kpi-pill mono">{data.length} registros</span>
            </div>
            <button onClick={exportCSV}
                    disabled={exporting || data.length === 0}
                    className="cc-resol-export-btn">
              <Icon name={exporting ? 'rotate' : 'download'} size={14} />
              <span>{exporting ? 'Exportando…' : 'Exportar CSV'}</span>
            </button>
          </div>
          <div className="cc-resol-kpi-body">
            <div className="cc-resol-kpi-num">{data.length}</div>
            <div className="cc-resol-kpi-cap">Registros encontrados para o período selecionado</div>
            <div className="cc-resol-kpi-hint">Para visualizar detalhes, baixe o CSV.</div>
          </div>
        </div>
      )}

      {periodLimitOpen && (
        <div className="cc-resol-modal-backdrop" onClick={() => setPeriodLimitOpen(false)}>
          <div className="cc-resol-modal" onClick={e => e.stopPropagation()}>
            <div className="cc-resol-modal-icon">⚠️</div>
            <h3 className="cc-resol-modal-title">Período maior que {MAX_PERIOD_DAYS} dias</h3>
            <p className="cc-resol-modal-text">
              Este período não pode ser gerado automaticamente.
            </p>
            <p className="cc-resol-modal-text">
              Para períodos mais longos, solicite um relatório manual falando diretamente conosco.
            </p>
            <button className="cc-resol-modal-ok" onClick={() => setPeriodLimitOpen(false)}>
              Entendi
            </button>
          </div>
        </div>
      )}

      {loading && (
        <div className="cc-resol-loading-overlay">
          <div className="cc-resol-loading-box">
            <div className="cc-resol-spin">⟳</div>
            <div className="cc-resol-loading-title">Estamos organizando seu relatório</div>
            <div className="cc-resol-loading-sub">{progressMessage}</div>
            <div className="cc-resol-loading-sub mono" style={{ marginTop: 6, opacity: 0.7 }}>
              Pode levar de 1 a 20 min dependendo do filtro
            </div>
            <div className="cc-resol-loading-sub" style={{ marginTop: 4, opacity: 0.85 }}>
              Ao finalizar, você receberá um alerta sonoro
            </div>
          </div>
        </div>
      )}
    </section>
  );
}

// =============================================================
// gs-cfg-horario — Horário de atendimento (CRUD)
// =============================================================
// =============================================================
// gs-cfg-times — Colaborador × Times
// =============================================================
function PageAssigneeTimesReal() {
  const [users, setUsers] = useIS([]);
  const [allTeams, setAllTeams] = useIS([]);
  const [filter, setFilter] = useIS('');
  const [selected, setSelected] = useIS(null);
  const [loadingDir, setLoadingDir] = useIS(true);
  const [loadingTeams, setLoadingTeams] = useIS(false);
  const [busy, setBusy] = useIS(null);
  const [error, setError] = useIS(null);
  const [assigneeTeamIds, setAssigneeTeamIds] = useIS(new Set());

  useIE(() => {
    Promise.all([
      window.CCApi.admin.listUsers(),
      window.CCApi.admin.listTeams(),
    ]).then(([u, t]) => {
      setUsers(u.items ?? []);
      setAllTeams((t.items ?? []).sort((a, b) => (a.name || '').localeCompare(b.name || '')));
      setLoadingDir(false);
    }).catch(e => { setError(e.message); setLoadingDir(false); });
  }, []);

  const filtered = useIM(() => {
    const q = filter.trim().toLowerCase();
    if (!q) return users.slice(0, 100);
    return users.filter(u =>
      (u.email || '').toLowerCase().includes(q) ||
      (u.display_name || '').toLowerCase().includes(q) ||
      String(u.assignee_id || '').includes(q)).slice(0, 100);
  }, [users, filter]);

  async function selectUser(u) {
    setSelected(u);
    setError(null);
    setLoadingTeams(true);
    try {
      const r = await window.CCApi.admin.getAssigneeTeams(u.assignee_id);
      setAssigneeTeamIds(new Set(r.team_ids ?? []));
    } catch (e) { setError(e.message); }
    setLoadingTeams(false);
  }

  async function toggleTeam(teamId) {
    if (!selected || busy) return;
    const isIn = assigneeTeamIds.has(teamId);
    setBusy(teamId);
    setError(null);
    try {
      if (isIn) {
        await window.CCApi.admin.updateAssigneeTeams(selected.assignee_id, { remove: [teamId] });
        setAssigneeTeamIds(prev => { const n = new Set(prev); n.delete(teamId); return n; });
      } else {
        await window.CCApi.admin.updateAssigneeTeams(selected.assignee_id, { add: [teamId] });
        setAssigneeTeamIds(prev => new Set([...prev, teamId]));
      }
    } catch (e) { setError(e.message); }
    setBusy(null);
  }

  return (
    <div className="page">
      <PH eyebrow="GESTOR · CONFIGURAÇÕES" title="Colaborador × Times"
          sub="Adicione ou remova colaboradores dos times do Chatwoot." />

      {loadingDir && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loadingDir && !selected && (
        <UserPickerPlus users={filtered} onFilter={setFilter} selected={null} onSelect={selectUser} />
      )}

      {selected && (
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
            <button type="button" onClick={() => { setSelected(null); setAssigneeTeamIds(new Set()); setError(null); }}
              style={{ padding: '7px 10px', borderRadius: 7, background: 'transparent', border: 'none', color: 'rgba(255,255,255,0.55)', fontSize: 11.5, cursor: 'pointer' }}>
              ← trocar
            </button>
            <span style={{ fontSize: 13, fontWeight: 500 }}>{selected.display_name}</span>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>#{selected.assignee_id}</span>
            <span style={{ flex: 1 }} />
            <span className="mono" style={{ fontSize: 10, color: 'var(--green)' }}>{assigneeTeamIds.size} time{assigneeTeamIds.size !== 1 ? 's' : ''}</span>
          </div>

          {loadingTeams && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

          {!loadingTeams && (
            <div className="card card-topline" style={{ padding: 22 }}>
              <div className="mono" style={{ fontSize: 10, letterSpacing: '0.22em', color: 'var(--green)', marginBottom: 14 }}>/ TIMES DO CHATWOOT</div>
              {error && <div className="chip is-warn" style={{ marginBottom: 10 }}>{error}</div>}
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                {allTeams.map(t => {
                  const isIn = assigneeTeamIds.has(t.id);
                  const isLoading = busy === t.id;
                  return (
                    <button key={t.id} type="button" onClick={() => toggleTeam(t.id)}
                      disabled={isLoading}
                      style={{
                        padding: '8px 14px', borderRadius: 8, cursor: isLoading ? 'wait' : 'pointer',
                        display: 'flex', alignItems: 'center', gap: 6,
                        fontSize: 12, fontWeight: isIn ? 600 : 400,
                        background: isIn ? 'rgba(34,197,94,0.10)' : 'rgba(255,255,255,0.03)',
                        border: '1px solid ' + (isIn ? 'rgba(34,197,94,0.30)' : 'transparent'),
                        color: isIn ? 'var(--green)' : 'rgba(255,255,255,0.55)',
                        opacity: isLoading ? 0.5 : 1,
                        transition: 'all 150ms',
                      }}>
                      {isIn ? '✓' : '+'} {t.name}
                    </button>
                  );
                })}
              </div>
              {allTeams.length === 0 && <div style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12 }}>Nenhum time encontrado.</div>}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function PageHorarioReal() {
  const [users, setUsers] = useIS([]);
  const [withSched, setWithSched] = useIS(new Set());
  const [filter, setFilter] = useIS('');
  const [selected, setSelected] = useIS(null);
  const [loadingDir, setLoadingDir] = useIS(true);
  const [loadingSched, setLoadingSched] = useIS(false);
  const [saving, setSaving] = useIS(false);
  const [error, setError] = useIS(null);
  const [success, setSuccess] = useIS(null);
  const [sched, setSched] = useIS({});

  useIE(() => {
    Promise.all([
      window.CCApi.admin.listUsers(),
      window.CCApi.admin.listAssigneesWithSchedule(),
    ]).then(([u, s]) => {
      setUsers(u.items ?? []);
      setWithSched(new Set((s.items ?? []).map(i => Number(i.assignee_id))));
      setLoadingDir(false);
    }).catch(e => { setError(e.message); setLoadingDir(false); });
  }, []);

  const filtered = useIM(() => {
    const q = filter.trim().toLowerCase();
    let list = users;
    if (q) {
      list = list.filter(u =>
        (u.email || '').toLowerCase().includes(q) ||
        (u.display_name || '').toLowerCase().includes(q) ||
        String(u.assignee_id || '').includes(q));
    }
    list = list.slice().sort((a, b) => {
      const aHas = withSched.has(Number(a.assignee_id)) ? 0 : 1;
      const bHas = withSched.has(Number(b.assignee_id)) ? 0 : 1;
      return aHas - bHas;
    });
    return list.slice(0, 100);
  }, [users, filter, withSched]);

  async function selectUser(u) {
    setSelected(u);
    setSuccess(null);
    setError(null);
    setLoadingSched(true);
    try {
      const r = await window.CCApi.admin.getSchedule(u.assignee_id);
      setSched(r.schedule || {});
    } catch (e) { setError(e.message); }
    setLoadingSched(false);
  }

  function patch(field, value) {
    setSched(s => ({ ...s, [field]: value }));
  }

  async function save() {
    if (!selected) return;
    setSaving(true); setError(null); setSuccess(null);
    try {
      await window.CCApi.admin.updateSchedule(selected.assignee_id, sched);
      setSuccess('Horários atualizados com sucesso.');
    } catch (e) { setError(e.message); }
    setSaving(false);
  }

  const timeInput = (label, field) => (
    <Field label={label}>
      <input type="time" value={sched[field] || ''} onChange={e => patch(field, e.target.value)} style={inp} />
    </Field>
  );

  return (
    <div className="page">
      <PH eyebrow="GESTOR · CONFIGURAÇÕES" title="Horário de atendimento"
          sub="Edite os horários de entrada, saída, pausas e almoço dos colaboradores." />

      {loadingDir && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loadingDir && !selected && (
        <UserPickerPlus users={filtered} onFilter={setFilter} selected={null} onSelect={selectUser} />
      )}

      {selected && (
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
            <button type="button" onClick={() => { setSelected(null); setSched({}); setSuccess(null); setError(null); }}
              style={{ padding: '7px 10px', borderRadius: 7, background: 'transparent', border: 'none', color: 'rgba(255,255,255,0.55)', fontSize: 11.5, cursor: 'pointer' }}>
              ← trocar
            </button>
            <span style={{ fontSize: 13, fontWeight: 500 }}>{selected.display_name}</span>
            <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>#{selected.assignee_id} · {selected.email}</span>
          </div>

          {loadingSched && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

          {!loadingSched && (
            <div className="card card-topline" style={{ padding: 22, display: 'flex', flexDirection: 'column', gap: 14 }}>
              <div className="mono" style={{ fontSize: 10, letterSpacing: '0.22em', color: 'var(--green)' }}>/ EXPEDIENTE</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                {timeInput('Entrada', 'horario_entrada')}
                {timeInput('Saída', 'horario_saida')}
              </div>

              <div className="mono" style={{ fontSize: 10, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.5)', marginTop: 4 }}>/ ALMOÇO</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                {timeInput('Início', 'almoco_inicio')}
                {timeInput('Fim', 'almoco_fim')}
              </div>

              <div className="mono" style={{ fontSize: 10, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.5)', marginTop: 4 }}>/ PAUSA 1</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                {timeInput('Início', 'pausa_1_inicio')}
                {timeInput('Fim', 'pausa_1_fim')}
              </div>

              <div className="mono" style={{ fontSize: 10, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.5)', marginTop: 4 }}>/ PAUSA 2</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                {timeInput('Início', 'pausa_2_inicio')}
                {timeInput('Fim', 'pausa_2_fim')}
              </div>

              {error && <div className="chip is-warn">{error}</div>}
              {success && <div className="chip" style={{ color: 'var(--green)', borderColor: 'rgba(34,197,94,0.30)' }}>{success}</div>}

              <button type="button" onClick={save} disabled={saving}
                style={{
                  padding: '10px 20px', borderRadius: 8, border: 'none', cursor: saving ? 'not-allowed' : 'pointer',
                  background: 'linear-gradient(135deg, var(--green), var(--green-dark))',
                  color: '#06170d', fontWeight: 600, fontSize: 13, alignSelf: 'flex-start',
                  opacity: saving ? 0.6 : 1,
                }}>
                {saving ? 'Salvando…' : 'Salvar horários'}
              </button>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function PageComingSoonReal({ tool } = {}) {
  const t = tool || {};
  return (
    <section className="page-shell rise">
      <div className="ph-row" style={{ marginBottom: 16 }}>
        <div className="ph-ic kind-warn"><Icon name={t.icon || 'bell'} size={16} /></div>
        <div>
          <div className="ph-eyebrow mono">EM BREVE</div>
          <div className="ph-title">{t.title || 'Em desenvolvimento'}</div>
        </div>
      </div>
      <div style={{
        background: 'rgba(255,255,255,0.03)',
        border: 'none',
        borderRadius: 14,
        padding: '48px 24px',
        textAlign: 'center',
        color: 'rgba(255,255,255,0.6)',
      }}>
        <div style={{ fontSize: 42, marginBottom: 8 }}>🚧</div>
        <div style={{ fontWeight: 600, color: 'rgba(255,255,255,0.85)', fontSize: 15, marginBottom: 6 }}>
          Esta ferramenta está em desenvolvimento
        </div>
        <div style={{ fontSize: 12.5, lineHeight: 1.55 }}>
          {t.desc || 'Volte em breve para acompanhar as novidades.'}
        </div>
      </div>
    </section>
  );
}

// =============================================================
// tf-kanban — Quadro Kanban de Tarefas
// =============================================================
const KANBAN_COLS = [
  { key: 'p1',        label: 'EM DESENVOLVIMENTO',   color: 'var(--danger)' },
  { key: 'p2',        label: 'PRÓXIMOS',              color: 'var(--warning)' },
  { key: 'sem_prazo', label: 'SEM PRAZO',             color: 'rgba(255,255,255,0.45)' },
  { key: 'melhoria',  label: 'NÃO PRIORITÁRIO',      color: 'var(--info)' },
  { key: 'recorrente', label: 'RECORRENTE',           color: 'var(--green)' },
];
const KANBAN_PRIO = { low: 'Baixa', med: 'Média', high: 'Alta' };
const KANBAN_PRIO_COLOR = { low: 'var(--info)', med: 'var(--warning)', high: 'var(--danger)' };

function KanbanDatePicker({ date, time, recurrence, onChangeDate, onChangeTime, onChangeRecurrence, onClose }) {
  const today = new Date();
  const todayStr = today.toISOString().slice(0, 10);
  const selected = date || '';
  const initDate = selected ? new Date(selected + 'T12:00:00') : today;
  const [viewYear, setViewYear] = useIS(initDate.getFullYear());
  const [viewMonth, setViewMonth] = useIS(initDate.getMonth());
  const [showTime, setShowTime] = useIS(!!time);
  const [showRepeat, setShowRepeat] = useIS(!!recurrence);

  const WEEKDAYS = ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'];
  const MONTHS = ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'];

  const firstDay = new Date(viewYear, viewMonth, 1).getDay();
  const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
  const prevMonthDays = new Date(viewYear, viewMonth, 0).getDate();

  const cells = [];
  for (let i = 0; i < firstDay; i++) cells.push({ day: prevMonthDays - firstDay + 1 + i, outside: true });
  for (let d = 1; d <= daysInMonth; d++) cells.push({ day: d, outside: false });
  const remaining = 7 - (cells.length % 7);
  if (remaining < 7) for (let i = 1; i <= remaining; i++) cells.push({ day: i, outside: true });

  function cellDate(cell) {
    if (cell.outside) return null;
    return `${viewYear}-${String(viewMonth + 1).padStart(2, '0')}-${String(cell.day).padStart(2, '0')}`;
  }

  function prevMonth() { if (viewMonth === 0) { setViewYear(y => y - 1); setViewMonth(11); } else setViewMonth(m => m - 1); }
  function nextMonth() { if (viewMonth === 11) { setViewYear(y => y + 1); setViewMonth(0); } else setViewMonth(m => m + 1); }
  function goToday() { setViewYear(today.getFullYear()); setViewMonth(today.getMonth()); }

  const REPEAT_OPTIONS = [
    { label: 'Nenhuma', value: '' },
    { label: 'Diária', value: 'RRULE:FREQ=DAILY;INTERVAL=1' },
    { label: 'Dias úteis', value: 'RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR' },
    { label: 'Semanal', value: 'RRULE:FREQ=WEEKLY;INTERVAL=1' },
    { label: 'Mensal', value: 'RRULE:FREQ=MONTHLY;INTERVAL=1' },
  ];

  return (
    <div className="kdp-backdrop" onClick={onClose}>
      <div className="kdp" onClick={e => e.stopPropagation()}>

        <div className="kdp-header">
          <span style={{ fontSize: 14, fontWeight: 600 }}>{MONTHS[viewMonth]} {viewYear}</span>
          <span style={{ flex: 1 }} />
          <button type="button" onClick={prevMonth} className="kdp-nav">‹</button>
          <button type="button" onClick={goToday} className="kdp-nav" title="Hoje">○</button>
          <button type="button" onClick={nextMonth} className="kdp-nav">›</button>
        </div>

        <div className="kdp-weekdays">
          {WEEKDAYS.map((w, i) => <span key={i}>{w}</span>)}
        </div>

        <div className="kdp-grid">
          {cells.map((cell, i) => {
            const d = cellDate(cell);
            const isSelected = d === selected;
            const isToday = d === todayStr;
            return (
              <button key={i} type="button"
                className={`kdp-day ${cell.outside ? 'outside' : ''} ${isSelected ? 'selected' : ''} ${isToday ? 'today' : ''}`}
                onClick={() => { if (!cell.outside) onChangeDate(d); }}>
                {cell.day}
              </button>
            );
          })}
        </div>

        <div className="kdp-options">
          <button type="button" className="kdp-option" onClick={() => setShowTime(o => !o)}>
            <span>🕐</span> <span style={{ flex: 1 }}>Hora</span>
            <span style={{ color: 'rgba(255,255,255,0.4)' }}>{time || '—'}</span>
            <span style={{ color: 'rgba(255,255,255,0.3)' }}>›</span>
          </button>
          {showTime && (
            <div style={{ padding: '6px 16px 10px' }}>
              <input type="time" value={time || ''} onChange={e => onChangeTime(e.target.value)}
                style={{ padding: '6px 10px', borderRadius: 6, border: 'none', background: 'rgba(255,255,255,0.05)', color: '#fff', fontSize: 13, width: '100%' }} />
            </div>
          )}

          <button type="button" className="kdp-option" onClick={() => setShowRepeat(o => !o)}>
            <span>↻</span> <span style={{ flex: 1 }}>Repetir</span>
            <span style={{ color: 'rgba(255,255,255,0.4)', fontSize: 11 }}>
              {REPEAT_OPTIONS.find(r => r.value === (recurrence || ''))?.label || 'Custom'}
            </span>
            <span style={{ color: 'rgba(255,255,255,0.3)' }}>›</span>
          </button>
          {showRepeat && (
            <div style={{ padding: '4px 12px 10px', display: 'flex', flexDirection: 'column', gap: 2 }}>
              {REPEAT_OPTIONS.map(r => (
                <button key={r.value} type="button" onClick={() => onChangeRecurrence(r.value)}
                  style={{
                    padding: '6px 10px', borderRadius: 6, textAlign: 'left', cursor: 'pointer',
                    background: (recurrence || '') === r.value ? 'rgba(34,197,94,0.10)' : 'transparent',
                    border: '1px solid ' + ((recurrence || '') === r.value ? 'rgba(34,197,94,0.25)' : 'transparent'),
                    color: (recurrence || '') === r.value ? 'var(--green)' : 'rgba(255,255,255,0.7)',
                    fontSize: 12.5,
                  }}>{r.label}</button>
              ))}
            </div>
          )}
        </div>

        <div className="kdp-footer">
          <button type="button" onClick={() => { onChangeDate(''); onChangeTime(''); onChangeRecurrence(''); }}
            className="kdp-btn-clear">Limpar</button>
          <button type="button" onClick={onClose} className="kdp-btn-ok">OK</button>
        </div>
      </div>
    </div>
  );
}


function KbQuickAdd({ colKey, onCreated, onCancel }) {
  const [title, setTitle] = useIS('');
  const [prio, setPrio] = useIS('med');
  const inputRef = useIR(null);

  useIE(() => { if (inputRef.current) inputRef.current.focus(); }, []);

  async function submit() {
    if (title.trim().length < 2) return;
    try {
      await window.CCApi.tasks.create({ title: title.trim(), column_key: colKey, priority: prio });
      setTitle('');
      setPrio('med');
      onCreated();
      if (inputRef.current) inputRef.current.focus();
    } catch {}
  }

  function handleKeyDown(e) {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submit(); }
    if (e.key === 'Escape') onCancel();
  }

  return (
    <div className="kb-quick-add">
      <div className="kb-qa-main">
        <span className={`kb-qa-dot prio-${prio}`} />
        <input ref={inputRef} value={title} onChange={e => setTitle(e.target.value)}
          onKeyDown={handleKeyDown} placeholder="Nome da tarefa…" className="kb-qa-input" />
      </div>
      <div className="kb-qa-bar">
        <div className="kb-qa-prio">
          {[{ id: 'low', c: 'var(--info)' }, { id: 'med', c: 'var(--warning)' }, { id: 'high', c: 'var(--danger)' }].map(p => (
            <button key={p.id} type="button" onClick={() => setPrio(p.id)}
              className={`kb-qa-flag ${prio === p.id ? 'active' : ''}`}
              style={{ color: p.c }} title={KANBAN_PRIO[p.id]}>⚑</button>
          ))}
        </div>
        <span style={{ flex: 1 }} />
        <button type="button" className="kb-qa-cancel" onClick={onCancel}>Cancelar</button>
        <button type="button" className="kb-qa-submit" onClick={submit}
          disabled={title.trim().length < 2}>Adicionar</button>
      </div>
    </div>
  );
}

function KbDetailPanel({ task, onUpdate, onDelete, onComplete, onClose }) {
  const [title, setTitle] = useIS(task.title || '');
  const [desc, setDesc] = useIS(task.description || '');
  const [prio, setPrio] = useIS(task.priority || 'med');
  const [colKey, setColKey] = useIS(task.column_key || 'p1');
  const [progress, setProgress] = useIS(task.progress || 0);
  const [showDatePicker, setShowDatePicker] = useIS(false);
  const [dueDate, setDueDate] = useIS(task.due_date || '');
  const [dueTime, setDueTime] = useIS(task.due_time || '');
  const [recurrence, setRecurrence] = useIS(task.recurrence || '');
  const timerRef = useIR(null);

  useIE(() => {
    setTitle(task.title || '');
    setDesc(task.description || '');
    setPrio(task.priority || 'med');
    setColKey(task.column_key || 'p1');
    setProgress(task.progress || 0);
    setDueDate(task.due_date || '');
    setDueTime(task.due_time || '');
    setRecurrence(task.recurrence || '');
  }, [task.id]);

  function patch(field, value) {
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      window.CCApi.tasks.update(task.id, { [field]: value }).catch(() => {});
      onUpdate(task.id, { [field]: value });
    }, 400);
  }

  function changePrio(p) { setPrio(p); patch('priority', p); }
  function changeCol(c) {
    setColKey(c);
    window.CCApi.tasks.move(task.id, { column_key: c, position: 0 }).catch(() => {});
    onUpdate(task.id, { column_key: c });
  }
  function changeProgress(v) { setProgress(v); patch('progress', v); }

  const today = new Date().toISOString().slice(0, 10);
  const isOverdue = dueDate && dueDate < today;

  const REPEAT_LABELS = {
    '': 'Nenhuma',
    'RRULE:FREQ=DAILY;INTERVAL=1': 'Diária',
    'RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR': 'Dias úteis',
    'RRULE:FREQ=WEEKLY;INTERVAL=1': 'Semanal',
    'RRULE:FREQ=MONTHLY;INTERVAL=1': 'Mensal',
  };

  return (
    <React.Fragment>
      <div className="kb-panel-backdrop" onClick={onClose} />
      <div className="kb-panel">
        <div className="kb-panel-head">
          <button type="button" className="kb-panel-done-btn" onClick={onComplete}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
            Concluir
          </button>
          <span style={{ flex: 1 }} />
          <button type="button" className="kb-panel-x" onClick={onClose} title="Fechar">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
          </button>
        </div>

        <div className="kb-panel-body">
          <input className="kb-panel-title" value={title}
            onChange={e => { setTitle(e.target.value); patch('title', e.target.value.trim()); }}
            placeholder="Nome da tarefa" />
          <textarea className="kb-panel-desc" value={desc}
            onChange={e => { setDesc(e.target.value); patch('description', e.target.value.trim() || null); }}
            placeholder="Adicionar descrição…" />

          <div className="kb-panel-sep" />

          <div className="kb-panel-props">
            <div className="kb-panel-row">
              <span className="kb-panel-label">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
                Coluna
              </span>
              <div className="kb-panel-pills">
                {KANBAN_COLS.map(c => (
                  <button key={c.key} type="button"
                    className={`kb-pill ${colKey === c.key ? 'active' : ''}`}
                    onClick={() => changeCol(c.key)}>
                    <span className="kb-pill-dot" style={{ background: c.color }} />
                    {c.label}
                  </button>
                ))}
              </div>
            </div>

            <div className="kb-panel-row">
              <span className="kb-panel-label">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/></svg>
                Prioridade
              </span>
              <div className="kb-panel-prio">
                {[{ id: 'low', c: 'var(--info)', l: 'Baixa', abg: 'rgba(59,130,246,0.08)', abc: 'rgba(59,130,246,0.25)' },
                  { id: 'med', c: 'var(--warning)', l: 'Média', abg: 'rgba(245,158,11,0.08)', abc: 'rgba(245,158,11,0.25)' },
                  { id: 'high', c: 'var(--danger)', l: 'Alta', abg: 'rgba(239,68,68,0.08)', abc: 'rgba(239,68,68,0.25)' }
                ].map(p => (
                  <button key={p.id} type="button"
                    className={`kb-prio-opt ${prio === p.id ? 'active' : ''}`}
                    style={prio === p.id ? { background: p.abg, borderColor: p.abc, color: p.c, fontWeight: 600 } : {}}
                    onClick={() => changePrio(p.id)}>
                    ⚑ {p.l}
                  </button>
                ))}
              </div>
            </div>

            <div className="kb-panel-row">
              <span className="kb-panel-label">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
                Data
              </span>
              <button type="button" className={`kb-date-btn ${dueDate ? (isOverdue ? 'is-overdue' : 'has-date') : ''}`}
                onClick={() => setShowDatePicker(true)}>
                {dueDate ? (
                  <React.Fragment>
                    {dueDate.slice(8)}/{dueDate.slice(5, 7)}
                    {dueTime ? ` · ${dueTime}` : ''}
                    {recurrence ? ` · ↻ ${REPEAT_LABELS[recurrence] || 'Custom'}` : ''}
                  </React.Fragment>
                ) : 'Definir data'}
              </button>
            </div>

            <div className="kb-panel-row">
              <span className="kb-panel-label">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="20" x2="12" y2="10"/><line x1="18" y1="20" x2="18" y2="4"/><line x1="6" y1="20" x2="6" y2="16"/></svg>
                Progresso
              </span>
              <div className="kb-panel-progress">
                <input type="range" min="0" max="100" step="10" value={progress}
                  onChange={e => changeProgress(Number(e.target.value))}
                  className="kb-slider" />
                <span className="kb-panel-pct" style={{
                  color: progress >= 100 ? 'var(--green)' : progress >= 50 ? 'var(--warning)' : 'var(--info)',
                }}>{progress}%</span>
              </div>
            </div>

            <div className="kb-panel-row">
              <span className="kb-panel-label">
                {task.hidden
                  ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
                  : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
                }
                Visibilidade
              </span>
              <button type="button" className={`kb-vis-btn ${task.hidden ? 'is-hidden' : ''}`}
                onClick={() => {
                  window.CCApi.tasks.update(task.id, { hidden: !task.hidden });
                  onUpdate(task.id, { hidden: !task.hidden });
                }}>
                {task.hidden ? 'Oculto — clique para mostrar' : 'Visível — clique para ocultar'}
              </button>
            </div>
          </div>

          <div className="kb-panel-sep" />

          <button type="button" className="kb-panel-del" onClick={onDelete}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
            Excluir tarefa
          </button>
        </div>
      </div>

      {showDatePicker && (
        <KanbanDatePicker
          date={dueDate} time={dueTime} recurrence={recurrence}
          onChangeDate={d => { setDueDate(d); patch('due_date', d || null); }}
          onChangeTime={t => { setDueTime(t); patch('due_time', t || null); }}
          onChangeRecurrence={r => { setRecurrence(r); patch('recurrence', r || null); }}
          onClose={() => setShowDatePicker(false)}
        />
      )}
    </React.Fragment>
  );
}

function PageKanbanReal() {
  const [tasks, setTasks] = useIS([]);
  const [loading, setLoading] = useIS(true);
  const [dragId, setDragId] = useIS(null);
  const [selectedTask, setSelectedTask] = useIS(null);
  const [addingInCol, setAddingInCol] = useIS(null);
  const [collapsedCols, setCollapsedCols] = useIS({});

  async function load() {
    setLoading(true);
    try {
      const r = await window.CCApi.tasks.list();
      setTasks(r.items ?? []);
    } catch {}
    setLoading(false);
  }
  useIE(() => { load(); }, []);

  function onDragStart(e, task) {
    setDragId(task.id);
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', String(task.id));
  }
  function onDragEnd() { setDragId(null); }

  async function onDrop(e, colKey) {
    e.preventDefault();
    const id = Number(e.dataTransfer.getData('text/plain'));
    const task = tasks.find(t => t.id === id);
    if (!task || task.column_key === colKey) { setDragId(null); return; }
    setTasks(prev => prev.map(t => t.id === id ? { ...t, column_key: colKey } : t));
    setSelectedTask(prev => prev && prev.id === id ? { ...prev, column_key: colKey } : prev);
    setDragId(null);
    try {
      const colTasks = tasks.filter(t => t.column_key === colKey);
      await window.CCApi.tasks.move(id, { column_key: colKey, position: colTasks.length });
    } catch { load(); }
  }

  async function completeTask(task) {
    const ok = await window.CCConfirm({
      title: 'Concluir tarefa',
      message: `Marcar "${task.title}" como concluída?`,
      confirmLabel: 'Concluir',
      cancelLabel: 'Cancelar',
      tone: 'primary',
    });
    if (!ok) return;
    await window.CCApi.tasks.update(task.id, { completed: true });
    setSelectedTask(null);
    load();
  }

  async function deleteTask(id) {
    const ok = await window.CCConfirm({
      title: 'Excluir tarefa',
      message: 'Tem certeza que deseja excluir esta tarefa?',
      confirmLabel: 'Excluir',
      cancelLabel: 'Cancelar',
      tone: 'danger',
    });
    if (!ok) return;
    try {
      await window.CCApi.tasks.remove(id);
      setSelectedTask(null);
      load();
    } catch {}
  }

  function toggleCollapse(colKey) {
    setCollapsedCols(prev => ({ ...prev, [colKey]: !prev[colKey] }));
  }

  const today = new Date().toISOString().slice(0, 10);

  return (
    <div className="page">
      <div className="kb-page-head">
        <div className="kb-page-head-left">
          <div className="kb-page-icon">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--green)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
          </div>
          <div>
            <h1 className="kb-page-title">Conexão Green</h1>
            <span className="kb-page-sub">{tasks.length} tarefa{tasks.length !== 1 ? 's' : ''}</span>
          </div>
        </div>
        <button type="button" className="kb-refresh" onClick={load} disabled={loading}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
          {loading ? 'Atualizando…' : 'Atualizar'}
        </button>
      </div>

      {loading && (
        <div className="kb-loading">
          <span className="dot-pulse" />
          <span>Carregando tarefas…</span>
        </div>
      )}

      {!loading && (
        <div className="kb-board">
          {KANBAN_COLS.map(col => {
            const colTasks = tasks.filter(t => t.column_key === col.key).sort((a, b) => {
              if (a.due_date && b.due_date) return b.due_date.localeCompare(a.due_date);
              if (a.due_date && !b.due_date) return -1;
              if (!a.due_date && b.due_date) return 1;
              return a.position - b.position;
            });
            const isCollapsed = collapsedCols[col.key];

            if (isCollapsed) {
              return (
                <div key={col.key} className="kb-col kb-col--collapsed"
                  onClick={() => toggleCollapse(col.key)} title={`${col.label} (${colTasks.length})`}>
                  <div className="kb-col-accent-bar" style={{ background: col.color }} />
                  <span className="kb-col-c-count">{colTasks.length}</span>
                  <span className="kb-col-c-label">{col.label}</span>
                </div>
              );
            }

            return (
              <div key={col.key} className="kb-col">
                <div className="kb-col-head">
                  <div className="kb-col-accent-bar" style={{ background: col.color }} />
                  <div className="kb-col-head-row">
                    <button type="button" className="kb-col-toggle" onClick={() => toggleCollapse(col.key)} title="Recolher">
                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
                    </button>
                    <span className="kb-col-name" style={{ color: col.color }}>{col.label}</span>
                    <span className="kb-col-badge">{colTasks.length}</span>
                    <span style={{ flex: 1 }} />
                    <button type="button" className="kb-col-add-btn" title="Nova tarefa"
                      onClick={() => setAddingInCol(addingInCol === col.key ? null : col.key)}>
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                    </button>
                  </div>
                </div>

                <div className="kb-col-body"
                  onDragOver={e => { e.preventDefault(); e.currentTarget.classList.add('is-drag-over'); }}
                  onDragEnter={e => { e.currentTarget.classList.add('is-drag-over'); }}
                  onDragLeave={e => { e.currentTarget.classList.remove('is-drag-over'); }}
                  onDrop={e => { e.currentTarget.classList.remove('is-drag-over'); onDrop(e, col.key); }}>

                  {colTasks.map(t => {
                    const overdue = t.due_date && t.due_date < today;
                    return (
                      <div key={t.id}
                        className={`kb-card ${dragId === t.id ? 'is-dragging' : ''} ${selectedTask && selectedTask.id === t.id ? 'is-selected' : ''}`}
                        draggable="true"
                        onDragStart={e => onDragStart(e, t)}
                        onDragEnd={onDragEnd}
                        onClick={() => setSelectedTask(t)}>

                        <div className="kb-card-row">
                          <button type="button" className={`kb-check prio-${t.priority}`}
                            title="Concluir" onClick={ev => { ev.stopPropagation(); completeTask(t); }}>
                            <svg className="kb-check-icon" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                          </button>
                          <div className="kb-card-text">
                            <span className="kb-card-title">{t.title}</span>
                            {t.description && <span className="kb-card-desc">{t.description}</span>}
                          </div>
                          <button type="button"
                            className={`kb-eye ${t.hidden ? 'is-hidden' : ''}`}
                            title={t.hidden ? 'Oculto' : 'Visível'}
                            onClick={ev => { ev.stopPropagation(); window.CCApi.tasks.update(t.id, { hidden: !t.hidden }).then(load); }}>
                            {t.hidden
                              ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
                              : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
                            }
                          </button>
                        </div>

                        <div className="kb-card-chips">
                          <span className={`kb-chip kb-chip--prio prio-${t.priority}`}>
                            <span className="kb-chip-dot" style={{ background: KANBAN_PRIO_COLOR[t.priority] }} />
                            {KANBAN_PRIO[t.priority]}
                          </span>
                          {t.due_date && (
                            <span className={`kb-chip ${overdue ? 'kb-chip--overdue' : 'kb-chip--date'}`}>
                              {overdue && '⚠ '}{t.due_date.slice(8)}/{t.due_date.slice(5, 7)}
                              {t.due_time ? ` ${t.due_time}` : ''}
                            </span>
                          )}
                          {t.recurrence && <span className="kb-chip kb-chip--recur">↻</span>}
                        </div>

                        <div className="kb-card-progress" onClick={ev => {
                          ev.stopPropagation();
                          const rect = ev.currentTarget.getBoundingClientRect();
                          const pct = Math.round(((ev.clientX - rect.left) / rect.width) * 10) * 10;
                          const clamped = Math.max(0, Math.min(100, pct));
                          setTasks(prev => prev.map(x => x.id === t.id ? { ...x, progress: clamped } : x));
                          window.CCApi.tasks.update(t.id, { progress: clamped });
                        }}>
                          <div className="kb-progress-track">
                            <div className="kb-progress-fill" style={{
                              width: `${t.progress || 0}%`,
                              background: (t.progress || 0) >= 100 ? 'var(--green)' : (t.progress || 0) >= 50 ? 'var(--warning)' : 'var(--info)',
                            }} />
                          </div>
                          <span className="kb-progress-pct">{t.progress || 0}%</span>
                        </div>
                      </div>
                    );
                  })}

                  {colTasks.length === 0 && addingInCol !== col.key && (
                    <div className="kb-empty">Arraste tarefas aqui</div>
                  )}

                  {addingInCol === col.key && (
                    <KbQuickAdd colKey={col.key} onCreated={load} onCancel={() => setAddingInCol(null)} />
                  )}

                  {addingInCol !== col.key && (
                    <button type="button" className="kb-add-inline" onClick={() => setAddingInCol(col.key)}>
                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                      Adicionar tarefa
                    </button>
                  )}
                </div>
              </div>
            );
          })}
        </div>
      )}

      {selectedTask && selectedTask.id && (
        <KbDetailPanel
          task={selectedTask}
          onUpdate={(id, p) => {
            setTasks(prev => prev.map(t => t.id === id ? { ...t, ...p } : t));
            setSelectedTask(prev => prev ? { ...prev, ...p } : prev);
          }}
          onDelete={() => deleteTask(selectedTask.id)}
          onComplete={() => completeTask(selectedTask)}
          onClose={() => setSelectedTask(null)}
        />
      )}
    </div>
  );
}


// =============================================================
// tk-dev — Acompanhar Desenvolvimento (tasks da coluna p1)
// =============================================================
function PageDevReal() {
  const [tasks, setTasks] = useIS([]);
  const [done, setDone] = useIS([]);
  const [loading, setLoading] = useIS(true);

  async function load() {
    setLoading(true);
    try {
      const [r, c] = await Promise.all([
        window.CCApi.tasks.list({ column_key: 'p1', visible: '1' }),
        window.CCApi.tasks.completed(20),
      ]);
      setTasks(r.items ?? []);
      const completed = c.items ?? [];
      const todayStr = new Date().toISOString().slice(0, 10);
      const todayItems = completed.filter(t => t.completed_at && t.completed_at.slice(0, 10) === todayStr);
      const show = todayItems.length > 3 ? todayItems : completed.slice(0, 3);
      setDone(show);
    } catch {}
    setLoading(false);
  }
  useIE(() => { load(); }, []);

  const today = new Date().toISOString().slice(0, 10);

  return (
    <div className="page">
      <PH eyebrow="TIME TECH & IA" title="Acompanhar Desenvolvimento"
          sub="O que o time está construindo agora."
          right={<button onClick={load} disabled={loading} style={{
            padding: '6px 14px', borderRadius: 6, background: 'rgba(255,255,255,0.04)',
            border: 'none', color: 'rgba(255,255,255,0.7)',
            fontSize: 12, cursor: 'pointer',
          }}>{loading ? '…' : 'Atualizar'}</button>} />

      {loading && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loading && tasks.length === 0 && (
        <div className="card" style={{ padding: 24, color: 'rgba(255,255,255,0.5)', fontSize: 13 }}>
          Nenhuma tarefa em desenvolvimento no momento.
        </div>
      )}

      {!loading && tasks.length > 0 && (
        <div className="card card-topline" style={{ padding: 18 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
            <span className="mono" style={{ color: 'var(--green)', fontSize: 11 }}>01</span>
            <h2 style={{ margin: 0, fontSize: 14, fontWeight: 500 }}>O que o time está construindo</h2>
            <div style={{ flex: 1, height: 1, background: 'rgba(255,255,255,0.06)' }} />
            <span className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.4)' }}>
              {tasks.length} {tasks.length === 1 ? 'ATIVO' : 'ATIVOS'}
            </span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 12 }}>
            {tasks.map(t => {
              const overdue = t.due_date && t.due_date < today;
              const prioColor = KANBAN_PRIO_COLOR[t.priority] || 'rgba(255,255,255,0.4)';
              return (
                <div key={t.id} style={{
                  padding: 14, borderRadius: 10,
                  background: 'rgba(255,255,255,0.02)',
                  border: 'none',
                  borderLeft: `3px solid ${prioColor}`,
                }}>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
                    <span className="mono" style={{ fontSize: 9.5, color: 'var(--green)', letterSpacing: '0.22em' }}>TASK-{t.id}</span>
                    <span className="chip" style={{ fontSize: 9.5 }}>
                      <span style={{ color: prioColor }}>●</span> {KANBAN_PRIO[t.priority] || t.priority}
                    </span>
                  </div>
                  <div style={{ fontSize: 13, fontWeight: 500, marginTop: 6, lineHeight: 1.35 }}>{t.title}</div>
                  {t.description && (
                    <div style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.55)', marginTop: 4, lineHeight: 1.5,
                      display: '-webkit-box', WebkitLineClamp: 3, WebkitBoxOrient: 'vertical', overflow: 'hidden',
                    }}>{t.description}</div>
                  )}
                  {t.due_date && (
                    <div className="mono" style={{
                      fontSize: 9.5, letterSpacing: '0.22em', marginTop: 8,
                      color: overdue ? 'var(--danger)' : 'rgba(255,255,255,0.42)',
                    }}>
                      {overdue ? '⚠ ATRASADO · ' : 'PRAZO · '}{t.due_date.slice(8) + '/' + t.due_date.slice(5, 7)}
                    </div>
                  )}
                  {!t.due_date && (
                    <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', marginTop: 8, color: 'rgba(255,255,255,0.3)' }}>
                      SEM PRAZO DEFINIDO
                    </div>
                  )}
                  <div style={{ marginTop: 10, display: 'flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ flex: 1, height: 4, borderRadius: 999, background: 'rgba(255,255,255,0.06)', overflow: 'hidden' }}>
                      <div style={{
                        width: `${t.progress || 0}%`, height: '100%', borderRadius: 999,
                        background: (t.progress || 0) >= 100 ? 'var(--green)' : (t.progress || 0) >= 50 ? 'var(--warning)' : 'var(--info)',
                        transition: 'width 150ms',
                      }} />
                    </div>
                    <span className="mono" style={{ fontSize: 10, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.18em' }}>{t.progress || 0}%</span>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {!loading && done.length > 0 && (
        <div className="card" style={{ padding: 18 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
            <span style={{ color: 'var(--green)', fontSize: 13 }}>✓</span>
            <h2 style={{ margin: 0, fontSize: 14, fontWeight: 500 }}>Entregas recentes</h2>
            <div style={{ flex: 1, height: 1, background: 'rgba(255,255,255,0.06)' }} />
            <span className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.4)' }}>
              {done.length} CONCLUÍDA{done.length === 1 ? '' : 'S'}
            </span>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {done.map(t => (
              <div key={t.id} style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '9px 12px', borderRadius: 8,
                background: 'rgba(34,197,94,0.04)',
                border: 'none',
              }}>
                <span style={{
                  width: 20, height: 20, borderRadius: '50%', flexShrink: 0,
                  background: 'var(--green)', display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 11, color: '#06170d', fontWeight: 700,
                }}>✓</span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 500, textDecoration: 'line-through', color: 'rgba(255,255,255,0.55)' }}>{t.title}</div>
                </div>
                <span className="mono" style={{ fontSize: 9.5, letterSpacing: '0.18em', color: 'rgba(255,255,255,0.35)', flexShrink: 0 }}>
                  {t.completed_at ? t.completed_at.slice(8, 10) + '/' + t.completed_at.slice(5, 7) : ''}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// =============================================================
// tf-tickets — Aprovação de Tickets (kanban triagem)
// =============================================================
const TICKET_COLS = [
  { key: 'triage',   label: 'NOVOS',      color: 'var(--danger)' },
  { key: 'progress', label: 'ANALISANDO', color: 'var(--warning)' },
];

function PageTicketTriageReal() {
  const [tickets, setTickets] = useIS([]);
  const [loading, setLoading] = useIS(true);
  const [selected, setSelected] = useIS(null);
  const [dragId, setDragId] = useIS(null);

  async function load() {
    setLoading(true);
    try {
      const r = await window.CCApi.support.listTickets({ scope: 'all' });
      const active = (r.items ?? []).filter(t => t.status !== 'done' && t.status !== 'resolved');
      setTickets(active);
    } catch {}
    setLoading(false);
  }
  useIE(() => { load(); }, []);

  function mapCol(status) {
    if (status === 'triage') return 'triage';
    return 'progress';
  }

  async function moveTicket(id, newCol) {
    const newStatus = newCol === 'triage' ? 'triage' : 'progress';
    setTickets(prev => prev.map(t => t.id === id ? { ...t, status: newStatus } : t));
    try { await window.CCApi.support.updateTicket(id, { status: newStatus }); } catch { load(); }
  }

  async function concludeTicket(ticket, response, createTask, opts = {}) {
    try {
      if (response.trim()) {
        await window.CCApi.support.addComment(ticket.id, response.trim());
      }
      await window.CCApi.support.updateTicket(ticket.id, { status: 'done' });
      if (createTask) {
        await window.CCApi.tasks.create({
          title: ticket.title,
          description: ticket.description || '',
          column_key: opts.column_key || 'p1',
          priority: opts.priority || 'med',
          due_date: opts.due_date || null,
        });
      }
      setSelected(null);
      load();
    } catch {}
  }

  return (
    <div className="page">
      <PH eyebrow="TASKS.DEV" title="Aprovação de Tickets"
          sub="Triagem dos tickets. Analise, responda e converta em tarefa quando necessário."
          right={<button onClick={load} disabled={loading} style={{
            padding: '6px 14px', borderRadius: 6, background: 'rgba(255,255,255,0.04)',
            border: 'none', color: 'rgba(255,255,255,0.7)',
            fontSize: 12, cursor: 'pointer',
          }}>{loading ? '…' : 'Atualizar'}</button>} />

      {loading && <div style={{display:"flex",alignItems:"center",justifyContent:"center",padding:"40px 0",gap:10}}><span className="dot-pulse"/><span style={{color:"rgba(255,255,255,0.35)",fontSize:12}}>Carregando</span></div>}

      {!loading && <div className="kanban-board">
        {TICKET_COLS.map(col => {
          const colTickets = tickets.filter(t => mapCol(t.status) === col.key);
          return (
            <div key={col.key} className="kanban-col">
              <div className="kanban-col-head">
                <span className="mono kanban-col-label" style={{ color: col.color }}>/ {col.label}</span>
                <span className="mono kanban-col-count">{colTickets.length}</span>
              </div>
              <div className="kanban-col-body"
                onDragOver={(e) => { e.preventDefault(); e.currentTarget.classList.add('is-drag-over'); }}
                onDragEnter={(e) => { e.currentTarget.classList.add('is-drag-over'); }}
                onDragLeave={(e) => { e.currentTarget.classList.remove('is-drag-over'); }}
                onDrop={(e) => {
                  e.currentTarget.classList.remove('is-drag-over');
                  e.preventDefault();
                  const id = Number(e.dataTransfer.getData('text/plain'));
                  if (id) moveTicket(id, col.key);
                  setDragId(null);
                }}>
                {colTickets.map(t => {
                  const prioColor = t.priority === 'high' ? 'var(--danger)' : t.priority === 'med' ? 'var(--warning)' : 'var(--info)';
                  return (
                    <div key={t.id}
                      className={`kanban-card prio-${t.priority || 'low'} ${dragId === t.id ? 'is-dragging' : ''}`}
                      draggable="true"
                      onDragStart={(e) => { setDragId(t.id); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', String(t.id)); }}
                      onDragEnd={() => setDragId(null)}
                      onClick={() => setSelected(t)}>
                      <div className="kanban-card-title">{t.title}</div>
                      {t.description && <div className="kanban-card-desc">{t.description}</div>}
                      <div className="kanban-card-meta">
                        <span className="mono kanban-card-chip">{t.code}</span>
                        <span className="mono kanban-card-chip" style={{ color: prioColor }}>
                          ● {t.priority === 'high' ? 'Alta' : t.priority === 'med' ? 'Média' : 'Baixa'}
                        </span>
                        {t.requester_name && (
                          <span className="mono kanban-card-chip">{t.requester_name}</span>
                        )}
                      </div>
                    </div>
                  );
                })}
                {colTickets.length === 0 && !loading && (
                  <div style={{ padding: 16, color: 'rgba(255,255,255,0.3)', fontSize: 12, textAlign: 'center' }}>
                    {col.key === 'triage' ? 'Nenhum ticket novo' : 'Nenhum em análise'}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>}

      {selected && (
        <TicketConcludeModal ticket={selected} onClose={() => setSelected(null)} onConclude={concludeTicket} />
      )}
    </div>
  );
}

const TRIAGE_PRESETS = {
  high: {
    col: 'p1',
    response: 'Recebemos seu ticket e ele foi classificado como prioridade alta. Vamos resolver hoje.',
    responseNoDate: null,
    responseWithDate: null,
    deadlineToday: true,
  },
  med: {
    col: 'p2',
    responseNoDate: 'Recebemos seu ticket e ele foi classificado como prioridade média. Vamos definir uma data de entrega em breve.',
    responseWithDate: null,
    deadlineToday: false,
  },
  low: {
    col: 'sem_prazo',
    response: 'Recebemos seu ticket e ele foi classificado como prioridade baixa. Ele foi adicionado à nossa fila de desenvolvimento.',
    responseNoDate: null,
    responseWithDate: null,
    deadlineToday: false,
  },
};

function TicketConcludeModal({ ticket, onClose, onConclude }) {
  const [prio, setPrio] = useIS(null);
  const [colKey, setColKey] = useIS('p1');
  const [response, setResponse] = useIS('');
  const [dueDate, setDueDate] = useIS('');
  const [createTask, setCreateTask] = useIS(true);
  const [submitting, setSubmitting] = useIS(false);
  const [edited, setEdited] = useIS(false);

  function formatDateBR(d) {
    if (!d) return '';
    const parts = d.split('-');
    return `${parts[2]}/${parts[1]}/${parts[0]}`;
  }

  function buildResponse(p, date) {
    const preset = TRIAGE_PRESETS[p];
    if (!preset) return '';
    if (p === 'high') return preset.response;
    if (p === 'low') return preset.response;
    if (date) return `Recebemos seu ticket e ele foi classificado como prioridade média. Essa tarefa ficará estimada para ${formatDateBR(date)}.`;
    return preset.responseNoDate;
  }

  function selectPrio(p) {
    setPrio(p);
    const preset = TRIAGE_PRESETS[p];
    setColKey(preset.col);
    if (preset.deadlineToday) {
      const today = new Date().toISOString().slice(0, 10);
      setDueDate(today);
      if (!edited) setResponse(buildResponse(p, today));
    } else if (p === 'low') {
      setDueDate('');
      if (!edited) setResponse(buildResponse(p, ''));
    } else {
      setDueDate('');
      if (!edited) setResponse(buildResponse(p, ''));
    }
  }

  function handleDateChange(newDate) {
    setDueDate(newDate);
    if (prio === 'med' && !edited) {
      setResponse(buildResponse('med', newDate));
    }
  }

  const canSubmit = prio && response.trim().length >= 3;

  async function submit() {
    if (!canSubmit) return;
    setSubmitting(true);
    await onConclude(ticket, response, createTask, { priority: prio, column_key: colKey, due_date: dueDate || null });
    setSubmitting(false);
  }

  const prioOptions = [
    { id: 'high', label: 'Alta', sub: 'Resolver hoje', color: 'var(--danger)', icon: '⚡' },
    { id: 'med',  label: 'Média', sub: 'Próximos dias', color: 'var(--warning)', icon: '◐' },
    { id: 'low',  label: 'Baixa', sub: 'Sem prazo', color: 'var(--info)', icon: '○' },
  ];

  const typeLabel = { bug: 'Bug', feature: 'Sugestão', melhoria_ia: 'Melhoria IA', investigacao: 'Investigação', outro: 'Outro' };

  return (
    <div className="kanban-modal-backdrop" onClick={onClose}>
      <div className="kanban-detail" style={{ width: 560 }} onClick={(e) => e.stopPropagation()}>

        <div className="kanban-detail-topbar">
          <span className="mono" style={{ fontSize: 10, color: 'var(--green)', letterSpacing: '0.22em' }}>{ticket.code}</span>
          <span style={{ flex: 1 }} />
          <span className="chip" style={{ fontSize: 10 }}>{typeLabel[ticket.type] || ticket.type}</span>
          <button type="button" onClick={onClose} style={{ background: 'none', border: 'none', color: 'rgba(255,255,255,0.4)', fontSize: 20, cursor: 'pointer' }}>×</button>
        </div>

        <div className="kanban-detail-body" style={{ gap: 0 }}>
          <div style={{ fontSize: 18, fontWeight: 600, lineHeight: 1.3 }}>{ticket.title}</div>
          {ticket.description && (
            <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.55)', lineHeight: 1.6, marginTop: 8, whiteSpace: 'pre-wrap' }}>{ticket.description}</div>
          )}
          <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.35)', marginTop: 10, marginBottom: 20 }}>
            Aberto por {ticket.requester_name || ticket.requester_email || '—'} · {ticket.created_at?.slice(0, 10)}
          </div>

          <div style={{ borderTop: '1px solid rgba(255,255,255,0.06)', paddingTop: 18 }}>
            <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.45)', marginBottom: 10 }}>
              CLASSIFICAR PRIORIDADE
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
              {prioOptions.map(p => (
                <button key={p.id} type="button" onClick={() => selectPrio(p.id)}
                  style={{
                    padding: '12px 10px', borderRadius: 10, cursor: 'pointer',
                    background: prio === p.id ? 'rgba(255,255,255,0.04)' : 'transparent',
                    border: '2px solid ' + (prio === p.id ? p.color : 'transparent'),
                    display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
                    transition: 'all 150ms',
                  }}>
                  <span style={{ fontSize: 20 }}>{p.icon}</span>
                  <span style={{ fontSize: 13, fontWeight: 600, color: prio === p.id ? p.color : 'rgba(255,255,255,0.75)' }}>{p.label}</span>
                  <span style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>{p.sub}</span>
                </button>
              ))}
            </div>
          </div>

          {prio && (
            <div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 14, animation: 'fadein 200ms ease' }}>

              <div style={{ display: 'flex', gap: 10, alignItems: 'flex-end' }}>
                <div style={{ flex: 1 }}>
                  <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.45)', marginBottom: 6 }}>COLUNA NO KANBAN</div>
                  <select value={colKey} onChange={e => setColKey(e.target.value)} style={{
                    width: '100%', padding: '8px 10px', borderRadius: 6, border: 'none',
                    background: 'rgba(255,255,255,0.04)', color: '#fff', fontSize: 12, outline: 'none',
                  }}>
                    {KANBAN_COLS.map(c => <option key={c.key} value={c.key}>{c.label}</option>)}
                  </select>
                </div>
                {prio !== 'low' && (
                  <div style={{ flex: 1 }}>
                    <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.45)', marginBottom: 6 }}>
                      {prio === 'high' ? 'PRAZO (HOJE)' : 'PRAZO (OPCIONAL)'}
                    </div>
                    <input type="date" value={dueDate} onChange={e => handleDateChange(e.target.value)}
                      style={{
                        width: '100%', padding: '8px 10px', borderRadius: 6, border: 'none',
                        background: 'rgba(255,255,255,0.04)', color: '#fff', fontSize: 12, outline: 'none',
                      }} />
                  </div>
                )}
              </div>

              <div>
                <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.22em', color: 'rgba(255,255,255,0.45)', marginBottom: 6 }}>
                  RESPOSTA AO SOLICITANTE
                </div>
                <textarea value={response} onChange={e => { setResponse(e.target.value); setEdited(true); }}
                  placeholder="Escreva a resposta..."
                  style={{
                    width: '100%', minHeight: 80, padding: '10px 12px', borderRadius: 8,
                    border: 'none', background: 'rgba(255,255,255,0.04)',
                    color: '#fff', fontSize: 13, lineHeight: 1.6, resize: 'vertical', outline: 'none',
                  }} />
              </div>

              <label style={{
                display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer',
                padding: '10px 12px', borderRadius: 8,
                background: createTask ? 'rgba(34,197,94,0.06)' : 'rgba(255,255,255,0.02)',
                border: '1px solid ' + (createTask ? 'rgba(34,197,94,0.20)' : 'transparent'),
              }}>
                <input type="checkbox" checked={createTask} onChange={e => setCreateTask(e.target.checked)}
                  style={{ accentColor: 'var(--green)', width: 16, height: 16 }} />
                <div>
                  <div style={{ fontSize: 12.5, fontWeight: 500, color: createTask ? 'var(--green)' : 'rgba(255,255,255,0.7)' }}>
                    Converter em tarefa no Conexão Green
                  </div>
                  <div style={{ fontSize: 10.5, color: 'rgba(255,255,255,0.4)', marginTop: 1 }}>
                    Vai pra coluna "{KANBAN_COLS.find(c => c.key === colKey)?.label || colKey}" com prioridade {KANBAN_PRIO[prio]}
                  </div>
                </div>
              </label>
            </div>
          )}
        </div>

        <div className="kanban-detail-footer">
          <button type="button" onClick={onClose} style={{
            padding: '8px 14px', borderRadius: 6, background: 'rgba(255,255,255,0.04)',
            border: 'none', color: 'rgba(255,255,255,0.6)',
            fontSize: 12, cursor: 'pointer',
          }}>Cancelar</button>
          <span style={{ flex: 1 }} />
          <button type="button" onClick={submit} disabled={submitting || !canSubmit} style={{
            padding: '8px 18px', borderRadius: 6,
            background: canSubmit ? 'linear-gradient(135deg, var(--green), var(--green-dark))' : 'rgba(255,255,255,0.05)',
            border: 'none', color: canSubmit ? '#06170d' : 'rgba(255,255,255,0.3)',
            fontWeight: 600, fontSize: 12, cursor: canSubmit ? 'pointer' : 'not-allowed',
          }}>{submitting ? 'Concluindo…' : 'Concluir ticket'}</button>
        </div>

      </div>
    </div>
  );
}

// =============================================================
// Pages registry (separate from window.PAGES — ToolPage in app.jsx
// checks PAGE_OVERRIDES first so this can't be wiped by re-execution
// order of pages.jsx). Bumped via console.log so we can verify load.
// =============================================================
window.PAGE_OVERRIDES = {
  'at-conv-origem': PageConvOrigemReal,
  'at-backlog':     PageBacklogReal,
  'gs-cad-criar':   PageCriarReal,
  'gs-cad-reset':   PageResetReal,
  'gs-cad-role':    PageRoleReal,
  'gs-cad-nivel':   PageNivelReal,
  'gs-cad-desligar': PageDesligarReal,
  'gs-fam-col':     PageFamColReal,
  'gs-fam-time':    PageFamTimeReal,
  'gs-fam-forn':    PageFamFornReal,
  'gs-fam-cat':     PageFamCatReal,
  'gs-atrb-relacao': PageAtribuicaoReal,
  'gs-link-sac':    PageLinkSACReal,
  'gs-alert-prod':  PageComingSoonReal,
  'gs-alert-crit':  PageComingSoonReal,
  'gs-rel-resol':   PageRelResolReal,
  'gs-cfg-horario': PageHorarioReal,
  'gs-cfg-times':   PageAssigneeTimesReal,
  'tf-kanban':      PageKanbanReal,
  'tk-dev':         PageDevReal,
  'tf-tickets':     PageTicketTriageReal,
};
console.log('[central] pages-impl loaded — overrides:', Object.keys(window.PAGE_OVERRIDES).join(', '));
