// ─── SuiviSignatures.jsx — Écran dédié au suivi des signatures ───────────────
// Pas de build, pas d'import/export — tout est global (React CDN), comme le reste.
//
// Modèle calqué sur AppelsFonds.jsx : accordéons par programme (repliés par
// défaut), une ligne par lot (TOUS les lots, même LIBRE), une pastille de statut
// commercial par ligne + des colonnes-étapes avec dates ÉDITABLES.
//
// Étapes (colonnes) : Optionné (date posée + expiration), Sous LIA (envoyée +
// signée), Sous compromis (envoyé + signé), Vendu (1 date), Livré (1 date).
// On EXCLUT volontairement les étapes d'avancement AV10/40/70/95 (pas leur place
// ici — elles restent visibles via la pastille de statut).
//
// Stockage : nouveaux champs sur le lot (dateOption, dateOptionExpire,
// dateLiaEnvoyee, dateLiaSignee, dateCompromisEnvoye, dateCompromisSigne,
// dateVendu, dateLivre). Ils partent dans la colonne JSONB lots.data via le save
// granulaire 'fiches' (aucune migration BDD). L'écriture se fait par onUpdateLotDate
// (handler App.jsx → setFiches → autosave useDomainSave).
//
// Pré-remplissage : tant que Pierre n'a pas saisi de date pour un champ, on AFFICHE
// une date dérivée de l'historique des statuts du lot (sans l'écrire en base) :
//   - Optionné posée      ← passage en OPTION
//   - Optionné expire      ← (option posée) + 7 jours
//   - LIA envoyée          ← passage en OPTION (la LIA est envoyée au client à l'option)
//   - LIA transmise notaire ← passage en LIA (auto-rempli à l'envoi de l'email notaire)
//   - Compromis envoyé     ← passage en LIA (dossier transmis au notaire à ce moment)
//   - Compromis signé      ← passage en COMPROMIS
//   - Vendu / Livré        ← passage en VENDU / LIVRE
// Dès qu'un champ est touché (même vidé), la valeur saisie prime définitivement.

