// SQSEO AI Visibility / GEO screen (Phase 8). Surfaces the generative-engine
// optimization signal we derive from Search Console: a 0-100 GEO score with its
// four drivers, the intent mix of demand, an AI-impressions trend, and the
// per-query AI-Overview interception flags (where AI answers are eating clicks).
// Live: GET /api/geo. Sync now reuses /api/sync. Honest about sample data.
(function init(){
if(!window.LongtailIQDesignSystem_ae8f12 || !window.LTQ || !window.LTQ.useApi || !window.LTQ.BrandOrb || !window.LTQ.SectionTitle){return setTimeout(init,30);}
const React = window.React;
const DS = window.LongtailIQDesignSystem_ae8f12;
const { Card, Icon, Button } = DS;
const { useApi, api, toast, reloadBoot, BrandOrb, SectionTitle, EmptyState, fmtCompact, InteractiveArea } = window.LTQ;
const t = (k, v) => (window.LTQ.t ? window.LTQ.t(k, v) : k);

// Score → band (label + color). Drives the gauge arc + headline tone.
function bandOf(score) {
  const key = score >= 70 ? "strong" : score >= 50 ? "building" : score >= 30 ? "at_risk" : "critical";
  const color = score >= 70 ? "var(--viz-green)" : score >= 50 ? "var(--accent-500)" : score >= 30 ? "var(--viz-amber)" : "var(--viz-red)";
  return { label: t("aivis.band_" + key), color, note: t("aivis.band_" + key + "_note") };
}

const INTENT_COLOR = {
  question: "var(--viz-blue)", comparative: "var(--viz-violet)", informational: "var(--viz-cyan)",
  commercial: "var(--viz-green)", branded: "var(--text-faint)",
};
const intentMeta = (k) => (INTENT_COLOR[k] ? { label: t("aivis.intent_" + k), color: INTENT_COLOR[k] } : { label: k, color: "var(--text-faint)" });

const PARTS = [
  { key: "answerPresence", icon: "crosshair" },
  { key: "intentCoverage", icon: "compass" },
  { key: "citationResilience", icon: "shield" },
  { key: "breadthDepth", icon: "layers" },
];

const Skel = ({ h = 16, w = "100%" }) => <span className="lt-shimmer" style={{ display: "block", height: h, width: w, borderRadius: 6, background: "var(--ink-100)" }} />;

// Animated count-up for the big score (respects reduced-motion).
function useCountUp(target) {
  const [v, setV] = React.useState(0);
  React.useEffect(() => {
    const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce || !target) { setV(target || 0); return; }
    let raf = 0; const start = performance.now(), dur = 900;
    const tick = (now) => {
      const p = Math.min(1, (now - start) / dur);
      const e = 1 - Math.pow(1 - p, 3); // easeOutCubic
      setV(Math.round(target * e));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target]);
  return v;
}

// Circular score gauge with the living BrandOrb at its core.
function ScoreGauge({ score, band }) {
  const size = 232, stroke = 12, r = (size - stroke) / 2, C = 2 * Math.PI * r;
  const [draw, setDraw] = React.useState(false);
  React.useEffect(() => { const t = setTimeout(() => setDraw(true), 60); return () => clearTimeout(t); }, []);
  const n = useCountUp(score);
  const offset = draw ? C * (1 - score / 100) : C;
  return (
    <div style={{ position: "relative", width: size, height: size, flex: "none" }}>
      <svg width={size} height={size} style={{ display: "block", transform: "rotate(-90deg)" }} aria-hidden>
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--ink-100)" strokeWidth={stroke} />
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke={band.color} strokeWidth={stroke} strokeLinecap="round"
          strokeDasharray={C} strokeDashoffset={offset}
          style={{ transition: "stroke-dashoffset 1.1s var(--ease-out)" }} />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 2 }}>
        <BrandOrb size={84} />
        <div style={{ display: "flex", alignItems: "baseline", gap: 2, marginTop: 2 }}>
          <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 40, fontWeight: 700, color: "var(--text-strong)", letterSpacing: "-0.03em", lineHeight: 1 }}>{n}</span>
          <span style={{ fontFamily: "var(--font-mono)", fontSize: 15, color: "var(--text-faint)" }}>/100</span>
        </div>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12, fontWeight: 600, color: "var(--text-body)" }}>
          <span style={{ width: 7, height: 7, borderRadius: 999, background: band.color }} />{band.label}
        </span>
      </div>
    </div>
  );
}

