/* ============================================================
   POP Cadastro — App principal
   Split-view: formulário à esquerda, preview ao vivo à direita.
   Tema "institucional Anchieta" + tokens Kerdos refinados.
   ============================================================ */

const { useState, useEffect, useRef, useCallback, useMemo } = React;
const RepositoryView = window.RepositoryView;

// ---------- Constantes ----------
// STORAGE_KEY e THEME_KEY ficam no localStorage (rascunho e preferência pessoal).
// O histórico e o arquivo de PDFs vão para o Supabase (compartilhado).
const STORAGE_KEY      = 'pop_form_draft_v1';
const THEME_KEY        = 'pop_theme_v1'; // 'light' | 'dark'

const EMPTY_FORM = {
  nomeAtividade: '',
  versao: '1.0',
  dataPrimeira: new Date().toISOString().slice(0, 10),
  dataRevisao: '',
  autor: '',
  validador: '',
  setor: 'pos',
  objetivo: '',
  responsavel: 'Pós-Graduação UniAnchieta',
  local: '',
  procedimentos: [{ id: 'p1', text: '', imagens: [], videos: [], links: [] }]
};



// ---------- Utilidades ----------
function formatDateBR(iso) {
  if (!iso) return '';
  const p = iso.split('-');
  return p.length === 3 ? `${p[2]}/${p[1]}/${p[0]}` : iso;
}

function uid() { return Math.random().toString(36).slice(2, 9); }

// ---------- Hook: form state com autosave ----------
function useForm() {
  const [data, setData] = useState(() => {
    try {
      const saved = localStorage.getItem(STORAGE_KEY);
      if (saved) return migrateData({ ...EMPTY_FORM, ...JSON.parse(saved) });
    } catch (e) {}
    return EMPTY_FORM;
  });
  const [savedAt, setSavedAt] = useState(null);

  useEffect(() => {
    const t = setTimeout(() => {
      try {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
        setSavedAt(new Date());
      } catch (e) {}
    }, 400);
    return () => clearTimeout(t);
  }, [data]);

  const set = useCallback((field, value) => {
    setData(d => ({ ...d, [field]: value }));
  }, []);

  const reset = useCallback(() => {
    setData(EMPTY_FORM);
    localStorage.removeItem(STORAGE_KEY);
  }, []);

  const load = useCallback((newData) => {
    setData(migrateData({ ...EMPTY_FORM, ...newData }));
  }, []);

  return { data, set, setData, reset, load, savedAt };
}

// Migra dados antigos (procedimentos como string + anexos no topo) para novo formato
function migrateData(d) {
  const next = { ...d };
  const steps = next.procedimentos || [];
  const isOld = steps.length > 0 && typeof steps[0] === 'string';
  if (isOld) {
    next.procedimentos = steps.map((s, i) => ({
      id: 'p' + i + Math.random().toString(36).slice(2, 6),
      text: s,
      imagens: [], videos: [], links: []
    }));
    // Anexos antigos do topo entram no primeiro passo (se houver)
    if (next.procedimentos[0]) {
      if (Array.isArray(d.imagens) && d.imagens.length) next.procedimentos[0].imagens = d.imagens;
      if (Array.isArray(d.videos)  && d.videos.length)  next.procedimentos[0].videos  = d.videos;
      if (Array.isArray(d.links)   && d.links.length)   next.procedimentos[0].links   = d.links;
    }
  }
  // Garante shape mesmo em itens novos sem campos
  next.procedimentos = (next.procedimentos || []).map((s, i) => ({
    id: s.id || 'p' + i,
    text: s.text || '',
    imagens: s.imagens || [],
    videos:  s.videos  || [],
    links:   s.links   || []
  }));
  if (!next.procedimentos.length) {
    next.procedimentos = [{ id: 'p0', text: '', imagens: [], videos: [], links: [] }];
  }
  // Remove campos legados do topo
  delete next.imagens; delete next.videos; delete next.links;
  return next;
}

// ---------- Hook: histórico (Supabase) ----------
// Lê e escreve na tabela pop_history. Compartilhado entre todos os usuários.
// Em caso de falha de rede, mostra a lista vazia mas não derruba a app.
function useHistory() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);

  // Carrega ao montar — espera o supabaseClient ficar pronto se ainda não estiver
  useEffect(() => {
    let cancelled = false;

    const load = async () => {
      const client = window.supabaseClient;
      if (!client) { setLoading(false); return; }
      const list = await client.history.list();
      if (!cancelled) {
        setItems(list);
        setLoading(false);
      }
    };

    if (window.supabaseClient) {
      load();
    } else {
      // Cliente ainda não inicializou (CDN async) — escuta o evento
      const onReady = () => { if (!cancelled) load(); };
      window.addEventListener('supabaseClientReady', onReady);
      return () => {
        cancelled = true;
        window.removeEventListener('supabaseClientReady', onReady);
      };
    }
    return () => { cancelled = true; };
  }, []);

  const save = useCallback(async (data) => {
    const client = window.supabaseClient;
    if (!client) return null;
    const entry = await client.history.add(data);
    if (entry) setItems(prev => [entry, ...prev]);
    return entry;
  }, []);

  const remove = useCallback(async (id) => {
    const client = window.supabaseClient;
    if (!client) return;
    const ok = await client.history.remove(id);
    if (ok) setItems(prev => prev.filter(x => x.id !== id));
  }, []);

  return { items, save, remove, loading };
}

// ---------- Hook: arquivo de PDFs (Supabase + Storage) ----------
// Cada item: { id, title, setor, savedAt, size, mime, storagePath, publicUrl }
// O PDF físico vive no bucket "pop-arquivos" do Supabase Storage.
// Os metadados ficam na tabela pop_archived. Compartilhado entre todos.
function useArchive() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);

  // Carrega ao montar — espera o supabaseClient ficar pronto se ainda não estiver
  useEffect(() => {
    let cancelled = false;

    const load = async () => {
      const client = window.supabaseClient;
      if (!client) { setLoading(false); return; }
      const list = await client.archive.list();
      if (!cancelled) {
        setItems(list);
        setLoading(false);
      }
    };

    if (window.supabaseClient) {
      load();
    } else {
      const onReady = () => { if (!cancelled) load(); };
      window.addEventListener('supabaseClientReady', onReady);
      return () => {
        cancelled = true;
        window.removeEventListener('supabaseClientReady', onReady);
      };
    }
    return () => { cancelled = true; };
  }, []);

  // Recebe { title, setor, file (File object) }.
  // O dataUrl que vinha do front-end agora não é usado — o cliente
  // Supabase faz upload do File diretamente pro Storage.
  const add = useCallback(async ({ title, setor, file }) => {
    const client = window.supabaseClient;
    if (!client) return null;
    const entry = await client.archive.add({ title, setor, file });
    if (entry) setItems(prev => [entry, ...prev]);
    return entry;
  }, []);

  const remove = useCallback(async (id) => {
    const client = window.supabaseClient;
    if (!client) return;
    // Encontra o item completo (precisa do storagePath pra remover do bucket)
    const item = items.find(x => x.id === id);
    if (!item) return;
    const ok = await client.archive.remove(item);
    if (ok) setItems(prev => prev.filter(x => x.id !== id));
  }, [items]);

  return { items, add, remove, loading };
}

// ---------- Hook: tema (claro/escuro) ----------
// Aplica data-theme em <html> e persiste em localStorage.
// O index.html tem um inline-script que aplica o tema ANTES do React
// montar — evita o "flash" de cor errada no primeiro paint.
function useTheme() {
  const [theme, setThemeState] = useState(() => {
    try {
      const saved = localStorage.getItem(THEME_KEY);
      if (saved === 'dark' || saved === 'light') return saved;
    } catch (e) {}
    // Fallback: respeita preferência do SO se nunca escolheu
    if (typeof window !== 'undefined' && window.matchMedia) {
      return window.matchMedia('(prefers-color-scheme: dark)').matches
        ? 'dark' : 'light';
    }
    return 'light';
  });

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    try { localStorage.setItem(THEME_KEY, theme); } catch (e) {}
  }, [theme]);

  const toggle = useCallback(() => {
    setThemeState(t => t === 'dark' ? 'light' : 'dark');
  }, []);

  return { theme, toggle, setTheme: setThemeState };
}

// ---------- Validação ----------
function validate(data) {
  const errs = {};
  if (!data.nomeAtividade.trim()) errs.nomeAtividade = 'Informe o nome da atividade.';
  if (!data.versao.trim()) errs.versao = 'Informe a versão.';
  if (!data.dataPrimeira) errs.dataPrimeira = 'Informe a data da 1ª versão.';
  if (!data.autor.trim()) errs.autor = 'Informe o autor (nome e e-mail).';
  if (!data.validador.trim()) errs.validador = 'Informe o validador.';
  if (!data.setor) errs.setor = 'Selecione o setor responsável.';
  if (!data.objetivo.trim()) errs.objetivo = 'Descreva o objetivo.';
  if (!data.responsavel.trim()) errs.responsavel = 'Informe o responsável.';
  if (!data.local.trim()) errs.local = 'Informe o local.';
  const proc = (data.procedimentos || []).filter(s => (s.text || '').trim());
  if (!proc.length) errs.procedimentos = 'Adicione pelo menos um passo.';
  return errs;
}

