/* =====================================================================
   Activus platform demo — Sarah Chen
   ---------------------------------------------------------------------
   STATIC re-skin of the redesigned profile template (app/src/app/[slug]/
   _view.tsx, the "Claude Design handoff" editorial layout). This is an
   INDEPENDENT demo: a hand-mirrored static copy, hardcoded mock data, no
   DB/auth. It deliberately does NOT track future product changes.

   Flow: land in the AGENT view → open AGENTEVAL → flip to CLIENT view with
   the simple toggle in the demo bar.
   ===================================================================== */
const { useState, useMemo, useEffect, useRef } = React;

/* ====================================================================== */
/*  THEME (mirrors THEME_CSS in _view.tsx, scoped to .ai-page)             */
/* ====================================================================== */
const THEME_CSS = `
  .ai-page {
    --bg: #FDFCF8; --paper: #FFFDF8; --paper-2: #F3EEE5;
    --ink: #0F0E0C; --ink-2: #2E2A26; --ink-3: #5A554C; --mute: #8A8378;
    --line: #E2DBCC; --line-2: #C9C0AC; --hair: #0F0E0C;
    --accent: oklch(0.55 0.19 295); --accent-ink: oklch(0.40 0.18 295);
    --accent-tint: oklch(0.97 0.03 295); --accent-tint-2: oklch(0.93 0.06 295);
    --precon: oklch(0.55 0.19 295); --resale: oklch(0.50 0.13 240);
    --luxury: oklch(0.62 0.14 60); --investor: oklch(0.58 0.12 160);
    --coral: oklch(0.66 0.15 32); --teal: oklch(0.62 0.09 195);
    /* internal AgentEval signals — ahead / behind peers */
    --good: oklch(0.52 0.13 158); --good-ink: oklch(0.42 0.12 158); --good-tint: oklch(0.95 0.04 158);
    --bad: oklch(0.58 0.16 32); --bad-ink: oklch(0.46 0.16 32); --bad-tint: oklch(0.95 0.04 32);
    background: var(--bg); color: var(--ink);
    font-family: "Prompt", system-ui, sans-serif; font-size: 15px; line-height: 1.55;
    min-height: 100vh; -webkit-font-smoothing: antialiased;
  }
  .ai-page .mono { font-family: "JetBrains Mono", ui-monospace, monospace; }
  .ai-page .news { font-family: "Newsreader", Georgia, serif; }
  .ai-page ::selection { background: var(--accent); color: var(--paper); }
  .ai-heat-cell { position: relative; transition: transform .13s ease, opacity .15s; transform-origin: center bottom; }
  .ai-heat-cell:hover { transform: scale(1.4); z-index: 3; }
  .ai-main { display: grid; grid-template-columns: minmax(208px, 290px) 1fr; column-gap: 36px; align-items: start; max-width: 1840px; margin: 0 auto; padding: 8px 16px 0; }
  @media (max-width: 900px) {
    .ai-main { grid-template-columns: 1fr !important; }
    .ai-sticky { position: static !important; }
    .ai-grid-shift { margin-left: 0 !important; }
  }
`;

/* ====================================================================== */
/*  MOCK DATA (Fixture shape from _view.tsx, hardcoded for Sarah Chen)      */
/* ====================================================================== */
const TYPE_LABEL = { precon: "Pre-construction", resale: "Re-sale", luxury: "Luxury", investor: "Investor" };

const SHORT_MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
function monthLabelsEndingAt(end) {
  const labels = [];
  const year = end.getFullYear();
  const month = end.getMonth();
  for (let i = 11; i >= 0; i--) {
    const target = new Date(year, month - i, 1);
    labels.push(SHORT_MONTH_NAMES[target.getMonth()]);
  }
  return labels;
}
const MONTHS = monthLabelsEndingAt(new Date());

function hashStr(s) {
  let h = 2166136261;
  for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); }
  return h >>> 0;
}
function seeded(seed) {
  let s = seed % 2147483647;
  if (s <= 0) s += 2147483646;
  return () => { s = (s * 16807) % 2147483647; return s / 2147483647; };
}

// Deal lifecycle stage (1-6) per house. Front-loaded so the heatmap shows
// plenty of early-stage deals (first contact / consultation), not just mature
// ones. See the deal-evidence-chain in AgentEval for what each stage means.
const STAGE_DIST = [22, 20, 16, 16, 14, 12];
function pickStage(rng) {
  const tot = STAGE_DIST.reduce((a, b) => a + b, 0);
  let x = rng() * tot;
  for (let i = 0; i < STAGE_DIST.length; i++) { x -= STAGE_DIST[i]; if (x <= 0) return i + 1; }
  return STAGE_DIST.length;
}
// 6-grade opacity ramp keyed to deal stage (1 = early lead → 6 = closed). The
// house's shade IS its progress, so the graph legend reads as a stage gradient.
const STAGE_SHADE = [0.3, 0.44, 0.58, 0.72, 0.86, 1.0];
const NINETY_DAYS = 90 * 24 * 3600 * 1000; // MLS-verification window for a closed deal
// Fixed "today" so the demo reads like a frozen real dataset — the graph and the
// 90-day verification window don't drift as wall-clock time passes.
const DEMO_NOW_MS = Date.UTC(2026, 5, 5); // 2026-06-05
const DEMO_NOW = new Date(DEMO_NOW_MS);

const SPECIALIZATION = [
  { key: "precon", pct: 38, label: "Pre-construction" },
  { key: "luxury", pct: 27, label: "Luxury" },
  { key: "resale", pct: 25, label: "Re-sale" },
  { key: "investor", pct: 10, label: "Investor" },
];

// 12 months of grouped "houses" — buildHeatmap collapses these into the daily
// production grid. Each house carries a deal type + an events (signal) count.
const TIMELINE = (function () {
  const r = seeded(hashStr("sarahchen-timeline"));
  const pool = [];
  SPECIALIZATION.forEach((t) => { for (let i = 0; i < t.pct; i++) pool.push(t.key); });
  const months = [];
  for (let m = 0; m < 12; m++) {
    const season = 0.5 + 0.4 * Math.sin((m / 12) * Math.PI * 2 - 0.6);
    const houseCount = 2 + Math.floor(r() * 3);
    const houses = [];
    for (let h = 0; h < houseCount; h++) {
      const type = pool[Math.floor(r() * pool.length)];
      const events = Math.max(2, Math.round((4 + r() * 12) * (0.6 + season)));
      houses.push({ intensity: Math.min(4, Math.round(events / 4)), type, label: "", events, month: m, side: r() < 0.55 ? "seller" : "buyer" });
    }
    months.push(houses);
  }
  return months;
})();

const DATA = {
  generated_at: "2026-06-04T00:00:00.000Z",
  agent: { clerk_user_id: "demo", display_name: "Sarah Chen" },
  identity: {
    slug: "sarahchen",
    avatar_url: null,
    bio: "Works across downtown Toronto and the western GTA — portfolio skewed toward pre-construction and luxury re-sale condos. Seven years in market, deep builder network, top-quartile repeat rate.",
    brokerage: "Brokerage member",
    years_active: 7,
    languages: [
      { name: "English", primary: true },
      { name: "Mandarin", primary: false },
      { name: "Cantonese", primary: false },
    ],
  },
  profile_stats: {
    total_deals: 319,
    real_deals: 47,
    closed_deals: 10,
    active_deals: 31,
    multi_property_days: 167,
    listing_presentations: 22,
    counter_offer_rounds_total: 63,
    offer_prep_sessions_total: 73,
    misc_showings: 120,
    top_areas: [
      { city: "Toronto", deal_count: 21 },
      { city: "Mississauga", deal_count: 12 },
      { city: "Oakville", deal_count: 8 },
      { city: "Vaughan", deal_count: 6 },
    ],
    client_retention: { repeat_rate: 0.68 }, // all-time · linked across years
    avg_days_to_close: 34, // all-time · trailing 2 years
    listing_close_rate: 92, // all-time
    rep_mix: { listing: 55, buyer: 45 },
    avg_response_minutes: 108, // fallback; per-year value lives in byYear
  },
  specialization: SPECIALIZATION,
  areas_served: ["Toronto · 21", "Mississauga · 12", "Oakville · 8", "Vaughan · 6"],
  // Major deals only ever shows CLOSED deals (public/MLS-verifiable), so these
  // carry a closed_at. Deals closed > 90 days ago are MLS-verified; ~within 90
  // days are recently closed (no badge yet). A couple of active deals are kept
  // for the pipeline stat but never surface in Major deals.
  deals: [
    { deal_key: "183-yorkville", address_display: "183 Yorkville Ave · Unit 1604", city: "Toronto", side: "seller", status: "closed", deal_type: "luxury", closed_at: "2025-09-12" },
    { deal_key: "8-mercer", address_display: "8 Mercer St · Phase 1", city: "Toronto", side: "buyer", status: "closed", deal_type: "precon", closed_at: "2025-11-03" },
    { deal_key: "42-helene", address_display: "42 Helene St N", city: "Mississauga", side: "seller", status: "closed", deal_type: "resale", closed_at: "2026-01-20" },
    { deal_key: "70-temperance", address_display: "70 Temperance St · Unit 2208", city: "Toronto", side: "buyer", status: "closed", deal_type: "resale", closed_at: "2025-06-30" },
    { deal_key: "1500-lakeshore", address_display: "1500 Lakeshore Rd E", city: "Mississauga", side: "seller", status: "closed", deal_type: "luxury", closed_at: "2026-03-28" },
    { deal_key: "85-east-liberty", address_display: "85 East Liberty St · 3 units", city: "Toronto", side: "buyer", status: "active", deal_type: "investor" },
  ],
  performance_by_year: {
    years: [2026, 2025, 2024],
    primarySide: "listing",
    // Trailing window (the default "last 12 months" view) — distinct from any one
    // calendar year because it straddles two.
    trailing: { closed: 10, active: 31, response_minutes: 108 },
    // Per-calendar-year stats. Build-up: first year 2 deals, second year 5; 2026
    // is only Jan–Jun so far (6 closed), so its numbers differ from the trailing
    // window. Active is 0 for past (completed) years.
    byYear: {
      "2026": { year: 2026, closed: 6, active: 31, response_minutes: 108, period: "2026" },
      "2025": { year: 2025, closed: 5, active: 0, response_minutes: 132, period: "2025" },
      "2024": { year: 2024, closed: 2, active: 0, response_minutes: 165, period: "2024" },
    },
  },
  // Units kept proportionate to the precon share of actual closed deals (a
  // handful, not dozens). One project dropped vs. the earlier inflated list.
  precon_projects: [
    { name: "M City 2", area: "Mississauga · Burnhamthorpe Rd W", units: 2, lat: 43.5905, lng: -79.6440 },
    { name: "CentreCourt — Tower Series", area: "Toronto · downtown core", units: 2, lat: 43.6548, lng: -79.3805 },
    { name: "LeftBank · Regent Park", area: "Toronto · Regent Park", units: 1, lat: 43.6595, lng: -79.3610 },
    { name: "Pier House Towns", area: "Mississauga · Lakeshore Rd E", units: 1, lat: 43.5710, lng: -79.5610 },
  ],
  timeline: TIMELINE,
};

/* ====================================================================== */
/*  PRIMITIVES                                                              */
/* ====================================================================== */
function Mark({ size = 22 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 10 10" aria-hidden="true" style={{ display: "block" }}>
      <path d="M1 5 L5 1.3 L9 5 L9 9 L1 9 Z" fill="var(--ink)" />
      <rect x="4.2" y="6.2" width="1.6" height="2.8" fill="var(--bg)" />
    </svg>
  );
}

function Hairline({ vertical, color = "var(--hair)", opacity = 1, style }) {
  if (vertical) return <div style={{ width: 1, alignSelf: "stretch", background: color, opacity, ...style }} />;
  return <div style={{ height: 1, background: color, opacity, ...style }} />;
}

function Eyebrow({ children, style }) {
  return (
    <div className="mono" style={{ fontSize: 10, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 600, ...style }}>
      {children}
    </div>
  );
}

function SectionRule({ label, right }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 16, marginBottom: 14 }}>
      <Eyebrow style={{ whiteSpace: "nowrap" }}>{label}</Eyebrow>
      <Hairline style={{ flex: 1, opacity: 0.5 }} />
      {right && <div className="mono" style={{ fontSize: 10, color: "var(--mute)", letterSpacing: "0.08em" }}>{right}</div>}
    </div>
  );
}

function InfoDot({ text, align = "left" }) {
  const [open, setOpen] = useState(false);
  return (
    <span style={{ position: "relative", display: "inline-flex", verticalAlign: "middle" }}
      onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
      <button onClick={(e) => { e.stopPropagation(); setOpen((o) => !o); }} aria-label="What this means"
        style={{
          width: 14, height: 14, borderRadius: "50%", flexShrink: 0, padding: 0,
          border: `1px solid ${open ? "var(--ink)" : "var(--line-2)"}`,
          background: open ? "var(--ink)" : "var(--paper)", color: open ? "var(--paper)" : "var(--ink-3)",
          cursor: "pointer", display: "grid", placeItems: "center",
          fontFamily: "Newsreader, serif", fontStyle: "italic", fontSize: 10, lineHeight: 1, transition: "all 0.12s",
        }}>i</button>
      {open && (
        <span style={{
          position: "absolute", top: "calc(100% + 7px)", zIndex: 60,
          [align === "right" ? "right" : "left"]: 0, width: 208, textAlign: "left",
          background: "var(--paper)", border: "1px solid var(--ink)", padding: "9px 11px",
          boxShadow: "0 10px 30px -10px rgba(15,14,12,0.3)",
        }}>
          <span className="mono" style={{ fontSize: 10, lineHeight: 1.55, color: "var(--ink-2)", textTransform: "none", fontWeight: 400, display: "block" }}>
            {text}
          </span>
        </span>
      )}
    </span>
  );
}

function HeroStat({ value, label, sub, accent, size = "md", style, info, infoAlign }) {
  const valueSize = size === "lg" ? 56 : size === "sm" ? 30 : 38;
  return (
    <div style={{ minWidth: 0, ...style }}>
      <div style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: valueSize, lineHeight: 0.95, letterSpacing: "-0.04em", color: accent ? "var(--accent-ink)" : "var(--ink)", fontVariantNumeric: "tabular-nums" }}>{value}</div>
      <div className="mono" style={{ display: "flex", alignItems: "center", gap: 6, fontSize: size === "lg" ? 11 : 10, color: size === "lg" ? "var(--ink-2)" : "var(--ink-3)", marginTop: size === "lg" ? 8 : 6, letterSpacing: "0.12em", textTransform: "uppercase", fontWeight: 600 }}>
        {label}
        {info && <InfoDot text={info} align={infoAlign} />}
      </div>
      {sub && <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 2 }}>{sub}</div>}
    </div>
  );
}

function VerifiedPill() {
  return (
    <span className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 10, padding: "3px 8px", background: "var(--ink)", color: "var(--paper)", letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600 }}>
      <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.5"><path d="M5 12l5 5L20 7" /></svg>
      Verified
    </span>
  );
}

