@sensiblestats/widget-react 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -36,7 +36,7 @@ __export(index_exports, {
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/StatsWidget.tsx
39
- var import_react13 = require("react");
39
+ var import_react17 = require("react");
40
40
  var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
41
41
 
42
42
  // src/kickoff.ts
@@ -608,7 +608,14 @@ function reducer(state, action) {
608
608
  return {
609
609
  ...state,
610
610
  messages: state.messages.map(
611
- (m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: { ...m.clubFanDashboard, cards: action.cards } } : m
611
+ (m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: { ...m.clubFanDashboard, cards: action.cards, greeting: action.greeting ?? m.clubFanDashboard.greeting } } : m
612
+ )
613
+ };
614
+ case "LIVE_TRANSITION":
615
+ return {
616
+ ...state,
617
+ messages: state.messages.map(
618
+ (m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: action.dashboard } : m
612
619
  )
613
620
  };
614
621
  }
@@ -617,26 +624,74 @@ function reducer(state, action) {
617
624
  // src/useLiveDashboardPoll.ts
618
625
  var import_react = require("react");
619
626
  var POLL_INTERVAL_MS = 6e4;
620
- function findLiveMessageId(messages) {
627
+ var LEAD_MS = 12e4;
628
+ var GIVE_UP_MS = 30 * 6e4;
629
+ function extractKickoffMs(dashboard) {
630
+ const countdown = dashboard.cards.find((c) => c.cardType === "countdown");
631
+ if (countdown) return countdown.data.kickoffUnix * 1e3;
632
+ for (const c of dashboard.cards) {
633
+ const d = c.data;
634
+ if (typeof d?.kickoffUnix === "number") return d.kickoffUnix * 1e3;
635
+ }
636
+ return null;
637
+ }
638
+ function findAnchor(messages) {
639
+ const now = Date.now();
621
640
  for (const m of messages.slice().reverse()) {
622
- if (m.role === "assistant" && m.clubFanDashboard?.phase === "live") return m.id;
641
+ if (m.role !== "assistant" || !m.clubFanDashboard) continue;
642
+ const { phase } = m.clubFanDashboard;
643
+ if (phase === "live") return { messageId: m.id, kind: "live" };
644
+ if (phase === "preMatch") {
645
+ const kickoffMs = extractKickoffMs(m.clubFanDashboard);
646
+ if (kickoffMs === null) continue;
647
+ if (now >= kickoffMs - LEAD_MS && now <= kickoffMs + GIVE_UP_MS) {
648
+ return { messageId: m.id, kind: "preMatch", kickoffMs };
649
+ }
650
+ }
623
651
  }
624
652
  return null;
625
653
  }
654
+ function findEarliestWakeupMs(messages) {
655
+ const now = Date.now();
656
+ let earliest = null;
657
+ for (const m of messages) {
658
+ if (m.role !== "assistant" || m.clubFanDashboard?.phase !== "preMatch") continue;
659
+ const kickoffMs = extractKickoffMs(m.clubFanDashboard);
660
+ if (kickoffMs === null) continue;
661
+ const wakeAt = kickoffMs - LEAD_MS;
662
+ if (wakeAt > now && (earliest === null || wakeAt < earliest)) earliest = wakeAt;
663
+ }
664
+ return earliest;
665
+ }
626
666
  function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
627
- const messageId = findLiveMessageId(messages);
667
+ const [, forceUpdate] = (0, import_react.useReducer)((x) => x + 1, 0);
668
+ const anchor = findAnchor(messages);
669
+ const anchorRef = (0, import_react.useRef)(anchor);
670
+ anchorRef.current = anchor;
628
671
  const inFlightRef = (0, import_react.useRef)(false);
672
+ const anchorKey = anchor ? `${anchor.messageId}:${anchor.kind}` : null;
629
673
  (0, import_react.useEffect)(() => {
630
- if (!messageId) return;
674
+ if (!anchorKey || !anchorRef.current) return;
631
675
  const tick = async () => {
632
676
  if (inFlightRef.current) return;
633
677
  inFlightRef.current = true;
634
678
  try {
635
679
  const payload = await getLiveDashboard();
636
- if (payload && payload.phase === "live") {
637
- dispatch({ kind: "LIVE_REFRESH", messageId, cards: payload.cards });
680
+ const current = anchorRef.current;
681
+ if (!current) return;
682
+ if (payload?.phase === "live") {
683
+ if (current.kind === "preMatch") {
684
+ dispatch({ kind: "LIVE_TRANSITION", messageId: current.messageId, dashboard: payload });
685
+ window.clearInterval(id);
686
+ } else {
687
+ dispatch({ kind: "LIVE_REFRESH", messageId: current.messageId, cards: payload.cards, greeting: payload.greeting });
688
+ }
638
689
  } else {
639
- window.clearInterval(id);
690
+ if (current.kind === "live") {
691
+ window.clearInterval(id);
692
+ } else if (Date.now() > current.kickoffMs + GIVE_UP_MS) {
693
+ window.clearInterval(id);
694
+ }
640
695
  }
641
696
  } catch {
642
697
  } finally {
@@ -647,7 +702,16 @@ function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
647
702
  void tick();
648
703
  }, POLL_INTERVAL_MS);
649
704
  return () => window.clearInterval(id);
650
- }, [messageId, dispatch, getLiveDashboard]);
705
+ }, [anchorKey, dispatch, getLiveDashboard]);
706
+ (0, import_react.useEffect)(() => {
707
+ if (anchorKey !== null) return;
708
+ const wakeAt = findEarliestWakeupMs(messages);
709
+ if (wakeAt === null) return;
710
+ const delay = wakeAt - Date.now();
711
+ if (delay <= 0) return;
712
+ const id = window.setTimeout(forceUpdate, delay);
713
+ return () => window.clearTimeout(id);
714
+ }, [anchorKey, messages, forceUpdate]);
651
715
  }
652
716
 
653
717
  // src/useChatStream.ts
@@ -812,7 +876,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
812
876
  }
813
877
 
814
878
  // src/components/ChatPanel.tsx
815
- var import_react12 = require("react");
879
+ var import_react16 = require("react");
816
880
 
817
881
  // src/components/ChatHeader.tsx
818
882
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -825,7 +889,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
825
889
  }
826
890
 
827
891
  // src/components/MessageList.tsx
828
- var import_react10 = require("react");
892
+ var import_react14 = require("react");
829
893
 
830
894
  // src/components/Markdown.tsx
831
895
  var import_react_markdown = __toESM(require("react-markdown"), 1);
@@ -1135,22 +1199,44 @@ function ReasonStrip({ it, ui }) {
1135
1199
  ) : null
1136
1200
  ] });
1137
1201
  }
