/* bundle 3/4 */

/* ╔══ pages-bailleur-form.jsx ══╗ */
/* global React, FormSection, Field, FieldRow, Input, Textarea, MoneyInput,
          Select, Toggle, RadioGroup, Segmented, ColorSwatch, Banner, fmtFCFA */

/* ═══════════════════════════════════════════════════════════════════════
   BAILLEUR FORM — création + modification
   Gère les 4 types : Particulier · SCI · SARL · Indivision
   ═══════════════════════════════════════════════════════════════════════ */

const ENTITY_TYPES = [
  { value: "Particulier",                  label: "Particulier",   sub: "Personne physique" },
  { value: "Société civile immobilière",   label: "SCI",           sub: "Société civile immobilière" },
  { value: "SARL",                         label: "SARL",          sub: "Personne morale OHADA" },
  { value: "Indivision",                   label: "Indivision",    sub: "Plusieurs co-propriétaires" },
];

const AVATAR_COLORS = ["#3F7DDB","#C9559C","#3FB583","#F2933E","#9B8AE6","#E0A74A","#1F6E4A","#9A2A2A","#7C3AED"];

function makeEmptyBailleur() {
  return {
    id: "",
    name: "",
    entity: "Particulier",
    avatar: "",
    color: "#3F7DDB",
    phone: "",
    email: "",
    bank: "",
    ninea: "",
    rccm: "",
    commissionRate: 0.08,
    contractDate: new Date().toISOString().slice(0,10),
    paymentFrequency: "monthly",
    paymentDay: "30",
    notes: "",
    // Indivision-specific
    indivisaires: [
      { id: 1, name: "", quotePart: 50, phone: "", email: "", bank: "", lien: "" },
      { id: 2, name: "", quotePart: 50, phone: "", email: "", bank: "", lien: "" },
    ],
    representant: "",
    versementMode: "proportional",
    conventionDate: "",
    // SCI/SARL specific
    capital: "",
    legalRep: "",
    legalRepRole: "Gérant",
  };
}

function fillFromExisting(b) {
  const base = makeEmptyBailleur();
  return {
    ...base,
    ...b,
    indivisaires: b.indivisaires && b.indivisaires.length ? b.indivisaires : base.indivisaires,
  };
}

function deriveAvatar(name) {
  if (!name) return "";
  const parts = name.replace(/^(M\.|Mme|Mlle|Indivision|Famille)\s+/i, "").split(/\s+/).filter(Boolean);
  if (parts.length === 1) return parts[0].slice(0,2).toUpperCase();
  return (parts[0][0] + parts[parts.length-1][0]).toUpperCase();
}

