/* ------------------------------------------------------------------ */
/*  ACTIVITY GRID ALTERNATIVES                                          */
/*  4 proptech-appropriate visualizations of weekly activity            */
/* ------------------------------------------------------------------ */

// Deterministic pseudo-random for stable grid
function seeded(seed) {
  let s = seed;
  return () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };
}

// Generate 52 weeks × 7 days, each cell: {intensity 0-4, dealType}
function makeActivityData() {
  const r = seeded(71);
  const weeks = 52, days = 7;
  const types = ["precon", "resale", "luxury", "investor"];
  const cells = [];
  for (let w = 0; w < weeks; w++) {
    const weekCells = [];
    // varying density per week
    const base = 0.35 + 0.45 * Math.sin(w / 9) + 0.2 * r();
    for (let d = 0; d < days; d++) {
      let intensity = 0;
      // fewer weekend
      const weekendMul = (d === 0 || d === 6) ? 0.4 : 1;
      const v = r() * base * weekendMul;
      if (v > 0.08) intensity = 1;
      if (v > 0.22) intensity = 2;
      if (v > 0.38) intensity = 3;
      if (v > 0.55) intensity = 4;
      // bias dealType by "specialization" — 60% precon
      const roll = r();
      let type;
      if (roll < 0.6) type = "precon";
      else if (roll < 0.82) type = "resale";
      else if (roll < 0.93) type = "luxury";
      else type = "investor";
      weekCells.push({ intensity, type });
    }
    cells.push(weekCells);
  }
  return cells;
}

// Generate 12 months × N houses per month. Each house represents ONE classified activity.
// Non-chronological within a month (randomized position), privacy-safe.
// The daily-refresh seed nudge makes the mix feel alive without exposing real order.
function makeMonthlyActivityData() {
  const daySeed = Math.floor(Date.now() / (24 * 3600 * 1000)); // rolls once per UTC day
  const r = seeded(71 + daySeed);
  const months = 12;
  // Counts roughly mirror busy vs quiet months
  const monthCounts = [38, 42, 36, 48, 30, 34, 52, 46, 40, 44, 38, 50];

  // Per-type pools of generic activity labels — randomly assigned per house
  const activityPool = {
    precon: [
      ["Builder site visit", 1],
      ["Buyer consult · pre-con", 2],
      ["Deposit follow-up", 1],
      ["Assignment contract review", 2],
      ["VIP release thread", 1],
      ["Floorplan walkthrough", 1],
      ["Platinum access arranged", 1],
      ["Deposit structure explained", 2],
      ["Mortgage broker intro", 1],
      ["Builder Q&A · occupancy timing", 2],
    ],
    resale: [
      ["Property tour · 2-bed condo", 2],
      ["Property tour · detached", 2],
      ["Offer review", 1],
      ["Listing prep walkthrough", 1],
      ["Buyer consult · first-time", 2],
      ["Comparable market analysis", 1],
      ["Staging vendor coordination", 1],
      ["Multiple-offer strategy call", 2],
      ["Home inspection attended", 1],
      ["Counter-offer thread", 2],
    ],
    luxury: [
      ["Private showing · detached", 1],
      ["Past-client relationship check-in", 1],
      ["Concierge service intro", 1],
      ["Off-market preview", 1],
      ["Luxury brokerage counter-party thread", 2],
      ["White-glove closing coordination", 1],
      ["Architect walkthrough", 2],
      ["Referral from past client", 1],
      ["Neighborhood-expert meeting", 1],
    ],
    investor: [
      ["Portfolio cap-rate review", 1],
      ["Assignment contract thread", 2],
      ["Rental comparables pulled", 1],
      ["Cash flow projection review", 1],
      ["Cash-flow pro-forma review", 2],
      ["Building inspection for yield", 1],
      ["Tenant turnover consult", 2],
      ["Second-property financing call", 1],
    ],
  };

  const data = [];
  for (let m = 0; m < months; m++) {
    const n = monthCounts[m];
    const houses = [];
    for (let i = 0; i < n; i++) {
      // Deal-type distribution ~60% precon, 22% resale, 11% luxury, 7% investor
      const roll = r();
      let type;
      if (roll < 0.60) type = "precon";
      else if (roll < 0.82) type = "resale";
      else if (roll < 0.93) type = "luxury";
      else type = "investor";

      // Pick a specific activity label from the pool for this house
      const pool = activityPool[type];
      const pick = pool[Math.floor(r() * pool.length)];
      const [label, baseEvents] = pick;

      // Add small noise to event count: base ± 0..2 extra events
      const events = Math.max(1, baseEvents + Math.floor(r() * 3));
      // Intensity visually mirrors events: 1→low, 2→mid, 3+→high
      const intensity = events <= 1 ? 2 : events <= 2 ? 3 : 4;

      houses.push({ intensity, type, label, events });
    }
    // Shuffle so order in a month isn't chronological — privacy + aesthetic
    for (let i = houses.length - 1; i > 0; i--) {
      const j = Math.floor(r() * (i + 1));
      [houses[i], houses[j]] = [houses[j], houses[i]];
    }
    data.push(houses);
  }
  return data;
}