1202
+ function CardFixture({ it }) {
1203
+ if (it.homeTeamName && it.awayTeamName) {
1204
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1205
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "team", url: it.homeCrest, name: it.homeTeamName }),
1206
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-team", children: it.homeTeamName }),
1207
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-vs", "aria-hidden": "true", children: "vs" }),
1208
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-team", children: it.awayTeamName }),
1209
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "team", url: it.awayCrest, name: it.awayTeamName })
1210
+ ] });
1211
+ }
1212
+ if (it.fixture) return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-fixture-txt", children: it.fixture });
1213
+ if (it.competition) {
1214
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1215
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "comp", url: it.competitionCrest, name: it.competition }),
1216
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-comp", children: it.competition })
1217
+ ] });
1218
+ }
1219
+ return null;
1220
+ }
1138
1221
  function InsightCard({ it, ui, slip }) {
1139
1222
  const live = it.isLive || it.phase === "live";
1140
1223
  const kickoff = it.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(it.kickoffUtc) : void 0;
1141
1224
  const hasNamedTeams = !!it.homeTeamName && !!it.awayTeamName;
1142
- const showHeader = !!it.competition || live || !!kickoff;
1225
+ const leftIsFixture = hasNamedTeams || !!it.fixture;
1226
+ const compInMeta = leftIsFixture && !!it.competition;
1227
+ const showHeader = leftIsFixture || !!it.competition || live || !!kickoff;
1143
1228
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-binsight", children: [
1144
1229
  showHeader ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-header", children: [
1145
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-header-left", children: it.competition ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1146
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "comp", url: it.competitionCrest, name: it.competition }),
1147
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-comp", children: it.competition })
1148
- ] }) : null }),
1230
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-header-left", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CardFixture, { it }) }),
1149
1231
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-header-right", children: [
1150
1232
  live ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-live", children: [
1151
1233
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
1152
1234
  ui?.liveLabel ?? "Live"
1153
1235
  ] }) : null,
1236
+ compInMeta ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-comp-meta", children: [
1237
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "comp", url: it.competitionCrest, name: it.competition }),
1238
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-comp", children: it.competition })
1239
+ ] }) : null,
1154
1240
  kickoff ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-kick", children: [
1155
1241
  kickoff.dayLabel,
1156
1242
  " ",
@@ -1158,28 +1244,22 @@ function InsightCard({ it, ui, slip }) {
1158
1244
  ] }) : null
1159
1245
  ] })
1160
1246
  ] }) : null,
1161
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-body", children: [
1162
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "ss-w-bi-fixture", children: hasNamedTeams ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1163
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "team", url: it.homeCrest, name: it.homeTeamName }),
1164
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-team", children: it.homeTeamName }),
1165
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-vs", "aria-hidden": "true", children: "vs" }),
1166
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-team", children: it.awayTeamName }),
1167
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Crest2, { kind: "team", url: it.awayCrest, name: it.awayTeamName })
1168
- ] }) : it.fixture ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-fixture-txt", children: it.fixture }) : null }),
1169
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-action", children: [
1170
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-mkt", children: [
1171
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-market", children: it.market }),
1172
- it.selection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-sel", children: it.selection }) : null
1173
- ] }),
1174
- it.isMarketFloor ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-floor", children: ui?.marketFloorLabel ?? "Market price \xB7 not a value pick" }) : null,
1175
- it.odds ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-odds", children: [
1176
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: it.odds }),
1177
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(OddsMovementBadge, { previous: it.previousOdds, current: it.odds, direction: it.movementDirection, ui }),
1178
- it.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
1179
- ] }) : null,
1180
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AddToSlipButton, { it, slip, ui })
1181
- ] })
1247
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-action-row", children: [
1248
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-pick", children: [
1249
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-market", children: it.market }),
1250
+ it.selection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1251
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-pick-dot", "aria-hidden": "true", children: "\xB7" }),
1252
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-sel", children: it.selection })
1253
+ ] }) : null
1254
+ ] }),
1255
+ it.odds ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ss-w-bi-odds", children: [
1256
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: it.odds }),
1257
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(OddsMovementBadge, { previous: it.previousOdds, current: it.odds, direction: it.movementDirection, ui }),
1258
+ it.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
1259
+ ] }) : null,
1260
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AddToSlipButton, { it, slip, ui })
1182
1261
  ] }),
1262
+ it.isMarketFloor ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-bi-floor", children: ui?.marketFloorLabel ?? "Market price \xB7 not a value pick" }) : null,
1183
1263
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ReasonStrip, { it, ui }),
1184
1264
  it.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
1185
1265
  ] });
@@ -1739,139 +1819,352 @@ function RestingFormCard({ card }) {
1739
1819
  ] });
1740
1820
  }
1741
1821
 
1822
+ // src/components/dashboard/LiveScoreCard.tsx
1823
+ var import_react11 = require("react");
1824
+
1825
+ // src/useLiveClock.ts
1826
+ var import_react10 = require("react");
1827
+ var TICK_MS = 6e4;
1828
+ var MAX_LIVE_TICK_MINUTES = 15;
1829
+ function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
1830
+ if (!clockRunning) return minute;
1831
+ const elapsedMinutes = Math.floor((Date.now() - receivedAtMs) / TICK_MS);
1832
+ return minute + Math.min(Math.max(0, elapsedMinutes), MAX_LIVE_TICK_MINUTES);
1833
+ }
1834
+ function useLiveClock(minute, clockRunning, receivedAtMs) {
1835
+ const [displayMinute, setDisplayMinute] = (0, import_react10.useState)(
1836
+ () => computeDisplayMinute(minute, clockRunning, receivedAtMs)
1837
+ );
1838
+ (0, import_react10.useEffect)(() => {
1839
+ setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
1840
+ if (!clockRunning) return;
1841
+ const id = window.setInterval(() => {
1842
+ setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
1843
+ }, TICK_MS);
1844
+ return () => window.clearInterval(id);
1845
+ }, [minute, clockRunning, receivedAtMs]);
1846
+ return displayMinute;
1847
+ }
1848
+
1742
1849
  // src/components/dashboard/LiveScoreCard.tsx
1743
1850
  var import_jsx_runtime34 = require("react/jsx-runtime");
1744
- function LiveScoreCard({ card }) {
1851
+ function initials3(name) {
1852
+ return name.trim().slice(0, 3).toUpperCase();
1853
+ }
1854
+ function LiveCrest({ url, name, side }) {
1855
+ const [failed, setFailed] = (0, import_react11.useState)(false);
1856
+ const cls = `ss-w-fandash-live-crest ss-w-fandash-live-crest--${side}`;
1857
+ if (url && !failed) {
1858
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
1859
+ }
1860
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: `${cls} ss-w-fandash-live-crest--fallback`, "aria-hidden": "true", children: initials3(name) });
1861
+ }
1862
+ function scorersLine(goalEvents) {
1863
+ if (!goalEvents || goalEvents.length === 0) return null;
1864
+ const named = goalEvents.filter((e) => e.type === "goal" && e.player);
1865
+ if (named.length === 0) return null;
1866
+ return named.slice().sort((a, b) => a.minute - b.minute).map((e) => `${e.player} ${e.minuteLabel}`).join(", ");
1867
+ }
1868
+ function LiveScoreCard({
1869
+ card,
1870
+ goalEvents
1871
+ }) {
1745
1872
  const { data } = card;
1873
+ const seedKey = `${data.minute}:${data.homeScore}:${data.awayScore}`;
1874
+ const seedRef = (0, import_react11.useRef)(null);
1875
+ if (!seedRef.current || seedRef.current.key !== seedKey) {
1876
+ seedRef.current = { key: seedKey, receivedAtMs: Date.now() };
1877
+ }
1878
+ const displayMinute = useLiveClock(data.minute, data.clockRunning, seedRef.current.receivedAtMs);
1879
+ const minuteSuffix = data.minuteLabel.replace(/^\d+/, "");
1880
+ const scorers = scorersLine(goalEvents);
1746
1881
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-score", children: [
1747
1882
  card.title ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1748
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-postmatch-scoreboard", children: [
1749
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-team", children: data.homeName }),
1750
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-score", children: data.homeScore }),
1751
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-sep", children: "\u2013" }),
1752
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-score", children: data.awayScore }),
1753
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-team ss-w-fandash-postmatch-team--away", children: data.awayName })
1754
- ] }),
1755
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-meta", children: [
1756
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-minute", children: data.minuteLabel }),
1757
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-status", children: data.statusLabel }),
1883
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-top", children: [
1884
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "ss-w-fandash-live-pill", children: [
1885
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-dot", "aria-hidden": "true" }),
1886
+ data.statusLabel
1887
+ ] }),
1888
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-minute", children: `${displayMinute}${minuteSuffix}` }),
1758
1889
  data.competition ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-competition", children: data.competition }) : null
