function ReceptionPage({ projects, fiches, crm, receptions, setReceptions, onLivraison }) {
  const STATUT_CFG = {
    'planifiée': { bg:'#dbeafe', color:'#1d4ed8', label:'Planifiée' },
    'en cours':  { bg:'#ffedd5', color:'#c2410c', label:'En cours' },
    'validée':   { bg:'#dcfce7', color:'#15803d', label:'Validée' },
    'litige':    { bg:'#fee2e2', color:'#dc2626', label:'Litige' },
  };
  const STATUT_RES = {
    'ouverte':   { bg:'#fef9c3', color:'#92400e', label:'Ouverte' },
    'levée':     { bg:'#dcfce7', color:'#15803d', label:'Levée' },
    'contestée': { bg:'#fff3cd', color:'#b45309', label:'Contestée' },
    'fermée':    { bg:'#dbeafe', color:'#1d4ed8', label:'Fermée (accord)' },
  };

  const fmtD = iso=>{ if(!iso)return''; const[y,m,d]=iso.split('-'); return `${d}/${m}/${y}`; };

  const progs = React.useMemo(()=>projects.filter(p=>!p.isGlobal&&p.statut!=='Abandonné'),[projects]);
  const progName = (id)=>{ const p=projects.find(x=>x.id===id); return p?(p.ville?p.ville+' – ':'')+p.nom:id; };

  /** Construit le chemin OneDrive par défaut pour une réception */
  const buildOneDrivePath = React.useCallback((programmeId, _lotLabel)=>{
    const prog = projects.find(p=>p.id===programmeId);
    const ville = (prog?.ville || '').trim();
    const nom   = (prog?.nom   || 'Programme').trim();
    const progFolder = ville ? `${ville} - ${nom}` : nom;
    return `Blue/Programmes/Programmes validés/${progFolder}/03 - Vente/02 - Lots`;
  },[projects]);

  /** Retourne les lots d'un programme sous forme [{label, statut, clientNom}] */
  const lotsForProg = React.useCallback((progId)=>{
    const f = fiches && fiches[progId];
    if(!f||!Array.isArray(f.lots)||f.lots.length===0) return [];
    return f.lots.map((lot,idx)=>({
      idx,
      label: `Lot ${idx+1}${lot.clientNom?' – '+lot.clientNom:''}`,
      statutCommercial: lot.statutCommercial||'',
      clientNom:   lot.clientNom||'',
    }));
  },[fiches]);

  const [filterProg,   setFilterProg]   = React.useState('all');
  const [filterStatut, setFilterStatut] = React.useState('all');
  const [view,         setView]         = React.useState('list'); // 'list'|'form'|'detail'
  const [form,         setForm]         = React.useState(null);
  const [confirmDel,   setConfirmDel]   = React.useState(null);
  const [newResDesc,      setNewResDesc]      = React.useState('');
  const [newResLoc,       setNewResLoc]       = React.useState('');
  const [newResPhotos,    setNewResPhotos]    = React.useState([]); // [{url, filename, uploadedAt}]
  const [photoUploading,  setPhotoUploading]  = React.useState(false);
  const [photoError,      setPhotoError]      = React.useState('');
  const [lightbox,        setLightbox]        = React.useState(null); // url | null
  // ── Partage MOE ──
  const [shareModalOpen,  setShareModalOpen]  = React.useState(false);
  const [shareData,       setShareData]       = React.useState(null);  // { url, expiresAt, days }
  const [shareTokens,     setShareTokens]     = React.useState([]);    // tokens actifs
  const [shareLoading,    setShareLoading]    = React.useState(false);
  const [shareError,      setShareError]      = React.useState('');
  const [shareDays,       setShareDays]       = React.useState(7);
  const [copied,          setCopied]          = React.useState(false);

  // ── Nouvelle modale d'envoi PDF ──
  const [sendModal,       setSendModal]       = React.useState(null);
  // sendModal = { type:'cr'|'pv', pdfBase64, filename, subject, clientEmail, moeEmail, moeNom, pvData }
  const [sendModalState,  setSendModalState]  = React.useState({ sending:false, error:'', success:false });
  const [generatingPdf,   setGeneratingPdf]   = React.useState(false);
  const [generateError,   setGenerateError]   = React.useState('');
  const [oneDriveModal,   setOneDriveModal]   = React.useState(null); // {photos,folderPath,saving,error,results} | null
  const [odBrowserPath,    setOdBrowserPath]    = React.useState('');
  const [odBrowserFolders, setOdBrowserFolders] = React.useState([]);
  const [odBrowserLoading, setOdBrowserLoading] = React.useState(false);
  const [odBrowserError,   setOdBrowserError]   = React.useState('');
  const [saveError,        setSaveError]        = React.useState('');
  const [isSaving,         setIsSaving]         = React.useState(false);
  const [odUploadError,    setOdUploadError]    = React.useState('');
  // ── Validation livraison ──
  const [validating,       setValidating]       = React.useState(false);
  const [validateError,    setValidateError]    = React.useState('');
  const [pvResult,         setPvResult]         = React.useState(null); // { pvPdf, pvFilename, appelId, montantSolde }

  const EMPTY = React.useCallback(()=>{
    const progId   = progs[0]?.id||'';
    const lots     = lotsForProg(progId);
    const lotLabel = lots.length>0 ? lots[0].label : '';
    return {
      id: uid(),
      programmeId: progId,
      lotIdx: lots.length>0 ? 0 : null,
      lot: lotLabel,
      dateReception: new Date().toISOString().slice(0,10),
      heureReception: '09:00',
      lieu: '',
      statut: 'planifiée',
      reserves: [],
      notes: '',
      onedrivePath: buildOneDrivePath(progId, lotLabel),
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    };
  },[progs, lotsForProg, buildOneDrivePath]);

  const filtered = React.useMemo(()=>{
    let r=receptions;
    if(filterProg!=='all') r=r.filter(x=>x.programmeId===filterProg);
    if(filterStatut!=='all') r=r.filter(x=>x.statut===filterStatut);
    return [...r].sort((a,b)=>(b.dateReception||b.createdAt||'').localeCompare(a.dateReception||a.createdAt||''));
  },[receptions,filterProg,filterStatut]);

  const openNew    = ()=>{ setForm(EMPTY()); setSaveError(''); setOdUploadError(''); setView('detail'); };
  const openDetail = (rec)=>{ setForm({...rec}); setView('detail'); };
  const setField = (k,v)=>setForm(f=>{
    if(k==='programmeId'){
      const lots = lotsForProg(v);
      const lotIdx = lots.length>0 ? 0 : null;
      const lot    = lots.length>0 ? lots[0].label : '';
      return {...f,programmeId:v,lotIdx,lot,onedrivePath:buildOneDrivePath(v,lot),updatedAt:new Date().toISOString()};
    }
    if(k==='lotIdx'){
      const lots = lotsForProg(f.programmeId);
      const idx  = Number(v);
      const lot  = lots[idx]?.label || '';
      return {...f,lotIdx:idx,lot,onedrivePath:buildOneDrivePath(f.programmeId,lot),updatedAt:new Date().toISOString()};
    }
    return {...f,[k]:v,updatedAt:new Date().toISOString()};
  });

  const save = async ()=>{
    const lots = lotsForProg(form.programmeId);
    const lotOk = lots.length===0 ? form.lot.trim()!=='' : form.lotIdx!==null;
    if(!form.programmeId||!lotOk||!form.dateReception) return;
    const allPhotos = (form.reserves||[]).flatMap(r=>(r.photos||[]));
    const hasPhotos = allPhotos.length > 0;
    if(hasPhotos && !(form.onedrivePath||'').trim()) {
      setSaveError('Veuillez choisir un dossier OneDrive avant de sauvegarder — des photos sont attachées à cette réception.');
      return;
    }
    setSaveError(''); setOdUploadError(''); setIsSaving(true);
    const isNew = !receptions.some(r=>r.id===form.id);
    if(isNew) setReceptions(rs=>[...rs,form]);
    else setReceptions(rs=>rs.map(r=>r.id===form.id?form:r));
    if(hasPhotos && (form.onedrivePath||'').trim()) {
      try {
        const r = await fetch('/api/reception/photos/save-to-onedrive',{
          method:'POST', headers:{'Content-Type':'application/json'},
          body: JSON.stringify({ photoUrls: allPhotos.map(p=>p.url), onedriveFolderPath: form.onedrivePath.trim() }),
        });
        const d = await r.json();
        if(!r.ok && !d.results) throw new Error(d.error||'Erreur OneDrive');
        const fails = (d.results||[]).filter(x=>!x.ok);
        if(fails.length>0) {
          setOdUploadError('Photos non envoyées : '+fails.map(x=>x.filename+' ('+x.error+')').join(', '));
          setIsSaving(false);
          return;
        }
        // Persister l'oneDriveUrl dans chaque photo-object
        const urlMap = {};
        (d.results||[]).forEach(r2=>{ if(r2.ok && r2.oneDriveUrl) urlMap[r2.filename] = r2.oneDriveUrl; });
        if(Object.keys(urlMap).length > 0) {
          const updatedReserves = (form.reserves||[]).map(res=>({
            ...res,
            photos: (res.photos||[]).map(ph=>({
              ...ph,
              oneDriveUrl: urlMap[ph.url.split('/').pop()] || ph.oneDriveUrl || null,
            })),
          }));
          const formWithOD = { ...form, reserves: updatedReserves };
          setForm(formWithOD);
          setReceptions(rs=>rs.map(r2=>r2.id===formWithOD.id?formWithOD:r2));
        }
      } catch(e){
        setOdUploadError('Erreur OneDrive : '+e.message);
        setIsSaving(false);
        return;
      }
    }
    setIsSaving(false);
    setView('list');
  };

  const browseFolders = React.useCallback(async(path)=>{
    setOdBrowserLoading(true); setOdBrowserError('');
    try {
      const url = '/api/reception/onedrive/browse'+(path?'?path='+encodeURIComponent(path):'');
      const r = await fetch(url);
      const d = await r.json();
      if(!r.ok) throw new Error(d.error||'Erreur accès OneDrive');
      setOdBrowserPath(path||'');
      setOdBrowserFolders(d);
    } catch(e){ setOdBrowserError(e.message); }
    finally{ setOdBrowserLoading(false); }
  },[]);

  const del = (id)=>{
    setReceptions(rs=>rs.filter(r=>r.id!==id));
    setConfirmDel(null);
    setView('list');
  };

  /* pushReserve défini plus haut avec support photos */

  const toggleReserve = (resId,statut)=>{
    const updated={...form,reserves:(form.reserves||[]).map(r=>r.id===resId?{...r,statut}:r),updatedAt:new Date().toISOString()};
    setForm(updated);
    setReceptions(rs=>rs.map(r=>r.id===form.id?updated:r));
  };

  const delReserve = (resId)=>{
    const res = (form.reserves||[]).find(r=>r.id===resId);
    const photosToDelete = (res?.photos||[]).filter(p=>p.url);
    // Suppression OneDrive + locale (fire-and-forget, silencieux côté UI)
    if(photosToDelete.length>0 && (form.onedrivePath||'').trim()) {
      fetch('/api/reception/photos/delete-from-onedrive',{
        method:'POST',
        headers:{'Content-Type':'application/json'},
        body: JSON.stringify({
          photoUrls: photosToDelete.map(p=>p.url),
          onedriveFolderPath: form.onedrivePath.trim(),
        }),
      }).catch(()=>{});
    }
    const updated={...form,reserves:(form.reserves||[]).filter(r=>r.id!==resId),updatedAt:new Date().toISOString()};
    setForm(updated);
    setReceptions(rs=>rs.map(r=>r.id===form.id?updated:r));
  };

  /* Upload photos pour une réserve */
  const uploadPhotos = React.useCallback(async (files)=>{
    if(!files||files.length===0) return;
    setPhotoUploading(true); setPhotoError('');
    try {
      const fd=new FormData();
      Array.from(files).forEach(f=>fd.append('photos',f));
      const r=await fetch('/api/reception/photos',{method:'POST',body:fd});
      const d=await r.json();
      if(!r.ok) throw new Error(d.error||'Erreur upload');
      setNewResPhotos(prev=>[...prev,...d]);
    } catch(e){ setPhotoError(e.message); }
    finally{ setPhotoUploading(false); }
  },[]);

  /* Ajout réserve (avec photos) */
  const pushReserve = ()=>{
    if(!newResDesc.trim()) return;
    const res={id:uid(),description:newResDesc.trim(),localisation:newResLoc.trim(),statut:'ouverte',createdAt:new Date().toISOString(),photos:newResPhotos};
    const updated={...form,reserves:[...(form.reserves||[]),res],updatedAt:new Date().toISOString()};
    setForm(updated);
    setReceptions(rs=>rs.map(r=>r.id===form.id?updated:r));
    setNewResDesc(''); setNewResLoc(''); setNewResPhotos([]); setPhotoError('');
  };



  /* Récupère l'email du client pour un lot via le CRM (clientId du lot → crm.clients). */
  const getClientEmail = React.useCallback((programmeId, lotIdx) => {
    const fiche = fiches && fiches[programmeId];
    if (!fiche) return '';
    const lot = (fiche.lots || [])[parseInt(lotIdx) || 0];
    if (!lot) return '';
    if (lot.clientId && crm) {
      const client = (crm.clients || []).find(c => c.id === lot.clientId);
      if (client && client.email) return client.email;
    }
    return '';
  }, [fiches, crm]);

  /* Sprint Indivision-5 : récupère l'email + label + nom du 2e destinataire (couple ou indivision).
     - 'seul'       → { email:'', label:'', name:'', role:'' } (pas de 2e destinataire)
     - 'couple'     → { email: conjointEmail, label:'Email conjoint (en copie)', name: conjointNom prenom, role:'conjoint' }
     - 'indivision' → { email: coAcheteur.email, label:'Email co-acheteur', name: coAcheteur.nom, role:'co-acheteur' } */
  const getCoBuyerInfo = React.useCallback((programmeId, lotIdx) => {
    const fiche = fiches && fiches[programmeId];
    if (!fiche || !crm) return { email:'', label:'', name:'', role:'' };
    const lot = (fiche.lots || [])[parseInt(lotIdx) || 0];
    if (!lot) return { email:'', label:'', name:'', role:'' };
    const typeAcq = lot.typeAcquisition || 'seul';
    if (typeAcq === 'couple') {
      const client = (crm.clients || []).find(c => c.id === lot.clientId);
      if (!client) return { email:'', label:'', name:'', role:'' };
      const nom = [client.conjointPrenom, client.conjointNom].filter(Boolean).join(' ').trim();
      return {
        email: client.conjointEmail || '',
        label: 'Email conjoint (en copie)',
        name:  nom,
        role:  'conjoint',
      };
    }
    if (typeAcq === 'indivision') {
      if (!lot.coAcheteurClientId) return { email:'', label:'Email co-acheteur', name:'', role:'co-acheteur' };
      const co = (crm.clients || []).find(c => c.id === lot.coAcheteurClientId);
      if (!co) return { email:'', label:'Email co-acheteur', name:'', role:'co-acheteur' };
      return {
        email: co.email || '',
        label: 'Email co-acheteur',
        name:  co.nom || '',
        role:  'co-acheteur',
      };
    }
    return { email:'', label:'', name:'', role:'' };
  }, [fiches, crm]);

  /* Récupère email + nom du MOE pour un programme */
  const getMoeInfo = React.useCallback((programmeId) => {
    const fiche = fiches && fiches[programmeId];
    if (!fiche) return { email: '', nom: '' };
    const moeInterv = (fiche.intervenants || []).find(iv => iv.role === 'MOE');
    if (!moeInterv) return { email: '', nom: '' };
    const ent = ((crm && crm.entreprises) || []).find(e => e.id === moeInterv.entrepriseId);
    const firstEmail = ent && ent.emails
      ? ent.emails.split(/[,;]/).map(s => s.trim()).filter(Boolean)[0] || ''
      : '';
    return { email: firstEmail, nom: moeInterv.entrepriseNom || '' };
  }, [fiches, crm]);

  /* Envoie la modale d'envoi PDF */
  const sendPdf = React.useCallback(async () => {
    if (!sendModal) return;
    const recipients = [];
    if (sendModal.clientEmail && sendModal.clientEmail.trim()) {
      recipients.push({ email: sendModal.clientEmail.trim(), name: '', role: 'client' });
    }
    // Sprint Indivision-5 : 2e destinataire (couple → conjoint, indivision → co-acheteur)
    if (sendModal.coBuyerEmail && sendModal.coBuyerEmail.trim()) {
      recipients.push({
        email: sendModal.coBuyerEmail.trim(),
        name:  sendModal.coBuyerName || '',
        role:  sendModal.coBuyerRole || 'co-client',
      });
    }
    if (sendModal.moeEmail && sendModal.moeEmail.trim()) {
      recipients.push({ email: sendModal.moeEmail.trim(), name: sendModal.moeNom || '', role: 'moe' });
    }
    if (recipients.length === 0) {
      setSendModalState(s => ({ ...s, error: 'Veuillez renseigner au moins un destinataire.' }));
      return;
    }
    setSendModalState({ sending: true, error: '', success: false });
    try {
      const r = await fetch(`/api/reception/${form.id}/send-pdf`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          pdfBase64:  sendModal.pdfBase64,
          filename:   sendModal.filename,
          subject:    sendModal.subject,
          recipients,
        }),
      });
      const d = await r.json();
      if (!r.ok && !d.results) throw new Error(d.error || 'Erreur envoi');
      const fails = (d.results || []).filter(x => !x.ok);
      if (fails.length > 0) {
        setSendModalState({ sending: false, error: 'Échec : ' + fails.map(f => f.email + ' (' + f.error + ')').join(', '), success: false });
        return;
      }
      setSendModalState({ sending: false, error: '', success: true });
      // Si PV : passer la réception en validée + déclencher LIVRE
      if (sendModal.type === 'pv' && sendModal.pvData) {
        const updated = { ...form, statut: 'validée', updatedAt: new Date().toISOString() };
        setForm(updated);
        setReceptions(rs => rs.map(x => x.id === form.id ? updated : x));
        setPvResult(sendModal.pvData);
        if (onLivraison) onLivraison(form.programmeId, parseInt(form.lotIdx) || 0);
      }
      setTimeout(() => setSendModal(null), 1800);
    } catch (e) {
      setSendModalState({ sending: false, error: e.message, success: false });
    }
  }, [sendModal, form, setReceptions, onLivraison]);

  /* Styles partagés */
  const inp2={background:'#f8fafc',border:'1px solid #cbd5e1',borderRadius:8,color:'#1e293b',padding:'10px 14px',fontSize:15,width:'100%',boxSizing:'border-box'};
  const btn=(bg,sm)=>({background:bg||'#2563eb',color:bg==='#f1f5f9'?'#334155':'#fff',border:'none',borderRadius:8,padding:sm?'6px 14px':'12px 20px',cursor:'pointer',fontSize:sm?13:15,fontWeight:600,minHeight:sm?32:44,display:'inline-flex',alignItems:'center',justifyContent:'center'});
  const badgeStatut=(st)=>{const c=STATUT_CFG[st]||STATUT_CFG['planifiée'];return{background:c.bg,color:c.color,borderRadius:20,padding:'3px 12px',fontSize:12,fontWeight:700,display:'inline-block'};};

  /* ── VUE LISTE ── */
  if(view==='list') return (
    <div style={{maxWidth:640,margin:'0 auto',padding:'16px 12px',fontFamily:'system-ui,sans-serif'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:16}}>
        <div style={{fontWeight:800,fontSize:18,color:'#1e293b'}}>🏠 Réceptions</div>
        <button onClick={openNew} style={{...btn('#2563eb',false),padding:'10px 18px',fontSize:14,gap:6}}>
          ＋ Nouvelle réception
        </button>
      </div>
      <div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap'}}>
        <select value={filterProg} onChange={e=>setFilterProg(e.target.value)}
          style={{...inp2,width:'auto',flex:1,minWidth:140,fontSize:13,padding:'8px 10px'}}>
          <option value="all">Tous les programmes</option>
          <GroupedProgrammeOptions projects={projects} fiches={fiches}/>
        </select>
        <select value={filterStatut} onChange={e=>setFilterStatut(e.target.value)}
          style={{...inp2,width:'auto',flex:1,minWidth:120,fontSize:13,padding:'8px 10px'}}>
          <option value="all">Tous les statuts</option>
          {Object.entries(STATUT_CFG).map(([k,v])=><option key={k} value={k}>{v.label}</option>)}
        </select>
      </div>
      {filtered.length===0&&(
        <div style={{textAlign:'center',padding:'48px 16px',color:'#64748b',background:'#f8fafc',borderRadius:12,border:'1px dashed #cbd5e1'}}>
          <div style={{fontSize:36,marginBottom:12}}>🏠</div>
          <div style={{fontWeight:700,fontSize:15,marginBottom:6}}>Aucune réception</div>
          <div style={{fontSize:13}}>Appuyez sur « ＋ Nouvelle réception » pour commencer.</div>
        </div>
      )}
      {filtered.map(rec=>{
        const nbRes=(rec.reserves||[]).length;
        const nbOuv=(rec.reserves||[]).filter(r=>r.statut==='ouverte').length;
        return (
          <div key={rec.id} onClick={()=>openDetail(rec)}
            style={{background:'#fff',borderRadius:12,border:'1px solid #e2e8f0',padding:'14px 16px',marginBottom:10,cursor:'pointer',boxShadow:'0 1px 4px #0001'}}>
            <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:8}}>
              <span style={badgeStatut(rec.statut)}>{STATUT_CFG[rec.statut]?.label||rec.statut}</span>
              <span style={{fontSize:12,color:'#64748b'}}>{fmtD(rec.dateReception)}{rec.heureReception?' à '+rec.heureReception:''}</span>
            </div>
            <div style={{fontWeight:700,fontSize:15,marginBottom:4,color:'#1e293b'}}>{rec.lot||'(lot non défini)'}</div>
            <div style={{fontSize:13,color:'#64748b',marginBottom:rec.lieu?4:0}}>{progName(rec.programmeId)}</div>
            {rec.lieu&&<div style={{fontSize:12,color:'#94a3b8',marginBottom:nbRes>0?6:0}}>📍 {rec.lieu}</div>}
            {nbRes>0&&(
              <div style={{display:'flex',alignItems:'center',gap:8,marginTop:6}}>
                <span style={{fontSize:12,background:'#f1f5f9',borderRadius:8,padding:'2px 10px',color:'#334155',fontWeight:600}}>
                  {nbRes} réserve{nbRes>1?'s':''}
                </span>
                {nbOuv>0&&<span style={{fontSize:12,background:'#fef9c3',borderRadius:8,padding:'2px 10px',color:'#92400e',fontWeight:600}}>{nbOuv} ouverte{nbOuv>1?'s':''}</span>}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );

  /* ── VUE ÉDITION (création et modification) ── */
  if(view==='detail'&&form) {
    const isNew = !receptions.some(r=>r.id===form.id);
    const nbOuvDet=(form.reserves||[]).filter(r=>r.statut==='ouverte').length;
    return (
      <div style={{maxWidth:640,margin:'0 auto',padding:'16px 12px',fontFamily:'system-ui,sans-serif'}}>
        <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:18}}>
          <button onClick={()=>setView('list')} style={{...btn('#f1f5f9',true),padding:'8px 14px'}}>← Retour</button>
          <div style={{flex:1}}>
            <div style={{fontWeight:800,fontSize:16,color:'#1e293b'}}>{isNew?'Nouvelle réception':form.lot||'Réception'}</div>
            <div style={{fontSize:12,color:'#64748b'}}>{progName(form.programmeId)}</div>
          </div>
          {!isNew&&<button onClick={()=>setShareModalOpen(true)} style={{background:'#f0fdf4',border:'1px solid #86efac',borderRadius:8,padding:'8px 12px',cursor:'pointer',fontSize:13,fontWeight:600,color:'#15803d',minHeight:40,whiteSpace:'nowrap'}} title="Partager avec le maître d'œuvre">🔗 Partager</button>}
          {!isNew&&<button onClick={()=>setConfirmDel(form.id)} style={{background:'#fee2e2',border:'none',borderRadius:8,padding:'8px 12px',cursor:'pointer',fontSize:16,color:'#dc2626',minHeight:40}}>🗑</button>}
        </div>

        {/* Statut — "validée" est réservé au flux de validation en bas de page */}
        <div style={{marginBottom:16}}>
          <div style={{fontSize:12,fontWeight:600,color:'#94a3b8',marginBottom:8,textTransform:'uppercase',letterSpacing:'.04em'}}>Statut</div>
          {(()=>{
            const reservesArr2 = form.reserves || [];
            const nonLevees2 = reservesArr2.filter(r => !['levée','fermée'].includes(r.statut));
            const canMarkValidated = nonLevees2.length === 0;
            const isValidee = form.statut === 'validée';
            const vCfg = STATUT_CFG['validée'];
            return (
              <div style={{display:'flex',gap:8,flexWrap:'wrap',alignItems:'center'}}>
                {Object.entries(STATUT_CFG).filter(([k])=>k!=='validée').map(([k,v])=>(
                  <button key={k}
                    onClick={()=>setField('statut',k)}
                    disabled={isValidee}
                    style={{background:form.statut===k?v.color:v.bg,color:form.statut===k?'#fff':v.color,border:'2px solid '+(form.statut===k?v.color:'transparent'),borderRadius:20,padding:'7px 16px',cursor:isValidee?'default':'pointer',fontWeight:700,fontSize:13,minHeight:40,opacity:isValidee&&form.statut!==k?0.4:1}}>
                    {v.label}
                  </button>
                ))}
                {/* Validée : cliquable seulement si toutes les réserves sont levées/fermées */}
                <button
                  onClick={()=>{ if(canMarkValidated && !isValidee) setField('statut','validée'); }}
                  disabled={!canMarkValidated || isValidee}
                  title={!canMarkValidated ? `${nonLevees2.length} réserve(s) encore ouverte(s) ou contestée(s)` : isValidee ? 'Réception déjà validée' : 'Marquer comme validée'}
                  style={{background:isValidee?vCfg.color:canMarkValidated?vCfg.bg:'#f1f5f9',color:isValidee?'#fff':canMarkValidated?vCfg.color:'#94a3b8',border:'2px solid '+(isValidee?vCfg.color:canMarkValidated?vCfg.color:'transparent'),borderRadius:20,padding:'7px 16px',cursor:canMarkValidated&&!isValidee?'pointer':'default',fontWeight:700,fontSize:13,minHeight:40,opacity:!canMarkValidated?0.5:1}}>
                  {isValidee ? '✓ Validée' : 'Validée'}
                </button>
                {!canMarkValidated && reservesArr2.length > 0 && (
                  <span style={{fontSize:11,color:'#b45309',marginLeft:4}}>
                    ({nonLevees2.length} réserve{nonLevees2.length>1?'s':''} à lever)
                  </span>
                )}
              </div>
            );
          })()}
        </div>

        {/* Champs */}
        <div style={{marginBottom:12}}>
          <div style={{fontSize:12,fontWeight:600,color:'#94a3b8',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>Programme</div>
          <select value={form.programmeId} onChange={e=>setField('programmeId',e.target.value)} style={{...inp2,fontSize:14}}>
            <GroupedProgrammeOptions projects={projects} fiches={fiches}/>
          </select>
        </div>
        <div style={{marginBottom:12}}>
          <div style={{fontSize:12,fontWeight:600,color:'#94a3b8',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>Lot / Logement</div>
          {(()=>{
            const lots=lotsForProg(form.programmeId);
            if(lots.length>0) return (
              <select value={form.lotIdx??0} onChange={e=>setField('lotIdx',e.target.value)} style={{...inp2,fontSize:14}}>
                {lots.map(l=>(
                  <option key={l.idx} value={l.idx}>
                    {l.label}{l.statutCommercial?' ('+l.statutCommercial+')':''}
                  </option>
                ))}
              </select>
            );
            return <input type="text" value={form.lot||''} onChange={e=>setField('lot',e.target.value)} style={{...inp2,fontSize:14}}/>;
          })()}
        </div>
        <div style={{display:'flex',gap:10,marginBottom:12}}>
          <div style={{flex:1}}>
            <div style={{fontSize:12,fontWeight:600,color:'#94a3b8',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>Date</div>
            <input type="date" value={form.dateReception||''} onChange={e=>setField('dateReception',e.target.value)} style={{...inp2,fontSize:14}}/>
          </div>
          <div style={{flex:1}}>
            <div style={{fontSize:12,fontWeight:600,color:'#94a3b8',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>Heure</div>
            <input type="time" value={form.heureReception||''} onChange={e=>setField('heureReception',e.target.value)} style={{...inp2,fontSize:14}}/>
          </div>
        </div>
        <div style={{marginBottom:12}}>
          <div style={{fontSize:12,fontWeight:600,color:'#94a3b8',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>Lieu</div>
          <input type="text" value={form.lieu||''} placeholder="Adresse ou description" onChange={e=>setField('lieu',e.target.value)} style={{...inp2,fontSize:14}}/>
        </div>
        {/* Dossier OneDrive */}
        <div style={{marginBottom:12}}>
          <div style={{fontSize:12,fontWeight:600,color:'#94a3b8',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>Dossier OneDrive</div>
          <div style={{display:'flex',alignItems:'center',gap:8,background:'#f8fafc',border:'1px solid #cbd5e1',borderRadius:8,padding:'8px 12px'}}>
            <span style={{flex:1,fontSize:12,fontFamily:'monospace',color:form.onedrivePath?'#1e293b':'#94a3b8',minWidth:0,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>
              📁 {form.onedrivePath||'Aucun dossier sélectionné'}
            </span>
            <button
              onClick={()=>{
                const p=(form.onedrivePath||'').trim()||buildOneDrivePath(form.programmeId,form.lot);
                setOdBrowserPath(p); setOdBrowserFolders([]); setOdBrowserError('');
                setOneDriveModal(true); browseFolders(p);
              }}
              style={{background:'#2563eb',color:'#fff',border:'none',borderRadius:6,padding:'5px 12px',cursor:'pointer',fontSize:12,fontWeight:600,whiteSpace:'nowrap',flexShrink:0}}>
              📁 Choisir
            </button>
          </div>
        </div>
        {/* Section réserves */}
        <div style={{borderTop:'2px solid #e2e8f0',margin:'20px 0 14px'}}>
          <div style={{fontWeight:800,fontSize:15,color:'#1e293b',marginTop:14,display:'flex',alignItems:'center',gap:10}}>
            ⚠️ Réserves
            {nbOuvDet>0&&<span style={{background:'#fef9c3',color:'#92400e',borderRadius:10,padding:'1px 10px',fontSize:12,fontWeight:700}}>{nbOuvDet} ouverte{nbOuvDet>1?'s':''}</span>}
          </div>
        </div>

        {(form.reserves||[]).length===0&&(
          <div style={{textAlign:'center',padding:'20px',color:'#94a3b8',fontSize:13,background:'#f8fafc',borderRadius:10,marginBottom:14}}>
            Aucune réserve enregistrée
          </div>
        )}

        {(form.reserves||[]).map(res=>{
          const rCfg=STATUT_RES[res.statut]||STATUT_RES['ouverte'];
          return (
            <div key={res.id} style={{background:'#f8fafc',borderRadius:10,border:'1px solid #e2e8f0',padding:'12px 14px',marginBottom:8}}>
              <div style={{display:'flex',alignItems:'flex-start',gap:8,marginBottom:8}}>
                <span style={{background:rCfg.bg,color:rCfg.color,borderRadius:10,padding:'2px 10px',fontSize:11,fontWeight:700,flexShrink:0,whiteSpace:'nowrap'}}>{rCfg.label}</span>
                <div style={{flex:1}}>
                  {res.localisation&&<div style={{fontSize:11,color:'#94a3b8',marginBottom:2}}>📍 {res.localisation}</div>}
                  <div style={{fontSize:14,color:'#1e293b',lineHeight:1.4}}>{res.description}</div>
                </div>
                <button onClick={()=>delReserve(res.id)} style={{background:'none',border:'none',color:'#dc2626',cursor:'pointer',fontSize:16,padding:'0 4px',flexShrink:0,lineHeight:1}}>🗑</button>
              </div>
              {/* Vignettes photos */}
              {(res.photos||[]).length>0&&(
                <div style={{display:'flex',flexWrap:'wrap',gap:6,marginBottom:6}}>
                  {res.photos.map((ph,pi)=>(
                    <img key={pi} src={ph.url} alt={ph.filename||'photo'}
                      onClick={()=>setLightbox(ph.url)}
                      style={{width:80,height:80,objectFit:'cover',borderRadius:8,border:'1px solid #e2e8f0',cursor:'zoom-in'}}/>
                  ))}
                </div>
              )}
              <div style={{display:'flex',gap:6,flexWrap:'wrap'}}>
                {Object.entries(STATUT_RES).map(([k,v])=>(
                  <button key={k} onClick={()=>toggleReserve(res.id,k)}
                    style={{background:res.statut===k?v.color:v.bg,color:res.statut===k?'#fff':v.color,border:'1px solid '+(res.statut===k?v.color:'transparent'),borderRadius:14,padding:'4px 12px',cursor:'pointer',fontWeight:600,fontSize:11,minHeight:28}}>
                    {v.label}
                  </button>
                ))}
              </div>
            </div>
          );
        })}

        {/* Ajout réserve */}
        <div style={{background:'#f0f9ff',borderRadius:10,border:'1px solid #bfdbfe',padding:'12px 14px',marginBottom:20}}>
          <div style={{fontSize:13,fontWeight:700,color:'#1d4ed8',marginBottom:8}}>＋ Ajouter une réserve</div>
          <input value={newResLoc} onChange={e=>setNewResLoc(e.target.value)}
            placeholder="Localisation (Cuisine, SdB, Entrée…)" style={{...inp2,marginBottom:8,fontSize:14}}/>
          <textarea value={newResDesc} onChange={e=>setNewResDesc(e.target.value)}
            placeholder="Description du défaut constaté…" rows={2}
            style={{...inp2,resize:'vertical',fontSize:14,marginBottom:8}}/>
          {/* Zone photos en attente */}
          {newResPhotos.length>0&&(
            <div style={{display:'flex',flexWrap:'wrap',gap:6,marginBottom:8}}>
              {newResPhotos.map((ph,pi)=>(
                <div key={pi} style={{position:'relative'}}>
                  <img src={ph.url} alt={ph.filename||'photo'}
                    onClick={()=>setLightbox(ph.url)}
                    style={{width:72,height:72,objectFit:'cover',borderRadius:6,border:'1px solid #bfdbfe',cursor:'zoom-in'}}/>
                  <button onClick={()=>setNewResPhotos(p=>p.filter((_,i)=>i!==pi))}
                    style={{position:'absolute',top:-6,right:-6,background:'#dc2626',color:'#fff',border:'none',borderRadius:'50%',width:18,height:18,fontSize:10,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',padding:0,lineHeight:1}}>✕</button>
                </div>
              ))}
            </div>
          )}
          {photoError&&<div style={{fontSize:12,color:'#dc2626',marginBottom:6}}>{photoError}</div>}
          <div style={{display:'flex',gap:8,marginBottom:8}}>
            <label style={{...btn('#e0f2fe',true),flex:'1 1 0',color:'#0369a1',cursor:'pointer',textAlign:'center',opacity:photoUploading?0.6:1,gap:4}}>
              📷 Caméra
              <input type="file" accept="image/*" capture="environment" style={{display:'none'}}
                onChange={e=>{uploadPhotos(e.target.files);e.target.value='';}}
                disabled={photoUploading}/>
            </label>
            <label style={{...btn('#e0f2fe',true),flex:'1 1 0',color:'#0369a1',cursor:'pointer',textAlign:'center',opacity:photoUploading?0.6:1,gap:4}}>
              🖼 Galerie
              <input type="file" accept="image/*" multiple style={{display:'none'}}
                onChange={e=>{uploadPhotos(e.target.files);e.target.value='';}}
                disabled={photoUploading}/>
            </label>
            {photoUploading&&<span style={{fontSize:12,color:'#0369a1',alignSelf:'center',whiteSpace:'nowrap'}}>Envoi…</span>}
          </div>
          <button onClick={pushReserve} disabled={!newResDesc.trim()||photoUploading}
            style={{...btn('#2563eb'),width:'100%',opacity:(!newResDesc.trim()||photoUploading)?0.5:1}}>
            {photoUploading?'Upload en cours…':'Ajouter la réserve'}
          </button>
        </div>

        {/* ── Boutons + erreurs ── */}
        {(()=>{
          const clientEmailVal = getClientEmail(form.programmeId, form.lotIdx ?? 0);
          const coBuyerInfoVal = getCoBuyerInfo(form.programmeId, form.lotIdx ?? 0);
          const moeInfoVal     = getMoeInfo(form.programmeId);
          const reservesArr    = form.reserves || [];
          const nonLevees      = reservesArr.filter(r => !['levée','fermée'].includes(r.statut));
          const canValidate    = form.statut === 'validée'; // PV actif seulement si réception validée
          const btnSm = (bg, disabled) => ({
            background: disabled ? '#94a3b8' : (bg || '#2563eb'),
            color: '#fff', border: 'none', borderRadius: 8,
            padding: '9px 14px', cursor: disabled ? 'not-allowed' : 'pointer',
            fontSize: 13, fontWeight: 600, minHeight: 38,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 4,
            opacity: disabled ? 0.6 : 1,
          });
          const openModal = (type, pdfBase64, filename, subject, pvData) => {
            setSendModal({
              type, pdfBase64, filename, subject,
              clientEmail: clientEmailVal,
              // Sprint Indivision-5 : 2e destinataire pré-rempli si couple ou indivision
              coBuyerEmail: coBuyerInfoVal.email || '',
              coBuyerLabel: coBuyerInfoVal.label || '',
              coBuyerName:  coBuyerInfoVal.name  || '',
              coBuyerRole:  coBuyerInfoVal.role  || '',
              moeEmail:     moeInfoVal.email,
              moeNom:       moeInfoVal.nom,
              pvData:       pvData || null,
            });
            setSendModalState({ sending: false, error: '', success: false });
          };
          const handleSendCR = async () => {
            setGenerateError(''); setGeneratingPdf(true);
            try {
              const r = await fetch(`/api/reception/${form.id}/generate-pdf`, {
                method: 'POST', headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ programmeId: form.programmeId, lotIdx: form.lotIdx ?? 0 }),
              });
              const d = await r.json();
              if (!r.ok) throw new Error(d.error || 'Erreur génération PDF');
              openModal('cr', d.pdf, d.filename, `Compte rendu de réception – ${form.lot} – ${fmtD(form.dateReception)}`);
            } catch(e) { setGenerateError(e.message); }
            finally { setGeneratingPdf(false); }
          };
          const handleSendPV = async () => {
            setValidateError(''); setValidating(true);
            try {
              const lotIdx = form.lotIdx != null ? parseInt(form.lotIdx) : 0;
              const r = await fetch(`/api/reception/${form.id}/validate`, {
                method: 'POST', headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ programmeId: form.programmeId, lotIdx, lotId: String(lotIdx + 1) }),
              });
              const d = await r.json();
              if (!r.ok) { setValidateError(d.error || 'Erreur validation'); return; }
              openModal('pv', d.pvPdf, d.pvFilename, `Procès-verbal de livraison – ${form.lot} – ${fmtD(form.dateReception)}`, d);
            } catch(e) { setValidateError(e.message); }
            finally { setValidating(false); }
          };
          return (
            <div>
              {saveError&&<div style={{background:'#fee2e2',color:'#dc2626',borderRadius:8,padding:'8px 12px',fontSize:13,marginBottom:8}}>{saveError}</div>}
              {odUploadError&&<div style={{background:'#fee2e2',color:'#dc2626',borderRadius:8,padding:'8px 12px',fontSize:13,marginBottom:8}}>⚠️ {odUploadError}</div>}
              {generateError&&<div style={{background:'#fee2e2',color:'#dc2626',borderRadius:8,padding:'8px 12px',fontSize:13,marginBottom:8}}>✕ {generateError}</div>}
              {validateError&&<div style={{background:'#fee2e2',color:'#dc2626',borderRadius:8,padding:'8px 12px',fontSize:13,marginBottom:8}}>✕ {validateError}</div>}
              {form.statut!=='validée'&&(
                <div style={{background:'#fff3cd',color:'#92400e',borderRadius:8,padding:'8px 12px',fontSize:12,marginBottom:8}}>
                  ⚠ Passez la réception en statut <strong>Validée</strong> (en haut) pour pouvoir envoyer le PV de livraison.
                  {nonLevees.length>0 && ` (${nonLevees.length} réserve${nonLevees.length>1?'s':''} encore ouverte${nonLevees.length>1?'s':''})`}
                </div>
              )}
              {/* 4 boutons sur une ligne */}
              <div style={{display:'flex',gap:8,flexWrap:'wrap',marginBottom:12}}>
                <button onClick={save} disabled={isSaving} style={{...btnSm('#16a34a',isSaving),flex:'1 1 auto'}}>
                  {isSaving?'…':'✓ Sauvegarder'}
                </button>
                <button onClick={handleSendCR} disabled={generatingPdf||validating} style={{...btnSm('#7c3aed',generatingPdf||validating),flex:'2 1 auto'}}>
                  {generatingPdf?'Génération…':'📄 Compte rendu'}
                </button>
                <button onClick={handleSendPV} disabled={!canValidate||validating||generatingPdf}
                  title={!canValidate?'Passez d\'abord la réception en statut Validée':''}
                  style={{...btnSm('#15803d',!canValidate||validating||generatingPdf),flex:'2 1 auto'}}>
                  {validating?'Génération PV…':'📋 PV livraison'}
                </button>
                <button onClick={()=>setView('list')} style={{...btnSm('#64748b',false)}}>Annuler</button>
              </div>
              {/* Bandeau si déjà validée */}
              {form.statut==='validée'&&(
                <div style={{background:'#dcfce7',border:'1px solid #86efac',borderRadius:10,padding:'12px 16px',marginBottom:8}}>
                  <div style={{fontWeight:700,color:'#15803d',fontSize:13}}>✓ Réception validée — livraison confirmée</div>
                  {pvResult&&<div style={{fontSize:12,color:'#166534',marginTop:4}}>
                    Appel solde 5% créé (n°{pvResult.appelId}){pvResult.montantSolde>0?' — '+Number(pvResult.montantSolde).toLocaleString('fr-FR',{maximumFractionDigits:0})+' €':''}. Générez la facture depuis l'onglet Notifications.
                  </div>}
                </div>
              )}
            </div>
          );
        })()}

        {/* Lightbox photo */}
        {lightbox&&(
          <div onClick={()=>setLightbox(null)}
            style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.85)',zIndex:4000,display:'flex',alignItems:'center',justifyContent:'center',padding:16,cursor:'zoom-out'}}>
            <img src={lightbox} alt="photo" style={{maxWidth:'100%',maxHeight:'90vh',borderRadius:8,boxShadow:'0 4px 32px #0008'}}/>
            <button onClick={e=>{e.stopPropagation();setLightbox(null);}}
              style={{position:'absolute',top:16,right:16,background:'rgba(255,255,255,.15)',border:'none',color:'#fff',borderRadius:'50%',width:40,height:40,fontSize:20,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center'}}>✕</button>
          </div>
        )}

        {/* ── Modale d'envoi PDF (compte rendu ou PV) ── */}
        {sendModal&&(
          <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.55)',zIndex:3500,display:'flex',alignItems:'flex-start',justifyContent:'center',padding:16,overflowY:'auto'}}>
            <div style={{background:'#fff',borderRadius:16,padding:24,width:'100%',maxWidth:520,boxShadow:'0 8px 32px #0002',marginTop:16,marginBottom:16}}>
              <div style={{fontWeight:800,fontSize:17,marginBottom:4,color:'#1e293b'}}>
                {sendModal.type==='pv'?'📋 Envoyer le PV de livraison':'📄 Envoyer le compte rendu'}
              </div>
              <div style={{fontSize:12,color:'#64748b',marginBottom:12}}>
                {sendModal.filename}
              </div>
              {/* Aperçu PDF */}
              <div style={{marginBottom:14}}>
                <div style={{border:'1px solid #e2e8f0',borderRadius:8,overflow:'hidden',height:280}}>
                  <iframe
                    src={`data:application/pdf;base64,${sendModal.pdfBase64}`}
                    style={{width:'100%',height:'100%',border:'none'}}
                    title="Aperçu PDF"
                  />
                </div>
                <div style={{fontSize:11,color:'#94a3b8',marginTop:4,textAlign:'center'}}>
                  Si l'aperçu ne s'affiche pas —{' '}
                  <a href={`data:application/pdf;base64,${sendModal.pdfBase64}`}
                    download={sendModal.filename} style={{color:'#2563eb'}}>télécharger le PDF</a>
                </div>
              </div>

              {/* Sujet */}
              <div style={{marginBottom:12}}>
                <div style={{fontSize:12,fontWeight:600,color:'#64748b',marginBottom:4}}>SUJET</div>
                <input value={sendModal.subject}
                  onChange={e=>setSendModal(m=>({...m,subject:e.target.value}))}
                  style={{...inp2,fontSize:14}}/>
              </div>

              {/* Email client */}
              <div style={{marginBottom:12}}>
                <div style={{fontSize:12,fontWeight:600,color:'#64748b',marginBottom:4}}>EMAIL CLIENT</div>
                <input value={sendModal.clientEmail}
                  onChange={e=>setSendModal(m=>({...m,clientEmail:e.target.value}))}
                  placeholder="email du client…" style={{...inp2,fontSize:14}}/>
              </div>

              {/* Sprint Indivision-5 : 2e destinataire (couple ou indivision) — label dynamique */}
              {sendModal.coBuyerLabel&&(
                <div style={{marginBottom:12}}>
                  <div style={{fontSize:12,fontWeight:600,color:'#64748b',marginBottom:4}}>
                    {sendModal.coBuyerLabel.toUpperCase()}
                    {sendModal.coBuyerName?<span style={{fontWeight:400,color:'#94a3b8'}}> ({sendModal.coBuyerName})</span>:''}
                  </div>
                  <input value={sendModal.coBuyerEmail}
                    onChange={e=>setSendModal(m=>({...m,coBuyerEmail:e.target.value}))}
                    placeholder={sendModal.coBuyerRole==='conjoint'?'email du conjoint — laisser vide pour ne pas envoyer':'email du co-acheteur…'}
                    style={{...inp2,fontSize:14}}/>
                </div>
              )}

              {/* Email MOE */}
              <div style={{marginBottom:16}}>
                <div style={{fontSize:12,fontWeight:600,color:'#64748b',marginBottom:4}}>
                  EMAIL MAÎTRE D'ŒUVRE {sendModal.moeNom?<span style={{fontWeight:400,color:'#94a3b8'}}>({sendModal.moeNom})</span>:''}
                </div>
                <input value={sendModal.moeEmail}
                  onChange={e=>setSendModal(m=>({...m,moeEmail:e.target.value}))}
                  placeholder="email du MOE — laisser vide pour ne pas envoyer"
                  style={{...inp2,fontSize:14}}/>
                {!sendModal.moeEmail&&(
                  <div style={{fontSize:11,color:'#94a3b8',marginTop:4}}>
                    Aucun email MOE dans le CRM pour ce programme. Vous pouvez le saisir manuellement.
                  </div>
                )}
              </div>

              {sendModalState.error&&(
                <div style={{color:'#dc2626',fontSize:13,marginBottom:10,background:'#fee2e2',borderRadius:6,padding:'8px 12px'}}>
                  ✕ {sendModalState.error}
                </div>
              )}
              {sendModalState.success&&(
                <div style={{color:'#15803d',fontSize:13,marginBottom:10,background:'#dcfce7',borderRadius:6,padding:'8px 12px'}}>
                  ✓ Email{sendModal.moeEmail?'s envoyés':' envoyé'} avec succès !
                  {sendModal.type==='pv'?' La réception est marquée comme validée.':''}
                </div>
              )}

              <div style={{display:'flex',gap:10}}>
                <button
                  disabled={sendModalState.sending||sendModalState.success||(!sendModal.clientEmail&&!sendModal.coBuyerEmail&&!sendModal.moeEmail)}
                  onClick={sendPdf}
                  style={{...btn('#2563eb'),flex:1,opacity:(sendModalState.sending||sendModalState.success||(!sendModal.clientEmail&&!sendModal.coBuyerEmail&&!sendModal.moeEmail))?0.6:1}}>
                  {sendModalState.sending?'Envoi…':'📤 Envoyer'}
                </button>
                <button onClick={()=>setSendModal(null)} style={{...btn('#64748b'),padding:'12px 16px'}}>Annuler</button>
              </div>
            </div>
          </div>
        )}

        {/* Modale OneDrive — sélecteur de dossier */}
        {oneDriveModal&&(
          <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.55)',zIndex:3300,display:'flex',alignItems:'flex-start',justifyContent:'center',padding:16,overflowY:'auto'}}>
            <div style={{background:'#fff',borderRadius:16,padding:24,width:'100%',maxWidth:480,boxShadow:'0 8px 32px #0002',marginTop:16,marginBottom:16}}>
              <div style={{fontWeight:800,fontSize:17,marginBottom:4,color:'#1e293b'}}>📁 Choisir un dossier OneDrive</div>
              <div style={{fontSize:12,color:'#64748b',marginBottom:14}}>Naviguez et sélectionnez le dossier de destination des photos.</div>
              {/* Fil d'Ariane */}
              <div style={{display:'flex',flexWrap:'wrap',alignItems:'center',gap:2,marginBottom:8,background:'#f8fafc',borderRadius:6,padding:'6px 10px',border:'1px solid #e2e8f0'}}>
                <button onClick={()=>browseFolders('')} disabled={odBrowserLoading}
                  style={{background:'none',border:'none',color:'#2563eb',cursor:'pointer',padding:'1px 4px',fontSize:12,fontWeight:600}}>
                  Racine
                </button>
                {(odBrowserPath||'').split('/').filter(Boolean).map((seg,i,arr)=>{
                  const pathUpTo=arr.slice(0,i+1).join('/');
                  return (
                    <React.Fragment key={i}>
                      <span style={{color:'#94a3b8',fontSize:10}}>/</span>
                      <button onClick={()=>browseFolders(pathUpTo)} disabled={odBrowserLoading}
                        style={{background:'none',border:'none',color:i===arr.length-1?'#1e293b':'#2563eb',cursor:'pointer',padding:'1px 4px',fontSize:12,fontWeight:i===arr.length-1?700:400,maxWidth:130,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>
                        {seg}
                      </button>
                    </React.Fragment>
                  );
                })}
              </div>
              {/* Dossier courant + bouton Sélectionner */}
              <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:12,background:'#f0fdf4',border:'1px solid #86efac',borderRadius:8,padding:'8px 12px'}}>
                <span style={{fontSize:11,color:'#15803d',flex:1,wordBreak:'break-all',fontFamily:'monospace',minWidth:0}}>
                  📁 {odBrowserPath||'(racine OneDrive)'}
                </span>
                <button onClick={()=>{ setField('onedrivePath',odBrowserPath); setOneDriveModal(null); }}
                  style={{background:'#16a34a',color:'#fff',border:'none',borderRadius:6,padding:'5px 10px',cursor:'pointer',fontSize:12,fontWeight:700,whiteSpace:'nowrap',flexShrink:0}}>
                  ✓ Sélectionner
                </button>
              </div>
              {/* Erreur */}
              {odBrowserError&&(
                <div style={{fontSize:12,color:'#dc2626',background:'#fee2e2',borderRadius:6,padding:'7px 10px',marginBottom:8}}>
                  {odBrowserError.toLowerCase().includes('non connecté')||odBrowserError.toLowerCase().includes('microsoft')
                    ?'Non connecté à Microsoft — reconnectez-vous via le bouton Microsoft.'
                    :odBrowserError}
                </div>
              )}
              {/* Liste des sous-dossiers */}
              <div style={{border:'1px solid #e2e8f0',borderRadius:8,overflow:'hidden',maxHeight:200,overflowY:'auto',marginBottom:16}}>
                {odBrowserLoading&&<div style={{fontSize:12,color:'#64748b',textAlign:'center',padding:'14px 8px'}}>Chargement…</div>}
                {!odBrowserLoading&&!odBrowserError&&odBrowserFolders.length===0&&(
                  <div style={{fontSize:12,color:'#94a3b8',textAlign:'center',padding:'14px 8px'}}>Aucun sous-dossier</div>
                )}
                {!odBrowserLoading&&odBrowserFolders.map((folder,fi)=>(
                  <button key={folder.id||fi}
                    onClick={()=>browseFolders((odBrowserPath?odBrowserPath+'/':'')+folder.name)}
                    style={{display:'flex',alignItems:'center',width:'100%',textAlign:'left',background:'none',border:'none',borderBottom:'1px solid #f1f5f9',padding:'8px 12px',cursor:'pointer',fontSize:13,color:'#1e293b',gap:8}}>
                    <span>📁</span>
                    <span style={{flex:1,minWidth:0,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{folder.name}</span>
                    <span style={{color:'#94a3b8',fontSize:11,flexShrink:0}}>›</span>
                  </button>
                ))}
              </div>
              <button onClick={()=>setOneDriveModal(null)} style={{...btn('#64748b'),width:'100%'}}>Annuler</button>
            </div>
          </div>
        )}

        {/* ── Modale partage MOE ── */}
        {shareModalOpen&&(
          <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.55)',zIndex:3200,display:'flex',alignItems:'flex-start',justifyContent:'center',padding:16,overflowY:'auto'}}>
            <div style={{background:'#fff',borderRadius:16,padding:24,width:'100%',maxWidth:480,boxShadow:'0 8px 32px #0002',marginTop:16,marginBottom:16}}>
              <div style={{fontWeight:800,fontSize:17,marginBottom:4,color:'#1e293b'}}>🔗 Partager avec le maître d'œuvre</div>
              <div style={{fontSize:12,color:'#64748b',marginBottom:16}}>
                Le lien donne accès uniquement à cette réception. Le MOE peut consulter les réserves, en créer et uploader des photos — pas de changement de statuts.
              </div>

              {/* Durée */}
              <div style={{marginBottom:14}}>
                <div style={{fontSize:12,fontWeight:600,color:'#64748b',marginBottom:6}}>DURÉE DE VALIDITÉ</div>
                <div style={{display:'flex',gap:8}}>
                  {[3,7,14,30].map(d=>(
                    <button key={d} onClick={()=>setShareDays(d)}
                      style={{flex:1,padding:'8px 0',borderRadius:8,border:'2px solid '+(shareDays===d?'#2e6b5e':'#e2e8f0'),background:shareDays===d?'#f0fdf4':'#fff',color:shareDays===d?'#15803d':'#334155',fontWeight:600,fontSize:13,cursor:'pointer'}}>
                      {d}j
                    </button>
                  ))}
                </div>
              </div>

              {shareError&&<div style={{background:'#fee2e2',color:'#dc2626',borderRadius:8,padding:'8px 12px',fontSize:13,marginBottom:12}}>✕ {shareError}</div>}

              {/* Lien généré */}
              {shareData&&(
                <div style={{marginBottom:14}}>
                  <div style={{fontSize:12,fontWeight:600,color:'#64748b',marginBottom:6}}>LIEN DE PARTAGE</div>
                  <div style={{display:'flex',gap:8,alignItems:'center',background:'#f8fafc',border:'1px solid #e2e8f0',borderRadius:8,padding:'10px 12px'}}>
                    <span style={{flex:1,fontSize:12,fontFamily:'monospace',color:'#1e293b',wordBreak:'break-all'}}>{shareData.url}</span>
                    <button onClick={()=>{navigator.clipboard.writeText(shareData.url);setCopied(true);setTimeout(()=>setCopied(false),2000);}}
                      style={{background:copied?'#dcfce7':'#2e6b5e',color:copied?'#15803d':'#fff',border:'none',borderRadius:6,padding:'6px 12px',cursor:'pointer',fontSize:12,fontWeight:600,flexShrink:0,whiteSpace:'nowrap'}}>
                      {copied?'✓ Copié':'Copier'}
                    </button>
                  </div>
                  <div style={{fontSize:11,color:'#64748b',marginTop:6}}>
                    Expire le {new Date(shareData.expiresAt).toLocaleDateString('fr-FR',{day:'numeric',month:'long',year:'numeric'})}
                  </div>
                </div>
              )}

              <div style={{display:'flex',gap:10}}>
                <button
                  disabled={shareLoading}
                  onClick={async()=>{
                    setShareLoading(true); setShareError(''); setShareData(null);
                    try {
                      const r = await fetch(`/api/reception/${form.id}/share`,{
                        method:'POST', headers:{'Content-Type':'application/json'},
                        body: JSON.stringify({ days: shareDays }),
                      });
                      const d = await r.json();
                      if(!r.ok) throw new Error(d.error||'Erreur génération');
                      setShareData(d);
                    } catch(e){ setShareError(e.message); }
                    finally { setShareLoading(false); }
                  }}
                  style={{background:'#2e6b5e',color:'#fff',border:'none',borderRadius:8,padding:'11px 18px',cursor:shareLoading?'not-allowed':'pointer',fontSize:14,fontWeight:600,opacity:shareLoading?0.6:1,flex:1}}>
                  {shareLoading?'Génération…':shareData?'Nouveau lien':'Générer le lien'}
                </button>
                <button onClick={()=>{setShareModalOpen(false);setShareData(null);setShareError('');}}
                  style={{background:'#f1f5f9',color:'#334155',border:'none',borderRadius:8,padding:'11px 16px',cursor:'pointer',fontSize:14,fontWeight:600}}>
                  Fermer
                </button>
              </div>
            </div>
          </div>
        )}

        {/* Modale suppression */}
        {confirmDel&&(
          <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.55)',zIndex:3000,display:'flex',alignItems:'center',justifyContent:'center',padding:16}}>
            <div style={{background:'#fff',borderRadius:16,padding:28,width:'100%',maxWidth:340,textAlign:'center',boxShadow:'0 8px 32px #0002'}}>
              <div style={{fontSize:36,marginBottom:12}}>🗑</div>
              <div style={{fontWeight:800,fontSize:16,marginBottom:8}}>Supprimer cette réception ?</div>
              <div style={{fontSize:13,color:'#64748b',marginBottom:20}}>{form.lot} — {fmtD(form.dateReception)}</div>
              <div style={{display:'flex',gap:10}}>
                <button onClick={()=>del(confirmDel)} style={{...btn('#dc2626'),flex:1}}>Supprimer</button>
                <button onClick={()=>setConfirmDel(null)} style={{...btn('#64748b'),flex:1}}>Annuler</button>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }
  return null;
}

/* ══════════════════════════════════════════════════════════════
   ReconciliationPage
══════════════════════════════════════════════════════════════ */