/* bundle 1/4 */

/* ╔══ logo.jsx ══╗ */
/* global React */

// Logestimmo mark — reconstructed from brand file.
// Two interlocking isometric slabs forming an S:
//   · Blue: wide flat shelf, top-left (dark navy → bright blue gradient)
//   · Orange: compact block, bottom-right (yellow-orange → red gradient)
function Logo({ size = 28 }) {
  const id = `lg${size}`;
  return (
    <svg width={size} height={size} viewBox="0 0 100 100" style={{display:"block",flexShrink:0}}>
      <defs>
        {/* Blue — horizontal gradient, dark navy left → bright blue right */}
        <linearGradient id={`${id}-bt`} x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%"   stopColor="#0B2270"/>
          <stop offset="45%"  stopColor="#1948C8"/>
          <stop offset="100%" stopColor="#3A9AF5"/>
        </linearGradient>
        {/* Blue side face — darker */}
        <linearGradient id={`${id}-bs`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%"   stopColor="#0A1E5C"/>
          <stop offset="100%" stopColor="#0D2A7A"/>
        </linearGradient>
        {/* Orange top — yellow left → orange right */}
        <linearGradient id={`${id}-ot`} x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%"   stopColor="#FFD040"/>
          <stop offset="50%"  stopColor="#F97316"/>
          <stop offset="100%" stopColor="#E85A00"/>
        </linearGradient>
        {/* Orange front face — deeper orange → red */}
        <linearGradient id={`${id}-of`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%"   stopColor="#F07010"/>
          <stop offset="100%" stopColor="#B83A00"/>
        </linearGradient>
      </defs>

      {/* ── BLUE SLAB (top-left, wide flat shelf) ── */}
      {/* Top face */}
      <path d="M 6 36  L 56 12  L 76 28  L 26 52 Z"
            fill={`url(#${id}-bt)`}/>
      {/* Left / bottom face (dark, thin) */}
      <path d="M 6 36  L 26 52  L 26 68  L 6 52 Z"
            fill={`url(#${id}-bs)`}/>

      {/* ── ORANGE BLOCK (bottom-right, compact cube) ── */}
      {/* Top face */}
      <path d="M 40 54  L 76 38  L 92 54  L 56 70 Z"
            fill={`url(#${id}-ot)`}/>
      {/* Front face */}
      <path d="M 56 70  L 92 54  L 92 80  L 56 96 Z"
            fill={`url(#${id}-of)`}/>
      {/* Left face (tiny accent) */}
      <path d="M 40 54  L 56 70  L 56 96  L 40 80 Z"
            fill="#C44408" opacity="0.9"/>
    </svg>
  );
}

Object.assign(window, { Logo });

/* ─── Toast global de feedback (démo) ─── */
function lgiToast(msg, tone) {
  let host = document.getElementById("lgi-toast-host");
  if (!host) {
    host = document.createElement("div");
    host.id = "lgi-toast-host";
    host.style.cssText = "position:fixed;bottom:24px;left:50%;transform:translateX(-50%);z-index:9999;display:flex;flex-direction:column;gap:8px;align-items:center;pointer-events:none;";
    document.body.appendChild(host);
  }
  const t = document.createElement("div");
  const accent = tone === "warn" ? "#F97316" : tone === "ok" ? "#0F9D58" : "#2563EB";
  t.style.cssText = "background:#111827;color:#fff;font:500 13px/1.4 system-ui,sans-serif;padding:11px 16px 11px 14px;border-radius:9px;box-shadow:0 12px 32px -8px rgba(0,0,0,0.5);display:flex;align-items:center;gap:10px;max-width:380px;opacity:0;transform:translateY(8px);transition:opacity .2s,transform .2s;border-left:3px solid "+accent+";";
  t.innerHTML = '<span style="flex-shrink:0;color:'+accent+';font-size:15px">●</span><span></span>';
  t.lastChild.textContent = msg;
  host.appendChild(t);
  requestAnimationFrame(() => { t.style.opacity = "1"; t.style.transform = "translateY(0)"; });
  setTimeout(() => { t.style.opacity = "0"; t.style.transform = "translateY(8px)"; setTimeout(() => t.remove(), 250); }, 2600);
}
Object.assign(window, { lgiToast });

/* ╚══ end logo.jsx ══╝ */

/* ╔══ shell.jsx ══╗ */
/* global React, MOCK, fmtFCFA, fmtCompact, Logo */

const SB_TIPS = {
  overview: "Vue d'ensemble : encaissé, recouvrement, impayés, occupation du mois.",
  cashflow: "Trésorerie : encaissé vs attendu, mois par mois.",
  reports: "Rapports et exports de synthèse par période.",
  properties: "Vos immeubles et lots, avec leur statut d'occupation.",
  bailleurs: "Les propriétaires pour qui vous gérez.",
  tenants: "Vos locataires et leur solde.",
  leases: "Les baux en cours (nu, meublé, location-vente).",
  charges: "Charges & travaux refacturés au locataire ou déduits au bailleur.",
  payments: "Enregistrer et suivre chaque encaissement (espèces, Wave, OM, virement).",
  invoices: "Quittances émises et à générer pour le mois.",
  overdue: "Loyers en retard : relances et dossiers en 1 clic.",
  crg: "CRG : compte rendu de gestion — décompte mensuel par bailleur.",
  disbursements: "Versements : reverser le net au bailleur.",
  recepisses: "Récépissés : justificatifs de versement remis au bailleur.",
  "compta-bailleurs": "Comptabilité tenue pour chaque bailleur.",
  "trop-percus": "Excédents encaissés à régulariser.",
  "compta-agence": "Comptabilité propre à votre agence (commissions, résultat).",
  "export-compta": "Exports comptables (SYSCOHADA, journaux).",
  contracts: "Contrats et documents (baux, location-vente).",
};

function Sidebar({ route, setRoute, counts }) {
  const item = (id, label, opts = {}) => (
    <div className={"sb-item" + (route === id ? " active" : "")} onClick={() => setRoute(id)} key={id} title={SB_TIPS[id] || undefined}>
      <span className="sb-dot" />
      <span className="sb-item-label">{label}</span>
      {opts.count != null && <span className="sb-item-count">{opts.count}</span>}
      {opts.badge != null && <span className="sb-item-badge">{opts.badge}</span>}
    </div>
  );
  return (
    <aside className="sb">
      <div className="sb-logo">
        <img src="/logos/v2/logo-icon-trim.png" alt="" style={{height:24, width:"auto", display:"block"}} />
        <span className="sb-logo-name">Logestimmo</span>
      </div>
      <div className="sb-agency">
        <div className="sb-agency-main">
          <div className="sb-agency-avatar">BA</div>
          <div>
            <div className="sb-agency-name">BA Immo · Dakar</div>
            <div className="sb-agency-role">Agence · Plan Agence</div>
          </div>
        </div>
        <span className="sb-agency-caret">▾</span>
      </div>
      <div className="sb-group">
        <div className="sb-group-label">Pilotage</div>
        {item("overview", "Tableau de bord")}
        {item("cashflow", "Trésorerie")}
        {item("reports", "Rapports")}
      </div>
      <div className="sb-group">
        <div className="sb-group-label">Gestion</div>
        {item("properties", "Biens", { count: counts.properties })}
        {item("bailleurs", "Bailleurs", { count: counts.bailleurs })}
        {item("tenants", "Locataires", { count: counts.tenants })}
        {item("leases", "Baux", { count: counts.tenants })}
        {item("charges", "Charges & travaux", { badge: "1" })}
      </div>
      <div className="sb-group">
        <div className="sb-group-label">Opérations</div>
        {item("payments", "Paiements")}
        {item("invoices", "Quittances")}
        {item("overdue", "Impayés", { badge: "3" })}
        <div key="reversements"
          className={"sb-item" + (["crg","disbursements","recepisses"].includes(route) ? " active" : "")}
          onClick={() => setRoute("disbursements")}
          title="CRG, versement au bailleur et récépissé — le cycle de reversement.">
          <span className="sb-dot" />
          <span className="sb-item-label">CRG - Reversement</span>
          <span className="sb-item-count">6</span>
        </div>
      </div>
      <div className="sb-group">
        <div className="sb-group-label">Comptabilité</div>
        {item("compta-bailleurs", "Compta bailleurs", { count: counts.bailleurs })}
        {item("trop-percus", "Trop-perçus", { badge: "3" })}
        {item("compta-agence", "Compta agence")}
        {item("export-compta", "Export comptable")}
      </div>
      <div className="sb-group">
        <div className="sb-group-label">Documents</div>
        {item("contracts", "Contrats & LOA")}
      </div>
    </aside>
  );
}

function Topbar({ crumbs, onCrumb, onOpenLima, theme, setTheme, accent, setAccent, onBurger }) {
  const [appOpen, setAppOpen] = React.useState(false);
  const ACCENTS = [
    ["#F97316","Orange Logestimmo"], ["#2563EB","Bleu"], ["#5C4DFF","Violet"],
    ["#0F9D58","Vert"], ["#0D9488","Sarcelle"], ["#334155","Ardoise"],
  ];
  return (
    <div className="top">
      {onBurger && <button className="sb-burger" onClick={onBurger} title="Menu">☰</button>}
      <div className="crumbs">
        {crumbs.map((c, i) => (
          <React.Fragment key={i}>
            {i > 0 && <span className="crumb-sep">/</span>}
            <span className={i === crumbs.length - 1 ? "crumb-now" : "crumb-link"} onClick={() => i < crumbs.length - 1 && onCrumb && onCrumb(c)}>
              {c.label}
            </span>
          </React.Fragment>
        ))}
      </div>
      <div className="top-right">
        <div className="top-search">
          <span style={{color:"var(--d-ink-4)"}}>⌕</span>
          <input placeholder="Rechercher bien, locataire, quittance…" />
          <span className="kbd">⌘K</span>
        </div>
        {setTheme && (
          <div style={{position:"relative"}}>
            <button className="top-iconbtn" title="Apparence" onClick={()=>setAppOpen(o=>!o)} style={{fontSize:14}}>{theme==="light"?"☀":"☾"}</button>
            {appOpen && (
              <>
                <div onClick={()=>setAppOpen(false)} style={{position:"fixed", inset:0, zIndex:60}}/>
                <div style={{position:"absolute", right:0, top:"calc(100% + 8px)", zIndex:61, width:248, background:"var(--d-panel)", border:"1px solid var(--d-line)", borderRadius:10, boxShadow:"0 20px 50px -16px rgba(0,0,0,0.5)", padding:"14px"}}>
                  <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.08em", textTransform:"uppercase", color:"var(--d-ink-4)", marginBottom:9}}>Thème</div>
                  <div className="seg" style={{display:"flex", width:"100%", marginBottom:14}}>
                    {[["dark","Sombre"],["light","Clair"]].map(([k,l])=>(
                      <button key={k} className={"seg-btn"+(theme===k?" active":"")} style={{flex:1}} onClick={()=>setTheme(k)}>{l}</button>
                    ))}
                  </div>
                  <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.08em", textTransform:"uppercase", color:"var(--d-ink-4)", marginBottom:9}}>Couleur de l'agence</div>
                  <div style={{display:"grid", gridTemplateColumns:"repeat(6,1fr)", gap:8}}>
                    {ACCENTS.map(([c,name])=>(
                      <button key={c} title={name} onClick={()=>setAccent(c)} style={{
                        width:28, height:28, borderRadius:7, background:c, cursor:"pointer", position:"relative",
                        border: accent===c?"2px solid var(--d-ink)":"2px solid transparent", boxShadow: accent===c?"0 0 0 2px "+c+"55":"none",
                      }}>{accent===c && <span style={{position:"absolute", inset:0, display:"grid", placeItems:"center", color:"#fff", fontSize:13, fontWeight:700}}>✓</span>}</button>
                    ))}
                  </div>
                  <div style={{marginTop:12, fontSize:11, color:"var(--d-ink-4)", lineHeight:1.45}}>Personnalisez le tableau de bord aux couleurs de votre agence.</div>
                </div>
              </>
            )}
          </div>
        )}
        {onOpenLima && <LimaTopbarBtn onClick={onOpenLima}/>}
        <button className="top-iconbtn"><span>◌</span><span className="top-iconbtn-dot"/></button>
        <div className="avatar sq" style={{background:"var(--accent, var(--blue))", color:"#fff"}}>SN</div>
      </div>
    </div>
  );
}

function CashflowChart({ data, mode = "line" }) {
  const W = 800, H = 220, pad = { l: 44, r: 14, t: 16, b: 26 };
  const innerW = W - pad.l - pad.r, innerH = H - pad.t - pad.b;
  const max = Math.max(...data.map(d => Math.max(d.collected, d.expected)));
  const min = Math.min(...data.map(d => Math.min(d.collected, d.expected))) * 0.92;
  const range = max - min;
  const x = i => pad.l + (i / (data.length - 1)) * innerW;
  const y = v => pad.t + innerH - ((v - min) / range) * innerH;
  const xBand = i => pad.l + (i + 0.5) * (innerW / data.length);
  const ticks = 4;
  const gridVals = Array.from({ length: ticks + 1 }, (_, i) => min + (range * i) / ticks);
  const linePath = "M " + data.map((d, i) => `${x(i)} ${y(d.collected)}`).join(" L ");
  const areaPath = linePath + ` L ${x(data.length - 1)} ${pad.t + innerH} L ${x(0)} ${pad.t + innerH} Z`;
  const expPath = "M " + data.map((d, i) => `${x(i)} ${y(d.expected)}`).join(" L ");
  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: "block" }}>
      <defs>
        <linearGradient id="areaFill" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="#2563EB" stopOpacity="0.28" />
          <stop offset="100%" stopColor="#2563EB" stopOpacity="0" />
        </linearGradient>
      </defs>
      {gridVals.map((v, i) => (
        <g key={i}>
          <line x1={pad.l} x2={W - pad.r} y1={y(v)} y2={y(v)} stroke="#1A2130" strokeWidth="1" />
          <text className="axis" x={pad.l - 8} y={y(v) + 3} textAnchor="end">{fmtCompact(v)}</text>
        </g>
      ))}
      {data.map((d, i) => (
        <text key={d.m} className="axis" x={mode === "bar" ? xBand(i) : x(i)} y={H - 8} textAnchor="middle">{d.m}</text>
      ))}
      {mode === "bar" && data.map((d, i) => {
        const bw = (innerW / data.length) * 0.42;
        const cx = xBand(i);
        return (
          <g key={i}>
            <rect x={cx - bw} y={y(d.expected)} width={bw} height={pad.t + innerH - y(d.expected)} fill="#222B3E" rx="1"/>
            <rect x={cx + 2} y={y(d.collected)} width={bw} height={pad.t + innerH - y(d.collected)} fill="#2563EB" rx="1"/>
          </g>
        );
      })}
      {mode === "area" && (<>
        <path d={areaPath} fill="url(#areaFill)" />
        <path d={linePath} fill="none" stroke="#6192FF" strokeWidth="1.5" />
        <path d={expPath} fill="none" stroke="#525C6E" strokeWidth="1" strokeDasharray="3 3" />
      </>)}
      {mode === "line" && (<>
        <path d={expPath} fill="none" stroke="#525C6E" strokeWidth="1" strokeDasharray="3 3" />
        <path d={linePath} fill="none" stroke="#6192FF" strokeWidth="1.75" />
        {data.map((d, i) => <circle key={i} cx={x(i)} cy={y(d.collected)} r="2.2" fill="#6192FF" />)}
      </>)}
    </svg>
  );
}

// Mini sparkline for KPIs
function Spark({ values, color = "var(--d-ink-4)", w = 60, h = 20 }) {
  const max = Math.max(...values), min = Math.min(...values), range = max - min || 1;
  const pts = values.map((v, i) => `${(i / (values.length - 1)) * w},${h - ((v - min) / range) * h}`).join(" ");
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{display:"block"}}>
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.25"/>
    </svg>
  );
}