1759
- ] })
1890
+ ] }),
1891
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-main", children: [
1892
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-side ss-w-fandash-live-side--home", children: [
1893
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LiveCrest, { url: data.homeCrestUrl, name: data.homeName, side: "home" }),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-team-name", children: data.homeName })
1895
+ ] }),
1896
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-score-main", children: [
1897
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-score-value", children: data.homeScore }),
1898
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-score-sep", children: "\u2013" }),
1899
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-score-value", children: data.awayScore })
1900
+ ] }),
1901
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-side ss-w-fandash-live-side--away", children: [
1902
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LiveCrest, { url: data.awayCrestUrl, name: data.awayName, side: "away" }),
1903
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-team-name", children: data.awayName })
1904
+ ] })
1905
+ ] }),
1906
+ scorers ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-scorers", children: [
1907
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-scorers-icon", "aria-hidden": "true", children: "\u26BD" }),
1908
+ scorers
1909
+ ] }) : null
1760
1910
  ] });
1761
1911
  }
1762
1912
 
1763
1913
  // src/components/dashboard/LiveTimelineCard.tsx
1914
+ var import_react12 = require("react");
1764
1915
  var import_jsx_runtime35 = require("react/jsx-runtime");
1765
- function LiveTimelineCard({ card }) {
1916
+ var SHOWN = 3;
1917
+ function EventIcon({ type }) {
1918
+ if (type === "goal") {
1919
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico ss-w-fandash-live-tl-ico--goal", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ball" }) });
1920
+ }
1921
+ if (type === "yellow" || type === "red") {
1922
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: `ss-w-fandash-live-tl-card ss-w-fandash-live-tl-card--${type}` }) });
1923
+ }
1924
+ if (type === "sub") {
1925
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-subarrows", children: "\u21C5" }) });
1926
+ }
1927
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-ico", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-dot" }) });
1928
+ }
1929
+ function EventLabel({ e }) {
1930
+ if (e.type === "sub") {
1931
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ss-w-fandash-live-tl-who", children: [
1932
+ e.player ?? e.text,
1933
+ e.playerOff ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ss-w-fandash-live-tl-off", children: [
1934
+ " for ",
1935
+ e.playerOff
1936
+ ] }) : null
1937
+ ] });
1938
+ }
1939
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-who", children: e.player ?? e.text });
1940
+ }
1941
+ function EventRow({ e }) {
1942
+ const cls = [
1943
+ "ss-w-fandash-live-tl-ev",
1944
+ e.side === "home" ? "ss-w-fandash-live-tl-ev--home" : "ss-w-fandash-live-tl-ev--away",
1945
+ e.type === "goal" ? "ss-w-fandash-live-tl-ev--goal" : ""
1946
+ ].filter(Boolean).join(" ");
1947
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cls, children: [
1948
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ss-w-fandash-live-tl-txt", children: [
1949
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(EventLabel, { e }),
1950
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(EventIcon, { type: e.type })
1951
+ ] }),
1952
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-tl-badge", children: e.minuteLabel })
1953
+ ] });
1954
+ }
1955
+ function LiveTimelineCard({ card, ui }) {
1956
+ const [expanded, setExpanded] = (0, import_react12.useState)(false);
1957
+ const events = card.data.events;
1958
+ if (!events || events.length === 0) return null;
1959
+ const sorted = [...events].sort((a, b) => b.minute - a.minute);
1960
+ const rest = sorted.length - SHOWN;
1961
+ const shown = expanded ? sorted : sorted.slice(0, SHOWN);
1962
+ const showMore = ui?.showMore ?? ((n) => `Show ${n} more`);
1963
+ const showLess = ui?.showLess ?? "Show less";
1766
1964
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-timeline", children: [
1767
1965
  card.title ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1768
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "ss-w-fandash-live-event-list", children: card.data.events.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: e.scoredByClub ? "ss-w-fandash-live-event ss-w-fandash-live-event--club" : "ss-w-fandash-live-event", children: [
1769
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-event-minute", children: e.minuteLabel }),
1770
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-event-text", children: e.text })
1771
- ] }, `${e.minuteLabel}-${i}`)) })
1966
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "ss-w-fandash-live-tl", children: shown.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(EventRow, { e }, `${e.minute}-${e.side}-${e.type}-${i}`)) }),
1967
+ rest > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1968
+ "button",
1969
+ {
1970
+ type: "button",
1971
+ className: "ss-w-fandash-live-tl-more",
1972
+ "aria-expanded": expanded,
1973
+ onClick: () => setExpanded((v) => !v),
1974
+ children: expanded ? showLess : showMore(rest)
1975
+ }
1976
+ ) : null
1772
1977
  ] });
1773
1978
  }
1774
1979
 
1775
- // src/components/dashboard/LiveStatsCard.tsx
1980
+ // src/components/dashboard/MomentumWave.tsx
1981
+ var import_react13 = require("react");
1776
1982
  var import_jsx_runtime36 = require("react/jsx-runtime");