/* ─── Modal shell ─── */
function BailleurFormModal({ open, mode = "create", initial, onClose, onSave }) {
  const [form, setForm] = React.useState(() => initial ? fillFromExisting(initial) : makeEmptyBailleur());
  const [step, setStep] = React.useState("identite");
  const [touched, setTouched] = React.useState(false);

  React.useEffect(() => {
    if (open) {
      setForm(initial ? fillFromExisting(initial) : makeEmptyBailleur());
      setStep("identite");
      setTouched(false);
    }
  }, [open, initial]);

  const set = (k, v) => { setForm(f => ({...f, [k]: v})); setTouched(true); };
  const setIndivisaire = (idx, patch) => {
    setForm(f => ({
      ...f,
      indivisaires: f.indivisaires.map((it,i) => i === idx ? {...it, ...patch} : it),
    }));
    setTouched(true);
  };
  const addIndivisaire = () => {
    setForm(f => ({
      ...f,
      indivisaires: [...f.indivisaires, { id: Date.now(), name:"", quotePart: 0, phone:"", email:"", bank:"", lien:"" }],
    }));
    setTouched(true);
  };
  const removeIndivisaire = (idx) => {
    setForm(f => ({
      ...f,
      indivisaires: f.indivisaires.filter((_, i) => i !== idx),
    }));
    setTouched(true);
  };

  const isIndivision = form.entity === "Indivision";
  const isCompany = form.entity === "Société civile immobilière" || form.entity === "SARL";

  // Auto-derive avatar
  React.useEffect(() => {
    if (form.name && !touched) return;
    if (form.name) set("avatar", deriveAvatar(form.name));
    // eslint-disable-next-line
  }, [form.name]);

  const quotePartTotal = form.indivisaires.reduce((s,i) => s + (Number(i.quotePart) || 0), 0);
  const quotePartValid = !isIndivision || quotePartTotal === 100;

  const errors = {};
  if (!form.name.trim()) errors.name = "Le nom est obligatoire";
  if (!form.email.trim() && !isIndivision) errors.email = "Email obligatoire pour la correspondance agence";
  if (isIndivision) {
    if (form.indivisaires.length < 2) errors.indivisaires = "Une indivision compte au moins 2 indivisaires";
    if (quotePartTotal !== 100) errors.quotePart = `Total des quote-parts = ${quotePartTotal}% (attendu : 100%)`;
    if (!form.representant) errors.representant = "Désignez le représentant de l'indivision";
  }
  if (isCompany && !form.legalRep.trim()) errors.legalRep = "Représentant légal requis";

  const canSave = Object.keys(errors).length === 0;

  function handleSave() {
    if (!canSave) return;
    const payload = {
      ...form,
      avatar: form.avatar || deriveAvatar(form.name),
      id: form.id || `B-${String(Date.now()).slice(-3).padStart(3,"0")}`,
      commissionRate: Number(form.commissionRate) || 0.08,
    };
    onSave?.(payload);
  }

  // Esc to close
  React.useEffect(() => {
    if (!open) return;
    function onKey(e) { if (e.key === "Escape") onClose?.(); }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);

  if (!open) return null;

  const STEPS = [
    { id:"identite", label:"Identité & type" },
    ...(isIndivision ? [{ id:"indivision", label:"Indivisaires", badge: form.indivisaires.length }] : []),
    { id:"contact",  label:"Contact" },
    { id:"bancaire", label:"Versement" },
    { id:"gestion",  label:"Contrat de gestion" },
  ];

  return (
    <>
      <div onClick={onClose} style={{
        position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:200,
        backdropFilter:"blur(3px)",
      }}/>
      <div role="dialog" style={{
        position:"fixed", inset:"5vh 10vw", zIndex:201, maxWidth:1080, margin:"0 auto",
        background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10,
        display:"flex", flexDirection:"column", overflow:"hidden",
        boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)",
        animation:"baill-pop 180ms ease-out",
      }}>
        {/* Header */}
        <header style={{
          padding:"16px 22px", borderBottom:"1px solid var(--d-line)",
          display:"flex", alignItems:"center", justifyContent:"space-between",
          background:"var(--d-panel)",
        }}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>
              {mode === "edit" ? "Modifier le bailleur" : "Nouveau bailleur"}
            </div>
            <div style={{fontSize:16, fontWeight:600, color:"var(--d-ink)", display:"flex", alignItems:"center", gap:10}}>
              {form.name || "Bailleur sans nom"}
              {mode === "edit" && form.id && (
                <span className="mono" style={{
                  fontSize:10.5, padding:"2px 7px", borderRadius:3,
                  background:"var(--d-elev)", color:"var(--d-ink-3)",
                }}>{form.id}</span>
              )}
            </div>
          </div>
          <button onClick={onClose} title="Fermer · Esc" style={{
            width:32, height:32, borderRadius:6, color:"var(--d-ink-3)",
            fontSize:18, display:"grid", placeItems:"center",
            background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer",
          }}>×</button>
        </header>

        {/* Body */}
        <div style={{display:"grid", gridTemplateColumns:"220px 1fr", flex:1, minHeight:0}}>
          {/* Steps nav */}
          <nav style={{
            borderRight:"1px solid var(--d-line)", padding:"18px 14px",
            background:"var(--d-sunken)", display:"flex", flexDirection:"column", gap:2,
          }}>
            {STEPS.map((s, i) => {
              const active = s.id === step;
              return (
                <button key={s.id} onClick={()=>setStep(s.id)} style={{
                  textAlign:"left", padding:"9px 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: "2px solid " + (active ? "var(--blue-d)" : "transparent"),
                  paddingLeft: 12, cursor:"pointer",
                  display:"flex", alignItems:"center", justifyContent:"space-between", gap:8,
                }}>
                  <span><span className="mono" style={{color:"var(--d-ink-4)", marginRight:8, fontSize:10.5}}>{String(i+1).padStart(2,"0")}</span>{s.label}</span>
                  {s.badge != null && (
                    <span className="mono" style={{
                      fontSize:10, padding:"1px 6px", borderRadius:3,
                      background:"var(--d-bg)", color:"var(--d-ink-3)",
                    }}>{s.badge}</span>
                  )}
                </button>
              );
            })}

            {/* Validation summary */}
            <div style={{marginTop:14, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
              <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>
                Validation
              </div>
              {Object.keys(errors).length === 0 ? (
                <div style={{display:"flex", gap:6, alignItems:"center", fontSize:11.5, color:"var(--pos-d)"}}>
                  <span style={{width:6, height:6, borderRadius:50, background:"var(--pos-d)"}}/>
                  Prêt à enregistrer
                </div>
              ) : (
                <div style={{display:"flex", flexDirection:"column", gap:4}}>
                  {Object.values(errors).map((e,i) => (
                    <div key={i} style={{fontSize:11, color:"var(--orange)", lineHeight:1.45, display:"flex", gap:6, alignItems:"flex-start"}}>
                      <span style={{color:"var(--orange)", marginTop:2}}>!</span>
                      <span>{e}</span>
                    </div>
                  ))}
                </div>
              )}
            </div>
          </nav>

          {/* Step content */}
          <div style={{overflow:"auto", padding:"22px 28px"}}>
            {step === "identite"   && <IdentiteStep form={form} set={set}/>}
            {step === "indivision" && <IndivisionStep
              form={form} set={set}
              setIndivisaire={setIndivisaire}
              addIndivisaire={addIndivisaire}
              removeIndivisaire={removeIndivisaire}
              quotePartTotal={quotePartTotal}
              quotePartValid={quotePartValid}
            />}
            {step === "contact"    && <ContactStep form={form} set={set}/>}
            {step === "bancaire"   && <BancaireStep form={form} set={set}/>}
            {step === "gestion"    && <GestionStep form={form} set={set}/>}
          </div>
        </div>

        {/* Footer */}
        <footer style={{
          padding:"14px 22px", borderTop:"1px solid var(--d-line)",
          background:"var(--d-panel)",
          display:"flex", justifyContent:"space-between", alignItems:"center",
        }}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>
            {isIndivision ? (
              <>Indivision · {form.indivisaires.length} indivisaires · quote-parts : <b className="mono" style={{color: quotePartValid ? "var(--pos-d)" : "var(--orange)"}}>{quotePartTotal}%</b></>
            ) : (
              <>{form.entity} · Commission par défaut <b style={{color:"var(--d-ink-2)"}}>{(Number(form.commissionRate)*100).toFixed(1).replace(".",",")}%</b></>
            )}
          </div>
          <div style={{display:"flex", gap:8}}>
            <button onClick={onClose} className="btn btn-ghost">Annuler</button>
            {STEPS.findIndex(s => s.id === step) < STEPS.length - 1 && (
              <button onClick={() => {
                const idx = STEPS.findIndex(s => s.id === step);
                setStep(STEPS[idx + 1].id);
              }} className="btn btn-secondary">Suivant →</button>
            )}
            <button onClick={handleSave} disabled={!canSave} className="btn btn-primary" style={{opacity: canSave ? 1 : 0.5, cursor: canSave ? "pointer" : "not-allowed"}}>
              {mode === "edit" ? "Enregistrer les modifications" : "Créer le bailleur"}
            </button>
          </div>
        </footer>
      </div>

      <style>{`
        @keyframes baill-pop {
          from { transform: scale(0.98) translateY(8px); opacity: 0; }
          to   { transform: scale(1) translateY(0); opacity: 1; }
        }
      `}</style>
    </>
  );
}

/* ─── STEP 1 · Identité ─── */
function IdentiteStep({ form, set }) {
  const isIndivision = form.entity === "Indivision";
  const isCompany = form.entity === "Société civile immobilière" || form.entity === "SARL";

  return (
    <>
      <FormSection
        title="Type de bailleur"
        subtitle="Le type détermine les champs requis et le mode de gestion : personne physique, société (SCI/SARL) ou indivision entre plusieurs co-propriétaires."
      >
        <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:8}}>
          {ENTITY_TYPES.map(t => {
            const active = form.entity === t.value;
            return (
              <button key={t.value} onClick={()=>set("entity", t.value)} style={{
                padding:"12px 14px", textAlign:"left", cursor:"pointer",
                border:"1px solid " + (active ? "var(--blue-d)" : "var(--d-line)"),
                background: active ? "rgba(97,146,255,0.06)" : "var(--d-panel)",
                borderRadius:6,
              }}>
                <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:4}}>
                  <span style={{
                    width:14, height:14, borderRadius:50, flexShrink:0,
                    border: "4px solid " + (active ? "var(--blue-d)" : "var(--d-line)"),
                  }}/>
                  <span style={{fontSize:12.5, fontWeight:600, color:"var(--d-ink)"}}>{t.label}</span>
                </div>
                <div style={{fontSize:10.5, color:"var(--d-ink-3)", paddingLeft:22, lineHeight:1.4}}>{t.sub}</div>
              </button>
            );
          })}
        </div>
      </FormSection>

      <FormSection
        title="Identité"
        subtitle="Tels que figurant sur la pièce d'identité, le RCCM ou la convention d'indivision. Ces informations apparaissent sur les CRG et bordereaux de versement."
      >
        <FieldRow columns="100px 1fr">
          <Field label="Avatar">
            <div style={{
              width:72, height:72, borderRadius:8,
              background: form.color, color:"#fff",
              display:"grid", placeItems:"center",
              fontSize:24, fontWeight:600, letterSpacing:"-0.01em",
            }}>{form.avatar || deriveAvatar(form.name) || "?"}</div>
            <div style={{marginTop:10}}>
              <ColorSwatch value={form.color} onChange={v=>set("color", v)} options={AVATAR_COLORS}/>
            </div>
          </Field>
          <div>
            <Field
              label={isIndivision ? "Dénomination de l'indivision" : isCompany ? "Raison sociale" : "Nom complet"}
              required
              hint={isIndivision ? "Ex : Indivision Wade · Famille Diop · Hoirie Sarr" : isCompany ? "Telle qu'enregistrée au RCCM" : "M. / Mme + prénom et nom"}
            >
              <Input value={form.name} onChange={e=>set("name", e.target.value)}/>
            </Field>
            <FieldRow>
              <Field label="Avatar (initiales)" hint="2 lettres · auto-déduit du nom">
                <Input value={form.avatar} onChange={e=>set("avatar", e.target.value.slice(0,2).toUpperCase())} style={{textTransform:"uppercase"}}/>
              </Field>
              <Field label="Référence interne" hint={form.id ? "ID attribué" : "Auto-généré à la création"}>
                <Input value={form.id} onChange={e=>set("id", e.target.value)} placeholder="B-007" style={{fontFamily:"var(--font-mono)"}} readOnly={!!form.id}/>
              </Field>
            </FieldRow>
          </div>
        </FieldRow>

        {/* Conditional fields by type */}
        {isCompany && (
          <>
            <FieldRow>
              <Field label="NINEA" required hint="Numéro d'Identification National">
                <Input value={form.ninea} onChange={e=>set("ninea", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
              </Field>
              <Field label="RCCM" required hint="Registre du Commerce">
                <Input value={form.rccm} onChange={e=>set("rccm", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
              </Field>
            </FieldRow>
            <FieldRow>
              <Field label="Capital social" optional>
                <MoneyInput value={form.capital} onChange={e=>set("capital", e.target.value)}/>
              </Field>
              <Field label="Représentant légal" required hint="Personne signataire pour la société">
                <Input value={form.legalRep} onChange={e=>set("legalRep", e.target.value)}/>
              </Field>
            </FieldRow>
            <Field label="Qualité du représentant">
              <Segmented value={form.legalRepRole} onChange={v=>set("legalRepRole", v)} options={[
                {value:"Gérant", label:"Gérant"},
                {value:"PDG", label:"PDG"},
                {value:"DG", label:"Directeur général"},
                {value:"Associé", label:"Associé"},
              ]}/>
            </Field>
          </>
        )}

        {form.entity === "Particulier" && (
          <Field label="NINEA ou CNI" optional hint="N° pièce d'identité ou NINEA si commerçant individuel">
            <Input value={form.ninea} onChange={e=>set("ninea", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
        )}

        {isIndivision && (
          <Banner tone="info" title="Indivision">
            Régie par les articles 826 et suivants du Code des Obligations Civiles et Commerciales (COCC) sénégalais.
            Tous les indivisaires sont co-propriétaires des biens loués. Vous pourrez préciser leurs quote-parts à l'étape suivante,
            et choisir le mode de versement (proportionnel ou à un représentant unique).
          </Banner>
        )}
      </FormSection>
    </>
  );
}

/* ─── STEP 2 · Indivision ─── */
function IndivisionStep({ form, set, setIndivisaire, addIndivisaire, removeIndivisaire, quotePartTotal, quotePartValid }) {
  return (
    <>
      <FormSection
        title="Indivisaires"
        subtitle="Liste des co-propriétaires de l'indivision avec leur quote-part. Le total doit être égal à 100%. Chaque indivisaire perçoit sa part de revenus selon le mode de versement choisi."
        footer={<>
          <span>
            {form.indivisaires.length} indivisaires · total <b className="mono" style={{color: quotePartValid ? "var(--pos-d)" : "var(--orange)"}}>{quotePartTotal}%</b>
            {!quotePartValid && <span style={{color:"var(--orange)", marginLeft:8}}>· doit être 100%</span>}
          </span>
          <button className="btn btn-secondary" style={{padding:"5px 12px"}} onClick={addIndivisaire}>+ Ajouter un indivisaire</button>
        </>}
      >
        <div style={{display:"flex", flexDirection:"column", gap:10}}>
          {form.indivisaires.map((it, idx) => (
            <div key={it.id || idx} style={{
              padding:"14px 16px", border:"1px solid var(--d-line)", borderRadius:6,
              background:"var(--d-sunken)",
            }}>
              <div style={{display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:12}}>
                <div style={{display:"flex", alignItems:"center", gap:10}}>
                  <span className="mono" style={{
                    width:22, height:22, borderRadius:4, background:"var(--d-elev)",
                    color:"var(--d-ink-2)", display:"grid", placeItems:"center",
                    fontSize:11, fontWeight:600,
                  }}>{idx + 1}</span>
                  <span style={{fontSize:12, fontWeight:600, color:"var(--d-ink)"}}>
                    {it.name || `Indivisaire ${idx + 1}`}
                  </span>
                  {form.representant === it.name && it.name && (
                    <span style={{
                      fontSize:9.5, fontWeight:600, padding:"1px 7px", borderRadius:3,
                      background:"rgba(124,58,237,0.15)", color:"#A78BFA",
                      letterSpacing:"0.04em", textTransform:"uppercase",
                    }}>Représentant</span>
                  )}
                </div>
                {form.indivisaires.length > 2 && (
                  <button onClick={()=>removeIndivisaire(idx)} title="Retirer" style={{
                    width:26, height:26, borderRadius:4, color:"var(--d-ink-4)",
                    background:"transparent", border:"1px solid var(--d-line)",
                    cursor:"pointer", fontSize:12,
                  }}>×</button>
                )}
              </div>
              <FieldRow columns="2fr 1fr">
                <Field label="Nom complet" required>
                  <Input value={it.name} onChange={e=>setIndivisaire(idx, {name: e.target.value})}/>
                </Field>
                <Field label="Quote-part" required hint="% des revenus locatifs">
                  <Input
                    value={it.quotePart}
                    onChange={e=>setIndivisaire(idx, {quotePart: e.target.value.replace(/[^0-9.]/g,"")})}
                    suffix="%"
                    style={{fontFamily:"var(--font-mono)", textAlign:"right"}}
                  />
                </Field>
              </FieldRow>
              <FieldRow>
                <Field label="Lien avec l'indivision" optional>
                  <Select value={it.lien} onChange={v=>setIndivisaire(idx, {lien: v})} options={[
                    {value:"", label:"—"},
                    {value:"ascendant", label:"Ascendant (parent)"},
                    {value:"descendant", label:"Descendant (enfant)"},
                    {value:"conjoint", label:"Conjoint survivant"},
                    {value:"fratrie", label:"Frère / sœur"},
                    {value:"autre", label:"Autre"},
                  ]}/>
                </Field>
                <Field label="Téléphone">
                  <Input value={it.phone} onChange={e=>setIndivisaire(idx, {phone: e.target.value})} icon="☎"/>
                </Field>
                <Field label="Email">
                  <Input type="email" value={it.email} onChange={e=>setIndivisaire(idx, {email: e.target.value})} icon="@"/>
                </Field>
              </FieldRow>
              <Field label="Compte de réception" hint="Utilisé uniquement en mode versement proportionnel">
                <Input value={it.bank} onChange={e=>setIndivisaire(idx, {bank: e.target.value})} placeholder="Wave · 77 ••• ••• ou CBAO · ••• ••••" style={{fontFamily:"var(--font-mono)", fontSize:12}}/>
              </Field>
            </div>
          ))}
        </div>

        {/* Quote-part repartition helper */}
        <div style={{
          marginTop:14, padding:"12px 14px", borderRadius:6,
          background:"var(--d-panel)", border:"1px solid var(--d-line)",
        }}>
          <div style={{
            display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:10,
            fontSize:11, color:"var(--d-ink-3)",
          }}>
            <span>Répartition visuelle des quote-parts</span>
            <span className="mono" style={{color: quotePartValid ? "var(--pos-d)" : "var(--orange)"}}>{quotePartTotal} / 100%</span>
          </div>
          <div style={{
            display:"flex", height:18, borderRadius:4, overflow:"hidden",
            background:"var(--d-elev)", border:"1px solid var(--d-line-2)",
          }}>
            {form.indivisaires.map((it, i) => {
              const pct = Number(it.quotePart) || 0;
              if (!pct) return null;
              const color = AVATAR_COLORS[i % AVATAR_COLORS.length];
              return (
                <div key={i} title={`${it.name || "Indivisaire " + (i+1)} · ${pct}%`} style={{
                  width:`${(pct / Math.max(quotePartTotal, 100)) * 100}%`, background: color,
                  borderRight:"1px solid var(--d-bg)",
                  display:"grid", placeItems:"center", fontSize:9.5, color:"#fff", fontWeight:600,
                  fontFamily:"var(--font-mono)",
                }}>{pct >= 8 ? `${pct}%` : ""}</div>
              );
            })}
          </div>
          <div style={{
            display:"flex", flexWrap:"wrap", gap:10, marginTop:10,
          }}>
            {form.indivisaires.map((it, i) => {
              const color = AVATAR_COLORS[i % AVATAR_COLORS.length];
              return (
                <div key={i} style={{display:"flex", alignItems:"center", gap:6, fontSize:11, color:"var(--d-ink-3)"}}>
                  <span style={{width:8, height:8, borderRadius:2, background:color}}/>
                  <span>{it.name || `Indivisaire ${i+1}`}</span>
                  <span className="mono" style={{color:"var(--d-ink-4)"}}>{it.quotePart || 0}%</span>
                </div>
              );
            })}
          </div>
        </div>
      </FormSection>

      <FormSection
        title="Représentation & mode de versement"
        subtitle="Désignation du représentant de l'indivision (interlocuteur unique de l'agence) et règles de répartition des revenus."
      >
        <Field label="Représentant de l'indivision" required hint="Désigné par convention d'indivision · interlocuteur agence et signataire des actes">
          <Select
            value={form.representant}
            onChange={v=>set("representant", v)}
            placeholder="— Choisir un indivisaire —"
            options={form.indivisaires.filter(it => it.name).map(it => ({
              value: it.name,
              label: `${it.name} · ${it.quotePart}%${it.lien ? " · " + it.lien : ""}`,
            }))}
          />
        </Field>

        <Field label="Mode de versement des revenus" required hint="Comment l'agence reverse les loyers nets aux indivisaires">
          <RadioGroup
            value={form.versementMode}
            onChange={v=>set("versementMode", v)}
            options={[
              {
                value:"proportional",
                label:"Versement proportionnel — recommandé",
                description:"L'agence verse à chaque indivisaire sa part nette directement sur son compte, selon la quote-part. Conforme à l'article 829 COCC : chaque indivisaire perçoit les fruits selon sa part.",
              },
              {
                value:"representant",
                label:"Versement unique au représentant",
                description:"L'agence verse l'intégralité du net au représentant, qui se charge ensuite de la redistribution. Nécessite une convention d'indivision explicite avec mandat.",
              },
              {
                value:"compte-indivision",
                label:"Compte d'indivision dédié",
                description:"Versement sur un compte ouvert au nom de l'indivision (rare ; nécessite NINEA indivision).",
              },
            ]}
          />
        </Field>

        <FieldRow>
          <Field label="Date convention d'indivision" optional hint="Date de signature de la convention chez le notaire">
            <Input type="date" value={form.conventionDate} onChange={e=>set("conventionDate", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
          <Field label="NINEA indivision" optional hint="Si l'indivision est immatriculée séparément">
            <Input value={form.ninea} onChange={e=>set("ninea", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
        </FieldRow>
      </FormSection>
    </>
  );
}

/* ─── STEP 3 · Contact ─── */
function ContactStep({ form, set }) {
  const isIndivision = form.entity === "Indivision";
  return (
    <FormSection
      title="Coordonnées de l'agence"
      subtitle={isIndivision
        ? "Coordonnées du représentant de l'indivision pour la correspondance courante : avis de versement, CRG, échanges urgents."
        : "Coordonnées principales pour les versements, CRG mensuels et alertes (impayés, fin de bail, échéances fiscales)."}
    >
      <FieldRow>
        <Field label="Téléphone" required>
          <Input value={form.phone} onChange={e=>set("phone", e.target.value)} icon="☎" placeholder="+221 77 ..."/>
        </Field>
        <Field label="Email" required={!isIndivision} hint="Pour l'envoi des CRG et bordereaux">
          <Input type="email" value={form.email} onChange={e=>set("email", e.target.value)} icon="@"/>
        </Field>
      </FieldRow>

      <Field label="Adresse postale" optional hint="Pour l'envoi de documents physiques (récépissés, courriers RAR)">
        <Input value={form.address || ""} onChange={e=>set("address", e.target.value)} placeholder="12 Rue Carnot, Plateau, Dakar"/>
      </Field>

      <Field label="Préférence de communication">
        <RadioGroup
          layout="row"
          value={form.commPref || "email"}
          onChange={v=>set("commPref", v)}
          options={[
            {value:"email",    label:"Email"},
            {value:"whatsapp", label:"WhatsApp"},
            {value:"sms",      label:"SMS"},
          ]}
        />
      </Field>
    </FormSection>
  );
}

/* ─── STEP 4 · Versement ─── */
function BancaireStep({ form, set }) {
  const isIndivision = form.entity === "Indivision";
  return (
    <>
      {isIndivision && form.versementMode === "proportional" && (
        <Banner tone="info" title="Versement proportionnel actif">
          Les comptes de chaque indivisaire ont été renseignés à l'étape précédente. L'agence calculera automatiquement la part nette
          (loyers encaissés × quote-part − commission − charges) et créera un bordereau par indivisaire chaque mois.
        </Banner>
      )}

      <FormSection
        title={isIndivision
          ? (form.versementMode === "representant" ? "Compte du représentant" : "Compte principal (référence)")
          : "Compte de versement"}
        subtitle="Compte sur lequel l'agence reverse les loyers nets. Mobile money accepté pour les versements jusqu'à 500 000 FCFA / mois (réglementation BCEAO)."
      >
        <Field
          label={isIndivision && form.versementMode === "representant" ? "Compte du représentant" : "Compte bancaire ou mobile money"}
          required={!isIndivision || form.versementMode !== "proportional"}
          hint="Format : Banque · numéro · ou opérateur mobile · numéro"
        >
          <Input
            value={form.bank}
            onChange={e=>set("bank", e.target.value)}
            placeholder="CBAO · 0048 217 33 6512 / Wave · +221 77 ..."
            style={{fontFamily:"var(--font-mono)", fontSize:12.5}}
          />
        </Field>

        <FieldRow>
          <Field label="Type de compte">
            <Segmented
              value={form.bankType || "banque"}
              onChange={v=>set("bankType", v)}
              options={[
                {value:"banque", label:"Banque"},
                {value:"wave", label:"Wave"},
                {value:"om", label:"Orange Money"},
              ]}
            />
          </Field>
          <Field label="Bénéficiaire" hint="Nom exact tel qu'enregistré chez la banque / l'opérateur">
            <Input value={form.beneficiaire || form.name} onChange={e=>set("beneficiaire", e.target.value)}/>
          </Field>
        </FieldRow>

        <FieldRow>
          <Field label="Fréquence des versements" required>
            <Select value={form.paymentFrequency} onChange={v=>set("paymentFrequency", v)} options={[
              {value:"monthly",   label:"Mensuel"},
              {value:"quarterly", label:"Trimestriel"},
              {value:"manual",    label:"À la demande"},
            ]}/>
          </Field>
          <Field label="Jour de versement" hint="Du mois">
            <Input value={form.paymentDay} onChange={e=>set("paymentDay", e.target.value)} suffix="du mois"/>
          </Field>
        </FieldRow>
      </FormSection>
    </>
  );
}

/* ─── STEP 5 · Gestion ─── */
function GestionStep({ form, set }) {
  return (
    <>
      <FormSection
        title="Contrat de gestion"
        subtitle="Termes commerciaux du mandat de gestion locative. Modifiable individuellement par bien ensuite."
      >
        <FieldRow>
          <Field label="Date de signature" required>
            <Input type="date" value={form.contractDate} onChange={e=>set("contractDate", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
          <Field label="Durée du mandat">
            <Select value={form.contractDuration || "1y-tacite"} onChange={v=>set("contractDuration", v)} options={[
              {value:"1y-tacite", label:"1 an · tacite reconduction"},
              {value:"3y", label:"3 ans"},
              {value:"5y", label:"5 ans"},
              {value:"indef", label:"Indéterminée"},
            ]}/>
          </Field>
        </FieldRow>

        <FieldRow>
          <Field label="Taux de commission" required hint="Appliqué sur les loyers encaissés">
            <Input
              value={(Number(form.commissionRate) * 100).toFixed(1).replace(".",",")}
              onChange={e=>{
                const v = e.target.value.replace(",",".").replace(/[^0-9.]/g,"");
                set("commissionRate", (Number(v) || 0) / 100);
              }}
              suffix="%"
              style={{fontFamily:"var(--font-mono)", textAlign:"right"}}
            />
          </Field>
          <Field label="Commission minimum / mois" optional>
            <MoneyInput value={form.minCommission || ""} onChange={e=>set("minCommission", e.target.value)}/>
          </Field>
        </FieldRow>
      </FormSection>

      <FormSection
        title="Notes internes"
        subtitle="Visible uniquement par votre équipe. Préférences, historique relationnel, points d'attention."
      >
        <Textarea rows={4} value={form.notes} onChange={e=>set("notes", e.target.value)} placeholder="Ex : préfère le versement par Wave · alertes WhatsApp · résidence à l'étranger…"/>
      </FormSection>
    </>
  );
}

Object.assign(window, { BailleurFormModal, makeEmptyBailleur, deriveAvatar });

/* ╚══ end pages-bailleur-form.jsx ══╝ */

/* ╔══ pages-tenant-form.jsx ══╗ */
/* global React, FormSection, Field, FieldRow, Input, Textarea, MoneyInput,
          Select, RadioGroup, Segmented, ColorSwatch, Banner, FileDrop, fmtFCFA, MOCK */

/* ═══════════════════════════════════════════════════════════════════════
   TENANT FORM — création + modification d'un locataire
   Types : Particulier · Professionnel · Étudiant · Diplomatique
   ═══════════════════════════════════════════════════════════════════════ */

const TENANT_TYPES = [
  { value: "Particulier",     label: "Particulier",     sub: "Personne physique" },
  { value: "Professionnel",   label: "Professionnel",   sub: "Société · libéral" },
  { value: "Étudiant",        label: "Étudiant",        sub: "Avec garant solidaire" },
  { value: "Diplomatique",    label: "Diplomatique",    sub: "Mission · ambassade" },
];

const T_AVATAR_COLORS = ["#3F7DDB","#C9559C","#3FB583","#F2933E","#9B8AE6","#E0A74A","#1F6E4A","#9A2A2A","#7C3AED"];

function makeEmptyTenant() {
  return {
    id: "",
    name: "",
    avatar: "",
    color: "#3F7DDB",
    type: "Particulier",
    civilite: "M.",
    birthDate: "",
    nationality: "Sénégalaise",
    idDocType: "CNI",
    idDocNumber: "",
    profession: "",
    employer: "",
    monthlyIncome: "",
    // Pro
    raisonSociale: "",
    ninea: "",
    rccm: "",
    legalRep: "",
    // Coordonnées
    phone: "",
    email: "",
    previousAddress: "",
    commPref: "whatsapp",
    // Garants
    guarantyType: "caution-solidaire",
    guarantors: [
      { id: 1, name: "", lien: "", phone: "", email: "", income: "", idNumber: "" },
    ],
    // Notes & docs
    files: [],
    notes: "",
    // Lease bookkeeping (set when created via lease form)
    unit: "",
    property: "",
    propertyId: "",
    rent: 0,
    leaseStart: "",
    leaseEnd: "",
    status: "à jour",
    daysLate: 0,
    balance: 0,
  };
}

function fillTenantFromExisting(t) {
  const base = makeEmptyTenant();
  return {
    ...base,
    ...t,
    guarantors: t.guarantors && t.guarantors.length ? t.guarantors : base.guarantors,
    files: t.files || [],
  };
}

function deriveTenantAvatar(name) {
  if (!name) return "";
  const parts = name.replace(/^(M\.|Mme|Mlle|Dr|Pr)\s+/i, "").split(/\s+/).filter(Boolean);
  if (parts.length === 1) return parts[0].slice(0,2).toUpperCase();
  return (parts[0][0] + parts[parts.length-1][0]).toUpperCase();
}

/* ─── Tenants store ─── */
const _tenantListeners = new Set();
function _notifyTenants() { _tenantListeners.forEach(fn => fn()); }
function upsertTenant(t) {
  const i = MOCK.tenants.findIndex(x => x.id === t.id);
  if (i >= 0) MOCK.tenants[i] = { ...MOCK.tenants[i], ...t };
  else MOCK.tenants.push(t);
  _notifyTenants();
}
function useTenants() {
  const [, tick] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => { _tenantListeners.add(tick); return () => _tenantListeners.delete(tick); }, []);
  return MOCK.tenants;
}
function getTenantById(id) { return MOCK.tenants.find(t => t.id === id); }
function nextTenantId() {
  const max = MOCK.tenants.reduce((m,t) => Math.max(m, parseInt(t.id.replace("T-",""),10) || 0), 2000);
  return "T-" + (max + 1);
}

/* ═══ MODAL SHELL ═══ */
function TenantFormModal({ open, mode = "create", initial, onClose, onSave, preset }) {
  const [form, setForm] = React.useState(() => initial ? fillTenantFromExisting(initial) : { ...makeEmptyTenant(), ...(preset || {}) });
  const [step, setStep] = React.useState("identite");

  React.useEffect(() => {
    if (open) {
      setForm(initial ? fillTenantFromExisting(initial) : { ...makeEmptyTenant(), ...(preset || {}) });
      setStep("identite");
    }
  }, [open, initial, preset]);

  const set = (k, v) => setForm(f => ({...f, [k]: v}));
  const setG = (idx, patch) => setForm(f => ({...f, guarantors: f.guarantors.map((g,i) => i === idx ? {...g, ...patch} : g)}));
  const addG = () => setForm(f => ({...f, guarantors: [...f.guarantors, { id: Date.now(), name:"", lien:"", phone:"", email:"", income:"", idNumber:"" }]}));
  const rmG  = idx => setForm(f => ({...f, guarantors: f.guarantors.filter((_,i)=>i!==idx)}));

  const isPro = form.type === "Professionnel";
  const isStudent = form.type === "Étudiant";
  const needsGuarantor = form.guarantyType === "caution-solidaire";

  // Auto-derive avatar
  React.useEffect(() => {
    if (form.name && !form.avatar) set("avatar", deriveTenantAvatar(form.name));
    // eslint-disable-next-line
  }, [form.name]);

  const errors = {};
  if (!form.name.trim()) errors.name = "Nom du locataire requis";
  if (!form.phone.trim()) errors.phone = "Téléphone requis";
  if (!form.email.trim()) errors.email = "Email requis pour les quittances";
  if (isPro && !form.ninea.trim()) errors.ninea = "NINEA requis pour bailleur professionnel";
  if (isPro && !form.legalRep.trim()) errors.legalRep = "Représentant légal requis";
  if (needsGuarantor && (!form.guarantors[0] || !form.guarantors[0].name.trim())) errors.garants = "Au moins un garant solidaire";
  if (isStudent && !needsGuarantor) errors.guarantyType = "Étudiant → garant solidaire obligatoire";

  const canSave = Object.keys(errors).length === 0;

  function handleSave() {
    if (!canSave) return;
    onSave?.({
      ...form,
      avatar: form.avatar || deriveTenantAvatar(form.name),
      id: form.id || nextTenantId(),
      monthlyIncome: Number(String(form.monthlyIncome).replace(/[^0-9]/g,"")) || 0,
    });
  }

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

  if (!open) return null;

  const STEPS = [
    { id:"identite",  label:"Identité & type" },
    { id:"contact",   label:"Coordonnées" },
    { id:"garants",   label:"Garants & solvabilité", badge: needsGuarantor ? form.guarantors.length : null },
    { id:"docs",      label:"Documents & notes" },
  ];

  return (
    <>
      <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:200, backdropFilter:"blur(3px)"}}/>
      <div role="dialog" style={{
        position:"fixed", inset:"5vh 10vw", zIndex:201, maxWidth:1080, margin:"0 auto",
        background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10,
        display:"flex", flexDirection:"column", overflow:"hidden",
        boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)", animation:"baill-pop 180ms ease-out",
      }}>
        <header style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", alignItems:"center", justifyContent:"space-between", background:"var(--d-panel)"}}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>
              {mode === "edit" ? "Modifier le locataire" : "Nouveau locataire"}
            </div>
            <div style={{fontSize:16, fontWeight:600, color:"var(--d-ink)", display:"flex", alignItems:"center", gap:10}}>
              {form.name || "Locataire sans nom"}
              {mode === "edit" && form.id && (
                <span className="mono" style={{fontSize:10.5, padding:"2px 7px", borderRadius:3, background:"var(--d-elev)", color:"var(--d-ink-3)"}}>{form.id}</span>
              )}
            </div>
          </div>
          <button onClick={onClose} title="Fermer · Esc" style={{
            width:32, height:32, borderRadius:6, color:"var(--d-ink-3)", fontSize:18, display:"grid", placeItems:"center",
            background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer",
          }}>×</button>
        </header>

        <div style={{display:"grid", gridTemplateColumns:"220px 1fr", flex:1, minHeight:0}}>
          <nav style={{borderRight:"1px solid var(--d-line)", padding:"18px 14px", background:"var(--d-sunken)", display:"flex", flexDirection:"column", gap:2}}>
            {STEPS.map((s, i) => {
              const active = s.id === step;
              return (
                <button key={s.id} onClick={()=>setStep(s.id)} style={{
                  textAlign:"left", padding:"9px 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: "2px solid " + (active ? "var(--blue-d)" : "transparent"),
                  paddingLeft: 12, cursor:"pointer", display:"flex", alignItems:"center", justifyContent:"space-between", gap:8,
                }}>
                  <span><span className="mono" style={{color:"var(--d-ink-4)", marginRight:8, fontSize:10.5}}>{String(i+1).padStart(2,"0")}</span>{s.label}</span>
                  {s.badge != null && (
                    <span className="mono" style={{fontSize:10, padding:"1px 6px", borderRadius:3, background:"var(--d-bg)", color:"var(--d-ink-3)"}}>{s.badge}</span>
                  )}
                </button>
              );
            })}

            <div style={{marginTop:14, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
              <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>Validation</div>
              {Object.keys(errors).length === 0 ? (
                <div style={{display:"flex", gap:6, alignItems:"center", fontSize:11.5, color:"var(--pos-d)"}}>
                  <span style={{width:6, height:6, borderRadius:50, background:"var(--pos-d)"}}/>Prêt à enregistrer
                </div>
              ) : (
                <div style={{display:"flex", flexDirection:"column", gap:4}}>
                  {Object.values(errors).map((e,i) => (
                    <div key={i} style={{fontSize:11, color:"var(--orange)", lineHeight:1.45, display:"flex", gap:6, alignItems:"flex-start"}}>
                      <span style={{color:"var(--orange)", marginTop:2}}>!</span><span>{e}</span>
                    </div>
                  ))}
                </div>
              )}
            </div>

            {/* Solvabilité indicator */}
            {form.monthlyIncome > 0 && form.rent > 0 && (
              <div style={{marginTop:10, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
                <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>Solvabilité</div>
                {(() => {
                  const ratio = (Number(form.monthlyIncome) / Number(form.rent));
                  const ok = ratio >= 3;
                  return (
                    <div style={{fontSize:11.5, color: ok ? "var(--pos-d)" : "var(--orange)", fontFamily:"var(--font-mono)"}}>
                      Loyer ≤ {(100/ratio).toFixed(0)}% revenus {ok ? "· ok" : "· seuil 33%"}
                    </div>
                  );
                })()}
              </div>
            )}
          </nav>

          <div style={{overflow:"auto", padding:"22px 28px"}}>
            {step === "identite" && <TIdentiteStep form={form} set={set}/>}
            {step === "contact"  && <TContactStep form={form} set={set}/>}
            {step === "garants"  && <TGarantsStep form={form} set={set} setG={setG} addG={addG} rmG={rmG}/>}
            {step === "docs"     && <TDocsStep form={form} set={set}/>}
          </div>
        </div>

        <footer style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-panel)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>
            {form.type} · {form.guarantors.filter(g => g.name).length} garants
            {form.monthlyIncome > 0 && <> · revenu déclaré <b style={{color:"var(--d-ink-2)"}}>{Number(form.monthlyIncome).toLocaleString("fr-FR")} FCFA</b></>}
          </div>
          <div style={{display:"flex", gap:8}}>
            <button onClick={onClose} className="btn btn-ghost">Annuler</button>
            {STEPS.findIndex(s => s.id === step) < STEPS.length - 1 && (
              <button onClick={() => {
                const idx = STEPS.findIndex(s => s.id === step);
                setStep(STEPS[idx + 1].id);
              }} className="btn btn-secondary">Suivant →</button>
            )}
            <button onClick={handleSave} disabled={!canSave} className="btn btn-primary" style={{opacity: canSave ? 1 : 0.5, cursor: canSave ? "pointer" : "not-allowed"}}>
              {mode === "edit" ? "Enregistrer les modifications" : "Créer le locataire"}
            </button>
          </div>
        </footer>
      </div>
    </>
  );
}

/* ─── STEP 1 · Identité ─── */
function TIdentiteStep({ form, set }) {
  const isPro = form.type === "Professionnel";
  return (
    <>
      <FormSection
        title="Type de locataire"
        subtitle="Le type ajuste les champs (pièces, garants, fiscalité) et l'arbre documentaire requis avant signature."
      >
        <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:8}}>
          {TENANT_TYPES.map(t => {
            const active = form.type === t.value;
            return (
              <button key={t.value} onClick={()=>set("type", t.value)} style={{
                padding:"12px 14px", textAlign:"left", cursor:"pointer",
                border:"1px solid " + (active ? "var(--blue-d)" : "var(--d-line)"),
                background: active ? "rgba(97,146,255,0.06)" : "var(--d-panel)", borderRadius:6,
              }}>
                <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:4}}>
                  <span style={{width:14, height:14, borderRadius:50, flexShrink:0, border: "4px solid " + (active ? "var(--blue-d)" : "var(--d-line)")}}/>
                  <span style={{fontSize:12.5, fontWeight:600, color:"var(--d-ink)"}}>{t.label}</span>
                </div>
                <div style={{fontSize:10.5, color:"var(--d-ink-3)", paddingLeft:22, lineHeight:1.4}}>{t.sub}</div>
              </button>
            );
          })}
        </div>
      </FormSection>

      <FormSection
        title={isPro ? "Identité de l'entreprise" : "Identité"}
        subtitle={isPro
          ? "Telle qu'enregistrée au RCCM. Le représentant légal signe le bail au nom de la société."
          : "Telle qu'inscrite sur la pièce d'identité présentée."}
      >
        <FieldRow columns="100px 1fr">
          <Field label="Avatar">
            <div style={{
              width:72, height:72, borderRadius:8, background: form.color, color:"#fff",
              display:"grid", placeItems:"center", fontSize:24, fontWeight:600, letterSpacing:"-0.01em",
            }}>{form.avatar || deriveTenantAvatar(form.name) || "?"}</div>
            <div style={{marginTop:10}}>
              <ColorSwatch value={form.color} onChange={v=>set("color", v)} options={T_AVATAR_COLORS}/>
            </div>
          </Field>
          <div>
            {!isPro && (
              <FieldRow columns="100px 1fr">
                <Field label="Civilité">
                  <Select value={form.civilite} onChange={v=>set("civilite", v)} options={["M.","Mme","Mlle","Dr","Pr"]}/>
                </Field>
                <Field label="Nom complet" required>
                  <Input value={form.name} onChange={e=>set("name", e.target.value)} placeholder="Prénom Nom"/>
                </Field>
              </FieldRow>
            )}
            {isPro && (
              <Field label="Raison sociale" required>
                <Input value={form.name} onChange={e=>set("name", e.target.value)} placeholder="Telle qu'enregistrée au RCCM"/>
              </Field>
            )}
            <FieldRow>
              <Field label="Avatar (initiales)" hint="Auto-déduit du nom · modifiable">
                <Input value={form.avatar} onChange={e=>set("avatar", e.target.value.slice(0,2).toUpperCase())} style={{textTransform:"uppercase"}}/>
              </Field>
              <Field label="Référence interne" hint={form.id ? "ID attribué" : "Auto-généré à la création"}>
                <Input value={form.id} onChange={e=>set("id", e.target.value)} placeholder="T-2125" style={{fontFamily:"var(--font-mono)"}} readOnly={!!form.id}/>
              </Field>
            </FieldRow>
          </div>
        </FieldRow>

        {!isPro && (
          <>
            <FieldRow>
              <Field label="Date de naissance">
                <Input type="date" value={form.birthDate} onChange={e=>set("birthDate", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
              </Field>
              <Field label="Nationalité">
                <Input value={form.nationality} onChange={e=>set("nationality", e.target.value)}/>
              </Field>
            </FieldRow>
            <FieldRow columns="180px 1fr">
              <Field label="Pièce d'identité">
                <Select value={form.idDocType} onChange={v=>set("idDocType", v)} options={[
                  {value:"CNI", label:"CNI sénégalaise"},
                  {value:"Passeport", label:"Passeport"},
                  {value:"Séjour", label:"Carte de séjour"},
                  {value:"Permis", label:"Permis de conduire"},
                ]}/>
              </Field>
              <Field label="Numéro de pièce" required hint="Pour archivage et vérification">
                <Input value={form.idDocNumber} onChange={e=>set("idDocNumber", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
              </Field>
            </FieldRow>
            <FieldRow>
              <Field label="Profession">
                <Input value={form.profession} onChange={e=>set("profession", e.target.value)} placeholder="Ex : Ingénieur, Médecin, Étudiant…"/>
              </Field>
              <Field label="Employeur" optional>
                <Input value={form.employer} onChange={e=>set("employer", e.target.value)}/>
              </Field>
            </FieldRow>
          </>
        )}

        {isPro && (
          <>
            <FieldRow>
              <Field label="NINEA" required>
                <Input value={form.ninea} onChange={e=>set("ninea", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
              </Field>
              <Field label="RCCM" required>
                <Input value={form.rccm} onChange={e=>set("rccm", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
              </Field>
            </FieldRow>
            <Field label="Représentant légal · signataire" required>
              <Input value={form.legalRep} onChange={e=>set("legalRep", e.target.value)}/>
            </Field>
          </>
        )}

        <Field label="Revenu mensuel déclaré" optional hint="Sert au calcul du ratio loyer / revenus (recommandé ≤ 33%)">
          <MoneyInput value={form.monthlyIncome} onChange={e=>set("monthlyIncome", e.target.value.replace(/[^0-9]/g,""))}/>
        </Field>

        {form.type === "Étudiant" && (
          <Banner tone="info" title="Locataire étudiant">
            Un garant solidaire est obligatoire. Précisez à l'étape suivante au moins un garant (parent ou tuteur)
            avec ses revenus et sa pièce d'identité.
          </Banner>
        )}
      </FormSection>
    </>
  );
}

/* ─── STEP 2 · Contact ─── */
function TContactStep({ form, set }) {
  return (
    <FormSection
      title="Coordonnées"
      subtitle="Coordonnées principales pour les quittances, relances et avis de paiement. Le SMS et WhatsApp sont les canaux les plus suivis localement."
    >
      <FieldRow>
        <Field label="Téléphone" required>
          <Input value={form.phone} onChange={e=>set("phone", e.target.value)} icon="☎" placeholder="+221 77 ..."/>
        </Field>
        <Field label="Email" required hint="Envoi des quittances · réception des avis">
          <Input type="email" value={form.email} onChange={e=>set("email", e.target.value)} icon="@"/>
        </Field>
      </FieldRow>

      <Field label="Adresse précédente" optional hint="Pour vérification de l'historique de paiement">
        <Input value={form.previousAddress} onChange={e=>set("previousAddress", e.target.value)} placeholder="Rue · Quartier · Ville"/>
      </Field>

      <Field label="Préférence de communication">
        <RadioGroup
          layout="row"
          value={form.commPref}
          onChange={v=>set("commPref", v)}
          options={[
            {value:"whatsapp", label:"WhatsApp"},
            {value:"sms",      label:"SMS"},
            {value:"email",    label:"Email"},
            {value:"appel",    label:"Appel"},
          ]}
        />
      </Field>
    </FormSection>
  );
}

/* ─── STEP 3 · Garants ─── */
function TGarantsStep({ form, set, setG, addG, rmG }) {
  return (
    <>
      <FormSection
        title="Type de garantie"
        subtitle="Mécanisme de couverture des impayés. La caution solidaire est la plus courante au Sénégal et engage personnellement le garant sur les loyers et charges."
      >
        <RadioGroup
          value={form.guarantyType}
          onChange={v=>set("guarantyType", v)}
          options={[
            {
              value:"caution-solidaire",
              label:"Caution solidaire — recommandé",
              description:"Un proche (parent, employeur) s'engage à payer en cas de défaillance du locataire. Article 850 COCC. Renseignez le garant ci-dessous.",
            },
            {
              value:"caution-bancaire",
              label:"Caution bancaire",
              description:"La banque du locataire émet une garantie à première demande, généralement plafonnée à 6 mois de loyer.",
            },
            {
              value:"depot-majore",
              label:"Dépôt de garantie majoré",
              description:"3 à 6 mois de loyer placés sur compte séquestre en remplacement d'une caution. Adapté aux profils internationaux.",
            },
            {
              value:"aucune",
              label:"Aucune garantie",
              description:"Réservé aux profils diplomatiques ou aux conventions sociétés-bailleur. À justifier dans les notes.",
            },
          ]}
        />
      </FormSection>

      {form.guarantyType === "caution-solidaire" && (
        <FormSection
          title="Garants solidaires"
          subtitle="Personne(s) qui se portent caution. Pour un étudiant, le garant doit justifier de revenus stables au moins 3× supérieurs au loyer."
          footer={<>
            <span>{form.guarantors.filter(g=>g.name).length} / {form.guarantors.length} garants renseignés</span>
            {form.guarantors.length < 2 && <button className="btn btn-secondary" style={{padding:"5px 12px"}} onClick={addG}>+ Ajouter un garant</button>}
          </>}
        >
          <div style={{display:"flex", flexDirection:"column", gap:10}}>
            {form.guarantors.map((g, idx) => (
              <div key={g.id || idx} style={{padding:"14px 16px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-sunken)"}}>
                <div style={{display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:12}}>
                  <div style={{display:"flex", alignItems:"center", gap:10}}>
                    <span className="mono" style={{
                      width:22, height:22, borderRadius:4, background:"var(--d-elev)", color:"var(--d-ink-2)",
                      display:"grid", placeItems:"center", fontSize:11, fontWeight:600,
                    }}>{idx + 1}</span>
                    <span style={{fontSize:12, fontWeight:600, color:"var(--d-ink)"}}>{g.name || `Garant ${idx + 1}`}</span>
                  </div>
                  {form.guarantors.length > 1 && (
                    <button onClick={()=>rmG(idx)} title="Retirer" style={{
                      width:26, height:26, borderRadius:4, color:"var(--d-ink-4)", background:"transparent",
                      border:"1px solid var(--d-line)", cursor:"pointer", fontSize:12,
                    }}>×</button>
                  )}
                </div>
                <FieldRow>
                  <Field label="Nom complet" required={idx===0}>
                    <Input value={g.name} onChange={e=>setG(idx,{name:e.target.value})}/>
                  </Field>
                  <Field label="Lien">
                    <Select value={g.lien} onChange={v=>setG(idx,{lien:v})} options={[
                      {value:"", label:"—"},
                      {value:"parent", label:"Parent"},
                      {value:"conjoint", label:"Conjoint"},
                      {value:"frere-soeur", label:"Frère / sœur"},
                      {value:"employeur", label:"Employeur"},
                      {value:"autre", label:"Autre"},
                    ]}/>
                  </Field>
                </FieldRow>
                <FieldRow>
                  <Field label="Téléphone">
                    <Input value={g.phone} onChange={e=>setG(idx,{phone:e.target.value})} icon="☎"/>
                  </Field>
                  <Field label="Email">
                    <Input value={g.email} onChange={e=>setG(idx,{email:e.target.value})} icon="@"/>
                  </Field>
                </FieldRow>
                <FieldRow>
                  <Field label="N° pièce d'identité">
                    <Input value={g.idNumber} onChange={e=>setG(idx,{idNumber:e.target.value})} style={{fontFamily:"var(--font-mono)"}}/>
                  </Field>
                  <Field label="Revenu mensuel" hint="≥ 3× le loyer recommandé">
                    <MoneyInput value={g.income} onChange={e=>setG(idx,{income:e.target.value.replace(/[^0-9]/g,"")})}/>
                  </Field>
                </FieldRow>
              </div>
            ))}
          </div>
        </FormSection>
      )}
    </>
  );
}

/* ─── STEP 4 · Documents ─── */
function TDocsStep({ form, set }) {
  return (
    <>
      <FormSection
        title="Pièces justificatives"
        subtitle="Joindre les documents requis pour la signature du bail. Vous pourrez compléter plus tard ; le bail reste en brouillon tant que les pièces obligatoires manquent."
      >
        <Field label="Documents du locataire" hint="CNI, fiche de paie 3 derniers mois, attestation d'employeur">
          <FileDrop
            label="Glisser CNI · fiches de paie · attestation"
            hint="PDF, JPG, PNG · max 10 Mo par fichier"
            multiple
            files={form.files}
            onChange={files => set("files", files)}
          />
        </Field>

        <div style={{
          marginTop:6, padding:"12px 14px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-sunken)",
        }}>
          <div style={{fontSize:11, fontWeight:600, color:"var(--d-ink-2)", marginBottom:8, letterSpacing:"0.02em"}}>Pièces attendues</div>
          <div style={{display:"flex", flexDirection:"column", gap:6, fontSize:11.5, color:"var(--d-ink-3)"}}>
            {[
              "Pièce d'identité en cours de validité",
              "3 dernières fiches de paie · ou attestation employeur",
              "Justificatif de domicile précédent (facture < 3 mois)",
              form.type === "Étudiant" && "Certificat de scolarité",
              form.type === "Professionnel" && "RCCM + NINEA + statuts à jour",
              form.guarantyType === "caution-solidaire" && "Pièce d'identité + revenus du garant",
              form.guarantyType === "caution-bancaire" && "Acte de cautionnement bancaire",
            ].filter(Boolean).map((d,i) => (
              <div key={i} style={{display:"flex", alignItems:"center", gap:8}}>
                <span style={{width:5, height:5, borderRadius:50, background:"var(--d-ink-4)"}}/>
                <span>{d}</span>
              </div>
            ))}
          </div>
        </div>
      </FormSection>

      <FormSection
        title="Notes internes"
        subtitle="Observations, antécédents, points d'attention. Visible uniquement par votre équipe."
      >
        <Textarea
          rows={4}
          value={form.notes}
          onChange={e=>set("notes", e.target.value)}
          placeholder="Ex : recommandé par M. X · profil sérieux · arrivée prévue le 1er juin…"
        />
      </FormSection>
    </>
  );
}

Object.assign(window, {
  TenantFormModal, upsertTenant, useTenants, getTenantById,
  makeEmptyTenant, deriveTenantAvatar, nextTenantId,
});

/* ╚══ end pages-tenant-form.jsx ══╝ */

/* ╔══ pages-bail-form.jsx ══╗ */
/* global React, FormSection, Field, FieldRow, Input, Textarea, MoneyInput,
          Select, RadioGroup, Segmented, Banner, FileDrop, fmtFCFA, fmtCompact, MOCK,
          useTenants, getTenantById, upsertTenant, nextTenantId, deriveTenantAvatar */

/* ═══════════════════════════════════════════════════════════════════════
   BAIL FORM — création + modification d'un bail (lease)
   Étapes : Locataire · Bien · Durée · Financier · Clauses · Récap
   ═══════════════════════════════════════════════════════════════════════ */

const LEASE_TYPES = [
  { value:"Résidentiel",   label:"Résidentiel",  sub:"Logement nu · usage habitation" },
  { value:"Meublé",        label:"Meublé",       sub:"Inventaire annexé au bail" },
  { value:"Professionnel", label:"Professionnel", sub:"Bureau · libéral · profession" },
  { value:"Commercial",    label:"Commercial",   sub:"OHADA · pas de porte" },
];

function makeEmptyLease() {
  return {
    id: "",
    // Tenant binding
    tenantMode: "new", // "existing" | "new"
    tenantId: "",
    newTenant: {
      name: "", phone: "", email: "", avatar: "",
      idDocType: "CNI", idDocNumber: "", profession: "", monthlyIncome: "",
      color: "#3F7DDB",
    },
    // Co-titulaire (conjoint / partenaire) — un seul foyer, solidaire du bail
    coTenant: {
      enabled: false,
      name: "", relation: "conjoint", // conjoint | partenaire | autre
      idDocType: "CNI", idDocNumber: "", phone: "", email: "",
    },
    // Property
    propertyId: "",
    unit: "",
    type: "Résidentiel",
    // Durée
    start: new Date().toISOString().slice(0,10),
    durationMonths: 24,
    renewal: "tacite",
    // Financier
    rent: 0,
    charges: 18_000,
    deposit: 0,
    paymentDay: 5,
    paymentMode: "wave",
    indexation: "irl",
    customIndex: 1.8,
    honoraires: "preneur",
    // LOA — Location avec Option d'Achat (version légère)
    isLOA: false,
    loaSalePrice: 0,        // prix de levée d'option (valeur de rachat du bien)
    loaUpfront: 0,          // apport / prime d'option versé à l'entrée
    loaCapitalShare: 30,    // % du loyer imputé au capital de rachat
    loaOptionMonths: 36,    // mois avant la levée d'option
    // Clauses
    destination: "habitation",
    subletAllowed: false,
    petsAllowed: false,
    insuranceRequired: true,
    preavis: 3,
    notes: "",
    // Bookkeeping
    signedAt: new Date().toISOString().slice(0,10),
    leaseStatus: "brouillon",
  };
}

function fillLeaseFromExisting(l) {
  const base = makeEmptyLease();
  return {
    ...base,
    ...l,
    newTenant: l.newTenant || base.newTenant,
    coTenant: l.coTenant || base.coTenant,
    tenantMode: l.tenantId ? "existing" : "new",
    durationMonths: l.duration || base.durationMonths,
  };
}

/* Compute end date from start + duration */
function computeEnd(startISO, months) {
  const d = new Date(startISO);
  if (isNaN(d)) return "";
  d.setMonth(d.getMonth() + Number(months || 0));
  return d.toISOString().slice(0,10);
}

/* LOA — calcule l'encours de rachat (version légère) à partir des champs du bail */
function loaCompute(f) {
  const rent    = Number(f.rent) || 0;
  const sale    = Number(f.loaSalePrice) || 0;
  const upfront = Number(f.loaUpfront) || 0;
  const share   = Math.max(0, Math.min(100, Number(f.loaCapitalShare) || 0));
  const months  = Math.max(0, Number(f.loaOptionMonths) || 0);
  const capitalPerMonth = Math.round(rent * share / 100);
  const capitalOverTerm = capitalPerMonth * months;
  const accumulated = upfront + capitalOverTerm;          // total imputé au rachat à la levée
  const residual = Math.max(0, sale - accumulated);       // solde à régler pour lever l'option
  const coverage = sale > 0 ? Math.min(100, Math.round(accumulated / sale * 100)) : 0;
  return { rent, sale, upfront, share, months, capitalPerMonth, capitalOverTerm, accumulated, residual, coverage };
}

/* ─── Leases store ─── */
const _leaseListeners = new Set();
function _notifyLeases() { _leaseListeners.forEach(fn => fn()); }
if (!window.__EXTRA_LEASES) window.__EXTRA_LEASES = [];
function upsertLease(l) {
  const i = window.__EXTRA_LEASES.findIndex(x => x.id === l.id);
  if (i >= 0) window.__EXTRA_LEASES[i] = l; else window.__EXTRA_LEASES.push(l);
  _notifyLeases();
}
function useExtraLeases() {
  const [, tick] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => { _leaseListeners.add(tick); return () => _leaseListeners.delete(tick); }, []);
  return window.__EXTRA_LEASES;
}
function nextLeaseId() {
  const max = window.__EXTRA_LEASES.reduce((m,l) => Math.max(m, parseInt((l.id||"").replace("BX-","").replace(/\D/g,""),10) || 0), 9000);
  return "BX-" + (max + 1);
}

/* ═══ MODAL SHELL ═══ */
function BailFormModal({ open, mode = "create", initial, onClose, onSave, presetPropertyId }) {
  const tenants = useTenants();
  const [form, setForm] = React.useState(() =>
    initial ? fillLeaseFromExisting(initial)
            : { ...makeEmptyLease(), propertyId: presetPropertyId || "" }
  );
  const [step, setStep] = React.useState("locataire");

  React.useEffect(() => {
    if (open) {
      setForm(initial ? fillLeaseFromExisting(initial) : { ...makeEmptyLease(), propertyId: presetPropertyId || "" });
      setStep("locataire");
    }
  }, [open, initial, presetPropertyId]);

  const set = (k, v) => setForm(f => ({...f, [k]: v}));
  const setNT = (patch) => setForm(f => ({...f, newTenant: {...f.newTenant, ...patch}}));
  const setCo = (patch) => setForm(f => ({...f, coTenant: {...f.coTenant, ...patch}}));

  const property = MOCK.properties.find(p => p.id === form.propertyId);
  const end = computeEnd(form.start, form.durationMonths);
  const existingTenant = form.tenantId ? getTenantById(form.tenantId) : null;
  const tenantName = form.tenantMode === "existing" ? (existingTenant?.name || "") : form.newTenant.name;

  const rentNum = Number(form.rent) || 0;
  const errors = {};
  if (form.tenantMode === "existing" && !form.tenantId) errors.tenant = "Choisissez un locataire existant";
  if (form.tenantMode === "new" && !form.newTenant.name.trim()) errors.tenant = "Renseignez le nom du nouveau locataire";
  if (form.tenantMode === "new" && !form.newTenant.phone.trim()) errors.tenantPhone = "Téléphone du nouveau locataire requis";
  if (form.coTenant.enabled && !form.coTenant.name.trim()) errors.coTenant = "Nom du co-titulaire requis";
  if (!form.propertyId) errors.property = "Sélectionnez un bien";
  if (!form.unit && property && property.units > 1) errors.unit = "Précisez le lot (le bien contient plusieurs lots)";
  if (!form.start) errors.start = "Date de début requise";
  if (!form.durationMonths || form.durationMonths < 1) errors.duration = "Durée minimum 1 mois";
  if (rentNum <= 0) errors.rent = "Loyer requis";
  if (form.paymentDay < 1 || form.paymentDay > 31) errors.paymentDay = "Jour 1-31";

  const canSave = Object.keys(errors).length === 0;

  function handleSave() {
    if (!canSave) return;

    // Persist tenant first
    let tenantId = form.tenantId;
    let tenantPayload;
    if (form.tenantMode === "new") {
      tenantId = nextTenantId();
      tenantPayload = {
        id: tenantId,
        name: form.newTenant.name,
        avatar: form.newTenant.avatar || deriveTenantAvatar(form.newTenant.name),
        color: form.newTenant.color,
        phone: form.newTenant.phone,
        email: form.newTenant.email,
        idDocType: form.newTenant.idDocType,
        idDocNumber: form.newTenant.idDocNumber,
        profession: form.newTenant.profession,
        monthlyIncome: Number(String(form.newTenant.monthlyIncome).replace(/[^0-9]/g,"")) || 0,
        // lease-linked fields:
        property: property?.name || "",
        propertyId: form.propertyId,
        unit: form.unit || "—",
        rent: rentNum,
        leaseStart: form.start,
        leaseEnd: end,
        status: "à jour",
        daysLate: 0,
        balance: 0,
      };
      upsertTenant(tenantPayload);
    } else {
      // Existing tenant: update their lease fields too
      const existing = getTenantById(tenantId);
      if (existing) {
        upsertTenant({
          ...existing,
          property: property?.name || existing.property,
          propertyId: form.propertyId,
          unit: form.unit || existing.unit,
          rent: rentNum,
          leaseStart: form.start,
          leaseEnd: end,
        });
      }
    }

    const payload = {
      ...form,
      id: form.id || nextLeaseId(),
      tenantId,
      rent: rentNum,
      charges: Number(form.charges) || 0,
      deposit: Number(form.deposit) || rentNum * 2,
      end,
      duration: Number(form.durationMonths),
      signedAt: form.signedAt || form.start,
      property: property?.name || "",
      commissionRate: commissionForLeaseType(form.type),
      _tenantName: tenantName,
    };
    onSave?.(payload);
  }

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

  if (!open) return null;

  const STEPS = [
    { id:"locataire", label:"Locataire" },
    { id:"bien",      label:"Bien & type" },
    { id:"duree",     label:"Durée & dates" },
    { id:"financier", label:"Financier" },
    { id:"clauses",   label:"Clauses" },
    { id:"recap",     label:"Récapitulatif" },
  ];

  return (
    <>
      <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:200, backdropFilter:"blur(3px)"}}/>
      <div role="dialog" style={{
        position:"fixed", inset:"4vh 8vw", zIndex:201, maxWidth:1180, margin:"0 auto",
        background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10,
        display:"flex", flexDirection:"column", overflow:"hidden",
        boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)", animation:"baill-pop 180ms ease-out",
      }}>
        <header style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", alignItems:"center", justifyContent:"space-between", background:"var(--d-panel)"}}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>
              {mode === "edit" ? "Modifier le bail" : "Nouveau bail"}
            </div>
            <div style={{fontSize:16, fontWeight:600, color:"var(--d-ink)", display:"flex", alignItems:"center", gap:10}}>
              {tenantName || "Locataire à choisir"} {property && <span style={{color:"var(--d-ink-3)", fontWeight:400}}>· {property.name}</span>}
              {mode === "edit" && form.id && (
                <span className="mono" style={{fontSize:10.5, padding:"2px 7px", borderRadius:3, background:"var(--d-elev)", color:"var(--d-ink-3)"}}>{form.id}</span>
              )}
            </div>
          </div>
          <button onClick={onClose} title="Fermer · Esc" style={{
            width:32, height:32, borderRadius:6, color:"var(--d-ink-3)", fontSize:18, display:"grid", placeItems:"center",
            background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer",
          }}>×</button>
        </header>

        <div style={{display:"grid", gridTemplateColumns:"220px 1fr", flex:1, minHeight:0}}>
          <nav style={{borderRight:"1px solid var(--d-line)", padding:"18px 14px", background:"var(--d-sunken)", display:"flex", flexDirection:"column", gap:2}}>
            {STEPS.map((s, i) => {
              const active = s.id === step;
              return (
                <button key={s.id} onClick={()=>setStep(s.id)} style={{
                  textAlign:"left", padding:"9px 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: "2px solid " + (active ? "var(--blue-d)" : "transparent"),
                  paddingLeft: 12, cursor:"pointer", display:"flex", alignItems:"center", justifyContent:"space-between", gap:8,
                }}>
                  <span><span className="mono" style={{color:"var(--d-ink-4)", marginRight:8, fontSize:10.5}}>{String(i+1).padStart(2,"0")}</span>{s.label}</span>
                </button>
              );
            })}

            <div style={{marginTop:14, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
              <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>Validation</div>
              {Object.keys(errors).length === 0 ? (
                <div style={{display:"flex", gap:6, alignItems:"center", fontSize:11.5, color:"var(--pos-d)"}}>
                  <span style={{width:6, height:6, borderRadius:50, background:"var(--pos-d)"}}/>Prêt à enregistrer
                </div>
              ) : (
                <div style={{display:"flex", flexDirection:"column", gap:4}}>
                  {Object.values(errors).map((e,i) => (
                    <div key={i} style={{fontSize:11, color:"var(--orange)", lineHeight:1.45, display:"flex", gap:6, alignItems:"flex-start"}}>
                      <span style={{color:"var(--orange)", marginTop:2}}>!</span><span>{e}</span>
                    </div>
                  ))}
                </div>
              )}
            </div>

            {/* Mini financial snapshot */}
            {rentNum > 0 && (
              <div style={{marginTop:10, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
                <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>Aperçu</div>
                <div style={{fontSize:11.5, color:"var(--d-ink-2)", fontFamily:"var(--font-mono)", display:"flex", flexDirection:"column", gap:3}}>
                  <div>Loyer {rentNum.toLocaleString("fr-FR")}</div>
                  <div style={{color:"var(--d-ink-3)"}}>+ ch. {Number(form.charges).toLocaleString("fr-FR")}</div>
                  <div style={{color:"var(--d-ink-3)"}}>DG {Number(form.deposit || rentNum*2).toLocaleString("fr-FR")}</div>
                  <div style={{color:"var(--pos-d)", marginTop:3, borderTop:"1px solid var(--d-line-2)", paddingTop:3}}>Annuel {fmtCompact(rentNum*12)} FCFA</div>
                </div>
              </div>
            )}
          </nav>

          <div style={{overflow:"auto", padding:"22px 28px"}}>
            {step === "locataire" && <BLocataireStep form={form} set={set} setNT={setNT} setCo={setCo} tenants={tenants}/>}
            {step === "bien"      && <BBienStep      form={form} set={set}/>}
            {step === "duree"     && <BDureeStep     form={form} set={set} end={end}/>}
            {step === "financier" && <BFinancierStep form={form} set={set}/>}
            {step === "clauses"   && <BClausesStep   form={form} set={set}/>}
            {step === "recap"     && <BRecapStep     form={form} property={property} tenantName={tenantName} end={end}/>}
          </div>
        </div>

        <footer style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-panel)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>
            {form.type} · {form.durationMonths} mois · début <b style={{color:"var(--d-ink-2)"}}>{form.start}</b>
            {end && <> → fin <b style={{color:"var(--d-ink-2)"}}>{end}</b></>}
          </div>
          <div style={{display:"flex", gap:8}}>
            <button onClick={onClose} className="btn btn-ghost">Annuler</button>
            {STEPS.findIndex(s => s.id === step) < STEPS.length - 1 && (
              <button onClick={() => {
                const idx = STEPS.findIndex(s => s.id === step);
                setStep(STEPS[idx + 1].id);
              }} className="btn btn-secondary">Suivant →</button>
            )}
            <button onClick={() => { set("leaseStatus", "brouillon"); handleSave(); }} className="btn btn-ghost">
              Enregistrer brouillon
            </button>
            <button onClick={() => { set("leaseStatus", "actif"); handleSave(); }} disabled={!canSave} className="btn btn-primary" style={{opacity: canSave ? 1 : 0.5, cursor: canSave ? "pointer" : "not-allowed"}}>
              {mode === "edit" ? "Enregistrer" : "Créer et signer"}
            </button>
          </div>
        </footer>
      </div>
    </>
  );
}

/* ─── STEP 1 · Locataire ─── */
function BLocataireStep({ form, set, setNT, setCo, tenants }) {
  return (
    <>
      <FormSection
        title="Locataire"
        subtitle="Choisissez un locataire déjà enregistré dans votre base ou créez-en un nouveau directement depuis ce bail. Un dossier locataire complet pourra être finalisé après signature."
      >
        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:10, marginBottom:14}}>
          {[
            { value:"existing", label:"Locataire existant", sub:`${tenants.length} dans votre base` },
            { value:"new",      label:"Nouveau locataire", sub:"Création express" },
          ].map(opt => {
            const active = form.tenantMode === opt.value;
            return (
              <button key={opt.value} onClick={()=>set("tenantMode", opt.value)} style={{
                padding:"14px 16px", textAlign:"left", cursor:"pointer",
                border:"1px solid " + (active ? "var(--blue-d)" : "var(--d-line)"),
                background: active ? "rgba(97,146,255,0.06)" : "var(--d-panel)", borderRadius:6,
              }}>
                <div style={{display:"flex", alignItems:"center", gap:10, marginBottom:4}}>
                  <span style={{width:14, height:14, borderRadius:50, flexShrink:0, border: "4px solid " + (active ? "var(--blue-d)" : "var(--d-line)")}}/>
                  <span style={{fontSize:13, fontWeight:600, color:"var(--d-ink)"}}>{opt.label}</span>
                </div>
                <div style={{fontSize:11, color:"var(--d-ink-3)", paddingLeft:24}}>{opt.sub}</div>
              </button>
            );
          })}
        </div>

        {form.tenantMode === "existing" && (
          <Field label="Locataire" required hint="Filtrez par nom ou ID">
            <Select
              value={form.tenantId}
              onChange={v=>set("tenantId", v)}
              placeholder="— Choisir un locataire —"
              options={tenants.map(t => ({
                value: t.id,
                label: `${t.name} · ${t.id} · ${t.property || "sans bien"}`,
              }))}
            />
          </Field>
        )}

        {form.tenantMode === "new" && (
          <>
            <FieldRow>
              <Field label="Nom complet" required>
                <Input value={form.newTenant.name} onChange={e=>setNT({name: e.target.value, avatar: deriveTenantAvatar(e.target.value)})}/>
              </Field>
              <Field label="Téléphone" required>
                <Input value={form.newTenant.phone} onChange={e=>setNT({phone: e.target.value})} icon="☎" placeholder="+221 77 ..."/>
              </Field>
            </FieldRow>
            <FieldRow>
              <Field label="Email" hint="Pour les quittances">
                <Input value={form.newTenant.email} onChange={e=>setNT({email: e.target.value})} icon="@"/>
              </Field>
              <Field label="Profession">
                <Input value={form.newTenant.profession} onChange={e=>setNT({profession: e.target.value})}/>
              </Field>
            </FieldRow>
            <FieldRow columns="180px 1fr 1fr">
              <Field label="Pièce d'identité">
                <Select value={form.newTenant.idDocType} onChange={v=>setNT({idDocType: v})} options={[
                  {value:"CNI", label:"CNI sénégalaise"},
                  {value:"Passeport", label:"Passeport"},
                  {value:"Séjour", label:"Carte de séjour"},
                ]}/>
              </Field>
              <Field label="N° de pièce">
                <Input value={form.newTenant.idDocNumber} onChange={e=>setNT({idDocNumber: e.target.value})} style={{fontFamily:"var(--font-mono)"}}/>
              </Field>
              <Field label="Revenu mensuel" hint="Optionnel · pour ratio loyer/revenus">
                <MoneyInput value={form.newTenant.monthlyIncome} onChange={e=>setNT({monthlyIncome: e.target.value.replace(/[^0-9]/g,"")})}/>
              </Field>
            </FieldRow>
            <Banner tone="info" title="Création express">
              Le locataire sera créé avec les informations minimales. Vous pourrez compléter son dossier (garants, pièces, fiches de paie) depuis la fiche locataire après signature du bail.
            </Banner>
          </>
        )}
      </FormSection>

      <FormSection
        title="Co-titulaire"
        subtitle="Ajoutez un conjoint ou partenaire co-titulaire du bail. Les deux signataires sont solidairement tenus du loyer (un seul foyer, une seule quittance). Pour des locataires distincts qui se partagent un logement, utilisez plutôt la colocation."
      >
        <Toggle
          label="Ajouter un co-titulaire (conjoint / partenaire)"
          hint="Deuxième signataire solidaire sur le même contrat"
          value={form.coTenant.enabled}
          onChange={v => setCo({ enabled: v })}
        />

        {form.coTenant.enabled && (
          <div style={{ marginTop: 6 }}>
            <Field label="Lien avec le locataire principal">
              <Segmented
                value={form.coTenant.relation}
                onChange={v => setCo({ relation: v })}
                options={[
                  { value: "conjoint", label: "Conjoint(e)" },
                  { value: "partenaire", label: "Partenaire" },
                  { value: "autre", label: "Autre" },
                ]}
              />
            </Field>
            <FieldRow>
              <Field label="Nom complet du co-titulaire" required>
                <Input value={form.coTenant.name} onChange={e => setCo({ name: e.target.value })} />
              </Field>
              <Field label="Téléphone" hint="Optionnel">
                <Input value={form.coTenant.phone} onChange={e => setCo({ phone: e.target.value })} icon="☎" placeholder="+221 77 ..." />
              </Field>
            </FieldRow>
            <FieldRow columns="180px 1fr 1fr">
              <Field label="Pièce d'identité">
                <Select value={form.coTenant.idDocType} onChange={v => setCo({ idDocType: v })} options={[
                  { value: "CNI", label: "CNI sénégalaise" },
                  { value: "Passeport", label: "Passeport" },
                  { value: "Séjour", label: "Carte de séjour" },
                ]} />
              </Field>
              <Field label="N° de pièce">
                <Input value={form.coTenant.idDocNumber} onChange={e => setCo({ idDocNumber: e.target.value })} style={{ fontFamily: "var(--font-mono)" }} />
              </Field>
              <Field label="Email" hint="Optionnel">
                <Input value={form.coTenant.email} onChange={e => setCo({ email: e.target.value })} icon="@" />
              </Field>
            </FieldRow>
            <Banner tone="info" title="Clause de solidarité">
              Une clause de solidarité sera insérée au contrat : chaque co-titulaire répond de la totalité des obligations du bail (loyer, charges, dépôt). La quittance reste unique, adressée au locataire principal.
            </Banner>
          </div>
        )}
      </FormSection>
    </>
  );
}

/* ─── STEP 2 · Bien ─── */
function BBienStep({ form, set }) {
  const property = MOCK.properties.find(p => p.id === form.propertyId);
  const vacantUnits = property ? property.units - property.occupied : 0;
  return (
    <>
      <FormSection
        title="Bien à louer"
        subtitle="Sélectionnez le bien du parc géré. Le bailleur est automatiquement déduit de la propriété."
      >
        <Field label="Bien" required>
          <Select
            value={form.propertyId}
            onChange={v=>set("propertyId", v)}
            placeholder="— Choisir un bien —"
            options={MOCK.properties.map(p => ({
              value: p.id,
              label: `${p.name} · ${p.address} · ${p.occupied}/${p.units} occupés`,
            }))}
          />
        </Field>

        {property && (
          <div style={{
            padding:"14px 16px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-sunken)",
            display:"grid", gridTemplateColumns:"1fr 1fr 1fr 1fr", gap:14, marginBottom:14,
          }}>
            <div>
              <div style={{fontSize:9.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:4}}>Bailleur</div>
              <div style={{fontSize:12.5, fontWeight:500}}>{property.owner}</div>
            </div>
            <div>
              <div style={{fontSize:9.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:4}}>Type</div>
              <div style={{fontSize:12.5, fontWeight:500}}>{property.type}</div>
            </div>
            <div>
              <div style={{fontSize:9.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:4}}>Lots vacants</div>
              <div style={{fontSize:12.5, fontWeight:500, color: vacantUnits === 0 ? "var(--orange)" : "var(--pos-d)"}}>
                {vacantUnits} / {property.units}
              </div>
            </div>
            <div>
              <div style={{fontSize:9.5, textTransform:"uppercase", letterSpacing:"0.06em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:4}}>Loyer moyen</div>
              <div className="mono" style={{fontSize:12.5, fontWeight:500}}>{fmtFCFA(Math.round(property.mrr / Math.max(1, property.occupied)))}</div>
            </div>
          </div>
        )}

        {property && property.units > 1 && (
          <Field label="Numéro de lot" required hint="Identifiant unique dans le bien (ex : A-203, 5, RDC-gauche)">
            <Input value={form.unit} onChange={e=>set("unit", e.target.value)} placeholder="A-203"/>
          </Field>
        )}
      </FormSection>

      <FormSection
        title="Type de bail"
        subtitle="Le type définit le cadre juridique applicable et les clauses standard insérées dans le contrat."
      >
        <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:8}}>
          {LEASE_TYPES.map(t => {
            const active = form.type === t.value;
            return (
              <button key={t.value} onClick={()=>set("type", t.value)} style={{
                padding:"12px 14px", textAlign:"left", cursor:"pointer",
                border:"1px solid " + (active ? "var(--blue-d)" : "var(--d-line)"),
                background: active ? "rgba(97,146,255,0.06)" : "var(--d-panel)", borderRadius:6,
              }}>
                <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:4}}>
                  <span style={{width:14, height:14, borderRadius:50, flexShrink:0, border: "4px solid " + (active ? "var(--blue-d)" : "var(--d-line)")}}/>
                  <span style={{fontSize:12.5, fontWeight:600, color:"var(--d-ink)"}}>{t.label}</span>
                </div>
                <div style={{fontSize:10.5, color:"var(--d-ink-3)", paddingLeft:22, lineHeight:1.4}}>{t.sub}</div>
              </button>
            );
          })}
        </div>
      </FormSection>

      <FormSection
        title="Location-vente (LOA)"
        subtitle="Cas particulier : le locataire pourra acquérir le bien en fin de contrat, une partie du loyer étant imputée au prix de rachat. Activez uniquement si un accord de location avec option d'achat est prévu — sinon laissez désactivé."
      >
        <div style={{
          display:"flex", justifyContent:"space-between", alignItems:"center", gap:16,
          padding:"4px 2px",
        }}>
          <div style={{fontSize:12, color:"var(--d-ink-3)", lineHeight:1.5, maxWidth:420}}>
            {form.isLOA
              ? "Bail en location-vente. Les paramètres de l'option d'achat se renseignent à l'étape Financier."
              : "Bail de location classique, sans option d'achat."}
          </div>
          <Segmented
            value={form.isLOA ? "yes" : "no"}
            onChange={v=>set("isLOA", v === "yes")}
            options={[{value:"no", label:"Location classique"}, {value:"yes", label:"Location-vente"}]}
          />
        </div>
        {form.isLOA && (
          <Banner tone="info" title="Option d'achat activée">
            Renseignez le prix de levée d'option, l'apport éventuel et la part du loyer imputée au capital dans l'étape <b>Financier</b>. Un encours de rachat sera suivi sur la fiche du contrat.
          </Banner>
        )}
      </FormSection>
    </>
  );
}

/* ─── STEP 3 · Durée ─── */
function BDureeStep({ form, set, end }) {
  return (
    <>
      <FormSection
        title="Durée et dates"
        subtitle="La durée standard d'un bail d'habitation est de 24 mois. Les baux meublés peuvent être conclus pour 12 mois. Les baux commerciaux suivent la durée minimale OHADA (2-3 ans selon nature)."
      >
        <FieldRow>
          <Field label="Date de prise d'effet" required>
            <Input type="date" value={form.start} onChange={e=>set("start", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
          <Field label="Date de fin (calculée)" hint="Modifiable via la durée">
            <Input value={end} readOnly style={{fontFamily:"var(--font-mono)", color:"var(--d-ink-3)", background:"var(--d-elev)"}}/>
          </Field>
        </FieldRow>

        <Field label="Durée du bail" required>
          <div style={{display:"flex", alignItems:"center", gap:14}}>
            <div className="seg" style={{display:"inline-flex"}}>
              {[12, 24, 36, 60].map(m => (
                <button key={m} className={"seg-btn"+(Number(form.durationMonths)===m?" active":"")} onClick={()=>set("durationMonths", m)}>{m} mois</button>
              ))}
            </div>
            <span style={{fontSize:11, color:"var(--d-ink-4)"}}>ou</span>
            <div style={{width:120}}>
              <Input
                value={form.durationMonths}
                onChange={e=>set("durationMonths", Number(e.target.value.replace(/[^0-9]/g,"")) || 0)}
                suffix="mois"
                style={{fontFamily:"var(--font-mono)", textAlign:"right"}}
              />
            </div>
          </div>
        </Field>

        <Field label="Renouvellement à l'échéance">
          <RadioGroup
            value={form.renewal}
            onChange={v=>set("renewal", v)}
            options={[
              {value:"tacite",  label:"Tacite reconduction", description:"Le bail se renouvelle automatiquement pour une durée identique, sauf préavis donné par l'une des parties."},
              {value:"expresse", label:"Renouvellement exprès", description:"Le bail prend fin à l'échéance et un nouveau bail doit être signé pour continuer la location."},
            ]}
          />
        </Field>
      </FormSection>
    </>
  );
}

/* ─── STEP 4 · Financier ─── */
function BFinancierStep({ form, set }) {
  const rent = Number(form.rent) || 0;
  const dep = Number(form.deposit) || rent * 2;
  return (
    <>
      <FormSection
        title="Loyer et charges"
        subtitle="Le loyer est exprimé hors charges. Les charges récupérables sont forfaitisées et figurent séparément sur les quittances."
      >
        <FieldRow columns="1fr 1fr 1fr">
          <Field label="Loyer mensuel" required>
            <MoneyInput value={form.rent || ""} onChange={e=>set("rent", e.target.value.replace(/[^0-9]/g,""))}/>
          </Field>
          <Field label="Charges récupérables" hint="Eau, gardiennage, parties communes">
            <MoneyInput value={form.charges} onChange={e=>set("charges", e.target.value.replace(/[^0-9]/g,""))}/>
          </Field>
          <Field label="Total quittance mensuelle" hint="Affiché au locataire">
            <Input value={(rent + Number(form.charges||0)).toLocaleString("fr-FR") + " FCFA"} readOnly style={{
              fontFamily:"var(--font-mono)", textAlign:"right", color:"var(--pos-d)",
              background:"var(--d-elev)", fontWeight:600,
            }}/>
          </Field>
        </FieldRow>

        <FieldRow columns="1fr 1fr 1fr">
          <Field label="Dépôt de garantie" required hint="2 mois de loyer · standard">
            <MoneyInput value={form.deposit || ""} onChange={e=>set("deposit", e.target.value.replace(/[^0-9]/g,""))} placeholder={String(rent * 2)}/>
          </Field>
          <Field label="Soit en mois de loyer">
            <Input value={rent > 0 ? (dep / rent).toFixed(1).replace(".",",") + " mois" : "—"} readOnly style={{
              fontFamily:"var(--font-mono)", color:"var(--d-ink-3)", background:"var(--d-elev)",
            }}/>
          </Field>
          <Field label="Jour de paiement" required hint="Du mois">
            <Input
              value={form.paymentDay}
              onChange={e=>set("paymentDay", Number(e.target.value.replace(/[^0-9]/g,"")) || 1)}
              suffix="du mois"
              style={{fontFamily:"var(--font-mono)", textAlign:"right"}}
            />
          </Field>
        </FieldRow>

        <Field label="Mode de paiement par défaut">
          <Segmented
            value={form.paymentMode}
            onChange={v=>set("paymentMode", v)}
            options={[
              {value:"wave", label:"Wave"},
              {value:"om",   label:"Orange Money"},
              {value:"virement", label:"Virement bancaire"},
              {value:"especes", label:"Espèces"},
            ]}
          />
        </Field>
      </FormSection>

      <FormSection
        title="Révision du loyer"
        subtitle="Mécanisme d'indexation annuelle appliqué à la date anniversaire. L'IRL est publié par l'ANSD."
      >
        <RadioGroup
          value={form.indexation}
          onChange={v=>set("indexation", v)}
          options={[
            {value:"irl",       label:"IRL Sénégal — recommandé", description:"Indice de Référence des Loyers publié trimestriellement par l'ANSD. Limite l'augmentation à l'évolution officielle des prix locatifs."},
            {value:"custom",    label:"Taux fixe contractuel",      description:"Pourcentage figé négocié entre les parties."},
            {value:"none",      label:"Pas d'indexation",            description:"Le loyer reste figé sur toute la durée du bail."},
          ]}
        />

        {form.indexation === "custom" && (
          <Field label="Taux annuel d'augmentation" hint="% appliqué à la date anniversaire">
            <Input
              value={form.customIndex}
              onChange={e=>set("customIndex", Number(e.target.value.replace(",",".").replace(/[^0-9.]/g,"")) || 0)}
              suffix="%"
              style={{fontFamily:"var(--font-mono)", textAlign:"right", maxWidth:160}}
            />
          </Field>
        )}
      </FormSection>

      <FormSection
        title="Honoraires d'agence"
        subtitle="Frais de rédaction et d'état des lieux. Le partage entre bailleur et preneur est libre dans la limite légale."
      >
        <RadioGroup
          layout="row"
          value={form.honoraires}
          onChange={v=>set("honoraires", v)}
          options={[
            {value:"preneur",  label:"À la charge du preneur"},
            {value:"bailleur", label:"À la charge du bailleur"},
            {value:"partage",  label:"Partagés 50/50"},
          ]}
        />
      </FormSection>

      <BCommissionSection form={form}/>

      {form.isLOA && <BLoaSection form={form} set={set}/>}
    </>
  );
}

/* ─── Commission de gestion selon le type (nue vs meublée) ─── */
function BCommissionSection({ form }) {
  const rent = Number(form.rent) || 0;
  const isMeuble = /meubl/i.test(form.type || "");
  const rate = commissionForLeaseType(form.type);
  const amount = Math.round(rent * rate);
  const net = rent - amount;
  return (
    <FormSection
      title="Commission de gestion"
      subtitle="Le taux est déterminé automatiquement par le type de location. Le meublé est commissionné plus haut que le nu. Modifiable dans Paramètres → Commissions."
    >
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }}>
        <span className="chip" style={{ background: isMeuble ? "rgba(63,181,131,0.14)" : "var(--d-elev)", color: isMeuble ? "var(--pos-d)" : "var(--d-ink-2)", border: "1px solid " + (isMeuble ? "rgba(63,181,131,0.3)" : "var(--d-line)") }}>
          {isMeuble ? "Location meublée" : "Location nue"} · {form.type}
        </span>
        <span style={{ fontSize: 11.5, color: "var(--d-ink-3)" }}>→ taux appliqué <b className="mono" style={{ color: "var(--d-ink)" }}>{(rate * 100).toFixed(1).replace(".", ",")}%</b></span>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 0, border: "1px solid var(--d-line)", borderRadius: 6, overflow: "hidden" }}>
        {[
          ["Loyer mensuel", rent, "var(--d-ink)"],
          ["Commission agence", amount, "var(--d-ink-2)"],
          ["Net propriétaire / mois", net, "var(--pos-d)"],
        ].map(([k, v, col], i) => (
          <div key={i} style={{ padding: "12px 14px", borderRight: i < 2 ? "1px solid var(--d-line-2)" : "none" }}>
            <div style={{ fontSize: 9.5, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--d-ink-4)", fontWeight: 600, marginBottom: 5 }}>{k}</div>
            <div className="mono" style={{ fontSize: 14, fontWeight: 600, color: col }}>{v.toLocaleString("fr-FR")}</div>
            <div style={{ fontSize: 9.5, color: "var(--d-ink-4)", marginTop: 1 }}>FCFA</div>
          </div>
        ))}
      </div>
    </FormSection>
  );
}

/* ─── Sous-section LOA (uniquement si form.isLOA) ─── */
function BLoaSection({ form, set }) {
  const c = loaCompute(form);
  return (
    <FormSection
      title="Option d'achat · location-vente"
      subtitle="Paramètres de la LOA. Une part du loyer mensuel est imputée au capital de rachat ; ajoutée à l'apport initial, elle constitue l'encours déjà acquis lorsque le locataire lève l'option."
    >
      <FieldRow columns="1fr 1fr">
        <Field label="Prix de levée d'option" required hint="Valeur de rachat du bien en fin de contrat">
          <MoneyInput value={form.loaSalePrice || ""} onChange={e=>set("loaSalePrice", e.target.value.replace(/[^0-9]/g,""))}/>
        </Field>
        <Field label="Apport / prime d'option" hint="Versé à l'entrée, imputé au rachat">
          <MoneyInput value={form.loaUpfront || ""} onChange={e=>set("loaUpfront", e.target.value.replace(/[^0-9]/g,""))}/>
        </Field>
      </FieldRow>

      <FieldRow columns="1fr 1fr">
        <Field label="Part du loyer imputée au capital" required hint="% du loyer mensuel qui constitue le capital de rachat">
          <Input
            value={form.loaCapitalShare}
            onChange={e=>set("loaCapitalShare", Math.max(0, Math.min(100, Number(e.target.value.replace(/[^0-9]/g,"")) || 0)))}
            suffix="%"
            style={{fontFamily:"var(--font-mono)", textAlign:"right"}}
          />
        </Field>
        <Field label="Échéance de levée d'option" required hint="Nombre de mois avant la possibilité de racheter">
          <Input
            value={form.loaOptionMonths}
            onChange={e=>set("loaOptionMonths", Math.max(0, Number(e.target.value.replace(/[^0-9]/g,"")) || 0))}
            suffix="mois"
            style={{fontFamily:"var(--font-mono)", textAlign:"right"}}
          />
        </Field>
      </FieldRow>

      {/* Aperçu live de l'encours de rachat */}
      <div style={{border:"1px solid var(--d-line)", borderRadius:8, background:"var(--d-sunken)", overflow:"hidden"}}>
        <div style={{padding:"12px 16px", borderBottom:"1px solid var(--d-line-2)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase"}}>Simulation du rachat</div>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>à la levée d'option ({c.months} mois)</div>
        </div>
        <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:0}}>
          {[
            ["Capital / mois", c.capitalPerMonth, "var(--d-ink)"],
            ["Capital cumulé", c.capitalOverTerm, "var(--d-ink)"],
            ["Total imputé au rachat", c.accumulated, "var(--pos-d)"],
            ["Solde à la levée", c.residual, "var(--orange)"],
          ].map(([k,v,col], i) => (
            <div key={i} style={{padding:"12px 14px", borderRight: i < 3 ? "1px solid var(--d-line-2)" : "none"}}>
              <div style={{fontSize:9.5, textTransform:"uppercase", letterSpacing:"0.05em", color:"var(--d-ink-4)", fontWeight:600, marginBottom:5}}>{k}</div>
              <div className="mono" style={{fontSize:13.5, fontWeight:600, color:col}}>{v.toLocaleString("fr-FR")}</div>
              <div style={{fontSize:9.5, color:"var(--d-ink-4)", marginTop:1}}>FCFA</div>
            </div>
          ))}
        </div>
        <div style={{padding:"12px 16px", borderTop:"1px solid var(--d-line-2)"}}>
          <div style={{display:"flex", justifyContent:"space-between", fontSize:11, color:"var(--d-ink-3)", marginBottom:6}}>
            <span>Couverture du prix de rachat à la levée</span>
            <span className="mono" style={{color:"var(--d-ink-2)", fontWeight:600}}>{c.coverage}%</span>
          </div>
          <div style={{height:8, borderRadius:4, background:"var(--d-elev)", overflow:"hidden"}}>
            <div style={{width:`${c.coverage}%`, height:"100%", background: c.coverage >= 100 ? "var(--pos-d)" : "var(--blue-d)", borderRadius:4, transition:"width 200ms ease"}}/>
          </div>
        </div>
      </div>

      <Banner tone="warn" title="Périmètre version légère">
        Logestimmo suit l'encours de rachat de façon indicative. La comptabilisation détaillée (ventilation loyer/capital en écritures, prime d'option, transfert de propriété) reste à traiter hors plateforme avec votre comptable.
      </Banner>
    </FormSection>
  );
}

/* ─── STEP 5 · Clauses ─── */
function BClausesStep({ form, set }) {
  return (
    <>
      <FormSection
        title="Clauses particulières"
        subtitle="Modulations du contrat type. Les choix ci-dessous activent ou désactivent les clauses correspondantes dans la version finale du bail."
      >
        <Field label="Destination des lieux">
          <RadioGroup
            value={form.destination}
            onChange={v=>set("destination", v)}
            options={[
              {value:"habitation", label:"Habitation principale exclusive"},
              {value:"mixte",      label:"Mixte habitation / professionnel"},
              {value:"pro",        label:"Professionnel uniquement"},
            ]}
            layout="row"
          />
        </Field>

        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:18, marginTop:6}}>
          <Field label="Sous-location">
            <Segmented value={form.subletAllowed ? "yes" : "no"} onChange={v=>set("subletAllowed", v === "yes")} options={[{value:"no", label:"Interdite"}, {value:"yes", label:"Autorisée"}]}/>
          </Field>
          <Field label="Animaux">
            <Segmented value={form.petsAllowed ? "yes" : "no"} onChange={v=>set("petsAllowed", v === "yes")} options={[{value:"no", label:"Non"}, {value:"yes", label:"Oui"}]}/>
          </Field>
          <Field label="Assurance habitation">
            <Segmented value={form.insuranceRequired ? "yes" : "no"} onChange={v=>set("insuranceRequired", v === "yes")} options={[{value:"yes", label:"Obligatoire"}, {value:"no", label:"Optionnelle"}]}/>
          </Field>
          <Field label="Préavis de résiliation">
            <div style={{display:"flex", alignItems:"center", gap:10}}>
              <div className="seg" style={{display:"inline-flex"}}>
                {[1, 2, 3].map(m => (
                  <button key={m} className={"seg-btn"+(Number(form.preavis)===m?" active":"")} onClick={()=>set("preavis", m)}>{m} mois</button>
                ))}
              </div>
            </div>
          </Field>
        </div>
      </FormSection>

      <FormSection
        title="Notes additionnelles"
        subtitle="Clauses spéciales, équipements inclus (climatisation, électroménager pour les meublés), travaux à la charge du preneur, etc. Apparaissent en clause finale du bail."
      >
        <Textarea
          rows={5}
          value={form.notes}
          onChange={e=>set("notes", e.target.value)}
          placeholder="Ex : climatisations split fournies dans chaque pièce · entretien à la charge du preneur · jardinier 2×/mois inclus dans les charges…"
        />
      </FormSection>
    </>
  );
}

/* ─── STEP 6 · Récap (paper preview) ─── */
function BRecapStep({ form, property, tenantName, end }) {
  const rent = Number(form.rent) || 0;
  const dep = Number(form.deposit) || rent * 2;
  const indexationLabel = form.indexation === "irl" ? "Annuelle · IRL Sénégal (ANSD)"
    : form.indexation === "custom" ? `Annuelle · +${form.customIndex}%`
    : "Aucune";

  return (
    <>
      <FormSection
        title="Récapitulatif du bail"
        subtitle="Vérifiez les termes avant de générer le contrat. Vous pourrez le télécharger en PDF et l'envoyer à signature électronique à la prochaine étape."
      >
        <div style={{
          background:"#FAFAF7", color:"#1A1A1A", borderRadius:6, padding:"22px 24px",
          fontFamily:"var(--font-sans)", border:"1px solid #E4E4DE",
        }}>
          <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 · {form.type.toLowerCase()}{form.isLOA ? " · location-vente" : ""}
              </div>
              <div style={{fontSize:14, fontWeight:600, color:"#1A1A1A", letterSpacing:"-0.01em"}}>
                {tenantName || "—"} · {property?.name || "—"}{form.unit ? ` · lot ${form.unit}` : ""}
              </div>
              <div className="mono" style={{fontSize:10, color:"#7B7B73", marginTop:3}}>
                Brouillon · {form.start} → {end}
              </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>

          <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}}>{form.coTenant.enabled ? "Preneurs (co-titulaires)" : "Preneur"}</div>
              <div style={{fontSize:12, fontWeight:600, color:"#1A1A1A"}}>{tenantName || "—"}</div>
              <div style={{fontSize:10.5, color:"#5B5B53"}}>
                {form.tenantMode === "new" ? `${form.newTenant.phone || "—"} · ${form.newTenant.email || ""}` : (getTenantById(form.tenantId)?.phone || "")}
              </div>
              {form.coTenant.enabled && (
                <div style={{marginTop:6, paddingTop:6, borderTop:"1px dashed #E4E4DE"}}>
                  <div style={{fontSize:12, fontWeight:600, color:"#1A1A1A"}}>{form.coTenant.name || "—"} <span style={{fontWeight:400, color:"#7B7B73", fontSize:10.5}}>· {form.coTenant.relation}</span></div>
                  <div style={{fontSize:10.5, color:"#5B5B53"}}>{form.coTenant.phone || "—"}{form.coTenant.email ? " · " + form.coTenant.email : ""}</div>
                </div>
              )}
            </div>
          </div>

          <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:0, marginBottom:14, border:"1px solid #E4E4DE", borderRadius:4, overflow:"hidden"}}>
            {[
              ["Loyer mensuel", rent.toLocaleString("fr-FR") + " FCFA"],
              ["Charges", Number(form.charges).toLocaleString("fr-FR") + " FCFA"],
              ["Dépôt garantie", dep.toLocaleString("fr-FR") + " FCFA"],
              ["Jour de paiement", `Le ${form.paymentDay} de chaque mois`],
              ["Début", form.start],
              ["Fin", end],
              ["Durée", `${form.durationMonths} mois`],
              ["Indexation", indexationLabel],
            ].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: /\d/.test(v) && k !== "Jour de paiement" && k !== "Indexation" ? "var(--font-mono)" : "var(--font-sans)"}}>{v}</div>
              </div>
            ))}
          </div>

          <div>
            <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:8}}>Clauses particulières</div>
            <ul style={{margin:0, paddingLeft:18, fontSize:10.5, color:"#3A3A33", lineHeight:1.6}}>
              <li>Destination : {form.destination === "habitation" ? "habitation principale exclusive" : form.destination === "mixte" ? "mixte habitation/professionnel" : "professionnel uniquement"}</li>
              <li>Sous-location : {form.subletAllowed ? "autorisée avec accord écrit" : "interdite"}</li>
              <li>Animaux : {form.petsAllowed ? "autorisés" : "non autorisés"}</li>
              <li>Assurance habitation : {form.insuranceRequired ? "obligatoire, attestation annuelle exigée" : "optionnelle"}</li>
              <li>Préavis de résiliation côté preneur : {form.preavis} mois</li>
              <li>Renouvellement : {form.renewal === "tacite" ? "tacite reconduction" : "exprès, par signature d'un nouveau bail"}</li>
              <li>Honoraires d'agence : {form.honoraires === "preneur" ? "à la charge du preneur" : form.honoraires === "bailleur" ? "à la charge du bailleur" : "partagés 50/50"}</li>
              {form.coTenant.enabled && <li><b>Solidarité.</b> {form.coTenant.name || "Le co-titulaire"} ({form.coTenant.relation}) est co-titulaire solidaire : chaque preneur répond de la totalité des obligations du bail.</li>}
              {form.notes && <li>{form.notes}</li>}
            </ul>
          </div>

          {form.isLOA && (() => {
            const c = loaCompute(form);
            return (
              <div style={{marginTop:14, paddingTop:14, borderTop:"1px solid #E4E4DE"}}>
                <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:8}}>Option d'achat (location-vente)</div>
                <ul style={{margin:0, paddingLeft:18, fontSize:10.5, color:"#3A3A33", lineHeight:1.6}}>
                  <li>Le preneur dispose d'une option d'achat du bien, exerçable à partir du <b>{c.months}<sup>e</sup> mois</b>.</li>
                  <li>Prix de levée d'option : <b>{c.sale.toLocaleString("fr-FR")} FCFA</b>{c.upfront > 0 && <> · apport initial de <b>{c.upfront.toLocaleString("fr-FR")} FCFA</b></>}.</li>
                  <li><b>{c.share}%</b> du loyer mensuel (soit {c.capitalPerMonth.toLocaleString("fr-FR")} FCFA) est imputé au capital de rachat.</li>
                  <li>Encours acquis à la levée : <b>{c.accumulated.toLocaleString("fr-FR")} FCFA</b> · solde résiduel <b>{c.residual.toLocaleString("fr-FR")} FCFA</b> ({c.coverage}% du prix couvert).</li>
                </ul>
              </div>
            );
          })()}
        </div>

        <Banner tone="pos" title="Prêt à signer">
          Cliquez sur <b>Créer et signer</b> pour générer le bail définitif et l'envoyer en signature électronique au locataire. Le statut passera à « actif » dès la signature reçue.
        </Banner>
      </FormSection>
    </>
  );
}

