/* global React, Icon, Spreadsheet */
const { useState: useStateE } = React;

// ==================== EX-ANTE APPRAISAL ====================
// Data: window.melr.useExanteDossiers(projectId) returns live
// dossiers (seed-batch-3.sql adds 3 dossiers: P-001 v1/v2 + P-002 v1)
// with NPV/IRR/DSCR/etc. The Ex-ante UI below is a complex multi-tab
// document with extensive sub-section content (cashflows, sensitivity,
// actors, environment, institutional, funding, impact, recommendations)
// whose detailed structure differs from the schema's flat dossier
// header. The fixture is kept for rendering; the hook is exposed via
// window.melr so a future refactor can read live KPIs from the header.
const EXANTE_TOC = (lang) => [
  { num: "II", id: "ii", title: lang === "fr" ? "Contexte et justification" : "Context and rationale", children: [
    { num: "2.1", id: "ii-1", title: lang === "fr" ? "Secteur" : "Sector" },
    { num: "2.2", id: "ii-2", title: lang === "fr" ? "Situation régionale" : "Regional situation" },
    { num: "2.3", id: "ii-3", title: lang === "fr" ? "Situation actuelle des sites" : "Current site situation" },
  ]},
  { num: "III", id: "iii", title: lang === "fr" ? "Situation sans projet (Baseline)" : "Without-project situation (Baseline)", children: [
    { num: "3.1", id: "iii-1", title: lang === "fr" ? "Données par site" : "Site data" },
    { num: "3.2", id: "iii-2", title: lang === "fr" ? "Comptes d'exploitation actuels" : "Current operating accounts" },
    { num: "3.3", id: "iii-3", title: lang === "fr" ? "Problèmes identifiés" : "Issues identified" },
  ]},
  { num: "IV", id: "iv", title: lang === "fr" ? "Description du projet" : "Project description", children: [
    { num: "4.1", id: "iv-1", title: lang === "fr" ? "Objectifs" : "Objectives" },
    { num: "4.2", id: "iv-2", title: lang === "fr" ? "Composantes & réalisables" : "Components & deliverables" },
    { num: "4.3", id: "iv-3", title: lang === "fr" ? "Calendrier" : "Schedule" },
  ]},
  { num: "V", id: "v", title: lang === "fr" ? "Analyse financière" : "Financial analysis", children: [
    { num: "5.1", id: "v-1", title: "CAPEX" },
    { num: "5.2", id: "v-2", title: "OPEX" },
    { num: "5.3", id: "v-3", title: lang === "fr" ? "Projections (10 ans)" : "Projections (10 yrs)" },
    { num: "5.4", id: "v-4", title: "VAN · TRI · DSCR" },
  ]},
  { num: "VI", id: "vi", title: lang === "fr" ? "Analyse économique" : "Economic analysis", children: [
    { num: "6.1", id: "vi-1", title: lang === "fr" ? "Coûts-bénéfices" : "Cost-benefit" },
    { num: "6.2", id: "vi-2", title: "MPR" },
    { num: "6.3", id: "vi-3", title: "VANE · TIRE" },
  ]},
  { num: "VII", id: "vii", title: lang === "fr" ? "Analyse par acteur" : "Stakeholder analysis", children: [
    { num: "7.1", id: "vii-1", title: lang === "fr" ? "Comptes avec projet" : "With-project accounts" },
    { num: "7.2", id: "vii-2", title: lang === "fr" ? "Distribution de la valeur" : "Value distribution" },
    { num: "7.3", id: "vii-3", title: lang === "fr" ? "Impact par catégorie" : "Impact per category" },
  ]},
  { num: "VIII", id: "viii", title: lang === "fr" ? "Sensibilité" : "Sensitivity", children: [
    { num: "8.1", id: "viii-1", title: lang === "fr" ? "Variation paramètres" : "Parameter variation" },
    { num: "8.2", id: "viii-2", title: lang === "fr" ? "Multicritères" : "Multi-criteria" },
  ]},
  { num: "IX", id: "ix", title: lang === "fr" ? "Environnemental & social" : "Environmental & social", children: [
    { num: "9.1", id: "ix-1", title: "EIES" },
    { num: "9.2", id: "ix-2", title: lang === "fr" ? "Plan de gestion" : "Management plan" },
    { num: "9.3", id: "ix-3", title: lang === "fr" ? "Genre & vulnérables" : "Gender & vulnerable" },
  ]},
  { num: "X", id: "x", title: lang === "fr" ? "Institutionnel" : "Institutional", children: [
    { num: "10.1", id: "x-1", title: lang === "fr" ? "Modèles de gestion" : "Management models" },
    { num: "10.2", id: "x-2", title: lang === "fr" ? "Durabilité" : "Sustainability" },
  ]},
  { num: "XI", id: "xi", title: lang === "fr" ? "Plan de financement" : "Financing plan" },
  { num: "XII", id: "xii", title: lang === "fr" ? "Impact sur les Finances publiques" : "Impact on public finances" },
  { num: "XIII", id: "xiii", title: lang === "fr" ? "Recommandations" : "Recommendations" },
];

function ExAnte({ t, lang, activeSection, isSuperAdmin, actingOrgId, activeOrgId, myOrgId, isAdmin, hasPerm }) {
  // "Nouvelle évaluation" creates an exante_dossiers row + can also
  // create a project inline. "Générer rapport" downloads the .docx
  // and is treated as an admin action (the report is the official
  // deliverable to the donor). "Soumettre dossier" actually submits
  // the ex-ante dossier into the validation workflow — also admin.
  // Only "Recalculer" stays open to any reader (it just refreshes).
  const canManageExante = !!isSuperAdmin || !!isAdmin
    || (hasPerm && hasPerm("users.manage"));
  const { useEffect } = React;
  const [active, setActive] = useStateE(activeSection || "v");
  const [identEditOpen, setIdentEditOpen] = useStateE(false);
  const [inputsEditOpen, setInputsEditOpen] = useStateE(false);
  const [scenEditOpen, setScenEditOpen] = useStateE(false);
  const [phaseModalOpen, setPhaseModalOpen]   = useStateE(false);
  const [activityModalOpen, setActivityModalOpen] = useStateE(false);
  const [editingActivity, setEditingActivity] = useStateE(null);
  const [capexModalOpen, setCapexModalOpen] = useStateE(false);
  const [editingCapex, setEditingCapex] = useStateE(null);
  const [opexModalOpen, setOpexModalOpen] = useStateE(false);
  const [editingOpex, setEditingOpex] = useStateE(null);
  const [revModalOpen, setRevModalOpen] = useStateE(false);
  const [editingRev, setEditingRev] = useStateE(null);
  const [finModalOpen, setFinModalOpen] = useStateE(false);
  const [editingFin, setEditingFin] = useStateE(null);
  const [pubTransferModalOpen, setPubTransferModalOpen] = useStateE(false);
  const [editingPubTransfer, setEditingPubTransfer] = useStateE(null);
  const [mprTransferModalOpen, setMprTransferModalOpen] = useStateE(false);
  const [editingMprTransfer, setEditingMprTransfer] = useStateE(null);
  const [extModalOpen, setExtModalOpen] = useStateE(false);
  const [editingExt, setEditingExt] = useStateE(null);
  const [editingQuality, setEditingQuality] = useStateE(null);
  const [editingMc, setEditingMc] = useStateE(null);
  const [reportOpen, setReportOpen] = useStateE(false);
  const [stkModalOpen, setStkModalOpen] = useStateE(false);
  const [editingStk, setEditingStk] = useStateE(null);
  const [editingCf, setEditingCf] = useStateE(null);
  const [instEditOpen, setInstEditOpen] = useStateE(false);
  const [submitOpen, setSubmitOpen] = useStateE(false);
  const [recalcBusy, setRecalcBusy] = useStateE(false);
  const [newDossierOpen, setNewDossierOpen] = useStateE(false);
  // ID of the dossier the user explicitly selected (lets them browse
  // older dossiers / scenarios). Falls back to liveDossiers[0] when null.
  const [selectedDossierId, setSelectedDossierId] = useStateE(null);
  // Sync internal active state with the activeSection prop from the
  // sidebar drill-down. activeSection can be either:
  //   • a CRUD card anchor ("card-capex", "card-mpr-p1", …) → scroll there
  //   • a demo-doc section ("ii", "iii", …) → scroll there + highlight TOC
  //   • "exante-demo-doc" → scroll to the start of the legacy demo doc
  //   • null → land on the overview (scroll the container back to top)
  //
  // Cards mount asynchronously while data hooks fetch data, so we
  // can't rely on a single requestAnimationFrame. We use a MutationObserver
  // that fires as soon as the target element appears in the DOM, with a
  // 5-second safety timeout. Existing-in-DOM cases scroll immediately.
  useEffect(() => {
    if (!activeSection) {
      const sc = document.querySelector(".scroll");
      if (sc) sc.scrollTo({ top: 0, behavior: "smooth" });
      return;
    }
    setActive(activeSection);

    const doScroll = (el) => {
      // Find the nearest scrollable ancestor explicitly — sometimes
      // scrollIntoView misbehaves when the page is in a transitional
      // state. Using scrollTo on the .scroll container gives us full
      // control over the offset.
      const scroller = document.querySelector(".scroll");
      if (scroller) {
        const elRect = el.getBoundingClientRect();
        const scRect = scroller.getBoundingClientRect();
        const targetTop = scroller.scrollTop + (elRect.top - scRect.top) - 12;
        scroller.scrollTo({ top: Math.max(0, targetTop), behavior: "smooth" });
      } else {
        el.scrollIntoView({ behavior: "smooth", block: "start" });
      }
    };

    let done = false;
    const tryNow = () => {
      const el = document.getElementById(activeSection);
      if (el) {
        done = true;
        // Defer one frame so layout settles after the DOM mutation.
        requestAnimationFrame(() => doScroll(el));
        return true;
      }
      return false;
    };

    if (tryNow()) return; // already in DOM

    const observer = new MutationObserver(() => { if (!done) tryNow(); });
    observer.observe(document.body, { childList: true, subtree: true });
    const safety = setTimeout(() => { observer.disconnect(); }, 5000);

    return () => {
      observer.disconnect();
      clearTimeout(safety);
    };
  }, [activeSection]);
  const toc = EXANTE_TOC(lang);
  const { data: allDossiers, refresh: refreshDossiers } = window.melr.useExanteDossiers();
  // Multi-org: narrow the dossier list to the effective org so 'latest'
  // (the dossier that drives the entire screen via latestDossierId) is the
  // most recent dossier for the user's current context — not the platform-
  // wide latest one. The dossier's project carries organization_id via
  // the embed; filter on that.
  const effOrg = (isSuperAdmin && actingOrgId) ? actingOrgId : (activeOrgId || myOrgId);
  const liveDossiers = (effOrg && allDossiers)
    ? allDossiers.filter((d) => d.projects && d.projects.organization_id === effOrg)
    : (allDossiers || []);
  // Honour an explicit selection from the dossier dropdown; otherwise
  // default to the most-recent dossier (liveDossiers is already sorted
  // by created_at desc via the SQL hook).
  const latest = (selectedDossierId && liveDossiers.find((d) => d.id === selectedDossierId))
    || (liveDossiers && liveDossiers[0])
    || null;
  const latestDossierId = latest && latest.id;
  // Identification CRUD — Phase 1 of the structured ex-ante builder.
  const { data: ident, refresh: refreshIdent, realtime: identRealtime } =
    window.melr.useExanteIdentification(latestDossierId);
  const { data: inputs, refresh: refreshInputs } =
    window.melr.useExanteInputs(latestDossierId);
  const { data: scen, refresh: refreshScen } =
    window.melr.useExanteScenarios(latestDossierId);
  const { phases: exPhases, activities: exActivities, refresh: refreshCal } =
    window.melr.useExanteCalendar(latestDossierId);
  const { data: capexLines, refresh: refreshCapex } =
    window.melr.useExanteCapex(latestDossierId);
  const { data: opexLines, refresh: refreshOpex } =
    window.melr.useExanteOpex(latestDossierId);
  const { data: revLines, refresh: refreshRev } =
    window.melr.useExanteRevenue(latestDossierId);
  const { data: finSources, refresh: refreshFin } =
    window.melr.useExanteFinancing(latestDossierId);
  const { data: pubTransfers, refresh: refreshPubTransfers } =
    window.melr.useExantePublicTransfers(latestDossierId);
  const { data: mprTransfers, refresh: refreshMprTransfers } =
    window.melr.useExanteMprTransfers(latestDossierId);
  const { data: externalities, refresh: refreshExternalities } =
    window.melr.useExanteExternalities(latestDossierId);
  const { data: conversionFactors, refresh: refreshCf } =
    window.melr.useExanteConversionFactors(latestDossierId);
  const { data: qualityItems, refresh: refreshQuality } =
    window.melr.useExanteQualityItems(latestDossierId);
  const { data: mcItems, refresh: refreshMc } =
    window.melr.useExanteMulticriteriaItems(latestDossierId);
  const { data: stakeholders, refresh: refreshStk } =
    window.melr.useExanteStakeholders(latestDossierId);

  const stakeholderAccounts = (window.exanteEngine && exanteComputed && stakeholders)
    ? window.exanteEngine.computeStakeholderAccounts({
        stakeholders, inputs, scen,
        revenueLines: revLines, opexLines, exanteComputed,
      })
    : null;

  const { data: institutional, refresh: refreshInst } =
    window.melr.useExanteInstitutional(latestDossierId);

  const qualityResult = window.exanteEngine && qualityItems
    ? window.exanteEngine.computeQualityGrid(qualityItems)
    : null;
  const mcResult = window.exanteEngine && mcItems
    ? window.exanteEngine.computeMulticriteria(mcItems)
    : null;

  // ===== Final decision (Phase 4.2) =====
  // Always compute (the engine accepts null/empty inputs and returns a
  // "no_go" with explicit reasons) so the sidebar's "Décision finale"
  // entry can always scroll to the card.
  const finalDecision = window.exanteEngine
    ? window.exanteEngine.computeFinalDecision({
        financial: exanteComputed && exanteComputed.indicators,
        economic:  mprResult && mprResult.indicators,
        quality:   qualityResult,
        multicriteria: mcResult,
      })
    : null;

  // MPR computation (Phase 3.2). Runs whenever any underlying row changes.
  const mprResult = (exanteComputed && window.exanteEngine && latestDossierId)
    ? window.exanteEngine.computeMpr({
        inputs,
        capexLines,
        opexByYear: exanteComputed.opexByYear,
        revenueByYear: exanteComputed.revenueByYear,
        debtSchedule: exanteComputed.debt,
        mprTransfers,
        externalities,
        conversionFactors,
      })
    : null;

  // ========== EXANTE LIVE CALCULATION ENGINE ==========
  // computeAll runs whenever any underlying row changes, producing
  // capex/financing/opex/revenue projections + BFR + debt schedule +
  // PL + cash flow + DSCR + indicators in one pass.
  const exanteComputed = (latestDossierId && window.exanteEngine)
    ? window.exanteEngine.computeAll({
        inputs, scen,
        capexLines, opexLines, revenueLines: revLines, financingSources: finSources,
      })
    : null;
  const liveTitle = latest && latest.projects && latest.projects.code
    ? `${t("nav.exante")} — ${latest.projects.code} (${latest.version})`
    : `${t("nav.exante")} — P-241`;
  const { currency } = window.melr.useCurrency();
  // Live amounts in exante_dossiers are EUR raw. Convert to
  // millions before passing to formatMoney (which expects M EUR).
  const fmtM = (n) => n == null ? "—" : window.melr.formatMoney(Number(n) / 1_000_000, currency, lang);
  // Convenience for fixture values already expressed in M EUR.
  const fmtMM = (n) => n == null ? "—" : window.melr.formatMoney(Number(n), currency, lang);
  const fmtP = (n) => n == null ? "—" : Number(n).toFixed(1) + " %";
  const decisionBadge = (d) => {
    if (!d) return null;
    const colors = { go: { bg: "#dcfce7", c: "#166534" }, no_go: { bg: "#fee2e2", c: "#991b1b" }, conditional: { bg: "#fef3c7", c: "#92400e" } };
    const col = colors[d] || colors.conditional;
    const label = lang === "fr"
      ? ({ go: "GO", no_go: "NO-GO", conditional: "Conditionnel" })[d] || d
      : ({ go: "GO", no_go: "NO-GO", conditional: "Conditional" })[d] || d;
    return <span style={{ background: col.bg, color: col.c, padding: "2px 10px", borderRadius: 999, fontSize: 11, fontWeight: 600 }}>{label}</span>;
  };
  return (
    <div className="page" style={{ minHeight: 0 }}>
      <div className="page-header">
        <div className="page-eyebrow">{lang === "fr" ? "MODULE / ÉVALUATION EX ANTE" : "MODULE / EX-ANTE APPRAISAL"}</div>
        <div className="page-header-row">
          <div>
            <h1 className="page-title">{liveTitle}</h1>
            <div className="page-sub">{lang === "fr"
              ? "Document structuré II → XIII · feuilles Excel intégrées (CAPEX, OPEX, projections, VAN/TRI, MPR, sensibilité) · auto-recalcul à chaque saisie"
              : "Structured document II → XIII · embedded Excel sheets · auto-recalculation"}</div>
          </div>
          <div className="page-header-actions" style={{ flexWrap: "wrap", gap: 6 }}>
            {/* Project repository — every distinct project that has at
                least one dossier in the effective org. Always visible
                (even when there's only one) so the user has a single
                obvious place to pick which project to inspect, and an
                explicit signal that the screen is dossier-scoped. The
                "Tous les dossiers" entry collapses back to the latest-
                across-all behaviour. */}
            {liveDossiers.length > 0 && (
              <select
                value={(latest && latest.projects && latest.projects.code) || ""}
                onChange={(e) => {
                  const code = e.target.value;
                  if (!code) { setSelectedDossierId(null); return; }
                  // Pick the most recent dossier for that project
                  // (liveDossiers is already sorted desc by created_at).
                  const d = liveDossiers.find((x) => x.projects && x.projects.code === code);
                  if (d) setSelectedDossierId(d.id);
                }}
                style={{ padding: "4px 8px", borderRadius: 6, border: "1px solid var(--line)",
                         fontSize: 12, background: "var(--bg)", color: "var(--text)",
                         minWidth: 180, maxWidth: 260 }}
                title={lang === "fr" ? "Projet évalué (repository)" : "Evaluated project (repository)"}>
                {(() => {
                  // De-dupe by project code, keep the most recent
                  // dossier per project for display.
                  const seen = new Set();
                  const projectsList = [];
                  for (const d of liveDossiers) {
                    const code = d.projects && d.projects.code;
                    if (!code || seen.has(code)) continue;
                    seen.add(code);
                    projectsList.push({ code, name: (d.projects && d.projects.name_fr) || code, latestVersion: d.version });
                  }
                  return projectsList.map((p) => (
                    <option key={p.code} value={p.code}>
                      {p.code} — {p.name} ({p.latestVersion})
                    </option>
                  ));
                })()}
              </select>
            )}
            {/* When the selected project has multiple dossiers, expose
                a second dropdown to switch versions / scenarios. */}
            {latest && liveDossiers.filter((d) => d.projects && latest.projects && d.projects.code === latest.projects.code).length > 1 && (
              <select
                value={selectedDossierId || (latest && latest.id) || ""}
                onChange={(e) => setSelectedDossierId(e.target.value || null)}
                style={{ padding: "4px 8px", borderRadius: 6, border: "1px solid var(--line)",
                         fontSize: 12, background: "var(--bg)", color: "var(--text)",
                         maxWidth: 200 }}
                title={lang === "fr" ? "Version du dossier" : "Dossier version"}>
                {liveDossiers
                  .filter((d) => d.projects && latest.projects && d.projects.code === latest.projects.code)
                  .map((d) => (
                    <option key={d.id} value={d.id}>
                      {d.version} · {d.state}
                    </option>
                  ))}
              </select>
            )}
            {canManageExante && (
              <button className="btn sm" onClick={() => setNewDossierOpen(true)}
                title={lang === "fr" ? "Créer une nouvelle évaluation" : "Create a new appraisal"}>
                <Icon.plus /> {lang === "fr" ? "Nouvelle évaluation" : "New appraisal"}
              </button>
            )}
            <button className="btn sm" onClick={async () => {
              // The engine recomputes deterministically each render from the
              // raw rows pulled by each useExante* hook. So "Recalculer" is
              // really "re-fetch every input table and re-render the page" —
              // useful after a teammate edits a row in another tab. Fire
              // every refresh in parallel and let the engine pick it up on
              // the next render pass.
              if (!latestDossierId) return;
              setRecalcBusy(true);
              try {
                await Promise.all([
                  refreshIdent && refreshIdent(),
                  refreshInputs && refreshInputs(),
                  refreshScen && refreshScen(),
                  refreshCal && refreshCal(),
                  refreshCapex && refreshCapex(),
                  refreshOpex && refreshOpex(),
                  refreshRev && refreshRev(),
                  refreshFin && refreshFin(),
                  refreshPubTransfers && refreshPubTransfers(),
                  refreshMprTransfers && refreshMprTransfers(),
                  refreshExternalities && refreshExternalities(),
                  refreshCf && refreshCf(),
                  refreshQuality && refreshQuality(),
                  refreshMc && refreshMc(),
                  refreshStk && refreshStk(),
                  refreshInst && refreshInst(),
                ].filter(Boolean));
              } finally { setRecalcBusy(false); }
            }} disabled={!latestDossierId || recalcBusy}
              title={latestDossierId
                ? (lang === "fr" ? "Recharger toutes les tables d'entrée et recalculer" : "Re-fetch every input table and recompute")
                : (lang === "fr" ? "Aucun dossier sélectionné" : "No dossier selected")}>
              <Icon.zap /> {recalcBusy
                ? (lang === "fr" ? "Recalcul…" : "Recomputing…")
                : (lang === "fr" ? "Recalculer" : "Recompute")}
            </button>
            {canManageExante && (
              <button className="btn sm" onClick={() => setReportOpen(true)} disabled={!latestDossierId}>
                <Icon.download /> {lang === "fr" ? "Générer rapport" : "Generate report"}
              </button>
            )}
            {canManageExante && (
              <button className="btn sm primary" onClick={() => setSubmitOpen(true)} disabled={!latestDossierId}>
                <Icon.send /> {lang === "fr" ? "Soumettre dossier" : "Submit dossier"}
              </button>
            )}
          </div>
        </div>
      </div>

      {!latest && (
        <div className="card" style={{ marginBottom: 14, background: "var(--bg-sunken)", borderColor: "var(--line-strong)" }}>
          <div className="card-body" style={{ padding: 18, textAlign: "center" }}>
            <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 6 }}>
              {lang === "fr" ? "Aucun dossier d'évaluation à consulter" : "No appraisal dossier to inspect"}
            </div>
            <div className="muted" style={{ fontSize: 12.5, marginBottom: 14, maxWidth: 560, margin: "0 auto 14px" }}>
              {lang === "fr"
                ? "Le module ex-ante affiche les analyses CAPEX, OPEX, projections financières, VAN/TRI, MPR et sensibilité par dossier. Créez une nouvelle évaluation pour démarrer, ou sélectionnez un projet déjà évalué dans le menu en haut à droite (le sélecteur n'apparaît que lorsqu'au moins un dossier existe pour votre organisation)."
                : "The ex-ante module displays CAPEX, OPEX, financial projections, NPV/IRR, MPR and sensitivity per dossier. Create a new appraisal to get started, or pick an already-evaluated project from the top-right selector (which only appears once your org has at least one dossier)."}
            </div>
            {canManageExante ? (
              <button className="btn sm primary" onClick={() => setNewDossierOpen(true)}>
                <Icon.plus /> {lang === "fr" ? "Démarrer une évaluation" : "Start an appraisal"}
              </button>
            ) : (
              <div className="text-faint" style={{ fontSize: 11.5 }}>
                {lang === "fr"
                  ? "Contactez un administrateur pour créer une évaluation."
                  : "Contact an administrator to create an appraisal."}
              </div>
            )}
          </div>
        </div>
      )}

      {latest && (
        <div className="card" style={{ marginBottom: 14, background: "#eef2ff", borderColor: "#6366f1" }}>
          <div className="card-head" style={{ alignItems: "center", gap: 8 }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: "#6366f1" }}></span>
            <div className="card-title" style={{ color: "#3730a3" }}>
              {lang === "fr" ? "Dossier ex-ante live" : "Live ex-ante dossier"}
            </div>
            <span className="tag-mono" style={{ marginLeft: "auto" }}>
              {latest.projects && latest.projects.code} · {latest.version}
            </span>
            {decisionBadge(latest.decision)}
            {liveDossiers.length > 1 && (
              <span className="text-faint" style={{ fontSize: 11, marginLeft: 6 }}>
                +{liveDossiers.length - 1} {lang === "fr" ? "autre(s)" : "more"}
              </span>
            )}
          </div>
          <div className="card-body" style={{ display: "grid", gridTemplateColumns: "repeat(6, minmax(0, 1fr))", gap: 12, paddingTop: 4 }}>
            <div><div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>VAN</div><div className="mono" style={{ fontSize: 16, fontWeight: 500 }}>{fmtM(latest.npv)}</div></div>
            <div><div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>TRI</div><div className="mono" style={{ fontSize: 16, fontWeight: 500 }}>{fmtP(latest.irr)}</div></div>
            <div><div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>DSCR</div><div className="mono" style={{ fontSize: 16, fontWeight: 500 }}>{latest.dscr == null ? "—" : Number(latest.dscr).toFixed(2)}</div></div>
            <div><div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>VANE</div><div className="mono" style={{ fontSize: 16, fontWeight: 500 }}>{fmtM(latest.enpv)}</div></div>
            <div><div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>TRIE</div><div className="mono" style={{ fontSize: 16, fontWeight: 500 }}>{fmtP(latest.eirr)}</div></div>
            <div><div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>B/C</div><div className="mono" style={{ fontSize: 16, fontWeight: 500 }}>{latest.bc_ratio == null ? "—" : Number(latest.bc_ratio).toFixed(2)}</div></div>
          </div>
        </div>
      )}

      {/* ===== Phase 4.2 — Final decision dashboard (always at top) ===== */}
      {latestDossierId && (
        <div id="card-decision">
          {finalDecision && (
            <ExanteFinalDecisionCard
              lang={lang}
              inputs={inputs}
              financial={exanteComputed && exanteComputed.indicators}
              economic={mprResult && mprResult.indicators}
              quality={qualityResult}
              multicriteria={mcResult}
              decision={finalDecision}
            />
          )}
        </div>
      )}

      {/* Identification CRUD — Phase 1 foundation */}
      {latestDossierId && (
        <div id="card-identification">
          <ExanteIdentificationCard
            lang={lang}
            dossierId={latestDossierId}
            ident={ident}
            realtime={identRealtime}
            onEdit={() => setIdentEditOpen(true)}
          />
        </div>
      )}
      {identEditOpen && latestDossierId && (
        <ExanteIdentificationModal
          lang={lang}
          dossierId={latestDossierId}
          initial={ident}
          onClose={() => setIdentEditOpen(false)}
          onSaved={async () => { await refreshIdent(); setIdentEditOpen(false); }}
        />
      )}

      {/* Inputs + Scenarios — Phase 1 (commit 2/3) */}
      {latestDossierId && (
        <div className="grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 14 }}>
          <div id="card-inputs"><ExanteInputsCard lang={lang} inputs={inputs} onEdit={() => setInputsEditOpen(true)} /></div>
          <div id="card-scenarios"><ExanteScenariosCard lang={lang} scen={scen} activeScenario={inputs && inputs.scenario} onEdit={() => setScenEditOpen(true)} /></div>
        </div>
      )}
      {inputsEditOpen && latestDossierId && (
        <ExanteInputsModal
          lang={lang}
          dossierId={latestDossierId}
          initial={inputs}
          onClose={() => setInputsEditOpen(false)}
          onSaved={async () => { await refreshInputs(); setInputsEditOpen(false); }}
        />
      )}
      {scenEditOpen && latestDossierId && (
        <ExanteScenariosModal
          lang={lang}
          dossierId={latestDossierId}
          initial={scen}
          onClose={() => setScenEditOpen(false)}
          onSaved={async () => { await refreshScen(); setScenEditOpen(false); }}
        />
      )}

      {/* Calendar — Phase 1 (commit 3/3) */}
      {latestDossierId && (
        <div id="card-calendar">
          <ExanteCalendarCard
            lang={lang}
            dossierId={latestDossierId}
            phases={exPhases}
            activities={exActivities}
            onAddPhase={() => setPhaseModalOpen(true)}
            onAddActivity={() => setActivityModalOpen(true)}
            onEditActivity={(a) => setEditingActivity(a)}
            onChanged={refreshCal}
          />
        </div>
      )}
      {phaseModalOpen && latestDossierId && (
        <ExantePhaseModal
          lang={lang}
          dossierId={latestDossierId}
          existingCount={exPhases.length}
          onClose={() => setPhaseModalOpen(false)}
          onSaved={async () => { await refreshCal(); setPhaseModalOpen(false); }}
        />
      )}
      {activityModalOpen && latestDossierId && (
        <ExanteActivityModal
          lang={lang}
          dossierId={latestDossierId}
          phases={exPhases}
          existingCount={exActivities.length}
          onClose={() => setActivityModalOpen(false)}
          onSaved={async () => { await refreshCal(); setActivityModalOpen(false); }}
        />
      )}
      {editingActivity && (
        <ExanteActivityModal
          lang={lang}
          dossierId={latestDossierId}
          phases={exPhases}
          existingCount={exActivities.length}
          editing={editingActivity}
          onClose={() => setEditingActivity(null)}
          onSaved={async () => { await refreshCal(); setEditingActivity(null); }}
        />
      )}

      {/* CAPEX — Phase 2.1 */}
      {latestDossierId && (
        <div id="card-capex">
          <ExanteCapexCard
            lang={lang}
            lines={capexLines}
            contingencyRate={inputs && inputs.contingencies_capex}
            currency={inputs && inputs.currency}
            onAdd={() => setCapexModalOpen(true)}
            onEdit={(l) => setEditingCapex(l)}
          />
        </div>
      )}
      {(capexModalOpen || editingCapex) && latestDossierId && (
        <ExanteCapexModal
          lang={lang}
          dossierId={latestDossierId}
          editing={editingCapex}
          existingCount={capexLines.length}
          onClose={() => { setCapexModalOpen(false); setEditingCapex(null); }}
          onSaved={async () => { await refreshCapex(); setCapexModalOpen(false); setEditingCapex(null); }}
        />
      )}

      {/* OPEX + Revenue — Phase 2.2 */}
      {latestDossierId && (
        <>
          <div id="card-opex"><ExanteOpexCard
            lang={lang}
            lines={opexLines}
            inputs={inputs}
            scen={scen}
            onAdd={() => setOpexModalOpen(true)}
            onEdit={(l) => setEditingOpex(l)}
          /></div>
          {(opexModalOpen || editingOpex) && (
            <ExanteOpexModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingOpex}
              existingCount={opexLines.length}
              onClose={() => { setOpexModalOpen(false); setEditingOpex(null); }}
              onSaved={async () => { await refreshOpex(); setOpexModalOpen(false); setEditingOpex(null); }}
            />
          )}
          <div id="card-revenue"><ExanteRevenueCard
            lang={lang}
            lines={revLines}
            inputs={inputs}
            scen={scen}
            onAdd={() => setRevModalOpen(true)}
            onEdit={(l) => setEditingRev(l)}
          /></div>
          {(revModalOpen || editingRev) && (
            <ExanteRevenueModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingRev}
              existingCount={revLines.length}
              onClose={() => { setRevModalOpen(false); setEditingRev(null); }}
              onSaved={async () => { await refreshRev(); setRevModalOpen(false); setEditingRev(null); }}
            />
          )}

          {/* ===== Phase 2.3 — Financing + computed financial outputs ===== */}
          <div id="card-financing"><ExanteFinancingCard
            lang={lang}
            sources={finSources}
            inputs={inputs}
            computed={exanteComputed}
            onAdd={() => setFinModalOpen(true)}
            onEdit={(f) => setEditingFin(f)}
          /></div>
          {(finModalOpen || editingFin) && (
            <ExanteFinancingModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingFin}
              existingCount={finSources.length}
              defaults={inputs}
              onClose={() => { setFinModalOpen(false); setEditingFin(null); }}
              onSaved={async () => { await refreshFin(); setFinModalOpen(false); setEditingFin(null); }}
            />
          )}

          <div id="card-financial-indicators"><ExanteFinancialIndicators lang={lang} inputs={inputs} computed={exanteComputed} /></div>

          <div id="card-debt"><ExanteDebtScheduleCard lang={lang} inputs={inputs} computed={exanteComputed} /></div>
          <div id="card-pl"><ExantePLCard lang={lang} inputs={inputs} computed={exanteComputed} /></div>
          <div id="card-cashflow"><ExanteCashFlowCard lang={lang} inputs={inputs} computed={exanteComputed} /></div>

          {/* ===== Phase 7.1 — Stakeholder accounts ===== */}
          <div id="card-stakeholders"><ExanteStakeholderCard
            lang={lang}
            dossierId={latestDossierId}
            stakeholders={stakeholders}
            accounts={stakeholderAccounts}
            currency={inputs && inputs.currency}
            onAdd={() => setStkModalOpen(true)}
            onEdit={(s) => setEditingStk(s)}
            onSeeded={refreshStk}
          /></div>
          {(stkModalOpen || editingStk) && (
            <ExanteStakeholderModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingStk}
              existingCount={(stakeholders || []).length}
              onClose={() => { setStkModalOpen(false); setEditingStk(null); }}
              onSaved={async () => { await refreshStk(); setStkModalOpen(false); setEditingStk(null); }}
            />
          )}

          {/* ===== Phase 3.1 — Public Finance impact ===== */}
          <div id="card-public-finance"><ExantePublicFinanceCard
            lang={lang}
            inputs={inputs}
            scen={scen}
            revenueByYear={exanteComputed && exanteComputed.revenueByYear}
            opexLines={opexLines}
            financingSources={finSources}
            publicTransfers={pubTransfers}
            onAddTransfer={() => setPubTransferModalOpen(true)}
            onEditTransfer={(t) => setEditingPubTransfer(t)}
          /></div>
          {(pubTransferModalOpen || editingPubTransfer) && (
            <ExantePublicTransferModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingPubTransfer}
              existingCount={(pubTransfers || []).length}
              onClose={() => { setPubTransferModalOpen(false); setEditingPubTransfer(null); }}
              onSaved={async () => { await refreshPubTransfers(); setPubTransferModalOpen(false); setEditingPubTransfer(null); }}
            />
          )}

          {/* ===== Phase 3.2 — MPR (3 phases) + Externalities ===== */}
          <div id="card-mpr-p1"><ExanteMprPhase1Card
            lang={lang}
            inputs={inputs}
            transfers={mprTransfers}
            mprResult={mprResult}
            onAdd={() => setMprTransferModalOpen(true)}
            onEdit={(t) => setEditingMprTransfer(t)}
          /></div>
          {(mprTransferModalOpen || editingMprTransfer) && (
            <ExanteMprTransferModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingMprTransfer}
              existingCount={(mprTransfers || []).length}
              onClose={() => { setMprTransferModalOpen(false); setEditingMprTransfer(null); }}
              onSaved={async () => { await refreshMprTransfers(); setMprTransferModalOpen(false); setEditingMprTransfer(null); }}
            />
          )}

          <div id="card-mpr-p2"><ExanteExternalitiesCard
            lang={lang}
            inputs={inputs}
            externalities={externalities}
            mprResult={mprResult}
            onAdd={() => setExtModalOpen(true)}
            onEdit={(e) => setEditingExt(e)}
          /></div>
          {(extModalOpen || editingExt) && (
            <ExanteExternalityModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingExt}
              existingCount={(externalities || []).length}
              onClose={() => { setExtModalOpen(false); setEditingExt(null); }}
              onSaved={async () => { await refreshExternalities(); setExtModalOpen(false); setEditingExt(null); }}
            />
          )}

          {/* MPR Phase 3 — Conversion factors (Phase 7.2) */}
          <div id="card-mpr-p3"><ExanteConversionFactorsCard
            lang={lang}
            dossierId={latestDossierId}
            factors={conversionFactors}
            onEdit={(f) => setEditingCf(f)}
            onSeeded={refreshCf}
          /></div>
          {editingCf && (
            <ExanteConversionFactorModal
              lang={lang}
              dossierId={latestDossierId}
              editing={editingCf}
              onClose={() => setEditingCf(null)}
              onSaved={async () => { await refreshCf(); setEditingCf(null); }}
            />
          )}

          <div id="card-economic-indicators"><ExanteEconomicIndicators lang={lang} inputs={inputs} mprResult={mprResult} /></div>

          {/* ===== Phase 4.1 — Multicritère + Contrôle Qualité ===== */}
          <div id="card-multicriteria"><ExanteMulticriteriaCard
            lang={lang}
            dossierId={latestDossierId}
            items={mcItems}
            result={mcResult}
            onEditItem={(it) => setEditingMc(it)}
            onSeeded={refreshMc}
          /></div>
          {editingMc && (
            <ExanteScoringRowModal
              lang={lang}
              kind="multicriteria"
              editing={editingMc}
              onClose={() => setEditingMc(null)}
              onSaved={async () => { await refreshMc(); setEditingMc(null); }}
            />
          )}
          <div id="card-quality"><ExanteQualityGridCard
            lang={lang}
            dossierId={latestDossierId}
            items={qualityItems}
            result={qualityResult}
            onEditItem={(it) => setEditingQuality(it)}
            onSeeded={refreshQuality}
          /></div>
          {editingQuality && (
            <ExanteScoringRowModal
              lang={lang}
              kind="quality"
              editing={editingQuality}
              onClose={() => setEditingQuality(null)}
              onSaved={async () => { await refreshQuality(); setEditingQuality(null); }}
            />
          )}

          {/* ===== Phase 7.3 — Institutional analysis ===== */}
          <div id="card-institutional"><ExanteInstitutionalCard
            lang={lang}
            dossierId={latestDossierId}
            institutional={institutional}
            onEdit={() => setInstEditOpen(true)}
          /></div>
          {instEditOpen && (
            <ExanteInstitutionalModal
              lang={lang}
              dossierId={latestDossierId}
              initial={institutional}
              onClose={() => setInstEditOpen(false)}
              onSaved={async () => { await refreshInst(); setInstEditOpen(false); }}
            />
          )}

          {/* ===== Phase 3.3 — Sensitivity ===== */}
          <div id="card-sensitivity"><ExanteSensitivityCard
            lang={lang}
            inputs={inputs}
            baseOpts={{
              inputs, scen,
              capexLines, opexLines, revenueLines: revLines,
              financingSources: finSources,
            }}
            mprBaseOpts={{
              inputs,
              capexLines,
              opexByYear: exanteComputed && exanteComputed.opexByYear,
              revenueByYear: exanteComputed && exanteComputed.revenueByYear,
              debtSchedule: exanteComputed && exanteComputed.debt,
              mprTransfers,
              externalities,
              conversionFactors,
            }}
          /></div>
        </>
      )}

      <div id="exante-demo-doc" className="toc-page">
        <div className="toc">
          {toc.map((s) => (
            <div key={s.id} className="toc-section">
              <div className={"toc-h" + (active === s.id ? " active" : "")} onClick={() => setActive(s.id)}>
                <span className="num">{s.num}</span>
                <span style={{ flex: 1 }}>{s.title}</span>
                {s.children && <Icon.chevronDown className="sm" />}
              </div>
              {s.children && (
                <div className="toc-children">
                  {s.children.map((c) => (
                    <div key={c.id} className={"toc-child" + (active === c.id ? " active" : "")} onClick={() => setActive(c.id)}>
                      <span className="mono text-faint">{c.num}</span> {c.title}
                    </div>
                  ))}
                </div>
              )}
            </div>
          ))}
        </div>

        <div className="toc-content">
          <ExAnteContext lang={lang} />
          <ExAnteBaseline lang={lang} />
          <ExAnteDesc lang={lang} />
          <ExAnteFinance lang={lang} fmtM={fmtMM} />
          <ExAnteEcon lang={lang} fmtM={fmtMM} />
          <ExAnteActors lang={lang} fmtM={fmtMM} />
          <ExAnteSensitivity lang={lang} fmtM={fmtMM} />
          <ExAnteEnv lang={lang} />
          <ExAnteInst lang={lang} />
          <ExAnteFunding lang={lang} />
          <ExAnteImpact lang={lang} fmtM={fmtMM} />
          <ExAnteReco lang={lang} fmtM={fmtMM} />
        </div>
      </div>
      {submitOpen && latestDossierId && (
        <ExanteSubmitDossierModal
          lang={lang}
          dossier={latest}
          payload={{
            lang, dossier: latest, ident, inputs, scen,
            calendar: { phases: exPhases, activities: exActivities },
            capexLines, opexLines, revenueLines: revLines, finSources,
            pubTransfers, mprTransfers, externalities,
            qualityItems, mcItems,
            stakeholders, stakeholderAccounts, conversionFactors, institutional,
            exanteComputed, mprResult, qualityResult, mcResult, finalDecision,
          }}
          onClose={() => setSubmitOpen(false)}
          onOpenReport={() => { setSubmitOpen(false); setReportOpen(true); }}
        />
      )}
      {reportOpen && latestDossierId && window.ExanteReportModal && React.createElement(window.ExanteReportModal, {
        lang, dossier: latest, ident, inputs, scen,
        calendar: { phases: exPhases, activities: exActivities },
        capexLines, opexLines, revenueLines: revLines, finSources,
        pubTransfers, mprTransfers, externalities,
        qualityItems, mcItems,
        // Phase 7 additions
        stakeholders, stakeholderAccounts,
        conversionFactors,
        institutional,
        exanteComputed, mprResult, qualityResult, mcResult, finalDecision,
        onClose: () => setReportOpen(false),
      })}

      {newDossierOpen && (
        <NewExanteDossierModal
          lang={lang}
          effOrg={effOrg}
          onClose={() => setNewDossierOpen(false)}
          onCreated={async (newDossierId) => {
            await refreshDossiers();
            setSelectedDossierId(newDossierId);
            setNewDossierOpen(false);
          }}
        />
      )}
    </div>
  );
}

