/* global React */
/* Stats charts - Holt's real GitHub stats, hand-drawn SVG (no Chart.js).
   Exposes window.HoltStats = { StatsSection, StatsData } */
const { useMemo, useState, useRef, useEffect } = React;

/* ── Real data ─────────────────────────────────────────────────────────── */
const PLATFORM_DAILY = {"2025-10-13":14,"2025-10-14":4,"2025-11-07":16,"2025-11-08":24,"2025-11-10":14,"2025-11-11":10,"2025-11-12":2,"2025-11-13":4,"2025-11-17":4,"2025-12-08":8,"2025-12-09":2,"2026-01-02":18,"2026-01-03":8,"2026-01-05":2,"2026-01-06":2,"2026-01-07":2,"2026-01-09":4,"2026-01-15":4,"2026-01-26":4,"2026-01-30":6,"2026-01-31":56,"2026-02-01":138,"2026-02-02":2,"2026-02-03":225,"2026-02-04":20,"2026-02-05":52,"2026-02-06":148,"2026-02-07":26,"2026-02-08":28,"2026-02-09":102,"2026-02-10":83,"2026-02-11":86,"2026-02-12":58,"2026-02-13":85,"2026-02-15":34,"2026-02-16":6,"2026-02-17":66,"2026-02-18":190,"2026-02-19":156,"2026-02-20":90,"2026-02-21":120,"2026-02-22":46,"2026-02-23":55,"2026-02-24":130,"2026-02-25":120,"2026-02-26":232,"2026-02-27":108,"2026-02-28":56,"2026-03-01":66,"2026-03-02":282,"2026-03-03":292,"2026-03-04":278,"2026-03-05":72,"2026-03-06":74,"2026-03-07":14,"2026-03-08":47,"2026-03-09":36,"2026-03-10":84,"2026-03-11":138,"2026-03-12":141,"2026-03-13":94,"2026-03-14":7,"2026-03-15":22,"2026-03-16":11,"2026-03-17":5,"2026-03-18":111,"2026-03-19":97,"2026-03-20":72,"2026-03-21":77,"2026-03-22":145,"2026-03-23":31,"2026-03-24":169,"2026-03-26":67,"2026-03-27":101,"2026-03-28":162,"2026-03-29":25,"2026-03-30":66,"2026-03-31":83,"2026-04-01":113,"2026-04-02":121,"2026-04-03":133,"2026-04-04":31,"2026-04-05":43,"2026-04-06":28,"2026-04-07":52,"2026-04-08":50,"2026-04-09":44,"2026-04-10":23,"2026-04-11":62,"2026-04-12":48,"2026-04-13":29,"2026-04-14":55,"2026-04-15":94,"2026-04-16":52,"2026-04-17":131,"2026-04-18":35,"2026-04-19":15,"2026-04-20":53,"2026-04-21":10,"2026-04-22":15,"2026-04-23":2,"2026-04-24":13,"2026-04-25":18,"2026-04-26":9,"2026-04-27":17,"2026-04-28":154,"2026-04-29":49,"2026-04-30":142,"2026-05-01":77,"2026-05-02":63,"2026-05-03":155,"2026-05-04":124,"2026-05-05":37,"2026-05-06":41,"2026-05-07":27,"2026-05-08":133,"2026-05-09":30,"2026-05-10":93,"2026-05-11":54,"2026-05-14":102,"2026-05-15":111,"2026-05-16":208,"2026-05-17":180,"2026-05-18":149,"2026-05-19":71,"2026-05-20":78,"2026-05-21":47,"2026-05-22":61,"2026-05-23":49,"2026-05-24":43,"2026-05-25":130,"2026-05-26":123,"2026-05-27":2};