// ---------- Ícones ----------
const Icon = ({ name, size = 18 }) => {
  const paths = {
    'file-text': 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8',
    'download':  'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 M7 10l5 5 5-5 M12 15V3',
    'eye':       'M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z M12 12m-3 0a3 3 0 1 0 6 0 3 3 0 1 0-6 0',
    'check':     'M20 6L9 17l-5-5',
    'plus':      'M12 5v14 M5 12h14',
    'trash':     'M3 6h18 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',
    'history':   'M3 12a9 9 0 1 0 3-6.7L3 8 M3 3v5h5 M12 7v5l4 2',
    'sparkles':  'M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z',
    'arrow-up-right':  'M7 17L17 7 M7 7h10v10',
    'menu':      'M3 12h18 M3 6h18 M3 18h18',
    'x':         'M18 6L6 18 M6 6l12 12',
    'alert':     'M12 9v4 M12 17h.01 M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z',
    'grip':      'M9 5h.01 M9 12h.01 M9 19h.01 M15 5h.01 M15 12h.01 M15 19h.01',
    'arrow-up':  'M12 19V5 M5 12l7-7 7 7',
    'arrow-down':'M12 5v14 M19 12l-7 7-7-7',
    'rotate':    'M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8 M21 3v5h-5 M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16 M8 16H3v5',
    'image':     'M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z M9 11a2 2 0 1 0 0-4 2 2 0 0 0 0 4z M21 15l-5-5L5 21',
    'video':     'M23 7l-7 5 7 5z M3 5a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z',
    'link':      'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72 M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72',
    'paperclip': 'M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48',
    'chevron-down': 'M6 9l6 6 6-6',
    'sparkle-doc': 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6',
    'sun':         'M12 3v2 M12 19v2 M5.6 5.6l1.4 1.4 M17 17l1.4 1.4 M3 12h2 M19 12h2 M5.6 18.4l1.4-1.4 M17 7l1.4-1.4 M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',
    'moon':        'M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z',
    'user':        'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2 M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',
    'log-out':     'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4 M16 17l5-5-5-5 M21 12H9'
  };
  const d = paths[name] || '';
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor"
         strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      {d.split(' M').map((seg, i) => (
        <path key={i} d={(i === 0 ? '' : 'M') + seg} />
      ))}
    </svg>
  );
};

// ---------- Field components ----------
const Field = ({ label, hint, error, children, required }) => (
  <div className={"field" + (error ? ' field--error' : '')}>
    <label className="field__label">
      {label}{required && <span className="field__req">*</span>}
    </label>
    {children}
    {error && <div className="field__error"><Icon name="alert" size={13}/> {error}</div>}
    {hint && !error && <div className="field__hint">{hint}</div>}
  </div>
);

const Input = ({ value, onChange, ...rest }) => (
  <input className="input" value={value || ''} onChange={e => onChange(e.target.value)} {...rest} />
);

const Textarea = ({ value, onChange, rows = 3, ...rest }) => (
  <textarea className="input input--area" rows={rows} value={value || ''}
            onChange={e => onChange(e.target.value)} {...rest} />
);

// ---------- Theme toggle (sol/lua) ----------
const ThemeToggle = ({ theme, onToggle }) => {
  const [switching, setSwitching] = useState(false);
  const isDark = theme === 'dark';
  const handleClick = () => {
    setSwitching(true);
    onToggle();
    setTimeout(() => setSwitching(false), 380);
  };
  return (
    <button
      type="button"
      className={"theme-toggle" + (switching ? ' is-switching' : '')}
      onClick={handleClick}
      aria-label={isDark ? 'Mudar para tema claro' : 'Mudar para tema escuro'}
      title={isDark ? 'Tema claro' : 'Tema escuro'}
    >
      <span className="theme-toggle__icon" key={theme}>
        <Icon name={isDark ? 'sun' : 'moon'} size={16}/>
      </span>
    </button>
  );
};

// ---------- User menu (avatar com email + logout) ----------
const UserMenu = ({ session }) => {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);

  // user pode vir tanto em session.user quanto na raiz, dependendo da versão do SDK
  const user  = session?.user || session || {};
  const email = user.email || '';
  const name  = user.user_metadata?.full_name || '';
  const displayName = name || email || 'Usuário';

  // Iniciais pro avatar (max 2)
  const initials = (name || email || '?')
    .split(/[\s@.]+/)
    .filter(Boolean)
    .slice(0, 2)
    .map(p => p[0].toUpperCase())
    .join('') || '?';

  // Fecha ao clicar fora ou pressionar Esc
  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (ref.current && !ref.current.contains(e.target)) setOpen(false);
    };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  async function handleLogout() {
    setOpen(false);
    const client = window.supabaseClient;
    if (!client) {
      console.warn('[logout] cliente Supabase não disponível.');
      return;
    }
    // Limpa o flag de seed pra que, se o usuário entrar de novo, o seed
    // refaça normalmente (relevante quando há trocas de conta no mesmo browser).
    try { localStorage.removeItem('pop_history_seeded_v1'); } catch (e) {}
    await client.auth.signOut();
    // O Root escuta onAuthChange e volta automaticamente pra AuthView.
  }

  return (
    <div className="user-menu" ref={ref}>
      <button
        type="button"
        className={"user-menu__btn" + (open ? ' is-open' : '')}
        onClick={() => setOpen(o => !o)}
        aria-haspopup="menu"
        aria-expanded={open}
        title={displayName}
      >
        <span className="user-menu__avatar" aria-hidden="true">{initials}</span>
        <span className="user-menu__name">{displayName}</span>
        <Icon name="chevron-down" size={14}/>
      </button>

      {open && (
        <div className="user-menu__pop" role="menu">
          <div className="user-menu__head">
            <div className="user-menu__avatar user-menu__avatar--lg" aria-hidden="true">{initials}</div>
            <div className="user-menu__head-text">
              {name && <div className="user-menu__head-name">{name}</div>}
              <div className="user-menu__head-email" title={email}>{email}</div>
            </div>
          </div>
          <div className="user-menu__sep" />
          <button
            type="button"
            className="user-menu__item user-menu__item--danger"
            role="menuitem"
            onClick={handleLogout}
          >
            <Icon name="log-out" size={15}/> Sair da conta
          </button>
        </div>
      )}
    </div>
  );
};

// ---------- Procedimentos editor ----------
function detectVideoProvider(url) {
  if (!url) return { provider: 'link', embed: '' };
  try {
    const u = new URL(url);
    const h = u.hostname.replace('www.', '');
    if (h.includes('youtube.com')) {
      const id = u.searchParams.get('v');
      if (id) return { provider: 'youtube', embed: 'https://www.youtube.com/embed/' + id };
    }
    if (h === 'youtu.be') {
      const id = u.pathname.slice(1);
      if (id) return { provider: 'youtube', embed: 'https://www.youtube.com/embed/' + id };
    }
    if (h.includes('vimeo.com')) {
      const id = u.pathname.split('/').filter(Boolean).pop();
      if (id) return { provider: 'vimeo', embed: 'https://player.vimeo.com/video/' + id };
    }
    if (h.includes('drive.google.com')) {
      const m = u.pathname.match(/\/file\/d\/([^/]+)/);
      if (m) return { provider: 'drive', embed: 'https://drive.google.com/file/d/' + m[1] + '/preview' };
    }
    return { provider: 'link', embed: '' };
  } catch (e) { return { provider: 'link', embed: '' }; }
}

const ProcedimentosEditor = ({ steps, onChange }) => {
  const updateStep = (i, patch) => {
    const next = [...steps]; next[i] = { ...next[i], ...patch }; onChange(next);
  };
  const add = () => onChange([...steps, { id: 'p' + uid(), text: '', imagens: [], videos: [], links: [] }]);
  const remove = (i) => {
    const next = steps.filter((_, idx) => idx !== i);
    onChange(next.length ? next : [{ id: 'p' + uid(), text: '', imagens: [], videos: [], links: [] }]);
  };
  const move = (i, dir) => {
    const j = i + dir;
    if (j < 0 || j >= steps.length) return;
    const next = [...steps];
    [next[i], next[j]] = [next[j], next[i]];
    onChange(next);
  };

  return (
    <div className="steps">
      {steps.map((s, i) => (
        <StepCard
          key={s.id || i}
          step={s}
          index={i}
          isFirst={i === 0}
          isLast={i === steps.length - 1}
          onUpdate={patch => updateStep(i, patch)}
          onMove={dir => move(i, dir)}
          onRemove={() => remove(i)}
        />
      ))}
      <button type="button" className="btn btn--ghost btn--sm" onClick={add}>
        <Icon name="plus" size={14}/> Adicionar passo
      </button>
    </div>
  );
};