// =====================================================================
// NewExanteDossierModal — creates a new exante_dossiers row. Lets the
// user either pick an existing project from their org or create a new
// project inline (the most common pre-condition for an ex-ante study).
// On success we hand the new dossier's id back to the parent so the
// page can switch to it immediately.
// =====================================================================
function NewExanteDossierModal({ lang, effOrg, onClose, onCreated }) {
  const { useState } = React;
  const { projects: liveProjects } = window.melr.useProjects();
  const { data: sectors }          = window.melr.useSectors();

  // Two modes: "existing" (pick a project) or "new" (create one).
  const [mode, setMode] = useState("existing");

  // Existing-project state.
  const [pickedProject, setPickedProject] = useState("");

  // New-project state — minimal set sufficient to satisfy NOT NULL
  // columns on projects (code, name_fr, sector_id).
  const [npCode, setNpCode]     = useState("");
  const [npNameFr, setNpNameFr] = useState("");
  const [npNameEn, setNpNameEn] = useState("");
  const [npSector, setNpSector] = useState("");
  const [npBudget, setNpBudget] = useState(""); // raw native currency

  // Dossier state.
  const [version, setVersion]   = useState("v1");
  const [discount, setDiscount] = useState("8");

  const [busy, setBusy] = useState(false);
  const [err, setErr]   = useState(null);

  // Filter projects to the effective org so super-admins acting in one
  // org don't accidentally pick another org's project.
  const visibleProjects = (liveProjects || []).filter((p) =>
    !effOrg || p.organizationId === effOrg
  );

  const onSubmit = async () => {
    setErr(null);
    setBusy(true);
    try {
      let projectUuid;
      if (mode === "existing") {
        const p = visibleProjects.find((x) => x.id === pickedProject);
        if (!p) throw new Error(lang === "fr" ? "Sélectionnez un projet." : "Pick a project.");
        projectUuid = p.uuid;
      } else {
        if (!npCode.trim() || !npNameFr.trim() || !npSector) {
          throw new Error(lang === "fr"
            ? "Code, nom français et secteur sont requis."
            : "Code, French name and sector are required.");
        }
        const created = await window.melr.createProject({
          code:        npCode.trim().toUpperCase(),
          name_fr:     npNameFr.trim(),
          name_en:     npNameEn.trim() || npNameFr.trim(),
          sector_id:   npSector,
          status:      "inception",
          budget_native: npBudget === "" ? 0 : Number(npBudget),
          currency:    "EUR",
          organization_id: effOrg || undefined,
        });
        projectUuid = created.uuid;
      }
      if (!projectUuid) throw new Error(lang === "fr" ? "Projet inexistant." : "Project not found.");
      const d = await window.melr.createExanteDossier(projectUuid, {
        version: version.trim() || "v1",
        discount_rate: discount === "" ? null : Number(discount),
      });
      await onCreated(d.id);
    } catch (e) { setErr(e.message); }
    finally { setBusy(false); }
  };

  const lbl = { display: "block", fontSize: 10.5, color: "var(--text-faint)", marginBottom: 3, textTransform: "uppercase", letterSpacing: "0.04em", marginTop: 10 };
  const inp = { width: "100%", padding: "6px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12.5, background: "var(--bg, white)", color: "var(--text)", boxSizing: "border-box", fontFamily: "inherit" };

  const Modal = window.Modal;
  return (
    <Modal
      title={lang === "fr" ? "Nouvelle évaluation ex-ante" : "New ex-ante appraisal"}
      onClose={busy ? null : onClose}
      size="md"
      footer={
        <>
          <button className="btn sm" onClick={onClose} disabled={busy}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button className="btn sm primary" onClick={onSubmit} disabled={busy}>
            {busy ? "…" : (lang === "fr" ? "Créer le dossier" : "Create dossier")}
          </button>
        </>
      }
    >
      <div className="seg" style={{ marginBottom: 8 }}>
        <button type="button" className={"seg-btn" + (mode === "existing" ? " active" : "")} onClick={() => setMode("existing")}>
          {lang === "fr" ? "Projet existant" : "Existing project"}
        </button>
        <button type="button" className={"seg-btn" + (mode === "new" ? " active" : "")} onClick={() => setMode("new")}>
          {lang === "fr" ? "Nouveau projet" : "New project"}
        </button>
      </div>

      {mode === "existing" ? (
        <div>
          <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Projet" : "Project"} *</label>
          <select style={inp} value={pickedProject} onChange={(e) => setPickedProject(e.target.value)}>
            <option value="">— {lang === "fr" ? "Choisir…" : "Choose…"} —</option>
            {visibleProjects.map((p) => (
              <option key={p.uuid} value={p.id}>
                {p.id} — {lang === "fr" ? p.nameFr : (p.nameEn || p.nameFr)}
              </option>
            ))}
          </select>
          {visibleProjects.length === 0 && (
            <div style={{ marginTop: 6, fontSize: 11.5, color: "var(--text-faint)" }}>
              {lang === "fr"
                ? "Aucun projet dans votre organisation — basculez vers « Nouveau projet »."
                : "No project in your organization — switch to 'New project'."}
            </div>
          )}
        </div>
      ) : (
        <div>
          <div style={{ background: "var(--bg-sunken)", padding: 8, borderRadius: 6, fontSize: 11.5, color: "var(--text-faint)", marginBottom: 8 }}>
            {lang === "fr"
              ? "Un projet minimal sera créé (code, nom, secteur, budget). Vous pourrez compléter le détail plus tard depuis l'écran Projets."
              : "A minimal project will be created (code, name, sector, budget). You can fill the rest later from the Projects screen."}
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 10 }}>
            <div>
              <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Code" : "Code"} *</label>
              <input style={inp} value={npCode} onChange={(e) => setNpCode(e.target.value)}
                placeholder="P-099" />
            </div>
            <div>
              <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Nom (FR)" : "Name (FR)"} *</label>
              <input style={inp} value={npNameFr} onChange={(e) => setNpNameFr(e.target.value)}
                placeholder={lang === "fr" ? "Nom du projet" : "Project name"} />
            </div>
          </div>
          <label style={lbl}>{lang === "fr" ? "Nom (EN)" : "Name (EN)"}</label>
          <input style={inp} value={npNameEn} onChange={(e) => setNpNameEn(e.target.value)}
            placeholder={lang === "fr" ? "(facultatif — copie du FR si vide)" : "(optional — copies FR if empty)"} />
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            <div>
              <label style={lbl}>{lang === "fr" ? "Secteur" : "Sector"} *</label>
              <select style={inp} value={npSector} onChange={(e) => setNpSector(e.target.value)}>
                <option value="">— {lang === "fr" ? "Choisir…" : "Choose…"} —</option>
                {(sectors || []).map((s) => (
                  <option key={s.id} value={s.id}>{lang === "fr" ? s.fr : s.en}</option>
                ))}
              </select>
            </div>
            <div>
              <label style={lbl}>{lang === "fr" ? "Budget (EUR brut)" : "Budget (raw EUR)"}</label>
              <input type="number" min={0} style={inp} value={npBudget}
                onChange={(e) => setNpBudget(e.target.value)} placeholder="0" />
            </div>
          </div>
        </div>
      )}

      <div style={{ borderTop: "1px solid var(--line-faint)", marginTop: 14, paddingTop: 10 }}>
        <div className="strong" style={{ fontSize: 12.5, marginBottom: 4 }}>
          {lang === "fr" ? "Paramètres du dossier" : "Dossier parameters"}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <div>
            <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Version" : "Version"}</label>
            <input style={inp} value={version} onChange={(e) => setVersion(e.target.value)}
              placeholder="v1" />
          </div>
          <div>
            <label style={{ ...lbl, marginTop: 0 }}>{lang === "fr" ? "Taux d'actualisation (%)" : "Discount rate (%)"}</label>
            <input type="number" min={0} step="0.1" style={inp} value={discount}
              onChange={(e) => setDiscount(e.target.value)} placeholder="8" />
          </div>
        </div>
      </div>

      {err && <div style={{ color: "#b91c1c", fontSize: 12, marginTop: 10 }}>{err}</div>}
    </Modal>
  );
}

function SectionH({ num, t }) {
  return (<div className="section-h"><span className="num">{num}</span> {t}</div>);
}

function ExAnteContext({ lang }) {
  return (
    <div id="ii" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 16px", letterSpacing: "-0.01em" }}>II. {lang === "fr" ? "Contexte et justification" : "Context and rationale"}</h2>
      <SectionH num="2.1" t={lang === "fr" ? "Secteur" : "Sector"} />
      <p className="text-muted" style={{ fontSize: 12.5, margin: "0 0 12px", textWrap: "pretty" }}>
        {lang === "fr"
          ? "Le secteur santé du Sahel reste marqué par une forte mortalité maternelle (410/100k NV) et infantile, une couverture vaccinale en dégradation (DTC3 à 43%), et un système de santé primaire structurellement sous-financé (4.2% du PIB en moyenne, contre 6% recommandé OMS)."
          : "The Sahel health sector remains marked by high maternal mortality (410/100k LB) and infant mortality, deteriorating vaccination coverage (DTP3 at 43%), and a structurally underfunded primary health system (4.2% of GDP on average, vs 6% WHO-recommended)."}
      </p>
      <SectionH num="2.2" t={lang === "fr" ? "Situation régionale" : "Regional situation"} />
      <div className="grid cols-3" style={{ gap: 12, marginBottom: 16 }}>
        {[
          { l: lang === "fr" ? "Population totale" : "Total population", v: "44.8M", s: "Mali · BF · Niger" },
          { l: lang === "fr" ? "Densité médicale" : "Medical density", v: "0.18", s: lang === "fr" ? "/1000 hab. (cible OMS 1.0)" : "/1000 (WHO target 1.0)" },
          { l: lang === "fr" ? "Dépense santé / PIB" : "Health spend / GDP", v: "4.2%", s: lang === "fr" ? "Recommandé OMS 6%" : "WHO recommended 6%" },
          { l: lang === "fr" ? "Mortalité maternelle" : "Maternal mortality", v: "410", s: "/100k NV" },
          { l: lang === "fr" ? "DTC3 régional" : "Regional DTP3", v: "43%", s: lang === "fr" ? "Cible nationale 92%" : "National target 92%" },
          { l: lang === "fr" ? "Sites santé primaires" : "Primary health sites", v: "1 240", s: "42 " + (lang === "fr" ? "couverts par P-241" : "covered by P-241") },
        ].map((k, i) => (
          <div key={i} style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 12, background: "var(--bg-elev)" }}>
            <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 4 }}>{k.l}</div>
            <div className="mono" style={{ fontSize: 20, fontWeight: 600 }}>{k.v}</div>
            <div className="text-faint" style={{ fontSize: 11 }}>{k.s}</div>
          </div>
        ))}
      </div>
      <SectionH num="2.3" t={lang === "fr" ? "Situation actuelle des sites" : "Current site situation"} />
      <div className="placeholder-box" style={{ marginBottom: 8 }}>
        {lang === "fr" ? "Cartographie des 42 sites · voir module Baseline" : "Mapping of 42 sites · see Baseline module"}
      </div>
    </div>
  );
}

function ExAnteBaseline({ lang }) {
  return (
    <div id="iii" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 16px", letterSpacing: "-0.01em" }}>III. {lang === "fr" ? "Situation sans projet" : "Without-project situation"}</h2>
      <p className="text-muted" style={{ fontSize: 12.5, margin: "0 0 14px" }}>
        {lang === "fr"
          ? "Données de référence consolidées issues du module Baseline : 42 sites cartographiés, 18 acteurs identifiés, comptes d'exploitation 2022-2025, 23 problèmes majeurs."
          : "Reference data consolidated from the Baseline module: 42 mapped sites, 18 stakeholders identified, 2022-2025 operating accounts, 23 major issues."}
      </p>
      <div className="grid cols-3" style={{ gap: 12 }}>
        <a className="btn" href="#" onClick={(e) => { e.preventDefault(); window.__navigate && window.__navigate("baseline"); }}><Icon.layers /> {lang === "fr" ? "Voir Baseline complet" : "Open full Baseline"}</a>
        <span className="pill green">42 sites</span>
        <span className="pill amber">23 {lang === "fr" ? "problèmes" : "issues"}</span>
      </div>
    </div>
  );
}

function ExAnteDesc({ lang }) {
  return (
    <div id="iv" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 16px", letterSpacing: "-0.01em" }}>IV. {lang === "fr" ? "Description du projet" : "Project description"}</h2>
      <SectionH num="4.1" t={lang === "fr" ? "Objectif global et spécifiques" : "Global and specific objectives"} />
      <div style={{ background: "var(--accent-soft)", borderRadius: 6, padding: 14, marginBottom: 14, borderLeft: "3px solid var(--accent)" }}>
        <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 4 }}>{lang === "fr" ? "Objectif global" : "Global objective"}</div>
        <div style={{ fontSize: 14, fontWeight: 500, color: "var(--text-on-soft)" }}>
          {lang === "fr"
            ? "Renforcer durablement l'accès et la qualité des soins de santé primaires sur 42 sites au Sahel, en réduisant la mortalité maternelle de 24% et en portant la couverture DTC3 à 92% à 5 ans."
            : "Sustainably strengthen access to and quality of primary health care on 42 Sahel sites, reducing maternal mortality by 24% and bringing DTP3 coverage to 92% in 5 years."}
        </div>
      </div>
      <div className="grid cols-3" style={{ gap: 12, marginBottom: 16 }}>
        {[
          { o: "OS1", t: lang === "fr" ? "Améliorer la qualité des soins" : "Improve quality of care" },
          { o: "OS2", t: lang === "fr" ? "Restaurer la chaîne du froid" : "Restore cold chain" },
          { o: "OS3", t: lang === "fr" ? "Renforcer la santé communautaire" : "Strengthen community health" },
        ].map((s, i) => (
          <div key={i} style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 12, background: "var(--bg-elev)" }}>
            <span className="tag-mono">{s.o}</span>
            <div style={{ fontSize: 13, fontWeight: 500, marginTop: 4, textWrap: "pretty" }}>{s.t}</div>
          </div>
        ))}
      </div>

      <SectionH num="4.2" t={lang === "fr" ? "Composantes et réalisables par site" : "Components and deliverables per site"} />
      <Spreadsheet
        columns={[
          { key: "comp", label: "Composante", type: "text", width: 200 },
          { key: "real", label: lang === "fr" ? "Réalisable" : "Deliverable", type: "text", width: 280 },
          { key: "u", label: "Unité", type: "text", width: 80 },
          { key: "qty", label: "Qté", type: "num", width: 70 },
          { key: "cu", label: lang === "fr" ? "Coût unit. (€)" : "Unit cost (€)", type: "eur", width: 120 },
          { key: "tot", label: "Total (€)", type: "eur", width: 130 },
          { key: "site", label: "Site / zone", type: "text", width: 120 },
        ]}
        rows={[
          { values: { comp: "C1 · Infrastructure" }, kind: "section" },
          { values: { comp: "C1 · Infrastructure", real: lang === "fr" ? "Réhabilitation maternités" : "Maternity rehabilitation", u: lang === "fr" ? "site" : "site", qty: 24, cu: 42000, tot: { value: 1008000, formula: "=D2*E2" }, site: "Mali, BF, NE" } },
          { values: { comp: "C1 · Infrastructure", real: lang === "fr" ? "Réseau eau / latrines" : "Water/sanitation", u: "site", qty: 42, cu: 14500, tot: 609000, site: "all" } },
          { values: { comp: "C2 · Équipement" }, kind: "section" },
          { values: { comp: "C2 · Équipement", real: lang === "fr" ? "Réfrigérateurs solaires SDD" : "Solar-direct-drive fridges", u: "u.", qty: 38, cu: 3200, tot: { value: 121600, formula: "=D5*E5" }, site: "all" } },
          { values: { comp: "C2 · Équipement", real: lang === "fr" ? "Kits accouchement" : "Birth kits", u: "kit", qty: 1280, cu: 85, tot: 108800, site: "all" } },
          { values: { comp: "C3 · Capacités" }, kind: "section" },
          { values: { comp: "C3 · Capacités", real: lang === "fr" ? "Formation 1 850 ASC" : "Training 1,850 CHWs", u: "ASC", qty: 1850, cu: 240, tot: 444000, site: "all" } },
          { values: { comp: "C3 · Capacités", real: lang === "fr" ? "Formation continue sages-femmes" : "Continuing midwife training", u: "p.", qty: 320, cu: 480, tot: 153600, site: "Niamey, Bamako" } },
          { values: { comp: lang === "fr" ? "TOTAL CAPEX" : "TOTAL CAPEX", tot: { value: 2445000, formula: "=SOMME(F2:F9)", flag: "ok" } }, kind: "total" },
        ]}
        scrollHeight="40vh"
      />

      <div style={{ marginTop: 14 }}>
        <SectionH num="4.3" t={lang === "fr" ? "Calendrier de mise en œuvre" : "Implementation schedule"} />
        <Gantt lang={lang} />
      </div>
    </div>
  );
}

function Gantt({ lang }) {
  const items = [
    { n: lang === "fr" ? "Conception détaillée" : "Detailed design", s: 0, d: 3, c: "var(--accent)" },
    { n: lang === "fr" ? "Travaux infrastructure" : "Infrastructure works", s: 2, d: 14, c: "var(--accent)" },
    { n: lang === "fr" ? "Achats équipements" : "Equipment procurement", s: 3, d: 8, c: "var(--green)" },
    { n: lang === "fr" ? "Installation chaîne du froid" : "Cold chain install", s: 8, d: 6, c: "var(--green)" },
    { n: lang === "fr" ? "Formation ASC (vagues)" : "CHW training (waves)", s: 4, d: 18, c: "var(--violet)" },
    { n: lang === "fr" ? "Mise en service progressive" : "Progressive go-live", s: 14, d: 22, c: "var(--amber)" },
    { n: lang === "fr" ? "Évaluation à mi-parcours" : "Mid-term evaluation", s: 24, d: 2, c: "var(--text)" },
    { n: lang === "fr" ? "Évaluation finale" : "Final evaluation", s: 38, d: 3, c: "var(--text)" },
  ];
  const months = 42;
  const W = 760, H = items.length * 22 + 36, padL = 200, padR = 12, padT = 24;
  const cw = (W - padL - padR) / months;
  return (
    <div className="card" style={{ overflow: "hidden" }}>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%" }}>
        {Array.from({ length: months / 6 + 1 }).map((_, i) => (
          <g key={i}>
            <line x1={padL + i * 6 * cw} x2={padL + i * 6 * cw} y1={padT} y2={H - 4} stroke="var(--line-faint)" />
            <text x={padL + i * 6 * cw} y={padT - 6} fontSize="9" textAnchor="middle" fill="var(--text-faint)" fontFamily="var(--font-mono)">M{i * 6}</text>
          </g>
        ))}
        {items.map((it, i) => {
          const y = padT + i * 22;
          return (
            <g key={i}>
              <text x={padL - 10} y={y + 12} fontSize="10.5" textAnchor="end" fill="var(--text)">{it.n}</text>
              <rect x={padL + it.s * cw} y={y + 4} width={it.d * cw} height={14} fill={it.c} rx="2" opacity="0.85" />
              <text x={padL + it.s * cw + it.d * cw / 2} y={y + 14} fontSize="9" textAnchor="middle" fill="white" fontFamily="var(--font-mono)">M{it.s}–M{it.s + it.d}</text>
            </g>
          );
        })}
      </svg>
    </div>
  );
}

function ExAnteFinance({ lang, fmtM }) {
  return (
    <div id="v" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 8px", letterSpacing: "-0.01em" }}>V. {lang === "fr" ? "Analyse financière" : "Financial analysis"}</h2>
      <p className="text-muted" style={{ fontSize: 12.5, margin: "0 0 14px" }}>
        {lang === "fr"
          ? "Feuille Excel intégrée · 4 onglets liés (CAPEX → OPEX → Recettes → VAN/TRI/DSCR) · taux d'actualisation 8%"
          : "Embedded Excel · 4 linked tabs (CAPEX → OPEX → Revenue → NPV/IRR/DSCR) · discount rate 8%"}
      </p>
      <Spreadsheet
        columns={[
          { key: "y", label: lang === "fr" ? "Année" : "Year", type: "text", width: 80 },
          { key: "capex", label: "CAPEX", type: "eur", width: 110 },
          { key: "opex", label: "OPEX", type: "eur", width: 110 },
          { key: "rev", label: lang === "fr" ? "Recettes" : "Revenue", type: "eur", width: 110 },
          { key: "cf", label: lang === "fr" ? "Cash-flow" : "Cash flow", type: "eur", width: 110 },
          { key: "cum", label: lang === "fr" ? "CF cumulé" : "Cum. CF", type: "eur", width: 120 },
          { key: "act", label: lang === "fr" ? "CF actualisé" : "Disc. CF", type: "eur", width: 120 },
          { key: "dscr", label: "DSCR", type: "num", width: 80 },
        ]}
        rows={[
          { values: { y: "Y0", capex: -2445000, opex: 0, rev: 0, cf: -2445000, cum: -2445000, act: -2445000, dscr: { value: "—", flag: "warn" } } },
          { values: { y: "Y1", capex: 0, opex: -680000, rev: 420000, cf: -260000, cum: -2705000, act: { value: -240740, formula: "=E3/(1.08^1)" }, dscr: 0.62 } },
          { values: { y: "Y2", capex: 0, opex: -720000, rev: 780000, cf: 60000, cum: -2645000, act: 51440, dscr: 1.08 } },
          { values: { y: "Y3", capex: 0, opex: -750000, rev: 1120000, cf: 370000, cum: -2275000, act: 293770, dscr: 1.49 } },
          { values: { y: "Y4", capex: 0, opex: -780000, rev: 1380000, cf: 600000, cum: -1675000, act: 441020, dscr: 1.77 } },
          { values: { y: "Y5", capex: 0, opex: -800000, rev: 1560000, cf: 760000, cum: -915000, act: 517210, dscr: 1.95 } },
          { values: { y: "Y6", capex: 0, opex: -820000, rev: 1700000, cf: 880000, cum: -35000, act: 554460, dscr: 2.07 } },
          { values: { y: "Y7", capex: 0, opex: -840000, rev: 1820000, cf: 980000, cum: 945000, act: 571640, dscr: 2.17 } },
          { values: { y: "Y8", capex: 0, opex: -860000, rev: 1920000, cf: 1060000, cum: 2005000, act: 572740, dscr: 2.23 } },
          { values: { y: "Y9", capex: 0, opex: -880000, rev: 2010000, cf: 1130000, cum: 3135000, act: 565470, dscr: 2.28 } },
          { values: { y: "Y10", capex: 0, opex: -900000, rev: 2090000, cf: 1190000, cum: 4325000, act: 551140, dscr: 2.32 } },
          { values: { y: "TOT", capex: { value: -2445000, flag: "ok" }, opex: -7830000, rev: 14800000, cf: { value: 4525000, formula: "=SOMME(E2:E12)", flag: "ok" }, act: { value: 1432150, formula: "=VAN(0.08;...)", flag: "ok" } }, kind: "total" },
        ]}
        scrollHeight="40vh"
      />

      <div style={{ height: 14 }}></div>
      <SectionH num="5.4" t="Viabilité financière · VAN, TRI, DSCR" />
      <div className="grid cols-4">
        {[
          { l: "VAN @ 8%", v: fmtM ? fmtM(1.43) : "1,43 M€", st: "ok", s: lang === "fr" ? "> 0 → projet rentable" : "> 0 → profitable" },
          { l: "TRI", v: "14.6%", st: "ok", s: lang === "fr" ? "> taux 8% → acceptable" : "> 8% rate → acceptable" },
          { l: "DSCR moy.", v: "1.84", st: "ok", s: lang === "fr" ? "> 1.2 → couverture OK" : "> 1.2 → coverage OK" },
          { l: "Payback", v: "6.1 " + (lang === "fr" ? "ans" : "yrs"), st: "warn", s: lang === "fr" ? "Long mais sectoriel" : "Long but sector-typical" },
        ].map((k, i) => (
          <div key={i} style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
            <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 4 }}>{k.l}</div>
            <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: k.st === "ok" ? "var(--green)" : "var(--amber)" }}>{k.v}</div>
            <div className="text-faint" style={{ fontSize: 11 }}>{k.s}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function ExAnteEcon({ lang, fmtM }) {
  return (
    <div id="vi" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>VI. {lang === "fr" ? "Analyse économique" : "Economic analysis"}</h2>
      <SectionH num="6.2" t={lang === "fr" ? "Méthode des Prix de Référence (MPR)" : "Reference Pricing Method (RPM)"} />
      <Spreadsheet
        columns={[
          { key: "p", label: lang === "fr" ? "Poste" : "Item", type: "text", width: 220 },
          { key: "fin", label: lang === "fr" ? "Prix financier" : "Financial price", type: "eur", width: 130 },
          { key: "coef", label: lang === "fr" ? "Coef. conversion" : "Conv. factor", type: "num", width: 130 },
          { key: "ref", label: lang === "fr" ? "Prix de référence" : "Reference price", type: "eur", width: 140 },
          { key: "diff", label: lang === "fr" ? "Écart économique" : "Economic delta", type: "eur", width: 140 },
        ]}
        rows={[
          { values: { p: lang === "fr" ? "Main-d'œuvre qualifiée" : "Skilled labor", fin: 480000, coef: 0.95, ref: { value: 456000, formula: "=B2*C2" }, diff: -24000 } },
          { values: { p: lang === "fr" ? "Main-d'œuvre non qualifiée" : "Unskilled labor", fin: 220000, coef: 0.65, ref: 143000, diff: -77000 } },
          { values: { p: lang === "fr" ? "Matériaux importés" : "Imported materials", fin: 1200000, coef: 1.10, ref: 1320000, diff: 120000 } },
          { values: { p: lang === "fr" ? "Matériaux locaux" : "Local materials", fin: 320000, coef: 0.92, ref: 294400, diff: -25600 } },
          { values: { p: lang === "fr" ? "Énergie" : "Energy", fin: 180000, coef: 1.05, ref: 189000, diff: 9000 } },
          { values: { p: "Total", fin: 2400000, ref: { value: 2402400, formula: "=SOMME(D2:D6)" }, diff: 2400 }, kind: "total" },
        ]}
        scrollHeight="32vh"
      />
      <div style={{ height: 14 }}></div>
      <SectionH num="6.3" t={lang === "fr" ? "Rentabilité économique" : "Economic profitability"} />
      <div className="grid cols-3">
        <div style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
          <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>VANE @ 5%</div>
          <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: "var(--green)" }}>{fmtM ? fmtM(4.82) : "4,82 M€"}</div>
          <div className="text-faint" style={{ fontSize: 11 }}>{lang === "fr" ? "incluant externalités santé valorisées" : "incl. valued health externalities"}</div>
        </div>
        <div style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
          <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>TIRE</div>
          <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: "var(--green)" }}>22.4%</div>
          <div className="text-faint" style={{ fontSize: 11 }}>{lang === "fr" ? "DALY évités valorisés" : "DALYs averted valued"}</div>
        </div>
        <div style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
          <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{lang === "fr" ? "B/C économique" : "Economic B/C"}</div>
          <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: "var(--green)" }}>2.97</div>
          <div className="text-faint" style={{ fontSize: 11 }}>{lang === "fr" ? "≥ 1 → bénéfice net positif" : "≥ 1 → positive net benefit"}</div>
        </div>
      </div>
    </div>
  );
}