1983
+ var WIDTH = 270;
1984
+ var HEIGHT = 56;
1985
+ var BASELINE = HEIGHT / 2;
1986
+ var SCALE = 0.26;
1987
+ function buildPaths(points) {
1988
+ const lastIndex = points.length - 1;
1989
+ const px = (i) => lastIndex === 0 ? 0 : i / lastIndex * WIDTH;
1990
+ const py = (v) => BASELINE - v * SCALE;
1991
+ const linePts = points.map((p, i) => `${px(i).toFixed(1)},${py(p.value).toFixed(1)}`);
1992
+ const area = `M0,${BASELINE} L${linePts.join(" L")} L${WIDTH},${BASELINE} Z`;
1993
+ const line = `M${linePts.join(" L")}`;
1994
+ const lastValue = points[lastIndex]?.value ?? 0;
1995
+ return { area, line, lastX: px(lastIndex).toFixed(1), lastY: py(lastValue).toFixed(1) };
1996
+ }
1997
+ function MomentumWave({
1998
+ points,
1999
+ caption,
2000
+ clubIsHome
2001
+ }) {
2002
+ if (points.length === 0) return null;
2003
+ const { area, line, lastX, lastY } = buildPaths(points);
2004
+ const clipId = (0, import_react13.useId)();
2005
+ const topClipId = `${clipId}-top`;
2006
+ const bottomClipId = `${clipId}-bottom`;
2007
+ const topClass = clubIsHome ? "ss-w-fandash-live-mom-home" : "ss-w-fandash-live-mom-away";
2008
+ const bottomClass = clubIsHome ? "ss-w-fandash-live-mom-away" : "ss-w-fandash-live-mom-home";
2009
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-live-mom", children: [
2010
+ caption ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-mom-caption", children: caption }) : null,
2011
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
2012
+ "svg",
2013
+ {
2014
+ className: "ss-w-fandash-live-mom-svg",
2015
+ viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
2016
+ preserveAspectRatio: "none",
2017
+ role: "img",
2018
+ "aria-label": caption ?? "Momentum",
2019
+ children: [
2020
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("defs", { children: [
2021
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("clipPath", { id: topClipId, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("rect", { x: 0, y: 0, width: WIDTH, height: BASELINE }) }),
2022
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("clipPath", { id: bottomClipId, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("rect", { x: 0, y: BASELINE, width: WIDTH, height: HEIGHT - BASELINE }) })
2023
+ ] }),
2024
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: area, className: topClass, clipPath: `url(#${topClipId})` }),
2025
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: area, className: bottomClass, clipPath: `url(#${bottomClipId})` }),
2026
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("line", { x1: 0, y1: BASELINE, x2: WIDTH, y2: BASELINE, className: "ss-w-fandash-live-mom-base" }),
2027
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: line, className: "ss-w-fandash-live-mom-line", vectorEffect: "non-scaling-stroke" }),
2028
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { cx: lastX, cy: lastY, r: 3.2, className: "ss-w-fandash-live-mom-now", vectorEffect: "non-scaling-stroke" })
2029
+ ]
2030
+ }
2031
+ )
2032
+ ] });
2033
+ }
2034
+
2035
+ // src/components/dashboard/LiveStatsCard.tsx
2036
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2037
+ function splitWidths(homeNum, awayNum) {
2038
+ const total = homeNum + awayNum;
2039
+ if (total <= 0) return { home: "50%", away: "50%" };
2040
+ const homePct = Math.round(homeNum / total * 1e3) / 10;
2041
+ return { home: `${homePct}%`, away: `${Math.round((100 - homePct) * 10) / 10}%` };
2042
+ }
1777
2043
  function LiveStatsCard({ card }) {
1778
2044
  const { data } = card;
1779
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-stats", children: [
1780
- card.title ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1781
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "ss-w-fandash-live-stat-rows", children: data.rows.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-live-stat-row", children: [
1782
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-value", children: r.clubValue }),
1783
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-label", children: r.label }),
1784
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-value", children: r.oppValue })
1785
- ] }, `${r.label}-${i}`)) }),
1786
- data.momentumLabel ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-momentum", children: data.momentumLabel }) : null
2045
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-stats", children: [
2046
+ card.title ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
2047
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "ss-w-fandash-live-stat-rows", children: data.rows.map((r, i) => {
2048
+ const homeValue = data.clubIsHome ? r.clubValue : r.oppValue;
2049
+ const awayValue = data.clubIsHome ? r.oppValue : r.clubValue;
2050
+ const homeNum = data.clubIsHome ? r.clubNum : r.oppNum;
2051
+ const awayNum = data.clubIsHome ? r.oppNum : r.clubNum;
2052
+ const { home, away } = splitWidths(homeNum, awayNum);
2053
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-stat", children: [
2054
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-stat-row", children: [
2055
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-stat-value ss-w-fandash-live-stat-value--home", children: homeValue }),
2056
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-stat-label", children: r.label }),
2057
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-stat-value ss-w-fandash-live-stat-value--away", children: awayValue })
2058
+ ] }),
2059
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-bar", children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-bar-home", style: { width: home } }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-bar-away", style: { width: away } })
2062
+ ] })
2063
+ ] }, `${r.label}-${i}`);
2064
+ }) }),
2065
+ data.momentum.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MomentumWave, { points: data.momentum, caption: data.momentumLabel, clubIsHome: data.clubIsHome }) : data.momentumLabel ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-live-momentum", children: data.momentumLabel }) : null,
2066
+ data.rows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash-live-legend", children: [
2067
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "ss-w-fandash-live-legend-item", children: [
2068
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("i", { className: "ss-w-fandash-live-legend-swatch ss-w-fandash-live-legend-swatch--home" }),
2069
+ data.homeName
2070
+ ] }),
2071
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "ss-w-fandash-live-legend-item", children: [
2072
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("i", { className: "ss-w-fandash-live-legend-swatch ss-w-fandash-live-legend-swatch--away" }),
2073
+ data.awayName
2074
+ ] })
2075
+ ] }) : null
1787
2076
  ] });
1788
2077
  }
1789
2078
 
1790
2079
  // src/components/dashboard/ClubFanDashboard.tsx
1791
- var import_jsx_runtime37 = require("react/jsx-runtime");
2080
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1792
2081
  function ClubFanDashboard({ dashboard, onTap, ui }) {
1793
2082
  const orderedCards = [
1794
2083
  ...dashboard.cards.filter((c) => c.cardType === "countdown"),
1795
2084
  ...dashboard.cards.filter((c) => c.cardType !== "countdown")
1796
2085
  ];
1797
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash", children: [
1798
- dashboard.greeting ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
1799
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(FanDashboardCard, { card, onTap, ui }, `${card.cardType}-${i}`)) })
2086
+ const timelineCard = dashboard.cards.find(
2087
+ (c) => c.cardType === "liveTimeline"
2088
+ );
2089
+ const goalEvents = timelineCard?.data.events.filter((e) => e.type === "goal");
2090
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "ss-w-fandash", children: [
2091
+ dashboard.greeting ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
2092
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FanDashboardCard, { card, onTap, ui, goalEvents }, `${card.cardType}-${i}`)) })
1800
2093
  ] });
1801
2094
  }
1802
- function FanDashboardCard({ card, onTap, ui }) {
2095
+ function FanDashboardCard({ card, onTap, ui, goalEvents }) {
1803
2096
  switch (card.cardType) {
1804
2097
  case "fixture":
1805
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(FixtureCard, { card, onTap });
2098
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FixtureCard, { card, onTap });
1806
2099
  case "countdown":
1807
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CountdownCard, { card });
2100
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(CountdownCard, { card });
1808
2101
  case "injuries":
1809
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(InjuriesCard, { card });
2102
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(InjuriesCard, { card });
1810
2103
  case "reasonToBet":
1811
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReasonToBetCard, { card });
2104
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReasonToBetCard, { card });
1812
2105
  case "whereToWatch":
1813
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(WhereToWatchCard, { card });
2106
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(WhereToWatchCard, { card });
1814
2107
  case "congestedWeek":
1815
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CongestedWeekCard, { card, onTap });
2108
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(CongestedWeekCard, { card, onTap });
1816
2109
  case "transfers":
1817
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TransfersCard, { card });
2110
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TransfersCard, { card });
1818
2111
  case "links":
1819
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LinksCard, { card });
2112
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LinksCard, { card });
1820
2113
  case "postMatchResult":