function KPIs() {
  return (
    <div className="kpis">
      <div className="kpi primary">
        <div className="kpi-label"><span>Encaissé · avril</span><span style={{color:"var(--d-ink-4)", fontWeight:400, letterSpacing:0, textTransform:"none"}}>Sur 12,69 M attendus</span></div>
        <div className="kpi-value num">11 730 000<span className="kpi-unit">FCFA</span></div>
        <div className="kpi-meta">
          <span className="delta pos">▲ 4,2%</span><span>vs mars</span>
        </div>
        <div className="kpi-spark"><Spark values={[60,62,58,66,70,72,75,78,82,80,86,84]} color="#6192FF"/></div>
      </div>
      <div className="kpi">
        <div className="kpi-label"><span>Taux de recouvrement</span></div>
        <div className="kpi-value num">92,4<span className="kpi-unit">%</span></div>
        <div className="kpi-meta">
          <span className="delta flat">—</span><span>18 quittances émises</span>
        </div>
      </div>
      <div className="kpi alert">
        <div className="kpi-label"><span>Impayés</span><span style={{color:"var(--orange)", fontWeight:600}}>Action requise</span></div>
        <div className="kpi-value num" style={{color:"var(--orange)"}}>960 000<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
        <div className="kpi-meta">
          <span className="delta neg">▲ 2 dossiers</span><span>3 locataires · 42 j max</span>
        </div>
      </div>
      <div className="kpi">
        <div className="kpi-label"><span>Occupation</span></div>
        <div className="kpi-value num">47<span className="kpi-unit">/ 52 lots</span></div>
        <div className="kpi-meta">
          <span className="delta pos">▲ 1,4 pt</span><span>94,2%</span>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { Sidebar, Topbar, CashflowChart, KPIs, Spark });

/* ╚══ end shell.jsx ══╝ */

/* ╔══ pages.jsx ══╗ */
/* global React, MOCK, fmtFCFA, KPIs, CashflowChart */

function StatusChip({ status }) {
  const map = {
    "à jour": ["pos", "à jour"],
    "en retard": ["warn", "en retard"],
    "impayé": ["neg", "impayé"],
    "encaissé": ["pos", "encaissé"],
    "en attente": ["warn", "en attente"],
    "émis": ["info", "émis"],
  };
  const [cls, label] = map[status] || ["neutral", status];
  return <span className={`chip ${cls}`}>{label}</span>;
}

function Overview({ onOpenTenant, chartMode, setChartMode }) {
  const [range, setRange] = React.useState("12M");
  const [showNewQ, setShowNewQ] = React.useState(false);
  const [showGenQ, setShowGenQ] = React.useState(false);
  const [showBulkRelance, setShowBulkRelance] = React.useState(false);
  const overdueCount = (typeof OVERDUE !== "undefined") ? OVERDUE.length : 0;
  const data = range === "3M" ? MOCK.cashflow.slice(-3) : range === "6M" ? MOCK.cashflow.slice(-6) : MOCK.cashflow;
  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Tableau de bord</h1>
          <div className="page-sub">Mercredi 18 avril · Clôture avril dans 12 jours</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost" onClick={()=>lgiToast("Export du tableau de bord (PDF)","ok")}>Exporter</button>
          <button className="btn btn-secondary" onClick={()=>setShowBulkRelance(true)}>Relancer impayés</button>
          <button className="btn btn-primary" onClick={()=>setShowGenQ(true)}>Générer les quittances du mois</button>
        </div>
      </div>
      <KPIs />
      <div className="grid-2">
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Flux de trésorerie</h3>
              <div className="panel-sub">Encaissé vs attendu</div>
            </div>
            <div className="panel-head-right">
              <div className="seg">
                {["line","area","bar"].map(m => (
                  <button key={m} className={"seg-btn" + (chartMode === m ? " active" : "")} onClick={() => setChartMode(m)}>
                    {m === "line" ? "Ligne" : m === "area" ? "Aire" : "Barres"}
                  </button>
                ))}
              </div>
              <div className="seg">
                {["3M","6M","12M"].map(r => (
                  <button key={r} className={"seg-btn" + (range === r ? " active" : "")} onClick={() => setRange(r)}>{r}</button>
                ))}
              </div>
            </div>
          </div>
          <div className="chart-legend">
            <span><span className="sw" style={{background:"var(--blue-d)"}}/>Encaissé</span>
            <span><span className="sw" style={{background:"var(--d-ink-3)"}}/>Attendu</span>
            <span style={{marginLeft:"auto"}}>Total · <span className="mono" style={{color:"var(--d-ink)"}}>138,8 M FCFA</span></span>
          </div>
          <div className="chart-wrap"><CashflowChart data={data} mode={chartMode}/></div>
        </div>
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">À traiter</h3>
              <div className="panel-sub">4 éléments</div>
            </div>
            <button className="btn btn-ghost" onClick={()=>lgiToast("Tous les éléments à traiter")}>Tout voir</button>
          </div>
          <div>
            {MOCK.alerts.map(a => (
              <div key={a.id} className="alert-item">
                <div className={`alert-bar ${a.level}`}/>
                <div>
                  <div className="alert-title">{a.title}</div>
                  <div className="alert-meta">{a.meta}</div>
                </div>
                <button className="alert-action">Traiter</button>
              </div>
            ))}
          </div>
        </div>
      </div>
      <div className="panel" style={{marginTop:16}}>
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Transactions récentes</h3>
            <div className="panel-sub">12 opérations · rapprochées automatiquement</div>
          </div>
          <div className="panel-head-right">
            <button className="btn btn-ghost" onClick={()=>lgiToast("Filtres avancés")}>Filtrer</button>
            <button className="btn btn-ghost" onClick={()=>lgiToast("Historique complet des transactions")}>Voir tout →</button>
          </div>
        </div>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th style={{width:72}}>Date</th>
                <th style={{width:92}}>ID</th>
                <th>Locataire</th>
                <th>Bien</th>
                <th>Méthode</th>
                <th className="right">Montant</th>
                <th style={{width:120}}>Statut</th>
              </tr>
            </thead>
            <tbody>
              {MOCK.transactions.map(tx => (
                <tr key={tx.id} onClick={() => { const t = MOCK.tenants.find(tt => tt.name.split(" ")[0] === tx.tenant.split(" ")[0]); t && onOpenTenant(t); }}>
                  <td className="muted mono">{tx.date}</td>
                  <td className="mono muted">{tx.id}</td>
                  <td>{tx.tenant === "—" ? <span className="empty-dash">—</span> : tx.tenant}</td>
                  <td className="muted">{tx.property}</td>
                  <td className="muted">{tx.method === "—" ? <span className="empty-dash">—</span> : tx.method}</td>
                  <td className={"right" + (tx.status === "impayé" || tx.status === "en retard" ? " highlight-neg" : "")}>{fmtFCFA(tx.amount)}</td>
                  <td><StatusChip status={tx.status}/></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {showNewQ && typeof NewQuittanceModal !== "undefined" && (
        <NewQuittanceModal onClose={()=>setShowNewQ(false)} onSave={(payload)=>{ upsertQuittance(payload); setShowNewQ(false); }}/>
      )}
      {showGenQ && typeof BatchGenerationModal !== "undefined" && (
        <BatchGenerationModal onClose={()=>setShowGenQ(false)}/>
      )}
      {showBulkRelance && typeof BulkRelanceModal !== "undefined" && (
        <BulkRelanceModal
          count={overdueCount}
          onClose={()=>setShowBulkRelance(false)}
          onConfirm={({channel})=>{ OVERDUE.forEach(o => recordRelance(o.id, channel, "29 mai")); setShowBulkRelance(false); }}
        />
      )}
    </div>
  );
}

function PropertiesList({ onOpenProperty }) {
  const properties = useProperties();
  const [q, setQ] = React.useState("");
  const [filter, setFilter] = React.useState("all");
  const [modal, setModal] = React.useState(null);
  const filtered = properties.filter(p => {
    if (q && !`${p.name} ${p.address} ${p.owner}`.toLowerCase().includes(q.toLowerCase())) return false;
    if (filter === "vacancy" && p.occupied === p.units) return false;
    if (filter === "full" && p.occupied !== p.units) return false;
    return true;
  });
  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Biens immobiliers</h1>
          <div className="page-sub">{properties.length} biens · {properties.reduce((s,p)=>s+p.units,0)} lots · {new Set(properties.map(p=>p.owner)).size} propriétaires</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-secondary" onClick={()=>lgiToast("Import CSV de biens")}>Importer CSV</button>
          <button className="btn btn-primary" onClick={()=>setModal({mode:"create", initial:null})}>+ Ajouter un bien</button>
        </div>
      </div>
      <div className="panel">
        <div className="filter-bar">
          <div className="top-search" style={{width:320, background:"var(--d-panel)"}}>
            <span style={{color:"var(--d-ink-4)"}}>⌕</span>
            <input value={q} onChange={e => setQ(e.target.value)} placeholder="Rechercher…"/>
          </div>
          {[["all","Tous",properties.length],["vacancy","Avec vacance",properties.filter(p=>p.occupied!==p.units).length],["full","Complets",properties.filter(p=>p.occupied===p.units).length]].map(([k,l,n])=>(
            <button key={k} className={"filter-chip" + (filter === k ? " active" : "")} onClick={()=>setFilter(k)}>{l} <span className="k">{n}</span></button>
          ))}
          <div className="filter-spacer"/>
          <button className="filter-chip">Trier ▾</button>
        </div>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th style={{width:86}}>ID</th>
                <th>Bien</th>
                <th>Type</th>
                <th>Propriétaire</th>
                <th>Occupation</th>
                <th className="right">Loyer mensuel</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map(p => (
                <tr key={p.id} onClick={() => onOpenProperty(p)}>
                  <td className="mono muted">{p.id}</td>
                  <td>
                    <div style={{fontWeight:500}}>{p.name}</div>
                    <div className="muted" style={{fontSize:11.5, marginTop:1}}>{p.address}</div>
                  </td>
                  <td className="muted">{p.type}</td>
                  <td className="muted">{p.owner}</td>
                  <td>
                    <span className="occ-bar"><span className="occ-bar-fill" style={{width:`${(p.occupied/p.units)*100}%`, background: p.occupied === 0 ? "var(--orange)" : p.occupied === p.units ? "var(--pos-d)" : "var(--blue-d)"}}/></span>
                    <span className="mono" style={{marginLeft:10, fontSize:12}}>{p.occupied}/{p.units}</span>
                  </td>
                  <td className="right">{fmtFCFA(p.mrr)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      <PropertyFormModal
        open={!!modal}
        mode={modal?.mode || "create"}
        initial={modal?.initial}
        onClose={()=>setModal(null)}
        onSave={(payload)=>{ upsertProperty(payload); setModal(null); onOpenProperty(payload); }}
      />
    </div>
  );
}

function TenantsList({ onOpenTenant }) {
  const tenants = useTenants();
  const [tab, setTab] = React.useState("all");
  const [modal, setModal] = React.useState(null);
  const filtered = tenants.filter(t => tab === "late" ? t.daysLate > 0 : tab === "ok" ? t.daysLate === 0 : true);
  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Locataires</h1>
          <div className="page-sub">{tenants.length} actifs · {tenants.filter(t=>t.balance>0).length} en situation d'impayé</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-secondary" onClick={()=>lgiToast("Export de la liste des locataires (.xlsx)","ok")}>Exporter</button>
          <button className="btn btn-primary" onClick={()=>setModal({mode:"create", initial:null})}>+ Nouveau locataire</button>
        </div>
      </div>
      <div className="tabs">
        {[["all","Tous",tenants.length],["ok","À jour",tenants.filter(t=>t.daysLate===0).length],["late","En retard",tenants.filter(t=>t.daysLate>0).length]].map(([k,l,n])=>(
          <button key={k} className={"tab" + (tab===k?" active":"")} onClick={()=>setTab(k)}>{l} <span className="tab-count">{n}</span></button>
        ))}
      </div>
      <div className="panel">
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th style={{width:88}}>ID</th>
                <th>Locataire</th>
                <th>Bien · lot</th>
                <th className="right">Loyer</th>
                <th>Bail</th>
                <th>Statut</th>
                <th className="right">Solde</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map(t => (
                <tr key={t.id} onClick={() => onOpenTenant(t)}>
                  <td className="mono muted">{t.id}</td>
                  <td>
                    <div style={{display:"flex", alignItems:"center", gap:10}}>
                      <div className="avatar">{t.avatar}</div>
                      <div>
                        <div style={{fontWeight:500}}>{t.name}</div>
                        <div className="muted" style={{fontSize:11.5}}>{t.phone}</div>
                      </div>
                    </div>
                  </td>
                  <td className="muted">{t.property} · {t.unit}</td>
                  <td className="right">{fmtFCFA(t.rent)}</td>
                  <td className="muted mono" style={{fontSize:11.5}}>{t.leaseStart} → {t.leaseEnd}</td>
                  <td><StatusChip status={t.status}/></td>
                  <td className={"right" + (t.balance > 0 ? " highlight-neg" : "")}>{t.balance > 0 ? fmtFCFA(t.balance) : "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      <TenantFormModal
        open={!!modal}
        mode={modal?.mode || "create"}
        initial={modal?.initial}
        onClose={()=>setModal(null)}
        onSave={(payload)=>{ upsertTenant(payload); setModal(null); }}
      />
    </div>
  );
}

function PropertyDetail({ property, onBack, onOpenTenant }) {
  useProperties(); // subscribe to store updates
  const p = getPropertyById(property.id) || property;
  const [editing, setEditing] = React.useState(false);
  const relTenants = MOCK.tenants.filter(t => t.propertyId === p.id);
  const occ = Math.round((p.occupied / p.units) * 100);
  return (
    <div className="page">
      <div style={{marginBottom:14, display:"flex", alignItems:"center", gap:12}}>
        <button className="btn btn-ghost" onClick={onBack}>← Biens</button>
        <span className="chip neutral plain mono" style={{fontSize:11}}>{p.id}</span>
        <div style={{marginLeft:"auto", display:"flex", gap:8}}>
          <button className="btn btn-secondary" onClick={()=>setEditing(true)}>Modifier</button>
          <button className="btn btn-ghost" onClick={()=>lgiToast("Création d’un bail — ouvrez la page Baux")}>+ Nouveau bail</button>
        </div>
      </div>
      <div className="detail-head">
        <div>
          <div className="detail-id">BIEN · {p.type.toUpperCase()}</div>
          <div className="detail-name">{p.name}</div>
          <div className="detail-meta">
            <span><b>{p.address}</b></span>
            <span>Propriétaire : <b>{p.owner}</b></span>
            <span>Bail-type : <b>Résidentiel · OHADA</b></span>
          </div>
        </div>
        <div className="detail-stats">
          <div><div className="detail-stat-label">Lots</div><div className="detail-stat-value">{p.occupied}<span style={{color:"var(--d-ink-3)"}}>/{p.units}</span></div></div>
          <div><div className="detail-stat-label">Occupation</div><div className="detail-stat-value">{occ}%</div></div>
          <div><div className="detail-stat-label">Loyer / mois</div><div className="detail-stat-value">{(p.mrr/1000000).toFixed(2).replace(".",",")} M</div></div>
        </div>
      </div>
      <div className="tabs">
        <button className="tab active">Vue d'ensemble</button>
        <button className="tab">Lots <span className="tab-count">{p.units}</span></button>
        <button className="tab">Paiements</button>
        <button className="tab">Documents</button>
      </div>
      <div className="grid-detail">
        <div className="stack">
          <div className="panel">
            <div className="panel-head">
              <div><h3 className="panel-title">Locataires en place</h3><div className="panel-sub">{relTenants.length} baux actifs</div></div>
              <button className="btn btn-ghost" onClick={()=>lgiToast("Création d’un bail pour ce bien")}>+ Nouveau bail</button>
            </div>
            <div className="table-wrap">
              <table className="tbl">
                <thead><tr><th style={{width:70}}>Lot</th><th>Locataire</th><th className="right">Loyer</th><th>Bail</th><th>Statut</th></tr></thead>
                <tbody>
                  {relTenants.length === 0 && <tr><td colSpan={5} className="muted" style={{textAlign:"center", padding:24}}>Aucun locataire en place.</td></tr>}
                  {relTenants.map(t => (
                    <tr key={t.id} onClick={() => onOpenTenant(t)}>
                      <td className="mono">{t.unit}</td>
                      <td><div style={{display:"flex", alignItems:"center", gap:10}}><div className="avatar">{t.avatar}</div><div style={{fontWeight:500}}>{t.name}</div></div></td>
                      <td className="right">{fmtFCFA(t.rent)}</td>
                      <td className="muted mono" style={{fontSize:11.5}}>{t.leaseStart} → {t.leaseEnd}</td>
                      <td><StatusChip status={t.status}/></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
          <div className="panel">
            <div className="panel-head"><div><h3 className="panel-title">Encaissements · 6 mois</h3><div className="panel-sub">Taux de recouvrement mensuel</div></div></div>
            <div className="table-wrap">
              <table className="tbl">
                <thead><tr><th>Mois</th><th className="right">Attendu</th><th className="right">Encaissé</th><th className="right">Écart</th><th className="right">Taux</th></tr></thead>
                <tbody>
                  {["Novembre 25","Décembre 25","Janvier 26","Février 26","Mars 26","Avril 26"].map((m, i) => {
                    const exp = p.mrr, col = i === 5 ? p.mrr - 220_000 : i === 2 ? p.mrr : p.mrr - (i%2 === 0 ? 0 : 110_000);
                    const ec = col - exp, rate = exp > 0 ? Math.round((col/exp)*100) : 0;
                    return (
                      <tr key={m}>
                        <td>{m}</td>
                        <td className="right">{fmtFCFA(exp)}</td>
                        <td className="right">{fmtFCFA(col)}</td>
                        <td className={"right" + (ec < 0 ? " highlight-neg" : "")}>{ec === 0 ? "—" : fmtFCFA(ec)}</td>
                        <td className="right">{rate}%</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </div>
        </div>
        <div className="stack">
          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">Informations</h3></div>
            <div>
              <div className="info-row"><div className="info-k">Référence</div><div className="info-v mono">{p.id}</div></div>
              <div className="info-row"><div className="info-k">Type</div><div className="info-v">{p.type}</div></div>
              <div className="info-row"><div className="info-k">Adresse</div><div className="info-v">{p.address}</div></div>
              <div className="info-row"><div className="info-k">Propriétaire</div><div className="info-v">{p.owner}</div></div>
              <div className="info-row"><div className="info-k">Nombre de lots</div><div className="info-v mono">{p.units}</div></div>
              <div className="info-row"><div className="info-k">Titre foncier</div><div className="info-v mono">TF n° 14 228 / DK</div></div>
            </div>
          </div>
          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">Commission & versement</h3></div>
            <div>
              <div className="info-row"><div className="info-k">Commission agence</div><div className="info-v mono">8,0%</div></div>
              <div className="info-row"><div className="info-k">Loyer brut / mois</div><div className="info-v mono">{fmtFCFA(p.mrr)}</div></div>
              <div className="info-row"><div className="info-k">Commission</div><div className="info-v mono">{fmtFCFA(Math.round(p.mrr*0.08))}</div></div>
              <div className="info-row"><div className="info-k">Net propriétaire</div><div className="info-v mono" style={{color:"var(--pos-d)"}}>{fmtFCFA(Math.round(p.mrr*0.92))}</div></div>
              <div className="info-row"><div className="info-k">Prochain versement</div><div className="info-v mono">30 avr 2026</div></div>
            </div>
          </div>
        </div>
      </div>

      <PropertyFormModal
        open={editing}
        mode="edit"
        initial={p}
        onClose={()=>setEditing(false)}
        onSave={(payload)=>{ upsertProperty(payload); setEditing(false); }}
      />
    </div>
  );
}

function TenantDetail({ tenant, onBack, onOpenProperty }) {
  useTenants(); // subscribe to store updates
  const t = getTenantById(tenant.id) || tenant;
  const [editing, setEditing] = React.useState(false);
  const property = MOCK.properties.find(p => p.id === t.propertyId);
  const months = ["Mai 25","Juin 25","Juil 25","Août 25","Sept 25","Oct 25","Nov 25","Déc 25","Jan 26","Fév 26","Mars 26","Avr 26"];
  const history = months.map((m, i) => {
    let status = "encaissé", method = ["Wave","Orange Money","Virement"][i%3], paidOn = `${(i%28)+1} ${m.split(" ")[0].toLowerCase()}`;
    if (t.status === "impayé" && i >= 10) { status = "impayé"; method = "—"; paidOn = "—"; }
    if (t.status === "en retard" && i === 11) { status = "en retard"; method = "—"; paidOn = "—"; }
    return { month: m, status, method, paidOn, amount: t.rent };
  }).reverse();
  return (
    <div className="page">
      <div style={{marginBottom:14, display:"flex", alignItems:"center", gap:12}}>
        <button className="btn btn-ghost" onClick={onBack}>← Locataires</button>
        <span className="chip neutral plain mono" style={{fontSize:11}}>{t.id}</span>
      </div>
      <div className="t-hero">
        <div className="avatar lg">{t.avatar}</div>
        <div>
          <div className="t-hero-id">LOCATAIRE · {t.id}</div>
          <div className="t-hero-name">{t.name}</div>
          <div className="t-hero-meta">
            <span>{t.phone}</span><span>{t.email}</span>
            <span>Loue <b style={{color:"var(--d-ink-2)"}}>{t.property}</b> · lot {t.unit}</span>
          </div>
        </div>
        <div style={{display:"flex", gap:8}}>
          <button className="btn btn-secondary" onClick={()=>setEditing(true)}>Modifier</button>
          <button className="btn btn-secondary" onClick={()=>lgiToast("Quittance envoyée à "+t.name,"ok")}>Envoyer quittance</button>
          {t.balance > 0 && <button className="btn btn-primary" onClick={()=>lgiToast("Relance envoyée à "+t.name,"ok")}>Relancer · {fmtFCFA(t.balance)}</button>}
        </div>
      </div>
      <div className="kpis" style={{gridTemplateColumns:"repeat(4,1fr)"}}>
        <div className="kpi">
          <div className="kpi-label"><span>Loyer mensuel</span></div>
          <div className="kpi-value num">{t.rent.toLocaleString("fr-FR")}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span>Inchangé depuis bail</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Statut</span></div>
          <div className="kpi-value" style={{fontSize:17}}><StatusChip status={t.status}/></div>
          <div className="kpi-meta"><span>{t.daysLate > 0 ? `Retard ${t.daysLate} j` : "À jour"}</span></div>
        </div>
        <div className={"kpi" + (t.balance > 0 ? " alert" : "")}>
          <div className="kpi-label"><span>Solde</span>{t.balance > 0 && <span style={{color:"var(--orange)", fontWeight:600}}>Exigible</span>}</div>
          <div className="kpi-value num" style={{color: t.balance > 0 ? "var(--orange)" : "var(--d-ink)"}}>{t.balance > 0 ? t.balance.toLocaleString("fr-FR") : "0"}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span>{t.balance > 0 ? "À recouvrer" : "Rien à encaisser"}</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Ancienneté</span></div>
          <div className="kpi-value num">24<span className="kpi-unit">mois</span></div>
          <div className="kpi-meta"><span>Depuis {t.leaseStart}</span></div>
        </div>
      </div>
      <div className="grid-detail" style={{marginTop:16}}>
        <div className="panel">
          <div className="panel-head">
            <div><h3 className="panel-title">Historique de paiements</h3><div className="panel-sub">12 mois · synchronisés avec rapprochement bancaire</div></div>
            <button className="btn btn-ghost" onClick={()=>lgiToast("Historique de paiements exporté","ok")}>Exporter</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead><tr><th style={{width:90}}>Mois</th><th>Payé le</th><th>Méthode</th><th className="right">Montant</th><th style={{width:120}}>Statut</th></tr></thead>
              <tbody>
                {history.map((r,i) => (
                  <tr key={i}>
                    <td className="mono">{r.month}</td>
                    <td className="muted mono">{r.paidOn}</td>
                    <td className="muted">{r.method === "—" ? <span className="empty-dash">—</span> : r.method}</td>
                    <td className={"right" + (r.status !== "encaissé" ? " highlight-neg" : "")}>{fmtFCFA(r.amount)}</td>
                    <td><StatusChip status={r.status}/></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
        <div className="stack">
          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">Contrat de bail</h3></div>
            <div>
              <div className="info-row"><div className="info-k">Référence</div><div className="info-v mono">BX-{t.id.replace("T-","")}</div></div>
              <div className="info-row"><div className="info-k">Type</div><div className="info-v">Résidentiel · OHADA</div></div>
              <div className="info-row"><div className="info-k">Début</div><div className="info-v mono">{t.leaseStart}</div></div>
              <div className="info-row"><div className="info-k">Échéance</div><div className="info-v mono">{t.leaseEnd}</div></div>
              <div className="info-row"><div className="info-k">Dépôt garantie</div><div className="info-v mono">{fmtFCFA(t.rent*2)}</div></div>
            </div>
          </div>
          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">Bien loué</h3>{property && <button className="btn btn-ghost" onClick={() => onOpenProperty(property)}>Ouvrir →</button>}</div>
            <div>
              <div className="info-row"><div className="info-k">Bien</div><div className="info-v">{t.property}</div></div>
              <div className="info-row"><div className="info-k">Lot</div><div className="info-v mono">{t.unit}</div></div>
              {property && <div className="info-row"><div className="info-k">Adresse</div><div className="info-v">{property.address}</div></div>}
              {property && <div className="info-row"><div className="info-k">Propriétaire</div><div className="info-v">{property.owner}</div></div>}
            </div>
          </div>
        </div>
      </div>

      <TenantFormModal
        open={editing}
        mode="edit"
        initial={t}
        onClose={()=>setEditing(false)}
        onSave={(payload)=>{ upsertTenant(payload); setEditing(false); }}
      />
    </div>
  );
}

function PlaceholderPage({ title, subtitle }) {
  return (
    <div className="page">
      <div className="page-head"><div><h1 className="page-title">{title}</h1><div className="page-sub">{subtitle}</div></div></div>
      <div className="panel" style={{padding:"60px 40px", textAlign:"center"}}>
        <div style={{fontSize:13, color:"var(--d-ink-3)", marginBottom:8}}>Section en cours</div>
        <div style={{fontSize:13, color:"var(--d-ink-4)", maxWidth:440, margin:"0 auto"}}>
          Ce prototype démontre : Tableau de bord, Biens, Locataires. Les autres sections font partie du produit complet.
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { Overview, PropertiesList, TenantsList, PropertyDetail, TenantDetail, PlaceholderPage, StatusChip });

/* ╚══ end pages.jsx ══╝ */

/* ╔══ pages-finance.jsx ══╗ */
/* global React, MOCK, fmtFCFA, fmtCompact, StatusChip, Spark */

/* ─────────────────────────────────────────────────────────
   PAIEMENTS — full payments page
   ───────────────────────────────────────────────────────── */

function PaymentsPage({ onOpenTenant }) {
  const [tab, setTab] = React.useState("all");
  const [channel, setChannel] = React.useState("all");
  const [period, setPeriod] = React.useState("avr");
  const [q, setQ] = React.useState("");
  const [showCashModal, setShowCashModal] = React.useState(false);

  // Build an extended payments list from MOCK transactions, scoped to rent payments
  const allTx = MOCK.transactions
    .filter(tx => tx.kind === "loyer")
    .map(tx => ({
      ...tx,
      due: tx.date.replace("16 avr", "05 avr").replace("14 avr", "05 avr") || "05 avr",
    }));

  const filtered = allTx.filter(tx => {
    if (tab === "encaisse" && tx.status !== "encaissé") return false;
    if (tab === "attente" && tx.status !== "en attente") return false;
    if (tab === "retard" && tx.status !== "en retard" && tx.status !== "impayé") return false;
    if (channel !== "all" && !tx.method.toLowerCase().includes(channel)) return false;
    if (q && !`${tx.tenant} ${tx.property} ${tx.id}`.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });

  // Totals per status (current filtered set is "tab=all" view)
  const totalEnc = allTx.filter(t => t.status === "encaissé").reduce((s, t) => s + t.amount, 0);
  const totalAtt = allTx.filter(t => t.status === "en attente").reduce((s, t) => s + t.amount, 0);
  const totalRet = allTx.filter(t => t.status === "en retard" || t.status === "impayé").reduce((s, t) => s + t.amount, 0);
  const countEnc = allTx.filter(t => t.status === "encaissé").length;
  const countAtt = allTx.filter(t => t.status === "en attente").length;
  const countRet = allTx.filter(t => t.status === "en retard" || t.status === "impayé").length;

  // Breakdown by channel
  const byChannel = {
    "Espèces": 350_000,
    "Wave": allTx.filter(t => t.method === "Wave").reduce((s, t) => s + t.amount, 0),
    "Orange Money": allTx.filter(t => t.method === "Orange Money").reduce((s, t) => s + t.amount, 0),
    "Virement": allTx.filter(t => t.method === "Virement").reduce((s, t) => s + t.amount, 0),
  };
  const channelTotal = Object.values(byChannel).reduce((s, v) => s + v, 0);

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Paiements</h1>
          <div className="page-sub">Avril 2026 · {countEnc + countAtt + countRet} quittances suivies · rapprochement en temps réel</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost" onClick={()=>lgiToast("Export du journal d’encaissement en cours…","ok")}>Exporter</button>
          <button className="btn btn-secondary" onClick={()=>lgiToast("Relances WhatsApp envoyées aux locataires en retard","ok")}>Relancer WhatsApp</button>
          <button className="btn btn-primary" onClick={() => setShowCashModal(true)}>+ Saisir paiement</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Encaissé · avril</span></div>
          <div className="kpi-value num">{(totalEnc/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 4,2%</span><span>{countEnc} paiements · 4 canaux</span></div>
          <div className="kpi-spark"><Spark values={[60,62,58,66,70,72,75,78,82,80,86,84]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>En attente</span></div>
          <div className="kpi-value num" style={{color:"var(--warn-d)"}}>{fmtCompact(totalAtt)}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span className="delta flat">—</span><span>{countAtt} {countAtt > 1 ? "quittances émises" : "quittance émise"}</span></div>
        </div>
        <div className="kpi alert">
          <div className="kpi-label"><span>En retard / impayés</span><span style={{color:"var(--orange)", fontWeight:600}}>Action requise</span></div>
          <div className="kpi-value num" style={{color:"var(--orange)"}}>{fmtCompact(totalRet)}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span className="delta neg">▲ {countRet} dossiers</span><span>42 j max</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Quittances émises</span></div>
          <div className="kpi-value num">18<span className="kpi-unit">avril</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 1 vs mars</span><span>15 réglées · 3 en cours</span></div>
        </div>
      </div>

      <div className="grid-2">
        {/* Main: list */}
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Tous les paiements</h3>
              <div className="panel-sub">Triés du plus récent au plus ancien</div>
            </div>
          </div>

          <div className="tabs" style={{margin:"0 18px", borderBottom:"none"}}>
            {[["all","Tous",countEnc+countAtt+countRet],["encaisse","Encaissés",countEnc],["attente","En attente",countAtt],["retard","En retard",countRet]].map(([k,l,n])=>(
              <button key={k} className={"tab" + (tab===k?" active":"")} onClick={()=>setTab(k)}>{l} <span className="tab-count">{n}</span></button>
            ))}
          </div>

          <div className="filter-bar" style={{borderTop:"1px solid var(--d-line)"}}>
            <div className="top-search" style={{width:280, background:"var(--d-sunken)"}}>
              <span style={{color:"var(--d-ink-4)"}}>⌕</span>
              <input value={q} onChange={e=>setQ(e.target.value)} placeholder="Locataire, bien, ID…"/>
            </div>
            <div className="seg">
              {[["all","Tous"],["espèces","Espèces"],["wave","Wave"],["orange","OM"],["virement","Virement"]].map(([k,l])=>(
                <button key={k} className={"seg-btn"+(channel===k?" active":"")} onClick={()=>setChannel(k)}>{l}</button>
              ))}
            </div>
            <div className="filter-spacer"/>
            <div className="seg">
              {[["mar","Mars"],["avr","Avril"],["q2","T2"]].map(([k,l])=>(
                <button key={k} className={"seg-btn"+(period===k?" active":"")} onClick={()=>setPeriod(k)}>{l}</button>
              ))}
            </div>
          </div>

          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:72}}>Date</th>
                  <th style={{width:88}}>ID</th>
                  <th>Locataire</th>
                  <th>Bien · lot</th>
                  <th>Canal</th>
                  <th className="right">Montant</th>
                  <th style={{width:120}}>Statut</th>
                  <th style={{width:90}}></th>
                </tr>
              </thead>
              <tbody>
                {filtered.length === 0 && (
                  <tr><td colSpan={8} className="muted" style={{textAlign:"center", padding:32}}>Aucun paiement ne correspond à ces filtres.</td></tr>
                )}
                {filtered.map(tx => {
                  const t = MOCK.tenants.find(tt => tx.tenant.startsWith(tt.name.split(" ")[0]) || tt.name.includes(tx.tenant.split(" ").slice(-1)[0]));
                  return (
                    <tr key={tx.id} onClick={() => t && onOpenTenant(t)}>
                      <td className="muted mono">{tx.date}</td>
                      <td className="mono muted">{tx.id}</td>
                      <td>{tx.tenant}</td>
                      <td className="muted">{tx.property}</td>
                      <td>{tx.method === "—" ? <span className="empty-dash">—</span> : <MethodBadge m={tx.method}/>}</td>
                      <td className={"right" + (tx.status === "impayé" || tx.status === "en retard" ? " highlight-neg" : "")}>{fmtFCFA(tx.amount)}</td>
                      <td><StatusChip status={tx.status}/></td>
                      <td className="right">
                        {(tx.status === "en retard" || tx.status === "impayé") ? (
                          <button className="btn btn-ghost" style={{padding:"3px 8px", fontSize:11}} onClick={(e)=>{e.stopPropagation();lgiToast("Relance envoyée","ok");}}>Relancer</button>
                        ) : tx.status === "en attente" ? (
                          <button className="btn btn-ghost" style={{padding:"3px 8px", fontSize:11}} onClick={(e)=>{e.stopPropagation();lgiToast("Ouvrez « Saisir paiement » pour enregistrer ce règlement");}}>Saisir</button>
                        ) : <span className="muted" style={{fontSize:11}}>—</span>}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>

        {/* Side: breakdown */}
        <div className="stack">
          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Répartition par canal</h3>
                <div className="panel-sub">Avril · {(channelTotal/1_000_000).toFixed(2).replace(".",",")} M FCFA</div>
              </div>
            </div>
            <div style={{padding:"16px 18px"}}>
              {Object.entries(byChannel).map(([name, amt]) => {
                const pct = channelTotal ? Math.round((amt/channelTotal)*100) : 0;
                const color = name === "Espèces" ? "#8E97A6" : name === "Wave" ? "#1DC3F8" : name === "Orange Money" ? "#FF7A00" : "#0056A6";
                return (
                  <div key={name} style={{marginBottom:14}}>
                    <div style={{display:"flex", justifyContent:"space-between", marginBottom:6}}>
                      <span style={{fontSize:12.5, color:"var(--d-ink-2)", display:"flex", alignItems:"center", gap:8}}>
                        <span style={{width:8, height:8, borderRadius:2, background:color, display:"inline-block"}}/>
                        {name}
                      </span>
                      <span className="mono" style={{fontSize:12.5}}>{fmtFCFA(amt)}</span>
                    </div>
                    <div style={{height:6, background:"var(--d-sunken)", borderRadius:3, overflow:"hidden"}}>
                      <div style={{height:"100%", width:`${pct}%`, background:color, borderRadius:3}}/>
                    </div>
                    <div style={{fontSize:10.5, color:"var(--d-ink-4)", marginTop:3, fontFamily:"var(--font-mono)"}}>{pct}%</div>
                  </div>
                );
              })}
            </div>
          </div>

          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Relances à envoyer</h3>
                <div className="panel-sub">3 locataires · WhatsApp</div>
              </div>
              <button className="btn btn-secondary" style={{padding:"5px 10px", fontSize:11.5}} onClick={()=>lgiToast("Relances groupées envoyées par WhatsApp","ok")}>Tout envoyer</button>
            </div>
            <div>
              {MOCK.tenants.filter(t => t.balance > 0).map(t => (
                <div key={t.id} onClick={() => onOpenTenant(t)} style={{display:"flex", alignItems:"center", gap:12, padding:"12px 18px", borderBottom:"1px solid var(--d-line-2)", cursor:"pointer"}}>
                  <div className="avatar">{t.avatar}</div>
                  <div style={{flex:1, minWidth:0}}>
                    <div style={{fontSize:12.5, fontWeight:500}}>{t.name}</div>
                    <div className="muted" style={{fontSize:11}}>{t.property} · {t.daysLate} j retard</div>
                  </div>
                  <div style={{textAlign:"right"}}>
                    <div className="mono" style={{fontSize:12.5, color:"var(--orange)"}}>{fmtCompact(t.balance)}</div>
                    <button onClick={(e)=>e.stopPropagation()} style={{marginTop:4, fontSize:10.5, color:"#25D366", background:"rgba(37,211,102,0.1)", border:"1px solid rgba(37,211,102,0.25)", padding:"2px 8px", borderRadius:3, fontWeight:600}}>WhatsApp →</button>
                  </div>
                </div>
              ))}
            </div>
          </div>

          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Activité du rapprochement</h3>
                <div className="panel-sub">Dernières 24 h</div>
              </div>
            </div>
            <div style={{padding:"4px 0"}}>
              {[
                {tag:"WAVE", txt:"42 paiements rapprochés automatiquement", time:"il y a 12 min", c:"#1DC3F8"},
                {tag:"OM",   txt:"18 paiements rapprochés", time:"il y a 1 h", c:"#FF7A00"},
                {tag:"ESP",  txt:"3 paiements espèces saisis · A. Sall, F. Kane, P. Diop", time:"il y a 3 h", c:"var(--d-ink-4)"},
                {tag:"VIR",  txt:"2 virements bancaires importés (CSV)", time:"hier", c:"#0056A6"},
              ].map((r, i) => (
                <div key={i} style={{display:"flex", gap:12, padding:"10px 18px", alignItems:"flex-start", borderBottom:"1px solid var(--d-line-2)"}}>
                  <span style={{fontSize:9.5, fontWeight:700, color:"#fff", background:r.c, padding:"2px 6px", borderRadius:3, letterSpacing:"0.04em", marginTop:1, minWidth:38, textAlign:"center"}}>{r.tag}</span>
                  <div style={{flex:1, minWidth:0}}>
                    <div style={{fontSize:12, color:"var(--d-ink-2)"}}>{r.txt}</div>
                    <div className="muted mono" style={{fontSize:10.5, marginTop:2}}>{r.time}</div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>

      {showCashModal && <CashPaymentModal onClose={() => setShowCashModal(false)}/>}
    </div>
  );
}

function MethodBadge({ m }) {
  const config = {
    "Wave": { c:"#001B2E", bg:"#1DC3F8", l:"Wave" },
    "Orange Money": { c:"#fff", bg:"#FF7A00", l:"OM" },
    "Virement": { c:"#fff", bg:"#0056A6", l:"Virement" },
    "Espèces": { c:"var(--d-ink-2)", bg:"var(--d-elev)", l:"Espèces", border:"var(--d-line)" },
  };
  const cfg = config[m] || { c:"var(--d-ink-2)", bg:"var(--d-elev)", l:m };
  return (
    <span style={{
      fontSize:10.5, fontWeight:600, padding:"2px 8px", borderRadius:3,
      color:cfg.c, background:cfg.bg,
      border: cfg.border ? `1px solid ${cfg.border}` : "none",
      letterSpacing:"0.02em",
    }}>{cfg.l}</span>
  );
}

function CashPaymentModal({ onClose }) {
  const [tenantId, setTenantId] = React.useState("");
  const [amount, setAmount] = React.useState("");
  const [date, setDate] = React.useState("18/04/2026");
  const [note, setNote] = React.useState("");
  return (
    <div onClick={onClose} style={{
      position:"fixed", inset:0, background:"rgba(0,0,0,0.55)", zIndex:300,
      display:"flex", alignItems:"center", justifyContent:"center"
    }}>
      <div onClick={e=>e.stopPropagation()} style={{
        background:"var(--d-panel)", border:"1px solid var(--d-line)", borderRadius:10,
        width:480, color:"var(--d-ink)", fontFamily:"var(--font-sans)"
      }}>
        <div style={{padding:"16px 20px", borderBottom:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div>
            <div style={{fontSize:14, fontWeight:600}}>Saisir un paiement</div>
            <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:2}}>Espèces ou virement reçu hors plateforme</div>
          </div>
          <button onClick={onClose} style={{background:"none", border:"none", color:"var(--d-ink-3)", fontSize:20, cursor:"pointer"}}>×</button>
        </div>
        <div style={{padding:"20px 20px 8px"}}>
          <ModalField label="Locataire">
            <select value={tenantId} onChange={e=>setTenantId(e.target.value)} style={modalInput}>
              <option value="">Choisir un locataire…</option>
              {MOCK.tenants.map(t => (
                <option key={t.id} value={t.id}>{t.name} · {t.property} · {t.unit}</option>
              ))}
            </select>
          </ModalField>
          <ModalField label="Montant (FCFA)">
            <input type="text" value={amount} onChange={e=>setAmount(e.target.value)} placeholder="240 000" style={modalInput}/>
          </ModalField>
          <ModalField label="Date de paiement">
            <input type="text" value={date} onChange={e=>setDate(e.target.value)} style={modalInput}/>
          </ModalField>
          <ModalField label="Canal">
            <div style={{display:"flex", gap:6}}>
              {["Espèces","Wave","Orange Money","Virement"].map(m => (
                <button key={m} style={{flex:1, padding:"7px 8px", fontSize:12, borderRadius:5, border:"1px solid var(--d-line)", background: m==="Espèces"?"var(--d-elev)":"transparent", color:"var(--d-ink-2)"}}>{m}</button>
              ))}
            </div>
          </ModalField>
          <ModalField label="Note (optionnel)">
            <input type="text" value={note} onChange={e=>setNote(e.target.value)} placeholder="Reçu n° 0042" style={modalInput}/>
          </ModalField>
        </div>
        <div style={{padding:"12px 20px 16px", display:"flex", justifyContent:"flex-end", gap:8, borderTop:"1px solid var(--d-line)", marginTop:8}}>
          <button className="btn btn-ghost" onClick={onClose}>Annuler</button>
          <button className="btn btn-primary" onClick={()=>{lgiToast("Paiement enregistré · reçu généré","ok");onClose();}}>Enregistrer · générer le reçu</button>
        </div>
      </div>
    </div>
  );
}

function ModalField({ label, children }) {
  return (
    <div style={{marginBottom:14}}>
      <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>{label}</div>
      {children}
    </div>
  );
}
const modalInput = {
  width:"100%", padding:"8px 12px", borderRadius:5, border:"1px solid var(--d-line)",
  background:"var(--d-sunken)", color:"var(--d-ink)", fontSize:13, fontFamily:"var(--font-sans)",
  outline:"none",
};

/* ─────────────────────────────────────────────────────────
   TRÉSORERIE — cashflow page
   ───────────────────────────────────────────────────────── */

function CashflowPage() {
  const [view, setView] = React.useState("month");

  // Account balances (canal-level)
  const accounts = [
    { id:"acc-cbao", name:"Compte CBAO · 0048 217 33", type:"Banque", balance: 18_420_000, delta:+2_180_000, last:"18 avr · synchronisé" },
    { id:"acc-wave", name:"Wave Business · BA Immo",   type:"Mobile Money", balance: 4_140_000, delta:+790_000, last:"18 avr · auto" },
    { id:"acc-om",   name:"Orange Money · Pro",         type:"Mobile Money", balance: 2_810_000, delta:+550_000, last:"18 avr · auto" },
    { id:"acc-esp",  name:"Caisse espèces · siège",     type:"Espèces", balance: 980_000, delta:-220_000, last:"17 avr · saisie" },
  ];
  const totalBalance = accounts.reduce((s, a) => s + a.balance, 0);

  // Cashflow chart-ish data
  const months = MOCK.cashflow;
  const collected = months[months.length-1].collected;
  const outflow = 8_960_000; // monthly disbursements (versements)
  const net = collected - outflow;

  // Ledger movements
  const ledger = [
    { d:"18 avr", lib:"Loyer · M. Ndiaye · Rés. Teranga A-203", canal:"Wave", debit:0, credit:240_000, solde:26_350_000 },
    { d:"18 avr", lib:"Loyer · N. F. Thiam · Rés. Ngor 3", canal:"Orange Money", debit:0, credit:290_000, solde:26_110_000 },
    { d:"17 avr", lib:"Loyer · Awa Sall · Villa Mermoz 14", canal:"Virement", debit:0, credit:850_000, solde:25_820_000 },
    { d:"17 avr", lib:"Loyer · A. Sall · Rés. Plateau B-04", canal:"Espèces", debit:0, credit:350_000, solde:24_970_000 },
    { d:"17 avr", lib:"Loyer · P. M. Diop · Rés. Ouakam A-301", canal:"Wave", debit:0, credit:260_000, solde:24_620_000 },
    { d:"16 avr", lib:"Loyer · Omar Sy · Rés. Teranga A-104", canal:"Orange Money", debit:0, credit:240_000, solde:24_360_000 },
    { d:"15 avr", lib:"Loyer · F. B. Kane · Rés. Ngor 2", canal:"Wave", debit:0, credit:310_000, solde:24_120_000 },
    { d:"14 avr", lib:"Loyer · A. B. Cissé · Imm. Point E B-202", canal:"Virement", debit:0, credit:260_000, solde:23_810_000 },
    { d:"12 avr", lib:"Versement propriétaire · M. A. Ba", canal:"Virement", debit:1_840_000, credit:0, solde:23_550_000 },
    { d:"12 avr", lib:"Commission agence · avril partiel", canal:"Interne", debit:160_000, credit:0, solde:25_390_000 },
    { d:"10 avr", lib:"Versement propriétaire · Horizon SCI", canal:"Virement", debit:3_120_000, credit:0, solde:25_550_000 },
    { d:"08 avr", lib:"Charges courantes · Rés. Teranga", canal:"Virement", debit:285_000, credit:0, solde:28_670_000 },
  ];

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Trésorerie</h1>
          <div className="page-sub">Solde consolidé · {fmtFCFA(totalBalance)} · 4 comptes synchronisés</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost" onClick={()=>lgiToast("Grand livre exporté (.xlsx)","ok")}>Exporter grand livre</button>
          <button className="btn btn-secondary" onClick={()=>lgiToast("Rapprochement bancaire lancé")}>Rapprocher</button>
          <button className="btn btn-primary" onClick={()=>lgiToast("Saisie de mouvement — disponible prochainement","warn")}>+ Saisir mouvement</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Solde consolidé</span><span style={{color:"var(--d-ink-4)", fontWeight:400, letterSpacing:0, textTransform:"none"}}>4 comptes</span></div>
          <div className="kpi-value num">{(totalBalance/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 3,3 M</span><span>vs début avril</span></div>
          <div className="kpi-spark"><Spark values={months.map(m=>m.collected)} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Encaissements · avril</span></div>
          <div className="kpi-value num" style={{color:"var(--pos-d)"}}>+{fmtCompact(collected)}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 4,2%</span><span>15 paiements · 4 canaux</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Versements émis</span></div>
          <div className="kpi-value num" style={{color:"var(--d-ink)"}}>−{fmtCompact(outflow)}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span className="delta flat">—</span><span>6 propriétaires</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Net du mois</span></div>
          <div className="kpi-value num" style={{color: net >= 0 ? "var(--pos-d)" : "var(--orange)"}}>{net >= 0 ? "+" : "−"}{fmtCompact(Math.abs(net))}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span>Commission + marge</span></div>
        </div>
      </div>

      {/* Chart + accounts side by side */}
      <div className="grid-2">
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Flux de trésorerie</h3>
              <div className="panel-sub">Entrées vs sorties · 12 derniers mois</div>
            </div>
            <div className="seg">
              {[["month","Mensuel"],["week","Hebdo"],["day","Journalier"]].map(([k,l])=>(
                <button key={k} className={"seg-btn"+(view===k?" active":"")} onClick={()=>setView(k)}>{l}</button>
              ))}
            </div>
          </div>
          <div className="chart-legend">
            <span><span className="sw" style={{background:"#3FB583"}}/>Entrées</span>
            <span><span className="sw" style={{background:"#F97316"}}/>Sorties</span>
            <span><span className="sw" style={{background:"#6192FF"}}/>Net</span>
            <span style={{marginLeft:"auto"}}>Net cumulé · <span className="mono" style={{color:"var(--d-ink)"}}>{fmtFCFA(33_440_000)}</span></span>
          </div>
          <div className="chart-wrap">
            <CashflowBarsNet data={months}/>
          </div>
        </div>

        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Soldes par compte</h3>
              <div className="panel-sub">Synchronisés en temps réel</div>
            </div>
            <button className="btn btn-ghost" onClick={()=>lgiToast("Affichage de tous les comptes")}>Tout voir</button>
          </div>
          <div>
            {accounts.map(a => {
              const colorMap = { "Banque":"#0056A6", "Mobile Money": a.name.includes("Wave") ? "#1DC3F8" : "#FF7A00", "Espèces":"var(--d-ink-3)" };
              const c = colorMap[a.type];
              return (
                <div key={a.id} style={{display:"grid", gridTemplateColumns:"36px 1fr auto", gap:12, padding:"14px 18px", borderBottom:"1px solid var(--d-line-2)", alignItems:"center"}}>
                  <div style={{width:36, height:36, borderRadius:6, background:`${c}1F`, color:c, display:"grid", placeItems:"center", fontSize:10, fontWeight:700, letterSpacing:"0.04em"}}>
                    {a.type === "Banque" ? "BNK" : a.name.includes("Wave") ? "WV" : a.type === "Espèces" ? "ESP" : "OM"}
                  </div>
                  <div style={{minWidth:0}}>
                    <div style={{fontSize:12.5, fontWeight:500, whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{a.name}</div>
                    <div className="muted" style={{fontSize:11, marginTop:1}}>{a.type} · {a.last}</div>
                  </div>
                  <div style={{textAlign:"right"}}>
                    <div className="mono" style={{fontSize:13, fontWeight:500}}>{fmtFCFA(a.balance)}</div>
                    <div style={{fontSize:11, marginTop:2, fontFamily:"var(--font-mono)", color: a.delta >= 0 ? "var(--pos-d)" : "var(--orange)"}}>
                      {a.delta >= 0 ? "▲ +" : "▼ −"}{fmtCompact(Math.abs(a.delta))}
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
          <div style={{padding:"12px 18px", borderTop:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
            <span className="muted" style={{fontSize:11.5}}>Total consolidé</span>
            <span className="mono" style={{fontSize:14, fontWeight:600}}>{fmtFCFA(totalBalance)}</span>
          </div>
        </div>
      </div>

      {/* Ledger */}
      <div className="panel" style={{marginTop:16}}>
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Grand livre</h3>
            <div className="panel-sub">Mouvements de trésorerie · 12 dernières opérations</div>
          </div>
          <div className="panel-head-right">
            <div className="seg">
              {["Tous","Entrées","Sorties"].map(l=>(
                <button key={l} className={"seg-btn"+(l==="Tous"?" active":"")}>{l}</button>
              ))}
            </div>
            <button className="btn btn-ghost" onClick={()=>lgiToast("Export OHADA généré (journal + balance)","ok")}>Exporter OHADA</button>
          </div>
        </div>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th style={{width:80}}>Date</th>
                <th>Libellé</th>
                <th>Canal</th>
                <th className="right">Débit</th>
                <th className="right">Crédit</th>
                <th className="right">Solde</th>
              </tr>
            </thead>
            <tbody>
              {ledger.map((r, i) => (
                <tr key={i}>
                  <td className="muted mono">{r.d}</td>
                  <td>{r.lib}</td>
                  <td>{r.canal === "Interne" ? <span className="muted">{r.canal}</span> : <MethodBadge m={r.canal}/>}</td>
                  <td className={"right" + (r.debit > 0 ? " highlight-neg" : "")}>{r.debit > 0 ? fmtFCFA(r.debit) : <span className="empty-dash">—</span>}</td>
                  <td className="right" style={{color: r.credit > 0 ? "var(--pos-d)" : undefined}}>{r.credit > 0 ? fmtFCFA(r.credit) : <span className="empty-dash">—</span>}</td>
                  <td className="right">{fmtFCFA(r.solde)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div style={{padding:"10px 18px", borderTop:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <span className="muted" style={{fontSize:11.5}}>12 opérations affichées · 348 sur le mois</span>
          <button className="btn btn-ghost" onClick={()=>lgiToast("348 opérations du mois")}>Voir tout →</button>
        </div>
      </div>

      {/* Recon + upcoming */}
      <div className="grid-2" style={{marginTop:16}}>
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Rapprochement bancaire</h3>
              <div className="panel-sub">Dernier import · 18 avr 11:42</div>
            </div>
            <span className="chip pos">À jour</span>
          </div>
          <div style={{padding:"4px 0"}}>
            {[
              { src:"Wave Business · BA Immo", total:42, ok:42, ko:0, c:"#1DC3F8" },
              { src:"Orange Money · Pro", total:18, ok:18, ko:0, c:"#FF7A00" },
              { src:"CBAO · 0048 217 33", total:24, ok:23, ko:1, c:"#0056A6" },
              { src:"Caisse espèces · siège", total:6, ok:6, ko:0, c:"var(--d-ink-3)" },
            ].map((r, i) => (
              <div key={i} style={{display:"flex", alignItems:"center", gap:14, padding:"12px 18px", borderBottom:"1px solid var(--d-line-2)"}}>
                <span style={{width:8, height:8, background:r.c, borderRadius:2}}/>
                <div style={{flex:1}}>
                  <div style={{fontSize:12.5, fontWeight:500}}>{r.src}</div>
                  <div className="muted" style={{fontSize:11}}>{r.total} opérations · {r.ok} rapprochées</div>
                </div>
                {r.ko > 0
                  ? <span style={{fontSize:11.5, color:"var(--orange)", fontWeight:600}}>{r.ko} écart</span>
                  : <span style={{fontSize:11.5, color:"var(--pos-d)", fontWeight:600}}>✓ aucun écart</span>}
              </div>
            ))}
          </div>
        </div>

        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Versements à venir</h3>
              <div className="panel-sub">Échéance · 30 avril 2026</div>
            </div>
            <button className="btn btn-secondary" onClick={()=>lgiToast("Bordereaux de versement générés","ok")}>Générer les bordereaux</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr><th>Propriétaire</th><th className="right">Brut</th><th className="right">Commission</th><th className="right">Net à verser</th></tr>
              </thead>
              <tbody>
                {[
                  { o:"Horizon SCI", b:4_230_000, c:338_400 },
                  { o:"M. Abdoulaye Ba", b:2_640_000, c:211_200 },
                  { o:"Katos Consulting", b:3_800_000, c:304_000 },
                  { o:"Mme Aïssatou Ba", b:850_000, c:68_000 },
                  { o:"Mme Fatou Diop", b:1_150_000, c:92_000 },
                ].map(r => (
                  <tr key={r.o}>
                    <td>{r.o}</td>
                    <td className="right">{fmtFCFA(r.b)}</td>
                    <td className="right muted">−{fmtFCFA(r.c)}</td>
                    <td className="right" style={{color:"var(--pos-d)"}}>{fmtFCFA(r.b - r.c)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  );
}

/* Cashflow chart: bars (in/out) + net line */
function CashflowBarsNet({ data }) {
  const W = 800, H = 240, pad = { l:50, r:14, t:16, b:30 };
  const innerW = W - pad.l - pad.r, innerH = H - pad.t - pad.b;
  // Synthesize outflow per month (~75-80% of collected)
  const enriched = data.map((d, i) => {
    const out = Math.round(d.collected * (0.74 + (i%3)*0.02));
    return { ...d, out, net: d.collected - out };
  });
  const max = Math.max(...enriched.map(d => Math.max(d.collected, d.out)));
  const min = 0;
  const range = max - min;
  const x = i => pad.l + (i + 0.5) * (innerW / enriched.length);
  const y = v => pad.t + innerH - ((v - min) / range) * innerH;
  const ticks = 4;
  const gridVals = Array.from({ length: ticks + 1 }, (_, i) => min + (range * i) / ticks);
  const bw = (innerW / enriched.length) * 0.32;
  const netMax = Math.max(...enriched.map(d => d.net));
  const yNet = v => pad.t + innerH - ((v) / netMax) * innerH * 0.8;
  const netPath = "M " + enriched.map((d, i) => `${x(i)} ${yNet(d.net)}`).join(" L ");
  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{display:"block"}}>
      {gridVals.map((v, i) => (
        <g key={i}>
          <line x1={pad.l} x2={W - pad.r} y1={y(v)} y2={y(v)} stroke="#1A2130" strokeWidth="1"/>
          <text className="axis" x={pad.l - 8} y={y(v) + 3} textAnchor="end">{fmtCompact(v)}</text>
        </g>
      ))}
      {enriched.map((d, i) => (
        <text key={d.m} className="axis" x={x(i)} y={H - 8} textAnchor="middle">{d.m}</text>
      ))}
      {enriched.map((d, i) => {
        const cx = x(i);
        return (
          <g key={i}>
            <rect x={cx - bw - 1} y={y(d.collected)} width={bw} height={pad.t + innerH - y(d.collected)} fill="#3FB583" opacity="0.85" rx="1"/>
            <rect x={cx + 1} y={y(d.out)} width={bw} height={pad.t + innerH - y(d.out)} fill="#F97316" opacity="0.75" rx="1"/>
          </g>
        );
      })}
      <path d={netPath} fill="none" stroke="#6192FF" strokeWidth="1.75"/>
      {enriched.map((d, i) => <circle key={i} cx={x(i)} cy={yNet(d.net)} r="2.2" fill="#6192FF"/>)}
    </svg>
  );
}

Object.assign(window, { PaymentsPage, CashflowPage });

/* ╚══ end pages-finance.jsx ══╝ */

/* ╔══ pages-reports.jsx ══╗ */
/* global React, MOCK, fmtFCFA, fmtCompact, Spark */

/* ─────────────────────────────────────────────────────────
   RAPPORTS — reports catalog + preview + history
   ───────────────────────────────────────────────────────── */

const REPORT_CATALOG = [
  {
    cat: "Pilotage",
    color: "var(--blue-d)",
    items: [
      { id: "r-dash-monthly", name: "Tableau de bord mensuel", desc: "Synthèse encaissements, vacance, impayés", freq: "Mensuel", last: "01 avr · Auto", featured: true },
      { id: "r-perf-bien", name: "Performance par bien", desc: "Rendement, occupation, recouvrement par bien", freq: "Mensuel", last: "03 avr" },
      { id: "r-vacance", name: "Vacance locative", desc: "Lots vacants, durée moyenne, manque à gagner", freq: "À la demande", last: "11 avr" },
      { id: "r-synthese", name: "Synthèse de gestion", desc: "Vue agence consolidée · 12 derniers mois", freq: "Mensuel", last: "01 avr · Auto" },
    ],
  },
  {
    cat: "Analytique comptable",
    color: "var(--pos-d)",
    items: [
      { id: "r-balance", name: "Balance des locataires", desc: "Soldes ouverts, retards, échéances", freq: "À la demande", last: "17 avr" },
      { id: "r-rendement", name: "Rendement par bien", desc: "Top et bottom biens · 12 mois", freq: "Trimestriel", last: "31 mars" },
      { id: "r-cashflow", name: "Cash-flow consolidé", desc: "Encaissements vs sorties · réconciliation bancaire", freq: "Mensuel", last: "01 avr" },
    ],
  },
  {
    cat: "Fiscal & légal",
    color: "#C99550",
    items: [
      { id: "r-tva", name: "Récapitulatif TVA", desc: "TVA collectée et déductible · OHADA", freq: "Trimestriel", last: "31 mars" },
      { id: "r-attest", name: "Attestation fiscale propriétaire", desc: "Revenus locatifs annuels par propriétaire", freq: "Annuel", last: "31 jan 2026" },
      { id: "r-ohada", name: "État conforme OHADA", desc: "États financiers selon référentiel SYSCOHADA", freq: "Annuel", last: "—" },
    ],
  },
  {
    cat: "Propriétaires",
    color: "var(--orange)",
    items: [
      { id: "r-etat-loc", name: "État locatif annuel", desc: "Récap complet pour déclaration de revenus", freq: "Annuel", last: "—" },
      { id: "r-lettre", name: "Lettre d'information propriétaire", desc: "Travaux, sinistres, renouvellements à venir", freq: "Trimestriel", last: "31 mars" },
    ],
  },
];

const RECENT_REPORTS = [
  { id: "RPT-04223", name: "Performance par bien · Mars 2026", type: "Pilotage", period: "Mars 2026", size: "248 Ko", format: "PDF", date: "03 avr · 14:22", by: "S. Ndoye", status: "exporté" },
  { id: "RPT-04212", name: "Vacance locative · T1 2026", type: "Pilotage", period: "T1 2026", size: "112 Ko", format: "PDF", date: "11 avr · 09:14", by: "S. Ndoye", status: "exporté" },
  { id: "RPT-04207", name: "Balance des locataires", type: "Comptabilité", period: "Avril 2026 (en cours)", size: "92 Ko", format: "XLSX", date: "17 avr · 16:42", by: "S. Ndoye", status: "généré" },
  { id: "RPT-04201", name: "Synthèse de gestion · Mars 2026", type: "Pilotage", period: "Mars 2026", size: "1,2 Mo", format: "PDF", date: "01 avr · Auto", by: "Système", status: "archivé" },
  { id: "RPT-04198", name: "Cash-flow consolidé · Mars 2026", type: "Comptabilité", period: "Mars 2026", size: "148 Ko", format: "XLSX", date: "01 avr · Auto", by: "Système", status: "archivé" },
  { id: "RPT-04167", name: "Récapitulatif TVA · T1 2026", type: "Fiscal", period: "Janv–Mars 2026", size: "112 Ko", format: "PDF", date: "31 mars · 17:08", by: "S. Ndoye", status: "exporté" },
  { id: "RPT-04088", name: "Attestations fiscales 2025 · 6 propriétaires", type: "Fiscal", period: "Année 2025", size: "892 Ko", format: "PDF", date: "31 jan · 11:40", by: "S. Ndoye", status: "envoyé" },
];

const SCHEDULED_REPORTS = [
  { id: "S-01", name: "Comptes de gérance · tous propriétaires", next: "01 mai · 09:00", freq: "Le 1er de chaque mois", count: 6, channel: "Email" },
  { id: "S-02", name: "Synthèse de gestion mensuelle", next: "01 mai · 06:00", freq: "Le 1er de chaque mois", count: 1, channel: "Email + Drive" },
  { id: "S-03", name: "Récapitulatif TVA T2", next: "30 juin · 18:00", freq: "Trimestriel", count: 1, channel: "Téléchargement" },
];

function ReportsPage({ onRoute }) {
  const [selectedReport, setSelectedReport] = React.useState("r-synthese");
  const [period, setPeriod] = React.useState("avr-26");
  const [tab, setTab] = React.useState("catalog");

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Rapports</h1>
          <div className="page-sub">{REPORT_CATALOG.reduce((s,g)=>s+g.items.length,0)} modèles · {RECENT_REPORTS.length} rapports générés ce mois · 3 envois planifiés</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost" onClick={()=>lgiToast("Modèles de rapport personnalisés")}>Modèles personnalisés</button>
          <button className="btn btn-secondary" onClick={()=>lgiToast("Planification d’envoi automatique")}>Planifier un envoi</button>
          <button className="btn btn-primary" onClick={()=>lgiToast("Génération d’un nouveau rapport")}>+ Nouveau rapport</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Rapports ce mois</span></div>
          <div className="kpi-value num">12<span className="kpi-unit">exports</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 3</span><span>vs mars · analytique + fiscal</span></div>
          <div className="kpi-spark"><Spark values={[6,7,8,8,9,9,10,10,11,11,12,12]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Modèles disponibles</span></div>
          <div className="kpi-value num">{REPORT_CATALOG.reduce((s,g)=>s+g.items.length,0)}<span className="kpi-unit">templates</span></div>
          <div className="kpi-meta"><span>4 catégories · dont 3 fiscaux</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Envois planifiés</span></div>
          <div className="kpi-value num">3<span className="kpi-unit">actifs</span></div>
          <div className="kpi-meta"><span className="delta flat">—</span><span>Prochain · 01 mai 06:00</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Échéance fiscale</span></div>
          <div className="kpi-value num" style={{fontSize:22}}>30 juin<span className="kpi-unit">2026</span></div>
          <div className="kpi-meta"><span>TVA T2 · à préparer</span></div>
        </div>
      </div>

      {/* Tabs : catalogue | aperçu | historique */}
      <div className="tabs">
        <button className={"tab" + (tab==="catalog"?" active":"")} onClick={()=>setTab("catalog")}>Catalogue <span className="tab-count">{REPORT_CATALOG.reduce((s,g)=>s+g.items.length,0)}</span></button>
        <button className={"tab" + (tab==="recent"?" active":"")} onClick={()=>setTab("recent")}>Historique <span className="tab-count">{RECENT_REPORTS.length}</span></button>
        <button className={"tab" + (tab==="scheduled"?" active":"")} onClick={()=>setTab("scheduled")}>Envois planifiés <span className="tab-count">{SCHEDULED_REPORTS.length}</span></button>
      </div>

      {tab === "catalog" && (
        <OperationsCrossLinks onRoute={onRoute}/>
      )}

      {tab === "catalog" && (
        <div className="grid-2">
          {/* Main: catalog */}
          <div className="stack">
            {REPORT_CATALOG.map(group => (
              <div className="panel" key={group.cat}>
                <div className="panel-head">
                  <div style={{display:"flex", alignItems:"center", gap:10}}>
                    <span style={{width:3, height:14, background: group.color, borderRadius:1}}/>
                    <div>
                      <h3 className="panel-title">{group.cat}</h3>
                      <div className="panel-sub">{group.items.length} modèles</div>
                    </div>
                  </div>
                </div>
                <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:0}}>
                  {group.items.map((r, i) => {
                    const isLastRow = i >= group.items.length - 2;
                    const isOdd = i % 2 === 1;
                    const isSelected = selectedReport === r.id;
                    return (
                      <div
                        key={r.id}
                        onClick={() => setSelectedReport(r.id)}
                        style={{
                          padding:"14px 16px",
                          borderBottom: !isLastRow ? "1px solid var(--d-line-2)" : "none",
                          borderRight: !isOdd ? "1px solid var(--d-line-2)" : "none",
                          cursor:"pointer",
                          background: isSelected ? "var(--d-elev)" : "transparent",
                          position:"relative",
                        }}
                      >
                        {isSelected && <span style={{position:"absolute", left:0, top:14, bottom:14, width:2, background: group.color, borderRadius:"0 1px 1px 0"}}/>}
                        <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", marginBottom:4, gap:8}}>
                          <div style={{fontSize:12.5, fontWeight:500, color:"var(--d-ink)"}}>
                            {r.name}
                            {r.featured && <span style={{marginLeft:8, fontSize:9.5, fontWeight:600, color: group.color, background:"rgba(63,181,131,0.08)", border:`1px solid ${group.color}33`, padding:"1px 5px", borderRadius:2, letterSpacing:"0.04em"}}>POPULAIRE</span>}
                          </div>
                        </div>
                        <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginBottom:8, lineHeight:1.4}}>{r.desc}</div>
                        <div style={{display:"flex", alignItems:"center", gap:10, fontSize:10.5}}>
                          <span style={{color:"var(--d-ink-4)", fontFamily:"var(--font-mono)"}}>{r.freq}</span>
                          <span style={{color:"var(--d-ink-4)"}}>·</span>
                          <span style={{color:"var(--d-ink-3)"}}>Dernier : {r.last}</span>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            ))}
          </div>

          {/* Side: preview of the selected report */}
          <div className="stack">
            <ReportPreview reportId={selectedReport} period={period} setPeriod={setPeriod}/>
            <div className="panel">
              <div className="panel-head">
                <div>
                  <h3 className="panel-title">Astuces</h3>
                  <div className="panel-sub">Optimiser vos rapports</div>
                </div>
              </div>
              <div style={{padding:"4px 0"}}>
                {[
                  { t:"Planifier l'envoi automatique", d:"Le 1er du mois, les comptes de gérance partent par email aux propriétaires sans intervention." },
                  { t:"Modèle par propriétaire", d:"Personnalisez l'en-tête, le pied de page et le récapitulatif TVA pour chaque propriétaire." },
                  { t:"Exporter en lot", d:"Téléchargez tous les bordereaux du mois dans un seul .zip prêt à archiver." },
                ].map((tip, i) => (
                  <div key={i} style={{display:"flex", gap:12, padding:"11px 18px", alignItems:"flex-start", borderBottom: i<2?"1px solid var(--d-line-2)":"none"}}>
                    <span style={{width:18, height:18, borderRadius:4, background:"var(--d-elev)", color:"var(--d-ink-3)", fontSize:10.5, fontFamily:"var(--font-mono)", display:"grid", placeItems:"center", flexShrink:0, marginTop:1}}>{i+1}</span>
                    <div>
                      <div style={{fontSize:12.5, fontWeight:500, color:"var(--d-ink)", marginBottom:2}}>{tip.t}</div>
                      <div style={{fontSize:11.5, color:"var(--d-ink-3)", lineHeight:1.45}}>{tip.d}</div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </div>
      )}

      {tab === "recent" && <RecentReportsPanel/>}
      {tab === "scheduled" && <ScheduledReportsPanel/>}
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   PREVIEW — Compte de gérance, etc.
   ───────────────────────────────────────────────────────── */
function ReportPreview({ reportId, period, setPeriod }) {
  const catalog = REPORT_CATALOG.flatMap(g => g.items.map(i => ({...i, cat: g.cat, color: g.color})));
  const report = catalog.find(r => r.id === reportId) || catalog[0];

  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Aperçu · {report.name}</h3>
          <div className="panel-sub">{report.cat} · {report.freq}</div>
        </div>
        <div className="panel-head-right">
          <div className="seg">
            {[["avr-26","Avril"],["mar-26","Mars"],["t1-26","T1 2026"]].map(([k,l])=>(
              <button key={k} className={"seg-btn"+(period===k?" active":"")} onClick={()=>setPeriod(k)}>{l}</button>
            ))}
          </div>
          <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}} onClick={()=>lgiToast("Rapport généré","ok")}>Générer</button>
        </div>
      </div>
      <div style={{padding:"32px 24px", textAlign:"center", borderBottom:"1px solid var(--d-line)"}}>
        <div style={{width:48, height:60, margin:"0 auto 14px", background:"var(--d-elev)", border:"1px solid var(--d-line)", borderRadius:3, position:"relative"}}>
          <div style={{position:"absolute", inset:"12px 8px auto", height:1, background:"var(--d-line)"}}/>
          <div style={{position:"absolute", inset:"18px 8px auto", height:1, background:"var(--d-line)"}}/>
          <div style={{position:"absolute", inset:"24px 12px auto 8px", height:1, background:"var(--d-line)"}}/>
          <div style={{position:"absolute", inset:"34px 8px auto", height:1, background:"var(--d-line)"}}/>
          <div style={{position:"absolute", inset:"40px 14px auto 8px", height:1, background:"var(--d-line)"}}/>
        </div>
        <div style={{fontSize:13, fontWeight:500, color:"var(--d-ink)", marginBottom:4}}>{report.name}</div>
        <div style={{fontSize:11.5, color:"var(--d-ink-3)", maxWidth:320, margin:"0 auto"}}>{report.desc}</div>
      </div>
      <div style={{padding:"14px 18px"}}>
        <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:8, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600}}>Contenu</div>
        {report.id === "r-perf-bien" && <PreviewList items={["Loyer théorique · brut · net","Taux d'occupation moyen","Taux de recouvrement","Coût de vacance","Ranking par rendement"]}/>}
        {report.id === "r-vacance" && <PreviewList items={["Lots vacants par bien","Durée moyenne de vacance","Manque à gagner cumulé","Prochains baux à libérer"]}/>}
        {report.id === "r-synthese" && <PreviewList items={["KPI 12 derniers mois","Trésorerie consolidée","Top 3 / Bottom 3 biens","Évolution du parc"]}/>}
        {report.id === "r-dash-monthly" && <PreviewList items={["Encaissements détaillés","État des impayés","Vacance et mouvements","Versements propriétaires"]}/>}
        {report.id === "r-balance" && <PreviewList items={["Solde par locataire","Ancienneté du retard","Plan d'apurement éventuel"]}/>}
        {report.id === "r-rendement" && <PreviewList items={["Top 3 / Bottom 3 biens","Rendement net 12 mois","Coût complet par lot","Recommandations"]}/>}
        {report.id === "r-cashflow" && <PreviewList items={["Encaissements consolidés","Versements et charges","Réconciliation comptes bancaires","Solde de trésorerie"]}/>}
        {report.id === "r-tva" && <PreviewList items={["TVA collectée par mois","TVA déductible · charges","Solde net dû à l'État"]}/>}
        {report.id === "r-attest" && <PreviewList items={["Revenus bruts annuels","Commission déduite","Net imposable propriétaire"]}/>}
        {report.id === "r-ohada" && <PreviewList items={["Bilan locatif","Compte de résultat","Annexes obligatoires SYSCOHADA"]}/>}
        {report.id === "r-etat-loc" && <PreviewList items={["Récap annuel par bien","Cumul fiscal annuel","Liste des locataires"]}/>}
        {report.id === "r-lettre" && <PreviewList items={["Travaux réalisés","Sinistres en cours","Baux à renouveler","Recommandations"]}/>}
      </div>
    </div>
  );
}
function PreviewList({ items }) {
  return (
    <ul style={{margin:0, padding:0, listStyle:"none"}}>
      {items.map((it, i) => (
        <li key={i} style={{fontSize:12, color:"var(--d-ink-2)", padding:"6px 0", display:"flex", gap:8, alignItems:"center"}}>
          <span style={{width:4, height:4, borderRadius:50, background:"var(--d-ink-4)", flexShrink:0}}/>
          {it}
        </li>
      ))}
    </ul>
  );
}

/* ─────────────────────────────────────────────────────────
   OPERATIONS CROSS-LINKS
   Cards that point to the operational sections (CRG, Quittances,
   Versements, Paiements) — Rapports stays focused on analytics.
   ───────────────────────────────────────────────────────── */
function OperationsCrossLinks({ onRoute }) {
  const ops = [
    { route:"crg",           name:"CRG · Comptes rendus de gestion", desc:"Document mensuel par bailleur · 6 documents en avril",  c:"var(--pos-d)" },
    { route:"invoices",      name:"Quittances",                       desc:"18 émises en avril · réglées et en attente",            c:"var(--blue-d)" },
    { route:"disbursements", name:"Versements propriétaires",         desc:"6 virements à émettre · échéance 30 avril",              c:"#3FB583" },
    { route:"payments",      name:"Paiements & encaissements",        desc:"Journal en temps réel · Wave, OM, virements",           c:"var(--orange)" },
  ];
  return (
    <div className="panel" style={{marginTop:18, marginBottom:0}}>
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Documents opérationnels</h3>
          <div className="panel-sub">Ces documents sont gérés dans leurs sections dédiées — Rapports concentre l'analytique et le fiscal.</div>
        </div>
      </div>
      <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:0}}>
        {ops.map((o, i) => (
          <button
            key={o.route}
            onClick={() => onRoute && onRoute(o.route)}
            style={{
              padding:"14px 16px",
              borderRight: i < 3 ? "1px solid var(--d-line-2)" : "none",
              cursor:"pointer", textAlign:"left",
              background:"transparent", color:"var(--d-ink)", border:"none",
              borderBottom:"none",
              transition:"background 120ms ease",
              fontFamily:"var(--font-sans)",
            }}
            onMouseEnter={e => e.currentTarget.style.background = "var(--d-elev)"}
            onMouseLeave={e => e.currentTarget.style.background = "transparent"}
          >
            <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:6}}>
              <span style={{width:6, height:6, borderRadius:50, background:o.c}}/>
              <span style={{fontSize:12.5, fontWeight:500, color:"var(--d-ink)"}}>{o.name}</span>
              <span style={{marginLeft:"auto", color:"var(--d-ink-4)", fontSize:12}}>→</span>
            </div>
            <div style={{fontSize:11.5, color:"var(--d-ink-3)", lineHeight:1.45}}>{o.desc}</div>
          </button>
        ))}
      </div>
    </div>
  );
}
/* ─────────────────────────────────────────────────────────
   HISTORIQUE
   ───────────────────────────────────────────────────────── */
function RecentReportsPanel() {
  const [q, setQ] = React.useState("");
  const [typeFilter, setTypeFilter] = React.useState("all");
  const filtered = RECENT_REPORTS.filter(r => {
    if (typeFilter !== "all" && r.type.toLowerCase() !== typeFilter) return false;
    if (q && !`${r.name} ${r.id} ${r.period}`.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Historique des rapports générés</h3>
          <div className="panel-sub">{filtered.length} rapports · archivés 90 jours</div>
        </div>
        <button className="btn btn-ghost" onClick={()=>lgiToast("Archive .zip des rapports en préparation","ok")}>Tout exporter (.zip)</button>
      </div>
      <div className="filter-bar">
        <div className="top-search" style={{width:280, background:"var(--d-panel)"}}>
          <span style={{color:"var(--d-ink-4)"}}>⌕</span>
          <input value={q} onChange={e=>setQ(e.target.value)} placeholder="Nom, ID, période…"/>
        </div>
        {[["all","Tous",RECENT_REPORTS.length],["pilotage","Pilotage",RECENT_REPORTS.filter(r=>r.type==="Pilotage").length],["comptabilité","Comptabilité",RECENT_REPORTS.filter(r=>r.type==="Comptabilité").length],["fiscal","Fiscal",RECENT_REPORTS.filter(r=>r.type==="Fiscal").length],["propriétaires","Propriétaires",RECENT_REPORTS.filter(r=>r.type==="Propriétaires").length]].map(([k,l,n])=>(
          <button key={k} className={"filter-chip" + (typeFilter === k ? " active" : "")} onClick={()=>setTypeFilter(k)}>{l} <span className="k">{n}</span></button>
        ))}
        <div className="filter-spacer"/>
        <button className="filter-chip">Trier ▾</button>
      </div>
      <div className="table-wrap">
        <table className="tbl">
          <thead>
            <tr>
              <th style={{width:96}}>Réf.</th>
              <th>Rapport</th>
              <th>Type</th>
              <th>Période</th>
              <th>Format</th>
              <th className="right">Taille</th>
              <th>Généré</th>
              <th>Statut</th>
              <th style={{width:110}}></th>
            </tr>
          </thead>
          <tbody>
            {filtered.length === 0 && (
              <tr><td colSpan={9} className="muted" style={{textAlign:"center", padding:28}}>Aucun rapport ne correspond.</td></tr>
            )}
            {filtered.map(r => (
              <tr key={r.id}>
                <td className="mono muted">{r.id}</td>
                <td style={{fontWeight:500}}>{r.name}</td>
                <td className="muted">{r.type}</td>
                <td className="muted">{r.period}</td>
                <td>
                  <span style={{
                    fontSize:10, fontWeight:600, padding:"2px 6px", borderRadius:3,
                    color: r.format === "PDF" ? "#F16A63" : "#3FB583",
                    background: r.format === "PDF" ? "rgba(241,106,99,0.10)" : "rgba(63,181,131,0.10)",
                    border: `1px solid ${r.format === "PDF" ? "rgba(241,106,99,0.22)" : "rgba(63,181,131,0.22)"}`,
                    fontFamily:"var(--font-mono)",
                    letterSpacing:"0.02em",
                  }}>{r.format}</span>
                </td>
                <td className="right muted">{r.size}</td>
                <td className="muted mono" style={{fontSize:11.5}}>{r.date} · <span style={{color:"var(--d-ink-4)"}}>{r.by}</span></td>
                <td>
                  <span className={`chip ${r.status==="envoyé"?"pos":r.status==="archivé"?"neutral":r.status==="exporté"?"info":"warn"}`}>{r.status}</span>
                </td>
                <td className="right">
                  <button className="btn btn-ghost" style={{padding:"3px 8px", fontSize:11}} onClick={(e)=>{e.stopPropagation();lgiToast("Téléchargement du rapport (PDF)","ok");}}>⬇</button>
                  <button className="btn btn-ghost" style={{padding:"3px 8px", fontSize:11}} onClick={(e)=>{e.stopPropagation();lgiToast("Options : renvoyer · dupliquer · archiver");}}>…</button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   ENVOIS PLANIFIÉS
   ───────────────────────────────────────────────────────── */
function ScheduledReportsPanel() {
  return (
    <div className="grid-2">
      <div className="panel">
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Envois planifiés</h3>
            <div className="panel-sub">{SCHEDULED_REPORTS.length} automatisations actives</div>
          </div>
          <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}} onClick={()=>lgiToast("Nouvel envoi automatique programmé")}>+ Nouvel envoi</button>
        </div>
        <div>
          {SCHEDULED_REPORTS.map(s => (
            <div key={s.id} style={{padding:"16px 20px", borderBottom:"1px solid var(--d-line-2)", display:"grid", gridTemplateColumns:"1fr auto auto", gap:18, alignItems:"center"}}>
              <div>
                <div style={{fontSize:13, fontWeight:500, color:"var(--d-ink)", marginBottom:3}}>{s.name}</div>
                <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>{s.freq} · {s.count} {s.count > 1 ? "destinataires" : "destinataire"} · canal {s.channel}</div>
              </div>
              <div style={{textAlign:"right"}}>
                <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600}}>Prochain envoi</div>
                <div className="mono" style={{fontSize:12.5, color:"var(--d-ink)", marginTop:2}}>{s.next}</div>
              </div>
              <div style={{display:"flex", gap:6}}>
                <button className="btn btn-ghost" style={{padding:"4px 8px", fontSize:11.5}} onClick={()=>lgiToast("Modifier la planification")}>Modifier</button>
                <label style={{display:"inline-flex", alignItems:"center", gap:6, fontSize:11.5, color:"var(--d-ink-2)", padding:"4px 8px", borderRadius:4, border:"1px solid var(--d-line)", background:"var(--d-elev)", cursor:"pointer"}}>
                  <span style={{width:7, height:7, borderRadius:50, background:"var(--pos-d)", display:"inline-block"}}/> Actif
                </label>
              </div>
            </div>
          ))}
        </div>
      </div>
      <div className="stack">
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Calendrier · 30 jours</h3>
              <div className="panel-sub">Aperçu des envois à venir</div>
            </div>
          </div>
          <div style={{padding:"4px 0"}}>
            {[
              { d:"01 mai · 06:00", n:"Synthèse de gestion mensuelle", c:"var(--blue-d)" },
              { d:"01 mai · 09:00", n:"Comptes de gérance · 6 propriétaires", c:"var(--pos-d)" },
              { d:"02 mai · 08:00", n:"Bordereaux propriétaires · email", c:"var(--orange)" },
              { d:"15 mai · 17:00", n:"Rapport vacance locative", c:"var(--blue-d)" },
            ].map((e, i) => (
              <div key={i} style={{display:"flex", gap:12, padding:"11px 18px", alignItems:"center", borderBottom: i<3?"1px solid var(--d-line-2)":"none"}}>
                <span style={{width:3, height:24, background:e.c, borderRadius:1.5, flexShrink:0}}/>
                <div style={{flex:1}}>
                  <div style={{fontSize:12.5, fontWeight:500, color:"var(--d-ink)"}}>{e.n}</div>
                  <div className="mono" style={{fontSize:11, color:"var(--d-ink-3)", marginTop:2}}>{e.d}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Bonnes pratiques</h3>
              <div className="panel-sub">Recommandations agence</div>
            </div>
          </div>
          <div style={{padding:"4px 0"}}>
            {[
              { t:"Envoyer les comptes de gérance avant le 5", d:"Les propriétaires apprécient la régularité du jour d'envoi." },
              { t:"Joindre les justificatifs", d:"Joindre PDF des quittances et reçus pour la transparence." },
            ].map((p, i) => (
              <div key={i} style={{padding:"11px 18px", borderBottom: i<1?"1px solid var(--d-line-2)":"none"}}>
                <div style={{fontSize:12.5, fontWeight:500, color:"var(--d-ink)", marginBottom:2}}>{p.t}</div>
                <div style={{fontSize:11.5, color:"var(--d-ink-3)", lineHeight:1.45}}>{p.d}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ReportsPage });

/* ╚══ end pages-reports.jsx ══╝ */

/* ╔══ pages-quittances.jsx ══╗ */
/* global React, MOCK, fmtFCFA, fmtCompact, Spark, StatusChip */

/* ─────────────────────────────────────────────────────────
   QUITTANCES — receipts page
   ───────────────────────────────────────────────────────── */

const QUITTANCES = [
  { id: "Q-2026-04-018", tenant: "Mariama Ndiaye",  tenantId:"T-2041", property: "Rés. Teranga · A-203", amount: 240_000, method:"Wave",         period:"Avril 2026", issued:"01 avr", paid:"18 avr", status:"réglée" },
  { id: "Q-2026-04-017", tenant: "N. F. Thiam",      tenantId:"T-2118", property: "Rés. Ngor · 3",       amount: 290_000, method:"Orange Money", period:"Avril 2026", issued:"01 avr", paid:"18 avr", status:"réglée" },
  { id: "Q-2026-04-016", tenant: "Awa Sall",         tenantId:"T-2063", property: "Villa Mermoz 14",    amount: 850_000, method:"Virement",     period:"Avril 2026", issued:"01 avr", paid:"17 avr", status:"réglée" },
  { id: "Q-2026-04-015", tenant: "P. M. Diop",       tenantId:"T-2124", property: "Rés. Ouakam · A-301",amount: 260_000, method:"Wave",         period:"Avril 2026", issued:"01 avr", paid:"17 avr", status:"réglée" },
  { id: "Q-2026-04-014", tenant: "Bineta Diouf",     tenantId:"T-2102", property: "Rés. Plateau · 5",   amount: 225_000, method:"—",            period:"Avril 2026", issued:"01 avr", paid:"—",      status:"en attente" },
  { id: "Q-2026-04-013", tenant: "Omar Sy",          tenantId:"T-2094", property: "Rés. Teranga · A-104",amount: 240_000, method:"Orange Money", period:"Avril 2026", issued:"01 avr", paid:"16 avr", status:"réglée" },
  { id: "Q-2026-04-012", tenant: "F. B. Kane",       tenantId:"T-2089", property: "Rés. Ngor · 2",       amount: 310_000, method:"Wave",         period:"Avril 2026", issued:"01 avr", paid:"15 avr", status:"réglée" },
  { id: "Q-2026-04-011", tenant: "Cheikh T. Diagne", tenantId:"T-2058", property: "Imm. Point E · B-101",amount: 280_000, method:"—",            period:"Avril 2026", issued:"01 avr", paid:"—",      status:"en retard" },
  { id: "Q-2026-04-010", tenant: "Moussa Fall",      tenantId:"T-2077", property: "Rés. Teranga · C-304",amount: 220_000, method:"—",            period:"Avril 2026", issued:"01 avr", paid:"—",      status:"impayée" },
  { id: "Q-2026-04-009", tenant: "A. B. Cissé",      tenantId:"T-2110", property: "Imm. Point E · B-202",amount: 260_000, method:"Virement",     period:"Avril 2026", issued:"01 avr", paid:"14 avr", status:"réglée" },
  { id: "Q-2026-03-024", tenant: "Mariama Ndiaye",   tenantId:"T-2041", property: "Rés. Teranga · A-203",amount: 240_000, method:"Wave",         period:"Mars 2026",  issued:"01 mars", paid:"06 mars", status:"réglée" },
  { id: "Q-2026-03-023", tenant: "N. F. Thiam",      tenantId:"T-2118", property: "Rés. Ngor · 3",       amount: 290_000, method:"Orange Money", period:"Mars 2026",  issued:"01 mars", paid:"04 mars", status:"réglée" },
  { id: "Q-2026-03-022", tenant: "Moussa Fall",      tenantId:"T-2077", property: "Rés. Teranga · C-304",amount: 220_000, method:"—",            period:"Mars 2026",  issued:"01 mars", paid:"—",       status:"impayée" },
  { id: "Q-2026-03-021", tenant: "Awa Sall",         tenantId:"T-2063", property: "Villa Mermoz 14",    amount: 850_000, method:"Virement",     period:"Mars 2026",  issued:"01 mars", paid:"03 mars", status:"réglée" },
  { id: "Q-2026-03-020", tenant: "—",                tenantId:null,     property: "Rés. Teranga · B-201",amount: 240_000, method:"—",            period:"Mars 2026",  issued:"01 mars", paid:"—",       status:"annulée" },
  { id: "Q-2026-03-019", tenant: "Bineta Diouf",     tenantId:"T-2102", property: "Rés. Plateau · 5",   amount: 225_000, method:"Wave",         period:"Mars 2026",  issued:"01 mars", paid:"12 mars", status:"réglée" },
  { id: "Q-2026-03-018", tenant: "Omar Sy",          tenantId:"T-2094", property: "Rés. Teranga · A-104",amount: 240_000, method:"Orange Money", period:"Mars 2026",  issued:"01 mars", paid:"08 mars", status:"réglée" },
  { id: "Q-2026-03-017", tenant: "P. M. Diop",       tenantId:"T-2124", property: "Rés. Ouakam · A-301",amount: 260_000, method:"Wave",         period:"Mars 2026",  issued:"01 mars", paid:"05 mars", status:"réglée" },
];

/* ─── Quittances store + helpers ─── */
const Q_MONTHS_FULL = ["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"];
const Q_MONTHS_SHORT = ["janv","févr","mars","avr","mai","juin","juil","août","sept","oct","nov","déc"];
function qMonthShort(full) { const i = Q_MONTHS_FULL.indexOf(full.split(" ")[0]); return i>=0 ? Q_MONTHS_SHORT[i] : full; }
function qPeriodKey(period) { const [m,y] = period.split(" "); const i = Q_MONTHS_FULL.indexOf(m); return `${y}-${String(i+1).padStart(2,"0")}`; }
function nextQuittanceId(period) {
  const key = qPeriodKey(period);
  const prefix = `Q-${key}-`;
  const max = QUITTANCES.reduce((m,q)=> q.id.startsWith(prefix) ? Math.max(m, parseInt(q.id.slice(prefix.length),10)||0) : m, 0);
  return prefix + String(max+1).padStart(3,"0");
}
const _qListeners = new Set();
function _notifyQ(){ _qListeners.forEach(f=>f()); }
function upsertQuittance(q){
  const i = QUITTANCES.findIndex(x=>x.id===q.id);
  if (i>=0) QUITTANCES[i] = {...QUITTANCES[i], ...q}; else QUITTANCES.unshift(q);
  _notifyQ();
}
function useQuittances(){
  const [,tick] = React.useReducer(x=>x+1,0);
  React.useEffect(()=>{ _qListeners.add(tick); return ()=>_qListeners.delete(tick); },[]);
  return QUITTANCES;
}

function QuittancesPage({ onOpenTenant }) {
  useQuittances(); // re-render when a quittance is created
  const [tab, setTab] = React.useState("avr");
  const [statusFilter, setStatusFilter] = React.useState("all");
  const [q, setQ] = React.useState("");
  const [selectedId, setSelectedId] = React.useState("Q-2026-04-018");
  const [showBatch, setShowBatch] = React.useState(false);
  const [showNew, setShowNew] = React.useState(false);
  const [relanceTarget, setRelanceTarget] = React.useState(null);
  const [markTarget, setMarkTarget] = React.useState(null);

  const filtered = QUITTANCES.filter(r => {
    if (tab === "avr" && !r.period.includes("Avril")) return false;
    if (tab === "mar" && !r.period.includes("Mars")) return false;
    if (statusFilter !== "all" && r.status !== statusFilter) return false;
    if (q && !`${r.tenant} ${r.property} ${r.id}`.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });
  const selected = QUITTANCES.find(r => r.id === selectedId) || filtered[0];

  // Month-scoped totals (avril)
  const avr = QUITTANCES.filter(r => r.period.includes("Avril"));
  const emisAvr = avr.length;
  const reglAvr = avr.filter(r => r.status === "réglée").length;
  const totEmis = avr.reduce((s, r) => s + r.amount, 0);
  const totReg = avr.filter(r => r.status === "réglée").reduce((s, r) => s + r.amount, 0);
  const totAtt = avr.filter(r => r.status === "en attente" || r.status === "en retard" || r.status === "impayée").reduce((s, r) => s + r.amount, 0);

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Quittances</h1>
          <div className="page-sub">{emisAvr} quittances émises en avril · prochaine génération auto le 01 mai 06:00</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter (.zip)</button>
          <button className="btn btn-secondary" onClick={() => setShowNew(true)}>Quittance manuelle</button>
          <button className="btn btn-primary" onClick={() => setShowBatch(true)}>Générer les quittances du mois</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Émises · avril</span></div>
          <div className="kpi-value num">{emisAvr}<span className="kpi-unit">quittances</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 1 vs mars</span><span>{(totEmis/1_000_000).toFixed(2).replace(".",",")} M FCFA · 8 propriétaires</span></div>
          <div className="kpi-spark"><Spark values={[14,15,16,17,16,18,17,18,17,18,18,18]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Réglées</span></div>
          <div className="kpi-value num">{reglAvr}<span className="kpi-unit">/ {emisAvr}</span></div>
          <div className="kpi-meta"><span className="delta pos">{Math.round((reglAvr/emisAvr)*100)}%</span><span>{(totReg/1_000_000).toFixed(2).replace(".",",")} M FCFA encaissés</span></div>
        </div>
        <div className="kpi alert">
          <div className="kpi-label"><span>En attente / impayées</span><span style={{color:"var(--orange)", fontWeight:600}}>Suivre</span></div>
          <div className="kpi-value num" style={{color:"var(--orange)"}}>{emisAvr-reglAvr}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>quittances</span></div>
          <div className="kpi-meta"><span className="delta neg">{fmtCompact(totAtt)}</span><span>FCFA à recouvrer</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Délai moyen règlement</span></div>
          <div className="kpi-value num">8,4<span className="kpi-unit">jours</span></div>
          <div className="kpi-meta"><span className="delta pos">▼ 1,2 j</span><span>vs mars · cible 10 j</span></div>
        </div>
      </div>

      {/* Period tabs */}
      <div className="tabs">
        {[["avr","Avril 2026", QUITTANCES.filter(r=>r.period.includes("Avril")).length],["mar","Mars 2026", QUITTANCES.filter(r=>r.period.includes("Mars")).length],["all","Toutes", QUITTANCES.length]].map(([k,l,n])=>(
          <button key={k} className={"tab" + (tab===k?" active":"")} onClick={()=>setTab(k)}>{l} <span className="tab-count">{n}</span></button>
        ))}
      </div>

      <div className="grid-2">
        {/* Main: table */}
        <div className="panel">
          <div className="filter-bar" style={{borderTop:"none"}}>
            <div className="top-search" style={{width:280, background:"var(--d-panel)"}}>
              <span style={{color:"var(--d-ink-4)"}}>⌕</span>
              <input value={q} onChange={e=>setQ(e.target.value)} placeholder="Locataire, bien, ID…"/>
            </div>
            {[
              ["all","Toutes",filtered.length+QUITTANCES.filter(r=> (tab==="avr"&&r.period.includes("Avril"))||(tab==="mar"&&r.period.includes("Mars"))||tab==="all").length-filtered.length],
              ["réglée","Réglées",QUITTANCES.filter(r=>r.status==="réglée"&&((tab==="avr"&&r.period.includes("Avril"))||(tab==="mar"&&r.period.includes("Mars"))||tab==="all")).length],
              ["en attente","Attente",QUITTANCES.filter(r=>r.status==="en attente"&&((tab==="avr"&&r.period.includes("Avril"))||(tab==="mar"&&r.period.includes("Mars"))||tab==="all")).length],
              ["en retard","Retard",QUITTANCES.filter(r=>r.status==="en retard"&&((tab==="avr"&&r.period.includes("Avril"))||(tab==="mar"&&r.period.includes("Mars"))||tab==="all")).length],
              ["impayée","Impayées",QUITTANCES.filter(r=>r.status==="impayée"&&((tab==="avr"&&r.period.includes("Avril"))||(tab==="mar"&&r.period.includes("Mars"))||tab==="all")).length],
            ].map(([k,l,n])=>(
              <button key={k} className={"filter-chip" + (statusFilter === k ? " active" : "")} onClick={()=>setStatusFilter(k)}>{l} <span className="k">{n}</span></button>
            ))}
            <div className="filter-spacer"/>
            <button className="filter-chip">Trier ▾</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:130}}>Référence</th>
                  <th>Locataire</th>
                  <th>Bien · lot</th>
                  <th className="right">Montant</th>
                  <th>Émise</th>
                  <th>Réglée</th>
                  <th>Statut</th>
                </tr>
              </thead>
              <tbody>
                {filtered.length === 0 && <tr><td colSpan={7} className="muted" style={{textAlign:"center", padding:32}}>Aucune quittance ne correspond.</td></tr>}
                {filtered.map(r => (
                  <tr key={r.id} onClick={()=>setSelectedId(r.id)} style={{background: r.id===selectedId? "var(--d-elev)":undefined}}>
                    <td className="mono" style={{fontSize:11.5, color: r.id===selectedId? "var(--d-ink)":"var(--d-ink-2)"}}>{r.id}</td>
                    <td>{r.tenant === "—" ? <span className="empty-dash">—</span> : r.tenant}</td>
                    <td className="muted" style={{fontSize:11.5}}>{r.property}</td>
                    <td className={"right" + (r.status==="impayée"||r.status==="en retard" ? " highlight-neg":"")}>{fmtFCFA(r.amount)}</td>
                    <td className="muted mono" style={{fontSize:11.5}}>{r.issued}</td>
                    <td className="muted mono" style={{fontSize:11.5}}>{r.paid === "—" ? <span className="empty-dash">—</span> : r.paid}</td>
                    <td><QuittanceChip status={r.status}/></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>

        {/* Side: preview + actions */}
        <div className="stack">
          {selected && <QuittancePreview q={selected} onOpenTenant={onOpenTenant} onRelance={()=>setRelanceTarget(selected)} onMarkPaid={()=>setMarkTarget(selected)}/>}
        </div>
      </div>

      {/* Generation log */}
      <div className="panel" style={{marginTop:16}}>
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Journal de génération</h3>
            <div className="panel-sub">Dernières opérations · génération auto le 1er du mois</div>
          </div>
        </div>
        <div style={{padding:"4px 0"}}>
          {[
            {tag:"AUTO", c:"var(--blue-d)", txt:"18 quittances générées pour avril 2026 · envoi PDF par email", t:"01 avr · 06:00"},
            {tag:"BATCH",c:"var(--pos-d)", txt:"Envoi groupé WhatsApp · 12 destinataires · 0 échec", t:"01 avr · 06:14"},
            {tag:"MANUEL",c:"var(--d-ink-3)", txt:"Quittance Q-2026-04-018 renvoyée à M. Ndiaye · email", t:"15 avr · 11:42"},
            {tag:"ANNUL",c:"var(--orange)", txt:"Q-2026-03-020 annulée · lot libéré entre-temps", t:"02 mars · 10:18"},
          ].map((r, i) => (
            <div key={i} style={{display:"flex", gap:12, padding:"10px 18px", alignItems:"flex-start", borderBottom: i<3?"1px solid var(--d-line-2)":"none"}}>
              <span style={{fontSize:9.5, fontWeight:700, color:"#fff", background:r.c, padding:"2px 6px", borderRadius:3, letterSpacing:"0.04em", marginTop:1, minWidth:48, textAlign:"center"}}>{r.tag}</span>
              <div style={{flex:1, minWidth:0}}>
                <div style={{fontSize:12, color:"var(--d-ink-2)"}}>{r.txt}</div>
                <div className="muted mono" style={{fontSize:10.5, marginTop:2}}>{r.t}</div>
              </div>
            </div>
          ))}
        </div>
      </div>

      {showBatch && <BatchGenerationModal onClose={()=>setShowBatch(false)}/>}
      {showNew && <NewQuittanceModal onClose={()=>setShowNew(false)} onSave={(payload)=>{ upsertQuittance(payload); setSelectedId(payload.id); setTab("all"); setShowNew(false); }}/>}
      {markTarget && <MarkPaidModal q={markTarget} onClose={()=>setMarkTarget(null)} onConfirm={({method, day})=>{
        const moisShort = qMonthShort(markTarget.period);
        upsertQuittance({ ...markTarget, status:"réglée", method, paid:`${String(day).padStart(2,"0")} ${moisShort}` });
        if (typeof settleOverdueByTenant !== "undefined" && markTarget.tenantId) settleOverdueByTenant(markTarget.tenantId, markTarget.amount);
        setMarkTarget(null);
      }}/>}
      {relanceTarget && typeof RelanceModal !== "undefined" && (
        <RelanceModal
          target={{ name: relanceTarget.tenant, amount: relanceTarget.amount, period: relanceTarget.period, property: relanceTarget.property, tenantId: relanceTarget.tenantId }}
          onClose={()=>setRelanceTarget(null)}
          onSent={()=>setRelanceTarget(null)}
        />
      )}
    </div>
  );
}

function QuittanceChip({ status }) {
  const map = {
    "réglée":     ["pos","réglée"],
    "en attente": ["warn","en attente"],
    "en retard":  ["warn","en retard"],
    "impayée":    ["neg","impayée"],
    "annulée":    ["neutral","annulée"],
  };
  const [cls, label] = map[status] || ["neutral", status];
  return <span className={`chip ${cls}`}>{label}</span>;
}

/* ─────────────────────────────────────────────────────────
   QUITTANCE PREVIEW (paper-style)
   ───────────────────────────────────────────────────────── */
function QuittancePreview({ q, onOpenTenant, onRelance, onMarkPaid }) {
  const tenant = MOCK.tenants.find(t => t.id === q.tenantId);
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Aperçu · {q.id}</h3>
          <div className="panel-sub">{q.period} · {q.tenant !== "—" ? q.tenant : "—"}</div>
        </div>
        <div className="panel-head-right">
          <button className="btn btn-ghost" style={{padding:"5px 9px", fontSize:11.5}}>⬇ PDF</button>
          {q.status === "réglée" ? (
            <button className="btn btn-secondary" style={{padding:"5px 10px", fontSize:11.5}}>Renvoyer</button>
          ) : q.status === "annulée" ? (
            <button className="btn btn-secondary" style={{padding:"5px 10px", fontSize:11.5}} disabled>Annulée</button>
          ) : (
            <>
              <button className="btn btn-secondary" style={{padding:"5px 10px", fontSize:11.5}} onClick={onRelance}>Relancer</button>
              <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5, background:"#3FB583", borderColor:"#3FB583"}} onClick={onMarkPaid}>Marquer payé</button>
            </>
          )}
        </div>
      </div>

      {/* Paper preview */}
      <div style={{padding:"20px 22px"}}>
        <div style={{background:"#FAFAF7", color:"#1A1A1A", borderRadius:6, padding:"22px 24px", fontFamily:"var(--font-sans)", border:"1px solid #E4E4DE"}}>
          {/* Watermark */}
          {q.status === "annulée" && (
            <div style={{position:"absolute", fontSize:54, color:"rgba(154,42,42,0.10)", fontWeight:700, letterSpacing:"0.1em", transform:"rotate(-20deg)", pointerEvents:"none", marginTop:60, marginLeft:60}}>ANNULÉE</div>
          )}
          {/* Header */}
          <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", marginBottom:14, paddingBottom:12, borderBottom:"1px solid #E4E4DE"}}>
            <div>
              <div style={{fontSize:9.5, color:"#7B7B73", letterSpacing:"0.12em", textTransform:"uppercase", fontWeight:600, marginBottom:4}}>Quittance de loyer</div>
              <div style={{fontSize:16, fontWeight:600, color:"#1A1A1A", letterSpacing:"-0.01em"}}>{q.period}</div>
              <div className="mono" style={{fontSize:10.5, color:"#7B7B73", marginTop:3}}>{q.id}</div>
            </div>
            <div style={{textAlign:"right"}}>
              <div style={{fontSize:11, fontWeight:600, color:"#1A1A1A", marginBottom:1}}>BA Immo · Dakar</div>
              <div style={{fontSize:10, color:"#7B7B73"}}>Almadies, Dakar</div>
              <div style={{fontSize:10, color:"#7B7B73"}}>NINEA 005 482 6 G3</div>
            </div>
          </div>

          {/* Parties */}
          <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:14, marginBottom:14}}>
            <div>
              <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:4}}>Locataire</div>
              <div style={{fontSize:12, fontWeight:600, color:"#1A1A1A"}}>{q.tenant}</div>
              {tenant && <div style={{fontSize:10.5, color:"#5B5B53"}}>{tenant.phone}</div>}
              {tenant && <div className="mono" style={{fontSize:10, color:"#7B7B73"}}>{tenant.id}</div>}
            </div>
            <div>
              <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:4}}>Bien loué</div>
              <div style={{fontSize:12, fontWeight:600, color:"#1A1A1A"}}>{q.property}</div>
              {tenant && <div style={{fontSize:10.5, color:"#5B5B53"}}>Bail BX-{tenant.id.replace("T-","")} · jusqu'au {tenant.leaseEnd}</div>}
            </div>
          </div>

          {/* Body */}
          <div style={{background:"#F2EFEA", borderRadius:4, padding:"14px 16px", marginBottom:14}}>
            <div style={{fontSize:11.5, color:"#1A1A1A", lineHeight:1.55}}>
              Le soussigné, agissant en qualité d'agence de gestion locative, déclare avoir reçu de
              <b> {q.tenant !== "—" ? q.tenant : "—"}</b> la somme de
              <b style={{color: q.status === "réglée" ? "#0F7A4C" : "#9A2A2A"}}> {fmtFCFA(q.amount)}</b> au titre du loyer du mois de <b>{q.period.toLowerCase()}</b>{q.status === "réglée" && q.paid !== "—" ? <>, payée le <b>{q.paid}</b> par <b>{q.method}</b></> : ", non encore réglée à ce jour"}.
            </div>
          </div>

          {/* Line items */}
          <table style={{width:"100%", borderCollapse:"collapse", fontSize:11, marginBottom:14}}>
            <thead>
              <tr style={{textAlign:"left", color:"#5B5B53", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, fontSize:9.5}}>
                <th style={{padding:"6px 4px", borderBottom:"1px solid #E4E4DE"}}>Désignation</th>
                <th style={{padding:"6px 4px", borderBottom:"1px solid #E4E4DE", textAlign:"right"}}>Montant</th>
              </tr>
            </thead>
            <tbody style={{fontFamily:"var(--font-mono)"}}>
              <tr>
                <td style={{padding:"7px 4px", borderBottom:"1px solid #EEEEE6", fontFamily:"var(--font-sans)"}}>Loyer principal · {q.period}</td>
                <td style={{padding:"7px 4px", borderBottom:"1px solid #EEEEE6", textAlign:"right"}}>{q.amount.toLocaleString("fr-FR")}</td>
              </tr>
              <tr>
                <td style={{padding:"7px 4px", borderBottom:"1px solid #EEEEE6", fontFamily:"var(--font-sans)", color:"#7B7B73"}}>Charges locatives</td>
                <td style={{padding:"7px 4px", borderBottom:"1px solid #EEEEE6", textAlign:"right", color:"#7B7B73"}}>0</td>
              </tr>
              <tr style={{fontWeight:600, fontSize:13}}>
                <td style={{padding:"10px 4px", fontFamily:"var(--font-sans)"}}>Total</td>
                <td style={{padding:"10px 4px", textAlign:"right", color: q.status === "réglée" ? "#0F7A4C" : "#1A1A1A"}}>{q.amount.toLocaleString("fr-FR")} FCFA</td>
              </tr>
            </tbody>
          </table>

          {/* Footer */}
          <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:14, paddingTop:10, borderTop:"1px dashed #E4E4DE", fontSize:10.5, color:"#5B5B53"}}>
            <div>
              <div style={{textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, fontSize:9, marginBottom:4, color:"#7B7B73"}}>Mode de paiement</div>
              <div>{q.method === "—" ? "—" : <b style={{color:"#1A1A1A"}}>{q.method}</b>}</div>
              {q.status === "réglée" && q.paid !== "—" && <div>Réglée le <b style={{color:"#1A1A1A"}}>{q.paid}</b></div>}
            </div>
            <div style={{textAlign:"right"}}>
              <div style={{textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, fontSize:9, marginBottom:4, color:"#7B7B73"}}>Émise par</div>
              <div>Souleymane Ndoye</div>
              <div className="mono" style={{fontSize:9.5}}>souleymane@ba-immo.sn</div>
            </div>
          </div>
        </div>
      </div>

      {/* Actions row */}
      <div style={{display:"flex", alignItems:"center", gap:6, padding:"12px 22px 16px", justifyContent:"flex-end", borderTop:"1px solid var(--d-line)"}}>
        {tenant && <button className="btn btn-ghost" onClick={()=>onOpenTenant && onOpenTenant(tenant)}>Voir locataire →</button>}
        <button className="btn btn-secondary">Email</button>
        <button className="btn btn-secondary" style={{background:"rgba(37,211,102,0.10)", color:"#3FB583", borderColor:"rgba(63,181,131,0.30)"}}>WhatsApp</button>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   BATCH GENERATION MODAL
   ───────────────────────────────────────────────────────── */
function BatchGenerationModal({ onClose }) {
  const [period, setPeriod] = React.useState("mai-26");
  const [scope, setScope] = React.useState("tous");
  const [autoSend, setAutoSend] = React.useState(true);
  const periodLabel = { "mai-26":"Mai 2026", "avr-26":"Avril 2026", "custom":"Mai 2026" }[period];
  const activeTenants = (MOCK.tenants || []).filter(t => t.id && (t.rent || 0) > 0);
  const alreadyQ = new Set(QUITTANCES.filter(q => q.period === periodLabel).map(q => q.tenantId));
  const eligible = activeTenants.filter(t => !alreadyQ.has(t.id));
  const total = eligible.length;
  const skipped = activeTenants.length - eligible.length;

  function doGenerate() {
    const moisShort = qMonthShort(periodLabel);
    eligible.forEach(t => {
      upsertQuittance({
        id: nextQuittanceId(periodLabel),
        tenant: t.name, tenantId: t.id,
        property: `${t.property}${t.unit && t.unit !== "—" ? " · " + t.unit : ""}`,
        amount: t.rent, charges: 0, method: "—",
        period: periodLabel, issued: `01 ${moisShort}`, paid: "—", status: "en attente",
      });
    });
    onClose();
  }
  return (
    <div onClick={onClose} style={{
      position:"fixed", inset:0, background:"rgba(0,0,0,0.55)", zIndex:300,
      display:"flex", alignItems:"center", justifyContent:"center"
    }}>
      <div onClick={e=>e.stopPropagation()} style={{
        background:"var(--d-panel)", border:"1px solid var(--d-line)", borderRadius:10,
        width:520, color:"var(--d-ink)", fontFamily:"var(--font-sans)"
      }}>
        <div style={{padding:"16px 20px", borderBottom:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div>
            <div style={{fontSize:14, fontWeight:600}}>Générer les quittances du mois</div>
            <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:2}}>Une quittance par bail actif · {periodLabel}</div>
          </div>
          <button onClick={onClose} style={{background:"none", border:"none", color:"var(--d-ink-3)", fontSize:20, cursor:"pointer"}}>×</button>
        </div>
        <div style={{padding:"20px 20px 8px"}}>
          <div style={{marginBottom:14}}>
            <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Période</div>
            <div className="seg" style={{display:"flex", width:"100%"}}>
              {[["mai-26","Mai 2026"],["avr-26","Avril 2026"],["custom","Personnalisé…"]].map(([k,l])=>(
                <button key={k} className={"seg-btn"+(period===k?" active":"")} onClick={()=>setPeriod(k)} style={{flex:1}}>{l}</button>
              ))}
            </div>
          </div>
          <div style={{marginBottom:14}}>
            <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Périmètre</div>
            <div style={{display:"flex", flexDirection:"column", gap:6}}>
              {[
                ["tous","Tous les baux actifs",`${eligible.length} à générer`],
                ["occupés","Lots occupés uniquement",`${eligible.length} à générer`],
                ["proprio","Filtrer par propriétaire…","À configurer"],
              ].map(([k,l,h])=>(
                <label key={k} style={{display:"flex", alignItems:"center", gap:10, padding:"10px 12px", border:`1px solid ${scope===k?"var(--blue-d)":"var(--d-line)"}`, borderRadius:5, cursor:"pointer", background: scope===k?"rgba(97,146,255,0.06)":"transparent"}}>
                  <input type="radio" name="scope" checked={scope===k} onChange={()=>setScope(k)} style={{accentColor:"var(--blue-d)"}}/>
                  <div style={{flex:1}}>
                    <div style={{fontSize:12.5, color:"var(--d-ink)"}}>{l}</div>
                    <div style={{fontSize:11, color:"var(--d-ink-3)"}}>{h}</div>
                  </div>
                </label>
              ))}
            </div>
          </div>
          {skipped > 0 && (
            <div style={{marginBottom:14, padding:"10px 12px", borderRadius:5, fontSize:11.5, lineHeight:1.5, border:"1px solid rgba(63,181,131,0.3)", background:"rgba(63,181,131,0.06)", color:"var(--d-ink-3)"}}>
              <b style={{color:"var(--pos-d)"}}>{skipped}</b> bail{skipped>1?"s":""} {skipped>1?"ont":"a"} déjà une quittance pour {periodLabel} — ignoré{skipped>1?"s":""} pour éviter les doublons.
            </div>
          )}
          <div style={{marginBottom:14}}>
            <label style={{display:"flex", alignItems:"center", gap:10, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:5, cursor:"pointer"}}>
              <input type="checkbox" checked={autoSend} onChange={()=>setAutoSend(!autoSend)} style={{accentColor:"var(--blue-d)"}}/>
              <div style={{flex:1, fontSize:12.5, color:"var(--d-ink)"}}>Envoyer par email + WhatsApp à la génération</div>
            </label>
          </div>
        </div>
        <div style={{padding:"14px 20px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>Récapitulatif : <span style={{color:"var(--d-ink)", fontWeight:500}}>{total} quittance{total>1?"s":""}</span> · envoi {autoSend ? "automatique" : "manuel"}</div>
          <div style={{display:"flex", gap:8}}>
            <button className="btn btn-ghost" onClick={onClose}>Annuler</button>
            <button className="btn btn-primary" disabled={total===0} style={{opacity: total===0?0.5:1, cursor: total===0?"not-allowed":"pointer"}} onClick={doGenerate}>Générer · {total}</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   NEW QUITTANCE MODAL — création manuelle d'une quittance
   ───────────────────────────────────────────────────────── */
function NewQuittanceModal({ onClose, onSave }) {
  const tenants = (MOCK.tenants || []).filter(t => t.id);
  const [tenantId, setTenantId] = React.useState("");
  const [period, setPeriod] = React.useState("Mai 2026");
  const [rent, setRent] = React.useState("");
  const [charges, setCharges] = React.useState("");
  const [status, setStatus] = React.useState("en attente"); // en attente | réglée
  const [method, setMethod] = React.useState("Wave");
  const [payDay, setPayDay] = React.useState("");
  const [reason, setReason] = React.useState("courante");
  const [moveInDay, setMoveInDay] = React.useState("");

  const tenant = tenants.find(t => t.id === tenantId);

  // Auto-fill rent + property when tenant changes
  React.useEffect(() => {
    if (tenant) setRent(String(tenant.rent || ""));
  }, [tenantId]);

  const fullRent = Number(String(rent).replace(/[^0-9]/g,"")) || 0;
  const [py, pm] = qPeriodKey(period).split("-").map(Number);
  const daysInMonth = new Date(py, pm, 0).getDate();
  const proRated = (reason === "entree" && moveInDay) ? Math.round(fullRent * (daysInMonth - Number(moveInDay) + 1) / daysInMonth) : fullRent;
  const rentNum = proRated;
  const chargesNum = Number(String(charges).replace(/[^0-9]/g,"")) || 0;
  const total = rentNum + chargesNum;
  const moisShort = qMonthShort(period);

  const errors = {};
  if (!tenantId) errors.tenant = "Choisissez un locataire";
  if (rentNum <= 0) errors.rent = "Montant du loyer requis";
  if (status === "réglée" && !payDay) errors.pay = "Date de règlement requise";
  if (reason === "entree" && !moveInDay) errors.prorata = "Jour d'entrée requis pour le prorata";
  const canSave = Object.keys(errors).length === 0;

  function handleSave() {
    if (!canSave) return;
    const property = tenant ? `${tenant.property}${tenant.unit && tenant.unit !== "—" ? " · "+tenant.unit : ""}` : "—";
    const payload = {
      id: nextQuittanceId(period),
      tenant: tenant ? tenant.name : "—",
      tenantId: tenantId,
      property,
      amount: total,
      charges: chargesNum,
      method: status === "réglée" ? method : "—",
      period,
      issued: `01 ${moisShort}`,
      paid: status === "réglée" ? `${String(payDay).padStart(2,"0")} ${moisShort}` : "—",
      status,
      manual: true,
      reason,
    };
    onSave(payload);
  }

  React.useEffect(() => {
    const onKey = e => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  return (
    <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:300, backdropFilter:"blur(3px)", display:"flex", alignItems:"flex-start", justifyContent:"center", padding:"6vh 16px"}}>
      <div onClick={e=>e.stopPropagation()} role="dialog" style={{width:"min(880px, 96vw)", maxHeight:"86vh", background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10, display:"flex", flexDirection:"column", overflow:"hidden", boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)"}}>
        <header style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center", background:"var(--d-panel)"}}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>Nouvelle quittance</div>
            <div style={{fontSize:16, fontWeight:600}}>{tenant ? tenant.name : "Quittance manuelle"} <span style={{color:"var(--d-ink-3)", fontWeight:400, fontSize:13}}>· {period}</span></div>
          </div>
          <button onClick={onClose} style={{width:32, height:32, borderRadius:6, color:"var(--d-ink-3)", fontSize:18, background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer"}}>×</button>
        </header>

        <div style={{display:"grid", gridTemplateColumns:"1fr 300px", flex:1, minHeight:0}}>
          {/* Form */}
          <div style={{overflow:"auto", padding:"20px 24px"}}>
            <Banner tone="info" title="Quittance manuelle = cas exceptionnel">
              En temps normal, les quittances sont générées <b>automatiquement en lot au 1er du mois</b> pour tous les baux actifs. N'utilisez la création manuelle que pour une entrée en cours de mois (prorata), une régularisation ou un duplicata.
            </Banner>
            <FormSection title="Locataire & période" subtitle="Le bien et le loyer sont pré-remplis depuis la fiche du locataire.">
              <Field label="Locataire" required>
                <Select value={tenantId} onChange={setTenantId} placeholder="— Choisir un locataire —"
                  options={tenants.map(t => ({ value:t.id, label:`${t.name} · ${t.property || "sans bien"}${t.unit && t.unit!=="—" ? " · "+t.unit : ""}` }))}/>
              </Field>
              <FieldRow>
                <Field label="Période" required>
                  <Select value={period} onChange={setPeriod} options={["Mai 2026","Avril 2026","Mars 2026","Juin 2026"]}/>
                </Field>
                <Field label="Motif" hint="Pourquoi une quittance hors cycle ?">
                  <Select value={reason} onChange={setReason} options={[
                    {value:"courante", label:"Courante (hors cycle auto)"},
                    {value:"entree", label:"Entrée en cours de mois (prorata)"},
                    {value:"regularisation", label:"Régularisation / complément"},
                    {value:"duplicata", label:"Duplicata"},
                  ]}/>
                </Field>
              </FieldRow>
              {reason === "entree" && (
                <Field label="Jour d'entrée dans le mois" required hint={`Loyer calculé au prorata sur ${daysInMonth} jours`}>
                  <Input value={moveInDay} onChange={e=>setMoveInDay(e.target.value.replace(/[^0-9]/g,"").slice(0,2))} placeholder="15" suffix={moisShort} style={{fontFamily:"var(--font-mono)", textAlign:"right", maxWidth:160}}/>
                </Field>
              )}
            </FormSection>

            <FormSection title="Montants" subtitle="Loyer hors charges, charges récupérables à part.">
              <FieldRow columns="1fr 1fr 1fr">
                <Field label={reason==="entree" ? "Loyer mensuel plein" : "Loyer"} required><MoneyInput value={rent} onChange={e=>setRent(e.target.value.replace(/[^0-9]/g,""))}/></Field>
                <Field label="Charges" optional><MoneyInput value={charges} onChange={e=>setCharges(e.target.value.replace(/[^0-9]/g,""))}/></Field>
                <Field label="Total quittance"><Input value={total.toLocaleString("fr-FR")+" FCFA"} readOnly style={{fontFamily:"var(--font-mono)", textAlign:"right", color:"var(--pos-d)", background:"var(--d-elev)", fontWeight:600}}/></Field>
              </FieldRow>
              {reason==="entree" && moveInDay && (
                <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:-4}}>
                  Prorata : {fullRent.toLocaleString("fr-FR")} × {daysInMonth - Number(moveInDay) + 1}/{daysInMonth} j = <b style={{color:"var(--d-ink)"}}>{proRated.toLocaleString("fr-FR")} FCFA</b>
                </div>
              )}
            </FormSection>

            <FormSection title="Règlement" subtitle="Quittance en attente de paiement, ou déjà réglée.">
              <Field label="Statut">
                <Segmented value={status} onChange={setStatus} options={[{value:"en attente",label:"En attente"},{value:"réglée",label:"Déjà réglée"}]}/>
              </Field>
              {status === "réglée" && (
                <FieldRow>
                  <Field label="Mode de paiement"><Select value={method} onChange={setMethod} options={["Wave","Orange Money","Virement","Espèces"]}/></Field>
                  <Field label="Jour de règlement" required hint={`Mois de ${period.toLowerCase()}`}><Input value={payDay} onChange={e=>setPayDay(e.target.value.replace(/[^0-9]/g,"").slice(0,2))} placeholder="18" suffix={moisShort} style={{fontFamily:"var(--font-mono)", textAlign:"right"}}/></Field>
                </FieldRow>
              )}
            </FormSection>
          </div>

          {/* Mini live preview */}
          <div style={{borderLeft:"1px solid var(--d-line)", background:"var(--d-sunken)", padding:"20px 18px", overflow:"auto"}}>
            <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:10}}>Aperçu</div>
            <div style={{background:"#FAFAF7", color:"#1A1A1A", borderRadius:6, padding:"16px", border:"1px solid #E4E4DE", fontSize:11}}>
              <div style={{fontSize:9, color:"#7B7B73", letterSpacing:"0.1em", textTransform:"uppercase", fontWeight:600}}>Quittance de loyer</div>
              <div style={{fontSize:13, fontWeight:600, marginTop:3}}>{period}</div>
              <div className="mono" style={{fontSize:9.5, color:"#7B7B73", marginTop:2}}>{tenantId ? nextQuittanceId(period) : "Q-…"}</div>
              <div style={{borderTop:"1px solid #E4E4DE", margin:"10px 0", paddingTop:10}}>
                <div style={{fontSize:9, color:"#7B7B73", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600}}>Locataire</div>
                <div style={{fontWeight:600, marginTop:2}}>{tenant ? tenant.name : "—"}</div>
                <div style={{color:"#5B5B53", marginTop:1}}>{tenant ? `${tenant.property}${tenant.unit && tenant.unit!=="—" ? " · "+tenant.unit : ""}` : "—"}</div>
              </div>
              <table style={{width:"100%", borderCollapse:"collapse", marginTop:6}}>
                <tbody style={{fontFamily:"var(--font-mono)"}}>
                  <tr><td style={{padding:"4px 0", fontFamily:"var(--font-sans)"}}>Loyer</td><td style={{textAlign:"right"}}>{rentNum.toLocaleString("fr-FR")}</td></tr>
                  <tr><td style={{padding:"4px 0", fontFamily:"var(--font-sans)", color:"#7B7B73"}}>Charges</td><td style={{textAlign:"right", color:"#7B7B73"}}>{chargesNum.toLocaleString("fr-FR")}</td></tr>
                  <tr style={{fontWeight:700, fontSize:12}}><td style={{padding:"7px 0", fontFamily:"var(--font-sans)", borderTop:"1px solid #E4E4DE"}}>Total</td><td style={{textAlign:"right", borderTop:"1px solid #E4E4DE", color: status==="réglée"?"#0F7A4C":"#1A1A1A"}}>{total.toLocaleString("fr-FR")}</td></tr>
                </tbody>
              </table>
              <div style={{marginTop:8, fontSize:9.5, color: status==="réglée"?"#0F7A4C":"#C2620E", fontWeight:600}}>
                {status==="réglée" ? `Réglée${payDay ? ` le ${payDay} ${moisShort}` : ""} · ${method}` : "En attente de paiement"}
              </div>
            </div>
          </div>
        </div>

        <footer style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-panel)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>
            {canSave ? <span style={{color:"var(--pos-d)"}}>● Prêt à émettre</span> : <span style={{color:"var(--orange)"}}>! {Object.values(errors)[0]}</span>}
          </div>
          <div style={{display:"flex", gap:8}}>
            <button className="btn btn-ghost" onClick={onClose}>Annuler</button>
            <button className="btn btn-primary" disabled={!canSave} style={{opacity:canSave?1:0.5, cursor:canSave?"pointer":"not-allowed"}} onClick={handleSave}>Émettre la quittance</button>
          </div>
        </footer>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   MARK PAID MODAL — enregistre le règlement d'une quittance
   ───────────────────────────────────────────────────────── */
function MarkPaidModal({ q, onClose, onConfirm }) {
  const [method, setMethod] = React.useState("Wave");
  const [day, setDay] = React.useState("29");
  const moisShort = qMonthShort(q.period);
  React.useEffect(() => {
    const onKey = e => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);
  const valid = !!day;
  return (
    <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:300, backdropFilter:"blur(3px)", display:"flex", alignItems:"flex-start", justifyContent:"center", padding:"8vh 16px"}}>
      <div onClick={e=>e.stopPropagation()} role="dialog" style={{width:"min(480px, 96vw)", background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10, overflow:"hidden", boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)"}}>
        <header style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center", background:"var(--d-panel)"}}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>Enregistrer le règlement</div>
            <div style={{fontSize:15, fontWeight:600}}>{q.tenant} <span style={{color:"var(--d-ink-3)", fontWeight:400, fontSize:12.5}}>· {fmtFCFA(q.amount)}</span></div>
          </div>
          <button onClick={onClose} style={{width:32, height:32, borderRadius:6, color:"var(--d-ink-3)", fontSize:18, background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer"}}>×</button>
        </header>
        <div style={{padding:"18px 22px"}}>
          <div style={{display:"flex", justifyContent:"space-between", fontSize:11.5, color:"var(--d-ink-3)", marginBottom:16, padding:"8px 12px", background:"var(--d-sunken)", borderRadius:6, border:"1px solid var(--d-line)"}}>
            <span className="mono">{q.id}</span><span>{q.property} · {q.period}</span>
          </div>
          <FieldRow>
            <Field label="Mode de paiement"><Select value={method} onChange={setMethod} options={["Wave","Orange Money","Virement","Espèces"]}/></Field>
            <Field label="Jour de règlement" required hint={`Mois de ${q.period.toLowerCase()}`}><Input value={day} onChange={e=>setDay(e.target.value.replace(/[^0-9]/g,"").slice(0,2))} suffix={moisShort} style={{fontFamily:"var(--font-mono)", textAlign:"right"}}/></Field>
          </FieldRow>
          {q.tenantId && typeof OVERDUE !== "undefined" && OVERDUE.some(o=>o.tenantId===q.tenantId) && (
            <div style={{marginTop:4, padding:"10px 12px", borderRadius:6, fontSize:11.5, lineHeight:1.5, border:"1px solid rgba(63,181,131,0.3)", background:"rgba(63,181,131,0.06)", color:"var(--d-ink-3)"}}>
              Ce locataire a un <b style={{color:"var(--orange)"}}>dossier impayé</b> ouvert : il sera automatiquement <b style={{color:"var(--pos-d)"}}>soldé</b> à l'enregistrement.
            </div>
          )}
        </div>
        <footer style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-panel)", display:"flex", justifyContent:"flex-end", gap:8}}>
          <button className="btn btn-ghost" onClick={onClose}>Annuler</button>
          <button className="btn btn-primary" disabled={!valid} style={{background:"#3FB583", borderColor:"#3FB583", opacity:valid?1:0.5, cursor:valid?"pointer":"not-allowed"}} onClick={()=>onConfirm({method, day})}>Confirmer le paiement</button>
        </footer>
      </div>
    </div>
  );
}

Object.assign(window, { QuittancesPage, NewQuittanceModal, BatchGenerationModal, MarkPaidModal, upsertQuittance, useQuittances, nextQuittanceId });

/* ╚══ end pages-quittances.jsx ══╝ */

/* ╔══ pages-overdue.jsx ══╗ */
/* global React, MOCK, fmtFCFA, fmtCompact, Spark */

/* ─────────────────────────────────────────────────────────
   IMPAYÉS — overdue page
   Aging buckets · workflow de recouvrement · relances
   ───────────────────────────────────────────────────────── */

// Extended overdue dossiers — go beyond MOCK to make this page rich
const OVERDUE = [
  // From MOCK
  { id:"D-1041", tenant:"Cheikh T. Diagne", tenantId:"T-2058", property:"Imm. Point E · B-101", balance: 280_000, monthsOpen: 1, daysLate: 7,  level:"L1 · Amiable", lastContact:"15 avr · WhatsApp", nextAction:"Relance écrite", responsible:"S. Ndoye", riskScore: 22 },
  { id:"D-1038", tenant:"Bineta Diouf",     tenantId:"T-2102", property:"Rés. Plateau · 5",     balance: 225_000, monthsOpen: 1, daysLate: 3,  level:"L1 · Amiable", lastContact:"16 avr · SMS",      nextAction:"Appel téléphonique", responsible:"S. Ndoye", riskScore: 14 },
  { id:"D-1027", tenant:"Moussa Fall",      tenantId:"T-2077", property:"Rés. Teranga · C-304", balance: 440_000, monthsOpen: 2, daysLate: 42, level:"L2 · Recouvrement", lastContact:"12 avr · Lettre", nextAction:"Mise en demeure", responsible:"A. Diallo", riskScore: 71 },
  // Synthetic — give the page more density
  { id:"D-1019", tenant:"Bocar Sow",        tenantId:null,    property:"Imm. Fann Hock · 2",   balance: 690_000, monthsOpen: 3, daysLate: 78, level:"L3 · Pré-contentieux", lastContact:"02 avr · Huissier", nextAction:"Commandement de payer", responsible:"A. Diallo", riskScore: 88 },
  { id:"D-1003", tenant:"Aliou Diop",       tenantId:null,    property:"Rés. Plateau · 9",     balance:1_150_000, monthsOpen: 4, daysLate: 110,level:"L4 · Contentieux", lastContact:"08 avr · Avocat",  nextAction:"Audience tribunal", responsible:"Cabinet Sarr", riskScore: 95 },
  { id:"D-0994", tenant:"K. M. Sarr",       tenantId:null,    property:"Imm. Point E · A-204", balance: 195_000, monthsOpen: 1, daysLate: 12, level:"L1 · Amiable", lastContact:"10 avr · WhatsApp", nextAction:"Relance écrite", responsible:"S. Ndoye", riskScore: 28 },
  { id:"D-0981", tenant:"Y. C. Mbaye",      tenantId:null,    property:"Rés. Ouakam · B-102",  balance: 520_000, monthsOpen: 2, daysLate: 51, level:"L2 · Recouvrement", lastContact:"06 avr · Lettre R.AR", nextAction:"Mise en demeure", responsible:"A. Diallo", riskScore: 64 },
];

/* ─── Live activity log (seed + relances émises en session) ─── */
const RELANCE_LOG = [
  { tag:"WAVE",     c:"#1DC3F8", txt:"Paiement reçu · 280 000 FCFA · M. Diop (D-0987) · dossier soldé", t:"il y a 2 h" },
  { tag:"WHATSAPP", c:"#25D366", txt:"Relance amiable envoyée · B. Diouf · J+3", t:"il y a 14 h" },
  { tag:"LETTRE",   c:"var(--d-ink-3)", txt:"Mise en demeure expédiée · M. Fall · accusé de réception attendu", t:"hier" },
  { tag:"HUISSIER", c:"var(--orange)", txt:"Commandement de payer signifié · B. Sow", t:"16 avr" },
  { tag:"AUDIENCE", c:"var(--neg-d)", txt:"Audience programmée · A. Diop · 22 avr · TGI Dakar", t:"15 avr" },
];

const REL_CHANNELS = {
  whatsapp: { tag:"WHATSAPP", c:"#25D366",        label:"WhatsApp",    contact:"WhatsApp" },
  sms:      { tag:"SMS",      c:"var(--d-ink-3)", label:"SMS",         contact:"SMS" },
  email:    { tag:"EMAIL",    c:"var(--blue-d)",  label:"Email",       contact:"Email" },
  lettre:   { tag:"LETTRE",   c:"var(--orange)",  label:"Lettre R.AR", contact:"Lettre R.AR" },
};

const _ovListeners = new Set();
function _notifyOv(){ _ovListeners.forEach(f=>f()); }
function useOverdueStore(){
  const [,tick] = React.useReducer(x=>x+1,0);
  React.useEffect(()=>{ _ovListeners.add(tick); return ()=>_ovListeners.delete(tick); },[]);
  return OVERDUE;
}
function recordRelance(dossierId, channelKey, dateLabel) {
  const ch = REL_CHANNELS[channelKey] || REL_CHANNELS.whatsapp;
  const d = OVERDUE.find(o => o.id === dossierId);
  if (d) {
    d.lastContact = `${dateLabel} · ${ch.contact}`;
    d.relances = (d.relances || 0) + 1;
    RELANCE_LOG.unshift({ tag: ch.tag, c: ch.c, txt: `Relance ${ch.label} envoyée · ${d.tenant} · ${d.id}`, t:"à l'instant" });
  }
  _notifyOv();
}
/* Solde le dossier impayé d'un locataire (ex. quand sa quittance est payée). */
function settleOverdueByTenant(tenantId, amount) {
  const i = OVERDUE.findIndex(o => o.tenantId && o.tenantId === tenantId);
  if (i < 0) { _notifyOv(); return false; }
  const d = OVERDUE[i];
  OVERDUE.splice(i, 1);
  RELANCE_LOG.unshift({ tag:"WAVE", c:"#1DC3F8", txt:`Paiement reçu · ${fmtFCFA(amount || d.balance)} · ${d.tenant} (${d.id}) · dossier soldé`, t:"à l'instant" });
  _notifyOv();
  return true;
}

function OverduePage({ onOpenTenant }) {
  useOverdueStore(); // re-render when a relance is recorded
  const [bucket, setBucket] = React.useState("all");
  const [levelFilter, setLevelFilter] = React.useState("all");
  const [selectedId, setSelectedId] = React.useState(OVERDUE[2].id); // Moussa Fall
  const [showRelance, setShowRelance] = React.useState(false);
  const [relanceTarget, setRelanceTarget] = React.useState(null);
  const [q, setQ] = React.useState("");

  // Aging buckets
  const buckets = [
    { k:"1-7",   label:"1 à 7 j",   min: 1,  max: 7,   c:"var(--warn-d)" },
    { k:"8-30",  label:"8 à 30 j",  min: 8,  max: 30,  c:"#F4B240" },
    { k:"31-60", label:"31 à 60 j", min: 31, max: 60,  c:"#F2933E" },
    { k:"61-90", label:"61 à 90 j", min: 61, max: 90,  c:"#F2723E" },
    { k:"90+",   label:"90 j +",    min: 91, max: 9999,c:"var(--orange)" },
  ];
  const counts = buckets.map(b => {
    const items = OVERDUE.filter(o => o.daysLate >= b.min && o.daysLate <= b.max);
    return { ...b, count: items.length, total: items.reduce((s,o)=>s+o.balance,0) };
  });
  const totalAmount = OVERDUE.reduce((s,o)=>s+o.balance,0);

  const filtered = OVERDUE.filter(o => {
    if (bucket !== "all") {
      const b = buckets.find(b => b.k === bucket);
      if (b && (o.daysLate < b.min || o.daysLate > b.max)) return false;
    }
    if (levelFilter !== "all" && !o.level.startsWith(levelFilter)) return false;
    if (q && !`${o.tenant} ${o.property} ${o.id}`.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });
  const selected = OVERDUE.find(o => o.id === selectedId) || filtered[0] || OVERDUE[0];

  // Counters
  const inProc = OVERDUE.filter(o => o.level.startsWith("L3") || o.level.startsWith("L4")).length;
  const recoveredM = 1_280_000; // recovered this month

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Impayés</h1>
          <div className="page-sub">{OVERDUE.length} dossiers · {fmtCompact(totalAmount)} FCFA à recouvrer · suivi quotidien</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter</button>
          <button className="btn btn-secondary" onClick={()=>setShowRelance(true)}>Relance groupée</button>
          <button className="btn btn-primary">Engager une procédure</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi alert">
          <div className="kpi-label"><span>Total impayés</span><span style={{color:"var(--orange)", fontWeight:600}}>Action requise</span></div>
          <div className="kpi-value num" style={{color:"var(--orange)"}}>{(totalAmount/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>M FCFA</span></div>
          <div className="kpi-meta"><span className="delta neg">▲ 2 dossiers</span><span>{OVERDUE.length} locataires · 5 biens</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Retard moyen</span></div>
          <div className="kpi-value num">{Math.round(OVERDUE.reduce((s,o)=>s+o.daysLate,0)/OVERDUE.length)}<span className="kpi-unit">jours</span></div>
          <div className="kpi-meta"><span className="delta neg">▲ 4 j</span><span>vs mars · cible &lt; 25 j</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>En procédure</span></div>
          <div className="kpi-value num">{inProc}<span className="kpi-unit">dossiers</span></div>
          <div className="kpi-meta"><span style={{color:"var(--orange)", fontWeight:600}}>L3 · L4</span><span>1 audience cette semaine</span></div>
        </div>
        <div className="kpi primary">
          <div className="kpi-label"><span>Recouvré · avril</span></div>
          <div className="kpi-value num" style={{color:"var(--pos-d)"}}>{(recoveredM/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>M FCFA</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 38%</span><span>vs mars · 4 dossiers soldés</span></div>
          <div className="kpi-spark"><Spark values={[20,18,22,25,30,28,32,30,34,32,36,38]} color="#3FB583"/></div>
        </div>
      </div>

      {/* Aging breakdown */}
      <div className="panel" style={{marginBottom:16}}>
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Échelle d'ancienneté</h3>
            <div className="panel-sub">Répartition des soldes par retard · cliquez pour filtrer</div>
          </div>
          <button className={"filter-chip" + (bucket === "all" ? " active":"")} onClick={()=>setBucket("all")}>Voir tout</button>
        </div>
        <div style={{display:"grid", gridTemplateColumns:"repeat(5, 1fr)", gap:0}}>
          {counts.map((b, i) => {
            const isActive = bucket === b.k;
            const pct = totalAmount ? Math.round((b.total/totalAmount)*100) : 0;
            return (
              <div
                key={b.k}
                onClick={()=>setBucket(isActive ? "all" : b.k)}
                style={{
                  padding:"18px 18px",
                  borderRight: i < 4 ? "1px solid var(--d-line-2)" : "none",
                  cursor:"pointer",
                  background: isActive ? "var(--d-elev)" : "transparent",
                  position:"relative",
                }}
              >
                {isActive && <span style={{position:"absolute", left:0, right:0, top:0, height:2, background:b.c}}/>}
                <div style={{fontSize:10.5, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, marginBottom:8}}>{b.label}</div>
                <div style={{fontSize:22, fontWeight:500, color: b.count ? "var(--d-ink)" : "var(--d-ink-4)", fontFamily:"var(--font-mono)", marginBottom:4, letterSpacing:"-0.02em"}}>{fmtCompact(b.total)}</div>
                <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>{b.count} {b.count > 1 ? "dossiers" : "dossier"} · {pct}%</div>
                <div style={{height:3, background:"var(--d-sunken)", borderRadius:1.5, marginTop:10, overflow:"hidden"}}>
                  <div style={{height:"100%", width:`${pct}%`, background:b.c, borderRadius:1.5}}/>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      <div className="grid-2">
        {/* Left: list */}
        <div className="panel">
          <div className="filter-bar" style={{borderTop:"none"}}>
            <div className="top-search" style={{width:260, background:"var(--d-panel)"}}>
              <span style={{color:"var(--d-ink-4)"}}>⌕</span>
              <input value={q} onChange={e=>setQ(e.target.value)} placeholder="Locataire, bien, ID…"/>
            </div>
            {[
              ["all","Tous",OVERDUE.length],
              ["L1","Amiable",OVERDUE.filter(o=>o.level.startsWith("L1")).length],
              ["L2","Recouvrement",OVERDUE.filter(o=>o.level.startsWith("L2")).length],
              ["L3","Pré-contentieux",OVERDUE.filter(o=>o.level.startsWith("L3")).length],
              ["L4","Contentieux",OVERDUE.filter(o=>o.level.startsWith("L4")).length],
            ].map(([k,l,n])=>(
              <button key={k} className={"filter-chip" + (levelFilter === k ? " active" : "")} onClick={()=>setLevelFilter(k)}>{l} <span className="k">{n}</span></button>
            ))}
            <div className="filter-spacer"/>
            <button className="filter-chip">Trier ▾</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:88}}>Réf.</th>
                  <th>Locataire</th>
                  <th>Bien</th>
                  <th className="right">Solde</th>
                  <th>Retard</th>
                  <th>Niveau</th>
                  <th style={{width:60}}></th>
                </tr>
              </thead>
              <tbody>
                {filtered.length === 0 && <tr><td colSpan={7} className="muted" style={{textAlign:"center", padding:28}}>Aucun dossier ne correspond.</td></tr>}
                {filtered.map(o => (
                  <tr key={o.id} onClick={()=>setSelectedId(o.id)} style={{background: o.id===selectedId? "var(--d-elev)":undefined}}>
                    <td className="mono muted" style={{fontSize:11.5}}>{o.id}</td>
                    <td style={{fontWeight:500}}>{o.tenant}</td>
                    <td className="muted" style={{fontSize:11.5}}>{o.property}</td>
                    <td className="right highlight-neg">{fmtFCFA(o.balance)}</td>
                    <td>
                      <span className="mono" style={{
                        fontSize:11, fontWeight:600,
                        color: o.daysLate > 60 ? "var(--orange)" : o.daysLate > 30 ? "#F2933E" : "var(--warn-d)",
                      }}>{o.daysLate} j</span>
                    </td>
                    <td>
                      <LevelChip level={o.level}/>
                    </td>
                    <td className="right">
                      <RiskDot score={o.riskScore}/>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>

        {/* Right: dossier detail */}
        <div className="stack">
          <DossierDetail dossier={selected} onOpenTenant={onOpenTenant} onRelance={()=>setRelanceTarget(selected)}/>
          <RecoveryWorkflow dossier={selected}/>
        </div>
      </div>

      {/* Templates & activité */}
      <div className="grid-2" style={{marginTop:16}}>
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Modèles de relance</h3>
              <div className="panel-sub">Préparés pour WhatsApp, SMS et email</div>
            </div>
            <button className="btn btn-ghost">+ Nouveau modèle</button>
          </div>
          <div>
            {[
              { name:"Relance amiable J+3", chan:"WhatsApp", desc:"Ton courtois, rappel du montant et lien Wave de paiement", used:42 },
              { name:"Relance ferme J+15", chan:"SMS + Email", desc:"Mention des pénalités, demande de régularisation sous 7 jours", used:18 },
              { name:"Mise en demeure J+30", chan:"Lettre R.AR", desc:"Format légal · génération automatique du PDF", used:6 },
              { name:"Commandement de payer", chan:"Huissier", desc:"Transmission au cabinet partenaire · 24 h", used:2 },
            ].map((t, i) => (
              <div key={i} style={{padding:"12px 18px", borderBottom: i<3?"1px solid var(--d-line-2)":"none", display:"flex", alignItems:"center", gap:14}}>
                <div style={{flex:1}}>
                  <div style={{fontSize:12.5, fontWeight:500, color:"var(--d-ink)", marginBottom:2}}>{t.name}</div>
                  <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>{t.desc}</div>
                </div>
                <div style={{textAlign:"right"}}>
                  <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600}}>{t.chan}</div>
                  <div className="mono" style={{fontSize:11, color:"var(--d-ink-3)", marginTop:2}}>{t.used} envois</div>
                </div>
                <button className="btn btn-ghost" style={{padding:"4px 9px", fontSize:11.5}}>Utiliser</button>
              </div>
            ))}
          </div>
        </div>

        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Activité récente</h3>
              <div className="panel-sub">Actions de recouvrement · 7 derniers jours</div>
            </div>
          </div>
          <div style={{padding:"4px 0"}}>
            {RELANCE_LOG.map((r, i) => (
              <div key={i} style={{display:"flex", gap:12, padding:"10px 18px", alignItems:"flex-start", borderBottom: i<RELANCE_LOG.length-1?"1px solid var(--d-line-2)":"none"}}>
                <span style={{fontSize:9.5, fontWeight:700, color:"#fff", background:r.c, padding:"2px 6px", borderRadius:3, letterSpacing:"0.04em", marginTop:1, minWidth:62, textAlign:"center"}}>{r.tag}</span>
                <div style={{flex:1, minWidth:0}}>
                  <div style={{fontSize:12, color:"var(--d-ink-2)"}}>{r.txt}</div>
                  <div className="muted mono" style={{fontSize:10.5, marginTop:2}}>{r.t}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {showRelance && <BulkRelanceModal onClose={()=>setShowRelance(false)} count={filtered.length}/>}
      {relanceTarget && <RelanceModal
        target={{ name: relanceTarget.tenant, amount: relanceTarget.balance, period: "le mois en cours", property: relanceTarget.property, tenantId: relanceTarget.tenantId, dossierId: relanceTarget.id }}
        onClose={()=>setRelanceTarget(null)}
        onSent={({channel})=>{ recordRelance(relanceTarget.id, channel, "29 mai"); setRelanceTarget(null); }}
      />}
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   COMPONENTS
   ───────────────────────────────────────────────────────── */

function LevelChip({ level }) {
  const L = level.startsWith("L1") ? "warn" : level.startsWith("L2") ? "warn" : level.startsWith("L3") ? "neg" : "neg";
  return <span className={`chip ${L}`} style={{fontSize:10.5}}>{level}</span>;
}

function RiskDot({ score }) {
  const c = score < 30 ? "var(--warn-d)" : score < 60 ? "#F2933E" : score < 85 ? "var(--orange)" : "var(--neg-d)";
  return (
    <span title={`Risque ${score}/100`} style={{display:"inline-flex", alignItems:"center", gap:5}}>
      <span style={{width:7, height:7, borderRadius:50, background:c, display:"inline-block"}}/>
      <span className="mono" style={{fontSize:10.5, color:"var(--d-ink-3)"}}>{score}</span>
    </span>
  );
}

function DossierDetail({ dossier, onOpenTenant, onRelance }) {
  const d = dossier;
  const tenant = MOCK.tenants.find(t => t.id === d.tenantId);
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Dossier · {d.id}</h3>
          <div className="panel-sub">{d.tenant} · {d.property}</div>
        </div>
        <div className="panel-head-right">
          <button className="btn btn-ghost" style={{padding:"5px 9px", fontSize:11.5}}>Notes</button>
          <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}} onClick={onRelance}>Relancer</button>
        </div>
      </div>
      <div style={{padding:"16px 18px", display:"grid", gridTemplateColumns:"1fr 1fr", gap:14, borderBottom:"1px solid var(--d-line-2)"}}>
        <div>
          <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600, marginBottom:4}}>Solde dû</div>
          <div className="mono" style={{fontSize:20, fontWeight:500, color:"var(--orange)", letterSpacing:"-0.02em"}}>{fmtFCFA(d.balance)}</div>
          <div style={{fontSize:11, color:"var(--d-ink-3)", marginTop:2}}>{d.monthsOpen} {d.monthsOpen > 1 ? "mois ouverts" : "mois ouvert"}</div>
        </div>
        <div>
          <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600, marginBottom:4}}>Retard</div>
          <div className="mono" style={{fontSize:20, fontWeight:500, color: d.daysLate > 60 ? "var(--orange)" : "var(--warn-d)", letterSpacing:"-0.02em"}}>{d.daysLate} j</div>
          <div style={{fontSize:11, color:"var(--d-ink-3)", marginTop:2}}>Score de risque <b style={{color:"var(--d-ink-2)"}}>{d.riskScore}/100</b></div>
        </div>
      </div>
      <div>
        <div className="info-row"><div className="info-k">Niveau</div><div className="info-v"><LevelChip level={d.level}/></div></div>
        <div className="info-row"><div className="info-k">Dernier contact</div><div className="info-v mono">{d.lastContact}</div></div>
        <div className="info-row"><div className="info-k">Prochaine action</div><div className="info-v"><b>{d.nextAction}</b></div></div>
        <div className="info-row"><div className="info-k">Responsable</div><div className="info-v">{d.responsible}</div></div>
        {tenant && <div className="info-row"><div className="info-k">Téléphone</div><div className="info-v mono">{tenant.phone}</div></div>}
        {tenant && <div className="info-row" style={{borderBottom:"none"}}>
          <div className="info-k">Locataire</div>
          <div className="info-v"><a style={{color:"var(--blue-d)", cursor:"pointer"}} onClick={()=>onOpenTenant && onOpenTenant(tenant)}>Ouvrir la fiche →</a></div>
        </div>}
      </div>
    </div>
  );
}

function RecoveryWorkflow({ dossier }) {
  const steps = [
    { k:"L1", label:"Amiable",        sub:"WhatsApp + SMS · J+3 à J+15" },
    { k:"L2", label:"Recouvrement",   sub:"Lettre R.AR · J+15 à J+30" },
    { k:"L3", label:"Pré-contentieux",sub:"Mise en demeure · J+30 à J+60" },
    { k:"L4", label:"Contentieux",    sub:"Huissier · audience" },
    { k:"L5", label:"Soldé",          sub:"Recouvrement total ou abandon" },
  ];
  const currentLevel = dossier.level.split(" ")[0];
  const currentIdx = steps.findIndex(s => s.k === currentLevel);
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Workflow de recouvrement</h3>
          <div className="panel-sub">Procédure conforme OHADA</div>
        </div>
      </div>
      <div style={{padding:"18px 22px"}}>
        {steps.map((s, i) => {
          const done = i < currentIdx;
          const active = i === currentIdx;
          const next = i === currentIdx + 1;
          const color = done ? "var(--pos-d)" : active ? "var(--orange)" : "var(--d-ink-4)";
          return (
            <div key={s.k} style={{display:"flex", gap:12, position:"relative", paddingBottom: i < steps.length-1 ? 18 : 0}}>
              {/* Vertical line */}
              {i < steps.length - 1 && (
                <span style={{position:"absolute", left:11, top:24, bottom:0, width:1, background: done ? "var(--pos-d)" : "var(--d-line)"}}/>
              )}
              <div style={{width:22, height:22, borderRadius:50, border:`1.5px solid ${color}`, background: active ? "var(--orange)" : done ? "var(--pos-d)" : "var(--d-panel)", color: done || active ? "#fff" : color, display:"grid", placeItems:"center", fontSize:10.5, fontWeight:700, flexShrink:0, zIndex:1, fontFamily:"var(--font-mono)"}}>
                {done ? "✓" : i+1}
              </div>
              <div style={{flex:1, minWidth:0}}>
                <div style={{fontSize:12.5, fontWeight:500, color: done ? "var(--d-ink-2)" : active ? "var(--d-ink)" : "var(--d-ink-3)"}}>
                  {s.label}
                  {active && <span style={{marginLeft:8, fontSize:9.5, fontWeight:600, color:"var(--orange)", background:"rgba(249,115,22,0.10)", padding:"1px 6px", borderRadius:2, letterSpacing:"0.04em"}}>EN COURS</span>}
                  {next && <span style={{marginLeft:8, fontSize:9.5, fontWeight:600, color:"var(--d-ink-3)", background:"var(--d-elev)", padding:"1px 6px", borderRadius:2, letterSpacing:"0.04em"}}>SUIVANT</span>}
                </div>
                <div style={{fontSize:11, color:"var(--d-ink-3)", marginTop:2}}>{s.sub}</div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   BULK RELANCE MODAL
   ───────────────────────────────────────────────────────── */
function BulkRelanceModal({ onClose, count, onConfirm }) {
  const [channel, setChannel] = React.useState("whatsapp");
  const [template, setTemplate] = React.useState("amiable");
  return (
    <div onClick={onClose} style={{
      position:"fixed", inset:0, background:"rgba(0,0,0,0.55)", zIndex:300,
      display:"flex", alignItems:"center", justifyContent:"center"
    }}>
      <div onClick={e=>e.stopPropagation()} style={{
        background:"var(--d-panel)", border:"1px solid var(--d-line)", borderRadius:10,
        width:520, color:"var(--d-ink)", fontFamily:"var(--font-sans)"
      }}>
        <div style={{padding:"16px 20px", borderBottom:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div>
            <div style={{fontSize:14, fontWeight:600}}>Relance groupée</div>
            <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:2}}>Relancer {count} {count > 1 ? "locataires" : "locataire"} en une opération</div>
          </div>
          <button onClick={onClose} style={{background:"none", border:"none", color:"var(--d-ink-3)", fontSize:20, cursor:"pointer"}}>×</button>
        </div>
        <div style={{padding:"20px 20px 8px"}}>
          <div style={{marginBottom:14}}>
            <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Canal</div>
            <div style={{display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:6}}>
              {[["whatsapp","WhatsApp","#25D366"],["sms","SMS","var(--d-ink-3)"],["email","Email","var(--blue-d)"],["lettre","Lettre R.AR","var(--orange)"]].map(([k,l,c])=>(
                <button key={k} onClick={()=>setChannel(k)} style={{
                  padding:"10px 8px", borderRadius:5,
                  border:`1px solid ${channel===k?c:"var(--d-line)"}`,
                  background: channel===k ? `${c}1A` : "transparent",
                  color: channel===k ? "var(--d-ink)" : "var(--d-ink-2)",
                  fontSize:11.5, fontWeight:500, cursor:"pointer",
                }}>{l}</button>
              ))}
            </div>
          </div>
          <div style={{marginBottom:14}}>
            <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Modèle</div>
            <select value={template} onChange={e=>setTemplate(e.target.value)} style={{
              width:"100%", padding:"8px 12px", borderRadius:5, border:"1px solid var(--d-line)",
              background:"var(--d-sunken)", color:"var(--d-ink)", fontSize:13, fontFamily:"var(--font-sans)", outline:"none"
            }}>
              <option value="amiable">Relance amiable J+3</option>
              <option value="ferme">Relance ferme J+15</option>
              <option value="mise-demeure">Mise en demeure J+30</option>
              <option value="custom">Message personnalisé…</option>
            </select>
          </div>
          <div style={{marginBottom:14, padding:"12px 14px", background:"var(--d-sunken)", border:"1px solid var(--d-line)", borderRadius:5, fontSize:12, color:"var(--d-ink-2)", lineHeight:1.55}}>
            <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600, marginBottom:6}}>Aperçu</div>
            Bonjour <b>{"{{prenom}}"}</b>, nous n'avons pas reçu le règlement de votre loyer de <b>{"{{montant}}"}</b> FCFA pour <b>{"{{periode}}"}</b>. Merci de procéder au paiement avant le <b>{"{{date_limite}}"}</b> via le lien Wave ci-joint. <i>L'équipe BA Immo.</i>
          </div>
        </div>
        <div style={{padding:"14px 20px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>Envoi à <b style={{color:"var(--d-ink)"}}>{count}</b> destinataires · canal {channel}</div>
          <div style={{display:"flex", gap:8}}>
            <button className="btn btn-ghost" onClick={onClose}>Annuler</button>
            <button className="btn btn-primary" onClick={()=>{ onConfirm ? onConfirm({channel, template}) : onClose(); }}>Envoyer maintenant</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   RELANCE MODAL — relance d'un seul dossier (composer)
   Réutilisé par la page Impayés et par la page Quittances.
   ───────────────────────────────────────────────────────── */
const REL_TEMPLATES = {
  amiable: { label:"Relance amiable", text:(t)=>`Bonjour ${t.prenom}, nous n'avons pas encore reçu le règlement de votre loyer de ${t.montant} FCFA pour ${t.periode}. Merci de régulariser au plus vite via le lien Wave ci-joint. Cordialement, l'équipe BA Immo.` },
  ferme:   { label:"Relance ferme",   text:(t)=>`${t.prenom}, votre loyer de ${t.montant} FCFA (${t.periode}) reste impayé. Merci de procéder au règlement sous 7 jours afin d'éviter des pénalités de retard et une mise en demeure. BA Immo.` },
  "mise-demeure": { label:"Mise en demeure", text:(t)=>`MISE EN DEMEURE — ${t.prenom}, vous restez devoir la somme de ${t.montant} FCFA au titre de ${t.periode}. À défaut de règlement sous 8 jours, une procédure de recouvrement sera engagée conformément aux dispositions du COCC. BA Immo.` },
};

function RelanceModal({ target, onClose, onSent }) {
  const [channel, setChannel] = React.useState("whatsapp");
  const [tplKey, setTplKey] = React.useState("amiable");
  const prenom = (target.name || "").replace(/^(M\.|Mme|Mlle)\s+/i,"").split(" ")[0] || "Madame, Monsieur";
  const ctx = { prenom, montant: (Number(target.amount)||0).toLocaleString("fr-FR"), periode: target.period || "le mois en cours" };
  const [message, setMessage] = React.useState(REL_TEMPLATES[tplKey].text(ctx));

  React.useEffect(() => { setMessage(REL_TEMPLATES[tplKey].text(ctx)); /* eslint-disable-next-line */ }, [tplKey, target]);
  React.useEffect(() => {
    const onKey = e => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  const tenant = MOCK.tenants.find(t => t.id === target.tenantId);
  const ch = REL_CHANNELS[channel];

  return (
    <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:300, backdropFilter:"blur(3px)", display:"flex", alignItems:"flex-start", justifyContent:"center", padding:"7vh 16px"}}>
      <div onClick={e=>e.stopPropagation()} role="dialog" style={{width:"min(560px, 96vw)", background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10, overflow:"hidden", boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)"}}>
        <header style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center", background:"var(--d-panel)"}}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>Relancer</div>
            <div style={{fontSize:15, fontWeight:600}}>{target.name} <span style={{color:"var(--d-ink-3)", fontWeight:400, fontSize:12.5}}>· {fmtFCFA(target.amount)}</span></div>
          </div>
          <button onClick={onClose} style={{width:32, height:32, borderRadius:6, color:"var(--d-ink-3)", fontSize:18, background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer"}}>×</button>
        </header>

        <div style={{padding:"18px 22px"}}>
          {/* Recipient */}
          <div style={{display:"flex", justifyContent:"space-between", fontSize:11.5, color:"var(--d-ink-3)", marginBottom:14, padding:"8px 12px", background:"var(--d-sunken)", borderRadius:6, border:"1px solid var(--d-line)"}}>
            <span>{target.property}</span>
            <span className="mono">{tenant ? tenant.phone : (target.dossierId || target.tenantId || "")}</span>
          </div>

          <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Canal</div>
          <div style={{display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:6, marginBottom:14}}>
            {Object.entries(REL_CHANNELS).map(([k,c])=>(
              <button key={k} onClick={()=>setChannel(k)} style={{padding:"9px 8px", borderRadius:5, border:`1px solid ${channel===k?c.c:"var(--d-line)"}`, background: channel===k?`color-mix(in srgb, ${c.c} 14%, transparent)`:"transparent", color: channel===k?"var(--d-ink)":"var(--d-ink-2)", fontSize:11.5, fontWeight:500, cursor:"pointer"}}>{c.label}</button>
            ))}
          </div>

          <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Modèle</div>
          <div className="seg" style={{display:"flex", width:"100%", marginBottom:14}}>
            {Object.entries(REL_TEMPLATES).map(([k,t])=>(
              <button key={k} className={"seg-btn"+(tplKey===k?" active":"")} onClick={()=>setTplKey(k)} style={{flex:1}}>{t.label}</button>
            ))}
          </div>

          <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Message</div>
          <textarea value={message} onChange={e=>setMessage(e.target.value)} rows={5} style={{width:"100%", padding:"10px 12px", borderRadius:6, border:"1px solid var(--d-line)", background:"var(--d-sunken)", color:"var(--d-ink)", fontSize:12.5, fontFamily:"var(--font-sans)", lineHeight:1.5, outline:"none", resize:"vertical"}}/>
          {channel === "lettre" && <div style={{fontSize:11, color:"var(--d-ink-4)", marginTop:6}}>Une lettre recommandée avec A.R. sera générée en PDF et archivée au dossier.</div>}
        </div>

        <footer style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-panel)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>Envoi par <b style={{color:ch.c}}>{ch.label}</b></div>
          <div style={{display:"flex", gap:8}}>
            <button className="btn btn-ghost" onClick={onClose}>Annuler</button>
            <button className="btn btn-primary" onClick={()=>onSent({ channel, template: tplKey, message })}>
              {channel === "lettre" ? "Générer & envoyer" : "Envoyer la relance"}
            </button>
          </div>
        </footer>
      </div>
    </div>
  );
}

Object.assign(window, { OverduePage, RelanceModal, recordRelance, settleOverdueByTenant, useOverdueStore, BulkRelanceModal, OVERDUE });

/* ╚══ end pages-overdue.jsx ══╝ */

/* ╔══ pages-bailleurs.jsx ══╗ */
/* global React, MOCK, fmtFCFA, fmtCompact, Spark, StatusChip */

/* ─────────────────────────────────────────────────────────
   BAILLEURS — propriétaires (landlords / owners)
   ───────────────────────────────────────────────────────── */

// Enriched owner data — derived from MOCK.properties + contact/bank/fiscal info
const BAILLEURS = [
  {
    id: "B-001", name: "M. Abdoulaye Ba", entity: "Particulier", avatar: "AB", color:"#3F7DDB",
    phone: "+221 77 540 12 88", email: "a.ba@gmail.com",
    bank: "CBAO · 0048 217 33 6512", ninea: "—",
    commissionRate: 0.08, contractDate: "2022-03-14",
    notes: "Client historique · entre par recommandation",
  },
  {
    id: "B-002", name: "Mme Aïssatou Ba", entity: "Particulier", avatar: "ÄB", color:"#C9559C",
    phone: "+221 77 421 06 18", email: "aissatou.ba@gmail.com",
    bank: "Wave · 77 421 ••• 18", ninea: "—",
    commissionRate: 0.08, contractDate: "2024-01-10",
    notes: "Préfère le versement par Wave · alertes WhatsApp",
  },
  {
    id: "B-003", name: "Horizon SCI", entity: "Société civile immobilière", avatar: "HS", color:"#3FB583",
    phone: "+221 33 869 14 02", email: "contact@horizon-sci.sn",
    bank: "Ecobank · ••• 8841", ninea: "012 884 4 J5",
    commissionRate: 0.075, contractDate: "2021-09-01",
    notes: "Gestion multi-biens · interlocuteur K. Sarr (DG)",
  },
  {
    id: "B-004", name: "Katos Consulting", entity: "SARL", avatar: "KC", color:"#F2933E",
    phone: "+221 33 821 47 80", email: "finance@katos-consulting.sn",
    bank: "BICIS · ••• 1124", ninea: "008 421 5 K2",
    commissionRate: 0.08, contractDate: "2022-11-22",
    notes: "Bordereau attendu avant le 5 de chaque mois",
  },
  {
    id: "B-005", name: "M. Khadim Fall", entity: "Particulier", avatar: "KF", color:"#9B8AE6",
    phone: "+221 78 119 84 21", email: "k.fall@mail.sn",
    bank: "Orange Money · 78 119 ••• 21", ninea: "—",
    commissionRate: 0.09, contractDate: "2025-02-08",
    notes: "Villa actuellement vacante · recherche locataire",
  },
  {
    id: "B-006", name: "Mme Fatou Diop", entity: "Particulier", avatar: "FD", color:"#E0A74A",
    phone: "+221 77 803 26 14", email: "f.diop@mail.sn",
    bank: "CBAO · ••• 9304", ninea: "—",
    commissionRate: 0.085, contractDate: "2023-04-19",
    notes: "Réside à l'étranger · communication par email",
  },
  {
    id: "B-007", name: "Indivision Wade", entity: "Indivision", avatar: "IW", color:"#7C3AED",
    phone: "+221 77 612 88 04", email: "indivision.wade@mail.sn",
    bank: "CBAO · 0048 217 41 9923", ninea: "—",
    commissionRate: 0.08, contractDate: "2024-09-18",
    notes: "Succession Wade · 3 héritiers · convention notariée Me Sow (12/09/2024).\nVersement proportionnel demandé par les indivisaires.",
    representant: "M. Ousmane Wade",
    versementMode: "proportional",
    conventionDate: "2024-09-12",
    indivisaires: [
      { id: 1, name: "M. Ousmane Wade",  quotePart: 50, phone: "+221 77 612 88 04", email: "o.wade@mail.sn",   bank: "CBAO · ••• 9923",        lien: "fratrie" },
      { id: 2, name: "Mme Awa Wade",     quotePart: 25, phone: "+221 78 204 11 56", email: "awa.wade@mail.sn", bank: "Wave · 78 204 ••• 56",   lien: "fratrie" },
      { id: 3, name: "M. Ibrahima Wade", quotePart: 25, phone: "+221 76 408 92 17", email: "i.wade@mail.sn",   bank: "Orange Money · ••• 92 17", lien: "fratrie" },
    ],
  },
];

/* ─── Mutable store ─── allows the form modal to add/edit bailleurs
   while keeping all consumers (list, detail, versements, crg…) in sync. */
const _bailleurListeners = new Set();
function _notifyBailleurs() { _bailleurListeners.forEach(fn => fn()); }
function upsertBailleur(b) {
  const i = BAILLEURS.findIndex(x => x.id === b.id);
  if (i >= 0) BAILLEURS[i] = { ...BAILLEURS[i], ...b };
  else BAILLEURS.push(b);
  if (!BAILLEUR_LOSSES[b.id]) BAILLEUR_LOSSES[b.id] = [0,0,0,0,0,0];
  _notifyBailleurs();
}
function useBailleurs() {
  const [, tick] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => { _bailleurListeners.add(tick); return () => _bailleurListeners.delete(tick); }, []);
  return BAILLEURS;
}
function getBailleurById(id) { return BAILLEURS.find(b => b.id === id); }

// Helper — pull computed metrics for a bailleur from MOCK.properties
function getBailleurMetrics(b) {
  const props = MOCK.properties.filter(p => p.owner === b.name);
  const units = props.reduce((s,p)=>s+p.units, 0);
  const occupied = props.reduce((s,p)=>s+p.occupied, 0);
  const mrr = props.reduce((s,p)=>s+p.mrr, 0);
  const commission = Math.round(mrr * b.commissionRate);
  const net = mrr - commission;
  return { props, units, occupied, mrr, commission, net };
}

/* ─────────────────────────────────────────────────────────
   SHARED SOURCE OF TRUTH — 6-month bailleur activity
   Used by: pages-versements (current + history) AND
            pages-compta (grand livre + balance + récépissés)
   ───────────────────────────────────────────────────────── */

// 6-month window. Last entry = current month being closed.
const BAILLEUR_MONTHS = [
  { code:"2025-11", label:"Nov 25",  paidOn:"30 nov", status:"versé"      },
  { code:"2025-12", label:"Déc 25",  paidOn:"30 déc", status:"versé"      },
  { code:"2026-01", label:"Jan 26",  paidOn:"30 jan", status:"versé"      },
  { code:"2026-02", label:"Fév 26",  paidOn:"29 fév", status:"versé"      },
  { code:"2026-03", label:"Mars 26", paidOn:"30 mars",status:"versé"      },
  { code:"2026-04", label:"Avr 26",  paidOn:null,     status:"à émettre"  },
];

// Realistic per-bailleur per-month losses (delinquent rent that wasn't
// collected that month). Zero = nominal mrr collected.
const BAILLEUR_LOSSES = {
  "B-001": [0,       0,       0,       110_000, 0,      0],  // Mariama Ndiaye missed Feb
  "B-002": [0,       0,       0,       0,       0,      0],
  "B-003": [0,       0,       220_000, 0,       0,      0],  // Bineta Diouf irregular
  "B-004": [0,       0,       0,       0,       75_000, 0],  // small adjustment March
  "B-005": [0,       0,       0,       0,       0,      0],  // mrr=0 (vacancy) — already zero
  "B-006": [0,       0,       0,       0,       0,      0],
  "B-007": [0,       0,       0,       0,       0,      0],
};

// Channel reference for each bailleur — used by versements + recepisses
const bailleurChannel = b => b.bank.startsWith("Wave")        ? "Wave"
                          :  b.bank.startsWith("Orange")     ? "Orange Money"
                          :  b.bank.startsWith("Ecobank")    ? "Ecobank"
                          :  b.bank.startsWith("BICIS")      ? "BICIS"
                          :  "CBAO";

// THE function. For (bailleur, month index 0..5) returns the standard
// breakdown: collected → commission → charges → versement → net.
// Charges = sum of NON-RECOVERABLE lines from the Charges module
// (récupérables go on tenant quittances, not on the bailleur 467 account).
function getBailleurMonthly(b, monthIdx) {
  const m = getBailleurMetrics(b);
  const loss = (BAILLEUR_LOSSES[b.id] || [])[monthIdx] || 0;
  const collected = Math.max(0, m.mrr - loss);
  const commission = Math.round(collected * b.commissionRate);
  // Real charges via charges module, if loaded. Fallback to flat formula.
  const monthCode = BAILLEUR_MONTHS[monthIdx].code;
  const [yr, mo] = monthCode.split("-").map(Number);
  const ch = (typeof window !== "undefined" && typeof window.monthlyChargesByOwner === "function")
    ? window.monthlyChargesByOwner(b.name, yr, mo)
    : null;
  const charges = ch
    ? ch.nonRecoverable
    : ((collected === 0 && m.units >= 1) ? 35_000
      : m.units > 5 ? 65_000
      : m.units > 1 ? 35_000
      : 0);
  const net = collected - commission - charges;
  const isCurrent = monthIdx === BAILLEUR_MONTHS.length - 1;
  return {
    month: BAILLEUR_MONTHS[monthIdx],
    units: m.units, occupied: m.occupied,
    collected, commission, charges, net,
    versement: isCurrent ? 0 : Math.max(0, net),
    paid: !isCurrent && net > 0,
    channel: bailleurChannel(b),
  };
}

// Convenience — full history for a bailleur on the 6-month window
function getBailleurHistory(b) {
  return BAILLEUR_MONTHS.map((_, i) => getBailleurMonthly(b, i));
}

/* ─────────────────────────────────────────────────────────
   LIST
   ───────────────────────────────────────────────────────── */
function BailleursList({ onOpenBailleur }) {
  const bailleurs = useBailleurs();
  const [q, setQ] = React.useState("");
  const [tab, setTab] = React.useState("all");
  const [modal, setModal] = React.useState(null); // null | { mode, initial }

  const enriched = bailleurs.map(b => ({ ...b, ...getBailleurMetrics(b) }));
  const filtered = enriched.filter(b => {
    if (tab === "sci" && !b.entity.includes("Société") && !b.entity.includes("SARL")) return false;
    if (tab === "particulier" && b.entity !== "Particulier") return false;
    if (tab === "indivision" && b.entity !== "Indivision") return false;
    if (q && !`${b.name} ${b.id} ${b.entity}`.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });

  const totalMrr = enriched.reduce((s,b)=>s+b.mrr, 0);
  const totalCommission = enriched.reduce((s,b)=>s+b.commission, 0);
  const totalNet = enriched.reduce((s,b)=>s+b.net, 0);
  const totalUnits = enriched.reduce((s,b)=>s+b.units, 0);

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Bailleurs</h1>
          <div className="page-sub">{bailleurs.length} propriétaires · {MOCK.properties.length} biens · {totalUnits} lots</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost" onClick={()=>lgiToast("Export de la liste des bailleurs (.xlsx)","ok")}>Exporter</button>
          <button className="btn btn-secondary" onClick={()=>lgiToast("Import CSV de bailleurs")}>Importer CSV</button>
          <button className="btn btn-primary" onClick={()=>setModal({mode:"create", initial:null})}>+ Nouveau bailleur</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Parc géré</span></div>
          <div className="kpi-value num">{(totalMrr/1_000_000).toFixed(1).replace(".",",")}<span className="kpi-unit">M FCFA / mois</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 2,4%</span><span>vs mars · loyer brut consolidé</span></div>
          <div className="kpi-spark"><Spark values={[42,44,46,46,48,50,52,52,53,54,55,55]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Commission agence</span></div>
          <div className="kpi-value num">{fmtCompact(totalCommission)}<span className="kpi-unit">FCFA / mois</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 2,1%</span><span>moyenne pondérée 8,1%</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Net propriétaires</span></div>
          <div className="kpi-value num">{(totalNet/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span style={{color:"var(--pos-d)", fontWeight:600}}>À verser</span><span>échéance 30 avril</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Occupation moyenne</span></div>
          <div className="kpi-value num">{Math.round((enriched.reduce((s,b)=>s+b.occupied,0)/totalUnits)*100)}<span className="kpi-unit">%</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 1,4 pt</span><span>4 lots vacants à pourvoir</span></div>
        </div>
      </div>

      {/* Tabs */}
      <div className="tabs">
        {[
          ["all", "Tous", enriched.length],
          ["particulier", "Particuliers", enriched.filter(b=>b.entity==="Particulier").length],
          ["sci", "Sociétés", enriched.filter(b=>b.entity!=="Particulier" && b.entity!=="Indivision").length],
          ["indivision", "Indivisions", enriched.filter(b=>b.entity==="Indivision").length],
        ].map(([k,l,n])=>(
          <button key={k} className={"tab" + (tab===k?" active":"")} onClick={()=>setTab(k)}>{l} <span className="tab-count">{n}</span></button>
        ))}
      </div>

      <div className="panel">
        <div className="filter-bar" style={{borderTop:"none"}}>
          <div className="top-search" style={{width:300, background:"var(--d-panel)"}}>
            <span style={{color:"var(--d-ink-4)"}}>⌕</span>
            <input value={q} onChange={e=>setQ(e.target.value)} placeholder="Nom, ID, entité…"/>
          </div>
          <div className="filter-spacer"/>
          <button className="filter-chip">Trier ▾</button>
        </div>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th style={{width:74}}>ID</th>
                <th>Bailleur</th>
                <th>Type</th>
                <th>Parc</th>
                <th>Occupation</th>
                <th className="right">Loyer brut</th>
                <th className="right">Commission</th>
                <th className="right">Net mensuel</th>
              </tr>
            </thead>
            <tbody>
              {filtered.length === 0 && <tr><td colSpan={8} className="muted" style={{textAlign:"center", padding:28}}>Aucun bailleur ne correspond.</td></tr>}
              {filtered.map(b => (
                <tr key={b.id} onClick={()=>onOpenBailleur(b)}>
                  <td className="mono muted" style={{fontSize:11.5}}>{b.id}</td>
                  <td>
                    <div style={{display:"flex", alignItems:"center", gap:10}}>
                      <div className="avatar" style={{background:b.color, color:"#fff"}}>{b.avatar}</div>
                      <div>
                        <div style={{fontWeight:500}}>{b.name}</div>
                        <div className="muted" style={{fontSize:11.5}}>{b.email}</div>
                      </div>
                    </div>
                  </td>
                  <td className="muted">
                    {b.entity}
                    {b.entity === "Indivision" && b.indivisaires && (
                      <span style={{marginLeft:6, fontSize:11, color:"var(--d-ink-4)"}}>· {b.indivisaires.length} indivisaires</span>
                    )}
                  </td>
                  <td className="muted mono" style={{fontSize:11.5}}>{b.props.length} biens · {b.units} lots</td>
                  <td>
                    <span className="occ-bar"><span className="occ-bar-fill" style={{width:`${(b.occupied/b.units)*100}%`, background: b.occupied === 0 ? "var(--orange)" : b.occupied === b.units ? "var(--pos-d)" : "var(--blue-d)"}}/></span>
                    <span className="mono" style={{marginLeft:10, fontSize:12}}>{b.occupied}/{b.units}</span>
                  </td>
                  <td className="right">{fmtFCFA(b.mrr)}</td>
                  <td className="right muted">−{fmtFCFA(b.commission).replace(" FCFA","")}<span style={{color:"var(--d-ink-4)", marginLeft:4}}>({(b.commissionRate*100).toFixed(b.commissionRate*100%1===0?0:1).replace(".",",")}%)</span></td>
                  <td className="right" style={{color: b.net > 0 ? "var(--pos-d)" : "var(--d-ink-4)", fontWeight:500}}>{b.net > 0 ? fmtFCFA(b.net) : "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      <BailleurFormModal
        open={!!modal}
        mode={modal?.mode || "create"}
        initial={modal?.initial}
        onClose={()=>setModal(null)}
        onSave={(payload)=>{ upsertBailleur(payload); setModal(null); }}
      />
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   DETAIL
   ───────────────────────────────────────────────────────── */
function BailleurDetail({ bailleur, onBack, onOpenProperty }) {
  useBailleurs(); // subscribe so edits via the modal re-render this view
  const b = getBailleurById(bailleur.id) || bailleur;
  const m = getBailleurMetrics(b);
  const occPct = m.units ? Math.round((m.occupied/m.units)*100) : 0;
  const isIndivision = b.entity === "Indivision";
  const [editing, setEditing] = React.useState(false);

  // Synthetic 12-month disbursements history
  const months = ["Mai 25","Juin 25","Juil","Août","Sept","Oct","Nov","Déc","Jan","Fév","Mars","Avr 26"];
  const history = months.map((mo, i) => ({
    month: mo,
    expected: m.mrr,
    collected: m.mrr - (i === 2 ? 110_000 : i === 7 ? 220_000 : 0),
    commission: Math.round(m.mrr * b.commissionRate),
    charges: m.units > 5 ? 65_000 : m.units > 1 ? 35_000 : 0,
    paidOn: `${(i%26)+2} ${mo.split(" ")[0].toLowerCase()}`,
    status: i === 11 ? "à émettre" : "versé",
  }));

  const totals12m = {
    collected: history.reduce((s,h)=>s+h.collected, 0),
    commission: history.reduce((s,h)=>s+h.commission, 0),
    net: history.reduce((s,h)=>s + h.collected - h.commission - h.charges, 0),
  };

  return (
    <div className="page">
      <div style={{marginBottom:14, display:"flex", alignItems:"center", gap:12}}>
        <button className="btn btn-ghost" onClick={onBack}>← Bailleurs</button>
        <span className="chip neutral plain mono" style={{fontSize:11}}>{b.id}</span>
      </div>

      {/* Hero */}
      <div className="t-hero">
        <div className="avatar lg" style={{background:b.color}}>{b.avatar}</div>
        <div>
          <div className="t-hero-id">BAILLEUR · {b.entity.toUpperCase()}</div>
          <div className="t-hero-name">{b.name}</div>
          <div className="t-hero-meta">
            <span>{b.phone}</span>
            <span>{b.email}</span>
            <span>Sous gestion depuis <b style={{color:"var(--d-ink-2)"}}>{b.contractDate}</b></span>
          </div>
        </div>
        <div style={{display:"flex", gap:8}}>
          <button className="btn btn-secondary" onClick={()=>setEditing(true)}>Modifier</button>
          <button className="btn btn-secondary" onClick={()=>lgiToast("Compte de gérance envoyé au bailleur","ok")}>Envoyer compte de gérance</button>
          <button className="btn btn-primary" onClick={()=>lgiToast("Versement de "+fmtCompact(m.net)+" FCFA initié","ok")}>+ Verser {fmtCompact(m.net)} FCFA</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Loyer brut mensuel</span></div>
          <div className="kpi-value num">{fmtCompact(m.mrr)}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ stable</span><span>{m.props.length} biens · {m.units} lots</span></div>
          <div className="kpi-spark"><Spark values={[55,56,55,57,58,58,59,60,60,61,62,62]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Occupation</span></div>
          <div className="kpi-value num">{occPct}<span className="kpi-unit">%</span></div>
          <div className="kpi-meta"><span>{m.occupied}/{m.units} lots occupés</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Commission agence</span></div>
          <div className="kpi-value num">{(b.commissionRate*100).toFixed(b.commissionRate*100%1===0?0:1).replace(".",",")}<span className="kpi-unit">%</span></div>
          <div className="kpi-meta"><span>{fmtCompact(m.commission)} FCFA / mois</span></div>
        </div>
        <div className="kpi" style={{borderColor:"rgba(63,181,131,0.30)"}}>
          <div className="kpi-label"><span>Net à verser · avril</span><span style={{color:"var(--pos-d)", fontWeight:600}}>30 avril</span></div>
          <div className="kpi-value num" style={{color:"var(--pos-d)"}}>{fmtCompact(m.net)}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span>Virement sur <b style={{color:"var(--d-ink-2)"}}>{b.bank.split(" · ")[0]}</b></span></div>
        </div>
      </div>

      {/* Tabs */}
      <div className="tabs">
        <button className="tab active">Vue d'ensemble</button>
        <button className="tab">Biens <span className="tab-count">{m.props.length}</span></button>
        <button className="tab">Versements <span className="tab-count">12</span></button>
        <button className="tab">Documents</button>
        <button className="tab">Activité</button>
      </div>

      <div className="grid-detail">
        <div className="stack">
          {isIndivision && b.indivisaires && (
            <IndivisairesPanel b={b} m={m}/>
          )}
          {/* Properties owned */}
          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Parc immobilier</h3>
                <div className="panel-sub">{m.props.length} biens sous gestion · {m.units} lots</div>
              </div>
              <button className="btn btn-ghost" onClick={()=>lgiToast("Ajout d’un bien pour ce bailleur")}>+ Ajouter un bien</button>
            </div>
            <div className="table-wrap">
              <table className="tbl">
                <thead>
                  <tr>
                    <th style={{width:80}}>ID</th>
                    <th>Bien</th>
                    <th>Type</th>
                    <th>Occupation</th>
                    <th className="right">Loyer / mois</th>
                  </tr>
                </thead>
                <tbody>
                  {m.props.length === 0 && <tr><td colSpan={5} className="muted" style={{textAlign:"center", padding:24}}>Aucun bien en gestion.</td></tr>}
                  {m.props.map(p => (
                    <tr key={p.id} onClick={()=>onOpenProperty(p)}>
                      <td className="mono muted" style={{fontSize:11.5}}>{p.id}</td>
                      <td>
                        <div style={{fontWeight:500}}>{p.name}</div>
                        <div className="muted" style={{fontSize:11.5, marginTop:1}}>{p.address}</div>
                      </td>
                      <td className="muted">{p.type}</td>
                      <td>
                        <span className="occ-bar"><span className="occ-bar-fill" style={{width:`${(p.occupied/p.units)*100}%`, background: p.occupied === 0 ? "var(--orange)" : p.occupied === p.units ? "var(--pos-d)" : "var(--blue-d)"}}/></span>
                        <span className="mono" style={{marginLeft:10, fontSize:12}}>{p.occupied}/{p.units}</span>
                      </td>
                      <td className="right">{fmtFCFA(p.mrr)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>

          {/* Disbursements history */}
          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Historique des versements</h3>
                <div className="panel-sub">12 derniers mois · cumul {fmtCompact(totals12m.net)} FCFA versés</div>
              </div>
              <button className="btn btn-ghost" onClick={()=>lgiToast("Historique des versements exporté (PDF)","ok")}>Exporter PDF</button>
            </div>
            <div className="table-wrap">
              <table className="tbl">
                <thead>
                  <tr>
                    <th style={{width:90}}>Mois</th>
                    <th className="right">Encaissé</th>
                    <th className="right">Commission</th>
                    <th className="right">Charges</th>
                    <th className="right">Net versé</th>
                    <th>Date</th>
                    <th style={{width:110}}>Statut</th>
                  </tr>
                </thead>
                <tbody>
                  {history.slice().reverse().map((h, i) => {
                    const net = h.collected - h.commission - h.charges;
                    return (
                      <tr key={i}>
                        <td className="mono">{h.month}</td>
                        <td className="right">{fmtFCFA(h.collected)}</td>
                        <td className="right muted">−{h.commission.toLocaleString("fr-FR")}</td>
                        <td className="right muted">{h.charges ? "−"+h.charges.toLocaleString("fr-FR") : "—"}</td>
                        <td className="right" style={{color:"var(--pos-d)", fontWeight:500}}>{fmtFCFA(net)}</td>
                        <td className="muted mono" style={{fontSize:11.5}}>{h.status === "à émettre" ? <span className="empty-dash">en cours</span> : h.paidOn}</td>
                        <td><span className={`chip ${h.status==="versé"?"pos":"warn"}`}>{h.status}</span></td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </div>
        </div>

        {/* Sidebar */}
        <div className="stack">
          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">Coordonnées</h3></div>
            <div>
              <div className="info-row"><div className="info-k">Nom</div><div className="info-v">{b.name}</div></div>
              <div className="info-row"><div className="info-k">Entité</div><div className="info-v">{b.entity}{isIndivision && b.indivisaires ? ` · ${b.indivisaires.length} indivisaires` : ""}</div></div>
              {isIndivision && b.representant && (
                <div className="info-row"><div className="info-k">Représentant</div><div className="info-v">{b.representant}</div></div>
              )}
              <div className="info-row"><div className="info-k">Téléphone</div><div className="info-v mono">{b.phone}</div></div>
              <div className="info-row"><div className="info-k">Email</div><div className="info-v" style={{fontSize:12}}>{b.email}</div></div>
              <div className="info-row" style={{borderBottom:"none"}}><div className="info-k">NINEA</div><div className="info-v mono">{b.ninea || "—"}</div></div>
            </div>
          </div>
          <div className="panel">
            <div className="panel-head">
              <h3 className="panel-title">Compte bancaire</h3>
              <button className="btn btn-ghost" style={{padding:"3px 8px", fontSize:11}}>Modifier</button>
            </div>
            <div>
              <div className="info-row"><div className="info-k">Compte</div><div className="info-v mono" style={{fontSize:11.5}}>{b.bank}</div></div>
              <div className="info-row"><div className="info-k">Devise</div><div className="info-v mono">XOF (FCFA)</div></div>
              <div className="info-row" style={{borderBottom:"none"}}><div className="info-k">Bénéficiaire</div><div className="info-v">{b.name}</div></div>
            </div>
          </div>
          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">Contrat de gestion</h3></div>
            <div>
              <div className="info-row"><div className="info-k">Référence</div><div className="info-v mono">CTG-{b.id.replace("B-","")}</div></div>
              <div className="info-row"><div className="info-k">Signature</div><div className="info-v mono">{b.contractDate}</div></div>
              <div className="info-row"><div className="info-k">Commission</div><div className="info-v mono">{(b.commissionRate*100).toFixed(b.commissionRate*100%1===0?0:1).replace(".",",")}% / loyers encaissés</div></div>
              <div className="info-row"><div className="info-k">Fréquence versement</div><div className="info-v">Mensuelle · le 30</div></div>
              <div className="info-row" style={{borderBottom:"none"}}><div className="info-k">Durée</div><div className="info-v">Tacite reconduction</div></div>
            </div>
          </div>
          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">Notes</h3></div>
            <div style={{padding:"14px 18px", fontSize:12.5, color:"var(--d-ink-2)", lineHeight:1.55, whiteSpace:"pre-line"}}>
              {b.notes}
            </div>
          </div>
        </div>
      </div>

      <BailleurFormModal
        open={editing}
        mode="edit"
        initial={b}
        onClose={()=>setEditing(false)}
        onSave={(payload)=>{ upsertBailleur(payload); setEditing(false); }}
      />
    </div>
  );
}

Object.assign(window, {
  BailleursList, BailleurDetail, BAILLEURS,
  getBailleurMetrics, getBailleurMonthly, getBailleurHistory,
  BAILLEUR_MONTHS, bailleurChannel,
  upsertBailleur, useBailleurs, getBailleurById,
});

/* ─────────────────────────────────────────────────────────
   INDIVISAIRES PANEL — shown on detail page when entity = Indivision
   ───────────────────────────────────────────────────────── */
function IndivisairesPanel({ b, m }) {
  const items = b.indivisaires || [];
  const total = items.reduce((s,i)=>s+(Number(i.quotePart)||0), 0);
  const palette = ["#3F7DDB","#C9559C","#3FB583","#F2933E","#9B8AE6","#E0A74A","#1F6E4A","#9A2A2A","#7C3AED"];
  const modeLabel = {
    proportional:        "Versement proportionnel · chaque indivisaire reçoit sa part",
    representant:        "Versement unique au représentant",
    "compte-indivision": "Versement sur compte d'indivision dédié",
  }[b.versementMode] || "—";

  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Indivisaires <span style={{color:"var(--d-ink-4)", fontWeight:400, marginLeft:6}}>({items.length})</span></h3>
          <div className="panel-sub">{modeLabel}</div>
        </div>
        <div style={{display:"flex", alignItems:"center", gap:8}}>
          <span className="mono" style={{fontSize:11, color: total===100 ? "var(--pos-d)" : "var(--orange)"}}>
            Quote-parts {total} / 100%
          </span>
        </div>
      </div>

      {/* Quote-parts bar */}
      <div style={{padding:"6px 22px 14px"}}>
        <div style={{display:"flex", height:14, borderRadius:3, overflow:"hidden", border:"1px solid var(--d-line-2)"}}>
          {items.map((it, i) => {
            const pct = Number(it.quotePart) || 0;
            if (!pct) return null;
            return (
              <div key={i} title={`${it.name} · ${pct}%`} style={{
                width:`${(pct / Math.max(total, 100)) * 100}%`,
                background: palette[i % palette.length],
                borderRight: i < items.length - 1 ? "1px solid var(--d-bg)" : "none",
                display:"grid", placeItems:"center",
                fontSize:9.5, color:"#fff", fontWeight:600, fontFamily:"var(--font-mono)",
              }}>{pct >= 10 ? `${pct}%` : ""}</div>
            );
          })}
        </div>
      </div>

      <div className="table-wrap" style={{borderTop:"1px solid var(--d-line)"}}>
        <table className="tbl">
          <thead>
            <tr>
              <th style={{width:34}}></th>
              <th>Indivisaire</th>
              <th>Lien</th>
              <th className="right">Quote-part</th>
              <th className="right">Part nette estimée</th>
              <th>Versement</th>
            </tr>
          </thead>
          <tbody>
            {items.map((it, i) => {
              const pct = Number(it.quotePart) || 0;
              const share = Math.round((m.mrr - Math.round(m.mrr * b.commissionRate)) * pct / 100);
              const isRep = b.representant === it.name;
              return (
                <tr key={i}>
                  <td><span style={{display:"block", width:10, height:10, borderRadius:2, background: palette[i % palette.length]}}/></td>
                  <td>
                    <div style={{fontWeight:500, display:"flex", alignItems:"center", gap:8}}>
                      {it.name}
                      {isRep && <span style={{
                        fontSize:9.5, fontWeight:600, padding:"1px 7px", borderRadius:3,
                        background:"rgba(124,58,237,0.15)", color:"#A78BFA",
                        letterSpacing:"0.04em", textTransform:"uppercase",
                      }}>Représentant</span>}
                    </div>
                    <div className="muted" style={{fontSize:11.5, marginTop:1}}>{it.email || it.phone}</div>
                  </td>
                  <td className="muted" style={{textTransform:"capitalize"}}>{it.lien || "—"}</td>
                  <td className="right mono">{pct}%</td>
                  <td className="right" style={{color:"var(--pos-d)", fontWeight:500}}>{share > 0 ? fmtFCFA(share) : "—"}</td>
                  <td className="mono muted" style={{fontSize:11.5}}>
                    {b.versementMode === "proportional"
                      ? (it.bank || <span className="empty-dash">non renseigné</span>)
                      : (isRep ? b.bank : <span className="muted">via représentant</span>)}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      {b.conventionDate && (
        <div style={{padding:"10px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:11.5, color:"var(--d-ink-3)", display:"flex", justifyContent:"space-between"}}>
          <span>Convention d'indivision signée le <b className="mono" style={{color:"var(--d-ink-2)"}}>{b.conventionDate}</b></span>
          <button className="btn btn-ghost" style={{padding:"2px 10px", fontSize:11}}>Voir la convention</button>
        </div>
      )}
    </div>
  );
}

/* ╚══ end pages-bailleurs.jsx ══╝ */

/* ╔══ pages-versements.jsx ══╗ */
/* global React, MOCK, BAILLEURS, fmtFCFA, fmtCompact, Spark,
          getBailleurMonthly, BAILLEUR_MONTHS, bailleurChannel */

/* ─────────────────────────────────────────────────────────
   VERSEMENTS — owner disbursements
   À verser · planifiés · historique · bordereau CSV / Wave / OM
   Source unique partagée avec Compta bailleurs (getBailleurMonthly)
   ───────────────────────────────────────────────────────── */

// Build current-month disbursement rows (April = last month of BAILLEUR_MONTHS).
// Each row consumes the SHARED `getBailleurMonthly` so figures stay aligned
// with Compta bailleurs / Récépissés / Grand livre.
function buildDisbursements() {
  const currentIdx = BAILLEUR_MONTHS.length - 1;
  return BAILLEURS.map(b => {
    const r = getBailleurMonthly(b, currentIdx);
    return {
      id: "V-" + r.month.code + "-" + b.id.replace("B-","0"),
      bailleur: b,
      props: MOCK.properties.filter(p => p.owner === b.name),
      collected: r.collected, commission: r.commission,
      charges: r.charges, net: r.net,
      channel: r.channel,
      iban: b.bank,
      scheduled: "30 avr 2026",
      status: r.net <= 0 ? "rien à verser" : "à émettre",
    };
  });
}

// Derive history from the SAME source — every prior month, every bailleur
// with a payable net. Ordered newest → oldest.
function buildHistory() {
  const rows = [];
  for (let idx = BAILLEUR_MONTHS.length - 2; idx >= 0; idx--) {
    const mo = BAILLEUR_MONTHS[idx];
    BAILLEURS.forEach(b => {
      const r = getBailleurMonthly(b, idx);
      if (r.net <= 0) return;  // skip — vacancy months land in "Exceptions"
      const channel = r.channel;
      const refPrefix = channel === "Wave" ? "WV-" : channel === "Orange Money" ? "OM-" : "VIR-";
      const refNum = (4_500 + idx*200 + BAILLEURS.indexOf(b)*3).toString();
      rows.push({
        id: "V-" + mo.code + "-" + b.id.replace("B-","0"),
        date: mo.paidOn,
        bailleur: b.name,
        channel,
        amount: r.net,
        ref: refPrefix + refNum,
        status: "émis",
      });
    });
  }
  return rows;
}
const HISTORY = buildHistory();

function VersementsPage({ onOpenBailleur }) {
  const disbursements = React.useMemo(() => buildDisbursements(), []);
  const [emittedIds, setEmittedIds] = React.useState([]);
  const allToEmit = disbursements.filter(d => d.status === "à émettre");
  const toEmit = allToEmit.filter(d => !emittedIds.includes(d.id));
  const skipped = disbursements.filter(d => d.status === "rien à verser");

  const [selected, setSelected] = React.useState(allToEmit.map(d => d.id));
  const selCount = toEmit.filter(d => selected.includes(d.id)).length;
  const allChecked = toEmit.length > 0 && selCount === toEmit.length;
  const someChecked = selCount > 0 && !allChecked;

  function emitSelected(ids) {
    ids.forEach(id => {
      const d = allToEmit.find(x => x.id === id);
      if (d) HISTORY.unshift({ id: `VIR-2026-05-${String(d.bailleur.id).replace("B-","0")}`, bailleur: d.bailleur.name, channel: d.channel, amount: d.net, date: "29 mai", ref: d.channel === "Wave" ? "WV-"+Math.floor(Math.random()*900000+100000) : "—", status: "émis" });
    });
    setEmittedIds(p => [...new Set([...p, ...ids])]);
    setSelected(s => s.filter(x => !ids.includes(x)));
  }
  const [tab, setTab] = React.useState("a-emettre");
  const [showRunModal, setShowRunModal] = React.useState(false);

  const totalToEmit = toEmit.reduce((s, d) => s + d.net, 0);
  const totalSelected = toEmit.filter(d => selected.includes(d.id)).reduce((s, d) => s + d.net, 0);
  const totalCollected = disbursements.reduce((s, d) => s + d.collected, 0);
  const totalCommission = disbursements.reduce((s, d) => s + d.commission, 0);

  function toggle(id) {
    setSelected(s => s.includes(id) ? s.filter(x => x !== id) : [...s, id]);
  }
  function toggleAll() {
    setSelected(s => s.length === toEmit.length ? [] : toEmit.map(d => d.id));
  }

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Versements</h1>
          <div className="page-sub">{toEmit.length} virements à émettre · échéance 30 avril · {fmtCompact(totalToEmit)} FCFA</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter le bordereau (CSV)</button>
          <button className="btn btn-secondary">Planifier</button>
          <button className="btn btn-primary" disabled={selCount===0} style={{opacity:selCount===0?0.5:1, cursor:selCount===0?"not-allowed":"pointer"}} onClick={() => setShowRunModal(true)}>Émettre {selCount} virement{selCount>1?"s":""}</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>À verser · avril</span></div>
          <div className="kpi-value num">{(totalToEmit/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span style={{color:"var(--pos-d)", fontWeight:600}}>{toEmit.length} bénéficiaires</span><span>échéance dans 12 jours</span></div>
          <div className="kpi-spark"><Spark values={[8,8.2,8.5,8.4,8.7,9,9.2,9.5,9.4,9.6,9.8,10.6]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Loyer brut collecté</span></div>
          <div className="kpi-value num">{(totalCollected/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span>Base de calcul · avril</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Commission agence</span></div>
          <div className="kpi-value num">{fmtCompact(totalCommission)}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span>Conservée sur compte BA Immo</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Délai moyen versement</span></div>
          <div className="kpi-value num">1,2<span className="kpi-unit">jour</span></div>
          <div className="kpi-meta"><span className="delta pos">▼ 0,4 j</span><span>vs mars · cible &lt; 2 j</span></div>
        </div>
      </div>

      {/* Tabs */}
      <div className="tabs">
        <button className={"tab" + (tab==="a-emettre"?" active":"")} onClick={()=>setTab("a-emettre")}>À émettre <span className="tab-count">{toEmit.length}</span></button>
        <button className={"tab" + (tab==="planifies"?" active":"")} onClick={()=>setTab("planifies")}>Planifiés <span className="tab-count">{toEmit.length}</span></button>
        <button className={"tab" + (tab==="historique"?" active":"")} onClick={()=>setTab("historique")}>Historique <span className="tab-count">{HISTORY.length}</span></button>
        <button className={"tab" + (tab==="exceptions"?" active":"")} onClick={()=>setTab("exceptions")}>Exceptions <span className="tab-count">{skipped.length}</span></button>
      </div>

      {tab === "a-emettre" && (
        <div className="grid-2">
          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Bordereaux à émettre</h3>
                <div className="panel-sub">{selCount} sélectionné{selCount>1?"s":""} · {fmtCompact(totalSelected)} FCFA</div>
              </div>
              <button className="btn btn-ghost" onClick={toggleAll}>{allChecked ? "Tout désélectionner" : "Tout sélectionner"}</button>
            </div>
            <div className="table-wrap">
              <table className="tbl">
                <thead>
                  <tr>
                    <th style={{width:34}}>
                      <span onClick={toggleAll} title={allChecked?"Tout désélectionner":"Tout sélectionner"} style={{
                        display:"inline-block", width:14, height:14, borderRadius:3, verticalAlign:"middle", cursor:"pointer", position:"relative",
                        border: (allChecked||someChecked) ? "1.5px solid var(--blue-d)" : "1.5px solid var(--d-line)",
                        background: (allChecked||someChecked) ? "var(--blue-d)" : "transparent",
                      }}>
                        {allChecked && <span style={{position:"absolute", inset:0, color:"#fff", fontSize:10, lineHeight:"12px", textAlign:"center", fontWeight:700}}>✓</span>}
                        {someChecked && <span style={{position:"absolute", inset:0, color:"#fff", fontSize:11, lineHeight:"11px", textAlign:"center", fontWeight:700}}>–</span>}
                      </span>
                    </th>
                    <th>Bénéficiaire</th>
                    <th>Canal</th>
                    <th className="right">Encaissé</th>
                    <th className="right">Commission</th>
                    <th className="right">Net à verser</th>
                  </tr>
                </thead>
                <tbody>
                  {toEmit.length === 0 && <tr><td colSpan={6} className="muted" style={{textAlign:"center", padding:28}}>Tous les virements ont été émis — voir l'onglet Historique.</td></tr>}
                  {toEmit.map(d => {
                    const checked = selected.includes(d.id);
                    return (
                      <tr key={d.id} onClick={() => toggle(d.id)} style={{background: checked ? "rgba(97,146,255,0.04)" : undefined}}>
                        <td>
                          <span onClick={(e) => { e.stopPropagation(); toggle(d.id); }} style={{
                            display:"inline-block", width:14, height:14, borderRadius:3,
                            border: checked ? "1.5px solid var(--blue-d)" : "1.5px solid var(--d-line)",
                            background: checked ? "var(--blue-d)" : "transparent",
                            position:"relative", cursor:"pointer", verticalAlign:"middle",
                          }}>
                            {checked && <span style={{position:"absolute", inset:0, color:"#fff", fontSize:10, lineHeight:"12px", textAlign:"center", fontWeight:700}}>✓</span>}
                          </span>
                        </td>
                        <td>
                          <div style={{display:"flex", alignItems:"center", gap:10}}>
                            <div className="avatar" style={{background:d.bailleur.color, color:"#fff"}}>{d.bailleur.avatar}</div>
                            <div>
                              <div style={{fontWeight:500}}>{d.bailleur.name}</div>
                              <div className="muted mono" style={{fontSize:11}}>{d.iban}</div>
                            </div>
                          </div>
                        </td>
                        <td>
                          <ChannelBadge channel={d.channel}/>
                        </td>
                        <td className="right">{fmtFCFA(d.collected)}</td>
                        <td className="right muted">−{d.commission.toLocaleString("fr-FR")}</td>
                        <td className="right" style={{color:"var(--pos-d)", fontWeight:600}}>{fmtFCFA(d.net)}</td>
                      </tr>
                    );
                  })}
                </tbody>
                <tfoot>
                  <tr style={{borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)"}}>
                    <td></td>
                    <td colSpan={2} style={{padding:"12px var(--cell-px)", fontWeight:600, fontSize:12}}>TOTAL · {selCount} virement{selCount>1?"s":""}</td>
                    <td className="right mono" style={{padding:"12px var(--cell-px)", color:"var(--d-ink-3)"}}>{fmtFCFA(toEmit.reduce((s,d)=>s+d.collected,0))}</td>
                    <td className="right mono" style={{padding:"12px var(--cell-px)", color:"var(--d-ink-3)"}}>−{toEmit.reduce((s,d)=>s+d.commission,0).toLocaleString("fr-FR")}</td>
                    <td className="right mono" style={{padding:"12px var(--cell-px)", color:"var(--pos-d)", fontSize:13, fontWeight:600}}>{fmtFCFA(totalSelected)}</td>
                  </tr>
                </tfoot>
              </table>
            </div>
          </div>

          <div className="stack">
            <DisbursementSummary toEmit={toEmit} selected={selected} totalSelected={totalSelected}/>
          </div>
        </div>
      )}

      {tab === "planifies" && (
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Envois planifiés · 30 avril</h3>
              <div className="panel-sub">Exécution automatique à 09:00 si non émis manuellement</div>
            </div>
            <button className="btn btn-ghost">Modifier la planification</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th>Bénéficiaire</th>
                  <th>Canal</th>
                  <th className="right">Montant</th>
                  <th>Date prévue</th>
                  <th>Statut</th>
                </tr>
              </thead>
              <tbody>
                {toEmit.map(d => (
                  <tr key={d.id} onClick={() => onOpenBailleur(d.bailleur)}>
                    <td>
                      <div style={{display:"flex", alignItems:"center", gap:10}}>
                        <div className="avatar" style={{background:d.bailleur.color, color:"#fff"}}>{d.bailleur.avatar}</div>
                        <div style={{fontWeight:500}}>{d.bailleur.name}</div>
                      </div>
                    </td>
                    <td><ChannelBadge channel={d.channel}/></td>
                    <td className="right" style={{color:"var(--pos-d)", fontWeight:500}}>{fmtFCFA(d.net)}</td>
                    <td className="mono muted">{d.scheduled} · 09:00</td>
                    <td><span className="chip info">planifié</span></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {tab === "historique" && (
        <div className="panel">
          <div className="filter-bar" style={{borderTop:"none"}}>
            <div className="top-search" style={{width:280, background:"var(--d-panel)"}}>
              <span style={{color:"var(--d-ink-4)"}}>⌕</span>
              <input placeholder="Référence, bénéficiaire…"/>
            </div>
            <button className="filter-chip active">Tous <span className="k">{HISTORY.length}</span></button>
            {BAILLEUR_MONTHS.slice(0, -1).reverse().map(m => {
              const n = HISTORY.filter(h => h.date === m.paidOn).length;
              return <button key={m.code} className="filter-chip">{m.label.split(" ")[0]} <span className="k">{n}</span></button>;
            })}
            <div className="filter-spacer"/>
            <button className="filter-chip">Trier ▾</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:130}}>Référence</th>
                  <th>Bénéficiaire</th>
                  <th>Canal</th>
                  <th className="right">Montant</th>
                  <th>Date</th>
                  <th>Référence canal</th>
                  <th>Statut</th>
                </tr>
              </thead>
              <tbody>
                {HISTORY.map(h => (
                  <tr key={h.id}>
                    <td className="mono muted" style={{fontSize:11.5}}>{h.id}</td>
                    <td style={{fontWeight:500}}>{h.bailleur}</td>
                    <td><ChannelBadge channel={h.channel}/></td>
                    <td className="right" style={{color: h.amount === 0 ? "var(--d-ink-4)" : "var(--pos-d)", fontWeight:500}}>{h.amount === 0 ? "—" : fmtFCFA(h.amount)}</td>
                    <td className="muted mono" style={{fontSize:11.5}}>{h.date}</td>
                    <td className="muted mono" style={{fontSize:11}}>{h.ref}</td>
                    <td><span className={`chip ${h.status==="émis"?"pos":"neutral"}`}>{h.status}</span></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {tab === "exceptions" && (
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Versements suspendus</h3>
              <div className="panel-sub">Bailleurs sans versement ce mois · revue manuelle requise</div>
            </div>
          </div>
          <div>
            {skipped.length === 0 && (
              <div style={{padding:"40px 24px", textAlign:"center", color:"var(--d-ink-3)", fontSize:12.5}}>Aucune exception · tous les versements sont à émettre.</div>
            )}
            {skipped.map(d => (
              <div key={d.id} style={{padding:"14px 18px", borderBottom:"1px solid var(--d-line-2)", display:"grid", gridTemplateColumns:"auto 1fr auto auto", gap:14, alignItems:"center"}}>
                <div className="avatar" style={{background:d.bailleur.color, color:"#fff"}}>{d.bailleur.avatar}</div>
                <div>
                  <div style={{fontWeight:500}}>{d.bailleur.name}</div>
                  <div className="muted" style={{fontSize:11.5}}>{d.props.length} bien · 0 lot occupé · loyer collecté nul</div>
                </div>
                <div style={{textAlign:"right"}}>
                  <div className="mono" style={{fontSize:11, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.04em"}}>raison</div>
                  <div style={{fontSize:12, color:"var(--warn-d)", fontWeight:500}}>Vacance locative</div>
                </div>
                <div style={{display:"flex", gap:6}}>
                  <button className="btn btn-ghost" style={{padding:"4px 9px", fontSize:11.5}} onClick={()=>onOpenBailleur(d.bailleur)}>Voir bailleur →</button>
                  <button className="btn btn-secondary" style={{padding:"4px 9px", fontSize:11.5}}>Notifier</button>
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {showRunModal && <EmitDisbursementsModal toEmit={toEmit.filter(d => selected.includes(d.id))} total={totalSelected} onEmitted={emitSelected} onClose={() => setShowRunModal(false)}/>}
    </div>
  );
}

function ChannelBadge({ channel }) {
  const cfg = {
    "Wave":          { c:"#001B2E", bg:"#1DC3F8" },
    "Orange Money":  { c:"#fff",     bg:"#FF7A00" },
    "CBAO":          { c:"#fff",     bg:"#1E4FCC" },
    "Ecobank":       { c:"#fff",     bg:"#0F7A4C" },
    "BICIS":         { c:"#fff",     bg:"#9C2B2B" },
  };
  const c = cfg[channel] || { c:"var(--d-ink-2)", bg:"var(--d-elev)" };
  return (
    <span style={{
      fontSize:10.5, fontWeight:600, padding:"2px 8px", borderRadius:3,
      color: c.c, background: c.bg, letterSpacing:"0.02em",
    }}>{channel}</span>
  );
}

/* ─────────────────────────────────────────────────────────
   SUMMARY · selected disbursements
   ───────────────────────────────────────────────────────── */
function DisbursementSummary({ toEmit, selected, totalSelected }) {
  const selRows = toEmit.filter(d => selected.includes(d.id));
  // Group by channel
  const byChannel = {};
  selRows.forEach(d => {
    byChannel[d.channel] = byChannel[d.channel] || { count: 0, total: 0 };
    byChannel[d.channel].count += 1;
    byChannel[d.channel].total += d.net;
  });
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Récapitulatif</h3>
          <div className="panel-sub">{selRows.length} virements sélectionnés</div>
        </div>
      </div>
      <div style={{padding:"18px 20px 8px"}}>
        <div style={{fontSize:10, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:6}}>Montant total à débiter</div>
        <div className="mono" style={{fontSize:26, fontWeight:500, color:"var(--pos-d)", letterSpacing:"-0.025em", marginBottom:14}}>{fmtFCFA(totalSelected)}</div>
        <div style={{fontSize:10, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:8}}>Répartition par canal</div>
      </div>
      <div style={{padding:"0 20px 16px"}}>
        {Object.entries(byChannel).map(([name, v]) => {
          const pct = totalSelected ? Math.round((v.total/totalSelected)*100) : 0;
          return (
            <div key={name} style={{marginBottom:12}}>
              <div style={{display:"flex", justifyContent:"space-between", marginBottom:6, alignItems:"center"}}>
                <span style={{display:"flex", alignItems:"center", gap:8}}>
                  <ChannelBadge channel={name}/>
                  <span className="muted mono" style={{fontSize:11}}>{v.count} virement{v.count > 1 ? "s" : ""}</span>
                </span>
                <span className="mono" style={{fontSize:12.5}}>{fmtFCFA(v.total)}</span>
              </div>
              <div style={{height:5, background:"var(--d-sunken)", borderRadius:2.5, overflow:"hidden"}}>
                <div style={{height:"100%", width:`${pct}%`, background:"var(--blue-d)", borderRadius:2.5}}/>
              </div>
            </div>
          );
        })}
      </div>
      <div style={{padding:"14px 20px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:11.5, color:"var(--d-ink-3)"}}>
        <div style={{display:"flex", justifyContent:"space-between", marginBottom:4}}>
          <span>Compte source</span>
          <span className="mono" style={{color:"var(--d-ink-2)"}}>CBAO · BA Immo · ••• 8841</span>
        </div>
        <div style={{display:"flex", justifyContent:"space-between"}}>
          <span>Solde disponible</span>
          <span className="mono" style={{color:"var(--d-ink)"}}>18,42 M FCFA</span>
        </div>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   CHECKLIST PANEL
   ───────────────────────────────────────────────────────── */
function ChecklistPanel() {
  const items = [
    { done: true,  txt: "Rapprochement bancaire avril terminé",   sub: "42 opérations · 0 écart" },
    { done: true,  txt: "Comptes de gérance générés",              sub: "6 bordereaux · prêts à envoyer" },
    { done: true,  txt: "IBAN bénéficiaires vérifiés",             sub: "Aucun IBAN bloqué ou suspect" },
    { done: false, txt: "Validation comptable",                    sub: "À approuver par K. Diop · DAF" },
    { done: false, txt: "Émission des virements",                  sub: "Bordereau + envois Wave / Orange Money / virement" },
  ];
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Préparation</h3>
          <div className="panel-sub">3 sur 5 étapes complétées</div>
        </div>
      </div>
      <div style={{padding:"6px 0"}}>
        {items.map((it, i) => (
          <div key={i} style={{display:"flex", gap:12, padding:"11px 18px", alignItems:"flex-start", borderBottom: i < items.length-1 ? "1px solid var(--d-line-2)" : "none"}}>
            <span style={{
              width:18, height:18, borderRadius:50,
              border: it.done ? "none" : "1.5px solid var(--d-line)",
              background: it.done ? "var(--pos-d)" : "transparent",
              color:"#fff", fontSize:10, fontWeight:700,
              display:"grid", placeItems:"center", flexShrink:0, marginTop:1,
            }}>{it.done && "✓"}</span>
            <div style={{flex:1}}>
              <div style={{fontSize:12.5, color: it.done ? "var(--d-ink-3)" : "var(--d-ink)", fontWeight:500, textDecoration: it.done ? "line-through" : "none"}}>{it.txt}</div>
              <div style={{fontSize:11.5, color:"var(--d-ink-4)", marginTop:2}}>{it.sub}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   EMIT MODAL · confirmation
   ───────────────────────────────────────────────────────── */
function EmitDisbursementsModal({ toEmit, total, onClose, onEmitted }) {
  const [step, setStep] = React.useState("confirm"); // confirm · processing · done
  const [progress, setProgress] = React.useState(0);

  React.useEffect(() => {
    if (step !== "processing") return;
    const total = toEmit.length;
    let i = 0;
    const itv = setInterval(() => {
      i += 1;
      setProgress(i);
      if (i >= total) { clearInterval(itv); setTimeout(() => { setStep("done"); onEmitted && onEmitted(toEmit.map(d => d.id)); }, 400); }
    }, 320);
    return () => clearInterval(itv);
  }, [step, toEmit.length]);

  return (
    <div onClick={step === "processing" ? undefined : onClose} style={{
      position:"fixed", inset:0, background:"rgba(0,0,0,0.55)", zIndex:300,
      display:"flex", alignItems:"center", justifyContent:"center"
    }}>
      <div onClick={e=>e.stopPropagation()} style={{
        background:"var(--d-panel)", border:"1px solid var(--d-line)", borderRadius:10,
        width:560, color:"var(--d-ink)", fontFamily:"var(--font-sans)"
      }}>
        <div style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div>
            <div style={{fontSize:14, fontWeight:600}}>
              {step === "confirm" ? "Confirmer l'émission" : step === "processing" ? "Émission en cours…" : "Virements émis"}
            </div>
            <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:2}}>
              {step === "confirm" && `${toEmit.length} virements · ${fmtFCFA(total)}`}
              {step === "processing" && `${progress} / ${toEmit.length} traités`}
              {step === "done" && `Tous les virements ont été émis avec succès`}
            </div>
          </div>
          {step !== "processing" && (
            <button onClick={onClose} style={{background:"none", border:"none", color:"var(--d-ink-3)", fontSize:20, cursor:"pointer"}}>×</button>
          )}
        </div>

        {step === "confirm" && (
          <>
            <div style={{padding:"20px 22px", maxHeight:280, overflowY:"auto"}}>
              {toEmit.map(d => (
                <div key={d.id} style={{display:"grid", gridTemplateColumns:"auto 1fr auto", gap:12, padding:"10px 0", borderBottom:"1px solid var(--d-line-2)", alignItems:"center"}}>
                  <div className="avatar" style={{background:d.bailleur.color, color:"#fff"}}>{d.bailleur.avatar}</div>
                  <div>
                    <div style={{fontSize:12.5, fontWeight:500}}>{d.bailleur.name}</div>
                    <div className="mono muted" style={{fontSize:11}}>{d.iban}</div>
                  </div>
                  <div style={{textAlign:"right"}}>
                    <div className="mono" style={{color:"var(--pos-d)", fontSize:13, fontWeight:500}}>{fmtFCFA(d.net)}</div>
                    <ChannelBadge channel={d.channel}/>
                  </div>
                </div>
              ))}
            </div>
            <div style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", display:"flex", alignItems:"center", justifyContent:"space-between"}}>
              <div>
                <div style={{fontSize:11, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600}}>Total à débiter</div>
                <div className="mono" style={{fontSize:18, fontWeight:500, color:"var(--pos-d)"}}>{fmtFCFA(total)}</div>
              </div>
              <div style={{display:"flex", gap:8}}>
                <button className="btn btn-ghost" onClick={onClose}>Annuler</button>
                <button className="btn btn-primary" onClick={() => setStep("processing")}>Émettre · {toEmit.length} virements</button>
              </div>
            </div>
          </>
        )}

        {step === "processing" && (
          <div style={{padding:"40px 22px", textAlign:"center"}}>
            <div style={{fontSize:28, fontWeight:500, color:"var(--blue-d)", fontFamily:"var(--font-mono)", marginBottom:14, letterSpacing:"-0.02em"}}>
              {progress} / {toEmit.length}
            </div>
            <div style={{height:6, background:"var(--d-sunken)", borderRadius:3, overflow:"hidden", maxWidth:420, margin:"0 auto 16px"}}>
              <div style={{height:"100%", width:`${(progress/toEmit.length)*100}%`, background:"var(--blue-d)", borderRadius:3, transition:"width 200ms ease"}}/>
            </div>
            <div style={{fontSize:12, color:"var(--d-ink-3)"}}>
              {progress < toEmit.length ? `Transmission à ${toEmit[Math.min(progress, toEmit.length-1)].channel}…` : "Finalisation…"}
            </div>
          </div>
        )}

        {step === "done" && (
          <>
            <div style={{padding:"32px 22px", textAlign:"center"}}>
              <div style={{width:54, height:54, borderRadius:50, background:"rgba(63,181,131,0.15)", margin:"0 auto 16px", display:"grid", placeItems:"center", color:"var(--pos-d)", fontSize:24, fontWeight:700}}>✓</div>
              <div style={{fontSize:16, fontWeight:500, color:"var(--d-ink)", marginBottom:6}}>{toEmit.length} virements émis</div>
              <div style={{fontSize:12.5, color:"var(--d-ink-3)", maxWidth:380, margin:"0 auto"}}>
                Total débité : <b className="mono" style={{color:"var(--pos-d)"}}>{fmtFCFA(total)}</b>. Les avis de virement ont été envoyés par email à chaque bailleur.
              </div>
            </div>
            <div style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", display:"flex", justifyContent:"flex-end", gap:8}}>
              <button className="btn btn-ghost">Télécharger le récap (PDF)</button>
              <button className="btn btn-primary" onClick={onClose}>Terminer</button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { VersementsPage });

/* ╚══ end pages-versements.jsx ══╝ */