function Photo({ w = 150, h = 178, src }) {
  const ticks = [
    { top: 5, left: 5, borderTopWidth: 1, borderLeftWidth: 1 },
    { top: 5, right: 5, borderTopWidth: 1, borderRightWidth: 1 },
    { bottom: 5, left: 5, borderBottomWidth: 1, borderLeftWidth: 1 },
    { bottom: 5, right: 5, borderBottomWidth: 1, borderRightWidth: 1 },
  ];
  return (
    <div style={{
      width: w, height: h, position: "relative", flexShrink: 0, overflow: "hidden",
      border: "1px solid var(--hair)",
      background: "linear-gradient(155deg, color-mix(in oklab, var(--accent-tint-2) 60%, var(--paper-2)), var(--paper-2) 70%, color-mix(in oklab, var(--accent) 18%, var(--paper-2)))",
    }}>
      {src ? (
        <img src={src} alt="" width={w} height={h} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
      ) : (
        <svg viewBox="0 0 100 130" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", opacity: 0.85 }}>
          <defs>
            <linearGradient id="gportrait" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor="var(--ink-2)" stopOpacity="0.85" />
              <stop offset="100%" stopColor="var(--ink-2)" stopOpacity="0.35" />
            </linearGradient>
          </defs>
          <path d="M -10 130 Q -10 95, 20 90 Q 30 88, 35 80 Q 50 75, 65 80 Q 70 88, 80 90 Q 110 95, 110 130 Z" fill="url(#gportrait)" />
          <path d="M 40 92 Q 40 80, 42 76 L 58 76 Q 60 80, 60 92 Z" fill="url(#gportrait)" />
          <ellipse cx="50" cy="55" rx="18" ry="22" fill="url(#gportrait)" />
          <path d="M 32 50 Q 32 28, 50 28 Q 68 28, 68 50 Q 64 38, 50 38 Q 36 38, 32 50 Z" fill="var(--ink)" opacity="0.7" />
        </svg>
      )}
      {ticks.map((s, i) => (
        <div key={i} style={{ position: "absolute", width: 10, height: 10, borderColor: "var(--ink)", borderStyle: "solid", borderWidth: 0, ...s }} />
      ))}
    </div>
  );
}

const DETAIL_ICONS = {
  pin: <><path d="M12 21s-7-6.5-7-11a7 7 0 0 1 14 0c0 4.5-7 11-7 11z" /><circle cx="12" cy="10" r="2.5" /></>,
  clock: <><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" /></>,
  star: <><path d="M12 3l2.6 6.3 6.8.5-5.2 4.4 1.6 6.6L12 17.8 6.2 20.8l1.6-6.6L2.6 9.8l6.8-.5z" /></>,
  briefcase: <><rect x="3" y="7" width="18" height="13" rx="1.5" /><path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></>,
  link: <><path d="M10 13a5 5 0 0 0 7 0l2-2a5 5 0 0 0-7-7l-1 1" /><path d="M14 11a5 5 0 0 0-7 0l-2 2a5 5 0 0 0 7 7l1-1" /></>,
};

function DetailRow({ icon, children, link }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 10, fontSize: 13, color: link ? "var(--accent-ink)" : "var(--ink-2)" }}>
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--ink-3)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
        {DETAIL_ICONS[icon]}
      </svg>
      <span style={{ minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{children}</span>
    </div>
  );
}

function formatResponse(minutes) {
  if (minutes == null) return null;
  if (minutes < 90) return `${minutes}m`;
  return `${(minutes / 60).toFixed(1)}h`;
}
function formatRetention(stats) {
  if (!stats || stats.repeat_rate == null) return null;
  return `${Math.round(stats.repeat_rate * 100)}%`;
}