const WORKSPACE_DAILY = {"2025-08-19":13,"2025-08-20":18,"2025-08-21":14,"2025-08-22":12,"2025-08-23":4,"2025-08-24":6,"2025-08-25":68,"2025-08-26":87,"2025-08-27":27,"2025-08-28":33,"2025-08-29":95,"2025-08-30":23,"2025-08-31":60,"2025-09-01":49,"2025-09-02":85,"2025-09-03":36,"2025-09-18":48,"2025-09-19":92,"2025-09-20":64,"2025-09-21":10,"2025-09-22":2,"2025-09-23":37,"2025-09-24":51,"2025-09-25":71,"2025-09-26":45,"2025-09-27":5,"2025-09-28":50,"2025-09-29":81,"2025-09-30":93,"2025-10-01":105,"2025-10-02":105,"2025-10-03":57,"2025-10-04":136,"2025-10-05":93,"2025-10-06":44,"2025-10-07":65,"2025-10-08":38,"2025-10-09":35,"2025-10-13":1,"2025-10-17":5,"2025-11-03":76,"2025-11-04":46,"2025-11-05":77,"2025-11-06":51,"2025-11-07":63,"2025-11-08":67,"2025-11-09":16,"2025-11-10":47,"2025-11-11":43,"2025-11-12":39,"2025-11-13":19,"2025-11-14":46,"2025-11-15":12,"2025-11-16":20,"2025-11-17":24,"2025-11-18":19,"2025-11-19":46,"2025-11-20":22,"2025-11-21":25,"2025-11-22":16,"2025-11-23":12,"2025-11-24":27,"2025-11-25":45,"2025-11-26":10,"2025-11-27":45,"2025-11-28":30,"2025-11-29":5,"2025-12-01":1,"2025-12-03":12,"2025-12-04":1,"2025-12-05":56,"2025-12-06":12,"2025-12-07":6,"2025-12-08":2,"2025-12-09":4,"2025-12-10":12,"2025-12-11":14,"2025-12-12":11,"2025-12-13":4,"2025-12-17":2,"2025-12-24":33,"2025-12-25":7,"2026-01-02":5,"2026-01-15":3,"2026-01-18":1,"2026-01-19":4,"2026-01-20":13,"2026-01-22":63,"2026-01-27":14,"2026-01-28":31,"2026-01-29":36,"2026-01-30":5,"2026-01-31":18,"2026-02-01":40,"2026-02-02":11,"2026-02-04":10,"2026-02-05":9,"2026-02-28":24,"2026-03-23":18,"2026-03-24":2};

const PLATFORM_MONTHLY = {"2025-10":88,"2025-11":90,"2025-12":10,"2026-01":106,"2026-02":2462,"2026-03":2869,"2026-04":1641,"2026-05":2188};
const WORKSPACE_MONTHLY = {"2025-08":460,"2025-09":819,"2025-10":684,"2025-11":948,"2025-12":177,"2026-01":193,"2026-02":94,"2026-03":20};
const MONTHS = ["2025-08","2025-09","2025-10","2025-11","2025-12","2026-01","2026-02","2026-03","2026-04","2026-05"];
const MONTH_LABELS = ["Aug","Sep","Oct","Nov","Dec","Jan","Feb","Mar","Apr","May"];
const MONTH_NAME = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];

const HOURLY_PLATFORM = {0:23,1:5,5:8,6:42,7:250,8:492,9:471,10:608,11:725,12:775,13:529,14:581,15:416,16:498,17:534,18:458,19:338,20:515,21:245,22:123,23:39};
const HOURLY_WORKSPACE = {0:77,1:20,2:7,3:18,4:20,5:18,6:14,7:61,8:109,9:171,10:155,11:216,12:259,13:214,14:262,15:233,16:279,17:211,18:242,19:244,20:216,21:203,22:98,23:48};

const WEEKDAY_PLATFORM = [874,1459,1308,1261,1193,759,821];   // Mon..Sun
const WEEKDAY_WORKSPACE = [455,566,547,576,547,390,314];
const WEEKDAY_LABELS = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

/* ── Helpers ───────────────────────────────────────────────────────────── */
function allDays(start, end) {
  const out = [];
  const d = new Date(start + "T00:00:00Z");
  const e = new Date(end + "T00:00:00Z");
  while (d <= e) { out.push(d.toISOString().slice(0,10)); d.setUTCDate(d.getUTCDate()+1); }
  return out;
}

function useReveal(deps = []) {
  const [shown, setShown] = useState(false);
  useEffect(() => { setShown(false); const id = requestAnimationFrame(() => setShown(true)); return () => cancelAnimationFrame(id); }, deps);
  return shown;
}

