@sensiblestats/widget-react 0.28.0 → 0.29.1

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);
@@ -1755,139 +1819,352 @@ function RestingFormCard({ card }) {
1755
1819
  ] });
1756
1820
  }
1757
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
+
1758
1849
  // src/components/dashboard/LiveScoreCard.tsx
1759
1850
  var import_jsx_runtime34 = require("react/jsx-runtime");
1760
- 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
+ }) {
1761
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);
1762
1881
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-score", children: [
1763
1882
  card.title ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1764
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-postmatch-scoreboard", children: [
1765
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-team", children: data.homeName }),
1766
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-score", children: data.homeScore }),
1767
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-sep", children: "\u2013" }),
1768
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-score", children: data.awayScore }),
1769
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-team ss-w-fandash-postmatch-team--away", children: data.awayName })
1770
- ] }),
1771
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-meta", children: [
1772
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-minute", children: data.minuteLabel }),
1773
- /* @__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}` }),
1774
1889
  data.competition ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-competition", children: data.competition }) : null
1775
- ] })
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
1776
1910
  ] });
1777
1911
  }
1778
1912
 
1779
1913
  // src/components/dashboard/LiveTimelineCard.tsx
1914
+ var import_react12 = require("react");
1780
1915
  var import_jsx_runtime35 = require("react/jsx-runtime");
1781
- 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";
1782
1964
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-timeline", children: [
1783
1965
  card.title ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1784
- /* @__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: [
1785
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-event-minute", children: e.minuteLabel }),
1786
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-event-text", children: e.text })
1787
- ] }, `${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
1788
1977
  ] });
1789
1978
  }
1790
1979
 
1791
- // src/components/dashboard/LiveStatsCard.tsx
1980
+ // src/components/dashboard/MomentumWave.tsx
1981
+ var import_react13 = require("react");
1792
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
+ }
1793
2043
  function LiveStatsCard({ card }) {
1794
2044
  const { data } = card;
1795
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-stats", children: [
1796
- card.title ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1797
- /* @__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: [
1798
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-value", children: r.clubValue }),
1799
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-label", children: r.label }),
1800
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-value", children: r.oppValue })
1801
- ] }, `${r.label}-${i}`)) }),
1802
- 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
1803
2076
  ] });
1804
2077
  }
1805
2078
 
1806
2079
  // src/components/dashboard/ClubFanDashboard.tsx
1807
- var import_jsx_runtime37 = require("react/jsx-runtime");
2080
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1808
2081
  function ClubFanDashboard({ dashboard, onTap, ui }) {
1809
2082
  const orderedCards = [
1810
2083
  ...dashboard.cards.filter((c) => c.cardType === "countdown"),
1811
2084
  ...dashboard.cards.filter((c) => c.cardType !== "countdown")
1812
2085
  ];
1813
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash", children: [
1814
- dashboard.greeting ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
1815
- /* @__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}`)) })
1816
2093
  ] });
1817
2094
  }
1818
- function FanDashboardCard({ card, onTap, ui }) {
2095
+ function FanDashboardCard({ card, onTap, ui, goalEvents }) {
1819
2096
  switch (card.cardType) {
1820
2097
  case "fixture":
1821
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(FixtureCard, { card, onTap });
2098
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FixtureCard, { card, onTap });
1822
2099
  case "countdown":
1823
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CountdownCard, { card });
2100
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(CountdownCard, { card });
1824
2101
  case "injuries":
1825
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(InjuriesCard, { card });
2102
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(InjuriesCard, { card });
1826
2103
  case "reasonToBet":
1827
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReasonToBetCard, { card });
2104
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReasonToBetCard, { card });
1828
2105
  case "whereToWatch":
1829
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(WhereToWatchCard, { card });
2106
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(WhereToWatchCard, { card });
1830
2107
  case "congestedWeek":
1831
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CongestedWeekCard, { card, onTap });
2108
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(CongestedWeekCard, { card, onTap });
1832
2109
  case "transfers":
1833
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TransfersCard, { card });
2110
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TransfersCard, { card });
1834
2111
  case "links":
1835
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LinksCard, { card });
2112
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LinksCard, { card });
1836
2113
  case "postMatchResult":
1837
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchResultCard, { card });
2114
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchResultCard, { card });
1838
2115
  case "postMatchGoals":
1839
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchGoalsCard, { card, onTap });
2116
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchGoalsCard, { card, onTap });
1840
2117
  case "postMatchStats":
1841
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchStatsCard, { card, onTap });
2118
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchStatsCard, { card, onTap });
1842
2119
  case "postMatchTopPerformer":
1843
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchTopPerformerCard, { card, onTap });
2120
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchTopPerformerCard, { card, onTap });
1844
2121
  case "postMatchNext":
1845
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchNextCard, { card });
2122
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PostMatchNextCard, { card });
1846
2123
  case "restingNext":
1847
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingNextCard, { card, onTap });
2124
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingNextCard, { card, onTap });
1848
2125
  case "restingStandings":
1849
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingStandingsCard, { card, onTap });
2126
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingStandingsCard, { card, onTap });
1850
2127
  case "restingSeason":
1851
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingSeasonCard, { card, onTap });
2128
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingSeasonCard, { card, onTap });
1852
2129
  case "restingForm":
1853
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingFormCard, { card });
2130
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(RestingFormCard, { card });
1854
2131
  case "liveScore":
1855
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveScoreCard, { card });
2132
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveScoreCard, { card, goalEvents });
1856
2133
  case "liveTimeline":
1857
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveTimelineCard, { card });
2134
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveTimelineCard, { card, ui });
1858
2135
  case "liveStats":
1859
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveStatsCard, { card });
2136
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LiveStatsCard, { card });
1860
2137
  default:
1861
2138
  return null;
1862
2139
  }
1863
2140
  }
1864
2141
 
1865
2142
  // src/components/ChatMessage.tsx
1866
- var import_jsx_runtime38 = require("react/jsx-runtime");
2143
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1867
2144
  function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
1868
2145
  if (message.role === "user") {
1869
- 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 }) });
1870
2147
  }
1871
2148
  const onFanDashboardTap = (card) => {
1872
2149
  onAct({ message: card.actionLabel, actionRef: card.actionRef, label: card.actionLabel || card.title || "" });
1873
2150
  };
1874
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "ss-w-msg ss-w-bot", "data-msg-id": message.id, children: [
1875
- message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Markdown, { text: message.text }) : null,
1876
- message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1877
- message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
1878
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1879
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
1880
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1881
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1882
- /* @__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 })
1883
2160
  ] });
1884
2161
  }
1885
2162
 
1886
2163
  // src/components/IntentChips.tsx
1887
- var import_jsx_runtime39 = require("react/jsx-runtime");
2164
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1888
2165
  function IntentChips({ chips, onPick }) {
1889
2166
  if (chips.length === 0) return null;
1890
- 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}`)) });
1891
2168
  }
1892
2169
 
1893
2170
  // src/scroll.ts
@@ -1897,7 +2174,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
1897
2174
  }
1898
2175
 
1899
2176
  // src/components/MessageList.tsx
1900
- var import_jsx_runtime40 = require("react/jsx-runtime");
2177
+ var import_jsx_runtime41 = require("react/jsx-runtime");
1901
2178
  function turnAnchor(messages) {
1902
2179
  const last = messages[messages.length - 1];
1903
2180
  if (!last || last.role !== "assistant") return null;
@@ -1906,11 +2183,11 @@ function turnAnchor(messages) {
1906
2183
  }
1907
2184
  function MessageList({ controller, ui, slip, onDashboardTap }) {
1908
2185
  const { state, send } = controller;
1909
- const listRef = (0, import_react10.useRef)(null);
1910
- const endRef = (0, import_react10.useRef)(null);
1911
- const pinnedRef = (0, import_react10.useRef)(true);
1912
- const anchoredTurnRef = (0, import_react10.useRef)(null);
1913
- (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)(() => {
1914
2191
  const anchor = turnAnchor(state.messages);
1915
2192
  const list = listRef.current;
1916
2193
  if (anchor && anchor.turnId !== anchoredTurnRef.current) {
@@ -1925,7 +2202,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1925
2202
  }, [state.messages, state.status, state.isStreaming]);
1926
2203
  const hasUserMessage = state.messages.some((m) => m.role === "user");
1927
2204
  const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
1928
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2205
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
1929
2206
  "div",
1930
2207
  {
1931
2208
  className: "ss-w-list",
@@ -1935,7 +2212,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1935
2212
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
1936
2213
  },
1937
2214
  children: [
1938
- state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2215
+ state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1939
2216
  ChatMessage,
1940
2217
  {
1941
2218
  message: m,
@@ -1950,25 +2227,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1950
2227
  },
1951
2228
  m.id
1952
2229
  )),
1953
- 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: () => {
1954
2231
  } }) : null,
1955
- state.status ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
1956
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
1957
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1958
- /* @__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 })
1959
2236
  ] }) : null,
1960
- !hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1961
- /* @__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" })
1962
2239
  ]
1963
2240
  }
1964
2241
  );
1965
2242
  }
1966
2243
 
1967
2244
  // src/components/ChatInput.tsx
1968
- var import_react11 = require("react");
1969
- var import_jsx_runtime41 = require("react/jsx-runtime");
2245
+ var import_react15 = require("react");
2246
+ var import_jsx_runtime42 = require("react/jsx-runtime");
1970
2247
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
1971
- const [value, setValue] = (0, import_react11.useState)("");
2248
+ const [value, setValue] = (0, import_react15.useState)("");
1972
2249
  const submit = () => {
1973
2250
  const t = value.trim();
1974
2251
  if (!t) return;
@@ -1981,18 +2258,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
1981
2258
  submit();
1982
2259
  }
1983
2260
  };
1984
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ss-w-input", children: [
1985
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1986
- 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, {}) })
1987
2264
  ] });
1988
2265
  }
1989
2266
 
1990
2267
  // src/components/ChatPanel.tsx
1991
- var import_jsx_runtime42 = require("react/jsx-runtime");
2268
+ var import_jsx_runtime43 = require("react/jsx-runtime");
1992
2269
  var FULLSCREEN_QUERY = "(max-width: 639px)";
1993
2270
  function useIsModal() {
1994
- const [modal, setModal] = (0, import_react12.useState)(false);
1995
- (0, import_react12.useEffect)(() => {
2271
+ const [modal, setModal] = (0, import_react16.useState)(false);
2272
+ (0, import_react16.useEffect)(() => {
1996
2273
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1997
2274
  const mql = window.matchMedia(FULLSCREEN_QUERY);
1998
2275
  const sync = () => setModal(mql.matches);
@@ -2005,17 +2282,17 @@ function useIsModal() {
2005
2282
  function ChatPanel({ controller, ui, slip, onDashboardTap }) {
2006
2283
  const { state, close, stop, send } = controller;
2007
2284
  const isModal = useIsModal();
2008
- (0, import_react12.useEffect)(() => {
2285
+ (0, import_react16.useEffect)(() => {
2009
2286
  const onKey = (e) => {
2010
2287
  if (e.key === "Escape") close();
2011
2288
  };
2012
2289
  window.addEventListener("keydown", onKey);
2013
2290
  return () => window.removeEventListener("keydown", onKey);
2014
2291
  }, [close]);
2015
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
2016
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
2017
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
2018
- /* @__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 })
2019
2296
  ] });
2020
2297
  }
2021
2298
 
@@ -2028,6 +2305,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2028
2305
  --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;
2029
2306
  --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;
2030
2307
  --ss-w-pos: #1aa563; --ss-w-neg: #e0524b;
2308
+ --ss-w-home: #3a63a8; --ss-w-away: #c8102e; --ss-w-track: #e9edf1;
2031
2309
  --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;
2032
2310
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
2033
2311
  font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);
@@ -2038,6 +2316,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2038
2316
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
2039
2317
  --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
2040
2318
  --ss-w-pos: #37c891; --ss-w-neg: #f0655d;
2319
+ --ss-w-home: #5b83c8; --ss-w-away: #e8384f; --ss-w-track: #232d3a;
2041
2320
  }
2042
2321
  }
2043
2322
  .ss-w-root[data-ss-w-theme="dark"] {
@@ -2045,11 +2324,13 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2045
2324
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
2046
2325
  --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
2047
2326
  --ss-w-pos: #37c891; --ss-w-neg: #f0655d;
2327
+ --ss-w-home: #5b83c8; --ss-w-away: #e8384f; --ss-w-track: #232d3a;
2048
2328
  }
2049
2329
  .ss-w-root[data-ss-w-theme="light"] {
2050
2330
  --ss-w-accent: #17936a; --ss-w-cta: #f5a623; --ss-w-cta-ink: #1b1300;
2051
2331
  --ss-w-ink: #14181d; --ss-w-faint: #6b7580;
2052
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;
2053
2334
  }
2054
2335
 
2055
2336
  /* launcher */