const StepCard = ({ step, index, isFirst, isLast, onUpdate, onMove, onRemove }) => {
  const fileRef = useRef(null);
  const dropRef = useRef(null);
  const [activeTab, setActiveTab] = useState(null); // 'img' | 'video' | 'link' | null

  const imagens = step.imagens || [];
  const videos  = step.videos  || [];
  const links   = step.links   || [];

  // Redimensiona imagens automaticamente para 1000x800 mantendo proporção
  const resizeImage = (file) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => {
        const maxW = 1000, maxH = 800, minW = 800, minH = 600;
        let w = img.naturalWidth, h = img.naturalHeight;
        // Calcula escala para caber em 1000x800 mantendo proporção
        const ratio = Math.min(maxW / w, maxH / h, 1);
        let outW = Math.round(w * ratio);
        let outH = Math.round(h * ratio);
        // Garante mínimo (cresce se for muito pequena)
        if (outW < minW && outH < minH) {
          const grow = Math.min(minW / outW, minH / outH);
          outW = Math.round(outW * grow);
          outH = Math.round(outH * grow);
        }
        const canvas = document.createElement('canvas');
        canvas.width = outW; canvas.height = outH;
        const ctx = canvas.getContext('2d');
        ctx.fillStyle = '#fff';
        ctx.fillRect(0, 0, outW, outH);
        ctx.drawImage(img, 0, 0, outW, outH);
        const out = canvas.toDataURL(file.type === 'image/png' ? 'image/png' : 'image/jpeg', 0.88);
        resolve({ dataUrl: out, w: outW, h: outH });
      };
      img.onerror = reject;
      img.src = e.target.result;
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });

  const handleFiles = async (files) => {
    const arr = Array.from(files || []);
    const next = [...imagens];
    for (const f of arr) {
      if (!f.type.startsWith('image/')) continue;
      if (f.size > 8 * 1024 * 1024) {
        alert('Imagem "' + f.name + '" excede 8MB.');
        continue;
      }
      try {
        const { dataUrl, w, h } = await resizeImage(f);
        next.push({ id: uid(), name: f.name, dataUrl, caption: '', w, h, type: f.type });
      } catch (e) {
        console.error(e);
      }
    }
    onUpdate({ imagens: next });
    setActiveTab('img');
  };

  const updateImage = (i, patch) => {
    const next = [...imagens]; next[i] = { ...next[i], ...patch }; onUpdate({ imagens: next });
  };
  const removeImage = (i) => onUpdate({ imagens: imagens.filter((_, idx) => idx !== i) });

  const addVideo = () => { onUpdate({ videos: [...videos, { id: uid(), title: '', url: '', note: '' }] }); setActiveTab('video'); };
  const updateVideo = (i, patch) => {
    const next = [...videos];
    next[i] = { ...next[i], ...patch };
    if (patch.url !== undefined) {
      const det = detectVideoProvider(patch.url);
      next[i].provider = det.provider;
      next[i].embed = det.embed;
    }
    onUpdate({ videos: next });
  };
  const removeVideo = (i) => onUpdate({ videos: videos.filter((_, idx) => idx !== i) });

  const addLink = () => { onUpdate({ links: [...links, { id: uid(), title: '', url: '', note: '' }] }); setActiveTab('link'); };
  const updateLink = (i, patch) => {
    const next = [...links]; next[i] = { ...next[i], ...patch }; onUpdate({ links: next });
  };
  const removeLink = (i) => onUpdate({ links: links.filter((_, idx) => idx !== i) });

  const mediaCount = imagens.length + videos.length + links.length;
  const expanded = activeTab !== null;

  // Auto-abre tab se já tem mídia e ainda não escolheu nenhuma
  useEffect(() => {
    if (activeTab === null && mediaCount > 0) {
      if (imagens.length) setActiveTab('img');
      else if (videos.length) setActiveTab('video');
      else if (links.length) setActiveTab('link');
    }
  }, []); // eslint-disable-line

  return (
    <div className={"step-card" + (expanded ? ' step-card--expanded' : '')}>
      <div className="step-card__head">
        <div className="step__num">{index + 1}</div>
        <textarea
          className="input input--area step__text"
          rows={2}
          placeholder={index === 0 ? 'Ex.: Acessar o portal em portal.anchieta.br com login institucional.' : 'Próximo passo…'}
          value={step.text || ''}
          onChange={e => onUpdate({ text: e.target.value })}
        />
        <div className="step__actions">
          <button type="button" className="icon-btn" title="Mover para cima"
                  disabled={isFirst} onClick={() => onMove(-1)}>
            <Icon name="arrow-up" size={14}/>
          </button>
          <button type="button" className="icon-btn" title="Mover para baixo"
                  disabled={isLast} onClick={() => onMove(1)}>
            <Icon name="arrow-down" size={14}/>
          </button>
          <button type="button" className="icon-btn icon-btn--danger" title="Remover passo"
                  onClick={onRemove}>
            <Icon name="trash" size={14}/>
          </button>
        </div>
      </div>

      {/* Tabs de anexos */}
      <div className="att-tabs">
        <button type="button"
                className={"att-tab" + (activeTab === 'img' ? ' is-active' : '')}
                onClick={() => setActiveTab(activeTab === 'img' ? null : 'img')}>
          <Icon name="image" size={14}/>
          <span>Imagens</span>
          {imagens.length > 0 && <span className="att-tab__count">{imagens.length}</span>}
        </button>
        <button type="button"
                className={"att-tab" + (activeTab === 'link' ? ' is-active' : '')}
                onClick={() => setActiveTab(activeTab === 'link' ? null : 'link')}>
          <Icon name="link" size={14}/>
          <span>Links</span>
          {links.length > 0 && <span className="att-tab__count">{links.length}</span>}
        </button>
        <div className="att-tabs__spacer"/>
        {expanded && (
          <button type="button" className="att-close" onClick={() => setActiveTab(null)}
                  title="Recolher">
            <Icon name="x" size={14}/>
          </button>
        )}
      </div>

      {expanded && (
        <div className="att-panel">
          {activeTab === 'img' && (
            <div className="att-content">
              <div
                ref={dropRef}
                className="att-drop"
                onClick={() => fileRef.current?.click()}
                onDragOver={e => { e.preventDefault(); dropRef.current?.classList.add('is-over'); }}
                onDragLeave={() => dropRef.current?.classList.remove('is-over')}
                onDrop={e => {
                  e.preventDefault();
                  dropRef.current?.classList.remove('is-over');
                  handleFiles(e.dataTransfer.files);
                }}
              >
                <div className="att-drop__icon"><Icon name="image" size={26}/></div>
                <div className="att-drop__text">
                  <strong>Solte uma imagem aqui</strong> ou clique para escolher
                  <span>PNG, JPG · redimensionada para 1000×800 automaticamente</span>
                </div>
              </div>

              {imagens.length > 0 && (
                <div className="att-img-grid">
                  {imagens.map((img, i) => (
                    <div key={img.id} className="att-img">
                      <div className="att-img__frame">
                        <img
                          src={img.dataUrl}
                          alt={img.name}
                          style={{ cursor: 'zoom-in' }}
                          title="Clique para ampliar"
                          onClick={() => {
                            window.dispatchEvent(new CustomEvent('imageZoom', {
                              detail: { src: img.dataUrl, alt: img.caption || img.name }
                            }));
                          }}
                        />
                        <button type="button" className="att-img__remove"
                                onClick={(e) => { e.stopPropagation(); removeImage(i); }} title="Remover">
                          <Icon name="x" size={12}/>
                        </button>
                        {img.w && img.h && (
                          <span className="att-img__dim">{img.w}×{img.h}</span>
                        )}
                      </div>
                      <input className="input input--sm"
                             placeholder="Legenda (opcional)"
                             value={img.caption || ''}
                             onChange={e => updateImage(i, { caption: e.target.value })}/>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}

          {activeTab === 'link' && (
            <div className="att-content">
              {links.length === 0 && (
                <div className="att-empty">
                  <Icon name="link" size={22}/>
                  <p>Nenhum link adicionado.</p>
                </div>
              )}
              {links.map((l, i) => (
                <div key={l.id} className="att-row">
                  <div className="att-row__lead">
                    <Icon name="link" size={16}/>
                  </div>
                  <div className="att-row__body">
                    <Input placeholder="Título do link (ex.: Manual da disciplina)"
                           value={l.title} onChange={x => updateLink(i, { title: x })}/>
                    <Input placeholder="https://…"
                           value={l.url} onChange={x => updateLink(i, { url: x })}/>
                  </div>
                  <button type="button" className="icon-btn icon-btn--danger"
                          onClick={() => removeLink(i)} title="Remover">
                    <Icon name="trash" size={14}/>
                  </button>
                </div>
              ))}
              <button type="button" className="att-add" onClick={addLink}>
                <Icon name="plus" size={14}/> Adicionar link
              </button>
            </div>
          )}
        </div>
      )}

      <input ref={fileRef} type="file" accept="image/*" multiple
             style={{ display: 'none' }}
             onChange={e => { handleFiles(e.target.files); e.target.value = ''; }}/>
    </div>
  );
};

// ---------- Preview do DOC ----------
// Reproduz fielmente a estrutura visual do PDF gerado pelo POPPdfGenerator:
// • Mesma proporção A4 (210×297mm) em escala consistente
// • Mesmos SVGs de header/footer institucional do PDF
// • Mesma fonte (Helvetica), cores, espaçamentos e estrutura de seções
// • Meta-grid 3×2 + box do Objetivo + cards de passo com badge azul
// Atualização instantânea ao digitar, mas visualmente idêntico ao PDF final.
//
// Cache compartilhado dos SVGs (uma fetch só, mesmo se o preview re-renderiza)
let _previewSvgCache = null;
let _previewSvgPromise = null;
function loadPreviewSvgs() {
  if (_previewSvgCache) return Promise.resolve(_previewSvgCache);
  if (_previewSvgPromise) return _previewSvgPromise;
  _previewSvgPromise = Promise.all([
    fetch('assets/header.svg').then(r => r.text()),
    fetch('assets/footer.svg').then(r => r.text()),
  ]).then(([header, footer]) => {
    _previewSvgCache = { header, footer };
    return _previewSvgCache;
  }).catch(() => ({ header: '', footer: '' }));
  return _previewSvgPromise;
}

const DocPreview = ({ data }) => {
  const [svgs, setSvgs] = useState(_previewSvgCache || { header: '', footer: '' });

  useEffect(() => {
    if (!_previewSvgCache) {
      loadPreviewSvgs().then(setSvgs);
    }
  }, []);

  const proc = (data.procedimentos || []).filter(s => (s.text || '').trim());

  // ============================================================
  // BLOCOS — cada item é um elemento que vai entrar na esteira de
  // paginação. Cada bloco é renderizado primeiro num shadow oculto
  // pra ser medido, depois distribuído em páginas.
  // ============================================================
  const MetaCell = ({ label, value }) => (
    <div className="dp-meta-cell">
      <div className="dp-meta-cell__label">{label}</div>
      <div className={"dp-meta-cell__value" + (!value ? ' is-empty' : '')}>
        {value || '—'}
      </div>
    </div>
  );

  // Monta a lista de blocos sequenciais. Cada um tem um `key`
  // estável pra não re-medir desnecessariamente, e um `keepWith`
  // opcional pra "colar" no próximo (não usamos hoje, mas deixa
  // a porta aberta).
  const blocks = [];

  // 1) DocHead (eyebrow + título + linha dourada) — bloco coeso
  blocks.push({
    key: 'dochead',
    node: (
      <div className="dp-dochead-block">
        <div className="dp-eyebrow">
          {'Instrução de Trabalho • V' + (data.versao || '1.0')}
        </div>
        <h1 className={"dp-title" + (!data.nomeAtividade ? ' is-empty' : '')}>
          {data.nomeAtividade || 'Nome da atividade'}
        </h1>
        <div className="dp-rule-gold" />
      </div>
    ),
  });

  // 2) Meta-grid 3×2 — bloco coeso
  blocks.push({
    key: 'metagrid',
    node: (
      <div className="dp-meta-grid">
        <MetaCell label="1ª VERSÃO"      value={formatDateBR(data.dataPrimeira)} />
        <MetaCell label="ÚLTIMA REVISÃO" value={formatDateBR(data.dataRevisao)} />
        <MetaCell label="AUTOR"          value={data.autor} />
        <MetaCell label="VALIDADOR"      value={data.validador} />
        <MetaCell label="RESPONSÁVEL"    value={data.responsavel} />
        <MetaCell label="LOCAL"          value={data.local} />
      </div>
    ),
  });

  // 3) Box do Objetivo
  blocks.push({
    key: 'objetivo',
    node: (
      <div className="dp-field-box">
        <div className="dp-meta-cell__label">OBJETIVO</div>
        <div className={"dp-meta-cell__value" + (!data.objetivo ? ' is-empty' : '')}>
          {data.objetivo || '—'}
        </div>
      </div>
    ),
  });

  // 4) Título "Procedimentos" + linha dourada curta — fica colado
  //    no primeiro card para não sobrar "Procedimentos" sozinho no
  //    fim de uma página. Marcamos como keepWithNext.
  blocks.push({
    key: 'proctitle',
    keepWithNext: true,
    node: (
      <div className="dp-proc-title-wrap">
        <h2 className="dp-proc-title">Procedimentos</h2>
        <div className="dp-rule-gold dp-rule-gold--short" />
      </div>
    ),
  });

  // 5) Cada passo é um card inteiro, indivisível
  if (proc.length === 0) {
    blocks.push({
      key: 'empty-steps',
      node: <div className="dp-empty-steps">Passo a passo aparecerá aqui</div>,
    });
  } else {
    proc.forEach((s, i) => {
      const imgs = (s.imagens || []).filter(im => im && im.dataUrl);
      const links = (s.links || []).filter(l => l && l.url);
      blocks.push({
        key: 'step-' + (s.id || i),
        node: (
          <div className="dp-step-card">
            <div className="dp-step-head">
              <div className="dp-step-badge">{i + 1}</div>
              <div className="dp-step-text">{s.text}</div>
            </div>

            {imgs.map((img, j) => (
              <div className="dp-step-fig" key={img.id || j}>
                <img
                  src={img.dataUrl}
                  alt={img.caption || img.name}
                  onClick={() => {
                    window.dispatchEvent(new CustomEvent('imageZoom', {
                      detail: { src: img.dataUrl, alt: img.caption || img.name }
                    }));
                  }}
                  title="Clique para ampliar"
                />
                {img.caption && (
                  <div className="dp-step-caption">
                    Figura {i + 1}.{j + 1} — {img.caption}
                  </div>
                )}
              </div>
            ))}

            {links.length > 0 && (
              <div className="dp-step-media">
                <div className="dp-step-media__header">Links:</div>
                {links.map(l => (
                  <div className="dp-step-media__item" key={l.id}>
                    <span className="dp-step-media__bullet">•</span>
                    <a href={l.url} target="_blank" rel="noopener noreferrer"
                       className="dp-step-media__link">
                      {l.title || l.url}
                    </a>
                  </div>
                ))}
              </div>
            )}
          </div>
        ),
      });
    });
  }

  // ============================================================
  // PAGINAÇÃO — fluxo:
  //  (a) renderizar blocos no shadow invisível com a MESMA largura
  //      útil da página real
  //  (b) medir altura de cada um após imagens decodificarem
  //  (c) distribuir em "pages" respeitando o usableHeight
  //  (d) debounce 300ms ao mudar `data`
  // ============================================================
  const measureRef = useRef(null);
  // pages é uma lista de "página", cada uma { items: [{idx, imgScale}] }
  // imgScale = 1 (sem redução) ou valor entre 0.70..1 (encolhe imagens só
  // o suficiente pra caber, como o PDF faz em drawStepCard caso 2).
  const [pages, setPages] = useState([{ items: blocks.map((_, idx) => ({ idx, imgScale: 1 })) }]);
  const [scale, setScale] = useState(2.3);

  // Lê o --dp-scale efetivo do :root (varia por media-query)
  useEffect(() => {
    const readScale = () => {
      const v = getComputedStyle(document.documentElement).getPropertyValue('--dp-scale');
      const f = parseFloat(v);
      if (!isNaN(f) && f > 0) setScale(f);
    };
    readScale();
    window.addEventListener('resize', readScale);
    return () => window.removeEventListener('resize', readScale);
  }, []);

  // Altura útil em px da área de conteúdo de uma página A4
  // (PDF: top 36.36mm, bottom 31.08mm → 297 - 36.36 - 31.08 = 229.56mm)
  const usableHeightPx = (297 - 36.36 - 31.08) * scale;

  // Medição com debounce de 300ms — espelha a estratégia de paginação
  // do POPPdfGenerator.drawStepCard (3 casos):
  //   1. Bloco cabe → entra na página atual
  //   2. Não cabe inteiro, mas dá pra reduzir só as imagens em até 30% → reduz e entra
  //   3. Nem reduzindo cabe → pula página, desenha inteiro na próxima
  // Casos patológicos (card maior que página inteira mesmo após redução
  // máxima): aplica redução máxima (0.70 das imagens) e segue — garante
  // que o usuário veja tudo, ainda que apertado.
  useEffect(() => {
    const timer = setTimeout(async () => {
      if (!measureRef.current) return;

      // Espera todas as imagens decodificarem dentro do shadow
      const imgs = Array.from(measureRef.current.querySelectorAll('img'));
      await Promise.all(imgs.map(img => {
        if (img.complete && img.naturalHeight > 0) return Promise.resolve();
        return new Promise(resolve => {
          img.addEventListener('load',  resolve, { once: true });
          img.addEventListener('error', resolve, { once: true });
        });
      }));

      // Mede cada bloco. Para step cards, também mede separadamente
      // a "altura sem imagens" (header+texto+links), pra saber quanto
      // do total é imagem e quanto é texto/links.
      const nodes = measureRef.current.querySelectorAll('[data-block-idx]');
      const heights = [];           // altura total de cada bloco
      const nonImgHeights = [];     // pra step-card: altura sem as imagens
      const imgHeights = [];        // pra step-card: altura só das imagens
      nodes.forEach(n => {
        const idx = parseInt(n.getAttribute('data-block-idx'), 10);
        heights[idx] = n.getBoundingClientRect().height;
        // Pra step-cards: mede a altura agregada das .dp-step-fig
        const figs = n.querySelectorAll('.dp-step-fig');
        if (figs.length > 0) {
          let imgsH = 0;
          figs.forEach(f => { imgsH += f.getBoundingClientRect().height; });
          imgHeights[idx] = imgsH;
          nonImgHeights[idx] = heights[idx] - imgsH;
        } else {
          imgHeights[idx] = 0;
          nonImgHeights[idx] = heights[idx];
        }
      });

      // Distribui em páginas
      const newPages = [];
      let currentItems = [];
      let currentH = 0;

      const flushCurrent = () => {
        if (currentItems.length) newPages.push({ items: currentItems });
        currentItems = [];
        currentH = 0;
      };

      // Função auxiliar: calcula a altura efetiva de um bloco com um
      // determinado fator de redução nas imagens. Para blocos sem
      // imagens, o fator é ignorado.
      const heightWithScale = (idx, imgScale) => {
        return nonImgHeights[idx] + imgHeights[idx] * imgScale;
      };

      let i = 0;
      while (i < blocks.length) {
        // Forma o grupo (keepWithNext acumula até quebrar a corrente)
        const group = [i];
        while (blocks[group[group.length - 1]].keepWithNext &&
               (group[group.length - 1] + 1) < blocks.length) {
          group.push(group[group.length - 1] + 1);
        }

        // Altura do grupo sem redução
        const groupH = group.reduce((a, idx) => a + heights[idx], 0);
        const remaining = usableHeightPx - currentH;

        // Caso 1: cabe inteiro na atual
        if (groupH <= remaining) {
          group.forEach(idx => currentItems.push({ idx, imgScale: 1 }));
          currentH += groupH;
          i = group[group.length - 1] + 1;
          continue;
        }

        // Soma altura de imagens do grupo (pra ver se redução resolve)
        const groupImgsH = group.reduce((a, idx) => a + imgHeights[idx], 0);
        const groupNonImgH = groupH - groupImgsH;

        // Quanto teria que reduzir as imagens pra caber na atual?
        // Caso 2: encolher imagens até 30% (fator 0.70..1.0)
        if (groupImgsH > 0 && currentItems.length > 0) {
          const targetImgsH = remaining - groupNonImgH;
          if (targetImgsH > 0) {
            const reduction = targetImgsH / groupImgsH;
            if (reduction >= 0.70) {
              // Aplica e entra na atual
              group.forEach(idx => currentItems.push({
                idx,
                imgScale: imgHeights[idx] > 0 ? reduction : 1
              }));
              currentH += groupNonImgH + groupImgsH * reduction;
              i = group[group.length - 1] + 1;
              continue;
            }
          }
        }

        // Caso 3: pula página — fecha a atual e tenta o grupo numa
        // página vazia. Se mesmo numa página vazia o grupo não cabe,
        // aplica redução suficiente para caber (sem piso de 0.70 nesse
        // caso patológico — prioridade é o usuário ver tudo no preview).
        if (currentItems.length > 0) flushCurrent();

        // Numa página vazia o grupo cabe inteiro?
        if (groupH <= usableHeightPx) {
          group.forEach(idx => currentItems.push({ idx, imgScale: 1 }));
          currentH = groupH;
        } else if (groupImgsH > 0) {
          // Patológico: precisa reduzir agressivamente.  Calcula o
          // fator que faz o grupo caber numa página vazia. Se o texto
          // sozinho (sem imagens) já é maior que a página, usa o mínimo
          // que permite mostrar as imagens (0.30) e o texto estoura
          // graciosamente.
          const targetImgsH = usableHeightPx - groupNonImgH;
          let reduction;
          if (targetImgsH <= 0) {
            reduction = 0.30; // texto > página inteira, caso raríssimo
          } else {
            reduction = targetImgsH / groupImgsH;
            if (reduction > 1) reduction = 1;
            if (reduction < 0.30) reduction = 0.30; // limite de legibilidade
          }
          group.forEach(idx => currentItems.push({
            idx,
            imgScale: imgHeights[idx] > 0 ? reduction : 1
          }));
          currentH = groupNonImgH + groupImgsH * reduction;
        } else {
          // Bloco sem imagens, maior que a página (texto monstro):
          // entra mesmo, estoura graciosamente.
          group.forEach(idx => currentItems.push({ idx, imgScale: 1 }));
          currentH = groupH;
        }
        i = group[group.length - 1] + 1;
      }
      flushCurrent();

      if (newPages.length === 0) newPages.push({ items: [] });

      // Atualiza só se mudou de fato
      setPages(prev => {
        if (prev.length !== newPages.length) return newPages;
        for (let p = 0; p < prev.length; p++) {
          if (prev[p].items.length !== newPages[p].items.length) return newPages;
          for (let k = 0; k < prev[p].items.length; k++) {
            if (prev[p].items[k].idx !== newPages[p].items[k].idx) return newPages;
            if (Math.abs((prev[p].items[k].imgScale || 1) - (newPages[p].items[k].imgScale || 1)) > 0.001)
              return newPages;
          }
        }
        return prev;
      });
    }, 300);

    return () => clearTimeout(timer);
  }, [data, scale, svgs]);

  // Renderiza uma página A4 com seu pedaço de blocos
  const renderPage = (pageData, pageIdx, totalPages) => (
    <div className="dp-page-wrap" key={'page-' + pageIdx}>
      <div className="dp-page-label">
        Página {pageIdx + 1} de {totalPages}
      </div>
      <div className="dp-page">
        <div className="dp-header" dangerouslySetInnerHTML={{ __html: svgs.header }} />
        <div className="dp-content">
          {pageData.items.map(({ idx, imgScale }) => {
            const block = blocks[idx];
            // Durante o debounce de repaginação (300ms após edição), o array
            // `blocks` pode ter encolhido (ex: usuário apagou um procedimento)
            // mas `pageData.items` ainda referencia índices antigos. Ignoramos
            // o item nesse caso — a próxima repaginação corrige.
            if (!block) return null;
            return (
              <div
                key={block.key}
                className="dp-block-slot"
                style={imgScale !== 1 ? { '--dp-img-scale': imgScale } : undefined}
              >
                {block.node}
              </div>
            );
          })}
        </div>
        <div className="dp-footer" dangerouslySetInnerHTML={{ __html: svgs.footer }} />
      </div>
    </div>
  );

  return (
    <>
      {/* SHADOW: mesmo width da área de conteúdo, fora da viewport.
          Mede a altura real de cada bloco antes da paginação. */}
      <div className="dp-shadow" aria-hidden="true" ref={measureRef}>
        <div className="dp-shadow__content">
          {blocks.map((b, idx) => (
            <div data-block-idx={idx} key={b.key}>{b.node}</div>
          ))}
        </div>
      </div>

      {/* Páginas reais, empilhadas verticalmente */}
      <div className="dp-pages">
        {pages.map((pg, i) => renderPage(pg, i, pages.length))}
      </div>
    </>
  );
};

// ---------- Modal: Arquivar POP antigo (PDF) ----------
const ArchiveModal = ({ open, onClose, onConfirm, defaultSetor = 'pos' }) => {
  const [file, setFile] = useState(null);
  const [title, setTitle] = useState('');
  const [setor, setSetor] = useState(defaultSetor);
  const [error, setError] = useState('');
  const [busy, setBusy] = useState(false);
  const [drag, setDrag] = useState(false);
  const fileRef = useRef(null);

  // Resetar quando abrir
  useEffect(() => {
    if (open) {
      setFile(null); setTitle(''); setSetor(defaultSetor);
      setError(''); setBusy(false); setDrag(false);
    }
  }, [open, defaultSetor]);

  // Fechar com ESC
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  if (!open) return null;

  const acceptFile = (f) => {
    if (!f) return;
    if (f.type !== 'application/pdf' && !f.name.toLowerCase().endsWith('.pdf')) {
      setError('Apenas arquivos PDF são aceitos.');
      return;
    }
    // 8MB de limite — localStorage geralmente comporta até ~10MB total
    if (f.size > 8 * 1024 * 1024) {
      setError('Arquivo muito grande (máx. 8 MB). Para arquivos maiores, será necessário backend.');
      return;
    }
    setError('');
    setFile(f);
    if (!title) setTitle(f.name.replace(/\.pdf$/i, ''));
  };

  const handleDrop = (e) => {
    e.preventDefault(); setDrag(false);
    acceptFile(e.dataTransfer.files?.[0]);
  };

  const handleSubmit = async () => {
    if (!file) { setError('Selecione um arquivo PDF.'); return; }
    if (!title.trim()) { setError('Informe um nome para o arquivo.'); return; }
    if (!setor) { setError('Selecione um setor.'); return; }
    setBusy(true);
    try {
      // O File é passado direto — o upload binário pro Supabase Storage
      // é feito no hook useArchive (mais eficiente do que converter pra base64).
      onConfirm({ title: title.trim(), setor, file });
    } catch (e) {
      console.error(e);
      setError('Falha ao processar o arquivo. Tente novamente.');
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-dialog" onClick={(e) => e.stopPropagation()} role="dialog" aria-modal="true">
        <header className="modal-dialog__head">
          <div>
            <div className="modal-dialog__eyebrow">
              <Icon name="paperclip" size={13}/> Arquivar POP existente
            </div>
            <h2 className="modal-dialog__title">Enviar PDF para o repositório</h2>
            <p className="modal-dialog__sub">
              Faça upload de um POP já existente em PDF. Ele será preservado no
              setor escolhido sem precisar ser recadastrado.
            </p>
          </div>
          <button className="modal-dialog__close" onClick={onClose} aria-label="Fechar">
            <Icon name="x" size={18}/>
          </button>
        </header>

        <div className="modal-dialog__body">
          <Field label="Arquivo PDF" required>
            <div
              className={"upload-drop" + (drag ? ' is-drag' : '') + (file ? ' has-file' : '')}
              onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
              onDragLeave={() => setDrag(false)}
              onDrop={handleDrop}
              onClick={() => fileRef.current?.click()}
              role="button"
              tabIndex={0}
            >
              <input
                ref={fileRef}
                type="file"
                accept="application/pdf,.pdf"
                style={{display: 'none'}}
                onChange={(e) => acceptFile(e.target.files?.[0])}
              />
              {file ? (
                <div className="upload-drop__file">
                  <Icon name="file-text" size={28}/>
                  <div>
                    <strong>{file.name}</strong>
                    <span>{(file.size / 1024).toFixed(0)} KB · PDF</span>
                  </div>
                  <button
                    type="button"
                    className="icon-btn"
                    onClick={(e) => { e.stopPropagation(); setFile(null); }}
                    aria-label="Remover arquivo"
                  >
                    <Icon name="x" size={14}/>
                  </button>
                </div>
              ) : (
                <>
                  <div className="upload-drop__icon"><Icon name="paperclip" size={28}/></div>
                  <strong>Arraste um PDF aqui ou clique para selecionar</strong>
                  <span>Apenas arquivos PDF, até 8 MB</span>
                </>
              )}
            </div>
          </Field>

          <Field label="Nome do arquivo" required hint="Como o POP aparecerá no repositório.">
            <Input
              value={title}
              onChange={setTitle}
              placeholder="Ex.: Procedimento de matrícula EAD — versão antiga"
            />
          </Field>

          <Field label="Setor de destino" required hint="O PDF será arquivado neste setor.">
            <div className="setor-picker">
              {(window.SETORES || []).map(s => (
                <button
                  type="button"
                  key={s.id}
                  className={"setor-opt" + (setor === s.id ? ' is-active' : '')}
                  onClick={() => setSetor(s.id)}
                >
                  <span className="setor-opt__dot" data-sector={s.id}/>
                  <span className="setor-opt__body">
                    <span className="setor-opt__label">{s.label}</span>
                    <span className="setor-opt__desc">{s.desc}</span>
                  </span>
                </button>
              ))}
            </div>
          </Field>

          {error && (
            <div className="modal-dialog__error">
              <Icon name="alert" size={14}/> {error}
            </div>
          )}
        </div>

        <footer className="modal-dialog__foot">
          <button className="btn btn--ghost" onClick={onClose} disabled={busy}>
            Cancelar
          </button>
          <button className="btn btn--primary" onClick={handleSubmit} disabled={busy || !file}>
            {busy ? 'Arquivando…' : (
              <><Icon name="check" size={14}/> Arquivar no repositório</>
            )}
          </button>
        </footer>
      </div>
    </div>
  );
};

// ============================================================
// Modal de revisão de importação de PDF
// ============================================================
// Recebe o resultado do POPPdfImporter.extract() e mostra todos
// os campos pra revisão. Permite editar antes de jogar no formulário.
const ImportReviewModal = ({ open, result, onClose, onConfirm }) => {
  const [fields, setFields] = useState(null);
  const [procedures, setProcedures] = useState([]);
  const [setor, setSetor] = useState('pos');

  // Sincroniza o estado quando o resultado da extração chega
  useEffect(() => {
    if (result) {
      setFields({ ...result.fields });
      setProcedures(result.procedures.map(p => ({ ...p })));
    }
  }, [result]);

  if (!open || !fields) return null;

  const setField = (k, v) => setFields(f => ({ ...f, [k]: v }));
  const setProc = (i, k, v) => setProcedures(prev => {
    const next = [...prev];
    next[i] = { ...next[i], [k]: v };
    return next;
  });
  const removeProc = (i) => setProcedures(prev => prev.filter((_, idx) => idx !== i));
  const removeProcImage = (procIdx, imgIdx) => setProcedures(prev => {
    const next = [...prev];
    next[procIdx] = {
      ...next[procIdx],
      imagens: next[procIdx].imagens.filter((_, j) => j !== imgIdx)
    };
    return next;
  });

  const handleConfirm = () => {
    // Monta o objeto compatível com o formulário do gerador
    const formData = {
      nomeAtividade: fields.nomeAtividade || '',
      versao: fields.versao || '1.0',
      dataPrimeira: fields.dataPrimeira || new Date().toISOString().slice(0, 10),
      dataRevisao: fields.dataRevisao || '',
      autor: fields.autor || '',
      validador: fields.validador || '',
      setor: setor || 'pos',
      objetivo: fields.objetivo || '',
      responsavel: fields.responsavel ||
        (setor === 'marketing' ? 'Marketing UniAnchieta' :
         setor === 'graduacao' ? 'Graduação UniAnchieta' :
         setor === 'juridico'  ? 'Jurídico UniAnchieta'  :
         'Pós-Graduação UniAnchieta'),
      local: fields.local || '',
      procedimentos: procedures.length
        ? procedures.map((p, i) => ({
            id: 'p' + (i + 1) + uid().slice(0, 3),
            text: p.text || '',
            imagens: p.imagens || [],
            videos: [],
            links: []
          }))
        : [{ id: 'p1', text: '', imagens: [], videos: [], links: [] }]
    };
    onConfirm(formData);
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div
        className="modal-dialog"
        onClick={(e) => e.stopPropagation()}
        role="dialog"
        aria-modal="true"
        style={{ maxWidth: 820, maxHeight: '90vh', display: 'flex', flexDirection: 'column' }}
      >
        <header className="modal-dialog__head">
          <div>
            <div className="modal-dialog__eyebrow">
              <Icon name="paperclip" size={13}/> Revisar importação
            </div>
            <h2 className="modal-dialog__title">Confira os dados extraídos do PDF</h2>
            <p className="modal-dialog__sub">
              Edite o que precisar antes de confirmar. Ao confirmar, o formulário do
              Gerador será preenchido com estes dados.
            </p>
          </div>
        </header>

        {result.warnings && result.warnings.length > 0 && (
          <div style={{
            margin: '0 24px',
            padding: '10px 14px',
            background: 'rgba(245, 158, 11, 0.10)',
            border: '1px solid rgba(245, 158, 11, 0.35)',
            borderRadius: 8,
            fontSize: 13,
            color: 'var(--ink, #1A1F2E)'
          }}>
            <strong>Atenção:</strong>
            <ul style={{ margin: '6px 0 0', paddingLeft: 20 }}>
              {result.warnings.map((w, i) => <li key={i}>{w}</li>)}
            </ul>
          </div>
        )}

        <div style={{ overflow: 'auto', padding: '16px 24px', flex: 1 }}>
          <h3 style={{ fontSize: 13, fontWeight: 700, textTransform: 'uppercase',
                       letterSpacing: 0.5, opacity: 0.6, margin: '0 0 8px' }}>
            Cabeçalho ({result.stats.pages} páginas)
          </h3>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <Field label="Nome da atividade">
              <Input value={fields.nomeAtividade}
                     onChange={v => setField('nomeAtividade', v)}/>
            </Field>
            <Field label="Versão">
              <Input value={fields.versao} onChange={v => setField('versao', v)}/>
            </Field>
            <Field label="Data 1ª versão">
              <Input type="date" value={fields.dataPrimeira}
                     onChange={v => setField('dataPrimeira', v)}/>
            </Field>
            <Field label="Última revisão">
              <Input type="date" value={fields.dataRevisao}
                     onChange={v => setField('dataRevisao', v)}/>
            </Field>
            <Field label="Autor">
              <Input value={fields.autor} onChange={v => setField('autor', v)}/>
            </Field>
            <Field label="Validador">
              <Input value={fields.validador} onChange={v => setField('validador', v)}/>
            </Field>
            <Field label="Setor">
              <select className="input"
                      value={setor}
                      onChange={e => setSetor(e.target.value)}>
                {(window.SETORES || [
                    { id: 'pos',       label: 'Pós' },
                    { id: 'graduacao', label: 'Graduação' },
                    { id: 'marketing', label: 'Marketing' },
                    { id: 'juridico',  label: 'Jurídico' },
                  ])
                  .map(s => <option key={s.id} value={s.id}>{s.label}</option>)}
              </select>
            </Field>
            <Field label="Responsável">
              <Input value={fields.responsavel}
                     onChange={v => setField('responsavel', v)}/>
            </Field>
            <div style={{ gridColumn: '1 / -1' }}>
              <Field label="Objetivo">
                <textarea className="input"
                          rows={2}
                          value={fields.objetivo}
                          onChange={e => setField('objetivo', e.target.value)}/>
              </Field>
            </div>
            <div style={{ gridColumn: '1 / -1' }}>
              <Field label="Local">
                <Input value={fields.local} onChange={v => setField('local', v)}/>
              </Field>
            </div>
          </div>

          <h3 style={{ fontSize: 13, fontWeight: 700, textTransform: 'uppercase',
                       letterSpacing: 0.5, opacity: 0.6, margin: '24px 0 8px' }}>
            Procedimentos extraídos ({procedures.length})
            {result.stats.images > 0 && ` · ${result.stats.images} imagens`}
          </h3>

          {procedures.length === 0 && (
            <div style={{
              padding: 24, textAlign: 'center',
              border: '1px dashed var(--line, #E4E7EE)',
              borderRadius: 8, opacity: 0.7
            }}>
              Nenhum procedimento detectado. Você poderá adicionar manualmente após confirmar.
            </div>
          )}

          {procedures.map((p, i) => (
            <div key={i} style={{
              border: '1px solid var(--line, #E4E7EE)',
              borderRadius: 8,
              padding: 12,
              marginBottom: 10
            }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
                <span style={{
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  width: 24, height: 24, borderRadius: '50%',
                  background: 'var(--brand, #0B3A82)', color: '#fff',
                  fontSize: 12, fontWeight: 700
                }}>{i + 1}</span>
                <span style={{ flex: 1, fontSize: 13, opacity: 0.7 }}>
                  Procedimento {i + 1}
                </span>
                <button type="button"
                        className="icon-btn icon-btn--danger"
                        onClick={() => removeProc(i)}
                        title="Remover este procedimento">
                  <Icon name="trash" size={14}/>
                </button>
              </div>
              <textarea className="input"
                        rows={Math.max(2, Math.ceil((p.text || '').length / 80))}
                        value={p.text}
                        onChange={e => setProc(i, 'text', e.target.value)}
                        style={{ fontSize: 13 }}/>
              {p.imagens && p.imagens.length > 0 && (
                <div style={{
                  display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 8
                }}>
                  {p.imagens.map((img, j) => (
                    <div key={j} style={{
                      position: 'relative',
                      width: 100, height: 70,
                      border: '1px solid var(--line, #E4E7EE)',
                      borderRadius: 4, overflow: 'hidden'
                    }}>
                      <img src={img.dataUrl}
                           alt=""
                           style={{ width: '100%', height: '100%', objectFit: 'cover',
                                    cursor: 'zoom-in' }}
                           onClick={() => window.dispatchEvent(new CustomEvent('imageZoom', {
                             detail: { src: img.dataUrl, alt: 'Imagem ' + (j + 1) }
                           }))}/>
                      <button type="button"
                              onClick={() => removeProcImage(i, j)}
                              title="Remover imagem"
                              style={{
                                position: 'absolute', top: 2, right: 2,
                                width: 20, height: 20, borderRadius: '50%',
                                border: 'none',
                                background: 'rgba(0,0,0,0.7)',
                                color: '#fff', cursor: 'pointer',
                                display: 'flex', alignItems: 'center', justifyContent: 'center',
                                fontSize: 12, lineHeight: 1
                              }}>×</button>
                    </div>
                  ))}
                </div>
              )}
            </div>
          ))}
        </div>

        <footer className="modal-dialog__foot" style={{
          padding: '16px 24px',
          borderTop: '1px solid var(--line, #E4E7EE)',
          display: 'flex', justifyContent: 'flex-end', gap: 8
        }}>
          <button className="btn btn--ghost" onClick={onClose}>
            Cancelar
          </button>
          <button className="btn btn--primary" onClick={handleConfirm}>
            <Icon name="check" size={16}/> Confirmar e abrir no Gerador
          </button>
        </footer>
      </div>
    </div>
  );
};

// ---------- App principal ----------
function POPApp({ variant = 'classic', session = null }) {
  const { data, set, setData, reset, load, savedAt } = useForm();
  const history = useHistory();
  const archive = useArchive();
  const { theme, toggle: toggleTheme } = useTheme();
  const [errors, setErrors] = useState({});
  const [generating, setGenerating] = useState(false);
  const [showHistory, setShowHistory] = useState(false);
  const [toast, setToast] = useState(null);
  const [showGenMenu, setShowGenMenu] = useState(false);
  const [view, setView] = useState('gerador'); // 'gerador' | 'repositorio'
  const [archiveOpen, setArchiveOpen] = useState(false);
  const [zoomImg, setZoomImg] = useState(null); // { src, alt } | null
  const [importing, setImporting] = useState(false); // está extraindo agora?
  const [importResult, setImportResult] = useState(null); // resultado do extract
  const genMenuRef = useRef(null);

  // Fecha o menu ao clicar fora
  useEffect(() => {
    if (!showGenMenu) return;
    const onDoc = (e) => {
      if (genMenuRef.current && !genMenuRef.current.contains(e.target)) {
        setShowGenMenu(false);
      }
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [showGenMenu]);

  // Listener global para abrir o modal de zoom de imagem
  useEffect(() => {
    const onZoom = (e) => setZoomImg(e.detail || null);
    window.addEventListener('imageZoom', onZoom);
    return () => window.removeEventListener('imageZoom', onZoom);
  }, []);

  // ESC fecha o zoom
  useEffect(() => {
    if (!zoomImg) return;
    const onKey = (e) => { if (e.key === 'Escape') setZoomImg(null); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [zoomImg]);

  const flash = (msg, kind = 'success') => {
    setToast({ msg, kind });
    setTimeout(() => setToast(null), 3000);
  };

  // Validação compartilhada por todos os tipos de geração.
  // Retorna true se OK, false se tem erros (e já dispara o feedback).
  const runValidation = () => {
    const errs = validate(data);
    setErrors(errs);
    if (Object.keys(errs).length) {
      flash('Preencha os campos obrigatórios marcados em vermelho.', 'error');
      const first = Object.keys(errs)[0];
      const el = document.querySelector(`[data-field="${first}"]`);
      if (el) el.scrollIntoView({ block: 'center', behavior: 'smooth' });
      return false;
    }
    return true;
  };

  const handleGenerateWord = async () => {
    setShowGenMenu(false);
    if (!runValidation()) return;
    setGenerating(true);
    try {
      await POPDocxGenerator.download(data);
      history.save(data);
      flash('Documento Word gerado com sucesso. O download começou.');
    } catch (e) {
      console.error(e);
      flash('Erro ao gerar o Word. Tente novamente.', 'error');
    } finally {
      setGenerating(false);
    }
  };

  const handleGeneratePdf = async (style) => {
    setShowGenMenu(false);
    if (!runValidation()) return;
    setGenerating(true);
    try {
      await POPPdfGenerator.print(data, { style });
      history.save(data);
      flash('Diálogo de impressão aberto — escolha "Salvar como PDF" no destino.');
    } catch (e) {
      console.error(e);
      flash('Erro ao gerar o PDF. Tente novamente.', 'error');
    } finally {
      setGenerating(false);
    }
  };

  // Ação primária do botão (clique no texto principal): gera PDF moderno.
  const handleGeneratePrimary = () => handleGeneratePdf('modern');

  const completion = useMemo(() => {
    const fields = ['nomeAtividade','versao','dataPrimeira','autor','validador','objetivo','responsavel','local'];
    const filled = fields.filter(f => (data[f] || '').toString().trim()).length;
    const procFilled = (data.procedimentos || []).some(s => (s && (s.text || (typeof s === 'string' ? s : ''))).trim()) ? 1 : 0;
    return Math.round(((filled + procFilled) / (fields.length + 1)) * 100);
  }, [data]);

  return (
    <div className={"app app--" + variant}>
      {/* Topbar */}
      <header className="topbar">
        <div className="topbar__brand">
          <a href="#" className="brand-logo" aria-label="UniAnchieta" onClick={e => { e.preventDefault(); setView('gerador'); }}>
            <img src="assets/logo_unianchieta.svg" alt="UniAnchieta" className="brand-logo__img"/>
          </a>
          <nav className="topbar__tabs" aria-label="Navegação principal">
            <button
              className={"topbar__tab" + (view === 'gerador' ? ' is-active' : '')}
              onClick={() => setView('gerador')}>
              <Icon name="sparkle-doc" size={15}/> Gerador de POP
            </button>
            <button
              className={"topbar__tab" + (view === 'repositorio' ? ' is-active' : '')}
              onClick={() => setView('repositorio')}>
              <Icon name="file-text" size={15}/> Repositório
              {(history.items.length + archive.items.length) > 0 && (
                <span className="badge">{history.items.length + archive.items.length}</span>
              )}
            </button>
            <button
              className="topbar__tab topbar__tab--action"
              onClick={() => setArchiveOpen(true)}
              title="Enviar PDF de POP existente">
              <Icon name="paperclip" size={15}/> Arquivar POP
            </button>
          </nav>
        </div>
        <nav className="topbar__nav">
          {view === 'gerador' && (
            <>
              <button className="topbar__link" onClick={() => setShowHistory(true)}>
                <Icon name="history" size={16}/> Histórico
                {history.items.length > 0 && <span className="badge">{history.items.length}</span>}
              </button>
              <button className="topbar__link" onClick={() => {
                if (confirm('Limpar todos os campos do formulário atual?')) {
                  reset(); setErrors({}); flash('Formulário limpo.');
                }
              }}>
                <Icon name="rotate" size={16}/> Limpar
              </button>
            </>
          )}
          {view === 'repositorio' && (
            <button className="topbar__link" onClick={() => setView('gerador')}>
              <Icon name="plus" size={16}/> Novo POP
            </button>
          )}
          <ThemeToggle theme={theme} onToggle={toggleTheme}/>
          <UserMenu session={session}/>
        </nav>
      </header>

      {/* Hero stripe */}
      {view === 'gerador' && (
      <section className="hero">
        <div className="hero__inner">
          <div className="hero__copy">
            <div className="hero__eyebrow">Equipe interna · Pós-Graduação</div>
            <h1 className="hero__title">Cadastre uma nova Instrução de Trabalho</h1>
            <p className="hero__sub">
              Preencha o formulário ao lado e gere o documento Word no padrão oficial da
              UniAnchieta. O preview ao vivo mostra exatamente como o arquivo final ficará.
            </p>
          </div>
          <div className="hero__progress">
            <div className="hero__progress-label">
              <span>Preenchimento</span>
              <strong className="num">{completion}%</strong>
            </div>
            <div className="hero__progress-bar">
              <div className="hero__progress-fill" style={{ width: completion + '%' }}/>
            </div>
            <div className="hero__progress-meta">
              {savedAt
                ? <>Rascunho salvo automaticamente · {savedAt.toLocaleTimeString('pt-BR', {hour:'2-digit',minute:'2-digit'})}</>
                : 'Os campos serão salvos automaticamente.'}
            </div>
          </div>
        </div>
      </section>
      )}

      {view === 'repositorio' && (
        <RepositoryView
          items={history.items}
          archived={archive.items}
          onLoad={(item) => {
            load(item.data);
            setView('gerador');
            flash('POP carregado no formulário.');
          }}
          onDownload={(item) => POPPdfGenerator.print(item.data, { style: 'modern' })}
          onRemove={(item) => {
            if (confirm('Remover "' + item.title + '" do repositório?')) {
              history.remove(item.id);
              flash('POP removido do repositório.');
            }
          }}
          onArchivedView={(item) => {
            // Abre o PDF em nova aba — usa a URL pública do Supabase Storage
            try {
              if (item.publicUrl) {
                window.open(item.publicUrl, '_blank');
              }
            } catch (e) { console.error(e); }
          }}
          onArchivedDownload={(item) => {
            // Faz download direto da URL pública
            const a = document.createElement('a');
            a.href = item.publicUrl;
            a.download = (item.title || 'POP') + '.pdf';
            a.target = '_blank';
            document.body.appendChild(a); a.click(); a.remove();
          }}
          onArchivedImport={async (item) => {
            // 1) Baixa o PDF do Supabase Storage
            // 2) Extrai os campos via POPPdfImporter
            // 3) Abre o modal de revisão
            if (!window.POPPdfImporter) {
              flash('Importador não carregado. Recarregue a página.', 'error');
              return;
            }
            if (importing) return;
            setImporting(true);
            flash('Lendo PDF e extraindo campos...', 'info');
            try {
              const url = item.publicUrl;
              if (!url) throw new Error('PDF sem URL pública');
              const res = await fetch(url);
              if (!res.ok) throw new Error('Falha ao baixar o PDF');
              const buffer = await res.arrayBuffer();
              const result = await window.POPPdfImporter.extract(buffer);
              setImportResult(result);
              flash('Confira os campos extraídos antes de confirmar.');
            } catch (e) {
              console.error(e);
              flash('Erro ao importar o PDF: ' + (e.message || 'desconhecido'), 'error');
            } finally {
              setImporting(false);
            }
          }}
          onArchivedRemove={(item) => {
            if (confirm('Remover "' + item.title + '" do arquivo?')) {
              archive.remove(item.id);
              flash('PDF removido do arquivo.');
            }
          }}
          onGoToGerador={() => setView('gerador')}
          onArchivePOP={() => setArchiveOpen(true)}
        />
      )}

      {/* Split content */}
      {view === 'gerador' && (
      <main className="split">
        {/* ========= FORM ========= */}
        <section className="split__form">
          <FormSection
            number="1" title="Identificação"
            subtitle="Nome, versão e datas do procedimento."
          >
            <Field label="Nome da atividade" required error={errors.nomeAtividade}
                   hint='Verbo no infinitivo + objeto. Ex: "Cadastrar aluno no SIA".'
                   data-field="nomeAtividade">
              <div data-field="nomeAtividade">
                <Input value={data.nomeAtividade}
                       onChange={v => set('nomeAtividade', v)}
                       placeholder="Ex.: Cadastrar nova disciplina no portal EAD"/>
              </div>
            </Field>
            <div className="grid-3">
              <Field label="Versão" required error={errors.versao}>
                <div data-field="versao">
                  <Input value={data.versao} onChange={v => set('versao', v)} placeholder="1.0"/>
                </div>
              </Field>
              <Field label="Data da 1ª versão" required error={errors.dataPrimeira}>
                <div data-field="dataPrimeira">
                  <Input type="date" value={data.dataPrimeira}
                         onChange={v => set('dataPrimeira', v)}/>
                </div>
              </Field>
              <Field label="Última revisão" hint="Opcional na primeira versão.">
                <Input type="date" value={data.dataRevisao}
                       onChange={v => set('dataRevisao', v)}/>
              </Field>
            </div>
          </FormSection>

          <FormSection
            number="2" title="Pessoas envolvidas"
            subtitle="Quem elaborou e quem validou esta IT."
          >
            <Field label="Autor" required error={errors.autor}
                   hint="Nome completo e e-mail institucional.">
              <div data-field="autor">
                <Input value={data.autor} onChange={v => set('autor', v)}
                       placeholder="Ex.: Maria Silva — maria.silva@anchieta.br"/>
              </div>
            </Field>
            <Field label="Validador" required error={errors.validador}
                   hint="Quem revisou e aprovou o procedimento.">
              <div data-field="validador">
                <Input value={data.validador} onChange={v => set('validador', v)}
                       placeholder="Ex.: João Souza — joao.souza@anchieta.br"/>
              </div>
            </Field>
          </FormSection>

          <FormSection
            number="3" title="Escopo"
            subtitle="O que esta IT faz, quem é responsável e onde acontece."
          >
            <Field label="Setor" required error={errors.setor}
                   hint="O POP será arquivado no repositório deste setor.">
              <div data-field="setor" className="setor-picker">
                {(window.SETORES || []).map(s => (
                  <button
                    key={s.id}
                    type="button"
                    className={"setor-opt" + (data.setor === s.id ? ' is-active' : '')}
                    onClick={() => set('setor', s.id)}
                  >
                    <span className="setor-opt__dot" data-sector={s.id}/>
                    <span className="setor-opt__body">
                      <span className="setor-opt__label">{s.label}</span>
                      <span className="setor-opt__desc">{s.desc}</span>
                    </span>
                  </button>
                ))}
              </div>
            </Field>
            <Field label="Objetivo" required error={errors.objetivo}
                   hint='Comece com um verbo: "Instruir sobre", "Elaborar", "Cadastrar"…'>
              <div data-field="objetivo">
                <Textarea value={data.objetivo} onChange={v => set('objetivo', v)} rows={3}
                          placeholder="Ex.: Instruir o cadastro de novas disciplinas no portal EAD da Pós."/>
              </div>
            </Field>
            <div className="grid-2">
              <Field label="Responsável" required error={errors.responsavel}>
                <div data-field="responsavel">
                  <Input value={data.responsavel} onChange={v => set('responsavel', v)}
                         placeholder="Pós-Graduação UniAnchieta (LIVE / EAD / CURADORIA)"/>
                </div>
              </Field>
              <Field label="Local" required error={errors.local}
                     hint="Site, sistema, planilha ou procedimento manual.">
                <div data-field="local">
                  <Input value={data.local} onChange={v => set('local', v)}
                         placeholder="Ex.: Portal EAD — módulo Curadoria"/>
                </div>
              </Field>
            </div>
          </FormSection>

          <FormSection
            number="4" title="Procedimentos"
            subtitle="Passo a passo — cada passo pode ter imagens, vídeos e links de apoio."
          >
            <div data-field="procedimentos">
              <ProcedimentosEditor
                steps={data.procedimentos}
                onChange={v => set('procedimentos', v)}
              />
              {errors.procedimentos && (
                <div className="field__error" style={{ marginTop: 8 }}>
                  <Icon name="alert" size={13}/> {errors.procedimentos}
                </div>
              )}
            </div>
          </FormSection>

          <div className="form-footer">
            <button className="btn btn--ghost" onClick={() => {
              if (confirm('Limpar todos os campos?')) { reset(); setErrors({}); }
            }}>
              <Icon name="rotate" size={16}/> Limpar formulário
            </button>

            <div className="btn-split" ref={genMenuRef}>
              <button
                className="btn btn--primary"
                onClick={handleGeneratePrimary}
                disabled={generating}
                title="Gerar documento PDF"
              >
                <Icon name="download" size={16}/>
                {generating ? 'Gerando…' : 'Gerar documento'}
              </button>
              <button
                className="btn-split__caret"
                onClick={() => setShowGenMenu(v => !v)}
                disabled={generating}
                aria-haspopup="menu"
                aria-expanded={showGenMenu}
                title="Mais opções"
              >
                <Icon name="chevron-down" size={16}/>
              </button>

              {showGenMenu && (
                <div className="btn-split__menu" role="menu">
                  <button className="btn-split__option" role="menuitem" onClick={() => handleGeneratePdf('modern')}>
                    <span className="btn-split__option-icon"><Icon name="sparkles" size={16}/></span>
                    <span className="btn-split__option-text">
                      <div className="btn-split__option-title">PDF</div>
                      <div className="btn-split__option-sub">Formato principal — para visualização e impressão</div>
                    </span>
                  </button>
                  <button className="btn-split__option" role="menuitem" onClick={handleGenerateWord}>
                    <span className="btn-split__option-icon"><Icon name="file-text" size={16}/></span>
                    <span className="btn-split__option-text">
                      <div className="btn-split__option-title">Word (.docx)</div>
                      <div className="btn-split__option-sub">Para edição posterior</div>
                    </span>
                  </button>
                </div>
              )}
            </div>
          </div>
        </section>

        {/* ========= PREVIEW ========= */}
        <aside className="split__preview">
          <div className="preview-head">
            <div>
              <div className="preview-head__eyebrow"><Icon name="eye" size={13}/> Preview ao vivo</div>
              <div className="preview-head__title">Pré-visualização do .docx</div>
            </div>
            <div className="preview-head__meta">A4 · Book Antiqua</div>
          </div>
          <div className="preview-stage">
            <DocPreview data={data}/>
          </div>
        </aside>
      </main>
      )}

      {/* History drawer */}
      {showHistory && (
        <div className="drawer-scrim" onClick={() => setShowHistory(false)}>
          <aside className="drawer" onClick={e => e.stopPropagation()}>
            <header className="drawer__head">
              <div>
                <div className="drawer__title">Histórico de POPs</div>
                <div className="drawer__sub">Salvos localmente neste navegador</div>
              </div>
              <button className="icon-btn" onClick={() => setShowHistory(false)}>
                <Icon name="x" size={18}/>
              </button>
            </header>
            <div className="drawer__body">
              {history.items.length === 0 && (
                <div className="empty">
                  <Icon name="file-text" size={28}/>
                  <p>Ainda não há POPs salvos. Gere o primeiro documento e ele aparecerá aqui.</p>
                </div>
              )}
              {history.items.map(item => (
                <div key={item.id} className="hist-item">
                  <div className="hist-item__main">
                    <div className="hist-item__title">{item.title}</div>
                    <div className="hist-item__meta">
                      v{item.versao} · {new Date(item.savedAt).toLocaleString('pt-BR')}
                    </div>
                  </div>
                  <div className="hist-item__actions">
                    <button className="btn btn--ghost btn--sm"
                            onClick={() => { load(item.data); setShowHistory(false); flash('POP carregado no formulário.'); }}>
                      Carregar
                    </button>
                    <button className="btn btn--ghost btn--sm"
                            onClick={() => POPPdfGenerator.print(item.data, { style: 'modern' })}>
                      <Icon name="download" size={13}/>
                    </button>
                    <button className="icon-btn icon-btn--danger" onClick={() => history.remove(item.id)}>
                      <Icon name="trash" size={14}/>
                    </button>
                  </div>
                </div>
              ))}
            </div>
          </aside>
        </div>
      )}

      {toast && (
        <div className={"toast toast--" + toast.kind}>
          <Icon name={toast.kind === 'error' ? 'alert' : 'check'} size={16}/>
          {toast.msg}
        </div>
      )}

      <ArchiveModal
        open={archiveOpen}
        defaultSetor={data.setor || 'pos'}
        onClose={() => setArchiveOpen(false)}
        onConfirm={async ({ title, setor, file }) => {
          setArchiveOpen(false);
          flash('Enviando PDF...', 'info');
          const entry = await archive.add({ title, setor, file });
          if (entry) {
            flash('POP arquivado com sucesso em ' +
              ((window.SETORES || []).find(s => s.id === setor)?.label || setor) + '.');
            setView('repositorio');
          } else {
            flash('Erro ao arquivar o PDF. Tente novamente.', 'error');
          }
        }}
      />

      <ImportReviewModal
        open={!!importResult}
        result={importResult}
        onClose={() => setImportResult(null)}
        onConfirm={(formData) => {
          load(formData);
          setImportResult(null);
          setView('gerador');
          flash('PDF importado. Edite o que precisar e gere o documento novo.');
        }}
      />

      {/* Modal de zoom de imagem — abre ao clicar em qualquer thumbnail/preview */}
      {zoomImg && (
        <div
          className="img-zoom-overlay"
          onClick={() => setZoomImg(null)}
          style={{
            position: 'fixed', inset: 0, zIndex: 9999,
            background: 'rgba(0,0,0,0.85)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            padding: '40px', cursor: 'zoom-out',
            animation: 'fadeIn 0.15s ease-out'
          }}
        >
          <button
            type="button"
            onClick={(e) => { e.stopPropagation(); setZoomImg(null); }}
            title="Fechar (ESC)"
            style={{
              position: 'absolute', top: 20, right: 20,
              width: 40, height: 40, borderRadius: '50%',
              border: 'none', background: 'rgba(255,255,255,0.15)',
              color: '#fff', cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 20, fontWeight: 'bold'
            }}
          >×</button>
          <img
            src={zoomImg.src}
            alt={zoomImg.alt}
            onClick={(e) => e.stopPropagation()}
            style={{
              maxWidth: '95%', maxHeight: '90vh',
              objectFit: 'contain',
              boxShadow: '0 20px 60px rgba(0,0,0,0.5)',
              borderRadius: 4,
              cursor: 'default'
            }}
          />
          {zoomImg.alt && (
            <div style={{
              position: 'absolute', bottom: 20, left: 0, right: 0,
              textAlign: 'center', color: '#fff',
              fontSize: 14, opacity: 0.85,
              padding: '0 40px',
              pointerEvents: 'none'
            }}>{zoomImg.alt}</div>
          )}
        </div>
      )}
    </div>
  );
}

const FormSection = ({ number, title, subtitle, children }) => (
  <section className="form-sec">
    <header className="form-sec__head">
      <div className="form-sec__num">{number}</div>
      <div>
        <h2 className="form-sec__title">{title}</h2>
        <p className="form-sec__sub">{subtitle}</p>
      </div>
    </header>
    <div className="form-sec__body">{children}</div>
  </section>
);

window.POPApp = POPApp;
window.Icon = Icon;