/* ====================================================================== */
/*  LEFT RAIL — identity                                                    */
/* ====================================================================== */
function ProfileSidebar({ data, showOwnerView }) {
  const id = data.identity;
  const name = data.agent.display_name;
  const handle = id && id.slug ? id.slug : "";
  const areas = (data.profile_stats.top_areas || []).slice(0, 3).map((a) => a.city);
  const url = handle ? `activusai.com/${handle}` : "activusai.com";

  // Editable profile fields — local state seeded from data, temporary (resets on
  // full refresh). Ported from the design handoff agentprofile.jsx ProfileSidebar.
  const [brokerage, setBrokerage] = useState((id && id.brokerage) || "");
  const [langs, setLangs] = useState((id && id.languages) || []);
  const [bio, setBio] = useState((id && id.bio) || "");
  const [editing, setEditing] = useState(false);
  const [draftBrok, setDraftBrok] = useState(brokerage);
  const [draftLangs, setDraftLangs] = useState(langs);
  const [draftBio, setDraftBio] = useState(bio);
  const [newLang, setNewLang] = useState("");
  const isEditing = editing && showOwnerView;
  const startEdit = () => { setDraftBrok(brokerage); setDraftLangs(langs.map((l) => ({ ...l }))); setDraftBio(bio); setNewLang(""); setEditing(true); };
  const cancel = () => setEditing(false);
  const save = () => { setBrokerage(draftBrok.trim() || brokerage); setLangs(draftLangs); setBio(draftBio.trim()); setEditing(false); };
  const addLang = () => {
    const v = newLang.trim();
    if (!v) return;
    if (draftLangs.some((l) => l.name.toLowerCase() === v.toLowerCase())) { setNewLang(""); return; }
    setDraftLangs((ls) => [...ls, { name: v, primary: ls.length === 0 }]);
    setNewLang("");
  };
  const removeLang = (nm) => setDraftLangs((ls) => {
    const next = ls.filter((l) => l.name !== nm).map((l) => ({ ...l }));
    if (next.length && !next.some((l) => l.primary)) next[0].primary = true;
    return next;
  });
  const setPrimary = (nm) => setDraftLangs((ls) => ls.map((l) => ({ ...l, primary: l.name === nm })));
  const shownLangs = isEditing ? draftLangs : langs;

  return (
    <aside style={{ padding: "20px 24px 26px", display: "flex", flexDirection: "column", gap: 12 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 2 }}>
        <Mark size={18} />
        <span className="mono" style={{ fontSize: 13, fontWeight: 600, letterSpacing: "-0.01em" }}>Activus</span>
      </div>
      <Photo w={150} h={178} src={id && id.avatar_url} />

      {showOwnerView && !isEditing && (
        <button onClick={startEdit} className="mono" style={{
          display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6,
          background: "var(--paper)", border: "1px solid var(--line-2)", color: "var(--ink-2)",
          padding: "8px 12px", fontSize: 10.5, letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer",
        }}>
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 20h9" /><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z" /></svg>
          Edit profile
        </button>
      )}
      {isEditing && (
        <div className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 7, alignSelf: "flex-start", fontSize: 9.5, padding: "5px 9px", color: "var(--accent-ink)", background: "var(--accent-tint)", border: "1px solid var(--accent)", letterSpacing: "0.12em", textTransform: "uppercase", fontWeight: 600 }}>
          <span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--accent)" }} />
          Editing profile
        </div>
      )}

      <div>
        <h1 style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 26, lineHeight: 1, letterSpacing: "-0.03em", margin: 0 }}>{name}</h1>
        {handle && <div className="mono" style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 4 }}>@{handle}</div>}
      </div>

      <div><VerifiedPill /></div>

      {isEditing ? (
        <div>
          <Eyebrow style={{ marginBottom: 6 }}>Bio</Eyebrow>
          <textarea value={draftBio} onChange={(e) => setDraftBio(e.target.value)} rows={5} placeholder="Short bio…" style={{ ...inputStyle, resize: "vertical", minHeight: 96, lineHeight: 1.4, fontFamily: "Newsreader, Georgia, serif" }} />
        </div>
      ) : (
        bio && <p className="news" style={{ margin: 0, fontSize: 14, lineHeight: 1.4, color: "var(--ink-2)" }}>{bio}</p>
      )}

      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        <ShareButton full name={name} url={url} />
        <ActionBtn full>Download PDF</ActionBtn>
      </div>

      <Hairline style={{ opacity: 0.4 }} />

      <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
        {areas.length > 0 && <DetailRow icon="pin">{areas.join(" · ")}</DetailRow>}
        {id && id.years_active != null && <DetailRow icon="clock">{id.years_active} years active</DetailRow>}
        {isEditing ? (
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--ink-3)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>{DETAIL_ICONS.briefcase}</svg>
            <input value={draftBrok} onChange={(e) => setDraftBrok(e.target.value)} placeholder="Brokerage" style={{ ...inputStyle, padding: "6px 8px", fontSize: 13 }} />
          </div>
        ) : (
          brokerage && <DetailRow icon="briefcase">{brokerage}</DetailRow>
        )}
        <DetailRow icon="link" link>{url}</DetailRow>
      </div>

      {(shownLangs.length > 0 || isEditing) && (
        <>
          <Hairline style={{ opacity: 0.4 }} />
          <div>
            <Eyebrow style={{ marginBottom: 10 }}>Languages</Eyebrow>
            {isEditing ? (
              <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
                <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                  {draftLangs.map(({ name: nm, primary }) => (
                    <span key={nm} className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 7, fontSize: 10, padding: "5px 7px 5px 9px", background: primary ? "var(--ink)" : "var(--paper)", color: primary ? "var(--paper)" : "var(--ink-2)", border: "1px solid var(--ink)", letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600 }}>
                      <button onClick={() => setPrimary(nm)} title="Set as primary" style={{ background: "none", border: "none", cursor: "pointer", padding: 0, color: "inherit", font: "inherit", letterSpacing: "inherit" }}>{nm}</button>
                      <button onClick={() => removeLang(nm)} aria-label={"Remove " + nm} style={{ background: "none", border: "none", cursor: "pointer", padding: 0, color: "inherit", opacity: 0.65, fontSize: 13, lineHeight: 1 }}>×</button>
                    </span>
                  ))}
                </div>
                <div style={{ display: "flex", gap: 6 }}>
                  <input value={newLang} onChange={(e) => setNewLang(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); addLang(); } }} placeholder="Add a language…" style={{ ...inputStyle, flex: 1, padding: "6px 8px", fontSize: 12 }} />
                  <button onClick={addLang} className="mono" style={{ background: "var(--paper)", color: "var(--ink)", border: "1px solid var(--ink)", padding: "0 12px", fontSize: 10, letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer", whiteSpace: "nowrap" }}>Add</button>
                </div>
                <span className="mono" style={{ fontSize: 9.5, color: "var(--mute)", letterSpacing: "0.04em" }}>Click a language to set it as primary.</span>
              </div>
            ) : (
              <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                {shownLangs.map((l) => (
                  <span key={l.name} className="mono" style={{ fontSize: 10, padding: "5px 9px", background: l.primary ? "var(--ink)" : "var(--paper)", color: l.primary ? "var(--paper)" : "var(--ink-2)", border: "1px solid var(--ink)", letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600 }}>{l.name}</span>
                ))}
              </div>
            )}
          </div>
        </>
      )}

      {isEditing && (
        <div style={{ display: "flex", gap: 8 }}>
          <button onClick={save} className="mono" style={{ flex: 1, background: "var(--ink)", color: "var(--paper)", border: "1px solid var(--ink)", padding: "10px 12px", fontSize: 11, letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer" }}>Save</button>
          <button onClick={cancel} className="mono" style={{ flex: 1, background: "var(--paper)", color: "var(--ink)", border: "1px solid var(--line-2)", padding: "10px 12px", fontSize: 11, letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer" }}>Cancel</button>
        </div>
      )}
    </aside>
  );
}

/* ====================================================================== */
/*  STATS BAND                                                              */
/* ====================================================================== */
const STAT_INFO = {
  retention: "Share of past clients who came back or referred someone new. All-time — linked across years, not reset per year.",
  closed: "Deals that reached a firm, completed closing during this period.",
  avgDays: "Median days from signed representation to a firm/closed deal. All-time, smoothed over the trailing two years.",
  active: "Live deals currently in progress — signed but not yet closed or fallen through.",
  listingClose: "Of every listing this agent took on, the share that sold with them rather than going cold. All-time.",
  response: "Median time for this agent to send a first reply to a new client message.",
};

// Three stats are tracked all-time across the whole profile (client retention,
// avg days to close [trailing 2yr], listing close rate). Everything else
// (closed, active, response) is per the selected year / trailing window.
function StatsBand({ data, year, yearView }) {
  const perf = data.performance_by_year;
  const yp = perf && perf.byYear[String(year)];
  const ps = data.profile_stats;
  const isTrailing = !yearView;
  // Per-window stats: trailing-12 block on first load, else the selected year.
  const stat = (isTrailing && perf && perf.trailing) ? perf.trailing : (yp || {});
  const retention = formatRetention(ps.client_retention); // all-time
  const avgDays = ps.avg_days_to_close; // all-time
  const listingClose = ps.listing_close_rate; // all-time
  const response = formatResponse(stat.response_minutes != null ? stat.response_minutes : ps.avg_response_minutes); // per window
  const period = isTrailing ? "trailing 12 months" : (yp && yp.period) || String(year);
  const closedLabel = isTrailing ? "Closed deals · 12mo" : `Closed deals · ${year}`;

  return (
    <section style={{ padding: "14px 32px 12px", borderBottom: "1px solid var(--hair)" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 14 }}>
        <Eyebrow>{isTrailing ? "Performance · trailing 12 months" : `Performance · ${year} full year`}</Eyebrow>
        <Hairline style={{ flex: 1, opacity: 0.35 }} />
        <span className="mono" style={{ fontSize: 10, color: "var(--mute)" }}>{period}</span>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", alignItems: "end" }}>
        <HeroStat value={retention || "—"} label="Client retention" sub="all-time · linked" size="lg" info={STAT_INFO.retention} />
        <HeroStat value={String(stat.closed || 0)} label={closedLabel} sub={isTrailing ? "last 12 months" : "full year"} size="sm" style={{ paddingLeft: 14 }} info={STAT_INFO.closed} />
        <HeroStat value={avgDays != null ? String(avgDays) : "—"} label="Avg days to close" sub="all-time · 2-yr" size="sm" accent style={{ paddingLeft: 14 }} info={STAT_INFO.avgDays} infoAlign="right" />
      </div>

      <div style={{ marginTop: 12, display: "flex", border: "1px solid var(--hair)" }}>
        {[
          { v: String(ps.active_deals != null ? ps.active_deals : (stat.active || 0)), l: "Active deals", info: STAT_INFO.active, align: "left" },
          { v: listingClose != null ? `${listingClose}%` : "—", l: "Listing close rate", info: STAT_INFO.listingClose, align: "left", allTime: true },
          { v: response || "—", l: "Avg response time", info: STAT_INFO.response, align: "right" },
        ].map((s, i) => (
          <div key={s.l} style={{ flex: 1, padding: "9px 14px", borderLeft: i > 0 ? "1px solid var(--line)" : "none", display: "flex", alignItems: "baseline", gap: 9 }}>
            <span style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 20, letterSpacing: "-0.02em", fontVariantNumeric: "tabular-nums" }}>{s.v}</span>
            <span className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 10, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 600 }}>
              {s.l}{s.allTime && <span style={{ color: "var(--mute)", fontSize: 8 }}>· all-time</span>}<InfoDot text={s.info} align={s.align} />
            </span>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ====================================================================== */
/*  SPLIT BARS                                                              */
/* ====================================================================== */
// Representation split — clickable filter by side (listing/buyer), usable at the
// same time as the specialization (type) filter.
function RepSplitBar({ repMix, filter, toggle }) {
  const [hov, setHov] = useState(false);
  const expanded = hov || filter.side !== null;
  const segs = [
    { key: "listing", label: "Listing-side", pct: repMix.listing, color: "var(--coral)" },
    { key: "buyer", label: "Buyer-side", pct: repMix.buyer, color: "var(--teal)" },
  ];
  return (
    <div style={{ width: 200, maxWidth: "100%" }} onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}>
      <Eyebrow style={{ marginBottom: 6 }}>Representation</Eyebrow>
      <div style={{ display: "flex", height: expanded ? 11 : 2, cursor: "pointer", transition: "height 0.18s ease", border: expanded ? "1px solid var(--line-2)" : "none" }}>
        {segs.map((s, i) => {
          const active = filter.side === s.key;
          const dim = filter.side && !active;
          return (
            <div key={s.key} onClick={() => toggle("side", s.key)} style={{ width: `${s.pct}%`, background: s.color, borderLeft: i > 0 && expanded ? "1px solid var(--paper)" : "none", opacity: dim ? 0.28 : 1, display: "flex", alignItems: "center", justifyContent: "center", transition: "opacity 0.15s", boxShadow: active && expanded ? "inset 0 0 0 2px var(--paper)" : "none" }}>
              {expanded && s.pct >= 20 && <span className="mono" style={{ fontSize: 7, fontWeight: 700, color: "var(--paper)" }}>{s.pct}%</span>}
            </div>
          );
        })}
      </div>
      <div style={{ maxHeight: expanded ? 22 : 0, opacity: expanded ? 1 : 0, overflow: "hidden", transition: "all 0.18s ease", marginTop: expanded ? 6 : 0 }}>
        <div style={{ display: "flex", justifyContent: "space-between" }}>
          {segs.map((s) => {
            const active = filter.side === s.key;
            const dim = filter.side && !active;
            return (
              <button key={s.key} onClick={() => toggle("side", s.key)} className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 5, background: "none", border: "none", cursor: "pointer", padding: 0, opacity: dim ? 0.4 : 1, fontSize: 9.5, letterSpacing: "0.06em", textTransform: "uppercase", fontWeight: 600, color: active ? "var(--ink)" : "var(--ink-2)" }}>
                <span style={{ width: 9, height: 4, background: s.color }} />{s.label}
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}

function TypeSplitBar({ specialization, filter, toggle }) {
  const [hov, setHov] = useState(false);
  const expanded = hov || filter.type !== null;
  return (
    <div style={{ width: 360, maxWidth: "100%" }} onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}>
      <Eyebrow style={{ marginBottom: 6 }}>Specialization mix</Eyebrow>
      <div style={{ display: "flex", height: expanded ? 11 : 2, cursor: "pointer", transition: "height 0.18s ease", border: expanded ? "1px solid var(--line-2)" : "none" }}>
        {specialization.map((t, i) => {
          const active = filter.type === t.key;
          const dim = filter.type && !active;
          return (
            <div key={t.key} onClick={() => toggle("type", t.key)} style={{
              width: `${t.pct}%`, background: `var(--${t.key})`, borderLeft: i > 0 && expanded ? "1px solid var(--paper)" : "none",
              opacity: dim ? 0.28 : 1, display: "flex", alignItems: "center", justifyContent: "center", transition: "opacity 0.15s",
              boxShadow: active && expanded ? "inset 0 0 0 2px var(--paper)" : "none",
            }}>
              {expanded && t.pct >= 18 && <span className="mono" style={{ fontSize: 7, fontWeight: 700, color: "var(--paper)" }}>{t.pct}%</span>}
            </div>
          );
        })}
      </div>
      <div style={{ maxHeight: expanded ? 40 : 0, opacity: expanded ? 1 : 0, overflow: "hidden", transition: "all 0.18s ease", marginTop: expanded ? 6 : 0 }}>
        <div style={{ display: "flex", flexWrap: "wrap", gap: "5px 12px" }}>
          {specialization.map((t) => {
            const active = filter.type === t.key;
            const dim = filter.type && !active;
            return (
              <button key={t.key} onClick={() => toggle("type", t.key)} className="mono" style={{
                display: "inline-flex", alignItems: "center", gap: 5, background: "none", border: "none", cursor: "pointer",
                padding: 0, opacity: dim ? 0.4 : 1, fontSize: 9.5, letterSpacing: "0.06em", textTransform: "uppercase",
                fontWeight: 600, color: active ? "var(--ink)" : "var(--ink-2)", whiteSpace: "nowrap",
              }}>
                <span style={{ width: 9, height: 4, background: `var(--${t.key})` }} />
                {t.label}<span style={{ opacity: 0.6, marginLeft: 4 }}>{t.pct}%</span>
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}

/* ====================================================================== */
/*  ACTIVITY HEATMAP                                                        */
/* ====================================================================== */
// mode "trailing" → 52 weeks ending today (default last-12-months view).
// mode "year"     → calendar Jan→Dec of opts.year; future days (current year)
//                   render empty. opts.activity scales overall density so past
//                   build-up years (fewer deals) look sparser.
function buildHeatmap(timeline, seedStr, opts) {
  opts = opts || {};
  const mode = opts.mode || "trailing";
  const activity = opts.activity == null ? 1 : opts.activity;
  const r = seeded(hashStr(seedStr || "activus"));
  const months = (timeline || []).map((houses) => {
    const events = houses.reduce((a, h) => a + (h.events || 0), 0);
    const byType = new Map();
    for (const h of houses) byType.set(h.type, (byType.get(h.type) || 0) + (h.events || 1));
    const types = [...byType.entries()].sort((a, b) => b[1] - a[1]);
    return { events, types };
  });
  const maxEvents = Math.max(1, ...months.map((m) => m.events));
  const pickType = (types) => {
    if (!types.length) return "resale";
    const total = types.reduce((a, t) => a + t[1], 0);
    let x = r() * total;
    for (const [t, c] of types) { x -= c; if (x <= 0) return t; }
    return types[0][0];
  };
  const fmt = (d) => d.toLocaleDateString("en-US", { month: "long", day: "numeric", timeZone: "UTC" });

  const todayUTC = DEMO_NOW_MS;
  let start, WEEKS;
  if (mode === "year") {
    WEEKS = 53;
    start = new Date(Date.UTC(opts.year, 0, 1));
    const dow = (start.getUTCDay() + 6) % 7; // align to the Monday on/before Jan 1
    start.setUTCDate(start.getUTCDate() - dow);
  } else {
    WEEKS = 52;
    start = new Date(DEMO_NOW_MS);
    start.setUTCDate(start.getUTCDate() - (WEEKS * 7 - 1));
  }

  const weeks = [];
  let activeDays = 0;
  for (let w = 0; w < WEEKS; w++) {
    const days = [];
    for (let d = 0; d < 7; d++) {
      const dt = new Date(start);
      dt.setUTCDate(start.getUTCDate() + w * 7 + d);
      let monthIdx, inRange = true;
      if (mode === "year") {
        const dUTC = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
        if (dt.getUTCFullYear() !== opts.year || dUTC > todayUTC) inRange = false;
        monthIdx = dt.getUTCMonth();
      } else {
        monthIdx = Math.min(months.length - 1, Math.floor((w / WEEKS) * Math.max(1, months.length)));
      }
      const m = months[monthIdx] || { events: 0, types: [] };
      const density = (0.2 + 0.7 * (m.events / maxEvents)) * activity;
      const weekend = d >= 5;
      const base = weekend ? Math.min(0.95, density * 1.25) : density;
      if (inRange && m.events > 0 && r() < base) {
        const type = pickType(m.types);
        const weight = (weekend ? 0.6 : 0.4) + r() * 0.4;
        const stage = pickStage(r);
        const dayUTC = Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate());
        // A closed deal (stage 6) becomes MLS-verified once it's past the ~90-day
        // closing window; more recent closings just don't carry the badge yet.
        const verified = stage === 6 && (todayUTC - dayUTC) > NINETY_DAYS;
        days.push({ active: true, type, side: r() < 0.55 ? "listing" : "buyer", stage, verified, weight, shade: STAGE_SHADE[stage - 1], signals: Math.round(weight * 8 + 3), date: fmt(dt), iso: dt.toISOString().slice(0, 10) });
        activeDays++;
      } else {
        days.push({ active: false, date: fmt(dt) });
      }
    }
    weeks.push(days);
  }
  return { weeks, activeDays };
}

const HEAT_DOW = { 0: "M", 2: "W", 5: "S", 6: "S" };
const HEAT_PATH = "M0.7 3.5 L5 0.9 L9.3 3.5 L9.3 9.3 L0.7 9.3 Z";

function ContributionHeatmap({ weeks, monthLabels, filter, onPick }) {
  const GAP = 1, DAYW = 13, COLGAP = 5;
  const [hover, setHover] = useState(null);
  const fieldRef = useRef(null);
  const showHover = (e, signals, date) => {
    if (!fieldRef.current) return;
    const wrap = fieldRef.current.getBoundingClientRect();
    const cell = e.currentTarget.getBoundingClientRect();
    setHover({ x: cell.left - wrap.left + cell.width / 2, y: cell.top - wrap.top, signals, date });
  };
  return (
    <div style={{ width: "100%", border: "1px solid var(--ink)", padding: "16px 18px", background: "#fff" }}>
      <div style={{ position: "relative", height: 18, marginLeft: DAYW + COLGAP, marginBottom: 5 }}>
        {monthLabels.map((m, i) => (
          <span key={i} className="mono" style={{ position: "absolute", top: 0, left: `${(i / monthLabels.length) * 100}%`, fontSize: 11, letterSpacing: "0.05em", color: "var(--ink-2)", fontWeight: 600 }}>{m}</span>
        ))}
      </div>
      <div style={{ display: "flex", alignItems: "stretch", gap: COLGAP }}>
        <div style={{ width: DAYW, flexShrink: 0, display: "grid", gridTemplateRows: "repeat(7, 1fr)", gap: GAP }}>
          {[0, 1, 2, 3, 4, 5, 6].map((d) => (
            <span key={d} className="mono" style={{ display: "flex", alignItems: "flex-end", paddingBottom: 1, fontSize: 10, lineHeight: 1, fontWeight: 700, color: d >= 5 ? "var(--accent-ink)" : "var(--mute)" }}>{HEAT_DOW[d] || ""}</span>
          ))}
        </div>
        <div ref={fieldRef} style={{ position: "relative", flex: 1, minWidth: 0, display: "grid", gridTemplateColumns: `repeat(${weeks.length}, minmax(0, 1fr))`, gridTemplateRows: "repeat(7, auto)", gridAutoFlow: "column", gap: GAP }}>
          {weeks.map((wk, wi) => wk.map((c, di) => {
            const key = `${wi}-${di}`;
            const box = { width: "100%", aspectRatio: "1 / 1.35" };
            if (!c.active) {
              return (
                <div key={key} className="ai-heat-cell" onMouseEnter={(e) => showHover(e, 0, c.date)} onMouseLeave={() => setHover(null)} style={{ ...box, cursor: "default" }}>
                  <svg viewBox="0 0 10 10" preserveAspectRatio="xMidYMax meet" style={{ display: "block", width: "100%", height: "100%" }}>
                    <path d={HEAT_PATH} fill="#C4C3BF" opacity="0.7" />
                  </svg>
                </div>
              );
            }
            const dimmed = (filter.type && c.type !== filter.type) || (filter.side && c.side !== filter.side);
            return (
              <div key={key} className="ai-heat-cell"
                onClick={(e) => { if (!dimmed && onPick) { setHover(null); onPick(e, key, c); } }}
                onMouseEnter={(e) => !dimmed && showHover(e, c.signals || 0, c.date)} onMouseLeave={() => setHover(null)}
                style={{ ...box, cursor: dimmed ? "default" : "pointer", opacity: dimmed ? 0.12 : (c.shade || c.weight), transition: "opacity 0.15s" }}>
                <svg viewBox="0 0 10 10" preserveAspectRatio="xMidYMax meet" style={{ display: "block", width: "100%", height: "100%" }}>
                  <path d={HEAT_PATH} fill={`var(--${c.type})`} />
                </svg>
              </div>
            );
          }))}
          {hover && (
            <div style={{ position: "absolute", left: hover.x, top: hover.y, transform: "translate(-50%, -100%)", marginTop: -7, pointerEvents: "none", zIndex: 20, whiteSpace: "nowrap" }}>
              <div className="mono" style={{ background: "var(--ink)", color: "var(--paper)", fontSize: 10.5, fontWeight: 500, padding: "5px 9px", lineHeight: 1.2 }}>
                {hover.signals > 0 ? <><b style={{ fontWeight: 700 }}>{hover.signals}</b> signals · {hover.date}</> : <>No signals on {hover.date}</>}
              </div>
              <div style={{ width: 0, height: 0, margin: "0 auto", borderLeft: "5px solid transparent", borderRight: "5px solid transparent", borderTop: "5px solid var(--ink)" }} />
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// Click-to-open detail on a heatmap house (ported from the design's profile.jsx
// PickedDetail) — shown in both agent and client views.
function MlsVerifiedBadge({ onClick, style }) {
  return (
    <span onClick={onClick} title="Closing confirmed against MLS" className="mono" style={{
      display: "inline-flex", alignItems: "center", gap: 5, fontSize: 8.5, padding: "3px 7px",
      background: "var(--good-tint)", color: "var(--good-ink)", border: "1px solid var(--good)",
      letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 700, whiteSpace: "nowrap",
      cursor: onClick ? "pointer" : "default", ...style,
    }}>
      <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.6"><path d="M12 3l8 3v6c0 5-8 9-8 9s-8-4-8-9V6z" /><path d="M9 12l2 2 4-4" /></svg>
      Verified on MLS
    </span>
  );
}

function PickedDetail({ picked, onClose }) {
  const typeLabel = TYPE_LABEL[picked.type] || picked.type;
  return (
    <div style={{
      position: "absolute", left: picked.x, top: picked.y, transform: "translateX(-50%)",
      zIndex: 20, width: 300, background: "var(--paper)", border: "1px solid var(--ink)",
      boxShadow: "0 16px 40px -12px rgba(15,14,12,0.45)",
    }}>
      <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10, padding: "12px 14px 10px", borderBottom: "1px solid var(--line)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, minWidth: 0 }}>
          <div style={{ display: "grid", placeItems: "center", width: 30, height: 30, background: `var(--${picked.type})`, flexShrink: 0 }}>
            <svg width="15" height="15" viewBox="0 0 10 10"><path d="M1 5 L5 1.3 L9 5 L9 9 L1 9 Z" fill="var(--paper)" /></svg>
          </div>
          <div style={{ minWidth: 0 }}>
            <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 14, letterSpacing: "-0.01em", lineHeight: 1.1 }}>{typeLabel} engagement</div>
            <div className="mono" style={{ fontSize: 9.5, color: "var(--mute)", letterSpacing: "0.06em", textTransform: "uppercase", marginTop: 2 }}>
              {picked.side === "listing" ? "Listing" : "Buyer"}-side
            </div>
          </div>
        </div>
        <button onClick={onClose} aria-label="close" style={{ background: "none", border: "none", cursor: "pointer", color: "var(--ink-3)", fontSize: 17, lineHeight: 1, padding: 0 }}>×</button>
      </div>
      <div style={{ padding: "12px 14px" }}>
        <StageProgress side={picked.side} stage={picked.stage} />
        {picked.verified && <div style={{ marginTop: 9 }}><MlsVerifiedBadge /></div>}
        <div className="mono" style={{ fontSize: 10, color: "var(--ink-3)", marginTop: 8, lineHeight: 1.5 }}>
          {picked.signals} cross-source signals · confidence {(0.84 + picked.weight * 0.12).toFixed(2)} · generalized for privacy
        </div>
      </div>
    </div>
  );
}

// Deal lifecycle stages, side-aware (listing vs buyer). Used by both popups and
// the AgentEval evidence chain. Mapped to OREA forms / email + calendar signals
// in buildDealChain() below.
const LISTING_STAGES = ["First contact", "Listing presentation", "Active client", "Listing live", "Offer period", "Closing"];
const BUYER_STAGES = ["First contact", "Buyer consultation", "Active client", "Showings", "Offer submitted", "Closing"];
function stageLabels(side) { return side === "buyer" ? BUYER_STAGES : LISTING_STAGES; }

// Shared progress bar + current-stage label. Visible in both agent and client
// popups (client just omits the address / last-activity context).
function StageProgress({ side, stage }) {
  const labels = stageLabels(side);
  const idx = Math.max(0, Math.min(labels.length - 1, (stage || 1) - 1));
  return (
    <>
      <div className="mono" style={{ fontSize: 9, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 600, marginBottom: 8 }}>Deal progress</div>
      <div style={{ display: "flex", gap: 4, marginBottom: 8 }}>
        {labels.map((st, i) => (
          <div key={st} style={{ flex: 1, height: 4, background: i <= idx ? "var(--accent)" : "var(--line)" }} />
        ))}
      </div>
      <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8 }}>
        <span style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 14 }}>{labels[idx]}</span>
        <span className="mono" style={{ fontSize: 10, color: "var(--mute)" }}>stage {idx + 1}/{labels.length}</span>
      </div>
    </>
  );
}

// Agent-view popup (ported from agentprofile.jsx DealPopup): adds the deal
// address, last activity, and the "View in AgentEval" entry on top of the shared
// progress bar. The client popup (PickedDetail) shows the same bar, generalized.
const DEAL_STREETS = [
  "Yonge St", "Bathurst St", "Lakeshore Rd E", "Burnhamthorpe Rd W", "Dundas St W",
  "Bloor St W", "Eglinton Ave E", "Queen St E", "Bayview Ave", "Trafalgar Rd",
  "Hurontario St", "Royal York Rd", "Avenue Rd", "Steeles Ave W", "Britannia Rd E",
];
const DEAL_CITIES = ["Toronto", "Mississauga", "Oakville", "Milton", "Etobicoke"];
function dealAddress(picked) {
  let h = 0;
  const s = String(picked.key || "") + (picked.date || "");
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  const num = 10 + (h % 240);
  const street = DEAL_STREETS[h % DEAL_STREETS.length];
  const city = DEAL_CITIES[(h >> 4) % DEAL_CITIES.length];
  return { line: `${num} ${street}`, city };
}
function AgentDealPopup({ picked, onClose, onOpenEval }) {
  const typeLabel = TYPE_LABEL[picked.type] || picked.type;
  // A focused deal (from a Major-deals badge) carries its real address; an
  // arbitrary clicked house gets a stable synthesized one.
  const addr = picked.address ? { line: picked.address, city: picked.city } : dealAddress(picked);
  const openDeal = () => onOpenEval({
    address: addr.line, city: addr.city, type: picked.type, side: picked.side,
    stage: picked.stage || 1, date: picked.date, iso: picked.iso, signals: picked.signals, weight: picked.weight,
  });
  return (
    <div style={{
      position: "absolute", left: picked.x, top: picked.y, transform: "translateX(-50%)",
      zIndex: 20, width: 300, background: "var(--paper)", border: "1px solid var(--ink)",
      boxShadow: "0 16px 40px -12px rgba(15,14,12,0.45)",
    }}>
      <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10, padding: "12px 14px 10px", borderBottom: "1px solid var(--line)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, minWidth: 0 }}>
          <div style={{ display: "grid", placeItems: "center", width: 30, height: 30, background: `var(--${picked.type})`, flexShrink: 0 }}>
            <svg width="15" height="15" viewBox="0 0 10 10"><path d="M1 5 L5 1.3 L9 5 L9 9 L1 9 Z" fill="var(--paper)" /></svg>
          </div>
          <div style={{ minWidth: 0 }}>
            <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 14, letterSpacing: "-0.01em", lineHeight: 1.1 }}>{addr.line}</div>
            <div className="mono" style={{ fontSize: 9.5, color: "var(--mute)", letterSpacing: "0.06em", textTransform: "uppercase", marginTop: 2 }}>
              {typeLabel} · {picked.side === "listing" ? "Listing" : "Buyer"}-side
            </div>
          </div>
        </div>
        <button onClick={onClose} aria-label="close" style={{ background: "none", border: "none", cursor: "pointer", color: "var(--ink-3)", fontSize: 17, lineHeight: 1, padding: 0 }}>×</button>
      </div>
      <div style={{ padding: "12px 14px" }}>
        <StageProgress side={picked.side} stage={picked.stage} />
        {picked.verified && <div style={{ marginTop: 9 }}><MlsVerifiedBadge /></div>}
        <div className="mono" style={{ fontSize: 10, color: "var(--ink-3)", marginTop: 6, lineHeight: 1.5 }}>
          {Math.round(picked.weight * 6 + 2)} email exchanges · last activity {picked.date}
        </div>
        <button onClick={openDeal} className="mono" style={{
          marginTop: 12, width: "100%", display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 7,
          background: "var(--ink)", color: "var(--paper)", border: "1px solid var(--ink)",
          padding: "9px 12px", fontSize: 10.5, letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer",
        }}>
          View in AgentEval
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M7 17L17 7M17 7H8M17 7v9"/></svg>
        </button>
      </div>
    </div>
  );
}

const YEAR_MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

function ActivityGrid({ data, filter, toggle, year, yearView, selectYear, showOwnerView, onOpenEval, focusDeal }) {
  const years = (data.performance_by_year && data.performance_by_year.years) || [];
  const currentYear = years[0] || year;
  const seed = (data.identity && data.identity.slug) || data.agent.display_name;
  // Trailing 12-month view by default; a clicked year shows that calendar year,
  // with density scaled by the year's deal volume (build-up years look sparser).
  const heat = useMemo(() => {
    if (!yearView) return buildHeatmap(data.timeline, seed, { mode: "trailing", activity: 1 });
    const yp = data.performance_by_year && data.performance_by_year.byYear[String(year)];
    const closed = (yp && yp.closed) || 0;
    // Current year is busy but partial (Jan→today); past build-up years scale by
    // their deal volume so they look progressively sparser.
    const activity = year === currentYear ? 0.95 : Math.max(0.14, Math.min(1, closed / 10));
    return buildHeatmap(data.timeline, seed + "-" + year, { mode: "year", year, activity });
  }, [data, yearView, year]);
  const specialization = data.specialization || [];
  const monthLabels = yearView ? YEAR_MONTHS : MONTHS.map((m) => m.slice(0, 3));
  const [picked, setPicked] = useState(null);
  const gridRef = useRef(null);
  useEffect(() => { setPicked(null); }, [yearView, year]);
  // Focus a specific closed deal (from a Major-deals badge): anchor a popup on
  // its month column and scroll the graph into view.
  useEffect(() => {
    if (!focusDeal || !gridRef.current) return;
    const d = focusDeal.deal;
    const dt = d.closed_at ? new Date(d.closed_at + "T00:00:00Z") : new Date();
    const month = dt.getUTCMonth();
    const wrap = gridRef.current.getBoundingClientRect();
    const x = Math.max(150, Math.min(wrap.width - 150, ((month + 0.5) / 12) * wrap.width));
    setPicked({
      key: "focus-" + d.deal_key + "-" + focusDeal.nonce, x, y: Math.max(40, wrap.height * 0.42),
      type: d.deal_type || "resale", side: d.side === "buyer" ? "buyer" : "listing",
      stage: 6, verified: true, weight: 0.9, signals: 14,
      date: dt.toLocaleDateString("en-US", { month: "long", day: "numeric", timeZone: "UTC" }),
      iso: d.closed_at, address: d.address_display, city: d.city,
    });
    gridRef.current.scrollIntoView({ behavior: "smooth", block: "center" });
  }, [focusDeal]);
  const handlePick = (e, key, c) => {
    if (picked && picked.key === key) { setPicked(null); return; }
    const wrap = gridRef.current.getBoundingClientRect();
    const cell = e.currentTarget.getBoundingClientRect();
    const W = 300;
    let x = cell.left - wrap.left + cell.width / 2;
    x = Math.max(W / 2, Math.min(wrap.width - W / 2, x));
    const y = cell.top - wrap.top + cell.height + 6;
    setPicked({ key, x, y, type: c.type, side: c.side, stage: c.stage, verified: c.verified, weight: c.weight, signals: c.signals, date: c.date, iso: c.iso });
  };

  // First load shows the trailing 12 months with the current year highlighted;
  // clicking any year switches to that calendar year + its per-year stats.
  const railItems = years.map((y) => ({ key: String(y), label: String(y), year: y }));

  return (
    <section style={{ padding: "20px 18px 18px", marginLeft: -44 }} className="ai-grid-shift">
      <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 16, marginBottom: 18, flexWrap: "wrap" }}>
        <div>
          <Eyebrow style={{ marginBottom: 9 }}>Production history</Eyebrow>
          <div style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 34, letterSpacing: "-0.035em", lineHeight: 0.95 }}>
            {heat.activeDays} <span style={{ fontWeight: 600 }}>active days</span>{" "}
            <span style={{ color: "var(--mute)", fontWeight: 500, fontSize: 19 }}>· {yearView ? year : "last 12 months"}</span>
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 14, paddingBottom: 4 }}>
          <span className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 9.5, color: "var(--mute)", letterSpacing: "0.08em" }}>
            LEAD{STAGE_SHADE.map((o, i) => <span key={i} style={{ width: 9, height: 9, background: "var(--ink)", opacity: o }} />)}CLOSED
          </span>
          <span className="mono" style={{ fontSize: 10, color: "var(--mute)", letterSpacing: "0.04em" }}>click any house</span>
        </div>
      </div>

      <div style={{ display: "flex", gap: 20, alignItems: "stretch", marginTop: 14 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          {(specialization.length > 0 || data.profile_stats.rep_mix) && (
            <div style={{ display: "flex", flexWrap: "wrap", alignItems: "flex-start", gap: 28, marginBottom: 14 }}>
              {data.profile_stats.rep_mix && <RepSplitBar repMix={data.profile_stats.rep_mix} filter={filter} toggle={toggle} />}
              {specialization.length > 0 && <TypeSplitBar specialization={specialization} filter={filter} toggle={toggle} />}
            </div>
          )}
          <div ref={gridRef} style={{ position: "relative" }}>
            <ContributionHeatmap weeks={heat.weeks} monthLabels={monthLabels} filter={filter} onPick={handlePick} />
            {picked && (showOwnerView
              ? <AgentDealPopup picked={picked} onClose={() => setPicked(null)} onOpenEval={onOpenEval} />
              : <PickedDetail picked={picked} onClose={() => setPicked(null)} />
            )}
          </div>
        </div>

        <div role="tablist" aria-label="Production year" style={{ display: "flex", flexDirection: "column", gap: 2, width: 44, flexShrink: 0 }}>
          {railItems.map((it) => {
            const on = it.year === year;
            return (
              <button key={it.key} role="tab" aria-selected={on} onClick={() => selectYear(it.year)} className="mono" style={{
                appearance: "none", cursor: "pointer", textAlign: "left", padding: "8px 10px", fontSize: 11.5, fontWeight: 600, letterSpacing: "0.06em",
                color: on ? "var(--accent-ink)" : "var(--ink-3)", background: on ? "var(--accent-tint-2)" : "transparent",
                border: on ? "1px solid var(--accent-tint-2)" : "1px solid transparent", borderRadius: 7, transition: "all 0.12s",
                display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6,
              }}>
                {it.label}{it.year === currentYear && <span style={{ width: 5, height: 5, borderRadius: "50%", background: on ? "var(--accent)" : "var(--line-2)" }} />}
              </button>
            );
          })}
        </div>
      </div>
    </section>
  );
}