function HeadlineStat({ label, value, sub, icon, tone }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
      <span style={{ width: 38, height: 38, borderRadius: 10, flex: "none", display: "grid", placeItems: "center", background: "var(--ink-50)", border: "1px solid var(--border-subtle)" }}>
        <Icon name={icon} size={17} style={{ color: tone || "var(--text-faint)" }} />
      </span>
      <div style={{ minWidth: 0 }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 7 }}>
          <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 19, fontWeight: 700, color: "var(--text-strong)" }}>{value}</span>
          {sub && <span style={{ fontSize: 11.5, color: "var(--text-faint)" }}>{sub}</span>}
        </div>
        <div style={{ fontSize: 12.5, color: "var(--text-muted)", fontWeight: 600 }}>{label}</div>
      </div>
    </div>
  );
}

function PartCard({ part, value }) {
  const v = Math.max(0, Math.min(100, value || 0));
  const tone = v >= 67 ? "var(--viz-green)" : v >= 40 ? "var(--accent-500)" : "var(--viz-amber)";
  const [w, setW] = React.useState(0);
  React.useEffect(() => { const t = setTimeout(() => setW(v), 80); return () => clearTimeout(t); }, [v]);
  return (
    <Card pad={16}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 10 }}>
        <Icon name={part.icon} size={15} style={{ color: tone }} />
        <span style={{ fontSize: 12.5, color: "var(--text-muted)", fontWeight: 600 }}>{t("aivis.part_" + part.key)}</span>
      </div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 4, marginBottom: 10 }}>
        <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 26, fontWeight: 700, color: "var(--text-strong)", letterSpacing: "-0.02em" }}>{v}</span>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-faint)" }}>/100</span>
      </div>
      <span style={{ display: "block", height: 6, background: "var(--ink-100)", borderRadius: 999, overflow: "hidden", marginBottom: 10 }}>
        <span style={{ display: "block", height: "100%", width: w + "%", background: tone, borderRadius: 999, transition: "width var(--dur-slow) var(--ease-out)" }} />
      </span>
      <div style={{ fontSize: 11.5, color: "var(--text-faint)", lineHeight: 1.45 }}>{t("aivis.part_" + part.key + "_desc")}</div>
    </Card>
  );
}

function IntentMix({ mix }) {
  const order = ["question", "comparative", "informational", "commercial", "branded"];
  const rows = order.map((k) => ({ k, pct: mix[k] || 0 })).filter((r) => r.pct > 0 || true);
  const max = Math.max(1, ...rows.map((r) => r.pct));
  return (
    <Card pad={18}>
      <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)", marginBottom: 4 }}>{t("aivis.demand_by_intent")}</div>
      <div style={{ fontSize: 12, color: "var(--text-faint)", marginBottom: 14 }}>{t("aivis.demand_by_intent_sub")}</div>
      <div style={{ display: "flex", flexDirection: "column", gap: 11 }}>
        {rows.map((r) => {
          const m = intentMeta(r.k);
          return (
            <div key={r.k} style={{ display: "grid", gridTemplateColumns: "112px 1fr 40px", alignItems: "center", gap: 10 }}>
              <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 12.5, fontWeight: 600, color: "var(--text-body)" }}>
                <span style={{ width: 8, height: 8, borderRadius: 999, background: m.color, flex: "none" }} />{m.label}
              </span>
              <span style={{ height: 8, background: "var(--ink-100)", borderRadius: 999, overflow: "hidden" }}>
                <span style={{ display: "block", height: "100%", width: (r.pct / max) * 100 + "%", background: m.color, borderRadius: 999, transition: "width var(--dur-slow) var(--ease-out)" }} />
              </span>
              <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 12.5, fontWeight: 600, color: "var(--text-strong)", textAlign: "right" }}>{r.pct}%</span>
            </div>
          );
        })}
      </div>
    </Card>
  );
}