Object.assign(window, {
  BailFormModal, upsertLease, useExtraLeases, nextLeaseId,
  makeEmptyLease, loaCompute,
});

/* ╚══ end pages-bail-form.jsx ══╝ */

/* ╔══ pages-contracts.jsx ══╗ */
/* global React, MOCK, BAILLEURS, fmtFCFA, fmtCompact, loaCompute,
          BailFormModal, upsertLease, useExtraLeases, Banner */

/* ─────────────────────────────────────────────────────────
   CONTRATS & LOA — registre des contrats + suivi location-vente
   Cible : hub documentaire des contrats (baux, avenants, mandats)
   avec une section dédiée à la LOA (version légère) :
   suivi de l'encours de rachat + documentation.
   ───────────────────────────────────────────────────────── */

const CT_TODAY = new Date("2026-05-18");

function ct_monthsBetween(fromISO, to) {
  const a = new Date(fromISO);
  if (isNaN(a)) return 0;
  return Math.max(0, (to.getFullYear() - a.getFullYear()) * 12 + (to.getMonth() - a.getMonth()));
}

function ct_addMonths(iso, months) {
  const d = new Date(iso);
  if (isNaN(d)) return "—";
  d.setMonth(d.getMonth() + Number(months || 0));
  return d.toISOString().slice(0, 10);
}

