/* global React, window */
// =====================================================================
// CROView v2 — CRO Social command center, CONTRIBUTION-MARGIN edition.
//
// v1 optimized for CPL (wrong). v2 optimizes for CONTRIBUTION MARGIN (CM)
// and respects per-product CAC / cost-per-appointment / CPL ceilings over a
// 45-day+ window, channel-agnostic (meta/google/seo/agentic).
//
// Two sections:
//   1. Decisiones por CM  — campaign grain, 90d (window.__CRO_CAMPAIGNS). THE decision layer.
//   2. Salud creativa     — ad grain, 30d (window.__CRO_SEED). Creative-refresh signals only.
//
// Econ ceilings: window.__CRO_ECON · thresholds: window.__CRO_RULES · channels: window.__CRO_CHANNELS.
// Live actions create an approval request (Bryan/Julian) — they do NOT change budget.
// =====================================================================
(function () {
  const { useState, useEffect, useMemo } = React;

  const fmtPct = (n) => (n == null || isNaN(n)) ? "—" : (n * 100).toFixed(n < 0.1 ? 1 : 0) + "%";
  const fmtMoney = (n) => (n == null || isNaN(n)) ? "—" : (n < 0 ? "−$" : "$") + Math.round(Math.abs(n)).toLocaleString("en-US");
  const fmtFreq = (n) => (n == null || isNaN(n)) ? "—" : Number(n).toFixed(1);

  function median(arr) {
    const v = arr.filter(x => x != null && !isNaN(x)).sort((a, b) => a - b);
    if (!v.length) return null;
    const m = Math.floor(v.length / 2);
    return v.length % 2 ? v[m] : (v[m - 1] + v[m]) / 2;
  }

  // ===================================================================
  // computeEconVerdict — THE v2 verdict engine. Optimizes for CM, gated on
  // per-product CAC / cost-per-appt / CPL ceilings. Used for BOTH grains.
  //   row: { spend, cpl, leads (or derive), product, maturity_days, channel, frequency? }
  //   grain: "campaign" | "creative"
  // Returns { verdict, color, reason, cm, cac, cost_per_appt, appts, won, cpl_target, cac_max }.
  // ===================================================================
  function computeEconVerdict(row, grain) {
    const E = window.__CRO_ECON[row.product] || window.__CRO_ECON.mixed;
    const R = window.__CRO_RULES;

    const spend = Number(row.spend || 0);
    const cpl = (row.cpl != null && !isNaN(row.cpl)) ? Number(row.cpl) : null;
    let leads = row.leads;
    if (leads == null && cpl != null && cpl > 0) leads = Math.round(spend / cpl);
    leads = (leads != null && !isNaN(leads)) ? Number(leads) : null;
    const maturity = Number(row.maturity_days != null ? row.maturity_days : 60);
    const freq = row.frequency != null ? Number(row.frequency) : 0;

    // Funnel economics. Tiered: (1) this campaign's OWN CRM rate when it has enough
    // leads (many leads + few wins = a trustworthy low rate); (2) else the real channel
    // rate; (3) else the blended floor. `fr.source` ∈ {"real","modelado"}.
    let fr;
    if (row.leads_crm != null && row.leads_crm >= R.funnel_min_leads_campaign && row.won_crm != null) {
      const ch0 = window.__CRO_FUNNEL_CHANNEL[row.channel || "meta"];
      fr = {
        sit: (row.sits_crm != null && row.leads_crm > 0) ? row.sits_crm / row.leads_crm : (ch0 ? ch0.sit : R.blended_lead_to_sit),
        won: row.won_crm / row.leads_crm,
        source: "real", grain: "campaign", n_leads: row.leads_crm, n_won: row.won_crm,
      };
    } else {
      fr = window.__CRO_FUNNEL_RATE(row.product, row.channel || "meta");
    }
    const rate_word = fr.source === "real" ? "real" : "modelado";
    const appts = leads != null ? leads * fr.sit : null;
    const won = leads != null ? leads * fr.won : null;
    const cost_per_appt = (appts != null && appts > 0) ? spend / appts : null;
    const cac = (won != null && won > 0) ? spend / won : null;
    const cm = won != null ? won * E.cm_usd - spend : null;
    const cm_roi = (cm != null && spend > 0) ? cm / spend : null;

    const base = { cm, cac, cost_per_appt, appts, won, cm_roi, cpl_target: E.cpl_target, cac_max: E.cac_max, leads,
                   rate_source: fr.source, lead_to_sit: fr.sit, lead_to_won: fr.won, rate_n_leads: fr.n_leads, rate_n_won: fr.n_won };

    // --- Priority 1: non-marketable / non-sales gates ------------------
    if (row.product === "careers")
      return Object.assign({ verdict: "exclude", color: "var(--wm-fg-muted)", reason: "Reclutamiento — fuera del CM de ventas" }, base);
    if (row.product === "awareness")
      return Object.assign({ verdict: "awareness", color: "var(--wm-fg-muted)", reason: "Awareness — se mide por alcance, no CAC" }, base);
    if (!E.marketable)
      return Object.assign({ verdict: "exclude", color: "var(--wm-fg-muted)", reason: "DOE — no mercadear" }, base);

    // --- Priority 2: immature — can't judge CAC yet --------------------
    if (maturity < R.maturity_days) {
      if (cpl != null && cpl < E.cpl_target * R.scale_cpl_multiplier && leads != null && leads >= R.scale_min_leads)
        return Object.assign({ verdict: "scale", color: "var(--op-green)", reason: `CPL ${fmtMoney(cpl)} < ${fmtMoney(E.cpl_target * R.scale_cpl_multiplier)} y joven — escalar temprano` }, base);
      return Object.assign({ verdict: "watch", color: "var(--wm-blue)", reason: `Joven (${maturity}d) — observar, aún no se puede juzgar CAC` }, base);
    }

    // --- Priority 3: KILL ---------------------------------------------
    const bleed = (leads === 0 && spend >= R.bleed_min_spend);
    const cacOver = (cac != null && cac > E.cac_max);
    const apptOver = (cost_per_appt != null && cost_per_appt > E.cost_per_appt_max);
    const cmNeg = (cm != null && cm < 0);
    const cplBlown = (cpl != null && cpl > E.cpl_target * R.pause_cpl_multiplier && spend >= E.cpl_target * R.pause_min_spend_vs_target);
    if (bleed || cacOver || apptOver || cmNeg || cplBlown) {
      let reason;
      if (bleed) reason = `${fmtMoney(spend)} gastado, 0 leads — pausar`;
      else if (cacOver) reason = `CAC ${rate_word} ${fmtMoney(cac)} > techo ${fmtMoney(E.cac_max)} — quema CM`;
      else if (cmNeg) reason = `CM ${rate_word} ${fmtMoney(cm)} — bleeding margin`;
      else if (apptOver) reason = `Costo/cita ${fmtMoney(cost_per_appt)} > techo ${fmtMoney(E.cost_per_appt_max)}`;
      else reason = `CPL ${fmtMoney(cpl)} > ${fmtMoney(E.cpl_target * R.pause_cpl_multiplier)} (techo pausa) — insostenible`;
      return Object.assign({ verdict: "kill", color: "var(--op-red)", reason }, base);
    }

    // --- Priority 4: SCALE --------------------------------------------
    if (cm != null && cm > 0 && cac != null && cac <= E.cac_max &&
        cpl != null && cpl <= E.cpl_target * R.scale_cpl_multiplier && leads != null && leads >= R.scale_min_leads) {
      return Object.assign({ verdict: "scale", color: "var(--op-green)", reason: `CM ${fmtMoney(cm)} · CAC ${fmtMoney(cac)} < techo ${fmtMoney(E.cac_max)} — escalar` }, base);
    }

    // --- Priority 5: FIX ----------------------------------------------
    let reason;
    if (cpl != null && cpl > E.cpl_target) reason = `CPL ${fmtMoney(cpl)} > target ${fmtMoney(E.cpl_target)}`;
    else if (cac != null && cac > E.cac_max * 0.8) reason = `CAC ${fmtMoney(cac)} acercándose al techo ${fmtMoney(E.cac_max)}`;
    else if (grain === "creative" && freq >= 3.5) reason = `Frecuencia ${fmtFreq(freq)} — fatiga`;
    else reason = "Margen ajustado — optimizar funnel";
    return Object.assign({ verdict: "fix", color: "var(--op-amber)", reason }, base);
  }

  // ===================================================================
  // computeCreativeHealth — secondary "creative health" signal (v1 hook/hold/
  // fatigue). NOT a CM verdict. v1 bug fixed: only treat as video (compute hook)
  // when ad_type ∈ {video,carousel} OR video_3s/impressions >= 0.01; else hook=null.
  // ===================================================================
  function computeCreativeHealth(ad) {
    const imp = Number(ad.impressions || 0);
    const v3 = ad.video_3s != null ? Number(ad.video_3s) : null;
    const tp = ad.thruplay != null ? Number(ad.thruplay) : null;
    const at = (ad.ad_type || "").toLowerCase();
    const v3Ratio = (v3 != null && imp > 0) ? v3 / imp : null;
    const isVideo = (at === "video" || at === "carousel") || (v3Ratio != null && v3Ratio >= 0.01);
    const hook = isVideo ? v3Ratio : null;
    const hold = (isVideo && tp != null && v3 != null && v3 > 0) ? tp / v3 : null;
    const freq = ad.frequency != null ? Number(ad.frequency) : 0;
    const ectr = (ad.quality_ectr || "").toLowerCase();
    const fatigue = freq >= 3.5;

    let signal, color, note;
    if (freq >= 5 || ectr.indexOf("below average") !== -1) {
      signal = "refresh"; color = "var(--op-red)";
      note = freq >= 5 ? `Frecuencia ${fmtFreq(freq)} — saturado, refrescar` : "Calidad eCTR baja — refrescar creativo";
    } else if (fatigue || (hook != null && hook < 0.25)) {
      signal = "watch"; color = "var(--op-amber)";
      note = fatigue ? `Frecuencia ${fmtFreq(freq)} — fatiga` : `Hook ${fmtPct(hook)} — no engancha`;
    } else {
      signal = "healthy"; color = "var(--op-green)";
      note = hook != null ? `Hook ${fmtPct(hook)} — sano` : "Creativo sano";
    }
    return { hook, hold, fatigue, signal, color, note };
  }

  const VERDICT_LABEL = {
    es: { scale: "Escalar", fix: "Arreglar", kill: "Matar", watch: "Observar", awareness: "Awareness", exclude: "Excluir" },
    en: { scale: "Scale", fix: "Fix", kill: "Kill", watch: "Watch", awareness: "Awareness", exclude: "Exclude" },
  };
  const VERDICT_BG = {
    scale: "var(--op-green-50, #E8F4EE)",
    fix: "var(--op-amber-50, #FCF1DE)",
    kill: "var(--op-red-50, #FBE8E5)",
    watch: "rgba(29,66,155,0.08)",
    awareness: "rgba(0,0,0,0.05)",
    exclude: "rgba(0,0,0,0.05)",
  };
  const HEALTH_BG = {
    healthy: "var(--op-green-50, #E8F4EE)",
    watch: "var(--op-amber-50, #FCF1DE)",
    refresh: "var(--op-red-50, #FBE8E5)",
  };
  const HEALTH_LABEL = {
    es: { healthy: "Sano", watch: "Vigilar", refresh: "Refrescar" },
    en: { healthy: "Healthy", watch: "Watch", refresh: "Refresh" },
  };
  const CHANNEL_LABEL = { meta: "Meta", google: "Google", seo: "SEO", agentic: "Agéntico" };
  const PRODUCT_LABEL = {
    solar_commercial: "Solar comercial", solar_residential: "Solar residencial", roofing: "Roofing",
    anker: "Anker", water: "Water", solar_doe: "Solar DOE", mixed: "Mixto", careers: "Reclutamiento", awareness: "Awareness",
  };

  function Chip({ children, tone }) {
    const bg = tone === "blue" ? "rgba(29,66,155,0.08)" : tone === "grey" ? "rgba(0,0,0,0.05)" : "rgba(248,155,36,0.12)";
    const fg = tone === "blue" ? "var(--wm-blue)" : tone === "grey" ? "var(--wm-fg-muted)" : "var(--op-amber)";
    return <span style={{ fontSize: 10, background: bg, color: fg, padding: "1px 7px", borderRadius: 8, fontWeight: 700, whiteSpace: "nowrap" }}>{children}</span>;
  }

  // Badge de la plataforma que "nos está hablando" (icono + color).
  function PlatformBadge({ ch }) {
    const map = {
      meta:    { icon: "facebook", label: "Meta",     color: "#1877F2" },
      google:  { icon: "search",   label: "Google",   color: "#EA4335" },
      seo:     { icon: "globe",     label: "SEO",      color: "#0F9D58" },
      agentic: { icon: "cpu",       label: "Agéntico", color: "#7C3AED" },
    };
    const p = map[ch] || { icon: "megaphone", label: ch || "—", color: "var(--wm-fg-muted)" };
    return (
      <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 10, fontWeight: 800, color: p.color, background: "rgba(0,0,0,0.045)", padding: "2px 8px", borderRadius: 8, whiteSpace: "nowrap" }}>
        <window.Icon name={p.icon} size={11} /> {p.label}
      </span>
    );
  }

  function Thumb({ url }) {
    const [err, setErr] = useState(false);
    const box = { width: 56, height: 56, borderRadius: 10, flexShrink: 0, objectFit: "cover", background: "var(--op-line)", border: "1px solid var(--op-line)" };
    if (err || !url) return <div style={{ ...box, display: "flex", alignItems: "center", justifyContent: "center", color: "var(--wm-fg-muted)" }}><window.Icon name="image-off" size={18} /></div>;
    return <img src={url} alt="" style={box} onError={() => setErr(true)} />;
  }

  function CROView({ lang = "es" }) {
    const es = lang === "es";
    const [data, setData] = useState(null);      // creative grain: { rows, source }
    const [vFilter, setVFilter] = useState("all"); // all | scale | fix | kill | watch
    const [chFilter, setChFilter] = useState("all"); // channel filter
    const [toast, setToast] = useState(null);
    // campaign decision layer: { rows, source } — live from windmar-marketing or static snapshot
    const [cro, setCro] = useState({ rows: window.__CRO_CAMPAIGNS || [], source: "snapshot", ratesLive: false });
    // Admin execute mode: when ON, the row buttons EXECUTE via /api/actions (with confirm);
    // when OFF, they create a governance approval request (Bryan/Julian).
    const [execMode, setExecMode] = useState(() => { try { return localStorage.getItem("cro_exec_admin") === "1"; } catch (_) { return false; } });
    const [confirmExec, setConfirmExec] = useState(null); // { opt, row }
    const [actionMenu, setActionMenu] = useState(null);   // row (full options menu)
    const [decisions, setDecisions] = useState(null);     // decision log rows
    const [auds, setAuds] = useState(null);               // audiencias del agente → decisiones
    const [audBusy, setAudBusy] = useState(null);         // id en proceso (review/decide)

    useEffect(() => {
      if (window.wm?.ready && window.wm.creativePerf) window.wm.creativePerf().then(setData);
      else setData({ rows: (window.__CRO_SEED || []), source: "seed" });
    }, []);

    const loadAuds = () => { if (window.wm?.ready && window.wm.croAudiences) window.wm.croAudiences().then(r => setAuds(Array.isArray(r) ? r : [])); else setAuds([]); };
    useEffect(() => { loadAuds(); }, []);
    const reviewAudience = async (id) => {
      setAudBusy(id);
      // 1) Agente LLM (lee economics + RAG, razona como experto) — ya escribe a DB.
      let r = window.wm?.audienceAnalyst ? await window.wm.audienceAnalyst(id) : null;
      const viaLLM = !!(r && r.verdict);
      // 2) Fallback: reglas transparentes si el LLM no está disponible.
      if (!viaLLM && window.wm?.croAudienceReview) r = await window.wm.croAudienceReview(id);
      setAudBusy(null);
      if (r && r.verdict) { setToast({ kind: "ok", msg: (es ? "Revisión " : "Review ") + (viaLLM ? "🧠 IA · " : "· ") + r.verdict }); loadAuds(); }
      else setToast({ kind: "err", msg: es ? "No se pudo revisar" : "Review failed" });
    };
    const decideAudience = async (id, action) => {
      if (!window.wm?.croAudienceDecide) return;
      setAudBusy(id);
      const r = await window.wm.croAudienceDecide(id, action, "dashboard");
      setAudBusy(null);
      if (r && r.ok) { setToast({ kind: "ok", msg: (es ? "Decisión registrada: " : "Decision logged: ") + action }); loadAuds(); if (window.wm.decisionsRecent) window.wm.decisionsRecent(40).then(setDecisions); }
      else setToast({ kind: "err", msg: (r && r.error) || (es ? "Error" : "Error") });
    };
    // "Dejarlo como está" — una decisión válida: registra keep sin cambiar nada en Meta.
    const keepCampaign = async (c) => {
      if (window.wm?.logDecision) await window.wm.logDecision({
        actor: "dashboard", channel: c.channel, product: c.product, meta_campaign_id: c.meta_campaign_id,
        campaign_name: c.campaign_name, verdict: c.verdict, action_category: "keep", action_key: "keep",
        action_label: "Mantener (dejar como está)", status: "confirmed",
        reason: es ? "Decisión: dejar la campaña como está" : "Decision: leave campaign as is",
      });
      setToast({ kind: "ok", msg: es ? "✓ Mantener: dejado como está" : "✓ Kept as is" });
      if (window.wm?.decisionsRecent) window.wm.decisionsRecent(40).then(setDecisions);
    };
    const audBtn = (c) => ({ padding: "5px 10px", borderRadius: 8, border: "1px solid " + c, background: "transparent", color: c, fontWeight: 700, fontSize: 11, cursor: "pointer" });
    const AUD_VC = { recomendado: "var(--op-green)", testear: "var(--wm-blue)", afinar: "var(--op-amber)", descartar: "var(--op-red)" };
    const AUD_VBG = { recomendado: "var(--op-green-50,#E8F4EE)", testear: "rgba(29,66,155,0.08)", afinar: "var(--op-amber-50,#FCF1DE)", descartar: "var(--op-red-50,#FBE8E5)" };

    // Live CRO decision inputs: real per-channel funnel rates (merged into the resolver) +
    // per-campaign spend/funnel. Falls back to the static snapshot if RPCs aren't deployed.
    useEffect(() => {
      let alive = true;
      (async () => {
        if (!(window.wm?.ready && window.wm.croFunnelRates && window.wm.croCampaignsLive)) return;
        let econLive = false;
        // Unit economics EN VIVO: live override sobre el baked (baked solo rellena huecos como 'mixed').
        try {
          if (window.wm.croEconomics) {
            const ec = await window.wm.croEconomics();
            if (ec && Object.keys(ec).length) { window.__CRO_ECON = Object.assign({}, window.__CRO_ECON, ec); econLive = true; }
          }
        } catch (_) {}
        // Config EN VIVO: reglas + auto_exec + catálogo de acciones (baked solo fallback).
        try {
          if (window.wm.croConfig) {
            const cfg = await window.wm.croConfig();
            if (cfg && cfg.rules) { window.__CRO_RULES = Object.assign({}, window.__CRO_RULES, cfg.rules); econLive = true; }
            if (cfg && Array.isArray(cfg.actions) && cfg.actions.length) window.__CRO_ACTIONS = cfg.actions;
            if (cfg && Array.isArray(cfg.auto_exec) && cfg.auto_exec.length) window.__CRO_AUTO_EXEC = cfg.auto_exec;
          }
        } catch (_) {}
        let ratesLive = false;
        try {
          const fr = await window.wm.croFunnelRates();
          if (fr && (fr.meta || fr.google)) {
            ["meta", "google"].forEach(ch => {
              const r = fr[ch];
              if (r && r.won != null) {
                window.__CRO_FUNNEL_CHANNEL[ch] = { sit: Number(r.sit), won: Number(r.won), n_leads: Number(r.n_leads), n_won: Number(r.n_won) };
                ratesLive = true;
              }
            });
          }
        } catch (_) {}
        let rows = null;
        try { const live = await window.wm.croCampaignsLive(); if (Array.isArray(live) && live.length) rows = live; } catch (_) {}
        if (!alive) return;
        if (rows || ratesLive || econLive) setCro({ rows: rows || (window.__CRO_CAMPAIGNS || []), source: rows ? "live" : "snapshot", ratesLive, econLive });
      })();
      return () => { alive = false; };
    }, []);

    // ----- SECTION 1: campaign decision layer (90d, CM) ----------------
    const campaigns = useMemo(() => {
      // Performance CC: fuera careers/reclutamiento (defensivo, además del filtro en el matview).
      const rows = (cro.rows || []).filter(c =>
        c.product !== "careers" &&
        !/reclut|recruit|career|freedom|empleo|vacante|hiring|talent/i.test(String(c.campaign_name || c.name || ""))
      ).slice();
      return rows.map(c => {
        const v = computeEconVerdict(c, "campaign");
        const decorated = Object.assign({}, c, v);
        decorated._verdict_reason = v.reason; // used by approvalRequest
        return decorated;
      }).sort((a, b) => {
        // biggest CM losers first; nulls (awareness/exclude) sink to the bottom
        const av = a.cm == null ? Infinity : a.cm;
        const bv = b.cm == null ? Infinity : b.cm;
        return av - bv;
      });
    }, [cro]);

    const visibleCampaigns = campaigns.filter(c => chFilter === "all" || c.channel === chFilter);

    const econKpis = useMemo(() => {
      const judged = campaigns.filter(c => c.verdict !== "awareness" && c.verdict !== "exclude");
      const cmTotal = judged.reduce((s, c) => s + (c.cm != null ? c.cm : 0), 0);
      const cmAtRisk = judged.filter(c => c.cm != null && c.cm < 0).reduce((s, c) => s + c.cm, 0);
      const overCac = judged.filter(c => c.cac != null && c.cac > c.cac_max).length;
      const cacs = judged.map(c => c.cac).filter(x => x != null);
      const blendedCac = median(cacs);
      const scaleReady = campaigns.filter(c => c.verdict === "scale").length;
      // blended ceiling reference = median of cac_max across judged products
      const ceilings = judged.map(c => c.cac_max).filter(x => x != null);
      const blendedCeiling = median(ceilings);
      return { cmTotal, cmAtRisk, overCac, blendedCac, blendedCeiling, scaleReady };
    }, [campaigns]);

    // ----- SECTION 2: creative health (30d) ----------------------------
    const decoratedCreatives = useMemo(() => {
      const rows = (data?.rows || []).slice();
      return rows.map(ad => {
        const econ = computeEconVerdict(
          { spend: ad.spend, cpl: ad.cpl, leads: null, product: mapCreativeProduct(ad.product), maturity_days: 60, channel: "meta", frequency: ad.frequency },
          "creative"
        );
        const health = computeCreativeHealth(ad);
        const merged = Object.assign({}, ad, { econ, health });
        merged._verdict_reason = econ.reason;
        return merged;
      }).sort((a, b) => Number(b.spend || 0) - Number(a.spend || 0));
    }, [data]);

    const visibleCreatives = decoratedCreatives.filter(d =>
      (vFilter === "all" || d.econ.verdict === vFilter)
    );

    async function requestAction(action, src) {
      if (!window.wm?.approvalRequest) { setToast(es ? "Gate de aprobación pendiente de deploy (migración)" : "Approval gate pending deploy (migration)"); return; }
      const res = await window.wm.approvalRequest(action, src);
      if (res == null) setToast(es ? "Gate de aprobación pendiente de deploy (migración)" : "Approval gate pending deploy (migration)");
      else setToast(es ? "Solicitud enviada → requiere aprobación de Bryan o Julian" : "Request sent → requires Bryan or Julian approval");
    }
    useEffect(() => { if (!toast) return; const id = setTimeout(() => setToast(null), 4200); return () => clearTimeout(id); }, [toast]);
    useEffect(() => { if (window.wm && window.wm.decisionsRecent) window.wm.decisionsRecent(40).then(d => setDecisions(Array.isArray(d) ? d : [])); }, []);

    function toggleExec() { setExecMode(v => { const nv = !v; try { localStorage.setItem("cro_exec_admin", nv ? "1" : "0"); } catch (_) {} return nv; }); }

    function loadDecisions() { if (window.wm && window.wm.decisionsRecent) window.wm.decisionsRecent(40).then(d => setDecisions(Array.isArray(d) ? d : [])); }
    async function logDecision(entry) { try { if (window.wm && window.wm.logDecision) { const id = await window.wm.logDecision(entry); loadDecisions(); return id; } } catch (_) {} return null; }

    function getAgentKey() {
      let k = null; try { k = localStorage.getItem("cro_agent_key"); } catch (_) {}
      if (!k) { k = window.prompt(es ? "Admin key del agente ejecutor (CRO_ADMIN_KEY):" : "Executor agent admin key:"); if (k) { try { localStorage.setItem("cro_agent_key", k); } catch (_) {} } }
      return k;
    }
    async function runAgent(decisionId) {
      if (!window.wm || !window.wm.runExecutor) { setToast(es ? "Agente no disponible" : "Agent unavailable"); return; }
      const key = getAgentKey(); if (!key) return;
      setToast(es ? "🤖 Ejecutando con agente…" : "🤖 Running agent…");
      const res = await window.wm.runExecutor(decisionId, key);
      const r0 = res && res.results && res.results[0];
      if (res && res.status === "forbidden") { try { localStorage.removeItem("cro_agent_key"); } catch (_) {} setToast(es ? "🔒 admin_key inválido — reintenta" : "🔒 invalid admin_key"); }
      else if (res && res.status === "skipped") setToast(es ? "⏳ Falta META_ACCESS_TOKEN en el agente" : "⏳ agent META_ACCESS_TOKEN missing");
      else if (r0) setToast("🤖 " + (r0.status === "executed" ? (es ? "Ejecutado: " : "Executed: ") : r0.status + ": ") + (r0.action || "") + (r0.result && r0.result.note ? " — " + r0.result.note : ""));
      else setToast(es ? "🤖 Procesado" : "🤖 Processed");
      loadDecisions();
    }

    // Ejecuta una opción auto-exec (budget/pause) contra /api/actions. Devuelve {status, json}.
    async function execActionRaw(exec, row, params) {
      const id = row.meta_campaign_id;
      if (!id) return { status: "failed", json: { error: "no meta_campaign_id (snapshot)" } };
      let url, body;
      if (exec === "pause") { url = "/api/actions/pause"; body = { meta_campaign_id: id, triggered_by: "cro-admin" }; }
      else if (exec === "budget_up") { url = "/api/actions/budget"; body = { meta_campaign_id: id, direction: "up", percent: (params && params.percent) || 20, triggered_by: "cro-admin" }; }
      else if (exec === "budget_down") { url = "/api/actions/budget"; body = { meta_campaign_id: id, direction: "down", percent: (params && params.percent) || 20, triggered_by: "cro-admin" }; }
      else return { status: "failed", json: { error: "not auto-executable" } };
      try {
        const r = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
        const j = await r.json().catch(() => ({}));
        return { status: j.status || ("http_" + r.status), json: j };
      } catch (e) { return { status: "failed", json: { error: String(e.message || e) } }; }
    }

    // Corre una opción del catálogo: SIEMPRE registra la decisión (full log), y ejecuta o encola.
    async function runOption(opt, row) {
      const base = {
        actor: "cro-admin", channel: row.channel, product: row.product,
        meta_campaign_id: row.meta_campaign_id || null, campaign_name: row.campaign_name,
        verdict: row.verdict, action_category: opt.category, action_key: opt.key, action_label: opt.label,
        exec_type: opt.exec, params: opt.params || {}, reason: row.reason, skill_source: opt.skill,
        before_state: { cpl: row.cpl, cm: row.cm, cac: row.cac, spend: row.spend, won: row.won_crm },
      };
      // Ruta unificada: con "Ejecución directa" ON, TODA opción (pausar/budget/duplicar/
      // lookalike/A·B) se registra y la ejecuta el agente cro-executor (gated por CRO_ADMIN_KEY,
      // logueada con before/after). OFF = propuesta + solicitud de aprobación a Bryan/Julian.
      if (execMode) {
        const id = await logDecision(Object.assign({}, base, { status: "queued", result: { note: "ejecutando vía agente cro-executor" } }));
        if (id) await runAgent(id);
        else setToast(es ? "No se pudo registrar la decisión" : "Could not log decision");
      } else {
        const auto = window.__CRO_AUTO_EXEC.indexOf(opt.exec) >= 0;
        const actMap = { pause: "pause", budget_up: "scale", budget_down: "reduce" };
        if (auto && window.wm && window.wm.approvalRequest) window.wm.approvalRequest(actMap[opt.exec] || opt.exec, row);
        await logDecision(Object.assign({}, base, { status: "proposed", result: { gate: "approval Bryan/Julian" } }));
        setToast(es ? "📩 Propuesta registrada — aprobación Bryan/Julian (o pulsa 🤖 en la bitácora)" : "📩 Proposal logged — Bryan/Julian approval (or hit 🤖 in the log)");
      }
    }
    // Abrir confirmación para una opción concreta del catálogo.
    function chooseOption(opt, row) { setActionMenu(null); setConfirmExec({ opt, row }); }

    const source = data?.source;
    const loading = data === null;

    return (
      <div style={{ padding: "4px 2px" }}>
        {/* A. Header */}
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
          <window.Icon name="shield-check" size={20} />
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 800 }}>{es ? "Decisiones" : "Decisions"}</h2>
          <span style={{ fontSize: 11, fontWeight: 700, color: "var(--wm-blue)", background: "var(--wm-blue-50,#EEF3FD)", padding: "3px 9px", borderRadius: 9 }}>CM · windmar-marketing</span>
          <span style={{ marginLeft: "auto" }}>
            {source === "live"
              ? <span style={{ fontSize: 11, fontWeight: 700, color: "var(--op-green)", background: "var(--op-green-50,#E8F4EE)", padding: "3px 9px", borderRadius: 9 }}>● Live · windmar-marketing</span>
              : <span style={{ fontSize: 11, fontWeight: 700, color: "var(--op-amber)", background: "var(--op-amber-50,#FCF1DE)", padding: "3px 9px", borderRadius: 9 }}>● {es ? "Seed · Supermetrics" : "Seed · Supermetrics"}</span>}
          </span>
        </div>

        {/* CM doctrine banner */}
        <div style={{ fontSize: 12, color: "var(--wm-fg-muted)", marginBottom: 10, display: "flex", gap: 6, alignItems: "flex-start" }}>
          <window.Icon name="target" size={14} />
          <span>{es
            ? "Optimizado para CM, no CPL. Funnel REAL desde el CRM (lead 'won'): por campaña si tiene ≥100 leads, si no por canal (Meta ~2.8% / Google ~3.8% lead→venta), si no piso blended 1.4%. Ventana 45d+, madurez 45d."
            : "Optimized for CM, not CPL. REAL funnel from the CRM (lead 'won'): per-campaign when ≥100 leads, else per-channel (Meta ~2.8% / Google ~3.8% lead→won), else blended floor 1.4%. 45d+ window, 45d maturity."}</span>
        </div>

        {/* Governance note */}
        <div style={{ fontSize: 12, color: "var(--wm-fg-muted)", marginBottom: 16, display: "flex", gap: 6, alignItems: "flex-start" }}>
          <window.Icon name="shield-check" size={14} />
          <span>{es
            ? "Las acciones en vivo crean una solicitud de aprobación (Bryan/Julian). El dashboard no cambia presupuesto directamente."
            : "Live actions create an approval request (Bryan/Julian). The dashboard does not change budget directly."}</span>
        </div>

        {/* B. KPI strip — CM economics */}
        <div style={{ display: "grid", gridTemplateColumns: "repeat(5,1fr)", gap: 12, marginBottom: 18 }}>
          {[
            { l: es ? "CM total modelado (45d+)" : "Modeled CM total (45d+)", v: fmtMoney(econKpis.cmTotal), c: econKpis.cmTotal >= 0 ? "var(--op-green)" : "var(--op-red)" },
            { l: es ? "CM en riesgo" : "CM at risk", v: fmtMoney(econKpis.cmAtRisk), c: "var(--op-red)" },
            { l: es ? "Campañas sobre CAC" : "Over-CAC campaigns", v: econKpis.overCac, c: "var(--wm-blue)" },
            { l: es ? "CAC blended vs techo" : "Blended CAC vs ceiling", v: `${fmtMoney(econKpis.blendedCac)} / ${fmtMoney(econKpis.blendedCeiling)}`, c: "var(--wm-blue)" },
            { l: es ? "Scale-ready" : "Scale-ready", v: econKpis.scaleReady, c: "var(--op-green)" },
          ].map((s, i) => (
            <div key={i} style={{ background: "var(--op-card,#fff)", border: "1px solid var(--op-line)", borderRadius: 12, padding: "12px 14px" }}>
              <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", textTransform: "uppercase", letterSpacing: ".05em" }}>{s.l}</div>
              <div style={{ fontSize: 20, fontWeight: 900, color: s.c }}>{s.v}</div>
            </div>
          ))}
        </div>

        {/* Channel filter */}
        <div style={{ display: "flex", gap: 6, alignItems: "center", flexWrap: "wrap", marginBottom: 14 }}>
          {["all"].concat(window.__CRO_CHANNELS || ["meta", "google", "seo", "agentic"]).map(k => {
            const hasData = k === "all" || k === "meta";
            return (
              <button key={k} onClick={() => hasData && setChFilter(k)} disabled={!hasData}
                style={{ padding: "5px 12px", borderRadius: 9, border: "1px solid var(--op-line)", cursor: hasData ? "pointer" : "not-allowed", fontSize: 12, fontWeight: 700,
                  background: chFilter === k ? "var(--wm-blue)" : "var(--op-card,#fff)", color: chFilter === k ? "#fff" : (hasData ? "var(--wm-fg-muted)" : "var(--op-line-strong)") }}>
                {k === "all" ? (es ? "Todos los canales" : "All channels") : CHANNEL_LABEL[k]}{!hasData && (es ? " · sin data aún" : " · no data yet")}
              </button>
            );
          })}
        </div>

        {/* =========================================================== */}
        {/* SECTION 1 — Decisiones por CM (campaign grain, 90d)         */}
        {/* =========================================================== */}
        <div style={{ display: "flex", alignItems: "center", gap: 8, margin: "6px 0 4px" }}>
          <window.Icon name="trending-down" size={18} />
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 800 }}>{es ? "Decisiones por CM" : "CM Decisions"}</h3>
          <Chip tone="grey">{es ? "campaña · 90d" : "campaign · 90d"}</Chip>
          <Chip tone={cro.source === "live" ? "blue" : "grey"}>
            {cro.source === "live" ? "● live · windmar-marketing" : "snapshot"}
            {cro.ratesLive ? (es ? " · funnel real" : " · real funnel") : ""}
          </Chip>
          <button onClick={toggleExec} title={es ? "Modo ejecución directa (admin). OFF = pide aprobación a Bryan/Julian." : "Direct execute mode (admin). OFF = requests Bryan/Julian approval."}
            style={{ marginLeft: "auto", padding: "4px 10px", borderRadius: 8, fontSize: 11, fontWeight: 800, cursor: "pointer",
              border: "1px solid " + (execMode ? "var(--op-red)" : "var(--op-line)"),
              background: execMode ? "var(--op-red-50,#FBE8E5)" : "transparent",
              color: execMode ? "var(--op-red)" : "var(--wm-fg-muted)" }}>
            {execMode ? (es ? "⚡ Ejecución directa: ON" : "⚡ Execute: ON") : (es ? "Ejecución directa: OFF" : "Execute: OFF")}
          </button>
        </div>
        <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", marginBottom: 10 }}>
          {es ? "Ordenado por CM ascendente — lo que quema margen primero." : "Sorted by CM ascending — biggest margin losers first."}
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: 10, marginBottom: 26 }}>
          {visibleCampaigns.map((c, i) => {
            const label = VERDICT_LABEL[lang][c.verdict];
            const showPause = c.verdict === "kill" || c.verdict === "fix";
            const showScale = c.verdict === "scale";
            const cpaOver = c.cost_per_appt != null && c.cost_per_appt > c.cost_per_appt_max;
            const cacOver = c.cac != null && c.cac > c.cac_max;
            const cplOver = c.cpl != null && c.cpl > c.cpl_target;
            return (
              <div key={i} style={{ background: "var(--op-card,#fff)", border: "1px solid var(--op-line)", borderRadius: 12, padding: 12 }}>
                <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "flex-start" }}>
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ fontWeight: 800, fontSize: 13, lineHeight: 1.25, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={c.campaign_name}>{c.campaign_name}</div>
                    <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 6, alignItems: "center" }}>
                      <PlatformBadge ch={c.channel} />
                      <Chip tone="blue">{PRODUCT_LABEL[c.product] || c.product}</Chip>
                      <Chip>{c.maturity_days}d</Chip>
                    </div>
                  </div>
                  <span style={{ fontSize: 11, fontWeight: 800, padding: "2px 9px", borderRadius: 9, background: VERDICT_BG[c.verdict], color: c.color, whiteSpace: "nowrap" }}>{label}</span>
                </div>

                {/* metric row */}
                <div style={{ display: "flex", gap: 16, marginTop: 10, flexWrap: "wrap", alignItems: "center" }}>
                  <span style={{ fontSize: 11, color: "var(--wm-fg-muted)" }}><b style={{ color: "var(--wm-fg, #1a1a1a)" }}>{fmtMoney(c.spend)}</b> Spend</span>
                  <span style={{ fontSize: 11, color: "var(--wm-fg-muted)" }} title="cost_per_lead vs target">CPL <b style={{ color: cplOver ? "var(--op-red)" : "var(--wm-fg, #1a1a1a)" }}>{fmtMoney(c.cpl)}</b> / target {fmtMoney(c.cpl_target)}</span>
                  <span style={{ fontSize: 11, color: "var(--wm-fg-muted)" }} title="CAC modelado vs techo">CAC <b style={{ color: cacOver ? "var(--op-red)" : "var(--wm-fg, #1a1a1a)" }}>{fmtMoney(c.cac)}</b> / techo {fmtMoney(c.cac_max)}</span>
                  <span style={{ fontSize: 11, color: "var(--wm-fg-muted)" }} title="costo por cita modelado vs techo">{es ? "Costo/cita" : "Cost/appt"} <b style={{ color: cpaOver ? "var(--op-red)" : "var(--wm-fg, #1a1a1a)" }}>{fmtMoney(c.cost_per_appt)}</b> / {fmtMoney(c.cost_per_appt_max)}</span>
                  <span style={{ fontSize: 11, color: "var(--wm-fg-muted)" }}>{es ? "CM modelado" : "Modeled CM"} <b style={{ fontSize: 13, color: c.cm == null ? "var(--wm-fg-muted)" : (c.cm >= 0 ? "var(--op-green)" : "var(--op-red)") }}>{c.cm == null ? "—" : fmtMoney(c.cm)}</b></span>
                </div>

                {/* verdict reason + action */}
                <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 10, borderTop: "1px solid var(--op-line)", paddingTop: 8 }}>
                  <div style={{ fontSize: 11, color: c.color, fontWeight: 700, flex: 1 }}>{c.reason}</div>
                  {showScale && (
                    <button onClick={() => chooseOption(window.__CRO_ACTIONS.find(a => a.key === "scale_budget_20"), c)}
                      style={{ padding: "5px 12px", borderRadius: 8, border: "1px solid var(--op-green)", background: "var(--op-green-50,#E8F4EE)", color: "var(--op-green)", fontWeight: 700, fontSize: 11, cursor: "pointer" }}>
                      {es ? "Escalar +20%" : "Scale +20%"}
                    </button>
                  )}
                  {showPause && (
                    <button onClick={() => chooseOption(window.__CRO_ACTIONS.find(a => a.key === "pause_cm_neg"), c)}
                      style={{ padding: "5px 12px", borderRadius: 8, border: "1px solid var(--op-red)", background: "var(--op-red-50,#FBE8E5)", color: "var(--op-red)", fontWeight: 700, fontSize: 11, cursor: "pointer" }}>
                      {es ? "Pausar" : "Pause"}
                    </button>
                  )}
                  <button onClick={() => keepCampaign(c)} title={es ? "Dejar la campaña como está (registra la decisión, no cambia nada)" : "Leave as is (logs the decision, changes nothing)"}
                    style={{ padding: "5px 12px", borderRadius: 8, border: "1px solid var(--op-line)", background: "transparent", color: "var(--wm-fg-muted)", fontWeight: 700, fontSize: 11, cursor: "pointer" }}>
                    {es ? "Mantener" : "Keep"}
                  </button>
                  {c.verdict !== "awareness" && c.verdict !== "exclude" && (
                    <button onClick={() => setActionMenu(c)}
                      style={{ padding: "5px 12px", borderRadius: 8, border: "1px solid var(--wm-blue)", background: "transparent", color: "var(--wm-blue)", fontWeight: 800, fontSize: 11, cursor: "pointer" }}>
                      {es ? "Acciones ▾" : "Actions ▾"}
                    </button>
                  )}
                </div>
              </div>
            );
          })}
          {!visibleCampaigns.length && <div style={{ color: "var(--wm-fg-muted)", fontSize: 13, padding: 16 }}>{es ? "Sin campañas para este canal." : "No campaigns for this channel."}</div>}
        </div>

        {/* =========================================================== */}
        {/* SECTION 1b — Audiencias → decisiones (gate de revisión)     */}
        {/* =========================================================== */}
        <div style={{ display: "flex", alignItems: "center", gap: 8, margin: "6px 0 4px" }}>
          <window.Icon name="radar" size={18} />
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 800 }}>{es ? "Audiencias → decisiones" : "Audiences → decisions"}</h3>
          <Chip tone="grey">{es ? "agente de audiencias" : "audience agent"}</Chip>
          <Chip tone="blue">{(auds || []).length} {es ? "descubiertas" : "found"}</Chip>
        </div>
        <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", marginBottom: 10 }}>
          {es ? "El agente propone audiencias; la analítica de windmar-marketing las revisa como experta de marketing ANTES de postear." : "The agent proposes audiences; windmar-marketing analytics reviews them as a marketing expert BEFORE posting."}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 26 }}>
          {(auds || []).map((a, i) => {
            const cm = Number(a.meta && a.meta.cm_fit || 0);
            const col = AUD_VC[a.review_verdict] || "var(--wm-fg-muted)";
            return (
              <div key={i} style={{ background: "var(--op-card,#fff)", border: "1px solid var(--op-line)", borderRadius: 12, padding: 12, display: "flex", flexDirection: "column", gap: 8 }}>
                <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "flex-start" }}>
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ fontWeight: 800, fontSize: 13, lineHeight: 1.25 }}>{a.title}</div>
                    <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 6, alignItems: "center" }}>
                      <PlatformBadge ch="meta" />
                      {a.meta && a.meta.vertical && <Chip tone="blue">{a.meta.vertical}</Chip>}
                      {a.meta && a.meta.est_size && <Chip>~{window.fmt.num(a.meta.est_size)}</Chip>}
                      {a.meta && a.meta.sports_angle && <Chip>{a.meta.sports_angle}</Chip>}
                    </div>
                  </div>
                  <span style={{ fontWeight: 900, color: cm >= 0.85 ? "var(--op-green)" : cm >= 0.78 ? "var(--wm-blue)" : "var(--wm-orange)" }} title="CM-fit">{cm ? cm.toFixed(2) : "—"}</span>
                </div>

                {!a.reviewed ? (
                  <div style={{ display: "flex", alignItems: "center", gap: 8, borderTop: "1px solid var(--op-line)", paddingTop: 8 }}>
                    <span style={{ fontSize: 11, color: "var(--wm-fg-muted)", flex: 1, display: "flex", alignItems: "center", gap: 4 }}><window.Icon name="clock" size={12} /> {es ? "Pendiente de revisión analítica" : "Pending analytics review"}</span>
                    <button disabled={audBusy === a.id} onClick={() => reviewAudience(a.id)}
                      style={{ padding: "5px 12px", borderRadius: 8, border: "1px solid var(--wm-blue)", background: "var(--wm-blue)", color: "#fff", fontWeight: 800, fontSize: 11, cursor: "pointer", opacity: audBusy === a.id ? 0.6 : 1 }}>
                      {audBusy === a.id ? (es ? "Revisando…" : "Reviewing…") : (es ? "Revisar con analytics" : "Review with analytics")}
                    </button>
                  </div>
                ) : (
                  <>
                    <div style={{ borderTop: "1px solid var(--op-line)", paddingTop: 8 }}>
                      <span style={{ fontSize: 11, fontWeight: 800, padding: "2px 9px", borderRadius: 9, background: AUD_VBG[a.review_verdict] || "rgba(0,0,0,0.05)", color: col }}>{(es ? "Analítica: " : "Analytics: ") + (a.review_verdict || "—")}</span>
                      <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", marginTop: 6, lineHeight: 1.4 }}>{a.review_reason}</div>
                    </div>
                    {a.decision ? (
                      <div style={{ fontSize: 11, fontWeight: 700, color: "var(--op-green)", display: "flex", alignItems: "center", gap: 4 }}><window.Icon name="check" size={12} /> {(es ? "Decisión: " : "Decision: ") + a.decision}</div>
                    ) : (
                      <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                        <button disabled={audBusy === a.id} onClick={() => decideAudience(a.id, "crear")} style={audBtn("var(--op-green)")}>{es ? "Crear audiencia" : "Create"}</button>
                        <button disabled={audBusy === a.id} onClick={() => decideAudience(a.id, "testear")} style={audBtn("var(--wm-blue)")}>{es ? "Testear A·B" : "A/B test"}</button>
                        <button disabled={audBusy === a.id} onClick={() => decideAudience(a.id, "mantener")} style={audBtn("var(--wm-fg-muted)")}>{es ? "Mantener" : "Keep"}</button>
                        <button disabled={audBusy === a.id} onClick={() => decideAudience(a.id, "descartar")} style={audBtn("var(--op-red)")}>{es ? "Descartar" : "Discard"}</button>
                      </div>
                    )}
                  </>
                )}
              </div>
            );
          })}
          {!(auds || []).length && <div style={{ color: "var(--wm-fg-muted)", fontSize: 13, padding: 16 }}>{es ? "El agente de audiencias aún no ha corrido." : "Audience agent hasn't run yet."}</div>}
        </div>

        {/* =========================================================== */}
        {/* SECTION 2 — Salud creativa (ad grain, 30d)                  */}
        {/* =========================================================== */}
        <div style={{ display: "flex", alignItems: "center", gap: 8, margin: "6px 0 4px" }}>
          <window.Icon name="image" size={18} />
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 800 }}>{es ? "Salud creativa" : "Creative health"}</h3>
          <Chip tone="grey">{es ? "creativo · 30d" : "ad · 30d"}</Chip>
        </div>
        <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", marginBottom: 10 }}>
          {es ? "Hook/Hold/fatiga como señal de refresco creativo. El veredicto CM por creativo es secundario al de campaña." : "Hook/Hold/fatigue as creative-refresh signal. Per-ad CM verdict is secondary to the campaign layer."}
        </div>

        {/* Verdict filter (creative econ verdict) */}
        <div style={{ display: "flex", gap: 6, marginBottom: 14, flexWrap: "wrap" }}>
          {[["all", es ? "Todos" : "All"], ["scale", VERDICT_LABEL[lang].scale], ["fix", VERDICT_LABEL[lang].fix], ["kill", VERDICT_LABEL[lang].kill], ["watch", VERDICT_LABEL[lang].watch]].map(([k, label]) => (
            <button key={k} onClick={() => setVFilter(k)}
              style={{ padding: "5px 12px", borderRadius: 9, border: "1px solid var(--op-line)", cursor: "pointer", fontSize: 12, fontWeight: 700,
                background: vFilter === k ? "var(--wm-blue)" : "var(--op-card,#fff)", color: vFilter === k ? "#fff" : "var(--wm-fg-muted)" }}>
              {label}
            </button>
          ))}
        </div>

        {loading && <div style={{ padding: 16, color: "var(--wm-fg-muted)" }}>{es ? "Cargando creativos…" : "Loading creatives…"}</div>}

        {!loading && (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 12 }}>
            {visibleCreatives.map((ad, i) => {
              const econ = ad.econ, health = ad.health;
              const eLabel = VERDICT_LABEL[lang][econ.verdict];
              const hLabel = HEALTH_LABEL[lang][health.signal];
              const showPause = econ.verdict === "kill" || econ.verdict === "fix";
              const showScale = econ.verdict === "scale";
              return (
                <div key={i} style={{ background: "var(--op-card,#fff)", border: "1px solid var(--op-line)", borderRadius: 12, padding: 12 }}>
                  <div style={{ display: "flex", gap: 12 }}>
                    <Thumb url={ad.thumbnail_url} />
                    <div style={{ minWidth: 0, flex: 1 }}>
                      <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "flex-start" }}>
                        <div style={{ fontWeight: 800, fontSize: 13, lineHeight: 1.25, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={ad.ad_name}>{ad.ad_name}</div>
                        <span style={{ fontSize: 11, fontWeight: 800, padding: "2px 9px", borderRadius: 9, background: VERDICT_BG[econ.verdict], color: econ.color, whiteSpace: "nowrap" }}>{eLabel}</span>
                      </div>
                      <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", marginTop: 3, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={ad.campaign_name}>{ad.campaign_name}</div>
                      <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 6 }}>
                        <Chip tone="blue">{ad.product}</Chip>
                        <Chip>{ad.ad_type}</Chip>
                        <span style={{ fontSize: 10, background: HEALTH_BG[health.signal], color: health.color, padding: "1px 7px", borderRadius: 8, fontWeight: 700, whiteSpace: "nowrap" }}>{hLabel}</span>
                      </div>
                    </div>
                  </div>

                  {/* metric row */}
                  <div style={{ display: "flex", gap: 12, marginTop: 10, fontSize: 11, color: "var(--wm-fg-muted)", flexWrap: "wrap" }}>
                    <span><b style={{ color: "var(--wm-fg, #1a1a1a)" }}>{fmtPct(health.hook)}</b> Hook</span>
                    <span><b style={{ color: "var(--wm-fg, #1a1a1a)" }}>{fmtPct(health.hold)}</b> Hold</span>
                    <span><b style={{ color: "var(--wm-fg, #1a1a1a)" }}>{fmtPct(ad.link_ctr)}</b> CTR</span>
                    <span><b style={{ color: "var(--wm-fg, #1a1a1a)" }}>{fmtMoney(ad.cpl)}</b> CPL</span>
                    <span><b style={{ color: "var(--wm-fg, #1a1a1a)" }}>{fmtFreq(ad.frequency)}</b> Freq</span>
                    <span title="quality_ectr">{ad.quality_ectr || "—"}</span>
                  </div>

                  {/* econ verdict reason + creative-health note + action */}
                  <div style={{ marginTop: 10, borderTop: "1px solid var(--op-line)", paddingTop: 8 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <div style={{ fontSize: 11, color: econ.color, fontWeight: 700, flex: 1 }}>{econ.reason}</div>
                      {showPause && (
                        <button onClick={() => requestAction("pause", ad)}
                          style={{ padding: "5px 12px", borderRadius: 8, border: "1px solid var(--op-red)", background: "var(--op-red-50,#FBE8E5)", color: "var(--op-red)", fontWeight: 700, fontSize: 11, cursor: "pointer" }}>
                          {es ? "Pausar" : "Pause"}
                        </button>
                      )}
                      {showScale && (
                        <button onClick={() => requestAction("scale", ad)}
                          style={{ padding: "5px 12px", borderRadius: 8, border: "1px solid var(--op-green)", background: "var(--op-green-50,#E8F4EE)", color: "var(--op-green)", fontWeight: 700, fontSize: 11, cursor: "pointer" }}>
                          {es ? "Escalar +20%" : "Scale +20%"}
                        </button>
                      )}
                    </div>
                    <div style={{ fontSize: 10, color: health.color, marginTop: 4 }}>{es ? "Creativo: " : "Creative: "}{health.note}</div>
                  </div>
                </div>
              );
            })}
            {!visibleCreatives.length && <div style={{ color: "var(--wm-fg-muted)", fontSize: 13, padding: 16 }}>{es ? "Sin creativos para este filtro." : "No creatives for this filter."}</div>}
          </div>
        )}

        {/* =========================================================== */}
        {/* SECTION 3 — Bitácora de decisiones (full log)              */}
        {/* =========================================================== */}
        <div style={{ display: "flex", alignItems: "center", gap: 8, margin: "20px 0 8px" }}>
          <window.Icon name="history" size={18} />
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 800 }}>{es ? "Bitácora de decisiones" : "Decision log"}</h3>
          <Chip tone="grey">{(decisions || []).length}</Chip>
          <button onClick={loadDecisions} title="refresh" style={{ marginLeft: "auto", padding: "3px 9px", borderRadius: 7, border: "1px solid var(--op-line)", background: "transparent", fontSize: 11, fontWeight: 700, cursor: "pointer", color: "var(--wm-fg-muted)" }}>↻</button>
        </div>
        <div style={{ border: "1px solid var(--op-line)", borderRadius: 12, overflow: "hidden", marginBottom: 24 }}>
          {decisions === null && <div style={{ padding: 14, fontSize: 12, color: "var(--wm-fg-muted)" }}>{es ? "Cargando…" : "Loading…"}</div>}
          {decisions && !decisions.length && <div style={{ padding: 14, fontSize: 12, color: "var(--wm-fg-muted)" }}>{es ? "Sin decisiones aún. Cuando elijas una acción (Escalar / Tweak / A·B / Pausar) queda registrada aquí con before/after." : "No decisions yet — every action is logged here."}</div>}
          {decisions && decisions.map((d, i) => {
            const sc = d.status === "executed" ? "var(--op-green)" : d.status === "failed" ? "var(--op-red)" : d.status === "proposed" ? "var(--wm-blue)" : "var(--op-amber)";
            return (
              <div key={d.id || i} style={{ display: "flex", gap: 10, alignItems: "center", padding: "8px 12px", borderTop: i ? "1px solid var(--op-line)" : "none", fontSize: 12 }}>
                <span style={{ fontSize: 9.5, fontWeight: 800, padding: "2px 8px", borderRadius: 20, color: sc, border: "1px solid " + sc, whiteSpace: "nowrap" }}>{d.status}</span>
                <span style={{ fontSize: 10, color: "var(--wm-fg-muted)", whiteSpace: "nowrap" }}>{d.action_category}</span>
                <span style={{ fontWeight: 700, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={d.action_label}>{d.action_label}</span>
                <span style={{ color: "var(--wm-fg-muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1 }} title={d.campaign_name}>{d.campaign_name}</span>
                <span style={{ marginLeft: "auto", color: "var(--wm-fg-muted)", fontSize: 10, whiteSpace: "nowrap" }}>{d.skill_source || ""} · {String(d.created_at || "").slice(5, 16).replace("T", " ")}</span>
                {["queued", "proposed", "needs_config", "failed"].indexOf(d.status) >= 0 && (
                  <button onClick={() => runAgent(d.id)} title={es ? "Ejecutar con agente" : "Run with agent"}
                    style={{ padding: "2px 8px", borderRadius: 7, border: "1px solid var(--wm-blue)", background: "transparent", color: "var(--wm-blue)", fontSize: 11, fontWeight: 800, cursor: "pointer", whiteSpace: "nowrap" }}>🤖</button>
                )}
              </div>
            );
          })}
        </div>

        {/* Action menu modal — el catálogo de opciones (skills) por veredicto */}
        {actionMenu && (() => {
          const r = actionMenu;
          const opts = window.__CRO_ACTIONS_FOR(r.verdict);
          const cats = ["Escalar", "Tweak", "A/B Test", "Iterar", "Pausar"];
          return (
            <div style={{ position: "fixed", inset: 0, background: "rgba(10,15,27,0.55)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 10000, padding: 20 }} onClick={() => setActionMenu(null)}>
              <div onClick={e => e.stopPropagation()} style={{ background: "var(--wm-card,#fff)", borderRadius: 16, padding: 20, maxWidth: 560, width: "100%", maxHeight: "82vh", overflow: "auto", boxShadow: "0 12px 48px rgba(0,0,0,0.3)" }}>
                <div style={{ fontSize: 15, fontWeight: 900 }}>{es ? "Acciones para" : "Actions for"} <span style={{ color: "var(--wm-blue)" }}>{r.campaign_name}</span></div>
                <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", margin: "4px 0 12px" }}>{es ? "Generadas por los skills (paid-ads · ab-test · ad-creative · revops). Cada elección se registra en la bitácora." : "Generated by the skills. Every choice is logged."}</div>
                {cats.map(cat => {
                  const co = opts.filter(o => o.category === cat);
                  if (!co.length) return null;
                  return (
                    <div key={cat} style={{ marginBottom: 12 }}>
                      <div style={{ fontSize: 11, fontWeight: 800, textTransform: "uppercase", letterSpacing: ".5px", color: "var(--wm-fg-muted)", marginBottom: 6 }}>{cat}</div>
                      {co.map(o => (
                        <button key={o.key} onClick={() => chooseOption(o, r)}
                          style={{ display: "block", width: "100%", textAlign: "left", padding: "9px 11px", marginBottom: 6, borderRadius: 9, border: "1px solid var(--op-line)", background: "var(--op-card,#fff)", cursor: "pointer" }}>
                          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8 }}>
                            <span style={{ fontWeight: 700, fontSize: 13 }}>{o.label}</span>
                            <span style={{ fontSize: 9.5, fontWeight: 800, color: o.auto ? "var(--op-green)" : "var(--wm-fg-muted)", border: "1px solid " + (o.auto ? "var(--op-green)" : "var(--op-line)"), borderRadius: 20, padding: "1px 7px", whiteSpace: "nowrap" }}>{o.auto ? (es ? "ejecuta" : "executes") : (es ? "propuesta" : "proposed")} · {o.skill}</span>
                          </div>
                          <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", marginTop: 3, lineHeight: 1.35 }}>{o.what}</div>
                        </button>
                      ))}
                    </div>
                  );
                })}
                <div style={{ textAlign: "right" }}><button onClick={() => setActionMenu(null)} style={{ padding: "7px 14px", borderRadius: 8, border: "1px solid var(--op-line)", background: "transparent", fontWeight: 700, fontSize: 12, cursor: "pointer" }}>{es ? "Cerrar" : "Close"}</button></div>
              </div>
            </div>
          );
        })()}

        {/* Confirm modal — confirma la opción elegida */}
        {confirmExec && confirmExec.opt && (() => {
          const o = confirmExec.opt, r = confirmExec.row;
          const auto = window.__CRO_AUTO_EXEC.indexOf(o.exec) >= 0;
          const tone = o.category === "Pausar" ? "var(--op-red)" : o.category === "Escalar" ? "var(--op-green)" : "var(--wm-blue)";
          const willExec = auto && execMode, willApprove = auto && !execMode;
          const headline = willExec ? (es ? "Ejecutar EN VIVO (Meta)" : "Execute LIVE (Meta)") : willApprove ? (es ? "Pedir aprobación a Bryan/Julian" : "Request Bryan/Julian approval") : (es ? "Registrar propuesta (equipo/agentes)" : "Log proposal (team/agents)");
          return (
            <div style={{ position: "fixed", inset: 0, background: "rgba(10,15,27,0.55)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 10001, padding: 20 }} onClick={() => setConfirmExec(null)}>
              <div onClick={e => e.stopPropagation()} style={{ background: "var(--wm-card,#fff)", borderRadius: 16, padding: 24, maxWidth: 460, boxShadow: "0 12px 48px rgba(0,0,0,0.3)" }}>
                <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 8 }}>
                  <span style={{ fontSize: 10, fontWeight: 800, padding: "2px 9px", borderRadius: 9, background: "rgba(29,66,155,0.08)", color: tone }}>{o.category}</span>
                  <span style={{ fontSize: 16, fontWeight: 900 }}>{o.label}</span>
                </div>
                <div style={{ fontSize: 12.5, color: "var(--wm-fg-muted)", lineHeight: 1.5, marginBottom: 8 }}>{o.what}</div>
                <div style={{ fontSize: 11, color: "var(--wm-fg-muted)", marginBottom: 4 }}>{es ? "Campaña:" : "Campaign:"} <b style={{ color: "var(--wm-fg,#13203f)" }}>{r.campaign_name}</b> · skill: <b>{o.skill}</b></div>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: tone, marginBottom: 14 }}>→ {headline}{willExec ? (es ? " · cambia presupuesto/estado real" : " · changes real budget/status") : ""}. {es ? "Se registra en la bitácora con before/after." : "Logged with before/after."}</div>
                <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
                  <button onClick={() => setConfirmExec(null)} style={{ padding: "8px 16px", borderRadius: 8, border: "1px solid var(--op-line)", background: "transparent", fontWeight: 700, fontSize: 12, cursor: "pointer" }}>{es ? "Cancelar" : "Cancel"}</button>
                  <button onClick={() => { runOption(o, r); setConfirmExec(null); }} style={{ padding: "8px 16px", borderRadius: 8, border: "none", background: tone, color: "#fff", fontWeight: 800, fontSize: 12, cursor: "pointer" }}>{willExec ? (es ? "Ejecutar ahora" : "Execute now") : (es ? "Confirmar y registrar" : "Confirm & log")}</button>
                </div>
              </div>
            </div>
          );
        })()}

        {/* Toast */}
        {toast && (
          <div style={{ position: "fixed", bottom: 24, left: "50%", transform: "translateX(-50%)", background: "var(--wm-blue-900,#0A0F1B)", color: "#fff", padding: "10px 18px", borderRadius: 12, fontSize: 13, fontWeight: 700, boxShadow: "0 6px 24px rgba(0,0,0,0.25)", zIndex: 9999 }}>
            {toast}
          </div>
        )}
      </div>
    );
  }

  // Map the seed's v1 product labels (Roofing/Solar/Anker/Water/Mixed) to v2 econ keys.
  function mapCreativeProduct(p) {
    const s = (p || "").toLowerCase();
    if (s === "roofing") return "roofing";
    if (s === "anker") return "anker";
    if (s === "water") return "water";
    if (s === "solar") return "solar_residential";
    return "mixed";
  }

  window.CROView = CROView;
})();