function RiskPill({ score }) {
  const tone = score >= 60 ? "var(--viz-red)" : score >= 35 ? "var(--viz-amber)" : "var(--text-faint)";
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 7, minWidth: 86 }}>
      <span style={{ flex: 1, height: 6, background: "var(--ink-100)", borderRadius: 999, overflow: "hidden" }}>
        <span style={{ display: "block", height: "100%", width: Math.max(6, score) + "%", background: tone, borderRadius: 999 }} />
      </span>
      <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 12, fontWeight: 700, color: tone, width: 22, textAlign: "right" }}>{score}</span>
    </span>
  );
}

function IntentTag({ k }) {
  const m = intentMeta(k);
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 11.5, fontWeight: 600, color: "var(--text-body)", padding: "2px 8px", borderRadius: 999, background: "var(--ink-50)", border: "1px solid var(--border-subtle)", whiteSpace: "nowrap" }}>
      <span style={{ width: 6, height: 6, borderRadius: 999, background: m.color }} />{m.label}
    </span>
  );
}

function Th({ children, right }) {
  return <th style={{ textAlign: right ? "right" : "left", padding: "0 14px", height: 42, background: "var(--ink-50)", borderBottom: "1px solid var(--border-strong)", fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.06em", color: "var(--text-faint)", whiteSpace: "nowrap" }}>{children}</th>;
}

function InterceptedTable({ rows }) {
  return (
    <Card pad={0} style={{ overflow: "hidden" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 18px", borderBottom: "1px solid var(--border-subtle)" }}>
        <div>
          <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.intercepting_title")}</div>
          <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2 }}>{t("aivis.intercepting_sub")}</div>
        </div>
        <a href="/api/export/ai-opportunities.csv" onClick={() => toast(t("chrome.export_started"), { tone: "success" })}
          style={{ display: "inline-flex", alignItems: "center", gap: 7, height: 32, padding: "0 12px", border: "1px solid var(--border-subtle)", background: "var(--paper)", borderRadius: 8, cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600, color: "var(--text-body)", textDecoration: "none", flex: "none" }}>
          <Icon name="download" size={14} /> CSV
        </a>
      </div>
      {rows.length === 0 ? (
        <div style={{ padding: "26px 18px", fontSize: 13, color: "var(--text-faint)", textAlign: "center" }}>{t("aivis.no_interception")}</div>
      ) : (
        <div className="lt-scroll" style={{ overflowX: "auto" }}>
          <table style={{ borderCollapse: "collapse", width: "100%", minWidth: 540 }}>
            <thead><tr><Th>{t("gaps.col_query")}</Th><Th>{t("research.intent")}</Th><Th>{t("perf.col_ai_risk")}</Th><Th right>{t("perf.col_impr")}</Th><Th right>{t("aivis.col_lost")}</Th></tr></thead>
            <tbody>
              {rows.map((q, i) => {
                const bd = { padding: "12px 14px", borderBottom: i < rows.length - 1 ? "1px solid var(--border-subtle)" : "none", verticalAlign: "middle" };
                return (
                  <tr key={i} onMouseEnter={(e)=>e.currentTarget.style.background="var(--ink-50)"} onMouseLeave={(e)=>e.currentTarget.style.background="transparent"}>
                    <td style={{ ...bd, maxWidth: 280 }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text-strong)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{q.query}</div>
                      <div style={{ fontSize: 11, color: "var(--text-faint)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{q.page}</div>
                    </td>
                    <td style={bd}><IntentTag k={q.intent} /></td>
                    <td style={{ ...bd, width: 130 }}><RiskPill score={q.aioScore} /></td>
                    <td style={{ ...bd, textAlign: "right", fontFamily: "var(--font-mono)", fontSize: 12.5, color: "var(--text-body)" }}>{fmtCompact(q.impressions)}</td>
                    <td style={{ ...bd, textAlign: "right", fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 600, color: "var(--viz-red)" }}>-{fmtCompact(q.lostClicks)}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}
    </Card>
  );
}

// Prioritised, deterministic GEO actions (from /api/geo geo.recommendations).
function RecommendationsCard({ recs }) {
  if (!recs || !recs.length) return null;
  const PR = {
    High: { bg: "var(--amber-50)", fg: "var(--amber-500)", bd: "rgba(217,138,11,0.25)" },
    Medium: { bg: "var(--ink-50)", fg: "var(--text-muted)", bd: "var(--border-subtle)" },
  };
  return (
    <Card pad={0} style={{ overflow: "hidden" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "14px 18px", borderBottom: "1px solid var(--border-subtle)" }}>
        <Icon name="lightbulb" size={16} style={{ color: "var(--accent-500)" }} />
        <div>
          <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.recommended_actions")}</div>
          <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 1 }}>{t("aivis.recommended_sub")}</div>
        </div>
      </div>
      <div>
        {recs.map((r, i) => {
          const pr = PR[r.priority] || PR.Medium;
          return (
            <div key={r.id || i} style={{ display: "flex", alignItems: "flex-start", gap: 13, padding: "14px 18px", borderBottom: i < recs.length - 1 ? "1px solid var(--border-subtle)" : "none" }}>
              <span style={{ width: 26, height: 26, borderRadius: 8, flex: "none", marginTop: 1, display: "grid", placeItems: "center", background: "var(--ink-50)", border: "1px solid var(--border-subtle)", fontFamily: "var(--font-mono)", fontSize: 12, fontWeight: 700, color: "var(--text-muted)" }}>{i + 1}</span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 9, flexWrap: "wrap", marginBottom: 3 }}>
                  <span style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>{r.title}</span>
                  <span style={{ fontSize: 10.5, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.04em", color: pr.fg, background: pr.bg, border: "1px solid " + pr.bd, borderRadius: 999, padding: "1px 7px" }}>{t("level." + String(r.priority).toLowerCase())}</span>
                  {r.intent && <IntentTag k={r.intent} />}
                </div>
                <div style={{ fontSize: 12.5, color: "var(--text-muted)", lineHeight: 1.5 }}>{r.detail}</div>
                {r.queries && r.queries.length > 0 && (
                  <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 8 }}>
                    {r.queries.map((q, j) => (
                      <span key={j} style={{ fontSize: 11.5, color: "var(--text-body)", background: "var(--ink-50)", border: "1px solid var(--border-subtle)", borderRadius: 6, padding: "2px 7px", fontFamily: "var(--font-mono)", maxWidth: 260, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{q}</span>
                    ))}
                  </div>
                )}
              </div>
              {r.recoverable > 0 && (
                <div style={{ flex: "none", textAlign: "right", minWidth: 92 }}>
                  <div className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 16, fontWeight: 700, color: "var(--viz-green)" }}>+{fmtCompact(r.recoverable)}</div>
                  <div style={{ fontSize: 10.5, color: "var(--text-faint)" }}>{t("aivis.clicks_winnable")}</div>
                </div>
              )}
            </div>
          );
        })}
      </div>
    </Card>
  );
}

// Monthly GEO-score trajectory (geo.history), recomputed from the metric frames.
function GeoTrendCard({ history }) {
  const data = history.map((h) => ({ date: (h.month || "").slice(0, 7), score: h.geoScore }));
  const first = history[0].geoScore, last = history[history.length - 1].geoScore, delta = last - first;
  const up = delta >= 0;
  return (
    <Card pad={20}>
      <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: 6 }}>
        <div>
          <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.trend_title")}</div>
          <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2 }}>{t("aivis.trend_sub")}</div>
        </div>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 700, color: up ? "var(--viz-green)" : "var(--viz-red)" }}>
          <Icon name={up ? "trending-up" : "trending-down"} size={15} />{up ? "+" : ""}{delta} {t("aivis.pts")}
        </span>
      </div>
      <InteractiveArea data={data} yKey="score" height={200} color="var(--accent-500)" format={(v) => Math.round(v)} />
    </Card>
  );
}