/* Exemples de contrats LOA (cas de niche — 2 dossiers de démonstration) */
const CT_LOA_EXAMPLES = [
  {
    id: "LOA-2401", isLOA: true,
    tenant: { name: "Mamadou Ndiaye", avatar: "MN", phone: "+221 77 614 90 22" },
    property: "Rés. Les Almadies", unit: "B-12", owner: "Horizon SCI",
    start: "2024-02-01", rent: 450_000, type: "Résidentiel",
    loaSalePrice: 38_000_000, loaUpfront: 4_000_000, loaCapitalShare: 35, loaOptionMonths: 60,
    coTenant: { enabled: true, name: "Awa Ndiaye", relation: "conjoint", phone: "+221 78 145 60 33", email: "" },
  },
  {
    id: "LOA-2502", isLOA: true,
    tenant: { name: "Sokhna Mbaye", avatar: "SM", phone: "+221 78 230 71 45" },
    property: "Imm. Sacré-Cœur 3", unit: "4", owner: "Katos Consulting",
    start: "2025-03-01", rent: 320_000, type: "Meublé",
    loaSalePrice: 24_000_000, loaUpfront: 2_500_000, loaCapitalShare: 30, loaOptionMonths: 48,
  },
];

/* Progression d'une LOA à la date courante */
function ct_loaProgress(lease) {
  const c = loaCompute(lease); // chiffres "à la levée d'option"
  const elapsed = Math.min(c.months, ct_monthsBetween(lease.start, CT_TODAY));
  const accToDate = c.upfront + c.capitalPerMonth * elapsed;
  const covToDate = c.sale > 0 ? Math.min(100, Math.round(accToDate / c.sale * 100)) : 0;
  const residualToDate = Math.max(0, c.sale - accToDate);
  const optionDate = ct_addMonths(lease.start, c.months);
  const exercisable = elapsed >= c.months;
  return { ...c, elapsed, accToDate, covToDate, residualToDate, optionDate, exercisable };
}