function useIsMobile() {
  const [mobile, setMobile] = useState(() => typeof window !== "undefined" && window.innerWidth <= 820);
  useEffect(() => {
    const check = () => setMobile(window.innerWidth <= 820);
    window.addEventListener("resize", check);
    return () => window.removeEventListener("resize", check);
  }, []);
  return mobile;
}

/* ── Card chrome ───────────────────────────────────────────────────────── */
function ChartCard({ title, subtitle, children, full }) {
  return (
    <div className={"chart-card " + (full ? "chart-full" : "")}>
      <div className="chart-card-head">
        <div className="chart-card-title">{title}</div>
        {subtitle && <div className="chart-card-sub">{subtitle}</div>}
      </div>
      {children}
    </div>
  );
}

/* ── 1. Daily timeline ─────────────────────────────────────────────────── */
function TimelineChart({ accent, isMobile }) {
  const start = isMobile ? "2026-02-01" : "2025-09-01";
  const days = useMemo(() => allDays(start, "2026-05-27"), [start]);
  const data = useMemo(() => days.map(d => ({
    d,
    p: PLATFORM_DAILY[d] || 0,
    w: WORKSPACE_DAILY[d] || 0,
    sprint: d >= "2026-02-01" && d <= "2026-03-31"
  })), [days]);

  const max = Math.max(...data.map(x => x.p + x.w));
  const W = 1000, H = 200, PAD_L = 40, PAD_B = 28, PAD_T = 24, PAD_R = 8;
  const innerW = W - PAD_L - PAD_R;
  const innerH = H - PAD_T - PAD_B;
  const bw = innerW / data.length;

  const reveal = useReveal([]);
  const [tooltip, setTooltip] = useState(null);
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
  const wrapRef = useRef(null);

  const gapPeriods = useMemo(() => {
    const gaps = [];
    let gapStart = -1;
    const firstActive = data.findIndex(x => x.p + x.w > 0);
    if (firstActive >= 10) {
      gaps.push({ start: 0, end: firstActive - 1, label: "Tracking started mid-August - commits from before this aren't in the chart." });
    }
    data.forEach((x, i) => {
      if (i < firstActive + 5) return;
      const total = x.p + x.w;
      if (!total) {
        if (gapStart < 0) gapStart = i;
      } else {
        if (gapStart >= 0 && i - gapStart >= 10) gaps.push({ start: gapStart, end: i - 1 });
        gapStart = -1;
      }
    });
    return gaps;
  }, [data]);

  // gridlines
  const ticks = [0, 0.25, 0.5, 0.75, 1].map(t => Math.round(max * t));
  // month markers
  const months = [];
  let lastMonth = "";
  data.forEach((x, i) => {
    const m = x.d.slice(0,7);
    if (m !== lastMonth) { months.push({ i, m }); lastMonth = m; }
  });

  return (
    <div
      className="chart-wrap"
      ref={wrapRef}
      onMouseMove={(e) => {
        const rect = wrapRef.current.getBoundingClientRect();
        setMousePos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
      }}
    >
      <svg viewBox={`0 0 ${W} ${H}`} className="chart-svg">
        {/* gridlines */}
        {ticks.map((t, i) => {
          const y = PAD_T + innerH - (t / max) * innerH;
          return (
            <g key={i}>
              <line x1={PAD_L} x2={W - PAD_R} y1={y} y2={y} stroke="rgba(255,255,255,0.06)" strokeDasharray={i === 0 ? "" : "2 4"} />
              <text x={PAD_L - 6} y={y + 3} fill="var(--fg-muted)" fontSize="9" textAnchor="end" fontFamily="ui-monospace, monospace">{t}</text>
            </g>
          );
        })}
        {/* sprint highlight */}
        {(() => {
          const start = data.findIndex(x => x.sprint);
          const end = data.length - [...data].reverse().findIndex(x => x.sprint);
          if (start < 0) return null;
          return <rect x={PAD_L + start * bw} y={PAD_T} width={(end - start) * bw} height={innerH} fill={accent} opacity="0.06" />;
        })()}
        {/* bars */}
        {data.map((x, i) => {
          const total = x.p + x.w;
          if (!total) return null;
          const fullH = (total / max) * innerH;
          const h = reveal ? fullH : 0;
          const wH = (x.w / total) * fullH;
          const pH = (x.p / total) * fullH;
          const px = PAD_L + i * bw;
          const baseY = PAD_T + innerH;
          const pColor = x.sprint ? accent : "rgba(255,255,255,0.62)";
          const wColor = "rgba(255,255,255,0.28)";
          return (
            <g key={x.d}>
              {/* workspace at bottom */}
              <rect x={px + 0.3} y={reveal ? baseY - (wH * fullH/fullH) : baseY} width={Math.max(bw - 0.6, 0.5)} height={reveal ? wH : 0} fill={wColor} style={{ transition: `height 800ms cubic-bezier(0.25,0.4,0.25,1) ${i * 0.5}ms, y 800ms cubic-bezier(0.25,0.4,0.25,1) ${i * 0.5}ms` }} />
              {/* platform on top */}
              <rect x={px + 0.3} y={reveal ? baseY - fullH : baseY} width={Math.max(bw - 0.6, 0.5)} height={reveal ? pH : 0} fill={pColor} style={{ transition: `height 800ms cubic-bezier(0.25,0.4,0.25,1) ${i * 0.5}ms, y 800ms cubic-bezier(0.25,0.4,0.25,1) ${i * 0.5}ms` }} />
            </g>
          );
        })}
        {/* month labels */}
        {months.map(({i,m}, idx) => {
          const px = PAD_L + i * bw;
          return (
            <text key={m} x={px} y={H - 8} fill="var(--fg-muted)" fontSize="10" fontFamily="ui-monospace, monospace">{MONTH_NAME[parseInt(m.slice(5)) - 1] || m.slice(5)}</text>
          );
        })}
        {/* sprint label */}
        <g>
          {(() => {
            const start = data.findIndex(x => x.sprint);
            if (start < 0) return null;
            const px = PAD_L + start * bw;
            return (
              <>
                <line x1={px} x2={px} y1={2} y2={PAD_T + innerH} stroke={accent} strokeWidth="1" strokeDasharray="2 3" opacity="0.6" />
                <rect x={px + 4} y={2} width="170" height="16" fill="rgba(0,0,0,0.7)" rx="3" />
                <text x={px + 10} y={14} fill={accent} fontSize="10" fontWeight="700" letterSpacing="0.08em">CONVEX MIGRATION SPRINT →</text>
              </>
            );
          })()}
        </g>
        {/* gap hover regions */}
        {gapPeriods.map((g, i) => {
          const gx = PAD_L + g.start * bw;
          const gw = (g.end - g.start + 1) * bw;
          return (
            <rect
              key={i}
              x={gx} y={PAD_T} width={Math.max(gw, 4)} height={innerH}
              fill={tooltip === i ? "rgba(255,255,255,0.04)" : "transparent"}
              style={{ cursor: "help" }}
              onMouseEnter={() => setTooltip(i)}
              onMouseLeave={() => setTooltip(null)}
            />
          );
        })}
      </svg>
      {tooltip !== null && (() => {
        const gap = gapPeriods[tooltip];
        return (
          <div className="chart-tooltip" style={{ left: mousePos.x, top: mousePos.y }}>
            <div className="chart-tooltip-title">
              {gap?.label ? "Before tracking began" : "Local work - not pushed to GitHub"}
            </div>
            <div className="chart-tooltip-body">
              {gap?.label || "Refactoring & architecture done locally, not committed."}
            </div>
          </div>
        );
      })()}
      <div className="chart-legend">
        <span className="lg-item"><span className="lg-dot" style={{ background: accent }} /> Sprint days</span>
        <span className="lg-item"><span className="lg-dot" style={{ background: "rgba(255,255,255,0.62)" }} /> Platform</span>
        <span className="lg-item"><span className="lg-dot" style={{ background: "rgba(255,255,255,0.28)" }} /> Workspace / R&amp;D</span>
      </div>
    </div>
  );
}