// The property's own CTR-by-position curve — the baseline the model measures
// interception against. Shown so the score is transparent, not a black box.
function CtrCurveCard({ curve }) {
  const pts = [];
  for (let p = 1; p <= 10; p++) pts.push({ pos: p, ctr: curve[p] || 0 });
  const max = Math.max(0.001, ...pts.map((p) => p.ctr));
  return (
    <Card pad={20}>
      <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.ctr_title")}</div>
      <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2, marginBottom: 16 }}>{t("aivis.ctr_sub")}</div>
      <div style={{ display: "flex", alignItems: "flex-end", gap: 6, height: 120 }}>
        {pts.map((p) => (
          <div key={p.pos} title={t("aivis.position_ctr", { pos: p.pos, ctr: window.LTQ.fmtDec1(p.ctr * 100) })} style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 5, height: "100%", justifyContent: "flex-end" }}>
            <span style={{ width: "100%", borderRadius: "4px 4px 0 0", background: p.pos <= 3 ? "var(--accent-500)" : "var(--ink-200, var(--ink-100))", height: Math.max(3, (p.ctr / max) * 92) + "px", transition: "height var(--dur-slow) var(--ease-out)" }} />
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--text-faint)" }}>{p.pos}</span>
          </div>
        ))}
      </div>
      <div style={{ display: "flex", justifyContent: "space-between", marginTop: 8, fontSize: 11, color: "var(--text-faint)" }}>
        <span>{t("aivis.best")} <b className="lt-num" style={{ fontFamily: "var(--font-mono)", color: "var(--text-body)" }}>{window.LTQ.fmtPct1(curve[1] * 100)}</b> {t("aivis.at_1")}</span>
        <span>{t("aivis.position_arrow")}</span>
      </div>
    </Card>
  );
}

