/* ================================================================
   Khronus — Agenda (calendário unificado)
   Tudo dentro de uma IIFE: os .jsx do Khronus são carregados como
   script global no navegador (sem bundler), então dois arquivos
   declarando a mesma const no escopo de topo derrubam o app inteiro.
   Isolar aqui dentro evita esse problema pra sempre neste arquivo.

   Fase 3: modo Mês + filtros (fonte, pessoa, categoria) + clique num
   item derivado leva pro módulo de origem.
   Fase 4: eventos próprios — criar, editar, recorrência (guarda uma
   linha só e expande na exibição) e arrastar pra remarcar.
   Fase 5: tarefas em modo Lista e modo Kanban (Fazer/Fazendo/Feito).
   ================================================================ */
(function () {
  const { useState, useEffect, useMemo } = React;
  const { LucideIcon, Btn, Badge, TopBar, IniciaisAvatar, useEscapeToClose, AVATAR_POR_NOME, AVATAR_COMUM, PrazoBadge, EmpresaDot } = window;

  // Mesmo padrão de cor por coluna do Kanban do Orgânico (organico.jsx COLUMNS).
  const COLUNAS_TAREFA = [
    { id: 'Fazer',   label: 'Fazer',   colorDot: '#94a3b8', colorBg: 'rgba(148,163,184,.06)', colorBorder: 'rgba(148,163,184,.22)' },
    { id: 'Fazendo', label: 'Fazendo', colorDot: '#38bdf8', colorBg: 'rgba(56,189,248,.06)',   colorBorder: 'rgba(56,189,248,.22)'  },
    { id: 'Feito',   label: 'Feito',   colorDot: 'var(--clr-pos)', colorBg: 'rgba(74,222,128,.06)',   colorBorder: 'rgba(74,222,128,.22)'  },
  ];

  const DIAS_SEMANA = ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb']; // indexado por getDay()/getUTCDay() (0=domingo), usado pra rótulo de data específica
  const DIAS_SEMANA_GRID = ['Seg','Ter','Qua','Qui','Sex','Sáb','Dom']; // indexado por posição na grade (0=segunda), igual ao Google Agenda
  const MESES_PT = ['Janeiro','Fevereiro','Março','Abril','Maio','Junho',
    'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'];

  const CORES = ['var(--accent)', 'var(--clr-info)', 'var(--clr-pos)', '#f472b6', '#a78bfa', 'var(--clr-neg)',
    '#fb923c', '#34d399', '#94a3b8', '#facc15'];

  const ORDENACAO_OPCOES = [
    { value: 'prazo', label: 'Prazo' },
    { value: 'prioridade', label: 'Prioridade' },
    { value: 'titulo', label: 'Título (A-Z)' },
    { value: 'manual', label: 'Manual (arrastar)' },
  ];
  const PRIORIDADE_PESO = { alta: 0, normal: 1, baixa: 2 };

  function ordenarTarefas(tarefas, ordenarPor) {
    const copia = [...tarefas];
    if (ordenarPor === 'prazo') {
      copia.sort((a, b) => (a.prazo || '9999-99-99').localeCompare(b.prazo || '9999-99-99'));
    } else if (ordenarPor === 'prioridade') {
      copia.sort((a, b) => (PRIORIDADE_PESO[a.prioridade] ?? 1) - (PRIORIDADE_PESO[b.prioridade] ?? 1));
    } else if (ordenarPor === 'titulo') {
      copia.sort((a, b) => a.titulo.localeCompare(b.titulo, 'pt-BR'));
    } else {
      copia.sort((a, b) => (a.ordem ?? 0) - (b.ordem ?? 0));
    }
    return copia;
  }

  const RECORRENCIA_OPCOES = [
    { value: 'nenhuma', label: 'Não repete' },
    { value: 'diaria',  label: 'Diariamente' },
    { value: 'semanal', label: 'Semanalmente' },
    { value: 'mensal',  label: 'Mensalmente' },
    { value: 'anual',   label: 'Anualmente' },
  ];

  // Pra onde cada fonte derivada leva quando o item é clicado (mecanismo
  // de navegação entre telas exposto pelo main.jsx, já que o app não tem
  // roteador — ver window.khronusNavegar).
  const FONTE_META = {
    financeiro: { label: 'Financeiro', icon: 'wallet',        tela: 'financeiro' },
    nf:         { label: 'NFs',        icon: 'file-text',     tela: 'notas' },
    organico:   { label: 'Orgânico',   icon: 'image',         tela: 'organico' },
    evento:     { label: 'Eventos',    icon: 'calendar-days', tela: null },
    tarefa:     { label: 'Tarefas',    icon: 'check-square',  tela: null },
  };
  const TODAS_FONTES = Object.keys(FONTE_META);

  function AvatarFiltroPessoa({ membros, value, onChange }) {
    const alguemAtivo = value !== 'todos';
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        {membros.map(m => {
          const ativo = value === m.id;
          const foto = AVATAR_POR_NOME[m.nome];
          return (
            <button key={m.id} onClick={() => onChange(ativo ? 'todos' : m.id)} title={m.nome}
              style={{ width: 30, height: 30, borderRadius: '50%', padding: 0, cursor: 'pointer',
                overflow: 'hidden', flexShrink: 0, background: 'var(--app-surface-2)',
                border: ativo ? `2px solid ${m.cor}` : '2px solid transparent',
                boxShadow: ativo ? `0 0 0 2px ${m.cor}40` : 'none',
                opacity: !alguemAtivo || ativo ? 1 : 0.4,
                transition: 'opacity 120ms, border-color 120ms' }}>
              {foto
                ? <img src={foto} alt={m.nome} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
                : <IniciaisAvatar nome={m.nome} cor={m.cor} size={30} />}
            </button>
          );
        })}
        <button onClick={() => onChange(value === 'comum' ? 'todos' : 'comum')} title="Comum (de todos)"
          style={{ width: 30, height: 30, borderRadius: '50%', padding: 0, cursor: 'pointer', flexShrink: 0,
            overflow: 'hidden', background: 'var(--app-surface-2)',
            border: value === 'comum' ? '2px solid var(--accent)' : '2px solid transparent',
            boxShadow: value === 'comum' ? '0 0 0 2px rgba(234,170,65,.25)' : 'none',
            opacity: !alguemAtivo || value === 'comum' ? 1 : 0.4,
            transition: 'opacity 120ms, border-color 120ms' }}>
          <img src={AVATAR_COMUM} alt="Comum" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
        </button>
      </div>
    );
  }

  /* ── Datas (helpers próprios; nada em shared.jsx pra não acoplar) ─*/
  function paraDataUTC(iso) {
    const [y, m, d] = iso.split('-').map(Number);
    return Date.UTC(y, m - 1, d);
  }
  function addDiasISO(iso, n) {
    const [y, m, d] = iso.split('-').map(Number);
    const dt = new Date(Date.UTC(y, m - 1, d));
    dt.setUTCDate(dt.getUTCDate() + n);
    return dt.toISOString().slice(0, 10);
  }
  function addMesesISO(iso, n) {
    const [y, m, d] = iso.split('-').map(Number);
    return new Date(Date.UTC(y, m - 1 + n, d)).toISOString().slice(0, 10);
  }
  function addAnosISO(iso, n) {
    const [y, m, d] = iso.split('-').map(Number);
    return new Date(Date.UTC(y + n, m - 1, d)).toISOString().slice(0, 10);
  }
  function diasEntreISO(a, b) { return Math.round((paraDataUTC(b) - paraDataUTC(a)) / 86400000); }
  function hojeISO() { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; }

  // Expande UM evento (guardado como uma linha só) nas datas em que ele
  // ocorre dentro da janela visível [janelaInicio, janelaFim]. Séries
  // diárias/semanais pulam direto pra primeira ocorrência >= janelaInicio
  // em vez de iterar dia a dia desde a criação — importante pra um evento
  // diário criado há anos não travar ao abrir o mês atual.
  function expandirOcorrencias(evento, janelaInicio, janelaFim) {
    const datas = [];
    if (evento.recorrencia === 'nenhuma') {
      if (evento.data >= janelaInicio && evento.data <= janelaFim) datas.push(evento.data);
      return datas;
    }
    const limite = (evento.recorrencia_ate && evento.recorrencia_ate < janelaFim) ? evento.recorrencia_ate : janelaFim;
    if (evento.data > limite) return datas;

    let cursor = evento.data;
    if (evento.recorrencia === 'diaria' || evento.recorrencia === 'semanal') {
      const passoDias = evento.recorrencia === 'diaria' ? 1 : 7;
      if (cursor < janelaInicio) {
        const saltos = Math.ceil(diasEntreISO(cursor, janelaInicio) / passoDias);
        cursor = addDiasISO(cursor, saltos * passoDias);
      }
      let guard = 0;
      while (cursor <= limite && guard < 400) {
        if (cursor >= janelaInicio) datas.push(cursor);
        cursor = addDiasISO(cursor, passoDias);
        guard++;
      }
    } else {
      const passo = evento.recorrencia === 'mensal' ? (d => addMesesISO(d, 1)) : (d => addAnosISO(d, 1));
      let guard = 0;
      while (cursor <= limite && guard < 1200) {
        if (cursor >= janelaInicio) datas.push(cursor);
        cursor = passo(cursor);
        guard++;
      }
    }
    return datas;
  }

  // Junta as séries (linhas-pai) com as exceções de ocorrência única e
  // devolve uma lista pronta pra exibir no formato do crm_agenda_feed,
  // dentro da janela do mês visível.
  function gerarItensEventos(rawEventos, janelaInicio, janelaFim, categoriasPorId, membrosPorId) {
    const pais = rawEventos.filter(e => !e.recorrencia_pai);
    const excecoesPorChave = new Map();
    rawEventos.filter(e => e.recorrencia_pai).forEach(e => {
      excecoesPorChave.set(`${e.recorrencia_pai}|${e.recorrencia_data_original}`, e);
    });

    function paraFeedItem(row, dataExibida, extra) {
      const cat = row.categoria_id ? categoriasPorId.get(row.categoria_id) : null;
      const mem = row.responsavel_id ? membrosPorId.get(row.responsavel_id) : null;
      // Evento de vários dias: a duração vem da linha original (data → data_fim)
      // e é reaplicada a partir da data exibida — vale tanto pra ocorrência
      // expandida de uma série quanto pra exceção de um dia só.
      const duracaoDias = row.data_fim ? diasEntreISO(row.data, row.data_fim) : 0;
      return {
        fonte: 'evento', ref_id: row.id, titulo: row.titulo, subtitulo: row.local,
        data: dataExibida, data_fim: duracaoDias > 0 ? addDiasISO(dataExibida, duracaoDias) : null,
        hora: row.hora_inicio, concluido: !!row.concluido, valor: null,
        categoria_id: row.categoria_id || null, categoria_nome: cat?.nome || null, categoria_cor: cat?.cor || null,
        responsavel_id: row.responsavel_id || null, responsavel_nome: mem?.nome || null, responsavel_cor: mem?.cor || null,
        studio_id: row.studio_id, _evento: row, ...extra,
      };
    }

    const resultado = [];
    pais.forEach(pai => {
      const simples = pai.recorrencia === 'nenhuma';
      expandirOcorrencias(pai, janelaInicio, janelaFim).forEach(d => {
        const chave = `${pai.id}|${d}`;
        const excecao = excecoesPorChave.get(chave);
        if (excecao) {
          excecoesPorChave.delete(chave);
          if (excecao.cancelado || excecao.concluido) return;
          if (excecao.data >= janelaInicio && excecao.data <= janelaFim) {
            resultado.push(paraFeedItem(excecao, excecao.data, { _kind: 'excecao', _pai: pai, _ocorrenciaOriginal: d }));
          }
          return;
        }
        if (simples && pai.concluido) return;
        resultado.push(paraFeedItem(pai, d, simples
          ? { _kind: 'simples' }
          : { _kind: 'serie', _pai: pai, _ocorrenciaOriginal: d }));
      });
    });
    // Sobras: exceções cuja data original não caiu nesta janela, mas cuja
    // data NOVA (reagendada) cai — precisam aparecer no mês de destino.
    excecoesPorChave.forEach(excecao => {
      if (excecao.cancelado || excecao.concluido) return;
      if (excecao.data >= janelaInicio && excecao.data <= janelaFim) {
        const pai = pais.find(p => p.id === excecao.recorrencia_pai);
        resultado.push(paraFeedItem(excecao, excecao.data, { _kind: 'excecao', _pai: pai, _ocorrenciaOriginal: excecao.recorrencia_data_original }));
      }
    });
    return resultado;
  }

  function FiltroPill({ label, active, color, onClick }) {
    return (
      <button onClick={onClick}
        style={{ padding: '4px 12px', borderRadius: 'var(--r-full)', cursor: 'pointer', fontSize: 'var(--fs-sm)',
          fontFamily: 'Roboto,sans-serif', fontWeight: 700, transition: 'all 130ms',
          background: active ? (color ? `${color}22` : 'rgba(234,170,65,.15)') : 'var(--overlay-05)',
          border: active ? `1px solid ${color || 'rgba(234,170,65,.4)'}66` : '1px solid var(--overlay-10)',
          color: active ? (color || 'var(--accent)') : 'var(--text-3)', whiteSpace: 'nowrap' }}>
        {label}
      </button>
    );
  }

  function SeletorSimples({ value, onChange, options }) {
    return (
      <select value={value} onChange={e => onChange(e.target.value)}
        style={{ padding: '4px 10px', borderRadius: 'var(--r-sm)', fontSize: 'var(--fs-sm)',
          fontFamily: 'Roboto,sans-serif', fontWeight: 700,
          background: 'var(--overlay-05)', border: '1px solid var(--overlay-10)',
          color: 'var(--text-2)', outline: 'none', cursor: 'pointer' }}>
        {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
      </select>
    );
  }

  const inputStyle = {
    width: '100%', boxSizing: 'border-box', padding: '8px 12px', borderRadius: 'var(--r-sm)',
    background: 'var(--app-surface-2)', border: '1px solid var(--app-border)',
    color: 'var(--text-1)', fontFamily: 'Roboto, sans-serif', fontSize: 'var(--fs-lg)', outline: 'none',
  };
  function Campo({ label, children }) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto, sans-serif', fontWeight: 700,
          letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--text-3)' }}>{label}</span>
        {children}
      </div>
    );
  }
  function SeletorCor({ value, onChange }) {
    return (
      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
        {CORES.map(c => (
          <button key={c} onClick={() => onChange(c)}
            style={{ width: 24, height: 24, borderRadius: '50%', background: c, cursor: 'pointer',
              border: value === c ? '2px solid var(--text-1)' : '2px solid transparent',
              boxShadow: value === c ? '0 0 0 2px var(--app-bg)' : 'none' }} />
        ))}
      </div>
    );
  }

  /* ── Chooser de escopo — série inteira x só este dia ─────────────*/
  function EscopoChooser({ item, onEscolher, onClose }) {
    useEscapeToClose(onClose);
    return (
      <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', zIndex: 1100,
        display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}
        onClick={onClose}>
        <div style={{ width: '100%', maxWidth: 340, background: 'var(--app-surface)',
          border: '1px solid var(--app-border)', borderRadius: 'var(--r-xl)', padding: 20,
          display: 'flex', flexDirection: 'column', gap: 12 }}
          onClick={e => e.stopPropagation()}>
          <span style={{ fontFamily: 'Roboto, sans-serif', fontWeight: 900, fontSize: 'var(--fs-lg)', color: 'var(--text-1)' }}>
            "{item.titulo}" se repete
          </span>
          <span style={{ fontSize: 'var(--fs-md)', color: 'var(--text-3)', fontFamily: 'Roboto, sans-serif' }}>
            O que você quer fazer com o dia {item.data?.split('-').reverse().join('/')}?
          </span>
          <Btn variant="ghost" onClick={() => onEscolher('serie')}>Editar toda a série</Btn>
          <Btn variant="ghost" onClick={() => onEscolher('unico')}>Editar só este dia</Btn>
          <Btn variant="ghost" onClick={() => onEscolher('concluir')}>Marcar como concluído (só este dia)</Btn>
          <Btn variant="danger" onClick={() => onEscolher('cancelar')}>Cancelar só este dia</Btn>
          <Btn variant="ghost" onClick={onClose}>Fechar</Btn>
        </div>
      </div>
    );
  }

  /* ── Modal de evento ──────────────────────────────────────────*/
  function EventoModal({ modal, categorias, membros, onClose, onSaved, onDeleted }) {
    useEscapeToClose(onClose);
    const { escopo, item } = modal;
    const [titulo, setTitulo] = useState(item?.titulo || '');
    const [descricao, setDescricao] = useState(item?.descricao || '');
    const [local, setLocal] = useState(item?.local || '');
    const [data, setData] = useState(item?.data || hojeISO());
    const [variosDias, setVariosDias] = useState(!!item?.data_fim);
    const [dataFim, setDataFim] = useState(item?.data_fim || '');
    const [diaInteiro, setDiaInteiro] = useState(item?.dia_inteiro || false);
    const [horaInicio, setHoraInicio] = useState(item?.hora_inicio?.slice(0,5) || '');
    const [horaFim, setHoraFim] = useState(item?.hora_fim?.slice(0,5) || '');
    const [categoriaId, setCategoriaId] = useState(item?.categoria_id || '');
    const [responsavelId, setResponsavelId] = useState(item?.responsavel_id || '');
    const [recorrencia, setRecorrencia] = useState(item?.recorrencia || 'nenhuma');
    const [recorrenciaAte, setRecorrenciaAte] = useState(item?.recorrencia_ate || '');
    const [salvando, setSalvando] = useState(false);
    const [erro, setErro] = useState('');

    const categoriasEvento = categorias.filter(c => c.aplica_em === 'ambos' || c.aplica_em === 'evento');
    const mostraRecorrencia = escopo !== 'unico' && escopo !== 'unico-novo';
    const podeExcluir = escopo === 'serie' || escopo === 'unico' || !escopo;

    async function salvar() {
      if (!titulo.trim()) { setErro('Dê um título.'); return; }
      if (!data) { setErro('Escolha uma data.'); return; }
      if (variosDias && !dataFim) { setErro('Escolha a data final.'); return; }
      if (variosDias && dataFim < data) { setErro('A data final não pode ser antes da data inicial.'); return; }
      setSalvando(true); setErro('');
      try {
        const payload = {
          titulo: titulo.trim(), descricao: descricao.trim() || null, local: local.trim() || null,
          data, data_fim: variosDias ? dataFim : null, dia_inteiro: diaInteiro,
          hora_inicio: diaInteiro ? null : (horaInicio || null),
          hora_fim: diaInteiro ? null : (horaFim || null),
          categoria_id: categoriaId || null,
          responsavel_id: responsavelId || null,
        };
        if (mostraRecorrencia) {
          payload.recorrencia = recorrencia;
          payload.recorrencia_ate = recorrencia === 'nenhuma' ? null : (recorrenciaAte || null);
        }

        if (escopo === 'unico-novo') {
          payload.recorrencia = 'nenhuma';
          payload.recorrencia_ate = null;
          payload.recorrencia_pai = modal.pai.id;
          payload.recorrencia_data_original = modal.ocorrenciaOriginal;
          const { error } = await window.db.from('crm_agenda_events').insert(payload);
          if (error) throw error;
        } else if (item?.id) {
          const { error } = await window.db.from('crm_agenda_events').update(payload).eq('id', item.id);
          if (error) throw error;
        } else {
          const { error } = await window.db.from('crm_agenda_events').insert(payload);
          if (error) throw error;
        }
        onSaved();
      } catch (e) {
        setErro(e.message || String(e));
      } finally {
        setSalvando(false);
      }
    }

    async function excluir() {
      const msg = escopo === 'serie'
        ? `Excluir "${titulo}" apaga toda a série (e qualquer dia editado separadamente dela). Confirma?`
        : `Excluir "${titulo}"?`;
      if (!confirm(msg)) return;
      await window.db.from('crm_agenda_events').delete().eq('id', item.id);
      onDeleted();
    }

    // Marca este evento como feito — some da grade sem apagar o registro.
    // Não se aplica ao editar a série inteira (escopo 'serie'): pra série
    // recorrente, o check por ocorrência fica no EscopoChooser.
    async function concluir() {
      await window.db.from('crm_agenda_events').update({ concluido: true }).eq('id', item.id);
      onSaved();
    }

    return (
      <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', zIndex: 1000,
        display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}
        onClick={onClose}>
        <div style={{ width: '100%', maxWidth: 420, background: 'var(--app-surface)',
          border: '1px solid var(--app-border)', borderRadius: 'var(--r-xl)', padding: 22,
          display: 'flex', flexDirection: 'column', gap: 12, maxHeight: '90vh', overflowY: 'auto' }}
          onClick={e => e.stopPropagation()}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontFamily: 'Roboto, sans-serif', fontWeight: 900, fontSize: 'var(--fs-xl)', color: 'var(--text-1)' }}>
              {item?.id ? 'Editar evento' : 'Novo evento'}
              {escopo === 'serie' && ' (série)'}
              {(escopo === 'unico' || escopo === 'unico-novo') && ' (só este dia)'}
            </span>
            <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-3)' }}>
              <LucideIcon icon="x" size={18} />
            </button>
          </div>

          <Campo label="Título">
            <input style={inputStyle} value={titulo} onChange={e => setTitulo(e.target.value)} placeholder="Ex.: Reunião com a Amanda" />
          </Campo>

          <div style={{ display: 'grid', gridTemplateColumns: variosDias ? '1fr 1fr' : '1fr 1fr', gap: 10 }}>
            <Campo label={variosDias ? 'Data (início)' : 'Data'}>
              <input type="date" style={inputStyle} value={data}
                onChange={e => { setData(e.target.value); if (variosDias && dataFim && dataFim < e.target.value) setDataFim(e.target.value); }} />
            </Campo>
            {variosDias ? (
              <Campo label="Data (fim)">
                <input type="date" style={inputStyle} value={dataFim} min={data} onChange={e => setDataFim(e.target.value)} />
              </Campo>
            ) : (
              <Campo label="Local">
                <input style={inputStyle} value={local} onChange={e => setLocal(e.target.value)} placeholder="Opcional" />
              </Campo>
            )}
          </div>

          {variosDias && (
            <Campo label="Local">
              <input style={inputStyle} value={local} onChange={e => setLocal(e.target.value)} placeholder="Opcional" />
            </Campo>
          )}

          <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <input type="checkbox" id="ag-varios-dias" checked={variosDias}
                onChange={e => { setVariosDias(e.target.checked); if (!e.target.checked) setDataFim(''); else if (!dataFim) setDataFim(data); }} />
              <label htmlFor="ag-varios-dias" style={{ fontSize: 'var(--fs-md)', fontFamily: 'Roboto,sans-serif', color: 'var(--text-2)', cursor: 'pointer' }}>
                Vários dias
              </label>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <input type="checkbox" id="ag-dia-inteiro" checked={diaInteiro} onChange={e => setDiaInteiro(e.target.checked)} />
              <label htmlFor="ag-dia-inteiro" style={{ fontSize: 'var(--fs-md)', fontFamily: 'Roboto,sans-serif', color: 'var(--text-2)', cursor: 'pointer' }}>
                Dia inteiro
              </label>
            </div>
          </div>

          {!diaInteiro && (
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <Campo label="Início">
                <input type="time" style={inputStyle} value={horaInicio} onChange={e => setHoraInicio(e.target.value)} />
              </Campo>
              <Campo label="Fim">
                <input type="time" style={inputStyle} value={horaFim} onChange={e => setHoraFim(e.target.value)} />
              </Campo>
            </div>
          )}

          <Campo label="Descrição">
            <textarea style={{ ...inputStyle, minHeight: 60, resize: 'vertical' }} value={descricao}
              onChange={e => setDescricao(e.target.value)} placeholder="Opcional" />
          </Campo>

          <Campo label="Categoria">
            <select style={inputStyle} value={categoriaId} onChange={e => setCategoriaId(e.target.value)}>
              <option value="">Sem categoria</option>
              {categoriasEvento.map(c => <option key={c.id} value={c.id}>{c.nome}</option>)}
            </select>
          </Campo>

          <Campo label="Responsável">
            <select style={inputStyle} value={responsavelId} onChange={e => setResponsavelId(e.target.value)}>
              <option value="">Comum (de todos)</option>
              {membros.map(m => <option key={m.id} value={m.id}>{m.nome}</option>)}
            </select>
          </Campo>

          {mostraRecorrencia && (
            <>
              <Campo label="Repetir">
                <select style={inputStyle} value={recorrencia} onChange={e => setRecorrencia(e.target.value)}>
                  {RECORRENCIA_OPCOES.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                </select>
              </Campo>
              {recorrencia !== 'nenhuma' && (
                <Campo label="Repetir até (opcional)">
                  <input type="date" style={inputStyle} value={recorrenciaAte} onChange={e => setRecorrenciaAte(e.target.value)} />
                </Campo>
              )}
            </>
          )}

          {erro && (
            <div style={{ padding: '8px 12px', borderRadius: 'var(--r-sm)', background: 'rgba(248,113,113,.1)',
              border: '1px solid rgba(248,113,113,.25)', color: 'var(--clr-neg)', fontSize: 'var(--fs-md)' }}>{erro}</div>
          )}

          <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: 8 }}>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {(podeExcluir && item?.id) && <Btn variant="danger" icon="trash-2" onClick={excluir}>Excluir</Btn>}
              {(item?.id && escopo !== 'serie') && <Btn variant="ghost" icon="check-circle" onClick={concluir}>Concluir</Btn>}
            </div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginLeft: 'auto' }}>
              <Btn variant="ghost" onClick={onClose}>Cancelar</Btn>
              <Btn variant="primary" onClick={salvar} disabled={salvando} icon={salvando ? undefined : 'check'}>
                {salvando ? 'Salvando…' : 'Salvar'}
              </Btn>
            </div>
          </div>
        </div>
      </div>
    );
  }

  /* ── Modal de tarefa ──────────────────────────────────────────*/
  function TaskModal({ item, defaultStatus, categorias, membros, empresaTags, onClose, onSaved, onDeleted }) {
    useEscapeToClose(onClose);
    const [titulo, setTitulo] = useState(item?.titulo || '');
    const [notas, setNotas] = useState(item?.notas || '');
    const [prazo, setPrazo] = useState(item?.prazo || '');
    const [hora, setHora] = useState(item?.hora?.slice(0,5) || '');
    const [categoriaId, setCategoriaId] = useState(item?.categoria_id || '');
    const [responsavelId, setResponsavelId] = useState(item?.responsavel_id || '');
    const [companyTagId, setCompanyTagId] = useState(item?.company_tag_id || null);
    const [prioridade, setPrioridade] = useState(item?.prioridade || 'normal');
    const [salvando, setSalvando] = useState(false);
    const [erro, setErro] = useState('');

    const categoriasTarefa = categorias.filter(c => c.aplica_em === 'ambos' || c.aplica_em === 'tarefa');

    async function salvar() {
      if (!titulo.trim()) { setErro('Dê um título.'); return; }
      setSalvando(true); setErro('');
      try {
        const payload = {
          titulo: titulo.trim(), notas: notas.trim() || null,
          prazo: prazo || null, hora: prazo ? (hora || null) : null,
          categoria_id: categoriaId || null, responsavel_id: responsavelId || null,
          company_tag_id: companyTagId || null,
          prioridade: prioridade || null,
        };
        if (item?.id) {
          const { error } = await window.db.from('crm_agenda_tasks').update(payload).eq('id', item.id);
          if (error) throw error;
        } else {
          const status = defaultStatus || 'Fazer';
          const { data: max } = await window.db.from('crm_agenda_tasks')
            .select('ordem').eq('status', status).order('ordem', { ascending: false }).limit(1);
          payload.status = status;
          payload.ordem = (max?.[0]?.ordem ?? 0) + 1;
          const { error } = await window.db.from('crm_agenda_tasks').insert(payload);
          if (error) throw error;
        }
        onSaved();
      } catch (e) {
        setErro(e.message || String(e));
      } finally {
        setSalvando(false);
      }
    }

    async function excluir() {
      if (!confirm(`Excluir "${titulo}"?`)) return;
      await window.db.from('crm_agenda_tasks').delete().eq('id', item.id);
      onDeleted();
    }

    return (
      <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', zIndex: 1000,
        display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}
        onClick={onClose}>
        <div style={{ width: '100%', maxWidth: 420, background: 'var(--app-surface)',
          border: '1px solid var(--app-border)', borderRadius: 'var(--r-xl)', padding: 22,
          display: 'flex', flexDirection: 'column', gap: 12, maxHeight: '90vh', overflowY: 'auto' }}
          onClick={e => e.stopPropagation()}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontFamily: 'Roboto, sans-serif', fontWeight: 900, fontSize: 'var(--fs-xl)', color: 'var(--text-1)' }}>
              {item?.id ? 'Editar tarefa' : 'Nova tarefa'}
            </span>
            <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-3)' }}>
              <LucideIcon icon="x" size={18} />
            </button>
          </div>

          <Campo label="Título">
            <input style={inputStyle} value={titulo} onChange={e => setTitulo(e.target.value)} placeholder="Ex.: Responder orçamento da Scania" />
          </Campo>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            <Campo label="Prazo (opcional)">
              <input type="date" style={inputStyle} value={prazo} onChange={e => setPrazo(e.target.value)} />
            </Campo>
            {prazo && (
              <Campo label="Hora (opcional)">
                <input type="time" style={inputStyle} value={hora} onChange={e => setHora(e.target.value)} />
              </Campo>
            )}
          </div>

          <Campo label="Notas">
            <textarea style={{ ...inputStyle, minHeight: 60, resize: 'vertical' }} value={notas}
              onChange={e => setNotas(e.target.value)} placeholder="Opcional" />
          </Campo>

          <Campo label="Categoria">
            <select style={inputStyle} value={categoriaId} onChange={e => setCategoriaId(e.target.value)}>
              <option value="">Sem categoria</option>
              {categoriasTarefa.map(c => <option key={c.id} value={c.id}>{c.nome}</option>)}
            </select>
          </Campo>

          <Campo label="Responsável">
            <select style={inputStyle} value={responsavelId} onChange={e => setResponsavelId(e.target.value)}>
              <option value="">Comum (de todos)</option>
              {membros.map(m => <option key={m.id} value={m.id}>{m.nome}</option>)}
            </select>
          </Campo>

          <Campo label="Empresa">
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              <button onClick={() => setCompanyTagId(null)}
                style={{ padding: '6px 12px', borderRadius: 'var(--r-full)', cursor: 'pointer',
                  fontFamily: 'Roboto,sans-serif', fontWeight: 700, fontSize: 'var(--fs-md)', transition: 'all 150ms',
                  display: 'flex', alignItems: 'center', gap: 6,
                  background: !companyTagId ? 'var(--overlay-15)' : 'var(--overlay-08)',
                  border: `1px solid ${!companyTagId ? 'var(--app-border-2)' : 'var(--app-border)'}`,
                  color: !companyTagId ? 'var(--text-1)' : 'var(--text-3)' }}>
                Nenhuma
              </button>
              {(empresaTags || []).map(t => {
                const active = companyTagId === t.id;
                return (
                  <button key={t.id} onClick={() => setCompanyTagId(t.id)}
                    style={{ padding: '6px 12px', borderRadius: 'var(--r-full)', cursor: 'pointer',
                      fontFamily: 'Roboto,sans-serif', fontWeight: 700, fontSize: 'var(--fs-md)', transition: 'all 150ms',
                      display: 'flex', alignItems: 'center', gap: 6,
                      background: active ? 'var(--overlay-15)' : 'var(--overlay-08)',
                      border: `1px solid ${active ? 'var(--app-border-2)' : 'var(--app-border)'}`,
                      color: active ? 'var(--text-1)' : 'var(--text-3)' }}>
                    <EmpresaDot cor={t.cor} size={10} />{t.nome}
                  </button>
                );
              })}
            </div>
          </Campo>

          <Campo label="Prioridade">
            <select style={inputStyle} value={prioridade} onChange={e => setPrioridade(e.target.value)}>
              <option value="baixa">Baixa</option>
              <option value="normal">Normal</option>
              <option value="alta">Alta</option>
            </select>
          </Campo>

          {erro && (
            <div style={{ padding: '8px 12px', borderRadius: 'var(--r-sm)', background: 'rgba(248,113,113,.1)',
              border: '1px solid rgba(248,113,113,.25)', color: 'var(--clr-neg)', fontSize: 'var(--fs-md)' }}>{erro}</div>
          )}

          <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}>
            {item?.id ? <Btn variant="danger" icon="trash-2" onClick={excluir}>Excluir</Btn> : <span />}
            <div style={{ display: 'flex', gap: 8 }}>
              <Btn variant="ghost" onClick={onClose}>Cancelar</Btn>
              <Btn variant="primary" onClick={salvar} disabled={salvando} icon={salvando ? undefined : 'check'}>
                {salvando ? 'Salvando…' : 'Salvar'}
              </Btn>
            </div>
          </div>
        </div>
      </div>
    );
  }

  /* ── Cartão de tarefa (Kanban e Lista) ────────────────────────*/
  function TarefaCard({ tarefa, onOpen, onConcluir, draggable, onDragStart, onDragEnd, empresaTags, colCfg }) {
    const [hov, setHov] = useState(false);
    const cor = tarefa.categoria_cor || '#94a3b8';
    const concluida = tarefa.status === 'Feito';
    const empresa = (empresaTags || []).find(t => t.id === tarefa.company_tag_id);
    return (
      <div
        draggable={draggable}
        onDragStart={onDragStart}
        onDragEnd={onDragEnd}
        onClick={() => onOpen(tarefa)}
        onMouseEnter={() => setHov(true)}
        onMouseLeave={() => setHov(false)}
        style={{ display: 'flex', flexDirection: 'column', gap: 6, padding: '10px 12px',
          borderRadius: 'var(--r-sm)', background: hov ? 'var(--app-surface-3)' : 'var(--app-surface-2)',
          border: `1px solid ${hov && colCfg ? colCfg.colorBorder : 'var(--app-border)'}`,
          borderLeft: `3px solid ${cor}`, cursor: draggable ? 'grab' : 'pointer',
          opacity: concluida ? 0.5 : 1,
          transition: 'all 160ms var(--ease-out), opacity 150ms', transform: hov ? 'translateY(-1px)' : 'none' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
          {onConcluir && (
            <input type="checkbox" checked={concluida} title="Marcar como concluída"
              onClick={e => e.stopPropagation()}
              onChange={e => { e.stopPropagation(); onConcluir(tarefa); }}
              style={{ cursor: 'pointer', flexShrink: 0, marginTop: 2 }} />
          )}
          <span style={{ fontSize: 'var(--fs-md)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
            color: 'var(--text-1)', textDecoration: concluida ? 'line-through' : 'none', flex: 1 }}>
            {tarefa.titulo}
          </span>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
          {tarefa.prazo && (
            tarefa.status === 'Feito'
              ? <span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 'var(--fs-xs)',
                  fontFamily: 'Roboto,sans-serif', color: 'var(--text-3)' }}>
                  <LucideIcon icon="calendar" size={10} />
                  {tarefa.prazo.split('-').reverse().join('/')}
                </span>
              : <PrazoBadge data={tarefa.prazo} comIcone size={10.5} />
          )}
          {empresa && <EmpresaDot cor={empresa.cor} nome={empresa.nome} size={10} />}
          <span style={{ marginLeft: 'auto' }}>
            <IniciaisAvatar nome={tarefa.responsavel_nome} cor={tarefa.responsavel_cor} size={18} />
          </span>
        </div>
      </div>
    );
  }

  /* ── Tarefas — modo Kanban ────────────────────────────────────*/
  function TarefasKanban({ tarefas, onOpen, onMover, ordenarPor, empresaTags, onAddNew }) {
    const [dragId, setDragId] = useState(null);
    const [dragOverCol, setDragOverCol] = useState(null);

    const porColuna = COLUNAS_TAREFA.map(col => ({
      col, itens: ordenarTarefas(tarefas.filter(t => t.status === col.id), ordenarPor),
    }));

    // Checkbox de concluir: manda pro fim da coluna Feito (ou de volta pra
    // Fazer, se já estava concluída) — mesmo destino de quem arrasta.
    function concluir(tarefa) {
      const alvo = tarefa.status === 'Feito' ? 'Fazer' : 'Feito';
      const coluna = porColuna.find(p => p.col.id === alvo);
      const ultimo = coluna?.itens[coluna.itens.length - 1];
      onMover(tarefa.id, alvo, (ultimo?.ordem ?? 0) + 1);
    }

    return (
      <div style={{ flex: 1, display: 'flex', gap: 10, padding: '16px 20px', overflowX: 'auto', minHeight: 0 }}>
        {porColuna.map(({ col, itens }) => (
          <div key={col.id}
            onDragOver={e => { e.preventDefault(); setDragOverCol(col.id); }}
            onDragLeave={() => setDragOverCol(prev => prev === col.id ? null : prev)}
            onDrop={e => {
              e.preventDefault();
              setDragOverCol(null);
              if (dragId) {
                const ultimo = itens[itens.length - 1];
                onMover(dragId, col.id, (ultimo?.ordem ?? 0) + 1);
              }
              setDragId(null);
            }}
            style={{ flex: '0 0 260px', display: 'flex', flexDirection: 'column',
              borderRadius: 'var(--r-md)', minHeight: 200, maxHeight: '100%', overflow: 'hidden',
              background: dragOverCol === col.id ? 'rgba(234,170,65,.06)' : col.colorBg,
              border: dragOverCol === col.id ? '1px solid rgba(234,170,65,.4)' : `1px solid ${col.colorBorder}` }}>
            <span style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '10px 10px 0' }}>
              <span style={{ width: 8, height: 8, borderRadius: '50%', background: col.colorDot, flexShrink: 0 }} />
              <span style={{ fontSize: 'var(--fs-sm)', fontFamily: 'Roboto,sans-serif', fontWeight: 900,
                letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-3)' }}>
                {col.label} · {itens.length}
              </span>
            </span>
            <div style={{ flex: 1, overflowY: 'auto', padding: 10, display: 'flex', flexDirection: 'column', gap: 8 }}>
              {itens.map(t => (
                <TarefaCard key={t.id} tarefa={t} onOpen={onOpen} onConcluir={concluir} draggable empresaTags={empresaTags} colCfg={col}
                  onDragStart={() => setDragId(t.id)} onDragEnd={() => setDragId(null)} />
              ))}
            </div>
            <div style={{ padding: '6px 10px 10px', borderTop: `1px solid ${col.colorBorder}`, flexShrink: 0 }}>
              <button onClick={() => onAddNew(col.id)}
                style={{ width: '100%', padding: 7, borderRadius: 'var(--r-sm)', cursor: 'pointer',
                  background: 'transparent', border: `1px dashed ${col.colorBorder}`,
                  color: 'var(--text-3)', fontSize: 'var(--fs-sm)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 4 }}
                onMouseEnter={e => { e.currentTarget.style.color = col.colorDot; e.currentTarget.style.borderColor = col.colorDot; }}
                onMouseLeave={e => { e.currentTarget.style.color = 'var(--text-3)'; e.currentTarget.style.borderColor = col.colorBorder; }}>
                <LucideIcon icon="plus" size={13} />Adicionar
              </button>
            </div>
          </div>
        ))}
      </div>
    );
  }

  /* ── Tarefas — modo Lista ─────────────────────────────────────*/
  // Agrupado por Pessoa (responsável), não pelo campo Lista — Lista virou
  // texto livre desde que passou a acompanhar o responsável automaticamente,
  // então agrupar por pessoa (Comum primeiro, depois ordem alfabética) é o
  // que realmente ajuda a se achar aqui.
  function TarefasLista({ tarefas, onOpen, onToggle, ordenarPor }) {
    const grupos = new Map();
    tarefas.forEach(t => {
      const chave = t.responsavel_nome || 'Comum';
      if (!grupos.has(chave)) grupos.set(chave, []);
      grupos.get(chave).push(t);
    });
    const gruposOrdenados = [...grupos.entries()].sort(([a], [b]) => {
      if (a === 'Comum') return -1;
      if (b === 'Comum') return 1;
      return a.localeCompare(b, 'pt-BR');
    });

    return (
      <div style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: '16px 24px', display: 'flex', flexDirection: 'column', gap: 20 }}>
        {gruposOrdenados.map(([nomePessoa, itens]) => (
          <div key={nomePessoa} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <span style={{ display: 'flex', alignItems: 'center', gap: 6,
              fontSize: 'var(--fs-sm)', fontFamily: 'Roboto,sans-serif', fontWeight: 900,
              letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-3)' }}>
              <IniciaisAvatar nome={nomePessoa === 'Comum' ? null : nomePessoa}
                cor={itens[0]?.responsavel_cor} size={16} />
              {nomePessoa}
            </span>
            {ordenarTarefas(itens, ordenarPor).map(t => (
              <div key={t.id}
                style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px',
                  borderRadius: 'var(--r-sm)', background: 'var(--app-surface-2)', border: '1px solid var(--app-border)',
                  opacity: t.status === 'Feito' ? 0.5 : 1, transition: 'opacity 150ms' }}>
                <input type="checkbox" checked={t.status === 'Feito'} onChange={() => onToggle(t)}
                  style={{ cursor: 'pointer', flexShrink: 0 }} />
                <span onClick={() => onOpen(t)}
                  style={{ flex: 1, fontSize: 'var(--fs-lg)', fontFamily: 'Roboto,sans-serif', cursor: 'pointer',
                    color: 'var(--text-1)', textDecoration: t.status === 'Feito' ? 'line-through' : 'none' }}>
                  {t.titulo}
                </span>
                {t.categoria_cor && <div style={{ width: 10, height: 10, borderRadius: '50%', background: t.categoria_cor, flexShrink: 0 }} />}
                {t.prazo && (
                  <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', color: 'var(--text-3)', flexShrink: 0 }}>
                    {t.prazo.split('-').reverse().join('/')}
                  </span>
                )}
                <IniciaisAvatar nome={t.responsavel_nome} cor={t.responsavel_cor} size={18} />
              </div>
            ))}
          </div>
        ))}
        {tarefas.length === 0 && (
          <div style={{ textAlign: 'center', color: 'var(--text-3)', fontSize: 'var(--fs-lg)', padding: 40 }}>Nenhuma tarefa por aqui.</div>
        )}
      </div>
    );
  }

  /* ── Grade de mês ─────────────────────────────────────────────*/
  // Junta itens derivados (já filtrados) com eventos próprios expandidos
  // dentro de uma janela [janelaInicio, janelaFim] — usado pelos 4 modos
  // de calendário (Mês, Semana, Dia, Lista), cada um só varia a janela.
  function useItensDaJanela(items, eventosRaw, categorias, membros, janelaInicio, janelaFim) {
    const categoriasPorId = useMemo(() => new Map(categorias.map(c => [c.id, c])), [categorias]);
    const membrosPorId = useMemo(() => new Map(membros.map(m => [m.id, m])), [membros]);
    const itensEventos = useMemo(
      () => gerarItensEventos(eventosRaw, janelaInicio, janelaFim, categoriasPorId, membrosPorId),
      [eventosRaw, janelaInicio, janelaFim, categoriasPorId, membrosPorId]
    );
    return useMemo(() => [...items, ...itensEventos], [items, itensEventos]);
  }

  // Evento de vários dias aparece em cada dia do intervalo, não só no início
  // (mesmo padrão do Google Agenda). Itens de um dia só (data_fim vazio)
  // continuam caindo numa única data, como antes.
  function agruparPorData(itens) {
    const byDate = {};
    itens.forEach(item => {
      if (!item.data) return;
      const inicio = item.data.slice(0, 10);
      const fim = item.data_fim ? item.data_fim.slice(0, 10) : inicio;
      let cursor = inicio;
      let guard = 0;
      while (cursor <= fim && guard < 366) {
        if (!byDate[cursor]) byDate[cursor] = [];
        byDate[cursor].push(item);
        cursor = addDiasISO(cursor, 1);
        guard++;
      }
    });
    return byDate;
  }

  function AgendaMes({ items, eventosRaw, categorias, membros, onOpenDerivado, onOpenEvento, onNovoEventoData, onDropEvento }) {
    const today = new Date();
    const [ano, setAno] = useState(today.getFullYear());
    const [mes, setMes] = useState(today.getMonth());
    const [dragItem, setDragItem] = useState(null);
    const [dragOverDate, setDragOverDate] = useState(null);
    const [diaDetalhe, setDiaDetalhe] = useState(null); // dateStr com o dia expandido em modal, ou null
    useEscapeToClose(() => setDiaDetalhe(null));

    // getDay() é 0=domingo...6=sábado; convertemos pra offset com a semana
    // começando na segunda (0=segunda...6=domingo), igual ao Google Agenda.
    const primeiroDia = (new Date(ano, mes, 1).getDay() + 6) % 7;
    const diasNoMes = new Date(ano, mes + 1, 0).getDate();
    const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;

    // A grade mostra também os dias vizinhos que completam a primeira e a
    // última semana, então a janela de eventos vai do primeiro ao último dia
    // VISÍVEL, não do dia 1 ao dia 30. Sem isso, esses dias apareceriam
    // sempre vazios mesmo tendo compromisso marcado.
    const _iso = d => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
    const _celulas = Math.ceil((primeiroDia + diasNoMes) / 7) * 7;
    const janelaInicio = _iso(new Date(ano, mes, 1 - primeiroDia));
    const janelaFim = _iso(new Date(ano, mes, diasNoMes + (_celulas - primeiroDia - diasNoMes)));

    const todosItens = useItensDaJanela(items, eventosRaw, categorias, membros, janelaInicio, janelaFim);
    const byDate = agruparPorData(todosItens);

    const prevMes = () => { if (mes === 0) { setMes(11); setAno(a => a - 1); } else setMes(m => m - 1); };
    const nextMes = () => { if (mes === 11) { setMes(0); setAno(a => a + 1); } else setMes(m => m + 1); };

    // Bordas da grade preenchidas com os dias do mês vizinho, mostrando o que
    // está marcado neles, bem mais apagados que sábado e domingo. Combinado com
    // Felipe em 2026-08-29: a semana que atravessa a virada do mês é uma semana
    // real de trabalho, e escondê-la fazia parecer que não tinha nada ali.
    const cells = [];
    const _antes = new Date(ano, mes, 0);
    const _diasAntes = _antes.getDate();
    for (let i = primeiroDia; i > 0; i--) {
      cells.push({ ano: _antes.getFullYear(), mes: _antes.getMonth(), dia: _diasAntes - i + 1, fora: true });
    }
    for (let d = 1; d <= diasNoMes; d++) cells.push({ ano, mes, dia: d, fora: false });
    const _depois = new Date(ano, mes + 1, 1);
    let _prox = 1;
    while (cells.length % 7 !== 0) {
      cells.push({ ano: _depois.getFullYear(), mes: _depois.getMonth(), dia: _prox++, fora: true });
    }

    return (
      <>
      <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', padding: '0 20px 16px' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 16, padding: '12px 0 16px' }}>
          <button onClick={prevMes}
            style={{ width: 30, height: 30, borderRadius: 'var(--r-sm)', border: '1px solid var(--app-border)',
              background: 'var(--overlay-05)', color: 'var(--text-2)', cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <LucideIcon icon="chevron-left" size={16} />
          </button>
          <span style={{ fontSize: 'var(--fs-xl)', fontFamily: 'Roboto,sans-serif', fontWeight: 900,
            color: 'var(--text-1)', minWidth: 180, textAlign: 'center' }}>
            {MESES_PT[mes]} {ano}
          </span>
          <button onClick={nextMes}
            style={{ width: 30, height: 30, borderRadius: 'var(--r-sm)', border: '1px solid var(--app-border)',
              background: 'var(--overlay-05)', color: 'var(--text-2)', cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <LucideIcon icon="chevron-right" size={16} />
          </button>
        </div>

        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7,1fr)', gap: 4, marginBottom: 4 }}>
            {DIAS_SEMANA_GRID.map((d, i) => {
              const isWeekend = i === 5 || i === 6;
              return (
                <div key={d} style={{ textAlign: 'center', fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif',
                  fontWeight: 900, letterSpacing: '0.06em',
                  color: isWeekend ? 'rgba(148,163,184,.45)' : 'var(--text-3)',
                  textTransform: 'uppercase', padding: '4px 0' }}>
                  {d}
                </div>
              );
            })}
          </div>

          <div style={{ flex: 1, minHeight: 0, display: 'grid', gridTemplateColumns: 'repeat(7,1fr)',
            gridAutoRows: 'minmax(112px, 1fr)', gap: 4, overflowY: 'auto', paddingRight: 2 }}>
            {cells.map((cel, idx) => {
              const { dia, fora } = cel;
              const colIdx = idx % 7;
              const isWeekend = colIdx === 5 || colIdx === 6;
              const dateStr = `${cel.ano}-${String(cel.mes + 1).padStart(2, '0')}-${String(dia).padStart(2, '0')}`;
              const dayItems = byDate[dateStr] || [];
              const isToday = dateStr === todayStr;
              const isDragOver = dragOverDate === dateStr;

              let bgColor = 'var(--app-surface-2)';
              if (isDragOver) bgColor = 'rgba(234,170,65,.1)';
              else if (isToday) bgColor = 'rgba(234,170,65,.06)';
              else if (fora) bgColor = 'transparent';
              else if (isWeekend) bgColor = 'var(--overlay-05)';

              let borderColor = 'var(--app-border)';
              if (isDragOver) borderColor = 'rgba(234,170,65,.7)';
              else if (isToday) borderColor = 'rgba(234,170,65,.45)';
              else if (fora) borderColor = 'rgba(255,255,255,.035)';
              else if (isWeekend) borderColor = 'var(--overlay-08)';

              return (
                <div key={dateStr}
                  onClick={() => onNovoEventoData(dateStr)}
                  onDragOver={e => { e.preventDefault(); if (dragOverDate !== dateStr) setDragOverDate(dateStr); }}
                  onDragLeave={() => setDragOverDate(prev => prev === dateStr ? null : prev)}
                  onDrop={e => {
                    e.preventDefault();
                    setDragOverDate(null);
                    if (dragItem && dateStr !== dragItem.data) onDropEvento(dragItem, dateStr);
                    setDragItem(null);
                  }}
                  style={{ border: `1px solid ${borderColor}`, borderRadius: 'var(--r-sm)', padding: '6px 8px',
                    background: bgColor, cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 4,
                    overflow: 'hidden', transition: 'border-color 150ms, background 150ms',
                    opacity: isToday ? 1 : fora ? 0.34 : isWeekend ? 0.7 : 1 }}
                  onMouseOver={e => { if (fora) e.currentTarget.style.opacity = 0.75; }}
                  onMouseOut={e => { if (fora && !isToday) e.currentTarget.style.opacity = 0.34; }}>

                  <span style={{ fontSize: 'var(--fs-sm)', fontFamily: 'Roboto,sans-serif', fontWeight: 900,
                    color: isToday ? 'var(--accent)' : fora ? 'rgba(148,163,184,.4)' : isWeekend ? 'rgba(148,163,184,.5)' : 'var(--text-3)',
                    background: isToday ? 'rgba(234,170,65,.15)' : 'transparent',
                    borderRadius: 5, padding: isToday ? '1px 5px' : '0',
                    alignSelf: 'flex-start', lineHeight: 1.6, flexShrink: 0 }}>
                    {dia}
                  </span>

                  {dayItems.slice(0, 4).map(item => {
                    const meta = FONTE_META[item.fonte] || {};
                    const color = item.concluido ? 'var(--clr-pos)' : (item.categoria_cor || '#94a3b8');
                    const ehEvento = item.fonte === 'evento';
                    const clicavel = !!meta.tela || ehEvento;
                    return (
                      <div key={`${item.fonte}-${item.ref_id}-${item.data}`}
                        draggable={ehEvento}
                        onDragStart={ehEvento ? (e => { e.stopPropagation(); setDragItem(item); e.dataTransfer.effectAllowed = 'move'; }) : undefined}
                        onDragEnd={ehEvento ? (() => setDragItem(null)) : undefined}
                        onClick={clicavel ? (e => { e.stopPropagation(); ehEvento ? onOpenEvento(item) : onOpenDerivado(item); }) : (e => e.stopPropagation())}
                        style={{ display: 'flex', alignItems: 'center', gap: 4, padding: '4px 6px',
                          borderRadius: 'var(--r-sm)', background: `${color}18`, border: `1px solid ${color}33`,
                          cursor: clicavel ? (ehEvento ? 'grab' : 'pointer') : 'default', overflow: 'hidden',
                          transition: 'background 120ms', opacity: dragItem === item ? 0.4 : 1 }}
                        onMouseEnter={e => { if (clicavel) e.currentTarget.style.background = `${color}32`; }}
                        onMouseLeave={e => { e.currentTarget.style.background = `${color}18`; }}>
                        <LucideIcon icon={item.concluido ? 'check-circle' : (meta.icon || 'circle')} size={11} style={{ color, flexShrink: 0 }} />
                        <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 900,
                          color, letterSpacing: '0.03em', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', flex: 1 }}>
                          {item.titulo}
                        </span>
                        <IniciaisAvatar nome={item.responsavel_nome} cor={item.responsavel_cor} size={14} />
                      </div>
                    );
                  })}
                  {dayItems.length > 4 && (
                    <span onClick={e => { e.stopPropagation(); setDiaDetalhe(dateStr); }}
                      style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700, color: 'var(--accent)',
                        paddingLeft: 4, cursor: 'pointer', textDecoration: 'underline', textUnderlineOffset: 2 }}>
                      +{dayItems.length - 4} mais
                    </span>
                  )}
                  {dayItems.length === 0 && (
                    <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif',
                      color: isWeekend ? 'var(--text-3)' : 'var(--text-3)',
                      marginTop: 'auto', textAlign: 'center' }}>+ novo evento</span>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      </div>

      {diaDetalhe && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', zIndex: 1000,
          display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}
          onClick={() => setDiaDetalhe(null)}>
          <div style={{ width: '100%', maxWidth: 420, background: 'var(--app-surface)',
            border: '1px solid var(--app-border)', borderRadius: 'var(--r-xl)', padding: 22,
            display: 'flex', flexDirection: 'column', gap: 10, maxHeight: '80vh', overflowY: 'auto' }}
            onClick={e => e.stopPropagation()}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <span style={{ fontFamily: 'Roboto, sans-serif', fontWeight: 900, fontSize: 'var(--fs-xl)', color: 'var(--text-1)' }}>
                {diaDetalhe.split('-').reverse().join('/')}
              </span>
              <button onClick={() => setDiaDetalhe(null)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-3)' }}>
                <LucideIcon icon="x" size={18} />
              </button>
            </div>
            {(byDate[diaDetalhe] || []).map(item => (
              <ItemChip key={`${item.fonte}-${item.ref_id}-${item.data}`} item={item}
                onOpenDerivado={i => { setDiaDetalhe(null); onOpenDerivado(i); }}
                onOpenEvento={i => { setDiaDetalhe(null); onOpenEvento(i); }} />
            ))}
          </div>
        </div>
      )}
      </>
    );
  }

  function ItemChip({ item, onOpenDerivado, onOpenEvento }) {
    const meta = FONTE_META[item.fonte] || {};
    const color = item.concluido ? 'var(--clr-pos)' : (item.categoria_cor || '#94a3b8');
    const ehEvento = item.fonte === 'evento';
    const clicavel = !!meta.tela || ehEvento;
    return (
      <div onClick={clicavel ? () => (ehEvento ? onOpenEvento(item) : onOpenDerivado(item)) : undefined}
        style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 8px',
          borderRadius: 'var(--r-sm)', background: `${color}18`, border: `1px solid ${color}33`,
          cursor: clicavel ? 'pointer' : 'default', overflow: 'hidden' }}
        onMouseEnter={e => { if (clicavel) e.currentTarget.style.background = `${color}32`; }}
        onMouseLeave={e => { e.currentTarget.style.background = `${color}18`; }}>
        <LucideIcon icon={item.concluido ? 'check-circle' : (meta.icon || 'circle')} size={12} style={{ color, flexShrink: 0 }} />
        {item.hora && (
          <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700, color, opacity: .85, flexShrink: 0 }}>
            {item.hora.slice(0, 5)}
          </span>
        )}
        <span style={{ fontSize: 'var(--fs-sm)', fontFamily: 'Roboto,sans-serif', fontWeight: 700, color,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', flex: 1 }}>
          {item.titulo}
        </span>
        <IniciaisAvatar nome={item.responsavel_nome} cor={item.responsavel_cor} size={16} />
      </div>
    );
  }

  function NavData({ label, onPrev, onNext }) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 16, padding: '12px 0 16px' }}>
        <button onClick={onPrev}
          style={{ width: 30, height: 30, borderRadius: 'var(--r-sm)', border: '1px solid var(--app-border)',
            background: 'var(--overlay-05)', color: 'var(--text-2)', cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <LucideIcon icon="chevron-left" size={16} />
        </button>
        <span style={{ fontSize: 'var(--fs-xl)', fontFamily: 'Roboto,sans-serif', fontWeight: 900,
          color: 'var(--text-1)', minWidth: 200, textAlign: 'center', textTransform: 'capitalize' }}>
          {label}
        </span>
        <button onClick={onNext}
          style={{ width: 30, height: 30, borderRadius: 'var(--r-sm)', border: '1px solid var(--app-border)',
            background: 'var(--overlay-05)', color: 'var(--text-2)', cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <LucideIcon icon="chevron-right" size={16} />
        </button>
      </div>
    );
  }

  function inicioSemanaISO(iso) {
    const dow = new Date(paraDataUTC(iso)).getUTCDay(); // 0=domingo...6=sábado
    const diff = (dow + 6) % 7; // dias desde a última segunda-feira
    return addDiasISO(iso, -diff);
  }

  /* ── Grade de semana ──────────────────────────────────────────*/
  function AgendaSemana({ items, eventosRaw, categorias, membros, onOpenDerivado, onOpenEvento, onNovoEventoData, onDropEvento }) {
    const [inicio, setInicio] = useState(() => inicioSemanaISO(hojeISO()));
    const [dragItem, setDragItem] = useState(null);
    const [dragOverDate, setDragOverDate] = useState(null);

    const dias = Array.from({ length: 7 }, (_, i) => addDiasISO(inicio, i));
    const janelaFim = dias[6];

    const todosItens = useItensDaJanela(items, eventosRaw, categorias, membros, inicio, janelaFim);
    const byDate = agruparPorData(todosItens);
    const todayStr = hojeISO();

    const label = `${dias[0].split('-').reverse().join('/')} a ${dias[6].split('-').reverse().join('/')}`;

    return (
      <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', padding: '0 20px 16px' }}>
        <NavData label={label} onPrev={() => setInicio(d => addDiasISO(d, -7))} onNext={() => setInicio(d => addDiasISO(d, 7))} />
        <div style={{ flex: 1, display: 'grid', gridTemplateColumns: 'repeat(7,1fr)', gridTemplateRows: '1fr', gap: 6, minHeight: 0 }}>
          {dias.map((dateStr, i) => {
            const isWeekend = i === 5 || i === 6;
            const isToday = dateStr === todayStr;
            const dayItems = (byDate[dateStr] || []).sort((a, b) => (a.hora || '99:99').localeCompare(b.hora || '99:99'));
            const isDragOver = dragOverDate === dateStr;
            return (
              <div key={dateStr}
                onClick={() => onNovoEventoData(dateStr)}
                onDragOver={e => { e.preventDefault(); if (dragOverDate !== dateStr) setDragOverDate(dateStr); }}
                onDragLeave={() => setDragOverDate(prev => prev === dateStr ? null : prev)}
                onDrop={e => {
                  e.preventDefault(); setDragOverDate(null);
                  if (dragItem && dateStr !== dragItem.data) onDropEvento(dragItem, dateStr);
                  setDragItem(null);
                }}
                style={{ border: `1px solid ${isDragOver ? 'rgba(234,170,65,.7)' : isToday ? 'rgba(234,170,65,.45)' : 'var(--app-border)'}`,
                  borderRadius: 'var(--r-sm)', padding: 8, minHeight: 0, display: 'flex', flexDirection: 'column', gap: 6, overflowY: 'auto',
                  cursor: 'pointer', background: isDragOver ? 'rgba(234,170,65,.1)' : isToday ? 'rgba(234,170,65,.06)' : (isWeekend ? 'var(--overlay-05)' : 'var(--app-surface-2)') }}>
                <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 900, letterSpacing: '0.05em',
                  color: isToday ? 'var(--accent)' : 'var(--text-3)', textTransform: 'uppercase', flexShrink: 0 }}>
                  {DIAS_SEMANA_GRID[i]} {dateStr.slice(8,10)}
                </span>
                {dayItems.map(item => (
                  <div key={`${item.fonte}-${item.ref_id}-${item.data}`}
                    draggable={item.fonte === 'evento'}
                    onDragStart={item.fonte === 'evento' ? (e => { e.stopPropagation(); setDragItem(item); e.dataTransfer.effectAllowed = 'move'; }) : undefined}
                    onDragEnd={() => setDragItem(null)}
                    onClick={e => e.stopPropagation()}>
                    <ItemChip item={item} onOpenDerivado={onOpenDerivado} onOpenEvento={onOpenEvento} />
                  </div>
                ))}
                {dayItems.length === 0 && (
                  <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', color: 'var(--text-3)', marginTop: 'auto', textAlign: 'center' }}>+ novo evento</span>
                )}
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  /* ── Um dia só ────────────────────────────────────────────────*/
  function AgendaDia({ items, eventosRaw, categorias, membros, onOpenDerivado, onOpenEvento, onNovoEventoData }) {
    const [dataAtual, setDataAtual] = useState(hojeISO());
    const todosItens = useItensDaJanela(items, eventosRaw, categorias, membros, dataAtual, dataAtual);
    const doDia = (byDateOf(todosItens, dataAtual)).sort((a, b) => (a.hora || '99:99').localeCompare(b.hora || '99:99'));

    const dt = new Date(paraDataUTC(dataAtual));
    const label = `${DIAS_SEMANA[dt.getUTCDay()]}, ${dataAtual.split('-').reverse().join('/')}`;

    return (
      <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', padding: '0 20px 16px' }}>
        <NavData label={label} onPrev={() => setDataAtual(d => addDiasISO(d, -1))} onNext={() => setDataAtual(d => addDiasISO(d, 1))} />
        <div style={{ flex: 1, minHeight: 0, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 8, maxWidth: 480, margin: '0 auto', width: '100%' }}>
          {doDia.map(item => (
            <ItemChip key={`${item.fonte}-${item.ref_id}-${item.data}`} item={item} onOpenDerivado={onOpenDerivado} onOpenEvento={onOpenEvento} />
          ))}
          {doDia.length === 0 && (
            <div style={{ textAlign: 'center', color: 'var(--text-3)', fontSize: 'var(--fs-lg)', padding: 40 }}>
              Nada marcado pra este dia.
            </div>
          )}
          <Btn variant="ghost" icon="plus" onClick={() => onNovoEventoData(dataAtual)}>Novo evento neste dia</Btn>
        </div>
      </div>
    );
  }
  function byDateOf(itens, dateStr) {
    return itens.filter(i => {
      if (!i.data) return false;
      const inicio = i.data.slice(0, 10);
      const fim = i.data_fim ? i.data_fim.slice(0, 10) : inicio;
      return dateStr >= inicio && dateStr <= fim;
    });
  }

  /* ── Lista (agenda cronológica) ───────────────────────────────*/
  function AgendaListaView({ items, eventosRaw, categorias, membros, onOpenDerivado, onOpenEvento }) {
    const today = new Date();
    const [ano, setAno] = useState(today.getFullYear());
    const [mes, setMes] = useState(today.getMonth());
    const diasNoMes = new Date(ano, mes + 1, 0).getDate();
    const janelaInicio = `${ano}-${String(mes + 1).padStart(2, '0')}-01`;
    const janelaFim = `${ano}-${String(mes + 1).padStart(2, '0')}-${String(diasNoMes).padStart(2, '0')}`;

    const todosItens = useItensDaJanela(items, eventosRaw, categorias, membros, janelaInicio, janelaFim);
    const byDate = agruparPorData(todosItens);
    const datasOrdenadas = Object.keys(byDate).sort();

    const prevMes = () => { if (mes === 0) { setMes(11); setAno(a => a - 1); } else setMes(m => m - 1); };
    const nextMes = () => { if (mes === 11) { setMes(0); setAno(a => a + 1); } else setMes(m => m + 1); };

    return (
      <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', padding: '0 20px 16px' }}>
        <NavData label={`${MESES_PT[mes]} ${ano}`} onPrev={prevMes} onNext={nextMes} />
        <div style={{ flex: 1, minHeight: 0, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 560, margin: '0 auto', width: '100%' }}>
          {datasOrdenadas.map(dateStr => {
            const dt = new Date(paraDataUTC(dateStr));
            const isToday = dateStr === hojeISO();
            const itensDia = byDate[dateStr].sort((a, b) => (a.hora || '99:99').localeCompare(b.hora || '99:99'));
            return (
              <div key={dateStr} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                <span style={{ fontSize: 'var(--fs-sm)', fontFamily: 'Roboto,sans-serif', fontWeight: 900, letterSpacing: '0.06em',
                  textTransform: 'uppercase', color: isToday ? 'var(--accent)' : 'var(--text-3)' }}>
                  {DIAS_SEMANA[dt.getUTCDay()]}, {dateStr.split('-').reverse().join('/')}{isToday ? ' · hoje' : ''}
                </span>
                {itensDia.map(item => (
                  <ItemChip key={`${item.fonte}-${item.ref_id}-${item.data}`} item={item} onOpenDerivado={onOpenDerivado} onOpenEvento={onOpenEvento} />
                ))}
              </div>
            );
          })}
          {datasOrdenadas.length === 0 && (
            <div style={{ textAlign: 'center', color: 'var(--text-3)', fontSize: 'var(--fs-lg)', padding: 40 }}>Nada marcado neste mês.</div>
          )}
        </div>
      </div>
    );
  }

  /* ── Tela ─────────────────────────────────────────────────────*/
  function AgendaScreen() {
    const [items, setItems] = useState([]);
    const [eventosRaw, setEventosRaw] = useState([]);
    const [tarefasRaw, setTarefasRaw] = useState([]);
    const [membros, setMembros] = useState([]);
    const [categorias, setCategorias] = useState([]);
    const [empresaTags, setEmpresaTags] = useState([]);
    const [loading, setLoading] = useState(true);

    const [modoPrincipal, setModoPrincipal] = useState('calendario'); // 'calendario' | 'tarefas'
    const [modoCalendario, setModoCalendario] = useState('mes'); // 'mes' | 'semana' | 'dia' | 'lista'
    const [viewModoTarefas, setViewModoTarefas] = useState('kanban'); // 'kanban' | 'lista'
    const [ordenarPor, setOrdenarPor] = useState('prazo');
    const [statusFiltro, setStatusFiltro] = useState('pendentes'); // 'todas' | 'pendentes' | 'concluidas'

    const [fontesAtivas, setFontesAtivas] = useState(new Set(TODAS_FONTES));
    const [pessoaFiltro, setPessoaFiltro] = useState('todos');
    const [categoriaFiltro, setCategoriaFiltro] = useState('todas');

    const [eventoModal, setEventoModal] = useState(null); // { escopo, item, pai?, ocorrenciaOriginal? } | null
    const [escopoChooser, setEscopoChooser] = useState(null); // item de ocorrência de série | null
    const [taskModal, setTaskModal] = useState(null); // item da tarefa (ou {} pra nova) | null

    async function load() {
      setLoading(true);
      const [{ data: feed }, { data: eventos }, { data: tarefas }, { data: m }, { data: c }] = await Promise.all([
        window.db.from('crm_agenda_feed').select('*'),
        window.db.from('crm_agenda_events').select('*'),
        window.db.from('crm_agenda_tasks').select('*'),
        window.db.from('crm_team_members').select('*').eq('ativo', true).order('ordem'),
        window.db.from('crm_agenda_categories').select('*').eq('ativo', true).order('ordem'),
      ]);
      setItems((feed || []).filter(i => i.fonte !== 'evento' && i.fonte !== 'tarefa'));
      setEventosRaw(eventos || []);
      setTarefasRaw(tarefas || []);
      setMembros(m || []);
      setCategorias(c || []);
      setLoading(false);
    }
    useEffect(() => { load(); }, []);
    useEffect(() => {
      if (!window.db) return;
      window.db.from('crm_company_tags').select('*').eq('ativo', true).order('ordem')
        .then(({ data }) => setEmpresaTags(data || []));
    }, []);

    const categoriasPorId = useMemo(() => new Map(categorias.map(c => [c.id, c])), [categorias]);
    const membrosPorId = useMemo(() => new Map(membros.map(m => [m.id, m])), [membros]);

    function toggleFonte(f) {
      setFontesAtivas(prev => {
        const next = new Set(prev);
        if (next.has(f)) next.delete(f); else next.add(f);
        return next;
      });
    }

    // Regra do "Comum": item sem responsável aparece em QUALQUER filtro de
    // pessoa (além do próprio filtro "Comum"), pra nunca sumir sem dono.
    function passaFiltroPessoaCategoria(categoriaId, responsavelId) {
      if (categoriaFiltro !== 'todas' && categoriaId !== categoriaFiltro) return false;
      if (pessoaFiltro === 'comum') return !responsavelId;
      if (pessoaFiltro !== 'todos') return !responsavelId || responsavelId === pessoaFiltro;
      return true;
    }

    const filtered = useMemo(() => items.filter(item =>
      fontesAtivas.has(item.fonte) && passaFiltroPessoaCategoria(item.categoria_id, item.responsavel_id)
    ), [items, fontesAtivas, pessoaFiltro, categoriaFiltro]);

    const eventosFiltrados = useMemo(() => {
      if (!fontesAtivas.has('evento')) return [];
      return eventosRaw.filter(e => passaFiltroPessoaCategoria(e.categoria_id, e.responsavel_id));
    }, [eventosRaw, fontesAtivas, pessoaFiltro, categoriaFiltro]);

    // Tarefas enriquecidas com nome/cor da categoria e do responsável (a
    // tabela só guarda o id de cada um) — mesmo padrão do gerarItensEventos.
    const tarefasFiltradas = useMemo(() => tarefasRaw
      .filter(t => passaFiltroPessoaCategoria(t.categoria_id, t.responsavel_id))
      .filter(t => statusFiltro === 'todas' ? true : statusFiltro === 'concluidas' ? t.status === 'Feito' : t.status !== 'Feito')
      .map(t => {
        const cat = t.categoria_id ? categoriasPorId.get(t.categoria_id) : null;
        const mem = t.responsavel_id ? membrosPorId.get(t.responsavel_id) : null;
        return { ...t, categoria_nome: cat?.nome || null, categoria_cor: cat?.cor || null,
          responsavel_nome: mem?.nome || null, responsavel_cor: mem?.cor || null };
      }), [tarefasRaw, pessoaFiltro, categoriaFiltro, statusFiltro, categoriasPorId, membrosPorId]);

    function abrirDerivado(item) {
      const meta = FONTE_META[item.fonte];
      if (!meta?.tela || !window.khronusNavegar) return;
      window.khronusNavegar(meta.tela, { tipo: item.fonte, id: item.ref_id });
    }

    function abrirEvento(item) {
      if (item._kind === 'serie') { setEscopoChooser(item); return; }
      // 'simples' ou 'excecao': edita a linha diretamente, sem perguntar nada.
      setEventoModal({ escopo: item._kind === 'excecao' ? 'unico' : undefined, item: item._evento });
    }

    async function escolherEscopo(acao) {
      const item = escopoChooser;
      setEscopoChooser(null);
      if (acao === 'serie') {
        setEventoModal({ escopo: 'serie', item: item._pai });
      } else if (acao === 'unico') {
        setEventoModal({
          escopo: 'unico-novo',
          item: { ...item._pai, id: null, data: item.data },
          pai: item._pai, ocorrenciaOriginal: item._ocorrenciaOriginal,
        });
      } else if (acao === 'concluir') {
        await concluirEvento(item);
      } else if (acao === 'cancelar') {
        if (!confirm(`Cancelar só o dia ${item.data.split('-').reverse().join('/')} de "${item.titulo}"?`)) return;
        await window.db.from('crm_agenda_events').insert({
          titulo: item._pai.titulo, data: item.data, recorrencia: 'nenhuma',
          recorrencia_pai: item._pai.id, recorrencia_data_original: item._ocorrenciaOriginal, cancelado: true,
        });
        load();
      }
    }

    function novoEventoNaData(dateStr) {
      setEventoModal({ escopo: undefined, item: { data: dateStr } });
    }

    // Marca o evento como feito e ele some da grade (sem apagar o registro).
    // Em série recorrente sem exceção ainda pra essa ocorrência, o check cria
    // a exceção (mesmo mecanismo do "cancelar só este dia") — assim só aquele
    // dia some, as próximas ocorrências continuam aparecendo normalmente.
    async function concluirEvento(item) {
      if (item._kind === 'serie') {
        await window.db.from('crm_agenda_events').insert({
          titulo: item._pai.titulo, data: item.data, recorrencia: 'nenhuma',
          recorrencia_pai: item._pai.id, recorrencia_data_original: item._ocorrenciaOriginal, concluido: true,
        });
      } else {
        await window.db.from('crm_agenda_events').update({ concluido: true }).eq('id', item.ref_id);
      }
      load();
    }

    async function moverEvento(item, novaData) {
      // Preserva a duração do evento (se for de vários dias) ao arrastar:
      // desloca a data final junto, na mesma quantidade de dias.
      const duracao = item.data_fim ? diasEntreISO(item.data, item.data_fim) : 0;
      const novaDataFim = duracao > 0 ? addDiasISO(novaData, duracao) : null;
      if (item._kind === 'serie') {
        // Ocorrência de série sem exceção ainda: arrastar cria a exceção.
        await window.db.from('crm_agenda_events').insert({
          titulo: item._pai.titulo, descricao: item._pai.descricao, local: item._pai.local,
          categoria_id: item._pai.categoria_id, responsavel_id: item._pai.responsavel_id,
          hora_inicio: item._pai.hora_inicio, hora_fim: item._pai.hora_fim, dia_inteiro: item._pai.dia_inteiro,
          data: novaData, data_fim: novaDataFim, recorrencia: 'nenhuma',
          recorrencia_pai: item._pai.id, recorrencia_data_original: item._ocorrenciaOriginal,
        });
      } else {
        await window.db.from('crm_agenda_events').update({ data: novaData, data_fim: novaDataFim }).eq('id', item.ref_id);
      }
      load();
    }

    function novaTarefa(status) { setTaskModal({ defaultStatus: status || 'Fazer' }); }
    function abrirTarefa(t) { setTaskModal(t); }

    async function alternarStatusLista(t) {
      const novoStatus = t.status === 'Feito' ? 'Fazer' : 'Feito';
      setTarefasRaw(prev => prev.map(x => x.id === t.id ? { ...x, status: novoStatus } : x));
      await window.db.from('crm_agenda_tasks')
        .update({ status: novoStatus, concluida_em: novoStatus === 'Feito' ? new Date().toISOString() : null })
        .eq('id', t.id);
    }

    async function moverTarefaKanban(taskId, novoStatus, novaOrdem) {
      setTarefasRaw(prev => prev.map(x => x.id === taskId ? { ...x, status: novoStatus, ordem: novaOrdem } : x));
      await window.db.from('crm_agenda_tasks')
        .update({ status: novoStatus, ordem: novaOrdem, concluida_em: novoStatus === 'Feito' ? new Date().toISOString() : null })
        .eq('id', taskId);
    }

    return (
      <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0, overflow: 'hidden' }}>
        <TopBar title="Agenda"
          actions={
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{ display: 'flex', borderRadius: 'var(--r-sm)', overflow: 'hidden', border: '1px solid var(--app-border)' }}>
                {[{ id: 'calendario', label: 'Calendário', icon: 'calendar-days' }, { id: 'tarefas', label: 'Tarefas', icon: 'check-square' }].map(m => (
                  <button key={m.id} onClick={() => setModoPrincipal(m.id)}
                    style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 12px', border: 'none', cursor: 'pointer',
                      background: modoPrincipal === m.id ? 'rgba(234,170,65,.15)' : 'transparent',
                      color: modoPrincipal === m.id ? 'var(--accent)' : 'var(--text-3)',
                      fontSize: 'var(--fs-md)', fontFamily: 'Roboto,sans-serif', fontWeight: 700 }}>
                    <LucideIcon icon={m.icon} size={13} />{m.label}
                  </button>
                ))}
              </div>
              {modoPrincipal === 'calendario' ? (
                <Btn variant="primary" icon="plus" onClick={() => novoEventoNaData(hojeISO())}>Novo evento</Btn>
              ) : (
                <Btn variant="primary" icon="plus" onClick={novaTarefa}>Nova tarefa</Btn>
              )}
            </div>
          } />

        <div style={{ padding: '8px 24px', borderBottom: '1px solid var(--app-border)',
          background: 'var(--app-bg)', display: 'flex', alignItems: 'center', gap: 16,
          flexShrink: 0, flexWrap: 'wrap' }}>
          {modoPrincipal === 'calendario' && (
            <>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
                  letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-3)' }}>Ver</span>
                {[{ id: 'mes', label: 'Mês' }, { id: 'semana', label: 'Semana' }, { id: 'dia', label: 'Dia' }, { id: 'lista', label: 'Lista' }].map(o => (
                  <FiltroPill key={o.id} label={o.label} active={modoCalendario === o.id} onClick={() => setModoCalendario(o.id)} />
                ))}
              </div>
              <div style={{ width: 1, height: 20, background: 'var(--app-border)', flexShrink: 0 }} />
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
                  letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-3)' }}>Fonte</span>
                <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                  {TODAS_FONTES.map(f => (
                    <FiltroPill key={f} label={FONTE_META[f].label} active={fontesAtivas.has(f)} onClick={() => toggleFonte(f)} />
                  ))}
                </div>
              </div>
              <div style={{ width: 1, height: 20, background: 'var(--app-border)', flexShrink: 0 }} />
            </>
          )}

          {modoPrincipal === 'tarefas' && (
            <>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
                  letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-3)' }}>Ver</span>
                <FiltroPill label="Kanban" active={viewModoTarefas === 'kanban'} onClick={() => setViewModoTarefas('kanban')} />
                <FiltroPill label="Lista" active={viewModoTarefas === 'lista'} onClick={() => setViewModoTarefas('lista')} />
              </div>
              <div style={{ width: 1, height: 20, background: 'var(--app-border)', flexShrink: 0 }} />

              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
                  letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-3)' }}>Ordenar por</span>
                <SeletorSimples value={ordenarPor} onChange={setOrdenarPor} options={ORDENACAO_OPCOES} />
              </div>

              <div style={{ width: 1, height: 20, background: 'var(--app-border)', flexShrink: 0 }} />

              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
                  letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-3)' }}>Status</span>
                <FiltroPill label="Pendentes" active={statusFiltro === 'pendentes'} onClick={() => setStatusFiltro('pendentes')} />
                <FiltroPill label="Concluídas" active={statusFiltro === 'concluidas'} onClick={() => setStatusFiltro('concluidas')} />
                <FiltroPill label="Todas" active={statusFiltro === 'todas'} onClick={() => setStatusFiltro('todas')} />
              </div>
              <div style={{ width: 1, height: 20, background: 'var(--app-border)', flexShrink: 0 }} />
            </>
          )}

          <AvatarFiltroPessoa membros={membros} value={pessoaFiltro} onChange={setPessoaFiltro} />

          <div style={{ width: 1, height: 20, background: 'var(--app-border)', flexShrink: 0 }} />

          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <span style={{ fontSize: 'var(--fs-xs)', fontFamily: 'Roboto,sans-serif', fontWeight: 700,
              letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-3)' }}>Categoria</span>
            <SeletorSimples value={categoriaFiltro} onChange={setCategoriaFiltro}
              options={[{ value: 'todas', label: 'Todas' }, ...categorias.map(c => ({ value: c.id, label: c.nome }))]} />
          </div>
        </div>

        {loading ? (
          <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: 'var(--text-3)', fontSize: 'var(--fs-lg)' }}>Carregando…</div>
        ) : modoPrincipal === 'calendario' ? (
          modoCalendario === 'mes' ? (
            <AgendaMes items={filtered} eventosRaw={eventosFiltrados} categorias={categorias} membros={membros}
              onOpenDerivado={abrirDerivado} onOpenEvento={abrirEvento}
              onNovoEventoData={novoEventoNaData} onDropEvento={moverEvento} />
          ) : modoCalendario === 'semana' ? (
            <AgendaSemana items={filtered} eventosRaw={eventosFiltrados} categorias={categorias} membros={membros}
              onOpenDerivado={abrirDerivado} onOpenEvento={abrirEvento}
              onNovoEventoData={novoEventoNaData} onDropEvento={moverEvento} />
          ) : modoCalendario === 'dia' ? (
            <AgendaDia items={filtered} eventosRaw={eventosFiltrados} categorias={categorias} membros={membros}
              onOpenDerivado={abrirDerivado} onOpenEvento={abrirEvento}
              onNovoEventoData={novoEventoNaData} />
          ) : (
            <AgendaListaView items={filtered} eventosRaw={eventosFiltrados} categorias={categorias} membros={membros}
              onOpenDerivado={abrirDerivado} onOpenEvento={abrirEvento} />
          )
        ) : viewModoTarefas === 'kanban' ? (
          <TarefasKanban tarefas={tarefasFiltradas} onOpen={abrirTarefa} onMover={moverTarefaKanban} ordenarPor={ordenarPor} empresaTags={empresaTags} onAddNew={novaTarefa} />
        ) : (
          <TarefasLista tarefas={tarefasFiltradas} onOpen={abrirTarefa} onToggle={alternarStatusLista} ordenarPor={ordenarPor} />
        )}

        {escopoChooser && (
          <EscopoChooser item={escopoChooser} onEscolher={escolherEscopo} onClose={() => setEscopoChooser(null)} />
        )}

        {taskModal && (
          <TaskModal item={taskModal.id ? taskModal : null} defaultStatus={taskModal.defaultStatus} categorias={categorias} membros={membros} empresaTags={empresaTags}
            onClose={() => setTaskModal(null)}
            onSaved={() => { setTaskModal(null); load(); }}
            onDeleted={() => { setTaskModal(null); load(); }} />
        )}

        {eventoModal && (
          <EventoModal modal={eventoModal} categorias={categorias} membros={membros}
            onClose={() => setEventoModal(null)}
            onSaved={() => { setEventoModal(null); load(); }}
            onDeleted={() => { setEventoModal(null); load(); }} />
        )}
      </div>
    );
  }

  window.AgendaScreen = AgendaScreen;
})();