@@ -2538,34 +2819,90 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2538
2819
  /* resting form: reuses the fixture last-five pill classes (ss-w-fandash-form / -form-pill) */
2539
2820
  .ss-w-fandash-resting-form .ss-w-fandash-form { margin-top: 2px; }
2540
2821
 
2541
- /* live score: reuses the post-match scoreboard classes for the team/score row, with its own
2542
- meta row -- minute called out in the accent color, status and competition beside it */
2543
- .ss-w-fandash-live-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11.5px; color: var(--ss-w-faint); }
2544
- .ss-w-fandash-live-minute { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }
2545
- .ss-w-fandash-live-status { font-weight: 650; color: var(--ss-w-ink); }
2546
- .ss-w-fandash-live-competition { color: var(--ss-w-faint); }
2547
-
2548
- /* live timeline: a plain event list, the club's own events picked out in the accent color --
2549
- same mechanism as the post-match goal list, plus a minute column */
2550
- .ss-w-fandash-live-event-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
2551
- .ss-w-fandash-live-event { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--ss-w-faint); }
2552
- .ss-w-fandash-live-event--club { color: var(--ss-w-ink); font-weight: 650; }
2553
- .ss-w-fandash-live-event-minute { flex: none; font-variant-numeric: tabular-nums; color: var(--ss-w-faint); }
2554
- .ss-w-fandash-live-event--club .ss-w-fandash-live-event-minute { color: var(--ss-w-accent); }
2555
- .ss-w-fandash-live-event-text { min-width: 0; }
2556
-
2557
- /* live stats: club value / stat label / opponent value, same 3-column layout as post-match
2558
- stats, plus an optional momentum line underneath */
2559
- .ss-w-fandash-live-stat-rows { display: flex; flex-direction: column; gap: 5px; }
2560
- .ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
2561
- .ss-w-fandash-live-stat-value { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
2562
- .ss-w-fandash-live-stat-row .ss-w-fandash-live-stat-value:last-child { text-align: right; }
2563
- .ss-w-fandash-live-stat-label { font-size: 10.5px; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .03em; }
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: 10.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: #fff; background: var(--ss-w-neg); border-radius: 6px; padding: 3px 8px; }
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: 13.5px; 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: 10px; }
2836
+ .ss-w-fandash-live-side { display: flex; align-items: center; gap: 10px; min-width: 0; }
2837
+ .ss-w-fandash-live-side--away { flex-direction: row-reverse; text-align: right; }
2838
+ .ss-w-fandash-live-crest { width: 38px; height: 38px; border-radius: 10px; flex: none; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 12.5px; object-fit: cover; box-shadow: 0 2px 6px rgba(0, 0, 0, .18); }
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: 15px; font-weight: 700; line-height: 1.15; color: var(--ss-w-ink); }
2842
+ .ss-w-fandash-live-score-main { display: flex; align-items: baseline; justify-content: center; font-size: 36px; font-weight: 850; 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 5px; }
2844
+ .ss-w-fandash-live-scorers { margin-top: 12px; display: flex; justify-content: flex-end; gap: 7px; align-items: center; font-size: 12.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: 7px; font-size: 13px; 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: 29px; height: 19px; padding: 0 4px; border-radius: 6px; display: grid; place-items: center; font-size: 11px; 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: 17px; height: 17px; 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: 12px; height: 12px; border-radius: 50%; background: radial-gradient(circle at 50% 42%, #fff 0 34%, transparent 36%), var(--ss-w-ink); border: 1px solid var(--ss-w-ink); }
2864
+ .ss-w-fandash-live-tl-card { width: 9px; height: 12px; 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: 15px; }
2877
+ .ss-w-fandash-live-stat { display: flex; flex-direction: column; gap: 7px; }
2878
+ .ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: 8px; }
2879
+ .ss-w-fandash-live-stat-value { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
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: 10.5px; 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: 8px; 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
+
2564
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: 56px; 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); }
2565
2902
  `;
2566
2903
 
2567
2904
  // src/StatsWidget.tsx
2568
- var import_jsx_runtime43 = require("react/jsx-runtime");
2905
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2569
2906
  function buildClient(props) {
2570
2907
  try {
2571
2908
  if ("client" in props && props.client) {
@@ -2590,11 +2927,11 @@ function injectStyles(root) {
2590
2927
  function StatsWidget(props) {
2591
2928
  const injectedClient = "client" in props ? props.client : void 0;
2592
2929
  const cfg = props.config;
2593
- const built = (0, import_react13.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
2594
- const ui = (0, import_react13.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
2595
- 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);
2596
2933
  const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
2597
- const slip = (0, import_react13.useMemo)(
2934
+ const slip = (0, import_react17.useMemo)(
2598
2935
  () => ({
2599
2936
  onAddToSlip: props.config?.onAddToSlip,
2600
2937
  conversationId: controller.state.conversationId ?? void 0,
@@ -2607,18 +2944,18 @@ function StatsWidget(props) {
2607
2944
  }),
2608
2945
  [props.config?.onAddToSlip, controller.state.conversationId, built]
2609
2946
  );
2610
- const onDashboardTap = (0, import_react13.useMemo)(
2947
+ const onDashboardTap = (0, import_react17.useMemo)(
2611
2948
  () => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
2612
2949
  [built]
2613
2950
  );
2614
- (0, import_react13.useEffect)(() => {
2951
+ (0, import_react17.useEffect)(() => {
2615
2952
  if (!built || !ui.injectStyles) return;
2616
2953
  const node = rootRef.current?.getRootNode();
2617
2954
  if (node) injectStyles(node);
2618
2955
  }, [built, ui.injectStyles]);
2619
2956
  if (!built) return null;
2620
2957
  const attrs = themeAttrs(ui);
2621
- 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)(
2622
2959
  ChatFab,
2623
2960
  {
2624
2961
  label: ui.launcherLabel,