/* ── 2. Monthly stack ──────────────────────────────────────────────────── */
function MonthlyChart({ accent, isMobile }) {
  const visMonths = isMobile ? ["2026-02","2026-03","2026-04","2026-05"] : MONTHS;
  const visLabels = isMobile ? ["Feb","Mar","Apr","May"] : MONTH_LABELS;
  const [tooltip, setTooltip] = useState(null);
  const data = visMonths.map((m, i) => ({
    m,
    label: visLabels[i],
    p: PLATFORM_MONTHLY[m] || 0,
    w: WORKSPACE_MONTHLY[m] || 0,
    sprint: m === "2026-02" || m === "2026-03",
    disclaimer: m === "2025-12" || m === "2026-01"
  }));
  const max = Math.max(...data.map(x => x.p + x.w));
  const reveal = useReveal([]);

  return (
    <div className="chart-wrap" style={{ position: "relative" }}>
      {tooltip && (
        <div className="tl-tooltip" style={{ left: Math.min(Math.max(tooltip.x, 160), 600), top: tooltip.y, transform: "translate(-50%, -100%)" }}>
          <div className="tl-tt-title">Local work - not pushed to GitHub</div>
          <div className="tl-tt-sub">Refactoring &amp; architecture done locally, not committed.</div>
        </div>
      )}
      <div className="bars-vertical">
        {data.map((x, i) => {
          const total = x.p + x.w;
          const pPct = (x.p / max) * 100;
          const wPct = (x.w / max) * 100;
          const pColor = x.sprint ? accent : "rgba(255,255,255,0.55)";
          return (
            <div
              key={x.m}
              className={"bv-col" + (x.disclaimer ? " bv-col-disclaimer" : "")}
              onMouseEnter={x.disclaimer ? (e) => { const r = e.currentTarget.getBoundingClientRect(); const wr = e.currentTarget.closest(".chart-wrap").getBoundingClientRect(); setTooltip({ x: r.left - wr.left + r.width / 2, y: r.top - wr.top - 8 }); } : undefined}
              onMouseLeave={x.disclaimer ? () => setTooltip(null) : undefined}
              style={x.disclaimer ? { cursor: "help" } : undefined}
            >
              <div className="bv-num">{total ? total.toLocaleString() : ""}</div>
              <div className="bv-duo">
                {x.p > 0 && <div className="bv-bar" style={{ height: reveal ? pPct + "%" : 0, background: pColor, transitionDelay: (i * 60) + "ms" }} />}
                {x.w > 0 && <div className="bv-bar" style={{ height: reveal ? wPct + "%" : 0, background: "rgba(255,255,255,0.28)", transitionDelay: (i * 60 + 60) + "ms" }} />}
              </div>
              <div className="bv-lbl">{x.label}{x.disclaimer ? <span style={{ color: "rgba(255,255,255,0.3)", fontSize: 9, marginLeft: 2 }}>?</span> : null}</div>
            </div>
          );
        })}
      </div>
      <div className="chart-legend">
        <span className="lg-item"><span className="lg-dot" style={{ background: accent }} /> Sprint months</span>
        <span className="lg-item"><span className="lg-dot" style={{ background: "rgba(255,255,255,0.55)" }} /> Platform</span>
        <span className="lg-item"><span className="lg-dot" style={{ background: "rgba(255,255,255,0.28)" }} /> Workspace</span>
      </div>
    </div>
  );
}

