/* bundle 2/4 */

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

/* ─────────────────────────────────────────────────────────
   CRG — Compte Rendu de Gestion
   Document mensuel transmis à chaque propriétaire (bailleur)
   ───────────────────────────────────────────────────────── */

// Charges catalogue — categories typically deductible from a property owner statement
const CRG_CHARGE_CATEGORIES = [
  { k:"entretien",  label:"Entretien courant",   color:"#6192FF" },
  { k:"reparation", label:"Réparations",          color:"#F2933E" },
  { k:"assurance",  label:"Assurance PNO",        color:"#9B8AE6" },
  { k:"taxes",      label:"Taxes foncières",      color:"#C99550" },
  { k:"syndic",     label:"Charges copropriété",  color:"#3FB583" },
  { k:"intervention",label:"Intervention agence",  color:"#E0A74A" },
];

// Deterministic per-property charges given a property + period seed
function chargesFor(p, periodSeed) {
  const seed = (p.id.charCodeAt(2) + periodSeed) % 100;
  const out = [];
  // Always: small monthly maintenance line for multi-unit buildings
  if (p.units > 5) {
    out.push({ cat:"entretien", label:"Entretien parties communes · " + p.address.split(",")[0], ref:"F-2604-" + (100+seed), amount: 45_000 });
  }
  if (p.units > 5) {
    out.push({ cat:"syndic", label:"Charges copropriété · trimestre courant", ref:"SY-26-Q2-" + (300+seed%50), amount: 28_000 });
  }
  // Occasional repairs
  if (seed % 7 === 0) {
    out.push({ cat:"reparation", label:"Plomberie · " + (p.units > 1 ? "lot A-" + (200 + seed%10) : "douche principale"), ref:"F-2604-" + (200+seed), amount: 75_000 });
  }
  if (seed % 11 === 0) {
    out.push({ cat:"reparation", label:"Électricité · remplacement disjoncteur", ref:"F-2604-" + (250+seed), amount: 42_000 });
  }
  // Insurance: prorated monthly
  if (seed % 3 === 0) {
    out.push({ cat:"assurance", label:"Assurance Propriétaire Non Occupant · avr", ref:"SUNU-PNO-" + (1000+seed), amount: 18_500 });
  }
  // Taxe foncière (rare, but yearly prorated)
  if (seed % 9 === 0 && periodSeed % 12 === 4) {
    out.push({ cat:"taxes", label:"Taxe foncière · acompte avril", ref:"DGID-26-" + (8000+seed), amount: 95_000 });
  }
  // Agency intervention fee
  if (p.units > 1 && seed % 5 === 0) {
    out.push({ cat:"intervention", label:"Intervention état des lieux entrant", ref:"BA-26-" + (400+seed), amount: 25_000 });
  }
  return out;
}

// Build per-tenant rent lines for a property, in a CRG-friendly shape
function rentLinesFor(p, allTenants) {
  const tenants = allTenants.filter(t => t.propertyId === p.id);
  if (tenants.length === 0) {
    // Vacant lot
    return [{ unit: p.units > 1 ? "—" : "Bien", tenant: "Lot vacant", expected: p.mrr / Math.max(p.units,1), collected: 0, method:"—", paidOn:"—", status:"vacance" }];
  }
  return tenants.map(t => ({
    unit: t.unit,
    tenant: t.name,
    tenantId: t.id,
    expected: t.rent,
    collected: t.status === "à jour" ? t.rent : t.status === "en retard" ? 0 : 0,
    method: t.status === "à jour" ? (t.id.endsWith("3")||t.id.endsWith("1") ? "Wave" : t.id.endsWith("4") ? "Orange Money" : "Virement") : "—",
    paidOn: t.status === "à jour" ? (5 + (t.id.charCodeAt(3) % 14)) + " avr" : "—",
    status: t.status,
  }));
}

/* ─── CRG status override store (CRG dérivés → on persiste les changements) ─── */
const CRG_OVERRIDE = {};
const _crgListeners = new Set();
function _notifyCRG(){ _crgListeners.forEach(f=>f()); }
function setCRGStatus(id, status){ CRG_OVERRIDE[id] = status; _notifyCRG(); }
function useCRGStore(){ const [,t]=React.useReducer(x=>x+1,0); React.useEffect(()=>{ _crgListeners.add(t); return ()=>_crgListeners.delete(t); },[]); }

// Build CRG entries per bailleur per period — synthesised from MOCK + BAILLEURS
function buildCRGs(period) {
  const periodSeed = period === "mai-26" ? 5 : period === "mar-26" ? 3 : 4;
  return BAILLEURS.map((b, i) => {
    const props = MOCK.properties.filter(p => p.owner === b.name).map(p => {
      const rentLines = rentLinesFor(p, MOCK.tenants);
      const propCharges = chargesFor(p, periodSeed);
      const expected = rentLines.reduce((s,l)=>s+l.expected, 0);
      const collected = rentLines.reduce((s,l)=>s+l.collected, 0);
      const chargesTotal = propCharges.reduce((s,c)=>s+c.amount, 0);
      return { ...p, rentLines, charges: propCharges, expected, collected, chargesTotal };
    });

    const expected = props.reduce((s,p) => s+p.expected, 0);
    const collected = props.reduce((s,p) => s+p.collected, 0);
    const commission = Math.round(collected * b.commissionRate);
    const chargesTotal = props.reduce((s,p) => s+p.chargesTotal, 0);
    const net = collected - commission - chargesTotal;

    // Status logic
    let status, sentAt = "—", validatedAt = "—";
    if (collected <= 0) status = "non applicable";
    else if (period === "mai-26") status = "à générer";
    else if (i === 0) { status = "validé"; sentAt = "01 avr · 09:14"; validatedAt = "03 avr"; }
    else if (i === 2) { status = "envoyé"; sentAt = "01 avr · 09:14"; }
    else if (i === 4) { status = "à générer"; }
    else { status = "généré"; }

    const id = `CRG-${period === "mai-26" ? "2026-05-" : period === "mar-26" ? "2026-03-" : "2026-04-"}${b.id.replace("B-","0")}`;
    if (CRG_OVERRIDE[id]) status = CRG_OVERRIDE[id];
    return {
      id,
      bailleur: b, props, expected, collected, commission, chargesTotal, charges: chargesTotal, net,
      period, status, sentAt, validatedAt,
    };
  });
}

const CRG_HISTORY = [
  { id:"CRG-2026-03-001", date:"01 mars", bailleur:"M. Abdoulaye Ba",    period:"Mars 2026",    net:1_840_000, status:"validé",  sent:"01 mars · auto", validated:"04 mars" },
  { id:"CRG-2026-03-003", date:"01 mars", bailleur:"Horizon SCI",         period:"Mars 2026",    net:3_120_000, status:"validé",  sent:"01 mars · auto", validated:"02 mars" },
  { id:"CRG-2026-03-004", date:"01 mars", bailleur:"Katos Consulting",    period:"Mars 2026",    net:2_564_000, status:"validé",  sent:"01 mars · auto", validated:"05 mars" },
  { id:"CRG-2026-03-002", date:"01 mars", bailleur:"Mme Aïssatou Ba",    period:"Mars 2026",    net:782_000,   status:"validé",  sent:"01 mars · auto", validated:"03 mars" },
  { id:"CRG-2026-03-006", date:"01 mars", bailleur:"Mme Fatou Diop",      period:"Mars 2026",    net:1_017_000, status:"validé",  sent:"01 mars · auto", validated:"06 mars" },
  { id:"CRG-2026-02-001", date:"01 fév",  bailleur:"M. Abdoulaye Ba",    period:"Février 2026", net:1_840_000, status:"archivé", sent:"01 fév · auto",  validated:"04 fév"  },
  { id:"CRG-2026-02-003", date:"01 fév",  bailleur:"Horizon SCI",         period:"Février 2026", net:3_120_000, status:"archivé", sent:"01 fév · auto",  validated:"02 fév"  },
];