function ExAnteActors({ lang, fmtM }) {
  return (
    <div id="vii" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>VII. {lang === "fr" ? "Analyse par acteur" : "Stakeholder analysis"}</h2>
      <p className="text-muted" style={{ fontSize: 12.5, margin: "0 0 12px" }}>
        {lang === "fr" ? "Distribution de la valeur créée par catégorie d'acteur (10 ans, valeur actualisée)" : "Distribution of created value per stakeholder category (10 yrs, discounted)"}
      </p>
      <div className="grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 16 }}>
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Distribution de la valeur" : "Value distribution"}</div></div>
          <div className="card-body">
            <ValueChart lang={lang} fmtM={fmtM} />
          </div>
        </div>
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Impact par catégorie" : "Impact per category"}</div></div>
          <div className="card-body flush">
            <table className="tbl">
              <thead><tr><th>{lang === "fr" ? "Acteur" : "Stakeholder"}</th><th className="num">VAN €</th><th className="num">%</th><th>{lang === "fr" ? "Effet net" : "Net effect"}</th></tr></thead>
              <tbody>
                {[
                  { a: lang === "fr" ? "Bénéficiaires (ménages)" : "Beneficiaries (households)", v: 2840000, p: 41, e: "ok" },
                  { a: lang === "fr" ? "Centres de santé" : "Health centers", v: 1620000, p: 23, e: "ok" },
                  { a: "État · MSP", v: 980000, p: 14, e: "ok" },
                  { a: lang === "fr" ? "ASC & personnel" : "CHWs & staff", v: 720000, p: 10, e: "ok" },
                  { a: lang === "fr" ? "Coopératives pharmacie" : "Pharmacy co-ops", v: 410000, p: 6, e: "warn" },
                  { a: lang === "fr" ? "Bailleurs (cofinancement)" : "Donors (co-financing)", v: 320000, p: 5, e: "ok" },
                  { a: lang === "fr" ? "Fournisseurs imp. (perte)" : "Import suppliers (loss)", v: -180000, p: -3, e: "bad" },
                ].map((r, i) => (
                  <tr key={i}><td className="strong">{r.a}</td><td className="num">{r.v.toLocaleString("fr-FR")}</td><td className="num">{r.p}%</td><td>
                    {r.e === "ok" && <span className="pill green dot">{lang === "fr" ? "Gagnant" : "Net gain"}</span>}
                    {r.e === "warn" && <span className="pill amber dot">{lang === "fr" ? "Mitigé" : "Mixed"}</span>}
                    {r.e === "bad" && <span className="pill red dot">{lang === "fr" ? "Perdant" : "Net loss"}</span>}
                  </td></tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  );
}

function ValueChart({ lang, fmtM }) {
  const data = [
    { n: lang === "fr" ? "Bénéficiaires" : "Beneficiaries", v: 41, c: "var(--accent)" },
    { n: lang === "fr" ? "Centres santé" : "Health centers", v: 23, c: "var(--green)" },
    { n: "État", v: 14, c: "var(--violet)" },
    { n: lang === "fr" ? "ASC" : "CHWs", v: 10, c: "var(--amber)" },
    { n: lang === "fr" ? "Pharmacies" : "Pharmacies", v: 6, c: "oklch(0.55 0.12 200)" },
    { n: lang === "fr" ? "Bailleurs" : "Donors", v: 5, c: "oklch(0.55 0.12 320)" },
  ];
  let cum = 0;
  const cx = 110, cy = 110, R = 80, r = 50;
  return (
    <div className="row" style={{ gap: 16, alignItems: "center" }}>
      <svg width="220" height="220" viewBox="0 0 220 220">
        {data.map((d, i) => {
          const start = cum / 100 * Math.PI * 2 - Math.PI / 2;
          cum += d.v;
          const end = cum / 100 * Math.PI * 2 - Math.PI / 2;
          const large = end - start > Math.PI ? 1 : 0;
          const x1 = cx + Math.cos(start) * R, y1 = cy + Math.sin(start) * R;
          const x2 = cx + Math.cos(end) * R, y2 = cy + Math.sin(end) * R;
          const x3 = cx + Math.cos(end) * r, y3 = cy + Math.sin(end) * r;
          const x4 = cx + Math.cos(start) * r, y4 = cy + Math.sin(start) * r;
          return <path key={i} d={`M ${x1} ${y1} A ${R} ${R} 0 ${large} 1 ${x2} ${y2} L ${x3} ${y3} A ${r} ${r} 0 ${large} 0 ${x4} ${y4} Z`} fill={d.c} />;
        })}
        <text x={cx} y={cy - 4} textAnchor="middle" fontSize="10" fill="var(--text-faint)">VAN total</text>
        <text x={cx} y={cy + 12} textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--text)" fontFamily="var(--font-mono)">{fmtM ? fmtM(6.71) : "6,71 M€"}</text>
      </svg>
      <div className="donut-legend">
        {data.map((d, i) => (
          <div key={i} className="row">
            <span className="swatch" style={{ background: d.c }}></span>
            <span style={{ fontSize: 12 }}>{d.n}</span>
            <span className="num">{d.v}%</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function ExAnteSensitivity({ lang, fmtM }) {
  return (
    <div id="viii" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>VIII. {lang === "fr" ? "Analyse de sensibilité" : "Sensitivity analysis"}</h2>
      <div className="grid cols-2">
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Tornado · sensibilité VAN" : "Tornado · NPV sensitivity"}</div></div>
          <div className="card-body">
            <Tornado lang={lang} />
          </div>
        </div>
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Scénarios multicritères" : "Multi-criteria scenarios"}</div></div>
          <div className="card-body flush">
            <table className="tbl">
              <thead><tr><th>Scénario</th><th className="num">VAN</th><th className="num">TRI</th><th className="num">DSCR</th><th>{lang === "fr" ? "Verdict" : "Verdict"}</th></tr></thead>
              <tbody>
                {[
                  { s: lang === "fr" ? "Référence" : "Baseline", v: "1.43 M€", t: "14.6%", d: "1.84", k: "ok" },
                  { s: lang === "fr" ? "OPEX +15%" : "OPEX +15%", v: "0.42 M€", t: "10.2%", d: "1.41", k: "ok" },
                  { s: lang === "fr" ? "Recettes −20%" : "Revenue −20%", v: "−0.61 M€", t: "5.8%", d: "0.92", k: "bad" },
                  { s: lang === "fr" ? "Retard 12 mois" : "12-month delay", v: "0.81 M€", t: "11.7%", d: "1.52", k: "warn" },
                  { s: lang === "fr" ? "Optimiste +10%" : "Optimistic +10%", v: "2.37 M€", t: "18.1%", d: "2.18", k: "ok" },
                  { s: lang === "fr" ? "Combiné défavorable" : "Combined adverse", v: "−1.12 M€", t: "3.2%", d: "0.78", k: "bad" },
                ].map((r, i) => (
                  <tr key={i}><td className="strong">{r.s}</td><td className="num">{r.v}</td><td className="num">{r.t}</td><td className="num">{r.d}</td><td>
                    {r.k === "ok" && <span className="pill green dot">{lang === "fr" ? "Acceptable" : "Acceptable"}</span>}
                    {r.k === "warn" && <span className="pill amber dot">{lang === "fr" ? "Attention" : "Caution"}</span>}
                    {r.k === "bad" && <span className="pill red dot">{lang === "fr" ? "Non viable" : "Not viable"}</span>}
                  </td></tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  );
}

function Tornado({ lang }) {
  const data = [
    { n: lang === "fr" ? "Volume bénéficiaires" : "Beneficiary volume", neg: -2.1, pos: 2.3 },
    { n: lang === "fr" ? "Coût construction" : "Construction cost", neg: -1.6, pos: 1.4 },
    { n: lang === "fr" ? "Tarif consultations" : "Consultation tariff", neg: -1.4, pos: 1.5 },
    { n: lang === "fr" ? "Taux d'actualisation" : "Discount rate", neg: -1.0, pos: 0.8 },
    { n: lang === "fr" ? "Coût personnel" : "Staff cost", neg: -0.7, pos: 0.6 },
    { n: lang === "fr" ? "Maintenance" : "Maintenance", neg: -0.4, pos: 0.4 },
  ];
  const W = 380, H = data.length * 28 + 40, padL = 130, padR = 24, padT = 24;
  const range = 3;
  const cx = padL + (W - padL - padR) / 2;
  const scale = (W - padL - padR) / 2 / range;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%" }}>
      <line x1={cx} x2={cx} y1={padT - 6} y2={H - 6} stroke="var(--text)" />
      {[-3, -2, -1, 1, 2, 3].map((g) => (
        <g key={g}>
          <line x1={cx + g * scale} x2={cx + g * scale} y1={padT - 4} y2={H - 6} stroke="var(--line-faint)" />
          <text x={cx + g * scale} y={padT - 8} fontSize="9" textAnchor="middle" fill="var(--text-faint)" fontFamily="var(--font-mono)">{g > 0 ? "+" : ""}{g}M</text>
        </g>
      ))}
      {data.map((d, i) => {
        const y = padT + 4 + i * 28;
        return (
          <g key={i}>
            <text x={padL - 8} y={y + 14} fontSize="10.5" textAnchor="end" fill="var(--text)">{d.n}</text>
            <rect x={cx + d.neg * scale} y={y + 4} width={-d.neg * scale} height={18} fill="var(--red)" opacity="0.7" />
            <rect x={cx} y={y + 4} width={d.pos * scale} height={18} fill="var(--green)" opacity="0.7" />
            <text x={cx + d.neg * scale - 4} y={y + 16} fontSize="9.5" textAnchor="end" fill="var(--text-muted)" fontFamily="var(--font-mono)">{d.neg}M</text>
            <text x={cx + d.pos * scale + 4} y={y + 16} fontSize="9.5" fill="var(--text-muted)" fontFamily="var(--font-mono)">+{d.pos}M</text>
          </g>
        );
      })}
    </svg>
  );
}

function ExAnteEnv({ lang }) {
  return (
    <div id="ix" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>IX. {lang === "fr" ? "Analyse environnementale et sociale" : "Environmental and social analysis"}</h2>
      <div className="grid cols-3">
        <div style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
          <div className="row" style={{ marginBottom: 6 }}><Icon.trees /> <b style={{ fontWeight: 500 }}>EIES — {lang === "fr" ? "Catégorie B" : "Category B"}</b></div>
          <div className="text-muted" style={{ fontSize: 12 }}>{lang === "fr" ? "Impacts modérés, site-spécifiques, gérables. EIES allégée + PGES." : "Moderate site-specific manageable impacts. Lite ESIA + ESMP."}</div>
        </div>
        <div style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
          <div className="row" style={{ marginBottom: 6 }}><Icon.users /> <b style={{ fontWeight: 500 }}>{lang === "fr" ? "Genre" : "Gender"}</b></div>
          <div className="text-muted" style={{ fontSize: 12 }}>{lang === "fr" ? "OECD GE-2 · 64% bénéficiaires femmes · indicateurs désagrégés." : "OECD GE-2 · 64% women beneficiaries · disaggregated indicators."}</div>
        </div>
        <div style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
          <div className="row" style={{ marginBottom: 6 }}><Icon.shieldHalf /> <b style={{ fontWeight: 500 }}>{lang === "fr" ? "Vulnérables" : "Vulnerable groups"}</b></div>
          <div className="text-muted" style={{ fontSize: 12 }}>{lang === "fr" ? "Quotas 18% PMR, 12% communautés nomades · mesures dédiées." : "Quotas 18% PWD, 12% nomadic communities · dedicated measures."}</div>
        </div>
      </div>
    </div>
  );
}

function ExAnteInst({ lang }) {
  return (
    <div id="x" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>X. {lang === "fr" ? "Analyse institutionnelle" : "Institutional analysis"}</h2>
      <div className="grid cols-2">
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Modèles de gestion étudiés" : "Management models considered"}</div></div>
          <div className="card-body flush">
            <table className="tbl">
              <thead><tr><th>{lang === "fr" ? "Modèle" : "Model"}</th><th>{lang === "fr" ? "Avantage" : "Strength"}</th><th>{lang === "fr" ? "Faiblesse" : "Weakness"}</th><th>{lang === "fr" ? "Sélection" : "Selected"}</th></tr></thead>
              <tbody>
                {[
                  { m: lang === "fr" ? "Régie directe MSP" : "Direct MoH operation", s: lang === "fr" ? "Pérennité étatique" : "State sustainability", w: lang === "fr" ? "Lenteur, sous-effectif" : "Slow, understaffed", sel: false },
                  { m: lang === "fr" ? "Délégation ONG locale" : "Local NGO delegation", s: lang === "fr" ? "Agilité terrain" : "Field agility", w: lang === "fr" ? "Risque pérennité" : "Sustainability risk", sel: false },
                  { m: lang === "fr" ? "PPP avec coopérative" : "Co-op PPP", s: lang === "fr" ? "Partage risque/incitation" : "Shared risk/incentive", w: lang === "fr" ? "Régulation prix" : "Price regulation", sel: true },
                  { m: lang === "fr" ? "Financement basé résultats" : "Results-based financing", s: lang === "fr" ? "Performance" : "Performance", w: lang === "fr" ? "Coût audit" : "Audit cost", sel: true },
                ].map((r, i) => (
                  <tr key={i}><td className="strong">{r.m}</td><td className="muted">{r.s}</td><td className="muted">{r.w}</td><td>{r.sel ? <span className="pill green dot">{lang === "fr" ? "Retenu" : "Selected"}</span> : <span className="pill">—</span>}</td></tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Durabilité financière post-projet" : "Post-project financial sustainability"}</div></div>
          <div className="card-body">
            <p className="text-muted" style={{ fontSize: 12.5, margin: "0 0 10px" }}>
              {lang === "fr" ? "Modèle PPP + RBF · transition à 5 ans · le ratio recettes/charges atteint 1.32 en année 5, garantissant la viabilité sans subvention exceptionnelle." : "PPP + RBF model · 5-year transition · revenue/expense ratio reaches 1.32 in Y5, ensuring viability without exceptional subsidy."}
            </p>
            <div className="grid cols-2" style={{ gap: 8 }}>
              {[
                { l: lang === "fr" ? "Ratio R/C Y5" : "R/E ratio Y5", v: "1.32" },
                { l: lang === "fr" ? "Subvention résiduelle" : "Residual subsidy", v: "12%" },
                { l: lang === "fr" ? "Autonomie financière" : "Financial autonomy", v: "Y6+" },
                { l: lang === "fr" ? "Capacité maintenance" : "Maintenance capacity", v: "OK" },
              ].map((r, i) => (
                <div key={i} style={{ background: "var(--bg-sunken)", padding: 8, borderRadius: 4 }}>
                  <div className="text-faint" style={{ fontSize: 10.5 }}>{r.l}</div>
                  <div className="mono" style={{ fontSize: 16, fontWeight: 600 }}>{r.v}</div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function ExAnteFunding({ lang }) {
  return (
    <div id="xi" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>XI. {lang === "fr" ? "Plan de financement" : "Financing plan"}</h2>
      <div className="card">
        <div className="card-head"><div className="card-title">{lang === "fr" ? "Sources et contributions" : "Sources and contributions"}</div></div>
        <div className="card-body flush">
          <table className="tbl">
            <thead><tr><th>{lang === "fr" ? "Source" : "Source"}</th><th>{lang === "fr" ? "Type" : "Type"}</th><th className="num">CAPEX €</th><th className="num">OPEX €</th><th className="num">Total €</th><th className="num">%</th></tr></thead>
            <tbody>
              {[
                { s: "AFD", t: lang === "fr" ? "Prêt souverain" : "Sovereign loan", c: 1400000, o: 0, p: 41 },
                { s: "UE · DG INTPA", t: lang === "fr" ? "Subvention" : "Grant", c: 480000, o: 320000, p: 24 },
                { s: "UNICEF", t: lang === "fr" ? "Subvention" : "Grant", c: 280000, o: 180000, p: 14 },
                { s: lang === "fr" ? "État · MSP" : "State · MoH", t: lang === "fr" ? "Contrepartie" : "Counterpart", c: 180000, o: 280000, p: 13 },
                { s: lang === "fr" ? "Bénéficiaires (in-kind)" : "Beneficiaries (in-kind)", t: lang === "fr" ? "Nature" : "In-kind", c: 105000, o: 90000, p: 6 },
                { s: lang === "fr" ? "Coopératives" : "Co-ops", t: lang === "fr" ? "Cofinancement" : "Co-financing", c: 0, o: 50000, p: 2 },
              ].map((r, i) => (
                <tr key={i}><td className="strong">{r.s}</td><td className="muted">{r.t}</td><td className="num">{r.c.toLocaleString("fr-FR")}</td><td className="num">{r.o.toLocaleString("fr-FR")}</td><td className="num">{(r.c + r.o).toLocaleString("fr-FR")}</td><td className="num">{r.p}%</td></tr>
              ))}
              <tr style={{ background: "var(--bg-sunken)" }}>
                <td className="strong">Total</td><td></td><td className="num strong">2 445 000</td><td className="num strong">920 000</td><td className="num strong">3 365 000</td><td className="num strong">100%</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

function ExAnteImpact({ lang, fmtM }) {
  return (
    <div id="xii" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>XII. {lang === "fr" ? "Impact sur les Finances publiques" : "Impact on public finances"}</h2>
      <div className="grid cols-3">
        {[
          { l: lang === "fr" ? "Coût annuel récurrent État" : "Annual recurrent State cost", v: fmtM ? fmtM(0.28) : "0.28 M€", s: lang === "fr" ? "0.04% budget santé" : "0.04% health budget" },
          { l: lang === "fr" ? "Économies évitées (curatif)" : "Avoided costs (curative)", v: fmtM ? fmtM(0.92) : "0.92 M€", s: lang === "fr" ? "DALYs évités valorisés" : "Valued averted DALYs" },
          { l: lang === "fr" ? "Bénéfice net État" : "Net State benefit", v: "+" + (fmtM ? fmtM(0.64) : "0.64 M€"), s: lang === "fr" ? "ratio 3.3" : "ratio 3.3" },
        ].map((k, i) => (
          <div key={i} style={{ border: "1px solid var(--line)", borderRadius: 6, padding: 14, background: "var(--bg-elev)" }}>
            <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.05em" }}>{k.l}</div>
            <div className="mono" style={{ fontSize: 22, fontWeight: 600 }}>{k.v}</div>
            <div className="text-faint" style={{ fontSize: 11 }}>{k.s}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function ExAnteReco({ lang, fmtM }) {
  return (
    <div id="xiii" className="section-block">
      <h2 style={{ fontSize: 18, fontWeight: 600, margin: "0 0 12px", letterSpacing: "-0.01em" }}>XIII. {lang === "fr" ? "Recommandations et conclusion" : "Recommendations and conclusion"}</h2>
      <div style={{ background: "var(--green-soft)", border: "1px solid oklch(from var(--green) calc(l + 0.30) c h)", borderRadius: 8, padding: 16, marginBottom: 14 }}>
        <div className="row" style={{ gap: 8, marginBottom: 6 }}>
          <Icon.badgeCheck className="lg" style={{ color: "var(--green)" }} />
          <b style={{ fontSize: 14, color: "var(--green)" }}>{lang === "fr" ? "Avis favorable à la mise en œuvre" : "Favorable opinion for implementation"}</b>
        </div>
        <p style={{ margin: 0, fontSize: 12.5, color: "var(--text)" }}>
          {lang === "fr"
            ? "Le projet présente une rentabilité financière acceptable (VAN +1,43 M€, TRI 14.6%) et une rentabilité économique solide (VANE +4,82 M€, TIRE 22.4%). Les analyses de sensibilité révèlent une fragilité au scénario combiné défavorable, à mitiger par un fonds de garantie."
            : "The project shows acceptable financial profitability (NPV +1.43M€, IRR 14.6%) and solid economic profitability (ENPV +4.82M€, EIRR 22.4%). Sensitivity analyses reveal fragility under the combined adverse scenario, to be mitigated by a guarantee fund."}
        </p>
      </div>
      <div className="grid cols-2">
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Conditions de mise en œuvre" : "Implementation conditions"}</div></div>
          <div className="card-body flush">
            {[
              lang === "fr" ? "Sécuriser cofinancement UNICEF (signé 12 sept. 2025)" : "Secure UNICEF co-financing (signed 12 Sep 2025)",
              lang === "fr" ? "Fonds de garantie OPEX 6% du total OPEX 10 ans" : "OPEX guarantee fund 6% of 10y OPEX total",
              lang === "fr" ? "Comité PPP-RBF opérationnel à T0+3 mois" : "PPP-RBF committee operational at T0+3 months",
              lang === "fr" ? "Système S&E déployé avant phase d'exploitation" : "M&E system deployed before operations phase",
              lang === "fr" ? "Audit DQA semestriel à compter de l'année 2" : "Bi-annual DQA audit from year 2",
            ].map((c, i) => (
              <div key={i} style={{ padding: "10px 14px", borderBottom: "1px solid var(--line-faint)", display: "flex", gap: 10, alignItems: "center" }}>
                <Icon.check style={{ color: "var(--green)" }} />
                <span style={{ fontSize: 12.5 }}>{c}</span>
              </div>
            ))}
          </div>
        </div>
        <div className="card">
          <div className="card-head"><div className="card-title">{lang === "fr" ? "Risques résiduels à suivre" : "Residual risks to monitor"}</div></div>
          <div className="card-body flush">
            {[
              { r: lang === "fr" ? "Inflation locale > 10%" : "Local inflation > 10%", p: lang === "fr" ? "Moyenne" : "Medium", i: lang === "fr" ? "Élevée" : "High" },
              { r: lang === "fr" ? "Sécurité régionale" : "Regional security", p: lang === "fr" ? "Élevée" : "High", i: lang === "fr" ? "Élevée" : "High" },
              { r: lang === "fr" ? "Adoption PPP par MSP" : "MoH adoption of PPP", p: lang === "fr" ? "Faible" : "Low", i: lang === "fr" ? "Moyenne" : "Medium" },
              { r: lang === "fr" ? "Volatilité prix médicaments" : "Drug price volatility", p: lang === "fr" ? "Moyenne" : "Medium", i: lang === "fr" ? "Moyenne" : "Medium" },
            ].map((r, i) => (
              <div key={i} style={{ padding: "10px 14px", borderBottom: "1px solid var(--line-faint)", display: "grid", gridTemplateColumns: "1fr auto auto", gap: 8, alignItems: "center" }}>
                <span style={{ fontSize: 12.5 }}>{r.r}</span>
                <span className="pill amber" style={{ fontSize: 10.5 }}>P · {r.p}</span>
                <span className="pill red" style={{ fontSize: 10.5 }}>I · {r.i}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

// ==================== EX-ANTE PHASE 1 — IDENTIFICATION CRUD ====================

function ExanteIdentificationCard({ lang, dossierId, ident, realtime, onEdit }) {
  const LiveBadge = window.melr.LiveBadge;
  const empty = !ident || (!ident.intitule && !ident.zone && !ident.general_objective);
  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Identification du dossier (Tableau 8)" : "Dossier identification (Table 8)"}
        </div>
        <LiveBadge on={realtime} lang={lang} />
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onEdit}>
          <Icon.edit /> {empty ? (lang === "fr" ? "Compléter" : "Fill in") : (lang === "fr" ? "Modifier" : "Edit")}
        </button>
      </div>
      <div className="card-body">
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "16px 4px", textAlign: "center" }}>
            {lang === "fr"
              ? "La fiche d'identification du dossier est vide. Cliquez « Compléter » pour démarrer."
              : "The dossier identification is empty. Click \"Fill in\" to start."}
            <div style={{ fontSize: 11.5, marginTop: 6 }}>
              {lang === "fr"
                ? "Identification du dossier — 9 blocs, ~45 champs (méthodologie ex-ante multi-pays)."
                : "Dossier identification — 9 blocks, ~45 fields (multi-country ex-ante methodology)."}
            </div>
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 14, fontSize: 12 }}>
            <ExIdField label={lang === "fr" ? "Intitulé" : "Title"} value={ident.intitule} fullWidth />
            <ExIdField label={lang === "fr" ? "Sous-secteur" : "Sub-sector"} value={ident.sub_sector} />
            <ExIdField label={lang === "fr" ? "Ministère / Tutelle" : "Ministry"} value={ident.ministry} />
            <ExIdField label={lang === "fr" ? "Devise" : "Currency"} value={ident.currency} />
            <ExIdField label={lang === "fr" ? "Coût global" : "Global cost"} value={ident.global_cost_native != null ? Number(ident.global_cost_native).toLocaleString() : "—"} suffix={ident.currency} />
            <ExIdField label={lang === "fr" ? "Date d'élaboration" : "Elaboration date"} value={ident.elaboration_date} />
            <ExIdField label={lang === "fr" ? "Zone d'intervention" : "Zone"} value={ident.zone} fullWidth />
            <ExIdField label={lang === "fr" ? "Durée investissement (ans)" : "Investment duration (yrs)"} value={ident.investment_duration_years} />
            <ExIdField label={lang === "fr" ? "Durée exploitation (ans)" : "Operation duration (yrs)"} value={ident.operation_duration_years} />
            <ExIdField label={lang === "fr" ? "Horizon total (ans)" : "Total horizon (yrs)"} value={ident.total_horizon_years} />
            <ExIdField label={lang === "fr" ? "Plan stratégique" : "Strategic plan"} value={ident.strategy_plan} />
            <ExIdField label={lang === "fr" ? "Axe stratégique" : "Strategic axis"} value={ident.strategic_axis} />
            <ExIdField label={lang === "fr" ? "Politique sectorielle" : "Sectoral policy"} value={ident.sectoral_policy} />
            <ExIdField label={lang === "fr" ? "Cible directe — taille" : "Direct target — size"} value={ident.direct_target_size != null ? Number(ident.direct_target_size).toLocaleString() : "—"} />
            <ExIdField label={lang === "fr" ? "Objectif général" : "General objective"} value={ident.general_objective} fullWidth />
            {ident.specific_objective_1 && <ExIdField label="OS 1" value={ident.specific_objective_1} fullWidth />}
            {ident.specific_objective_2 && <ExIdField label="OS 2" value={ident.specific_objective_2} fullWidth />}
            {ident.specific_objective_3 && <ExIdField label="OS 3" value={ident.specific_objective_3} fullWidth />}
            {ident.central_problem && <ExIdField label={lang === "fr" ? "Problème central" : "Central problem"} value={ident.central_problem} fullWidth />}
            {ident.main_risks && <ExIdField label={lang === "fr" ? "Principaux risques" : "Main risks"} value={ident.main_risks} fullWidth />}
          </div>
        )}
      </div>
    </div>
  );
}

function ExIdField({ label, value, suffix, fullWidth }) {
  return (
    <div style={{ gridColumn: fullWidth ? "1 / -1" : undefined }}>
      <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: 2 }}>
        {label}
      </div>
      <div style={{ fontSize: 12, fontWeight: 500, color: "var(--text)" }}>
        {value != null && value !== "" ? value : <span className="text-faint">—</span>}
        {suffix && value != null && value !== "" && <span className="text-faint mono" style={{ marginLeft: 4, fontSize: 11 }}>{suffix}</span>}
      </div>
    </div>
  );
}

function ExanteIdentificationModal({ lang, dossierId, initial, onClose, onSaved }) {
  const [form, setForm] = useStateE(() => initial || {});
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const setField = (key, value) => setForm((f) => ({ ...f, [key]: value }));

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      // Coerce numeric fields
      const payload = {
        ...form,
        global_cost_native: form.global_cost_native === "" ? null : (form.global_cost_native != null ? Number(form.global_cost_native) : null),
        investment_duration_years: form.investment_duration_years === "" ? null : (form.investment_duration_years != null ? Number(form.investment_duration_years) : null),
        operation_duration_years:  form.operation_duration_years  === "" ? null : (form.operation_duration_years  != null ? Number(form.operation_duration_years)  : null),
        total_horizon_years:       form.total_horizon_years       === "" ? null : (form.total_horizon_years       != null ? Number(form.total_horizon_years)       : null),
        direct_target_size:        form.direct_target_size        === "" ? null : (form.direct_target_size        != null ? Number(form.direct_target_size)        : null),
        component_1_amount:        form.component_1_amount        === "" ? null : (form.component_1_amount        != null ? Number(form.component_1_amount)        : null),
        component_2_amount:        form.component_2_amount        === "" ? null : (form.component_2_amount        != null ? Number(form.component_2_amount)        : null),
        component_3_amount:        form.component_3_amount        === "" ? null : (form.component_3_amount        != null ? Number(form.component_3_amount)        : null),
        component_4_amount:        form.component_4_amount        === "" ? null : (form.component_4_amount        != null ? Number(form.component_4_amount)        : null),
        component_5_amount:        form.component_5_amount        === "" ? null : (form.component_5_amount        != null ? Number(form.component_5_amount)        : null),
        component_6_amount:        form.component_6_amount        === "" ? null : (form.component_6_amount        != null ? Number(form.component_6_amount)        : null),
      };
      await window.melr.upsertExanteIdentification(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };
  const F = (key, label, opts = {}) => (
    <label style={{ display: "grid", gap: 4, gridColumn: opts.full ? "1 / -1" : undefined }}>
      <span style={lbl}>{label}</span>
      {opts.textarea ? (
        <textarea rows={opts.rows || 2} value={form[key] || ""} onChange={(e) => setField(key, e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
      ) : (
        <input type={opts.type || "text"} step={opts.step} value={form[key] != null ? form[key] : ""} onChange={(e) => setField(key, e.target.value)} placeholder={opts.placeholder} style={inp} />
      )}
    </label>
  );

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 820, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 14,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Identification du dossier" : "Dossier identification"}
        </div>

        <ExSectionH n="1" t={lang === "fr" ? "Identification générale" : "General identification"} />
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 10 }}>
          {F("intitule", lang === "fr" ? "Intitulé du projet/programme" : "Project/programme title", { full: true })}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          {F("sub_sector", lang === "fr" ? "Sous-secteur" : "Sub-sector")}
          {F("ministry", lang === "fr" ? "Ministère / Tutelle" : "Ministry")}
          {F("global_cost_native", lang === "fr" ? "Coût global (devise native)" : "Global cost (native currency)", { type: "number", step: "any" })}
          {F("currency", lang === "fr" ? "Devise" : "Currency", { placeholder: "EUR, XOF, USD…" })}
          {F("elaboration_date", lang === "fr" ? "Date d'élaboration" : "Elaboration date", { type: "date" })}
        </div>

        <ExSectionH n="2" t={lang === "fr" ? "Localisation et durée" : "Location and duration"} />
        {F("zone", lang === "fr" ? "Zone d'intervention / Localisation" : "Intervention zone / Location", { full: true })}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          {F("investment_duration_years", lang === "fr" ? "Phase investissement (ans)" : "Investment phase (yrs)", { type: "number" })}
          {F("operation_duration_years", lang === "fr" ? "Phase exploitation (ans)" : "Operation phase (yrs)", { type: "number" })}
          {F("total_horizon_years", lang === "fr" ? "Horizon total (ans)" : "Total horizon (yrs)", { type: "number" })}
        </div>

        <ExSectionH n="3" t={lang === "fr" ? "Ancrage stratégique" : "Strategic anchoring"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          {F("strategy_plan", lang === "fr" ? "Plan de développement (PSE, PND, etc.)" : "Development plan (PSE, NDP, etc.)")}
          {F("strategic_axis", lang === "fr" ? "Axe stratégique concerné" : "Strategic axis")}
          {F("sectoral_policy", lang === "fr" ? "Lettre de politique sectorielle" : "Sectoral policy")}
          {F("budget_alignment", lang === "fr" ? "Cohérence PTIP / Budget État" : "PTIP / State budget alignment")}
        </div>

        <ExSectionH n="4" t={lang === "fr" ? "Situation initiale et problème" : "Initial situation and problem"} />
        {F("initial_situation", lang === "fr" ? "Description de la situation initiale" : "Initial situation description", { textarea: true, full: true })}
        {F("without_project_situation", lang === "fr" ? "Situation de référence (sans projet)" : "Without-project (baseline) situation", { textarea: true, full: true })}
        {F("central_problem", lang === "fr" ? "Problème central identifié" : "Central problem identified", { textarea: true, full: true })}
        {F("main_constraints", lang === "fr" ? "Principales contraintes" : "Main constraints", { textarea: true, full: true })}

        <ExSectionH n="5" t={lang === "fr" ? "Population cible" : "Target population"} />
        {F("direct_targets", lang === "fr" ? "Groupes cibles directs" : "Direct target groups", { full: true })}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 10 }}>
          {F("direct_target_size", lang === "fr" ? "Taille groupe cible direct" : "Direct target size", { type: "number" })}
          {F("indirect_beneficiaries", lang === "fr" ? "Bénéficiaires indirects" : "Indirect beneficiaries")}
        </div>

        <ExSectionH n="6" t={lang === "fr" ? "Objectifs" : "Objectives"} />
        {F("general_objective", lang === "fr" ? "Objectif général" : "General objective", { textarea: true, full: true })}
        {F("specific_objective_1", "OS 1", { full: true })}
        {F("specific_objective_2", "OS 2", { full: true })}
        {F("specific_objective_3", "OS 3", { full: true })}
        {F("specific_objective_4", "OS 4 " + (lang === "fr" ? "(optionnel)" : "(optional)"), { full: true })}

        <ExSectionH n="7" t={lang === "fr" ? "Résultats attendus" : "Expected results"} />
        {F("immediate_results", lang === "fr" ? "Résultats immédiats" : "Immediate results", { textarea: true, full: true })}
        {F("intermediate_results", lang === "fr" ? "Résultats intermédiaires" : "Intermediate results", { textarea: true, full: true })}
        {F("ultimate_results", lang === "fr" ? "Résultats ultimes / Impact" : "Ultimate results / Impact", { textarea: true, full: true })}

        <ExSectionH n="8" t={lang === "fr" ? "Composantes" : "Components"} />
        {[1, 2, 3, 4, 5, 6].map((i) => (
          <div key={i} style={{ display: "grid", gridTemplateColumns: "3fr 1fr", gap: 10 }}>
            {F(`component_${i}_label`, lang === "fr" ? `Composante ${i} — libellé` : `Component ${i} — label`)}
            {F(`component_${i}_amount`, lang === "fr" ? "Budget" : "Budget", { type: "number", step: "any" })}
          </div>
        ))}

        <ExSectionH n="9" t={lang === "fr" ? "Risques et conditions" : "Risks and preconditions"} />
        {F("main_risks", lang === "fr" ? "Principaux risques et mitigation" : "Main risks and mitigation", { textarea: true, full: true, rows: 3 })}
        {F("preconditions", lang === "fr" ? "Conditions préalables" : "Preconditions", { textarea: true, full: true, rows: 3 })}

        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", borderTop: "1px solid var(--line-faint)", paddingTop: 12, position: "sticky", bottom: 0, background: "var(--bg, white)" }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#059669", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (lang === "fr" ? "Enregistrer" : "Save")}
          </button>
        </div>
      </form>
    </div>
  );
}

function ExSectionH({ n, t }) {
  return (
    <div style={{
      fontSize: 12, fontWeight: 600, color: "var(--text)",
      borderBottom: "1px solid var(--line-faint)", paddingBottom: 4,
      marginTop: 6,
    }}>
      <span className="mono text-faint" style={{ marginRight: 8 }}>{n}.</span>
      {t}
    </div>
  );
}

// ==================== PHASE 1 (2/3) — INPUTS CRUD =============================

function ExanteInputsCard({ lang, inputs, onEdit }) {
  const empty = !inputs;
  const pct = (v) => v == null ? "—" : (Number(v) * 100).toFixed(1) + "%";
  const num = (v) => v == null ? "—" : Number(v).toLocaleString();
  return (
    <div className="card">
      <div className="card-head">
        <div className="card-title">{lang === "fr" ? "Paramètres globaux (Inputs)" : "Global parameters (Inputs)"}</div>
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onEdit}>
          <Icon.edit /> {empty ? (lang === "fr" ? "Configurer" : "Configure") : (lang === "fr" ? "Modifier" : "Edit")}
        </button>
      </div>
      <div className="card-body">
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "12px 4px", textAlign: "center" }}>
            {lang === "fr" ? "Aucun paramètre saisi." : "No parameters yet."}
            <div style={{ fontSize: 11.5, marginTop: 4 }}>
              {lang === "fr"
                ? "Tableau des intrants (paramètres économiques, prix, taux d'actualisation, etc.)."
                : "Inputs table (economic parameters, prices, discount rate, etc.)."}
            </div>
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, fontSize: 11.5 }}>
            <ExKvRow label={lang === "fr" ? "Scénario actif" : "Active scenario"} value={inputs.scenario} mono />
            <ExKvRow label={lang === "fr" ? "Horizon (ans)" : "Horizon (yrs)"} value={inputs.horizon_years} mono />
            <ExKvRow label={lang === "fr" ? "Devise" : "Currency"} value={inputs.currency} mono />
            <ExKvRow label={lang === "fr" ? "Année de base" : "Base year"} value={inputs.base_year} mono />
            <ExKvRow label={lang === "fr" ? "Taux actu. financier" : "Discount fin."} value={pct(inputs.discount_rate_financial)} mono />
            <ExKvRow label={lang === "fr" ? "Taux actu. économique" : "Discount eco."} value={pct(inputs.discount_rate_economic)} mono />
            <ExKvRow label={lang === "fr" ? "Inflation annuelle" : "Inflation"} value={pct(inputs.inflation_annual)} mono />
            <ExKvRow label={lang === "fr" ? "Taux d'impôt" : "Tax rate"} value={pct(inputs.tax_rate_effective)} mono />
            <ExKvRow label={lang === "fr" ? "Part dette" : "Debt share"} value={pct(inputs.debt_share_capex)} mono />
            <ExKvRow label={lang === "fr" ? "Taux d'intérêt dette" : "Debt rate"} value={pct(inputs.debt_interest_rate)} mono />
            <ExKvRow label={lang === "fr" ? "Maturité (ans)" : "Maturity (yrs)"} value={inputs.debt_maturity_years} mono />
            <ExKvRow label={lang === "fr" ? "Différé (ans)" : "Grace (yrs)"} value={inputs.debt_grace_years} mono />
            <ExKvRow label={lang === "fr" ? "Part subvention" : "Subsidy share"} value={pct(inputs.public_subsidy_share)} mono />
            <ExKvRow label={lang === "fr" ? "Imprévus CAPEX" : "Contingencies"} value={pct(inputs.contingencies_capex)} mono />
            <ExKvRow label={lang === "fr" ? "Amort. infrastructures" : "Amort. infra (yrs)"} value={inputs.amort_infrastructure_years} mono />
            <ExKvRow label={lang === "fr" ? "Amort. équipements" : "Amort. equip (yrs)"} value={inputs.amort_equipment_years} mono />
            <ExKvRow label={lang === "fr" ? "Taux recette publique" : "Public revenue rate"} value={pct(inputs.public_revenue_rate)} mono />
            <ExKvRow label={lang === "fr" ? "Support O&M An 1" : "O&M support Y1"} value={num(inputs.om_support_year1)} mono />
          </div>
        )}
      </div>
    </div>
  );
}

function ExKvRow({ label, value, mono }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", gap: 8, borderBottom: "1px solid var(--line-faint)", paddingBottom: 3 }}>
      <span className="text-faint" style={{ fontSize: 11 }}>{label}</span>
      <span className={mono ? "mono strong" : "strong"} style={{ fontSize: 11.5 }}>
        {value != null && value !== "" ? value : <span className="text-faint">—</span>}
      </span>
    </div>
  );
}

function ExanteInputsModal({ lang, dossierId, initial, onClose, onSaved }) {
  const [form, setForm] = useStateE(() => initial || {});
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);
  const [applying, setApplying] = useStateE(false);
  const setField = (key, value) => setForm((f) => ({ ...f, [key]: value }));

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      // Coerce: ints, floats, plain text
      const intFields = ["horizon_years","base_year","debt_maturity_years","debt_grace_years","receivables_days","inventory_days","payables_days","amort_infrastructure_years","amort_equipment_years"];
      const numFields = ["discount_rate_financial","discount_rate_economic","inflation_annual","exchange_rate_usd","tax_rate_effective","debt_share_capex","debt_interest_rate","public_subsidy_share","contingencies_capex","public_revenue_rate","om_support_year1"];
      const payload = { ...form };
      intFields.forEach((k) => { if (payload[k] === "") payload[k] = null; else if (payload[k] != null) payload[k] = parseInt(payload[k], 10); });
      numFields.forEach((k) => { if (payload[k] === "") payload[k] = null; else if (payload[k] != null) payload[k] = Number(payload[k]); });
      await window.melr.upsertExanteInputs(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  // Country defaults dropdown — pull a country's standards into the form.
  const [countries, setCountries] = useStateE([]);
  React.useEffect(() => {
    window.melr.listCountryDefaults().then(setCountries).catch(() => {});
  }, []);

  const applyCountry = async (code) => {
    if (!code) return;
    setApplying(true);
    try {
      const d = await window.melr.fetchCountryDefaults(code);
      if (d) {
        setForm((f) => ({
          ...f,
          currency:                d.default_currency || f.currency,
          discount_rate_financial: d.default_discount_financial,
          discount_rate_economic:  d.default_discount_economic,
          inflation_annual:        d.default_inflation,
          tax_rate_effective:      d.default_tax_rate,
          debt_share_capex:        d.default_debt_share,
          debt_interest_rate:      d.default_debt_interest_rate,
          debt_maturity_years:     d.default_debt_maturity_yrs,
          debt_grace_years:        d.default_debt_grace_yrs,
          public_subsidy_share:    d.default_subsidy_share,
          om_support_year1:        d.default_om_support_year1,
        }));
      }
    } catch (e) { setErr(e.message); }
    finally { setApplying(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };
  const F = (key, label, opts = {}) => (
    <label style={{ display: "grid", gap: 4 }}>
      <span style={lbl}>{label}{opts.unit && <span className="text-faint mono" style={{ marginLeft: 6 }}>{opts.unit}</span>}</span>
      {opts.select ? (
        <select value={form[key] != null ? form[key] : ""} onChange={(e) => setField(key, e.target.value)} style={inp}>
          {opts.select.map((o) => <option key={o} value={o}>{o}</option>)}
        </select>
      ) : (
        <input type={opts.type || "text"} step={opts.step || "any"} value={form[key] != null ? form[key] : ""} onChange={(e) => setField(key, e.target.value)} style={inp} />
      )}
    </label>
  );

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 760, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 12,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Paramètres globaux (Inputs)" : "Global parameters (Inputs)"}
        </div>

        {/* Country defaults shortcut */}
        <div className="row gap-sm" style={{
          padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)",
          border: "1px solid var(--line-faint)", alignItems: "center",
        }}>
          <span style={{ fontSize: 11.5 }}>
            {lang === "fr" ? "Charger les défauts pays :" : "Load country defaults:"}
          </span>
          <select onChange={(e) => applyCountry(e.target.value)} defaultValue=""
            disabled={applying}
            style={{ ...inp, width: "auto", fontSize: 12 }}>
            <option value="">— {lang === "fr" ? "choisir" : "select"} —</option>
            {countries.map((c) => (
              <option key={c.country_code} value={c.country_code}>
                {c.country_code} ({c.default_currency}){c.strategic_framework_name ? " · " + c.strategic_framework_name : ""}
              </option>
            ))}
          </select>
          {applying && <span className="text-faint" style={{ fontSize: 11 }}>{lang === "fr" ? "Chargement…" : "Loading…"}</span>}
        </div>

        <ExSectionH n="A" t={lang === "fr" ? "Contrôle général" : "General control"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 10 }}>
          {F("scenario", lang === "fr" ? "Scénario actif" : "Active scenario", { select: ["Bas","Moyen","Haut"] })}
          {F("horizon_years", lang === "fr" ? "Horizon" : "Horizon", { type: "number", unit: lang === "fr" ? "ans" : "yrs" })}
          {F("currency", lang === "fr" ? "Devise" : "Currency", { unit: "ISO" })}
          {F("base_year", lang === "fr" ? "Année de base" : "Base year", { type: "number" })}
        </div>

        <ExSectionH n="B" t={lang === "fr" ? "Paramètres financiers" : "Financial parameters"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          {F("discount_rate_financial", lang === "fr" ? "Taux actu. financier" : "Discount fin.", { type: "number", unit: "0–1" })}
          {F("discount_rate_economic", lang === "fr" ? "Taux actu. économique" : "Discount eco.", { type: "number", unit: "0–1" })}
          {F("inflation_annual", lang === "fr" ? "Inflation annuelle" : "Inflation", { type: "number", unit: "0–1" })}
          {F("exchange_rate_usd", lang === "fr" ? "Taux change USD" : "USD exchange", { type: "number" })}
          {F("tax_rate_effective", lang === "fr" ? "Taux d'impôt" : "Tax rate", { type: "number", unit: "0–1" })}
        </div>

        <ExSectionH n="C" t={lang === "fr" ? "Financement" : "Financing"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          {F("debt_share_capex", lang === "fr" ? "Part dette / CAPEX" : "Debt / CAPEX", { type: "number", unit: "0–1" })}
          {F("debt_interest_rate", lang === "fr" ? "Taux d'intérêt dette" : "Debt rate", { type: "number", unit: "0–1" })}
          {F("public_subsidy_share", lang === "fr" ? "Part subvention" : "Subsidy share", { type: "number", unit: "0–1" })}
          {F("debt_maturity_years", lang === "fr" ? "Maturité dette" : "Debt maturity", { type: "number", unit: lang === "fr" ? "ans" : "yrs" })}
          {F("debt_grace_years", lang === "fr" ? "Différé" : "Grace period", { type: "number", unit: lang === "fr" ? "ans" : "yrs" })}
        </div>

        <ExSectionH n="D" t={lang === "fr" ? "Exploitation" : "Operations"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          {F("receivables_days", lang === "fr" ? "Créances clients" : "Receivables", { type: "number", unit: "j" })}
          {F("inventory_days", lang === "fr" ? "Stock" : "Inventory", { type: "number", unit: "j" })}
          {F("payables_days", lang === "fr" ? "Dettes fournisseurs" : "Payables", { type: "number", unit: "j" })}
          {F("amort_infrastructure_years", lang === "fr" ? "Amort. infrastructures" : "Amort. infra", { type: "number", unit: lang === "fr" ? "ans" : "yrs" })}
          {F("amort_equipment_years", lang === "fr" ? "Amort. équipements" : "Amort. equip.", { type: "number", unit: lang === "fr" ? "ans" : "yrs" })}
          {F("contingencies_capex", lang === "fr" ? "Imprévus CAPEX" : "Contingencies", { type: "number", unit: "0–1" })}
        </div>

        <ExSectionH n="E" t={lang === "fr" ? "Paramètres sectoriels" : "Sector parameters"} />
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr", gap: 10 }}>
          {F("project_sector", lang === "fr" ? "Secteur du projet" : "Project sector")}
          {F("public_revenue_rate", lang === "fr" ? "Taux recette publique" : "Public revenue rate", { type: "number", unit: "0–1" })}
          {F("om_support_year1", lang === "fr" ? "Support O&M An 1" : "O&M support Y1", { type: "number" })}
        </div>

        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", borderTop: "1px solid var(--line-faint)", paddingTop: 12, position: "sticky", bottom: 0, background: "var(--bg, white)" }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#059669", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (lang === "fr" ? "Enregistrer" : "Save")}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 1 (2/3) — SCENARIOS CRUD ==========================

function ExanteScenariosCard({ lang, scen, activeScenario, onEdit }) {
  const empty = !scen;
  const pct = (v) => v == null ? "—" : (Number(v) * 100).toFixed(1) + "%";
  const num = (v) => v == null ? "—" : Number(v).toLocaleString();
  return (
    <div className="card">
      <div className="card-head">
        <div className="card-title">{lang === "fr" ? "Scénarios Bas / Moyen / Haut" : "Scenarios Low / Mid / High"}</div>
        {activeScenario && (
          <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
            {lang === "fr" ? "Actif :" : "Active:"} {activeScenario}
          </span>
        )}
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onEdit}>
          <Icon.edit /> {empty ? (lang === "fr" ? "Configurer" : "Configure") : (lang === "fr" ? "Modifier" : "Edit")}
        </button>
      </div>
      <div className="card-body flush">
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "12px 14px", textAlign: "center" }}>
            {lang === "fr" ? "Aucun scénario saisi." : "No scenarios yet."}
            <div style={{ fontSize: 11.5, marginTop: 4 }}>
              {lang === "fr"
                ? "Hypothèses Bas/Moyen/Haut (Guide IV.5)."
                : "Low/Mid/High assumptions (Guide IV.5)."}
            </div>
          </div>
        ) : (
          <table className="tbl">
            <thead><tr>
              <th>{lang === "fr" ? "Paramètre" : "Parameter"}</th>
              <th className="num">{lang === "fr" ? "Bas" : "Low"}</th>
              <th className="num">{lang === "fr" ? "Moyen" : "Mid"}</th>
              <th className="num">{lang === "fr" ? "Haut" : "High"}</th>
            </tr></thead>
            <tbody>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "Croiss. volumes" : "Volume growth"}</td>
                <td className="num mono">{pct(scen.volume_growth_low)}</td>
                <td className="num mono">{pct(scen.volume_growth_mid)}</td>
                <td className="num mono">{pct(scen.volume_growth_high)}</td></tr>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "Croiss. tarifs" : "Tariff growth"}</td>
                <td className="num mono">{pct(scen.tariff_growth_low)}</td>
                <td className="num mono">{pct(scen.tariff_growth_mid)}</td>
                <td className="num mono">{pct(scen.tariff_growth_high)}</td></tr>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "Inflation personnel" : "Personnel infl."}</td>
                <td className="num mono">{pct(scen.personnel_inflation_low)}</td>
                <td className="num mono">{pct(scen.personnel_inflation_mid)}</td>
                <td className="num mono">{pct(scen.personnel_inflation_high)}</td></tr>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "Inflation énergie" : "Energy infl."}</td>
                <td className="num mono">{pct(scen.energy_inflation_low)}</td>
                <td className="num mono">{pct(scen.energy_inflation_mid)}</td>
                <td className="num mono">{pct(scen.energy_inflation_high)}</td></tr>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "Volume C1 An 1" : "C1 vol Y1"}</td>
                <td className="num mono">{num(scen.volume_c1_low)}</td>
                <td className="num mono">{num(scen.volume_c1_mid)}</td>
                <td className="num mono">{num(scen.volume_c1_high)}</td></tr>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "Tarif C1 An 1" : "C1 tariff Y1"}</td>
                <td className="num mono">{num(scen.tariff_c1_low)}</td>
                <td className="num mono">{num(scen.tariff_c1_mid)}</td>
                <td className="num mono">{num(scen.tariff_c1_high)}</td></tr>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "OPEX personnel A1" : "OPEX personnel Y1"}</td>
                <td className="num mono">{num(scen.opex_personnel_low)}</td>
                <td className="num mono">{num(scen.opex_personnel_mid)}</td>
                <td className="num mono">{num(scen.opex_personnel_high)}</td></tr>
              <tr><td className="strong" style={{ fontSize: 11 }}>{lang === "fr" ? "Bénéf. emplois A1" : "Jobs benefit Y1"}</td>
                <td className="num mono">{num(scen.benefit_jobs_low)}</td>
                <td className="num mono">{num(scen.benefit_jobs_mid)}</td>
                <td className="num mono">{num(scen.benefit_jobs_high)}</td></tr>
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

function ExanteScenariosModal({ lang, dossierId, initial, onClose, onSaved }) {
  const [form, setForm] = useStateE(() => initial || {});
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);
  const setField = (key, value) => setForm((f) => ({ ...f, [key]: value }));

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {};
      Object.keys(form).forEach((k) => {
        const v = form[k];
        if (v === "" || v == null) payload[k] = null;
        else payload[k] = Number(v);
      });
      // Keep id-like fields untouched
      delete payload.dossier_id;
      delete payload.updated_at;
      await window.melr.upsertExanteScenarios(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "6px 8px", borderRadius: 5, border: "1px solid var(--line)", fontSize: 12, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const cellInput = (key) => (
    <input type="number" step="any" value={form[key] != null ? form[key] : ""}
      onChange={(e) => setField(key, e.target.value)} style={inp} />
  );

  const sectionRow = (title, base, unit) => (
    <tr>
      <td className="strong" style={{ fontSize: 11.5 }}>
        {title}
        {unit && <span className="text-faint mono" style={{ marginLeft: 6, fontSize: 10 }}>{unit}</span>}
      </td>
      <td style={{ padding: 4 }}>{cellInput(base + "_low")}</td>
      <td style={{ padding: 4 }}>{cellInput(base + "_mid")}</td>
      <td style={{ padding: 4 }}>{cellInput(base + "_high")}</td>
    </tr>
  );

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 880, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 12,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Scénarios Bas / Moyen / Haut (Guide IV.5)" : "Scenarios Low / Mid / High (Guide IV.5)"}
        </div>
        <div className="text-faint" style={{ fontSize: 11.5 }}>
          {lang === "fr"
            ? "Saisir les hypothèses des trois scénarios. Le scénario actif est défini dans les Inputs."
            : "Enter the assumptions for the three scenarios. The active scenario is set in Inputs."}
        </div>

        <table className="tbl">
          <thead><tr>
            <th>{lang === "fr" ? "Paramètre" : "Parameter"}</th>
            <th style={{ width: 130 }}>{lang === "fr" ? "Bas" : "Low"}</th>
            <th style={{ width: 130 }}>{lang === "fr" ? "Moyen" : "Mid"}</th>
            <th style={{ width: 130 }}>{lang === "fr" ? "Haut" : "High"}</th>
          </tr></thead>
          <tbody>
            <tr><td colSpan="4" style={{ background: "var(--bg-sunken)", fontWeight: 600, fontSize: 11 }}>A. {lang === "fr" ? "Croissance & inflation" : "Growth & inflation"} <span className="text-faint">(0–1, ex: 0.03 = 3%)</span></td></tr>
            {sectionRow(lang === "fr" ? "Croissance volumes" : "Volume growth", "volume_growth", "0–1")}
            {sectionRow(lang === "fr" ? "Croissance tarifs" : "Tariff growth", "tariff_growth", "0–1")}
            {sectionRow(lang === "fr" ? "Inflation personnel" : "Personnel inflation", "personnel_inflation", "0–1")}
            {sectionRow(lang === "fr" ? "Inflation énergie" : "Energy inflation", "energy_inflation", "0–1")}
            {sectionRow(lang === "fr" ? "Inflation autres" : "Other inflation", "other_inflation", "0–1")}

            <tr><td colSpan="4" style={{ background: "var(--bg-sunken)", fontWeight: 600, fontSize: 11 }}>B. {lang === "fr" ? "Volumes Année 1" : "Volumes Year 1"}</td></tr>
            {sectionRow(lang === "fr" ? "Volume C1" : "C1 volume", "volume_c1")}
            {sectionRow(lang === "fr" ? "Volume C2" : "C2 volume", "volume_c2")}
            {sectionRow(lang === "fr" ? "Volume C3" : "C3 volume", "volume_c3")}
            {sectionRow(lang === "fr" ? "Volume C4 (opt.)" : "C4 volume (opt.)", "volume_c4")}

            <tr><td colSpan="4" style={{ background: "var(--bg-sunken)", fontWeight: 600, fontSize: 11 }}>C. {lang === "fr" ? "Tarifs Année 1" : "Tariffs Year 1"}</td></tr>
            {sectionRow(lang === "fr" ? "Tarif C1" : "C1 tariff", "tariff_c1")}
            {sectionRow(lang === "fr" ? "Tarif C2" : "C2 tariff", "tariff_c2")}
            {sectionRow(lang === "fr" ? "Tarif C3" : "C3 tariff", "tariff_c3")}
            {sectionRow(lang === "fr" ? "Tarif C4 (opt.)" : "C4 tariff (opt.)", "tariff_c4")}
            {sectionRow(lang === "fr" ? "Autres revenus A1" : "Other revenue Y1", "other_revenue")}

            <tr><td colSpan="4" style={{ background: "var(--bg-sunken)", fontWeight: 600, fontSize: 11 }}>D. {lang === "fr" ? "OPEX Année 1" : "OPEX Year 1"}</td></tr>
            {sectionRow(lang === "fr" ? "Personnel" : "Personnel", "opex_personnel")}
            {sectionRow(lang === "fr" ? "Énergie" : "Energy", "opex_energy")}
            {sectionRow(lang === "fr" ? "Autres fixes" : "Other fixed", "opex_fixed")}
            {sectionRow(lang === "fr" ? "Autres variables" : "Other variable", "opex_variable")}

            <tr><td colSpan="4" style={{ background: "var(--bg-sunken)", fontWeight: 600, fontSize: 11 }}>E. {lang === "fr" ? "Bénéfices économiques Année 1" : "Economic benefits Year 1"}</td></tr>
            {sectionRow(lang === "fr" ? "Emplois direct/indirect" : "Jobs direct/indirect", "benefit_jobs")}
            {sectionRow(lang === "fr" ? "Réduction des pertes" : "Loss reduction", "benefit_losses_reduced")}
            {sectionRow(lang === "fr" ? "Autres bénéfices" : "Other benefits", "benefit_other")}
          </tbody>
        </table>

        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", borderTop: "1px solid var(--line-faint)", paddingTop: 12, position: "sticky", bottom: 0, background: "var(--bg, white)" }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#059669", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (lang === "fr" ? "Enregistrer" : "Save")}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 1 (3/3) — CALENDAR CRUD ==========================

const EXANTE_PHASE_COLORS = [
  "oklch(0.55 0.13 230)",
  "oklch(0.55 0.13 145)",
  "oklch(0.55 0.15 290)",
  "oklch(0.55 0.14 75)",
  "oklch(0.55 0.12 12)",
  "oklch(0.45 0.10 0)",
];

const EXANTE_STATUS_FROM_PROGRESS = (progress, today, end) => {
  if (progress >= 100) return "done";
  if (today > new Date(end)) return "late";
  if (progress > 0) return "active";
  return "planned";
};

function ExanteCalendarCard({ lang, dossierId, phases, activities, onAddPhase, onAddActivity, onEditActivity, onChanged }) {
  // Map ex-ante phases/activities into the shape PlanGantt expects.
  const planActions = (activities || []).map((a) => {
    const phase = (phases || []).find((p) => p.id === a.phase_id);
    const status = EXANTE_STATUS_FROM_PROGRESS(
      a.progress || 0,
      new Date(),
      a.end_date || "2999-01-01"
    );
    return {
      id: a.id,
      uuid: a.id,
      wbs: a.wbs || "",
      name: { fr: a.name_fr, en: a.name_en || a.name_fr },
      start: a.start_date,
      end: a.end_date,
      phase: phase ? phase.id : null,
      progress: a.progress || 0,
      status,
      milestone: !!a.milestone,
      owner: a.owner || "",
      dep: [],
    };
  });
  const planPhases = (phases || []).map((p) => ({
    id: p.id,
    uuid: p.id,
    code: p.code,
    color: p.color || "var(--accent)",
    name: { fr: p.name_fr, en: p.name_en || p.name_fr },
  }));

  // KPIs
  const total = activities.length;
  const done = activities.filter((a) => (a.progress || 0) >= 100).length;
  const active = activities.filter((a) => (a.progress || 0) > 0 && (a.progress || 0) < 100).length;
  const milestones = activities.filter((a) => a.milestone).length;
  const avg = total > 0 ? Math.round(activities.reduce((s, a) => s + (a.progress || 0), 0) / total) : 0;

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Calendrier de mise en œuvre" : "Implementation calendar"}
        </div>
        {phases && phases.length > 0 && (
          <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
            {phases.length} {lang === "fr" ? (phases.length > 1 ? "phases" : "phase") : (phases.length > 1 ? "phases" : "phase")}
          </span>
        )}
        {total > 0 && (
          <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
            {total} {lang === "fr" ? "actions" : "actions"} · {avg}% {lang === "fr" ? "moy." : "avg"}
          </span>
        )}
        <div style={{ flex: 1 }} />
        <button className="btn xs ghost" onClick={onAddPhase}>
          <Icon.layers /> {lang === "fr" ? "Phase" : "Phase"}
        </button>
        <button className="btn xs primary" onClick={onAddActivity}>
          <Icon.plus /> {lang === "fr" ? "Action" : "Action"}
        </button>
      </div>
      <div className="card-body">
        {phases.length === 0 && total === 0 ? (
          <div style={{ padding: 20, textAlign: "center" }}>
            <div className="text-faint" style={{ fontSize: 13, marginBottom: 6 }}>
              {lang === "fr"
                ? "Aucune phase ni action définie."
                : "No phase or action defined yet."}
            </div>
            <div className="text-faint" style={{ fontSize: 11.5 }}>
              {lang === "fr"
                ? "Calendrier de réalisation : créez d'abord une phase (A-B Études, B Travaux, OPER Exploitation, etc.) puis ajoutez les actions."
                : "Implementation schedule: start by creating phases (A-B Studies, B Works, OPER Operation, etc.) then add actions."}
            </div>
            <div className="row gap-sm" style={{ justifyContent: "center", marginTop: 14 }}>
              <button className="btn sm" onClick={onAddPhase}>
                <Icon.layers /> {lang === "fr" ? "Créer la 1ère phase" : "Create first phase"}
              </button>
            </div>
          </div>
        ) : (
          <>
            {/* Compact KPI row */}
            <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5 }}>
              <span><b style={{ fontSize: 14 }}>{total}</b> <span className="text-faint">{lang === "fr" ? "actions" : "actions"}</span></span>
              <span style={{ color: "var(--green)" }}><b style={{ fontSize: 14 }}>{done}</b> <span className="text-faint">{lang === "fr" ? "terminées" : "done"}</span></span>
              <span style={{ color: "var(--accent)" }}><b style={{ fontSize: 14 }}>{active}</b> <span className="text-faint">{lang === "fr" ? "en cours" : "active"}</span></span>
              <span><b style={{ fontSize: 14 }}>◆ {milestones}</b> <span className="text-faint">{lang === "fr" ? "jalons" : "milestones"}</span></span>
              <span style={{ marginLeft: "auto", color: "var(--text-faint)" }}>
                {lang === "fr" ? "Avancement moyen :" : "Avg progress:"} <b style={{ color: "var(--text)" }}>{avg}%</b>
              </span>
            </div>
            {/* Reuse the existing PlanGantt component (exposed via window.PlanGantt) */}
            {total > 0 && window.PlanGantt && (
              React.createElement(window.PlanGantt, { actions: planActions, phases: planPhases, lang, height: 360 })
            )}
            {total === 0 && (
              <div className="text-faint" style={{ padding: 14, textAlign: "center", fontSize: 12 }}>
                {lang === "fr" ? "Ajoutez une première action pour afficher le Gantt." : "Add a first action to display the Gantt."}
              </div>
            )}
            {/* Table of activities */}
            {total > 0 && (
              <div style={{ marginTop: 14 }}>
                <table className="tbl">
                  <thead><tr>
                    <th style={{ width: 56 }}>WBS</th>
                    <th>{lang === "fr" ? "Action" : "Action"}</th>
                    <th style={{ width: 110 }}>{lang === "fr" ? "Phase" : "Phase"}</th>
                    <th className="num" style={{ width: 96 }}>{lang === "fr" ? "Début" : "Start"}</th>
                    <th className="num" style={{ width: 96 }}>{lang === "fr" ? "Fin" : "End"}</th>
                    <th style={{ width: 130 }}>{lang === "fr" ? "Avancement" : "Progress"}</th>
                    <th style={{ width: 44 }}></th>
                  </tr></thead>
                  <tbody>
                    {activities.map((a) => {
                      const ph = phases.find((p) => p.id === a.phase_id);
                      return (
                        <tr key={a.id}>
                          <td className="mono text-faint">{a.wbs || "—"}</td>
                          <td className="strong">
                            {a.milestone && <span className="plan-ms-icon" style={{ marginRight: 4 }}>◆</span>}
                            {a.name_fr}
                          </td>
                          <td>
                            {ph ? (
                              <span className="phase-chip" style={{ background: ph.color, color: "white" }}>{ph.name_fr}</span>
                            ) : <span className="text-faint">—</span>}
                          </td>
                          <td className="num mono">{a.start_date || "—"}</td>
                          <td className="num mono">{a.end_date || "—"}</td>
                          <td>
                            <div className="row gap-sm">
                              <div className="bar" style={{ width: 80 }}>
                                <div className="bar-fill" style={{ width: (a.progress || 0) + "%" }}></div>
                              </div>
                              <span className="mono num-sm">{a.progress || 0}%</span>
                            </div>
                          </td>
                          <td>
                            <button className="btn xs ghost" onClick={() => onEditActivity(a)}>
                              <Icon.edit />
                            </button>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

function ExantePhaseModal({ lang, dossierId, existingCount, onClose, onSaved }) {
  const [code, setCode]     = useStateE("ph" + (existingCount + 1));
  const [nameFr, setNameFr] = useStateE("");
  const [nameEn, setNameEn] = useStateE("");
  const [color, setColor]   = useStateE(EXANTE_PHASE_COLORS[existingCount % EXANTE_PHASE_COLORS.length]);
  const [startDate, setStartDate] = useStateE("");
  const [endDate, setEndDate]     = useStateE("");
  const [busy, setBusy]     = useStateE(false);
  const [err, setErr]       = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      await window.melr.createExantePhase(dossierId, {
        code: code.trim(),
        name_fr: nameFr.trim(),
        name_en: nameEn.trim() || nameFr.trim(),
        color,
        position: existingCount + 1,
        start_date: startDate || null,
        end_date:   endDate   || null,
      });
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 480, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Nouvelle phase" : "New phase"}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Code (ex: A-B)" : "Code (e.g. A-B)"}</span>
            <input required value={code} onChange={(e) => setCode(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Couleur" : "Color"}</span>
            <select value={color} onChange={(e) => setColor(e.target.value)} style={inp}>
              {EXANTE_PHASE_COLORS.map((c, i) => <option key={c} value={c}>{["Bleu","Vert","Violet","Ambre","Rouge","Gris"][i]}</option>)}
            </select>
          </label>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Nom (FR)" : "Name (FR)"}</span>
          <input required value={nameFr} onChange={(e) => setNameFr(e.target.value)}
            placeholder={lang === "fr" ? "Études APS/APD, Travaux, Exploitation…" : "Studies, Works, Operation…"} style={inp} />
        </label>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Nom (EN — optionnel)" : "Name (EN — optional)"}</span>
          <input value={nameEn} onChange={(e) => setNameEn(e.target.value)} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Début (optionnel)" : "Start (optional)"}</span>
            <input type="date" value={startDate} onChange={(e) => setStartDate(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Fin (optionnel)" : "End (optional)"}</span>
            <input type="date" value={endDate} onChange={(e) => setEndDate(e.target.value)} style={inp} />
          </label>
        </div>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4 }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (lang === "fr" ? "Créer la phase" : "Create phase")}
          </button>
        </div>
      </form>
    </div>
  );
}

function ExanteActivityModal({ lang, dossierId, phases, existingCount, editing, onClose, onSaved }) {
  const isEdit = !!editing;
  const today = new Date().toISOString().slice(0, 10);
  const in90  = new Date(Date.now() + 90 * 86400000).toISOString().slice(0, 10);
  const [phaseId, setPhaseId] = useStateE(editing ? editing.phase_id || "" : (phases[0] ? phases[0].id : ""));
  const [wbs, setWbs]         = useStateE(editing ? (editing.wbs || "") : "");
  const [nameFr, setNameFr]   = useStateE(editing ? (editing.name_fr || "") : "");
  const [nameEn, setNameEn]   = useStateE(editing ? (editing.name_en || "") : "");
  const [start, setStart]     = useStateE(editing ? (editing.start_date || today) : today);
  const [end, setEnd]         = useStateE(editing ? (editing.end_date || in90) : in90);
  const [progress, setProgress] = useStateE(editing ? String(editing.progress || 0) : "0");
  const [milestone, setMilestone] = useStateE(editing ? !!editing.milestone : false);
  const [owner, setOwner]     = useStateE(editing ? (editing.owner || "") : "");
  const [notes, setNotes]     = useStateE(editing ? (editing.notes || "") : "");
  const [busy, setBusy]       = useStateE(false);
  const [err, setErr]         = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        phase_id: phaseId || null,
        wbs: wbs.trim() || null,
        name_fr: nameFr.trim(),
        name_en: nameEn.trim() || nameFr.trim(),
        start_date: start || null,
        end_date: end || null,
        progress: Math.max(0, Math.min(100, parseInt(progress, 10) || 0)),
        milestone,
        owner: owner.trim() || null,
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) {
        await window.melr.updateExanteActivity(editing.id, payload);
      } else {
        await window.melr.createExanteActivity(dossierId, payload);
      }
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer cette action ?" : "Delete this action?")) return;
    setBusy(true);
    try {
      await window.melr.deleteExanteActivity(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 620, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier l'action" : "Edit action")
            : (lang === "fr" ? "Nouvelle action" : "New action")}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Phase" : "Phase"}</span>
            <select required value={phaseId} onChange={(e) => setPhaseId(e.target.value)} style={inp}>
              <option value="">— {lang === "fr" ? "choisir une phase" : "pick a phase"} —</option>
              {phases.map((p) => <option key={p.id} value={p.id}>{p.code} — {p.name_fr}</option>)}
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>WBS (ex: 1.2)</span>
            <input value={wbs} onChange={(e) => setWbs(e.target.value)} placeholder="1.2" style={inp} />
          </label>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Libellé (FR)" : "Label (FR)"}</span>
          <input required value={nameFr} onChange={(e) => setNameFr(e.target.value)}
            placeholder={lang === "fr" ? "Mise en service du quai, réception travaux, etc." : "Quay commissioning, works handover, etc."} style={inp} />
        </label>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Libellé (EN — optionnel)" : "Label (EN — optional)"}</span>
          <input value={nameEn} onChange={(e) => setNameEn(e.target.value)} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Début" : "Start"}</span>
            <input required type="date" value={start} onChange={(e) => setStart(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Fin" : "End"}</span>
            <input required type="date" value={end} onChange={(e) => setEnd(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Avancement %" : "Progress %"}</span>
            <input type="number" min="0" max="100" value={progress} onChange={(e) => setProgress(e.target.value)} style={inp} />
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 10, alignItems: "center" }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Responsable" : "Owner"}</span>
            <input value={owner} onChange={(e) => setOwner(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "flex", gap: 6, alignItems: "center", paddingTop: 18 }}>
            <input type="checkbox" checked={milestone} onChange={(e) => setMilestone(e.target.checked)} />
            <span>{lang === "fr" ? "Jalon (◆)" : "Milestone (◆)"}</span>
          </label>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy || !phaseId}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer l'action" : "Create action"))}
          </button>
        </div>
        {!phaseId && phases.length === 0 && (
          <div className="text-faint" style={{ fontSize: 11 }}>
            {lang === "fr" ? "Créez d'abord une phase via le bouton « Phase »." : "First create a phase via the \"Phase\" button."}
          </div>
        )}
      </form>
    </div>
  );
}

// ==================== PHASE 2.1 — CAPEX CRUD ===================================

const EXANTE_CAPEX_CATEGORIES = [
  { v: "studies",        fr: "Études / EIES",        en: "Studies / ESIA",       defaultAmort: 0 },
  { v: "infrastructure", fr: "Infrastructures",      en: "Infrastructure",       defaultAmort: 25 },
  { v: "equipment",      fr: "Équipements",          en: "Equipment",            defaultAmort: 10 },
  { v: "vehicles",       fr: "Véhicules",            en: "Vehicles",             defaultAmort: 7 },
  { v: "office",         fr: "Mobilier / Bureau",    en: "Office / Furniture",   defaultAmort: 5 },
  { v: "other",          fr: "Autres",               en: "Other",                defaultAmort: 0 },
];

function ExanteCapexCard({ lang, lines, contingencyRate, currency, onAdd, onEdit }) {
  const E = window.exanteEngine;
  const ccy = currency || "EUR";
  const fmt = (v) => v == null ? "—" : Number(v).toLocaleString();
  const lineTotal = (l) => (Number(l.quantity) || 1) * (Number(l.unit_cost) || 0);
  // Engine summary (uses contingency from Inputs)
  const summary = E && lines ? E.summarizeCapex(lines, contingencyRate) : { subtotal: 0, contingencies: 0, total: 0 };
  const byCat = E ? E.capexByCategory(lines || []) : {};
  const annualDepreciation = E ? E.totalAnnualDepreciation(lines || []) : 0;

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "CAPEX — Investissements (Tableau 13)" : "CAPEX — Investments (Table 13)"}
        </div>
        {lines && lines.length > 0 && (
          <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
            {lines.length} {lang === "fr" ? "ligne" + (lines.length > 1 ? "s" : "") : "line" + (lines.length > 1 ? "s" : "")}
          </span>
        )}
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onAdd}>
          <Icon.plus /> {lang === "fr" ? "Ligne CAPEX" : "CAPEX line"}
        </button>
      </div>
      <div className="card-body">
        {!lines || lines.length === 0 ? (
          <div style={{ padding: 20, textAlign: "center" }}>
            <div className="text-faint" style={{ fontSize: 13, marginBottom: 6 }}>
              {lang === "fr"
                ? "Aucune ligne d'investissement."
                : "No investment line yet."}
            </div>
            <div className="text-faint" style={{ fontSize: 11.5 }}>
              {lang === "fr"
                ? "Ajoutez chaque rubrique (études, génie civil, équipements, etc.) avec quantité, coût unitaire et durée d'amortissement. Les imprévus (10% par défaut) sont calculés depuis les Inputs."
                : "Add each item (studies, civil works, equipment, etc.) with quantity, unit cost and amortization years. Contingencies (10% by default) are pulled from Inputs."}
            </div>
          </div>
        ) : (
          <>
            {/* KPI strip */}
            <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5, flexWrap: "wrap" }}>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(summary.subtotal)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "sous-total" : "subtotal"}</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>+{fmt(Math.round(summary.contingencies))}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "imprévus" : "contingencies"}</span></span>
              <span style={{ color: "var(--accent)" }}>
                <b className="mono" style={{ fontSize: 16 }}>{fmt(Math.round(summary.total))}</b>
                <span className="text-faint"> {ccy} · {lang === "fr" ? "CAPEX TOTAL" : "TOTAL CAPEX"}</span>
              </span>
              <span style={{ marginLeft: "auto" }}>
                <span className="text-faint">{lang === "fr" ? "Amort. annuelle :" : "Annual depreciation:"}</span>{" "}
                <b className="mono">{fmt(Math.round(annualDepreciation))} {ccy}</b>
              </span>
            </div>
            {/* Lines table */}
            <table className="tbl">
              <thead><tr>
                <th>{lang === "fr" ? "Rubrique" : "Item"}</th>
                <th style={{ width: 110 }}>{lang === "fr" ? "Catégorie" : "Category"}</th>
                <th style={{ width: 60 }}>Phase</th>
                <th className="num" style={{ width: 70 }}>{lang === "fr" ? "Qté" : "Qty"}</th>
                <th className="num" style={{ width: 110 }}>{lang === "fr" ? "Coût unit." : "Unit cost"}</th>
                <th className="num" style={{ width: 130 }}>{lang === "fr" ? "Coût total" : "Total cost"}</th>
                <th style={{ width: 90 }}>{lang === "fr" ? "Amort. (ans)" : "Amort. (yrs)"}</th>
                <th style={{ width: 44 }}></th>
              </tr></thead>
              <tbody>
                {lines.map((l) => {
                  const cat = EXANTE_CAPEX_CATEGORIES.find((c) => c.v === l.category);
                  return (
                    <tr key={l.id}>
                      <td className="strong">{l.rubric}</td>
                      <td>
                        {cat ? (
                          <span className="pill" style={{ fontSize: 10.5 }}>{lang === "fr" ? cat.fr : cat.en}</span>
                        ) : <span className="text-faint">—</span>}
                      </td>
                      <td className="mono text-faint">{l.phase || "—"}</td>
                      <td className="num mono">{Number(l.quantity || 1).toLocaleString()}</td>
                      <td className="num mono">{fmt(l.unit_cost)}</td>
                      <td className="num mono strong">{fmt(lineTotal(l))}</td>
                      <td className="mono">{l.amort_years > 0 ? l.amort_years : "—"}</td>
                      <td>
                        <button className="btn xs ghost" onClick={() => onEdit(l)}>
                          <Icon.edit />
                        </button>
                      </td>
                    </tr>
                  );
                })}
                <tr style={{ background: "var(--bg-sunken)", fontWeight: 600 }}>
                  <td colSpan="5">{lang === "fr" ? "Sous-total" : "Subtotal"}</td>
                  <td className="num mono">{fmt(summary.subtotal)}</td>
                  <td colSpan="2"></td>
                </tr>
                <tr>
                  <td colSpan="5" className="text-faint">{lang === "fr" ? "Imprévus" : "Contingencies"} ({((contingencyRate != null ? contingencyRate : 0.1) * 100).toFixed(0)}%)</td>
                  <td className="num mono">{fmt(Math.round(summary.contingencies))}</td>
                  <td colSpan="2"></td>
                </tr>
                <tr style={{ background: "var(--bg-sunken)", fontWeight: 700 }}>
                  <td colSpan="5" style={{ color: "var(--accent)" }}>{lang === "fr" ? "CAPEX TOTAL" : "TOTAL CAPEX"}</td>
                  <td className="num mono" style={{ color: "var(--accent)" }}>{fmt(Math.round(summary.total))}</td>
                  <td colSpan="2"></td>
                </tr>
              </tbody>
            </table>
            {/* Per-category breakdown */}
            {Object.keys(byCat).length > 1 && (
              <div style={{ marginTop: 12, fontSize: 11.5 }}>
                <div className="text-faint" style={{ marginBottom: 6 }}>
                  {lang === "fr" ? "Répartition par catégorie :" : "By category:"}
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 8 }}>
                  {Object.entries(byCat).map(([cat, v]) => {
                    const meta = EXANTE_CAPEX_CATEGORIES.find((c) => c.v === cat);
                    return (
                      <div key={cat} style={{
                        padding: "6px 10px", borderRadius: 6,
                        background: "var(--bg-sunken)", border: "1px solid var(--line-faint)",
                      }}>
                        <div className="strong" style={{ fontSize: 12 }}>{meta ? (lang === "fr" ? meta.fr : meta.en) : cat}</div>
                        <div className="mono" style={{ fontSize: 12 }}>{fmt(Math.round(v.total))} {ccy}</div>
                        <div className="text-faint" style={{ fontSize: 10.5 }}>
                          {v.count} {lang === "fr" ? "ligne(s)" : "line(s)"} · {lang === "fr" ? "amort. " : "amort. "}{fmt(Math.round(v.depreciation))}/{lang === "fr" ? "an" : "yr"}
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

function ExanteCapexModal({ lang, dossierId, editing, existingCount, onClose, onSaved }) {
  const isEdit = !!editing;
  const [rubric, setRubric] = useStateE(editing ? editing.rubric || "" : "");
  const [phase, setPhase]   = useStateE(editing ? editing.phase || "" : "");
  const [category, setCategory] = useStateE(editing ? editing.category || "infrastructure" : "infrastructure");
  const [quantity, setQuantity] = useStateE(editing ? String(editing.quantity ?? 1) : "1");
  const [unitCost, setUnitCost] = useStateE(editing ? String(editing.unit_cost ?? 0) : "");
  const [amortYears, setAmortYears] = useStateE(editing ? String(editing.amort_years ?? 0) : "");
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  // When category changes (creation only), auto-suggest amortization years.
  React.useEffect(() => {
    if (!isEdit) {
      const meta = EXANTE_CAPEX_CATEGORIES.find((c) => c.v === category);
      if (meta && amortYears === "") setAmortYears(String(meta.defaultAmort));
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [category]);

  const lineTotal = (Number(quantity) || 0) * (Number(unitCost) || 0);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        rubric: rubric.trim(),
        phase: phase.trim() || null,
        category,
        quantity: Number(quantity) || 1,
        unit_cost: Number(unitCost) || 0,
        amort_years: parseInt(amortYears, 10) || 0,
        amort_rate: null,
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exanteCapexCrud.update(editing.id, payload);
      else await window.melr.exanteCapexCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer cette ligne CAPEX ?" : "Delete this CAPEX line?")) return;
    setBusy(true);
    try {
      await window.melr.exanteCapexCrud.remove(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 600, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier la ligne CAPEX" : "Edit CAPEX line")
            : (lang === "fr" ? "Nouvelle ligne CAPEX" : "New CAPEX line")}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Rubrique" : "Item / rubric"}</span>
          <input required value={rubric} onChange={(e) => setRubric(e.target.value)}
            placeholder={lang === "fr" ? "ex: Quai débarquement + appontements" : "e.g. Quay + landing structures"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Catégorie" : "Category"}</span>
            <select value={category} onChange={(e) => setCategory(e.target.value)} style={inp}>
              {EXANTE_CAPEX_CATEGORIES.map((c) => <option key={c.v} value={c.v}>{lang === "fr" ? c.fr : c.en}</option>)}
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Phase (code)" : "Phase (code)"}</span>
            <input value={phase} onChange={(e) => setPhase(e.target.value)} placeholder="A-B, B, C…" style={inp} />
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Quantité" : "Quantity"}</span>
            <input type="number" step="any" value={quantity} onChange={(e) => setQuantity(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Coût unitaire" : "Unit cost"}</span>
            <input type="number" step="any" value={unitCost} onChange={(e) => setUnitCost(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Amort. (ans, 0=non)" : "Amort. (yrs, 0=no)"}</span>
            <input type="number" min="0" max="50" value={amortYears} onChange={(e) => setAmortYears(e.target.value)} style={inp} />
          </label>
        </div>
        {/* Live total */}
        <div style={{
          padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)",
          border: "1px solid var(--line-faint)", fontSize: 12,
        }}>
          {lang === "fr" ? "Coût total de la ligne : " : "Line total: "}
          <b className="mono" style={{ fontSize: 14 }}>{lineTotal.toLocaleString()}</b>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer la ligne" : "Create line"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 2.2 — OPEX CRUD ====================================

const EXANTE_OPEX_CATEGORIES = [
  { v: "personnel", fr: "Personnel",       en: "Personnel" },
  { v: "energy",    fr: "Énergie",         en: "Energy" },
  { v: "fixed",     fr: "Autres fixes",    en: "Other fixed" },
  { v: "other",     fr: "Autres variables",en: "Other variable" },
  { v: "general",   fr: "Général",         en: "General" },
];

function ExanteOpexCard({ lang, lines, inputs, scen, onAdd, onEdit }) {
  const E = window.exanteEngine;
  const ccy = (inputs && inputs.currency) || "EUR";
  const years = (inputs && inputs.horizon_years) || 25;
  const previewYears = [0, 4, 9]; // Y1, Y5, Y10 (zero-indexed)
  const fmt = (v) => v == null ? "—" : Math.round(Number(v)).toLocaleString();
  const empty = !lines || lines.length === 0;

  // For each line, compute the projection so we can preview Y1/Y5/Y10
  const projections = (lines || []).map((l) => ({
    line: l,
    series: E ? E.projectOpexLine(l, years, inputs, scen) : [],
  }));
  const totalByYear = E ? E.totalOpexByYear(lines || [], years, inputs, scen) : new Array(years).fill(0);
  const totalY1 = totalByYear[0] || 0;
  const totalY5 = totalByYear[4] || 0;
  const totalYn = totalByYear[years - 1] || 0;

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "OPEX — Coûts d'exploitation (Tableau 14)" : "OPEX — Operating costs (Table 14)"}
        </div>
        {!empty && (
          <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
            {lines.length} {lang === "fr" ? "rubrique(s)" : "line(s)"}
          </span>
        )}
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onAdd}>
          <Icon.plus /> {lang === "fr" ? "Rubrique OPEX" : "OPEX line"}
        </button>
      </div>
      <div className="card-body">
        {empty ? (
          <div style={{ padding: 20, textAlign: "center" }}>
            <div className="text-faint" style={{ fontSize: 13, marginBottom: 6 }}>
              {lang === "fr" ? "Aucune rubrique OPEX." : "No OPEX line yet."}
            </div>
            <div className="text-faint" style={{ fontSize: 11.5 }}>
              {lang === "fr"
                ? "Ajoutez chaque rubrique avec son montant Année 1. Les inflations se calculent automatiquement depuis les Scénarios et les Inputs."
                : "Add each line with its Year-1 amount. Inflations are computed automatically from Scenarios and Inputs."}
            </div>
          </div>
        ) : (
          <>
            <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5, flexWrap: "wrap" }}>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalY1)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "OPEX An 1" : "OPEX Y1"}</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalY5)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "OPEX An 5" : "OPEX Y5"}</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalYn)}</b> <span className="text-faint">{ccy} · OPEX An {years}</span></span>
              <span style={{ marginLeft: "auto" }} className="text-faint">
                {lang === "fr" ? "Scénario actif : " : "Active scenario: "}
                <b style={{ color: "var(--text)" }}>{(inputs && inputs.scenario) || "Moyen"}</b>
              </span>
            </div>
            <table className="tbl">
              <thead><tr>
                <th>{lang === "fr" ? "Rubrique" : "Line"}</th>
                <th style={{ width: 130 }}>{lang === "fr" ? "Catégorie" : "Category"}</th>
                <th className="num" style={{ width: 130 }}>{lang === "fr" ? "Montant An 1" : "Year 1 amount"}</th>
                <th className="num" style={{ width: 100 }}>An 5</th>
                <th className="num" style={{ width: 100 }}>An 10</th>
                <th style={{ width: 70 }}>O&M pub.</th>
                <th style={{ width: 44 }}></th>
              </tr></thead>
              <tbody>
                {projections.map((p) => {
                  const cat = EXANTE_OPEX_CATEGORIES.find((c) => c.v === p.line.inflation_category);
                  return (
                    <tr key={p.line.id}>
                      <td className="strong">{p.line.rubric}</td>
                      <td>
                        {cat ? <span className="pill" style={{ fontSize: 10.5 }}>{lang === "fr" ? cat.fr : cat.en}</span> : <span className="text-faint">—</span>}
                      </td>
                      <td className="num mono strong">{fmt(p.line.year1_amount)}</td>
                      <td className="num mono text-faint">{fmt(p.series[4])}</td>
                      <td className="num mono text-faint">{fmt(p.series[9])}</td>
                      <td>{p.line.is_public_support ? <span className="pill amber dot" style={{ fontSize: 10 }}>O&M</span> : ""}</td>
                      <td>
                        <button className="btn xs ghost" onClick={() => onEdit(p.line)}>
                          <Icon.edit />
                        </button>
                      </td>
                    </tr>
                  );
                })}
                <tr style={{ background: "var(--bg-sunken)", fontWeight: 700 }}>
                  <td colSpan="2">{lang === "fr" ? "OPEX TOTAL" : "TOTAL OPEX"}</td>
                  <td className="num mono">{fmt(totalY1)}</td>
                  <td className="num mono">{fmt(totalY5)}</td>
                  <td className="num mono">{fmt(totalByYear[9])}</td>
                  <td colSpan="2"></td>
                </tr>
              </tbody>
            </table>
            <div className="text-faint" style={{ fontSize: 10.5, marginTop: 6 }}>
              {lang === "fr"
                ? "Projection : OPEX_t = OPEX_An1 × (1 + inflation_catégorie)^(t-1). Inflations spécifiques par catégorie depuis les Scénarios actifs."
                : "Projection: OPEX_t = OPEX_Y1 × (1 + category inflation)^(t-1). Per-category rates from the active Scenario."}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

function ExanteOpexModal({ lang, dossierId, editing, existingCount, onClose, onSaved }) {
  const isEdit = !!editing;
  const [rubric, setRubric] = useStateE(editing ? editing.rubric || "" : "");
  const [category, setCategory] = useStateE(editing ? editing.inflation_category || "general" : "general");
  const [year1, setYear1] = useStateE(editing ? String(editing.year1_amount ?? 0) : "");
  const [isPublic, setIsPublic] = useStateE(editing ? !!editing.is_public_support : false);
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        rubric: rubric.trim(),
        inflation_category: category,
        year1_amount: Number(year1) || 0,
        is_public_support: isPublic,
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exanteOpexCrud.update(editing.id, payload);
      else await window.melr.exanteOpexCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer cette ligne OPEX ?" : "Delete this OPEX line?")) return;
    setBusy(true);
    try {
      await window.melr.exanteOpexCrud.remove(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 540, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier la ligne OPEX" : "Edit OPEX line")
            : (lang === "fr" ? "Nouvelle ligne OPEX" : "New OPEX line")}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Rubrique" : "Line"}</span>
          <input required value={rubric} onChange={(e) => setRubric(e.target.value)}
            placeholder={lang === "fr" ? "ex: Personnel (gestionnaires, agents quai)" : "e.g. Personnel (managers, agents)"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Catégorie d'inflation" : "Inflation category"}</span>
            <select value={category} onChange={(e) => setCategory(e.target.value)} style={inp}>
              {EXANTE_OPEX_CATEGORIES.map((c) => <option key={c.v} value={c.v}>{lang === "fr" ? c.fr : c.en}</option>)}
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Montant Année 1" : "Year 1 amount"}</span>
            <input required type="number" step="any" value={year1} onChange={(e) => setYear1(e.target.value)} style={inp} />
          </label>
        </div>
        <label style={{ display: "flex", gap: 8, alignItems: "center", padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)", border: "1px solid var(--line-faint)" }}>
          <input type="checkbox" checked={isPublic} onChange={(e) => setIsPublic(e.target.checked)} />
          <span style={{ fontSize: 12 }}>
            {lang === "fr"
              ? "Support O&M public (alimente l'impact sur les Finances publiques)"
              : "Public O&M support (feeds the Public Finances impact)"}
          </span>
        </label>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 2.2 — REVENUE CRUD =================================

function ExanteRevenueCard({ lang, lines, inputs, scen, onAdd, onEdit }) {
  const E = window.exanteEngine;
  const ccy = (inputs && inputs.currency) || "EUR";
  const years = (inputs && inputs.horizon_years) || 25;
  const fmt = (v) => v == null ? "—" : Math.round(Number(v)).toLocaleString();
  const empty = !lines || lines.length === 0;

  const projections = (lines || []).map((l) => ({
    line: l,
    series: E ? E.projectRevenueLine(l, years, inputs, scen) : [],
  }));
  const totalByYear = E ? E.totalRevenueByYear(lines || [], years, inputs, scen) : new Array(years).fill(0);
  const totalY1 = totalByYear[0] || 0;
  const totalY5 = totalByYear[4] || 0;
  const totalY10 = totalByYear[9] || 0;
  const totalYn = totalByYear[years - 1] || 0;

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Revenus — Projection d'activité (Tableau 15)" : "Revenue — Activity projection (Table 15)"}
        </div>
        {!empty && (
          <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
            {lines.length} {lang === "fr" ? "ligne(s)" : "line(s)"}
          </span>
        )}
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onAdd}>
          <Icon.plus /> {lang === "fr" ? "Ligne de revenu" : "Revenue line"}
        </button>
      </div>
      <div className="card-body">
        {empty ? (
          <div style={{ padding: 20, textAlign: "center" }}>
            <div className="text-faint" style={{ fontSize: 13, marginBottom: 6 }}>
              {lang === "fr" ? "Aucune ligne de revenu." : "No revenue line yet."}
            </div>
            <div className="text-faint" style={{ fontSize: 11.5 }}>
              {lang === "fr"
                ? "Volume × Tarif × croissance par scénario. Cochez « Montant forfaitaire » si la ligne est un revenu fixe (subvention, frais, etc.)."
                : "Volume × Tariff × scenario growth. Tick \"Fixed amount\" for flat revenue lines (subsidy, fees, etc.)."}
            </div>
          </div>
        ) : (
          <>
            <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5, flexWrap: "wrap" }}>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalY1)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Revenus An 1" : "Revenue Y1"}</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalY5)}</b> <span className="text-faint">{ccy} · An 5</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalY10)}</b> <span className="text-faint">{ccy} · An 10</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalYn)}</b> <span className="text-faint">{ccy} · An {years}</span></span>
              <span style={{ marginLeft: "auto" }} className="text-faint">
                {lang === "fr" ? "Scénario : " : "Scenario: "}
                <b style={{ color: "var(--text)" }}>{(inputs && inputs.scenario) || "Moyen"}</b>
              </span>
            </div>
            <table className="tbl">
              <thead><tr>
                <th>{lang === "fr" ? "Rubrique" : "Line"}</th>
                <th style={{ width: 60 }}>Comp.</th>
                <th style={{ width: 80 }}>{lang === "fr" ? "Unité" : "Unit"}</th>
                <th className="num" style={{ width: 100 }}>{lang === "fr" ? "Vol. An 1" : "Vol. Y1"}</th>
                <th className="num" style={{ width: 100 }}>{lang === "fr" ? "Tarif An 1" : "Tariff Y1"}</th>
                <th className="num" style={{ width: 130 }}>{lang === "fr" ? "Revenu An 1" : "Revenue Y1"}</th>
                <th className="num" style={{ width: 110 }}>An 10</th>
                <th style={{ width: 44 }}></th>
              </tr></thead>
              <tbody>
                {projections.map((p) => (
                  <tr key={p.line.id}>
                    <td className="strong">
                      {p.line.is_fixed && <span className="pill" style={{ fontSize: 9.5, marginRight: 4 }}>{lang === "fr" ? "fixe" : "fixed"}</span>}
                      {p.line.rubric}
                    </td>
                    <td className="mono text-faint">{p.line.component_code || "—"}</td>
                    <td className="text-faint" style={{ fontSize: 11 }}>{p.line.unit || "—"}</td>
                    <td className="num mono">{p.line.is_fixed ? "—" : fmt(p.line.year1_volume)}</td>
                    <td className="num mono">{fmt(p.line.year1_tariff)}</td>
                    <td className="num mono strong">{fmt(p.series[0])}</td>
                    <td className="num mono text-faint">{fmt(p.series[9])}</td>
                    <td>
                      <button className="btn xs ghost" onClick={() => onEdit(p.line)}>
                        <Icon.edit />
                      </button>
                    </td>
                  </tr>
                ))}
                <tr style={{ background: "var(--bg-sunken)", fontWeight: 700 }}>
                  <td colSpan="5">{lang === "fr" ? "REVENU TOTAL" : "TOTAL REVENUE"}</td>
                  <td className="num mono">{fmt(totalY1)}</td>
                  <td className="num mono">{fmt(totalY10)}</td>
                  <td></td>
                </tr>
              </tbody>
            </table>
            <div className="text-faint" style={{ fontSize: 10.5, marginTop: 6 }}>
              {lang === "fr"
                ? "Projection : Vol_t = Vol_An1 × (1 + croissance_volumes)^(t-1) · Tarif_t = Tarif_An1 × (1 + croissance_tarifs)^(t-1). Croissances par scénario actif."
                : "Projection: Vol_t = Vol_Y1 × (1 + volume growth)^(t-1) · Tariff_t = Tariff_Y1 × (1 + tariff growth)^(t-1). Growth rates from the active scenario."}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

function ExanteRevenueModal({ lang, dossierId, editing, existingCount, onClose, onSaved }) {
  const isEdit = !!editing;
  const [rubric, setRubric] = useStateE(editing ? editing.rubric || "" : "");
  const [component, setComponent] = useStateE(editing ? editing.component_code || "C1" : "C1");
  const [unit, setUnit] = useStateE(editing ? editing.unit || "" : "");
  const [year1Vol, setYear1Vol] = useStateE(editing ? String(editing.year1_volume ?? 0) : "");
  const [year1Tariff, setYear1Tariff] = useStateE(editing ? String(editing.year1_tariff ?? 0) : "");
  const [isFixed, setIsFixed] = useStateE(editing ? !!editing.is_fixed : false);
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const previewY1 = isFixed
    ? Number(year1Tariff) || 0
    : (Number(year1Vol) || 0) * (Number(year1Tariff) || 0);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        rubric: rubric.trim(),
        component_code: component.trim() || null,
        unit: unit.trim() || null,
        year1_volume: isFixed ? 0 : (Number(year1Vol) || 0),
        year1_tariff: Number(year1Tariff) || 0,
        is_fixed: isFixed,
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exanteRevenueCrud.update(editing.id, payload);
      else await window.melr.exanteRevenueCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer cette ligne de revenu ?" : "Delete this revenue line?")) return;
    setBusy(true);
    try {
      await window.melr.exanteRevenueCrud.remove(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 620, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier la ligne de revenu" : "Edit revenue line")
            : (lang === "fr" ? "Nouvelle ligne de revenu" : "New revenue line")}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Rubrique" : "Line"}</span>
          <input required value={rubric} onChange={(e) => setRubric(e.target.value)}
            placeholder={lang === "fr" ? "ex: Débarquement poisson, Vente de glace" : "e.g. Fish landing, Ice sales"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Composante" : "Component"}</span>
            <select value={component} onChange={(e) => setComponent(e.target.value)} style={inp}>
              <option value="">— —</option>
              <option value="C1">C1</option>
              <option value="C2">C2</option>
              <option value="C3">C3</option>
              <option value="C4">C4</option>
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Unité" : "Unit"}</span>
            <input value={unit} onChange={(e) => setUnit(e.target.value)}
              placeholder={lang === "fr" ? "tonnes, jours-emplacements, u…" : "tonnes, day-spots, u…"} style={inp} />
          </label>
        </div>
        <label style={{ display: "flex", gap: 8, alignItems: "center", padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)", border: "1px solid var(--line-faint)" }}>
          <input type="checkbox" checked={isFixed} onChange={(e) => setIsFixed(e.target.checked)} />
          <span style={{ fontSize: 12 }}>
            {lang === "fr"
              ? "Montant forfaitaire (pas de volume × tarif — utiliser le « Tarif An 1 » comme montant fixe inflaté chaque année)"
              : "Fixed amount (no volume × tariff — use \"Year 1 tariff\" as flat amount inflated each year)"}
          </span>
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Volume An 1" : "Volume Y1"}</span>
            <input type="number" step="any" disabled={isFixed} value={year1Vol} onChange={(e) => setYear1Vol(e.target.value)} style={{ ...inp, opacity: isFixed ? 0.5 : 1 }} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{isFixed ? (lang === "fr" ? "Montant An 1" : "Amount Y1") : (lang === "fr" ? "Tarif unitaire An 1" : "Unit tariff Y1")}</span>
            <input required type="number" step="any" value={year1Tariff} onChange={(e) => setYear1Tariff(e.target.value)} style={inp} />
          </label>
        </div>
        <div style={{
          padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)",
          border: "1px solid var(--line-faint)", fontSize: 12,
        }}>
          {lang === "fr" ? "Revenu An 1 (calculé) : " : "Year 1 revenue (computed): "}
          <b className="mono" style={{ fontSize: 14 }}>{previewY1.toLocaleString()}</b>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 2.3 — FINANCING + COMPUTED OUTPUTS ================

const EXANTE_FIN_KINDS = [
  { v: "debt",   fr: "Dette",          en: "Debt" },
  { v: "grant",  fr: "Subvention",     en: "Grant" },
  { v: "equity", fr: "Fonds propres",  en: "Equity" },
  { v: "other",  fr: "Autre",          en: "Other" },
];

function ExanteFinancingCard({ lang, sources, inputs, computed, onAdd, onEdit }) {
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmt = (v) => v == null ? "—" : Math.round(Number(v)).toLocaleString();
  const empty = !sources || sources.length === 0;
  const capexTotal = computed && computed.capex ? computed.capex.total : 0;
  const grants = computed && computed.financing ? computed.financing.grants : 0;
  const equity = computed && computed.financing ? computed.financing.equity : 0;
  const debt   = computed && computed.financing ? computed.financing.debt   : 0;
  const totalSources = grants + equity + debt;
  const gap = totalSources - capexTotal;
  const balanced = Math.abs(gap) < 1;

  const kindLabel = (k) => {
    const m = EXANTE_FIN_KINDS.find((x) => x.v === k);
    return m ? (lang === "fr" ? m.fr : m.en) : k;
  };
  const kindColor = (k) => k === "debt" ? "var(--accent)" : k === "grant" ? "var(--green)" : k === "equity" ? "var(--violet)" : "var(--text-faint)";

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Plan de financement (Tableau 17)" : "Financing plan (Table 17)"}
        </div>
        {!empty && <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>{sources.length}</span>}
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onAdd}>
          <Icon.plus /> {lang === "fr" ? "Source" : "Source"}
        </button>
      </div>
      <div className="card-body">
        {empty ? (
          <div style={{ padding: 20, textAlign: "center" }}>
            <div className="text-faint" style={{ fontSize: 13, marginBottom: 6 }}>
              {lang === "fr" ? "Aucune source de financement." : "No financing source yet."}
            </div>
            <div className="text-faint" style={{ fontSize: 11.5 }}>
              {lang === "fr"
                ? "Ajoutez chaque source (dette, subvention, fonds propres). Le moteur vérifie automatiquement l'équilibre Emplois/Ressources avec le CAPEX total."
                : "Add each source (debt, grant, equity). The engine checks the funding gap against total CAPEX."}
            </div>
          </div>
        ) : (
          <>
            {/* Balance check */}
            <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5, flexWrap: "wrap" }}>
              <span style={{ color: "var(--accent)" }}>
                <span className="text-faint">{lang === "fr" ? "Emplois (CAPEX) : " : "Uses (CAPEX): "}</span>
                <b className="mono" style={{ fontSize: 14 }}>{fmt(capexTotal)}</b>
              </span>
              <span><span className="text-faint">{lang === "fr" ? "Ressources : " : "Sources: "}</span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalSources)}</b></span>
              <span style={{ color: balanced ? "var(--green)" : "var(--red)" }}>
                <b className="mono" style={{ fontSize: 14 }}>{balanced ? "ÉQUILIBRÉ ✓" : `Δ ${fmt(gap)}`}</b>
              </span>
              <span style={{ marginLeft: "auto" }} className="text-faint">
                {lang === "fr" ? "Mix : " : "Mix: "}
                <span className="mono" style={{ color: "var(--accent)" }}>{capexTotal > 0 ? Math.round(debt/capexTotal*100) : 0}%D</span>
                {" · "}
                <span className="mono" style={{ color: "var(--green)" }}>{capexTotal > 0 ? Math.round(grants/capexTotal*100) : 0}%G</span>
                {" · "}
                <span className="mono" style={{ color: "var(--violet)" }}>{capexTotal > 0 ? Math.round(equity/capexTotal*100) : 0}%E</span>
              </span>
            </div>
            <table className="tbl">
              <thead><tr>
                <th>{lang === "fr" ? "Source" : "Source"}</th>
                <th style={{ width: 100 }}>{lang === "fr" ? "Nature" : "Kind"}</th>
                <th className="num" style={{ width: 130 }}>{lang === "fr" ? "Montant" : "Amount"}</th>
                <th className="num" style={{ width: 80 }}>{lang === "fr" ? "Taux" : "Rate"}</th>
                <th className="num" style={{ width: 90 }}>{lang === "fr" ? "Mat./Diff." : "Mat./Grace"}</th>
                <th style={{ width: 44 }}></th>
              </tr></thead>
              <tbody>
                {sources.map((s) => (
                  <tr key={s.id}>
                    <td className="strong">{s.source}</td>
                    <td>
                      <span className="pill" style={{ fontSize: 10.5, color: kindColor(s.kind) }}>
                        {kindLabel(s.kind)}
                      </span>
                    </td>
                    <td className="num mono strong">{fmt(s.amount)}</td>
                    <td className="num mono">{s.kind === "debt" ? (s.interest_rate != null ? (Number(s.interest_rate) * 100).toFixed(1) + "%" : "→ Inp.") : "—"}</td>
                    <td className="num mono text-faint" style={{ fontSize: 11 }}>
                      {s.kind === "debt" ? `${s.maturity_years || (inputs && inputs.debt_maturity_years) || "—"} / ${s.grace_years != null ? s.grace_years : (inputs && inputs.debt_grace_years) || 0}` : "—"}
                    </td>
                    <td>
                      <button className="btn xs ghost" onClick={() => onEdit(s)}>
                        <Icon.edit />
                      </button>
                    </td>
                  </tr>
                ))}
                <tr style={{ background: "var(--bg-sunken)", fontWeight: 700 }}>
                  <td colSpan="2">{lang === "fr" ? "Ressources totales" : "Total sources"}</td>
                  <td className="num mono">{fmt(totalSources)}</td>
                  <td colSpan="3"></td>
                </tr>
              </tbody>
            </table>
          </>
        )}
      </div>
    </div>
  );
}

function ExanteFinancingModal({ lang, dossierId, editing, existingCount, defaults, onClose, onSaved }) {
  const isEdit = !!editing;
  const [source, setSource] = useStateE(editing ? editing.source || "" : "");
  const [kind, setKind] = useStateE(editing ? editing.kind || "debt" : "debt");
  const [amount, setAmount] = useStateE(editing ? String(editing.amount ?? 0) : "");
  const [currency, setCurrency] = useStateE(editing ? editing.currency || (defaults && defaults.currency) || "EUR" : (defaults && defaults.currency) || "EUR");
  const [rate, setRate] = useStateE(editing ? (editing.interest_rate != null ? String(editing.interest_rate) : "") : "");
  const [maturity, setMaturity] = useStateE(editing ? (editing.maturity_years != null ? String(editing.maturity_years) : "") : "");
  const [grace, setGrace] = useStateE(editing ? (editing.grace_years != null ? String(editing.grace_years) : "") : "");
  const [drawdown, setDrawdown] = useStateE(editing ? String(editing.drawdown_year ?? 0) : "0");
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        source: source.trim(),
        kind,
        amount: Number(amount) || 0,
        currency,
        interest_rate: rate === "" ? null : Number(rate),
        maturity_years: maturity === "" ? null : parseInt(maturity, 10),
        grace_years: grace === "" ? null : parseInt(grace, 10),
        drawdown_year: parseInt(drawdown, 10) || 0,
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exanteFinancingCrud.update(editing.id, payload);
      else await window.melr.exanteFinancingCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer cette source ?" : "Delete this source?")) return;
    setBusy(true);
    try {
      await window.melr.exanteFinancingCrud.remove(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };
  const isDebt = kind === "debt";

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 620, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier la source de financement" : "Edit financing source")
            : (lang === "fr" ? "Nouvelle source de financement" : "New financing source")}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Source" : "Source"}</span>
          <input required value={source} onChange={(e) => setSource(e.target.value)}
            placeholder={lang === "fr" ? "ex: Dette AFD, Subvention État, Fonds propres" : "e.g. AFD loan, State grant, Own equity"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Nature" : "Kind"}</span>
            <select value={kind} onChange={(e) => setKind(e.target.value)} style={inp}>
              {EXANTE_FIN_KINDS.map((k) => <option key={k.v} value={k.v}>{lang === "fr" ? k.fr : k.en}</option>)}
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Montant" : "Amount"}</span>
            <input required type="number" step="any" value={amount} onChange={(e) => setAmount(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Devise" : "Currency"}</span>
            <input value={currency} onChange={(e) => setCurrency(e.target.value)} style={inp} />
          </label>
        </div>
        {isDebt && (
          <>
            <div className="text-faint" style={{ fontSize: 11 }}>
              {lang === "fr"
                ? "Spécifique à la dette — laissez vide pour utiliser les valeurs des Inputs."
                : "Debt-specific — leave blank to inherit from Inputs."}
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 10 }}>
              <label style={{ display: "grid", gap: 4 }}>
                <span style={lbl}>{lang === "fr" ? "Taux d'intérêt (0–1)" : "Rate (0–1)"}</span>
                <input type="number" step="any" value={rate} onChange={(e) => setRate(e.target.value)} placeholder={defaults && defaults.debt_interest_rate} style={inp} />
              </label>
              <label style={{ display: "grid", gap: 4 }}>
                <span style={lbl}>{lang === "fr" ? "Maturité (ans)" : "Maturity (yrs)"}</span>
                <input type="number" value={maturity} onChange={(e) => setMaturity(e.target.value)} placeholder={defaults && defaults.debt_maturity_years} style={inp} />
              </label>
              <label style={{ display: "grid", gap: 4 }}>
                <span style={lbl}>{lang === "fr" ? "Différé (ans)" : "Grace (yrs)"}</span>
                <input type="number" value={grace} onChange={(e) => setGrace(e.target.value)} placeholder={defaults && defaults.debt_grace_years} style={inp} />
              </label>
              <label style={{ display: "grid", gap: 4 }}>
                <span style={lbl}>{lang === "fr" ? "Tirage (an)" : "Drawdown year"}</span>
                <input type="number" value={drawdown} onChange={(e) => setDrawdown(e.target.value)} style={inp} />
              </label>
            </div>
          </>
        )}
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ---------- Financial indicators dashboard ----------

function ExanteFinancialIndicators({ lang, inputs, computed }) {
  if (!computed) return null;
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmtAmount = (v) => v == null || !isFinite(v) ? "—" : Math.round(v).toLocaleString();
  const fmtPct = (v) => v == null || !isFinite(v) ? "—" : (v * 100).toFixed(1) + "%";
  const fmtRatio = (v) => v == null || !isFinite(v) ? "—" : v >= 999 ? "∞" : Number(v).toFixed(2);
  const ind = computed.indicators;
  const discountFin = (inputs && inputs.discount_rate_financial) || 0.09;

  const tile = (label, value, ok, sub) => (
    <div className="card" style={{ padding: 14, background: "var(--bg-elev)" }}>
      <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.04em" }}>{label}</div>
      <div className="mono" style={{ fontSize: 22, fontWeight: 600, marginTop: 4, color: ok === true ? "var(--green)" : ok === false ? "var(--red)" : "var(--text)" }}>
        {value}
      </div>
      {sub && <div className="text-faint" style={{ fontSize: 11, marginTop: 2 }}>{sub}</div>}
    </div>
  );

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Indicateurs financiers consolidés" : "Consolidated financial indicators"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Calculés en direct à chaque saisie (moteur JS)"
            : "Recomputed live on every edit (JS engine)"}
        </span>
        <div style={{ flex: 1 }} />
        {ind.viable
          ? <span className="pill green dot" style={{ fontSize: 11 }}>{lang === "fr" ? "VIABLE ✓" : "VIABLE ✓"}</span>
          : <span className="pill red dot" style={{ fontSize: 11 }}>{lang === "fr" ? "À RÉVISER ✗" : "TO REVIEW ✗"}</span>}
      </div>
      <div className="card-body">
        <div className="grid" style={{ gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 10 }}>
          {tile(
            "VAN " + (lang === "fr" ? "projet" : "project") + " (" + ccy + ")",
            fmtAmount(ind.npvProject),
            ind.npvProject != null && ind.npvProject >= 0,
            lang === "fr" ? "Cash-flow projet actualisé" : "Discounted project cashflow"
          )}
          {tile(
            "TRI " + (lang === "fr" ? "projet" : "project"),
            fmtPct(ind.irrProject),
            ind.irrProject != null && ind.irrProject > discountFin,
            (lang === "fr" ? "> " : "> ") + fmtPct(discountFin) + " " + (lang === "fr" ? "requis" : "required")
          )}
          {tile(
            "VAN " + (lang === "fr" ? "actionnaire" : "equity") + " (" + ccy + ")",
            fmtAmount(ind.npvEquity),
            ind.npvEquity != null && ind.npvEquity >= 0,
            "FCFE"
          )}
          {tile(
            "TRI " + (lang === "fr" ? "actionnaire" : "equity"),
            fmtPct(ind.irrEquity),
            ind.irrEquity != null && ind.irrEquity > discountFin,
            (lang === "fr" ? "Rendement actionnaire" : "Equity yield")
          )}
          {tile(
            "DSCR " + (lang === "fr" ? "min" : "min"),
            fmtRatio(ind.dscrMin),
            ind.dscrMin == null || ind.dscrMin >= 1.2,
            lang === "fr" ? "≥ 1,2 → bancable" : "≥ 1.2 → bankable"
          )}
          {tile(
            (lang === "fr" ? "Marge EBITDA moy." : "Avg EBITDA margin"),
            fmtPct(ind.ebitdaMarginAvg),
            ind.ebitdaMarginAvg > 0,
            lang === "fr" ? "Sur " + computed.operatingYears + " ans" : "Over " + computed.operatingYears + " yrs"
          )}
        </div>
        <div className="text-faint" style={{ fontSize: 11, marginTop: 10 }}>
          {lang === "fr"
            ? "Décision automatique : VAN ≥ 0 ET TRI > taux d'actualisation ET DSCR min ≥ 1,2 ⇒ VIABLE. Seuils financiers ex-ante standards."
            : "Auto decision: NPV ≥ 0 AND IRR > discount AND DSCR min ≥ 1.2 ⇒ VIABLE. Standard ex-ante financial thresholds."}
        </div>
      </div>
    </div>
  );
}

// ---------- Debt schedule ----------

function ExanteDebtScheduleCard({ lang, inputs, computed }) {
  if (!computed || !computed.debt) return null;
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmt = (v) => Math.round(Number(v) || 0).toLocaleString();
  const years = computed.operatingYears + 1; // includes Y0
  // Only show if debt exists
  const totalDrawdown = (computed.debt.drawdown || []).reduce((s, v) => s + v, 0);
  if (totalDrawdown < 1) {
    return (
      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-head">
          <div className="card-title">{lang === "fr" ? "Échéancier de la dette (Tableau 18)" : "Debt schedule (Table 18)"}</div>
        </div>
        <div className="card-body">
          <div className="text-faint" style={{ fontSize: 12, textAlign: "center", padding: 12 }}>
            {lang === "fr"
              ? "Aucune dette dans le plan de financement — l'échéancier est vide."
              : "No debt in the financing plan — schedule is empty."}
          </div>
        </div>
      </div>
    );
  }
  // Show 11 first columns (Y0..Y10) for compactness
  const cols = Math.min(11, years);
  const colIdx = Array.from({ length: cols }, (_, i) => i);

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">{lang === "fr" ? "Échéancier de la dette (Tableau 18)" : "Debt schedule (Table 18)"}</div>
        <div style={{ flex: 1 }} />
        <span className="text-faint" style={{ fontSize: 11 }}>
          {lang === "fr" ? "Affichage : An 0 → An " + (cols - 1) : "Display: Y0 → Y" + (cols - 1)} ({ccy})
        </span>
      </div>
      <div className="card-body flush" style={{ overflowX: "auto" }}>
        <table className="tbl" style={{ minWidth: 800 }}>
          <thead><tr>
            <th>{lang === "fr" ? "Rubrique" : "Item"}</th>
            {colIdx.map((t) => <th key={t} className="num">An {t}</th>)}
          </tr></thead>
          <tbody>
            <tr><td className="strong">{lang === "fr" ? "Ouverture" : "Opening"}</td>{colIdx.map((t) => <td key={t} className="num mono text-faint">{fmt(computed.debt.opening[t])}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Tirage" : "Drawdown"}</td>{colIdx.map((t) => <td key={t} className="num mono">{fmt(computed.debt.drawdown[t])}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Intérêts" : "Interest"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>{fmt(computed.debt.interest[t])}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Capital remboursé" : "Principal"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>{fmt(computed.debt.principal[t])}</td>)}</tr>
            <tr style={{ background: "var(--bg-sunken)", fontWeight: 600 }}><td>{lang === "fr" ? "Service de la dette" : "Debt service"}</td>{colIdx.map((t) => <td key={t} className="num mono">{fmt(computed.debt.debtService[t])}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Clôture" : "Closing"}</td>{colIdx.map((t) => <td key={t} className="num mono text-faint">{fmt(computed.debt.closing[t])}</td>)}</tr>
            <tr style={{ background: "var(--bg-sunken)" }}>
              <td className="strong">DSCR</td>
              <td className="num text-faint">—</td>
              {(computed.dscrSeries || []).slice(0, cols - 1).map((v, t) => (
                <td key={t} className="num mono" style={{ color: v >= 1.2 ? "var(--green)" : "var(--red)", fontWeight: 600 }}>
                  {v >= 999 ? "∞" : v.toFixed(2)}
                </td>
              ))}
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ---------- Profit & Loss ----------

function ExantePLCard({ lang, inputs, computed }) {
  if (!computed || !computed.pl || computed.pl.length === 0) return null;
  const fmt = (v) => Math.round(Number(v) || 0).toLocaleString();
  const ccy = (inputs && inputs.currency) || "EUR";
  const cols = Math.min(10, computed.pl.length);
  const colIdx = Array.from({ length: cols }, (_, i) => i);

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">{lang === "fr" ? "Compte de résultat prévisionnel" : "Forecast income statement"}</div>
        <div style={{ flex: 1 }} />
        <span className="text-faint" style={{ fontSize: 11 }}>
          {lang === "fr" ? "An 1 → An " + cols : "Y1 → Y" + cols} ({ccy})
        </span>
      </div>
      <div className="card-body flush" style={{ overflowX: "auto" }}>
        <table className="tbl" style={{ minWidth: 800 }}>
          <thead><tr>
            <th>{lang === "fr" ? "Rubrique" : "Item"}</th>
            {colIdx.map((t) => <th key={t} className="num">An {t + 1}</th>)}
          </tr></thead>
          <tbody>
            <tr><td className="strong">{lang === "fr" ? "Revenus" : "Revenue"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--green)" }}>{fmt(computed.pl[t].revenue)}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "OPEX" : "OPEX"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>({fmt(computed.pl[t].opex)})</td>)}</tr>
            <tr style={{ background: "var(--bg-sunken)", fontWeight: 600 }}><td>EBITDA</td>{colIdx.map((t) => <td key={t} className="num mono">{fmt(computed.pl[t].ebitda)}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Amortissement" : "Depreciation"}</td>{colIdx.map((t) => <td key={t} className="num mono text-faint">({fmt(computed.pl[t].amortization)})</td>)}</tr>
            <tr style={{ background: "var(--bg-sunken)", fontWeight: 600 }}><td>EBIT</td>{colIdx.map((t) => <td key={t} className="num mono">{fmt(computed.pl[t].ebit)}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Intérêts" : "Interest"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>({fmt(computed.pl[t].interest)})</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Résultat avant impôt" : "Pre-tax income"}</td>{colIdx.map((t) => <td key={t} className="num mono">{fmt(computed.pl[t].ebt)}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Impôt" : "Tax"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>({fmt(computed.pl[t].tax)})</td>)}</tr>
            <tr style={{ background: "var(--bg-sunken)", fontWeight: 700 }}><td style={{ color: "var(--accent)" }}>{lang === "fr" ? "Résultat net" : "Net income"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: computed.pl[t].netIncome >= 0 ? "var(--green)" : "var(--red)", fontWeight: 700 }}>{fmt(computed.pl[t].netIncome)}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Marge EBITDA" : "EBITDA margin"}</td>{colIdx.map((t) => <td key={t} className="num mono text-faint">{(computed.pl[t].ebitdaMargin * 100).toFixed(1)}%</td>)}</tr>
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ---------- Cash flow ----------

function ExanteCashFlowCard({ lang, inputs, computed }) {
  if (!computed || !computed.cashFlow) return null;
  const fmt = (v) => Math.round(Number(v) || 0).toLocaleString();
  const ccy = (inputs && inputs.currency) || "EUR";
  const cf = computed.cashFlow;
  const cols = Math.min(11, cf.fcfProject.length);
  const colIdx = Array.from({ length: cols }, (_, i) => i);

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">{lang === "fr" ? "Tableau des flux de trésorerie (Tableau 19)" : "Cash flow statement (Table 19)"}</div>
        <div style={{ flex: 1 }} />
        <span className="text-faint" style={{ fontSize: 11 }}>
          {lang === "fr" ? "An 0 → An " + (cols - 1) : "Y0 → Y" + (cols - 1)} ({ccy})
        </span>
      </div>
      <div className="card-body flush" style={{ overflowX: "auto" }}>
        <table className="tbl" style={{ minWidth: 800 }}>
          <thead><tr>
            <th>{lang === "fr" ? "Flux" : "Flow"}</th>
            {colIdx.map((t) => <th key={t} className="num">An {t}</th>)}
          </tr></thead>
          <tbody>
            <tr><td className="strong">{lang === "fr" ? "Entrées totales" : "Total inflows"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--green)" }}>{fmt(cf.inflows[t])}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Sorties totales" : "Total outflows"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>({fmt(cf.outflows[t])})</td>)}</tr>
            <tr style={{ background: "var(--bg-sunken)", fontWeight: 600 }}><td>{lang === "fr" ? "FCF projet (non levier)" : "Project FCF (unleveraged)"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: cf.fcfProject[t] >= 0 ? "var(--text)" : "var(--red)" }}>{fmt(cf.fcfProject[t])}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Cumul FCF projet" : "Cumulative project FCF"}</td>{colIdx.map((t) => <td key={t} className="num mono text-faint">{fmt(cf.cumProject[t])}</td>)}</tr>
            <tr style={{ background: "var(--bg-sunken)", fontWeight: 600 }}><td>{lang === "fr" ? "FCFE actionnaire" : "FCFE equity"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: cf.fcfEquity[t] >= 0 ? "var(--text)" : "var(--red)" }}>{fmt(cf.fcfEquity[t])}</td>)}</tr>
            <tr><td className="strong">{lang === "fr" ? "Cumul FCFE" : "Cumulative FCFE"}</td>{colIdx.map((t) => <td key={t} className="num mono text-faint">{fmt(cf.cumEquity[t])}</td>)}</tr>
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ==================== PHASE 3.1 — PUBLIC FINANCE ===============================

function ExantePublicFinanceCard({ lang, inputs, scen, revenueByYear, opexLines, financingSources, publicTransfers, onAddTransfer, onEditTransfer }) {
  const E = window.exanteEngine;
  if (!E || !revenueByYear) return null;
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmt = (v) => Math.round(Number(v) || 0).toLocaleString();
  const result = E.computePublicFinance({
    inputs, scen, revenueByYear, opexLines, financingSources, publicTransfers,
  });
  const cols = Math.min(10, result.perYear.length);
  const colIdx = Array.from({ length: cols }, (_, i) => i);
  const cumFinal = result.cumulativeFinal;
  const cumOk = cumFinal >= 0;

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Impact sur les Finances publiques" : "Impact on public finances"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Recettes − coûts publics, actualisés au taux économique"
            : "Public revenues − public costs, discounted at economic rate"}
        </span>
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onAddTransfer}>
          <Icon.plus /> {lang === "fr" ? "Transfert" : "Transfer"}
        </button>
      </div>
      <div className="card-body">
        {/* KPI strip */}
        <div className="row gap-md" style={{ marginBottom: 12, fontSize: 11.5, flexWrap: "wrap" }}>
          <span><b className="mono" style={{ fontSize: 14, color: "var(--green)" }}>+{fmt(result.totalRevenues)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Recettes cumulées" : "Cumulative revenues"}</span></span>
          <span><b className="mono" style={{ fontSize: 14, color: "var(--red)" }}>−{fmt(result.subsidy)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Subvention CAPEX" : "Capex subsidy"}</span></span>
          <span><b className="mono" style={{ fontSize: 14, color: "var(--red)" }}>−{fmt(result.totalOmSupport)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Support O&M cumulé" : "Cumul. O&M support"}</span></span>
          <span style={{ marginLeft: "auto", color: cumOk ? "var(--green)" : "var(--red)" }}>
            <span className="text-faint">{lang === "fr" ? "Cumul net final : " : "Final cumulative: "}</span>
            <b className="mono" style={{ fontSize: 16 }}>{fmt(cumFinal)} {ccy}</b>
            {" "}{cumOk ? "✓" : "✗"}
          </span>
        </div>
        {/* Per-year table */}
        <div style={{ overflowX: "auto" }}>
          <table className="tbl" style={{ minWidth: 800 }}>
            <thead><tr>
              <th>{lang === "fr" ? "Flux" : "Flow"}</th>
              {colIdx.map((t) => <th key={t} className="num">An {t + 1}</th>)}
            </tr></thead>
            <tbody>
              <tr><td className="strong">{lang === "fr" ? "Recettes fiscales" : "Tax revenues"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--green)" }}>{fmt(result.perYear[t].revenues)}</td>)}</tr>
              <tr><td className="strong">{lang === "fr" ? "Support O&M public" : "Public O&M support"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>({fmt(result.perYear[t].omSupport)})</td>)}</tr>
              <tr><td className="strong">{lang === "fr" ? "Autres entrées" : "Other inflows"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--green)" }}>{fmt(result.perYear[t].otherIn)}</td>)}</tr>
              <tr><td className="strong">{lang === "fr" ? "Autres sorties" : "Other outflows"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: "var(--red)" }}>({fmt(result.perYear[t].otherOut)})</td>)}</tr>
              <tr style={{ background: "var(--bg-sunken)", fontWeight: 700 }}><td>{lang === "fr" ? "Impact net" : "Net impact"}</td>{colIdx.map((t) => <td key={t} className="num mono" style={{ color: result.perYear[t].net >= 0 ? "var(--green)" : "var(--red)" }}>{fmt(result.perYear[t].net)}</td>)}</tr>
              <tr><td className="strong">{lang === "fr" ? "Cumul" : "Cumulative"}</td>{colIdx.map((t) => <td key={t} className="num mono text-faint">{fmt(result.cumulative[t])}</td>)}</tr>
            </tbody>
          </table>
        </div>
        {/* Other transfers list */}
        {publicTransfers && publicTransfers.length > 0 && (
          <div style={{ marginTop: 12 }}>
            <div className="text-faint" style={{ fontSize: 11.5, marginBottom: 6 }}>
              {lang === "fr" ? "Autres transferts saisis :" : "Other transfers entered:"}
            </div>
            <table className="tbl">
              <thead><tr>
                <th>{lang === "fr" ? "Libellé" : "Label"}</th>
                <th style={{ width: 80 }}>{lang === "fr" ? "Sens" : "Direction"}</th>
                <th className="num" style={{ width: 130 }}>{lang === "fr" ? "Montant/an" : "Amount/yr"}</th>
                <th style={{ width: 100 }}>{lang === "fr" ? "Période" : "Period"}</th>
                <th style={{ width: 44 }}></th>
              </tr></thead>
              <tbody>
                {publicTransfers.map((t) => (
                  <tr key={t.id}>
                    <td className="strong">{t.label}</td>
                    <td>
                      <span className="pill" style={{ fontSize: 10.5, color: t.direction === "inflow" ? "var(--green)" : "var(--red)" }}>
                        {t.direction === "inflow" ? (lang === "fr" ? "Entrée" : "Inflow") : (lang === "fr" ? "Sortie" : "Outflow")}
                      </span>
                    </td>
                    <td className="num mono">{fmt(t.amount_per_year)}</td>
                    <td className="text-faint mono" style={{ fontSize: 11 }}>An {t.start_year}{t.end_year ? "–" + t.end_year : "+"}</td>
                    <td>
                      <button className="btn xs ghost" onClick={() => onEditTransfer(t)}>
                        <Icon.edit />
                      </button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
        <div className="text-faint" style={{ fontSize: 10.5, marginTop: 8 }}>
          {lang === "fr"
            ? "NPV impact public : " + fmt(result.npvNet) + " " + ccy + " (actualisé au taux économique " + (((inputs && inputs.discount_rate_economic) || 0.09) * 100).toFixed(1) + "%)"
            : "Public NPV impact: " + fmt(result.npvNet) + " " + ccy + " (discounted at " + (((inputs && inputs.discount_rate_economic) || 0.09) * 100).toFixed(1) + "%)"}
        </div>
      </div>
    </div>
  );
}

function ExantePublicTransferModal({ lang, dossierId, editing, existingCount, onClose, onSaved }) {
  const isEdit = !!editing;
  const [label, setLabel] = useStateE(editing ? editing.label || "" : "");
  const [direction, setDirection] = useStateE(editing ? editing.direction || "outflow" : "outflow");
  const [amount, setAmount] = useStateE(editing ? String(editing.amount_per_year ?? 0) : "");
  const [startYear, setStartYear] = useStateE(editing ? String(editing.start_year ?? 1) : "1");
  const [endYear, setEndYear] = useStateE(editing ? (editing.end_year != null ? String(editing.end_year) : "") : "");
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        label: label.trim(),
        direction,
        amount_per_year: Number(amount) || 0,
        start_year: parseInt(startYear, 10) || 1,
        end_year: endYear === "" ? null : parseInt(endYear, 10),
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exantePublicTransfersCrud.update(editing.id, payload);
      else await window.melr.exantePublicTransfersCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer ce transfert ?" : "Delete this transfer?")) return;
    setBusy(true);
    try {
      await window.melr.exantePublicTransfersCrud.remove(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 540, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier le transfert public" : "Edit public transfer")
            : (lang === "fr" ? "Nouveau transfert public" : "New public transfer")}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Libellé" : "Label"}</span>
          <input required value={label} onChange={(e) => setLabel(e.target.value)}
            placeholder={lang === "fr" ? "ex: Contribution exceptionnelle État" : "e.g. One-off State contribution"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Sens" : "Direction"}</span>
            <select value={direction} onChange={(e) => setDirection(e.target.value)} style={inp}>
              <option value="inflow">{lang === "fr" ? "Entrée (recette publique)" : "Inflow (public revenue)"}</option>
              <option value="outflow">{lang === "fr" ? "Sortie (coût public)" : "Outflow (public cost)"}</option>
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Montant par an" : "Amount per year"}</span>
            <input required type="number" step="any" value={amount} onChange={(e) => setAmount(e.target.value)} style={inp} />
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "An de début" : "Start year"}</span>
            <input type="number" min="0" value={startYear} onChange={(e) => setStartYear(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "An de fin (vide=fin horizon)" : "End year (blank=end of horizon)"}</span>
            <input type="number" value={endYear} onChange={(e) => setEndYear(e.target.value)} style={inp} />
          </label>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 3.2 — MPR (3 phases) + ECONOMIC INDICATORS =========

const EXANTE_MPR_CATEGORIES = [
  { v: "capex_tax", fr: "Taxes/TVA sur investissements", en: "Capex taxes/VAT" },
  { v: "opex_tax",  fr: "Taxes sur exploitation",        en: "OPEX taxes" },
  { v: "customs",   fr: "Droits de douane imports",      en: "Import duties" },
  { v: "subsidy",   fr: "Subventions incluses recettes", en: "Subsidies in revenue" },
  { v: "interest",  fr: "Intérêts de la dette",          en: "Debt interest" },
  { v: "other",     fr: "Autres transferts",             en: "Other transfers" },
];

const EXANTE_EXT_CATEGORIES = [
  { v: "jobs",          fr: "Emplois créés",                 en: "Jobs created" },
  { v: "time_saved",    fr: "Gain de temps",                 en: "Time saved" },
  { v: "safety",        fr: "Réduction des accidents",       en: "Accident reduction" },
  { v: "co2",           fr: "Réduction émissions CO2",       en: "CO2 reduction" },
  { v: "health",        fr: "Santé / espérance de vie",      en: "Health / life expectancy" },
  { v: "poverty",       fr: "Réduction de la pauvreté",      en: "Poverty reduction" },
  { v: "losses_reduced",fr: "Réduction des pertes",          en: "Loss reduction" },
  { v: "pollution",     fr: "Coûts environnementaux",        en: "Environmental costs" },
  { v: "displacement",  fr: "Déplacement populations/activités", en: "Displacement" },
  { v: "other",         fr: "Autre",                         en: "Other" },
];

function ExanteMprPhase1Card({ lang, inputs, transfers, mprResult, onAdd, onEdit }) {
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmt = (v) => Math.round(Number(v) || 0).toLocaleString();
  const empty = !transfers || transfers.length === 0;
  // Sum interest from debt (auto-detected) so the user knows it's already in
  const autoInterest = mprResult && mprResult.transfersBy
    ? mprResult.transfersBy.interest.reduce((s, v) => s + v, 0)
    : 0;
  const totalManual = (transfers || []).reduce((s, t) => s + Number(t.amount_per_year || 0) * Math.max(1, Number(t.end_year || (mprResult ? mprResult.realRevenueByYear.length : 1)) - Number(t.start_year || 1) + 1), 0);
  const totalEliminated = autoInterest + totalManual;

  const catLabel = (v) => {
    const m = EXANTE_MPR_CATEGORIES.find((c) => c.v === v);
    return m ? (lang === "fr" ? m.fr : m.en) : v;
  };

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "MPR Phase 1 — Élimination des transferts" : "MPR Phase 1 — Transfer elimination"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Taxes, douanes, subventions, intérêts — retirés des flux financiers pour obtenir les ressources réelles."
            : "Taxes, duties, subsidies, interest — removed from financial flows to get real-resource flows."}
        </span>
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onAdd}>
          <Icon.plus /> {lang === "fr" ? "Transfert" : "Transfer"}
        </button>
      </div>
      <div className="card-body">
        <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5, flexWrap: "wrap" }}>
          <span><b className="mono" style={{ fontSize: 14 }}>{fmt(autoInterest)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Intérêts dette (auto)" : "Debt interest (auto)"}</span></span>
          <span><b className="mono" style={{ fontSize: 14 }}>{fmt(totalManual)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Transferts manuels" : "Manual transfers"}</span></span>
          <span style={{ color: "var(--accent)" }}>
            <b className="mono" style={{ fontSize: 14 }}>{fmt(totalEliminated)}</b>
            <span className="text-faint"> {ccy} · {lang === "fr" ? "Total éliminé" : "Total eliminated"}</span>
          </span>
        </div>
        {empty ? (
          <div className="text-faint" style={{ fontSize: 12, padding: "8px 4px", textAlign: "center" }}>
            {lang === "fr"
              ? "Aucun transfert saisi manuellement. Les intérêts de la dette sont déduits automatiquement depuis le plan de financement."
              : "No manual transfer entered. Debt interest is automatically deducted from the financing plan."}
          </div>
        ) : (
          <table className="tbl">
            <thead><tr>
              <th>{lang === "fr" ? "Libellé" : "Label"}</th>
              <th style={{ width: 200 }}>{lang === "fr" ? "Catégorie" : "Category"}</th>
              <th className="num" style={{ width: 130 }}>{lang === "fr" ? "Montant/an" : "Amount/yr"}</th>
              <th style={{ width: 100 }}>{lang === "fr" ? "Période" : "Period"}</th>
              <th style={{ width: 44 }}></th>
            </tr></thead>
            <tbody>
              {transfers.map((t) => (
                <tr key={t.id}>
                  <td className="strong">{t.label}</td>
                  <td><span className="pill" style={{ fontSize: 10.5 }}>{catLabel(t.category)}</span></td>
                  <td className="num mono">{fmt(t.amount_per_year)}</td>
                  <td className="text-faint mono" style={{ fontSize: 11 }}>An {t.start_year}{t.end_year ? "–" + t.end_year : "+"}</td>
                  <td>
                    <button className="btn xs ghost" onClick={() => onEdit(t)}>
                      <Icon.edit />
                    </button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

function ExanteMprTransferModal({ lang, dossierId, editing, existingCount, onClose, onSaved }) {
  const isEdit = !!editing;
  const [label, setLabel] = useStateE(editing ? editing.label || "" : "");
  const [category, setCategory] = useStateE(editing ? editing.category || "opex_tax" : "opex_tax");
  const [amount, setAmount] = useStateE(editing ? String(editing.amount_per_year ?? 0) : "");
  const [startYear, setStartYear] = useStateE(editing ? String(editing.start_year ?? 1) : "1");
  const [endYear, setEndYear] = useStateE(editing ? (editing.end_year != null ? String(editing.end_year) : "") : "");
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        label: label.trim(),
        category,
        amount_per_year: Number(amount) || 0,
        start_year: parseInt(startYear, 10) || 1,
        end_year: endYear === "" ? null : parseInt(endYear, 10),
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exanteMprTransfersCrud.update(editing.id, payload);
      else await window.melr.exanteMprTransfersCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer ce transfert ?" : "Delete this transfer?")) return;
    setBusy(true);
    try {
      await window.melr.exanteMprTransfersCrud.remove(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 540, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier le transfert MPR" : "Edit MPR transfer")
            : (lang === "fr" ? "Nouveau transfert MPR (à éliminer)" : "New MPR transfer (to eliminate)")}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Libellé" : "Label"}</span>
          <input required value={label} onChange={(e) => setLabel(e.target.value)}
            placeholder={lang === "fr" ? "ex: TVA sur équipements, Droits de douane import" : "e.g. VAT on equipment, Import duties"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Catégorie" : "Category"}</span>
            <select value={category} onChange={(e) => setCategory(e.target.value)} style={inp}>
              {EXANTE_MPR_CATEGORIES.filter((c) => c.v !== "interest").map((c) => <option key={c.v} value={c.v}>{lang === "fr" ? c.fr : c.en}</option>)}
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Montant par an" : "Amount per year"}</span>
            <input required type="number" step="any" value={amount} onChange={(e) => setAmount(e.target.value)} style={inp} />
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "An de début (0=construction)" : "Start year (0=construction)"}</span>
            <input type="number" min="0" value={startYear} onChange={(e) => setStartYear(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "An de fin" : "End year"}</span>
            <input type="number" value={endYear} onChange={(e) => setEndYear(e.target.value)} style={inp} />
          </label>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

function ExanteExternalitiesCard({ lang, inputs, externalities, mprResult, onAdd, onEdit }) {
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmt = (v) => Math.round(Number(v) || 0).toLocaleString();
  const empty = !externalities || externalities.length === 0;
  const totalPos = mprResult ? mprResult.extPosByYear.reduce((s, v) => s + v, 0) : 0;
  const totalNeg = mprResult ? mprResult.extNegByYear.reduce((s, v) => s + v, 0) : 0;
  const net = totalPos - totalNeg;

  const positives = (externalities || []).filter((e) => e.kind === "positive");
  const negatives = (externalities || []).filter((e) => e.kind === "negative");

  const catLabel = (v) => {
    const m = EXANTE_EXT_CATEGORIES.find((c) => c.v === v);
    return m ? (lang === "fr" ? m.fr : m.en) : v;
  };

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "MPR Phase 2 — Externalités" : "MPR Phase 2 — Externalities"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Avantages et coûts pour l'économie sans flux financier direct."
            : "Benefits and costs to the economy without direct financial flows."}
        </span>
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onAdd}>
          <Icon.plus /> {lang === "fr" ? "Externalité" : "Externality"}
        </button>
      </div>
      <div className="card-body">
        <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5, flexWrap: "wrap" }}>
          <span style={{ color: "var(--green)" }}><b className="mono" style={{ fontSize: 14 }}>+{fmt(totalPos)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Cumul ext+" : "Cumul. ext+"}</span></span>
          <span style={{ color: "var(--red)" }}><b className="mono" style={{ fontSize: 14 }}>−{fmt(totalNeg)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Cumul ext−" : "Cumul. ext−"}</span></span>
          <span style={{ marginLeft: "auto", color: net >= 0 ? "var(--green)" : "var(--red)" }}>
            <span className="text-faint">{lang === "fr" ? "Bilan net cumulé : " : "Cumulative net: "}</span>
            <b className="mono" style={{ fontSize: 16 }}>{fmt(net)} {ccy}</b>
          </span>
        </div>
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "16px 4px", textAlign: "center" }}>
            {lang === "fr"
              ? "Aucune externalité saisie. Ajoutez les bénéfices socio-économiques (emplois créés, gain de temps, CO2 évité, etc.) et les coûts externalisés (pollution, déplacement, etc.)."
              : "No externality entered. Add socio-economic benefits (jobs created, time saved, CO2 reduction, etc.) and externalised costs (pollution, displacement, etc.)."}
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
            {/* Positive externalities */}
            <div>
              <div style={{ fontSize: 12, fontWeight: 600, color: "var(--green)", marginBottom: 6 }}>
                ↑ {lang === "fr" ? "Externalités positives" : "Positive externalities"} ({positives.length})
              </div>
              <table className="tbl">
                <thead><tr>
                  <th>{lang === "fr" ? "Libellé" : "Label"}</th>
                  <th className="num" style={{ width: 110 }}>An 1</th>
                  <th style={{ width: 50 }}>%/an</th>
                  <th style={{ width: 36 }}></th>
                </tr></thead>
                <tbody>
                  {positives.map((e) => (
                    <tr key={e.id}>
                      <td>
                        <div className="strong" style={{ fontSize: 11 }}>{e.label}</div>
                        <div className="text-faint" style={{ fontSize: 10 }}>{catLabel(e.category)}</div>
                      </td>
                      <td className="num mono" style={{ color: "var(--green)" }}>{fmt(e.amount_year1)}</td>
                      <td className="num mono text-faint">{((Number(e.growth_rate) || 0) * 100).toFixed(1)}%</td>
                      <td>
                        <button className="btn xs ghost" onClick={() => onEdit(e)}>
                          <Icon.edit />
                        </button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            {/* Negative externalities */}
            <div>
              <div style={{ fontSize: 12, fontWeight: 600, color: "var(--red)", marginBottom: 6 }}>
                ↓ {lang === "fr" ? "Externalités négatives" : "Negative externalities"} ({negatives.length})
              </div>
              <table className="tbl">
                <thead><tr>
                  <th>{lang === "fr" ? "Libellé" : "Label"}</th>
                  <th className="num" style={{ width: 110 }}>An 1</th>
                  <th style={{ width: 50 }}>%/an</th>
                  <th style={{ width: 36 }}></th>
                </tr></thead>
                <tbody>
                  {negatives.map((e) => (
                    <tr key={e.id}>
                      <td>
                        <div className="strong" style={{ fontSize: 11 }}>{e.label}</div>
                        <div className="text-faint" style={{ fontSize: 10 }}>{catLabel(e.category)}</div>
                      </td>
                      <td className="num mono" style={{ color: "var(--red)" }}>{fmt(e.amount_year1)}</td>
                      <td className="num mono text-faint">{((Number(e.growth_rate) || 0) * 100).toFixed(1)}%</td>
                      <td>
                        <button className="btn xs ghost" onClick={() => onEdit(e)}>
                          <Icon.edit />
                        </button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function ExanteExternalityModal({ lang, dossierId, editing, existingCount, onClose, onSaved }) {
  const isEdit = !!editing;
  const [label, setLabel] = useStateE(editing ? editing.label || "" : "");
  const [kind, setKind] = useStateE(editing ? editing.kind || "positive" : "positive");
  const [category, setCategory] = useStateE(editing ? editing.category || "jobs" : "jobs");
  const [amount, setAmount] = useStateE(editing ? String(editing.amount_year1 ?? 0) : "");
  const [growth, setGrowth] = useStateE(editing ? String(editing.growth_rate ?? 0) : "0");
  const [startYear, setStartYear] = useStateE(editing ? String(editing.start_year ?? 1) : "1");
  const [endYear, setEndYear] = useStateE(editing ? (editing.end_year != null ? String(editing.end_year) : "") : "");
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        label: label.trim(),
        kind, category,
        amount_year1: Number(amount) || 0,
        growth_rate: Number(growth) || 0,
        start_year: parseInt(startYear, 10) || 1,
        end_year: endYear === "" ? null : parseInt(endYear, 10),
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exanteExternalitiesCrud.update(editing.id, payload);
      else await window.melr.exanteExternalitiesCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer cette externalité ?" : "Delete this externality?")) return;
    setBusy(true);
    try {
      await window.melr.exanteExternalitiesCrud.remove(editing.id);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 600, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier l'externalité" : "Edit externality")
            : (lang === "fr" ? "Nouvelle externalité" : "New externality")}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Libellé" : "Label"}</span>
          <input required value={label} onChange={(e) => setLabel(e.target.value)}
            placeholder={lang === "fr" ? "ex: Emplois directs et indirects créés" : "e.g. Direct and indirect jobs created"} style={inp} />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Nature" : "Kind"}</span>
            <select value={kind} onChange={(e) => setKind(e.target.value)} style={inp}>
              <option value="positive">{lang === "fr" ? "Positive (avantage)" : "Positive (benefit)"}</option>
              <option value="negative">{lang === "fr" ? "Négative (coût)" : "Negative (cost)"}</option>
            </select>
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Catégorie" : "Category"}</span>
            <select value={category} onChange={(e) => setCategory(e.target.value)} style={inp}>
              {EXANTE_EXT_CATEGORIES.map((c) => <option key={c.v} value={c.v}>{lang === "fr" ? c.fr : c.en}</option>)}
            </select>
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Montant An 1" : "Year 1 amount"}</span>
            <input required type="number" step="any" value={amount} onChange={(e) => setAmount(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Croissance annuelle (0–1)" : "Annual growth (0–1)"}</span>
            <input type="number" step="any" value={growth} onChange={(e) => setGrowth(e.target.value)} style={inp} />
          </label>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "An de début" : "Start year"}</span>
            <input type="number" min="0" value={startYear} onChange={(e) => setStartYear(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "An de fin (vide=horizon)" : "End year (blank=horizon)"}</span>
            <input type="number" value={endYear} onChange={(e) => setEndYear(e.target.value)} style={inp} />
          </label>
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

function ExanteEconomicIndicators({ lang, inputs, mprResult }) {
  if (!mprResult) return null;
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmtAmount = (v) => v == null || !isFinite(v) ? "—" : Math.round(v).toLocaleString();
  const fmtPct = (v) => v == null || !isFinite(v) ? "—" : (v * 100).toFixed(1) + "%";
  const fmtRatio = (v) => v == null || !isFinite(v) ? "—" : Number(v).toFixed(2);
  const ind = mprResult.indicators;
  const discountEco = (inputs && inputs.discount_rate_economic) || 0.09;

  const tile = (label, value, ok, sub) => (
    <div className="card" style={{ padding: 14, background: "var(--bg-elev)" }}>
      <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.04em" }}>{label}</div>
      <div className="mono" style={{ fontSize: 22, fontWeight: 600, marginTop: 4, color: ok === true ? "var(--green)" : ok === false ? "var(--red)" : "var(--text)" }}>
        {value}
      </div>
      {sub && <div className="text-faint" style={{ fontSize: 11, marginTop: 2 }}>{sub}</div>}
    </div>
  );

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Indicateurs économiques (MPR)" : "Economic indicators (MPR)"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Calculés à partir des flux MPR (phases 1+2+3)"
            : "Computed from MPR flows (phases 1+2+3)"}
        </span>
        <div style={{ flex: 1 }} />
        {ind.viable
          ? <span className="pill green dot" style={{ fontSize: 11 }}>{lang === "fr" ? "ÉCO. VIABLE ✓" : "ECO. VIABLE ✓"}</span>
          : <span className="pill red dot" style={{ fontSize: 11 }}>{lang === "fr" ? "À RÉVISER ✗" : "TO REVIEW ✗"}</span>}
      </div>
      <div className="card-body">
        <div className="grid" style={{ gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 10 }}>
          {tile(
            "ENPV (" + ccy + ")",
            fmtAmount(ind.enpv),
            ind.enpv >= 0,
            lang === "fr" ? "VAN Économique" : "Economic NPV"
          )}
          {tile(
            "EIRR",
            fmtPct(ind.eirr),
            ind.eirr != null && ind.eirr > discountEco,
            (lang === "fr" ? "> " : "> ") + fmtPct(discountEco) + " " + (lang === "fr" ? "requis" : "required")
          )}
          {tile(
            (lang === "fr" ? "Ratio B/C" : "B/C ratio"),
            fmtRatio(ind.bcRatio),
            ind.bcRatio == null || ind.bcRatio >= 1,
            lang === "fr" ? "≥ 1 → avantages > coûts" : "≥ 1 → benefits > costs"
          )}
          {tile(
            "NPV " + (lang === "fr" ? "Bénéfices" : "Benefits") + " (" + ccy + ")",
            fmtAmount(ind.npvBenefits),
            null,
            lang === "fr" ? "Avantages actualisés" : "Discounted benefits"
          )}
          {tile(
            "NPV " + (lang === "fr" ? "Coûts" : "Costs") + " (" + ccy + ")",
            fmtAmount(ind.npvCosts),
            null,
            lang === "fr" ? "Coûts actualisés" : "Discounted costs"
          )}
        </div>
        <div className="text-faint" style={{ fontSize: 11, marginTop: 10 }}>
          {lang === "fr"
            ? "Décision automatique : ENPV ≥ 0 ET EIRR > taux d'actualisation économique ET B/C ≥ 1 ⇒ ÉCO. VIABLE. Seuils économiques ex-ante standards."
            : "Auto decision: ENPV ≥ 0 AND EIRR > economic discount AND B/C ≥ 1 ⇒ ECO. VIABLE. Standard ex-ante economic thresholds."}
        </div>
      </div>
    </div>
  );
}

// ==================== PHASE 3.3 — SENSITIVITY ==================================

const SENSITIVITY_VARS = [
  { v: "capex",    fr: "CAPEX",                en: "CAPEX" },
  { v: "volumes",  fr: "Volumes d'activité",   en: "Activity volumes" },
  { v: "tariffs",  fr: "Tarifs / prix",        en: "Tariffs / prices" },
  { v: "opex",     fr: "Coûts d'exploitation", en: "Operating costs" },
  { v: "discount", fr: "Taux d'actualisation", en: "Discount rate" },
];

const SENSITIVITY_DELTAS = [-0.20, -0.10, 0, 0.10, 0.20];

function ExanteSensitivityCard({ lang, inputs, baseOpts, mprBaseOpts }) {
  const E = window.exanteEngine;
  if (!E) return null;
  const fmtAmount = (v) => v == null || !isFinite(v) ? "—" : Math.round(v).toLocaleString();
  const fmtPct = (v) => v == null || !isFinite(v) ? "—" : (v * 100).toFixed(1) + "%";
  const fmtRatio = (v) => v == null || !isFinite(v) ? "—" : Number(v).toFixed(2);

  // Compute the full matrix in a single render. Heavy-ish (5×5 = 25
  // engine runs) but lines have few rows so still ms-level.
  const [metric, setMetric] = useStateE("npv");  // npv | irr | bc (financial) — npve | eirr | bce (economic)
  const matrix = React.useMemo(() => {
    return SENSITIVITY_VARS.map((v) => ({
      variable: v,
      rows: SENSITIVITY_DELTAS.map((d) => ({
        delta: d,
        result: E.economicSensitivityShock(baseOpts, mprBaseOpts, v.v, d),
      })),
    }));
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [JSON.stringify({ baseOpts: { i: baseOpts && baseOpts.inputs, capex: (baseOpts.capexLines || []).length, opex: (baseOpts.opexLines || []).length, rev: (baseOpts.revenueLines || []).length, fin: (baseOpts.financingSources || []).length }, mprIds: (mprBaseOpts.mprTransfers || []).length + "," + (mprBaseOpts.externalities || []).length })]);

  const valueOf = (result) => {
    if (!result) return null;
    if (metric === "npv")  return result.fin && result.fin.npvProject;
    if (metric === "irr")  return result.fin && result.fin.irrProject;
    if (metric === "dscr") return result.fin && result.fin.dscrMin;
    if (metric === "enpv") return result.eco && result.eco.enpv;
    if (metric === "eirr") return result.eco && result.eco.eirr;
    if (metric === "bc")   return result.eco && result.eco.bcRatio;
    return null;
  };
  const fmt = (v) => {
    if (metric === "npv" || metric === "enpv") return fmtAmount(v);
    if (metric === "irr" || metric === "eirr") return fmtPct(v);
    if (metric === "dscr" || metric === "bc")  return fmtRatio(v);
    return "—";
  };
  const threshold = (v) => {
    if (v == null || !isFinite(v)) return "neutral";
    if (metric === "npv" || metric === "enpv") return v >= 0 ? "ok" : "bad";
    if (metric === "irr") return v > ((inputs && inputs.discount_rate_financial) || 0.09) ? "ok" : "bad";
    if (metric === "eirr") return v > ((inputs && inputs.discount_rate_economic) || 0.09) ? "ok" : "bad";
    if (metric === "dscr") return v >= 1.2 ? "ok" : "bad";
    if (metric === "bc") return v >= 1 ? "ok" : "bad";
    return "neutral";
  };
  const colorFor = (state) => state === "ok" ? "var(--green)" : state === "bad" ? "var(--red)" : "var(--text)";

  const metricBtn = (k, label) => (
    <button
      key={k}
      className={"seg-btn" + (metric === k ? " active" : "")}
      onClick={() => setMetric(k)}>{label}</button>
  );

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Analyse de sensibilité (Tableau 20)" : "Sensitivity analysis (Table 20)"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "5 variables × 5 chocs (−20% à +20%). Recalcul live à chaque saisie."
            : "5 variables × 5 shocks (−20% to +20%). Live recompute on every edit."}
        </span>
      </div>
      <div className="card-body">
        <div className="row gap-sm" style={{ marginBottom: 12, flexWrap: "wrap" }}>
          <span className="text-faint" style={{ fontSize: 11.5 }}>{lang === "fr" ? "Indicateur :" : "Indicator:"}</span>
          <div className="seg sm">
            {metricBtn("npv",  "VAN")}
            {metricBtn("irr",  "TRI")}
            {metricBtn("dscr", "DSCR min")}
          </div>
          <div className="seg sm">
            {metricBtn("enpv", "ENPV")}
            {metricBtn("eirr", "EIRR")}
            {metricBtn("bc",   "B/C")}
          </div>
        </div>

        <table className="tbl">
          <thead><tr>
            <th>{lang === "fr" ? "Variable" : "Variable"}</th>
            {SENSITIVITY_DELTAS.map((d) => (
              <th key={d} className="num">{d === 0 ? (lang === "fr" ? "Base" : "Base") : (d > 0 ? "+" + Math.round(d * 100) + "%" : Math.round(d * 100) + "%")}</th>
            ))}
          </tr></thead>
          <tbody>
            {matrix.map((row) => (
              <tr key={row.variable.v}>
                <td className="strong">{lang === "fr" ? row.variable.fr : row.variable.en}</td>
                {row.rows.map((r) => {
                  const v = valueOf(r.result);
                  const state = threshold(v);
                  return (
                    <td key={r.delta} className="num mono" style={{
                      color: colorFor(state),
                      fontWeight: r.delta === 0 ? 700 : 500,
                      background: r.delta === 0 ? "var(--bg-sunken)" : undefined,
                    }}>
                      {fmt(v)}
                    </td>
                  );
                })}
              </tr>
            ))}
          </tbody>
        </table>

        <div className="text-faint" style={{ fontSize: 10.5, marginTop: 10 }}>
          {lang === "fr"
            ? "Lecture : chaque cellule = nouveau scénario où la variable a été choquée du % indiqué (autres inputs constants). Vert = au-dessus du seuil de viabilité, rouge = en dessous."
            : "Reading: each cell = scenario where the variable is shocked by the indicated %, other inputs held constant. Green = above viability threshold, red = below."}
        </div>
      </div>
    </div>
  );
}

// ==================== PHASE 4.1 — QUALITY GRID + MULTI-CRITERIA ================

// Decision pill helper (shared)
function ExScoringDecisionPill({ decision, lang }) {
  if (decision === "consistent") return <span className="pill green dot" style={{ fontSize: 11 }}>{lang === "fr" ? "CONSISTENT ✓ (≥75%)" : "CONSISTENT ✓ (≥75%)"}</span>;
  if (decision === "with_observations") return <span className="pill amber dot" style={{ fontSize: 11 }}>{lang === "fr" ? "AVEC OBSERVATIONS (60-74%)" : "WITH OBSERVATIONS (60-74%)"}</span>;
  return <span className="pill red dot" style={{ fontSize: 11 }}>{lang === "fr" ? "REJET ✗ (<60%)" : "REJECT ✗ (<60%)"}</span>;
}

// Group items by dimension into ordered array
function groupByDimension(items) {
  const groups = {};
  const order = [];
  (items || []).forEach((it) => {
    if (!groups[it.dimension]) { groups[it.dimension] = []; order.push(it.dimension); }
    groups[it.dimension].push(it);
  });
  return order.map((dim) => ({ dimension: dim, items: groups[dim] }));
}

function ExanteQualityGridCard({ lang, dossierId, items, result, onEditItem, onSeeded }) {
  const [seeding, setSeeding] = useStateE(false);
  const empty = !items || items.length === 0;
  const fmtPct = (v) => v == null || !isFinite(v) ? "—" : (v * 100).toFixed(0) + "%";
  const groups = groupByDimension(items);

  const seedNow = async () => {
    setSeeding(true);
    try {
      await window.melr.seedExanteQualityGrid(dossierId);
      await onSeeded();
    } catch (e) { window.alert("Erreur seed: " + e.message); }
    finally { setSeeding(false); }
  };

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Contrôle qualité du dossier (Tableau 9)" : "Quality grid (Table 9)"}
        </div>
        {result && (
          <>
            <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
              {result.scoredItems}/{result.totalItems}
            </span>
            <ExScoringDecisionPill decision={result.decision} lang={lang} />
          </>
        )}
        <div style={{ flex: 1 }} />
        {empty && (
          <button className="btn xs primary" onClick={seedNow} disabled={seeding}>
            <Icon.plus /> {seeding ? "…" : (lang === "fr" ? "Charger la grille standard (27 critères)" : "Load standard grid (27 criteria)")}
          </button>
        )}
      </div>
      <div className="card-body">
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "16px 4px", textAlign: "center" }}>
            {lang === "fr"
              ? "La grille de contrôle qualité est vide. Cliquez « Charger la grille standard » pour démarrer avec les 27 critères qualité ex-ante."
              : "Quality grid is empty. Click \"Load standard grid\" to start with the 27 standard criteria."}
          </div>
        ) : (
          <>
            <div className="row gap-md" style={{ marginBottom: 12, fontSize: 11.5, flexWrap: "wrap" }}>
              <span><b className="mono" style={{ fontSize: 16, color: "var(--accent)" }}>{fmtPct(result.pct)}</b> <span className="text-faint">{lang === "fr" ? "score global" : "global score"}</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>{result.avg.toFixed(2)} / 3</b> <span className="text-faint">{lang === "fr" ? "moyenne" : "average"}</span></span>
              <span style={{ marginLeft: "auto" }} className="text-faint">
                {lang === "fr" ? "Complétion : " : "Completion: "}
                <b style={{ color: "var(--text)" }}>{fmtPct(result.completion)}</b>
              </span>
            </div>
            {/* Per-dimension averages */}
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 8, marginBottom: 12 }}>
              {Object.entries(result.byDim).map(([dim, v]) => (
                <div key={dim} style={{
                  padding: "6px 10px", borderRadius: 6,
                  background: "var(--bg-sunken)", border: "1px solid var(--line-faint)",
                }}>
                  <div className="strong" style={{ fontSize: 11 }}>{dim}</div>
                  <div className="mono" style={{ fontSize: 13 }}>
                    {v.avg.toFixed(2)}/3 <span className="text-faint">·</span> <b style={{ color: v.pct >= 0.75 ? "var(--green)" : v.pct >= 0.6 ? "var(--amber)" : "var(--red)" }}>{fmtPct(v.pct)}</b>
                  </div>
                </div>
              ))}
            </div>
            {/* Items by dimension */}
            {groups.map((g) => (
              <div key={g.dimension} style={{ marginBottom: 10 }}>
                <div className="strong" style={{ fontSize: 12, background: "var(--bg-sunken)", padding: "4px 8px", borderRadius: 4, marginBottom: 4 }}>{g.dimension}</div>
                <table className="tbl">
                  <thead><tr>
                    <th>{lang === "fr" ? "Critère" : "Criterion"}</th>
                    <th className="num" style={{ width: 80 }}>{lang === "fr" ? "Note (0-3)" : "Note (0-3)"}</th>
                    <th>{lang === "fr" ? "Commentaire" : "Comment"}</th>
                    <th style={{ width: 44 }}></th>
                  </tr></thead>
                  <tbody>
                    {g.items.map((it) => (
                      <tr key={it.id}>
                        <td style={{ fontSize: 12 }}>{it.criterion}</td>
                        <td className="num mono strong" style={{ color: it.note == null ? "var(--text-faint)" : it.note === 3 ? "var(--green)" : it.note === 2 ? "var(--amber)" : "var(--red)" }}>
                          {it.note == null ? "—" : it.note}
                        </td>
                        <td className="text-faint" style={{ fontSize: 11 }}>{it.comment || "—"}</td>
                        <td>
                          <button className="btn xs ghost" onClick={() => onEditItem(it)}>
                            <Icon.edit />
                          </button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            ))}
            <div className="text-faint" style={{ fontSize: 10.5, marginTop: 6 }}>
              {lang === "fr"
                ? "Échelle : 0 = non vérifié, 1 = peu, 2 = partiellement, 3 = entièrement vérifié. Décision : ≥75% Consistent · 60-74% Avec observations · <60% Rejet."
                : "Scale: 0=not verified, 1=barely, 2=partially, 3=fully verified. Decision: ≥75% Consistent · 60-74% With obs · <60% Reject."}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

function ExanteMulticriteriaCard({ lang, dossierId, items, result, onEditItem, onSeeded }) {
  const [seeding, setSeeding] = useStateE(false);
  const empty = !items || items.length === 0;
  const fmtPct = (v) => v == null || !isFinite(v) ? "—" : (v * 100).toFixed(0) + "%";
  const groups = groupByDimension(items);

  const seedNow = async () => {
    setSeeding(true);
    try {
      await window.melr.seedExanteMulticriteria(dossierId);
      await onSeeded();
    } catch (e) { window.alert("Erreur seed: " + e.message); }
    finally { setSeeding(false); }
  };

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Analyse multicritère (Tableau 21)" : "Multi-criteria analysis (Table 21)"}
        </div>
        {result && (
          <>
            <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
              {result.scoredItems}/{result.totalItems}
            </span>
            <ExScoringDecisionPill decision={result.decision} lang={lang} />
          </>
        )}
        <div style={{ flex: 1 }} />
        {empty && (
          <button className="btn xs primary" onClick={seedNow} disabled={seeding}>
            <Icon.plus /> {seeding ? "…" : (lang === "fr" ? "Charger l'analyse standard (13 dimensions)" : "Load standard analysis (13 dimensions)")}
          </button>
        )}
      </div>
      <div className="card-body">
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "16px 4px", textAlign: "center" }}>
            {lang === "fr"
              ? "L'analyse multicritère est vide. Cliquez « Charger l'analyse standard » pour démarrer avec les 13 dimensions standards (pertinence, cohérence, efficience, équité, viabilité, etc.)."
              : "Multi-criteria analysis is empty. Click \"Load standard analysis\" to start with the 13 standard dimensions."}
          </div>
        ) : (
          <>
            <div className="row gap-md" style={{ marginBottom: 12, fontSize: 11.5, flexWrap: "wrap" }}>
              <span><b className="mono" style={{ fontSize: 16, color: "var(--accent)" }}>{fmtPct(result.globalPct)}</b> <span className="text-faint">{lang === "fr" ? "score global pondéré" : "weighted global"}</span></span>
              <span><b className="mono" style={{ fontSize: 14 }}>{result.globalAvg.toFixed(2)} / 3</b> <span className="text-faint">{lang === "fr" ? "moyenne pondérée" : "weighted avg"}</span></span>
              <span style={{ marginLeft: "auto" }} className="text-faint">
                {lang === "fr" ? "Complétion : " : "Completion: "}
                <b style={{ color: "var(--text)" }}>{fmtPct(result.completion)}</b>
              </span>
            </div>
            {/* Per-dimension scores */}
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 8, marginBottom: 12 }}>
              {Object.entries(result.byDim).map(([dim, v]) => (
                <div key={dim} style={{
                  padding: "6px 10px", borderRadius: 6,
                  background: "var(--bg-sunken)", border: "1px solid var(--line-faint)",
                }}>
                  <div className="strong" style={{ fontSize: 11 }}>{dim}</div>
                  <div className="mono" style={{ fontSize: 13 }}>
                    {v.weightedAvg.toFixed(2)}/3 <span className="text-faint">·</span> <b style={{ color: v.pct >= 0.75 ? "var(--green)" : v.pct >= 0.6 ? "var(--amber)" : "var(--red)" }}>{fmtPct(v.pct)}</b>
                  </div>
                </div>
              ))}
            </div>
            {/* Items by dimension */}
            {groups.map((g) => (
              <div key={g.dimension} style={{ marginBottom: 10 }}>
                <div className="strong" style={{ fontSize: 12, background: "var(--bg-sunken)", padding: "4px 8px", borderRadius: 4, marginBottom: 4 }}>{g.dimension}</div>
                <table className="tbl">
                  <thead><tr>
                    <th>{lang === "fr" ? "Sous-critère" : "Sub-criterion"}</th>
                    <th className="num" style={{ width: 80 }}>{lang === "fr" ? "Éval. (0-3)" : "Eval. (0-3)"}</th>
                    <th className="num" style={{ width: 60 }}>{lang === "fr" ? "Pond." : "Weight"}</th>
                    <th className="num" style={{ width: 70 }}>{lang === "fr" ? "Pondéré" : "Weighted"}</th>
                    <th>{lang === "fr" ? "Commentaire" : "Comment"}</th>
                    <th style={{ width: 44 }}></th>
                  </tr></thead>
                  <tbody>
                    {g.items.map((it) => {
                      const weighted = (Number(it.evaluation) || 0) * (Number(it.weight) || 1);
                      return (
                        <tr key={it.id}>
                          <td style={{ fontSize: 12 }}>{it.subcriterion}</td>
                          <td className="num mono strong" style={{ color: it.evaluation == null ? "var(--text-faint)" : it.evaluation === 3 ? "var(--green)" : it.evaluation === 2 ? "var(--amber)" : "var(--red)" }}>
                            {it.evaluation == null ? "—" : it.evaluation}
                          </td>
                          <td className="num mono">{it.weight}</td>
                          <td className="num mono text-faint">{it.evaluation == null ? "—" : weighted.toFixed(0)}</td>
                          <td className="text-faint" style={{ fontSize: 11 }}>{it.comment || "—"}</td>
                          <td>
                            <button className="btn xs ghost" onClick={() => onEditItem(it)}>
                              <Icon.edit />
                            </button>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            ))}
            <div className="text-faint" style={{ fontSize: 10.5, marginTop: 6 }}>
              {lang === "fr"
                ? "Échelle : 0 = non vérifié, 1 = faible, 2 = partiel, 3 = totalement. Pondération : 1=accessoire, 2=important, 3=critique. Score pondéré = éval × pond. Décision : ≥75% Consistent · 60-74% Avec observations · <60% Rejet."
                : "Scale: 0-3. Weight: 1-3 (accessory to critical). Weighted score = eval × weight."}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// Shared edit modal for both quality grid items and multicriteria items
function ExanteScoringRowModal({ lang, kind, editing, onClose, onSaved }) {
  const isQuality = kind === "quality";
  const [note, setNote] = useStateE(editing ? (isQuality ? (editing.note != null ? String(editing.note) : "") : (editing.evaluation != null ? String(editing.evaluation) : "")) : "");
  const [weight, setWeight] = useStateE(editing ? String(editing.weight || 1) : "1");
  const [comment, setComment] = useStateE(editing ? editing.comment || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = isQuality
        ? { note: note === "" ? null : parseInt(note, 10), comment: comment.trim() || null }
        : { evaluation: note === "" ? null : parseInt(note, 10), weight: parseInt(weight, 10) || 1, comment: comment.trim() || null };
      if (isQuality) await window.melr.exanteQualityItemsCrud.update(editing.id, payload);
      else await window.melr.exanteMulticriteriaItemsCrud.update(editing.id, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 540, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Évaluer le critère" : "Score the criterion"}
        </div>
        <div style={{
          padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)",
          border: "1px solid var(--line-faint)", fontSize: 12,
        }}>
          <div className="text-faint" style={{ fontSize: 10.5, marginBottom: 2 }}>{editing.dimension}</div>
          <div className="strong">{isQuality ? editing.criterion : editing.subcriterion}</div>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: isQuality ? "1fr" : "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{isQuality ? (lang === "fr" ? "Note (0-3)" : "Note (0-3)") : (lang === "fr" ? "Évaluation (0-3)" : "Evaluation (0-3)")}</span>
            <select value={note} onChange={(e) => setNote(e.target.value)} style={inp}>
              <option value="">— {lang === "fr" ? "non évalué" : "not scored"} —</option>
              <option value="0">0 — {lang === "fr" ? "non vérifié" : "not verified"}</option>
              <option value="1">1 — {lang === "fr" ? "peu" : "barely"}</option>
              <option value="2">2 — {lang === "fr" ? "partiellement" : "partially"}</option>
              <option value="3">3 — {lang === "fr" ? "entièrement" : "fully"}</option>
            </select>
          </label>
          {!isQuality && (
            <label style={{ display: "grid", gap: 4 }}>
              <span style={lbl}>{lang === "fr" ? "Pondération (1-3)" : "Weight (1-3)"}</span>
              <select value={weight} onChange={(e) => setWeight(e.target.value)} style={inp}>
                <option value="1">1 — {lang === "fr" ? "accessoire" : "accessory"}</option>
                <option value="2">2 — {lang === "fr" ? "important" : "important"}</option>
                <option value="3">3 — {lang === "fr" ? "critique" : "critical"}</option>
              </select>
            </label>
          )}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Commentaire / justification" : "Comment / justification"}</span>
          <textarea rows={3} value={comment} onChange={(e) => setComment(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#059669", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (lang === "fr" ? "Enregistrer" : "Save")}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 4.2 — FINAL DECISION DASHBOARD =====================

function ExanteFinalDecisionCard({ lang, inputs, financial, economic, quality, multicriteria, decision }) {
  const ccy = (inputs && inputs.currency) || "EUR";
  const fmtPct = (v) => v == null || !isFinite(v) ? "—" : (v * 100).toFixed(1) + "%";
  const fmtAmount = (v) => v == null || !isFinite(v) ? "—" : Math.round(v).toLocaleString();
  const fmtRatio = (v) => v == null || !isFinite(v) ? "—" : v >= 999 ? "∞" : Number(v).toFixed(2);

  // Visual elements per decision
  const decisionUi = {
    go: {
      bg: "linear-gradient(135deg, oklch(0.55 0.13 145 / 0.12), oklch(0.55 0.13 145 / 0.05))",
      border: "oklch(0.55 0.13 145)",
      labelColor: "oklch(0.40 0.15 145)",
      label: lang === "fr" ? "GO — APPROUVÉ POUR FINANCEMENT" : "GO — APPROVED FOR FUNDING",
      icon: "✓",
    },
    conditional: {
      bg: "linear-gradient(135deg, oklch(0.65 0.14 80 / 0.14), oklch(0.65 0.14 80 / 0.05))",
      border: "oklch(0.65 0.14 80)",
      labelColor: "oklch(0.45 0.16 75)",
      label: lang === "fr" ? "CONDITIONNEL — AVEC OBSERVATIONS" : "CONDITIONAL — WITH OBSERVATIONS",
      icon: "⚠",
    },
    no_go: {
      bg: "linear-gradient(135deg, oklch(0.55 0.16 12 / 0.12), oklch(0.55 0.16 12 / 0.05))",
      border: "oklch(0.55 0.16 12)",
      labelColor: "oklch(0.40 0.18 15)",
      label: lang === "fr" ? "NO-GO — À RÉVISER OU REJETER" : "NO-GO — TO REVISE OR REJECT",
      icon: "✗",
    },
  }[decision.decision];

  const check = (ok, label, sub) => (
    <div className="row" style={{ alignItems: "flex-start", gap: 8, padding: "6px 0" }}>
      <span style={{
        width: 22, height: 22, borderRadius: "50%",
        background: ok ? "var(--green)" : "var(--red)", color: "white",
        display: "flex", alignItems: "center", justifyContent: "center",
        fontSize: 13, fontWeight: 700, flexShrink: 0,
      }}>{ok ? "✓" : "✗"}</span>
      <div>
        <div className="strong" style={{ fontSize: 12 }}>{label}</div>
        {sub && <div className="text-faint" style={{ fontSize: 10.5 }}>{sub}</div>}
      </div>
    </div>
  );

  return (
    <div className="card" style={{
      marginBottom: 14,
      background: decisionUi.bg,
      borderColor: decisionUi.border,
      borderWidth: 2,
    }}>
      <div className="card-head" style={{ borderBottom: 0, paddingBottom: 0 }}>
        <div className="card-title" style={{ fontSize: 13 }}>
          {lang === "fr" ? "Décision du comité d'investissement" : "Investment committee decision"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr" ? "Synthèse automatique des 4 axes d'évaluation" : "Auto-synthesis of the 4 evaluation axes"}
        </span>
      </div>
      <div className="card-body">
        {/* Big decision banner */}
        <div style={{
          display: "flex", alignItems: "center", gap: 16,
          padding: "16px 20px", borderRadius: 8,
          background: "var(--bg-elev)", marginBottom: 16,
          border: "1px solid " + decisionUi.border,
        }}>
          <span style={{
            fontSize: 48, lineHeight: 1, fontWeight: 800,
            color: decisionUi.labelColor,
          }}>{decisionUi.icon}</span>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 18, fontWeight: 700, color: decisionUi.labelColor }}>
              {decisionUi.label}
            </div>
            <div className="text-faint" style={{ fontSize: 12, marginTop: 4 }}>
              {decision.reasons.map((r, i) => (
                <span key={i}>{i > 0 && " · "}{r}</span>
              ))}
            </div>
          </div>
        </div>

        {/* 4-axes checklist */}
        <div className="grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 14 }}>
          {/* Financial */}
          <div className="card" style={{ padding: 12, background: "var(--bg-elev)" }}>
            <div className="strong" style={{ fontSize: 12, marginBottom: 6 }}>
              {lang === "fr" ? "1. Viabilité financière" : "1. Financial viability"}
            </div>
            {check(
              decision.finOk,
              lang === "fr" ? "Critères financiers ex-ante" : "Ex-ante financial criteria",
              financial ? (
                "VAN " + fmtAmount(financial.npvProject) + " " + ccy + " · TRI " + fmtPct(financial.irrProject) + " · DSCR min " + fmtRatio(financial.dscrMin)
              ) : (lang === "fr" ? "Aucune donnée" : "No data")
            )}
          </div>
          {/* Economic */}
          <div className="card" style={{ padding: 12, background: "var(--bg-elev)" }}>
            <div className="strong" style={{ fontSize: 12, marginBottom: 6 }}>
              {lang === "fr" ? "2. Viabilité économique (MPR)" : "2. Economic viability (MPR)"}
            </div>
            {check(
              decision.ecoOk,
              lang === "fr" ? "Critères économiques ex-ante" : "Ex-ante economic criteria",
              economic ? (
                "ENPV " + fmtAmount(economic.enpv) + " " + ccy + " · EIRR " + fmtPct(economic.eirr) + " · B/C " + fmtRatio(economic.bcRatio)
              ) : (lang === "fr" ? "Aucune donnée" : "No data")
            )}
          </div>
          {/* Quality */}
          <div className="card" style={{ padding: 12, background: "var(--bg-elev)" }}>
            <div className="strong" style={{ fontSize: 12, marginBottom: 6 }}>
              {lang === "fr" ? "3. Contrôle qualité du dossier" : "3. Dossier quality grid"}
            </div>
            {check(
              decision.qualOk,
              lang === "fr" ? "Tableau 9 — seuil 60%" : "Table 9 — 60% threshold",
              quality ? (
                fmtPct(quality.pct) + " · " + quality.scoredItems + "/" + quality.totalItems + " " + (lang === "fr" ? "critères évalués" : "criteria scored")
              ) : (lang === "fr" ? "Grille non chargée" : "Grid not loaded")
            )}
          </div>
          {/* Multi-criteria */}
          <div className="card" style={{ padding: 12, background: "var(--bg-elev)" }}>
            <div className="strong" style={{ fontSize: 12, marginBottom: 6 }}>
              {lang === "fr" ? "4. Analyse multicritère" : "4. Multi-criteria analysis"}
            </div>
            {check(
              decision.mcGreat,
              lang === "fr" ? "Tableau 21 — seuil 75%" : "Table 21 — 75% threshold",
              multicriteria ? (
                fmtPct(multicriteria.globalPct) + " · " + multicriteria.scoredItems + "/" + multicriteria.totalItems + " " + (lang === "fr" ? "sous-critères évalués" : "sub-criteria scored")
              ) : (lang === "fr" ? "Analyse non chargée" : "Analysis not loaded")
            )}
          </div>
        </div>

        <div className="text-faint" style={{ fontSize: 10.5, marginTop: 12 }}>
          {lang === "fr"
            ? "Règle de décision : GO ⇒ les 4 axes franchissent leurs seuils respectifs (multicritère ≥75%). CONDITIONNEL ⇒ au moins un axe financier OU économique passe ET multicritère ≥60%. NO-GO ⇒ tous les autres cas. Recalculé en temps réel à chaque saisie."
            : "Decision rule: GO when all 4 axes pass (multi-criteria ≥75%). CONDITIONAL when one of financial/economic passes AND multi-criteria ≥60%. NO-GO otherwise. Live recompute on every edit."}
        </div>
      </div>
    </div>
  );
}

// ==================== PHASE 7.1 — STAKEHOLDER ACCOUNTS ========================

const STK_REVENUE_BASES = [
  { v: "all",    fr: "Tous les revenus",   en: "All revenue" },
  { v: "c1",     fr: "Composante C1",      en: "Component C1" },
  { v: "c2",     fr: "Composante C2",      en: "Component C2" },
  { v: "c3",     fr: "Composante C3",      en: "Component C3" },
  { v: "c4",     fr: "Composante C4",      en: "Component C4" },
  { v: "manual", fr: "Montant manuel An 1", en: "Manual Y1 amount" },
];

function ExanteStakeholderCard({ lang, dossierId, stakeholders, accounts, currency, onAdd, onEdit, onSeeded }) {
  const [seeding, setSeeding] = useStateE(false);
  const ccy = currency || "EUR";
  const fmt = (v) => v == null || !isFinite(v) ? "—" : Math.round(Number(v)).toLocaleString();
  const empty = !stakeholders || stakeholders.length === 0;

  const seedNow = async () => {
    setSeeding(true);
    try {
      await window.melr.seedExanteStakeholders(dossierId);
      await onSeeded();
    } catch (e) { window.alert("Erreur seed: " + e.message); }
    finally { setSeeding(false); }
  };

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Comptes par acteur (Tableau 22)" : "Stakeholder accounts (Table 22)"}
        </div>
        {!empty && <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>{stakeholders.length}</span>}
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Revenus, coûts, impôts et impact net par acteur (avec vs sans projet)"
            : "Revenue, costs, taxes and net impact per actor (with vs without project)"}
        </span>
        <div style={{ flex: 1 }} />
        {empty && (
          <button className="btn xs primary" onClick={seedNow} disabled={seeding}>
            <Icon.plus /> {seeding ? "…" : (lang === "fr" ? "Charger les acteurs standards (5)" : "Load standard actors (5)")}
          </button>
        )}
        <button className="btn xs" onClick={onAdd}>
          <Icon.plus /> {lang === "fr" ? "Acteur" : "Actor"}
        </button>
      </div>
      <div className="card-body">
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "16px 4px", textAlign: "center" }}>
            {lang === "fr"
              ? "Aucun acteur dans la chaîne de valeur du projet. Cliquez « Charger les acteurs standards » pour démarrer."
              : "No stakeholder yet. Click \"Load standard actors\" to start."}
          </div>
        ) : accounts ? (
          <>
            {/* Aggregate impact strip */}
            <div className="row gap-md" style={{ marginBottom: 12, fontSize: 11.5, flexWrap: "wrap" }}>
              <span><b className="mono" style={{ fontSize: 14 }}>{fmt(accounts.total.cumWithRevenue)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Revenus totaux générés" : "Total generated revenue"}</span></span>
              <span><b className="mono" style={{ fontSize: 14, color: "var(--accent)" }}>{fmt(accounts.total.cumWithNet)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Net cumulé (avec)" : "Cumulative net (with)"}</span></span>
              <span><b className="mono" style={{ fontSize: 14, color: "var(--text-faint)" }}>{fmt(accounts.total.cumBaselineNet)}</b> <span className="text-faint">{ccy} · {lang === "fr" ? "Net cumulé (sans)" : "Cumulative net (without)"}</span></span>
              <span style={{ marginLeft: "auto", color: accounts.total.cumImpact >= 0 ? "var(--green)" : "var(--red)" }}>
                <span className="text-faint">{lang === "fr" ? "Impact net cumulé : " : "Cumulative net impact: "}</span>
                <b className="mono" style={{ fontSize: 16 }}>{fmt(accounts.total.cumImpact)} {ccy}</b>
              </span>
            </div>
            <table className="tbl">
              <thead><tr>
                <th>{lang === "fr" ? "Acteur" : "Stakeholder"}</th>
                <th className="num" style={{ width: 60 }}>{lang === "fr" ? "Bénéf." : "Benef."}</th>
                <th className="num" style={{ width: 110 }}>{lang === "fr" ? "Revenu cumul." : "Cumul. revenue"}</th>
                <th className="num" style={{ width: 110 }}>{lang === "fr" ? "Coût cumul." : "Cumul. cost"}</th>
                <th className="num" style={{ width: 80 }}>{lang === "fr" ? "Impôts" : "Taxes"}</th>
                <th className="num" style={{ width: 110 }}>{lang === "fr" ? "Net (avec)" : "Net (with)"}</th>
                <th className="num" style={{ width: 110 }}>{lang === "fr" ? "Net (sans)" : "Net (without)"}</th>
                <th className="num" style={{ width: 110 }}>{lang === "fr" ? "Impact cumul." : "Cumul. impact"}</th>
                <th className="num" style={{ width: 100 }}>{lang === "fr" ? "Par bénéf./an" : "Per benef./yr"}</th>
                <th style={{ width: 44 }}></th>
              </tr></thead>
              <tbody>
                {accounts.rows.map((r, i) => (
                  <tr key={r.id}>
                    <td className="strong">{r.name}</td>
                    <td className="num mono">{r.beneficiary_count}</td>
                    <td className="num mono">{fmt(r.cumWithRevenue)}</td>
                    <td className="num mono" style={{ color: "var(--red)" }}>({fmt(r.cumWithCost)})</td>
                    <td className="num mono" style={{ color: "var(--red)" }}>{r.cumWithTax > 0 ? "(" + fmt(r.cumWithTax) + ")" : "—"}</td>
                    <td className="num mono strong" style={{ color: r.cumWithNet >= 0 ? "var(--text)" : "var(--red)" }}>{fmt(r.cumWithNet)}</td>
                    <td className="num mono text-faint">{fmt(r.cumBaselineNet)}</td>
                    <td className="num mono strong" style={{
                      color: r.cumImpact >= 0 ? "var(--green)" : "var(--red)",
                      background: r.cumImpact >= 0 ? "rgba(34,197,94,.08)" : "rgba(220,38,38,.08)",
                    }}>{r.cumImpact >= 0 ? "+" : ""}{fmt(r.cumImpact)}</td>
                    <td className="num mono">{fmt(r.perBeneficiaryImpactPerYear)}</td>
                    <td>
                      <button className="btn xs ghost" onClick={() => onEdit(stakeholders[i])}>
                        <Icon.edit />
                      </button>
                    </td>
                  </tr>
                ))}
                <tr style={{ background: "var(--bg-sunken)", fontWeight: 700 }}>
                  <td colSpan="2">TOTAL</td>
                  <td className="num mono">{fmt(accounts.total.cumWithRevenue)}</td>
                  <td className="num mono">({fmt(accounts.total.cumWithCost)})</td>
                  <td className="num mono">{fmt(accounts.total.cumWithTax)}</td>
                  <td className="num mono">{fmt(accounts.total.cumWithNet)}</td>
                  <td className="num mono">{fmt(accounts.total.cumBaselineNet)}</td>
                  <td className="num mono" style={{ color: accounts.total.cumImpact >= 0 ? "var(--green)" : "var(--red)" }}>{accounts.total.cumImpact >= 0 ? "+" : ""}{fmt(accounts.total.cumImpact)}</td>
                  <td colSpan="2"></td>
                </tr>
              </tbody>
            </table>
            <div className="text-faint" style={{ fontSize: 10.5, marginTop: 8 }}>
              {lang === "fr"
                ? "Revenu cumul. = somme sur " + accounts.years + " ans des revenus avec projet. Impact = net (avec projet) − net (sans projet). Les coûts incluent le support O&M public si l'acteur l'absorbe."
                : "Cumul. revenue = sum over " + accounts.years + " yrs of with-project revenue. Impact = with − without. Costs include the public O&M support if the actor absorbs it."}
            </div>
          </>
        ) : (
          <div className="text-faint" style={{ fontSize: 12 }}>{lang === "fr" ? "Calculs indisponibles." : "Computations unavailable."}</div>
        )}
      </div>
    </div>
  );
}

function ExanteStakeholderModal({ lang, dossierId, editing, existingCount, onClose, onSaved }) {
  const isEdit = !!editing;
  const [name, setName] = useStateE(editing ? editing.name || "" : "");
  const [benefCount, setBenefCount] = useStateE(editing ? String(editing.beneficiary_count || 0) : "");
  const [revBasis, setRevBasis] = useStateE(editing ? editing.revenue_basis || "all" : "all");
  const [revPct, setRevPct] = useStateE(editing ? String(editing.revenue_pct || 0) : "");
  const [manualRevY1, setManualRevY1] = useStateE(editing ? String(editing.manual_revenue_y1 != null ? editing.manual_revenue_y1 : "") : "");
  const [costPct, setCostPct] = useStateE(editing ? String(editing.cost_pct_of_revenue || 0) : "");
  const [manualCostY1, setManualCostY1] = useStateE(editing ? String(editing.manual_cost_y1 != null ? editing.manual_cost_y1 : "") : "");
  const [absorbsOm, setAbsorbsOm] = useStateE(editing ? !!editing.absorbs_public_om : false);
  const [taxPct, setTaxPct] = useStateE(editing ? String(editing.tax_pct || 0) : "");
  const [baselineRev, setBaselineRev] = useStateE(editing ? String(editing.baseline_revenue_y1 || 0) : "0");
  const [baselineCost, setBaselineCost] = useStateE(editing ? String(editing.baseline_cost_y1 || 0) : "0");
  const [notes, setNotes] = useStateE(editing ? editing.notes || "" : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = {
        name: name.trim(),
        beneficiary_count: parseInt(benefCount, 10) || 0,
        revenue_basis: revBasis,
        revenue_pct: Number(revPct) || 0,
        manual_revenue_y1: manualRevY1 === "" ? null : Number(manualRevY1),
        cost_pct_of_revenue: Number(costPct) || 0,
        manual_cost_y1: manualCostY1 === "" ? null : Number(manualCostY1),
        absorbs_public_om: absorbsOm,
        tax_pct: Number(taxPct) || 0,
        baseline_revenue_y1: Number(baselineRev) || 0,
        baseline_cost_y1: Number(baselineCost) || 0,
        notes: notes.trim() || null,
        position: isEdit ? editing.position : existingCount + 1,
      };
      if (isEdit) await window.melr.exanteStakeholdersCrud.update(editing.id, payload);
      else await window.melr.exanteStakeholdersCrud.create(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isEdit) return;
    if (!window.confirm(lang === "fr" ? "Supprimer cet acteur ?" : "Delete this stakeholder?")) return;
    setBusy(true);
    try { await window.melr.exanteStakeholdersCrud.remove(editing.id); await onSaved(); }
    catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };
  const isManual = revBasis === "manual";

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 660, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {isEdit
            ? (lang === "fr" ? "Modifier l'acteur" : "Edit stakeholder")
            : (lang === "fr" ? "Nouvel acteur de la chaîne de valeur" : "New value-chain stakeholder")}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Nom de l'acteur" : "Stakeholder name"}</span>
            <input required value={name} onChange={(e) => setName(e.target.value)}
              placeholder={lang === "fr" ? "ex: Pêcheurs artisanaux" : "e.g. Artisanal fishers"} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Bénéficiaires (#)" : "Beneficiaries (#)"}</span>
            <input type="number" min="0" value={benefCount} onChange={(e) => setBenefCount(e.target.value)} style={inp} />
          </label>
        </div>

        <ExSectionH n="A" t={lang === "fr" ? "Avec projet — Revenus" : "With project — Revenue"} />
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Base de calcul" : "Revenue basis"}</span>
            <select value={revBasis} onChange={(e) => setRevBasis(e.target.value)} style={inp}>
              {STK_REVENUE_BASES.map((b) => <option key={b.v} value={b.v}>{lang === "fr" ? b.fr : b.en}</option>)}
            </select>
          </label>
          {!isManual ? (
            <label style={{ display: "grid", gap: 4 }}>
              <span style={lbl}>{lang === "fr" ? "Part du revenu (%)" : "Revenue share (%)"}</span>
              <input type="number" step="any" value={revPct} onChange={(e) => setRevPct(e.target.value)} placeholder="ex: 15" style={inp} />
            </label>
          ) : (
            <label style={{ display: "grid", gap: 4 }}>
              <span style={lbl}>{lang === "fr" ? "Montant An 1" : "Year 1 amount"}</span>
              <input type="number" step="any" value={manualRevY1} onChange={(e) => setManualRevY1(e.target.value)} style={inp} />
            </label>
          )}
        </div>

        <ExSectionH n="B" t={lang === "fr" ? "Avec projet — Coûts et fiscalité" : "With project — Costs and tax"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Coûts en % du revenu" : "Cost % of revenue"}</span>
            <input type="number" step="any" value={costPct} onChange={(e) => setCostPct(e.target.value)} placeholder="ex: 30" style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Ou coût manuel An 1" : "Or manual cost Y1"}</span>
            <input type="number" step="any" value={manualCostY1} onChange={(e) => setManualCostY1(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Taux d'impôt (%)" : "Tax rate (%)"}</span>
            <input type="number" step="any" value={taxPct} onChange={(e) => setTaxPct(e.target.value)} placeholder="ex: 15" style={inp} />
          </label>
        </div>
        <label style={{ display: "flex", gap: 8, alignItems: "center", padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)", border: "1px solid var(--line-faint)" }}>
          <input type="checkbox" checked={absorbsOm} onChange={(e) => setAbsorbsOm(e.target.checked)} />
          <span style={{ fontSize: 12 }}>
            {lang === "fr"
              ? "Cet acteur absorbe le support O&M public (lignes OPEX marquées « O&M public »)"
              : "This actor absorbs the public O&M support (OPEX rows flagged as public)"}
          </span>
        </label>

        <ExSectionH n="C" t={lang === "fr" ? "Sans projet — Baseline annuelle" : "Without project — Annual baseline"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Revenu An 1 (sans projet)" : "Revenue Y1 (without)"}</span>
            <input type="number" step="any" value={baselineRev} onChange={(e) => setBaselineRev(e.target.value)} style={inp} />
          </label>
          <label style={{ display: "grid", gap: 4 }}>
            <span style={lbl}>{lang === "fr" ? "Coût An 1 (sans projet)" : "Cost Y1 (without)"}</span>
            <input type="number" step="any" value={baselineCost} onChange={(e) => setBaselineCost(e.target.value)} style={inp} />
          </label>
        </div>
        <div className="text-faint" style={{ fontSize: 10.5 }}>
          {lang === "fr"
            ? "Les baselines sont inflatées chaque année avec le taux d'inflation des Inputs. L'impact net = net (avec) − net (sans), cumulé sur l'horizon."
            : "Baselines are inflated each year using the Inputs inflation rate. Net impact = net (with) − net (without), cumulated over the horizon."}
        </div>

        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>Notes</span>
          <textarea rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>

        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isEdit && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isEdit ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isEdit ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 7.2 — MPR PHASE 3 CONVERSION FACTORS ===============

const EXANTE_CF_KINDS = [
  { v: "capex_local",     fr: "CAPEX local",            en: "Local CAPEX",      hint: "Dépenses locales (génie civil, main d'œuvre)" },
  { v: "capex_imported",  fr: "CAPEX importé",          en: "Imported CAPEX",   hint: "Équipements, intrants importés" },
  { v: "opex_personnel",  fr: "OPEX — Personnel",       en: "OPEX — Personnel", hint: "Salaires (sous-emploi → coefficient < 1)" },
  { v: "opex_materials",  fr: "OPEX — Matières",        en: "OPEX — Materials", hint: "Matières premières, consommables" },
  { v: "revenue",         fr: "Revenus",                en: "Revenue",          hint: "Recettes du projet (souvent = 1.0)" },
  { v: "other",           fr: "Autres",                 en: "Other",            hint: "Autres inputs spécifiques" },
];

function ExanteConversionFactorsCard({ lang, dossierId, factors, onEdit, onSeeded }) {
  const [seeding, setSeeding] = useStateE(false);
  const fmt = (v) => v == null ? "—" : Number(v).toFixed(2);

  // Build a complete view: every kind, with factor or "—" if not set
  const byKind = {};
  (factors || []).forEach((f) => { byKind[f.input_kind] = f; });

  const seedNow = async () => {
    setSeeding(true);
    try { await window.melr.seedExanteConversionFactors(dossierId); await onSeeded(); }
    catch (e) {
      // Most common cause: phase7.sql was never run, so the RPC doesn't exist.
      const msg = String(e.message || e);
      if (msg.match(/function .* does not exist|could not find the function/i)) {
        window.alert(
          "La fonction SQL `seed_exante_conversion_factors` n'existe pas dans votre base. "
          + "Exécutez le script `exante-phase7.sql` dans la console SQL de la base de données, "
          + "puis réessayez.\n\nVous pouvez aussi cliquer le bouton « + » de chaque ligne "
          + "ci-dessous pour saisir les 6 facteurs manuellement."
        );
      } else {
        window.alert("Erreur seed : " + msg);
      }
    }
    finally { setSeeding(false); }
  };

  const allEmpty = (factors || []).length === 0;
  // Status: average distance from 1.0 — informs whether shadow pricing actually corrects anything
  const setRows = EXANTE_CF_KINDS.filter((k) => byKind[k.v]);
  const avgFactor = setRows.length > 0
    ? setRows.reduce((s, k) => s + Number(byKind[k.v].factor || 1), 0) / setRows.length
    : null;

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "MPR Phase 3 — Facteurs de conversion" : "MPR Phase 3 — Conversion factors"}
        </div>
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Coefficients de prix fictifs appliqués aux flux pour refléter la valeur réelle pour la collectivité"
            : "Shadow-price coefficients applied to flows to reflect their real value to society"}
        </span>
        <div style={{ flex: 1 }} />
        {allEmpty && (
          <button className="btn xs primary" onClick={seedNow} disabled={seeding}>
            <Icon.plus /> {seeding ? "…" : (lang === "fr" ? "Charger les défauts (6 facteurs)" : "Load defaults (6 factors)")}
          </button>
        )}
      </div>
      <div className="card-body">
        {allEmpty && (
          <div className="text-faint" style={{ fontSize: 12, padding: "8px 10px", borderRadius: 6, background: "var(--bg-sunken)", border: "1px solid var(--line-faint)", marginBottom: 10 }}>
            {lang === "fr"
              ? "Aucun facteur saisi. Sans facteur, les flux conservent leur valeur de marché (coefficient implicite = 1.0). Cliquez « Charger les défauts » ci-dessus pour démarrer avec des valeurs typiques, ou cliquez le bouton « + » de chaque ligne pour saisir un facteur manuellement."
              : "No factor entered. Without factors, flows keep their market value (implicit coefficient = 1.0). Click \"Load defaults\" above for typical values, or click the + button on each row to set a factor manually."}
          </div>
        )}
        {/* Always render the full 6-row table — empty rows have a + button that opens the editor. */}
        <div className="row gap-md" style={{ marginBottom: 10, fontSize: 11.5 }}>
          <span><b className="mono" style={{ fontSize: 14 }}>{setRows.length} / {EXANTE_CF_KINDS.length}</b> <span className="text-faint">{lang === "fr" ? "facteurs saisis" : "factors set"}</span></span>
          {avgFactor != null && (
            <span style={{ marginLeft: "auto", color: Math.abs(avgFactor - 1) < 0.05 ? "var(--text-faint)" : "var(--accent)" }}>
              <span className="text-faint">{lang === "fr" ? "Coefficient moyen : " : "Average factor: "}</span>
              <b className="mono">{avgFactor.toFixed(3)}</b>
              {" "}
              <span className="text-faint">{Math.abs(avgFactor - 1) < 0.05
                ? (lang === "fr" ? "≈ prix de marché" : "≈ market prices")
                : avgFactor < 1
                ? (lang === "fr" ? "→ valeur économique < marché" : "→ economic value < market")
                : (lang === "fr" ? "→ valeur économique > marché" : "→ economic value > market")}</span>
            </span>
          )}
        </div>
        <table className="tbl">
          <thead><tr>
            <th>{lang === "fr" ? "Input" : "Input"}</th>
            <th className="num" style={{ width: 100 }}>{lang === "fr" ? "Facteur" : "Factor"}</th>
            <th className="num" style={{ width: 110 }}>{lang === "fr" ? "Effet (vs 1.0)" : "Effect (vs 1.0)"}</th>
            <th>{lang === "fr" ? "Description" : "Description"}</th>
            <th style={{ width: 44 }}></th>
          </tr></thead>
          <tbody>
            {EXANTE_CF_KINDS.map((k) => {
              const f = byKind[k.v];
              const val = f ? Number(f.factor) : null;
              const delta = val != null ? (val - 1) * 100 : null;
              return (
                <tr key={k.v}>
                  <td className="strong">{lang === "fr" ? k.fr : k.en}</td>
                  <td className="num mono strong" style={{ color: val == null ? "var(--text-faint)" : Math.abs(val - 1) < 0.02 ? "var(--text)" : val < 1 ? "var(--amber)" : "var(--green)" }}>
                    {f ? val.toFixed(2) : (lang === "fr" ? "(1.0)" : "(1.0)")}
                  </td>
                  <td className="num mono text-faint">
                    {delta == null ? "—" : (delta >= 0 ? "+" : "") + delta.toFixed(1) + "%"}
                  </td>
                  <td className="text-faint" style={{ fontSize: 11 }}>
                    {f && f.notes ? f.notes : k.hint}
                  </td>
                  <td>
                    {f ? (
                      <button className="btn xs ghost" onClick={() => onEdit(f)} title={lang === "fr" ? "Modifier" : "Edit"}>
                        <Icon.edit />
                      </button>
                    ) : (
                      <button className="btn xs primary" onClick={() => onEdit({ dossier_id: dossierId, input_kind: k.v, factor: 1.0, notes: null })}
                        title={lang === "fr" ? "Saisir un facteur" : "Set a factor"}>
                        <Icon.plus />
                      </button>
                    )}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
        <div className="text-faint" style={{ fontSize: 10.5, marginTop: 8 }}>
          {lang === "fr"
            ? "Les facteurs sont appliqués automatiquement par le moteur dans le calcul de l'ENPV / EIRR / B/C. Plage typique : 0.7 (sous-emploi fort) → 1.2 (rareté). 1.0 = pas de correction."
            : "Factors are auto-applied by the engine when computing ENPV / EIRR / B/C. Typical range: 0.7 (strong underemployment) → 1.2 (scarcity). 1.0 = no correction."}
        </div>
      </div>
    </div>
  );
}

function ExanteConversionFactorModal({ lang, dossierId, editing, onClose, onSaved }) {
  // editing may be either an existing row (with id) or a "to-create" stub (with input_kind only)
  const isExisting = !!(editing && editing.id);
  const [factor, setFactor] = useStateE(String(editing && editing.factor != null ? editing.factor : 1.0));
  const [notes, setNotes] = useStateE(editing && editing.notes ? editing.notes : "");
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);

  const kindMeta = EXANTE_CF_KINDS.find((k) => k.v === editing.input_kind) || { v: editing.input_kind, fr: editing.input_kind, en: editing.input_kind, hint: "" };
  const labelKind = lang === "fr" ? kindMeta.fr : kindMeta.en;

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const f = Number(factor);
      if (!isFinite(f) || f < 0) throw new Error(lang === "fr" ? "Facteur invalide" : "Invalid factor");
      const payload = { factor: f, notes: notes.trim() || null };
      if (isExisting) {
        await window.melr.exanteConversionFactorsCrud.update(editing.id, payload);
      } else {
        await window.melr.exanteConversionFactorsCrud.create(dossierId, { ...payload, input_kind: editing.input_kind });
      }
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const onDelete = async () => {
    if (!isExisting) return;
    if (!window.confirm(lang === "fr" ? "Supprimer ce facteur ?" : "Delete this factor?")) return;
    setBusy(true);
    try { await window.melr.exanteConversionFactorsCrud.remove(editing.id); await onSaved(); }
    catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };
  const f = Number(factor) || 1;
  const interpretation = Math.abs(f - 1) < 0.02
    ? (lang === "fr" ? "Pas de correction (valeur de marché)" : "No correction (market value)")
    : f < 1
    ? (lang === "fr" ? `Valeur économique inférieure à la valeur de marché (${((1-f)*100).toFixed(0)}% de réduction)` : `Economic value below market value (${((1-f)*100).toFixed(0)}% reduction)`)
    : (lang === "fr" ? `Valeur économique supérieure à la valeur de marché (+${((f-1)*100).toFixed(0)}%)` : `Economic value above market value (+${((f-1)*100).toFixed(0)}%)`);

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16,
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 520, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 10,
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Facteur de conversion — " : "Conversion factor — "}{labelKind}
        </div>
        <div className="text-faint" style={{ fontSize: 11.5, padding: "6px 8px", background: "var(--bg-sunken)", borderRadius: 5 }}>
          {kindMeta.hint}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Facteur (1.0 = prix de marché)" : "Factor (1.0 = market price)"}</span>
          <input required type="number" step="0.01" min="0" max="3" value={factor} onChange={(e) => setFactor(e.target.value)} style={inp} />
        </label>
        <div style={{
          padding: "8px 10px", borderRadius: 6,
          background: Math.abs(f-1) < 0.02 ? "var(--bg-sunken)" : f < 1 ? "rgba(245,158,11,0.08)" : "rgba(34,197,94,0.08)",
          color: Math.abs(f-1) < 0.02 ? "var(--text-faint)" : f < 1 ? "var(--amber)" : "var(--green)",
          fontSize: 12, fontWeight: 500,
        }}>
          {interpretation}
        </div>
        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Justification" : "Justification"}</span>
          <textarea rows={3} value={notes} onChange={(e) => setNotes(e.target.value)}
            placeholder={lang === "fr" ? "ex: Marché du travail local en sous-emploi (15%) → coefficient main d'œuvre 0.85" : "e.g. Local labour market with 15% underemployment → labour factor 0.85"}
            style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>
        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4, borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          {isExisting && (
            <button type="button" onClick={onDelete} disabled={busy}
              style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--red, #dc2626)", cursor: "pointer", marginRight: "auto" }}>
              {lang === "fr" ? "Supprimer" : "Delete"}
            </button>
          )}
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: isExisting ? "#059669" : "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (isExisting ? (lang === "fr" ? "Enregistrer" : "Save") : (lang === "fr" ? "Créer" : "Create"))}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 7.3 — INSTITUTIONAL ANALYSIS =======================

const EXANTE_MGMT_MODELS = [
  { v: "regie",       fr: "Régie publique directe",      en: "Direct public management" },
  { v: "concession",  fr: "Concession",                  en: "Concession" },
  { v: "ppp",         fr: "Partenariat Public-Privé",    en: "Public-Private Partnership" },
  { v: "delegation",  fr: "Délégation de service public",en: "Public service delegation" },
  { v: "community",   fr: "Gestion communautaire",       en: "Community-based" },
  { v: "mixed",       fr: "Modèle mixte",                en: "Mixed model" },
  { v: "other",       fr: "Autre",                       en: "Other" },
];

function ExanteInstitutionalCard({ lang, dossierId, institutional, onEdit }) {
  const empty = !institutional || (
    !institutional.legal_framework && !institutional.management_model &&
    !institutional.carrier_organizations && !institutional.sustainability_mechanisms
  );
  const completionFields = [
    "legal_framework","juridical_constraints","management_model","management_model_details",
    "carrier_organizations","existing_capacities","capacity_gaps","capacity_building_plan",
    "coordination_mechanisms","decision_making_bodies","sustainability_mechanisms",
    "ownership_transfer","institutional_risks","risk_mitigation_measures",
  ];
  const filled = institutional ? completionFields.filter((k) => institutional[k] && String(institutional[k]).trim()).length : 0;
  const pct = Math.round(filled / completionFields.length * 100);

  const modelMeta = institutional && institutional.management_model
    ? EXANTE_MGMT_MODELS.find((m) => m.v === institutional.management_model)
    : null;

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="card-head">
        <div className="card-title">
          {lang === "fr" ? "Analyse institutionnelle (Section X)" : "Institutional analysis (Section X)"}
        </div>
        {!empty && (
          <span className="pill" style={{ marginLeft: 6, fontSize: 10.5 }}>
            {filled}/{completionFields.length} · {pct}%
          </span>
        )}
        <span className="text-faint" style={{ fontSize: 11, marginLeft: 8 }}>
          {lang === "fr"
            ? "Cadre juridique, modèle de gestion, capacités, durabilité"
            : "Legal framework, management model, capacities, sustainability"}
        </span>
        <div style={{ flex: 1 }} />
        <button className="btn xs primary" onClick={onEdit}>
          <Icon.edit /> {empty ? (lang === "fr" ? "Compléter" : "Fill in") : (lang === "fr" ? "Modifier" : "Edit")}
        </button>
      </div>
      <div className="card-body">
        {empty ? (
          <div className="text-faint" style={{ fontSize: 13, padding: "16px 4px", textAlign: "center" }}>
            {lang === "fr"
              ? "L'analyse institutionnelle est vide. Cliquez « Compléter » pour saisir le cadre juridique, le modèle de gestion, les capacités des porteurs et les mécanismes de durabilité."
              : "Institutional analysis is empty. Click \"Fill in\" to enter the legal framework, management model, carrier capacities and sustainability mechanisms."}
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, fontSize: 12 }}>
            {/* Block 1 — Legal */}
            <div>
              <div className="strong" style={{ fontSize: 12, color: "var(--accent)", marginBottom: 4 }}>
                {lang === "fr" ? "Cadre juridique & réglementaire" : "Legal & regulatory framework"}
              </div>
              <ExInstField label={lang === "fr" ? "Cadre légal" : "Legal framework"} value={institutional.legal_framework} />
              <ExInstField label={lang === "fr" ? "Contraintes juridiques" : "Juridical constraints"} value={institutional.juridical_constraints} />
              <ExInstField label={lang === "fr" ? "Normes applicables" : "Applicable norms"} value={institutional.applicable_norms} />
            </div>
            {/* Block 2 — Management */}
            <div>
              <div className="strong" style={{ fontSize: 12, color: "var(--accent)", marginBottom: 4 }}>
                {lang === "fr" ? "Modèle de gestion" : "Management model"}
              </div>
              {modelMeta && (
                <div style={{ marginBottom: 6 }}>
                  <span className="pill" style={{ fontSize: 10.5, background: "var(--bg-sunken)" }}>
                    {lang === "fr" ? modelMeta.fr : modelMeta.en}
                  </span>
                </div>
              )}
              <ExInstField label={lang === "fr" ? "Détails" : "Details"} value={institutional.management_model_details} />
              <ExInstField label={lang === "fr" ? "Opérateur" : "Operator"} value={institutional.operator_identification} />
              <ExInstField label={lang === "fr" ? "Arrangements contractuels" : "Contractual arrangements"} value={institutional.contractual_arrangements} />
            </div>
            {/* Block 3 — Capacities */}
            <div>
              <div className="strong" style={{ fontSize: 12, color: "var(--accent)", marginBottom: 4 }}>
                {lang === "fr" ? "Capacités des porteurs" : "Carrier capacities"}
              </div>
              <ExInstField label={lang === "fr" ? "Organisations porteuses" : "Carrier organisations"} value={institutional.carrier_organizations} />
              <ExInstField label={lang === "fr" ? "Capacités actuelles" : "Existing capacities"} value={institutional.existing_capacities} />
              <ExInstField label={lang === "fr" ? "Écarts identifiés" : "Capacity gaps"} value={institutional.capacity_gaps} />
              <ExInstField label={lang === "fr" ? "Plan de renforcement" : "Capacity-building plan"} value={institutional.capacity_building_plan} />
            </div>
            {/* Block 4 — Coordination & sustainability */}
            <div>
              <div className="strong" style={{ fontSize: 12, color: "var(--accent)", marginBottom: 4 }}>
                {lang === "fr" ? "Coordination & durabilité" : "Coordination & sustainability"}
              </div>
              <ExInstField label={lang === "fr" ? "Mécanismes de coordination" : "Coordination mechanisms"} value={institutional.coordination_mechanisms} />
              <ExInstField label={lang === "fr" ? "Instances décisionnelles" : "Decision-making bodies"} value={institutional.decision_making_bodies} />
              <ExInstField label={lang === "fr" ? "Suivi institutionnel" : "Monitoring arrangements"} value={institutional.monitoring_arrangements} />
              <ExInstField label={lang === "fr" ? "Durabilité" : "Sustainability"} value={institutional.sustainability_mechanisms} />
              <ExInstField label={lang === "fr" ? "Transfert post-projet" : "Post-project transfer"} value={institutional.ownership_transfer} />
              <ExInstField label={lang === "fr" ? "Ressources O&M futures" : "Future O&M resources"} value={institutional.post_project_resources} />
            </div>
            {/* Block 5 — Risks */}
            {(institutional.institutional_risks || institutional.risk_mitigation_measures) && (
              <div style={{ gridColumn: "1 / -1" }}>
                <div className="strong" style={{ fontSize: 12, color: "var(--red)", marginBottom: 4 }}>
                  {lang === "fr" ? "Risques institutionnels" : "Institutional risks"}
                </div>
                <ExInstField label={lang === "fr" ? "Risques identifiés" : "Identified risks"} value={institutional.institutional_risks} />
                <ExInstField label={lang === "fr" ? "Mesures de mitigation" : "Mitigation measures"} value={institutional.risk_mitigation_measures} />
              </div>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

function ExInstField({ label, value }) {
  if (!value) return null;
  return (
    <div style={{ marginBottom: 6 }}>
      <div className="text-faint" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: "0.04em" }}>{label}</div>
      <div style={{ fontSize: 11.5, lineHeight: 1.5 }}>{value}</div>
    </div>
  );
}

function ExanteInstitutionalModal({ lang, dossierId, initial, onClose, onSaved }) {
  const [form, setForm] = useStateE(() => initial || {});
  const [busy, setBusy] = useStateE(false);
  const [err, setErr] = useStateE(null);
  const setField = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const payload = { ...form };
      delete payload.dossier_id; delete payload.created_at; delete payload.updated_at;
      await window.melr.upsertExanteInstitutional(dossierId, payload);
      await onSaved();
    } catch (e2) { setErr(e2.message); }
    finally { setBusy(false); }
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };
  const F = (key, label, opts = {}) => (
    <label style={{ display: "grid", gap: 4, gridColumn: opts.full ? "1 / -1" : undefined }}>
      <span style={lbl}>{label}</span>
      {opts.select ? (
        <select value={form[key] || ""} onChange={(e) => setField(key, e.target.value)} style={inp}>
          <option value="">—</option>
          {opts.select.map((o) => <option key={o.v} value={o.v}>{lang === "fr" ? o.fr : o.en}</option>)}
        </select>
      ) : (
        <textarea rows={opts.rows || 2} value={form[key] || ""} onChange={(e) => setField(key, e.target.value)}
          placeholder={opts.placeholder}
          style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
      )}
    </label>
  );

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.45)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <form onClick={(e) => e.stopPropagation()} onSubmit={submit} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 820, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 12,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Analyse institutionnelle" : "Institutional analysis"}
        </div>

        <ExSectionH n="1" t={lang === "fr" ? "Cadre juridique et réglementaire" : "Legal & regulatory framework"} />
        {F("legal_framework", lang === "fr" ? "Cadre légal applicable" : "Applicable legal framework", { full: true, rows: 2, placeholder: lang === "fr" ? "Lois, décrets, conventions internationales applicables…" : "Laws, decrees, international conventions…" })}
        {F("juridical_constraints", lang === "fr" ? "Contraintes / autorisations" : "Constraints / permits required", { full: true, rows: 2, placeholder: lang === "fr" ? "Autorisations préalables, exonérations, contraintes d'investissement…" : "Required permits, exemptions, investment constraints…" })}
        {F("applicable_norms", lang === "fr" ? "Normes techniques applicables" : "Applicable technical norms", { full: true, rows: 2 })}

        <ExSectionH n="2" t={lang === "fr" ? "Modèle de gestion proposé" : "Proposed management model"} />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 10 }}>
          {F("management_model", lang === "fr" ? "Modèle de gestion" : "Management model", { select: EXANTE_MGMT_MODELS })}
          {F("operator_identification", lang === "fr" ? "Opérateur identifié" : "Identified operator", { rows: 1 })}
        </div>
        {F("management_model_details", lang === "fr" ? "Description détaillée du modèle" : "Detailed model description", { full: true, rows: 3 })}
        {F("contractual_arrangements", lang === "fr" ? "Arrangements contractuels (conventions, contrats…)" : "Contractual arrangements", { full: true, rows: 2 })}

        <ExSectionH n="3" t={lang === "fr" ? "Capacités institutionnelles des porteurs" : "Institutional capacities of carriers"} />
        {F("carrier_organizations", lang === "fr" ? "Organisations porteuses (Ministère, ONG, secteur privé…)" : "Carrier organisations", { full: true, rows: 2 })}
        {F("existing_capacities", lang === "fr" ? "Capacités actuelles (RH, techniques, financières)" : "Existing capacities", { full: true, rows: 3 })}
        {F("capacity_gaps", lang === "fr" ? "Écarts de capacité identifiés" : "Identified capacity gaps", { full: true, rows: 2 })}
        {F("capacity_building_plan", lang === "fr" ? "Plan de renforcement de capacités" : "Capacity-building plan", { full: true, rows: 3, placeholder: lang === "fr" ? "Formations prévues, assistance technique, équipements…" : "Planned trainings, technical assistance, equipment…" })}

        <ExSectionH n="4" t={lang === "fr" ? "Coordination & gouvernance" : "Coordination & governance"} />
        {F("coordination_mechanisms", lang === "fr" ? "Mécanismes de coordination inter-acteurs" : "Inter-actor coordination mechanisms", { full: true, rows: 2 })}
        {F("decision_making_bodies", lang === "fr" ? "Instances de décision (comités, conseils…)" : "Decision-making bodies", { full: true, rows: 2 })}
        {F("monitoring_arrangements", lang === "fr" ? "Dispositif institutionnel de S&E" : "Institutional M&E arrangements", { full: true, rows: 2 })}

        <ExSectionH n="5" t={lang === "fr" ? "Durabilité institutionnelle" : "Institutional sustainability"} />
        {F("sustainability_mechanisms", lang === "fr" ? "Mécanismes de durabilité (financière + institutionnelle)" : "Sustainability mechanisms", { full: true, rows: 3 })}
        {F("ownership_transfer", lang === "fr" ? "Transfert de propriété / responsabilité post-projet" : "Post-project ownership transfer", { full: true, rows: 2 })}
        {F("post_project_resources", lang === "fr" ? "Ressources O&M récurrentes après le projet" : "Recurrent O&M resources after project end", { full: true, rows: 2, placeholder: lang === "fr" ? "Sources de financement pour l'entretien, l'exploitation et le renouvellement…" : "Funding sources for maintenance, operation, replacement…" })}

        <ExSectionH n="6" t={lang === "fr" ? "Risques institutionnels" : "Institutional risks"} />
        {F("institutional_risks", lang === "fr" ? "Risques identifiés (politiques, capacitaires, financiers…)" : "Identified risks (political, capacity, financial…)", { full: true, rows: 3 })}
        {F("risk_mitigation_measures", lang === "fr" ? "Mesures de mitigation" : "Mitigation measures", { full: true, rows: 3 })}

        {err && <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", borderTop: "1px solid var(--line-faint)", paddingTop: 12, position: "sticky", bottom: 0, background: "var(--bg, white)" }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="submit" disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#059669", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? "…" : (lang === "fr" ? "Enregistrer" : "Save")}
          </button>
        </div>
      </form>
    </div>
  );
}

// ==================== PHASE 8.2 — SUBMIT DOSSIER (email + attachments) =========

function ExanteSubmitDossierModal({ lang, dossier, payload, onClose, onOpenReport }) {
  const lead = dossier && dossier.projects && dossier.projects.lead;
  const projectCode = (dossier && dossier.projects && dossier.projects.code) || "PROJET";
  const version = (dossier && dossier.version) || "v1";
  const today = new Date().toLocaleDateString(lang === "fr" ? "fr-FR" : "en-US");

  const [recipient, setRecipient] = useStateE((lead && lead.email) || "");
  const [recipientName] = useStateE((lead && lead.full_name) || "");
  const [subject, setSubject] = useStateE(
    (lang === "fr"
      ? `Soumission dossier ex-ante — ${projectCode} (${version}) — ${today}`
      : `Ex-ante dossier submission — ${projectCode} (${version}) — ${today}`)
  );
  const [body, setBody] = useStateE(
    lang === "fr"
      ? `Bonjour${recipientName ? " " + recipientName : ""},\n\n`
        + `Veuillez trouver ci-joint le dossier d'évaluation ex-ante du projet ${projectCode} (version ${version}).\n\n`
        + `Deux formats sont fournis :\n`
        + `  • Document Word (.docx) — éditable\n`
        + `  • Document PDF — version d'impression\n\n`
        + `Cordialement,`
      : `Dear${recipientName ? " " + recipientName : ""},\n\n`
        + `Please find attached the ex-ante appraisal dossier for project ${projectCode} (version ${version}).\n\n`
        + `Two formats are provided:\n`
        + `  • Word document (.docx) — editable\n`
        + `  • PDF document — printable version\n\n`
        + `Best regards,`
  );
  const [busy, setBusy] = useStateE(false);
  const [step, setStep] = useStateE(0);  // 0 = idle, 1 = preparing, 2 = ready
  const [err, setErr] = useStateE(null);

  // Track which fallback path completed so the confirmation message
  // reflects reality (mailto opened vs clipboard fallback).
  const [outcome, setOutcome] = useStateE(null); // null | "docx_ok" | "docx_failed" | "mail_ok" | "clipboard_ok"

  const prepareAndOpenMail = async () => {
    setBusy(true); setErr(null); setStep(1);
    let docxOk = false;
    let mailOk = false;
    const errors = [];

    // 1. Try to trigger the .docx download. Wrapped in its own try
    //    so a docx-lib failure doesn't block the email step. This was
    //    the silent-failure path before — now we collect the error.
    try {
      if (window.exportExanteDocxAvailable && typeof window.exportExanteDocx === "function") {
        await window.exportExanteDocx(payload);
        docxOk = true;
      } else if (typeof window.exportExanteDocx === "function") {
        // Library unavailable but the no-op shim exists — calling it
        // opens an alert, so we just skip and log.
        errors.push("Librairie docx indisponible — téléchargement Word ignoré.");
      } else {
        errors.push("Export Word non installé sur cette instance.");
      }
    } catch (docxErr) {
      console.error("[ex-ante] docx export failed:", docxErr);
      errors.push("Échec génération .docx : " + (docxErr.message || docxErr));
    }

    // 2. Build the mailto URL and try to open it. mailto: silently
    //    fails when no default mail client is configured (very common
    //    on managed Windows machines). We always run the clipboard
    //    fallback in parallel so the user has something usable either
    //    way.
    const url = "mailto:" + encodeURIComponent(recipient)
      + "?subject=" + encodeURIComponent(subject)
      + "&body=" + encodeURIComponent(body);
    try {
      // Anchor-click trick — works even when window.location.href silently
      // fails on some OS/browser combos.
      const a = document.createElement("a");
      a.href = url;
      a.rel = "noopener";
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      mailOk = true;
    } catch (mailErr) {
      console.error("[ex-ante] mailto: failed:", mailErr);
      errors.push("Ouverture du client mail impossible.");
    }

    // 3. Clipboard fallback — copy the whole email block so the user
    //    can paste it into Gmail / Outlook web / their phone. Best-
    //    effort; only works on https or localhost.
    let clipOk = false;
    try {
      if (navigator.clipboard && navigator.clipboard.writeText) {
        const clipBlock =
          "À: " + recipient + "\n" +
          "Objet: " + subject + "\n\n" +
          body;
        await navigator.clipboard.writeText(clipBlock);
        clipOk = true;
      }
    } catch (clipErr) {
      // Permission denied / not in secure context — silent, just no clip.
    }

    // Decide outcome message
    if (docxOk && mailOk) setOutcome("mail_ok");
    else if (clipOk)     setOutcome("clipboard_ok");
    else if (docxOk)     setOutcome("docx_ok");
    else                 setOutcome("docx_failed");

    if (errors.length > 0 && !docxOk && !mailOk) {
      setErr(errors.join(" "));
    }
    setStep(2);
    setBusy(false);
  };

  const inp = { padding: "8px 10px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13, width: "100%", boxSizing: "border-box", background: "var(--bg, white)", color: "var(--text)" };
  const lbl = { fontSize: 11, opacity: 0.75 };

  return (
    <div onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,.55)", zIndex: 9999,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 16, overflow: "auto",
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        background: "var(--bg, white)", color: "var(--text, #111)",
        padding: 22, borderRadius: 10, width: 600, maxWidth: "100%",
        boxShadow: "0 10px 30px rgba(0,0,0,.25)", display: "grid", gap: 12,
        maxHeight: "calc(100vh - 32px)", overflow: "auto",
      }}>
        <div style={{ fontSize: 18, fontWeight: 600 }}>
          {lang === "fr" ? "Soumettre le dossier ex-ante" : "Submit ex-ante dossier"}
        </div>
        <div className="text-faint" style={{ fontSize: 11.5 }}>
          {lang === "fr"
            ? "Préparez l'envoi du dossier par email au chef de projet. Le document Word (.docx) sera téléchargé automatiquement, et votre client mail s'ouvrira avec le sujet et le corps pré-remplis. Vous joindrez le .docx (et le PDF si vous en avez généré un) manuellement à l'email."
            : "Prepare an email submission to the project lead. The Word document (.docx) is downloaded automatically and your email client opens with subject and body pre-filled. Attach the .docx (and PDF if generated) manually."}
        </div>

        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>
            {lang === "fr" ? "Destinataire (chef de projet)" : "Recipient (project lead)"}
            {recipientName && <span className="text-faint" style={{ marginLeft: 6 }}>· {recipientName}</span>}
          </span>
          <input type="email" required value={recipient} onChange={(e) => setRecipient(e.target.value)} style={inp} />
          {!lead && (
            <span className="text-faint" style={{ fontSize: 11 }}>
              {lang === "fr"
                ? "Aucun chef de projet renseigné dans la fiche projet — saisissez l'adresse manuellement."
                : "No project lead set in the project record — enter the address manually."}
            </span>
          )}
        </label>

        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Sujet" : "Subject"}</span>
          <input required value={subject} onChange={(e) => setSubject(e.target.value)} style={inp} />
        </label>

        <label style={{ display: "grid", gap: 4 }}>
          <span style={lbl}>{lang === "fr" ? "Message" : "Body"}</span>
          <textarea rows={9} value={body} onChange={(e) => setBody(e.target.value)} style={{ ...inp, resize: "vertical", fontFamily: "inherit" }} />
        </label>

        <div style={{
          padding: "10px 12px", borderRadius: 6,
          background: "var(--bg-sunken)", border: "1px solid var(--line-faint)",
          fontSize: 11.5,
        }}>
          <div style={{ fontWeight: 600, marginBottom: 4 }}>
            {lang === "fr" ? "Étapes :" : "Steps:"}
          </div>
          <ol style={{ margin: 0, paddingLeft: 18, lineHeight: 1.6 }}>
            <li>
              {lang === "fr"
                ? "Cliquez « Préparer et envoyer » — le .docx est téléchargé et votre email s'ouvre."
                : "Click \"Prepare and send\" — the .docx downloads and your email opens."}
            </li>
            <li>
              {lang === "fr" ? "(Optionnel) " : "(Optional) "}
              <button type="button" onClick={onOpenReport}
                style={{ background: "transparent", border: 0, color: "var(--accent)", cursor: "pointer", padding: 0, textDecoration: "underline", fontSize: 11.5 }}>
                {lang === "fr" ? "Générer un PDF" : "Generate a PDF"}
              </button>{" "}
              {lang === "fr"
                ? "via le bouton « Imprimer / PDF » de l'aperçu rapport."
                : "via the report overlay's \"Imprimer / PDF\" button."}
            </li>
            <li>
              {lang === "fr"
                ? "Joignez le .docx (et le PDF) à l'email avant de l'envoyer."
                : "Attach the .docx (and PDF) to the email before sending."}
            </li>
          </ol>
        </div>

        {step === 2 && outcome === "mail_ok" && (
          <div style={{
            padding: "10px 12px", borderRadius: 6,
            background: "#dcfce7", color: "#15803d", fontSize: 12, fontWeight: 500,
          }}>
            ✓ {lang === "fr"
              ? "Email ouvert dans votre client mail · .docx téléchargé. Joignez-le manuellement à l'email avant d'envoyer."
              : "Email opened in your mail client · .docx downloaded. Attach it manually before sending."}
          </div>
        )}
        {step === 2 && outcome === "clipboard_ok" && (
          <div style={{
            padding: "10px 12px", borderRadius: 6,
            background: "#fef3c7", color: "#92400e", fontSize: 12, fontWeight: 500,
          }}>
            ⚠ {lang === "fr"
              ? "Aucun client mail détecté. Le contenu de l'email (destinataire, objet, message) a été copié dans le presse-papier — collez-le dans Gmail / Outlook web / votre messagerie et joignez le .docx téléchargé."
              : "No default mail client detected. The full email (recipient, subject, body) has been copied to your clipboard — paste it into Gmail / Outlook web / your mail app and attach the downloaded .docx."}
          </div>
        )}
        {step === 2 && outcome === "docx_ok" && (
          <div style={{
            padding: "10px 12px", borderRadius: 6,
            background: "#fef3c7", color: "#92400e", fontSize: 12, fontWeight: 500,
          }}>
            ⚠ {lang === "fr"
              ? "Document .docx téléchargé. L'email n'a pas pu être préparé automatiquement — utilisez le destinataire / objet / message ci-dessus pour rédiger manuellement."
              : "The .docx downloaded. Email couldn't be prepared automatically — use the recipient / subject / body above to compose manually."}
          </div>
        )}
        {step === 2 && outcome === "docx_failed" && (
          <div style={{
            padding: "10px 12px", borderRadius: 6,
            background: "#fee2e2", color: "#991b1b", fontSize: 12, fontWeight: 500,
          }}>
            ✗ {lang === "fr"
              ? "La préparation a échoué. Voir le message d'erreur ci-dessous + la console (F12) pour les détails."
              : "Preparation failed. See the error message below + the browser console (F12) for details."}
          </div>
        )}

        {err && (
          <div style={{ color: "#b91c1c", fontSize: 12 }}>{err}</div>
        )}

        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", borderTop: "1px solid var(--line-faint)", paddingTop: 12 }}>
          <button type="button" onClick={onClose} disabled={busy}
            style={{ padding: "8px 14px", borderRadius: 6, border: "1px solid var(--line)", background: "transparent", color: "var(--text)", cursor: "pointer" }}>
            {lang === "fr" ? "Annuler" : "Cancel"}
          </button>
          <button type="button" onClick={prepareAndOpenMail} disabled={busy || !recipient}
            style={{ padding: "8px 14px", borderRadius: 6, border: 0, background: "#2563eb", color: "white", cursor: "pointer", fontWeight: 600 }}>
            {busy ? (lang === "fr" ? "Préparation…" : "Preparing…") : (lang === "fr" ? "📧 Préparer et envoyer" : "📧 Prepare and send")}
          </button>
        </div>
      </div>
    </div>
  );
}

window.ExAnte = ExAnte;