/* ═══════════════════════════════════════════════════════════
   PAGE
   ═══════════════════════════════════════════════════════════ */
function ContractsPage({ onOpenTenant, onOpenBailleur, onOpenProperty }) {
  const extra = useExtraLeases();
  const [tab, setTab] = React.useState("all");
  const [q, setQ] = React.useState("");
  const [modal, setModal] = React.useState(null);

  // LOA = exemples + tout bail LOA créé via le formulaire
  const userLoa = extra
    .filter(l => l.isLOA)
    .map(l => ({
      id: l.id, isLOA: true,
      tenant: { name: l._tenantName || "Locataire", avatar: (l._tenantName || "L").split(" ").map(s => s[0]).join("").slice(0, 2).toUpperCase(), phone: "" },
      property: l.property, unit: l.unit, owner: "—",
      start: l.start, rent: Number(l.rent) || 0, type: l.type,
      loaSalePrice: l.loaSalePrice, loaUpfront: l.loaUpfront, loaCapitalShare: l.loaCapitalShare, loaOptionMonths: l.loaOptionMonths,
      coTenant: l.coTenant,
    }));
  const loaList = [...userLoa, ...CT_LOA_EXAMPLES];

  // Registre documentaire : baux signés + avenants + LOA + mandats
  const register = ct_buildRegister(loaList);

  const counts = {
    all: register.length,
    bail: register.filter(r => r.kind === "bail").length,
    loa: loaList.length,
    avenant: register.filter(r => r.kind === "avenant").length,
    mandat: register.filter(r => r.kind === "mandat").length,
  };

  // Agrégats LOA
  const loaTotalSale = loaList.reduce((s, l) => s + (Number(l.loaSalePrice) || 0), 0);

  const filtered = register.filter(r => {
    if (tab !== "all" && r.kind !== tab) return false;
    if (q && !`${r.title} ${r.party} ${r.id} ${r.property || ""}`.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Contrats &amp; LOA</h1>
          <div className="page-sub">{register.length} documents contractuels · {loaList.length} contrats en location-vente</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost">Exporter le registre</button>
          <button className="btn btn-primary" onClick={() => setModal({ mode: "create", initial: null })}>+ Nouveau contrat</button>
        </div>
      </div>

      {/* KPIs */}
      <div className="kpis">
        <div className="kpi primary">
          <div className="kpi-label"><span>Contrats actifs</span></div>
          <div className="kpi-value num">{counts.bail}<span className="kpi-unit">baux</span></div>
          <div className="kpi-meta"><span>{counts.avenant} avenants · {counts.mandat} mandats</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Location-vente (LOA)</span></div>
          <div className="kpi-value num">{loaList.length}<span className="kpi-unit">contrats</span></div>
          <div className="kpi-meta"><span>Suivi de l'encours de rachat</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Valeur de rachat LOA</span></div>
          <div className="kpi-value num">{(loaTotalSale / 1_000_000).toFixed(1).replace(".", ",")}<span className="kpi-unit">M FCFA</span></div>
          <div className="kpi-meta"><span>Cumul des prix de levée d'option</span></div>
        </div>
        <div className="kpi">
          <div className="kpi-label"><span>Signatures en cours</span></div>
          <div className="kpi-value num">2<span className="kpi-unit">en attente</span></div>
          <div className="kpi-meta"><span>Relance automatique J+3</span></div>
        </div>
      </div>

      {/* Tabs */}
      <div className="tabs" style={{ marginTop: 18 }}>
        {[
          ["all", "Tous", counts.all],
          ["bail", "Baux", counts.bail],
          ["loa", "Location-vente (LOA)", counts.loa],
          ["avenant", "Avenants", counts.avenant],
          ["mandat", "Mandats de gestion", counts.mandat],
        ].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 === "loa" ? (
        <LoaSpotlight loaList={loaList} onNew={() => setModal({ mode: "create", initial: { isLOA: true } })} />
      ) : (
        <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="Contrat, partie, bien, référence…" />
            </div>
            <div className="filter-spacer" />
            <button className="filter-chip">Trier ▾</button>
          </div>
          <div className="table-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{ width: 110 }}>Référence</th>
                  <th>Document</th>
                  <th>Partie</th>
                  <th>Bien</th>
                  <th>Date</th>
                  <th className="right">Montant</th>
                  <th style={{ width: 120 }}>Statut</th>
                </tr>
              </thead>
              <tbody>
                {filtered.length === 0 && <tr><td colSpan={7} className="muted" style={{ textAlign: "center", padding: 28 }}>Aucun document ne correspond.</td></tr>}
                {filtered.map(r => (
                  <tr key={r.id}>
                    <td className="mono muted" style={{ fontSize: 11.5 }}>{r.id}</td>
                    <td>
                      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                        <ContractIcon kind={r.kind} />
                        <div>
                          <div style={{ fontWeight: 500 }}>{r.title}</div>
                          <div className="muted" style={{ fontSize: 11 }}>{CT_KIND_LABEL[r.kind]}</div>
                        </div>
                      </div>
                    </td>
                    <td className="muted" style={{ fontSize: 12 }}>{r.party}</td>
                    <td className="muted" style={{ fontSize: 11.5 }}>{r.property || "—"}</td>
                    <td className="mono muted" style={{ fontSize: 11.5 }}>{r.date}</td>
                    <td className="right mono" style={{ fontSize: 12 }}>{r.amountLabel}</td>
                    <td><ContractChip status={r.status} /></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>{" "}
            Le registre regroupe tous les documents contractuels générés par Logestimmo. Les <b style={{ color: "var(--d-ink-2)" }}>contrats de bail</b> et <b style={{ color: "var(--d-ink-2)" }}>avenants</b> sont aussi accessibles depuis la page Baux ; les <b style={{ color: "var(--d-ink-2)" }}>mandats</b> depuis chaque fiche bailleur.
          </div>
        </div>
      )}

      {/* Documentation LOA — toujours visible */}
      <LoaDoc />

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

/* ─── Registre : construction des lignes ─── */
const CT_KIND_LABEL = { bail: "Contrat de bail", loa: "Contrat de location-vente", avenant: "Avenant", mandat: "Mandat de gestion" };

function ct_buildRegister(loaList) {
  const rows = [];

  // Baux signés (échantillon depuis les locataires)
  MOCK.tenants.slice(0, 8).forEach((t, i) => {
    rows.push({
      kind: "bail",
      id: "BX-" + t.id.replace("T-", ""),
      title: `Bail · ${t.name}`,
      party: t.name,
      property: `${t.property}${t.unit && t.unit !== "—" ? " · lot " + t.unit : ""}`,
      date: t.leaseStart,
      amountLabel: fmtFCFA(t.rent) + "/m",
      status: i === 6 ? "à renouveler" : "actif",
    });
  });

  // LOA
  loaList.forEach(l => {
    rows.push({
      kind: "loa",
      id: l.id,
      title: `Location-vente · ${l.tenant.name}`,
      party: l.tenant.name,
      property: `${l.property}${l.unit ? " · lot " + l.unit : ""}`,
      date: l.start,
      amountLabel: fmtCompact(l.loaSalePrice),
      status: "actif",
    });
  });

  // Avenants (synthétiques)
  rows.push(
    { kind: "avenant", id: "AV-2611", title: "Avenant d'indexation · Aïda Touré", party: "Aïda Touré", property: "Rés. Plateau · lot 7", date: "2026-01-12", amountLabel: "+1,8%", status: "signé" },
    { kind: "avenant", id: "AV-2608", title: "Avenant de renouvellement · O. Sy", party: "Ousmane Sy", property: "Imm. Fann Hock · lot 3", date: "2025-11-30", amountLabel: "24 mois", status: "en signature" },
  );

  // Mandats de gestion (depuis les bailleurs)
  if (typeof BAILLEURS !== "undefined") {
    BAILLEURS.slice(0, 5).forEach(b => {
      rows.push({
        kind: "mandat",
        id: "MG-" + b.id.replace("B-", ""),
        title: `Mandat · ${b.name}`,
        party: b.name,
        property: b.entity,
        date: b.contractDate,
        amountLabel: Math.round(b.commissionRate * 100) + "% comm.",
        status: "actif",
      });
    });
  }

  return rows;
}

/* ─── Icône par type de contrat ─── */
function ContractIcon({ kind }) {
  const map = {
    bail: ["B", "var(--blue-d)"],
    loa: ["V", "var(--pos-d)"],
    avenant: ["A", "var(--warn-d)"],
    mandat: ["M", "var(--d-ink-3)"],
  };
  const [ch, col] = map[kind] || ["•", "var(--d-ink-3)"];
  return (
    <span style={{
      width: 26, height: 26, borderRadius: 6, flexShrink: 0,
      display: "grid", placeItems: "center", fontSize: 11, fontWeight: 700,
      fontFamily: "var(--font-mono)", color: col,
      background: "color-mix(in srgb, " + col + " 14%, transparent)",
      border: "1px solid color-mix(in srgb, " + col + " 30%, transparent)",
    }}>{ch}</span>
  );
}

function ContractChip({ status }) {
  const map = {
    "actif": ["pos", "actif"],
    "signé": ["pos", "signé"],
    "à renouveler": ["warn", "à renouveler"],
    "en signature": ["neutral", "en signature"],
  };
  const [cls, label] = map[status] || ["neutral", status];
  return <span className={`chip ${cls}`}>{label}</span>;
}

/* ═══════════════════════════════════════════════════════════
   LOA SPOTLIGHT — cartes de suivi de l'encours de rachat
   ═══════════════════════════════════════════════════════════ */
function LoaSpotlight({ loaList, onNew }) {
  return (
    <div className="panel" style={{ overflow: "hidden" }}>
      <div className="panel-head">
        <div>
          <h3 className="panel-title">Location-vente · encours de rachat</h3>
          <div className="panel-sub">Suivi indicatif de la part de loyer capitalisée vers l'acquisition du bien</div>
        </div>
        <button className="btn btn-secondary" style={{ padding: "5px 10px", fontSize: 11.5 }} onClick={onNew}>+ Nouveau contrat LOA</button>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 1, background: "var(--d-line-2)" }}>
        {loaList.map(l => <LoaCard key={l.id} lease={l} />)}
        {loaList.length === 0 && (
          <div style={{ padding: 32, textAlign: "center", color: "var(--d-ink-3)", fontSize: 12.5, gridColumn: "1 / -1", background: "var(--d-panel)" }}>
            Aucun contrat en location-vente. Créez un bail et activez l'option « Location-vente » à l'étape Bien &amp; type.
          </div>
        )}
      </div>
    </div>
  );
}

function LoaCard({ lease }) {
  const p = ct_loaProgress(lease);
  const [showDoc, setShowDoc] = React.useState(false);
  return (
    <div style={{ background: "var(--d-panel)", padding: "18px 20px" }}>
      {/* En-tête */}
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 14 }}>
        <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
          <div className="avatar lg" style={{ background: "var(--pos-d)" }}>{lease.tenant.avatar}</div>
          <div>
            <div style={{ fontWeight: 600, fontSize: 13.5 }}>{lease.tenant.name}{lease.coTenant && lease.coTenant.enabled ? <span style={{ fontWeight: 400, color: "var(--d-ink-3)", fontSize: 11.5 }}> &amp; {lease.coTenant.name}</span> : null}</div>
            <div style={{ fontSize: 11.5, color: "var(--d-ink-3)" }}>{lease.property}{lease.unit ? " · lot " + lease.unit : ""}</div>
            <div className="mono" style={{ fontSize: 10.5, color: "var(--d-ink-4)", marginTop: 2 }}>{lease.id} · loyer {fmtFCFA(lease.rent)}{lease.coTenant && lease.coTenant.enabled ? " · co-titulaires" : ""}</div>
          </div>
        </div>
        {p.exercisable
          ? <span className="chip pos">option exerçable</span>
          : <span className="chip neutral">en cours</span>}
      </div>

      {/* Barre de progression */}
      <div style={{ marginBottom: 12 }}>
        <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--d-ink-3)", marginBottom: 6 }}>
          <span>Capital acquis · {p.elapsed}/{p.months} mois</span>
          <span className="mono" style={{ color: "var(--d-ink)", fontWeight: 600 }}>{p.covToDate}%</span>
        </div>
        <div style={{ height: 10, borderRadius: 5, background: "var(--d-elev)", overflow: "hidden", position: "relative" }}>
          <div style={{ width: `${p.covToDate}%`, height: "100%", background: "var(--pos-d)", borderRadius: 5, transition: "width 200ms ease" }} />
          {/* repère levée d'option (100%) */}
          <span style={{ position: "absolute", right: 0, top: 0, bottom: 0, width: 2, background: "var(--orange)", opacity: 0.5 }} />
        </div>
      </div>

      {/* Chiffres clés */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0, border: "1px solid var(--d-line)", borderRadius: 6, overflow: "hidden" }}>
        {[
          ["Prix de rachat", fmtFCFA(p.sale), "var(--d-ink)"],
          ["Capital acquis à ce jour", fmtFCFA(p.accToDate), "var(--pos-d)"],
          ["Part loyer → capital", `${p.share}% · ${fmtFCFA(p.capitalPerMonth)}/m`, "var(--d-ink-2)"],
          ["Solde à la levée", fmtFCFA(p.residualToDate), "var(--orange)"],
        ].map(([k, v, col], i) => (
          <div key={i} style={{
            padding: "10px 12px",
            borderRight: i % 2 === 0 ? "1px solid var(--d-line-2)" : "none",
            borderBottom: i < 2 ? "1px solid var(--d-line-2)" : "none",
          }}>
            <div style={{ fontSize: 9.5, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--d-ink-4)", fontWeight: 600, marginBottom: 4 }}>{k}</div>
            <div className="mono" style={{ fontSize: 12.5, fontWeight: 600, color: col }}>{v}</div>
          </div>
        ))}
      </div>

      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 12 }}>
        <div style={{ fontSize: 11, color: "var(--d-ink-3)" }}>
          Levée d'option possible dès le <b className="mono" style={{ color: "var(--d-ink-2)" }}>{p.optionDate}</b>
        </div>
        <div style={{ display: "flex", gap: 6 }}>
          <button className="btn btn-ghost" style={{ padding: "4px 9px", fontSize: 11.5 }} onClick={() => setShowDoc(true)}>⬇ Contrat</button>
          <button className="btn btn-ghost" style={{ padding: "4px 9px", fontSize: 11.5 }}>Échéancier</button>
        </div>
      </div>
      {showDoc && <LoaContractModal lease={lease} onClose={() => setShowDoc(false)} />}
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════
   DOCUMENTATION LOA
   ═══════════════════════════════════════════════════════════ */
function LoaDoc() {
  const [open, setOpen] = React.useState(true);
  return (
    <div className="panel">
      <div className="panel-head" style={{ cursor: "pointer" }} onClick={() => setOpen(o => !o)}>
        <div>
          <h3 className="panel-title">Comment fonctionne la location-vente (LOA)</h3>
          <div className="panel-sub">Documentation · périmètre couvert par Logestimmo</div>
        </div>
        <span style={{ fontSize: 12, color: "var(--d-ink-3)", fontFamily: "var(--font-mono)" }}>{open ? "Masquer ▴" : "Afficher ▾"}</span>
      </div>

      {open && (
        <div style={{ padding: "20px 24px" }}>
          {/* Principe + étapes */}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 0, marginBottom: 22, border: "1px solid var(--d-line)", borderRadius: 8, overflow: "hidden" }}>
            {[
              ["1", "Mise en place", "Le bail est conclu en « location-vente » : prix de rachat, apport éventuel et part du loyer imputée au capital sont fixés à la signature."],
              ["2", "Pendant la location", "Chaque loyer se décompose : une part couvre la jouissance du bien, l'autre alimente le capital de rachat. L'encours progresse mois après mois."],
              ["3", "Échéance d'option", "Au terme convenu, le locataire peut lever l'option : il achète le bien en réglant le solde résiduel (prix − capital déjà acquis)."],
              ["4", "Issue", "Option levée → transfert de propriété. Option non levée → le contrat se solde selon les clauses (restitution ou bascule en location classique)."],
            ].map(([n, t, d], i) => (
              <div key={i} style={{ padding: "16px 16px", borderRight: i < 3 ? "1px solid var(--d-line-2)" : "none", background: "var(--d-panel)" }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
                  <span style={{ width: 22, height: 22, borderRadius: 50, background: "var(--pos-d)", color: "#fff", display: "grid", placeItems: "center", fontSize: 11, fontWeight: 700, fontFamily: "var(--font-mono)" }}>{n}</span>
                  <span style={{ fontSize: 12.5, fontWeight: 600, color: "var(--d-ink)" }}>{t}</span>
                </div>
                <div style={{ fontSize: 11.5, color: "var(--d-ink-3)", lineHeight: 1.55 }}>{d}</div>
              </div>
            ))}
          </div>

          {/* Exemple chiffré */}
          <div style={{ display: "grid", gridTemplateColumns: "1.1fr 0.9fr", gap: 18 }}>
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--d-ink-4)", marginBottom: 10 }}>Exemple de calcul</div>
              <div style={{ fontSize: 12.5, color: "var(--d-ink-2)", lineHeight: 1.7 }}>
                Bien à <b className="mono">38 M FCFA</b>, loyer <b className="mono">450 000/m</b>, <b>35%</b> imputés au capital
                (<b className="mono">157 500/m</b>), apport de <b className="mono">4 M</b>, option à <b>60 mois</b>.
                <div style={{ marginTop: 10, padding: "12px 14px", background: "var(--d-sunken)", border: "1px solid var(--d-line)", borderRadius: 6, fontFamily: "var(--font-mono)", fontSize: 12, lineHeight: 1.8 }}>
                  Capital sur 60 mois : 157 500 × 60 = <b style={{ color: "var(--d-ink)" }}>9 450 000</b><br />
                  + apport initial : <b style={{ color: "var(--d-ink)" }}>4 000 000</b><br />
                  = encours acquis : <b style={{ color: "var(--pos-d)" }}>13 450 000</b><br />
                  solde à la levée : 38 M − 13,45 M = <b style={{ color: "var(--orange)" }}>24 550 000</b>
                </div>
              </div>
            </div>
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--d-ink-4)", marginBottom: 10 }}>Périmètre Logestimmo</div>
              <ul style={{ margin: 0, paddingLeft: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 8 }}>
                {[
                  ["pos", "Bail en location-vente : prix, apport, part capital, échéance d'option"],
                  ["pos", "Suivi de l'encours de rachat et du solde résiduel sur la fiche contrat"],
                  ["pos", "Quittances et loyers classiques inchangés"],
                  ["neutral", "Ventilation comptable loyer/capital : hors plateforme (à venir)"],
                  ["neutral", "Prime d'option & acte de transfert de propriété : avec votre notaire/comptable"],
                ].map(([tone, txt], i) => (
                  <li key={i} style={{ display: "flex", gap: 10, alignItems: "flex-start", fontSize: 11.5, color: "var(--d-ink-2)", lineHeight: 1.5 }}>
                    <span style={{ marginTop: 1, color: tone === "pos" ? "var(--pos-d)" : "var(--d-ink-4)", fontWeight: 700, flexShrink: 0 }}>{tone === "pos" ? "✓" : "○"}</span>
                    <span>{txt}</span>
                  </li>
                ))}
              </ul>
            </div>
          </div>

          <Banner tone="info" title="Cas d'usage de niche">
            La location-vente reste peu fréquente : Logestimmo la couvre dans sa version légère (suivi indicatif). Pour la comptabilisation détaillée et le transfert de propriété, appuyez-vous sur votre notaire et votre comptable.
          </Banner>
        </div>
      )}
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════
   MODÈLE DE CONTRAT LOA — document imprimable / PDF
   Distinct du bail classique. Affiché en plein écran, isolé à
   l'impression via un portal + feuille de style @media print.
   ═══════════════════════════════════════════════════════════ */
function ct_ensureLoaPrintInfra() {
  if (!document.getElementById("loa-print-root")) {
    const d = document.createElement("div");
    d.id = "loa-print-root";
    document.body.appendChild(d);
  }
  if (!document.getElementById("loa-print-style")) {
    const s = document.createElement("style");
    s.id = "loa-print-style";
    s.textContent = `
      @media screen { #loa-print-root { display: none; } }
      @media print {
        body.loa-printing #root { display: none !important; }
        #loa-print-root { display: block; }
        @page { size: A4; margin: 14mm; }
      }
    `;
    document.head.appendChild(s);
  }
}

function LoaContractModal({ lease, onClose }) {
  ct_ensureLoaPrintInfra(); // synchrone : garantit la cible du portal dès le 1er rendu
  React.useEffect(() => {
    const onKey = e => { if (e.key === "Escape") onClose?.(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  function handlePrint() {
    document.body.classList.add("loa-printing");
    const cleanup = () => { document.body.classList.remove("loa-printing"); window.removeEventListener("afterprint", cleanup); };
    window.addEventListener("afterprint", cleanup);
    setTimeout(() => { window.print(); }, 60);
    // filet de sécurité si afterprint ne se déclenche pas
    setTimeout(cleanup, 4000);
  }

  const printPortal = (typeof ReactDOM !== "undefined" && document.getElementById("loa-print-root"))
    ? ReactDOM.createPortal(<LoaContractDoc lease={lease} print />, document.getElementById("loa-print-root"))
    : null;

  return (
    <>
      <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(8,12,20,0.62)", zIndex: 300, backdropFilter: "blur(3px)" }} />
      <div role="dialog" style={{
        position: "fixed", inset: "3vh 0", zIndex: 301, display: "flex", flexDirection: "column", alignItems: "center", pointerEvents: "none",
      }}>
        {/* Toolbar */}
        <div style={{
          pointerEvents: "auto", width: "min(820px, 92vw)", display: "flex", justifyContent: "space-between", alignItems: "center",
          background: "var(--d-panel)", border: "1px solid var(--d-line)", borderRadius: "8px 8px 0 0", padding: "12px 18px",
        }}>
          <div>
            <div style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: "0.12em", color: "var(--d-ink-4)", textTransform: "uppercase", whiteSpace: "nowrap" }}>Modèle de contrat · location-vente</div>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--d-ink)", marginTop: 2 }}>{lease.id} · {lease.tenant.name}</div>
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <button onClick={handlePrint} className="btn btn-primary" style={{ padding: "6px 14px", fontSize: 12 }}>⬇ Imprimer / PDF</button>
            <button onClick={onClose} className="btn btn-ghost" style={{ padding: "6px 12px", fontSize: 12 }}>Fermer</button>
          </div>
        </div>
        {/* Document scrollable */}
        <div style={{ pointerEvents: "auto", width: "min(820px, 92vw)", flex: 1, minHeight: 0, overflow: "auto", background: "var(--d-sunken)", border: "1px solid var(--d-line)", borderTop: "none", borderRadius: "0 0 8px 8px", padding: "24px" }}>
          <LoaContractDoc lease={lease} />
        </div>
      </div>
      {printPortal}
    </>
  );
}

/* Le document contractuel lui-même (papier) */
function LoaContractDoc({ lease, print }) {
  const c = loaCompute(lease);
  const optionDate = ct_addMonths(lease.start, c.months);
  const endDate = ct_addMonths(lease.start, c.months); // terme du contrat = échéance d'option
  const ink = "#1A1A1A", soft = "#5B5B53", faint = "#7B7B73", line = "#E4E4DE", line2 = "#EEEEE6";
  const paper = print ? "#FFFFFF" : "#FAFAF7";

  const Art = ({ n, title, children }) => (
    <div style={{ marginBottom: 14 }}>
      <div style={{ fontSize: 11, fontWeight: 700, color: ink, marginBottom: 5, letterSpacing: "0.01em" }}>
        Article {n} — {title}
      </div>
      <div style={{ fontSize: 10.5, color: "#3A3A33", lineHeight: 1.65 }}>{children}</div>
    </div>
  );
  const Money = ({ v }) => <b className="mono" style={{ color: ink }}>{(Number(v) || 0).toLocaleString("fr-FR")} FCFA</b>;

  return (
    <div style={{
      background: paper, color: ink, fontFamily: "var(--font-sans)", padding: print ? "0" : "40px 44px",
      border: print ? "none" : "1px solid " + line, borderRadius: print ? 0 : 6,
      maxWidth: 720, margin: "0 auto", boxShadow: print ? "none" : "0 20px 60px -30px rgba(0,0,0,0.5)",
    }}>
      {/* En-tête */}
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", paddingBottom: 14, borderBottom: "2px solid " + ink, marginBottom: 18 }}>
        <div>
          <div style={{ fontSize: 16, fontWeight: 700, color: ink, letterSpacing: "-0.01em", lineHeight: 1.2 }}>
            Contrat de location-vente
          </div>
          <div style={{ fontSize: 10.5, color: faint, marginTop: 3 }}>Location avec option d'achat (LOA) · acte sous seing privé</div>
        </div>
        <div style={{ textAlign: "right" }}>
          <div className="mono" style={{ fontSize: 11, fontWeight: 700, color: ink }}>{lease.id}</div>
          <div style={{ fontSize: 9.5, color: faint, marginTop: 2 }}>Droit OHADA · Sénégal</div>
        </div>
      </div>

      {/* Parties */}
      <div style={{ fontSize: 10.5, color: "#3A3A33", lineHeight: 1.65, marginBottom: 16 }}>
        <div style={{ fontSize: 9, textTransform: "uppercase", letterSpacing: "0.08em", fontWeight: 700, color: faint, marginBottom: 8 }}>Entre les soussignés</div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          <div style={{ padding: "12px 14px", background: print ? "#F6F6F2" : "#F2F2EC", borderRadius: 5, border: "1px solid " + line }}>
            <div style={{ fontSize: 8.5, textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 700, color: faint, marginBottom: 4 }}>Le bailleur-vendeur</div>
            <div style={{ fontSize: 12, fontWeight: 600, color: ink }}>{lease.owner || "—"}</div>
            <div style={{ fontSize: 10, color: soft, marginTop: 3 }}>Représenté par <b>BA Immo</b> · Dakar, agissant en vertu d'un mandat de gestion et de vente.</div>
          </div>
          <div style={{ padding: "12px 14px", background: print ? "#F6F6F2" : "#F2F2EC", borderRadius: 5, border: "1px solid " + line }}>
            <div style={{ fontSize: 8.5, textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 700, color: faint, marginBottom: 4 }}>{lease.coTenant && lease.coTenant.enabled ? "Les preneurs-accédants (co-titulaires)" : "Le preneur-accédant"}</div>
            <div style={{ fontSize: 12, fontWeight: 600, color: ink }}>{lease.tenant.name}</div>
            <div style={{ fontSize: 10, color: soft, marginTop: 3 }}>{lease.tenant.phone || "—"}</div>
            {lease.coTenant && lease.coTenant.enabled && (
              <div style={{ marginTop: 6, paddingTop: 6, borderTop: "1px dashed " + line }}>
                <div style={{ fontSize: 12, fontWeight: 600, color: ink }}>{lease.coTenant.name} <span style={{ fontWeight: 400, color: faint, fontSize: 9.5 }}>· {lease.coTenant.relation}</span></div>
                <div style={{ fontSize: 10, color: soft, marginTop: 2 }}>{lease.coTenant.phone || "—"}</div>
              </div>
            )}
          </div>
        </div>
        <div style={{ marginTop: 12, fontStyle: "italic", color: soft }}>
          Il a été convenu et arrêté ce qui suit, étant préalablement exposé que le présent contrat associe une location et une promesse unilatérale de vente assortie d'une option d'achat au profit du preneur.
        </div>
        {lease.coTenant && lease.coTenant.enabled && (
          <div style={{ marginTop: 10, padding: "9px 12px", background: print ? "#F6F6F2" : "#F2F2EC", border: "1px solid " + line, borderRadius: 5, fontSize: 10, color: "#3A3A33", lineHeight: 1.6 }}>
            <b style={{ color: ink }}>Solidarité —</b> {lease.tenant.name} et {lease.coTenant.name} ({lease.coTenant.relation}) sont co-titulaires solidaires du présent contrat : chacun répond de la totalité des obligations (loyer, charges, capital de rachat et solde de levée d'option).
          </div>
        )}
      </div>

      {/* Récap clés */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 0, marginBottom: 18, border: "1px solid " + line, borderRadius: 5, overflow: "hidden" }}>
        {[
          ["Bien", `${lease.property}${lease.unit ? " · lot " + lease.unit : ""}`],
          ["Loyer mensuel", c.rent.toLocaleString("fr-FR") + " FCFA"],
          ["Prix de levée", c.sale.toLocaleString("fr-FR") + " FCFA"],
          ["Échéance d'option", `${c.months} mois`],
        ].map(([k, v], i) => (
          <div key={i} style={{ padding: "9px 11px", borderRight: i < 3 ? "1px solid " + line2 : "none" }}>
            <div style={{ fontSize: 8, textTransform: "uppercase", color: faint, letterSpacing: "0.05em", fontWeight: 700, marginBottom: 3 }}>{k}</div>
            <div style={{ fontSize: 10.5, fontWeight: 600, color: ink, fontFamily: /\d/.test(v) && k !== "Bien" ? "var(--font-mono)" : "var(--font-sans)" }}>{v}</div>
          </div>
        ))}
      </div>

      {/* Articles */}
      <Art n="1" title="Objet et désignation du bien">
        Le bailleur-vendeur donne en location-vente au preneur-accédant, qui accepte, le bien sis <b>{lease.property}{lease.unit ? `, lot ${lease.unit}` : ""}</b>, à usage {(lease.type || "résidentiel").toLowerCase()}. Le bien est loué avec faculté pour le preneur d'en devenir propriétaire dans les conditions ci-après.
      </Art>
      <Art n="2" title="Durée et prise d'effet">
        Le contrat prend effet le <b className="mono">{lease.start}</b> pour une durée de <b>{c.months} mois</b>, expirant le <b className="mono">{endDate}</b>, date à laquelle l'option d'achat doit être levée ou abandonnée.
      </Art>
      <Art n="3" title="Loyer et ventilation">
        Le preneur verse un loyer mensuel de <Money v={c.rent} />, payable d'avance. Ce loyer se décompose en une part de jouissance et une part de <b>{c.share}%</b> imputée au capital d'acquisition, soit <Money v={c.capitalPerMonth} /> par mois venant en déduction du prix de levée d'option.
      </Art>
      <Art n="4" title="Apport initial / prime d'option">
        {c.upfront > 0
          ? <>À la signature, le preneur verse un apport de <Money v={c.upfront} />, intégralement imputé au capital d'acquisition et définitivement acquis au vendeur en cas de non-levée de l'option.</>
          : <>Aucun apport initial n'est prévu au présent contrat.</>}
      </Art>
      <Art n="5" title="Option d'achat">
        Le preneur dispose d'une option d'achat exerçable à compter du <b className="mono">{optionDate}</b>. Le prix de levée d'option est fixé à <Money v={c.sale} />. En cas d'exercice, le preneur règle le solde résiduel après imputation du capital acquis (apport et quotes-parts de loyer).
      </Art>

      {/* Tableau encours de rachat */}
      <div style={{ margin: "14px 0", border: "1px solid " + line, borderRadius: 5, overflow: "hidden" }}>
        <div style={{ padding: "7px 11px", background: print ? "#F0F0EA" : "#EDEDE5", fontSize: 9, textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 700, color: faint }}>
          Encours de rachat au terme ({c.months} mois)
        </div>
        {[
          ["Apport initial imputé", c.upfront],
          [`Capital cumulé (${c.share}% × ${c.rent.toLocaleString("fr-FR")} × ${c.months})`, c.capitalOverTerm],
          ["Total acquis au rachat", c.accumulated, "acc"],
          ["Solde résiduel à la levée d'option", c.residual, "res"],
        ].map(([k, v, tag], i) => (
          <div key={i} style={{
            display: "flex", justifyContent: "space-between", padding: "8px 11px",
            borderTop: i === 0 ? "none" : "1px solid " + line2,
            background: tag === "acc" ? (print ? "#F3F8F4" : "#EFF6F1") : tag === "res" ? (print ? "#FBF4EF" : "#FAF2EB") : "transparent",
          }}>
            <span style={{ fontSize: 10, color: tag ? ink : "#3A3A33", fontWeight: tag ? 600 : 400 }}>{k}</span>
            <span className="mono" style={{ fontSize: 10.5, fontWeight: 600, color: tag === "res" ? "#B5612A" : tag === "acc" ? "#2E7D55" : ink }}>{(Number(v) || 0).toLocaleString("fr-FR")} FCFA</span>
          </div>
        ))}
      </div>

      <Art n="6" title="Transfert de propriété">
        L'exercice de l'option et le paiement intégral du solde résiduel emportent transfert de propriété au profit du preneur. Les frais d'acte, d'enregistrement et de mutation, ainsi que la régularisation notariée, sont à la charge du preneur sauf convention contraire.
      </Art>
      <Art n="7" title="Charges, entretien et assurance">
        Pendant la durée du contrat, le preneur supporte les charges locatives, l'entretien courant et l'assurance du bien. Le gros œuvre demeure à la charge du bailleur jusqu'au transfert de propriété.
      </Art>
      <Art n="8" title="Défaut et non-levée de l'option">
        En cas de non-levée de l'option au terme, le contrat se dénoue : les sommes imputées au capital restent acquises au bailleur au titre de l'indemnité d'occupation, sauf clause de restitution expresse. Le défaut de paiement entraîne la résiliation dans les conditions de droit commun.
      </Art>
      <Art n="9" title="Litiges et droit applicable">
        Le présent contrat est régi par le droit OHADA et le droit sénégalais. Tout différend relève de la compétence des juridictions de Dakar, après tentative de règlement amiable.
      </Art>

      {/* Signatures */}
      <div style={{ marginTop: 20, paddingTop: 12, borderTop: "1px dashed " + line }}>
        <div style={{ fontSize: 10, color: soft, marginBottom: 14 }}>Fait à Dakar, le {CT_TODAY.toISOString().slice(0, 10)}, en trois exemplaires originaux.</div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 16 }}>
          {[["Le bailleur-vendeur", lease.owner || "—"], ["Le preneur-accédant", lease.tenant.name], ["L'agence", "BA Immo · Dakar"]].map(([role, who], i) => (
            <div key={i}>
              <div style={{ textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 700, fontSize: 8.5, marginBottom: 4, color: faint }}>{role}</div>
              <div style={{ height: 34, borderBottom: "1px solid #C4C4BD" }} />
              <div style={{ fontSize: 9, color: faint, marginTop: 3 }}>{who}</div>
            </div>
          ))}
        </div>
        <div style={{ fontSize: 8.5, color: faint, marginTop: 16, fontStyle: "italic", lineHeight: 1.5 }}>
          Modèle généré par Logestimmo à titre indicatif. La comptabilisation détaillée (ventilation loyer/capital, prime d'option) et l'acte de transfert de propriété doivent être validés par un notaire et un comptable.
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ContractsPage });
/* ╚══ end pages-contracts.jsx ══╝ */

/* ╔══ pages-property-form.jsx ══╗ */
/* global React, FormSection, Field, FieldRow, Input, Textarea, MoneyInput,
          Select, RadioGroup, Segmented, Banner, ColorSwatch, fmtFCFA, fmtCompact,
          MOCK, BAILLEURS, getBailleurById */

/* ═══════════════════════════════════════════════════════════════════════
   PROPERTY FORM — création + modification d'un bien
   Étapes : Identité · Localisation · Lots · Bailleur · Récapitulatif
   ═══════════════════════════════════════════════════════════════════════ */

const PROPERTY_TYPES = [
  { value:"Résidence",   label:"Résidence",      sub:"Immeuble collectif",   icon:"🏢" },
  { value:"Villa",       label:"Villa",           sub:"Maison individuelle",  icon:"🏡" },
  { value:"Immeuble",    label:"Immeuble",        sub:"Bâtiment mixte",       icon:"🏗" },
  { value:"Bureau",      label:"Bureau",          sub:"Locaux professionnels",icon:"🏛" },
  { value:"Commercial",  label:"Commercial",      sub:"Boutique · commerce",  icon:"🏪" },
  { value:"Terrain",     label:"Terrain",         sub:"Foncier nu",           icon:"📐" },
];

const LOT_TYPES = ["Studio","F2","F3","F4","F4+","Villa","Bureau","Local commercial","Chambre","Parking"];
const LOT_STATUTS = [
  { value:"libre",    label:"Libre",    color:"var(--pos-d)" },
  { value:"occupé",   label:"Occupé",   color:"var(--blue-d)" },
  { value:"réservé",  label:"Réservé",  color:"var(--orange)" },
  { value:"travaux",  label:"Travaux",  color:"var(--d-ink-4)" },
];

/* ─── Properties store ─── */
const _propListeners = new Set();
function _notifyProps() { _propListeners.forEach(fn => fn()); }
function upsertProperty(p) {
  const i = MOCK.properties.findIndex(x => x.id === p.id);
  if (i >= 0) MOCK.properties[i] = { ...MOCK.properties[i], ...p };
  else MOCK.properties.push(p);
  _notifyProps();
}
function useProperties() {
  const [, tick] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => { _propListeners.add(tick); return () => _propListeners.delete(tick); }, []);
  return MOCK.properties;
}
function getPropertyById(id) { return MOCK.properties.find(p => p.id === id); }
function nextPropertyId() {
  const max = MOCK.properties.reduce((m,p) => Math.max(m, parseInt((p.id||"").replace("P-",""),10)||0), 100);
  return "P-" + (max + 1);
}

function makeEmptyProperty() {
  return {
    id: "",
    name: "",
    type: "Résidence",
    status: "en-gestion",   // en-gestion | en-projet | suspendu
    address: "",
    quartier: "",
    commune: "Dakar",
    gps: "",
    titreFoncier: "",
    anneeConstruction: "",
    surface: "",            // m² total
    niveaux: "1",
    notes: "",
    // Lots
    lots: [
      { id:1, ref:"", type:"F3", surface:"", rent:0, status:"libre" },
    ],
    // Bailleur
    ownerId: "",            // id in BAILLEURS
    owner: "",              // name (for MOCK compatibility)
    commissionRate: 0.08,
    // Computed (written on save)
    units: 1,
    occupied: 0,
    mrr: 0,
  };
}

function fillFromExistingProperty(p) {
  const base = makeEmptyProperty();
  // Try to find bailleur by name
  const bailleur = (typeof BAILLEURS !== "undefined") ? BAILLEURS.find(b => b.name === p.owner) : null;
  // Rebuild lots from p.units if no explicit lots stored
  const lots = p.lots && p.lots.length ? p.lots : Array.from({length: p.units}, (_, i) => ({
    id: i + 1,
    ref: p.units === 1 ? "—" : String.fromCharCode(65 + Math.floor(i/10)) + "-" + String(i+1).padStart(3,"0"),
    type: p.type === "Villa" ? "Villa" : "F3",
    surface: "",
    rent: p.units > 0 ? Math.round(p.mrr / Math.max(p.occupied, 1)) : 0,
    status: i < p.occupied ? "occupé" : "libre",
  }));
  return {
    ...base,
    ...p,
    ownerId: bailleur ? bailleur.id : "",
    commissionRate: bailleur ? bailleur.commissionRate : base.commissionRate,
    lots,
  };
}

/* ═══ MODAL SHELL ═══ */
function PropertyFormModal({ open, mode = "create", initial, onClose, onSave, presetOwnerId }) {
  const [form, setForm] = React.useState(() =>
    initial ? fillFromExistingProperty(initial)
            : { ...makeEmptyProperty(), ownerId: presetOwnerId || "" }
  );
  const [step, setStep] = React.useState("identite");

  React.useEffect(() => {
    if (open) {
      setForm(initial ? fillFromExistingProperty(initial)
                      : { ...makeEmptyProperty(), ownerId: presetOwnerId || "" });
      setStep("identite");
    }
  }, [open, initial, presetOwnerId]);

  const set = (k, v) => setForm(f => ({...f, [k]: v}));

  /* Lot helpers */
  const setLot = (idx, patch) => setForm(f => ({
    ...f,
    lots: f.lots.map((l,i) => i === idx ? {...l, ...patch} : l),
  }));
  const addLot = () => setForm(f => ({
    ...f,
    lots: [...f.lots, { id: Date.now(), ref:"", type:"F3", surface:"", rent:0, status:"libre" }],
  }));
  const removeLot = (idx) => setForm(f => ({
    ...f,
    lots: f.lots.length > 1 ? f.lots.filter((_,i) => i !== idx) : f.lots,
  }));

  /* Derived values */
  const totalLots     = form.lots.length;
  const occupiedLots  = form.lots.filter(l => l.status === "occupé").length;
  const totalMrr      = form.lots.filter(l => l.status === "occupé").reduce((s,l) => s + (Number(l.rent)||0), 0);
  const bailleur      = (typeof BAILLEURS !== "undefined") ? BAILLEURS.find(b => b.id === form.ownerId) : null;

  /* Sync ownerId → owner name */
  React.useEffect(() => {
    if (bailleur) set("owner", bailleur.name);
  }, [form.ownerId]);

  /* Validation */
  const errors = {};
  if (!form.name.trim()) errors.name = "Nom du bien requis";
  if (!form.address.trim()) errors.address = "Adresse requise";
  if (!form.ownerId) errors.owner = "Associez ce bien à un bailleur";
  if (form.lots.length === 0) errors.lots = "Au moins un lot requis";
  const canSave = Object.keys(errors).length === 0;

  function handleSave() {
    if (!canSave) return;
    const payload = {
      ...form,
      id: form.id || nextPropertyId(),
      units: totalLots,
      occupied: occupiedLots,
      mrr: totalMrr,
      owner: bailleur ? bailleur.name : form.owner,
    };
    onSave?.(payload);
  }

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

  if (!open) return null;

  const STEPS = [
    { id:"identite",    label:"Identité & type" },
    { id:"localisation",label:"Localisation" },
    { id:"lots",        label:"Lots", badge: form.lots.length },
    { id:"bailleur",    label:"Bailleur" },
    { id:"recap",       label:"Récapitulatif" },
  ];

  return (
    <>
      <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:200, backdropFilter:"blur(3px)"}}/>
      <div role="dialog" style={{
        position:"fixed", inset:"4vh 8vw", zIndex:201, maxWidth:1180, margin:"0 auto",
        background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10,
        display:"flex", flexDirection:"column", overflow:"hidden",
        boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)", animation:"baill-pop 180ms ease-out",
      }}>
        <header style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", alignItems:"center", justifyContent:"space-between", background:"var(--d-panel)"}}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>
              {mode === "edit" ? "Modifier le bien" : "Nouveau bien"}
            </div>
            <div style={{fontSize:16, fontWeight:600, color:"var(--d-ink)", display:"flex", alignItems:"center", gap:10}}>
              {form.name || "Bien sans nom"}
              {form.address && <span style={{color:"var(--d-ink-3)", fontWeight:400, fontSize:13}}>· {form.address}</span>}
              {mode === "edit" && form.id && (
                <span className="mono" style={{fontSize:10.5, padding:"2px 7px", borderRadius:3, background:"var(--d-elev)", color:"var(--d-ink-3)"}}>{form.id}</span>
              )}
            </div>
          </div>
          <button onClick={onClose} title="Fermer · Esc" style={{
            width:32, height:32, borderRadius:6, color:"var(--d-ink-3)", fontSize:18, display:"grid", placeItems:"center",
            background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer",
          }}>×</button>
        </header>

        <div style={{display:"grid", gridTemplateColumns:"220px 1fr", flex:1, minHeight:0}}>
          {/* Steps nav */}
          <nav style={{borderRight:"1px solid var(--d-line)", padding:"18px 14px", background:"var(--d-sunken)", display:"flex", flexDirection:"column", gap:2}}>
            {STEPS.map((s, i) => {
              const active = s.id === step;
              return (
                <button key={s.id} onClick={()=>setStep(s.id)} style={{
                  textAlign:"left", padding:"9px 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: "2px solid " + (active ? "var(--blue-d)" : "transparent"),
                  paddingLeft: 12, cursor:"pointer", display:"flex", alignItems:"center", justifyContent:"space-between", gap:8,
                }}>
                  <span><span className="mono" style={{color:"var(--d-ink-4)", marginRight:8, fontSize:10.5}}>{String(i+1).padStart(2,"0")}</span>{s.label}</span>
                  {s.badge != null && (
                    <span className="mono" style={{fontSize:10, padding:"1px 6px", borderRadius:3, background:"var(--d-bg)", color:"var(--d-ink-3)"}}>{s.badge}</span>
                  )}
                </button>
              );
            })}

            {/* Validation */}
            <div style={{marginTop:14, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
              <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>Validation</div>
              {Object.keys(errors).length === 0 ? (
                <div style={{display:"flex", gap:6, alignItems:"center", fontSize:11.5, color:"var(--pos-d)"}}>
                  <span style={{width:6, height:6, borderRadius:50, background:"var(--pos-d)"}}/>Prêt à enregistrer
                </div>
              ) : (
                <div style={{display:"flex", flexDirection:"column", gap:4}}>
                  {Object.values(errors).map((e,i) => (
                    <div key={i} style={{fontSize:11, color:"var(--orange)", lineHeight:1.45, display:"flex", gap:6, alignItems:"flex-start"}}>
                      <span style={{color:"var(--orange)", marginTop:2}}>!</span><span>{e}</span>
                    </div>
                  ))}
                </div>
              )}
            </div>

            {/* Mini snapshot */}
            <div style={{marginTop:10, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
              <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>Aperçu du parc</div>
              <div style={{fontSize:11.5, color:"var(--d-ink-2)", display:"flex", flexDirection:"column", gap:3}}>
                <div><b className="mono">{totalLots}</b> lot{totalLots>1?"s":""} · <span style={{color:"var(--pos-d)"}}>{occupiedLots} occupé{occupiedLots>1?"s":""}</span></div>
                <div style={{color:"var(--d-ink-3)"}}>Taux {totalLots > 0 ? Math.round(occupiedLots/totalLots*100) : 0}%</div>
                {totalMrr > 0 && <div style={{color:"var(--pos-d)", fontFamily:"var(--font-mono)", fontSize:11}}>{fmtCompact(totalMrr)} FCFA/mois</div>}
              </div>
            </div>
          </nav>

          {/* Content */}
          <div style={{overflow:"auto", padding:"22px 28px"}}>
            {step === "identite"    && <PIdentiteStep    form={form} set={set}/>}
            {step === "localisation"&& <PLocalisationStep form={form} set={set}/>}
            {step === "lots"        && <PLotsStep         form={form} lots={form.lots} setLot={setLot} addLot={addLot} removeLot={removeLot} totalLots={totalLots} occupiedLots={occupiedLots} totalMrr={totalMrr}/>}
            {step === "bailleur"    && <PBailleurStep     form={form} set={set} bailleur={bailleur}/>}
            {step === "recap"       && <PRecapStep        form={form} bailleur={bailleur} totalLots={totalLots} occupiedLots={occupiedLots} totalMrr={totalMrr}/>}
          </div>
        </div>

        <footer style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-panel)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)"}}>
            {form.type} · {totalLots} lot{totalLots>1?"s":""} · {occupiedLots} occupé{occupiedLots>1?"s":""}
            {bailleur && <> · <b style={{color:"var(--d-ink-2)"}}>{bailleur.name}</b></>}
          </div>
          <div style={{display:"flex", gap:8}}>
            <button onClick={onClose} className="btn btn-ghost">Annuler</button>
            {STEPS.findIndex(s => s.id === step) < STEPS.length - 1 && (
              <button onClick={() => {
                const idx = STEPS.findIndex(s => s.id === step);
                setStep(STEPS[idx + 1].id);
              }} className="btn btn-secondary">Suivant →</button>
            )}
            <button onClick={handleSave} disabled={!canSave} className="btn btn-primary"
              style={{opacity: canSave ? 1 : 0.5, cursor: canSave ? "pointer" : "not-allowed"}}>
              {mode === "edit" ? "Enregistrer les modifications" : "Créer le bien"}
            </button>
          </div>
        </footer>
      </div>
    </>
  );
}