const MONTHS = ["May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan","Feb","Mar","Apr"];
const DAYS = ["Mon","Wed","Fri"];
const DAYS_FULL = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];

function intensityColor(i, type, showType) {
  if (i === 0) return "var(--paper-2)";
  if (!showType) {
    const alphas = [0, 0.22, 0.45, 0.7, 1];
    return `color-mix(in oklab, var(--accent) ${alphas[i] * 100}%, var(--paper))`;
  }
  const base = `var(--${type})`;
  const alphas = [0, 0.2, 0.42, 0.68, 1];
  return `color-mix(in oklab, ${base} ${alphas[i] * 100}%, var(--paper))`;
}

/* --- Style 1: Squares (baseline, for comparison) -------------------- */
function GridSquares({ data, showType }) {
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(52, 1fr)", gap: 3 }}>
      {data.map((week, wi) => (
        <div key={wi} style={{ display: "grid", gridTemplateRows: "repeat(7, 1fr)", gap: 3 }}>
          {week.map((c, di) => (
            <div key={di} style={{
              width: "100%", aspectRatio: "1",
              background: intensityColor(c.intensity, c.type, showType),
              borderRadius: 2,
              border: "1px solid color-mix(in oklab, var(--line) 60%, transparent)",
            }} title={`intensity ${c.intensity} · ${c.type}`} />
          ))}
        </div>
      ))}
    </div>
  );
}