// Engine badge colors for the demand-surfaces card (matches PlatformBadge hues).
const ENGINE_COLOR = { ChatGPT: "var(--viz-green)", Perplexity: "var(--viz-blue)", "AI Overview": "var(--viz-violet)", "Google AI Overview": "var(--viz-violet)", Claude: "var(--viz-amber)", Gemini: "var(--viz-cyan)" };

// Where AI & community demand lives — REAL aggregates only: this project's
// discovered AI prompts grouped by likely engine, plus its Reddit-keyword
// footprint. We don't chart per-engine citation trends because no honest data
// source for them exists here (yet).
function DemandSurfacesCard({ surfaces, onNavigate }) {
  const engines = (surfaces && surfaces.engines) || [];
  const reddit = (surfaces && surfaces.reddit) || { keywords: 0, demand: 0 };
  const max = Math.max(1, ...engines.map((e) => e.prompts));
  return (
    <Card pad={18}>
      <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.surfaces_title")}</div>
      <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2, marginBottom: 14 }}>{t("aivis.surfaces_sub")}</div>
      {engines.length === 0 && reddit.keywords === 0 ? (
        <div style={{ fontSize: 12.5, color: "var(--text-faint)" }}>{t("aivis.surfaces_empty")}</div>
      ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: 11 }}>
          {engines.map((e) => (
            <div key={e.engine} style={{ display: "grid", gridTemplateColumns: "104px 1fr auto", alignItems: "center", gap: 10 }}>
              <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 12.5, fontWeight: 600, color: "var(--text-body)", whiteSpace: "nowrap" }}>
                <span style={{ width: 8, height: 8, borderRadius: 999, background: ENGINE_COLOR[e.engine] || "var(--text-faint)", flex: "none" }} />{e.engine}
              </span>
              <span style={{ height: 8, background: "var(--ink-100)", borderRadius: 999, overflow: "hidden" }}>
                <span style={{ display: "block", height: "100%", width: (e.prompts / max) * 100 + "%", background: ENGINE_COLOR[e.engine] || "var(--ink-300)", borderRadius: 999, transition: "width var(--dur-slow) var(--ease-out)" }} />
              </span>
              <span style={{ fontSize: 11.5, color: "var(--text-faint)", whiteSpace: "nowrap" }}>
                <b className="lt-num" style={{ fontFamily: "var(--font-mono)", color: "var(--text-strong)" }}>{e.prompts}</b> {t("aivis.prompts_lc")}
                {e.highCitation > 0 && <> · <b className="lt-num" style={{ fontFamily: "var(--font-mono)", color: "var(--viz-green)" }}>{e.highCitation}</b> {t("aivis.high_citation_lc")}</>}
              </span>
            </div>
          ))}
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, marginTop: 4, padding: "10px 12px", borderRadius: 10, background: "var(--ink-50)", border: "1px solid var(--border-subtle)" }}>
            <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 12.5, fontWeight: 600, color: "var(--text-body)" }}>
              <Icon name="message-circle" size={14} style={{ color: "var(--viz-amber)" }} />{t("aivis.reddit_demand")}
            </span>
            <span style={{ fontSize: 11.5, color: "var(--text-faint)" }}>
              <b className="lt-num" style={{ fontFamily: "var(--font-mono)", color: "var(--text-strong)" }}>{reddit.keywords}</b> {t("saved.keywords_lc")} · <b className="lt-num" style={{ fontFamily: "var(--font-mono)", color: "var(--text-strong)" }}>{fmtCompact(reddit.demand)}</b> {t("aivis.monthly_demand")}
            </span>
          </div>
          <button onClick={() => onNavigate && onNavigate("ai")} style={{ alignSelf: "flex-start", display: "inline-flex", alignItems: "center", gap: 5, height: 28, padding: "0 9px", border: "1px solid var(--border-subtle)", borderRadius: 8, background: "var(--paper)", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 11.5, fontWeight: 600, color: "var(--text-body)" }}>
            {t("aivis.view_prompts")} <Icon name="arrow-right" size={13} />
          </button>
        </div>
      )}
    </Card>
  );
}