1821
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchResultCard, { card });
2114
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchResultCard, { card });
1822
2115
  case "postMatchGoals":
1823
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchGoalsCard, { card, onTap });
2116
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchGoalsCard, { card, onTap });
1824
2117
  case "postMatchStats":
1825
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchStatsCard, { card, onTap });
2118
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchStatsCard, { card, onTap });
1826
2119
  case "postMatchTopPerformer":
1827
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchTopPerformerCard, { card, onTap });
2120
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchTopPerformerCard, { card, onTap });
1828
2121
  case "postMatchNext":
1829
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchNextCard, { card });
2122
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchNextCard, { card });
1830
2123
  case "restingNext":
1831
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingNextCard, { card, onTap });
2124
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingNextCard, { card, onTap });
1832
2125
  case "restingStandings":
1833
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingStandingsCard, { card, onTap });
2126
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingStandingsCard, { card, onTap });
1834
2127
  case "restingSeason":
1835
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingSeasonCard, { card, onTap });
2128
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingSeasonCard, { card, onTap });
1836
2129
  case "restingForm":
1837
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingFormCard, { card });
2130
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingFormCard, { card });
1838
2131
  case "liveScore":
1839
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveScoreCard, { card });
2132
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveScoreCard, { card, goalEvents });
1840
2133
  case "liveTimeline":
1841
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveTimelineCard, { card });
2134
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveTimelineCard, { card, ui });
1842
2135
  case "liveStats":
1843
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveStatsCard, { card });
2136
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveStatsCard, { card });
1844
2137
  default:
1845
2138
  return null;
1846
2139
  }
1847
2140
  }
1848
2141
 
1849
2142
  // src/components/ChatMessage.tsx
1850
- var import_jsx_runtime38 = require("react/jsx-runtime");
2143
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1851
2144
  function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
1852
2145
  if (message.role === "user") {
1853
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-msg ss-w-user", "data-msg-id": message.id, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
2146
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "ss-w-msg ss-w-user", "data-msg-id": message.id, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
1854
2147
  }
1855
2148
  const onFanDashboardTap = (card) => {
1856
2149
  onAct({ message: card.actionLabel, actionRef: card.actionRef, label: card.actionLabel || card.title || "" });
1857
2150
  };
1858
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "ss-w-msg ss-w-bot", "data-msg-id": message.id, children: [
1859
- message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Markdown, { text: message.text }) : null,
1860
- message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1861
- message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
1862
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1863
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
1864
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1865
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1866
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
2151
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "ss-w-msg ss-w-bot", "data-msg-id": message.id, children: [
2152
+ message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Markdown, { text: message.text }) : null,
2153
+ message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
2154
+ message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
2155
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
2156
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
2157
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
2158
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
2159
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
1867
2160
  ] });
1868
2161
  }
1869
2162
 
1870
2163
  // src/components/IntentChips.tsx
1871
- var import_jsx_runtime39 = require("react/jsx-runtime");
2164
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1872
2165
  function IntentChips({ chips, onPick }) {
1873
2166
  if (chips.length === 0) return null;
1874
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
2167
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
1875
2168
  }
1876
2169
 
1877
2170
  // src/scroll.ts
@@ -1881,7 +2174,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
1881
2174
  }
1882
2175
 
1883
2176
  // src/components/MessageList.tsx
1884
- var import_jsx_runtime40 = require("react/jsx-runtime");
2177
+ var import_jsx_runtime41 = require("react/jsx-runtime");
1885
2178
  function turnAnchor(messages) {
1886
2179
  const last = messages[messages.length - 1];
1887
2180
  if (!last || last.role !== "assistant") return null;
@@ -1890,11 +2183,11 @@ function turnAnchor(messages) {
1890
2183
  }
1891
2184
  function MessageList({ controller, ui, slip, onDashboardTap }) {
1892
2185
  const { state, send } = controller;
1893
- const listRef = (0, import_react10.useRef)(null);
1894
- const endRef = (0, import_react10.useRef)(null);
1895
- const pinnedRef = (0, import_react10.useRef)(true);
1896
- const anchoredTurnRef = (0, import_react10.useRef)(null);
1897
- (0, import_react10.useEffect)(() => {
2186
+ const listRef = (0, import_react14.useRef)(null);
2187
+ const endRef = (0, import_react14.useRef)(null);
2188
+ const pinnedRef = (0, import_react14.useRef)(true);
2189
+ const anchoredTurnRef = (0, import_react14.useRef)(null);
2190
+ (0, import_react14.useEffect)(() => {
1898
2191
  const anchor = turnAnchor(state.messages);
1899
2192
  const list = listRef.current;
1900
2193
  if (anchor && anchor.turnId !== anchoredTurnRef.current) {
@@ -1909,7 +2202,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1909
2202
  }, [state.messages, state.status, state.isStreaming]);
1910
2203
  const hasUserMessage = state.messages.some((m) => m.role === "user");
1911
2204
  const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
1912
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2205
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
1913
2206
  "div",
1914
2207
  {
1915
2208
  className: "ss-w-list",
@@ -1919,7 +2212,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1919
2212
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
1920
2213
  },
1921
2214
  children: [
1922
- state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2215
+ state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1923
2216
  ChatMessage,
1924
2217
  {
1925
2218
  message: m,
@@ -1934,25 +2227,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1934
2227
  },
1935
2228
  m.id
1936
2229
  )),
1937
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
2230
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
1938
2231
  } }) : null,
1939
- state.status ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
1940
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
1941
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1942
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
2232
+ state.status ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
2233
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
2234
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
2235
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1943
2236
  ] }) : null,
1944
- !hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1945
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref: endRef, className: "ss-w-turn-spacer", "aria-hidden": "true" })
2237
+ !hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
2238
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { ref: endRef, className: "ss-w-turn-spacer", "aria-hidden": "true" })
1946
2239
  ]
1947
2240
  }
1948
2241
  );
1949
2242
  }
1950
2243
 
1951
2244
  // src/components/ChatInput.tsx
1952
- var import_react11 = require("react");
1953
- var import_jsx_runtime41 = require("react/jsx-runtime");
2245
+ var import_react15 = require("react");
2246
+ var import_jsx_runtime42 = require("react/jsx-runtime");
1954
2247
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
1955
- const [value, setValue] = (0, import_react11.useState)("");
2248
+ const [value, setValue] = (0, import_react15.useState)("");
1956
2249
  const submit = () => {
1957
2250
  const t = value.trim();
1958
2251
  if (!t) return;
@@ -1965,18 +2258,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
1965
2258
  submit();
1966
2259
  }
1967
2260
  };
1968
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ss-w-input", children: [
1969
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1970
- streaming ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SendIcon, {}) })
2261
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ss-w-input", children: [
2262
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
2263
+ streaming ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SendIcon, {}) })
1971
2264
  ] });
1972
2265
  }
1973
2266
 
1974
2267
  // src/components/ChatPanel.tsx
1975
- var import_jsx_runtime42 = require("react/jsx-runtime");
2268
+ var import_jsx_runtime43 = require("react/jsx-runtime");
1976
2269
  var FULLSCREEN_QUERY = "(max-width: 639px)";