/* --- Style 2: House glyphs --------------------------------------- */
function HouseCell({ intensity, type, showType, size }) {
  const fill = intensityColor(intensity, type, showType);
  const stroke = intensity === 0 ? "var(--line)" : "color-mix(in oklab, " + (showType ? `var(--${type})` : "var(--accent)") + " 50%, black)";
  return (
    <svg width={size} height={size} viewBox="0 0 10 10" style={{ display: "block" }}>
      {/* house silhouette */}
      <path d="M1 5 L5 1.3 L9 5 L9 9 L1 9 Z" fill={fill} stroke={intensity === 0 ? "var(--line)" : "none"} strokeWidth="0.3" />
      {intensity > 0 && <rect x="4.2" y="6.2" width="1.6" height="2.8" fill="var(--paper)" opacity="0.75" />}
    </svg>
  );
}
function GridHouses({ data, showType, onCellClick, selectedKey, cellRefs }) {
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(52, 1fr)", gap: 2 }}>
      {data.map((week, wi) => (
        <div key={wi} style={{ display: "grid", gridTemplateRows: "repeat(7, 1fr)", gap: 2 }}>
          {week.map((c, di) => {
            const key = `${wi}-${di}`;
            const selected = selectedKey === key;
            return (
              <div key={di}
                ref={el => { if (cellRefs) cellRefs.current[key] = el; }}
                onClick={() => onCellClick && c.intensity > 0 && onCellClick(key, c, wi, di)}
                style={{
                  cursor: c.intensity > 0 && onCellClick ? "pointer" : "default",
                  position: "relative",
                  outline: selected ? `2px solid var(--accent)` : "none",
                  outlineOffset: 1,
                  borderRadius: 2,
                  zIndex: selected ? 5 : 1,
                }}>
                <HouseCell intensity={c.intensity} type={c.type} showType={showType} size="100%" />
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

/* --- Style: Monthly house blocks ----------------------------------- */
/* 12 blocks, one per month. Each block is a flex-wrap of houses.       */
/* Non-chronological within a month. Privacy-safe.                      */
function GridMonths({ data, showType, onCellClick, selectedKey, cellRefs }) {
  return (
    <div className="grid-months" style={{
      display: "grid",
      gridTemplateColumns: "repeat(6, 1fr)",
      columnGap: 14,
      rowGap: 12,
    }}>
      {data.map((houses, mi) => (
        <div key={mi} style={{ display: "flex", flexDirection: "column", gap: 4 }}>
          <div className="mono" style={{
            fontSize: 9, color: "var(--mute)", letterSpacing: "0.06em",
            textTransform: "uppercase",
          }}>
            {MONTHS[mi]} <span style={{ color: "var(--line-2)" }}>· {houses.length}</span>
          </div>
          <div style={{
            display: "grid",
            gridTemplateColumns: "repeat(6, 1fr)",
            gap: 2,
          }}>
            {houses.slice(0, 18).map((c, hi) => {
              const key = `m${mi}-${hi}`;
              const selected = selectedKey === key;
              return (
                <div key={hi}
                  ref={el => { if (cellRefs) cellRefs.current[key] = el; }}
                  onClick={() => onCellClick && onCellClick(key, c, mi, hi)}
                  style={{
                    cursor: onCellClick ? "pointer" : "default",
                    position: "relative",
                    outline: selected ? `2px solid var(--accent)` : "none",
                    outlineOffset: 1,
                    borderRadius: 2,
                    zIndex: selected ? 5 : 1,
                  }}>
                  <HouseCell intensity={c.intensity} type={c.type} showType={showType} size="100%" />
                </div>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
}

/* --- Style 3: Floor-plan blocks (cells grow to fill when active) --- */
function GridFloorplan({ data, showType }) {
  // each week is a column, cells are sized by intensity (like rooms in a floor plan)
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(52, 1fr)", gap: 3 }}>
      {data.map((week, wi) => (
        <div key={wi} style={{ display: "grid", gridTemplateRows: "repeat(7, 1fr)", gap: 3 }}>
          {week.map((c, di) => {
            const pad = [4, 3, 2, 1, 0][c.intensity];
            return (
              <div key={di} style={{
                position: "relative",
                width: "100%", aspectRatio: "1",
                background: "var(--paper-2)",
                borderRadius: 2,
              }}>
                {c.intensity > 0 && (
                  <div style={{
                    position: "absolute", inset: `${pad}px`,
                    background: intensityColor(c.intensity, c.type, showType),
                    borderRadius: 1,
                    boxShadow: c.intensity >= 3 ? `inset 0 0 0 1px color-mix(in oklab, var(--${c.type}) 50%, black)` : "none",
                  }} />
                )}
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

/* --- Style 4: Deal bars — activity is segmented bar per day -------- */
function GridDealBars({ data, showType }) {
  // each day cell shows a vertical bar broken into deal-type segments based on intensity+type
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(52, 1fr)", gap: 3 }}>
      {data.map((week, wi) => (
        <div key={wi} style={{ display: "grid", gridTemplateRows: "repeat(7, 1fr)", gap: 3 }}>
          {week.map((c, di) => {
            const h = [0, 25, 50, 75, 100][c.intensity];
            const color = showType ? `var(--${c.type})` : "var(--accent)";
            return (
              <div key={di} style={{
                width: "100%", aspectRatio: "1",
                background: "var(--paper-2)",
                borderRadius: 2,
                display: "flex", alignItems: "flex-end",
                overflow: "hidden",
              }}>
                <div style={{ width: "100%", height: `${h}%`, background: color }} />
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

/* --- Grid wrapper with labels ------------------------------------ */
function ActivityGrid({ style, showType }) {
  // Monthly view is the default for house/keys styles; other styles still use the weekly data
  const isMonthly = (style === "houses" || style === "keys");
  const data = useMemo(() => isMonthly ? makeMonthlyActivityData() : makeActivityData(), [isMonthly]);
  const [selected, setSelected] = useState(null); // { key, cell, wi, di } — wi=month idx, di=house idx in monthly mode
  const [cursor, setCursor] = useState(null);
  const containerRef = useRef(null);
  const cellRefs = useRef({});
  const demoActiveRef = useRef(true);
  const demoTimeouts = useRef([]);

  const Cmp = {
    squares: GridSquares,
    houses: GridMonths,
    floorplan: GridFloorplan,
    dealbars: GridDealBars,
    keys: GridMonths,
  }[style] || GridFloorplan;

  const handleCellClick = (key, cell, wi, di) => {
    setSelected(prev => prev && prev.key === key ? null : { key, cell, wi, di });
  };

  // Cancel the demo the moment the user's real mouse enters the grid
  const cancelDemo = () => {
    if (!demoActiveRef.current) return;
    demoActiveRef.current = false;
    demoTimeouts.current.forEach(t => clearTimeout(t));
    demoTimeouts.current = [];
    setCursor(null);
    setSelected(null);
  };

  // Wait until the grid scrolls into view before starting the demo
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    if (!containerRef.current || !("IntersectionObserver" in window)) { setVisible(true); return; }
    const io = new IntersectionObserver((entries) => {
      if (entries.some(e => e.isIntersecting && e.intersectionRatio >= 0.4)) {
        setVisible(true);
        io.disconnect();
      }
    }, { threshold: [0, 0.4, 0.8] });
    io.observe(containerRef.current);
    return () => io.disconnect();
  }, []);

  // Auto-run cursor demo once the grid mounts (houses/keys style only)
  useEffect(() => {
    if (!visible) return;
    if (!(style === "houses" || style === "keys")) return;
    const schedule = (fn, delay) => {
      const t = setTimeout(() => { if (demoActiveRef.current) fn(); }, delay);
      demoTimeouts.current.push(t);
    };
    // Gather a pool of houses across months to demo; spread picks across different months and types
    const picks = [];
    const seen = new Set();
    for (let mi = 0; mi < data.length; mi++) {
      const month = data[mi];
      const limit = Math.min(month.length, 18);
      for (let hi = 0; hi < limit; hi++) {
        const cell = month[hi];
        const key = `m${mi}-${hi}`;
        if (cell.intensity >= 3 && !seen.has(key)) {
          picks.push({ wi: mi, di: hi, cell, key });
          seen.add(key);
          break; // one per month max
        }
      }
    }
    if (picks.length === 0) return;

    // Shuffle once for a less predictable order
    for (let i = picks.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [picks[i], picks[j]] = [picks[j], picks[i]];
    }

    // Start cursor at a neutral entry point
    schedule(() => {
      if (!containerRef.current) return;
      const rect = containerRef.current.getBoundingClientRect();
      setCursor({ x: rect.width - 40, y: 20, click: false });
    }, 500);

    let t = 900;
    let idx = 0;
    // Schedule one visit, then recursively schedule the next — loops forever until cancelDemo() runs
    const visitNext = () => {
      const p = picks[idx % picks.length];
      idx++;
      schedule(() => {
        const el = cellRefs.current[p.key];
        if (!el || !containerRef.current) return;
        const eRect = el.getBoundingClientRect();
        const cRect = containerRef.current.getBoundingClientRect();
        setCursor({ x: eRect.left - cRect.left + eRect.width / 2, y: eRect.top - cRect.top + eRect.height / 2, click: false });
      }, t);
      t += 200;
      schedule(() => {
        setCursor(c => c ? { ...c, click: true } : c);
        handleCellClick(p.key, p.cell, p.wi, p.di);
      }, t);
      t += 220;
      schedule(() => {
        setCursor(c => c ? { ...c, click: false } : c);
      }, t);
      t += 1600; // let the popover stay readable
      schedule(() => {
        setSelected(null); // close before moving on
        visitNext();
      }, t);
      t += 12;
    };
    visitNext();

    return () => {
      demoActiveRef.current = false;
      demoTimeouts.current.forEach(clearTimeout);
      demoTimeouts.current = [];
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [style, visible]);

  // Client-safe labels — each house now has its own pre-generated label + events count
  const dealTypeLabel = { precon: "Pre-construction", resale: "Re-sale", luxury: "Luxury", investor: "Investor" };
  const activityLabels = {
    precon: [
      "Builder site visit · 1-bed + den",
      "Buyer consultation · pre-con",
      "Deposit structure review",
      "Assignment opportunity thread",
      "Floor plan walkthrough · 2-bed",
      "Occupancy timeline update",
      "Platinum access · VIP release",
      "Amenity package review",
    ],
    resale: [
      "Property tour · 2-bed condo",
      "Offer preparation · detached",
      "Listing prep walkthrough",
      "Buyer consultation · first-time",
      "Comparative market analysis",
      "Staging consultation · townhouse",
      "Open house debrief",
      "Counter-offer review",
    ],
    luxury: [
      "Private showing · detached",
      "Past-client follow-up",
      "Concierge intro",
      "Off-market preview",
      "Estate walkthrough · waterfront",
      "Exclusive listing consultation",
      "Architectural review · custom build",
      "High-net-worth referral intro",
    ],
    investor: [
      "Portfolio cap-rate review",
      "Assignment contract thread",
      "Rental comparables · 3-unit",
      "Cash flow projection review",
      "Duplex inspection walkthrough",
      "Pre-construction assignment flip",
      "Multi-family acquisition intro",
      "Tenant turnover strategy",
    ],
  };
  const hashKey = (k) => { let h = 0; for (let i = 0; i < k.length; i++) h = (h * 31 + k.charCodeAt(i)) >>> 0; return h; };

  return (
    <div ref={containerRef}
      onMouseEnter={cancelDemo}
      onTouchStart={cancelDemo}
      style={{ position: "relative" }}>
      {isMonthly ? (
        <Cmp data={data} showType={showType} onCellClick={handleCellClick} selectedKey={selected?.key} cellRefs={cellRefs} />
      ) : (
        <>
          {/* month labels (weekly view only) */}
          <div style={{ display: "grid", gridTemplateColumns: "32px 1fr", gap: 8 }}>
            <div />
            <div className="mono" style={{ display: "grid", gridTemplateColumns: "repeat(12, 1fr)", fontSize: 10, color: "var(--mute)", marginBottom: 6 }}>
              {MONTHS.map(m => <div key={m}>{m}</div>)}
            </div>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "32px 1fr", gap: 8, alignItems: "start" }}>
            <div className="mono" style={{ display: "grid", gridTemplateRows: "repeat(7, 1fr)", fontSize: 10, color: "var(--mute)", gap: 2, paddingTop: 2 }}>
              <div /><div>Mon</div><div /><div>Wed</div><div /><div>Fri</div><div />
            </div>
            <Cmp data={data} showType={showType} onCellClick={handleCellClick} selectedKey={selected?.key} cellRefs={cellRefs} />
          </div>
        </>
      )}

      {/* Demo cursor — fades once user moves their mouse into the grid */}
      {cursor && (
        <div style={{
          position: "absolute",
          left: cursor.x,
          top: cursor.y,
          pointerEvents: "none",
          transition: "left 200ms cubic-bezier(.4,.8,.3,1), top 200ms cubic-bezier(.4,.8,.3,1), transform 120ms ease, opacity 300ms ease",
          transform: cursor.click ? "scale(0.88)" : "scale(1)",
          zIndex: 10,
        }}>
          <svg width="22" height="22" viewBox="0 0 24 24" style={{ filter: "drop-shadow(0 2px 3px rgba(0,0,0,0.25))" }}>
            <path d="M3 2 L3 17 L7.5 13.5 L10 19 L12.5 18 L10 12.5 L16 12.5 Z"
              fill="var(--ink)" stroke="var(--paper)" strokeWidth="1.2" strokeLinejoin="round" />
          </svg>
          {cursor.click && (
            <div style={{
              position: "absolute",
              left: 2, top: 2,
              width: 28, height: 28,
              borderRadius: "50%",
              border: "2px solid var(--accent)",
              transform: "translate(-50%, -50%)",
              animation: "cc-ping 400ms ease-out",
            }} />
          )}
        </div>
      )}
      <style>{`@keyframes cc-ping { 0%{transform:translate(-50%,-50%) scale(0.4); opacity:1} 100%{transform:translate(-50%,-50%) scale(1.8); opacity:0} }`}</style>

      {/* hint strip */}
      <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 10, display: "flex", alignItems: "center", gap: 6 }}>
        <span style={{ width: 6, height: 6, background: "var(--accent)", borderRadius: "50%" }} />
        each house is one classified activity · click any house to inspect
      </div>

      {/* Inline popover — opens just below the grid when a cell is selected */}
      {selected && (
        <div style={{
          marginTop: 14,
          background: "var(--paper)",
          border: "1px solid var(--accent-tint-2)",
          borderRadius: 10,
          padding: 14,
          boxShadow: "0 10px 30px -12px color-mix(in oklab, var(--accent) 40%, transparent)",
        }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 12 }}>
            <div>
              <div className="mono" style={{ fontSize: 10, color: "var(--mute)", letterSpacing: "0.06em" }}>
                {isMonthly ? MONTHS[selected.wi] : `week ${selected.wi + 1} · ${DAYS_FULL[selected.di]}`}
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 4 }}>
                <span style={{ width: 10, height: 10, background: `var(--${selected.cell.type})`, borderRadius: 2 }} />
                <div style={{ fontFamily: "Prompt, sans-serif", fontSize: 18, fontWeight: 500 }}>
                  {isMonthly && selected.cell.label
                    ? selected.cell.label
                    : `${dealTypeLabel[selected.cell.type]} · ${selected.cell.intensity} activities`}
                </div>
              </div>
            </div>
            <button onClick={() => setSelected(null)} className="mono"
              style={{ background: "none", border: "none", cursor: "pointer", color: "var(--mute)", fontSize: 14 }}>×</button>
          </div>
          <div style={{ marginTop: 12, display: "flex", flexDirection: "column", gap: 6 }}>
            {isMonthly ? (
              <>
                {(() => {
                  const acts = activityLabels[selected.cell.type] || [];
                  const h = hashKey(selected.key);
                  const a1 = acts[h % acts.length];
                  const a2 = acts[(h + 1) % acts.length];
                  return (
                    <>
                      <div style={{ display: "flex", gap: 10, fontSize: 13, alignItems: "center", padding: "8px 10px", background: "var(--paper-2)", borderRadius: 6 }}>
                        <span style={{ width: 2, height: 14, background: `var(--${selected.cell.type})`, borderRadius: 1 }} />
                        <span style={{ flex: 1, color: "var(--ink)" }}><b>{dealTypeLabel[selected.cell.type]}</b></span>
                      </div>
                      <div style={{ display: "flex", gap: 10, fontSize: 13, alignItems: "center", padding: "8px 10px", background: "var(--paper-2)", borderRadius: 6 }}>
                        <span style={{ width: 2, height: 14, background: `var(--${selected.cell.type})`, borderRadius: 1 }} />
                        <span style={{ flex: 1, color: "var(--ink)" }}>{a1}</span>
                      </div>
                      <div style={{ display: "flex", gap: 10, fontSize: 13, alignItems: "center", padding: "8px 10px", background: "var(--paper-2)", borderRadius: 6 }}>
                        <span style={{ width: 2, height: 14, background: `var(--${selected.cell.type})`, borderRadius: 1 }} />
                        <span style={{ flex: 1, color: "var(--ink)" }}>{a2}</span>
                      </div>
                      <div style={{ display: "flex", gap: 10, fontSize: 13, alignItems: "center", padding: "8px 10px", background: "var(--paper-2)", borderRadius: 6 }}>
                        <span style={{ width: 2, height: 14, background: `var(--${selected.cell.type})`, borderRadius: 1 }} />
                        <span style={{ flex: 1, color: "var(--ink)" }}>
                          {selected.cell.events} cross-source signal{selected.cell.events === 1 ? "" : "s"}
                          <span className="mono" style={{ fontSize: 10, color: "var(--mute)", marginLeft: 6 }}>
                            (calendar + email)
                          </span>
                        </span>
                      </div>
                    </>
                  );
                })()}
              </>
            ) : null}
          </div>
          <div className="mono" style={{ fontSize: 10, color: "var(--mute)", marginTop: 10 }}>
            // generalized activity · no timestamps, client names or addresses shown publicly
          </div>
        </div>
      )}
      {/* legend — placed well below the grid so chips don't overlap the last row */}
      <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 28, paddingTop: 14, borderTop: "1px dashed var(--line)" }}>
        {showType && (
          <div style={{ display: "flex", gap: 14, flexWrap: "wrap" }}>
            {[
              ["precon", "Pre-construction"],
              ["resale", "Re-sale"],
              ["luxury", "Luxury"],
              ["investor", "Investor"],
            ].map(([k, label]) => (
              <div key={k} className="mono" style={{ fontSize: 11, display: "flex", alignItems: "center", gap: 6, color: "var(--ink-2)" }}>
                <span style={{ width: 10, height: 10, background: `var(--${k})`, borderRadius: 2, display: "inline-block" }} />
                {label}
              </div>
            ))}
          </div>
        )}
        <div className="mono" style={{ fontSize: 11, color: "var(--mute)", display: "flex", alignItems: "center", gap: 6, alignSelf: "flex-end" }}>
          less
          {[1,2,3,4].map(i => (
            <span key={i} style={{ width: 10, height: 10, background: intensityColor(i, "precon", false), borderRadius: 2, display: "inline-block" }} />
          ))}
          more
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ActivityGrid, makeActivityData, MONTHS, DAYS });