function CRGPage({ onOpenBailleur }) {
  useCRGStore();
  const [period, setPeriod] = React.useState("avr-26");
  const [statusFilter, setStatusFilter] = React.useState("all");
  const [q, setQ] = React.useState("");
  const [tab, setTab] = React.useState("current");
  const [showGenerate, setShowGenerate] = React.useState(false);
  const [showNew, setShowNew] = React.useState(false);

  const crgs = buildCRGs(period);
  const applicable = crgs.filter(c => c.status !== "non applicable");

  const filtered = applicable.filter(c => {
    if (statusFilter !== "all" && c.status !== statusFilter) return false;
    if (q && !`${c.bailleur.name} ${c.id}`.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });

  const [selectedId, setSelectedId] = React.useState(crgs[0].id);
  // Keep selection valid when period changes
  React.useEffect(() => {
    if (!crgs.find(c => c.id === selectedId)) setSelectedId(crgs[0].id);
  }, [period]); // eslint-disable-line

  const selected = crgs.find(c => c.id === selectedId) || crgs[0];

  // KPI calc on applicable
  const counts = {
    aGenerer: applicable.filter(c => c.status === "à générer").length,
    genere:   applicable.filter(c => c.status === "généré").length,
    envoye:   applicable.filter(c => c.status === "envoyé").length,
    valide:   applicable.filter(c => c.status === "validé").length,
  };
  const totalNet = applicable.reduce((s, c) => s + c.net, 0);
  const enAttente = counts.envoye; // "envoyé non encore validé"

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Comptes rendus de gestion</h1>
          <div className="page-sub">Document mensuel transmis à chaque bailleur · base de calcul des versements</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter (.zip)</button>
          <button className="btn btn-secondary" onClick={() => setShowGenerate(true)}>Génération en lot</button>
          <button className="btn btn-primary" onClick={() => setShowNew(true)}>+ Nouveau CRG</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>CRG · avril 2026</span></div>
          <div className="kpi-value num">{applicable.length}<span className="kpi-unit">documents</span></div>
          <div className="kpi-meta"><span style={{color:"var(--pos-d)", fontWeight:600}}>{counts.valide + counts.envoye} envoyés</span><span>· {counts.aGenerer} à générer</span></div>
          <div className="kpi-spark"><Spark values={[5,5,5,5,5,5,5,5,6,6,5,5]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Net consolidé à verser</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>Base pour les versements</span></div>
        </div>
        <div className="kpi alert">
          <div className="kpi-label"><span>En attente de validation</span>{enAttente > 0 && <span style={{color:"var(--orange)", fontWeight:600}}>{enAttente} {enAttente > 1 ? "bailleurs" : "bailleur"}</span>}</div>
          <div className="kpi-value num" style={{color: enAttente > 0 ? "var(--orange)" : "var(--d-ink)"}}>{enAttente}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>document{enAttente > 1 ? "s" : ""}</span></div>
          <div className="kpi-meta"><span>SLA 48 h après envoi</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Délai moyen validation</span></div>
          <div className="kpi-value num">3,1<span className="kpi-unit">jours</span></div>
          <div className="kpi-meta"><span className="delta pos">▼ 0,4 j</span><span>vs mars · cible &lt; 5 j</span></div>
        </div>
      </div>

      {/* Tabs */}
      <div className="tabs">
        <button className={"tab" + (tab==="current"?" active":"")} onClick={()=>setTab("current")}>En cours <span className="tab-count">{applicable.length}</span></button>
        <button className={"tab" + (tab==="history"?" active":"")} onClick={()=>setTab("history")}>Historique <span className="tab-count">{CRG_HISTORY.length}</span></button>
        <button className={"tab" + (tab==="templates"?" active":"")} onClick={()=>setTab("templates")}>Modèles</button>
      </div>

      {tab === "current" && (
        <>
          <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="Bailleur, référence…"/>
                </div>
                <div className="seg">
                  {[["mar-26","Mars"],["avr-26","Avril"],["mai-26","Mai"]].map(([k,l])=>(
                    <button key={k} className={"seg-btn"+(period===k?" active":"")} onClick={()=>setPeriod(k)}>{l}</button>
                  ))}
                </div>
                <div className="filter-spacer"/>
                {[
                  ["all","Tous",applicable.length],
                  ["à générer","À générer",counts.aGenerer],
                  ["généré","Générés",counts.genere],
                  ["envoyé","Envoyés",counts.envoye],
                  ["validé","Validés",counts.valide],
                ].map(([k,l,n])=>(
                  <button key={k} className={"filter-chip" + (statusFilter === k ? " active" : "")} onClick={()=>setStatusFilter(k)}>{l} <span className="k">{n}</span></button>
                ))}
              </div>
              <div className="table-wrap">
                <table className="tbl">
                  <thead>
                    <tr>
                      <th style={{width:140}}>Référence</th>
                      <th>Bailleur</th>
                      <th className="right">Encaissé</th>
                      <th className="right">Net à verser</th>
                      <th>Statut</th>
                    </tr>
                  </thead>
                  <tbody>
                    {filtered.length === 0 && <tr><td colSpan={5} className="muted" style={{textAlign:"center", padding:28}}>Aucun CRG ne correspond.</td></tr>}
                    {filtered.map(c => (
                      <tr key={c.id} onClick={() => setSelectedId(c.id)} style={{background: c.id === selectedId ? "var(--d-elev)" : undefined}}>
                        <td className="mono" style={{fontSize:11.5, color: c.id===selectedId ? "var(--d-ink)" : "var(--d-ink-2)"}}>{c.id}</td>
                        <td>
                          <div style={{display:"flex", alignItems:"center", gap:10}}>
                            <div className="avatar" style={{background:c.bailleur.color, color:"#fff"}}>{c.bailleur.avatar}</div>
                            <div>
                              <div style={{fontWeight:500}}>{c.bailleur.name}</div>
                              <div className="muted" style={{fontSize:11}}>{c.props.length} bien{c.props.length>1?"s":""}</div>
                            </div>
                          </div>
                        </td>
                        <td className="right">{fmtFCFA(c.collected)}</td>
                        <td className="right" style={{color:"var(--pos-d)", fontWeight:500}}>{fmtFCFA(c.net)}</td>
                        <td><CRGChip status={c.status}/></td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>

            {/* Right: paper preview */}
            <div className="stack">
              <CRGPreview crg={selected} onOpenBailleur={onOpenBailleur}/>
            </div>
          </div>

          {/* Workflow */}
          <div className="panel" style={{marginTop:16}}>
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Cycle du CRG</h3>
                <div className="panel-sub">5 étapes du document mensuel jusqu'au versement</div>
              </div>
            </div>
            <CRGWorkflow status={selected.status}/>
          </div>
        </>
      )}

      {tab === "history" && (
        <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, bailleur, période…"/>
            </div>
            <button className="filter-chip active">Toutes <span className="k">{CRG_HISTORY.length}</span></button>
            <button className="filter-chip">Mars <span className="k">5</span></button>
            <button className="filter-chip">Février <span className="k">2</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:140}}>Référence</th>
                  <th>Bailleur</th>
                  <th>Période</th>
                  <th className="right">Net versé</th>
                  <th>Envoi</th>
                  <th>Validation</th>
                  <th>Statut</th>
                </tr>
              </thead>
              <tbody>
                {CRG_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 className="muted">{h.period}</td>
                    <td className="right" style={{color:"var(--pos-d)", fontWeight:500}}>{fmtFCFA(h.net)}</td>
                    <td className="muted mono" style={{fontSize:11.5}}>{h.sent}</td>
                    <td className="muted mono" style={{fontSize:11.5}}>{h.validated}</td>
                    <td><span className={`chip ${h.status === "validé" ? "pos" : "neutral"}`}>{h.status}</span></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {tab === "templates" && (
        <div className="grid-2">
          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Modèles CRG</h3>
                <div className="panel-sub">Personnalisez la mise en page par bailleur ou par type d'entité</div>
              </div>
              <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}}>+ Nouveau modèle</button>
            </div>
            <div>
              {[
                { name:"CRG standard · particulier",   used:4, desc:"En-tête simplifié, sans TVA",                  active:true },
                { name:"CRG société · OHADA",           used:2, desc:"Récapitulatif TVA · références juridiques",  active:true },
                { name:"CRG bailleur étranger",         used:1, desc:"Bilingue FR/EN · conversion EUR indicative",  active:false },
                { name:"CRG simplifié (1 bien)",        used:1, desc:"Une page · idéal villas individuelles",       active:false },
              ].map((t, i) => (
                <div key={i} style={{padding:"14px 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, display:"flex", gap:8, alignItems:"center"}}>
                      {t.name}
                      {t.active && <span style={{fontSize:9.5, fontWeight:600, color:"var(--pos-d)", background:"rgba(63,181,131,0.10)", padding:"1px 6px", borderRadius:2, letterSpacing:"0.04em"}}>ACTIF</span>}
                    </div>
                    <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>{t.desc}</div>
                  </div>
                  <div style={{textAlign:"right"}}>
                    <div className="mono" style={{fontSize:11, color:"var(--d-ink-3)"}}>{t.used} bailleur{t.used>1?"s":""}</div>
                  </div>
                  <button className="btn btn-ghost" style={{padding:"4px 9px", fontSize:11.5}}>Éditer</button>
                </div>
              ))}
            </div>
          </div>
          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">Mentions obligatoires OHADA</h3>
                <div className="panel-sub">Vérifiez la conformité de vos modèles</div>
              </div>
            </div>
            <div style={{padding:"6px 0"}}>
              {[
                "Identité complète du mandant et du gérant",
                "Numéro RC + NINEA du gérant",
                "Période couverte clairement indiquée",
                "Détail des loyers encaissés par locataire",
                "Commission et taux appliqué",
                "Charges récupérables justifiées",
                "Montant net dû avec signature",
              ].map((line, i) => (
                <div key={i} style={{display:"flex", gap:10, padding:"9px 18px", alignItems:"center", borderBottom: i<6?"1px solid var(--d-line-2)":"none"}}>
                  <span style={{width:14, height:14, borderRadius:50, background:"var(--pos-d)", color:"#fff", fontSize:9, fontWeight:700, display:"grid", placeItems:"center", flexShrink:0}}>✓</span>
                  <span style={{fontSize:12, color:"var(--d-ink-2)"}}>{line}</span>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}

      {showGenerate && <CRGGenerateModal applicable={applicable} onClose={() => setShowGenerate(false)} onConfirm={()=>{ applicable.filter(c=>c.status==="à générer").forEach(c=>setCRGStatus(c.id,"généré")); setShowGenerate(false); }}/>}
      {showNew && <NewCRGModal period={period} onClose={()=>setShowNew(false)} onGenerate={(crg)=>{ setCRGStatus(crg.id,"généré"); setPeriod(crg.period); setSelectedId(crg.id); setShowNew(false); }}/>}
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   CHIPS, PREVIEW, WORKFLOW, MODAL
   ───────────────────────────────────────────────────────── */
function CRGChip({ status }) {
  const map = {
    "à générer":      ["warn", "à générer"],
    "généré":         ["info", "généré"],
    "envoyé":         ["info", "envoyé"],
    "validé":         ["pos",  "validé"],
    "archivé":        ["neutral","archivé"],
    "non applicable": ["neutral","sans objet"],
  };
  const [cls, label] = map[status] || ["neutral", status];
  return <span className={`chip ${cls}`}>{label}</span>;
}

function CRGPreview({ crg, onOpenBailleur }) {
  const b = crg.bailleur;
  const periodLabel = crg.period === "mai-26" ? "Mai 2026" : crg.period === "mar-26" ? "Mars 2026" : "Avril 2026";

  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Aperçu · {crg.id}</h3>
          <div className="panel-sub">{b.name} · {periodLabel}</div>
        </div>
        <div className="panel-head-right">
          <button className="btn btn-ghost" style={{padding:"5px 9px", fontSize:11.5}} onClick={() => onOpenBailleur(b)}>Voir bailleur →</button>
          <button className="btn btn-ghost" style={{padding:"5px 9px", fontSize:11.5}}>⬇ PDF</button>
          {crg.status === "à générer" ? (
            <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}}>Générer</button>
          ) : crg.status === "généré" ? (
            <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}}>Envoyer</button>
          ) : (
            <button className="btn btn-secondary" style={{padding:"5px 10px", fontSize:11.5}}>Renvoyer</button>
          )}
        </div>
      </div>

      {/* Paper preview */}
      <div style={{padding:"18px 20px"}}>
        <div style={{background:"#FAFAF7", color:"#1A1A1A", borderRadius:6, padding:"22px 24px", fontFamily:"var(--font-sans)", border:"1px solid #E4E4DE", position:"relative", overflow:"hidden"}}>
          {/* Status watermark */}
          {(crg.status === "à générer") && (
            <div style={{position:"absolute", top:60, left:"50%", transform:"translateX(-50%) rotate(-12deg)", fontSize:42, color:"rgba(138,90,16,0.10)", fontWeight:700, letterSpacing:"0.1em", pointerEvents:"none"}}>BROUILLON</div>
          )}
          {/* Header */}
          <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", marginBottom:16, paddingBottom:14, borderBottom:"1px solid #E4E4DE"}}>
            <div>
              <div style={{fontSize:9.5, color:"#7B7B73", letterSpacing:"0.12em", textTransform:"uppercase", fontWeight:600, marginBottom:4}}>Compte rendu de gestion</div>
              <div style={{fontSize:17, fontWeight:600, color:"#1A1A1A", letterSpacing:"-0.01em", marginBottom:2}}>{b.name}</div>
              <div style={{fontSize:11, color:"#5B5B53"}}>{b.entity} · Période : <b>{periodLabel}</b></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 · NINEA 005 482 6 G3</div>
              <div style={{fontSize:10, color:"#7B7B73"}}>RC SN DKR 2019 B 14 228</div>
              <div className="mono" style={{fontSize:10, color:"#7B7B73", marginTop:6}}>Réf. {crg.id}</div>
            </div>
          </div>

          {crg.status === "non applicable" ? (
            <div style={{padding:"40px 20px", textAlign:"center", color:"#7B7B73", fontSize:13}}>
              <div style={{fontSize:30, fontWeight:300, marginBottom:8, color:"#A19E92"}}>—</div>
              Aucun encaissement sur la période. Le CRG n'est pas applicable.
            </div>
          ) : (
            <>
              {/* === 1 · ENCAISSEMENTS DÉTAILLÉS === */}
              <SectionTitle n={1} title="Encaissements détaillés" sub={`${crg.props.length} bien${crg.props.length>1?"s":""} · ${crg.props.reduce((s,p)=>s+p.rentLines.length,0)} ligne${crg.props.reduce((s,p)=>s+p.rentLines.length,0)>1?"s":""} de loyer`}/>
              {crg.props.map((p, pi) => (
                <div key={p.id} style={{marginBottom:14, border:"1px solid #EEEEE6", borderRadius:4, overflow:"hidden"}}>
                  <div style={{padding:"8px 12px", background:"#F2EFEA", display:"flex", justifyContent:"space-between", alignItems:"baseline"}}>
                    <div>
                      <span style={{fontSize:11.5, fontWeight:600, color:"#1A1A1A"}}>{p.name}</span>
                      <span style={{fontSize:10, color:"#7B7B73", marginLeft:8, fontFamily:"var(--font-mono)"}}>{p.id}</span>
                      <span style={{fontSize:10, color:"#7B7B73", marginLeft:8}}>· {p.address}</span>
                    </div>
                    <span style={{fontSize:10, color:"#7B7B73", fontFamily:"var(--font-mono)"}}>{p.occupied}/{p.units} lots</span>
                  </div>
                  <table style={{width:"100%", borderCollapse:"collapse", fontSize:10.5}}>
                    <thead>
                      <tr style={{textAlign:"left", color:"#5B5B53", fontSize:9, textTransform:"uppercase", letterSpacing:"0.05em", fontWeight:600}}>
                        <th style={{padding:"6px 10px", borderBottom:"1px solid #EEEEE6", width:48}}>Lot</th>
                        <th style={{padding:"6px 10px", borderBottom:"1px solid #EEEEE6"}}>Locataire</th>
                        <th style={{padding:"6px 10px", borderBottom:"1px solid #EEEEE6"}}>Réglé le</th>
                        <th style={{padding:"6px 10px", borderBottom:"1px solid #EEEEE6"}}>Mode</th>
                        <th style={{padding:"6px 10px", borderBottom:"1px solid #EEEEE6", textAlign:"right"}}>Théorique</th>
                        <th style={{padding:"6px 10px", borderBottom:"1px solid #EEEEE6", textAlign:"right"}}>Encaissé</th>
                      </tr>
                    </thead>
                    <tbody style={{fontFamily:"var(--font-mono)"}}>
                      {p.rentLines.map((l, li) => {
                        const gap = l.expected - l.collected;
                        return (
                          <tr key={li}>
                            <td style={{padding:"6px 10px", borderBottom:"1px solid #F4F2EC", color:"#5B5B53"}}>{l.unit}</td>
                            <td style={{padding:"6px 10px", borderBottom:"1px solid #F4F2EC", fontFamily:"var(--font-sans)", color: l.status === "vacance" ? "#A19E92" : "#1A1A1A", fontStyle: l.status === "vacance" ? "italic" : "normal"}}>{l.tenant}</td>
                            <td style={{padding:"6px 10px", borderBottom:"1px solid #F4F2EC", color:"#7B7B73"}}>{l.paidOn}</td>
                            <td style={{padding:"6px 10px", borderBottom:"1px solid #F4F2EC", color:"#7B7B73", fontFamily:"var(--font-sans)"}}>{l.method}</td>
                            <td style={{padding:"6px 10px", borderBottom:"1px solid #F4F2EC", textAlign:"right"}}>{l.expected.toLocaleString("fr-FR")}</td>
                            <td style={{padding:"6px 10px", borderBottom:"1px solid #F4F2EC", textAlign:"right", fontWeight: l.collected ? 500 : 400, color: l.collected ? "#0F7A4C" : "#A19E92"}}>
                              {l.collected ? l.collected.toLocaleString("fr-FR") : "—"}
                              {gap > 0 && l.status !== "vacance" && (
                                <div style={{fontSize:8.5, color:"#9A2A2A", fontWeight:600, marginTop:1, letterSpacing:"0.03em"}}>impayé · {gap.toLocaleString("fr-FR")}</div>
                              )}
                            </td>
                          </tr>
                        );
                      })}
                      <tr style={{fontWeight:600, background:"#FAF7F1"}}>
                        <td colSpan={4} style={{padding:"7px 10px", fontFamily:"var(--font-sans)", fontSize:10, textTransform:"uppercase", letterSpacing:"0.04em", color:"#5B5B53"}}>Sous-total · {p.name}</td>
                        <td style={{padding:"7px 10px", textAlign:"right"}}>{p.expected.toLocaleString("fr-FR")}</td>
                        <td style={{padding:"7px 10px", textAlign:"right", color:"#0F7A4C"}}>{p.collected.toLocaleString("fr-FR")}</td>
                      </tr>
                    </tbody>
                  </table>
                </div>
              ))}
              {/* Total encaissements */}
              <div style={{display:"flex", justifyContent:"space-between", padding:"10px 14px", background:"#1A1A1A", color:"#FAFAF7", borderRadius:4, marginBottom:18}}>
                <span style={{fontSize:11, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600}}>Total encaissé · {periodLabel}</span>
                <span style={{fontFamily:"var(--font-mono)", fontSize:13, fontWeight:600}}>{crg.collected.toLocaleString("fr-FR")} FCFA</span>
              </div>

              {/* === 2 · CHARGES DÉDUCTIBLES === */}
              <SectionTitle n={2} title="Charges déductibles" sub={`${crg.props.reduce((s,p)=>s+p.charges.length,0)} ligne${crg.props.reduce((s,p)=>s+p.charges.length,0)>1?"s":""} · payées par l'agence pour le compte du bailleur`}/>
              {crg.props.every(p => p.charges.length === 0) ? (
                <div style={{padding:"16px 14px", marginBottom:16, fontSize:11, color:"#7B7B73", fontStyle:"italic", textAlign:"center", background:"#FAF7F1", borderRadius:4}}>
                  Aucune charge déductible sur la période.
                </div>
              ) : (
                <>
                  {crg.props.filter(p => p.charges.length > 0).map(p => (
                    <div key={p.id} style={{marginBottom:12, border:"1px solid #EEEEE6", borderRadius:4, overflow:"hidden"}}>
                      <div style={{padding:"7px 12px", background:"#F2EFEA", display:"flex", justifyContent:"space-between"}}>
                        <span style={{fontSize:11.5, fontWeight:600, color:"#1A1A1A"}}>{p.name}</span>
                        <span style={{fontFamily:"var(--font-mono)", fontSize:11, color:"#9A2A2A", fontWeight:500}}>−{p.chargesTotal.toLocaleString("fr-FR")}</span>
                      </div>
                      <table style={{width:"100%", borderCollapse:"collapse", fontSize:10.5}}>
                        <tbody style={{fontFamily:"var(--font-mono)"}}>
                          {p.charges.map((c, ci) => {
                            const cat = CRG_CHARGE_CATEGORIES.find(x => x.k === c.cat);
                            return (
                              <tr key={ci}>
                                <td style={{padding:"6px 10px", borderBottom: ci < p.charges.length-1 ? "1px solid #F4F2EC" : "none", width:130, fontFamily:"var(--font-sans)"}}>
                                  <span style={{fontSize:9, fontWeight:600, color:cat?.color || "#5B5B53", textTransform:"uppercase", letterSpacing:"0.04em"}}>{cat?.label || c.cat}</span>
                                </td>
                                <td style={{padding:"6px 10px", borderBottom: ci < p.charges.length-1 ? "1px solid #F4F2EC" : "none", fontFamily:"var(--font-sans)", color:"#1A1A1A"}}>{c.label}</td>
                                <td style={{padding:"6px 10px", borderBottom: ci < p.charges.length-1 ? "1px solid #F4F2EC" : "none", color:"#7B7B73", width:120, textAlign:"right"}}>
                                  <span style={{fontSize:9.5, color:"#7B7B73"}}>réf. </span>{c.ref}
                                </td>
                                <td style={{padding:"6px 10px", borderBottom: ci < p.charges.length-1 ? "1px solid #F4F2EC" : "none", textAlign:"right", width:90, color:"#9A2A2A"}}>−{c.amount.toLocaleString("fr-FR")}</td>
                              </tr>
                            );
                          })}
                        </tbody>
                      </table>
                    </div>
                  ))}
                  <div style={{display:"flex", justifyContent:"space-between", padding:"10px 14px", background:"#FAF7F1", border:"1px dashed #E4E4DE", borderRadius:4, marginBottom:18}}>
                    <span style={{fontSize:11, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#5B5B53"}}>Total charges déductibles</span>
                    <span style={{fontFamily:"var(--font-mono)", fontSize:12, fontWeight:600, color:"#9A2A2A"}}>−{crg.chargesTotal.toLocaleString("fr-FR")} FCFA</span>
                  </div>
                </>
              )}

              {/* === 3 · COMMISSION === */}
              <SectionTitle n={3} title="Commission de gérance" sub={`Taux ${(b.commissionRate*100).toFixed(b.commissionRate*100%1===0?0:1).replace(".",",")}% · appliqué sur les loyers encaissés`}/>
              <table style={{width:"100%", borderCollapse:"collapse", fontSize:10.5, marginBottom:18, border:"1px solid #EEEEE6", borderRadius:4, overflow:"hidden"}}>
                <tbody style={{fontFamily:"var(--font-mono)"}}>
                  {crg.props.map(p => (
                    <tr key={p.id}>
                      <td style={{padding:"7px 12px", borderBottom:"1px solid #F4F2EC", fontFamily:"var(--font-sans)", color:"#1A1A1A"}}>{p.name}</td>
                      <td style={{padding:"7px 12px", borderBottom:"1px solid #F4F2EC", textAlign:"right", color:"#7B7B73"}}>{p.collected.toLocaleString("fr-FR")} × {(b.commissionRate*100).toFixed(b.commissionRate*100%1===0?0:1).replace(".",",")}%</td>
                      <td style={{padding:"7px 12px", borderBottom:"1px solid #F4F2EC", textAlign:"right", color:"#9A2A2A", width:110}}>−{Math.round(p.collected*b.commissionRate).toLocaleString("fr-FR")}</td>
                    </tr>
                  ))}
                  <tr style={{fontWeight:600, background:"#FAF7F1"}}>
                    <td style={{padding:"8px 12px", fontFamily:"var(--font-sans)", fontSize:10, textTransform:"uppercase", letterSpacing:"0.04em", color:"#5B5B53"}}>Total commission</td>
                    <td style={{padding:"8px 12px", textAlign:"right", color:"#7B7B73", fontSize:10}}>HT · exonérée TVA</td>
                    <td style={{padding:"8px 12px", textAlign:"right", color:"#9A2A2A"}}>−{crg.commission.toLocaleString("fr-FR")}</td>
                  </tr>
                </tbody>
              </table>

              {/* === 4 · RÉCAPITULATIF & NET === */}
              <SectionTitle n={4} title="Récapitulatif comptable"/>
              <div style={{background:"#F2EFEA", borderRadius:4, padding:"14px 16px", marginBottom:14}}>
                <RecapLine label="Loyers encaissés" value={crg.collected} sign="+"/>
                <RecapLine label={`Commission agence · ${(b.commissionRate*100).toFixed(b.commissionRate*100%1===0?0:1).replace(".",",")}%`} value={crg.commission} sign="−"/>
                <RecapLine label="Charges déductibles" value={crg.chargesTotal} sign="−"/>
                <div style={{borderTop:"1px solid #E4E4DE", marginTop:10, paddingTop:12, display:"flex", justifyContent:"space-between", alignItems:"baseline"}}>
                  <span style={{fontSize:11, color:"#5B5B53", textTransform:"uppercase", letterSpacing:"0.08em", fontWeight:700}}>Net à verser au bailleur</span>
                  <span style={{fontFamily:"var(--font-mono)", fontSize:18, fontWeight:700, color:"#0F7A4C", letterSpacing:"-0.01em"}}>{crg.net.toLocaleString("fr-FR")} FCFA</span>
                </div>
              </div>

              {/* 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"}}>Versement prévu</div>
                  <div>Virement sur <b style={{color:"#1A1A1A"}}>{b.bank.split(" · ")[0]}</b></div>
                  <div className="mono" style={{fontSize:10}}>{b.bank.split(" · ")[1]}</div>
                  <div>Date : <b style={{color:"#1A1A1A"}}>30 {periodLabel.toLowerCase().replace(" 2026","")} 2026</b></div>
                </div>
                <div style={{textAlign:"right"}}>
                  <div style={{textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, fontSize:9, marginBottom:4, color:"#7B7B73"}}>Établi par</div>
                  <div>Souleymane Ndoye · Gestionnaire</div>
                  <div className="mono">souleymane@ba-immo.sn</div>
                  <div style={{marginTop:6, fontSize:9, color:"#A19E92"}}>Signature électronique · {crg.sentAt !== "—" ? crg.sentAt : "à signer"}</div>
                </div>
              </div>
            </>
          )}
        </div>
      </div>

      {/* Action timeline */}
      {crg.status !== "non applicable" && (
        <div style={{padding:"12px 22px 16px", borderTop:"1px solid var(--d-line)"}}>
          <div style={{fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:10}}>Activité</div>
          <div style={{display:"flex", flexDirection:"column", gap:8, fontSize:11.5}}>
            {crg.status === "validé" && (
              <Timeline icon="✓" color="var(--pos-d)" txt={`Validé par ${b.name}`} t={crg.validatedAt}/>
            )}
            {(crg.status === "envoyé" || crg.status === "validé") && (
              <Timeline icon="↗" color="var(--blue-d)" txt="Envoyé par email + WhatsApp" t={crg.sentAt}/>
            )}
            {(crg.status === "généré" || crg.status === "envoyé" || crg.status === "validé") && (
              <Timeline icon="●" color="var(--d-ink-3)" txt="CRG généré · 1 page A4" t="01 avr · 06:00"/>
            )}
            {crg.status === "à générer" && (
              <Timeline icon="…" color="var(--warn-d)" txt="En attente de génération" t={`Auto · 1 ${periodLabel.toLowerCase().replace(" 2026","")} · 06:00`}/>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

function Timeline({ icon, color, txt, t }) {
  return (
    <div style={{display:"flex", gap:10, alignItems:"center"}}>
      <span style={{width:16, height:16, borderRadius:50, background:`${color}26`, color, display:"grid", placeItems:"center", fontSize:9, fontWeight:700, flexShrink:0}}>{icon}</span>
      <span style={{flex:1, color:"var(--d-ink-2)"}}>{txt}</span>
      <span className="mono" style={{color:"var(--d-ink-4)", fontSize:10.5}}>{t}</span>
    </div>
  );
}

/* Helpers for paper preview sections */
function SectionTitle({ n, title, sub }) {
  return (
    <div style={{display:"flex", alignItems:"baseline", gap:8, marginTop:14, marginBottom:10}}>
      <span style={{fontFamily:"var(--font-mono)", fontSize:9.5, color:"#9A2A2A", fontWeight:700, border:"1px solid #E4D5D2", padding:"1px 5px", borderRadius:2, background:"#FAF1EF"}}>{String(n).padStart(2,"0")}</span>
      <span style={{fontSize:12, fontWeight:600, color:"#1A1A1A", textTransform:"uppercase", letterSpacing:"0.04em"}}>{title}</span>
      {sub && <span style={{fontSize:10, color:"#7B7B73", marginLeft:2}}>· {sub}</span>}
    </div>
  );
}
function RecapLine({ label, value, sign }) {
  const color = sign === "−" ? "#9A2A2A" : "#1A1A1A";
  return (
    <div style={{display:"flex", justifyContent:"space-between", padding:"4px 0", fontSize:11.5}}>
      <span style={{color:"#5B5B53"}}>{label}</span>
      <span style={{fontFamily:"var(--font-mono)", color, fontWeight: sign === "−" ? 400 : 500}}>{sign === "−" ? "−" : ""}{value.toLocaleString("fr-FR")}</span>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   WORKFLOW · horizontal 5-step cycle
   ───────────────────────────────────────────────────────── */
function CRGWorkflow({ status }) {
  const steps = [
    { k:"data",   label:"Encaissements",   sub:"Rapprochés automatiquement" },
    { k:"draft",  label:"Génération",       sub:"PDF + données financières" },
    { k:"send",   label:"Envoi bailleur",   sub:"Email + WhatsApp" },
    { k:"ack",    label:"Validation",       sub:"Accusé du propriétaire" },
    { k:"pay",    label:"Versement",        sub:"Virement émis" },
  ];
  const idx = status === "à générer" ? 0
    : status === "généré" ? 1
    : status === "envoyé" ? 2
    : status === "validé" ? 3
    : 4;
  return (
    <div style={{display:"grid", gridTemplateColumns:"repeat(5, 1fr)", gap:0, padding:"24px 24px 28px"}}>
      {steps.map((s, i) => {
        const done = i < idx;
        const active = i === idx;
        const color = done ? "var(--pos-d)" : active ? "var(--orange)" : "var(--d-ink-4)";
        return (
          <div key={s.k} style={{position:"relative", display:"flex", flexDirection:"column", alignItems:"center", textAlign:"center"}}>
            {/* line */}
            {i < steps.length - 1 && (
              <span style={{position:"absolute", left:"calc(50% + 14px)", right:"calc(-50% + 14px)", top:13, height:2, background: done ? "var(--pos-d)" : "var(--d-line)"}}/>
            )}
            <div style={{
              width:28, height:28, borderRadius:50,
              border:`1.5px solid ${color}`,
              background: done ? "var(--pos-d)" : active ? "var(--orange)" : "var(--d-panel)",
              color: done || active ? "#fff" : color,
              display:"grid", placeItems:"center", fontSize:11, fontWeight:700, zIndex:1,
              fontFamily:"var(--font-mono)",
            }}>{done ? "✓" : i + 1}</div>
            <div style={{marginTop:10, fontSize:12, fontWeight:500, color: done ? "var(--d-ink-2)" : active ? "var(--d-ink)" : "var(--d-ink-3)"}}>
              {s.label}
              {active && <span style={{marginLeft:6, fontSize:9, fontWeight:600, color:"var(--orange)", background:"rgba(249,115,22,0.10)", padding:"1px 5px", borderRadius:2, letterSpacing:"0.04em"}}>EN COURS</span>}
            </div>
            <div style={{fontSize:11, color:"var(--d-ink-3)", marginTop:2}}>{s.sub}</div>
          </div>
        );
      })}
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   GENERATE MODAL
   ───────────────────────────────────────────────────────── */
function CRGGenerateModal({ applicable, onClose, onConfirm }) {
  const [period, setPeriod] = React.useState("mai-26");
  const [scope, setScope] = React.useState("all");
  const [autoSend, setAutoSend] = React.useState(true);
  const count = applicable.length;
  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:540, 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ération en lot · CRG</div>
            <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:2}}>Créer les comptes rendus pour tous les bailleurs en une seule 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"}}>Période</div>
            <div className="seg" style={{display:"flex", width:"100%"}}>
              {[["mai-26","Mai 2026"],["avr-26","Avril 2026"],["custom","Personnalisée…"]].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}}>
              {[
                ["all",`Tous les bailleurs actifs`,`${count} CRG`],
                ["multi","Bailleurs avec plusieurs biens","3 CRG"],
                ["filter","Filtrer par bailleur…","À 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>
          <div>
            <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:6, fontWeight:600, textTransform:"uppercase", letterSpacing:"0.06em"}}>Envoi automatique</div>
            <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 à chaque bailleur</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)"}}>{scope === "all" ? count : scope === "multi" ? 3 : 0} CRG · 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" onClick={()=>{ onConfirm ? onConfirm() : onClose(); }}>Générer</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   NEW CRG MODAL — génération d'un CRG pour un seul bailleur
   ───────────────────────────────────────────────────────── */
function NewCRGModal({ period, onClose, onGenerate }) {
  const [bailleurId, setBailleurId] = React.useState("");
  const [per, setPer] = React.useState(period === "mar-26" ? "mar-26" : period === "mai-26" ? "mai-26" : "avr-26");
  const crgs = buildCRGs(per);
  const crg = crgs.find(c => c.bailleur.id === bailleurId);
  const periodLabel = per === "mai-26" ? "Mai 2026" : per === "mar-26" ? "Mars 2026" : "Avril 2026";
  const exists = crg && crg.status !== "à générer" && crg.status !== "non applicable";

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

  const canGen = crg && crg.collected > 0;

  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}}>Nouveau CRG</div>
            <div style={{fontSize:15, fontWeight:600}}>{crg ? crg.bailleur.name : "Compte rendu de gestion"} <span style={{color:"var(--d-ink-3)", fontWeight:400, fontSize:12.5}}>· {periodLabel}</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"}}>
          <FieldRow>
            <Field label="Bailleur" required>
              <Select value={bailleurId} onChange={setBailleurId} placeholder="— Choisir un bailleur —"
                options={BAILLEURS.map(b => ({ value:b.id, label:`${b.name} · ${b.id}` }))}/>
            </Field>
            <Field label="Période" required>
              <Select value={per} onChange={setPer} options={[{value:"mai-26",label:"Mai 2026"},{value:"avr-26",label:"Avril 2026"},{value:"mar-26",label:"Mars 2026"}]}/>
            </Field>
          </FieldRow>

          {crg && (
            <div style={{marginTop:6, border:"1px solid var(--d-line)", borderRadius:8, overflow:"hidden"}}>
              <div style={{padding:"8px 14px", background:"var(--d-sunken)", fontSize:10.5, fontWeight:700, letterSpacing:"0.06em", textTransform:"uppercase", color:"var(--d-ink-4)"}}>
                Aperçu · {crg.props.length} bien{crg.props.length>1?"s":""}
              </div>
              {[
                ["Loyers encaissés", crg.collected, "var(--d-ink)"],
                ["Commission agence", -crg.commission, "var(--d-ink-2)"],
                ["Charges déductibles", -crg.chargesTotal, "var(--d-ink-2)"],
              ].map(([k,v,c],i)=>(
                <div key={i} style={{display:"flex", justifyContent:"space-between", padding:"8px 14px", borderTop: i?"1px solid var(--d-line-2)":"none", fontSize:12.5}}>
                  <span className="muted">{k}</span>
                  <span className="mono" style={{color:c, fontWeight:500}}>{v<0?"− ":""}{fmtFCFA(Math.abs(v))}</span>
                </div>
              ))}
              <div style={{display:"flex", justifyContent:"space-between", padding:"10px 14px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:13, fontWeight:600}}>
                <span>Net à verser</span>
                <span className="mono" style={{color:"var(--pos-d)"}}>{fmtFCFA(crg.net)}</span>
              </div>
            </div>
          )}

          {crg && exists && (
            <div style={{marginTop:12, padding:"10px 12px", borderRadius:6, fontSize:11.5, lineHeight:1.5, border:"1px solid rgba(224,167,74,0.3)", background:"rgba(224,167,74,0.06)", color:"var(--d-ink-3)"}}>
              Un CRG <b style={{color:"var(--d-ink-2)"}}>{crg.status}</b> existe déjà pour ce bailleur sur {periodLabel}. Le régénérer écrasera le document précédent.
            </div>
          )}
          {crg && crg.collected <= 0 && (
            <div style={{marginTop:12, padding:"10px 12px", borderRadius:6, fontSize:11.5, lineHeight:1.5, border:"1px solid var(--d-line)", background:"var(--d-sunken)", color:"var(--d-ink-3)"}}>
              Aucun encaissement sur la période → pas de CRG applicable pour ce bailleur.
            </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={!canGen} style={{opacity:canGen?1:0.5, cursor:canGen?"pointer":"not-allowed"}} onClick={()=>onGenerate(crg)}>Générer le CRG</button>
        </footer>
      </div>
    </div>
  );
}

Object.assign(window, { CRGPage, NewCRGModal, setCRGStatus });

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

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

/* ─────────────────────────────────────────────────────────
   BAUX — Leases page
   Liste, calendrier d'échéances, détail, renouvellement
   ───────────────────────────────────────────────────────── */

// Today (the design's "current" date)
const TODAY = new Date("2026-05-18");

// Build a lease object from each MOCK.tenant
function buildLeases() {
  const types = ["Résidentiel", "Résidentiel", "Résidentiel", "Résidentiel", "Résidentiel", "Résidentiel", "Meublé", "Résidentiel", "Professionnel", "Résidentiel"];
  return MOCK.tenants.map((t, i) => {
    const start = new Date(t.leaseStart);
    const end = new Date(t.leaseEnd);
    const daysToEnd = Math.round((end - TODAY) / (1000*60*60*24));
    const months = Math.round((end - start) / (1000*60*60*24*30));
    let leaseStatus;
    if (daysToEnd < 0) leaseStatus = "expiré";
    else if (daysToEnd <= 60) leaseStatus = "à renouveler";
    else leaseStatus = "actif";

    return {
      id: "BX-" + t.id.replace("T-",""),
      tenant: t, type: types[i] || "Résidentiel",
      start: t.leaseStart, end: t.leaseEnd,
      duration: months,
      rent: t.rent,
      deposit: t.rent * 2,
      charges: t.rent > 500_000 ? 35_000 : 18_000,
      indexation: "Annuelle · IRL Sénégal",
      paymentDay: 5,
      renewal: "Tacite reconduction",
      signedAt: t.leaseStart,
      daysToEnd,
      leaseStatus,
      property: t.property,
      propertyId: t.propertyId,
      unit: t.unit,
    };
  });
}

// Synthetic drafts and signed-but-not-started
const DRAFT_LEASES = [
  { id:"BX-DRAFT-01", tenant:{name:"Cheikh Diop", phone:"+221 77 902 14 88", avatar:"CD"}, property:"Rés. Plateau", unit:"7", rent:280_000, type:"Résidentiel", leaseStatus:"brouillon", note:"En attente signature locataire" },
  { id:"BX-DRAFT-02", tenant:{name:"Adji Sow",   phone:"+221 78 401 22 19", avatar:"AS"}, property:"Imm. Fann Hock", unit:"3", rent:230_000, type:"Meublé",       leaseStatus:"brouillon", note:"État des lieux planifié 22 mai" },
];

function BauxPage({ onOpenTenant, onOpenProperty }) {
  useTenants(); // re-render when a tenant is created via the bail form
  const extraLeases = useExtraLeases();
  const allLeases = buildLeases();
  const [tab, setTab] = React.useState("all");
  const [typeFilter, setTypeFilter] = React.useState("all");
  const [q, setQ] = React.useState("");
  const [selectedId, setSelectedId] = React.useState(allLeases.find(l => l.leaseStatus === "à renouveler")?.id || allLeases[0].id);
  const [modal, setModal] = React.useState(null); // null | { mode, initial, presetPropertyId }

  // Build drafts: hardcoded + any extra leases the user saved as brouillon
  const drafts = [
    ...DRAFT_LEASES,
    ...extraLeases.filter(l => l.leaseStatus === "brouillon").map(l => ({
      id: l.id,
      tenant: { name: l._tenantName || "Locataire", phone: "", avatar: (l._tenantName || "L").split(" ").map(s=>s[0]).join("").slice(0,2).toUpperCase() },
      property: l.property, unit: l.unit, rent: l.rent, type: l.type, leaseStatus: "brouillon",
      note: "Bail créé · en attente de signature",
    })),
  ];

  // Counts
  const cActif = allLeases.filter(l => l.leaseStatus === "actif").length;
  const cRen = allLeases.filter(l => l.leaseStatus === "à renouveler").length;
  const cExp = allLeases.filter(l => l.leaseStatus === "expiré").length;
  const cDraft = drafts.length;

  // Total monthly rent
  const totalMrr = allLeases.filter(l => l.leaseStatus !== "expiré").reduce((s,l)=>s+l.rent,0);

  const filtered = (() => {
    let arr = allLeases;
    if (tab === "actif") arr = arr.filter(l => l.leaseStatus === "actif");
    if (tab === "renouveler") arr = arr.filter(l => l.leaseStatus === "à renouveler");
    if (tab === "expired") arr = arr.filter(l => l.leaseStatus === "expiré");
    if (typeFilter !== "all") arr = arr.filter(l => l.type === typeFilter);
    if (q) arr = arr.filter(l => `${l.tenant.name} ${l.property} ${l.id}`.toLowerCase().includes(q.toLowerCase()));
    return arr;
  })();

  const selected = allLeases.find(l => l.id === selectedId) || filtered[0] || allLeases[0];

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Baux</h1>
          <div className="page-sub">{allLeases.length} baux gérés · {cRen > 0 ? `${cRen} à renouveler dans les 60 jours` : "aucune échéance imminente"}</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter</button>
          <button className="btn btn-secondary">Importer modèle</button>
          <button className="btn btn-primary" onClick={()=>setModal({mode:"create", initial:null})}>+ Nouveau bail</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Baux actifs</span></div>
          <div className="kpi-value num">{cActif}<span className="kpi-unit">/ {allLeases.length}</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ 2 ce mois</span><span>{cDraft} en signature</span></div>
          <div className="kpi-spark"><Spark values={[8,8,9,9,9,9,9,10,10,10,10,10]} color="#6192FF"/></div>
        </div>
        <div className="kpi alert">
          <div className="kpi-label"><span>À renouveler · 60 j</span>{cRen > 0 && <span style={{color:"var(--orange)", fontWeight:600}}>Action</span>}</div>
          <div className="kpi-value num" style={{color: cRen > 0 ? "var(--orange)" : "var(--d-ink)"}}>{cRen}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>baux</span></div>
          <div className="kpi-meta"><span>Première échéance dans {Math.min(...allLeases.filter(l => l.daysToEnd > 0).map(l => l.daysToEnd))} j</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Loyers contractés</span></div>
          <div className="kpi-value num">{(totalMrr/1_000_000).toFixed(2).replace(".",",")}<span className="kpi-unit">M FCFA / mois</span></div>
          <div className="kpi-meta"><span>Engagement 12 mois · {fmtCompact(totalMrr*12)}</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Durée moyenne</span></div>
          <div className="kpi-value num">24<span className="kpi-unit">mois</span></div>
          <div className="kpi-meta"><span>Renouvellement à 91%</span></div>
        </div>
      </div>

      {/* Renewals calendar — show next 12 months and pin each ending lease */}
      <RenewalsTimeline leases={allLeases} onPick={l => setSelectedId(l.id)} selectedId={selectedId}/>

      {/* Tabs */}
      <div className="tabs" style={{marginTop:18}}>
        {[
          ["all","Tous",allLeases.length],
          ["actif","Actifs",cActif],
          ["renouveler","À renouveler",cRen],
          ["expired","Expirés",cExp],
          ["draft","Brouillons",cDraft],
        ].map(([k,l,n])=>(
          <button key={k} className={"tab"+(tab===k?" active":"")} onClick={()=>setTab(k)}>{l} <span className="tab-count">{n}</span></button>
        ))}
      </div>

      {tab === "draft" ? (
        <DraftsList drafts={drafts} onNew={()=>setModal({mode:"create", initial:null})}/>
      ) : (
        <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, référence…"/>
              </div>
              {[["all","Tous types"],["Résidentiel","Résidentiel"],["Meublé","Meublé"],["Professionnel","Professionnel"]].map(([k,l]) => (
                <button key={k} className={"filter-chip"+(typeFilter===k?" active":"")} onClick={()=>setTypeFilter(k)}>{l}</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érence</th>
                    <th>Locataire</th>
                    <th>Bien · lot</th>
                    <th className="right">Loyer</th>
                    <th>Échéance</th>
                    <th style={{width:130}}>Statut</th>
                  </tr>
                </thead>
                <tbody>
                  {filtered.length === 0 && <tr><td colSpan={6} className="muted" style={{textAlign:"center", padding:28}}>Aucun bail ne correspond.</td></tr>}
                  {filtered.map(l => (
                    <tr key={l.id} onClick={() => setSelectedId(l.id)} style={{background: l.id === selectedId ? "var(--d-elev)" : undefined}}>
                      <td className="mono muted" style={{fontSize:11.5}}>{l.id}</td>
                      <td>
                        <div style={{display:"flex", alignItems:"center", gap:10}}>
                          <div className="avatar">{l.tenant.avatar}</div>
                          <div>
                            <div style={{fontWeight:500}}>{l.tenant.name}</div>
                            <div className="muted" style={{fontSize:11}}>{l.type}</div>
                          </div>
                        </div>
                      </td>
                      <td className="muted" style={{fontSize:11.5}}>{l.property} · {l.unit}</td>
                      <td className="right">{fmtFCFA(l.rent)}</td>
                      <td>
                        <div className="mono" style={{fontSize:11.5, color:"var(--d-ink-2)"}}>{l.end}</div>
                        <div style={{fontSize:10.5, color: l.daysToEnd <= 60 && l.daysToEnd > 0 ? "var(--orange)" : l.daysToEnd <= 0 ? "var(--neg-d)" : "var(--d-ink-4)", marginTop:1}}>
                          {l.daysToEnd > 0 ? `dans ${l.daysToEnd} j` : `expiré ${Math.abs(l.daysToEnd)} j`}
                        </div>
                      </td>
                      <td><LeaseChip status={l.leaseStatus}/></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>

          {/* Right — detail */}
          <div className="stack">
            <LeaseDetail lease={selected} onOpenTenant={onOpenTenant} onOpenProperty={onOpenProperty} onEdit={l => setModal({mode:"edit", initial:l})}/>
          </div>
        </div>
      )}

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

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

function LeaseChip({ status }) {
  const map = {
    "actif":         ["pos","actif"],
    "à renouveler":  ["warn","à renouveler"],
    "expiré":        ["neg","expiré"],
    "brouillon":     ["neutral","brouillon"],
    "résilié":       ["neg","résilié"],
  };
  const [cls, label] = map[status] || ["neutral", status];
  return <span className={`chip ${cls}`}>{label}</span>;
}

/* Calendar-ish timeline of next 12 months, with pins for each expiring lease */
function RenewalsTimeline({ leases, onPick, selectedId }) {
  // Build 12 month buckets starting from current month (May 2026)
  const months = [
    {k:5, label:"Mai", year:26}, {k:6, label:"Juin", year:26}, {k:7, label:"Juil", year:26}, {k:8, label:"Août", year:26},
    {k:9, label:"Sept", year:26}, {k:10, label:"Oct", year:26}, {k:11, label:"Nov", year:26}, {k:12, label:"Déc", year:26},
    {k:1, label:"Jan", year:27}, {k:2, label:"Fév", year:27}, {k:3, label:"Mar", year:27}, {k:4, label:"Avr", year:27},
  ];
  // Group leases by ending month
  const byMonth = months.map(m => {
    const items = leases.filter(l => {
      const end = new Date(l.end);
      const sameYear = end.getFullYear() === (m.year === 26 ? 2026 : 2027);
      return sameYear && end.getMonth() + 1 === m.k;
    });
    return { ...m, items };
  });
  const maxItems = Math.max(1, ...byMonth.map(b => b.items.length));

  return (
    <div className="panel" style={{marginBottom:0}}>
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Échéances à venir · 12 mois</h3>
          <div className="panel-sub">Visualisation des renouvellements et expirations</div>
        </div>
        <div className="panel-head-right">
          <span style={{fontSize:11, color:"var(--d-ink-3)", display:"flex", alignItems:"center", gap:14}}>
            <span style={{display:"inline-flex", alignItems:"center", gap:5}}><span style={{width:8, height:8, borderRadius:50, background:"var(--orange)"}}/>À renouveler</span>
            <span style={{display:"inline-flex", alignItems:"center", gap:5}}><span style={{width:8, height:8, borderRadius:50, background:"var(--pos-d)"}}/>Actif</span>
            <span style={{display:"inline-flex", alignItems:"center", gap:5}}><span style={{width:8, height:8, borderRadius:50, background:"var(--neg-d)"}}/>Expiré</span>
          </span>
        </div>
      </div>
      <div style={{padding:"22px 24px 18px"}}>
        <div style={{display:"grid", gridTemplateColumns:"repeat(12, 1fr)", gap:0, position:"relative"}}>
          {byMonth.map((m, i) => (
            <div key={i} style={{borderRight: i < 11 ? "1px dashed var(--d-line-2)" : "none", paddingTop: 4, paddingBottom: 22, position:"relative", minHeight: 110 + maxItems*22}}>
              {/* Month label */}
              <div style={{position:"absolute", bottom:0, left:0, right:0, textAlign:"center"}}>
                <div style={{fontSize:11, color:"var(--d-ink-3)", fontFamily:"var(--font-mono)", letterSpacing:"0.04em"}}>{m.label}</div>
                <div style={{fontSize:9.5, color:"var(--d-ink-4)", fontFamily:"var(--font-mono)"}}>'{m.year}</div>
              </div>
              {/* "Now" marker */}
              {i === 0 && (
                <div style={{position:"absolute", left:6, top:0, bottom:24, width:2, background:"var(--blue-d)", borderRadius:1}}>
                  <span style={{position:"absolute", top:-2, left:-3, width:8, height:8, borderRadius:50, background:"var(--blue-d)"}}/>
                  <span style={{position:"absolute", top:8, left:8, fontSize:9, color:"var(--blue-d)", fontWeight:600, letterSpacing:"0.04em"}}>AUJ.</span>
                </div>
              )}
              {/* Pins */}
              <div style={{display:"flex", flexDirection:"column", gap:4, padding:"4px 6px"}}>
                {m.items.map((l, li) => {
                  const isSel = l.id === selectedId;
                  const c = l.leaseStatus === "à renouveler" ? "var(--orange)" : l.leaseStatus === "expiré" ? "var(--neg-d)" : "var(--pos-d)";
                  return (
                    <button
                      key={l.id}
                      onClick={() => onPick(l)}
                      title={`${l.tenant.name} · ${l.property} · ${l.end}`}
                      style={{
                        background: isSel ? c : `${c}1A`,
                        border:`1px solid ${isSel ? c : `${c}66`}`,
                        color: isSel ? "#fff" : "var(--d-ink)",
                        padding:"4px 6px", borderRadius:3, fontSize:10.5, lineHeight:1.2,
                        textAlign:"left", cursor:"pointer", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap",
                        fontFamily:"var(--font-sans)",
                      }}
                    >
                      <div style={{fontWeight:500, fontSize:10.5}}>{l.tenant.name.split(" ")[0]} {l.tenant.name.split(" ")[1]?.[0] || ""}.</div>
                      <div style={{fontSize:9, color: isSel ? "rgba(255,255,255,0.85)" : "var(--d-ink-3)", marginTop:1, fontFamily:"var(--font-mono)"}}>{l.end.slice(8,10)}/{l.end.slice(5,7)} · {fmtCompact(l.rent)}</div>
                    </button>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────
   LEASE DETAIL — paper preview + clauses + workflow
   ───────────────────────────────────────────────────────── */
function LeaseDetail({ lease, onOpenTenant, onOpenProperty, onEdit }) {
  const l = lease;
  const property = MOCK.properties.find(p => p.id === l.propertyId);
  const isRenew = l.leaseStatus === "à renouveler";
  const isExpired = l.leaseStatus === "expiré";

  return (
    <>
      <div className="panel">
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Bail · {l.id}</h3>
            <div className="panel-sub">{l.type} · {l.duration} mois · {l.renewal}</div>
          </div>
          <div className="panel-head-right">
            <button className="btn btn-ghost" style={{padding:"5px 9px", fontSize:11.5}} onClick={() => onOpenTenant && onOpenTenant(l.tenant)}>Voir locataire →</button>
            <button className="btn btn-ghost" style={{padding:"5px 9px", fontSize:11.5}}>⬇ PDF</button>
            {onEdit && <button className="btn btn-ghost" style={{padding:"5px 9px", fontSize:11.5}} onClick={() => onEdit(l)}>Modifier</button>}
            {isRenew ? <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}}>Renouveler</button>
              : isExpired ? <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}}>Régulariser</button>
              : <button className="btn btn-secondary" style={{padding:"5px 10px", fontSize:11.5}}>Avenant</button>}
          </div>
        </div>

        {/* Header strip */}
        <div style={{padding:"16px 18px", display:"grid", gridTemplateColumns:"auto 1fr 1fr 1fr", gap:18, alignItems:"center", borderBottom:"1px solid var(--d-line-2)"}}>
          <div className="avatar lg" style={{background:"var(--blue)"}}>{l.tenant.avatar}</div>
          <div>
            <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600, marginBottom:2}}>Locataire</div>
            <div style={{fontWeight:500, fontSize:13}}>{l.tenant.name}</div>
            <div style={{fontSize:11, color:"var(--d-ink-3)"}}>{l.tenant.phone}</div>
          </div>
          <div>
            <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600, marginBottom:2}}>Bien loué</div>
            <div style={{fontWeight:500, fontSize:13}}>{l.property}</div>
            <div style={{fontSize:11, color:"var(--d-ink-3)"}}>Lot {l.unit}</div>
          </div>
          <div>
            <div style={{fontSize:10, textTransform:"uppercase", color:"var(--d-ink-4)", letterSpacing:"0.06em", fontWeight:600, marginBottom:2}}>Échéance</div>
            <div className="mono" style={{fontWeight:500, fontSize:13}}>{l.end}</div>
            <div style={{fontSize:11, color: l.daysToEnd <= 60 && l.daysToEnd > 0 ? "var(--orange)" : l.daysToEnd <= 0 ? "var(--neg-d)" : "var(--d-ink-3)", fontWeight:500}}>
              {l.daysToEnd > 0 ? `dans ${l.daysToEnd} jours` : `expiré il y a ${Math.abs(l.daysToEnd)} j`}
            </div>
          </div>
        </div>

        {/* Paper-style summary */}
        <div style={{padding:"18px 20px"}}>
          <div style={{background:"#FAFAF7", color:"#1A1A1A", borderRadius:6, padding:"22px 24px", fontFamily:"var(--font-sans)", border:"1px solid #E4E4DE", position:"relative", overflow:"hidden"}}>
            {/* 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}}>Contrat de bail · {l.type.toLowerCase()}</div>
                <div style={{fontSize:14, fontWeight:600, color:"#1A1A1A", letterSpacing:"-0.01em"}}>{l.tenant.name} · {l.property}</div>
                <div className="mono" style={{fontSize:10, color:"#7B7B73", marginTop:3}}>Réf. {l.id} · signé le {l.signedAt}</div>
              </div>
              <div style={{textAlign:"right"}}>
                <div style={{fontSize:10, fontWeight:600, color:"#1A1A1A"}}>OHADA</div>
                <div style={{fontSize:9.5, color:"#7B7B73"}}>Acte sous seing privé</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}}>Bailleur</div>
                <div style={{fontSize:12, fontWeight:600, color:"#1A1A1A"}}>{property?.owner || "—"}</div>
                <div style={{fontSize:10.5, color:"#5B5B53"}}>Représenté par BA Immo · mandat de gestion</div>
              </div>
              <div>
                <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:4}}>Preneur</div>
                <div style={{fontSize:12, fontWeight:600, color:"#1A1A1A"}}>{l.tenant.name}</div>
                <div style={{fontSize:10.5, color:"#5B5B53"}}>{l.tenant.phone} · {l.tenant.email || ""}</div>
              </div>
            </div>

            {/* Key terms grid */}
            <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:0, marginBottom:14, border:"1px solid #E4E4DE", borderRadius:4, overflow:"hidden"}}>
              {[
                ["Loyer mensuel", l.rent.toLocaleString("fr-FR") + " FCFA"],
                ["Charges", l.charges.toLocaleString("fr-FR") + " FCFA"],
                ["Dépôt garantie", l.deposit.toLocaleString("fr-FR") + " FCFA"],
                ["Jour de paiement", `Le ${l.paymentDay} de chaque mois`],
                ["Début", l.start],
                ["Fin", l.end],
                ["Durée", `${l.duration} mois`],
                ["Indexation", l.indexation],
              ].map(([k,v], i) => (
                <div key={i} style={{padding:"10px 12px", borderRight: i % 4 < 3 ? "1px solid #EEEEE6" : "none", borderBottom: i < 4 ? "1px solid #EEEEE6" : "none"}}>
                  <div style={{fontSize:9, textTransform:"uppercase", color:"#7B7B73", letterSpacing:"0.05em", fontWeight:600, marginBottom:3}}>{k}</div>
                  <div style={{fontSize:11.5, fontWeight:500, color:"#1A1A1A", fontFamily: typeof v === "string" && /\d/.test(v) && k !== "Jour de paiement" ? "var(--font-mono)" : "var(--font-sans)"}}>{v}</div>
                </div>
              ))}
            </div>

            {/* Clauses */}
            <div style={{marginBottom:12}}>
              <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:8}}>Clauses principales</div>
              <ol style={{margin:0, paddingLeft:18, fontSize:11, color:"#3A3A33", lineHeight:1.6}}>
                <li><b>Destination des lieux.</b> Usage à titre d'habitation principale. Sous-location interdite sauf accord écrit du bailleur.</li>
                <li><b>État des lieux.</b> Entrée le {l.start}, sortie obligatoire dans les 8 jours suivant la restitution des clés.</li>
                <li><b>Charges récupérables.</b> Forfait mensuel de {l.charges.toLocaleString("fr-FR")} FCFA couvrant eau commune, gardiennage et nettoyage des parties communes.</li>
                <li><b>Révision.</b> Le loyer est indexé annuellement à la date anniversaire selon l'indice IRL Sénégal publié par l'ANSD.</li>
                <li><b>Dépôt de garantie.</b> Versé à la signature, conservé sur compte séquestre, restitué dans un délai maximal de 60 jours après libération sous déduction d'éventuelles réparations.</li>
                <li><b>Résiliation.</b> Préavis de 3 mois pour le preneur. Le bailleur ne peut résilier qu'aux échéances et pour motifs prévus par la loi.</li>
                <li><b>Litiges.</b> Compétence du Tribunal de Grande Instance de Dakar · droit OHADA applicable.</li>
              </ol>
            </div>

            {/* Footer */}
            <div style={{display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:14, paddingTop:10, borderTop:"1px dashed #E4E4DE", fontSize:10, color:"#5B5B53"}}>
              <div>
                <div style={{textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, fontSize:8.5, marginBottom:4, color:"#7B7B73"}}>Le bailleur</div>
                <div style={{height:30, borderBottom:"1px solid #C4C4BD"}}/>
                <div style={{fontSize:9, color:"#7B7B73", marginTop:3}}>{property?.owner || "—"}</div>
              </div>
              <div>
                <div style={{textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, fontSize:8.5, marginBottom:4, color:"#7B7B73"}}>Le preneur</div>
                <div style={{height:30, borderBottom:"1px solid #C4C4BD"}}/>
                <div style={{fontSize:9, color:"#7B7B73", marginTop:3}}>{l.tenant.name}</div>
              </div>
              <div>
                <div style={{textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, fontSize:8.5, marginBottom:4, color:"#7B7B73"}}>L'agence</div>
                <div style={{height:30, borderBottom:"1px solid #C4C4BD"}}/>
                <div style={{fontSize:9, color:"#7B7B73", marginTop:3}}>BA Immo · Dakar</div>
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* Renewal panel — only show for à renouveler */}
      {(isRenew || isExpired) && <RenewalPanel lease={l}/>}

      {/* History timeline */}
      <div className="panel">
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Historique du bail</h3>
            <div className="panel-sub">Événements contractuels et étapes</div>
          </div>
        </div>
        <div style={{padding:"18px 22px"}}>
          {[
            { t: l.start, tag:"SIGNATURE", c:"var(--blue-d)", txt:`Signature du bail · ${l.duration} mois · dépôt de garantie ${fmtFCFA(l.deposit)}` },
            { t: l.start, tag:"ÉTAT LIEUX", c:"var(--d-ink-3)", txt:"État des lieux d'entrée · contradictoire · sans réserve" },
            ...(l.duration > 12 ? [{ t: l.start.replace("2024","2025").replace("2023","2024"), tag:"INDEX.", c:"var(--warn-d)", txt:`Indexation annuelle · +1,8% IRL` }] : []),
            ...(isRenew ? [{ t: "À planifier", tag:"RAPPEL", c:"var(--orange)", txt:`Rappel automatique de renouvellement programmé · J−60` }] : []),
            ...(isExpired ? [{ t: l.end, tag:"EXPIRÉ", c:"var(--neg-d)", txt:`Le bail est arrivé à échéance · action requise (renouvellement, régularisation ou résiliation)` }] : []),
          ].reverse().map((e, i, arr) => (
            <div key={i} style={{display:"flex", gap:14, paddingBottom: i < arr.length-1 ? 14 : 0, position:"relative"}}>
              {i < arr.length-1 && <span style={{position:"absolute", left:11, top:22, bottom:0, width:1, background:"var(--d-line)"}}/>}
              <span style={{fontSize:8.5, fontWeight:700, color:"#fff", background:e.c, padding:"2px 5px", borderRadius:3, letterSpacing:"0.04em", height:18, lineHeight:"14px", minWidth:78, textAlign:"center", flexShrink:0}}>{e.tag}</span>
              <div style={{flex:1, minWidth:0}}>
                <div style={{fontSize:12, color:"var(--d-ink-2)"}}>{e.txt}</div>
                <div className="mono" style={{fontSize:10.5, color:"var(--d-ink-4)", marginTop:2}}>{e.t}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

/* Renewal workflow — only shown for leases à renouveler */
function RenewalPanel({ lease }) {
  const [proposed, setProposed] = React.useState(lease.rent + Math.round(lease.rent * 0.018));
  const [duration, setDuration] = React.useState("24");
  const indexation = Math.round(lease.rent * 0.018);
  return (
    <div className="panel" style={{borderColor:"rgba(249,115,22,0.35)"}}>
      <div className="panel-head" style={{background:"linear-gradient(180deg, rgba(249,115,22,0.06) 0%, transparent 100%)"}}>
        <div>
          <h3 className="panel-title">Renouvellement à préparer</h3>
          <div className="panel-sub">Échéance dans {lease.daysToEnd} jours · proposez un avenant ou un nouveau bail</div>
        </div>
        <span className="chip warn">action requise</span>
      </div>

      {/* Workflow steps */}
      <div style={{padding:"18px 24px 8px"}}>
        <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:0, marginBottom:20}}>
          {[
            { label:"Proposition", done:false, active:true },
            { label:"Envoi locataire", done:false, active:false },
            { label:"Accord & signature", done:false, active:false },
            { label:"Avenant publié", done:false, active:false },
          ].map((s, i, arr) => (
            <div key={i} style={{position:"relative", display:"flex", flexDirection:"column", alignItems:"center", textAlign:"center"}}>
              {i < arr.length-1 && <span style={{position:"absolute", left:"calc(50% + 14px)", right:"calc(-50% + 14px)", top:13, height:2, background:"var(--d-line)"}}/>}
              <div style={{width:28, height:28, borderRadius:50, border:`1.5px solid ${s.active?"var(--orange)":"var(--d-line)"}`, background: s.active ? "var(--orange)":"var(--d-panel)", color: s.active?"#fff":"var(--d-ink-4)", display:"grid", placeItems:"center", fontSize:11, fontWeight:700, zIndex:1, fontFamily:"var(--font-mono)"}}>{i+1}</div>
              <div style={{marginTop:8, fontSize:11.5, color: s.active ? "var(--d-ink)" : "var(--d-ink-3)", fontWeight: s.active ? 500 : 400}}>{s.label}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Renewal form */}
      <div style={{padding:"0 22px 18px"}}>
        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:14, marginBottom:14}}>
          <div>
            <div style={{fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:6}}>Loyer actuel</div>
            <div className="mono" style={{fontSize:15, color:"var(--d-ink-2)", padding:"8px 10px", background:"var(--d-sunken)", border:"1px solid var(--d-line)", borderRadius:5}}>{fmtFCFA(lease.rent)}</div>
          </div>
          <div>
            <div style={{fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:6}}>Indexation IRL</div>
            <div className="mono" style={{fontSize:15, color:"var(--warn-d)", padding:"8px 10px", background:"var(--d-sunken)", border:"1px solid var(--d-line)", borderRadius:5}}>+{indexation.toLocaleString("fr-FR")} FCFA <span style={{color:"var(--d-ink-3)", fontSize:11}}>(+1,8%)</span></div>
          </div>
          <div>
            <div style={{fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:6}}>Nouveau loyer proposé</div>
            <input type="text" value={proposed.toLocaleString("fr-FR")} onChange={e => { const v = parseInt(e.target.value.replace(/\D/g,""))||0; setProposed(v); }} className="mono" style={{fontSize:15, color:"var(--pos-d)", padding:"8px 10px", background:"var(--d-sunken)", border:"1px solid var(--pos-d)", borderRadius:5, width:"100%", outline:"none", fontWeight:600}}/>
          </div>
        </div>
        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:14}}>
          <div>
            <div style={{fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:6}}>Durée du renouvellement</div>
            <div className="seg" style={{display:"flex"}}>
              {[["12","12 mois"],["24","24 mois"],["36","36 mois"]].map(([k,l]) => (
                <button key={k} className={"seg-btn"+(duration===k?" active":"")} onClick={()=>setDuration(k)} style={{flex:1}}>{l}</button>
              ))}
            </div>
          </div>
          <div>
            <div style={{fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:6}}>Nouvelle échéance</div>
            <div className="mono" style={{fontSize:13, color:"var(--d-ink)", padding:"7px 10px", background:"var(--d-sunken)", border:"1px solid var(--d-line)", borderRadius:5}}>{(() => {
              const end = new Date(lease.end); end.setMonth(end.getMonth() + parseInt(duration)); return end.toISOString().slice(0,10);
            })()}</div>
          </div>
        </div>
      </div>

      <div style={{padding:"14px 22px", 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)"}}>
          Impact mensuel : <b style={{color:"var(--pos-d)"}}>+{indexation.toLocaleString("fr-FR")} FCFA</b> · annuel <b style={{color:"var(--pos-d)"}}>+{(indexation*12).toLocaleString("fr-FR")} FCFA</b>
        </div>
        <div style={{display:"flex", gap:8}}>
          <button className="btn btn-ghost">Aperçu avenant</button>
          <button className="btn btn-secondary">Enregistrer brouillon</button>
          <button className="btn btn-primary">Envoyer au locataire</button>
        </div>
      </div>
    </div>
  );
}

/* Drafts list (signature in progress) */
function DraftsList({ drafts, onNew }) {
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Brouillons & signatures en cours</h3>
          <div className="panel-sub">{drafts.length} baux en préparation</div>
        </div>
        <button className="btn btn-primary" style={{padding:"5px 10px", fontSize:11.5}} onClick={onNew}>+ Nouveau bail</button>
      </div>
      <div>
        {drafts.map((d, i) => (
          <div key={d.id} style={{padding:"16px 18px", borderBottom: i < drafts.length-1 ? "1px solid var(--d-line-2)" : "none", display:"grid", gridTemplateColumns:"auto 1fr auto auto", gap:14, alignItems:"center"}}>
            <div className="avatar">{d.tenant.avatar}</div>
            <div>
              <div style={{fontWeight:500, marginBottom:2}}>{d.tenant.name} · <span style={{color:"var(--d-ink-3)", fontWeight:400}}>{d.property} · lot {d.unit}</span></div>
              <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>{d.note}</div>
            </div>
            <div style={{textAlign:"right"}}>
              <div className="mono" style={{fontSize:11, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.04em"}}>{d.type}</div>
              <div className="mono" style={{fontSize:13, fontWeight:500, marginTop:2}}>{fmtFCFA(d.rent)}</div>
            </div>
            <div style={{display:"flex", gap:6}}>
              <button className="btn btn-ghost" style={{padding:"4px 9px", fontSize:11.5}}>Aperçu</button>
              <button className="btn btn-primary" style={{padding:"4px 9px", fontSize:11.5}}>Reprendre</button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { BauxPage });

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

/* ╔══ pages-charges.jsx ══╗ */
/* global React, MOCK, BAILLEURS, fmtFCFA, fmtCompact, Spark,
          getBailleurMetrics, BAILLEUR_MONTHS */

/* ═══════════════════════════════════════════════════════════════════════
   CHARGES & TRAVAUX
   • Calendrier sur 12 mois — récurrentes + ponctuelles
   • Liste détaillée — toutes lignes de charge
   • Par bien — agrégation par immeuble
   • Travaux planifiés — projets avec échéancier
   • Provisions — fonds travaux par bailleur
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── DATA · catalogue des charges récurrentes par bien ─── */
const CHARGES = [
  // Rés. Teranga · P-104 · M. A. Ba · 12u
  { id:"CHG-101", propertyId:"P-104", category:"Gardiennage 24/7",        supplier:"Sécurité Plus SARL",  amount:180_000, freq:"M", recoverable:true,  account:"6225" },
  { id:"CHG-102", propertyId:"P-104", category:"Eau commune",             supplier:"SDE",                   amount:42_000,  freq:"M", recoverable:true,  account:"6061" },
  { id:"CHG-103", propertyId:"P-104", category:"Électricité commune",     supplier:"SENELEC",               amount:58_000,  freq:"M", recoverable:true,  account:"6062" },
  { id:"CHG-104", propertyId:"P-104", category:"Ménage parties communes", supplier:"Cleanline Services",    amount:80_000,  freq:"M", recoverable:true,  account:"6284" },
  { id:"CHG-105", propertyId:"P-104", category:"Syndic & gestion",        supplier:"AG Syndic Dakar",       amount:95_000,  freq:"Q", recoverable:false, account:"6226" },
  { id:"CHG-106", propertyId:"P-104", category:"Assurance multirisque",   supplier:"NSIA Assurances",       amount:280_000, freq:"A", recoverable:false, account:"616", dueMonth:1 },
  { id:"CHG-107", propertyId:"P-104", category:"Taxe foncière",           supplier:"DGID",                  amount:620_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },

  // Villa Mermoz · P-118 · Mme A. Ba · 1u
  { id:"CHG-201", propertyId:"P-118", category:"Jardinier",               supplier:"M. Mor Diouf",          amount:45_000,  freq:"M", recoverable:true,  account:"6225" },
  { id:"CHG-202", propertyId:"P-118", category:"Assurance habitation",    supplier:"Sonam Assurances",      amount:120_000, freq:"A", recoverable:false, account:"616", dueMonth:2 },
  { id:"CHG-203", propertyId:"P-118", category:"Taxe foncière",           supplier:"DGID",                  amount:210_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },

  // Imm. Point E · P-122 · Horizon SCI · 8u
  { id:"CHG-301", propertyId:"P-122", category:"Gardiennage",             supplier:"BG Security",           amount:140_000, freq:"M", recoverable:true,  account:"6225" },
  { id:"CHG-302", propertyId:"P-122", category:"Ascenseur · entretien",   supplier:"Schindler Sénégal",     amount:48_000,  freq:"M", recoverable:true,  account:"6231" },
  { id:"CHG-303", propertyId:"P-122", category:"Électricité commune",     supplier:"SENELEC",               amount:42_000,  freq:"M", recoverable:true,  account:"6062" },
  { id:"CHG-304", propertyId:"P-122", category:"Eau commune",             supplier:"SDE",                   amount:28_000,  freq:"M", recoverable:true,  account:"6061" },
  { id:"CHG-305", propertyId:"P-122", category:"Syndic",                  supplier:"AG Syndic Dakar",       amount:80_000,  freq:"Q", recoverable:false, account:"6226" },
  { id:"CHG-306", propertyId:"P-122", category:"Assurance multirisque",   supplier:"NSIA Assurances",       amount:240_000, freq:"A", recoverable:false, account:"616", dueMonth:3 },
  { id:"CHG-307", propertyId:"P-122", category:"Taxe foncière",           supplier:"DGID",                  amount:480_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },

  // Rés. Ngor · P-131 · Katos · 6u
  { id:"CHG-401", propertyId:"P-131", category:"Gardiennage",             supplier:"Sécurité Plus SARL",    amount:160_000, freq:"M", recoverable:true,  account:"6225" },
  { id:"CHG-402", propertyId:"P-131", category:"Eau commune",             supplier:"SDE",                   amount:32_000,  freq:"M", recoverable:true,  account:"6061" },
  { id:"CHG-403", propertyId:"P-131", category:"Électricité commune",     supplier:"SENELEC",               amount:38_000,  freq:"M", recoverable:true,  account:"6062" },
  { id:"CHG-404", propertyId:"P-131", category:"Syndic",                  supplier:"AG Syndic Dakar",       amount:75_000,  freq:"Q", recoverable:false, account:"6226" },
  { id:"CHG-405", propertyId:"P-131", category:"Assurance multirisque",   supplier:"NSIA Assurances",       amount:180_000, freq:"A", recoverable:false, account:"616", dueMonth:5 },
  { id:"CHG-406", propertyId:"P-131", category:"Taxe foncière",           supplier:"DGID",                  amount:340_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },

  // Villa Sacré-Cœur · P-140 · M. K. Fall · vacante
  { id:"CHG-501", propertyId:"P-140", category:"Surveillance villa vacante", supplier:"Sécurité Plus SARL", amount:35_000,  freq:"M", recoverable:false, account:"6225", vacancy:true },
  { id:"CHG-502", propertyId:"P-140", category:"Assurance habitation",    supplier:"Sonam Assurances",      amount:140_000, freq:"A", recoverable:false, account:"616", dueMonth:1 },
  { id:"CHG-503", propertyId:"P-140", category:"Taxe foncière",           supplier:"DGID",                  amount:240_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },

  // Rés. Plateau · P-147 · Horizon SCI · 10u
  { id:"CHG-601", propertyId:"P-147", category:"Gardiennage",             supplier:"BG Security",           amount:180_000, freq:"M", recoverable:true,  account:"6225" },
  { id:"CHG-602", propertyId:"P-147", category:"Ménage parties communes", supplier:"Cleanline Services",    amount:70_000,  freq:"M", recoverable:true,  account:"6284" },
  { id:"CHG-603", propertyId:"P-147", category:"Eau commune",             supplier:"SDE",                   amount:38_000,  freq:"M", recoverable:true,  account:"6061" },
  { id:"CHG-604", propertyId:"P-147", category:"Électricité commune",     supplier:"SENELEC",               amount:52_000,  freq:"M", recoverable:true,  account:"6062" },
  { id:"CHG-605", propertyId:"P-147", category:"Syndic",                  supplier:"AG Syndic Dakar",       amount:88_000,  freq:"Q", recoverable:false, account:"6226" },
  { id:"CHG-606", propertyId:"P-147", category:"Assurance multirisque",   supplier:"NSIA Assurances",       amount:260_000, freq:"A", recoverable:false, account:"616", dueMonth:6 },
  { id:"CHG-607", propertyId:"P-147", category:"Taxe foncière",           supplier:"DGID",                  amount:550_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },

  // Imm. Fann Hock · P-152 · Mme F. Diop · 5u
  { id:"CHG-701", propertyId:"P-152", category:"Gardiennage",             supplier:"Sécurité Plus SARL",    amount:130_000, freq:"M", recoverable:true,  account:"6225" },
  { id:"CHG-702", propertyId:"P-152", category:"Eau commune",             supplier:"SDE",                   amount:24_000,  freq:"M", recoverable:true,  account:"6061" },
  { id:"CHG-703", propertyId:"P-152", category:"Syndic",                  supplier:"AG Syndic Dakar",       amount:65_000,  freq:"Q", recoverable:false, account:"6226" },
  { id:"CHG-704", propertyId:"P-152", category:"Assurance multirisque",   supplier:"Sonam Assurances",      amount:180_000, freq:"A", recoverable:false, account:"616", dueMonth:8 },
  { id:"CHG-705", propertyId:"P-152", category:"Taxe foncière",           supplier:"DGID",                  amount:280_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },

  // Rés. Ouakam · P-163 · Katos · 8u
  { id:"CHG-801", propertyId:"P-163", category:"Gardiennage",             supplier:"BG Security",           amount:165_000, freq:"M", recoverable:true,  account:"6225" },
  { id:"CHG-802", propertyId:"P-163", category:"Eau commune",             supplier:"SDE",                   amount:35_000,  freq:"M", recoverable:true,  account:"6061" },
  { id:"CHG-803", propertyId:"P-163", category:"Électricité commune",     supplier:"SENELEC",               amount:42_000,  freq:"M", recoverable:true,  account:"6062" },
  { id:"CHG-804", propertyId:"P-163", category:"Ménage parties communes", supplier:"Cleanline Services",    amount:60_000,  freq:"M", recoverable:true,  account:"6284" },
  { id:"CHG-805", propertyId:"P-163", category:"Syndic",                  supplier:"AG Syndic Dakar",       amount:78_000,  freq:"Q", recoverable:false, account:"6226" },
  { id:"CHG-806", propertyId:"P-163", category:"Assurance multirisque",   supplier:"NSIA Assurances",       amount:220_000, freq:"A", recoverable:false, account:"616", dueMonth:7 },
  { id:"CHG-807", propertyId:"P-163", category:"Taxe foncière",           supplier:"DGID",                  amount:420_000, freq:"A", recoverable:false, account:"645", dueMonth:9 },
];

/* ─── DATA · travaux ponctuels (immobilisations & gros entretien) ─── */
const TRAVAUX = [
  {
    id:"TRV-001", propertyId:"P-147", title:"Ravalement façade",
    supplier:"BTP Construct SA", budget:1_200_000, paid:0,
    startDate:"2026-07-01", endDate:"2026-08-15",
    progress:0, status:"planifié", account:"234",
    schedule:[
      { date:"2026-06-15", amount:360_000, label:"Acompte 30%",   paid:false },
      { date:"2026-07-15", amount:480_000, label:"Avancement 40%",paid:false },
      { date:"2026-08-20", amount:360_000, label:"Solde 30%",     paid:false },
    ],
  },
  {
    id:"TRV-002", propertyId:"P-122", title:"Étanchéité toit terrasse",
    supplier:"Top Toit Dakar", budget:680_000, paid:204_000,
    startDate:"2026-05-08", endDate:"2026-05-25",
    progress:18, status:"en cours", account:"234",
    schedule:[
      { date:"2026-05-01", amount:204_000, label:"Acompte 30%", paid:true },
      { date:"2026-05-25", amount:476_000, label:"Solde 70%",   paid:false },
    ],
  },
  {
    id:"TRV-003", propertyId:"P-118", title:"Rénovation cuisine",
    supplier:"Cuisines Modernes SARL", budget:450_000, paid:0,
    startDate:"2026-06-10", endDate:"2026-06-28",
    progress:0, status:"devis validé", account:"234",
    schedule:[
      { date:"2026-06-05", amount:225_000, label:"Acompte 50%", paid:false },
      { date:"2026-06-30", amount:225_000, label:"Solde 50%",   paid:false },
    ],
  },
  {
    id:"TRV-004", propertyId:"P-163", title:"Reprise carrelage entrée",
    supplier:"BTP Construct SA", budget:320_000, paid:0,
    startDate:"2026-09-15", endDate:"2026-09-30",
    progress:0, status:"prévu", account:"234",
    schedule:[
      { date:"2026-09-10", amount:96_000,  label:"Acompte 30%", paid:false },
      { date:"2026-10-05", amount:224_000, label:"Solde 70%",   paid:false },
    ],
  },
  {
    id:"TRV-005", propertyId:"P-152", title:"Réparation ascenseur",
    supplier:"Schindler Sénégal", budget:240_000, paid:0,
    startDate:"2026-04-22", endDate:"2026-04-28",
    progress:0, status:"à valider", account:"6231",
    schedule:[
      { date:"2026-04-22", amount:240_000, label:"Forfait", paid:false },
    ],
  },
];

/* ─── PROVISIONS · fonds travaux constitués sur compte bailleur 16x ─── */
const PROVISIONS = [
  { bailleurId:"B-001", balance:1_240_000, monthly:60_000,  earmarked:"Ravalement Teranga 2027",      since:"2024-04-01" },
  { bailleurId:"B-003", balance:3_580_000, monthly:120_000, earmarked:"TRV-001 + TRV-002 + réserve",   since:"2023-09-01" },
  { bailleurId:"B-004", balance:1_870_000, monthly:80_000,  earmarked:"TRV-004 + réfection Ngor",      since:"2024-01-01" },
  { bailleurId:"B-006", balance:540_000,   monthly:30_000,  earmarked:"Réserve travaux Fann Hock",     since:"2024-08-01" },
];

const MONTH_NAMES_FR = ["Janv","Févr","Mars","Avr","Mai","Juin","Juil","Août","Sept","Oct","Nov","Déc"];
const MONTH_NAMES_FR_LONG = ["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"];

// 12-month window starting Nov 2025
function buildMonths() {
  const out = [];
  let y = 2025, mo = 11;
  for (let i = 0; i < 12; i++) {
    out.push({ y, mo, key:`${y}-${String(mo).padStart(2,"0")}`, label:`${MONTH_NAMES_FR[mo-1]} ${String(y).slice(2)}` });
    mo++; if (mo > 12) { mo = 1; y++; }
  }
  return out;
}
const MONTHS_12 = buildMonths();

// Does this charge apply in this (year, 1-based month)?
function chargeOccursIn(c, year, month) {
  if (c.freq === "M") return true;
  if (c.freq === "Q") return [3, 6, 9, 12].includes(month);
  if (c.freq === "A") return month === (c.dueMonth || 1);
  return false;
}

function monthlyChargesByProperty(propertyId, year, month) {
  const items = CHARGES.filter(c => c.propertyId === propertyId && chargeOccursIn(c, year, month));
  const recoverable = items.filter(c=>c.recoverable).reduce((s,c)=>s+c.amount, 0);
  const nonRecoverable = items.filter(c=>!c.recoverable).reduce((s,c)=>s+c.amount, 0);
  return { items, recoverable, nonRecoverable, total: recoverable + nonRecoverable };
}

function monthlyChargesByOwner(ownerName, year, month) {
  const props = MOCK.properties.filter(p => p.owner === ownerName);
  let recoverable = 0, nonRecoverable = 0;
  props.forEach(p => {
    const m = monthlyChargesByProperty(p.id, year, month);
    recoverable += m.recoverable;
    nonRecoverable += m.nonRecoverable;
  });
  return { recoverable, nonRecoverable };
}

const propById = id => MOCK.properties.find(p => p.id === id);
const freqLabel = f => f === "M" ? "Mensuelle" : f === "Q" ? "Trimestrielle" : f === "A" ? "Annuelle" : "Ponctuelle";

/* ─── Charges store ─── mutates CHARGES + notifies subscribers ─── */
const _chargeListeners = new Set();
function _notifyCharges() { _chargeListeners.forEach(fn => fn()); }
function upsertCharge(c) {
  const i = CHARGES.findIndex(x => x.id === c.id);
  if (i >= 0) CHARGES[i] = { ...CHARGES[i], ...c };
  else CHARGES.push(c);
  _notifyCharges();
}
function deleteCharge(id) {
  const i = CHARGES.findIndex(x => x.id === id);
  if (i >= 0) { CHARGES.splice(i, 1); _notifyCharges(); }
}
function useCharges() {
  const [, tick] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => { _chargeListeners.add(tick); return () => _chargeListeners.delete(tick); }, []);
  return CHARGES;
}
function nextChargeId() {
  const max = CHARGES.reduce((m, c) => Math.max(m, parseInt((c.id || "").replace("CHG-", ""), 10) || 0), 100);
  return "CHG-" + (max + 1);
}

/* ═══════════════════════════════════════════════════════════════════════
   PAGE
   ═══════════════════════════════════════════════════════════════════════ */
function ChargesPage({ onOpenProperty, onOpenBailleur }) {
  const [tab, setTab] = React.useState("calendrier");
  useCharges(); // re-render when a charge is added/edited/removed
  const [chargeModal, setChargeModal] = React.useState(null); // null | {mode, initial}

  // Current month figures (April 2026)
  const currentMonthIdx = 5;  // April in MONTHS_12 (0-indexed from Nov 25)
  const cur = MONTHS_12[currentMonthIdx];
  const totalsCurrent = MOCK.properties.reduce((acc, p) => {
    const m = monthlyChargesByProperty(p.id, cur.y, cur.mo);
    return {
      recoverable: acc.recoverable + m.recoverable,
      nonRecoverable: acc.nonRecoverable + m.nonRecoverable,
      total: acc.total + m.total,
    };
  }, { recoverable:0, nonRecoverable:0, total:0 });

  // Annual budget across all properties
  const annual = MOCK.properties.reduce((s, p) => {
    let yearly = 0;
    for (let i = 0; i < 12; i++) {
      const m = MONTHS_12[i];
      yearly += monthlyChargesByProperty(p.id, m.y, m.mo).total;
    }
    return s + yearly;
  }, 0);

  const totalProvisions = PROVISIONS.reduce((s,p)=>s+p.balance, 0);
  const totalTravaux = TRAVAUX.reduce((s,t)=>s+t.budget, 0);
  const travauxPending = TRAVAUX.filter(t => t.status === "à valider" || t.status === "planifié" || t.status === "devis validé" || t.status === "prévu");

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Charges & travaux</h1>
          <div className="page-sub">{CHARGES.length} charges récurrentes · {TRAVAUX.length} travaux planifiés · budget annuel {fmtCompact(annual)} FCFA</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter</button>
          <button className="btn btn-secondary">Importer factures</button>
          <button className="btn btn-primary" onClick={()=>setChargeModal({mode:"create", initial:null})}>+ Nouvelle charge</button>
        </div>
      </div>

      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Charges du mois</span><span style={{color:"var(--d-ink-4)", fontWeight:400, textTransform:"none", letterSpacing:0}}>Avril 2026</span></div>
          <div className="kpi-value num">{(totalsCurrent.total/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}}>{fmtCompact(totalsCurrent.recoverable)} récup</span>
            <span style={{color:"var(--orange)", fontWeight:600}}>{fmtCompact(totalsCurrent.nonRecoverable)} non-récup</span>
          </div>
          <div className="kpi-spark"><Spark values={MONTHS_12.map(m => MOCK.properties.reduce((s,p)=>s+monthlyChargesByProperty(p.id, m.y, m.mo).total, 0))} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Budget annuel</span></div>
          <div className="kpi-value num">{(annual/1_000_000).toFixed(1).replace(".",",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span>8 biens · {fmtCompact(annual/12)} FCFA / mois moyen</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Travaux planifiés</span></div>
          <div className="kpi-value num">{fmtCompact(totalTravaux)}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span className="delta neg">▲ {travauxPending.length}</span><span>{TRAVAUX.length} projets · 12 mois</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Provisions fonds travaux</span></div>
          <div className="kpi-value num" style={{color:"var(--pos-d)"}}>{(totalProvisions/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>{PROVISIONS.length} bailleurs · compte 161</span></div>
        </div>
      </div>

      <div className="tabs">
        {[
          ["calendrier","Calendrier · 12 mois"],
          ["liste",     "Liste détaillée", CHARGES.length],
          ["par-bien",  "Par bien", MOCK.properties.length],
          ["travaux",   "Travaux planifiés", TRAVAUX.length],
          ["provisions","Provisions", PROVISIONS.length],
        ].map(([k,l,n])=>(
          <button key={k} className={"tab" + (tab===k?" active":"")} onClick={()=>setTab(k)}>{l}{n != null && <span className="tab-count">{n}</span>}</button>
        ))}
      </div>

      {tab === "calendrier"  && <ChargesCalendar onOpenProperty={onOpenProperty}/>}
      {tab === "liste"       && <ChargesList onOpenProperty={onOpenProperty} onEditCharge={(c)=>setChargeModal({mode:"edit", initial:c})} onNewCharge={()=>setChargeModal({mode:"create", initial:null})}/>}
      {tab === "par-bien"    && <ChargesParBien onOpenProperty={onOpenProperty}/>}
      {tab === "travaux"     && <TravauxView onOpenProperty={onOpenProperty}/>}
      {tab === "provisions"  && <ProvisionsView onOpenBailleur={onOpenBailleur}/>}

      {typeof ChargeFormModal !== "undefined" && (
        <ChargeFormModal
          open={!!chargeModal}
          mode={chargeModal?.mode || "create"}
          initial={chargeModal?.initial}
          onClose={()=>setChargeModal(null)}
          onSaveMany={(rows)=>{ rows.forEach(upsertCharge); setChargeModal(null); }}
          onSave={(payload)=>{ upsertCharge(payload); setChargeModal(null); }}
          onDelete={(id)=>{ deleteCharge(id); setChargeModal(null); }}
        />
      )}
    </div>
  );
}

/* ─── CALENDRIER · heatmap 12 mois × 8 biens + gantt travaux ─── */
function ChargesCalendar({ onOpenProperty }) {
  const properties = MOCK.properties;
  // Find max cell for scaling
  const cells = properties.flatMap(p => MONTHS_12.map(m => monthlyChargesByProperty(p.id, m.y, m.mo).total));
  const maxCell = Math.max(...cells, 1);

  const todayIdx = MONTHS_12.findIndex(m => m.y === 2026 && m.mo === 4);

  return (
    <div className="stack">
      <div className="panel">
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Calendrier des charges récurrentes</h3>
            <div className="panel-sub">Heatmap par bien · 12 mois glissants · échéances annuelles visibles</div>
          </div>
          <div className="panel-head-right" style={{fontSize:11, color:"var(--d-ink-3)"}}>
            <span style={{display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:10, height:10, background:"rgba(63,181,131,0.45)", borderRadius:2, display:"inline-block"}}/>récupérable</span>
            <span style={{display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:10, height:10, background:"rgba(249,115,22,0.55)", borderRadius:2, display:"inline-block"}}/>non-récup</span>
          </div>
        </div>
        <div className="table-wrap">
          <table className="tbl" style={{tableLayout:"fixed"}}>
            <thead>
              <tr>
                <th style={{width:200}}>Bien</th>
                {MONTHS_12.map((m, i) => (
                  <th key={m.key} className="right" style={{width:72, position:"relative", background: i === todayIdx ? "rgba(249,115,22,0.06)" : undefined}}>
                    {m.label}
                    {i === todayIdx && <span style={{position:"absolute", bottom:-1, left:"50%", transform:"translateX(-50%)", fontSize:9, color:"var(--orange)", fontWeight:600, letterSpacing:"0.08em"}}>auj.</span>}
                  </th>
                ))}
                <th className="right" style={{width:90, color:"var(--blue-d)"}}>Total 12M</th>
              </tr>
            </thead>
            <tbody>
              {properties.map(p => {
                const monthly = MONTHS_12.map(m => monthlyChargesByProperty(p.id, m.y, m.mo));
                const total12 = monthly.reduce((s, x) => s + x.total, 0);
                return (
                  <tr key={p.id} onClick={() => onOpenProperty(p)}>
                    <td>
                      <div style={{fontWeight:500, fontSize:12.5}}>{p.name}</div>
                      <div className="muted" style={{fontSize:11, marginTop:1}}>{p.units} lot{p.units > 1 ? "s" : ""} · {p.id}</div>
                    </td>
                    {monthly.map((m, mi) => {
                      const intensity = m.total / maxCell;
                      const recRatio = m.total ? m.recoverable / m.total : 0;
                      return (
                        <td key={mi} style={{padding:"4px 4px", position:"relative", background: mi === todayIdx ? "rgba(249,115,22,0.06)" : undefined}}>
                          {m.total === 0 ? (
                            <div style={{height:24}}/>
                          ) : (
                            <div style={{
                              width:"100%", height:24, borderRadius:3, overflow:"hidden",
                              display:"grid", placeItems:"center",
                              background: `linear-gradient(to right,
                                rgba(63,181,131,${0.18 + 0.5*intensity}) 0%,
                                rgba(63,181,131,${0.18 + 0.5*intensity}) ${recRatio*100}%,
                                rgba(249,115,22,${0.2 + 0.5*intensity}) ${recRatio*100}%,
                                rgba(249,115,22,${0.2 + 0.5*intensity}) 100%)`,
                            }}>
                              <span style={{fontFamily:"var(--font-mono)", fontSize:10.5, color:"var(--d-ink)", fontWeight:500}}>
                                {fmtCompact(m.total)}
                              </span>
                            </div>
                          )}
                        </td>
                      );
                    })}
                    <td className="right mono" style={{fontSize:12, fontWeight:500}}>{fmtCompact(total12)}</td>
                  </tr>
                );
              })}
              <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
                <td style={{padding:"12px var(--cell-px)"}}>TOTAL · agence</td>
                {MONTHS_12.map((m, mi) => {
                  const totalMonth = properties.reduce((s, p) => s + monthlyChargesByProperty(p.id, m.y, m.mo).total, 0);
                  return <td key={mi} className="right mono" style={{fontSize:11.5, color: mi === todayIdx ? "var(--orange)" : "var(--d-ink-2)"}}>{fmtCompact(totalMonth)}</td>;
                })}
                <td className="right mono" style={{color:"var(--blue-d)", fontSize:13}}>{fmtCompact(cells.reduce((s,c)=>s+c,0))}</td>
              </tr>
            </tbody>
          </table>
        </div>
        <div style={{padding:"12px 18px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:11.5, color:"var(--d-ink-3)", lineHeight:1.55}}>
          <span style={{color:"var(--blue-d)", fontWeight:700}}>ⓘ</span>{" "}
          Les charges <b style={{color:"var(--d-ink-2)"}}>récupérables</b> (gardiennage, eau commune, électricité) sont refacturées au locataire via la quittance. Les charges <b style={{color:"var(--orange)"}}>non-récupérables</b> (syndic, assurance, taxe foncière) sont débitées au compte bailleur 467x · elles réduisent le net versé.
        </div>
      </div>

      <TravauxGantt onOpenProperty={onOpenProperty}/>
    </div>
  );
}

/* ─── GANTT · travaux dans le temps ─── */
function TravauxGantt({ onOpenProperty }) {
  const monthW = 86;
  const labelW = 240;
  const rowH = 34;
  const headerH = 36;
  const W = labelW + 12 * monthW + 90;
  const H = headerH + TRAVAUX.length * rowH + 12;

  // Map a YYYY-MM-DD date to an X position
  const monthIndexOf = (yyyymmdd) => {
    const [y, m] = yyyymmdd.split("-").map(Number);
    return MONTHS_12.findIndex(mo => mo.y === y && mo.mo === m);
  };
  const xOfDate = (dateStr) => {
    const [y, m, d] = dateStr.split("-").map(Number);
    const mi = monthIndexOf(dateStr);
    if (mi < 0) return labelW;  // out of window
    const daysInMonth = new Date(y, m, 0).getDate();
    return labelW + mi * monthW + ((d - 1) / daysInMonth) * monthW;
  };

  // Today April 18 2026
  const todayX = xOfDate("2026-04-18");

  const statusColor = (s) => {
    if (s === "en cours") return "#6192FF";
    if (s === "à valider") return "#F97316";
    if (s === "devis validé") return "#3FB583";
    if (s === "planifié") return "#9B8AE6";
    return "#7B8494";
  };

  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Travaux planifiés · diagramme de Gantt</h3>
          <div className="panel-sub">{TRAVAUX.length} projets · budget total {fmtCompact(TRAVAUX.reduce((s,t)=>s+t.budget, 0))} FCFA</div>
        </div>
      </div>
      <div style={{padding:"6px 12px 16px", overflowX:"auto"}}>
        <svg viewBox={`0 0 ${W} ${H}`} width={W} style={{display:"block", minWidth:W}}>
          {/* Month headers */}
          {MONTHS_12.map((m, i) => (
            <g key={m.key}>
              <line x1={labelW + i*monthW} x2={labelW + i*monthW} y1={0} y2={H} stroke="#1A2130" strokeWidth="0.75"/>
              <text x={labelW + i*monthW + monthW/2} y={22} textAnchor="middle" style={{fontFamily:"var(--font-mono)", fontSize:11, fill:"var(--d-ink-3)"}}>{m.label}</text>
            </g>
          ))}
          <line x1={labelW + 12*monthW} x2={labelW + 12*monthW} y1={0} y2={H} stroke="#1A2130" strokeWidth="0.75"/>
          <line x1={0} x2={W} y1={headerH} y2={headerH} stroke="var(--d-line)" strokeWidth="1"/>

          {/* Today line */}
          <line x1={todayX} x2={todayX} y1={headerH-2} y2={H} stroke="#F97316" strokeWidth="1.25" strokeDasharray="3 3"/>
          <text x={todayX + 4} y={headerH - 6} style={{fontFamily:"var(--font-mono)", fontSize:9.5, fill:"#F97316", fontWeight:600, letterSpacing:"0.08em"}}>18 AVR</text>

          {/* Rows */}
          {TRAVAUX.map((t, i) => {
            const y = headerH + i*rowH + 6;
            const x1 = xOfDate(t.startDate);
            const x2 = xOfDate(t.endDate);
            const c = statusColor(t.status);
            const prop = propById(t.propertyId);
            return (
              <g key={t.id} onClick={() => prop && onOpenProperty(prop)} style={{cursor:"pointer"}}>
                {/* Label area */}
                <text x={20} y={y + 11} style={{fontSize:11.5, fill:"var(--d-ink)", fontWeight:500}}>{t.title}</text>
                <text x={20} y={y + 22} style={{fontSize:10.5, fill:"var(--d-ink-3)", fontFamily:"var(--font-mono)"}}>{prop?.name} · {fmtCompact(t.budget)} FCFA</text>

                {/* Bar */}
                <rect x={x1} y={y} width={Math.max(8, x2 - x1)} height={20} rx={3} ry={3} fill={c} opacity={0.85}/>
                {t.progress > 0 && (
                  <rect x={x1} y={y} width={Math.max(8, (x2 - x1) * (t.progress / 100))} height={20} rx={3} ry={3} fill={c}/>
                )}
                <text x={x1 + 6} y={y + 13.5} style={{fontSize:10, fill:"#fff", fontWeight:600, fontFamily:"var(--font-mono)"}}>
                  {t.status}{t.progress > 0 ? ` · ${t.progress}%` : ""}
                </text>

                {/* Payment markers */}
                {t.schedule.map((s, si) => {
                  const sx = xOfDate(s.date);
                  return (
                    <g key={si}>
                      <line x1={sx} x2={sx} y1={y - 4} y2={y + 24} stroke={s.paid ? "var(--pos-d)" : "var(--d-ink-3)"} strokeWidth="1"/>
                      <circle cx={sx} cy={y - 6} r={3} fill={s.paid ? "var(--pos-d)" : "var(--d-bg)"} stroke={s.paid ? "var(--pos-d)" : "var(--d-ink-3)"} strokeWidth="1.25"/>
                    </g>
                  );
                })}

                {/* Row line */}
                {i < TRAVAUX.length - 1 && <line x1={0} x2={W} y1={y + rowH - 6} y2={y + rowH - 6} stroke="var(--d-line-2)" strokeWidth="0.75"/>}
              </g>
            );
          })}
        </svg>
      </div>
      <div style={{padding:"10px 18px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", display:"flex", gap:18, fontSize:11, color:"var(--d-ink-3)"}}>
        <span style={{display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:10, height:10, background:"#F97316", borderRadius:2}}/>à valider</span>
        <span style={{display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:10, height:10, background:"#3FB583", borderRadius:2}}/>devis validé</span>
        <span style={{display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:10, height:10, background:"#6192FF", borderRadius:2}}/>en cours</span>
        <span style={{display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:10, height:10, background:"#9B8AE6", borderRadius:2}}/>planifié</span>
        <span style={{marginLeft:"auto", display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:8, height:8, background:"var(--pos-d)", borderRadius:50}}/>règlement effectué</span>
        <span style={{display:"inline-flex", alignItems:"center", gap:6}}><span style={{width:8, height:8, background:"var(--d-bg)", border:"1px solid var(--d-ink-3)", borderRadius:50}}/>règlement à venir</span>
      </div>
    </div>
  );
}

/* ─── LISTE DÉTAILLÉE ─── */
function ChargesList({ onOpenProperty, onEditCharge, onNewCharge }) {
  const [q, setQ] = React.useState("");
  const [freq, setFreq] = React.useState("all");
  const [recov, setRecov] = React.useState("all");

  const filtered = CHARGES.filter(c => {
    if (freq !== "all" && c.freq !== freq) return false;
    if (recov === "rec" && !c.recoverable) return false;
    if (recov === "non" && c.recoverable) return false;
    if (q) {
      const p = propById(c.propertyId);
      const haystack = `${c.category} ${c.supplier} ${c.id} ${p?.name || ""}`.toLowerCase();
      if (!haystack.includes(q.toLowerCase())) return false;
    }
    return true;
  });

  // Annualized cost per row
  const annualOf = (c) => c.freq === "M" ? c.amount * 12 : c.freq === "Q" ? c.amount * 4 : c.amount;
  const annualTotal = filtered.reduce((s,c) => s + annualOf(c), 0);

  return (
    <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="Catégorie, fournisseur, bien…"/>
        </div>
        <div className="seg">
          {[["all","Toutes"],["M","Mensuelles"],["Q","Trimestriel."],["A","Annuelles"]].map(([k,l])=>(
            <button key={k} className={"seg-btn"+(freq===k?" active":"")} onClick={()=>setFreq(k)}>{l}</button>
          ))}
        </div>
        <div className="seg">
          {[["all","Toutes"],["rec","Récupérables"],["non","Non-récup."]].map(([k,l])=>(
            <button key={k} className={"seg-btn"+(recov===k?" active":"")} onClick={()=>setRecov(k)}>{l}</button>
          ))}
        </div>
        <div className="filter-spacer"/>
        <span className="muted mono" style={{fontSize:11}}>{filtered.length} / {CHARGES.length} lignes · budget annuel {fmtCompact(annualTotal)} FCFA</span>
        {onNewCharge && <button className="btn btn-primary" style={{padding:"5px 12px", fontSize:11.5, marginLeft:10}} onClick={onNewCharge}>+ Nouvelle charge</button>}
      </div>
      <div className="table-wrap">
        <table className="tbl">
          <thead>
            <tr>
              <th style={{width:90}}>Code</th>
              <th>Catégorie</th>
              <th>Bien</th>
              <th>Fournisseur</th>
              <th style={{width:90}}>Fréquence</th>
              <th>Cpt</th>
              <th style={{width:96}}>À charge de</th>
              <th className="right">Montant</th>
              <th className="right" style={{minWidth:110}}>Coût annuel</th>
              <th style={{width:44}}></th>
            </tr>
          </thead>
          <tbody>
            {filtered.map(c => {
              const p = propById(c.propertyId);
              return (
                <tr key={c.id} onClick={()=>p && onOpenProperty(p)}>
                  <td className="mono muted" style={{fontSize:11}}>{c.id}</td>
                  <td style={{fontWeight:500}}>
                    {c.category}
                    {c.scope === "lots" && c.lots && c.lots.length > 0 && (
                      <span style={{marginLeft:6, fontSize:10, fontFamily:"var(--font-mono)", padding:"1px 6px", borderRadius:3, background:"rgba(97,146,255,0.12)", color:"var(--blue-d)"}}>
                        lot{c.lots.length>1?"s":""} {c.lots.join(", ")}
                      </span>
                    )}
                  </td>
                  <td>
                    <div style={{fontSize:12}}>{p?.name}</div>
                    <div className="muted mono" style={{fontSize:10.5}}>{c.propertyId}{c.groupId ? " · groupé" : ""}</div>
                  </td>
                  <td className="muted" style={{fontSize:12}}>{c.supplier}</td>
                  <td>
                    <span style={{
                      fontSize:10.5, fontWeight:600, padding:"2px 8px", borderRadius:3,
                      background: c.freq === "M" ? "rgba(97,146,255,0.12)" : c.freq === "Q" ? "rgba(63,181,131,0.12)" : "rgba(155,138,230,0.12)",
                      color: c.freq === "M" ? "var(--blue-d)" : c.freq === "Q" ? "var(--pos-d)" : "#B5A4F0",
                      letterSpacing:"0.02em",
                    }}>{freqLabel(c.freq)}{c.freq === "A" && c.dueMonth ? ` · ${MONTH_NAMES_FR[c.dueMonth-1]}` : ""}</span>
                  </td>
                  <td className="mono" style={{color:"var(--blue-d)", fontSize:11.5}}>{c.account}</td>
                  <td>
                    {(() => {
                      const b = c.bearer || (c.recoverable ? "locataire" : "bailleur");
                      if (b === "locataire") return <span className="chip pos">locataire</span>;
                      if (b === "agence")    return <span className="chip info">agence</span>;
                      return <span className="chip warn">bailleur</span>;
                    })()}
                  </td>
                  <td className="right">{fmtFCFA(c.amount)}</td>
                  <td className="right mono" style={{color:"var(--d-ink-2)"}}>{fmtFCFA(annualOf(c))}</td>
                  <td style={{textAlign:"center"}}>
                    {onEditCharge && (
                      <button title="Modifier la charge" onClick={(e)=>{ e.stopPropagation(); onEditCharge(c); }} style={{
                        width:26, height:26, borderRadius:5, border:"1px solid var(--d-line)", background:"transparent",
                        color:"var(--d-ink-3)", cursor:"pointer", fontSize:12, display:"inline-grid", placeItems:"center",
                      }}>✎</button>
                    )}
                  </td>
                </tr>
              );
            })}
          </tbody>
          <tfoot>
            <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
              <td colSpan={8} style={{padding:"12px var(--cell-px)", fontSize:12}}>TOTAL · {filtered.length} lignes</td>
              <td className="right mono" style={{color:"var(--blue-d)", fontSize:13}}>{fmtFCFA(annualTotal)}</td>
              <td></td>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
  );
}

/* ─── PAR BIEN · cards ─── */
function ChargesParBien({ onOpenProperty }) {
  const properties = MOCK.properties;
  return (
    <div style={{display:"grid", gridTemplateColumns:"repeat(auto-fill, minmax(380px, 1fr))", gap:14}}>
      {properties.map(p => {
        const props = CHARGES.filter(c => c.propertyId === p.id);
        const rec = props.filter(c=>c.recoverable);
        const non = props.filter(c=>!c.recoverable);
        const monthlyAvg = (
          rec.reduce((s,c)=> s + (c.freq === "M" ? c.amount : c.freq === "Q" ? c.amount/3 : c.amount/12), 0) +
          non.reduce((s,c)=> s + (c.freq === "M" ? c.amount : c.freq === "Q" ? c.amount/3 : c.amount/12), 0)
        );
        const recMonthly = rec.reduce((s,c)=> s + (c.freq === "M" ? c.amount : c.freq === "Q" ? c.amount/3 : c.amount/12), 0);
        const nonMonthly = monthlyAvg - recMonthly;
        return (
          <div key={p.id} className="panel" onClick={()=>onOpenProperty(p)} style={{cursor:"pointer"}}>
            <div style={{padding:"16px 18px", borderBottom:"1px solid var(--d-line-2)"}}>
              <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", gap:8, marginBottom:6}}>
                <div>
                  <div style={{fontWeight:500, fontSize:13}}>{p.name}</div>
                  <div className="muted" style={{fontSize:11, marginTop:1}}>{p.type} · {p.units} lots · {p.owner}</div>
                </div>
                <span className="mono muted" style={{fontSize:11}}>{p.id}</span>
              </div>
              <div style={{display:"flex", gap:14, marginTop:12, fontSize:11.5}}>
                <div>
                  <div style={{color:"var(--d-ink-4)", textTransform:"uppercase", letterSpacing:"0.06em", fontSize:10, fontWeight:600}}>Charges / mois</div>
                  <div className="mono" style={{fontSize:15, fontWeight:600, color:"var(--d-ink)", marginTop:3}}>{fmtCompact(monthlyAvg)}</div>
                </div>
                <div>
                  <div style={{color:"var(--d-ink-4)", textTransform:"uppercase", letterSpacing:"0.06em", fontSize:10, fontWeight:600}}>Récupérable</div>
                  <div className="mono" style={{color:"var(--pos-d)", marginTop:3}}>{fmtCompact(recMonthly)}</div>
                </div>
                <div>
                  <div style={{color:"var(--d-ink-4)", textTransform:"uppercase", letterSpacing:"0.06em", fontSize:10, fontWeight:600}}>Non-récup.</div>
                  <div className="mono" style={{color:"var(--orange)", marginTop:3}}>{fmtCompact(nonMonthly)}</div>
                </div>
              </div>
              {/* Split bar */}
              <div style={{height:5, background:"var(--d-sunken)", borderRadius:2.5, overflow:"hidden", display:"flex", marginTop:10}}>
                <div style={{height:"100%", width:`${monthlyAvg ? (recMonthly/monthlyAvg)*100 : 0}%`, background:"var(--pos-d)"}}/>
                <div style={{height:"100%", width:`${monthlyAvg ? (nonMonthly/monthlyAvg)*100 : 0}%`, background:"var(--orange)"}}/>
              </div>
            </div>
            <div style={{padding:"4px 0"}}>
              {props.length === 0 && (
                <div style={{padding:"16px 18px", color:"var(--d-ink-4)", fontSize:12, textAlign:"center"}}>Aucune charge active.</div>
              )}
              {props.slice(0, 6).map(c => (
                <div key={c.id} style={{display:"grid", gridTemplateColumns:"1fr auto auto", gap:10, padding:"7px 18px", borderBottom:"1px solid var(--d-line-2)", alignItems:"center", fontSize:11.5}}>
                  <div style={{minWidth:0}}>
                    <div style={{whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{c.category}</div>
                    <div className="muted" style={{fontSize:10.5, whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{c.supplier}</div>
                  </div>
                  <span style={{
                    fontSize:9.5, fontWeight:600, padding:"1px 5px", borderRadius:2,
                    background: c.recoverable ? "rgba(63,181,131,0.15)" : "rgba(249,115,22,0.15)",
                    color: c.recoverable ? "var(--pos-d)" : "var(--orange)",
                    letterSpacing:"0.04em",
                  }}>{c.freq}</span>
                  <span className="mono" style={{fontSize:11.5, color:"var(--d-ink-2)", minWidth:64, textAlign:"right"}}>{fmtCompact(c.amount)}</span>
                </div>
              ))}
              {props.length > 6 && (
                <div style={{padding:"7px 18px", fontSize:11, color:"var(--d-ink-3)", textAlign:"center"}}>+ {props.length - 6} autres charges</div>
              )}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ─── TRAVAUX · projets détaillés ─── */
function TravauxView({ onOpenProperty }) {
  const [openId, setOpenId] = React.useState(TRAVAUX[0]?.id);
  const open = TRAVAUX.find(t => t.id === openId);

  return (
    <div className="grid-detail">
      <div className="stack">
        <div className="panel" style={{alignSelf:"start"}}>
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Projets travaux</h3>
              <div className="panel-sub">{TRAVAUX.length} projets · prochains 12 mois</div>
            </div>
            <button className="btn btn-primary">+ Nouveau projet</button>
          </div>
          <div>
            {TRAVAUX.map(t => {
              const prop = propById(t.propertyId);
              const isOpen = t.id === openId;
              return (
                <div key={t.id} onClick={()=>setOpenId(t.id)} style={{
                  padding:"14px 18px", borderBottom:"1px solid var(--d-line-2)", cursor:"pointer",
                  background: isOpen ? "rgba(97,146,255,0.05)" : undefined,
                  borderLeft: isOpen ? "2px solid var(--blue-d)" : "2px solid transparent",
                }}>
                  <div style={{display:"flex", justifyContent:"space-between", gap:8, marginBottom:5}}>
                    <div style={{fontSize:12.5, fontWeight:500}}>{t.title}</div>
                    <span className={`chip ${t.status === "en cours" ? "info" : t.status === "à valider" ? "warn" : "neutral"}`}>{t.status}</span>
                  </div>
                  <div className="muted" style={{fontSize:11, marginBottom:8}}>{prop?.name} · {t.supplier}</div>
                  <div style={{display:"flex", justifyContent:"space-between", alignItems:"center"}}>
                    <span className="mono" style={{fontSize:11, color:"var(--d-ink-3)"}}>{t.startDate.slice(8)}/{t.startDate.slice(5,7)} → {t.endDate.slice(8)}/{t.endDate.slice(5,7)}</span>
                    <span className="mono" style={{fontSize:12.5, fontWeight:500}}>{fmtCompact(t.budget)} FCFA</span>
                  </div>
                  {t.progress > 0 && (
                    <div style={{height:3, background:"var(--d-sunken)", borderRadius:2, overflow:"hidden", marginTop:8}}>
                      <div style={{height:"100%", width:`${t.progress}%`, background:"var(--blue-d)"}}/>
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      </div>

      {/* Detail */}
      {open && (
        <div className="stack">
          <TravauxDetailCard t={open} onOpenProperty={onOpenProperty}/>
          <TravauxScheduleCard t={open}/>
        </div>
      )}
    </div>
  );
}

function TravauxDetailCard({ t, onOpenProperty }) {
  const prop = propById(t.propertyId);
  const owner = prop ? BAILLEURS.find(b => b.name === prop.owner) : null;
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">{t.title}</h3>
          <div className="panel-sub mono" style={{fontSize:11}}>{t.id}</div>
        </div>
        <div className="panel-head-right">
          <span className={`chip ${t.status === "en cours" ? "info" : t.status === "à valider" ? "warn" : "neutral"}`}>{t.status}</span>
        </div>
      </div>
      <div>
        <div className="info-row"><div className="info-k">Bien</div><div className="info-v">
          <button onClick={()=>prop && onOpenProperty(prop)} style={{background:"none", border:"none", color:"var(--blue-d)", cursor:"pointer", textDecoration:"underline", textUnderlineOffset:2, fontSize:12.5, padding:0}}>{prop?.name}</button>
          <span className="muted mono" style={{fontSize:11, marginLeft:8}}>{prop?.id}</span>
        </div></div>
        <div className="info-row"><div className="info-k">Bailleur</div><div className="info-v">{prop?.owner}</div></div>
        <div className="info-row"><div className="info-k">Fournisseur</div><div className="info-v">{t.supplier}</div></div>
        <div className="info-row"><div className="info-k">Compte</div><div className="info-v mono" style={{color:"var(--blue-d)"}}>{t.account}{t.account === "234" && " · Immobilisations corporelles"}</div></div>
        <div className="info-row"><div className="info-k">Période</div><div className="info-v mono">{t.startDate} → {t.endDate}</div></div>
        <div className="info-row"><div className="info-k">Budget total</div><div className="info-v mono" style={{fontWeight:600}}>{fmtFCFA(t.budget)}</div></div>
        <div className="info-row"><div className="info-k">Déjà réglé</div><div className="info-v mono" style={{color:"var(--pos-d)"}}>{t.paid > 0 ? fmtFCFA(t.paid) : "—"}</div></div>
        <div className="info-row" style={{borderBottom:"none"}}><div className="info-k">Reste à payer</div><div className="info-v mono" style={{color:"var(--orange)", fontWeight:600}}>{fmtFCFA(t.budget - t.paid)}</div></div>
      </div>
      <div style={{padding:"14px 18px", borderTop:"1px solid var(--d-line)", display:"flex", gap:8, justifyContent:"flex-end", background:"var(--d-sunken)"}}>
        {t.status === "à valider" && <button className="btn btn-secondary">Refuser</button>}
        {t.status === "à valider" && <button className="btn btn-primary">Valider le devis</button>}
        {t.status !== "à valider" && <button className="btn btn-secondary">Modifier</button>}
        {t.status !== "à valider" && <button className="btn btn-primary">Marquer avancement</button>}
      </div>
    </div>
  );
}

function TravauxScheduleCard({ t }) {
  return (
    <div className="panel">
      <div className="panel-head">
        <h3 className="panel-title">Échéancier de paiement</h3>
        <span className="muted" style={{fontSize:11}}>{t.schedule.length} échéances</span>
      </div>
      <div className="table-wrap">
        <table className="tbl">
          <thead>
            <tr>
              <th style={{width:90}}>Date</th>
              <th>Libellé</th>
              <th className="right">Montant</th>
              <th className="right">% budget</th>
              <th style={{width:90}}>Statut</th>
            </tr>
          </thead>
          <tbody>
            {t.schedule.map((s, i) => (
              <tr key={i}>
                <td className="mono muted">{s.date.slice(8)}/{s.date.slice(5,7)}/{s.date.slice(2,4)}</td>
                <td>{s.label}</td>
                <td className="right">{fmtFCFA(s.amount)}</td>
                <td className="right muted mono" style={{fontSize:11}}>{Math.round((s.amount/t.budget)*100)}%</td>
                <td><span className={`chip ${s.paid ? "pos" : "neutral"}`}>{s.paid ? "réglé" : "à payer"}</span></td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
              <td colSpan={2} style={{padding:"10px var(--cell-px)"}}>TOTAL</td>
              <td className="right mono">{fmtFCFA(t.schedule.reduce((s,x)=>s+x.amount,0))}</td>
              <td></td>
              <td></td>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
  );
}

/* ─── PROVISIONS · fonds travaux par bailleur ─── */
function ProvisionsView({ onOpenBailleur }) {
  const total = PROVISIONS.reduce((s,p)=>s+p.balance, 0);
  const monthly = PROVISIONS.reduce((s,p)=>s+p.monthly, 0);

  return (
    <div className="stack">
      <div className="panel">
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Provisions fonds travaux · compte 161</h3>
            <div className="panel-sub">Sommes constituées sur chaque compte bailleur pour absorber les gros travaux</div>
          </div>
          <button className="btn btn-secondary">+ Nouvelle provision</button>
        </div>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>Bailleur</th>
                <th className="right">Solde provision</th>
                <th className="right">Abondement / mois</th>
                <th>Affectation prévue</th>
                <th>Depuis</th>
              </tr>
            </thead>
            <tbody>
              {PROVISIONS.map(p => {
                const b = BAILLEURS.find(x => x.id === p.bailleurId);
                return (
                  <tr key={p.bailleurId} onClick={()=>b && onOpenBailleur(b)}>
                    <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="mono muted" style={{fontSize:11}}>161-{p.bailleurId.replace("B-","")}</div>
                        </div>
                      </div>
                    </td>
                    <td className="right mono" style={{color:"var(--pos-d)", fontWeight:600, fontSize:13}}>{fmtFCFA(p.balance)}</td>
                    <td className="right mono">+{fmtFCFA(p.monthly)}</td>
                    <td className="muted" style={{fontSize:12}}>{p.earmarked}</td>
                    <td className="mono muted" style={{fontSize:11}}>{p.since}</td>
                  </tr>
                );
              })}
            </tbody>
            <tfoot>
              <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
                <td style={{padding:"12px var(--cell-px)"}}>TOTAL · {PROVISIONS.length} bailleurs</td>
                <td className="right mono" style={{color:"var(--pos-d)", fontSize:14}}>{fmtFCFA(total)}</td>
                <td className="right mono">+{fmtFCFA(monthly)} / mois</td>
                <td></td>
                <td></td>
              </tr>
            </tfoot>
          </table>
        </div>
      </div>

      <div className="grid-2">
        <div className="panel">
          <div className="panel-head">
            <h3 className="panel-title">Couverture des travaux planifiés</h3>
          </div>
          <div style={{padding:"6px 0"}}>
            {TRAVAUX.filter(t => t.status !== "à valider").map(t => {
              const prop = propById(t.propertyId);
              const b = prop ? BAILLEURS.find(x => x.name === prop.owner) : null;
              const prov = PROVISIONS.find(p => p.bailleurId === b?.id);
              const coverage = prov ? Math.min(100, Math.round((prov.balance/t.budget)*100)) : 0;
              const ok = coverage >= 100;
              return (
                <div key={t.id} style={{padding:"12px 18px", borderBottom:"1px solid var(--d-line-2)", display:"grid", gridTemplateColumns:"1fr auto", gap:14, alignItems:"center"}}>
                  <div style={{minWidth:0}}>
                    <div style={{fontSize:12.5, fontWeight:500, marginBottom:3}}>{t.title}</div>
                    <div className="muted" style={{fontSize:11, marginBottom:8}}>{prop?.name} · {b?.name}</div>
                    <div style={{height:5, background:"var(--d-sunken)", borderRadius:2.5, overflow:"hidden"}}>
                      <div style={{height:"100%", width:`${coverage}%`, background: ok ? "var(--pos-d)" : "var(--warn-d)"}}/>
                    </div>
                    <div style={{display:"flex", justifyContent:"space-between", marginTop:5, fontSize:10.5, color:"var(--d-ink-3)", fontFamily:"var(--font-mono)"}}>
                      <span>{prov ? fmtCompact(prov.balance) : "—"} provisionnés</span>
                      <span>{fmtCompact(t.budget)} requis</span>
                    </div>
                  </div>
                  <span className={`chip ${ok ? "pos" : coverage > 0 ? "warn" : "neg"}`}>{coverage}%</span>
                </div>
              );
            })}
          </div>
        </div>
        <div className="panel">
          <div className="panel-head"><h3 className="panel-title">Politique de provisions</h3></div>
          <div style={{padding:"16px 20px", fontSize:12.5, color:"var(--d-ink-2)", lineHeight:1.65}}>
            <p style={{margin:"0 0 12px"}}>Les provisions sont constituées <b style={{color:"var(--d-ink)"}}>mensuellement</b> par retenue automatique sur le net versé au bailleur. Elles s'inscrivent au passif du compte 467 et sont reclassées en compte <span className="mono" style={{color:"var(--blue-d)"}}>161</span> (provisions long terme).</p>
            <p style={{margin:"0 0 12px"}}>Lors d'un règlement de travaux, la provision est <b style={{color:"var(--d-ink)"}}>débitée en priorité</b> avant tout prélèvement complémentaire sur le compte courant du bailleur.</p>
            <p style={{margin:0, padding:"10px 12px", background:"var(--d-sunken)", borderRadius:5, borderLeft:"2px solid var(--orange)", color:"var(--d-ink-3)"}}>
              <b style={{color:"var(--d-ink-2)"}}>À surveiller</b> · Mme A. Ba n'a pas de provision constituée. La rénovation cuisine de juin (450 000 FCFA) sera entièrement prélevée sur ses encaissements de mai-juin.
            </p>
          </div>
        </div>
      </div>
    </div>
  );
}

/* Export */
Object.assign(window, {
  ChargesPage, CHARGES, TRAVAUX, PROVISIONS,
  chargeOccursIn, monthlyChargesByProperty, monthlyChargesByOwner,
  upsertCharge, deleteCharge, useCharges, nextChargeId,
  CHARGE_FREQ_LABEL: freqLabel, chargePropById: propById,
});

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

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

/* ═══════════════════════════════════════════════════════════════════════
   MODULE COMPTABILITÉ — SYSCOHADA
   • Compta Bailleurs     · grand livre par bailleur (compte 467x)
   • Trop-perçus          · soldes débiteurs bailleurs
   • Récépissés           · attestations de versement
   • Compta Agence        · compte de résultat agence
   • Export comptable     · vers Sage / Saari / Ciel / CSV SYSCOHADA
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Plan comptable ressemblance SYSCOHADA — projeté pour BA Immo ─── */
const PLAN = {
  "411":   "Clients · locataires",
  "467":   "Comptes de tiers · bailleurs",
  "5211":  "Banque CBAO · BA Immo",
  "5212":  "Banque Ecobank",
  "5215":  "Wave Business",
  "5216":  "Orange Money Pro",
  "531":   "Caisse principale",
  "706":   "Commissions de gestion locative",
  "707":   "Honoraires de relocation",
  "708":   "Refacturation charges",
  "601":   "Achats fournitures bureau",
  "622":   "Locations & charges locatives",
  "623":   "Publicité & annonces",
  "626":   "Téléphonie · internet",
  "628":   "Honoraires comptable",
  "641":   "Salaires bruts",
  "645":   "Charges sociales",
  "651":   "Frais bancaires",
};

const MONTHS = BAILLEUR_MONTHS.map(m => ({ code: m.code, l: m.label.replace("Mars 26","Mars 26").replace(" 25"," 25").replace(" 26"," 26") }));

// Compte 467 sub-account per bailleur — built from B-001 → "467001"
const compteBailleur = b => "467" + b.id.replace("B-","");

/* Build full ledger entries (last 6 months) for a given bailleur — and
   produce per-month aggregated totals + a running solde (créditeur = on
   doit au bailleur, débiteur = trop-perçu agence à régler).
   Numbers come from the SHARED source `getBailleurMonthly` so every
   page sees the same figures. */
function buildBailleurLedger(b) {
  const monthly = getBailleurHistory(b);  // [{month,collected,commission,charges,versement,net,channel}]
  // Build chronological entries. Each month posts up to 4 lines:
  //   credit  · loyers encaissés       (411 → 467)
  //   debit   · commission gestion     (467 → 706)
  //   debit   · charges refacturées    (467 → 708)
  //   debit   · versement net          (467 → 521x)
  const entries = [];
  monthly.forEach(({month, collected, commission, charges, versement, channel}) => {
    const sfx = month.code.slice(-7) + "-" + b.id.replace("B-","");
    const cpt521 = channel === "Wave" ? "5215" : channel === "Orange Money" ? "5216"
                 : channel === "Ecobank" ? "5212" : channel === "BICIS" ? "5213" : "5211";
    if (collected > 0) entries.push({
      date:`05 ${month.label}`, pcs:`QT-${sfx}`,
      lib:`Encaissement loyers · ${month.label}`,
      cpt:compteBailleur(b), ctp:"411", debit:0, credit:collected,
    });
    if (commission > 0) entries.push({
      date:`06 ${month.label}`, pcs:`COM-${sfx}`,
      lib:`Commission gestion ${(b.commissionRate*100).toFixed(1).replace(".",",")}% · ${month.label}`,
      cpt:compteBailleur(b), ctp:"706", debit:commission, credit:0,
    });
    if (charges > 0) entries.push({
      date:`08 ${month.label}`, pcs:`CHG-${sfx}`,
      lib:"Charges courantes refacturées",
      cpt:compteBailleur(b), ctp:"708", debit:charges, credit:0,
    });
    if (versement > 0) entries.push({
      date:`30 ${month.label}`, pcs:`VR-${sfx}`,
      lib:`Versement net · ${month.label}`,
      cpt:compteBailleur(b), ctp:cpt521, debit:versement, credit:0,
    });
  });
  // Running solde
  let s = 0;
  const withSolde = entries.map(e => { s += (e.credit - e.debit); return {...e, solde:s}; });
  const periodTotals = monthly.reduce((acc, m) => ({
    collected: acc.collected + m.collected,
    commission: acc.commission + m.commission,
    charges: acc.charges + m.charges,
    versement: acc.versement + m.versement,
  }), { collected:0, commission:0, charges:0, versement:0 });
  // Mapping for pages that want the monthly view directly
  const monthlyForView = monthly.map(m => ({
    mo: { l: m.month.label },
    collected: m.collected, commission: m.commission,
    charges: m.charges, versement: m.versement,
  }));
  return { entries: withSolde, monthly: monthlyForView, periodTotals, soldeFin: s };
}

/* ═══════════════════════════════════════════════════════════════════════
   1 · COMPTABILITÉ BAILLEURS
   ═══════════════════════════════════════════════════════════════════════ */
function ComptaBailleursPage({ onOpenBailleur }) {
  const [tab, setTab] = React.useState("consolidée");
  const [period, setPeriod] = React.useState("avril");
  const [selectedId, setSelectedId] = React.useState(BAILLEURS[0].id);
  const [closedPeriods, setClosedPeriods] = React.useState([]);
  const [showCloture, setShowCloture] = React.useState(false);
  const periodLabel = { mars:"Mars 2026", avril:"Avril 2026", t2:"T2 2026", "6m":"6 derniers mois" }[period] || period;
  const isClosed = closedPeriods.includes(periodLabel);

  const enriched = React.useMemo(() => BAILLEURS.map(b => {
    const l = buildBailleurLedger(b);
    return { ...b, ...l };
  }), []);

  const totals = enriched.reduce((acc, b) => ({
    collected: acc.collected + b.periodTotals.collected,
    commission: acc.commission + b.periodTotals.commission,
    charges: acc.charges + b.periodTotals.charges,
    versement: acc.versement + b.periodTotals.versement,
    solde: acc.solde + b.soldeFin,
  }), { collected:0, commission:0, charges:0, versement:0, solde:0 });

  const tropPercus = enriched.filter(b => b.soldeFin < 0);
  const aVerser = enriched.filter(b => b.soldeFin > 0);

  const selected = enriched.find(b => b.id === selectedId);

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Comptabilité bailleurs</h1>
          <div className="page-sub">Compte <span className="mono">467xxx</span> · {BAILLEURS.length} comptes de tiers · grand livre détaillé sur 6 mois</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Imprimer la balance</button>
          <button className="btn btn-secondary" onClick={()=>setTab("lettrage")}>Lettrage automatique</button>
          <button className="btn btn-primary" disabled={isClosed} style={{opacity:isClosed?0.5:1, cursor:isClosed?"not-allowed":"pointer"}} onClick={()=>setShowCloture(true)}>{isClosed ? "Période clôturée" : "Clôturer la période"}</button>
        </div>
      </div>

      {isClosed && (
        <div style={{display:"flex", alignItems:"center", gap:10, padding:"10px 16px", marginBottom:16, borderRadius:8, border:"1px solid rgba(63,181,131,0.3)", background:"rgba(63,181,131,0.06)", fontSize:12.5, color:"var(--d-ink-2)"}}>
          <span style={{width:20, height:20, borderRadius:50, background:"var(--pos-d)", color:"#fff", display:"grid", placeItems:"center", fontSize:11, fontWeight:700, flexShrink:0}}>🔒</span>
          <span><b>{periodLabel} clôturée.</b> Les écritures sont verrouillées — aucune modification possible. Les CRG et versements de la période sont définitifs.</span>
          <button className="btn btn-ghost" style={{marginLeft:"auto", padding:"4px 10px", fontSize:11.5}} onClick={()=>setClosedPeriods(p=>p.filter(x=>x!==periodLabel))}>Rouvrir</button>
        </div>
      )}

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Dû aux bailleurs</span><span style={{color:"var(--d-ink-4)", fontWeight:400, textTransform:"none", letterSpacing:0}}>Solde créditeur consolidé</span></div>
          <div className="kpi-value num">{(totals.solde/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}}>{aVerser.length} bailleurs</span><span>à régler avant le 30 avril</span></div>
          <div className="kpi-spark"><Spark values={[7.2,7.6,8.0,8.3,8.5,8.8,9.0,9.2,9.4,9.6,10.1,10.6]} color="#6192FF"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Loyers encaissés · 6M</span></div>
          <div className="kpi-value num">{(totals.collected/1_000_000).toFixed(1).replace(".",",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span>compte 411 → 467x</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Commissions encaissées</span></div>
          <div className="kpi-value num">{fmtCompact(totals.commission)}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span>compte 706 · produit agence</span></div>
        </div>
        <div className="kpi" style={{borderColor: tropPercus.length ? "rgba(249,115,22,0.30)" : undefined}}>
          <div className="kpi-label"><span>Trop-perçus</span>{tropPercus.length > 0 && <span style={{color:"var(--orange)", fontWeight:600}}>{tropPercus.length} à régulariser</span>}</div>
          <div className="kpi-value num" style={{color: tropPercus.length ? "var(--orange)" : "var(--d-ink)"}}>{tropPercus.length}</div>
          <div className="kpi-meta"><span>{tropPercus.length ? "soldes débiteurs" : "aucune anomalie"}</span></div>
        </div>
      </div>

      <div className="tabs">
        {[
          ["consolidée", "Vue consolidée", enriched.length],
          ["livre", "Grand livre", null],
          ["balance", "Balance âgée", null],
          ["lettrage", "Lettrage", 18],
        ].map(([k,l,n])=>(
          <button key={k} className={"tab" + (tab===k?" active":"")} onClick={()=>setTab(k)}>{l}{n != null && <span className="tab-count">{n}</span>}</button>
        ))}
        <div style={{flex:1}}/>
        <div className="seg" style={{margin:"0 0 -1px"}}>
          {[["mars","Mars"],["avril","Avril"],["t2","T2 26"],["6m","6M"]].map(([k,l])=>(
            <button key={k} className={"seg-btn"+(period===k?" active":"")} onClick={()=>setPeriod(k)}>{l}</button>
          ))}
        </div>
      </div>

      {tab === "consolidée" && (
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Balance des comptes 467 · bailleurs</h3>
              <div className="panel-sub">Sur 6 mois glissants · clôture mensuelle au 30</div>
            </div>
            <button className="btn btn-ghost">Exporter Excel</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:90}}>Compte</th>
                  <th>Bailleur</th>
                  <th className="right">Encaissements</th>
                  <th className="right">Commissions</th>
                  <th className="right">Charges</th>
                  <th className="right">Versements</th>
                  <th className="right" style={{minWidth:140}}>Solde fin de période</th>
                  <th style={{width:90}}>Statut</th>
                </tr>
              </thead>
              <tbody>
                {enriched.map(b => {
                  const isCred = b.soldeFin > 0, isDeb = b.soldeFin < 0;
                  return (
                    <tr key={b.id} onClick={()=>{ setSelectedId(b.id); setTab("livre"); }}>
                      <td className="mono muted" style={{fontSize:11.5}}>{compteBailleur(b)}</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}}>{b.entity}</div>
                          </div>
                        </div>
                      </td>
                      <td className="right" style={{color:"var(--pos-d)"}}>{fmtFCFA(b.periodTotals.collected)}</td>
                      <td className="right muted">−{b.periodTotals.commission.toLocaleString("fr-FR")}</td>
                      <td className="right muted">{b.periodTotals.charges ? "−"+b.periodTotals.charges.toLocaleString("fr-FR") : <span className="empty-dash">—</span>}</td>
                      <td className="right muted">−{b.periodTotals.versement.toLocaleString("fr-FR")}</td>
                      <td className="right" style={{
                        color: isCred ? "var(--pos-d)" : isDeb ? "var(--orange)" : "var(--d-ink-4)",
                        fontWeight: 600,
                      }}>
                        {isCred ? "+" : isDeb ? "−" : ""}{fmtFCFA(Math.abs(b.soldeFin)).replace("−","")}
                        <div style={{fontSize:10, color:"var(--d-ink-4)", fontWeight:500, textTransform:"uppercase", letterSpacing:"0.05em", marginTop:2}}>
                          {isCred ? "créditeur" : isDeb ? "débiteur" : "soldé"}
                        </div>
                      </td>
                      <td>
                        <span className={`chip ${isCred ? "info" : isDeb ? "neg" : "neutral"}`}>
                          {isCred ? "à verser" : isDeb ? "trop-perçu" : "lettré"}
                        </span>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
              <tfoot>
                <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
                  <td colSpan={2} style={{padding:"12px var(--cell-px)", fontSize:12}}>TOTAL · classe 467</td>
                  <td className="right mono" style={{color:"var(--pos-d)"}}>{fmtFCFA(totals.collected)}</td>
                  <td className="right mono muted">−{totals.commission.toLocaleString("fr-FR")}</td>
                  <td className="right mono muted">−{totals.charges.toLocaleString("fr-FR")}</td>
                  <td className="right mono muted">−{totals.versement.toLocaleString("fr-FR")}</td>
                  <td className="right mono" style={{color:"var(--pos-d)", fontSize:13}}>+{fmtFCFA(totals.solde).replace("−","")}</td>
                  <td></td>
                </tr>
              </tfoot>
            </table>
          </div>
        </div>
      )}

      {tab === "livre" && selected && (
        <div className="grid-detail">
          {/* Left — entries */}
          <div className="stack">
            <div className="panel">
              <div className="panel-head">
                <div style={{display:"flex", alignItems:"center", gap:14}}>
                  <select value={selectedId} onChange={e=>setSelectedId(e.target.value)} style={{
                    background:"var(--d-sunken)", color:"var(--d-ink)", border:"1px solid var(--d-line)",
                    padding:"6px 10px", borderRadius:5, fontSize:12.5, fontFamily:"var(--font-sans)",
                  }}>
                    {enriched.map(b => <option key={b.id} value={b.id}>{compteBailleur(b)} · {b.name}</option>)}
                  </select>
                  <div>
                    <div style={{fontSize:12.5, fontWeight:500}}>Grand livre · {selected.name}</div>
                    <div className="muted" style={{fontSize:11}}>{selected.entries.length} écritures · 6 mois</div>
                  </div>
                </div>
                <div className="panel-head-right">
                  <button className="btn btn-ghost" onClick={()=>onOpenBailleur(selected)}>Fiche bailleur →</button>
                  <button className="btn btn-secondary">Imprimer</button>
                </div>
              </div>
              <div className="table-wrap">
                <table className="tbl">
                  <thead>
                    <tr>
                      <th style={{width:70}}>Date</th>
                      <th style={{width:130}}>Pièce</th>
                      <th>Libellé</th>
                      <th style={{width:64}}>Compte</th>
                      <th style={{width:64}}>Contre-partie</th>
                      <th className="right">Débit</th>
                      <th className="right">Crédit</th>
                      <th className="right" style={{minWidth:120}}>Solde</th>
                    </tr>
                  </thead>
                  <tbody>
                    {selected.entries.map((e, i) => (
                      <tr key={i}>
                        <td className="muted mono">{e.date}</td>
                        <td className="mono muted" style={{fontSize:11}}>{e.pcs}</td>
                        <td>{e.lib}</td>
                        <td className="mono" style={{fontSize:11.5, color:"var(--blue-d)"}}>{e.cpt}</td>
                        <td className="mono muted" style={{fontSize:11.5}}>{e.ctp}</td>
                        <td className="right">{e.debit > 0 ? fmtFCFA(e.debit) : <span className="empty-dash">—</span>}</td>
                        <td className="right" style={{color: e.credit > 0 ? "var(--pos-d)" : undefined}}>{e.credit > 0 ? fmtFCFA(e.credit) : <span className="empty-dash">—</span>}</td>
                        <td className="right mono" style={{fontWeight: i === selected.entries.length-1 ? 600 : 400, color: e.solde >= 0 ? "var(--pos-d)" : "var(--orange)"}}>
                          {e.solde >= 0 ? "+" : "−"}{fmtCompact(Math.abs(e.solde))}
                        </td>
                      </tr>
                    ))}
                  </tbody>
                  <tfoot>
                    <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
                      <td colSpan={5} style={{padding:"12px var(--cell-px)", fontSize:12}}>TOTAUX</td>
                      <td className="right mono">{fmtFCFA(selected.entries.reduce((s,e)=>s+e.debit,0))}</td>
                      <td className="right mono" style={{color:"var(--pos-d)"}}>{fmtFCFA(selected.entries.reduce((s,e)=>s+e.credit,0))}</td>
                      <td className="right mono" style={{color: selected.soldeFin >= 0 ? "var(--pos-d)" : "var(--orange)", fontSize:13}}>
                        {selected.soldeFin >= 0 ? "+" : "−"}{fmtFCFA(Math.abs(selected.soldeFin)).replace("−","")}
                      </td>
                    </tr>
                  </tfoot>
                </table>
              </div>
            </div>
          </div>

          {/* Right — t-account + monthly + actions */}
          <div className="stack">
            <TAccountCard b={selected}/>
            <div className="panel">
              <div className="panel-head">
                <h3 className="panel-title">Récap mensuel</h3>
                <span className="muted mono" style={{fontSize:11}}>en FCFA</span>
              </div>
              <div className="table-wrap">
                <table className="tbl">
                  <thead>
                    <tr><th>Mois</th><th className="right">Crédit</th><th className="right">Débit</th><th className="right">Net</th></tr>
                  </thead>
                  <tbody>
                    {selected.monthly.map((m, i) => {
                      const cr = m.collected;
                      const db = m.commission + m.charges + m.versement;
                      return (
                        <tr key={i}>
                          <td className="mono">{m.mo.l}</td>
                          <td className="right" style={{color: cr ? "var(--pos-d)" : undefined}}>{cr ? fmtCompact(cr) : "—"}</td>
                          <td className="right muted">{db ? "−"+fmtCompact(db) : "—"}</td>
                          <td className="right mono" style={{color: (cr-db) > 0 ? "var(--pos-d)" : (cr-db) < 0 ? "var(--orange)" : "var(--d-ink-4)", fontWeight:500}}>
                            {(cr-db) === 0 ? "—" : (cr-db > 0 ? "+" : "−") + fmtCompact(Math.abs(cr-db))}
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            </div>
            <div className="panel">
              <div className="panel-head"><h3 className="panel-title">Identité bancaire</h3></div>
              <div>
                <div className="info-row"><div className="info-k">Compte tiers</div><div className="info-v mono" style={{color:"var(--blue-d)"}}>{compteBailleur(selected)}</div></div>
                <div className="info-row"><div className="info-k">RIB</div><div className="info-v mono" style={{fontSize:11.5}}>{selected.bank}</div></div>
                <div className="info-row"><div className="info-k">NINEA</div><div className="info-v mono">{selected.ninea || "—"}</div></div>
                <div className="info-row" style={{borderBottom:"none"}}><div className="info-k">Commission</div><div className="info-v mono">{(selected.commissionRate*100).toFixed(1).replace(".",",")}%</div></div>
              </div>
            </div>
          </div>
        </div>
      )}

      {tab === "balance" && <AgedBalanceCard enriched={enriched}/>}

      {tab === "lettrage" && <LettrageCard/>}

      {showCloture && <ClotureModal periodLabel={periodLabel} totals={totals} aVerser={aVerser.length} tropPercus={tropPercus.length} onClose={()=>setShowCloture(false)} onConfirm={()=>{ setClosedPeriods(p=>[...new Set([...p, periodLabel])]); setShowCloture(false); }}/>}
    </div>
  );
}

/* Clôture de période — confirmation + checklist */
function ClotureModal({ periodLabel, totals, aVerser, tropPercus, onClose, onConfirm }) {
  const checks = [
    { ok:true,  label:"Rapprochement bancaire terminé", sub:"42 opérations · 0 écart" },
    { ok:true,  label:"Lettrage des écritures", sub:"3 lettrées · 1 écart à surveiller" },
    { ok: tropPercus === 0, label:"Trop-perçus régularisés", sub: tropPercus === 0 ? "Aucun solde débiteur" : `${tropPercus} compte(s) en trop-perçu` },
    { ok:true,  label:"CRG générés et envoyés", sub:"Documents transmis aux bailleurs" },
  ];
  const allOk = checks.every(c => c.ok);
  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:"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}}>Clôture de période</div>
            <div style={{fontSize:16, fontWeight:600}}>{periodLabel}</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"}}>
          {/* Totals */}
          <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:0, border:"1px solid var(--d-line)", borderRadius:8, overflow:"hidden", marginBottom:16}}>
            {[
              ["Loyers encaissés", fmtFCFA(totals.collected)],
              ["Commissions", fmtFCFA(totals.commission)],
              ["Net à verser", fmtFCFA(totals.solde)],
              ["Bailleurs à régler", String(aVerser)],
            ].map(([k,v],i)=>(
              <div key={i} style={{padding:"10px 14px", borderRight: i%2===0?"1px solid var(--d-line-2)":"none", borderBottom: i<2?"1px solid var(--d-line-2)":"none"}}>
                <div style={{fontSize:10, textTransform:"uppercase", letterSpacing:"0.05em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:3}}>{k}</div>
                <div className="mono" style={{fontSize:13, fontWeight:600}}>{v}</div>
              </div>
            ))}
          </div>
          {/* Checklist */}
          <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.06em", textTransform:"uppercase", color:"var(--d-ink-4)", marginBottom:8}}>Contrôles avant clôture</div>
          <div style={{display:"flex", flexDirection:"column", gap:8, marginBottom:14}}>
            {checks.map((c,i)=>(
              <div key={i} style={{display:"flex", alignItems:"flex-start", gap:10, fontSize:12.5}}>
                <span style={{width:18, height:18, borderRadius:50, flexShrink:0, display:"grid", placeItems:"center", fontSize:10, fontWeight:700, color:"#fff", background: c.ok?"var(--pos-d)":"var(--orange)"}}>{c.ok?"✓":"!"}</span>
                <div><div style={{color:"var(--d-ink)"}}>{c.label}</div><div className="muted" style={{fontSize:11, marginTop:1}}>{c.sub}</div></div>
              </div>
            ))}
          </div>
          <div style={{padding:"10px 14px", borderRadius:6, fontSize:11.5, lineHeight:1.5, border:"1px solid var(--d-line)", background:"var(--d-sunken)", color:"var(--d-ink-3)"}}>
            La clôture <b style={{color:"var(--d-ink-2)"}}>verrouille définitivement</b> les écritures de {periodLabel}. Les CRG et versements deviennent non modifiables. {allOk ? "Tous les contrôles sont au vert." : "Des contrôles restent en attente — vous pouvez clôturer mais ils seront reportés."}
          </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" onClick={onConfirm}>Clôturer {periodLabel}</button>
        </footer>
      </div>
    </div>
  );
}

/* T-Account · classic accounting debit/credit visual */
function TAccountCard({ b }) {
  const debits = b.entries.filter(e => e.debit > 0);
  const credits = b.entries.filter(e => e.credit > 0);
  const totalD = debits.reduce((s,e)=>s+e.debit,0);
  const totalC = credits.reduce((s,e)=>s+e.credit,0);
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Compte en T · {compteBailleur(b)}</h3>
          <div className="panel-sub">Vue débit / crédit · 6 mois</div>
        </div>
      </div>
      <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", borderTop:"1px solid var(--d-line)"}}>
        <div style={{borderRight:"1px solid var(--d-line)"}}>
          <div style={{padding:"10px 14px", borderBottom:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-3)", fontWeight:600}}>Débit</div>
          {debits.slice(-7).map((e, i) => (
            <div key={i} style={{padding:"8px 14px", borderBottom:"1px solid var(--d-line-2)", display:"flex", justifyContent:"space-between", gap:8, fontSize:11.5}}>
              <span className="muted" style={{whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{e.lib}</span>
              <span className="mono">{fmtCompact(e.debit)}</span>
            </div>
          ))}
        </div>
        <div>
          <div style={{padding:"10px 14px", borderBottom:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--pos-d)", fontWeight:600}}>Crédit</div>
          {credits.slice(-7).map((e, i) => (
            <div key={i} style={{padding:"8px 14px", borderBottom:"1px solid var(--d-line-2)", display:"flex", justifyContent:"space-between", gap:8, fontSize:11.5}}>
              <span className="muted" style={{whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{e.lib}</span>
              <span className="mono" style={{color:"var(--pos-d)"}}>{fmtCompact(e.credit)}</span>
            </div>
          ))}
        </div>
      </div>
      <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)"}}>
        <div style={{padding:"12px 14px", borderRight:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between"}}>
          <span style={{fontSize:11, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600}}>Total D.</span>
          <span className="mono" style={{fontSize:13, fontWeight:600}}>{fmtFCFA(totalD)}</span>
        </div>
        <div style={{padding:"12px 14px", display:"flex", justifyContent:"space-between"}}>
          <span style={{fontSize:11, color:"var(--pos-d)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600}}>Total C.</span>
          <span className="mono" style={{fontSize:13, fontWeight:600, color:"var(--pos-d)"}}>{fmtFCFA(totalC)}</span>
        </div>
      </div>
      <div style={{padding:"14px 18px", borderTop:"1px solid var(--d-line)", display:"flex", justifyContent:"space-between", alignItems:"baseline"}}>
        <span style={{fontSize:11.5, color:"var(--d-ink-3)"}}>Solde {b.soldeFin >= 0 ? "créditeur" : "débiteur"}</span>
        <span className="mono" style={{fontSize:18, fontWeight:600, color: b.soldeFin >= 0 ? "var(--pos-d)" : "var(--orange)"}}>
          {b.soldeFin >= 0 ? "+" : "−"}{fmtFCFA(Math.abs(b.soldeFin)).replace("−","")}
        </span>
      </div>
    </div>
  );
}

/* Aged balance — how long has each bailleur's money been on our books */
function AgedBalanceCard({ enriched }) {
  // For each bailleur with a credit balance, fake an age distribution
  const rows = enriched.filter(b => b.soldeFin > 0).map(b => {
    const s = b.soldeFin;
    // April money = most of it (current), then progressively less older
    return {
      b,
      current: Math.round(s * 0.62),
      d30: Math.round(s * 0.22),
      d60: Math.round(s * 0.11),
      d90: Math.round(s * 0.05),
      total: s,
    };
  });
  const totals = rows.reduce((acc, r) => ({
    current: acc.current + r.current, d30: acc.d30 + r.d30,
    d60: acc.d60 + r.d60, d90: acc.d90 + r.d90, total: acc.total + r.total,
  }), { current:0, d30:0, d60:0, d90:0, total:0 });
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Balance âgée · soldes créditeurs bailleurs</h3>
          <div className="panel-sub">Ancienneté des fonds détenus pour compte · obligation prudentielle</div>
        </div>
        <button className="btn btn-ghost">Exporter</button>
      </div>
      <div className="table-wrap">
        <table className="tbl">
          <thead>
            <tr>
              <th>Bailleur</th>
              <th className="right">Courant · &lt;30j</th>
              <th className="right">30–60j</th>
              <th className="right">60–90j</th>
              <th className="right" style={{color:"var(--orange)"}}>&gt; 90j</th>
              <th className="right">Total dû</th>
            </tr>
          </thead>
          <tbody>
            {rows.map(r => (
              <tr key={r.b.id}>
                <td>
                  <div style={{display:"flex", alignItems:"center", gap:10}}>
                    <div className="avatar" style={{background:r.b.color, color:"#fff"}}>{r.b.avatar}</div>
                    <div>
                      <div style={{fontWeight:500}}>{r.b.name}</div>
                      <div className="mono muted" style={{fontSize:11}}>{compteBailleur(r.b)}</div>
                    </div>
                  </div>
                </td>
                <td className="right">{fmtFCFA(r.current)}</td>
                <td className="right">{fmtFCFA(r.d30)}</td>
                <td className="right" style={{color: r.d60 > 200_000 ? "var(--warn-d)" : undefined}}>{fmtFCFA(r.d60)}</td>
                <td className="right" style={{color: r.d90 > 0 ? "var(--orange)" : "var(--d-ink-4)"}}>{r.d90 > 0 ? fmtFCFA(r.d90) : <span className="empty-dash">—</span>}</td>
                <td className="right" style={{color:"var(--pos-d)", fontWeight:600}}>{fmtFCFA(r.total)}</td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
              <td style={{padding:"12px var(--cell-px)"}}>TOTAL</td>
              <td className="right mono">{fmtFCFA(totals.current)}</td>
              <td className="right mono">{fmtFCFA(totals.d30)}</td>
              <td className="right mono">{fmtFCFA(totals.d60)}</td>
              <td className="right mono" style={{color:"var(--orange)"}}>{fmtFCFA(totals.d90)}</td>
              <td className="right mono" style={{color:"var(--pos-d)", fontSize:13}}>{fmtFCFA(totals.total)}</td>
            </tr>
          </tfoot>
        </table>
      </div>
      <div style={{padding:"14px 18px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:11.5, color:"var(--d-ink-3)", display:"flex", alignItems:"flex-start", gap:10}}>
        <span style={{color:"var(--blue-d)", fontWeight:700, fontSize:13}}>ⓘ</span>
        <span>
          Les fonds détenus pour compte de tiers doivent être <b style={{color:"var(--d-ink-2)"}}>reversés sous 30 jours</b> au-delà du mois d'encaissement. Une ancienneté &gt; 60 jours peut déclencher un contrôle de la <span className="mono">DGEPDS</span> · loi sénégalaise 2009-23.
        </span>
      </div>
    </div>
  );
}

/* Lettrage — match credits/debits, manuel + automatique selon règles */
function LettrageCard() {
  const [items, setItems] = React.useState([
    { id:"LET-001", b:"M. Abdoulaye Ba", debit:"Versement VR-2026-03-001", credit:"Encaissements mars · QT-2026-03-001", amount:1_840_000, ecart:0,     status:"à lettrer" },
    { id:"LET-002", b:"Horizon SCI", debit:"Versement VR-2026-03-003", credit:"Encaissements mars · QT-2026-03-003", amount:3_120_000, ecart:0,     status:"à lettrer" },
    { id:"LET-003", b:"Katos Consulting", debit:"Charges 08 mars · CHG-2026-03-004", credit:"Encaissements 05 mars", amount:65_000, ecart:12_000, status:"écart 12 000" },
    { id:"LET-004", b:"Mme Fatou Diop", debit:"Commission CG mars", credit:"Encaissements 05 mars", amount:97_750, ecart:0,     status:"à lettrer" },
  ]);
  const [rules, setRules] = React.useState([
    { name:"Loyer ↔ Versement", desc:"Encaissement (411) puis versement (467 → 521x) du même bailleur, même montant net.", on:true },
    { name:"Commission ↔ Encaissement", desc:"Commission (706) calculée automatiquement sur chaque encaissement et lettrée à 100%.", on:true },
    { name:"Charges ↔ Encaissement", desc:"Charges refacturées (708) imputées sur le compte bailleur dans le mois de la facture.", on:true },
    { name:"Tolérance d'écart", desc:"Lettrer si l'écart est inférieur à 1 000 FCFA · différence portée au 658 (charges diverses).", on:false },
  ]);
  const [flash, setFlash] = React.useState(null); // résultat du dernier lettrage auto

  const tolerance = rules[3].on ? 1000 : 0;
  const pending = items.filter(i => i.status !== "lettré");
  const lettered = items.filter(i => i.status === "lettré");

  function runAuto() {
    let matched = 0, skipped = 0;
    setItems(list => list.map(i => {
      if (i.status === "lettré") return i;
      // règle : lettre si écart nul, ou écart ≤ tolérance active
      if (i.ecart === 0 || (tolerance > 0 && i.ecart <= tolerance)) { matched++; return { ...i, status:"lettré" }; }
      skipped++; return i;
    }));
    setFlash({ matched, skipped });
  }
  const toggleRule = (idx) => setRules(rs => rs.map((r,i)=> i===idx ? {...r, on:!r.on} : r));

  return (
    <div className="grid-2">
      <div className="panel">
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Lettrage à effectuer</h3>
            <div className="panel-sub">Rapprochement débit ↔ crédit · {pending.length} en attente{lettered.length>0 ? ` · ${lettered.length} lettré${lettered.length>1?"s":""}` : ""}</div>
          </div>
          <button className="btn btn-primary" disabled={pending.length===0} style={{opacity: pending.length===0?0.5:1, cursor: pending.length===0?"not-allowed":"pointer"}} onClick={runAuto}>Lettrer automatiquement</button>
        </div>

        {flash && (
          <div style={{margin:"12px 18px 0", padding:"10px 14px", 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-2)"}}>
            <b style={{color:"var(--pos-d)"}}>{flash.matched} écriture{flash.matched>1?"s":""} lettrée{flash.matched>1?"s":""}</b> automatiquement.
            {flash.skipped > 0 && <> {flash.skipped} en écart au-delà de la tolérance — {rules[3].on ? "à revoir manuellement" : <>activez la <b>tolérance d'écart</b> pour les inclure</>}.</>}
          </div>
        )}

        <div style={{marginTop: flash ? 8 : 0}}>
          {items.map(p => {
            const done = p.status === "lettré";
            return (
              <div key={p.id} style={{padding:"14px 18px", borderBottom:"1px solid var(--d-line-2)", display:"grid", gridTemplateColumns:"auto 1fr auto auto", gap:14, alignItems:"center", opacity: done ? 0.72 : 1}}>
                <span className="mono muted" style={{fontSize:11}}>{p.id}</span>
                <div>
                  <div style={{fontSize:12.5, fontWeight:500, marginBottom:4}}>{p.b}</div>
                  <div style={{display:"flex", gap:8, fontSize:11, color:"var(--d-ink-3)", fontFamily:"var(--font-mono)"}}>
                    <span style={{color:"var(--orange)"}}>D · {p.debit}</span>
                    <span>↔</span>
                    <span style={{color:"var(--pos-d)"}}>C · {p.credit}</span>
                  </div>
                </div>
                <span className="mono" style={{fontSize:13, fontWeight:500}}>{fmtFCFA(p.amount)}</span>
                {done
                  ? <span className="chip pos">✓ lettré</span>
                  : <span className={`chip ${p.status === "à lettrer" ? "info" : "warn"}`}>{p.status}</span>}
              </div>
            );
          })}
        </div>
      </div>
      <div className="panel">
        <div className="panel-head">
          <h3 className="panel-title">Règles de lettrage</h3>
        </div>
        <div style={{padding:"14px 0"}}>
          {rules.map((r, i) => (
            <div key={i} style={{padding:"10px 18px", borderBottom:"1px solid var(--d-line-2)", display:"grid", gridTemplateColumns:"1fr 38px", gap:14, alignItems:"flex-start"}}>
              <div>
                <div style={{fontSize:12.5, fontWeight:500}}>{r.name}</div>
                <div className="muted" style={{fontSize:11.5, marginTop:3, lineHeight:1.5}}>{r.desc}</div>
              </div>
              <button onClick={()=>toggleRule(i)} title={r.on?"Désactiver":"Activer"} style={{
                width:32, height:18, borderRadius:9, padding:2, cursor:"pointer",
                background: r.on ? "var(--pos-d)" : "var(--d-elev)",
                border: r.on ? "none" : "1px solid var(--d-line)",
                display:"inline-flex", alignItems:"center",
                justifyContent: r.on ? "flex-end" : "flex-start", flexShrink:0,
              }}>
                <span style={{width:14, height:14, borderRadius:7, background:"#fff", display:"block"}}/>
              </button>
            </div>
          ))}
        </div>
        <div style={{padding:"12px 18px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", fontSize:11.5, color:"var(--d-ink-3)", lineHeight:1.55}}>
          <span style={{color:"var(--blue-d)", fontWeight:700}}>ⓘ</span> Le lettrage automatique applique les règles actives ci-dessus. Les écritures dont l'écart dépasse la tolérance restent en attente d'un lettrage manuel.
        </div>
      </div>
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════════════════
   2 · TROP-PERÇUS
   ═══════════════════════════════════════════════════════════════════════ */
function TropPercusPage({ onOpenBailleur }) {
  // Auto-derive cases where the bailleur 467 account has a debit balance.
  // For each such bailleur, sum the unrecovered charges from the shared
  // monthly source. Manual narrative cases (over-disbursement, provision)
  // are added on top — they wouldn't appear in the standard ledger.
  const autoCases = BAILLEURS
    .map(b => {
      const months = getBailleurHistory(b);
      const totalDebit = months.reduce((s,m) => s + (m.net < 0 ? -m.net : 0), 0);
      return { b, months, totalDebit };
    })
    .filter(x => x.totalDebit > 0)
    .map(x => ({
      key:"auto-"+x.b.id,
      b: x.b,
      cause:"Charges sans loyer",
      detail:`Villa Sacré-Cœur vacante depuis 6 mois · charges courantes refacturées (35 000 FCFA × ${x.months.filter(m=>m.charges>0 && m.collected===0).length})`,
      since: x.months.find(m => m.charges > 0 && m.collected === 0)?.month.label || "—",
      amount: x.totalDebit,
      action:"Imputer sur prochaine relocation",
      severity:"warn",
      derived: true,
    }));

  // Manual cases — DAF-entered adjustments that don't sit in the ledger.
  const manualCases = [
    {
      key:"man-0",
      b: BAILLEURS[5],
      cause:"Versement supérieur à l'encaissement",
      detail:"Versement de mars effectué avant régularisation du loyer impayé · trop-versé à recouvrer",
      since:"30 mars 2026",
      amount: 135_000,
      action:"Demander remboursement",
      severity:"neg",
    },
    {
      key:"man-1",
      b: BAILLEURS[3],
      cause:"Provision de charges non régularisée",
      detail:"Provision Ouakam supérieure aux dépenses réelles · à restituer en mai",
      since:"30 mars 2026",
      amount: 42_500,
      action:"Lettrer en mai",
      severity:"info",
    },
  ];
  const [extra, setExtra] = React.useState([]);
  const [resolved, setResolved] = React.useState([]);
  const [selected, setSelected] = React.useState([]);
  const [showNew, setShowNew] = React.useState(false);
  const [showBulk, setShowBulk] = React.useState(false);
  const [flash, setFlash] = React.useState(null);
  const allCases = [...autoCases, ...manualCases, ...extra];
  const cases = allCases.filter(c => !resolved.includes(c.key));
  const total = cases.reduce((s,c)=>s+c.amount, 0);
  const selCount = cases.filter(c => selected.includes(c.key)).length;

  function resolveCases(keys, methodLabel) {
    setResolved(r => [...new Set([...r, ...keys])]);
    setSelected(s => s.filter(k => !keys.includes(k)));
    setFlash({ n: keys.length, method: methodLabel });
  }
  function toggleSel(key) {
    setSelected(s => s.includes(key) ? s.filter(k=>k!==key) : [...s, key]);
  }

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Trop-perçus</h1>
          <div className="page-sub">Soldes débiteurs sur comptes 467x · à régulariser avant clôture</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter</button>
          <button className="btn btn-secondary" disabled={cases.length===0} style={{opacity:cases.length===0?0.5:1, cursor:cases.length===0?"not-allowed":"pointer"}} onClick={()=>{ if(selCount===0) setSelected(cases.map(c=>c.key)); setShowBulk(true); }}>Imputer en masse{selCount>0?` (${selCount})`:""}</button>
          <button className="btn btn-primary" onClick={()=>setShowNew(true)}>+ Nouvelle régularisation</button>
        </div>
      </div>

      <div className="kpis">
        <div className="kpi primary" style={{borderColor:"rgba(249,115,22,0.30)", background:"linear-gradient(180deg, rgba(249,115,22,0.06) 0%, var(--d-panel) 60%)"}}>
          <div className="kpi-label"><span>Total trop-perçu</span><span style={{color:"var(--orange)", fontWeight:600}}>À régulariser</span></div>
          <div className="kpi-value num" style={{color:"var(--orange)"}}>{fmtCompact(total)}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span>{cases.length} dossiers · cumul sur 6 mois</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Vacance locative</span></div>
          <div className="kpi-value num">{fmtCompact(cases.filter(c=>c.cause.includes("Charges")).reduce((s,c)=>s+c.amount,0))}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span>1 dossier · M. K. Fall</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Trop-versé</span></div>
          <div className="kpi-value num">{fmtCompact(cases.filter(c=>c.cause.includes("supérieur")).reduce((s,c)=>s+c.amount,0))}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span>1 dossier · remboursement demandé</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Provisions</span></div>
          <div className="kpi-value num">{fmtCompact(cases.filter(c=>c.cause.includes("Provision")).reduce((s,c)=>s+c.amount,0))}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span>1 dossier · à lettrer</span></div>
        </div>
      </div>

      <div className="panel">
        <div className="panel-head">
          <div>
            <h3 className="panel-title">Dossiers ouverts</h3>
            <div className="panel-sub">Triés par ancienneté · plus anciens en haut</div>
          </div>
        </div>
        <div>
          {flash && (
            <div style={{margin:"12px 20px 0", padding:"10px 14px", borderRadius:6, fontSize:11.5, border:"1px solid rgba(63,181,131,0.3)", background:"rgba(63,181,131,0.06)", color:"var(--d-ink-2)"}}>
              {flash.text
                ? <b style={{color:"var(--pos-d)"}}>{flash.text}</b>
                : <><b style={{color:"var(--pos-d)"}}>{flash.n} dossier{flash.n>1?"s":""} régularisé{flash.n>1?"s":""}</b>{flash.method?` · ${flash.method}`:""}.</>}
            </div>
          )}
          {cases.length === 0 && (
            <div style={{padding:"40px 24px", textAlign:"center", color:"var(--d-ink-3)", fontSize:12.5}}>✓ Aucun trop-perçu ouvert — tous les dossiers sont régularisés.</div>
          )}
          {cases.map((c, i) => {
            const checked = selected.includes(c.key);
            return (
            <div key={c.key} style={{padding:"18px 20px", borderBottom: i < cases.length-1 ? "1px solid var(--d-line-2)" : "none", display:"grid", gridTemplateColumns:"auto auto 1fr auto auto", gap:18, alignItems:"flex-start"}}>
              <span onClick={()=>toggleSel(c.key)} style={{marginTop:14, display:"inline-block", width:15, height:15, borderRadius:3, flexShrink:0, cursor:"pointer", position:"relative", border: checked?"1.5px solid var(--blue-d)":"1.5px solid var(--d-line)", background: checked?"var(--blue-d)":"transparent"}}>
                {checked && <span style={{position:"absolute", inset:0, color:"#fff", fontSize:10, lineHeight:"13px", textAlign:"center", fontWeight:700}}>✓</span>}
              </span>
              <div style={{display:"flex", alignItems:"center", gap:12}}>
                <div className="avatar" style={{background:c.b.color, color:"#fff"}}>{c.b.avatar}</div>
                <div>
                  <div style={{fontWeight:500, fontSize:13}}>{c.b.name}</div>
                  <div className="mono muted" style={{fontSize:11}}>{compteBailleur(c.b)}</div>
                </div>
              </div>
              <div style={{minWidth:0}}>
                <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:6}}>
                  <span className={`chip ${c.severity}`}>{c.cause}</span>
                  <span className="muted mono" style={{fontSize:11}}>depuis {c.since}</span>
                </div>
                <div style={{fontSize:12.5, color:"var(--d-ink-2)", lineHeight:1.5}}>{c.detail}</div>
              </div>
              <div style={{textAlign:"right"}}>
                <div style={{fontSize:10, color:"var(--d-ink-4)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, marginBottom:2}}>Solde débiteur</div>
                <div className="mono" style={{fontSize:18, fontWeight:600, color:"var(--orange)"}}>−{fmtFCFA(c.amount).replace("−","")}</div>
              </div>
              <div style={{display:"flex", flexDirection:"column", gap:6, alignItems:"flex-end"}}>
                <button className="btn btn-secondary" style={{padding:"5px 10px", fontSize:11.5, whiteSpace:"nowrap"}} onClick={()=>resolveCases([c.key], c.action)}>{c.action}</button>
                <button onClick={()=>onOpenBailleur(c.b)} style={{background:"none", border:"none", color:"var(--d-ink-3)", fontSize:11, cursor:"pointer", textDecoration:"underline", textUnderlineOffset:2}}>Fiche bailleur →</button>
              </div>
            </div>
          );})}
        </div>
      </div>

      <div className="grid-2" style={{marginTop:16}}>
        <div className="panel">
          <div className="panel-head"><h3 className="panel-title">Évolution des trop-perçus</h3><span className="muted" style={{fontSize:11}}>6 derniers mois</span></div>
          <div style={{padding:"22px 20px 14px"}}>
            {[
              { m:"Nov 25", v: 12_000, n:1 },
              { m:"Déc 25", v: 28_500, n:2 },
              { m:"Jan 26", v: 18_000, n:1 },
              { m:"Fév 26", v: 42_500, n:2 },
              { m:"Mars 26", v:177_500, n:3 },
              { m:"Avr 26", v: 247_500, n:3 },
            ].map((r, i) => {
              const max = 260_000;
              const pct = (r.v / max) * 100;
              return (
                <div key={i} style={{display:"grid", gridTemplateColumns:"60px 1fr auto", gap:14, alignItems:"center", marginBottom:9, fontSize:11.5}}>
                  <span className="muted mono">{r.m}</span>
                  <div style={{height:8, background:"var(--d-sunken)", borderRadius:2, overflow:"hidden", position:"relative"}}>
                    <div style={{height:"100%", width:`${pct}%`, background: i >= 4 ? "var(--orange)" : "var(--warn-d)", borderRadius:2}}/>
                  </div>
                  <span className="mono" style={{minWidth:70, textAlign:"right", color: i >= 4 ? "var(--orange)" : "var(--d-ink-2)"}}>{fmtCompact(r.v)}</span>
                </div>
              );
            })}
            <div style={{fontSize:11, color:"var(--d-ink-3)", marginTop:14, padding:"10px 12px", background:"var(--d-sunken)", borderRadius:5, borderLeft:"2px solid var(--orange)"}}>
              <b style={{color:"var(--d-ink-2)"}}>Tendance à surveiller</b> · l'encours a quadruplé en 2 mois. Le pic de mars résulte de la vacance Sacré-Cœur (60 j) — à régulariser dès relocation.
            </div>
          </div>
        </div>

        <div className="panel">
          <div className="panel-head"><h3 className="panel-title">Méthodes de régularisation</h3></div>
          <div style={{padding:"8px 0"}}>
            {[
              { name:"Imputation sur versement suivant", desc:"Le trop-perçu est déduit du prochain versement net du bailleur · solution la plus rapide.", count:1 },
              { name:"Demande de remboursement", desc:"Émission d'un avis avec relevé · échéance 15 jours · suivi via WhatsApp & email.", count:1 },
              { name:"Compensation entre comptes", desc:"Si le bailleur a plusieurs comptes (sociétés liées), compensation possible avec accord écrit.", count:0 },
              { name:"Passation en perte", desc:"Au-delà de 12 mois sans recouvrement · charge exceptionnelle 658.", count:0 },
            ].map((r, i) => (
              <div key={i} style={{padding:"12px 18px", borderBottom: i<3 ? "1px solid var(--d-line-2)" : "none", display:"grid", gridTemplateColumns:"1fr auto", gap:14}}>
                <div>
                  <div style={{fontSize:12.5, fontWeight:500}}>{r.name}</div>
                  <div className="muted" style={{fontSize:11.5, marginTop:3, lineHeight:1.5}}>{r.desc}</div>
                </div>
                <span style={{
                  alignSelf:"flex-start", fontSize:10.5, fontWeight:600,
                  padding:"2px 8px", borderRadius:3, letterSpacing:"0.02em",
                  background: r.count ? "var(--blue-wash)" : "var(--d-elev)",
                  color: r.count ? "var(--blue-2)" : "var(--d-ink-4)",
                }}>{r.count > 0 ? `${r.count} dossier${r.count > 1 ? "s" : ""}` : "—"}</span>
              </div>
            ))}
          </div>
        </div>
      </div>

      {showNew && <TropNewModal onClose={()=>setShowNew(false)} onSave={(c)=>{ setExtra(e=>[...e, c]); setShowNew(false); setFlash({text:`Nouvelle régularisation ajoutée · ${c.b.name}`}); }}/>}
      {showBulk && <TropBulkModal count={selCount || cases.length} total={cases.filter(c=>selCount?selected.includes(c.key):true).reduce((s,c)=>s+c.amount,0)} onClose={()=>setShowBulk(false)} onConfirm={(method)=>{ const keys = selCount ? cases.filter(c=>selected.includes(c.key)).map(c=>c.key) : cases.map(c=>c.key); resolveCases(keys, method); setShowBulk(false); }}/>}
    </div>
  );
}

/* Nouvelle régularisation — saisie manuelle d'un trop-perçu */
function TropNewModal({ onClose, onSave }) {
  const [bailleurId, setBailleurId] = React.useState("");
  const [cause, setCause] = React.useState("Versement supérieur à l'encaissement");
  const [amount, setAmount] = React.useState("");
  const [detail, setDetail] = React.useState("");
  const [action, setAction] = React.useState("Imputer sur versement suivant");
  const b = BAILLEURS.find(x => x.id === bailleurId);
  const amt = Number(String(amount).replace(/[^0-9]/g,"")) || 0;
  const sevByCause = { "Versement supérieur à l'encaissement":"neg", "Charges sans loyer":"warn", "Provision de charges non régularisée":"info", "Autre":"info" };
  const valid = bailleurId && amt > 0;
  React.useEffect(() => { const k=e=>{if(e.key==="Escape")onClose();}; window.addEventListener("keydown",k); return ()=>window.removeEventListener("keydown",k); }, [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:"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}}>Nouvelle régularisation</div>
            <div style={{fontSize:15, fontWeight:600}}>{b ? b.name : "Trop-perçu manuel"}</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"}}>
          <FieldRow>
            <Field label="Bailleur" required>
              <Select value={bailleurId} onChange={setBailleurId} placeholder="— Choisir —" options={BAILLEURS.map(x=>({value:x.id, label:`${x.name} · ${x.id}`}))}/>
            </Field>
            <Field label="Montant" required><MoneyInput value={amount} onChange={e=>setAmount(e.target.value.replace(/[^0-9]/g,""))}/></Field>
          </FieldRow>
          <Field label="Cause">
            <Select value={cause} onChange={setCause} options={["Versement supérieur à l'encaissement","Charges sans loyer","Provision de charges non régularisée","Autre"]}/>
          </Field>
          <Field label="Méthode de régularisation">
            <Select value={action} onChange={setAction} options={["Imputer sur versement suivant","Demander remboursement","Compensation entre comptes","Lettrer en mai"]}/>
          </Field>
          <Field label="Détail" optional><Textarea rows={2} value={detail} onChange={e=>setDetail(e.target.value)} placeholder="Contexte du trop-perçu…"/></Field>
        </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={{opacity:valid?1:0.5, cursor:valid?"pointer":"not-allowed"}}
            onClick={()=>onSave({ key:"extra-"+Date.now(), b, cause, amount:amt, detail: detail||"Régularisation saisie manuellement", since:"29 mai 2026", action, severity: sevByCause[cause]||"info" })}>Enregistrer</button>
        </footer>
      </div>
    </div>
  );
}

/* Imputer en masse — régularise plusieurs trop-perçus d'un coup */
function TropBulkModal({ count, total, onClose, onConfirm }) {
  const [method, setMethod] = React.useState("Imputer sur versement suivant");
  React.useEffect(() => { const k=e=>{if(e.key==="Escape")onClose();}; window.addEventListener("keydown",k); return ()=>window.removeEventListener("keydown",k); }, [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:"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}}>Imputer en masse</div>
            <div style={{fontSize:15, fontWeight:600}}>{count} dossier{count>1?"s":""} · {fmtFCFA(total)}</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"}}>
          <Field label="Méthode appliquée à tous les dossiers">
            <Select value={method} onChange={setMethod} options={["Imputer sur versement suivant","Demander remboursement","Compensation entre comptes","Passation en perte (658)"]}/>
          </Field>
          <div style={{marginTop:4, padding:"10px 12px", borderRadius:6, fontSize:11.5, lineHeight:1.5, border:"1px solid var(--d-line)", background:"var(--d-sunken)", color:"var(--d-ink-3)"}}>
            Les {count} trop-perçus sélectionnés seront régularisés via <b style={{color:"var(--d-ink-2)"}}>{method.toLowerCase()}</b> et retirés des dossiers ouverts. L'écriture comptable correspondante est générée pour chaque compte 467x.
          </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" onClick={()=>onConfirm(method)}>Régulariser {count} dossier{count>1?"s":""}</button>
        </footer>
      </div>
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════════════════
   3 · RÉCÉPISSÉS BAILLEUR
   ═══════════════════════════════════════════════════════════════════════ */
function RecepissesPage({ onOpenBailleur }) {
  // Build récépissés from the SHARED source — one per (bailleur, month) where
  // there was a payable disbursement. Latest month is "à émettre".
  const all = [];
  BAILLEURS.forEach(b => {
    BAILLEUR_MONTHS.forEach((mo, idx) => {
      const r = getBailleurMonthly(b, idx);
      if (r.net <= 0) return;  // skip months with no payable amount
      const isCurrent = idx === BAILLEUR_MONTHS.length - 1;
      const isOldest = idx <= 2;
      all.push({
        id: `REC-${mo.code}-${b.id.replace("B-","")}`,
        b, mo: { code: mo.code, l: mo.label },
        collected: r.collected, commission: r.commission, charges: r.charges, net: r.net,
        status: isCurrent ? "à émettre" : isOldest ? "signé" : (idx === 4 ? "émis" : "signé"),
        date: isCurrent ? "—" : mo.paidOn,
      });
    });
  });
  // Order: pending first, then most recent
  all.sort((a, b) => {
    if (a.status === "à émettre" && b.status !== "à émettre") return -1;
    if (b.status === "à émettre" && a.status !== "à émettre") return 1;
    return b.mo.code.localeCompare(a.mo.code) || a.b.id.localeCompare(b.b.id);
  });
  const aprilPending = all.filter(r => r.status === "à émettre");

  const [tab, setTab] = React.useState("a-emettre");
  const [openId, setOpenId] = React.useState(aprilPending[0]?.id || all[0]?.id);

  const filtered = all.filter(r => {
    if (tab === "a-emettre") return r.status === "à émettre";
    if (tab === "emis") return r.status === "émis";
    if (tab === "signe") return r.status === "signé";
    return true;
  });
  const cnt = (s) => all.filter(r => r.status === s).length;
  const opened = all.find(r => r.id === openId) || all[0];

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Récépissés bailleurs</h1>
          <div className="page-sub">Attestations de versement nominatives · pièces comptables horodatées</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Téléchargement groupé</button>
          <button className="btn btn-secondary">Modèle de récépissé</button>
          <button className="btn btn-primary">+ Émettre {aprilPending.length} récépissés</button>
        </div>
      </div>

      <div className="tabs">
        {[
          ["tous","Tous", all.length],
          ["a-emettre", "À émettre", aprilPending.length],
          ["emis", "Émis", cnt("émis")],
          ["signe", "Signés", cnt("signé")],
        ].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-detail">
        {/* Left — list */}
        <div className="panel" style={{alignSelf:"start"}}>
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Récépissés · {tab === "a-emettre" ? "à émettre" : tab === "emis" ? "émis" : tab === "signe" ? "signés" : "tous"}</h3>
              <div className="panel-sub">{filtered.length} document{filtered.length > 1 ? "s" : ""}</div>
            </div>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:160}}>Référence</th>
                  <th>Bailleur</th>
                  <th>Mois</th>
                  <th className="right">Montant net</th>
                  <th style={{width:90}}>Statut</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(r => (
                  <tr key={r.id} onClick={()=>setOpenId(r.id)} style={{background: r.id === openId ? "rgba(97,146,255,0.05)" : undefined}}>
                    <td className="mono" style={{fontSize:11, color: r.id === openId ? "var(--blue-d)" : "var(--d-ink-3)"}}>{r.id}</td>
                    <td>
                      <div style={{display:"flex", alignItems:"center", gap:10}}>
                        <div className="avatar" style={{background:r.b.color, color:"#fff", fontSize:10}}>{r.b.avatar}</div>
                        <span style={{fontWeight:500}}>{r.b.name}</span>
                      </div>
                    </td>
                    <td className="mono">{r.mo.l}</td>
                    <td className="right" style={{color: r.status === "à émettre" ? "var(--d-ink-3)" : "var(--pos-d)", fontWeight:500}}>{fmtFCFA(r.net)}</td>
                    <td>
                      <span className={`chip ${r.status === "à émettre" ? "warn" : r.status === "signé" ? "pos" : "info"}`}>{r.status}</span>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>

        {/* Right — preview */}
        <div className="stack">
          {opened && <RecepissePreview r={opened} onOpenBailleur={onOpenBailleur}/>}
        </div>
      </div>
    </div>
  );
}

function RecepissePreview({ r, onOpenBailleur }) {
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Aperçu du récépissé</h3>
          <div className="panel-sub mono" style={{fontSize:11}}>{r.id}</div>
        </div>
        <div className="panel-head-right">
          <span className={`chip ${r.status === "à émettre" ? "warn" : r.status === "signé" ? "pos" : "info"}`}>{r.status}</span>
        </div>
      </div>
      {/* Document mock — light surface on dark panel */}
      <div style={{padding:24, background:"linear-gradient(180deg, #FAFBFD 0%, #F2F4F7 100%)", color:"#0B1220", margin:18, border:"1px solid #E4E7EC", borderRadius:6, fontFamily:"var(--font-sans)", fontSize:12}}>
        <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", paddingBottom:14, borderBottom:"1px solid #E4E7EC", marginBottom:18}}>
          <div>
            <div style={{fontSize:9, fontWeight:600, letterSpacing:"0.18em", textTransform:"uppercase", color:"#5B6577", marginBottom:6}}>BA IMMO · Agence de gestion locative</div>
            <div style={{fontSize:16, fontWeight:500, letterSpacing:"-0.01em"}}>Récépissé de versement</div>
            <div style={{fontSize:11, color:"#5B6577", marginTop:2}}>Document n° <span style={{fontFamily:"var(--font-mono)"}}>{r.id}</span></div>
          </div>
          <div style={{textAlign:"right"}}>
            <div style={{fontSize:9, fontWeight:600, letterSpacing:"0.1em", textTransform:"uppercase", color:"#5B6577"}}>Date d'émission</div>
            <div style={{fontSize:13, fontWeight:500, fontFamily:"var(--font-mono)", marginTop:2}}>{r.status === "à émettre" ? "—" : r.date}</div>
            <div style={{fontSize:10, color:"#8E97A6", marginTop:2}}>Période · {r.mo.l}</div>
          </div>
        </div>

        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:18, marginBottom:18}}>
          <div>
            <div style={{fontSize:9, fontWeight:600, letterSpacing:"0.1em", textTransform:"uppercase", color:"#5B6577", marginBottom:4}}>Bénéficiaire</div>
            <div style={{fontWeight:500}}>{r.b.name}</div>
            <div style={{fontSize:11, color:"#5B6577"}}>{r.b.entity}</div>
            <div style={{fontSize:10, color:"#8E97A6", fontFamily:"var(--font-mono)", marginTop:2}}>{r.b.bank}</div>
          </div>
          <div>
            <div style={{fontSize:9, fontWeight:600, letterSpacing:"0.1em", textTransform:"uppercase", color:"#5B6577", marginBottom:4}}>Émetteur</div>
            <div style={{fontWeight:500}}>BA Immo SARL</div>
            <div style={{fontSize:11, color:"#5B6577"}}>NINEA · 005 442 8 K3</div>
            <div style={{fontSize:10, color:"#8E97A6"}}>RC SN DKR 2018 B 9214</div>
          </div>
        </div>

        <table style={{width:"100%", borderCollapse:"collapse", fontSize:11.5}}>
          <thead>
            <tr style={{background:"#EEF0F3"}}>
              <th style={{textAlign:"left", padding:"8px 10px", fontSize:9, fontWeight:600, letterSpacing:"0.06em", color:"#5B6577", textTransform:"uppercase"}}>Libellé</th>
              <th style={{textAlign:"right", padding:"8px 10px", fontSize:9, fontWeight:600, letterSpacing:"0.06em", color:"#5B6577", textTransform:"uppercase"}}>FCFA</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td style={{padding:"8px 10px", borderBottom:"1px solid #EEF0F3"}}>Loyers encaissés · {r.mo.l}</td>
              <td style={{padding:"8px 10px", borderBottom:"1px solid #EEF0F3", textAlign:"right", fontFamily:"var(--font-mono)"}}>{r.collected.toLocaleString("fr-FR")}</td>
            </tr>
            <tr>
              <td style={{padding:"8px 10px", borderBottom:"1px solid #EEF0F3", color:"#5B6577"}}>Commission de gestion · {(r.b.commissionRate*100).toFixed(1).replace(".",",")}%</td>
              <td style={{padding:"8px 10px", borderBottom:"1px solid #EEF0F3", textAlign:"right", fontFamily:"var(--font-mono)", color:"#5B6577"}}>−{r.commission.toLocaleString("fr-FR")}</td>
            </tr>
            {r.charges > 0 && (
              <tr>
                <td style={{padding:"8px 10px", borderBottom:"1px solid #EEF0F3", color:"#5B6577"}}>Charges courantes refacturées</td>
                <td style={{padding:"8px 10px", borderBottom:"1px solid #EEF0F3", textAlign:"right", fontFamily:"var(--font-mono)", color:"#5B6577"}}>−{r.charges.toLocaleString("fr-FR")}</td>
              </tr>
            )}
            <tr style={{background:"#F2F4F7"}}>
              <td style={{padding:"10px 10px", fontWeight:600}}>NET VERSÉ AU BAILLEUR</td>
              <td style={{padding:"10px 10px", textAlign:"right", fontFamily:"var(--font-mono)", fontWeight:600, fontSize:14, color:"#0F7A4C"}}>{r.net.toLocaleString("fr-FR")}</td>
            </tr>
          </tbody>
        </table>

        <div style={{marginTop:18, fontSize:10.5, color:"#5B6577", lineHeight:1.6}}>
          Le bailleur reconnaît avoir reçu de BA Immo SARL la somme de <b style={{color:"#0B1220"}}>{r.net.toLocaleString("fr-FR")} FCFA</b> au titre des loyers nets de la période <b style={{color:"#0B1220"}}>{r.mo.l}</b>. Le détail des encaissements, commissions et charges figure en annexe (compte 467 · grand livre).
        </div>

        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:30, marginTop:24, paddingTop:14, borderTop:"1px dashed #E4E7EC"}}>
          <div>
            <div style={{fontSize:9, fontWeight:600, letterSpacing:"0.1em", textTransform:"uppercase", color:"#5B6577", marginBottom:6}}>Pour BA Immo SARL</div>
            <div style={{height:42, fontFamily:"'Caveat',cursive", fontSize:22, color:"#1E4FCC", fontStyle:"italic", fontWeight:500}}>{r.status !== "à émettre" ? "K. Diop" : ""}</div>
            <div style={{fontSize:11, color:"#5B6577", borderTop:"1px solid #E4E7EC", paddingTop:4}}>K. Diop · DAF</div>
          </div>
          <div>
            <div style={{fontSize:9, fontWeight:600, letterSpacing:"0.1em", textTransform:"uppercase", color:"#5B6577", marginBottom:6}}>Bon pour acquit du bailleur</div>
            <div style={{height:42, fontFamily:"'Caveat',cursive", fontSize:22, color:"#1E4FCC", fontStyle:"italic", fontWeight:500}}>{r.status === "signé" ? "Bon pour accord" : ""}</div>
            <div style={{fontSize:11, color:"#5B6577", borderTop:"1px solid #E4E7EC", paddingTop:4}}>{r.b.name}</div>
          </div>
        </div>
      </div>

      <div style={{padding:"14px 18px", borderTop:"1px solid var(--d-line)", display:"flex", gap:8, justifyContent:"space-between", alignItems:"center", background:"var(--d-sunken)"}}>
        <button onClick={()=>onOpenBailleur(r.b)} className="btn btn-ghost">Fiche bailleur →</button>
        <div style={{display:"flex", gap:8}}>
          <button className="btn btn-ghost">PDF</button>
          <button className="btn btn-secondary">Envoyer par email</button>
          {r.status === "à émettre" && <button className="btn btn-primary">Émettre · signer</button>}
        </div>
      </div>
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════════════════
   4 · COMPTABILITÉ AGENCE
   ═══════════════════════════════════════════════════════════════════════ */
function ComptaAgencePage() {
  const [tab, setTab] = React.useState("resultat");
  const [closed, setClosed] = React.useState(false);
  const [showClose, setShowClose] = React.useState(false);
  const [compareN1, setCompareN1] = React.useState(false);

  // Income (compte de résultat) — agency-side only (excludes funds held in trust)
  const produits = [
    { cpt:"706", lib:"Commissions de gestion locative", nov:780_000, dec:790_000, jan:810_000, fev:820_000, mar:880_000, avr:912_000 },
    { cpt:"707", lib:"Honoraires de relocation",        nov:0,       dec:280_000, jan:0,       fev:150_000, mar:0,       avr:320_000 },
    { cpt:"708", lib:"Refacturation charges & services",nov:65_000,  dec:65_000,  jan:65_000,  fev:65_000,  mar:65_000,  avr:65_000  },
  ];
  const charges = [
    { cpt:"601", lib:"Achats fournitures bureau",       nov:24_000, dec:31_000, jan:18_000, fev:22_000, mar:28_000, avr:25_000 },
    { cpt:"622", lib:"Locations & charges locatives",   nov:240_000, dec:240_000, jan:240_000, fev:240_000, mar:240_000, avr:240_000 },
    { cpt:"623", lib:"Publicité & annonces",            nov:45_000, dec:70_000, jan:35_000, fev:55_000, mar:80_000, avr:120_000 },
    { cpt:"626", lib:"Téléphonie · internet",           nov:48_000, dec:48_000, jan:52_000, fev:52_000, mar:52_000, avr:52_000 },
    { cpt:"628", lib:"Honoraires comptable",            nov:75_000, dec:75_000, jan:75_000, fev:75_000, mar:75_000, avr:75_000 },
    { cpt:"641", lib:"Salaires bruts",                  nov:340_000, dec:340_000, jan:350_000, fev:350_000, mar:350_000, avr:350_000 },
    { cpt:"645", lib:"Charges sociales",                nov:82_000, dec:82_000, jan:84_000, fev:84_000, mar:84_000, avr:84_000 },
    { cpt:"651", lib:"Frais bancaires",                 nov:12_500, dec:14_200, jan:11_800, fev:13_400, mar:15_600, avr:18_200 },
  ];
  const cols = ["nov","dec","jan","fev","mar","avr"];
  const colLabels = ["Nov 25","Déc 25","Jan 26","Fév 26","Mars 26","Avr 26"];
  const sum = arr => cols.reduce((acc, c) => ({ ...acc, [c]: arr.reduce((s, r) => s + r[c], 0) }), {});
  const totalP = sum(produits);
  const totalC = sum(charges);
  const totalR = cols.reduce((acc, c) => ({ ...acc, [c]: totalP[c] - totalC[c] }), {});
  const trim = cols.reduce((s, c) => s + totalR[c], 0);
  // Comparatif N-1 (avril 2025, mock ≈ -14% produits / -10% charges)
  const n1 = { produits: Math.round(totalP.avr*0.86), charges: Math.round(totalC.avr*0.90) };
  n1.resultat = n1.produits - n1.charges;
  const deltaPct = (cur, prev) => prev ? Math.round(((cur-prev)/prev)*100) : 0;

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Comptabilité agence</h1>
          <div className="page-sub">Compte d'exploitation BA Immo SARL · hors fonds détenus pour compte de tiers</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter PDF</button>
          <button className={"btn " + (compareN1 ? "btn-primary" : "btn-secondary")} onClick={()=>setCompareN1(v=>!v)}>Comparer · N-1</button>
          <button className="btn btn-primary" disabled={closed} style={{opacity:closed?0.5:1, cursor:closed?"not-allowed":"pointer"}} onClick={()=>setShowClose(true)}>{closed ? "Avril clôturé" : "Clôturer avril"}</button>
        </div>
      </div>

      {closed && (
        <div style={{display:"flex", alignItems:"center", gap:10, padding:"10px 16px", marginBottom:16, borderRadius:8, border:"1px solid rgba(63,181,131,0.3)", background:"rgba(63,181,131,0.06)", fontSize:12.5, color:"var(--d-ink-2)"}}>
          <span style={{flexShrink:0}}>🔒</span>
          <span><b>Exercice avril 2026 clôturé.</b> Le compte de résultat est figé · report à nouveau généré sur mai.</span>
          <button className="btn btn-ghost" style={{marginLeft:"auto", padding:"4px 10px", fontSize:11.5}} onClick={()=>setClosed(false)}>Rouvrir</button>
        </div>
      )}

      {compareN1 && (
        <div className="panel" style={{marginBottom:16}}>
          <div className="panel-head"><div><h3 className="panel-title">Comparatif avril 2026 vs avril 2025</h3><div className="panel-sub">Évolution sur 12 mois glissants</div></div></div>
          <div style={{display:"grid", gridTemplateColumns:"repeat(3,1fr)", gap:0}}>
            {[
              ["Produits", totalP.avr, n1.produits],
              ["Charges", totalC.avr, n1.charges],
              ["Résultat net", totalR.avr, n1.resultat],
            ].map(([lib, cur, prev], i) => {
              const d = deltaPct(cur, prev);
              const good = lib === "Charges" ? d < 0 : d > 0;
              return (
                <div key={i} style={{padding:"16px 18px", borderRight: i<2?"1px solid var(--d-line-2)":"none"}}>
                  <div style={{fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:6}}>{lib}</div>
                  <div className="mono" style={{fontSize:17, fontWeight:600}}>{fmtFCFA(cur)}</div>
                  <div style={{display:"flex", alignItems:"center", gap:8, marginTop:4, fontSize:11.5}}>
                    <span className={"delta " + (good ? "pos" : "neg")}>{d>0?"▲":"▼"} {Math.abs(d)}%</span>
                    <span className="muted mono">N-1 : {fmtCompact(prev)}</span>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {showClose && (
        <div onClick={()=>setShowClose(false)} 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:"10vh 16px"}}>
          <div onClick={e=>e.stopPropagation()} role="dialog" style={{width:"min(460px, 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)", background:"var(--d-panel)"}}>
              <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>Clôture d'exercice</div>
              <div style={{fontSize:16, fontWeight:600}}>Avril 2026 · résultat {fmtFCFA(totalR.avr)}</div>
            </header>
            <div style={{padding:"18px 22px", fontSize:12.5, color:"var(--d-ink-2)", lineHeight:1.55}}>
              La clôture fige le compte de résultat d'avril (produits {fmtCompact(totalP.avr)} · charges {fmtCompact(totalC.avr)}) et génère le <b>report à nouveau</b> sur mai. Les écritures de la période ne seront plus modifiables.
            </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={()=>setShowClose(false)}>Annuler</button>
              <button className="btn btn-primary" onClick={()=>{ setClosed(true); setShowClose(false); }}>Clôturer avril</button>
            </footer>
          </div>
        </div>
      )}

      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Résultat · avril</span></div>
          <div className="kpi-value num" style={{color:"var(--pos-d)"}}>{fmtCompact(totalR.avr)}<span className="kpi-unit" style={{color:"var(--d-ink-3)"}}>FCFA</span></div>
          <div className="kpi-meta"><span className="delta pos">▲ {Math.round(((totalR.avr-totalR.mar)/totalR.mar)*100)}%</span><span>vs mars · marge {Math.round((totalR.avr/totalP.avr)*100)}%</span></div>
          <div className="kpi-spark"><Spark values={cols.map(c=>totalR[c])} color="#3FB583"/></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Produits · avril</span></div>
          <div className="kpi-value num">{fmtCompact(totalP.avr)}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span>{produits.length} comptes · 706, 707, 708</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Charges · avril</span></div>
          <div className="kpi-value num">{fmtCompact(totalC.avr)}<span className="kpi-unit">FCFA</span></div>
          <div className="kpi-meta"><span className="delta neg">▲ 4%</span><span>vs mars · {charges.length} comptes</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Résultat 6 mois</span></div>
          <div className="kpi-value num">{(trim/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}}>+{Math.round((trim/cols.reduce((s,c)=>s+totalP[c],0))*100)}%</span><span>marge nette cumulée</span></div>
        </div>
      </div>

      <div className="tabs">
        {[
          ["resultat", "Compte de résultat"],
          ["balance", "Balance générale"],
          ["tresorerie", "Flux trésorerie"],
          ["ratios", "Ratios"],
        ].map(([k,l])=>(
          <button key={k} className={"tab" + (tab===k?" active":"")} onClick={()=>setTab(k)}>{l}</button>
        ))}
      </div>

      {tab === "resultat" && (
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3 className="panel-title">Compte de résultat · 6 mois</h3>
              <div className="panel-sub">SYSCOHADA · système normal · arrondi FCFA</div>
            </div>
            <div className="panel-head-right">
              <div className="seg">
                <button className="seg-btn active">Mensuel</button>
                <button className="seg-btn">Trimestriel</button>
                <button className="seg-btn">YTD</button>
              </div>
            </div>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{width:60}}>Cpt</th>
                  <th>Libellé</th>
                  {colLabels.map((l, i) => (
                    <th key={i} className="right" style={{width:90}}>{l}</th>
                  ))}
                  <th className="right" style={{width:110, color:"var(--blue-d)"}}>Total 6M</th>
                </tr>
              </thead>
              <tbody>
                {/* Produits */}
                <tr style={{background:"var(--d-sunken)"}}>
                  <td colSpan={cols.length + 3} style={{padding:"10px var(--cell-px)", fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.1em", color:"var(--pos-d)", fontWeight:700}}>Classe 7 · Produits</td>
                </tr>
                {produits.map(r => (
                  <tr key={r.cpt}>
                    <td className="mono" style={{color:"var(--pos-d)", fontSize:11.5}}>{r.cpt}</td>
                    <td>{r.lib}</td>
                    {cols.map(c => (
                      <td key={c} className="right mono" style={{fontSize:12, color: r[c] === 0 ? "var(--d-ink-4)" : undefined}}>{r[c] === 0 ? "—" : r[c].toLocaleString("fr-FR")}</td>
                    ))}
                    <td className="right mono" style={{color:"var(--pos-d)"}}>{cols.reduce((s,c)=>s+r[c],0).toLocaleString("fr-FR")}</td>
                  </tr>
                ))}
                <tr style={{background:"rgba(63,181,131,0.05)", fontWeight:600}}>
                  <td></td>
                  <td style={{padding:"10px var(--cell-px)"}}>Total Produits</td>
                  {cols.map(c => <td key={c} className="right mono" style={{fontSize:12, color:"var(--pos-d)"}}>{totalP[c].toLocaleString("fr-FR")}</td>)}
                  <td className="right mono" style={{color:"var(--pos-d)", fontSize:13}}>{cols.reduce((s,c)=>s+totalP[c],0).toLocaleString("fr-FR")}</td>
                </tr>
                {/* Charges */}
                <tr style={{background:"var(--d-sunken)"}}>
                  <td colSpan={cols.length + 3} style={{padding:"10px var(--cell-px)", fontSize:10.5, textTransform:"uppercase", letterSpacing:"0.1em", color:"var(--orange)", fontWeight:700}}>Classe 6 · Charges</td>
                </tr>
                {charges.map(r => (
                  <tr key={r.cpt}>
                    <td className="mono" style={{color:"var(--orange)", fontSize:11.5}}>{r.cpt}</td>
                    <td>{r.lib}</td>
                    {cols.map(c => (
                      <td key={c} className="right mono" style={{fontSize:12, color:"var(--d-ink-2)"}}>−{r[c].toLocaleString("fr-FR")}</td>
                    ))}
                    <td className="right mono" style={{color:"var(--d-ink-2)"}}>−{cols.reduce((s,c)=>s+r[c],0).toLocaleString("fr-FR")}</td>
                  </tr>
                ))}
                <tr style={{background:"rgba(249,115,22,0.05)", fontWeight:600}}>
                  <td></td>
                  <td style={{padding:"10px var(--cell-px)"}}>Total Charges</td>
                  {cols.map(c => <td key={c} className="right mono" style={{fontSize:12, color:"var(--orange)"}}>−{totalC[c].toLocaleString("fr-FR")}</td>)}
                  <td className="right mono" style={{color:"var(--orange)", fontSize:13}}>−{cols.reduce((s,c)=>s+totalC[c],0).toLocaleString("fr-FR")}</td>
                </tr>
                {/* Résultat */}
                <tr style={{background:"var(--d-elev)", fontWeight:700, borderTop:"2px solid var(--d-line)"}}>
                  <td></td>
                  <td style={{padding:"14px var(--cell-px)", fontSize:13}}>RÉSULTAT NET</td>
                  {cols.map(c => (
                    <td key={c} className="right mono" style={{fontSize:13, color: totalR[c] > 0 ? "var(--pos-d)" : "var(--orange)", padding:"14px var(--cell-px)"}}>
                      {totalR[c] > 0 ? "+" : "−"}{Math.abs(totalR[c]).toLocaleString("fr-FR")}
                    </td>
                  ))}
                  <td className="right mono" style={{fontSize:14, color:"var(--pos-d)", padding:"14px var(--cell-px)"}}>+{trim.toLocaleString("fr-FR")}</td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      )}

      {tab === "balance" && <BalanceGeneraleCard/>}
      {tab === "tresorerie" && <FluxTresorerieCard cols={cols} colLabels={colLabels} totalP={totalP} totalC={totalC} totalR={totalR}/>}
      {tab === "ratios" && <RatiosCard totalP={totalP} totalC={totalC} totalR={totalR} cols={cols}/>}
    </div>
  );
}

function BalanceGeneraleCard() {
  const rows = [
    { cls:"1 · Capitaux propres", debit:0,        credit:8_200_000 },
    { cls:"2 · Immobilisations",  debit:2_140_000, credit:0 },
    { cls:"4 · Tiers",             debit:380_000,  credit:10_640_000 },
    { cls:"5 · Trésorerie",        debit:26_350_000,credit:0 },
    { cls:"6 · Charges (cumul)",   debit:5_886_000, credit:0 },
    { cls:"7 · Produits (cumul)",  debit:0,        credit:15_916_000 },
  ];
  const td = rows.reduce((s,r)=>s+r.debit, 0);
  const tc = rows.reduce((s,r)=>s+r.credit, 0);
  return (
    <div className="panel">
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Balance générale au 30 avril 2026</h3>
          <div className="panel-sub">Synthèse par classe de comptes · règle des partie-doubles</div>
        </div>
      </div>
      <div className="table-wrap">
        <table className="tbl">
          <thead>
            <tr><th>Classe</th><th className="right">Débit</th><th className="right">Crédit</th><th className="right">Solde</th></tr>
          </thead>
          <tbody>
            {rows.map((r, i) => (
              <tr key={i}>
                <td style={{fontWeight:500}}>{r.cls}</td>
                <td className="right mono">{r.debit > 0 ? fmtFCFA(r.debit) : <span className="empty-dash">—</span>}</td>
                <td className="right mono" style={{color: r.credit > 0 ? "var(--pos-d)" : undefined}}>{r.credit > 0 ? fmtFCFA(r.credit) : <span className="empty-dash">—</span>}</td>
                <td className="right mono" style={{color: r.debit - r.credit > 0 ? "var(--d-ink)" : "var(--pos-d)", fontWeight:500}}>
                  {r.debit - r.credit > 0 ? fmtFCFA(r.debit - r.credit) : "+"+fmtFCFA(r.credit - r.debit).replace("−","")}
                </td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr style={{background:"var(--d-sunken)", fontWeight:600}}>
              <td style={{padding:"12px var(--cell-px)"}}>TOTAL · équilibre {td === tc ? "✓" : "✗"}</td>
              <td className="right mono" style={{fontSize:13}}>{fmtFCFA(td)}</td>
              <td className="right mono" style={{color:"var(--pos-d)", fontSize:13}}>{fmtFCFA(tc)}</td>
              <td className="right mono" style={{color: td === tc ? "var(--pos-d)" : "var(--orange)"}}>{td === tc ? "ÉQUILIBRÉE" : fmtFCFA(td - tc)}</td>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
  );
}

function FluxTresorerieCard({ cols, colLabels, totalP, totalC, totalR }) {
  // Synthetic operating/investing/financing flows
  const flows = [
    { cat:"Encaissements d'exploitation", row: cols.reduce((a,c)=>({...a,[c]:totalP[c]+Math.round(totalP[c]*0.04)}),{}), sign:1 },
    { cat:"Décaissements d'exploitation", row: cols.reduce((a,c)=>({...a,[c]:totalC[c]}),{}), sign:-1 },
    { cat:"Investissements", row: { nov:0, dec:240_000, jan:0, fev:0, mar:0, avr:340_000 }, sign:-1 },
    { cat:"Remboursement emprunt", row: { nov:0, dec:0, jan:0, fev:120_000, mar:0, avr:0 }, sign:-1 },
  ];
  const net = cols.reduce((a,c) => ({ ...a, [c]: flows.reduce((s,f)=>s + (f.sign * f.row[c]), 0) }), {});
  return (
    <div className="panel">
      <div className="panel-head">
        <h3 className="panel-title">Tableau de flux de trésorerie · 6 mois</h3>
      </div>
      <div className="table-wrap">
        <table className="tbl">
          <thead>
            <tr><th>Flux</th>{colLabels.map((l,i)=><th key={i} className="right">{l}</th>)}</tr>
          </thead>
          <tbody>
            {flows.map((f, i) => (
              <tr key={i}>
                <td>{f.cat}</td>
                {cols.map(c => (
                  <td key={c} className="right mono" style={{color: f.sign > 0 ? (f.row[c] ? "var(--pos-d)" : "var(--d-ink-4)") : (f.row[c] ? "var(--d-ink-2)" : "var(--d-ink-4)"), fontSize:12}}>
                    {f.row[c] === 0 ? "—" : (f.sign > 0 ? "+" : "−") + f.row[c].toLocaleString("fr-FR")}
                  </td>
                ))}
              </tr>
            ))}
            <tr style={{background:"var(--d-sunken)", fontWeight:600, borderTop:"1px solid var(--d-line)"}}>
              <td style={{padding:"12px var(--cell-px)"}}>Variation de trésorerie</td>
              {cols.map(c => (
                <td key={c} className="right mono" style={{color: net[c] >= 0 ? "var(--pos-d)" : "var(--orange)", fontSize:13}}>
                  {net[c] >= 0 ? "+" : "−"}{Math.abs(net[c]).toLocaleString("fr-FR")}
                </td>
              ))}
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  );
}

function RatiosCard({ totalP, totalC, totalR, cols }) {
  const ratios = [
    { l:"Marge nette",      v: Math.round((totalR.avr/totalP.avr)*100), unit:"%", target:30, hint:"Résultat / Produits" },
    { l:"Ratio charges fixes", v: Math.round(((totalC.avr - 25_000)/totalP.avr)*100), unit:"%", target:55, hint:"Charges récurrentes / Produits" },
    { l:"Coût de la masse salariale", v: Math.round(((434_000)/totalP.avr)*100), unit:"%", target:35, hint:"641 + 645 / Produits" },
    { l:"Croissance produits", v: Math.round(((totalP.avr - totalP.nov)/totalP.nov)*100), unit:"%", target:8, hint:"Avril vs Novembre" },
  ];
  return (
    <div className="grid-2">
      <div className="panel">
        <div className="panel-head"><h3 className="panel-title">Ratios de gestion</h3></div>
        <div style={{padding:"6px 0"}}>
          {ratios.map((r, i) => {
            const ok = r.v >= r.target;
            return (
              <div key={i} style={{padding:"14px 18px", borderBottom: i<ratios.length-1 ? "1px solid var(--d-line-2)" : "none", display:"grid", gridTemplateColumns:"1fr auto auto", gap:18, alignItems:"center"}}>
                <div>
                  <div style={{fontSize:12.5, fontWeight:500}}>{r.l}</div>
                  <div className="muted" style={{fontSize:11, marginTop:2}}>{r.hint} · cible ≥ {r.target}{r.unit}</div>
                </div>
                <div style={{width:120, height:6, background:"var(--d-sunken)", borderRadius:3, overflow:"hidden"}}>
                  <div style={{height:"100%", width:`${Math.min(100, (r.v/r.target)*100)}%`, background: ok ? "var(--pos-d)" : "var(--warn-d)", borderRadius:3}}/>
                </div>
                <div className="mono" style={{fontSize:16, fontWeight:600, color: ok ? "var(--pos-d)" : "var(--warn-d)", minWidth:60, textAlign:"right"}}>{r.v}{r.unit}</div>
              </div>
            );
          })}
        </div>
      </div>
      <div className="panel">
        <div className="panel-head"><h3 className="panel-title">Lecture managériale</h3></div>
        <div style={{padding:"16px 20px", fontSize:12.5, color:"var(--d-ink-2)", lineHeight:1.65}}>
          <p style={{margin:"0 0 12px"}}>L'agence dégage <b style={{color:"var(--pos-d)"}}>{fmtFCFA(totalR.avr)}</b> de résultat en avril, soit <b>{Math.round((totalR.avr/totalP.avr)*100)}% de marge nette</b>. Le ratio est confortable mais en baisse de 2 pts depuis novembre.</p>
          <p style={{margin:"0 0 12px"}}>La <b>masse salariale</b> représente 47% des produits — au-dessus de la cible 35%. À surveiller si vous embauchez avant que le portefeuille atteigne 60 lots.</p>
          <p style={{margin:0, padding:"10px 12px", background:"var(--d-sunken)", borderRadius:5, borderLeft:"2px solid var(--blue-d)", color:"var(--d-ink-3)"}}>
            <b style={{color:"var(--d-ink-2)"}}>Recommandation</b> · accélérer la commercialisation des 4 lots vacants — chaque lot occupé apporte ~21 000 FCFA de commission mensuelle nette.
          </p>
        </div>
      </div>
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════════════════
   5 · EXPORT COMPTABLE
   ═══════════════════════════════════════════════════════════════════════ */
function ExportComptablePage() {
  const [format, setFormat] = React.useState("sage");
  const [periodFrom, setFrom] = React.useState("2026-04-01");
  const [periodTo, setTo] = React.useState("2026-04-30");
  const [scope, setScope] = React.useState({ bailleurs:true, agence:true, banques:true });
  const [showPreview, setShowPreview] = React.useState(false);

  const formats = [
    { id:"sage",    name:"Sage 100 Comptabilité",     ext:".PNM", desc:"Format Sage Paye & Compta · structure 24 colonnes", logo:"SG" },
    { id:"saari",   name:"Sage Saari · Afrique",     ext:".CSV", desc:"Format standard cabinets ouest-africains (UEMOA)", logo:"SA" },
    { id:"ciel",    name:"Ciel Compta · XIMPORT",     ext:".TXT", desc:"Import Ciel · fichier texte délimité largeur fixe", logo:"CL" },
    { id:"csv",     name:"CSV SYSCOHADA générique",   ext:".CSV", desc:"Toutes colonnes plan OHADA · pour outils tiers", logo:"GN" },
    { id:"fec",     name:"FEC · Sénégal",            ext:".TXT", desc:"Fichier des écritures comptables · DGID/DGEPDS", logo:"FE" },
  ];
  const selected = formats.find(f => f.id === format);

  // Mock journals for preview
  const sample = [
    { date:"05/04/2026", piece:"QT-2026-04-001", cpt:"467001", lib:"Encaissement loyers M. Abdoulaye Ba", debit:"", credit:"2 640 000", journal:"BA" },
    { date:"06/04/2026", piece:"COM-2026-04-001", cpt:"706",   lib:"Commission gestion 8,0% · M. A. Ba",   debit:"211 200", credit:"", journal:"OD" },
    { date:"06/04/2026", piece:"COM-2026-04-001", cpt:"467001", lib:"Commission gestion 8,0% · M. A. Ba", debit:"", credit:"211 200", journal:"OD" },
    { date:"08/04/2026", piece:"CHG-2026-04-001", cpt:"708",   lib:"Refacturation charges Teranga",        debit:"65 000",  credit:"", journal:"OD" },
    { date:"30/04/2026", piece:"VR-2026-04-001",  cpt:"467001", lib:"Versement net M. A. Ba",              debit:"2 363 800", credit:"", journal:"BQ" },
    { date:"30/04/2026", piece:"VR-2026-04-001",  cpt:"5211",   lib:"Virement CBAO · M. A. Ba",            debit:"", credit:"2 363 800", journal:"BQ" },
  ];

  const history = [
    { id:"EXP-2026-03-001", date:"02 avr 2026", format:"Sage 100", period:"Mars 2026", journals:5, entries:412, size:"148 ko", status:"téléchargé" },
    { id:"EXP-2026-02-001", date:"02 mars 2026", format:"Sage 100", period:"Février 2026", journals:5, entries:388, size:"142 ko", status:"téléchargé" },
    { id:"EXP-2026-01-001", date:"03 fév 2026", format:"FEC SN", period:"Janvier 2026", journals:5, entries:402, size:"96 ko", status:"téléchargé" },
    { id:"EXP-2025-Q4-001", date:"05 jan 2026", format:"FEC SN", period:"T4 2025", journals:5, entries:1_244, size:"312 ko", status:"déposé DGID" },
    { id:"EXP-2025-12-001", date:"04 jan 2026", format:"Sage 100", period:"Décembre 2025", journals:5, entries:421, size:"154 ko", status:"téléchargé" },
  ];

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Export comptable</h1>
          <div className="page-sub">Génération des journaux pour votre logiciel comptable · OHADA · SYSCOHADA · FEC</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost" onClick={()=>setShowPreview(true)}>Aperçu</button>
          <button className="btn btn-primary">Générer l'export</button>
        </div>
      </div>

      <div className="grid-detail">
        {/* Configurator */}
        <div className="stack">
          <div className="panel">
            <div className="panel-head">
              <div>
                <h3 className="panel-title">1 · Choisir le format</h3>
                <div className="panel-sub">Le format détermine la structure du fichier et les codes journaux utilisés</div>
              </div>
            </div>
            <div style={{padding:"4px 0"}}>
              {formats.map(f => {
                const active = format === f.id;
                return (
                  <div key={f.id} onClick={()=>setFormat(f.id)} style={{
                    display:"grid", gridTemplateColumns:"42px 1fr auto auto", gap:14, padding:"12px 18px",
                    borderBottom:"1px solid var(--d-line-2)", cursor:"pointer",
                    background: active ? "rgba(97,146,255,0.05)" : undefined,
                  }}>
                    <div style={{width:42, height:42, borderRadius:6, background: active ? "rgba(97,146,255,0.15)" : "var(--d-elev)", color: active ? "var(--blue-d)" : "var(--d-ink-3)", display:"grid", placeItems:"center", fontSize:11, fontWeight:700, letterSpacing:"0.04em"}}>{f.logo}</div>
                    <div style={{minWidth:0}}>
                      <div style={{fontSize:12.5, fontWeight:500, marginBottom:2}}>{f.name}</div>
                      <div className="muted" style={{fontSize:11.5}}>{f.desc}</div>
                    </div>
                    <span className="mono" style={{fontSize:11, color:"var(--d-ink-3)", alignSelf:"center", letterSpacing:"0.04em"}}>{f.ext}</span>
                    <span style={{
                      width:18, height:18, borderRadius:50, alignSelf:"center",
                      border: active ? "5px solid var(--blue-d)" : "1.5px solid var(--d-line)",
                      background:"transparent",
                    }}/>
                  </div>
                );
              })}
            </div>
          </div>

          <div className="panel">
            <div className="panel-head">
              <h3 className="panel-title">2 · Période & périmètre</h3>
            </div>
            <div style={{padding:"16px 20px"}}>
              <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:14, marginBottom:18}}>
                <div>
                  <div style={{fontSize:10.5, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, marginBottom:6}}>Du</div>
                  <input type="date" value={periodFrom} onChange={e=>setFrom(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-mono)"}}/>
                </div>
                <div>
                  <div style={{fontSize:10.5, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, marginBottom:6}}>Au</div>
                  <input type="date" value={periodTo} onChange={e=>setTo(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-mono)"}}/>
                </div>
              </div>
              <div style={{display:"flex", gap:6, marginBottom:18}}>
                {[["Mars 26","2026-03-01","2026-03-31"],["Avril 26","2026-04-01","2026-04-30"],["T1 26","2026-01-01","2026-03-31"],["YTD","2026-01-01","2026-04-30"]].map(([l,f,t]) => (
                  <button key={l} onClick={()=>{setFrom(f); setTo(t);}} style={{
                    flex:1, padding:"6px 8px", fontSize:11.5, borderRadius:5,
                    border:"1px solid var(--d-line)",
                    background: periodFrom === f ? "var(--d-elev)" : "transparent",
                    color: periodFrom === f ? "var(--d-ink)" : "var(--d-ink-2)",
                  }}>{l}</button>
                ))}
              </div>
              <div style={{fontSize:10.5, color:"var(--d-ink-3)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, marginBottom:10}}>Journaux à inclure</div>
              {[
                { k:"bailleurs", l:"Comptes bailleurs (467x)", c:"6 comptes · 184 écritures", icon:"467" },
                { k:"agence",    l:"Compte de résultat agence",  c:"Classes 6 & 7 · 92 écritures", icon:"6/7" },
                { k:"banques",   l:"Mouvements bancaires",       c:"Comptes 521x + 531 · 348 lignes", icon:"521" },
              ].map(s => (
                <div key={s.k} onClick={()=>setScope({...scope, [s.k]: !scope[s.k]})} style={{
                  display:"grid", gridTemplateColumns:"22px 1fr auto", gap:12, padding:"10px 0",
                  borderBottom:"1px solid var(--d-line-2)", alignItems:"center", cursor:"pointer",
                }}>
                  <span style={{
                    width:16, height:16, borderRadius:3,
                    border: scope[s.k] ? "1.5px solid var(--blue-d)" : "1.5px solid var(--d-line)",
                    background: scope[s.k] ? "var(--blue-d)" : "transparent",
                    position:"relative",
                  }}>
                    {scope[s.k] && <span style={{position:"absolute", inset:0, color:"#fff", fontSize:10.5, lineHeight:"13px", textAlign:"center", fontWeight:700}}>✓</span>}
                  </span>
                  <div>
                    <div style={{fontSize:12.5, fontWeight:500}}>{s.l}</div>
                    <div className="muted" style={{fontSize:11}}>{s.c}</div>
                  </div>
                  <span className="mono" style={{fontSize:10.5, color:"var(--blue-d)", padding:"2px 6px", background:"rgba(97,146,255,0.1)", borderRadius:3, letterSpacing:"0.04em", fontWeight:600}}>{s.icon}</span>
                </div>
              ))}
            </div>
          </div>

          <div className="panel">
            <div className="panel-head"><h3 className="panel-title">3 · Mapping plan comptable</h3>
              <button className="btn btn-ghost">Personnaliser →</button>
            </div>
            <div className="table-wrap">
              <table className="tbl">
                <thead>
                  <tr><th style={{width:80}}>Code</th><th>Libellé Logestimmo</th><th>→</th><th>Code cible {selected.id.toUpperCase()}</th></tr>
                </thead>
                <tbody>
                  {Object.entries(PLAN).slice(0,9).map(([cpt, l]) => (
                    <tr key={cpt}>
                      <td className="mono" style={{color:"var(--blue-d)"}}>{cpt}</td>
                      <td className="muted">{l}</td>
                      <td className="muted">→</td>
                      <td className="mono">{cpt}{cpt === "467" && "001…006"}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        </div>

        {/* Right: summary + history */}
        <div className="stack">
          <div className="panel" style={{borderColor:"rgba(97,146,255,0.30)", background:"linear-gradient(180deg, rgba(97,146,255,0.05) 0%, var(--d-panel) 60%)"}}>
            <div className="panel-head" style={{borderColor:"rgba(97,146,255,0.20)"}}>
              <h3 className="panel-title">Récapitulatif de l'export</h3>
            </div>
            <div style={{padding:"18px 20px"}}>
              <div style={{display:"flex", alignItems:"center", gap:12, marginBottom:14}}>
                <div style={{width:48, height:48, borderRadius:8, background:"rgba(97,146,255,0.15)", color:"var(--blue-d)", display:"grid", placeItems:"center", fontSize:13, fontWeight:700, letterSpacing:"0.04em"}}>{selected.logo}</div>
                <div>
                  <div style={{fontSize:13.5, fontWeight:500}}>{selected.name}</div>
                  <div className="muted mono" style={{fontSize:11.5, marginTop:2}}>extension <b style={{color:"var(--d-ink-2)"}}>{selected.ext}</b></div>
                </div>
              </div>
              <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:10, marginBottom:14}}>
                <Stat label="Période" value={`${periodFrom.slice(8)}/${periodFrom.slice(5,7)} → ${periodTo.slice(8)}/${periodTo.slice(5,7)}`}/>
                <Stat label="Journaux" value={Object.values(scope).filter(Boolean).length + " / 3"}/>
                <Stat label="Écritures estimées" value="412"/>
                <Stat label="Taille fichier" value="~ 148 ko"/>
              </div>
              <div style={{padding:"10px 12px", background:"var(--d-sunken)", borderRadius:5, fontSize:11.5, color:"var(--d-ink-3)", lineHeight:1.55}}>
                <span style={{color:"var(--blue-d)", fontWeight:700}}>ⓘ</span>{" "}
                Tous les comptes 467x sont éclatés en sous-comptes par bailleur. Les codes journaux sont standards : <span className="mono" style={{color:"var(--d-ink-2)"}}>BA</span> (encaissements), <span className="mono" style={{color:"var(--d-ink-2)"}}>BQ</span> (banque), <span className="mono" style={{color:"var(--d-ink-2)"}}>OD</span> (opérations diverses).
              </div>
              <button className="btn btn-primary" style={{width:"100%", marginTop:14, padding:"10px", fontSize:13, justifyContent:"center"}}>
                Générer · {selected.name}
              </button>
            </div>
          </div>

          <div className="panel">
            <div className="panel-head">
              <h3 className="panel-title">Historique des exports</h3>
              <button className="btn btn-ghost">Voir tout →</button>
            </div>
            <div className="table-wrap">
              <table className="tbl">
                <thead>
                  <tr><th>Référence</th><th>Période</th><th>Format</th><th className="right">Écritures</th><th>Statut</th></tr>
                </thead>
                <tbody>
                  {history.map(h => (
                    <tr key={h.id}>
                      <td>
                        <div className="mono" style={{fontSize:11, color:"var(--blue-d)"}}>{h.id}</div>
                        <div className="muted mono" style={{fontSize:10.5}}>{h.date}</div>
                      </td>
                      <td className="mono">{h.period}</td>
                      <td className="muted" style={{fontSize:12}}>{h.format}</td>
                      <td className="right mono">{h.entries}</td>
                      <td><span className={`chip ${h.status === "déposé DGID" ? "pos" : "neutral"}`}>{h.status}</span></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>

      {showPreview && <ExportPreviewModal sample={sample} format={selected} onClose={()=>setShowPreview(false)}/>}
    </div>
  );
}

function Stat({ label, value }) {
  return (
    <div>
      <div style={{fontSize:10, color:"var(--d-ink-4)", textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, marginBottom:3}}>{label}</div>
      <div className="mono" style={{fontSize:13, fontWeight:500, color:"var(--d-ink)"}}>{value}</div>
    </div>
  );
}

function ExportPreviewModal({ sample, format, 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:780, maxHeight:"80vh", display:"flex", flexDirection:"column"}}>
        <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}}>Aperçu du fichier · {format.name}</div>
            <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:2}}>6 premières lignes sur ~412 · structure SYSCOHADA</div>
          </div>
          <button onClick={onClose} style={{background:"none", border:"none", color:"var(--d-ink-3)", fontSize:20, cursor:"pointer"}}>×</button>
        </div>
        <div style={{flex:1, overflowY:"auto"}}>
          <table className="tbl" style={{fontSize:11.5}}>
            <thead>
              <tr><th style={{width:90}}>Date</th><th style={{width:120}}>Pièce</th><th style={{width:60}}>Jnl</th><th style={{width:64}}>Cpt</th><th>Libellé</th><th className="right">Débit</th><th className="right">Crédit</th></tr>
            </thead>
            <tbody>
              {sample.map((r, i) => (
                <tr key={i}>
                  <td className="mono muted">{r.date}</td>
                  <td className="mono" style={{fontSize:10.5}}>{r.piece}</td>
                  <td className="mono" style={{fontSize:10.5, color:"var(--blue-d)"}}>{r.journal}</td>
                  <td className="mono" style={{color:"var(--blue-d)"}}>{r.cpt}</td>
                  <td style={{fontSize:11.5}}>{r.lib}</td>
                  <td className="right mono">{r.debit || <span className="empty-dash">—</span>}</td>
                  <td className="right mono" style={{color: r.credit ? "var(--pos-d)" : undefined}}>{r.credit || <span className="empty-dash">—</span>}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div style={{padding:"12px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <span className="muted" style={{fontSize:11}}>L'aperçu est limité — l'export complet contient 412 écritures</span>
          <div style={{display:"flex", gap:8}}>
            <button className="btn btn-ghost" onClick={onClose}>Fermer</button>
            <button className="btn btn-primary">Télécharger {format.ext}</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* Expose pages globally */
Object.assign(window, {
  ComptaBailleursPage, TropPercusPage, RecepissesPage,
  ComptaAgencePage, ExportComptablePage,
  compteBailleur, buildBailleurLedger,
});

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

/* ╔══ form-kit.jsx ══╗ */
/* global React */

/* ═══════════════════════════════════════════════════════════════════════
   FORM KIT — primitives de formulaires Logestimmo
   Référence-design pour Claude Code. Chaque primitive porte une seule
   responsabilité visuelle et expose une API React simple.

   Composition :
     <FormPage> · structure 2 colonnes (sidebar nav + form body)
       <FormSection title subtitle>      // section avec titre + description à gauche, champs à droite
         <Field label hint error required>
           <Input type … />              // <input> stylisé
           <Textarea …/>
           <Select options={[…]} …/>
           <MoneyInput suffix="FCFA" …/> // champ monétaire avec suffixe
           <DateInput …/>
           <Toggle label … />
           <RadioGroup options layout="row|stack" />
           <CheckboxGroup options />
           <Segmented options />          // pour choix court 2-4 options
           <FileDrop label />             // zone drop fichier
         </Field>
         <FieldRow>                      // 2 fields côte à côte
       </FormSection>
       <FormFooter>                      // barre actions sticky
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── PAGE STRUCTURE ─── */
function FormPage({ title, subtitle, nav, children }) {
  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">{title}</h1>
          {subtitle && <div className="page-sub">{subtitle}</div>}
        </div>
      </div>
      <div style={{display:"grid", gridTemplateColumns: nav ? "220px 1fr" : "1fr", gap:24, alignItems:"start"}}>
        {nav}
        <div style={{display:"flex", flexDirection:"column", gap:14}}>{children}</div>
      </div>
    </div>
  );
}

/* Sidebar nav (inside-page) for long settings pages */
function FormNav({ items, current, onChange }) {
  return (
    <nav style={{position:"sticky", top:16, display:"flex", flexDirection:"column", gap:1}}>
      {items.map(it => {
        const active = it.id === current;
        return (
          <button key={it.id} onClick={() => onChange(it.id)} style={{
            textAlign:"left", padding:"8px 12px", borderRadius:5,
            background: active ? "var(--d-elev)" : "transparent",
            color: active ? "var(--d-ink)" : "var(--d-ink-3)",
            fontSize:12.5, fontWeight: active ? 500 : 400,
            borderLeft: active ? "2px solid var(--blue-d)" : "2px solid transparent",
            paddingLeft: 14, cursor:"pointer",
          }}>
            {it.label}
            {it.badge != null && (
              <span style={{
                marginLeft:8, fontSize:10, padding:"1px 5px", borderRadius:3,
                background: it.badge === "!" ? "rgba(249,115,22,0.15)" : "var(--d-sunken)",
                color: it.badge === "!" ? "var(--orange)" : "var(--d-ink-4)",
                fontFamily:"var(--font-mono)", fontWeight:600,
              }}>{it.badge}</span>
            )}
          </button>
        );
      })}
    </nav>
  );
}

/* ─── SECTION ─── */
function FormSection({ title, subtitle, footer, children }) {
  return (
    <section className="panel">
      <div style={{padding:"18px 22px 6px"}}>
        <h3 style={{fontSize:14, fontWeight:500, margin:0, color:"var(--d-ink)"}}>{title}</h3>
        {subtitle && <div style={{fontSize:12, color:"var(--d-ink-3)", marginTop:4, lineHeight:1.5, maxWidth:520}}>{subtitle}</div>}
      </div>
      <div style={{padding:"14px 22px 18px"}}>
        {children}
      </div>
      {footer && (
        <div style={{padding:"12px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-sunken)", display:"flex", justifyContent:"space-between", alignItems:"center", fontSize:11.5, color:"var(--d-ink-3)"}}>
          {footer}
        </div>
      )}
    </section>
  );
}

/* ─── FIELDS ─── */
function Field({ label, hint, error, required, children, optional }) {
  return (
    <div style={{marginBottom:18}}>
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"baseline", marginBottom:6}}>
        <label style={{fontSize:11.5, fontWeight:500, color:"var(--d-ink-2)", letterSpacing:"0.01em"}}>
          {label}{required && <span style={{color:"var(--orange)", marginLeft:3}}>*</span>}
        </label>
        {optional && <span style={{fontSize:10.5, color:"var(--d-ink-4)", textTransform:"uppercase", letterSpacing:"0.06em"}}>optionnel</span>}
      </div>
      {children}
      {hint && !error && <div style={{fontSize:11, color:"var(--d-ink-4)", marginTop:5, lineHeight:1.5}}>{hint}</div>}
      {error && <div style={{fontSize:11, color:"var(--orange)", marginTop:5, fontWeight:500}}>{error}</div>}
    </div>
  );
}

function FieldRow({ children, columns = "1fr 1fr" }) {
  return <div style={{display:"grid", gridTemplateColumns:columns, gap:14}}>{children}</div>;
}

/* ─── INPUTS ─── */
const inputBase = {
  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", transition:"border-color 120ms ease",
};

function Input({ type = "text", icon, suffix, ...props }) {
  const wrapper = { position:"relative", display:"flex", alignItems:"stretch" };
  return (
    <div style={wrapper}>
      {icon && (
        <span style={{
          position:"absolute", left:10, top:"50%", transform:"translateY(-50%)",
          color:"var(--d-ink-4)", fontSize:13, pointerEvents:"none",
        }}>{icon}</span>
      )}
      <input type={type} {...props} style={{
        ...inputBase,
        paddingLeft: icon ? 30 : 12,
        paddingRight: suffix ? 60 : 12,
        ...(props.style || {}),
      }}/>
      {suffix && (
        <span style={{
          position:"absolute", right:0, top:0, bottom:0,
          paddingRight:12, display:"flex", alignItems:"center",
          fontSize:11, color:"var(--d-ink-4)", fontFamily:"var(--font-mono)",
          letterSpacing:"0.04em", pointerEvents:"none",
        }}>{suffix}</span>
      )}
    </div>
  );
}

function Textarea({ rows = 4, ...props }) {
  return <textarea rows={rows} {...props} style={{...inputBase, fontFamily:"var(--font-sans)", lineHeight:1.55, resize:"vertical", ...(props.style||{})}}/>;
}

function MoneyInput(props) {
  return <Input type="text" suffix="FCFA" {...props} style={{fontFamily:"var(--font-mono)", textAlign:"right", ...(props.style||{})}}/>;
}

function DateInput(props) {
  return <Input type="date" {...props} style={{fontFamily:"var(--font-mono)", ...(props.style||{})}}/>;
}

function Select({ options, value, onChange, placeholder, ...rest }) {
  return (
    <div style={{position:"relative"}}>
      <select value={value} onChange={e=>onChange?.(e.target.value)} {...rest} style={{
        ...inputBase, appearance:"none", paddingRight:32, cursor:"pointer",
        color: value ? "var(--d-ink)" : "var(--d-ink-4)",
      }}>
        {placeholder && <option value="">{placeholder}</option>}
        {options.map(o => typeof o === "string"
          ? <option key={o} value={o}>{o}</option>
          : <option key={o.value} value={o.value}>{o.label}</option>)}
      </select>
      <span style={{
        position:"absolute", right:12, top:"50%", transform:"translateY(-50%)",
        color:"var(--d-ink-3)", fontSize:10, pointerEvents:"none",
      }}>▾</span>
    </div>
  );
}

/* ─── TOGGLE ─── */
function Toggle({ label, hint, value, onChange }) {
  return (
    <div onClick={() => onChange?.(!value)} style={{
      display:"grid", gridTemplateColumns:"1fr auto", gap:14, alignItems:"flex-start",
      padding:"12px 0", cursor:"pointer",
    }}>
      <div>
        <div style={{fontSize:12.5, color:"var(--d-ink)", fontWeight:500}}>{label}</div>
        {hint && <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:3, lineHeight:1.5}}>{hint}</div>}
      </div>
      <span style={{
        width:34, height:20, borderRadius:10, padding:2,
        background: value ? "var(--pos-d)" : "var(--d-elev)",
        border: value ? "none" : "1px solid var(--d-line)",
        display:"inline-flex", alignItems:"center",
        justifyContent: value ? "flex-end" : "flex-start", flexShrink:0,
        transition:"background 160ms ease",
      }}>
        <span style={{width:14, height:14, borderRadius:7, background:"#fff", display:"block"}}/>
      </span>
    </div>
  );
}

/* ─── RADIO + CHECKBOX ─── */
function RadioGroup({ options, value, onChange, layout = "stack" }) {
  return (
    <div style={{display:"flex", flexDirection: layout === "row" ? "row" : "column", gap: layout === "row" ? 14 : 8, flexWrap:"wrap"}}>
      {options.map(o => {
        const opt = typeof o === "string" ? { value:o, label:o } : o;
        const active = value === opt.value;
        return (
          <label key={opt.value} onClick={() => onChange?.(opt.value)} style={{
            display:"flex", alignItems:"flex-start", gap:10, cursor:"pointer",
            padding: opt.description ? "10px 12px" : 0,
            border: opt.description ? `1px solid ${active ? "var(--blue-d)" : "var(--d-line)"}` : "none",
            borderRadius: opt.description ? 6 : 0,
            background: opt.description && active ? "rgba(97,146,255,0.06)" : "transparent",
            flex: opt.description && layout === "row" ? 1 : "initial",
          }}>
            <span style={{
              width:16, height:16, borderRadius:50, marginTop:1,
              border: active ? "5px solid var(--blue-d)" : "1.5px solid var(--d-line)",
              background:"transparent", flexShrink:0, transition:"border 120ms ease",
            }}/>
            <div>
              <div style={{fontSize:12.5, color:"var(--d-ink)", fontWeight: opt.description ? 500 : 400}}>{opt.label}</div>
              {opt.description && <div style={{fontSize:11, color:"var(--d-ink-3)", marginTop:3, lineHeight:1.5}}>{opt.description}</div>}
            </div>
          </label>
        );
      })}
    </div>
  );
}

function CheckboxGroup({ options, value = [], onChange }) {
  const toggle = (v) => {
    const next = value.includes(v) ? value.filter(x => x !== v) : [...value, v];
    onChange?.(next);
  };
  return (
    <div style={{display:"flex", flexDirection:"column", gap:8}}>
      {options.map(o => {
        const opt = typeof o === "string" ? { value:o, label:o } : o;
        const checked = value.includes(opt.value);
        return (
          <label key={opt.value} onClick={() => toggle(opt.value)} style={{display:"flex", alignItems:"flex-start", gap:10, cursor:"pointer"}}>
            <span style={{
              width:16, height:16, borderRadius:3, marginTop:1,
              border: checked ? "1.5px solid var(--blue-d)" : "1.5px solid var(--d-line)",
              background: checked ? "var(--blue-d)" : "transparent",
              position:"relative", flexShrink:0,
            }}>
              {checked && <span style={{position:"absolute", inset:0, color:"#fff", fontSize:10, lineHeight:"13px", textAlign:"center", fontWeight:700}}>✓</span>}
            </span>
            <div>
              <div style={{fontSize:12.5, color:"var(--d-ink)"}}>{opt.label}</div>
              {opt.description && <div style={{fontSize:11, color:"var(--d-ink-3)", marginTop:3, lineHeight:1.5}}>{opt.description}</div>}
            </div>
          </label>
        );
      })}
    </div>
  );
}

/* ─── SEGMENTED · short choice (2-4 options) ─── */
function Segmented({ options, value, onChange }) {
  return (
    <div className="seg" style={{display:"inline-flex"}}>
      {options.map(o => {
        const opt = typeof o === "string" ? { value:o, label:o } : o;
        return (
          <button key={opt.value} onClick={() => onChange?.(opt.value)} className={"seg-btn" + (value === opt.value ? " active" : "")}>
            {opt.label}
          </button>
        );
      })}
    </div>
  );
}

/* ─── FILE DROP ─── */
function FileDrop({ label, hint, accept, multiple, files = [], onChange }) {
  const inputRef = React.useRef(null);
  const [over, setOver] = React.useState(false);
  return (
    <div>
      <div
        onClick={() => inputRef.current?.click()}
        onDragOver={e => { e.preventDefault(); setOver(true); }}
        onDragLeave={() => setOver(false)}
        onDrop={e => { e.preventDefault(); setOver(false); onChange?.([...e.dataTransfer.files]); }}
        style={{
          padding:"22px 18px", border:"1.5px dashed " + (over ? "var(--blue-d)" : "var(--d-line)"),
          borderRadius:6, background: over ? "rgba(97,146,255,0.04)" : "var(--d-sunken)",
          cursor:"pointer", textAlign:"center", transition:"border 120ms ease, background 120ms ease",
        }}>
        <div style={{fontSize:18, color:"var(--d-ink-3)", marginBottom:6}}>↑</div>
        <div style={{fontSize:12.5, color:"var(--d-ink-2)", fontWeight:500}}>{label || "Glisser un fichier ou cliquer pour parcourir"}</div>
        {hint && <div style={{fontSize:11, color:"var(--d-ink-4)", marginTop:4}}>{hint}</div>}
        <input ref={inputRef} type="file" accept={accept} multiple={multiple} onChange={e=>onChange?.([...e.target.files])} style={{display:"none"}}/>
      </div>
      {files.length > 0 && (
        <div style={{marginTop:8, display:"flex", flexDirection:"column", gap:4}}>
          {files.map((f, i) => (
            <div key={i} style={{padding:"6px 10px", background:"var(--d-elev)", borderRadius:4, display:"flex", justifyContent:"space-between", fontSize:11.5}}>
              <span className="mono" style={{color:"var(--d-ink-2)"}}>{f.name || f}</span>
              <span className="muted">{f.size ? `${Math.round(f.size/1024)} ko` : ""}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ─── COLOR SWATCH PICKER ─── */
function ColorSwatch({ value, onChange, options }) {
  return (
    <div style={{display:"flex", gap:8, flexWrap:"wrap"}}>
      {options.map(c => (
        <button key={c} onClick={() => onChange?.(c)} style={{
          width:28, height:28, borderRadius:6, background:c, cursor:"pointer",
          border: value === c ? "2px solid var(--d-ink)" : "2px solid transparent",
          boxShadow: value === c ? "0 0 0 1px var(--d-bg)" : "none",
        }}/>
      ))}
    </div>
  );
}

/* ─── FOOTER ─── */
function FormFooter({ children, dirty, onSave, onCancel, saving }) {
  return (
    <div style={{
      position:"sticky", bottom:0, marginTop:6,
      padding:"14px 22px", background:"var(--d-panel)",
      border:"1px solid var(--d-line)", borderRadius:8,
      boxShadow:"0 -8px 24px -12px rgba(0,0,0,0.4)",
      display:"flex", justifyContent:"space-between", alignItems:"center", gap:12,
    }}>
      <div style={{fontSize:11.5, color: dirty ? "var(--orange)" : "var(--d-ink-3)", display:"flex", alignItems:"center", gap:8}}>
        {dirty ? (
          <><span style={{width:6, height:6, borderRadius:50, background:"var(--orange)"}}/>Modifications non enregistrées</>
        ) : (
          <><span style={{width:6, height:6, borderRadius:50, background:"var(--pos-d)"}}/>À jour</>
        )}
      </div>
      <div style={{display:"flex", gap:8}}>
        {children}
        {onCancel && <button className="btn btn-ghost" onClick={onCancel}>Annuler</button>}
        {onSave && <button className="btn btn-primary" disabled={saving || !dirty} onClick={onSave} style={{opacity: (saving || !dirty) ? 0.5 : 1}}>
          {saving ? "Enregistrement…" : "Enregistrer"}
        </button>}
      </div>
    </div>
  );
}

/* ─── INLINE HELP / BANNERS ─── */
function Banner({ tone = "info", title, children, action }) {
  const tones = {
    info: { border:"rgba(97,146,255,0.30)", bg:"rgba(97,146,255,0.06)", dot:"var(--blue-d)" },
    warn: { border:"rgba(249,115,22,0.30)", bg:"rgba(249,115,22,0.06)", dot:"var(--orange)" },
    pos:  { border:"rgba(63,181,131,0.30)", bg:"rgba(63,181,131,0.06)", dot:"var(--pos-d)" },
    neg:  { border:"rgba(241,106,99,0.30)", bg:"rgba(241,106,99,0.06)", dot:"var(--neg-d)" },
  };
  const t = tones[tone];
  return (
    <div style={{
      display:"grid", gridTemplateColumns:"auto 1fr auto", gap:12,
      padding:"12px 16px", border:`1px solid ${t.border}`, background:t.bg,
      borderRadius:6, marginBottom:14, alignItems:"center",
    }}>
      <span style={{width:6, height:6, borderRadius:50, background:t.dot, marginTop:6}}/>
      <div>
        {title && <div style={{fontSize:12.5, fontWeight:500, color:"var(--d-ink)", marginBottom: children ? 3 : 0}}>{title}</div>}
        {children && <div style={{fontSize:11.5, color:"var(--d-ink-3)", lineHeight:1.55}}>{children}</div>}
      </div>
      {action}
    </div>
  );
}

/* Expose the kit on window for cross-script use */
Object.assign(window, {
  FormPage, FormNav, FormSection, Field, FieldRow,
  Input, Textarea, MoneyInput, DateInput, Select,
  Toggle, RadioGroup, CheckboxGroup, Segmented, FileDrop, ColorSwatch,
  FormFooter, Banner,
});

/* ╚══ end form-kit.jsx ══╝ */