1977
2270
  function useIsModal() {
1978
- const [modal, setModal] = (0, import_react12.useState)(false);
1979
- (0, import_react12.useEffect)(() => {
2271
+ const [modal, setModal] = (0, import_react16.useState)(false);
2272
+ (0, import_react16.useEffect)(() => {
1980
2273
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1981
2274
  const mql = window.matchMedia(FULLSCREEN_QUERY);
1982
2275
  const sync = () => setModal(mql.matches);
@@ -1989,17 +2282,17 @@ function useIsModal() {
1989
2282
  function ChatPanel({ controller, ui, slip, onDashboardTap }) {
1990
2283
  const { state, close, stop, send } = controller;
1991
2284
  const isModal = useIsModal();
1992
- (0, import_react12.useEffect)(() => {
2285
+ (0, import_react16.useEffect)(() => {
1993
2286
  const onKey = (e) => {
1994
2287
  if (e.key === "Escape") close();
1995
2288
  };
1996
2289
  window.addEventListener("keydown", onKey);
1997
2290
  return () => window.removeEventListener("keydown", onKey);
1998
2291
  }, [close]);
1999
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
2000
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
2001
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
2002
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
2292
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
2293
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
2294
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
2295
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
2003
2296
  ] });
2004
2297
  }
2005
2298
 
@@ -2012,6 +2305,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2012
2305
  --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;
2013
2306
  --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;
2014
2307
  --ss-w-pos: #1aa563; --ss-w-neg: #e0524b;
2308
+ --ss-w-home: #3a63a8; --ss-w-away: #c8102e; --ss-w-track: #e9edf1;
2015
2309
  --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;
2016
2310
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
2017
2311
  font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);
@@ -2022,6 +2316,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2022
2316
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
2023
2317
  --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
2024
2318
  --ss-w-pos: #37c891; --ss-w-neg: #f0655d;
2319
+ --ss-w-home: #5b83c8; --ss-w-away: #e8384f; --ss-w-track: #232d3a;
2025
2320
  }
2026
2321
  }
2027
2322
  .ss-w-root[data-ss-w-theme="dark"] {
@@ -2029,11 +2324,13 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2029
2324
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
2030
2325
  --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
2031
2326
  --ss-w-pos: #37c891; --ss-w-neg: #f0655d;
2327
+ --ss-w-home: #5b83c8; --ss-w-away: #e8384f; --ss-w-track: #232d3a;
2032
2328
  }
2033
2329
  .ss-w-root[data-ss-w-theme="light"] {
2034
2330
  --ss-w-accent: #17936a; --ss-w-cta: #f5a623; --ss-w-cta-ink: #1b1300;
2035
2331
  --ss-w-ink: #14181d; --ss-w-faint: #6b7580;
2036
2332
  --ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;
2333
+ --ss-w-home: #3a63a8; --ss-w-away: #c8102e; --ss-w-track: #e9edf1;
2037
2334
  }
2038
2335
 
2039
2336
  /* launcher */
@@ -2148,7 +2445,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2148
2445
 
2149
2446
  /* row 1: competition logo + name on the left, live badge + kickoff on the right */
2150
2447
  .ss-w-bi-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; padding-bottom: 7px; border-bottom: 1px solid var(--ss-w-line); }
2151
- .ss-w-bi-header-left { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
2448
+ .ss-w-bi-header-left { display: inline-flex; align-items: center; gap: 6px; min-width: 0; flex: 1; font-size: 12.5px; font-weight: 650; color: var(--ss-w-ink); }
2152
2449
  .ss-w-bi-header-right { display: inline-flex; align-items: center; gap: 8px; flex: none; }
2153
2450
  .ss-w-bi-comp { font-size: 10.5px; color: var(--ss-w-faint); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2154
2451
  .ss-w-bi-kick { font-size: 10.5px; font-weight: 600; color: var(--ss-w-faint); white-space: nowrap; flex: none; }
@@ -2161,23 +2458,26 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2161
2458
  .ss-w-bi-crest { width: 24px; height: 24px; flex: none; display: block; border-radius: 50%; object-fit: contain; }
2162
2459
  .ss-w-bi-crest-mono { display: grid; place-items: center; font-size: 10px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 16%, var(--ss-w-panel)); }
2163
2460
 
2164
- /* row 2: crest-flanked fixture on the left, boxed action panel on the right */
2165
- .ss-w-bi-body { display: flex; align-items: center; gap: 10px; }
2166
- .ss-w-bi-fixture { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; font-size: 12px; font-weight: 650; color: var(--ss-w-ink); }
2461
+ /* header fixture atoms (team hero lives in .ss-w-bi-header-left; comp demotes to the meta) */
2167
2462
  .ss-w-bi-team { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2168
2463
  .ss-w-bi-vs { font-size: 10px; font-weight: 600; color: var(--ss-w-faint); flex: none; }
2169
2464
  .ss-w-bi-fixture-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2170
- .ss-w-bi-action { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; min-width: 0; }
2171
- .ss-w-bi-action .ss-w-slip { width: 100%; }
2172
- .ss-w-bi-action .ss-w-slip > button { flex: 1; width: 100%; text-align: center; border-radius: 8px; }
2173
- /* The action column is narrow; let its toast size to its own text and anchor to the card's right edge instead of being clipped to the button width. */
2174
- .ss-w-bi-action .ss-w-toast { left: auto; right: 0; width: max-content; max-width: 210px; white-space: normal; }
2175
- .ss-w-bi-mkt { display: flex; flex-direction: column; gap: 1px; min-width: 0; align-items: flex-end; text-align: right; }
2176
- .ss-w-bi-market { font-size: 12px; font-weight: 600; line-height: 1.2; color: var(--ss-w-faint); white-space: nowrap; }
2177
- .ss-w-bi-sel { font-size: 11px; line-height: 1.25; color: var(--ss-w-faint); }
2465
+ .ss-w-bi-comp-meta { display: inline-flex; align-items: center; gap: 5px; flex: none; min-width: 0; }
2466
+ /* shared crest-flanked team row, still consumed by MarketOddsCardList */
2467
+ .ss-w-bi-fixture { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; font-size: 12px; font-weight: 650; color: var(--ss-w-ink); }
2468
+
2469
+ /* row 2: one action line -- market \xB7 selection (left, market ellipsises first), odds, slip button */
2470
+ .ss-w-bi-action-row { display: flex; align-items: center; gap: 9px; }
2471
+ .ss-w-bi-pick { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 6px; }
2472
+ .ss-w-bi-market { font-size: 12px; font-weight: 600; line-height: 1.2; color: var(--ss-w-faint); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2473
+ .ss-w-bi-pick-dot { flex: none; color: var(--ss-w-faint); font-weight: 700; }
2474
+ .ss-w-bi-sel { flex: none; font-size: 12.5px; font-weight: 700; line-height: 1.2; color: var(--ss-w-ink); white-space: nowrap; }
2178
2475
  .ss-w-bi-odds { flex: none; display: flex; align-items: baseline; justify-content: flex-end; gap: 5px; }
2179
- .ss-w-bi-odds b { font-size: 23px; font-family: ui-monospace, monospace; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); line-height: 1.05; }
2476
+ .ss-w-bi-odds b { font-size: 20px; font-family: ui-monospace, monospace; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); line-height: 1.05; }
2180
2477
  .ss-w-bi-book { font-size: 9.5px; color: var(--ss-w-faint); }
2478
+ .ss-w-bi-action-row .ss-w-slip { flex: none; }
2479
+ /* let the slip toast size to its own text and anchor to the card's right edge, not the button width */
2480
+ .ss-w-bi-action-row .ss-w-toast { left: auto; right: 0; width: max-content; max-width: 210px; white-space: normal; }
2181
2481
 
2182
2482
  /* row 3: bar-chart glyph + reasoning prose + stats-expander chevron, in a subtle strip */
2183
2483
  .ss-w-bi-reason { display: flex; align-items: flex-start; gap: 8px; background: var(--ss-w-bubble); border-radius: 10px; padding: 8px 10px; }
@@ -2519,34 +2819,90 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2519
2819
  /* resting form: reuses the fixture last-five pill classes (ss-w-fandash-form / -form-pill) */
2520
2820
  .ss-w-fandash-resting-form .ss-w-fandash-form { margin-top: 2px; }
2521
2821
 
2522
- /* live score: reuses the post-match scoreboard classes for the team/score row, with its own
2523
- meta row -- minute called out in the accent color, status and competition beside it */
2524
- .ss-w-fandash-live-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11.5px; color: var(--ss-w-faint); }
2525
- .ss-w-fandash-live-minute { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }
2526
- .ss-w-fandash-live-status { font-weight: 650; color: var(--ss-w-ink); }
2527
- .ss-w-fandash-live-competition { color: var(--ss-w-faint); }
2528
-
2529
- /* live timeline: a plain event list, the club's own events picked out in the accent color --
2530
- same mechanism as the post-match goal list, plus a minute column */
2531
- .ss-w-fandash-live-event-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
2532
- .ss-w-fandash-live-event { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--ss-w-faint); }
2533
- .ss-w-fandash-live-event--club { color: var(--ss-w-ink); font-weight: 650; }
2534
- .ss-w-fandash-live-event-minute { flex: none; font-variant-numeric: tabular-nums; color: var(--ss-w-faint); }
2535
- .ss-w-fandash-live-event--club .ss-w-fandash-live-event-minute { color: var(--ss-w-accent); }
2536
- .ss-w-fandash-live-event-text { min-width: 0; }
2537
-
2538
- /* live stats: club value / stat label / opponent value, same 3-column layout as post-match
2539
- stats, plus an optional momentum line underneath */
2540
- .ss-w-fandash-live-stat-rows { display: flex; flex-direction: column; gap: 5px; }
2541
- .ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
2822
+ /* live score: pulsing Live pill, crests (real image or a coloured initials fallback), a big
2823
+ tabular score, and an optional scorers line. Also where the shared home/away venue-colour
2824
+ tokens (--ss-w-home / --ss-w-away / --ss-w-track) are defined above -- the timeline and stats
2825
+ cards reuse them. */
2826
+ .ss-w-fandash-live-top { display: flex; align-items: center; gap: 8px; }
2827
+ .ss-w-fandash-live-pill { display: inline-flex; flex: none; align-items: center; gap: 5px; font-size: 10px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: var(--ss-w-neg); border-radius: 6px; padding: 3px 7px; }
2828
+ .ss-w-fandash-live-dot { width: 5px; height: 5px; border-radius: 50%; background: #fff; animation: ss-w-fandash-live-pulse 1.3s ease-in-out infinite; }
2829
+ @keyframes ss-w-fandash-live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
2830
+ @media (prefers-reduced-motion: reduce) {
2831
+ .ss-w-fandash-live-dot { animation: none; }
2832
+ }
2833
+ .ss-w-fandash-live-minute { flex: none; font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
2834
+ .ss-w-fandash-live-competition { margin-left: auto; font-size: 11px; font-weight: 650; color: var(--ss-w-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2835
+ .ss-w-fandash-live-main { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
2836
+ .ss-w-fandash-live-side { display: flex; align-items: center; gap: 8px; min-width: 0; }
2837
+ .ss-w-fandash-live-side--away { flex-direction: row-reverse; text-align: right; }
2838
+ .ss-w-fandash-live-crest { width: 30px; height: 30px; border-radius: 8px; flex: none; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 10.5px; object-fit: cover; }
2839
+ .ss-w-fandash-live-crest--fallback.ss-w-fandash-live-crest--home { background: var(--ss-w-home); }
2840
+ .ss-w-fandash-live-crest--fallback.ss-w-fandash-live-crest--away { background: var(--ss-w-away); }
2841
+ .ss-w-fandash-live-team-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
2842
+ .ss-w-fandash-live-score-main { display: flex; align-items: baseline; justify-content: center; font-size: 26px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; color: var(--ss-w-ink); }
2843
+ .ss-w-fandash-live-score-sep { color: var(--ss-w-faint); font-weight: 500; margin: 0 4px; }
2844
+ .ss-w-fandash-live-scorers { margin-top: 2px; display: flex; justify-content: flex-end; gap: 6px; align-items: center; font-size: 11.5px; color: var(--ss-w-faint); overflow-wrap: anywhere; }
2845
+ .ss-w-fandash-live-scorers-icon { flex: none; color: var(--ss-w-away); }
2846
+
2847
+ /* live timeline: a compact two-sided event list down a centre spine -- each club's events sit on
2848
+ its own side (home left, away right), most-recent 3 shown with a "See more" toggle for the
2849
+ rest. A 3-column grid (txt | badge | txt) per row; the spine itself is a ::before on the list. */
2850
+ .ss-w-fandash-live-tl { position: relative; display: flex; flex-direction: column; gap: 6px; }
2851
+ .ss-w-fandash-live-tl::before { content: ""; position: absolute; left: 50%; top: 1px; bottom: 1px; width: 2px; background: var(--ss-w-line); transform: translateX(-1px); }
2852
+ .ss-w-fandash-live-tl-ev { display: grid; grid-template-columns: 1fr 32px 1fr; align-items: center; gap: 6px; }
2853
+ .ss-w-fandash-live-tl-txt { display: flex; align-items: center; gap: 6px; font-size: 12.5px; min-width: 0; }
2854
+ .ss-w-fandash-live-tl-ev--home .ss-w-fandash-live-tl-txt { grid-column: 1; justify-content: flex-end; }
2855
+ .ss-w-fandash-live-tl-ev--away .ss-w-fandash-live-tl-txt { grid-column: 3; justify-content: flex-start; flex-direction: row-reverse; }
2856
+ .ss-w-fandash-live-tl-who { font-weight: 650; color: var(--ss-w-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2857
+ .ss-w-fandash-live-tl-off { font-weight: 500; color: var(--ss-w-faint); }
2858
+ .ss-w-fandash-live-tl-ev--goal .ss-w-fandash-live-tl-who { font-weight: 800; }
2859
+ .ss-w-fandash-live-tl-badge { grid-column: 2; justify-self: center; min-width: 27px; height: 18px; padding: 0 4px; border-radius: 6px; display: grid; place-items: center; font-size: 10.5px; font-weight: 800; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); font-variant-numeric: tabular-nums; z-index: 1; }
2860
+ .ss-w-fandash-live-tl-ev--goal .ss-w-fandash-live-tl-badge { background: var(--ss-w-accent); color: #fff; border-color: transparent; }
2861
+ .ss-w-fandash-live-tl-ico { width: 16px; height: 16px; flex: none; display: grid; place-items: center; }
2862
+ .ss-w-fandash-live-tl-ico--goal { border-radius: 50%; background: color-mix(in srgb, var(--ss-w-away) 16%, transparent); }
2863
+ .ss-w-fandash-live-tl-ball { width: 11px; height: 11px; border-radius: 50%; background: var(--ss-w-ink); border: 1px solid var(--ss-w-ink); }
2864
+ .ss-w-fandash-live-tl-card { width: 8px; height: 11px; border-radius: 2px; display: block; }
2865
+ .ss-w-fandash-live-tl-card--yellow { background: var(--ss-w-cta); }
2866
+ .ss-w-fandash-live-tl-card--red { background: var(--ss-w-away); }
2867
+ .ss-w-fandash-live-tl-subarrows { font-size: 11px; font-weight: 900; line-height: 1; letter-spacing: -1px; color: var(--ss-w-accent); }
2868
+ .ss-w-fandash-live-tl-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ss-w-faint); }
2869
+ .ss-w-fandash-live-tl-more { margin-top: 4px; width: 100%; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 9px; padding: 7px; font: inherit; font-size: 11.5px; font-weight: 700; color: var(--ss-w-accent); cursor: pointer; }
2870
+ .ss-w-fandash-live-tl-more:focus-visible { outline: 2px solid var(--ss-w-accent); outline-offset: 2px; }
2871
+
2872
+ /* live stats: club value / stat label / opponent value, each row backed by a home/away split
2873
+ bar, plus an optional momentum wave (or its text fallback) and a club/opponent colour legend.
2874
+ No side signal reaches this card, so the club is always the home token and the opponent is
2875
+ always the away token -- see LiveStatsCard.tsx. */
2876
+ .ss-w-fandash-live-stat-rows { display: flex; flex-direction: column; gap: 14px; }
2877
+ .ss-w-fandash-live-stat { display: flex; flex-direction: column; gap: 6px; }
2878
+ .ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: 8px; }
2542
2879
  .ss-w-fandash-live-stat-value { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
2543
- .ss-w-fandash-live-stat-row .ss-w-fandash-live-stat-value:last-child { text-align: right; }
2544
- .ss-w-fandash-live-stat-label { font-size: 10.5px; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .03em; }
2880
+ .ss-w-fandash-live-stat-value--home { color: var(--ss-w-home); }
2881
+ .ss-w-fandash-live-stat-value--away { color: var(--ss-w-away); text-align: right; }
2882
+ .ss-w-fandash-live-stat-label { font-size: 10px; font-weight: 700; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .04em; }
2883
+ .ss-w-fandash-live-bar { position: relative; height: 7px; border-radius: 999px; background: var(--ss-w-track); overflow: hidden; display: flex; }
2884
+ .ss-w-fandash-live-bar-home { height: 100%; background: var(--ss-w-home); }
2885
+ .ss-w-fandash-live-bar-away { height: 100%; background: var(--ss-w-away); margin-left: auto; }
2886
+
2545
2887
  .ss-w-fandash-live-momentum { font-size: 11.5px; font-weight: 650; color: var(--ss-w-accent); }
2888
+ .ss-w-fandash-live-mom { margin-top: 15px; padding-top: 13px; border-top: 1px solid var(--ss-w-line); }
2889
+ .ss-w-fandash-live-mom-caption { display: block; margin-bottom: 5px; font-size: 10.5px; font-weight: 600; color: var(--ss-w-faint); }
2890
+ .ss-w-fandash-live-mom-svg { width: 100%; height: 50px; display: block; overflow: visible; }
2891
+ .ss-w-fandash-live-mom-home { fill: var(--ss-w-home); fill-opacity: .85; }
2892
+ .ss-w-fandash-live-mom-away { fill: var(--ss-w-away); fill-opacity: .85; }
2893
+ .ss-w-fandash-live-mom-line { fill: none; stroke: color-mix(in srgb, var(--ss-w-ink) 55%, transparent); stroke-width: 1.5; }
2894
+ .ss-w-fandash-live-mom-base { stroke: var(--ss-w-line); stroke-width: 1; stroke-dasharray: 3 3; }
2895
+ .ss-w-fandash-live-mom-now { fill: var(--ss-w-panel); stroke: var(--ss-w-ink); stroke-width: 1.5; }
2896
+
2897
+ .ss-w-fandash-live-legend { display: flex; gap: 14px; margin-top: 13px; padding-top: 13px; border-top: 1px solid var(--ss-w-line); font-size: 11px; color: var(--ss-w-faint); }
2898
+ .ss-w-fandash-live-legend-item { display: inline-flex; align-items: center; gap: 6px; }
2899
+ .ss-w-fandash-live-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
2900
+ .ss-w-fandash-live-legend-swatch--home { background: var(--ss-w-home); }
2901
+ .ss-w-fandash-live-legend-swatch--away { background: var(--ss-w-away); }
2546
2902
  `;
2547
2903
 
2548
2904
  // src/StatsWidget.tsx
2549
- var import_jsx_runtime43 = require("react/jsx-runtime");
2905
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2550
2906
  function buildClient(props) {
2551
2907
  try {
2552
2908
  if ("client" in props && props.client) {
@@ -2571,11 +2927,11 @@ function injectStyles(root) {
2571
2927
  function StatsWidget(props) {
2572
2928
  const injectedClient = "client" in props ? props.client : void 0;
2573
2929
  const cfg = props.config;
2574
- const built = (0, import_react13.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
2575
- const ui = (0, import_react13.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
2576
- const rootRef = (0, import_react13.useRef)(null);
2930
+ const built = (0, import_react17.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
2931
+ const ui = (0, import_react17.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
2932
+ const rootRef = (0, import_react17.useRef)(null);
2577
2933
  const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
2578
- const slip = (0, import_react13.useMemo)(
2934
+ const slip = (0, import_react17.useMemo)(
2579
2935
  () => ({
2580
2936
  onAddToSlip: props.config?.onAddToSlip,
2581
2937
  conversationId: controller.state.conversationId ?? void 0,
@@ -2588,18 +2944,18 @@ function StatsWidget(props) {
2588
2944
  }),
2589
2945
  [props.config?.onAddToSlip, controller.state.conversationId, built]
2590
2946
  );
2591
- const onDashboardTap = (0, import_react13.useMemo)(
2947
+ const onDashboardTap = (0, import_react17.useMemo)(
2592
2948
  () => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
2593
2949
  [built]
2594
2950
  );
2595
- (0, import_react13.useEffect)(() => {
2951
+ (0, import_react17.useEffect)(() => {
2596
2952
  if (!built || !ui.injectStyles) return;
2597
2953
  const node = rootRef.current?.getRootNode();
2598
2954
  if (node) injectStyles(node);
2599
2955
  }, [built, ui.injectStyles]);
2600
2956
  if (!built) return null;
2601
2957
  const attrs = themeAttrs(ui);
2602
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2958
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2603
2959
  ChatFab,
2604
2960
  {
2605
2961
  label: ui.launcherLabel,