/* ─── STEP 1 · Identité & type ─── */
function PIdentiteStep({ form, set }) {
  return (
    <>
      <FormSection
        title="Type de bien"
        subtitle="Le type détermine les champs affichés (surface, lots, usage), les clauses types de bail et la catégorie dans vos rapports."
      >
        <div style={{display:"grid", gridTemplateColumns:"repeat(3, 1fr)", gap:8}}>
          {PROPERTY_TYPES.map(t => {
            const active = form.type === t.value;
            return (
              <button key={t.value} onClick={()=>set("type", t.value)} style={{
                padding:"12px 14px", textAlign:"left", cursor:"pointer",
                border:"1px solid " + (active ? "var(--blue-d)" : "var(--d-line)"),
                background: active ? "rgba(97,146,255,0.06)" : "var(--d-panel)", borderRadius:6,
              }}>
                <div style={{display:"flex", alignItems:"center", gap:8, marginBottom:4}}>
                  <span style={{width:14, height:14, borderRadius:50, flexShrink:0, border: "4px solid " + (active ? "var(--blue-d)" : "var(--d-line)")}}/>
                  <span style={{fontSize:12.5, fontWeight:600, color:"var(--d-ink)"}}>{t.label}</span>
                </div>
                <div style={{fontSize:10.5, color:"var(--d-ink-3)", paddingLeft:22, lineHeight:1.4}}>{t.sub}</div>
              </button>
            );
          })}
        </div>
      </FormSection>

      <FormSection
        title="Identification"
        subtitle="Nom commercial du bien tel qu'il apparaît sur les CRG, quittances et documents contractuels."
      >
        <Field label="Nom du bien" required hint="Ex : Résidence Teranga · Villa Mermoz 14 · Immeuble Point E">
          <Input value={form.name} onChange={e=>set("name", e.target.value)} placeholder={
            form.type === "Villa" ? "Villa Sacré-Cœur 3" : form.type === "Résidence" ? "Résidence Almadies" : "Immeuble Fann"
          }/>
        </Field>

        <FieldRow>
          <Field label="Statut" required>
            <Select value={form.status} onChange={v=>set("status", v)} options={[
              {value:"en-gestion",  label:"En gestion"},
              {value:"en-projet",   label:"En projet · non encore loué"},
              {value:"suspendu",    label:"Suspendu · hors gestion"},
            ]}/>
          </Field>
          <Field label="Référence interne" hint={form.id ? "ID attribué" : "Auto-générée à la création"}>
            <Input value={form.id} onChange={e=>set("id", e.target.value)} placeholder="P-176" style={{fontFamily:"var(--font-mono)"}} readOnly={!!form.id}/>
          </Field>
        </FieldRow>

        <FieldRow columns="1fr 1fr 1fr">
          <Field label="Année de construction" optional>
            <Input value={form.anneeConstruction} onChange={e=>set("anneeConstruction", e.target.value)} placeholder="2018" style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
          <Field label="Surface totale (m²)" optional>
            <Input value={form.surface} onChange={e=>set("surface", e.target.value)} suffix="m²" style={{fontFamily:"var(--font-mono)", textAlign:"right"}}/>
          </Field>
          <Field label="Niveaux (étages + RDC)" optional>
            <Input value={form.niveaux} onChange={e=>set("niveaux", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
        </FieldRow>
      </FormSection>
    </>
  );
}

/* ─── STEP 2 · Localisation ─── */
function PLocalisationStep({ form, set }) {
  return (
    <FormSection
      title="Localisation"
      subtitle="Adresse complète telle qu'elle figure sur le titre foncier et les documents cadastraux."
    >
      <Field label="Adresse" required hint="Numéro + rue, ou parcelle + lotissement">
        <Input value={form.address} onChange={e=>set("address", e.target.value)} placeholder="Rue 10 × VDN, Almadies"/>
      </Field>

      <FieldRow columns="1fr 1fr 1fr">
        <Field label="Quartier">
          <Input value={form.quartier} onChange={e=>set("quartier", e.target.value)} placeholder="Almadies"/>
        </Field>
        <Field label="Commune / Ville" required>
          <Select value={form.commune} onChange={v=>set("commune", v)} options={[
            "Dakar","Plateau","Almadies","Mermoz","Sacré-Cœur","Point E","Fann",
            "Ngor","Ouakam","Parcelles Assainies","Liberté","Yoff","Guédiawaye",
            "Pikine","Thiès","Saint-Louis","Ziguinchor","Autre",
          ]}/>
        </Field>
        <Field label="Coordonnées GPS" optional hint="Latitude, longitude">
          <Input value={form.gps} onChange={e=>set("gps", e.target.value)} placeholder="14.7645, -17.3660" style={{fontFamily:"var(--font-mono)"}}/>
        </Field>
      </FieldRow>

      <FieldRow>
        <Field label="Titre foncier (TF)" optional hint="Numéro et arrondissement">
          <Input value={form.titreFoncier} onChange={e=>set("titreFoncier", e.target.value)} placeholder="TF n° 14 228 / DK" style={{fontFamily:"var(--font-mono)"}}/>
        </Field>
        <Field label="Référence cadastrale" optional>
          <Input value={form.cadastre || ""} onChange={e=>set("cadastre", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
        </Field>
      </FieldRow>
    </FormSection>
  );
}

/* ─── STEP 3 · Lots ─── */
function PLotsStep({ form, lots, setLot, addLot, removeLot, totalLots, occupiedLots, totalMrr }) {
  const palette = { "occupé":"var(--blue-d)", "libre":"var(--pos-d)", "réservé":"var(--orange)", "travaux":"var(--d-ink-4)" };

  return (
    <>
      <FormSection
        title="Configuration des lots"
        subtitle="Détaillez chaque lot loué ou disponible. La somme des loyers des lots occupés constitue le loyer brut mensuel du bien dans vos rapports."
        footer={<>
          <span>
            {totalLots} lot{totalLots>1?"s":""} · <span style={{color:"var(--pos-d)"}}>{occupiedLots} occupé{occupiedLots>1?"s":""}</span>
            {totalMrr > 0 && <> · MRR <b className="mono" style={{color:"var(--pos-d)"}}>{fmtFCFA(totalMrr)}</b></>}
          </span>
          <button className="btn btn-secondary" style={{padding:"5px 12px"}} onClick={addLot}>+ Ajouter un lot</button>
        </>}
      >
        {/* Visual occupancy bar */}
        {totalLots > 0 && (
          <div style={{marginBottom:14}}>
            <div style={{display:"flex", height:12, borderRadius:3, overflow:"hidden", border:"1px solid var(--d-line-2)", marginBottom:6}}>
              {lots.map((l,i) => (
                <div key={i} title={`${l.ref || "Lot "+(i+1)} · ${l.status}`}
                  style={{flex:1, background: palette[l.status] || "var(--d-elev)", borderRight: i < lots.length-1 ? "1px solid var(--d-bg)" : "none"}}/>
              ))}
            </div>
            <div style={{display:"flex", gap:12, fontSize:11, color:"var(--d-ink-3)", flexWrap:"wrap"}}>
              {Object.entries(palette).map(([status, color]) => {
                const cnt = lots.filter(l=>l.status===status).length;
                if (!cnt) return null;
                return <span key={status} style={{display:"flex", alignItems:"center", gap:5}}>
                  <span style={{width:8, height:8, borderRadius:2, background:color}}/>{cnt} {status}
                </span>;
              })}
            </div>
          </div>
        )}

        <div style={{display:"flex", flexDirection:"column", gap:8}}>
          {lots.map((lot, idx) => (
            <div key={lot.id || idx} style={{
              padding:"14px 16px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-sunken)",
            }}>
              <div style={{display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:12}}>
                <div style={{display:"flex", alignItems:"center", gap:10}}>
                  <span className="mono" style={{
                    width:22, height:22, borderRadius:4, background:"var(--d-elev)", color:"var(--d-ink-2)",
                    display:"grid", placeItems:"center", fontSize:11, fontWeight:600,
                  }}>{idx + 1}</span>
                  <span style={{fontSize:12, fontWeight:600, color:"var(--d-ink)"}}>
                    {lot.ref || `Lot ${idx + 1}`}
                  </span>
                  <span style={{
                    fontSize:10.5, padding:"1px 8px", borderRadius:3,
                    background: (palette[lot.status] || "var(--d-ink-4)") + "22",
                    color: palette[lot.status] || "var(--d-ink-4)",
                    fontWeight:600, letterSpacing:"0.03em",
                  }}>{lot.status}</span>
                </div>
                {lots.length > 1 && (
                  <button onClick={()=>removeLot(idx)} title="Supprimer ce lot" style={{
                    width:26, height:26, borderRadius:4, color:"var(--d-ink-4)",
                    background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer", fontSize:12,
                  }}>×</button>
                )}
              </div>

              <FieldRow columns="1fr 1fr 1fr 1fr">
                <Field label="Référence du lot">
                  <Input value={lot.ref} onChange={e=>setLot(idx,{ref:e.target.value})} placeholder={`A-${String(idx+1).padStart(3,"0")}`}/>
                </Field>
                <Field label="Type">
                  <Select value={lot.type} onChange={v=>setLot(idx,{type:v})} options={LOT_TYPES.map(t=>({value:t,label:t}))}/>
                </Field>
                <Field label="Surface" optional>
                  <Input value={lot.surface} onChange={e=>setLot(idx,{surface:e.target.value})} suffix="m²" style={{fontFamily:"var(--font-mono)", textAlign:"right"}}/>
                </Field>
                <Field label="Statut">
                  <Select value={lot.status} onChange={v=>setLot(idx,{status:v})} options={LOT_STATUTS.map(s=>({value:s.value,label:s.label}))}/>
                </Field>
              </FieldRow>

              <Field label="Loyer de base" hint="Loyer mensuel hors charges de ce lot">
                <MoneyInput value={lot.rent || ""} onChange={e=>setLot(idx,{rent:Number(e.target.value.replace(/[^0-9]/g,""))||0})}/>
              </Field>
            </div>
          ))}
        </div>
      </FormSection>
    </>
  );
}

/* ─── STEP 4 · Bailleur ─── */
function PBailleurStep({ form, set, bailleur }) {
  return (
    <>
      <FormSection
        title="Propriétaire du bien"
        subtitle="Liez ce bien à un bailleur de votre portefeuille. Ce lien est utilisé pour calculer les versements, éditer les CRG et imputer les charges."
      >
        <Field label="Bailleur propriétaire" required>
          <Select
            value={form.ownerId}
            onChange={v=>set("ownerId", v)}
            placeholder="— Choisir un bailleur —"
            options={(typeof BAILLEURS !== "undefined" ? BAILLEURS : []).map(b => ({
              value: b.id,
              label: `${b.name} · ${b.entity} · ${b.id}`,
            }))}
          />
        </Field>

        {bailleur && (
          <div style={{
            padding:"14px 16px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-sunken)",
            display:"grid", gridTemplateColumns:"auto 1fr 1fr", gap:14, alignItems:"center",
          }}>
            <div className="avatar" style={{background:bailleur.color, color:"#fff", fontSize:14, fontWeight:600}}>
              {bailleur.avatar}
            </div>
            <div>
              <div style={{fontWeight:600, fontSize:13}}>{bailleur.name}</div>
              <div style={{fontSize:11.5, color:"var(--d-ink-3)", marginTop:2}}>{bailleur.entity} · {bailleur.email}</div>
            </div>
            <div>
              <div style={{fontSize:11, color:"var(--d-ink-4)", marginBottom:2}}>Commission actuelle</div>
              <div className="mono" style={{fontSize:14, fontWeight:600}}>{(bailleur.commissionRate*100).toFixed(1).replace(".",",")}%</div>
            </div>
          </div>
        )}

        <FieldRow>
          <Field label="Taux de commission sur ce bien" required hint="Peut différer du taux par défaut du bailleur">
            <Input
              value={(Number(form.commissionRate)*100).toFixed(1).replace(".",",")}
              onChange={e=>{
                const v = e.target.value.replace(",",".").replace(/[^0-9.]/g,"");
                set("commissionRate", (Number(v)||0)/100);
              }}
              suffix="%"
              style={{fontFamily:"var(--font-mono)", textAlign:"right"}}
            />
          </Field>
          <Field label="Fréquence de versement">
            <Select value={form.paymentFreq || "monthly"} onChange={v=>set("paymentFreq", v)} options={[
              {value:"monthly",   label:"Mensuelle"},
              {value:"quarterly", label:"Trimestrielle"},
              {value:"manual",    label:"À la demande"},
            ]}/>
          </Field>
        </FieldRow>
      </FormSection>

      <FormSection
        title="Mandat de gestion"
        subtitle="Référence du mandat qui couvre ce bien. La plupart du temps, un seul mandat couvre tous les biens d'un bailleur."
      >
        <FieldRow>
          <Field label="Référence du mandat" optional>
            <Input value={form.mandatRef || ""} onChange={e=>set("mandatRef", e.target.value)} placeholder="CTG-007" style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
          <Field label="Date de prise en gestion">
            <Input type="date" value={form.mandatDate || ""} onChange={e=>set("mandatDate", e.target.value)} style={{fontFamily:"var(--font-mono)"}}/>
          </Field>
        </FieldRow>

        <Field label="Notes sur ce bien" optional>
          <Textarea rows={3} value={form.notes} onChange={e=>set("notes", e.target.value)} placeholder="Ex : gardien sur place · interphone · parking couvert…"/>
        </Field>
      </FormSection>
    </>
  );
}

/* ─── STEP 5 · Récapitulatif ─── */
function PRecapStep({ form, bailleur, totalLots, occupiedLots, totalMrr }) {
  const commission = Math.round(totalMrr * (Number(form.commissionRate)||0.08));
  const net = totalMrr - commission;
  const palette = { "occupé":"var(--blue-d)", "libre":"var(--pos-d)", "réservé":"var(--orange)", "travaux":"var(--d-ink-4)" };

  return (
    <FormSection
      title="Récapitulatif"
      subtitle="Vérifiez les informations avant d'enregistrer ce bien. Il sera immédiatement disponible dans votre parc."
    >
      {/* Paper card */}
      <div style={{
        background:"#FAFAF7", borderRadius:6, padding:"22px 24px",
        fontFamily:"var(--font-sans)", border:"1px solid #E4E4DE", color:"#1A1A1A",
      }}>
        <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}}>{form.type}</div>
            <div style={{fontSize:16, fontWeight:700, color:"#1A1A1A"}}>{form.name || "—"}</div>
            <div style={{fontSize:11, color:"#7B7B73", marginTop:4}}>{form.address}{form.quartier ? ` · ${form.quartier}` : ""} · {form.commune}</div>
          </div>
          {form.id && <span className="mono" style={{fontSize:10, padding:"3px 8px", borderRadius:3, background:"#EEEEE6", color:"#5B5B53"}}>{form.id}</span>}
        </div>

        {/* KPIs */}
        <div style={{display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:0, border:"1px solid #E4E4DE", borderRadius:4, overflow:"hidden", marginBottom:16}}>
          {[
            ["Lots", `${occupiedLots} / ${totalLots}`],
            ["Occupation", `${totalLots > 0 ? Math.round(occupiedLots/totalLots*100) : 0}%`],
            ["Loyer brut", fmtFCFA(totalMrr)],
            ["Net bailleur", fmtFCFA(net)],
          ].map(([k,v],i) => (
            <div key={i} style={{padding:"10px 12px", borderRight: i < 3 ? "1px solid #EEEEE6" : "none"}}>
              <div style={{fontSize:9, textTransform:"uppercase", color:"#7B7B73", letterSpacing:"0.05em", fontWeight:600, marginBottom:3}}>{k}</div>
              <div className="mono" style={{fontSize:13, fontWeight:600, color:"#1A1A1A"}}>{v}</div>
            </div>
          ))}
        </div>

        {/* Lots table */}
        <div style={{marginBottom:14}}>
          <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:8}}>Lots configurés</div>
          <div style={{display:"flex", flexDirection:"column", gap:1}}>
            {form.lots.map((lot,i) => (
              <div key={i} style={{display:"grid", gridTemplateColumns:"80px 80px 1fr 100px 80px", gap:8, padding:"6px 0", borderBottom:"1px solid #EEEEE6", fontSize:11, color:"#3A3A33", alignItems:"center"}}>
                <span className="mono" style={{fontWeight:600}}>{lot.ref || `Lot ${i+1}`}</span>
                <span style={{color:"#7B7B73"}}>{lot.type}</span>
                <span style={{color:"#7B7B73"}}>{lot.surface ? `${lot.surface} m²` : "—"}</span>
                <span className="mono">{lot.rent > 0 ? fmtFCFA(lot.rent) : "—"}</span>
                <span style={{color: palette[lot.status] || "#7B7B73", fontWeight:600, fontSize:10}}>{lot.status}</span>
              </div>
            ))}
          </div>
        </div>

        {/* Bailleur */}
        {bailleur && (
          <div style={{paddingTop:10, borderTop:"1px dashed #E4E4DE", display:"grid", gridTemplateColumns:"1fr 1fr", gap:10, fontSize:11, color:"#5B5B53"}}>
            <div><span style={{fontWeight:600, color:"#7B7B73", textTransform:"uppercase", fontSize:9, letterSpacing:"0.06em"}}>Propriétaire</span><br/>{bailleur.name}</div>
            <div><span style={{fontWeight:600, color:"#7B7B73", textTransform:"uppercase", fontSize:9, letterSpacing:"0.06em"}}>Commission</span><br/>{(Number(form.commissionRate)*100).toFixed(1).replace(".",",")}% · {fmtFCFA(commission)}/mois</div>
          </div>
        )}
      </div>

      {!bailleur && (
        <Banner tone="warn" title="Bailleur non associé">
          Ce bien n'est pas encore lié à un propriétaire. Revenez à l'étape précédente pour l'associer avant d'enregistrer.
        </Banner>
      )}
    </FormSection>
  );
}