function SuiviSignaturesView({ C, projects, fiches, crm, onUpdateLotDate, openStatutModal, onLotStatutChange, openLiaModal }) {
  const [collapsed, setCollapsed] = React.useState(new Set()); // programmes REPLIÉS
  const seededRef = React.useRef(false); // replier tous les accordéons une seule fois
  // Alerte option échue : fenêtres Prolonger / Libérer / Relance CGP.
  const [prolongerModal, setProlongerModal] = React.useState(null); // {projId,lotIdx,current}
  const [libererConfirm, setLibererConfirm] = React.useState(null); // {projId,lotIdx,label}
  const [cgpEmailModal, setCgpEmailModal]   = React.useState(null); // {projId,lotIdx}
  // Compromis signés reçus via Universign (notifications de l'agent email).
  const [compromisNotifs, setCompromisNotifs] = React.useState([]);
  const [compromisModal,  setCompromisModal]  = React.useState(null); // notif en cours de traitement

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

  // Charge les notifications « compromis signé reçu » à traiter (statut 'new').
  const refreshCompromis = React.useCallback(() => {
    fetch('/api/agent/notifications?category=compromis_signe&status=new', { credentials: 'same-origin' })
      .then(r => (r.ok ? r.json() : []))
      .then(d => setCompromisNotifs(Array.isArray(d) ? d : []))
      .catch(() => {});
  }, []);
  React.useEffect(() => { refreshCompromis(); }, [refreshCompromis]);

  // ── Helpers ────────────────────────────────────────────────────────────────
  const crmClients = Array.isArray(crm && crm.clients) ? crm.clients : [];
  const crmCgps    = Array.isArray(crm && crm.cgps) ? crm.cgps : [];

  // CGP rattaché au lot (depuis le CRM via lot.cgpId), avec fallback sur lot.cgpNom.
  function cgpOf(lot) {
    if (lot && lot.cgpId) { const g = crmCgps.find(c => c && c.id === lot.cgpId); if (g) return g; }
    return null;
  }
  function cgpName(lot) {
    const g = cgpOf(lot);
    if (g) return ((g.societe ? g.societe + ' – ' : '') + `${g.prenom || ''} ${g.nom || ''}`.trim()).trim();
    return (lot && lot.cgpNom) || '';
  }
  function cgpEmail(lot) {
    const g = cgpOf(lot);
    if (g && g.emails) return g.emails.split(/[,;\s]+/).map(e => e.trim()).filter(Boolean)[0] || '';
    return '';
  }

  // Résout {projId, lotIdx, progNom, proj, fiche} d'une notification compromis
  // (deviné par l'agent dans metadata / proposed_action).
  function resolveNotifLot(notif) {
    const md = (notif && notif.metadata) || {};
    const pa = (notif && notif.proposed_action && notif.proposed_action.params) || {};
    const projId = md.programme_id || pa.programmeId || '';
    const lotIdx = (md.lot_idx != null) ? md.lot_idx : (pa.lotIdx != null ? pa.lotIdx : null);
    const proj = (projects || []).find(x => x && x.id === projId) || null;
    const fiche = (fiches && fiches[projId]) || {};
    const ville = ((proj && proj.ville) || '').trim();
    const prog  = (fiche.nomProgramme || (proj && proj.nom) || '').trim();
    const progNom = (ville && prog && ville !== prog) ? (ville + ' – ' + prog) : (prog || ville || projId);
    return { projId, lotIdx, progNom, proj, fiche };
  }

  function clientName(lot) {
    if (lot && lot.clientNom) return lot.clientNom;
    const c = crmClients.find(c => c && c.id === (lot && lot.clientId));
    return c ? (`${(c.prenom || '').trim()} ${(c.nom || '').trim()}`).trim() : '';
  }

  // Pastille de statut commercial — même valeur/couleurs que l'onglet programme.
  // Cliquable si onClick est fourni (pilotage du statut depuis cet onglet).
  function statutBadge(statut, onClick) {
    const list = (typeof STATUTS_COMMERCIAL !== 'undefined') ? STATUTS_COMMERCIAL : [];
    const s = list.find(x => x.key === statut) || { label: statut || 'Libre', bg: '#f1f5f9', color: '#64748b' };
    const clickable = typeof onClick === 'function';
    return <span onClick={onClick} title={clickable ? 'Cliquer pour modifier le statut' : undefined} style={{ display: 'inline-block', borderRadius: 4, padding: '2px 8px', fontSize: 11, fontWeight: 700, background: s.bg, color: s.color, whiteSpace: 'nowrap', cursor: clickable ? 'pointer' : 'default', userSelect: 'none' }}>{s.label}</span>;
  }

  // Dernière date de passage vers un statut donné dans l'historique du lot.
  function lastTransitionDate(lot, toStatut) {
    const h = Array.isArray(lot && lot.historiqueStatut) ? lot.historiqueStatut : [];
    const matches = h.filter(e => e && e.to === toStatut && e.date);
    return matches.length ? matches[matches.length - 1].date : '';
  }

  function addDays(iso, n) {
    if (!iso) return '';
    const d = new Date(iso + 'T12:00:00Z');
    if (isNaN(d.getTime())) return '';
    d.setUTCDate(d.getUTCDate() + n);
    return d.toISOString().slice(0, 10);
  }

  // Valeur effective d'un champ : la valeur saisie (si la clé existe sur le lot,
  // même vide) prime ; sinon on retombe sur la valeur dérivée de l'historique.
  function eff(lot, field, derived) {
    return (lot && Object.prototype.hasOwnProperty.call(lot, field)) ? (lot[field] || '') : derived;
  }

  // Date d'expiration effective d'une option : valeur saisie (colonne Expiration)
  // sinon (date d'option) + 7 jours. Même dérivation que la cellule du tableau.
  function effExpiry(lot) {
    const dOption = lastTransitionDate(lot, 'OPTION');
    const effOption = eff(lot, 'dateOption', dOption);
    return eff(lot, 'dateOptionExpire', addDays(effOption, 7));
  }
  // Alerte sur un lot en OPTION : soit l'option est expirée (LIA pas encore envoyée),
  // soit la LIA est partie depuis ≥ 7 j sans retour signé. Retourne null sinon.
  // Une seule alerte à la fois : tant que la LIA n'est pas envoyée → option ; sinon → LIA.
  function lotAlert(lot) {
    if (!lot || (lot.statutCommercial || 'LIBRE') !== 'OPTION') return null;
    const liaSent = (Object.prototype.hasOwnProperty.call(lot, 'dateLiaEnvoyee') && lot.dateLiaEnvoyee) ? lot.dateLiaEnvoyee : '';
    const liaSigned = (Object.prototype.hasOwnProperty.call(lot, 'dateLiaSignee') && lot.dateLiaSignee) || lastTransitionDate(lot, 'LIA');
    if (liaSent && !liaSigned) {
      const deadline = (Object.prototype.hasOwnProperty.call(lot, 'dateLiaRelance') && lot.dateLiaRelance) ? lot.dateLiaRelance : addDays(liaSent, 7);
      if (deadline && today >= deadline) {
        return { reason: 'lia', badge: '⚠ LIA en retard', label: 'LIA envoyée le ' + liaSent + ', non revenue', deadline, prolongField: 'dateLiaRelance', prolongTitle: 'Repousser la relance de la LIA' };
      }
      return null;
    }
    const ex = effExpiry(lot);
    if (ex && today >= ex) {
      return { reason: 'option', badge: '⚠ option échue', label: 'Option expirée le ' + ex, deadline: ex, prolongField: 'dateOptionExpire', prolongTitle: "Prolonger l'option" };
    }
    return null;
  }

  // Statut du programme : fiche (éditée dans FichesProgrammes) sinon p.statut.
  // Même résolution que GroupedProgrammeOptions (constants.jsx).
  const statutOf = p => (fiches && fiches[p.id] && fiches[p.id].statut) || (p && p.statut) || 'En cours';

  // ── Données : un groupe par programme EN COURS ayant des lots, trié alpha ───
  const groups = React.useMemo(() => {
    return (projects || [])
      .filter(p => p && !p.isGlobal && statutOf(p) === 'En cours')
      .map(p => {
        const fiche = (fiches && fiches[p.id]) || {};
        const lots = Array.isArray(fiche.lots) ? fiche.lots : [];
        // Libellé "Ville – Programme" : ville + nom de programme (fiche prioritaire).
        // On évite "Morlaix – Morlaix" quand les deux coïncident, et on dégrade
        // proprement si l'un manque.
        const ville = (p.ville || '').trim();
        const prog  = (fiche.nomProgramme || p.nom || '').trim();
        const nom = (ville && prog && ville !== prog) ? (ville + ' – ' + prog) : (prog || ville || p.id);
        return { id: p.id, nom, lots };
      })
      .filter(g => g.lots.length > 0)
      .sort((a, b) => String(a.nom).localeCompare(String(b.nom), 'fr'));
  }, [projects, fiches]);

  // Alertes (options échues + LIA non revenues), tous programmes confondus.
  const alerts = React.useMemo(() => {
    const out = [];
    groups.forEach(G => (G.lots || []).forEach((lot, idx) => {
      const a = lotAlert(lot);
      if (a) out.push({ projId: G.id, projNom: G.nom, lotIdx: idx, lot, ...a });
    }));
    return out.sort((a, b) => String(a.deadline).localeCompare(String(b.deadline)));
  }, [groups, today]);

  // Au 1er rendu où des groupes existent : replier tous les accordéons (une fois).
  React.useEffect(() => {
    if (seededRef.current || groups.length === 0) return;
    seededRef.current = true;
    setCollapsed(new Set(groups.map(g => g.id)));
  }, [groups]);

  function toggle(id) {
    setCollapsed(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  }

  // ── Actions sur une option échue ───────────────────────────────────────────
  // Prolonger : écrit la nouvelle date dans le champ concerné (expiration d'option
  // ou prochaine relance LIA). Le lot reste en OPTION.
  function doProlonger(projId, lotIdx, field, newDate) {
    if (onUpdateLotDate) onUpdateLotDate(projId, lotIdx, field || 'dateOptionExpire', newDate, 'manual');
    setProlongerModal(null);
  }
  // Libérer : repasse le lot en LIBRE (historique + dates déjà saisies inchangés).
  function doLiberer(projId, lotIdx) {
    if (onLotStatutChange) onLotStatutChange(projId, lotIdx, 'LIBRE', today, null, 'Option non levée — lot libéré');
    setLibererConfirm(null);
  }

  // ── Styles ───────────────────────────────────────────────────────────────-
  const card = { background: C.card, border: '1px solid ' + C.border, borderRadius: 12 };
  const th = { padding: '8px 10px', textAlign: 'left', fontSize: 11, fontWeight: 700, color: C.muted, borderBottom: '1px solid ' + C.border, whiteSpace: 'nowrap' };
  const td = { padding: '8px 10px', fontSize: 13, borderBottom: '1px solid ' + C.border, verticalAlign: 'top' };
  const dInput = { background: '#f8fafc', border: '1px solid ' + C.border, borderRadius: 6, padding: '4px 6px', fontSize: 12, color: C.text, width: 140 };
  const subLabel = { fontSize: 10, color: C.muted, marginBottom: 2 };

  // Code couleur des dates « pilotables » (bleu = posée par l'appli, ambre = modifiée
  // à la main). Les autres restent neutres.
  const DATE_PALETTE = {
    auto:   { bg: '#eff6ff', border: '#93c5fd', color: '#1d4ed8' },
    manual: { bg: '#fffbeb', border: '#fcd34d', color: '#b45309' },
  };

  // État d'affichage d'une date pilotable : 'auto' / 'manual' / null (neutre).
  // - marqueur lot.dateSource[field] : prioritaire ('auto' ou 'manual')
  // - sinon Expiration non saisie = règle automatique +7 j → 'auto'
  // - sinon (vide, ou date déjà présente sans traçabilité) → null (gris)
  function dateState(lot, field, value) {
    if (!value) return null;
    const src = (lot && lot.dateSource && lot.dateSource[field]) || '';
    if (src === 'auto' || src === 'manual') return src;
    if (field === 'dateOptionExpire' && !Object.prototype.hasOwnProperty.call(lot, 'dateOptionExpire')) return 'auto';
    return null;
  }

  // Champ date unitaire — fonction qui RENVOIE du JSX (pas un sous-composant, pour
  // éviter tout remount de l'input à chaque rendu parent). Appelée via dateCell(...).
  // `state` (optionnel) : 'auto' | 'manual' | null → applique le code couleur.
  function dateCell(label, value, onChange, state) {
    const pal = state && DATE_PALETTE[state];
    const inpStyle = pal ? { ...dInput, background: pal.bg, border: '1px solid ' + pal.border, color: pal.color } : dInput;
    const tag = state === 'auto' ? ' · auto' : state === 'manual' ? ' · modifié' : '';
    const title = state === 'auto' ? "Date posée automatiquement par l'application"
      : state === 'manual' ? 'Date saisie ou modifiée à la main' : undefined;
    return (
      <div style={{ display: 'flex', flexDirection: 'column', marginBottom: label ? 6 : 0 }}>
        {label && <span style={subLabel}>{label}{pal && <span style={{ color: pal.color, fontWeight: 700 }}>{tag}</span>}</span>}
        <input type="date" value={value || ''} onChange={e => onChange(e.target.value)} style={inpStyle} title={title} />
      </div>
    );
  }

  // ── Rendu ────────────────────────────────────────────────────────────────-
  return (
    <div style={{ padding: '20px 24px', maxWidth: 1400, margin: '0 auto' }}>
      <div style={{ fontSize: 18, fontWeight: 800, marginBottom: 4 }}>Suivi des signatures</div>
      <div style={{ fontSize: 13, color: C.muted, marginBottom: 10 }}>
        Dates clés du parcours commercial de chaque lot. Les dates sont modifiables ;
        celles déjà connues sont pré-remplies depuis l'historique des statuts.
      </div>
      <div style={{ display: 'flex', gap: 16, alignItems: 'center', fontSize: 11, color: C.muted, marginBottom: 18, flexWrap: 'wrap' }}>
        <span style={{ fontWeight: 700 }}>Code couleur :</span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span style={{ width: 12, height: 12, borderRadius: 3, background: DATE_PALETTE.auto.bg, border: '1px solid ' + DATE_PALETTE.auto.border, display: 'inline-block' }}></span>
          posée par l'application
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span style={{ width: 12, height: 12, borderRadius: 3, background: DATE_PALETTE.manual.bg, border: '1px solid ' + DATE_PALETTE.manual.border, display: 'inline-block' }}></span>
          modifiée à la main
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span style={{ width: 12, height: 12, borderRadius: 3, background: '#f8fafc', border: '1px solid ' + C.border, display: 'inline-block' }}></span>
          neutre
        </span>
      </div>

      {alerts.length > 0 && (
        <div style={{ background: '#fef2f2', border: '1px solid #fecaca', borderRadius: 12, padding: '14px 16px', marginBottom: 18 }}>
          <div style={{ fontWeight: 800, fontSize: 14, color: '#b91c1c', marginBottom: 10 }}>
            ⚠️ {alerts.length} lot{alerts.length > 1 ? 's' : ''} à relancer
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {alerts.map(o => {
              const cn = clientName(o.lot);
              const gn = cgpName(o.lot);
              return (
                <div key={o.projId + '-' + o.lotIdx} style={{ background: '#fff', border: '1px solid #fecaca', borderRadius: 8, padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                  <div style={{ flex: 1, minWidth: 220 }}>
                    <div style={{ fontWeight: 700, fontSize: 13 }}>{o.projNom} — Lot {o.lotIdx + 1}{cn ? ' · ' + cn : ''}</div>
                    <div style={{ fontSize: 12, color: C.muted }}>
                      <b style={{ color: '#b91c1c' }}>{o.label}</b>{gn ? <> · CGP : {gn}</> : ''}
                    </div>
                  </div>
                  <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                    <button onClick={() => setProlongerModal({ projId: o.projId, lotIdx: o.lotIdx, current: o.deadline, field: o.prolongField, title: o.prolongTitle })}
                      style={{ background: '#2563eb', color: '#fff', border: 'none', borderRadius: 6, padding: '6px 12px', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}>Prolonger</button>
                    <button onClick={() => setLibererConfirm({ projId: o.projId, lotIdx: o.lotIdx, label: o.projNom + ' — Lot ' + (o.lotIdx + 1) })}
                      style={{ background: '#fff', color: '#b91c1c', border: '1px solid #fca5a5', borderRadius: 6, padding: '6px 12px', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}>Libérer</button>
                    <button onClick={() => setCgpEmailModal({ projId: o.projId, lotIdx: o.lotIdx })}
                      style={{ background: '#fff', color: '#334155', border: '1px solid ' + C.border, borderRadius: 6, padding: '6px 12px', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}>Relancer le CGP</button>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {compromisNotifs.length > 0 && (
        <div style={{ background: '#eff6ff', border: '1px solid #bfdbfe', borderRadius: 12, padding: '14px 16px', marginBottom: 18 }}>
          <div style={{ fontWeight: 800, fontSize: 14, color: '#1d4ed8', marginBottom: 10 }}>
            📄 {compromisNotifs.length} compromis signé{compromisNotifs.length > 1 ? 's' : ''} reçu{compromisNotifs.length > 1 ? 's' : ''} (Universign) à traiter
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {compromisNotifs.map(n => {
              const { progNom, lotIdx } = resolveNotifLot(n);
              const nbDocs = Array.isArray(n.metadata && n.metadata.compromis_attachments) ? n.metadata.compromis_attachments.length : 0;
              return (
                <div key={n.id} style={{ background: '#fff', border: '1px solid #bfdbfe', borderRadius: 8, padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                  <div style={{ flex: 1, minWidth: 220 }}>
                    <div style={{ fontWeight: 700, fontSize: 13 }}>{progNom}{lotIdx != null ? ' — Lot ' + (lotIdx + 1) : ' — lot à préciser'}</div>
                    <div style={{ fontSize: 12, color: C.muted }}>
                      {nbDocs} document{nbDocs > 1 ? 's' : ''}{n.received_at ? ' · reçu le ' + String(n.received_at).slice(0, 10) : ''}
                    </div>
                  </div>
                  <button onClick={() => setCompromisModal(n)}
                    style={{ background: '#2563eb', color: '#fff', border: 'none', borderRadius: 6, padding: '6px 14px', fontSize: 12, fontWeight: 700, cursor: 'pointer' }}>Traiter</button>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {groups.length === 0 ? (
        <div style={{ ...card, padding: 32, textAlign: 'center', color: C.muted, fontSize: 14 }}>Aucun lot à afficher.</div>
      ) : (
        groups.map(G => {
          const open = !collapsed.has(G.id);
          return (
            <div key={G.id} style={{ ...card, overflow: 'hidden', marginBottom: 10 }}>
              <div onClick={() => toggle(G.id)} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px', cursor: 'pointer', background: C.bg }}>
                <span style={{ color: C.muted, fontSize: 13, width: 14 }}>{open ? '▾' : '▸'}</span>
                <div style={{ fontWeight: 700, flex: 1 }}>{G.nom}</div>
                <div style={{ fontSize: 12, color: C.muted }}>{G.lots.length} lot{G.lots.length > 1 ? 's' : ''}</div>
              </div>
              {open && (
                <div style={{ overflowX: 'auto' }}>
                  <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 1350 }}>
                    <thead><tr style={{ background: C.card }}>
                      <th style={th}>Lot</th>
                      <th style={th}>Statut</th>
                      <th style={th}>Optionné</th>
                      <th style={th}>Sous LIA</th>
                      <th style={th}>Sous compromis</th>
                      <th style={th}>Vendu</th>
                      <th style={th}>Livré</th>
                      <th style={th}>Commentaire</th>
                    </tr></thead>
                    <tbody>
                      {G.lots.map((lot, idx) => {
                        if (!lot) return null;
                        const dOption = lastTransitionDate(lot, 'OPTION');
                        const dLia    = lastTransitionDate(lot, 'LIA');
                        const dComp   = lastTransitionDate(lot, 'COMPROMIS');
                        const dVendu  = lastTransitionDate(lot, 'VENDU');
                        const dLivre  = lastTransitionDate(lot, 'LIVRE');
                        const effOption = eff(lot, 'dateOption', dOption);
                        // Toute édition depuis l'onglet est une saisie manuelle → 'manual'.
                        const set = (field, val) => onUpdateLotDate(G.id, idx, field, val, 'manual');
                        const cn = clientName(lot);
                        return (
                          <tr key={idx}>
                            <td style={td}>
                              <div style={{ fontWeight: 600 }}>Lot {idx + 1}</div>
                              {cn && <div style={{ fontSize: 11, color: C.muted }}>{cn}</div>}
                            </td>
                            <td style={td}>
                              {statutBadge(lot.statutCommercial || 'LIBRE', () => openStatutModal && openStatutModal(G.id, idx))}
                              {(() => {
                                const a = lotAlert(lot);
                                return a ? (
                                  <div style={{ marginTop: 4 }}>
                                    <span style={{ display: 'inline-block', background: '#fee2e2', color: '#b91c1c', borderRadius: 4, padding: '1px 5px', fontSize: 9, fontWeight: 700, whiteSpace: 'nowrap' }}>{a.badge}</span>
                                  </div>
                                ) : null;
                              })()}
                            </td>
                            <td style={td}>
                              {dateCell('Option posée', eff(lot, 'dateOption', dOption), v => set('dateOption', v))}
                              {(() => { const v = eff(lot, 'dateOptionExpire', addDays(effOption, 7)); return dateCell('Option — expiration', v, x => set('dateOptionExpire', x), dateState(lot, 'dateOptionExpire', v)); })()}
                              {(lot.statutCommercial || 'LIBRE') === 'OPTION' && (
                                <button onClick={() => openLiaModal && openLiaModal(G.id, idx)}
                                  title="Ouvrir la fenêtre d'envoi de la LIA"
                                  style={{ marginTop: 6, background: '#2563eb', color: '#fff', border: 'none', borderRadius: 6, padding: '5px 10px', fontSize: 11, fontWeight: 700, cursor: 'pointer', width: 140 }}>
                                  ✓ Valider l'option
                                </button>
                              )}
                            </td>
                            <td style={td}>
                              {(() => { const v = eff(lot, 'dateLiaEnvoyee', dOption); return dateCell('LIA envoyée', v, x => set('dateLiaEnvoyee', x), dateState(lot, 'dateLiaEnvoyee', v)); })()}
                              {(() => { const v = eff(lot, 'dateLiaSignee', dLia); return dateCell('LIA transmise au notaire', v, x => set('dateLiaSignee', x), dateState(lot, 'dateLiaSignee', v)); })()}
                            </td>
                            <td style={td}>
                              {dateCell('Compromis envoyé', eff(lot, 'dateCompromisEnvoye', dLia), v => set('dateCompromisEnvoye', v))}
                              {(() => { const v = eff(lot, 'dateCompromisSigne', dComp); return dateCell('Compromis signé', v, x => set('dateCompromisSigne', x), dateState(lot, 'dateCompromisSigne', v)); })()}
                            </td>
                            <td style={td}>
                              {dateCell('RDV signature acte', eff(lot, 'dateRdvSignature', ''), v => set('dateRdvSignature', v))}
                              {dateCell('Acte signé', eff(lot, 'dateVendu', dVendu), v => set('dateVendu', v))}
                            </td>
                            <td style={td}>
                              {dateCell('Livraison', eff(lot, 'dateLivre', dLivre), v => set('dateLivre', v))}
                            </td>
                            <td style={td}>
                              <textarea
                                value={eff(lot, 'commentaireSignature', '')}
                                onChange={e => set('commentaireSignature', e.target.value)}
                                placeholder="Commentaire libre…"
                                rows={2}
                                style={{ background: '#f8fafc', border: '1px solid ' + C.border, borderRadius: 6, padding: '4px 6px', fontSize: 12, color: C.text, width: 200, minWidth: 160, resize: 'vertical', fontFamily: 'inherit' }} />
                            </td>
                          </tr>
                        );
                      })}
                    </tbody>
                  </table>
                </div>
              )}
            </div>
          );
        })
      )}

      {prolongerModal && (
        <ProlongerOptionModal
          C={C}
          current={prolongerModal.current}
          title={prolongerModal.title}
          onCancel={() => setProlongerModal(null)}
          onSave={(d) => doProlonger(prolongerModal.projId, prolongerModal.lotIdx, prolongerModal.field, d)}
        />
      )}

      {libererConfirm && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.45)', zIndex: 2000, display: 'flex', alignItems: 'center', justifyContent: 'center' }} onClick={() => setLibererConfirm(null)}>
          <div style={{ background: '#fff', borderRadius: 16, padding: 24, width: 420, maxWidth: '95vw', boxShadow: '0 8px 40px #0003', border: '1px solid #e2e8f0' }} onClick={e => e.stopPropagation()}>
            <div style={{ fontWeight: 800, fontSize: 15, marginBottom: 8 }}>Libérer le lot ?</div>
            <div style={{ fontSize: 13, color: C.muted, marginBottom: 16 }}>
              <b>{libererConfirm.label}</b> repassera en statut <b>Libre</b>. L'historique et les dates déjà saisies sont conservés.
            </div>
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
              <button onClick={() => setLibererConfirm(null)} style={{ background: '#f1f5f9', border: '1px solid #e2e8f0', borderRadius: 8, padding: '7px 16px', fontSize: 13, cursor: 'pointer' }}>Annuler</button>
              <button onClick={() => doLiberer(libererConfirm.projId, libererConfirm.lotIdx)} style={{ background: '#b91c1c', color: '#fff', border: 'none', borderRadius: 8, padding: '7px 18px', fontSize: 13, fontWeight: 700, cursor: 'pointer' }}>Libérer</button>
            </div>
          </div>
        </div>
      )}

      {cgpEmailModal && (() => {
        const fiche = (fiches && fiches[cgpEmailModal.projId]) || {};
        const lot = (fiche.lots || [])[cgpEmailModal.lotIdx];
        const g = groups.find(x => x.id === cgpEmailModal.projId);
        const projNom = g ? g.nom : cgpEmailModal.projId;
        return (
          <CgpRelanceModal
            C={C}
            to={cgpEmail(lot)}
            cgpNom={cgpName(lot)}
            projNom={projNom}
            lotIdx={cgpEmailModal.lotIdx}
            clientNom={clientName(lot)}
            expiry={effExpiry(lot)}
            onClose={() => setCgpEmailModal(null)}
          />
        );
      })()}

      {compromisModal && (() => {
        const { projId, lotIdx } = resolveNotifLot(compromisModal);
        return (
          <CompromisArchiveModal
            C={C}
            notif={compromisModal}
            projId={projId}
            lotIdx={lotIdx}
            projects={projects}
            fiches={fiches}
            crm={crm}
            onClose={() => setCompromisModal(null)}
            onDone={() => { setCompromisModal(null); refreshCompromis(); }}
            onSetDate={(pid, lidx, d) => { if (onUpdateLotDate && pid && lidx != null) onUpdateLotDate(pid, lidx, 'dateCompromisSigne', d, 'auto'); }}
          />
        );
      })()}
    </div>
  );
}

// ── Fenêtre « Prolonger l'option » : saisie d'une nouvelle date d'expiration ──
function ProlongerOptionModal({ C, current, title, onCancel, onSave }) {
  const [date, setDate] = React.useState(current || '');
  const valid = !!date;
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.45)', zIndex: 2000, display: 'flex', alignItems: 'center', justifyContent: 'center' }} onClick={onCancel}>
      <div style={{ background: '#fff', borderRadius: 16, padding: 24, width: 380, maxWidth: '95vw', boxShadow: '0 8px 40px #0003', border: '1px solid #e2e8f0' }} onClick={e => e.stopPropagation()}>
        <div style={{ fontWeight: 800, fontSize: 15, marginBottom: 8 }}>{title || "Prolonger l'option"}</div>
        <div style={{ fontSize: 12, color: C.muted, marginBottom: 6 }}>Nouvelle date</div>
        <input type="date" value={date} onChange={e => setDate(e.target.value)} style={{ background: '#f8fafc', border: '1px solid #cbd5e1', borderRadius: 8, color: '#1e293b', padding: '7px 10px', fontSize: 13, width: '100%', marginBottom: 16 }} />
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onCancel} style={{ background: '#f1f5f9', border: '1px solid #e2e8f0', borderRadius: 8, padding: '7px 16px', fontSize: 13, cursor: 'pointer' }}>Annuler</button>
          <button onClick={() => valid && onSave(date)} disabled={!valid} style={{ background: valid ? '#2563eb' : '#94a3b8', color: '#fff', border: 'none', borderRadius: 8, padding: '7px 18px', fontSize: 13, fontWeight: 700, cursor: valid ? 'pointer' : 'default' }}>Enregistrer</button>
        </div>
      </div>
    </div>
  );
}

// ── Fenêtre « Relancer le CGP » : email pré-rempli, envoi via sendEmailApi ──
function CgpRelanceModal({ C, to, cgpNom, projNom, lotIdx, clientNom, expiry, onClose }) {
  const [emailTo, setEmailTo] = React.useState(to || '');
  const [subject, setSubject] = React.useState(`Option arrivée à échéance — ${projNom} — Lot ${lotIdx + 1}`);
  const [body, setBody] = React.useState(
`Bonjour${cgpNom ? ' ' + cgpNom : ''},

L'option posée sur le lot ${lotIdx + 1} du programme ${projNom}${clientNom ? ' (client : ' + clientNom + ')' : ''} est arrivée à échéance${expiry ? ' le ' + expiry : ''}.

Merci de nous indiquer s'il faut la prolonger ou libérer le lot.

Cordialement,`);
  const [sending, setSending] = React.useState(false);
  const [sent, setSent] = React.useState(false);
  const [err, setErr] = React.useState('');
  const inp = { background: '#f8fafc', border: '1px solid #cbd5e1', borderRadius: 8, color: '#1e293b', padding: '6px 10px', fontSize: 12, width: '100%' };

  const handleSend = async () => {
    setSending(true); setErr('');
    try { await sendEmailApi({ to: emailTo, subject, body }); setSent(true); }
    catch (e) { setErr(e.message); }
    setSending(false);
  };

  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 2000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16 }} onClick={onClose}>
      <div style={{ background: '#fff', borderRadius: 16, padding: 24, width: 620, maxWidth: '96vw', maxHeight: '92vh', overflowY: 'auto', boxShadow: '0 8px 40px #0003', border: '1px solid #e2e8f0' }} onClick={e => e.stopPropagation()}>
        <div style={{ fontWeight: 800, fontSize: 16, marginBottom: 4 }}>📧 Relancer le CGP</div>
        <div style={{ fontSize: 12, color: C.muted, marginBottom: 14 }}>{projNom} — Lot {lotIdx + 1}{cgpNom ? ' · ' + cgpNom : ''}</div>
        {!to && <div style={{ background: '#fef9c3', border: '1px solid #fde68a', borderRadius: 8, padding: '8px 12px', marginBottom: 10, fontSize: 12, color: '#92400e' }}>⚠️ Aucun email CGP trouvé dans le CRM pour ce lot. Saisis-le ci-dessous.</div>}
        <div style={{ marginBottom: 8 }}>
          <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Destinataire (CGP)</div>
          <input value={emailTo} onChange={e => setEmailTo(e.target.value)} style={inp} placeholder="cgp@exemple.fr" />
        </div>
        <div style={{ marginBottom: 8 }}>
          <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Objet</div>
          <input value={subject} onChange={e => setSubject(e.target.value)} style={inp} />
        </div>
        <div style={{ marginBottom: 10 }}>
          <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Message</div>
          <textarea value={body} onChange={e => setBody(e.target.value)} style={{ ...inp, height: 160, resize: 'vertical', lineHeight: 1.5 }} />
        </div>
        {err && <div style={{ background: '#fee2e2', border: '1px solid #fca5a5', borderRadius: 8, padding: '8px 12px', marginBottom: 10, fontSize: 12, color: '#dc2626' }}>❌ {err}</div>}
        {sent && <div style={{ background: '#dcfce7', border: '1px solid #86efac', borderRadius: 8, padding: '8px 12px', marginBottom: 10, fontSize: 12, color: '#15803d' }}>✅ Email envoyé au CGP !</div>}
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onClose} style={{ background: '#f1f5f9', border: '1px solid #e2e8f0', borderRadius: 8, padding: '8px 18px', fontSize: 13, cursor: 'pointer' }}>Fermer</button>
          {!sent && <button onClick={handleSend} disabled={sending || !emailTo} style={{ background: sending || !emailTo ? '#94a3b8' : '#2563eb', color: '#fff', border: 'none', borderRadius: 8, padding: '8px 22px', fontSize: 13, fontWeight: 700, cursor: sending || !emailTo ? 'default' : 'pointer' }}>{sending ? '⏳ Envoi…' : '📧 Envoyer'}</button>}
        </div>
      </div>
    </div>
  );
}

// ── Fenêtre « Traiter un compromis signé (Universign) » ───────────────────────
// Archive les documents dans OneDrive (chemin éditable, créé si besoin par le back),
// envoie le(s) document(s) coché(s) au notaire, pose la date de réception et marque
// la notification traitée. Le programme/lot devinés sont modifiables.
function CompromisArchiveModal({ C, notif, projId, lotIdx, projects, fiches, crm, onClose, onDone, onSetDate }) {
  const MAX_EMAIL = 10 * 1024 * 1024; // 10 Mo
  const today = new Date().toISOString().slice(0, 10);
  const atts = Array.isArray(notif.metadata && notif.metadata.compromis_attachments) ? notif.metadata.compromis_attachments : [];

  // Construit le dossier OneDrive cible (…\Lot N\01 – Compromis\).
  const buildFolder = (p, f, idx) => {
    const seg = s => (s || '').replace(/[/\\:*?"<>|]/g, '_').trim() || 'inconnu';
    const ville = seg((p && p.ville) || '');
    const nom = seg((f && f.nomProgramme) || (p && p.nom) || 'Programme');
    const progSeg = ville ? `${ville} - ${nom}` : nom;
    const lot = `Lot ${((idx != null ? idx : 0)) + 1}`;
    return `\\Blue\\Programmes\\Programmes validés\\${progSeg}\\03 - Vente\\02 - Lots\\${lot}\\01 – Compromis\\`;
  };

  // Emails du notaire rattaché au programme (entreprise CRM ; à défaut, ses contacts).
  const notaireEmails = (f) => {
    const out = []; const seen = new Set();
    ((f && f.intervenants) || []).filter(iv => iv && iv.role === 'Notaire').forEach(iv => {
      const ent = ((crm && crm.entreprises) || []).find(e => e && e.id === iv.entrepriseId);
      if (!ent) return;
      const entE = (ent.emails || '').split(/[,;\s]+/).map(s => s.trim()).filter(Boolean);
      const src = entE.length ? entE : ((ent.contacts || []).map(c => c && c.email).filter(Boolean));
      src.forEach(a => { const k = a.toLowerCase(); if (!seen.has(k)) { seen.add(k); out.push(a); } });
    });
    return out;
  };

  const suggested = (notif.proposed_action && notif.proposed_action.params && notif.proposed_action.params.suggestedFolder) || '';
  const [selProjId, setSelProjId] = React.useState(projId || '');
  const [lotNum, setLotNum] = React.useState(lotIdx != null ? String(lotIdx + 1) : '');
  // Programme courant dérivé de la sélection (fix R3 : le programme est choisissable).
  const proj = (projects || []).find(x => x && x.id === selProjId) || null;
  const fiche = (fiches && fiches[selProjId]) || {};
  const ville_ = ((proj && proj.ville) || '').trim();
  const prog_  = ((fiche && fiche.nomProgramme) || (proj && proj.nom) || '').trim();
  const progNom = (ville_ && prog_ && ville_ !== prog_) ? (ville_ + ' – ' + prog_) : (prog_ || ville_ || selProjId);
  const [folder, setFolder] = React.useState(suggested || buildFolder(proj, fiche, lotIdx));
  // PJ cochées pour l'envoi notaire : par défaut la plus petite (document principal).
  const principalId = atts.length ? atts.slice().sort((a, b) => (a.size || 0) - (b.size || 0))[0].attachmentId : null;
  const [emailChecked, setEmailChecked] = React.useState(() => new Set(principalId ? [principalId] : []));
  const [to, setTo] = React.useState((notaireEmails(fiche) || []).join(', '));
  const [subject, setSubject] = React.useState(`Compromis signé — ${progNom}${lotIdx != null ? ' — Lot ' + (lotIdx + 1) : ''}`);
  const [body, setBody] = React.useState(
`Madame, Monsieur,

Vous trouverez ci-joint le compromis de vente signé pour le lot ${lotIdx != null ? lotIdx + 1 : ''} du programme ${progNom}.

Cordialement,`);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [okMsg, setOkMsg] = React.useState('');

  const inp = { background: '#f8fafc', border: '1px solid #cbd5e1', borderRadius: 8, color: '#1e293b', padding: '6px 10px', fontSize: 12, width: '100%', boxSizing: 'border-box' };
  const fmtMo = b => (b == null ? '?' : (b / (1024 * 1024)).toFixed(1) + ' Mo');

  const toggle = id => setEmailChecked(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const checkedSize = atts.filter(a => emailChecked.has(a.attachmentId)).reduce((s, a) => s + (a.size || 0), 0);
  const overSize = checkedSize > MAX_EMAIL;

  // Recalcule le chemin quand on change le n° de lot.
  const recompute = (num) => {
    const idx = num ? (parseInt(num, 10) - 1) : lotIdx;
    setFolder(buildFolder(proj, fiche, (idx != null && !isNaN(idx)) ? idx : lotIdx));
  };

  // Changement de programme (fix R3) : recale le dossier OneDrive et le destinataire notaire
  // sur le programme réellement choisi (la date posée suit aussi selProjId, cf. run()).
  const onProgChange = (pid) => {
    setSelProjId(pid);
    const np = (projects || []).find(x => x && x.id === pid) || null;
    const nf = (fiches && fiches[pid]) || {};
    const idx = lotNum ? (parseInt(lotNum, 10) - 1) : lotIdx;
    setFolder(buildFolder(np, nf, (idx != null && !isNaN(idx)) ? idx : lotIdx));
    setTo((notaireEmails(nf) || []).join(', '));
  };

  const run = async () => {
    setBusy(true); setErr(''); setOkMsg('');
    const idx = lotNum ? parseInt(lotNum, 10) - 1 : lotIdx;
    try {
      // 1. Archiver toutes les PJ dans OneDrive
      const archResp = await fetch(`/api/agent/notifications/${notif.id}/archive-compromis`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ folderPath: folder, attachmentIds: atts.map(a => a.attachmentId) }),
      });
      const archData = await archResp.json();
      if (!archResp.ok) throw new Error(archData.error || 'Archivage échoué');

      // 2. Envoyer le(s) document(s) coché(s) au notaire
      const toSend = atts.filter(a => emailChecked.has(a.attachmentId));
      if (toSend.length > 0) {
        if (!to.trim()) throw new Error('Aucun email notaire — décochez l’envoi ou renseignez un destinataire.');
        const attachments = [];
        for (const a of toSend) {
          const r = await fetch(`/api/agent/notifications/${notif.id}/attachment-base64?attachmentId=${encodeURIComponent(a.attachmentId)}`, { credentials: 'same-origin' });
          const d = await r.json();
          if (!r.ok) throw new Error(d.error || 'Récupération du document échouée');
          attachments.push({ filename: d.filename, content: d.content, contentType: d.contentType });
        }
        const mailResp = await fetch('/api/email/send', {
          method: 'POST', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ to: to.trim(), subject, body, attachments }),
        });
        const mailData = await mailResp.json();
        if (!mailResp.ok) throw new Error(mailData.error || 'Envoi au notaire échoué (archivage OK)');
      }

      // 3. Poser la date de réception sur le lot
      if (onSetDate) onSetDate(selProjId, idx, today);

      // 4. Marquer la notification traitée
      await fetch(`/api/agent/notifications/${notif.id}`, {
        method: 'PATCH', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'mark_done' }),
      }).catch(() => {});

      setOkMsg('Compromis archivé' + (toSend.length ? ' et envoyé au notaire' : '') + '.');
      setTimeout(() => { if (onDone) onDone(); }, 900);
    } catch (e) {
      setErr(e.message);
    }
    setBusy(false);
  };

  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 2000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16 }} onClick={onClose}>
      <div style={{ background: '#fff', borderRadius: 16, padding: 24, width: 680, maxWidth: '96vw', maxHeight: '92vh', overflowY: 'auto', boxShadow: '0 8px 40px #0003', border: '1px solid #e2e8f0' }} onClick={e => e.stopPropagation()}>
        <div style={{ fontWeight: 800, fontSize: 16, marginBottom: 4 }}>📄 Compromis signé — {progNom}</div>
        <div style={{ fontSize: 12, color: C.muted, marginBottom: 14 }}>{notif.from_email || 'Universign'}{notif.received_at ? ' · reçu le ' + String(notif.received_at).slice(0, 10) : ''}</div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 120px', gap: 10, marginBottom: 12 }}>
          <div>
            <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Programme</div>
            <select value={selProjId} onChange={e => onProgChange(e.target.value)} style={inp}>
              <option value="">— Choisir le programme —</option>
              <GroupedProgrammeOptions projects={projects} fiches={fiches} />
            </select>
          </div>
          <div>
            <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Lot n°</div>
            <input type="number" min="1" value={lotNum} onChange={e => { setLotNum(e.target.value); recompute(e.target.value); }} style={inp} />
          </div>
        </div>

        <div style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Dossier OneDrive (créé automatiquement s’il n’existe pas)</div>
          <input value={folder} onChange={e => setFolder(e.target.value)} style={inp} />
        </div>

        <div style={{ marginBottom: 12, background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: 8, padding: '10px 12px' }}>
          <div style={{ fontSize: 11, fontWeight: 700, color: '#334155', marginBottom: 6 }}>Documents (tous archivés dans OneDrive) — cochez celui à envoyer au notaire :</div>
          {atts.length === 0 && <div style={{ fontSize: 12, color: '#b91c1c' }}>Aucune pièce jointe trouvée sur cette notification.</div>}
          {atts.map(a => (
            <label key={a.attachmentId} style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', padding: '3px 0' }}>
              <input type="checkbox" checked={emailChecked.has(a.attachmentId)} onChange={() => toggle(a.attachmentId)} />
              <span style={{ flex: 1, fontSize: 12 }}>{a.filename}</span>
              <span style={{ fontSize: 11, color: (a.size || 0) > MAX_EMAIL ? '#b91c1c' : C.muted }}>{fmtMo(a.size)}</span>
            </label>
          ))}
          {overSize && <div style={{ marginTop: 6, fontSize: 11, color: '#b91c1c' }}>⚠️ La sélection à envoyer dépasse 10 Mo ({fmtMo(checkedSize)}) — l’email risque d’être refusé. Préférez n’envoyer que le document principal.</div>}
        </div>

        {emailChecked.size > 0 && (
          <div style={{ marginBottom: 12, background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: 8, padding: '10px 12px' }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: '#334155', marginBottom: 6 }}>Email au notaire</div>
            {!to && <div style={{ background: '#fef9c3', border: '1px solid #fde68a', borderRadius: 8, padding: '6px 10px', marginBottom: 8, fontSize: 11, color: '#92400e' }}>⚠️ Aucun notaire rattaché au programme (section Intervenants). Saisis l’email ci-dessous.</div>}
            <div style={{ marginBottom: 6 }}>
              <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Destinataire(s)</div>
              <input value={to} onChange={e => setTo(e.target.value)} style={inp} placeholder="notaire@etude.fr" />
            </div>
            <div style={{ marginBottom: 6 }}>
              <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Objet</div>
              <input value={subject} onChange={e => setSubject(e.target.value)} style={inp} />
            </div>
            <div>
              <div style={{ fontSize: 11, color: C.muted, marginBottom: 3 }}>Message</div>
              <textarea value={body} onChange={e => setBody(e.target.value)} style={{ ...inp, height: 120, resize: 'vertical', lineHeight: 1.5 }} />
            </div>
          </div>
        )}

        {err && <div style={{ background: '#fee2e2', border: '1px solid #fca5a5', borderRadius: 8, padding: '8px 12px', marginBottom: 10, fontSize: 12, color: '#dc2626' }}>❌ {err}</div>}
        {okMsg && <div style={{ background: '#dcfce7', border: '1px solid #86efac', borderRadius: 8, padding: '8px 12px', marginBottom: 10, fontSize: 12, color: '#15803d' }}>✅ {okMsg}</div>}

        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onClose} disabled={busy} style={{ background: '#f1f5f9', border: '1px solid #e2e8f0', borderRadius: 8, padding: '8px 18px', fontSize: 13, cursor: 'pointer' }}>Fermer</button>
          <button onClick={run} disabled={busy || atts.length === 0 || !folder.trim()} style={{ background: (busy || atts.length === 0 || !folder.trim()) ? '#94a3b8' : '#2563eb', color: '#fff', border: 'none', borderRadius: 8, padding: '8px 22px', fontSize: 13, fontWeight: 700, cursor: busy ? 'default' : 'pointer' }}>
            {busy ? '⏳ Traitement…' : (emailChecked.size > 0 ? 'Archiver + envoyer au notaire' : 'Archiver')}
          </button>
        </div>
      </div>
    </div>
  );
}