/* ====================================================================== */
/*  INSIGHTS — regions donut + major deals                                  */
/* ====================================================================== */
const CITY_COLORS = ["#5B9BD5", "#7FD6D0", "#F5D96B", "#F0936B", "#D96BAE"];

function MostActive({ areas }) {
  const top = areas.slice(0, 4);
  const total = top.reduce((a, r) => a + r.deal_count, 0) || 1;
  const R = 42, C = 2 * Math.PI * R;
  const lens = top.map((r) => (r.deal_count / total) * C);
  const segs = top.map((r, i) => {
    const off = -lens.slice(0, i).reduce((a, b) => a + b, 0);
    return { ...r, color: CITY_COLORS[i % CITY_COLORS.length], pct: Math.round((r.deal_count / total) * 100), dash: lens[i], gap: C - lens[i], off };
  });
  return (
    <div>
      <SectionRule label="Most active in" right="all years" />
      <div style={{ display: "flex", alignItems: "center", gap: 24 }}>
        <svg width="120" height="120" viewBox="0 0 116 116" style={{ transform: "rotate(-90deg)", flexShrink: 0 }}>
          {segs.map((s) => (
            <circle key={s.city} cx="58" cy="58" r={R} fill="none" stroke={s.color} strokeWidth="20" strokeDasharray={`${s.dash} ${s.gap}`} strokeDashoffset={s.off}>
              <title>{s.city} {s.pct}%</title>
            </circle>
          ))}
        </svg>
        <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 11 }}>
          {segs.map((s) => (
            <div key={s.city} style={{ display: "flex", alignItems: "center", gap: 9 }}>
              <span style={{ width: 10, height: 10, background: s.color, flexShrink: 0 }} />
              <span className="mono" style={{ fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, color: "var(--ink-2)" }}>{s.city}</span>
              <span style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 15, fontVariantNumeric: "tabular-nums", marginLeft: "auto" }}>{s.pct}%</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// Major deals are CLOSED deals only — these are public, so the full address
// shows in both agent and client views. Deals closed > 90 days carry a clickable
// "Verified on MLS" badge that jumps to the deal on the production graph.
function MajorDeals({ data, onFocusDeal }) {
  // A deal only qualifies as a "major deal" once it's CLOSED and MLS-verified
  // (closed > 90 days ago). Recent/active deals never appear here.
  const verifiedDeals = (data.deals || []).filter((d) => d.status === "closed" && d.closed_at && (DEMO_NOW_MS - Date.parse(d.closed_at + "T00:00:00Z")) > NINETY_DAYS);
  const ranked = [...verifiedDeals].sort((a, b) => (b.closed_at || "").localeCompare(a.closed_at || "")).slice(0, 4);
  if (ranked.length === 0) return null;
  const total = ranked.length;
  return (
    <div>
      <SectionRule label="Major deals" right={`${total} verified`} />
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2, minmax(0, 1fr))", border: "1px solid var(--hair)" }}>
        {ranked.map((d, i) => {
          const type = d.deal_type || "resale";
          return (
            <div key={d.deal_key} style={{ padding: "10px 14px", borderLeft: i % 2 !== 0 ? "1px solid var(--line)" : "none", borderTop: i >= 2 ? "1px solid var(--line)" : "none", display: "flex", flexDirection: "column", gap: 5, minWidth: 0 }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 }}>
                <span className="mono" style={{ fontSize: 10, color: "var(--mute)", fontVariantNumeric: "tabular-nums" }}>{String(i + 1).padStart(2, "0")}</span>
                <MlsVerifiedBadge onClick={() => onFocusDeal && onFocusDeal(d)} />
              </div>
              <div>
                <div title={d.address_display} style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 15, letterSpacing: "-0.01em", lineHeight: 1.1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.address_display}</div>
                {d.city && <div className="mono" style={{ fontSize: 10.5, color: "var(--mute)", marginTop: 3 }}>{d.city}</div>}
              </div>
              <span className="mono" style={{ fontSize: 9.5, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 600, marginTop: "auto" }}>{TYPE_LABEL[type]} · {d.side === "seller" ? "Listing" : d.side === "buyer" ? "Buyer" : "—"}</span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function Insights({ data, onFocusDeal }) {
  const areas = data.profile_stats.top_areas || [];
  if (areas.length === 0 && (data.deals || []).length === 0) return null;
  return (
    <section style={{ padding: "10px 32px 16px" }}>
      <div style={{ display: "grid", gridTemplateColumns: "minmax(220px, 280px) 1fr", gap: 36, alignItems: "start" }}>
        {areas.length > 0 && <MostActive areas={areas} />}
        <MajorDeals data={data} onFocusDeal={onFocusDeal} />
      </div>
    </section>
  );
}

/* ====================================================================== */
/*  PRE-CONSTRUCTION MAP                                                     */
/* ====================================================================== */
function PreconPanel({ projects }) {
  const elRef = useRef(null);
  useEffect(() => {
    let map = null;
    const L = window.L;
    if (!L || !elRef.current) return;
    map = L.map(elRef.current, { scrollWheelZoom: false }).setView([43.66, -79.42], 9);
    L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", { subdomains: "abcd", maxZoom: 19, attribution: "&copy; OpenStreetMap &copy; CARTO" }).addTo(map);
    const markers = projects.map((p, i) => {
      const icon = L.divIcon({
        className: "",
        html: `<div style="position:relative;width:26px;height:34px"><svg width="26" height="34" viewBox="0 0 24 32" fill="none"><path d="M12 0C5.4 0 0 5.3 0 11.8 0 20.4 12 32 12 32S24 20.4 24 11.8C24 5.3 18.6 0 12 0z" fill="oklch(0.55 0.19 295)"/><circle cx="12" cy="12" r="8" fill="#FFFDF8"/></svg><span style="position:absolute;top:4px;left:0;width:24px;text-align:center;font-family:'JetBrains Mono',monospace;font-weight:700;font-size:11px;color:oklch(0.40 0.18 295)">${i + 1}</span></div>`,
        iconSize: [26, 34], iconAnchor: [13, 34],
      });
      return L.marker([p.lat, p.lng], { icon }).addTo(map).bindPopup(`<b>${p.name}</b><br>${p.area}`);
    });
    if (markers.length) {
      const group = L.featureGroup(markers);
      map.fitBounds(group.getBounds().pad(0.25));
    }
    setTimeout(() => map && map.invalidateSize(), 120);
    return () => { if (map) map.remove(); };
  }, [projects]);

  return (
    <div style={{ border: "1px solid var(--accent)", background: "var(--paper)" }}>
      <div style={{ padding: "14px 18px", borderBottom: "1px solid var(--line)" }}>
        <span className="mono" style={{ fontSize: 11, letterSpacing: "0.14em", textTransform: "uppercase", fontWeight: 600, color: "var(--accent-ink)" }}>Pre-construction access · GTA projects</span>
      </div>
      <div style={{ display: "flex", alignItems: "stretch" }}>
        <div ref={elRef} style={{ flex: 1, minWidth: 150, height: 200, background: "var(--paper-2)" }} />
        <div style={{ width: 184, flexShrink: 0, borderLeft: "1px solid var(--line)", maxHeight: 200, overflowY: "auto" }}>
          {projects.map((p, i) => (
            <div key={p.name + i} style={{ padding: "10px 13px", borderTop: i > 0 ? "1px solid var(--line)" : "none", display: "flex", flexDirection: "column", gap: 4 }}>
              <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8 }}>
                <span style={{ display: "flex", alignItems: "baseline", gap: 7, minWidth: 0 }}>
                  <span className="mono" style={{ flexShrink: 0, fontSize: 10, fontWeight: 700, color: "var(--accent-ink)" }}>{i + 1}</span>
                  <span style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 13.5, lineHeight: 1.15, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.name}</span>
                </span>
                <span className="mono" style={{ flexShrink: 0, fontSize: 10, color: "var(--ink-2)", fontWeight: 600 }}>×{p.units}</span>
              </div>
              <span className="mono" style={{ fontSize: 9.5, color: "var(--mute)" }}>{p.area}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ====================================================================== */
/*  SHARE + BUTTONS                                                         */
/* ====================================================================== */
const inputStyle = { border: "1px solid var(--line-2)", padding: "9px 11px", fontSize: 13, fontFamily: "Prompt, sans-serif", background: "var(--paper)", color: "var(--ink)", outline: "none", width: "100%" };

function ActionBtn({ children, full }) {
  return (
    <button className="mono" style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6, background: "var(--paper)", border: "1px solid var(--ink)", color: "var(--ink)", padding: "10px 12px", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer", width: full ? "100%" : "auto" }}>
      {children}
    </button>
  );
}

function ShareButton({ full, name, url }) {
  const [open, setOpen] = useState(false);
  const [copied, setCopied] = useState(false);
  const [sent, setSent] = useState(false);
  const copyLink = async () => {
    setCopied(true);
    setTimeout(() => setCopied(false), 1800);
    try { await navigator.clipboard.writeText("https://" + url); } catch (e) {}
  };
  return (
    <>
      <button onClick={() => { setOpen(true); copyLink(); }} className="mono" style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6, background: "var(--ink)", border: "1px solid var(--ink)", color: "var(--paper)", padding: "10px 12px", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer", whiteSpace: "nowrap", width: full ? "100%" : "auto" }}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="18" cy="5" r="3" /><circle cx="6" cy="12" r="3" /><circle cx="18" cy="19" r="3" /><line x1="8.6" y1="13.5" x2="15.4" y2="17.5" /><line x1="15.4" y1="6.5" x2="8.6" y2="10.5" /></svg>
        Share profile
      </button>
      {open && <ShareModal onClose={() => setOpen(false)} copyLink={copyLink} copied={copied} url={url} name={name} sent={sent} setSent={setSent} />}
    </>
  );
}

// Full share modal, ported from the design handoff profile.jsx: QR + copy link,
// "send to someone" form, sent state, and the green "link copied" toast.
function ShareModal({ onClose, copyLink, copied, url, name, sent, setSent }) {
  const [recipient, setRecipient] = useState("");
  const [yourEmail, setYourEmail] = useState("");
  const [yourName, setYourName] = useState("");
  const [message, setMessage] = useState("Check out this realtor profile on Activus I shared with you!");
  return ReactDOM.createPortal(
    // className="ai-page" so the theme tokens (--paper etc.) resolve — the portal
    // mounts on document.body, outside the .ai-page tree, where they'd be undefined.
    <div onClick={onClose} className="ai-page" style={{
      position: "fixed", inset: 0, zIndex: 1000, minHeight: 0, background: "rgba(15,14,12,0.72)",
      backdropFilter: "blur(10px)", WebkitBackdropFilter: "blur(10px)",
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
      gap: 12, padding: 20, animation: "shFade 0.18s ease-out",
    }}>
      <style>{`@keyframes shFade { from { opacity: 0 } to { opacity: 1 } } @keyframes shPop { from { opacity:0; transform: translateY(8px) scale(0.98) } to { opacity:1; transform: none } } @keyframes shToast { from { opacity:0; transform: translateY(-6px) } to { opacity:1; transform: none } }`}</style>
      <div onClick={(e) => e.stopPropagation()} style={{ background: "var(--paper)", maxWidth: 440, width: "100%", border: "1px solid var(--ink)", animation: "shPop 0.22s ease-out" }}>
        <div style={{ padding: "20px 22px", borderBottom: "1px solid var(--hair)", display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 14 }}>
          <div>
            <Eyebrow style={{ marginBottom: 6 }}>{"// Share"}</Eyebrow>
            <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 20, letterSpacing: "-0.01em" }}>Share {name}&apos;s profile</div>
          </div>
          <button onClick={onClose} aria-label="close" style={{ background: "none", border: "none", cursor: "pointer", color: "var(--ink-3)", fontSize: 20, lineHeight: 1, padding: 0 }}>×</button>
        </div>
        {sent ? (
          <div style={{ padding: "30px 22px", textAlign: "center" }}>
            <div style={{ width: 44, height: 44, background: "var(--ink)", margin: "0 auto 12px", display: "grid", placeItems: "center", color: "var(--paper)" }}>
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><path d="M5 12l5 5L20 7" /></svg>
            </div>
            <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 20 }}>Sent.</div>
            <div className="mono" style={{ fontSize: 11, color: "var(--ink-3)", marginTop: 6, letterSpacing: "0.06em" }}>We&apos;ll let you know when they view the profile.</div>
          </div>
        ) : (
          <>
            <div style={{ padding: "18px 22px", borderBottom: "1px solid var(--hair)" }}>
              <Eyebrow style={{ marginBottom: 12 }}>Scan to view profile</Eyebrow>
              <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
                <img src={`https://api.qrserver.com/v1/create-qr-code/?size=200x200&margin=0&color=0F0E0C&bgcolor=FFFDF8&data=${encodeURIComponent("https://" + url)}`} alt="Profile QR code" width={104} height={104} style={{ display: "block", border: "1px solid var(--ink)", padding: 6, background: "var(--paper)", flexShrink: 0 }} />
                <div style={{ display: "flex", flexDirection: "column", gap: 8, minWidth: 0 }}>
                  <span className="mono" style={{ fontSize: 11, color: "var(--ink-3)", lineHeight: 1.5 }}>Point a phone camera at the code, or copy the link.</span>
                  <button onClick={copyLink} className="mono" style={{ alignSelf: "flex-start", display: "inline-flex", alignItems: "center", gap: 5, background: copied ? "var(--ink)" : "var(--paper)", color: copied ? "var(--paper)" : "var(--ink)", border: "1px solid var(--ink)", padding: "5px 9px", fontSize: 9.5, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer", whiteSpace: "nowrap", transition: "background 0.15s, color 0.15s" }}>
                    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="9" y="9" width="13" height="13" rx="1" /><path d="M5 15V5a2 2 0 0 1 2-2h10" /></svg>
                    {copied ? "Copied ✓" : "Copy link"}
                  </button>
                </div>
              </div>
            </div>
            <form onSubmit={(e) => { e.preventDefault(); setSent(true); }} style={{ padding: "12px 22px 16px", display: "flex", flexDirection: "column", gap: 8 }}>
              <Eyebrow>Or send to someone</Eyebrow>
              <Field label="Recipient email" required
                input={<input type="email" required value={recipient} onChange={(e) => setRecipient(e.target.value)} placeholder="client@example.com" style={inputStyle} />} />
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
                <Field label="Your email" required
                  input={<input type="email" required value={yourEmail} onChange={(e) => setYourEmail(e.target.value)} placeholder="you@example.com" style={inputStyle} />} />
                <Field label="Your name" required
                  input={<input required value={yourName} onChange={(e) => setYourName(e.target.value)} placeholder="Your name" style={inputStyle} />} />
              </div>
              <Field label="Your message"
                input={<textarea value={message} onChange={(e) => setMessage(e.target.value)} rows={2} placeholder="A short note…" style={{ ...inputStyle, resize: "vertical", minHeight: 42, fontFamily: "Prompt, sans-serif" }} />} />
              <button type="submit" className="mono" style={{ background: "var(--ink)", color: "var(--paper)", border: "1px solid var(--ink)", padding: "9px 14px", fontSize: 11, letterSpacing: "0.12em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer", marginTop: 2 }}>Send profile →</button>
            </form>
          </>
        )}
      </div>
      {copied && (
        <div className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 7, background: "#1F8A5B", color: "#fff", padding: "8px 14px", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, boxShadow: "0 6px 20px -6px rgba(0,0,0,0.4)", animation: "shToast 0.2s ease-out" }}>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3"><path d="M5 12l5 5L20 7" /></svg>
          Link copied
        </div>
      )}
    </div>,
    document.body
  );
}

function Field({ label, required, input }) {
  return (
    <label style={{ display: "flex", flexDirection: "column", gap: 5 }}>
      <span className="mono" style={{ fontSize: 9, letterSpacing: "0.14em", textTransform: "uppercase", fontWeight: 600, color: "var(--ink-3)", display: "inline-flex", alignItems: "center", gap: 5 }}>
        {label}
        {required && <span style={{ color: "var(--accent-ink)" }}>required</span>}
      </span>
      {input}
    </label>
  );
}

function Footer({ url }) {
  return (
    <footer style={{ padding: "20px 40px", display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 16 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
        <Mark size={16} />
        <span className="mono" style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.06em" }}>© 2026 Activus AI · {url}</span>
      </div>
    </footer>
  );
}

/* ====================================================================== */
/*  PROFILE BODY (mirrors AgentProfileView render, demo-toggle driven)      */
/* ====================================================================== */
function ProfileBody({ data, showOwnerView, onOpenEval }) {
  const [filter, setFilter] = useState({ type: null, side: null });
  const toggle = (dim, key) => setFilter((f) => ({ ...f, [dim]: f[dim] === key ? null : key }));
  const years = (data.performance_by_year && data.performance_by_year.years) || [new Date().getFullYear()];
  const currentYear = years[0];
  // Default: trailing 12 months (currentYear highlighted). Clicking a year in
  // the rail switches to that calendar year; "12 mo" returns to trailing.
  const [year, setYear] = useState(currentYear);
  const [yearView, setYearView] = useState(false);
  const selectYear = (y) => { setYear(y); setYearView(true); };
  const url = data.identity && data.identity.slug ? `activusai.com/${data.identity.slug}` : "activusai.com";
  const precon = data.precon_projects || [];
  const showPrecon = filter.type === "precon" && precon.length > 0;
  // Clicking a Major-deals "Verified on MLS" badge jumps to that closed deal on
  // the production graph (its calendar year + a popup anchored on its month).
  const [focusDeal, setFocusDeal] = useState(null);
  const onFocusDeal = (d) => {
    const y = d.closed_at ? new Date(d.closed_at + "T00:00:00Z").getUTCFullYear() : currentYear;
    setYear(y);
    setYearView(true);
    setFocusDeal({ deal: d, year: y, nonce: Date.now() });
  };

  return (
    <>
      <main className="ai-main">
        <div className="ai-sticky" style={{ position: "sticky", top: 64, alignSelf: "start" }}>
          <ProfileSidebar data={data} showOwnerView={showOwnerView} />
        </div>
        <div style={{ minWidth: 0 }}>
          <ActivityGrid data={data} filter={filter} toggle={toggle} year={year} yearView={yearView} selectYear={selectYear} showOwnerView={showOwnerView} onOpenEval={onOpenEval} focusDeal={focusDeal} />
          {showPrecon ? (
            <section style={{ padding: "12px 32px", borderBottom: "1px solid var(--hair)" }}><PreconPanel projects={precon} /></section>
          ) : (
            data.performance_by_year && <StatsBand data={data} year={year} yearView={yearView} />
          )}
          <Insights data={data} onFocusDeal={onFocusDeal} />
        </div>
      </main>
      <Footer url={url} />
    </>
  );
}

/* ====================================================================== */
/*  AGENTEVAL  (ported from the Claude Design handoff: agenteval.jsx)        */
/*  Funnel · after-signing outcomes · peer benchmark · NL inbox search.     */
/* ====================================================================== */
const AE_FUNNEL = [
  { key: "contact", label: "First contact", n: 142, note: "New conversations opened",
    reasons: [["No reply after 2 follow-ups", 31], ["Went cold — not ready to act", 12], ["Chose another agent", 5]] },
  { key: "meeting", label: "Discovery meeting", n: 94, note: "Met in person or by call",
    reasons: [["Budget / area mismatch", 14], ["Paused their search", 11], ["Lost to another agent", 6]] },
  { key: "showing", label: "Showing / presentation", n: 63, note: "Toured or pitched a listing",
    reasons: [["Still deciding", 9], ["Went with another agent", 5], ["Couldn't agree on price", 2]] },
  { key: "signed", label: "Representation signed", n: 47, note: "Buyer or listing agreement", reasons: [] },
];
const AE_OUTCOMES = [
  { key: "active", label: "Active in pipeline", n: 31, tone: "ink" },
  { key: "closed", label: "Closed this period", n: 10, tone: "good" },
  { key: "fell",   label: "Fell through",       n: 6,  tone: "bad" },
];
const AE_PEERS = [
  { label: "First-response time", you: "1.8h", peer: "5.2h", pct: 88, better: true,  sub: "Median time to first reply" },
  { label: "Contact → meeting",   you: "66%",  peer: "71%",  pct: 36, better: false, sub: "First contacts that book a meeting" },
  { label: "Days to close",       you: "34",   peer: "45",   pct: 80, better: true,  sub: "Signed to firm / closed" },
  { label: "Signed → closed",     you: "21%",  peer: "24%",  pct: 42, better: false, sub: "Trailing 12 months" },
  { label: "Client retention",    you: "68%",  peer: "51%",  pct: 84, better: true,  sub: "Repeat + referral share" },
];
const AE_STAT_CARDS = [
  { label: "Active pipeline", value: "31", sub: "Live deals in flight" },
  { label: "Signed · 12mo", value: "47", sub: "↑ 18% vs. prior year", tone: "accent" },
  { label: "Avg days to close", value: "34", sub: "↓ 11 vs. market avg", tone: "good" },
  { label: "Contact → signed", value: "33%", sub: "47 of 142 conversations" },
  { label: "Client retention", value: "68%", sub: "Top quartile in cohort" },
];
const AE_SEARCH_EXAMPLES = [
  { chip: "Pull up 15 Grove Creek from last month", q: "Pull up the details on 15 Grove Creek from last month", key: "deal" },
  { chip: "My pre-construction interactions last year", q: "Find all my pre-construction interactions from last year", key: "rollup" },
  { chip: "Compare a client's showings to my closed listings", q: "Compare my showings with Maya Chen in Oakville to the listing deals I closed there this year", key: "compare" },
];
const AE_SEARCH_RESULTS = {
  deal: {
    kind: "deal", address: "15 Grove Creek Dr · Milton", type: "Pre-construction assignment",
    stage: "Offer / assignment prep", period: "Last month · April 2026",
    summary: "Your most active deal last month: 11 threads, mostly assignment paperwork and one price revision. The buyer has gone quiet — no reply in the last 6 days.",
    meta: [["Email threads", "11"], ["Last activity", "Apr 28"], ["Counterparties", "Builder + 1 buyer"], ["Docs referenced", "Assignment agt., price sheet"]],
  },
  rollup: {
    kind: "rollup", title: "Pre-construction interactions · last 12 months",
    summary: "38 pre-construction conversations last year — about a quarter of everything you touched. They took noticeably longer to sign than your other work.",
    stats: [["38", "Conversations"], ["9", "Signed"], ["47 days", "Median to sign"], ["GTA Phase 1", "Most active project"]],
  },
  compare: {
    kind: "compare", title: "Maya Chen — Oakville showings  vs.  your closed Oakville listings",
    period: "trailing 12 months", aLabel: "This client", bLabel: "Your closed avg",
    summary: "Your work with Maya is running longer and chattier than a typical Oakville listing you've closed — mostly more showings and more back-and-forth before an offer lands.",
    rows: [
      { label: "Showings before an offer", a: "6", b: "3", aNum: 6, bNum: 3, note: "≈2× more" },
      { label: "First contact → offer", a: "58 days", b: "31 days", aNum: 58, bNum: 31, note: "27 days slower" },
      { label: "Email threads / week", a: "4.2", b: "2.1", aNum: 4.2, bNum: 2.1, note: "≈2× chattier" },
      { label: "Price points discussed", a: "3", b: "1", aNum: 3, bNum: 1, note: "more negotiation" },
    ],
  },
};
function aePickResultKey(q) {
  const s = q.toLowerCase();
  if (/grove|details on|pull up|address|\bunit\b/.test(s)) return "deal";
  if (/pre-?con|construction|last year|all my/.test(s)) return "rollup";
  return "compare";
}

// Datadog-style sparkline: soft area-fill gradient under a rounded trendline.
function AESpark({ pts, color }) {
  const w = 240, h = 26;
  const max = Math.max(...pts), min = Math.min(...pts), span = (max - min) || 1;
  const step = w / (pts.length - 1);
  const pd = pts.map((p, i) => `${i ? "L" : "M"}${(i * step).toFixed(1)} ${(h - 2 - ((p - min) / span) * (h - 4)).toFixed(1)}`).join(" ");
  const area = pd + ` L${w} ${h} L0 ${h} Z`;
  const gid = "sp" + pts.join("_");
  return (
    <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: "block", marginTop: 10 }}>
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor={color} stopOpacity="0.2" />
          <stop offset="1" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill={`url(#${gid})`} />
      <path d={pd} fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// Datadog-style donut/pie for the after-signing outcomes.
function AEDonut({ segs, colors, size = 152, stroke = 22 }) {
  const r = (size - stroke) / 2, c = 2 * Math.PI * r, cx = size / 2;
  const total = segs.reduce((a, b) => a + b, 0) || 1;
  let acc = 0;
  const circles = segs.map((s, i) => {
    const len = (s / total) * c;
    const el = (
      <circle key={i} cx={cx} cy={cx} r={r} fill="none" stroke={colors[i]} strokeWidth={stroke}
        strokeDasharray={`${len - 1.5} ${c - len + 1.5}`} strokeDashoffset={-acc} transform={`rotate(-90 ${cx} ${cx})`} />
    );
    acc += len;
    return el;
  });
  return <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>{circles}</svg>;
}

function AECard({ title, right, children, pad = 22, style }) {
  return (
    <div style={{ background: "var(--paper)", border: "1px solid var(--line)", borderRadius: 8, overflow: "hidden", boxShadow: "0 1px 2px rgba(15,14,12,0.04)", display: "flex", flexDirection: "column", ...style }}>
      {(title || right) && (
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, padding: `14px ${pad}px`, borderBottom: "1px solid var(--line)" }}>
          <span style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 15, letterSpacing: "-0.01em", display: "inline-flex", alignItems: "center", gap: 9 }}>
            <span style={{ width: 6, height: 6, borderRadius: 2, background: "var(--accent)", flexShrink: 0 }} />{title}
          </span>
          {right && <span className="mono" style={{ fontSize: 10, color: "var(--mute)", letterSpacing: "0.06em" }}>{right}</span>}
        </div>
      )}
      <div style={{ padding: pad, flex: 1 }}>{children}</div>
    </div>
  );
}

