/* AcquiCup — public player profile page.
 *
 * A read-only view of any player, reached by clicking them on a leaderboard. It
 * mirrors the Dashboard (headline stats, accuracy breakdown, their predictions,
 * and BOTH leaderboard time-series charts) but with two deliberate differences:
 *   1. For OTHER players it NEVER shows still-pending picks — only REVEALED
 *      predictions (matches that have kicked off: locked / live / finished). The
 *      server enforces this (GET /api/users/:id omits editable upcoming picks for
 *      other players); a player viewing their OWN profile gets every pick back,
 *      pending ones included. The client just renders whatever it's handed.
 *   2. Nothing here is editable — no dials, no X2, no "your pick" wording.
 *
 * The two charts reuse the shared RankSeriesCard (Top 10 / Top 20 / Me / All / Select tabs)
 * and the rank series that already ship in every window.ACQ payload (USER_RANK_SERIES /
 * COMPANY_RANK_SERIES / RANK_DATES), so the only thing fetched per-page is the target's
 * revealed picks. The "Me" tab here plots the profile's subject (labelled by their name).
 */
const { useState, useEffect } = React;

function UserPage({ store, go, userId }) {
  const A = window.ACQ;
  const tz = useTimezone();
  const [data, setData] = useState(null); // { user, predictions } from the API
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [showAll, setShowAll] = useState(false);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(null);
    setData(null);
    setShowAll(false);
    if (!userId) { setLoading(false); setError("No player selected."); return; }
    API.getUser(userId)
      .then((d) => { if (!cancelled) { setData(d); setLoading(false); } })
      .catch((e) => { if (!cancelled) { setError((e && e.error) || "Couldn't load this player."); setLoading(false); } });
    return () => { cancelled = true; };
  }, [userId]);

  // The leaderboard standings entry (rank/movement/series live here for every user).
  const standing = A.USERS.find((u) => u.id === userId);
  // Prefer the freshly-fetched profile, fall back to the standings row.
  const user = (data && data.user) || standing;
  const predictions = (data && data.predictions) || {};
  const isMe = !!(user && (user.you || (store.me && user.id === store.me.id)));
  const coName = (id) => (A.COMPANIES.find((c) => c.id === id) || {}).name || id;
  const myCo = user ? A.COMPANY_RANK.find((c) => c.id === user.company) : null;

  // Join every revealed pick to its match (used by the stats card + the table below).
  const matchById = {};
  A.MATCHES.forEach((m) => { matchById[m.id] = m; });

  // ---- stats: SETTLED predictions split two ways, mirroring the Dashboard's "My stats"
  // card (Accuracy rate + Risk rate). A prediction is settled once its match has finished
  // (`earned` present). ----
  const settled = Object.values(predictions).filter((p) => p && p.earned).length;
  // Accuracy: exact / good (right outcome, not exact) / wrong. exact + good are
  // authoritative server stats on the user row; wrong is the remainder.
  const exactN = user ? user.exacts : 0;
  const goodN = user ? (user.good ?? Math.max(0, user.correct - user.exacts)) : 0;
  const wrongN = Math.max(0, settled - exactN - goodN);
  // Points earned per accuracy bucket — summed from each settled pick's scored `total`
  // (includes any X2 doubling). Wrong picks score 0, so all points come from exact + good.
  let exactPts = 0, goodPts = 0;
  Object.values(predictions).forEach((p) => {
    if (!p || !p.earned) return;
    const t = Number(p.earned.total) || 0;
    if (p.earned.isExact) exactPts += t;
    else if (p.earned.outcome > 0) goodPts += t;
  });
  const statRows = [
    { label: "Exact score", short: "Exact", value: exactN, pts: exactPts, color: "var(--brand-navy)" },
    { label: "Good prediction", short: "Good", value: goodN, pts: goodPts, color: "var(--brand-blue)" },
    { label: "Wrong prediction", short: "Wrong", value: wrongN, pts: 0, color: "var(--brand-yellow)" },
  ];

  // Risk: the SAME settled picks split by how risky each bet was, read off the odds of the
  // outcome backed (per-match outcome points ≈ decimal-odds × 10) — low ≤20 (favourite),
  // medium 21–45, high >45 (long shot). Each tier tracks its count and the points it earned.
  const risk = { low: { n: 0, pts: 0 }, med: { n: 0, pts: 0 }, high: { n: 0, pts: 0 } };
  Object.entries(predictions).forEach(([mid, p]) => {
    if (!p || !p.earned) return;
    const m = matchById[mid];
    if (!m || !m.points) return;
    const outcome = p.home > p.away ? "home" : p.home < p.away ? "away" : "draw";
    const pts = Number(m.points[outcome]) || 0;
    if (!pts) return;
    const tier = pts <= 20 ? risk.low : pts <= 45 ? risk.med : risk.high;
    tier.n += 1;
    tier.pts += Number(p.earned.total) || 0;
  });
  const safetyTotal = risk.low.n + risk.med.n + risk.high.n;
  // Safety = inverse of risk: a LOW-risk pick (favourite) is HIGH safety, a long shot is LOW.
  const safetyRows = [
    { label: "High safety", short: "High", value: risk.low.n, pts: risk.low.pts, color: "var(--brand-navy)" },
    { label: "Medium safety", short: "Med", value: risk.med.n, pts: risk.med.pts, color: "var(--brand-blue)" },
    { label: "Low safety", short: "Low", value: risk.high.n, pts: risk.high.pts, color: "var(--brand-yellow)" },
  ];

  // ---- settled predictions (match finished + scored), joined to their match — the SAME
  // shape + layout as the Dashboard's "Recent points" table, recent-first, capped at 10. ----
  const rows = Object.entries(predictions)
    .map(([mid, pred]) => {
      const match = matchById[mid];
      if (!match || !pred || !pred.earned) return null;
      const right = outcomeOf(pred.home, pred.away) === outcomeOf(match.homeScore, match.awayScore);
      const exact = !!pred.earned.isExact;
      return { match, pred, pts: pred.earned.total, right, exact };
    })
    .filter(Boolean)
    .sort((a, b) => {
      if (a.match.date !== b.match.date) return a.match.date < b.match.date ? 1 : -1;
      const at = a.match.time || "", bt = b.match.time || "";
      return at < bt ? 1 : at > bt ? -1 : 0;
    });
  const visibleRows = showAll ? rows : rows.slice(0, 10);

  // ---- individual chart: the full board, this player's line highlighted. Fed to the shared
  // RankSeriesCard so it gets the same Top 10 / Top 20 / Me / All / Select tabs as the
  // leaderboards — here the "Me" tab is the profiled player (their first name labels it). ----
  const ordered = [...A.USERS].sort((a, b) => a.rank - b.rank);
  const indEntities = ordered.filter((u) => A.USER_RANK_SERIES[u.id]).map((u) => ({ id: u.id, rank: u.rank, name: userName(u) }));
  const indBuild = (e, { highlight }) => {
    const u = A.USERS.find((x) => x.id === e.id) || e;
    return {
      id: u.id,
      label: userName(u),
      color: highlight ? "var(--brand-navy)" : companyColor(u.company),
      ranks: A.USER_RANK_SERIES[u.id],
      highlight,
      node: <Avatar user={u} size={highlight ? 24 : 20} />,
    };
  };

  // ---- company chart: every company, this player's company highlighted ----
  const coRanked = [...A.COMPANY_RANK].sort((a, b) => b.avgTop3 - a.avgTop3).map((c, i) => ({ ...c, _rank: i + 1 }));
  const coEntities = coRanked.filter((c) => A.COMPANY_RANK_SERIES[c.id]).map((c) => ({ id: c.id, rank: c._rank, name: c.name }));
  const coBuild = (e, { highlight }) => {
    const c = A.COMPANY_RANK.find((x) => x.id === e.id) || e;
    return {
      id: c.id,
      label: c.shortName || (c.name || "").split(" ")[0],
      color: companyColor(c.id),
      ranks: A.COMPANY_RANK_SERIES[c.id],
      highlight,
      node: <CompanyLogo id={c.id} size={highlight ? 24 : 20} />,
    };
  };
  const coLabel = (coName(user && user.company) || "").split(" ")[0] || "Company";

  // Headline stat box (mirrors the Dashboard's StatBox).
  const StatBox = ({ label, value, sub, icon, color, movement }) => (
    <div className="card col gap-8" style={{ padding: "16px 18px", boxShadow: "inset 3px 0 0 " + color + ", var(--shadow-card)" }}>
      <div className="row between">
        <div className="row gap-10">
          <span style={{ width: 32, height: 32, borderRadius: 9, flex: "none", display: "flex", alignItems: "center",
            justifyContent: "center", color: color, background: "color-mix(in srgb, " + color + " 14%, var(--surface))" }}>
            <Icon name={icon} size={17} stroke={2} />
          </span>
          <span className="eyebrow" style={{ alignSelf: "center" }}>{label}</span>
        </div>
        {movement != null && <Movement value={movement} />}
      </div>
      <span className="mono" style={{ fontSize: 30, fontWeight: 700, lineHeight: 1 }}>{value}</span>
      {sub && <span className="muted" style={{ fontSize: 12.5 }}>{sub}</span>}
    </div>
  );

  const Back = () => (
    <button className="btn btn-ghost btn-sm" onClick={() => go("leaderboard")} style={{ alignSelf: "flex-start" }}>
      <Icon name="chevron" size={14} style={{ transform: "rotate(180deg)" }} />Leaderboard
    </button>
  );

  if (loading) {
    return (
      <div className="col gap-20">
        <Back />
        <div className="card card-pad" style={{ padding: "40px 20px", textAlign: "center" }}>
          <span className="muted" style={{ fontSize: 14 }}>Loading player…</span>
        </div>
      </div>
    );
  }
  if (error || !user) {
    return (
      <div className="col gap-20">
        <Back />
        <div className="card card-pad" style={{ padding: "40px 20px", textAlign: "center" }}>
          <span className="muted" style={{ fontSize: 14 }}>{error || "Player not found."}</span>
        </div>
      </div>
    );
  }

  return (
    <div className="col gap-24">
      <Back />

      {/* hero — player identity + headline */}
      <div className="card hero-card" style={{ background: "var(--hero-bg)", color: "var(--hero-ink)", border: "none", overflow: "hidden", position: "relative" }}>
        <div style={{ position: "absolute", inset: 0, opacity: .5, background:
          "radial-gradient(700px 240px at 88% -30%, color-mix(in srgb, var(--brand-blue) 40%, transparent), transparent)" }}></div>
        <div className="card-pad" style={{ padding: "26px 28px", position: "relative" }}>
          <div className="row between wrap gap-16">
            <div className="row gap-16 profile-hero-id" style={{ minWidth: 0, alignItems: "center" }}>
              <CompanyLogo id={user.company} size={56} />
              <div className="col gap-2" style={{ minWidth: 0 }}>
                <span className="eyebrow" style={{ color: "color-mix(in srgb, var(--hero-ink) 70%, transparent)" }}>
                  Player profile{isMe ? " · you" : ""}
                </span>
                <h1 style={{ fontSize: 26, marginTop: 4, fontWeight: 800 }} className="row gap-10">
                  {userName(user)}{user.isAi && <AiBadge />}
                </h1>
                <span style={{ opacity: .82, fontSize: 13.5, marginTop: 4 }}>
                  {store.openCompany && !user.isAi
                    ? <span style={{ cursor: "pointer", textDecoration: "underline", textUnderlineOffset: 2 }} onClick={() => store.openCompany({ id: user.company })} title={"View " + coName(user.company)}>{coName(user.company)}</span>
                    : coName(user.company)}
                </span>
              </div>
            </div>
            <div className="col profile-hero-aside" style={{ alignItems: "flex-end", lineHeight: 1.05 }}>
              <span className="mono" style={{ fontSize: 34, fontWeight: 800 }}>{user.points}</span>
              <span style={{ opacity: .72, fontSize: 12 }}>points · #{user.rank} of {A.USERS.length}</span>
            </div>
          </div>
        </div>
      </div>

      {/* headline stats — on mobile .dash-stats puts Total points full-width with the
          two ranking tiles sharing the row below (same reflow as the Dashboard KPIs) */}
      <div className="dash-stats" style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(190px,1fr))", gap: 16 }}>
        <StatBox label="Total points" value={user.points} icon="trophy" color="var(--accent)" sub="Across all predictions" />
        <StatBox label="Player ranking" value={"#" + user.rank} icon="user" color="var(--brand-blue)" movement={user.movement} sub={"of " + A.USERS.length + " players"} />
        <StatBox label="Company ranking" value={myCo ? "#" + myCo.rank : "–"} icon="building" color="var(--gold)" movement={myCo ? myCo.movement : null} sub={myCo ? "of " + A.COMPANY_RANK.length + " companies" : "Not ranked yet"} />
      </div>

      {/* stats — one card, two proportional bars (Accuracy + Safety), mirroring the
          Dashboard's "My stats" card and reusing the shared StatBar. */}
      <div className="card card-pad col gap-16" style={{ padding: "20px 22px" }}>
        <div className="row between wrap gap-8">
          <span className="eyebrow">{isMe ? "My stats" : capName(user.first) + "'s stats"}</span>
          <span className="muted" style={{ fontSize: 12.5 }}>{settled ? settled + " settled prediction" + (settled === 1 ? "" : "s") : "No settled predictions yet"}</span>
        </div>
        {settled === 0
          ? <span className="muted" style={{ fontSize: 13.5 }}>{isMe ? "Once your matches finish, you'll see how your predictions break down here." : "Once " + capName(user.first) + "'s matches finish, you'll see how their predictions break down here."}</span>
          : <div className="col gap-24">
              {/* 1 — accuracy rate: settled picks split exact / good (right outcome) / wrong */}
              <div className="col gap-12">
                <div className="col gap-2" style={{ minWidth: 0 }}>
                  <span style={{ fontWeight: 700, fontSize: 14 }}>Accuracy rate</span>
                  <span className="muted" style={{ fontSize: 11.5 }}>How {isMe ? "your" : "their"} settled picks scored — exact score, right outcome, or missed</span>
                </div>
                <StatBar rows={statRows} total={settled} />
              </div>
              {/* 2 — safety rate: settled picks split by the odds of the outcome backed
                  (high safety = favourite ≤20 pts, medium 21–45, low = long shot >45);
                  brand ramp navy → blue → yellow */}
              <div className="col gap-12">
                <div className="col gap-2" style={{ minWidth: 0 }}>
                  <span style={{ fontWeight: 700, fontSize: 14 }}>Safety rate</span>
                  <span className="muted" style={{ fontSize: 11.5 }}>How safe {isMe ? "your" : "their"} bets were — by the odds of the outcome backed</span>
                </div>
                {safetyTotal === 0
                  ? <span className="muted" style={{ fontSize: 12.5 }}>No priced bets settled yet.</span>
                  : <StatBar rows={safetyRows} total={safetyTotal} />}
              </div>
              {/* 3 + 4 — where the points came from, as two donuts: by prediction type
                  (exact vs good — wrong scores 0 so it drops out) and by safety tier. Both
                  chart the EARNED points (`pts`) the bars above carry, so donuts and bars stay
                  in lock-step — exactly like the company profile's "stats" card. */}
              <div className="profile-donuts" style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(260px,1fr))", gap: 24 }}>
                <div className="col gap-12">
                  <div className="col gap-2" style={{ minWidth: 0 }}>
                    <span style={{ fontWeight: 700, fontSize: 14 }}>Points by prediction type</span>
                    <span className="muted" style={{ fontSize: 11.5 }}>How {isMe ? "your" : "their"} points split between exact scores and right outcomes</span>
                  </div>
                  <Donut rows={statRows.map((r) => ({ label: r.label, value: r.pts, color: r.color }))}
                    centerSub="total pts" emptyLabel="No points yet" />
                </div>
                <div className="col gap-12">
                  <div className="col gap-2" style={{ minWidth: 0 }}>
                    <span style={{ fontWeight: 700, fontSize: 14 }}>Points by safety rate</span>
                    <span className="muted" style={{ fontSize: 11.5 }}>How {isMe ? "your" : "their"} points split across high, medium and low-safety bets</span>
                  </div>
                  <Donut rows={safetyRows.map((r) => ({ label: r.label, value: r.pts, color: r.color }))}
                    centerSub="total pts" emptyLabel="No priced bets settled yet." />
                </div>
              </div>
            </div>}
      </div>

      {/* predictions — reuses the Dashboard "Recent points" table layout (match ·
          prediction · score · points · kick-off · phase), recent-first, settled picks
          only, showing 10 by default with a Show-more toggle. */}
      <div className="card" style={{ overflow: "hidden" }}>
        <div className="row between" style={{ padding: "13px 16px 11px" }}>
          <span className="eyebrow">Predictions</span>
          {rows.length > 0 && <span className="muted" style={{ fontSize: 12.5 }}>{rows.length} settled</span>}
        </div>
        {rows.length === 0
          ? <div className="card-pad" style={{ padding: "0 16px 16px" }}><span className="muted" style={{ fontSize: 13.5 }}>{isMe ? "No points yet — they'll appear here once your matches finish." : "No settled predictions yet — they appear here once " + capName(user.first) + "'s matches finish."}</span></div>
          : <>
              <div style={{ overflowX: "auto" }}>
                <table className="lb dash-recent-table" style={{ textAlign: "center" }}>
                  <thead><tr>
                    <th style={{ textAlign: "center" }}>Match</th>
                    <th style={{ textAlign: "center" }}>Prediction</th>
                    <th style={{ textAlign: "center" }}>Score</th>
                    <th style={{ textAlign: "center" }}>Points</th>
                    <th style={{ textAlign: "center" }}>Kick-off</th>
                    <th style={{ textAlign: "center" }}>Phase</th>
                  </tr></thead>
                  <tbody>
                    {visibleRows.map((r) => (
                      <tr key={r.match.id}>
                        <td>
                          <div className="row gap-8" style={{ alignItems: "center", justifyContent: "center", minWidth: 0 }}>
                            <TeamBadge code={r.match.home} size="sm" />
                            <span className="mono" style={{ fontWeight: 700, fontSize: 12.5 }}>{T(r.match.home).code}</span>
                            <span className="muted" style={{ fontSize: 11 }}>v</span>
                            <span className="mono" style={{ fontWeight: 700, fontSize: 12.5 }}>{T(r.match.away).code}</span>
                            <TeamBadge code={r.match.away} size="sm" />
                          </div>
                        </td>
                        <td><span className="mono" style={{ fontWeight: 600, fontSize: 13 }}>{r.pred.home}–{r.pred.away}</span></td>
                        <td><span className="mono" style={{ fontWeight: 700, fontSize: 13 }}>{r.match.homeScore}–{r.match.awayScore}</span></td>
                        <td>
                          <span className={"badge mono " + (r.exact ? "navy" : r.right ? "blue" : "amber")}>
                            <Icon name={r.right ? "check" : "x"} size={11} stroke={2.5} />+{r.pts}
                          </span>
                        </td>
                        <td><span className="muted nowrap" style={{ fontSize: 12.5 }}>{fmtMatchDate(r.match, tz)}</span></td>
                        <td><span className="badge" style={{ background: "var(--chip-bg)" }}>{r.match.stage}{r.match.group ? " · " + r.match.group : ""}</span></td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
              {rows.length > 10 && (
                <div className="row center" style={{ padding: "12px 16px", borderTop: "1px solid var(--line-2)" }}>
                  <button className="btn btn-ghost btn-sm" onClick={() => setShowAll((v) => !v)}>
                    {showAll ? "Show less" : "Show " + (rows.length - 10) + " more"}
                    <Icon name="chevronDown" size={14} style={{ transform: showAll ? "rotate(180deg)" : "none" }} />
                  </button>
                </div>
              )}
            </>}
      </div>

      {/* individual ranking over time — this player highlighted */}
      <RankSeriesCard title="Player ranking over time"
        sub={`Daily rank across the tournament — ${capName(user.first)}'s line is highlighted.`}
        dates={A.RANK_DATES} entities={indEntities} buildEntry={indBuild}
        meId={userId} meLabel={isMe ? "Me" : capName(user.first)} kindLabel="players" defaultMode="me" />

      {/* company ranking over time — this player's company highlighted */}
      <RankSeriesCard title="Company ranking over time"
        sub={`Daily company standings across the tournament — ${isMe ? "your" : "their"} company is highlighted.`}
        dates={A.RANK_DATES} entities={coEntities} buildEntry={coBuild}
        meId={(user && user.company) || null} meLabel={coLabel} kindLabel="companies" defaultMode="me" />
    </div>
  );
}

/* AcquiCup — public company profile page.
 *
 * The company analogue of UserPage, reached by clicking a company on the leaderboard.
 * It mirrors that page's layout — hero, headline stats, accuracy + strategy breakdowns,
 * a roster (the company's equivalent of "their predictions"), and BOTH rank charts — but
 * aggregated across the whole squad:
 *   1. The company's headline stats, members and BOTH rank time-series already ship in
 *      every window.ACQ (COMPANY_RANK + the *_RANK_SERIES), so the page renders instantly
 *      from the bootstrap. Only the squad-wide accuracy / strategy split is fetched
 *      (GET /api/companies/:id), because per-member settled counts aren't in the payload.
 *   2. Nothing here is editable; member rows link through to each player's own profile.
 */
function CompanyPage({ store, go, companyId }) {
  const A = window.ACQ;
  const narrow = useNarrow(820);
  const [data, setData] = useState(null); // { company, accuracy, risk } from the API
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(null);
    setData(null);
    if (!companyId) { setLoading(false); setError("No company selected."); return; }
    API.getCompany(companyId)
      .then((d) => { if (!cancelled) { setData(d); setLoading(false); } })
      .catch((e) => { if (!cancelled) { setError((e && e.error) || "Couldn't load this company."); setLoading(false); } });
    return () => { cancelled = true; };
  }, [companyId]);

  // The standings row (rank/movement/members/series live here for every company). Prefer
  // the freshly-fetched company, fall back to the bootstrap's COMPANY_RANK entry.
  const standing = A.COMPANY_RANK.find((c) => c.id === companyId);
  const company = (data && data.company) || standing;
  const mine = !!(company && store.me && company.id === store.me.company);
  const meId = store.me && store.me.id;
  const members = (company && company.members) ? [...company.members].sort((a, b) => a.rank - b.rank) : [];

  // ---- squad "My stats": the SAME two proportional bars as the Dashboard / player profile,
  // aggregated server-side over all members' SETTLED picks and rendered with the shared
  // StatBar. (1) Accuracy rate — exact / good / wrong, each with the points it earned. ----
  const accuracy = (data && data.accuracy) || { settled: 0, exact: 0, good: 0, wrong: 0, exactPts: 0, goodPts: 0 };
  const settled = accuracy.settled;
  const statRows = [
    { label: "Exact score", short: "Exact", value: accuracy.exact, pts: accuracy.exactPts, color: "var(--brand-navy)" },
    { label: "Good prediction", short: "Good", value: accuracy.good, pts: accuracy.goodPts, color: "var(--brand-blue)" },
    { label: "Wrong prediction", short: "Wrong", value: accuracy.wrong, pts: 0, color: "var(--brand-yellow)" },
  ];

  // (2) Safety rate — the same settled picks split by how safe each bet was, read off the
  // odds of the outcome backed (high safety = favourite ≤20 pts, medium 21–45, low >45). ----
  const risk = (data && data.risk) || { low: { n: 0, pts: 0 }, med: { n: 0, pts: 0 }, high: { n: 0, pts: 0 } };
  const safetyTotal = risk.low.n + risk.med.n + risk.high.n;
  // Safety = inverse of risk: a LOW-risk pick (favourite) is HIGH safety, a long shot is LOW.
  const safetyRows = [
    { label: "High safety", short: "High", value: risk.low.n, pts: risk.low.pts, color: "var(--brand-navy)" },
    { label: "Medium safety", short: "Med", value: risk.med.n, pts: risk.med.pts, color: "var(--brand-blue)" },
    { label: "Low safety", short: "Low", value: risk.high.n, pts: risk.high.pts, color: "var(--brand-yellow)" },
  ];

  // ---- company chart: every company, this one highlighted. Shared RankSeriesCard, so the
  // same Top 10 / Top 20 / All / Select tabs as the leaderboards — the single-line tab is this
  // company (its name labels it). ----
  const coRanked = [...A.COMPANY_RANK].sort((a, b) => b.avgTop3 - a.avgTop3).map((c, i) => ({ ...c, _rank: i + 1 }));
  const coEntities = coRanked.filter((c) => A.COMPANY_RANK_SERIES[c.id]).map((c) => ({ id: c.id, rank: c._rank, name: c.name }));
  const coBuild = (e, { highlight }) => {
    const c = A.COMPANY_RANK.find((x) => x.id === e.id) || e;
    return {
      id: c.id,
      label: c.shortName || (c.name || "").split(" ")[0],
      color: companyColor(c.id),
      ranks: A.COMPANY_RANK_SERIES[c.id],
      highlight,
      node: <CompanyLogo id={c.id} size={highlight ? 24 : 20} />,
    };
  };
  const coLabel = (company && company.name ? company.name : companyId || "").split(" ")[0] || "Company";

  // ---- member chart: just this squad, re-ranked among themselves each matchday (1..N) so it
  // stays compact — the same technique as the Standings "your company" board. The "Me" tab is
  // the viewer's own line, shown only when they belong to this company. ----
  const D = (A.RANK_DATES || []).length;
  const memLocal = {};
  members.forEach((m) => (memLocal[m.id] = []));
  for (let i = 0; i < D; i++) {
    const order = [...members].sort((a, b) => {
      const ra = (A.USER_RANK_SERIES[a.id] || [])[i];
      const rb = (A.USER_RANK_SERIES[b.id] || [])[i];
      return (ra == null ? Infinity : ra) - (rb == null ? Infinity : rb);
    });
    order.forEach((m, j) => memLocal[m.id].push(j + 1));
  }
  const memEntities = members.map((u, i) => ({ id: u.id, rank: i + 1, name: userName(u) }));
  const memBuild = (e, { highlight }) => {
    const u = A.USERS.find((x) => x.id === e.id) || members.find((m) => m.id === e.id) || e;
    return {
      id: u.id,
      label: userName(u),
      color: highlight ? "var(--brand-navy)" : companyColor(company && company.id),
      ranks: memLocal[u.id],
      highlight,
      node: <Avatar user={u} size={highlight ? 24 : 20} />,
    };
  };
  const memMeId = (meId && members.some((m) => m.id === meId)) ? meId : null;

  // Headline stat box (mirrors UserPage / the Dashboard's StatBox).
  const StatBox = ({ label, value, sub, icon, color, movement }) => (
    <div className="card col gap-8" style={{ padding: "16px 18px", boxShadow: "inset 3px 0 0 " + color + ", var(--shadow-card)" }}>
      <div className="row between">
        <div className="row gap-10">
          <span style={{ width: 32, height: 32, borderRadius: 9, flex: "none", display: "flex", alignItems: "center",
            justifyContent: "center", color: color, background: "color-mix(in srgb, " + color + " 14%, var(--surface))" }}>
            <Icon name={icon} size={17} stroke={2} />
          </span>
          <span className="eyebrow" style={{ alignSelf: "center" }}>{label}</span>
        </div>
        {movement != null && <Movement value={movement} />}
      </div>
      <span className="mono" style={{ fontSize: 30, fontWeight: 700, lineHeight: 1 }}>{value}</span>
      {sub && <span className="muted" style={{ fontSize: 12.5 }}>{sub}</span>}
    </div>
  );

  const Back = () => (
    <button className="btn btn-ghost btn-sm" onClick={() => go("leaderboard")} style={{ alignSelf: "flex-start" }}>
      <Icon name="chevron" size={14} style={{ transform: "rotate(180deg)" }} />Leaderboard
    </button>
  );

  if (loading && !standing) {
    return (
      <div className="col gap-20">
        <Back />
        <div className="card card-pad" style={{ padding: "40px 20px", textAlign: "center" }}>
          <span className="muted" style={{ fontSize: 14 }}>Loading company…</span>
        </div>
      </div>
    );
  }
  if ((error && !company) || !company) {
    return (
      <div className="col gap-20">
        <Back />
        <div className="card card-pad" style={{ padding: "40px 20px", textAlign: "center" }}>
          <span className="muted" style={{ fontSize: 14 }}>{error || "Company not found."}</span>
        </div>
      </div>
    );
  }

  return (
    <div className="col gap-24">
      <Back />

      {/* hero — company identity + headline */}
      <div className="card hero-card" style={{ background: "var(--hero-bg)", color: "var(--hero-ink)", border: "none", overflow: "hidden", position: "relative" }}>
        <div style={{ position: "absolute", inset: 0, opacity: .5, background:
          "radial-gradient(700px 240px at 88% -30%, color-mix(in srgb, var(--brand-blue) 40%, transparent), transparent)" }}></div>
        <div className="card-pad" style={{ padding: "26px 28px", position: "relative" }}>
          <div className="row between wrap gap-16">
            <div className="row gap-16 profile-hero-id" style={{ minWidth: 0, alignItems: "center" }}>
              <CompanyLogo id={company.id} size={56} />
              <div className="col gap-2" style={{ minWidth: 0 }}>
                <span className="eyebrow" style={{ color: "color-mix(in srgb, var(--hero-ink) 70%, transparent)" }}>
                  Company profile{mine ? " · your company" : ""}
                </span>
                <h1 style={{ fontSize: 26, marginTop: 4, fontWeight: 800 }} className="row gap-10">
                  {company.name}{company.isHost && <span className="badge blue">Host</span>}{company.isAi && <AiBadge />}
                </h1>
                <span style={{ opacity: .82, fontSize: 13.5, marginTop: 4 }}>
                  {company.count} player{company.count === 1 ? "" : "s"}{company.bestUser ? " · top scorer " + capName(company.bestUser.first) + " " + capName(company.bestUser.last) : ""}
                </span>
                {company.domain && (
                  <a href={"https://" + company.domain} target="_blank" rel="noopener noreferrer"
                    className="row gap-6" title={"Visit " + company.domain}
                    style={{ opacity: .9, fontSize: 13, marginTop: 6, color: "inherit", textDecoration: "none", alignItems: "center", width: "fit-content" }}>
                    <Icon name="globe" size={14} />
                    <span style={{ textDecoration: "underline", textUnderlineOffset: 2 }}>{company.domain}</span>
                  </a>
                )}
              </div>
            </div>
            <div className="col profile-hero-aside" style={{ alignItems: "flex-end", lineHeight: 1.05 }}>
              <span className="mono" style={{ fontSize: 34, fontWeight: 800 }}>{company.avgTop3}</span>
              <span style={{ opacity: .72, fontSize: 12 }}>avg top 3 · #{company.rank} of {A.COMPANY_RANK.length}</span>
            </div>
          </div>
        </div>
      </div>

      {/* headline stats */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(190px,1fr))", gap: 16 }}>
        <StatBox label="Company score" value={company.avgTop3} icon="trophy" color="var(--accent)" sub="Average of top 3 players" />
        <StatBox label="Company ranking" value={"#" + company.rank} icon="building" color="var(--brand-blue)" movement={company.movement} sub={"of " + A.COMPANY_RANK.length + " companies"} />
        <StatBox label="Total points" value={company.total} icon="trophy" color="var(--gold)" sub="Across all members" />
        <StatBox label="Players" value={company.count} icon="user" color="var(--brand-navy)" sub={company.count === 1 ? "Solo entry" : "On the roster"} />
      </div>

      {/* my stats — one card, two proportional bars (Accuracy rate + Safety rate) over the
          squad's settled picks, reusing the shared StatBar exactly like the Dashboard's
          "My stats" card and the player profile. */}
      <div className="card card-pad col gap-16" style={{ padding: "20px 22px" }}>
        <div className="row between wrap gap-8">
          <span className="eyebrow">{mine ? "My stats" : company.name + "'s stats"}</span>
          <span className="muted" style={{ fontSize: 12.5 }}>{loading ? "Loading…" : settled ? settled + " settled prediction" + (settled === 1 ? "" : "s") : "No settled predictions yet"}</span>
        </div>
        {settled === 0
          ? <span className="muted" style={{ fontSize: 13.5 }}>{loading ? "Crunching the squad's results…" : "Once " + company.name + "'s matches finish, you'll see how their predictions break down here."}</span>
          : <div className="col gap-24">
              {/* 1 — accuracy rate: settled picks split exact / good (right outcome) / wrong */}
              <div className="col gap-12">
                <div className="col gap-2" style={{ minWidth: 0 }}>
                  <span style={{ fontWeight: 700, fontSize: 14 }}>Accuracy rate</span>
                  <span className="muted" style={{ fontSize: 11.5 }}>How the squad's settled picks scored — exact score, right outcome, or missed</span>
                </div>
                <StatBar rows={statRows} total={settled} />
              </div>
              {/* 2 — safety rate: settled picks split by the odds of the outcome backed
                  (high safety = favourite ≤20 pts, medium 21–45, low = long shot >45);
                  brand ramp navy → blue → yellow */}
              <div className="col gap-12">
                <div className="col gap-2" style={{ minWidth: 0 }}>
                  <span style={{ fontWeight: 700, fontSize: 14 }}>Safety rate</span>
                  <span className="muted" style={{ fontSize: 11.5 }}>How safe the squad's bets were — by the odds of the outcome backed</span>
                </div>
                {safetyTotal === 0
                  ? <span className="muted" style={{ fontSize: 12.5 }}>No priced bets settled yet.</span>
                  : <StatBar rows={safetyRows} total={safetyTotal} />}
              </div>
              {/* 3 + 4 — where the squad's points came from, as two donuts: by prediction type
                  (exact vs good — wrong scores 0 so it drops out) and by safety tier. Both
                  chart the EARNED points (`pts`) the bars above carry, so donuts and bars stay
                  in lock-step — exactly like the Dashboard's "My stats" card. */}
              <div className="profile-donuts" style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(260px,1fr))", gap: 24 }}>
                <div className="col gap-12">
                  <div className="col gap-2" style={{ minWidth: 0 }}>
                    <span style={{ fontWeight: 700, fontSize: 14 }}>Points by prediction type</span>
                    <span className="muted" style={{ fontSize: 11.5 }}>How the squad's points split between exact scores and right outcomes</span>
                  </div>
                  <Donut rows={statRows.map((r) => ({ label: r.label, value: r.pts, color: r.color }))}
                    centerSub="total pts" emptyLabel="No points yet" />
                </div>
                <div className="col gap-12">
                  <div className="col gap-2" style={{ minWidth: 0 }}>
                    <span style={{ fontWeight: 700, fontSize: 14 }}>Points by safety rate</span>
                    <span className="muted" style={{ fontSize: 11.5 }}>How the squad's points split across high, medium and low-safety bets</span>
                  </div>
                  <Donut rows={safetyRows.map((r) => ({ label: r.label, value: r.pts, color: r.color }))}
                    centerSub="total pts" emptyLabel="No priced bets settled yet." />
                </div>
              </div>
            </div>}
      </div>

      {/* roster — the company's members, ranked, each linking to their own profile */}
      <div className="card" style={{ overflow: "hidden" }}>
        <div className="row between" style={{ padding: "13px 16px 11px" }}>
          <span className="eyebrow">Roster</span>
          {members.length > 0 && <span className="muted" style={{ fontSize: 12.5 }}>{members.length} player{members.length === 1 ? "" : "s"}</span>}
        </div>
        {members.length === 0
          ? <div className="card-pad" style={{ padding: "0 16px 16px" }}><span className="muted" style={{ fontSize: 13.5 }}>No players on this company yet.</span></div>
          : <React.Fragment>
            <div style={{ padding: "0 16px 11px" }}><StatLegend align="flex-start" /></div>
            <div style={{ overflowX: "auto" }}>
              <table className="lb" style={{ width: "100%", tableLayout: narrow ? "fixed" : undefined }}>
                <thead><tr>
                  <th style={{ width: narrow ? 50 : 54 }}>Rank</th>
                  <th>Player</th>
                  <th style={{ textAlign: "right", width: narrow ? 44 : undefined }}>Points</th>
                  {!narrow && <th style={{ textAlign: "center" }}>Exact</th>}
                  {!narrow && <th style={{ textAlign: "center" }}>Correct</th>}
                  <th style={{ textAlign: "center", width: narrow ? 58 : undefined }}>Accuracy</th>
                  <th style={{ textAlign: "center", width: narrow ? 58 : undefined }}>Safety</th>
                  {!narrow && <th style={{ textAlign: "right" }}>Best</th>}
                </tr></thead>
                <tbody>
                  {members.map((m) => {
                    const you = m.id === meId;
                    return (
                    <tr key={m.id} className={"hov " + (you ? "me-row" : "")}
                      style={{ cursor: store.openUser ? "pointer" : undefined }}
                      onClick={() => store.openUser && store.openUser(m)}>
                      <td><span className="rank-num">{m.rank}</span></td>
                      <td>
                        <span className="row gap-8" style={{ alignItems: "center", minWidth: 0 }}>
                          <CompanyLogo id={m.company || company.id} size={22} />
                          <span style={{ fontWeight: 700, fontSize: 14, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{userName(m)}{you ? " · you" : ""}</span>
                          {m.isAi && <AiBadge />}
                        </span>
                      </td>
                      <td style={{ textAlign: "right" }}><span className="mono" style={{ fontWeight: 700, fontSize: 15 }}>{m.points}</span></td>
                      {!narrow && <td className="mono" style={{ textAlign: "center" }}>{m.exacts}</td>}
                      {!narrow && <td className="mono" style={{ textAlign: "center" }}>{m.correct}</td>}
                      <td><AccuracyBar exacts={m.exacts} good={m.good} settled={m.settled} width={narrow ? 34 : 120} showPct={!narrow} align="center" /></td>
                      <td><SafetyBar high={m.riskLow} med={m.riskMed} low={m.riskHigh} width={narrow ? 34 : 120} showPct={!narrow} align="center" /></td>
                      {!narrow && <td className="mono" style={{ textAlign: "right" }}>+{m.best}</td>}
                    </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </React.Fragment>}
      </div>

      {/* company ranking over time — this company highlighted */}
      <RankSeriesCard title="Company ranking over time"
        sub={`Daily company standings across the tournament — ${mine ? "your" : "this"} company is highlighted.`}
        dates={A.RANK_DATES} entities={coEntities} buildEntry={coBuild}
        meId={companyId} meLabel={coLabel} kindLabel="companies" defaultMode="me" />

      {/* member ranking over time — this company's squad, re-ranked among themselves */}
      <RankSeriesCard title="Member ranking over time"
        sub={`Daily rank within ${mine ? "your" : "the"} squad${memMeId ? " — your line is highlighted" : ""}.`}
        dates={A.RANK_DATES} entities={memEntities} buildEntry={memBuild}
        meId={memMeId} meLabel="Me" kindLabel="players" />
    </div>
  );
}

Object.assign(window, { UserPage, CompanyPage });