/* ── 3. Hour of day ────────────────────────────────────────────────────── */
function HourlyChart({ accent, isMobile }) {
  const hours = isMobile
    ? Array.from({length: 12}, (_, i) => {
        const h0 = i * 2, h1 = h0 + 1;
        const label = h0 === 0 ? "12a" : h0 < 12 ? `${h0}a` : h0 === 12 ? "12p" : `${h0-12}p`;
        return { h: h0, label, v: (HOURLY_PLATFORM[h0]||0)+(HOURLY_WORKSPACE[h0]||0)+(HOURLY_PLATFORM[h1]||0)+(HOURLY_WORKSPACE[h1]||0) };
      })
    : Array.from({length: 24}, (_, h) => ({
        h,
        label: h === 0 ? "12a" : h < 12 ? `${h}a` : h === 12 ? "12p" : `${h-12}p`,
        v: (HOURLY_PLATFORM[h] || 0) + (HOURLY_WORKSPACE[h] || 0)
      }));
  const max = Math.max(...hours.map(x => x.v));
  const reveal = useReveal([]);

  return (
    <div className="chart-wrap">
      <div className="bars-vertical bars-hourly">
        {hours.map((x, i) => {
          const ratio = x.v / max;
          const intensity = Math.max(0.18, ratio);
          const isPeak = ratio > 0.95;
          return (
            <div key={x.h} className="bv-col">
              <div className="bv-track">
                <div className="bv-fill" style={{
                  height: reveal ? (ratio * 100) + "%" : 0,
                  background: accent,
                  opacity: intensity,
                  transitionDelay: (i * 25) + "ms"
                }} />
                {isPeak && <div className="bv-peak" style={{ color: accent }}>{x.v}</div>}
              </div>
              <div className="bv-lbl bv-lbl-mini">{x.label}</div>
            </div>
          );
        })}
      </div>
      <div className="chart-legend">
        <span className="lg-item lg-quiet">Peak: <strong style={{ color: accent }}>12pm</strong> · 1,034 commits</span>
        <span className="lg-item lg-quiet">Late shift: <strong style={{ color: "var(--fg-primary)" }}>4–6pm</strong> second wind</span>
      </div>
    </div>
  );
}