function AEStatCard({ label, value, sub, tone, spark, sparkColor }) {
  const toneInk = tone === "good" ? "var(--good-ink)" : tone === "bad" ? "var(--bad-ink)" : tone === "accent" ? "var(--accent-ink)" : "var(--ink)";
  return (
    <div style={{ background: "var(--paper)", border: "1px solid var(--line)", borderRadius: 8, boxShadow: "0 1px 2px rgba(15,14,12,0.04)", padding: "16px 18px", display: "flex", flexDirection: "column" }}>
      <div className="mono" style={{ fontSize: 9.5, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 600 }}>{label}</div>
      <div style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 34, letterSpacing: "-0.04em", lineHeight: 1, marginTop: 12, color: toneInk, fontVariantNumeric: "tabular-nums" }}>{value}</div>
      {sub && <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 7 }}>{sub}</div>}
      {spark && <AESpark pts={spark} color={sparkColor || "var(--ink-3)"} />}
    </div>
  );
}

function AEFunnelCard({ funnel }) {
  const max = funnel[0].n;
  const transitions = funnel.slice(0, -1).map((s, i) => {
    const next = funnel[i + 1];
    const lost = s.n - next.n;
    return { key: s.key, from: s, lost, pct: Math.round((lost / s.n) * 100) };
  });
  const biggest = transitions.reduce((a, b) => (b.lost > a.lost ? b : a), transitions[0]);
  const [open, setOpen] = useState(biggest.key);
  return (
    <AECard title="Client funnel">
      {funnel.map((s, i) => {
        const isSigned = s.key === "signed";
        const w = (s.n / max) * 100;
        const tr = transitions[i];
        const isBiggest = tr && tr.key === biggest.key;
        const isOpen = tr && open === tr.key;
        return (
          <div key={s.key}>
            <div style={{ display: "grid", gridTemplateColumns: "180px 1fr 78px", gap: 14, alignItems: "center" }}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 14.5, letterSpacing: "-0.01em", lineHeight: 1.15 }}>{s.label}</div>
                <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 2 }}>{s.note}</div>
              </div>
              {/* Centered bar → stacked stages form the funnel shape. */}
              <div style={{ display: "flex", justifyContent: "center" }}>
                <div style={{ width: `${w}%`, minWidth: 56, height: 46, background: isSigned ? "var(--accent)" : "var(--ink)", borderRadius: 6, display: "flex", alignItems: "center", justifyContent: "center", transition: "width 0.4s cubic-bezier(.2,.7,.2,1)" }}>
                  <span className="mono" style={{ fontSize: 11, fontWeight: 600, letterSpacing: "0.06em", color: "var(--bg)" }}>{Math.round(w)}%</span>
                </div>
              </div>
              <div style={{ textAlign: "right", fontFamily: "Prompt", fontWeight: 700, fontSize: 24, letterSpacing: "-0.03em", fontVariantNumeric: "tabular-nums", color: isSigned ? "var(--accent-ink)" : "var(--ink)" }}>{s.n}</div>
            </div>
            {tr && (
              <div style={{ display: "grid", gridTemplateColumns: "180px 1fr 78px", gap: 14, margin: "6px 0" }}>
                <div />
                <div>
                  <button onClick={() => setOpen(isOpen ? null : tr.key)} className="mono" style={{
                    display: "inline-flex", alignItems: "center", gap: 9, background: "none", border: "none", cursor: "pointer", padding: "2px 0",
                    fontSize: 11, letterSpacing: "0.04em", fontWeight: 600, color: isBiggest ? "var(--bad-ink)" : "var(--ink-3)",
                  }}>
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{ transform: isOpen ? "none" : "rotate(-90deg)", transition: "transform 0.15s" }}><path d="M6 9l6 6 6-6"/></svg>
                    <span style={{ fontVariantNumeric: "tabular-nums" }}>−{tr.pct}%</span>
                    <span style={{ color: "var(--mute)" }}>·</span>
                    <span style={{ color: "var(--mute)" }}>{tr.lost} dropped</span>
                    {isBiggest && <span style={{ padding: "2px 6px", background: "var(--bad-tint)", color: "var(--bad-ink)", border: "1px solid var(--bad)", borderRadius: 4, letterSpacing: "0.1em", textTransform: "uppercase", fontSize: 8.5 }}>Biggest leak</span>}
                  </button>
                  {isOpen && (
                    <div style={{ marginTop: 8, marginBottom: 4, border: "1px solid var(--line)", background: "var(--paper)" }}>
                      <div className="mono" style={{ fontSize: 9, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--mute)", padding: "8px 12px 6px", borderBottom: "1px solid var(--line)" }}>Why they dropped · inferred from threads</div>
                      {tr.from.reasons.map((r, ri) => (
                        <div key={ri} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, padding: "7px 12px", borderTop: ri > 0 ? "1px solid var(--line)" : "none" }}>
                          <span style={{ fontSize: 13, color: "var(--ink-2)" }}>{r[0]}</span>
                          <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: "var(--ink)", fontVariantNumeric: "tabular-nums" }}>{r[1]}</span>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
                <div />
              </div>
            )}
          </div>
        );
      })}
    </AECard>
  );
}