// Which intents are bleeding clicks to AI answers (geo.interceptedByIntent).
function InterceptedByIntentCard({ byIntent }) {
  const order = ["question", "comparative", "informational", "commercial", "branded"];
  const rows = order.map((k) => ({ k, v: (byIntent && byIntent[k]) || 0 })).filter((r) => r.v > 0);
  if (!rows.length) return null;
  const max = Math.max(1, ...rows.map((r) => r.v));
  return (
    <Card pad={18}>
      <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.by_intent_title")}</div>
      <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2, marginBottom: 14 }}>{t("aivis.by_intent_sub")}</div>
      <div style={{ display: "flex", flexDirection: "column", gap: 11 }}>
        {rows.map((r) => {
          const m = intentMeta(r.k);
          return (
            <div key={r.k} style={{ display: "grid", gridTemplateColumns: "112px 1fr 52px", alignItems: "center", gap: 10 }}>
              <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 12.5, fontWeight: 600, color: "var(--text-body)" }}>
                <span style={{ width: 8, height: 8, borderRadius: 999, background: m.color, flex: "none" }} />{m.label}
              </span>
              <span style={{ height: 8, background: "var(--ink-100)", borderRadius: 999, overflow: "hidden" }}>
                <span style={{ display: "block", height: "100%", width: (r.v / max) * 100 + "%", background: m.color, borderRadius: 999, transition: "width var(--dur-slow) var(--ease-out)" }} />
              </span>
              <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 12.5, fontWeight: 600, color: "var(--viz-red)", textAlign: "right" }}>-{fmtCompact(r.v)}</span>
            </div>
          );
        })}
      </div>
    </Card>
  );
}