/* ── 4. Day of week ────────────────────────────────────────────────────── */
function WeekdayChart({ accent, isMobile }) {
  const data = WEEKDAY_LABELS.map((label, i) => ({
    label,
    p: WEEKDAY_PLATFORM[i],
    w: WEEKDAY_WORKSPACE[i],
    total: WEEKDAY_PLATFORM[i] + WEEKDAY_WORKSPACE[i]
  }));
  const maxVal = Math.max(...data.flatMap(x => [x.p, x.w]));
  const reveal = useReveal([]);
  return (
    <div className="chart-wrap">
      <div className="bars-vertical bars-weekday">
        {data.map((x, i) => {
          const peak = x.total === Math.max(...data.map(d => d.total));
          const pPct = (x.p / maxVal) * 100;
          const wPct = (x.w / maxVal) * 100;
          return (
            <div key={x.label} className="bv-col">
              {!isMobile && <div className="bv-num">{x.total.toLocaleString()}</div>}
              <div className="bv-duo">
                <div className="bv-bar" style={{ height: reveal ? pPct + "%" : 0, background: peak ? accent : "rgba(255,255,255,0.55)", transitionDelay: (i*70)+"ms" }} />
                <div className="bv-bar" style={{ height: reveal ? wPct + "%" : 0, background: "rgba(255,255,255,0.28)", transitionDelay: (i*70+60)+"ms" }} />
              </div>
              <div className="bv-lbl">{x.label}</div>
            </div>
          );
        })}
      </div>
      <div className="chart-legend">
        <span className="lg-item"><span className="lg-dot" style={{ background: "rgba(255,255,255,0.55)" }} /> Platform</span>
        <span className="lg-item"><span className="lg-dot" style={{ background: "rgba(255,255,255,0.28)" }} /> Workspace</span>
      </div>
    </div>
  );
}

/* ── 5. Coding intensity ────────────────────────────────────────────────── */
function CodingIntensityChart({ accent }) {
  const MAX = 50000;
  const bars = [
    { label: "p25",    note: "quiet",  value: 8200  },
    { label: "Median", note: "",       value: 22000, hi: true },
    { label: "Mean",   note: "",       value: 33000 },
    { label: "p75",    note: "heavy",  value: 48000 },
  ];
  const reveal = useReveal([]);
  return (
    <div className="chart-wrap">
      <div className="intensity-stats">
        <div className="intensity-stat">
          <div className="intensity-stat-num" style={{ color: accent }}>~22k</div>
          <div className="intensity-stat-lbl">median LOC / day</div>
        </div>
        <div className="intensity-stat">
          <div className="intensity-stat-num" style={{ color: accent }}>~33k</div>
          <div className="intensity-stat-lbl">mean LOC / day</div>
        </div>
      </div>
      <div className="intensity-bars">
        {bars.map((b, i) => (
          <div key={b.label} className="intensity-row">
            <div className="intensity-lbl">
              {b.label}{b.note && <span className="intensity-note">{b.note}</span>}
            </div>
            <div className="intensity-track">
              <div
                className="intensity-fill"
                style={{
                  width: reveal ? (b.value / MAX * 100) + "%" : "0%",
                  background: b.hi ? accent : "rgba(255,255,255,0.22)",
                  transitionDelay: (i * 80) + "ms",
                }}
              />
            </div>
            <div className="intensity-val">{Math.round(b.value / 1000)}k</div>
          </div>
        ))}
      </div>
      <div className="chart-legend">
        <span className="lg-item lg-quiet">insertions + deletions across .ts/.tsx/.js/.css · net new ≈ half</span>
      </div>
    </div>
  );
}