function AEAfterSigningCard({ funnel, outcomes }) {
  const signed = funnel[funnel.length - 1].n;
  const colors = { ink: "var(--ink)", good: "var(--good)", bad: "var(--bad)" };
  const inks = { ink: "var(--ink)", good: "var(--good-ink)", bad: "var(--bad-ink)" };
  return (
    <AECard title="After signing" right={`${signed} clients`}>
      <div style={{ display: "flex", gap: 24, alignItems: "center" }}>
        <div style={{ position: "relative", flexShrink: 0 }}>
          <AEDonut segs={outcomes.map((o) => o.n)} colors={outcomes.map((o) => colors[o.tone])} size={152} stroke={22} />
          <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", textAlign: "center" }}>
            <div>
              <div style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 28, letterSpacing: "-0.03em", lineHeight: 1 }}>{signed}</div>
              <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 3 }}>signed</div>
            </div>
          </div>
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          {outcomes.map((o, i) => (
            <div key={o.key} style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 0", borderTop: i > 0 ? "1px solid var(--line)" : "none" }}>
              <span style={{ width: 10, height: 10, background: colors[o.tone], flexShrink: 0 }} />
              <span className="mono" style={{ fontSize: 10, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 600 }}>{o.label}</span>
              <span style={{ marginLeft: "auto", fontFamily: "Prompt", fontWeight: 700, fontSize: 22, letterSpacing: "-0.03em", lineHeight: 1, fontVariantNumeric: "tabular-nums", color: inks[o.tone] }}>{o.n}</span>
              <span className="mono" style={{ fontSize: 10, color: "var(--mute)", width: 42, textAlign: "right" }}>{Math.round((o.n / signed) * 100)}%</span>
            </div>
          ))}
        </div>
      </div>
    </AECard>
  );
}