function AIVisibility({ onNavigate }) {
  const { loading, data, reload } = useApi("/api/geo");
  const [syncing, setSyncing] = React.useState(false);
  const d = data || {};
  const geo = d.geo;
  const sample = d.lastSync && d.lastSync.source === "sample";
  const aiHistory = Array.isArray(d.aiHistory) ? d.aiHistory : [];

  const syncNow = async () => {
    setSyncing(true);
    const r = await api.post("/api/sync");
    setSyncing(false);
    const o = r.data || {};
    if (r.ok && o.ok) { await reloadBoot(); reload(); toast(t("aivis.updated"), { tone: "success" }); }
    else if (o.error === "not_connected") toast(t("wizard.search.title"), { tone: "warning" });
    else toast(r.error || t("wizard.search.sync_failed"), { tone: "error" });
  };

  const SyncBtn = <Button variant="secondary" size="sm" leadingIcon="refresh-cw" onClick={syncNow} disabled={syncing}>{syncing ? t("perf.syncing") : t("perf.sync_now")}</Button>;
  const band = bandOf(geo ? geo.geoScore : 0);
  const history = geo && Array.isArray(geo.history) ? geo.history : [];
  const ctrCurve = geo && Array.isArray(geo.ctrCurve) && geo.ctrCurve.length > 10 ? geo.ctrCurve : null;
  const recs = geo && Array.isArray(geo.recommendations) ? geo.recommendations : [];
  const geoDelta = history.length > 1 ? geo.geoScore - history[0].geoScore : null;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
      <SectionTitle eyebrow={t("aivis.eyebrow")} title={t("nav.ai-visibility")}
        sub={t("aivis.sub")}
        right={(!loading && !geo) ? null : SyncBtn} />

      {!loading && !geo ? (
        <Card pad={0}><EmptyState icon="sparkles" sq="idle" title={t("aivis.empty_title")}
          body={t("aivis.empty_body")}
          action={<Button variant="primary" leadingIcon="plug" onClick={() => { window.location.href = "/oauth/google/authorize"; }}>{t("wizard.search.connect_title")}</Button>} /></Card>
      ) : loading && !geo ? (
        <Card pad={28}><Skel h={200} /></Card>
      ) : (
        <>
          {sample && (
            <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 14px", borderRadius: "var(--r-lg)", border: "1px solid var(--border-subtle)", background: "var(--amber-50)" }}>
              <Icon name="info" size={16} style={{ color: "var(--viz-amber)", flex: "none" }} />
              <span style={{ flex: 1, fontSize: 13, color: "var(--text-body)" }}>{t("perf.sample_1")} <b style={{ color: "var(--text-strong)" }}>{t("dash.sample_bold")}</b> {t("aivis.sample_2")}</span>
              <button onClick={() => onNavigate && onNavigate("settings")} style={{ height: 30, padding: "0 12px", borderRadius: 8, border: "none", background: "var(--ink-900)", color: "#fff", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600, flex: "none" }}>{t("wizard.search.connect_btn")}</button>
            </div>
          )}

          {/* hero: gauge + headline read-out */}
          <Card pad={24}>
            <div style={{ display: "grid", gridTemplateColumns: "minmax(232px, auto) 1fr", gap: 28, alignItems: "center" }}>
              <div style={{ display: "grid", placeItems: "center" }}><ScoreGauge score={geo.geoScore} band={band} /></div>
              <div style={{ display: "flex", flexDirection: "column", gap: 18, minWidth: 0 }}>
                <div>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }}>
                    <span style={{ fontSize: 16, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.band_headline", { band: band.label })}</span>
                    {geoDelta != null && (
                      <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontFamily: "var(--font-mono)", fontSize: 12, fontWeight: 700, color: geoDelta >= 0 ? "var(--viz-green)" : "var(--viz-red)", background: "var(--ink-50)", border: "1px solid var(--border-subtle)", borderRadius: 999, padding: "1px 8px" }}>
                        <Icon name={geoDelta >= 0 ? "trending-up" : "trending-down"} size={13} />{geoDelta >= 0 ? "+" : ""}{geoDelta} {t("aivis.pts")}
                      </span>
                    )}
                  </div>
                  <p style={{ fontSize: 13.5, color: "var(--text-muted)", lineHeight: 1.5, maxWidth: 560 }}>{band.note} {t("aivis.blend_note")}</p>
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(2, minmax(0,1fr))", gap: 18, maxWidth: 520 }}>
                  <HeadlineStat label={t("aivis.stat_intercepted")} value={fmtCompact(geo.interceptedClicks)} sub={t("aivis.stat_intercepted_sub")} icon="zap" tone="var(--viz-red)" />
                  <HeadlineStat label={t("aivis.stat_flagged")} value={geo.topIntercepted.length} sub={t("aivis.stat_flagged_sub")} icon="flag" tone="var(--viz-amber)" />
                  <HeadlineStat label={t("aivis.stat_top3")} value={geo.parts.answerPresence + "%"} sub={t("aivis.stat_top3_sub")} icon="crosshair" tone="var(--viz-green)" />
                  <HeadlineStat label={t("aivis.stat_coverage")} value={geo.parts.intentCoverage + "%"} icon="compass" tone="var(--viz-blue)" />
                </div>
              </div>
            </div>
          </Card>

          {/* four GEO drivers */}
          <div className="lt-keep-grid lt-metric-strip" style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 12 }}>
            {PARTS.map((p, i) => (
              <div key={p.key} className="fx-enter" style={{ "--enter-delay": i * 70 + "ms" }}>
                <PartCard part={p} value={geo.parts[p.key]} />
              </div>
            ))}
          </div>

          {/* the payoff: what to do, prioritised by recoverable clicks */}
          <RecommendationsCard recs={recs} />

          {/* GEO-score trajectory paired with the demand mix (mix full-width if no history) */}
          {history.length > 1 ? (
            <div style={{ display: "grid", gridTemplateColumns: "1.55fr 1fr", gap: 16 }}>
              <GeoTrendCard history={history} />
              <IntentMix mix={geo.intentMix} />
            </div>
          ) : (
            <IntentMix mix={geo.intentMix} />
          )}

          {/* real demand surfaces + which intents are bleeding clicks (Phase 11) */}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))", gap: 16 }}>
            <DemandSurfacesCard surfaces={d.surfaces} onNavigate={onNavigate} />
            <InterceptedByIntentCard byIntent={geo.interceptedByIntent} />
          </div>

          {/* AI-surface footprint + the CTR curve the model measures against */}
          {aiHistory.length > 1 && ctrCurve ? (
            <div style={{ display: "grid", gridTemplateColumns: "1.55fr 1fr", gap: 16 }}>
              <Card pad={20}>
                <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: 6 }}>
                  <div>
                    <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.ai_impressions")}</div>
                    <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2 }}>{t("aivis.ai_impressions_sub")}</div>
                  </div>
                  <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 18, fontWeight: 700, color: "var(--text-strong)" }}>{fmtCompact(aiHistory.reduce((a, r) => a + r.impressions, 0))}</span>
                </div>
                <InteractiveArea data={aiHistory.map((r) => ({ date: r.date, impressions: r.impressions }))} yKey="impressions" height={220} color="var(--accent-500)" format={fmtCompact} />
              </Card>
              <CtrCurveCard curve={ctrCurve} />
            </div>
          ) : aiHistory.length > 1 ? (
            <Card pad={20}>
              <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: 6 }}>
                <div>
                  <div style={{ fontSize: 14, fontWeight: 700, color: "var(--text-strong)" }}>{t("aivis.ai_impressions")}</div>
                  <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2 }}>{t("aivis.ai_impressions_sub")}</div>
                </div>
                <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 18, fontWeight: 700, color: "var(--text-strong)" }}>{fmtCompact(aiHistory.reduce((a, r) => a + r.impressions, 0))}</span>
              </div>
              <InteractiveArea data={aiHistory.map((r) => ({ date: r.date, impressions: r.impressions }))} yKey="impressions" height={220} color="var(--accent-500)" format={fmtCompact} />
            </Card>
          ) : ctrCurve ? (
            <CtrCurveCard curve={ctrCurve} />
          ) : null}

          {/* per-query AI-interception flags — the GEO worklist, full width */}
          <InterceptedTable rows={geo.topIntercepted} />
        </>
      )}
    </div>
  );
}
window.LTQ.AIVisibility = AIVisibility;
})();