Object.assign(window, {
  PropertyFormModal, upsertProperty, useProperties, getPropertyById, nextPropertyId,
});

/* ╚══ end pages-property-form.jsx ══╝ */

/* ╔══ pages-charge-form.jsx ══╗ */
/* global React, FormSection, Field, FieldRow, Input, Textarea, MoneyInput,
          Select, RadioGroup, Segmented, Toggle, Banner, fmtFCFA, fmtCompact,
          MOCK, nextChargeId */

/* ═══════════════════════════════════════════════════════════════════════
   CHARGE FORM — création + modification d'une charge récurrente
   • Peut concerner UN ou PLUSIEURS biens à la fois.
   • Sur plusieurs biens : la charge est ventilée en une ligne par bien
     (montant identique par bien, ou montant total réparti) — chaque ligne
     s'impute correctement au compte de gérance du bailleur concerné.
   Étapes : Catégorie & biens · Montant & récurrence · Récapitulatif
   ═══════════════════════════════════════════════════════════════════════ */

const CHARGE_CATEGORIES = [
  { label:"Gardiennage 24/7",          account:"6225", recoverable:true,  freq:"M" },
  { label:"Eau commune",               account:"6061", recoverable:true,  freq:"M" },
  { label:"Électricité commune",       account:"6062", recoverable:true,  freq:"M" },
  { label:"Ménage parties communes",   account:"6284", recoverable:true,  freq:"M" },
  { label:"Ascenseur · entretien",     account:"6231", recoverable:true,  freq:"M" },
  { label:"Espaces verts / jardinier", account:"6225", recoverable:true,  freq:"M" },
  { label:"Syndic & gestion",          account:"6226", recoverable:false, freq:"Q" },
  { label:"Assurance multirisque",     account:"616",  recoverable:false, freq:"A", dueMonth:1 },
  { label:"Assurance habitation",      account:"616",  recoverable:false, freq:"A", dueMonth:1 },
  { label:"Taxe foncière",             account:"645",  recoverable:false, freq:"A", dueMonth:9 },
  { label:"Entretien & réparations",   account:"6231", recoverable:false, freq:"M" },
  { label:"Autre charge",              account:"628",  recoverable:false, freq:"M" },
];

const CHARGE_ACCOUNTS = [
  { value:"6225", label:"6225 · Gardiennage & surveillance" },
  { value:"6061", label:"6061 · Eau" },
  { value:"6062", label:"6062 · Électricité" },
  { value:"6284", label:"6284 · Nettoyage des locaux" },
  { value:"6231", label:"6231 · Entretien & réparations" },
  { value:"6226", label:"6226 · Honoraires syndic / gestion" },
  { value:"616",  label:"616 · Primes d'assurance" },
  { value:"645",  label:"645 · Impôts & taxes (foncière)" },
  { value:"628",  label:"628 · Autres charges externes" },
];

const KNOWN_SUPPLIERS = [
  "Sécurité Plus SARL","BG Security","SDE","SENELEC","Cleanline Services",
  "AG Syndic Dakar","NSIA Assurances","Sonam Assurances","DGID",
  "Schindler Sénégal","M. Mor Diouf","BTP Construct SA",
];

const MONTHS_FR = ["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"];

function makeEmptyCharge() {
  return {
    id: "",
    propertyIds: [],       // ← multi-biens
    scope: "immeuble",     // "immeuble" (parties communes / bien entier) | "lots" (lots précis)
    lots: [],              // références de lots quand scope = "lots" (1 seul bien)
    allocMode: "each",     // "each" (montant identique par bien) | "split" (total réparti)
    splitBy: "equal",      // "equal" | "lots"  (quand allocMode = "split")
    category: "",
    supplier: "",
    amount: "",
    freq: "M",
    dueMonth: 1,
    bearer: "locataire",   // "locataire" (récup) | "bailleur" | "agence"
    recoverable: true,     // dérivé de bearer, conservé pour les agrégations existantes
    account: "6225",
    notes: "",
  };
}

function fillCharge(c) {
  const base = makeEmptyCharge();
  const bearer = c.bearer || (c.recoverable ? "locataire" : "bailleur");
  return {
    ...base,
    ...c,
    propertyIds: c.propertyIds && c.propertyIds.length ? c.propertyIds
               : c.propertyId ? [c.propertyId] : [],
    scope: c.scope || (c.lots && c.lots.length ? "lots" : "immeuble"),
    lots: c.lots || [],
    bearer,
    recoverable: bearer === "locataire",
    amount: c.amount || "",
  };
}

/* Lots connus d'un bien : d'abord ceux définis sur le bien, sinon déduits
   des locataires en place (leur champ unit). */
function lotsForProperty(id) {
  const out = new Set();
  const prop = (MOCK.properties || []).find(p => p.id === id);
  if (prop && Array.isArray(prop.lots)) prop.lots.forEach(l => l.ref && out.add(l.ref));
  (MOCK.tenants || []).forEach(t => { if (t.propertyId === id && t.unit && t.unit !== "—") out.add(t.unit); });
  return [...out];
}

const annualOfCharge = (freq, amount) => {
  const a = Number(amount) || 0;
  return freq === "M" ? a * 12 : freq === "Q" ? a * 4 : a;
};

function freqLabelLocal(f) {
  return f === "M" ? "Mensuelle" : f === "Q" ? "Trimestrielle" : f === "A" ? "Annuelle" : "Ponctuelle";
}

const BEARER_META = {
  locataire: { short:"Récupérable · locataire", chip:"Récupérable",   color:"var(--pos-d)",  paperBg:"rgba(63,181,131,0.15)", paperFg:"#1F8A5B" },
  bailleur:  { short:"À charge bailleur",        chip:"Bailleur",      color:"var(--orange)", paperBg:"rgba(249,115,22,0.15)", paperFg:"#C2620E" },
  agence:    { short:"À charge agence",          chip:"Agence",        color:"var(--blue-d)", paperBg:"rgba(97,146,255,0.15)", paperFg:"#2A5BC2" },
};

/* Ventilation : renvoie [{propertyId, amount}] selon le mode choisi. */
function allocateCharge(form) {
  const ids = form.propertyIds || [];
  const amt = Number(form.amount) || 0;
  if (ids.length === 0) return [];
  if (ids.length === 1) return [{ propertyId: ids[0], amount: amt }];

  // Montant identique appliqué à chaque bien
  if (form.allocMode === "each") return ids.map(id => ({ propertyId: id, amount: amt }));

  // Montant TOTAL réparti
  const props = ids.map(id => MOCK.properties.find(p => p.id === id)).filter(Boolean);
  if (form.splitBy === "lots") {
    const totalUnits = props.reduce((s, p) => s + (p.units || 0), 0) || 1;
    let acc = 0;
    return props.map((p, i) => {
      let v = Math.round(amt * ((p.units || 0) / totalUnits));
      if (i === props.length - 1) v = amt - acc;  // le dernier absorbe l'arrondi
      acc += v;
      return { propertyId: p.id, amount: v };
    });
  }
  // équitable
  const base = Math.floor(amt / ids.length);
  return ids.map((id, i) => ({
    propertyId: id,
    amount: i === ids.length - 1 ? amt - base * (ids.length - 1) : base,
  }));
}