function AEBenchRow({ label, you, peer, pct, better, sub }) {
  const tone = better ? "var(--good)" : "var(--bad)";
  const toneInk = better ? "var(--good-ink)" : "var(--bad-ink)";
  const lo = Math.min(50, pct), hi = Math.max(50, pct);
  const rank = pct >= 50 ? `Top ${100 - pct}%` : `${pct}th pctl`;
  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 240px 130px", gap: 20, alignItems: "center", padding: "15px 0", borderBottom: "1px solid var(--line)" }}>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 15, letterSpacing: "-0.01em" }}>{label}</div>
        <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 2 }}>{sub}</div>
      </div>
      <div style={{ position: "relative", height: 22 }}>
        <div style={{ position: "absolute", top: "50%", left: 0, right: 0, height: 2, background: "var(--line)", transform: "translateY(-50%)" }} />
        <div style={{ position: "absolute", top: "50%", left: `${lo}%`, width: `${hi - lo}%`, height: 2, background: tone, transform: "translateY(-50%)" }} />
        <div style={{ position: "absolute", top: "50%", left: "50%", width: 1, height: 14, background: "var(--ink)", transform: "translate(-50%,-50%)" }} />
        <div style={{ position: "absolute", top: "50%", left: `${pct}%`, width: 13, height: 13, borderRadius: "50%", background: tone, border: "2px solid var(--paper)", boxShadow: "0 0 0 1px " + tone, transform: "translate(-50%,-50%)" }} />
      </div>
      <div style={{ textAlign: "right" }}>
        <div style={{ display: "flex", alignItems: "baseline", justifyContent: "flex-end", gap: 6 }}>
          <span style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 22, letterSpacing: "-0.02em", lineHeight: 1, color: toneInk, fontVariantNumeric: "tabular-nums" }}>{you}</span>
          <span className="mono" style={{ fontSize: 10, color: "var(--mute)" }}>vs {peer}</span>
        </div>
        <div className="mono" style={{ marginTop: 4, fontSize: 9, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, color: toneInk, display: "inline-flex", alignItems: "center", gap: 4 }}>
          <span>{better ? "▲" : "▼"}</span>{rank}
        </div>
      </div>
    </div>
  );
}

function AEPeerCard() {
  return (
    <AECard title="How you compare" right="38 similar agents">
      <div className="mono" style={{ fontSize: 10.5, color: "var(--ink-3)", letterSpacing: "0.04em", marginBottom: 18, display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }}>
        <span style={{ color: "var(--mute)" }}>Cohort:</span>
        {["Western GTA", "$600K–$1.2M", "Condo & pre-con focus", "Comparable volume"].map(c => (
          <span key={c} style={{ padding: "3px 8px", border: "1px solid var(--line)", background: "var(--paper)" }}>{c}</span>
        ))}
      </div>
      <div style={{ borderTop: "1px solid var(--line)" }}>
        {AE_PEERS.map(p => <AEBenchRow key={p.label} {...p} />)}
      </div>
      <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 14, display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><span style={{ width: 10, height: 10, borderRadius: "50%", background: "var(--good)" }} /> Ahead of peers</span>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><span style={{ width: 10, height: 10, borderRadius: "50%", background: "var(--bad)" }} /> Behind peers</span>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><span style={{ width: 1, height: 12, background: "var(--ink)" }} /> Peer median</span>
      </div>
    </AECard>
  );
}

function AESparkIcon({ size = 17 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
      <path d="M12 3l1.6 5.4L19 10l-5.4 1.6L12 17l-1.6-5.4L5 10l5.4-1.6z" />
      <path d="M19 15l.7 2.3L22 18l-2.3.7L19 21l-.7-2.3L16 18l2.3-.7z" />
    </svg>
  );
}

function AESearchSkeleton() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
      {[88, 64, 72].map((w, i) => (
        <div key={i} style={{ height: 12, width: `${w}%`, background: "var(--paper-2)", animation: "aeskel 1.1s ease-in-out infinite", animationDelay: `${i * 0.12}s` }} />
      ))}
      <style>{`@keyframes aeskel { 0%,100% { opacity: 0.5 } 50% { opacity: 1 } }`}</style>
    </div>
  );
}