/* ── 6. Cumulative ─────────────────────────────────────────────────────── */
function CumulativeChart({ accent, isMobile }) {
  const start = isMobile ? "2026-02-01" : "2025-09-01";
  const days = useMemo(() => allDays(start, "2026-05-27"), [start]);
  const series = useMemo(() => {
    let cP = 0, cW = 0;
    return days.map(d => {
      cP += PLATFORM_DAILY[d] || 0;
      cW += WORKSPACE_DAILY[d] || 0;
      return { d, cP, cW, total: cP + cW };
    });
  }, [days]);

  const W = 1000, H = 220, PAD = { L: 70, R: 12, T: 16, B: 28 };
  const innerW = W - PAD.L - PAD.R;
  const innerH = H - PAD.T - PAD.B;
  const max = Math.ceil(series[series.length-1].total / 2000) * 2000;

  const x = i => PAD.L + (i / (series.length - 1)) * innerW;
  const y = v => PAD.T + innerH - (v / max) * innerH;

  const totalPath = series.map((s, i) => `${i === 0 ? "M" : "L"} ${x(i).toFixed(1)} ${y(s.total).toFixed(1)}`).join(" ");
  const platformPath = series.map((s, i) => `${i === 0 ? "M" : "L"} ${x(i).toFixed(1)} ${y(s.cP).toFixed(1)}`).join(" ");
  const totalArea = totalPath + ` L ${x(series.length-1)} ${PAD.T + innerH} L ${x(0)} ${PAD.T + innerH} Z`;

  const ticks = [0, 0.25, 0.5, 0.75, 1].map(t => Math.round(max * t));
  const reveal = useReveal([]);
  const pathLen = 1800;

  // month markers
  const months = [];
  let lastMonth = "";
  series.forEach((s, i) => {
    const m = s.d.slice(0,7);
    if (m !== lastMonth) { months.push({ i, m: s.d }); lastMonth = m; }
  });

  return (
    <div className="chart-wrap">
      <svg viewBox={`0 0 ${W} ${H}`} className="chart-svg">
        <defs>
          <linearGradient id="cumGrad" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={accent} stopOpacity="0.35" />
            <stop offset="100%" stopColor={accent} stopOpacity="0" />
          </linearGradient>
        </defs>
        {ticks.map((t, i) => {
          const py = y(t);
          return (
            <g key={i}>
              <line x1={PAD.L} x2={W - PAD.R} y1={py} y2={py} stroke="rgba(255,255,255,0.06)" strokeDasharray={i === 0 ? "" : "2 4"} />
              <text x={PAD.L - 8} y={py + 4} fill="var(--fg-muted)" fontSize="11" textAnchor="end" fontFamily="ui-monospace, monospace">{t.toLocaleString()}</text>
            </g>
          );
        })}
        <path d={totalArea} fill="url(#cumGrad)" />
        <path
          d={totalPath}
          fill="none"
          stroke={accent}
          strokeWidth="2.4"
          strokeLinejoin="round"
          strokeLinecap="round"
          style={{ strokeDasharray: pathLen, strokeDashoffset: reveal ? 0 : pathLen, transition: "stroke-dashoffset 1800ms cubic-bezier(0.25,0.4,0.25,1)" }}
        />
        <path
          d={platformPath}
          fill="none"
          stroke="rgba(255,255,255,0.55)"
          strokeWidth="1.4"
          strokeLinejoin="round"
          strokeDasharray="3 4"
          style={{ opacity: reveal ? 1 : 0, transition: "opacity 1200ms 800ms" }}
        />
        {/* end label */}
        {(() => {
          const last = series[series.length - 1];
          return (
            <g style={{ opacity: reveal ? 1 : 0, transition: "opacity 400ms 1800ms" }}>
              <circle cx={x(series.length-1)} cy={y(last.total)} r="4" fill={accent} />
              <text x={x(series.length-1) - 8} y={y(last.total) - 10} fill={accent} fontSize="13" fontWeight="700" textAnchor="end">{last.total.toLocaleString()}</text>
            </g>
          );
        })()}
        {months.map((m, i) => i % 2 === 0 && (
          <text key={m.m} x={x(m.i)} y={H - 8} fill="var(--fg-muted)" fontSize="10" fontFamily="ui-monospace, monospace">{MONTH_NAME[parseInt(m.m.slice(5,7)) - 1]}</text>
        ))}
      </svg>
      <div className="chart-legend">
        <span className="lg-item"><span className="lg-line" style={{ background: accent }} /> Total commits (cumulative)</span>
        <span className="lg-item"><span className="lg-line" style={{ background: "rgba(255,255,255,0.55)", borderTop: "1px dashed rgba(255,255,255,0.7)" }} /> Platform only</span>
      </div>
    </div>
  );
}