/* ═══ MODAL ═══ */
function ChargeFormModal({ open, mode = "create", initial, onClose, onSave, onSaveMany, onDelete }) {
  const [form, setForm] = React.useState(() => initial ? fillCharge(initial) : makeEmptyCharge());
  const [step, setStep] = React.useState("categorie");

  React.useEffect(() => {
    if (open) {
      setForm(initial ? fillCharge(initial) : makeEmptyCharge());
      setStep("categorie");
    }
  }, [open, initial]);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const toggleProperty = (id) => setForm(f => ({
    ...f,
    propertyIds: f.propertyIds.includes(id) ? f.propertyIds.filter(x => x !== id) : [...f.propertyIds, id],
  }));
  const toggleLot = (ref) => setForm(f => ({
    ...f,
    lots: f.lots.includes(ref) ? f.lots.filter(x => x !== ref) : [...f.lots, ref],
  }));

  const applyCategory = (cat) => {
    const meta = CHARGE_CATEGORIES.find(c => c.label === cat);
    setForm(f => ({
      ...f,
      category: cat,
      account: meta ? meta.account : f.account,
      recoverable: meta ? meta.recoverable : f.recoverable,
      freq: meta ? meta.freq : f.freq,
      dueMonth: meta && meta.dueMonth ? meta.dueMonth : f.dueMonth,
    }));
  };

  const selectedProps = (form.propertyIds || []).map(id => MOCK.properties.find(p => p.id === id)).filter(Boolean);
  const multi = selectedProps.length > 1;
  const canScopeLots = selectedProps.length === 1;       // lots ciblables sur un seul bien
  const scopeIsLots = canScopeLots && form.scope === "lots";
  const amountNum = Number(form.amount) || 0;
  const allocation = allocateCharge(form);
  const annualTotal = allocation.reduce((s, a) => s + annualOfCharge(form.freq, a.amount), 0);
  const perPeriodTotal = allocation.reduce((s, a) => s + a.amount, 0);

  const errors = {};
  if (!form.category.trim()) errors.category = "Catégorie requise";
  if (selectedProps.length === 0) errors.property = "Sélectionnez au moins un bien";
  if (amountNum <= 0) errors.amount = "Montant requis";
  if (!form.account) errors.account = "Compte comptable requis";
  const canSave = Object.keys(errors).length === 0;

  function handleSave() {
    if (!canSave) return;
    const dueMonth = form.freq === "A" ? Number(form.dueMonth) || 1 : undefined;
    const groupId = multi ? (form.groupId || "GRP-" + Date.now()) : undefined;
    // nextChargeId() reads from CHARGES which isn't mutated until after save,
    // so derive a numeric base ONCE and offset per row to guarantee unique ids.
    const baseNum = parseInt(String(nextChargeId()).replace(/\D/g, ""), 10) || 1;
    let offset = 0;
    const scope = (canScopeLots && form.scope === "lots" && form.lots.length > 0) ? "lots" : "immeuble";
    const lots = scope === "lots" ? form.lots : [];
    const rows = allocation.map((a, i) => {
      const reuseId = mode === "edit" && i === 0 && form.id;
      const id = reuseId ? form.id : "CHG-" + (baseNum + offset++);
      return {
        id,
        propertyId: a.propertyId,
        scope,
        lots,
        category: form.category,
        supplier: form.supplier,
        amount: a.amount,
        freq: form.freq,
        dueMonth,
        bearer: form.bearer,
        recoverable: form.bearer === "locataire",
        account: form.account,
        notes: form.notes,
        groupId,
      };
    });
    if (onSaveMany) onSaveMany(rows);
    else onSave?.(rows[0]);
  }

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

  if (!open) return null;

  const STEPS = [
    { id:"categorie", label:"Catégorie & biens", badge: selectedProps.length || null },
    { id:"montant",   label:"Montant & récurrence" },
    { id:"recap",     label:"Récapitulatif" },
  ];

  const headerProps = selectedProps.length === 0 ? ""
    : selectedProps.length === 1 ? selectedProps[0].name
    : `${selectedProps.length} biens`;

  return (
    <>
      <div onClick={onClose} style={{position:"fixed", inset:0, background:"rgba(8,12,20,0.6)", zIndex:200, backdropFilter:"blur(3px)"}}/>
      <div role="dialog" style={{
        position:"fixed", inset:"5vh 10vw", zIndex:201, maxWidth:1040, margin:"0 auto",
        background:"var(--d-bg)", border:"1px solid var(--d-line)", borderRadius:10,
        display:"flex", flexDirection:"column", overflow:"hidden",
        boxShadow:"0 30px 80px -20px rgba(0,0,0,0.6)", animation:"baill-pop 180ms ease-out",
      }}>
        <header style={{padding:"16px 22px", borderBottom:"1px solid var(--d-line)", display:"flex", alignItems:"center", justifyContent:"space-between", background:"var(--d-panel)"}}>
          <div>
            <div style={{fontSize:10.5, fontWeight:700, letterSpacing:"0.12em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:3}}>
              {mode === "edit" ? "Modifier la charge" : "Nouvelle charge"}
            </div>
            <div style={{fontSize:16, fontWeight:600, color:"var(--d-ink)", display:"flex", alignItems:"center", gap:10}}>
              {form.category || "Charge sans catégorie"}
              {headerProps && <span style={{color:"var(--d-ink-3)", fontWeight:400, fontSize:13}}>· {headerProps}</span>}
              {mode === "edit" && form.id && (
                <span className="mono" style={{fontSize:10.5, padding:"2px 7px", borderRadius:3, background:"var(--d-elev)", color:"var(--d-ink-3)"}}>{form.id}</span>
              )}
            </div>
          </div>
          <button onClick={onClose} title="Fermer · Esc" style={{
            width:32, height:32, borderRadius:6, color:"var(--d-ink-3)", fontSize:18, display:"grid", placeItems:"center",
            background:"transparent", border:"1px solid var(--d-line)", cursor:"pointer",
          }}>×</button>
        </header>

        <div style={{display:"grid", gridTemplateColumns:"220px 1fr", flex:1, minHeight:0}}>
          <nav style={{borderRight:"1px solid var(--d-line)", padding:"18px 14px", background:"var(--d-sunken)", display:"flex", flexDirection:"column", gap:2}}>
            {STEPS.map((s, i) => {
              const active = s.id === step;
              return (
                <button key={s.id} onClick={()=>setStep(s.id)} style={{
                  textAlign:"left", padding:"9px 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: "2px solid " + (active ? "var(--blue-d)" : "transparent"),
                  paddingLeft: 12, cursor:"pointer", display:"flex", alignItems:"center", justifyContent:"space-between", gap:8,
                }}>
                  <span><span className="mono" style={{color:"var(--d-ink-4)", marginRight:8, fontSize:10.5}}>{String(i+1).padStart(2,"0")}</span>{s.label}</span>
                  {s.badge != null && (
                    <span className="mono" style={{fontSize:10, padding:"1px 6px", borderRadius:3, background:"var(--d-bg)", color:"var(--d-ink-3)"}}>{s.badge}</span>
                  )}
                </button>
              );
            })}

            <div style={{marginTop:14, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
              <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>Validation</div>
              {Object.keys(errors).length === 0 ? (
                <div style={{display:"flex", gap:6, alignItems:"center", fontSize:11.5, color:"var(--pos-d)"}}>
                  <span style={{width:6, height:6, borderRadius:50, background:"var(--pos-d)"}}/>Prêt à enregistrer
                </div>
              ) : (
                <div style={{display:"flex", flexDirection:"column", gap:4}}>
                  {Object.values(errors).map((e,i) => (
                    <div key={i} style={{fontSize:11, color:"var(--orange)", lineHeight:1.45, display:"flex", gap:6, alignItems:"flex-start"}}>
                      <span style={{color:"var(--orange)", marginTop:2}}>!</span><span>{e}</span>
                    </div>
                  ))}
                </div>
              )}
            </div>

            {amountNum > 0 && (
              <div style={{marginTop:10, padding:"10px 12px", border:"1px solid var(--d-line)", borderRadius:6, background:"var(--d-bg)"}}>
                <div style={{fontSize:10, fontWeight:700, letterSpacing:"0.08em", color:"var(--d-ink-4)", textTransform:"uppercase", marginBottom:6}}>
                  {multi ? "Coût annuel cumulé" : "Coût annualisé"}
                </div>
                <div className="mono" style={{fontSize:15, fontWeight:600, color:"var(--d-ink)"}}>{fmtCompact(annualTotal)} <span style={{fontSize:11, color:"var(--d-ink-4)"}}>FCFA</span></div>
                {multi && (
                  <div style={{fontSize:10.5, color:"var(--d-ink-3)", marginTop:4}}>
                    {selectedProps.length} biens · {fmtCompact(perPeriodTotal)}/échéance
                  </div>
                )}
                <div style={{fontSize:10.5, color: (BEARER_META[form.bearer]||BEARER_META.bailleur).color, marginTop:4}}>
                  {(BEARER_META[form.bearer]||BEARER_META.bailleur).short}
                </div>
              </div>
            )}
          </nav>

          <div style={{overflow:"auto", padding:"22px 28px"}}>
            {step === "categorie" && <CCategorieStep form={form} set={set} applyCategory={applyCategory} toggleProperty={toggleProperty} toggleLot={toggleLot} selectedProps={selectedProps} canScopeLots={canScopeLots}/>}
            {step === "montant"   && <CMontantStep form={form} set={set} multi={multi} selectedProps={selectedProps} allocation={allocation}/>}
            {step === "recap"     && <CRecapStep form={form} selectedProps={selectedProps} allocation={allocation} annualTotal={annualTotal} multi={multi} scopeIsLots={scopeIsLots}/>}
          </div>
        </div>

        <footer style={{padding:"14px 22px", borderTop:"1px solid var(--d-line)", background:"var(--d-panel)", display:"flex", justifyContent:"space-between", alignItems:"center"}}>
          <div style={{fontSize:11.5, color:"var(--d-ink-3)", display:"flex", alignItems:"center", gap:10}}>
            {freqLabelLocal(form.freq)} · compte <span className="mono" style={{color:"var(--blue-d)"}}>{form.account}</span>
            {multi && <span>· {selectedProps.length} lignes générées</span>}
            {mode === "edit" && form.id && onDelete && (
              <button onClick={()=>{ if(confirm("Supprimer définitivement cette charge ?")) onDelete(form.id); }}
                style={{background:"none", border:"none", color:"var(--neg-d)", cursor:"pointer", fontSize:11.5, textDecoration:"underline", textUnderlineOffset:2, padding:0, marginLeft:6}}>
                Supprimer
              </button>
            )}
          </div>
          <div style={{display:"flex", gap:8}}>
            <button onClick={onClose} className="btn btn-ghost">Annuler</button>
            {STEPS.findIndex(s => s.id === step) < STEPS.length - 1 && (
              <button onClick={() => {
                const idx = STEPS.findIndex(s => s.id === step);
                setStep(STEPS[idx + 1].id);
              }} className="btn btn-secondary">Suivant →</button>
            )}
            <button onClick={handleSave} disabled={!canSave} className="btn btn-primary"
              style={{opacity: canSave ? 1 : 0.5, cursor: canSave ? "pointer" : "not-allowed"}}>
              {mode === "edit" ? "Enregistrer" : (multi ? `Créer ${selectedProps.length} charges` : "Créer la charge")}
            </button>
          </div>
        </footer>
      </div>
    </>
  );
}

/* ─── STEP 1 · Catégorie & biens ─── */
function CCategorieStep({ form, set, applyCategory, toggleProperty, toggleLot, selectedProps, canScopeLots }) {
  const allSelected = selectedProps.length === MOCK.properties.length;
  const selectAll = () => {
    if (allSelected) set("propertyIds", []);
    else set("propertyIds", MOCK.properties.map(p => p.id));
  };
  const theProp = selectedProps.length === 1 ? selectedProps[0] : null;
  const knownLots = theProp ? lotsForProperty(theProp.id) : [];
  const [lotInput, setLotInput] = React.useState("");
  const addLot = () => {
    const v = lotInput.trim();
    if (v && !form.lots.includes(v)) set("lots", [...form.lots, v]);
    setLotInput("");
  };
  return (
    <>
      <FormSection
        title="Catégorie de charge"
        subtitle="Choisissez une catégorie type — le compte comptable, la récupérabilité et la fréquence habituelle se pré-remplissent automatiquement."
      >
        <div style={{display:"grid", gridTemplateColumns:"repeat(3, 1fr)", gap:8, marginBottom:6}}>
          {CHARGE_CATEGORIES.filter(c => c.label !== "Autre charge").map(c => {
            const active = form.category === c.label;
            return (
              <button key={c.label} onClick={()=>applyCategory(c.label)} style={{
                padding:"10px 12px", textAlign:"left", cursor:"pointer",
                border:"1px solid " + (active ? "var(--blue-d)" : "var(--d-line)"),
                background: active ? "rgba(97,146,255,0.06)" : "var(--d-panel)", borderRadius:6,
              }}>
                <div style={{fontSize:12, fontWeight:600, color:"var(--d-ink)", marginBottom:3, lineHeight:1.3}}>{c.label}</div>
                <div style={{fontSize:10, color:"var(--d-ink-3)", display:"flex", gap:6, alignItems:"center"}}>
                  <span className="mono" style={{color:"var(--blue-d)"}}>{c.account}</span>
                  <span>·</span>
                  <span style={{color: c.recoverable ? "var(--pos-d)" : "var(--orange)"}}>{c.recoverable ? "récup" : "non-récup"}</span>
                </div>
              </button>
            );
          })}
        </div>

        <Field label="Ou catégorie personnalisée" hint="Saisie libre si aucune catégorie type ne convient">
          <Input value={form.category} onChange={e=>set("category", e.target.value)} placeholder="Ex : Désinsectisation trimestrielle"/>
        </Field>
      </FormSection>

      <FormSection
        title="Biens concernés"
        subtitle="Cochez un ou plusieurs biens. Une charge sur plusieurs biens (ex. contrat d'assurance ou de gardiennage groupé) sera ventilée en une ligne par bien pour s'imputer au bon bailleur."
        footer={<>
          <span>
            {selectedProps.length === 0 ? "Aucun bien sélectionné"
              : selectedProps.length === 1 ? "1 bien sélectionné"
              : `${selectedProps.length} biens sélectionnés`}
          </span>
          <button className="btn btn-secondary" style={{padding:"5px 12px"}} onClick={selectAll}>
            {allSelected ? "Tout désélectionner" : "Tout sélectionner"}
          </button>
        </>}
      >
        <div style={{display:"grid", gridTemplateColumns:"repeat(2, 1fr)", gap:8, maxHeight:280, overflow:"auto", paddingRight:2}}>
          {MOCK.properties.map(p => {
            const checked = form.propertyIds.includes(p.id);
            return (
              <button key={p.id} onClick={()=>toggleProperty(p.id)} style={{
                display:"flex", alignItems:"center", gap:10, textAlign:"left", cursor:"pointer",
                padding:"10px 12px", borderRadius:6,
                border:"1px solid " + (checked ? "var(--blue-d)" : "var(--d-line)"),
                background: checked ? "rgba(97,146,255,0.06)" : "var(--d-panel)",
              }}>
                <span style={{
                  width:16, height:16, borderRadius:4, flexShrink:0, 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>
                <span style={{minWidth:0}}>
                  <span style={{display:"block", fontSize:12.5, fontWeight:500, color:"var(--d-ink)", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{p.name}</span>
                  <span style={{display:"block", fontSize:10.5, color:"var(--d-ink-3)", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}>{p.owner} · {p.units} lot{p.units>1?"s":""}</span>
                </span>
              </button>
            );
          })}
        </div>

        <Field label="Fournisseur / prestataire" optional hint="Bénéficiaire du règlement">
          <Input value={form.supplier} onChange={e=>set("supplier", e.target.value)} list="charge-suppliers" placeholder="Ex : SENELEC, Sécurité Plus SARL…"/>
          <datalist id="charge-suppliers">
            {KNOWN_SUPPLIERS.map(s => <option key={s} value={s}/>)}
          </datalist>
        </Field>
      </FormSection>

      {/* Périmètre — uniquement pour un seul bien */}
      {canScopeLots && theProp && (
        <FormSection
          title="Périmètre de la charge"
          subtitle="La charge porte-t-elle sur l'immeuble entier (parties communes) ou seulement sur certains lots ? Une charge sur lot(s) précis n'est imputée qu'à ces lots."
        >
          <RadioGroup
            layout="row"
            value={form.scope}
            onChange={v=>set("scope", v)}
            options={[
              { value:"immeuble", label:"Immeuble entier / parties communes" },
              { value:"lots",     label:"Lot(s) précis" },
            ]}
          />

          {form.scope === "lots" && (
            <div style={{marginTop:12}}>
              {knownLots.length > 0 && (
                <>
                  <div style={{fontSize:11, color:"var(--d-ink-3)", marginBottom:8}}>Lots du bien <b style={{color:"var(--d-ink-2)"}}>{theProp.name}</b> — cliquez pour (dé)sélectionner :</div>
                  <div style={{display:"flex", flexWrap:"wrap", gap:8, marginBottom:12}}>
                    {knownLots.map(ref => {
                      const on = form.lots.includes(ref);
                      return (
                        <button key={ref} onClick={()=>toggleLot(ref)} style={{
                          padding:"5px 12px", borderRadius:5, cursor:"pointer", fontSize:12, fontFamily:"var(--font-mono)",
                          border:"1px solid " + (on ? "var(--blue-d)" : "var(--d-line)"),
                          background: on ? "rgba(97,146,255,0.12)" : "var(--d-panel)",
                          color: on ? "var(--blue-d)" : "var(--d-ink-2)", fontWeight: on ? 600 : 400,
                        }}>{on ? "✓ " : ""}{ref}</button>
                      );
                    })}
                  </div>
                </>
              )}
              <Field label="Ajouter un lot" hint="Référence libre si le lot n'est pas listé (ex. A-203, RDC-droite)">
                <div style={{display:"flex", gap:8}}>
                  <Input value={lotInput} onChange={e=>setLotInput(e.target.value)}
                    onKeyDown={e=>{ if(e.key==="Enter"){ e.preventDefault(); addLot(); } }}
                    placeholder="A-203" style={{fontFamily:"var(--font-mono)"}}/>
                  <button className="btn btn-secondary" style={{padding:"0 16px", whiteSpace:"nowrap"}} onClick={addLot}>Ajouter</button>
                </div>
              </Field>
              {form.lots.length > 0 && (
                <div style={{display:"flex", flexWrap:"wrap", gap:6, marginTop:10}}>
                  {form.lots.map(ref => (
                    <span key={ref} style={{
                      display:"inline-flex", alignItems:"center", gap:6, padding:"3px 6px 3px 10px", borderRadius:5,
                      background:"var(--d-elev)", border:"1px solid var(--d-line)", fontSize:11.5, fontFamily:"var(--font-mono)", color:"var(--d-ink)",
                    }}>
                      {ref}
                      <button onClick={()=>toggleLot(ref)} title="Retirer" style={{
                        width:16, height:16, borderRadius:3, border:"none", background:"transparent", color:"var(--d-ink-4)", cursor:"pointer", fontSize:12, lineHeight:1,
                      }}>×</button>
                    </span>
                  ))}
                </div>
              )}
              {form.lots.length === 0 && (
                <div style={{marginTop:8, fontSize:11, color:"var(--orange)"}}>Sélectionnez au moins un lot, sinon la charge portera sur l'immeuble entier.</div>
              )}
            </div>
          )}
        </FormSection>
      )}
    </>
  );
}

/* ─── STEP 2 · Montant & récurrence ─── */
function CMontantStep({ form, set, multi, selectedProps, allocation }) {
  const propName = id => (MOCK.properties.find(p => p.id === id) || {}).name || id;
  return (
    <>
      {multi && (
        <FormSection
          title="Répartition entre les biens"
          subtitle="Comment le montant saisi est appliqué aux biens sélectionnés."
        >
          <RadioGroup
            value={form.allocMode}
            onChange={v=>set("allocMode", v)}
            options={[
              { value:"each",  label:"Montant identique par bien", description:"Le montant saisi s'applique tel quel à CHAQUE bien (ex. un contrat de gardiennage par immeuble au même tarif)." },
              { value:"split", label:"Montant total à répartir",    description:"Le montant saisi est le total ; il est réparti entre les biens (ex. une seule facture d'assurance groupée)." },
            ]}
          />

          {form.allocMode === "split" && (
            <Field label="Clé de répartition">
              <Segmented
                value={form.splitBy}
                onChange={v=>set("splitBy", v)}
                options={[
                  { value:"equal", label:"Parts égales" },
                  { value:"lots",  label:"Au prorata des lots" },
                ]}
              />
            </Field>
          )}
        </FormSection>
      )}

      <FormSection
        title="Montant et récurrence"
        subtitle={multi
          ? (form.allocMode === "each" ? "Montant d'une échéance, pour chaque bien." : "Montant total d'une échéance, à répartir entre les biens.")
          : "Le montant est celui d'une échéance. La fréquence détermine combien de fois il est imputé sur l'année."}
      >
        <FieldRow columns="1fr 1fr">
          <Field label={multi && form.allocMode === "split" ? "Montant total par échéance" : "Montant par échéance"} required>
            <MoneyInput value={form.amount} onChange={e=>set("amount", e.target.value.replace(/[^0-9]/g,""))}/>
          </Field>
          <Field label="Coût annuel (calculé)" hint={multi ? "Cumulé sur tous les biens" : "Montant × fréquence"}>
            <Input value={fmtFCFA(allocation.reduce((s,a)=>s+annualOfCharge(form.freq, a.amount),0))} readOnly style={{
              fontFamily:"var(--font-mono)", textAlign:"right", color:"var(--blue-d)",
              background:"var(--d-elev)", fontWeight:600,
            }}/>
          </Field>
        </FieldRow>

        <Field label="Fréquence" required>
          <Segmented
            value={form.freq}
            onChange={v=>set("freq", v)}
            options={[
              {value:"M", label:"Mensuelle"},
              {value:"Q", label:"Trimestrielle"},
              {value:"A", label:"Annuelle"},
            ]}
          />
        </Field>

        {form.freq === "A" && (
          <Field label="Mois d'échéance" hint="Mois où la charge annuelle est due">
            <Select
              value={String(form.dueMonth)}
              onChange={v=>set("dueMonth", Number(v))}
              options={MONTHS_FR.map((m, i) => ({ value: String(i+1), label: m }))}
            />
          </Field>
        )}

        {/* Aperçu de la ventilation */}
        {multi && Number(form.amount) > 0 && (
          <div style={{marginTop:4, border:"1px solid var(--d-line)", borderRadius:6, overflow:"hidden"}}>
            <div style={{padding:"8px 12px", background:"var(--d-sunken)", fontSize:10.5, fontWeight:700, letterSpacing:"0.06em", textTransform:"uppercase", color:"var(--d-ink-4)"}}>
              Ventilation par bien · {freqLabelLocal(form.freq).toLowerCase()}
            </div>
            {allocation.map((a, i) => (
              <div key={i} style={{display:"flex", justifyContent:"space-between", alignItems:"center", padding:"7px 12px", borderTop: i ? "1px solid var(--d-line-2)" : "none", fontSize:12}}>
                <span style={{color:"var(--d-ink-2)"}}>{propName(a.propertyId)}</span>
                <span className="mono" style={{fontWeight:500}}>{fmtFCFA(a.amount)}</span>
              </div>
            ))}
          </div>
        )}
      </FormSection>

      <FormSection
        title="Imputation comptable"
        subtitle="Qui supporte cette charge ? Cela détermine son traitement comptable et son impact sur le net versé au bailleur."
      >
        <Field label="À la charge de" required>
          <RadioGroup
            value={form.bearer}
            onChange={v=>set("bearer", v)}
            options={[
              { value:"locataire", label:"Locataire — récupérable", description:"Refacturée au locataire via la quittance (provision pour charges). N'impacte pas le net du bailleur." },
              { value:"bailleur",  label:"Bailleur", description:"Débitée du compte du bailleur. Réduit le montant net qui lui est versé." },
              { value:"agence",    label:"Agence", description:"Supportée par l'agence de gestion (charge interne). N'impacte ni le locataire ni le bailleur." },
            ]}
          />
        </Field>

        <Field label="Compte comptable (SYSCOHADA)" required hint="Pré-rempli selon la catégorie, modifiable">
          <Select value={form.account} onChange={v=>set("account", v)} options={CHARGE_ACCOUNTS}/>
        </Field>

        <Field label="Notes / référence" optional>
          <Textarea rows={2} value={form.notes} onChange={e=>set("notes", e.target.value)} placeholder="Ex : contrat n° 2024-118 · renégociation prévue en janvier…"/>
        </Field>
      </FormSection>
    </>
  );
}

/* ─── STEP 3 · Récap ─── */
function CRecapStep({ form, selectedProps, allocation, annualTotal, multi }) {
  const amountNum = Number(form.amount) || 0;
  const propMeta = id => (MOCK.properties.find(p => p.id === id) || {});
  return (
    <FormSection
      title="Récapitulatif"
      subtitle={multi
        ? `Cette charge générera ${selectedProps.length} lignes, une par bien, imputées séparément à chaque compte de gérance.`
        : "Vérifiez avant d'enregistrer. La charge sera immédiatement prise en compte dans le calendrier et les rapports."}
    >
      <div style={{
        background:"#FAFAF7", borderRadius:6, padding:"22px 24px",
        fontFamily:"var(--font-sans)", border:"1px solid #E4E4DE", color:"#1A1A1A",
      }}>
        <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}}>Charge {freqLabelLocal(form.freq).toLowerCase()}</div>
            <div style={{fontSize:15, fontWeight:700, color:"#1A1A1A"}}>{form.category || "—"}</div>
            <div style={{fontSize:11, color:"#7B7B73", marginTop:3}}>
              {selectedProps.length === 0 ? "—" : selectedProps.length === 1 ? selectedProps[0].name : `${selectedProps.length} biens`}
              {scopeIsLots && form.lots.length > 0 ? ` · lot${form.lots.length>1?"s":""} ${form.lots.join(", ")}` : selectedProps.length === 1 ? " · immeuble entier" : ""}
              {form.supplier ? ` · ${form.supplier}` : ""}
            </div>
          </div>
          <span style={{
            fontSize:10, fontWeight:600, padding:"3px 9px", borderRadius:3,
            background: (BEARER_META[form.bearer]||BEARER_META.bailleur).paperBg,
            color: (BEARER_META[form.bearer]||BEARER_META.bailleur).paperFg,
          }}>{(BEARER_META[form.bearer]||BEARER_META.bailleur).chip}</span>
        </div>

        <div style={{display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:0, border:"1px solid #E4E4DE", borderRadius:4, overflow:"hidden", marginBottom: multi ? 16 : 0}}>
          {[
            [multi && form.allocMode === "split" ? "Montant total" : multi ? "Montant / bien" : "Montant", amountNum > 0 ? fmtFCFA(amountNum) : "—"],
            ["Fréquence", freqLabelLocal(form.freq) + (form.freq === "A" ? ` · ${MONTHS_FR[(form.dueMonth||1)-1]}` : "")],
            ["Coût annuel", fmtFCFA(annualTotal)],
            ["Compte", form.account],
          ].map(([k,v],i) => (
            <div key={i} style={{padding:"10px 12px", borderRight: i < 3 ? "1px solid #EEEEE6" : "none"}}>
              <div style={{fontSize:9, textTransform:"uppercase", color:"#7B7B73", letterSpacing:"0.05em", fontWeight:600, marginBottom:3}}>{k}</div>
              <div className="mono" style={{fontSize:12.5, fontWeight:600, color:"#1A1A1A"}}>{v}</div>
            </div>
          ))}
        </div>

        {multi && (
          <div>
            <div style={{fontSize:9, textTransform:"uppercase", letterSpacing:"0.06em", fontWeight:600, color:"#7B7B73", marginBottom:8}}>Ventilation · {allocation.length} lignes</div>
            <div style={{display:"flex", flexDirection:"column", gap:1}}>
              {allocation.map((a, i) => {
                const p = propMeta(a.propertyId);
                return (
                  <div key={i} style={{display:"grid", gridTemplateColumns:"1fr 1fr 100px", gap:8, padding:"6px 0", borderBottom:"1px solid #EEEEE6", fontSize:11, color:"#3A3A33", alignItems:"center"}}>
                    <span style={{fontWeight:500}}>{p.name}</span>
                    <span style={{color:"#7B7B73"}}>{p.owner}</span>
                    <span className="mono" style={{textAlign:"right", fontWeight:600}}>{fmtFCFA(a.amount)}</span>
                  </div>
                );
              })}
            </div>
          </div>
        )}

        {form.notes && (
          <div style={{marginTop:14, fontSize:11, color:"#5B5B53", lineHeight:1.5}}>
            <span style={{fontWeight:600, color:"#7B7B73", textTransform:"uppercase", fontSize:9, letterSpacing:"0.06em"}}>Notes</span><br/>{form.notes}
          </div>
        )}
      </div>

      {selectedProps.length === 0 && (
        <Banner tone="warn" title="Aucun bien sélectionné">
          Revenez à la première étape pour rattacher cette charge à au moins un bien.
        </Banner>
      )}
    </FormSection>
  );
}

Object.assign(window, { ChargeFormModal });

/* ╚══ end pages-charge-form.jsx ══╝ */