function AESearchResult({ data }) {
  if (!data) return null;
  if (data.kind === "compare") {
    return (
      <div>
        <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 17, letterSpacing: "-0.01em", lineHeight: 1.2 }}>{data.title}</div>
        <p className="news" style={{ margin: "8px 0 18px", fontSize: 15, lineHeight: 1.5, color: "var(--ink-2)", maxWidth: 620 }}>{data.summary}</p>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 150px 150px", gap: 16, alignItems: "center", paddingBottom: 8, borderBottom: "1px solid var(--line)" }}>
          <span />
          <span className="mono" style={{ fontSize: 9, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--accent-ink)", fontWeight: 600, textAlign: "right" }}>{data.aLabel}</span>
          <span className="mono" style={{ fontSize: 9, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 600, textAlign: "right" }}>{data.bLabel}</span>
        </div>
        {data.rows.map(r => {
          const max = Math.max(r.aNum, r.bNum);
          return (
            <div key={r.label} style={{ display: "grid", gridTemplateColumns: "1fr 150px 150px", gap: 16, alignItems: "center", padding: "13px 0", borderBottom: "1px solid var(--line)" }}>
              <div>
                <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 14 }}>{r.label}</div>
                <div className="mono" style={{ fontSize: 10, color: "var(--accent-ink)", marginTop: 2 }}>{r.note}</div>
              </div>
              {[{ v: r.a, n: r.aNum, c: "var(--accent)" }, { v: r.b, n: r.bNum, c: "var(--ink)" }].map((cell, i) => (
                <div key={i} style={{ display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-end" }}>
                  <span style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 18, letterSpacing: "-0.02em", fontVariantNumeric: "tabular-nums" }}>{cell.v}</span>
                  <div style={{ width: "100%", height: 4, background: "var(--paper-2)" }}>
                    <div style={{ width: `${(cell.n / max) * 100}%`, height: "100%", background: cell.c, marginLeft: "auto" }} />
                  </div>
                </div>
              ))}
            </div>
          );
        })}
      </div>
    );
  }
  if (data.kind === "rollup") {
    return (
      <div>
        <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 17, letterSpacing: "-0.01em" }}>{data.title}</div>
        <p className="news" style={{ margin: "8px 0 18px", fontSize: 15, lineHeight: 1.5, color: "var(--ink-2)", maxWidth: 620 }}>{data.summary}</p>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 0, border: "1px solid var(--line)" }}>
          {data.stats.map((s, i) => (
            <div key={i} style={{ padding: "14px 16px", borderLeft: i > 0 ? "1px solid var(--line)" : "none" }}>
              <div style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 24, letterSpacing: "-0.03em", lineHeight: 1, fontVariantNumeric: "tabular-nums" }}>{s[0]}</div>
              <div className="mono" style={{ fontSize: 9.5, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--ink-3)", marginTop: 7, fontWeight: 600 }}>{s[1]}</div>
            </div>
          ))}
        </div>
      </div>
    );
  }
  return (
    <div>
      <div style={{ display: "flex", alignItems: "flex-start", gap: 14 }}>
        <div style={{ display: "grid", placeItems: "center", width: 42, height: 42, background: "var(--precon)", flexShrink: 0 }}>
          <svg width="20" height="20" viewBox="0 0 10 10"><path d="M1 5 L5 1.3 L9 5 L9 9 L1 9 Z" fill="var(--paper)" /></svg>
        </div>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 18, letterSpacing: "-0.01em" }}>{data.address}</div>
          <div className="mono" style={{ fontSize: 10.5, color: "var(--ink-3)", letterSpacing: "0.04em", marginTop: 3 }}>{data.type} · {data.stage} · {data.period}</div>
        </div>
      </div>
      <p className="news" style={{ margin: "14px 0 16px", fontSize: 15, lineHeight: 1.5, color: "var(--ink-2)", maxWidth: 620 }}>{data.summary}</p>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 0, border: "1px solid var(--line)" }}>
        {data.meta.map((m, i) => (
          <div key={i} style={{ padding: "11px 16px", borderTop: i > 1 ? "1px solid var(--line)" : "none", borderLeft: i % 2 === 1 ? "1px solid var(--line)" : "none", display: "flex", justifyContent: "space-between", gap: 12 }}>
            <span className="mono" style={{ fontSize: 9.5, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--mute)", fontWeight: 600 }}>{m[0]}</span>
            <span style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 13, textAlign: "right" }}>{m[1]}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function AESearchPanel() {
  const [query, setQuery] = useState("");
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState(null);
  const [asked, setAsked] = useState("");
  const [ph, setPh] = useState(0);
  useEffect(() => {
    if (query) return;
    const id = setInterval(() => setPh(p => (p + 1) % AE_SEARCH_EXAMPLES.length), 3600);
    return () => clearInterval(id);
  }, [query]);
  const run = (text, key) => {
    const q = (text || "").trim();
    if (!q) return;
    setAsked(q); setLoading(true); setResult(null);
    setTimeout(() => { setResult(AE_SEARCH_RESULTS[key || aePickResultKey(q)]); setLoading(false); }, 700);
  };
  const pick = (ex) => { setQuery(ex.q); run(ex.q, ex.key); };
  return (
    <div style={{ marginBottom: 24 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, border: "1px solid var(--ink)", borderRadius: 7, background: "var(--paper)", padding: "0 8px 0 16px", height: 56, boxShadow: "0 1px 2px rgba(15,14,12,0.05)" }}>
        <span style={{ color: "var(--accent-ink)" }}><AESparkIcon size={18} /></span>
        {/* Not typeable — a clicked chip populates the query here; otherwise it
            shows the rotating example hint. */}
        <span style={{ flex: 1, fontFamily: "Prompt", fontSize: 16, color: query ? "var(--ink)" : "var(--mute)", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", userSelect: "none" }}>
          {query || `Ask anything — e.g. “${AE_SEARCH_EXAMPLES[ph].q}”`}
        </span>
        <span className="mono" aria-hidden="true" style={{ display: "inline-flex", alignItems: "center", gap: 7, height: 40, padding: "0 16px", background: "var(--ink)", color: "var(--paper)", fontSize: 11, letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600, opacity: 0.45 }}>
          Ask
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
        </span>
      </div>
      <div style={{ display: "flex", gap: 8, marginTop: 11, flexWrap: "wrap", alignItems: "center" }}>
        <span className="mono" style={{ fontSize: 9.5, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--mute)", fontWeight: 600 }}>Try</span>
        {AE_SEARCH_EXAMPLES.map(ex => (
          <button key={ex.key} onClick={() => pick(ex)} className="mono" style={{ background: "var(--paper)", border: "1px solid var(--line-2)", borderRadius: 7, color: "var(--ink-2)", padding: "6px 10px", fontSize: 10.5, letterSpacing: "0.02em", cursor: "pointer" }}>{ex.chip}</button>
        ))}
      </div>
      {(loading || result) && (
        <div style={{ marginTop: 16, border: "1px solid var(--line)", borderRadius: 8, overflow: "hidden", background: "var(--paper)", boxShadow: "0 1px 2px rgba(15,14,12,0.04)" }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, padding: "12px 18px", borderBottom: "1px solid var(--line)" }}>
            <span className="mono" style={{ fontSize: 9.5, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--accent-ink)", fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 7 }}>
              <AESparkIcon size={12} /> {loading ? "Reading your inbox…" : "Interpreted your question"}
            </span>
            {asked && <span className="mono" style={{ fontSize: 10, color: "var(--mute)", maxWidth: "55%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>“{asked}”</span>}
          </div>
          <div style={{ padding: 18 }}>{loading ? <AESearchSkeleton /> : <AESearchResult data={result} />}</div>
        </div>
      )}
    </div>
  );
}

const AE_PERIODS = [
  { key: "12m", label: "12 months", scale: 1, sub: "↑ 18% vs. prior year" },
  { key: "q", label: "Quarter", scale: 0.28, sub: "this quarter" },
  { key: "m", label: "Month", scale: 0.095, sub: "this month" },
];
function AEPageHead({ period, setPeriod }) {
  return (
    <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 24, flexWrap: "wrap", marginBottom: 22 }}>
      <div>
        <Eyebrow style={{ marginBottom: 8 }}>Agent intelligence</Eyebrow>
        <h1 style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 38, letterSpacing: "-0.04em", margin: 0, lineHeight: 1 }}>AgentEval</h1>
      </div>
      <div className="mono" style={{ display: "flex", border: "1px solid var(--line-2)", borderRadius: 7, overflow: "hidden" }}>
        {AE_PERIODS.map((pr, i) => {
          const on = pr.key === period.key;
          return (
            <button key={pr.key} onClick={() => setPeriod(pr)} style={{ background: on ? "var(--ink)" : "var(--paper)", color: on ? "var(--paper)" : "var(--ink-2)", border: "none", borderLeft: i > 0 ? "1px solid var(--line-2)" : "none", padding: "8px 14px", fontSize: 10, letterSpacing: "0.1em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer" }}>{pr.label}</button>
          );
        })}
      </div>
    </div>
  );
}

/* ====================================================================== */
/*  DEAL EVIDENCE CHAIN  (MOCK — what AgentEval reconstructs per deal)       */
/*  Maps each lifecycle stage to the email / calendar / OREA-form signals    */
/*  our Ontario classifier reads for. Hardcoded for the Sarah Chen demo;     */
/*  see agenteval-deal-chain-plan.md for the real collection plan.           */
/* ====================================================================== */
const EV_META = {
  email:    { label: "Email",     stroke: "var(--ink-3)",     icon: <><rect x="3" y="5" width="18" height="14" rx="1.5" /><path d="M3 7l9 6 9-6" /></> },
  calendar: { label: "Calendar",  stroke: "var(--accent-ink)", icon: <><rect x="3" y="4" width="18" height="17" rx="2" /><path d="M3 9h18M8 2v4M16 2v4" /></> },
  form:     { label: "OREA form", stroke: "var(--accent-ink)", icon: <><rect x="5" y="3" width="14" height="18" rx="1.5" /><path d="M8 8h8M8 12h8M8 16h5" /></> },
  mls:      { label: "MLS",       stroke: "var(--good-ink)",   icon: <><path d="M12 3l8 3v6c0 5-8 9-8 9s-8-4-8-9V6z" /><path d="M9 12l2 2 4-4" /></> },
};
function EvIcon({ src }) {
  const m = EV_META[src] || EV_META.email;
  return <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke={m.stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>{m.icon}</svg>;
}

function chainTemplate(deal) {
  const a = deal.address, city = deal.city, d = deal.date;
  if (deal.side === "buyer") {
    return [
      { tag: "Inbound · email", expect: "an inbound buyer inquiry thread", ev: [
        { src: "email", label: `Re: Looking to buy in ${city}`, meta: "replied 1.8h" },
        { src: "email", label: "Budget + must-haves captured", meta: "3 msgs" } ] },
      { tag: "listing_presentation", expect: "a buyer-consultation calendar event or needs email", ev: [
        { src: "calendar", label: "Buyer consultation", meta: "45 min" },
        { src: "email", label: `Shortlist + financing — ${city}`, meta: "pre-approved" } ] },
      { tag: "buyer_rep · OREA 300", expect: "OREA 300 / 371 buyer representation agreement", ev: [
        { src: "form", label: "OREA 300 · Buyer Representation Agreement — signed", meta: "high" },
        { src: "form", label: "OREA 320 · Confirmation of Co-operation", meta: "" } ] },
      { tag: "showing", expect: "showing calendar events + tour-feedback emails", ev: [
        { src: "calendar", label: "Showings booked ×4", meta: `incl. ${a}` },
        { src: "email", label: "Tour feedback — narrowing down", meta: "" } ] },
      { tag: "offer · OREA 100", expect: "an OREA 100 APS sent (or 400 lease)", ev: [
        { src: "form", label: `OREA 100 · Agreement of P&S — submitted on ${a}`, meta: "offer out" },
        { src: "form", label: "OREA 105 conditions + OREA 801 summary", meta: "" },
        { src: "email", label: "Sign-back received — counter #1", meta: "negotiation" } ] },
      { tag: "closing · firm → MLS", expect: "OREA 123/124 waiver/fulfilment, then MLS sold reconfirm", ev: [
        { src: "form", label: "OREA 123 · Waiver — conditions waived", meta: "firm" },
        { src: "email", label: `Closing ${d} · per Schedule A/B`, meta: "firm date" },
        { src: "mls", label: "MLS reconfirmed · Sold", meta: "flagged 11d earlier" } ] },
    ];
  }
  return [
    { tag: "Inbound · email", expect: "an inbound seller inquiry or referral thread", ev: [
      { src: "email", label: `Re: Thinking of selling — ${a}`, meta: "replied 1.8h" },
      { src: "email", label: "Seller intro + timeline questions", meta: "2 msgs" } ] },
    { tag: "listing_presentation", expect: "a CMA email or a listing-presentation calendar event", ev: [
      { src: "email", label: `CMA sent — ${a}`, meta: "pricing deck" },
      { src: "calendar", label: `Listing presentation · ${a}`, meta: "60 min" } ] },
    { tag: "listing · OREA 200", expect: "OREA 200 / 271 listing agreement", ev: [
      { src: "form", label: "OREA 200 · Listing Agreement — executed", meta: "high" },
      { src: "form", label: "OREA 320 · Confirmation of Co-operation", meta: "" } ] },
    { tag: "showing · MLS", expect: "an MLS listing + showing bookings", ev: [
      { src: "mls", label: `Listed on MLS · ${a}`, meta: "active" },
      { src: "calendar", label: "Showings booked ×3", meta: "this week" } ] },
    { tag: "offer · OREA 100", expect: "an OREA 100 APS received (or 400 lease)", ev: [
      { src: "form", label: `OREA 100 · Agreement of P&S — received on ${a}`, meta: "offer in" },
      { src: "form", label: "OREA 801 summary + OREA 105 conditions", meta: "" },
      { src: "email", label: "Counter #2 — price & close revised", meta: "negotiation" } ] },
    { tag: "closing · firm → MLS", expect: "OREA 123/124 waiver/fulfilment, then MLS sold reconfirm", ev: [
      { src: "form", label: "OREA 124 · Notice of Fulfilment — conditions met", meta: "firm" },
      { src: "email", label: `Closing ${d} · per Schedule A`, meta: "firm date" },
      { src: "mls", label: "MLS reconfirmed · Sold", meta: "flagged 9d earlier" } ] },
  ];
}
const CHAIN_STAGE_GAP = 25 * 24 * 3600 * 1000; // ~spacing between stage dates
function fmtFullDate(ms) {
  return new Date(ms).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
}
function buildDealChain(deal) {
  const tmpl = chainTemplate(deal);
  const labels = stageLabels(deal.side);
  const stage = Math.max(1, Math.min(6, deal.stage || 1));
  // The current stage is anchored on the deal's date; earlier (done) stages are
  // back-dated from it, so the dates correlate with the clicked house.
  const anchor = deal.iso ? Date.parse(deal.iso + "T00:00:00Z") : DEMO_NOW_MS;
  return tmpl.map((t, i) => {
    const n = i + 1;
    const status = n < stage ? "done" : n === stage ? "current" : "upcoming";
    const date = status === "upcoming" ? null : fmtFullDate(anchor - (stage - n) * CHAIN_STAGE_GAP);
    return { n, title: labels[i], tag: t.tag, status, ev: status === "upcoming" ? [] : t.ev, expect: t.expect, date };
  });
}

function DealChain({ deal, onBack }) {
  const chain = buildDealChain(deal);
  const labels = stageLabels(deal.side);
  const cur = Math.max(1, Math.min(6, deal.stage || 1));
  const isClosed = cur >= 6;
  return (
    <div style={{ maxWidth: 920, margin: "0 auto", padding: "22px 32px 50px" }}>
      <button onClick={onBack} className="mono" style={{ display: "inline-flex", alignItems: "center", gap: 7, background: "var(--paper)", border: "1px solid var(--line-2)", color: "var(--ink-2)", padding: "6px 11px", fontSize: 10.5, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, cursor: "pointer", marginBottom: 20 }}>
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M15 18l-6-6 6-6" /></svg>
        All deals
      </button>

      <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16, flexWrap: "wrap" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 14, minWidth: 0 }}>
          <div style={{ display: "grid", placeItems: "center", width: 46, height: 46, background: `var(--${deal.type})`, flexShrink: 0 }}>
            <svg width="22" height="22" viewBox="0 0 10 10"><path d="M1 5 L5 1.3 L9 5 L9 9 L1 9 Z" fill="var(--paper)" /></svg>
          </div>
          <div style={{ minWidth: 0 }}>
            <Eyebrow style={{ marginBottom: 5 }}>Deal evidence chain</Eyebrow>
            <h1 style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 26, letterSpacing: "-0.03em", margin: 0, lineHeight: 1 }}>{deal.address}</h1>
            <div className="mono" style={{ fontSize: 10.5, color: "var(--ink-3)", letterSpacing: "0.04em", marginTop: 5 }}>
              {deal.city} · {TYPE_LABEL[deal.type]} · {deal.side === "listing" ? "Listing" : "Buyer"}-side
            </div>
          </div>
        </div>
        <div style={{ textAlign: "right" }}>
          <div style={{ fontFamily: "Prompt", fontWeight: 700, fontSize: 22, letterSpacing: "-0.02em", color: isClosed ? "var(--good-ink)" : "var(--accent-ink)" }}>{isClosed ? "Closed" : `Stage ${cur}/6`}</div>
          <div className="mono" style={{ fontSize: 9.5, color: "var(--mute)", marginTop: 3 }}>{labels[cur - 1]}</div>
        </div>
      </div>

      <div style={{ marginTop: 22 }}>
        {chain.map((s, i) => {
          const last = i === chain.length - 1;
          const nodeColor = s.status === "done" ? "var(--good)" : s.status === "current" ? "var(--accent)" : "var(--line-2)";
          return (
            <div key={s.n} style={{ display: "flex", gap: 16, alignItems: "stretch" }}>
              <div style={{ display: "flex", flexDirection: "column", alignItems: "center", width: 24 }}>
                <div style={{ width: 24, height: 24, borderRadius: "50%", flexShrink: 0, display: "grid", placeItems: "center", background: s.status === "upcoming" ? "var(--paper)" : nodeColor, border: `2px solid ${nodeColor}` }}>
                  {s.status === "done"
                    ? <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="var(--paper)" strokeWidth="3"><path d="M5 12l5 5L20 7" /></svg>
                    : s.status === "current"
                      ? <span style={{ width: 7, height: 7, borderRadius: "50%", background: "var(--paper)" }} />
                      : <span className="mono" style={{ fontSize: 10, fontWeight: 700, color: "var(--mute)" }}>{s.n}</span>}
                </div>
                {!last && <div style={{ flex: 1, width: 2, background: s.status === "done" ? "var(--good)" : "var(--line)", minHeight: 18 }} />}
              </div>

              <div style={{ flex: 1, minWidth: 0, paddingBottom: last ? 0 : 22 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 9, flexWrap: "wrap" }}>
                  <span style={{ fontFamily: "Prompt", fontWeight: 600, fontSize: 16, letterSpacing: "-0.01em", color: s.status === "upcoming" ? "var(--mute)" : "var(--ink)" }}>{s.title}</span>
                  <span className="mono" style={{ fontSize: 8.5, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, color: "var(--ink-3)", border: "1px solid var(--line)", background: "var(--paper-2)", padding: "2px 6px" }}>{s.tag}</span>
                  {s.status === "current" && <span className="mono" style={{ fontSize: 8.5, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600, color: "var(--accent-ink)", border: "1px solid var(--accent)", background: "var(--accent-tint)", padding: "2px 6px" }}>Current</span>}
                  {s.date && <span className="mono" style={{ marginLeft: "auto", fontSize: 10, color: "var(--mute)", fontVariantNumeric: "tabular-nums" }}>{s.date}</span>}
                </div>

                {s.status === "upcoming" ? (
                  <div className="mono" style={{ fontSize: 11, color: "var(--mute)", marginTop: 8, lineHeight: 1.5, border: "1px dashed var(--line-2)", padding: "8px 11px" }}>
                    Awaiting — {s.expect}
                  </div>
                ) : (
                  <div style={{ marginTop: 9, border: "1px solid var(--line)", background: "var(--paper)" }}>
                    {s.ev.map((e, ei) => {
                      const m = EV_META[e.src] || EV_META.email;
                      return (
                        <div key={ei} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 12px", borderTop: ei > 0 ? "1px solid var(--line)" : "none" }}>
                          <EvIcon src={e.src} />
                          <span className="mono" style={{ fontSize: 8, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 700, color: m.stroke, width: 56, flexShrink: 0 }}>{m.label}</span>
                          <span style={{ flex: 1, minWidth: 0, fontSize: 13, color: "var(--ink-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{e.label}</span>
                          {e.meta && <span className="mono" style={{ fontSize: 9.5, color: "var(--mute)", flexShrink: 0 }}>{e.meta}</span>}
                        </div>
                      );
                    })}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function AgentEvalScreen({ deal, onBack }) {
  return deal ? <DealChain deal={deal} onBack={onBack} /> : <AgentEvalDashboard />;
}

function AgentEvalDashboard() {
  // The period toggle (12 months / Quarter / Month) scales the count-based cards;
  // rates (avg days, retention, contact→signed %) and the peer benchmark hold.
  const [period, setPeriod] = useState(AE_PERIODS[0]);
  const scale = period.scale;
  const funnel = AE_FUNNEL.map((s) => ({ ...s, n: Math.max(1, Math.round(s.n * scale)), reasons: s.reasons.map(([t, n]) => [t, Math.max(1, Math.round(n * scale))]) }));
  const outcomes = AE_OUTCOMES.map((o) => ({ ...o, n: Math.max(0, Math.round(o.n * scale)) }));
  const f0 = funnel[0].n, signedN = funnel[funnel.length - 1].n;
  const statCards = [
    { label: "Active pipeline", value: "31", sub: "Live deals in flight", spark: [18, 20, 19, 23, 25, 24, 27, 29, 28, 31], sparkColor: "var(--ink)" },
    { label: "Signed · " + period.label.toLowerCase(), value: String(signedN), sub: period.sub, tone: "accent", spark: [22, 26, 28, 31, 34, 36, 40, 42, 45, 47], sparkColor: "var(--accent)" },
    { label: "Avg days to close", value: "34", sub: "↓ 11 vs. market avg", tone: "good", spark: [49, 47, 45, 44, 42, 40, 38, 37, 35, 34], sparkColor: "var(--good)" },
    { label: "Contact → signed", value: Math.round((signedN / f0) * 100) + "%", sub: `${signedN} of ${f0} conversations`, spark: [28, 29, 30, 31, 30, 32, 31, 33, 32, 33], sparkColor: "var(--ink)" },
    { label: "Client retention", value: "68%", sub: "Top quartile in cohort", spark: [58, 60, 61, 63, 64, 65, 66, 67, 67, 68], sparkColor: "var(--good)" },
  ];
  return (
    <div style={{ maxWidth: 1280, margin: "0 auto", padding: "26px 32px 50px" }}>
      <AEPageHead period={period} setPeriod={setPeriod} />
      <AESearchPanel />
      <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 16 }}>
        {statCards.map((s) => <AEStatCard key={s.label} {...s} />)}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap: 20, marginTop: 20 }}>
        <div style={{ gridColumn: "span 7", minWidth: 0 }}><AEFunnelCard funnel={funnel} /></div>
        <div style={{ gridColumn: "span 5", minWidth: 0 }}><AEAfterSigningCard funnel={funnel} outcomes={outcomes} /></div>
        <div style={{ gridColumn: "span 12", minWidth: 0 }}><AEPeerCard /></div>
      </div>
      <div className="mono" style={{ marginTop: 28, paddingTop: 18, borderTop: "1px solid var(--hair)", fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.06em", display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
        <Mark size={16} /> © 2026 Activus AI · AgentEval · private to you
      </div>
    </div>
  );
}

/* ====================================================================== */
/*  DEMO BAR (simple Agent/Client toggle + AgentEval entry)                */
/* ====================================================================== */
function DemoBar({ profileMode, onPickMode, inEval, onToggleEval }) {
  const seg = (m, label) => {
    const on = profileMode === m && !inEval;
    return (
      <button key={m} onClick={() => onPickMode(m)} className="mono" style={{
        appearance: "none", border: "none", cursor: "pointer", padding: "5px 15px", fontSize: 11, fontWeight: 600,
        letterSpacing: "0.06em", textTransform: "uppercase", transition: "all 0.14s",
        background: on ? "var(--ink)" : "transparent", color: on ? "var(--paper)" : "var(--ink-3)",
      }}>{label}</button>
    );
  };
  return (
    <header style={{
      position: "sticky", top: 0, zIndex: 50, minHeight: 46,
      background: "var(--paper)", borderBottom: "1px solid var(--line)",
      display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16, flexWrap: "wrap", padding: "8px 22px",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 11, minWidth: 0 }}>
        <span className="mono" style={{ fontSize: 9.5, fontWeight: 700, letterSpacing: "0.16em", textTransform: "uppercase", border: "1px solid var(--ink)", padding: "3px 7px" }}>Demo</span>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <div role="tablist" aria-label="View" style={{ display: "inline-flex", border: "1px solid var(--line-2)", borderRadius: 999, overflow: "hidden", background: "var(--paper)" }}>
          {seg("agent", "Agent")}{seg("client", "Client")}
        </div>
        <button onClick={onToggleEval} className="mono" style={{
          padding: "6px 14px", fontSize: 11, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", cursor: "pointer",
          border: "1px solid var(--accent-ink)", borderRadius: 999,
          background: inEval ? "var(--accent-ink)" : "var(--paper)", color: inEval ? "var(--paper)" : "var(--accent-ink)",
        }}>{inEval ? "← Back to profile" : "AgentEval →"}</button>
      </div>
    </header>
  );
}

/* ====================================================================== */
/*  APP                                                                     */
/* ====================================================================== */
function App() {
  // Land in the agent view; AgentEval and the client toggle are reachable from
  // the demo bar.
  const [profileMode, setProfileMode] = useState("agent");
  const [inEval, setInEval] = useState(false);
  const [evalDeal, setEvalDeal] = useState(null);
  const showOwnerView = profileMode === "agent";
  const pickMode = (m) => { setProfileMode(m); setInEval(false); setEvalDeal(null); };
  const toggleEval = () => { setEvalDeal(null); setInEval((v) => !v); };

  return (
    <div className="ai-page">
      <style dangerouslySetInnerHTML={{ __html: THEME_CSS }} />
      <DemoBar profileMode={profileMode} onPickMode={pickMode} inEval={inEval} onToggleEval={toggleEval} />
      {inEval
        ? <AgentEvalScreen deal={evalDeal} onBack={() => setEvalDeal(null)} />
        : <ProfileBody data={DATA} showOwnerView={showOwnerView} onOpenEval={(deal) => { setEvalDeal(deal); setInEval(true); }} />}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