/* ── Section ───────────────────────────────────────────────────────────── */
function StatsSection({ accent, serif }) {
  const isMobile = useIsMobile();
  return (
    <div>
      <div className="overline" style={{ color: accent }}>By the numbers</div>
      <h1 className="display" style={{ fontFamily: serif }}>
        Proof of work.{" "}
        <span style={{ color: accent }}>Last 9 months of receipts.</span>
      </h1>
      <p className="lead">
        A 9-month snapshot of real data, pulled straight from git. Two repos: <span className="text-emphasis">platform</span> (the new Next.js + Convex rewrite) and <span className="text-emphasis">workspace</span> (the legacy PHP site). Nothing rounded up, nothing dressed up.
      </p>

      <div className="stat-strip">
        <div className="stat-strip-item"><div className="stat-strip-num" style={{ color: accent }}>12,303</div><div className="stat-strip-lbl">Total commits</div></div>
        <div className="stat-strip-item"><div className="stat-strip-num" style={{ color: accent }}>~10,000</div><div className="stat-strip-lbl">Unique commits</div></div>
        <div className="stat-strip-item"><div className="stat-strip-num" style={{ color: accent }}>9 mo</div><div className="stat-strip-lbl">This snapshot</div></div>
        <div className="stat-strip-item"><div className="stat-strip-num" style={{ color: accent }}>~42</div><div className="stat-strip-lbl">Per active day</div></div>
        <div className="stat-strip-item"><div className="stat-strip-num" style={{ color: accent }}>12pm</div><div className="stat-strip-lbl">Peak hour</div></div>
      </div>

      <div className="charts-grid">
        <ChartCard full title="Daily commits" subtitle={isMobile ? "Feb – May 2026 · two repos" : "Sep 2025 – May 2026 · two repos · gaps = local work not pushed"}>
          <TimelineChart accent={accent} isMobile={isMobile} />
        </ChartCard>

        <ChartCard title="Monthly volume" subtitle="Platform + workspace, side by side">
          <MonthlyChart accent={accent} isMobile={isMobile} />
        </ChartCard>

        <ChartCard title="Hour of day" subtitle="When I commit the most">
          <HourlyChart accent={accent} isMobile={isMobile} />
        </ChartCard>

        <ChartCard title="Day of week" subtitle="Tuesday wins. Sunday is rest-ish.">
          <WeekdayChart accent={accent} isMobile={isMobile} />
        </ChartCard>

        <ChartCard title="Coding intensity" subtitle="LOC changed per active day · last 6 months">
          <CodingIntensityChart accent={accent} />
        </ChartCard>

        <ChartCard full title="Cumulative growth">
          <CumulativeChart accent={accent} isMobile={isMobile} />
        </ChartCard>
      </div>

    </div>
  );
}

window.HoltStats = { StatsSection };
