@sensiblestats/widget-react 0.21.0 → 0.23.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.js CHANGED
@@ -55,12 +55,6 @@ var STRINGS = {
55
55
  const drift = direction ? `, ${direction}` : "";
56
56
  return `Price movement over the last ${span}: opened at ${opening}, currently ${current}${drift}.`;
57
57
  },
58
- clubMatchPreviewHeading: "Match preview",
59
- clubTeamDnaHeading: "How they play",
60
- clubFormHeading: "Recent form",
61
- clubCountdownLabel: "Countdown to kick-off",
62
- clubKickoff: (ms) => new Intl.DateTimeFormat("en-GB", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
63
- clubKickoffAria: (ms) => `Kick-off ${new Intl.DateTimeFormat("en-GB", { weekday: "long", day: "numeric", month: "long", hour: "2-digit", minute: "2-digit" }).format(new Date(ms))}`,
64
58
  inSlip: "In slip",
65
59
  slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
66
60
  slipDuplicate: "Already in your slip",
@@ -112,12 +106,6 @@ var STRINGS = {
112
106
  const drift = direction ? `, ${direction}` : "";
113
107
  return `Son ${span} i\xE7indeki oran hareketi: ${opening} ile a\xE7\u0131ld\u0131, \u015Fu anda ${current}${drift}.`;
114
108
  },
115
- clubMatchPreviewHeading: "Ma\xE7 \xF6nizlemesi",
116
- clubTeamDnaHeading: "Nas\u0131l oynuyorlar",
117
- clubFormHeading: "Son form",
118
- clubCountdownLabel: "Ba\u015Flama vuru\u015Funa kalan",
119
- clubKickoff: (ms) => new Intl.DateTimeFormat("tr-TR", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
120
- clubKickoffAria: (ms) => `Ba\u015Flama vuru\u015Fu ${new Intl.DateTimeFormat("tr-TR", { weekday: "long", day: "numeric", month: "long", hour: "2-digit", minute: "2-digit" }).format(new Date(ms))}`,
121
109
  inSlip: "Kuponda",
122
110
  slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
123
111
  slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
@@ -181,12 +169,6 @@ function normalizeUi(cfg) {
181
169
  excludedSuspended: strings.excludedSuspended,
182
170
  chartTimeLabel: strings.chartTimeLabel,
183
171
  chartAria: strings.chartAria,
184
- clubMatchPreviewHeading: strings.clubMatchPreviewHeading,
185
- clubTeamDnaHeading: strings.clubTeamDnaHeading,
186
- clubFormHeading: strings.clubFormHeading,
187
- clubCountdownLabel: strings.clubCountdownLabel,
188
- clubKickoff: strings.clubKickoff,
189
- clubKickoffAria: strings.clubKickoffAria,
190
172
  addToSlip: strings.addToSlip,
191
173
  inSlip: strings.inSlip,
192
174
  slipAdded: strings.slipAdded,
@@ -537,8 +519,8 @@ function applyEvent(state, event) {
537
519
  return { ...state, actionsLoading: true, status: event.data.message };
538
520
  case "turn_complete": {
539
521
  const active = activeTurn(state);
540
- const text = active?.text.trim() ?? "";
541
- const popupText = active?.isGreeting && !state.isOpen && text ? text.slice(0, POPUP_MAX) : state.popupText;
522
+ const greetingLine = event.data.clubFanDashboard?.greeting?.trim() || (active?.text.trim() ?? "");
523
+ const popupText = active?.isGreeting && !state.isOpen && greetingLine ? greetingLine.slice(0, POPUP_MAX) : state.popupText;
542
524
  return {
543
525
  ...state,
544
526
  isStreaming: false,
@@ -550,7 +532,7 @@ function applyEvent(state, event) {
550
532
  ...t,
551
533
  done: true,
552
534
  greetingDashboard: event.data.dashboard ?? void 0,
553
- clubDashboard: event.data.clubDashboard ?? void 0
535
+ clubFanDashboard: event.data.clubFanDashboard ?? void 0
554
536
  }))
555
537
  };
556
538
  }
@@ -747,7 +729,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
747
729
  }
748
730
 
749
731
  // src/components/ChatPanel.tsx
750
- import { useEffect as useEffect5, useState as useState9 } from "react";
732
+ import { useEffect as useEffect5, useState as useState8 } from "react";
751
733
 
752
734
  // src/components/ChatHeader.tsx
753
735
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -1306,246 +1288,286 @@ function GreetingDashboard({ dashboard, onTap }) {
1306
1288
  ] });
1307
1289
  }
1308
1290
 
1309
- // src/components/club-dashboard/GreetingHeaderBlock.tsx
1310
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1311
- function GreetingHeaderBlock({ block }) {
1312
- return /* @__PURE__ */ jsxs13("div", { className: "ss-w-cdash-greeting", children: [
1313
- /* @__PURE__ */ jsx17("div", { className: "ss-w-cdash-greeting-title", children: block.title }),
1314
- /* @__PURE__ */ jsx17("div", { className: "ss-w-cdash-greeting-subtitle", children: block.subtitle })
1315
- ] });
1291
+ // src/components/dashboard/FixtureCard.tsx
1292
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1293
+ function resultClass(result) {
1294
+ const upper = result.toUpperCase();
1295
+ if (upper === "W") return "ss-w-fandash-form-pill--win";
1296
+ if (upper === "L") return "ss-w-fandash-form-pill--loss";
1297
+ return "ss-w-fandash-form-pill--draw";
1316
1298
  }
1317
-
1318
- // src/components/club-dashboard/FixtureHeroBlock.tsx
1319
- import { useState as useState6 } from "react";
1320
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1321
- function initials3(name) {
1322
- return name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
1299
+ function FormStrip({ form }) {
1300
+ return /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-form", children: form.map((r, i) => /* @__PURE__ */ jsx17("span", { className: `ss-w-fandash-form-pill ${resultClass(r)}`, children: r }, `${r}-${i}`)) });
1323
1301
  }
1324
- function Crest3({ url, name }) {
1325
- const [broken, setBroken] = useState6(false);
1326
- if (!url || broken) {
1327
- return /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-crest ss-w-cdash-crest--fallback", "aria-hidden": "true", children: initials3(name) });
1328
- }
1329
- return /* @__PURE__ */ jsx18("img", { className: "ss-w-cdash-crest", src: url, alt: "", "aria-hidden": "true", onError: () => setBroken(true) });
1302
+ function FormRow({ name, form }) {
1303
+ return /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-formrow", children: [
1304
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-formrow-name", children: name }),
1305
+ /* @__PURE__ */ jsx17(FormStrip, { form })
1306
+ ] });
1330
1307
  }
1331
- function FixtureHeroBlock({
1332
- block,
1333
- ui,
1334
- onAction
1335
- }) {
1336
- return /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture", children: [
1337
- /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-tie", children: [
1338
- /* @__PURE__ */ jsx18(Crest3, { url: block.homeCrestUrl, name: block.homeName }),
1339
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-team", children: block.homeName }),
1340
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-vs", children: "v" }),
1341
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-team", children: block.awayName }),
1342
- /* @__PURE__ */ jsx18(Crest3, { url: block.awayCrestUrl, name: block.awayName })
1308
+ function FixtureCard({ card, onTap }) {
1309
+ const { data } = card;
1310
+ const h2h = data.h2H;
1311
+ return /* @__PURE__ */ jsxs13(Fragment5, { children: [
1312
+ /* @__PURE__ */ jsxs13("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-fixture", onClick: () => onTap(card), children: [
1313
+ /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-eyebrow", children: [
1314
+ card.title,
1315
+ " \xB7 ",
1316
+ data.competition
1317
+ ] }),
1318
+ /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-teams", children: [
1319
+ /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-fixture-team", children: [
1320
+ /* @__PURE__ */ jsx17(Crest2, { url: data.homeCrestUrl ?? void 0, name: data.homeName, kind: "team" }),
1321
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-name", children: data.homeName })
1322
+ ] }),
1323
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-vs", children: "v" }),
1324
+ /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-fixture-team ss-w-fandash-fixture-team--away", children: [
1325
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-name", children: data.awayName }),
1326
+ /* @__PURE__ */ jsx17(Crest2, { url: data.awayCrestUrl ?? void 0, name: data.awayName, kind: "team" })
1327
+ ] })
1328
+ ] }),
1329
+ /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-meta", children: [
1330
+ /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-metarow", children: [
1331
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metakey", children: data.dateLabel }),
1332
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metaval", children: data.kickoffLabel })
1333
+ ] }),
1334
+ data.venue ? /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-metarow", children: [
1335
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metakey", children: data.venueLabel }),
1336
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fixture-metaval", children: data.venue })
1337
+ ] }) : null
1338
+ ] }),
1339
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
1343
1340
  ] }),
1344
- /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture-meta", children: [
1345
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-kickoff", children: ui.clubKickoff(block.kickoffUnix * 1e3) }),
1346
- block.venue ? /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-venue", children: block.venue }) : null,
1347
- block.competition ? /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-comp", children: block.competition }) : null
1341
+ /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-card ss-w-fandash-lastfive", children: [
1342
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-eyebrow", children: data.formLabel }),
1343
+ /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-fixture-form", children: [
1344
+ /* @__PURE__ */ jsx17(FormRow, { name: data.homeName, form: data.homeForm }),
1345
+ /* @__PURE__ */ jsx17(FormRow, { name: data.awayName, form: data.awayForm })
1346
+ ] })
1348
1347
  ] }),
1349
- /* @__PURE__ */ jsx18("button", { type: "button", className: "ss-w-cdash-cta", onClick: () => onAction(block.ctaLabel, { actionRef: block.ctaActionRef }), children: block.ctaLabel })
1348
+ h2h || data.fact ? /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-card ss-w-fandash-h2h", children: [
1349
+ h2h ? /* @__PURE__ */ jsxs13(Fragment5, { children: [
1350
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-eyebrow", children: h2h.label }),
1351
+ /* @__PURE__ */ jsxs13("div", { className: "ss-w-fandash-split", children: [
1352
+ /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-split-cell", children: [
1353
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-value", children: h2h.wins }),
1354
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-label", children: h2h.homeWinsLabel })
1355
+ ] }),
1356
+ /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-split-cell", children: [
1357
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-value", children: h2h.draws }),
1358
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-label", children: h2h.drawsLabel })
1359
+ ] }),
1360
+ /* @__PURE__ */ jsxs13("span", { className: "ss-w-fandash-split-cell", children: [
1361
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-value", children: h2h.losses }),
1362
+ /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-split-label", children: h2h.awayWinsLabel })
1363
+ ] })
1364
+ ] })
1365
+ ] }) : null,
1366
+ data.fact ? /* @__PURE__ */ jsx17("span", { className: "ss-w-fandash-fact", children: data.fact }) : null
1367
+ ] }) : null
1350
1368
  ] });
1351
1369
  }
1352
1370
 
1353
- // src/components/club-dashboard/ResultPills.tsx
1354
- import { Fragment as Fragment5, jsx as jsx19 } from "react/jsx-runtime";
1355
- function ResultPills({ results }) {
1356
- return /* @__PURE__ */ jsx19(Fragment5, { children: results.map((r, i) => /* @__PURE__ */ jsx19("span", { className: `ss-w-cdash-pill ss-w-cdash-pill--${r.toLowerCase()}`, children: r }, i)) });
1371
+ // src/components/dashboard/CountdownCard.tsx
1372
+ import { Fragment as Fragment6, useEffect as useEffect3, useState as useState6 } from "react";
1373
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1374
+ var DAY_MS = 864e5;
1375
+ function remainingSegments(kickoffUnix, nowMs, withSeconds) {
1376
+ const totalSeconds = Math.floor((kickoffUnix * 1e3 - nowMs) / 1e3);
1377
+ if (totalSeconds <= 0) return null;
1378
+ const days = Math.floor(totalSeconds / 86400);
1379
+ const hours = Math.floor(totalSeconds % 86400 / 3600);
1380
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
1381
+ const seconds = totalSeconds % 60;
1382
+ const pad = (n) => String(n).padStart(2, "0");
1383
+ if (days > 0) return [{ value: pad(days), unit: "d" }, { value: pad(hours), unit: "h" }];
1384
+ const segs = [{ value: pad(hours), unit: "h" }, { value: pad(minutes), unit: "m" }];
1385
+ if (withSeconds) segs.push({ value: pad(seconds), unit: "s" });
1386
+ return segs;
1387
+ }
1388
+ function CountdownCard({ card }) {
1389
+ const { data } = card;
1390
+ const unitLabel = { d: data.daysUnit, h: data.hoursUnit, m: data.minutesUnit, s: data.secondsUnit };
1391
+ const [nowMs, setNowMs] = useState6(() => Date.now());
1392
+ const [reduced] = useState6(
1393
+ () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
1394
+ );
1395
+ const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
1396
+ useEffect3(() => {
1397
+ if (reduced) return;
1398
+ const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
1399
+ return () => window.clearInterval(id);
1400
+ }, [reduced, withinDay]);
1401
+ const segments = remainingSegments(data.kickoffUnix, nowMs, withinDay && !reduced);
1402
+ return /* @__PURE__ */ jsx18("div", { className: "ss-w-fandash-card ss-w-fandash-countdown", children: /* @__PURE__ */ jsxs14("div", { className: "ss-w-fandash-countdown-top", children: [
1403
+ /* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-eyebrow", children: card.title ?? "Kick-off in" }),
1404
+ segments ? /* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ jsxs14(Fragment6, { children: [
1405
+ i > 0 ? /* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-colon", children: ":" }) : null,
1406
+ /* @__PURE__ */ jsxs14("span", { className: "ss-w-fandash-countdown-seg", children: [
1407
+ /* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-value", children: s.value }),
1408
+ /* @__PURE__ */ jsx18("span", { className: "ss-w-fandash-countdown-unit", children: unitLabel[s.unit] })
1409
+ ] })
1410
+ ] }, s.unit)) }) : null
1411
+ ] }) });
1357
1412
  }
1358
1413
 
1359
- // src/components/club-dashboard/FormGuideBlock.tsx
1360
- import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
1361
- function FormGuideBlock({ block }) {
1362
- if (block.rows.length === 0) return null;
1363
- return /* @__PURE__ */ jsx20("div", { className: "ss-w-cdash-form", children: block.rows.map((row) => /* @__PURE__ */ jsxs15("div", { className: "ss-w-cdash-form-row", children: [
1364
- /* @__PURE__ */ jsxs15("span", { className: "ss-w-cdash-form-name", children: [
1365
- row.name,
1366
- row.position != null ? ` #${row.position}` : ""
1367
- ] }),
1368
- /* @__PURE__ */ jsx20("span", { className: "ss-w-cdash-form-pills", children: /* @__PURE__ */ jsx20(ResultPills, { results: row.last5 }) })
1369
- ] }, row.name)) });
1414
+ // src/components/dashboard/InjuriesCard.tsx
1415
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
1416
+ function ColHead({ name, crestUrl }) {
1417
+ return /* @__PURE__ */ jsxs15("span", { className: "ss-w-fandash-injuries-col-head", children: [
1418
+ /* @__PURE__ */ jsx19(Crest2, { url: crestUrl ?? void 0, name, kind: "team" }),
1419
+ /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injuries-col-head-name", children: name })
1420
+ ] });
1370
1421
  }
1371
-
1372
- // src/components/club-dashboard/MatchPreviewBlock.tsx
1373
- import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
1374
- function MatchPreviewBlock({
1375
- block,
1376
- ui,
1377
- onAction
1378
- }) {
1379
- if (block.facts.length === 0) return null;
1380
- const heading = ui.clubMatchPreviewHeading;
1381
- return /* @__PURE__ */ jsxs16("div", { className: "ss-w-cdash-lines", children: [
1382
- /* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-lines-heading", children: heading }),
1383
- block.facts.map(
1384
- (f, i) => f.actionRef ? /* @__PURE__ */ jsxs16(
1385
- "button",
1386
- {
1387
- type: "button",
1388
- className: "ss-w-cdash-fact",
1389
- onClick: () => onAction(f.label ?? f.sentence, { actionRef: f.actionRef }),
1390
- children: [
1391
- f.sentence,
1392
- f.label ? /* @__PURE__ */ jsxs16("span", { className: "ss-w-cdash-fact-label", children: [
1393
- " ",
1394
- f.label
1395
- ] }) : null
1396
- ]
1397
- },
1398
- i
1399
- ) : /* @__PURE__ */ jsx21("p", { children: f.sentence }, i)
1400
- )
1422
+ function InjuryList({ rows, emptyLabel }) {
1423
+ if (rows.length === 0) return /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injuries-empty", children: emptyLabel });
1424
+ return /* @__PURE__ */ jsx19("ul", { className: "ss-w-fandash-injury-list", children: rows.map((r, i) => /* @__PURE__ */ jsxs15("li", { className: "ss-w-fandash-injury-row", children: [
1425
+ /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-name", children: r.playerName }),
1426
+ /* @__PURE__ */ jsxs15("span", { className: "ss-w-fandash-injury-meta", children: [
1427
+ /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-status", children: r.status }),
1428
+ r.injuryType ? /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-type", children: r.injuryType }) : null,
1429
+ r.expectedReturnLabel ? /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-injury-return", children: r.expectedReturnLabel }) : null
1430
+ ] })
1431
+ ] }, `${r.playerName}-${i}`)) });
1432
+ }
1433
+ function InjuriesCard({ card }) {
1434
+ const { data } = card;
1435
+ return /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-card ss-w-fandash-injuries", children: [
1436
+ card.title ? /* @__PURE__ */ jsx19("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1437
+ /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-cols", children: [
1438
+ /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-col", children: [
1439
+ /* @__PURE__ */ jsx19(ColHead, { name: data.oursName, crestUrl: data.oursCrestUrl }),
1440
+ /* @__PURE__ */ jsx19(InjuryList, { rows: data.ours, emptyLabel: data.emptyLabel })
1441
+ ] }),
1442
+ /* @__PURE__ */ jsx19("div", { className: "ss-w-fandash-injuries-divider" }),
1443
+ /* @__PURE__ */ jsxs15("div", { className: "ss-w-fandash-injuries-col", children: [
1444
+ /* @__PURE__ */ jsx19(ColHead, { name: data.theirsName, crestUrl: data.theirsCrestUrl }),
1445
+ /* @__PURE__ */ jsx19(InjuryList, { rows: data.theirs, emptyLabel: data.emptyLabel })
1446
+ ] })
1447
+ ] })
1401
1448
  ] });
1402
1449
  }
1403
1450
 
1404
- // src/components/club-dashboard/MatchPreviewCardBlock.tsx
1405
- import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
1406
- function position(p) {
1407
- return p != null ? `#${p}` : "\u2014";
1408
- }
1409
- function MatchPreviewCardBlock({ block }) {
1410
- return /* @__PURE__ */ jsxs17("div", { className: "ss-w-cdash-preview", children: [
1411
- /* @__PURE__ */ jsxs17("div", { className: "ss-w-cdash-preview-header", children: [
1412
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-team", children: block.homeName }),
1413
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-vs", children: "v" }),
1414
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-team", children: block.awayName })
1415
- ] }),
1416
- /* @__PURE__ */ jsxs17("div", { className: "ss-w-cdash-preview-row", children: [
1417
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-preview-value", children: position(block.homePosition) }),
1418
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-preview-label", children: block.tableLabel }),
1419
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-preview-value", children: position(block.awayPosition) })
1420
- ] }),
1421
- /* @__PURE__ */ jsxs17("div", { className: "ss-w-cdash-preview-row", children: [
1422
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-preview-pills", children: /* @__PURE__ */ jsx22(ResultPills, { results: block.homeLast5 }) }),
1423
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-preview-label", children: block.last5Label }),
1424
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-preview-pills", children: /* @__PURE__ */ jsx22(ResultPills, { results: block.awayLast5 }) })
1425
- ] }),
1426
- block.edgeTeamName ? /* @__PURE__ */ jsxs17("div", { className: "ss-w-cdash-preview-edge", children: [
1427
- /* @__PURE__ */ jsx22("span", { "aria-hidden": "true", children: "\u25B8" }),
1428
- /* @__PURE__ */ jsx22("strong", { children: block.edgeTeamName }),
1429
- block.edgeReason ? /* @__PURE__ */ jsxs17("span", { children: [
1430
- " \u2014 ",
1431
- block.edgeReason
1432
- ] }) : null
1433
- ] }) : null,
1434
- block.h2HLines.length > 0 ? /* @__PURE__ */ jsx22("div", { className: "ss-w-cdash-preview-h2h", children: block.h2HLines.map((line, i) => /* @__PURE__ */ jsx22("p", { children: line }, i)) }) : null
1451
+ // src/components/dashboard/ReasonToBetCard.tsx
1452
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
1453
+ function ReasonToBetCard({ card }) {
1454
+ return /* @__PURE__ */ jsxs16("div", { className: "ss-w-fandash-card ss-w-fandash-reasons", children: [
1455
+ card.title ? /* @__PURE__ */ jsx20("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1456
+ /* @__PURE__ */ jsx20("ul", { className: "ss-w-fandash-reason-list", children: card.data.angles.map((angle, i) => /* @__PURE__ */ jsx20("li", { className: "ss-w-fandash-reason-item", children: angle }, `${angle}-${i}`)) })
1435
1457
  ] });
1436
1458
  }
1437
1459
 
1438
- // src/components/club-dashboard/TeamDnaBlock.tsx
1439
- import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
1440
- function TeamDnaBlock({
1441
- block,
1442
- ui,
1443
- onAction
1444
- }) {
1445
- if (block.facts.length === 0) return null;
1446
- const heading = ui.clubTeamDnaHeading;
1447
- return /* @__PURE__ */ jsxs18("div", { className: "ss-w-cdash-lines", children: [
1448
- /* @__PURE__ */ jsx23("span", { className: "ss-w-cdash-lines-heading", children: heading }),
1449
- block.facts.map(
1450
- (f, i) => f.actionRef ? /* @__PURE__ */ jsxs18(
1451
- "button",
1452
- {
1453
- type: "button",
1454
- className: "ss-w-cdash-fact",
1455
- onClick: () => onAction(f.label ?? f.sentence, { actionRef: f.actionRef }),
1456
- children: [
1457
- f.sentence,
1458
- f.label ? /* @__PURE__ */ jsxs18("span", { className: "ss-w-cdash-fact-label", children: [
1459
- " ",
1460
- f.label
1461
- ] }) : null
1462
- ]
1463
- },
1464
- i
1465
- ) : /* @__PURE__ */ jsx23("p", { children: f.sentence }, i)
1466
- )
1460
+ // src/components/dashboard/WhereToWatchCard.tsx
1461
+ import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
1462
+ function WhereToWatchCard({ card }) {
1463
+ return /* @__PURE__ */ jsxs17("div", { className: "ss-w-fandash-card ss-w-fandash-watch", children: [
1464
+ card.title ? /* @__PURE__ */ jsx21("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1465
+ /* @__PURE__ */ jsx21("span", { className: "ss-w-fandash-stations", children: card.data.stations.map((station, i) => /* @__PURE__ */ jsx21("span", { className: "ss-w-fandash-station-chip", children: station }, `${station}-${i}`)) })
1467
1466
  ] });
1468
1467
  }
1469
1468
 
1470
- // src/components/club-dashboard/CountdownBlock.tsx
1471
- import { useEffect as useEffect3, useState as useState7 } from "react";
1472
- import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
1473
- var DAY_MS = 24 * 3600 * 1e3;
1474
- function pad2(n) {
1475
- return String(n).padStart(2, "0");
1469
+ // src/components/dashboard/CongestedWeekCard.tsx
1470
+ import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
1471
+ function CongestedWeekCard({ card, onTap }) {
1472
+ return /* @__PURE__ */ jsxs18("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-congested", onClick: () => onTap(card), children: [
1473
+ card.title ? /* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1474
+ /* @__PURE__ */ jsx22("div", { className: "ss-w-fandash-congested-grid", children: card.data.entries.map((entry, i) => /* @__PURE__ */ jsxs18("span", { className: "ss-w-fandash-congested-cell", children: [
1475
+ /* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-day", children: entry.dayLabel }),
1476
+ /* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-opponent", children: entry.opponent }),
1477
+ /* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-comp", children: entry.competition }),
1478
+ /* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-congested-time", children: entry.timeLabel })
1479
+ ] }, `${entry.opponent}-${i}`)) }),
1480
+ /* @__PURE__ */ jsx22("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
1481
+ ] });
1476
1482
  }
1477
- function splitRemaining(remainingMs) {
1478
- const totalSeconds = Math.max(0, Math.floor(remainingMs / 1e3));
1479
- const h = Math.floor(totalSeconds / 3600);
1480
- const m = Math.floor(totalSeconds % 3600 / 60);
1481
- const s = totalSeconds % 60;
1482
- return { h: pad2(h), m: pad2(m), s: pad2(s) };
1483
+
1484
+ // src/components/dashboard/TransfersCard.tsx
1485
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
1486
+ function TransferList({ rows }) {
1487
+ if (rows.length === 0) return /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfers-empty", children: "None" });
1488
+ return /* @__PURE__ */ jsx23("ul", { className: "ss-w-fandash-transfer-list", children: rows.map((r, i) => /* @__PURE__ */ jsxs19("li", { className: "ss-w-fandash-transfer-row", children: [
1489
+ /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfer-name", children: r.playerName }),
1490
+ /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfer-club", children: r.otherClub }),
1491
+ r.type ? /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfer-type", children: r.type }) : null
1492
+ ] }, `${r.playerName}-${i}`)) });
1493
+ }
1494
+ function TransfersCard({ card }) {
1495
+ const { data } = card;
1496
+ return /* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-card ss-w-fandash-transfers", children: [
1497
+ card.title ? /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1498
+ /* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-transfers-cols", children: [
1499
+ /* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-transfers-col", children: [
1500
+ /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfers-col-head", children: "In" }),
1501
+ /* @__PURE__ */ jsx23(TransferList, { rows: data.in })
1502
+ ] }),
1503
+ /* @__PURE__ */ jsxs19("div", { className: "ss-w-fandash-transfers-col", children: [
1504
+ /* @__PURE__ */ jsx23("span", { className: "ss-w-fandash-transfers-col-head", children: "Out" }),
1505
+ /* @__PURE__ */ jsx23(TransferList, { rows: data.out })
1506
+ ] })
1507
+ ] })
1508
+ ] });
1483
1509
  }
1484
- function CountdownBlock({ block, ui }) {
1485
- const [remainingMs, setRemainingMs] = useState7(() => block.kickoffUnix * 1e3 - Date.now());
1486
- useEffect3(() => {
1487
- setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
1488
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
1489
- const id = setInterval(() => {
1490
- setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
1491
- }, 1e3);
1492
- return () => clearInterval(id);
1493
- }, [block.kickoffUnix]);
1494
- if (remainingMs <= 0) return null;
1495
- if (remainingMs > DAY_MS) return null;
1496
- const { h, m, s } = splitRemaining(remainingMs);
1497
- return /* @__PURE__ */ jsxs19("div", { className: "ss-w-cdash-cd", "aria-label": ui.clubKickoffAria(block.kickoffUnix * 1e3), children: [
1498
- /* @__PURE__ */ jsxs19("div", { className: "ss-w-cdash-cd-clock", "aria-hidden": "true", children: [
1499
- /* @__PURE__ */ jsx24("span", { className: "ss-w-cdash-cd-h", children: h }),
1500
- "h",
1501
- " ",
1502
- /* @__PURE__ */ jsx24("span", { className: "ss-w-cdash-cd-m", children: m }),
1503
- "m",
1504
- " ",
1505
- /* @__PURE__ */ jsx24("span", { className: "ss-w-cdash-cd-s", children: s }),
1506
- "s"
1507
- ] }),
1508
- /* @__PURE__ */ jsx24("div", { className: "ss-w-cdash-cd-label", children: ui.clubCountdownLabel })
1510
+
1511
+ // src/components/dashboard/LinksCard.tsx
1512
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1513
+ function LinksCard({ card }) {
1514
+ return /* @__PURE__ */ jsxs20("div", { className: "ss-w-fandash-card ss-w-fandash-links", children: [
1515
+ card.title ? /* @__PURE__ */ jsx24("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1516
+ /* @__PURE__ */ jsx24("div", { className: "ss-w-fandash-link-grid", children: card.data.links.map((link, i) => /* @__PURE__ */ jsxs20("a", { className: "ss-w-fandash-link-tile", href: link.url, target: "_blank", rel: "noopener noreferrer", children: [
1517
+ /* @__PURE__ */ jsx24("span", { className: "ss-w-fandash-link-label", children: link.label }),
1518
+ /* @__PURE__ */ jsx24("span", { className: "ss-w-fandash-link-chevron", children: /* @__PURE__ */ jsx24(ChevronIcon, {}) })
1519
+ ] }, `${link.url}-${i}`)) })
1509
1520
  ] });
1510
1521
  }
1511
1522
 
1512
- // src/components/club-dashboard/ClubDashboard.tsx
1513
- import { jsx as jsx25 } from "react/jsx-runtime";
1514
- var CLUB_BLOCK_REGISTRY = {
1515
- greetingHeader: ({ block }) => block.type === "greetingHeader" ? /* @__PURE__ */ jsx25(GreetingHeaderBlock, { block }) : null,
1516
- fixtureHero: ({ block, ui, onAction }) => block.type === "fixtureHero" ? /* @__PURE__ */ jsx25(FixtureHeroBlock, { block, ui, onAction }) : null,
1517
- formGuide: ({ block, ui }) => block.type === "formGuide" ? /* @__PURE__ */ jsx25(FormGuideBlock, { block, ui }) : null,
1518
- matchPreview: ({ block, ui, onAction }) => block.type === "matchPreview" ? /* @__PURE__ */ jsx25(MatchPreviewBlock, { block, ui, onAction }) : null,
1519
- matchPreviewCard: ({ block, ui }) => block.type === "matchPreviewCard" ? /* @__PURE__ */ jsx25(MatchPreviewCardBlock, { block, ui }) : null,
1520
- teamDna: ({ block, ui, onAction }) => block.type === "teamDna" ? /* @__PURE__ */ jsx25(TeamDnaBlock, { block, ui, onAction }) : null,
1521
- countdown: ({ block, ui }) => block.type === "countdown" ? /* @__PURE__ */ jsx25(CountdownBlock, { block, ui }) : null
1522
- };
1523
- function ClubDashboard({
1524
- dashboard,
1525
- ui,
1526
- onAction
1527
- }) {
1528
- if (!dashboard.blocks.length) return null;
1529
- return /* @__PURE__ */ jsx25("div", { className: "ss-w-cdash", children: dashboard.blocks.map((block, i) => {
1530
- const Comp = Object.prototype.hasOwnProperty.call(CLUB_BLOCK_REGISTRY, block.type) ? CLUB_BLOCK_REGISTRY[block.type] : void 0;
1531
- if (!Comp) {
1532
- console.warn(`[club-dashboard] no renderer for block type "${block.type}"`);
1523
+ // src/components/dashboard/ClubFanDashboard.tsx
1524
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
1525
+ function ClubFanDashboard({ dashboard, onTap, ui }) {
1526
+ const orderedCards = [
1527
+ ...dashboard.cards.filter((c) => c.cardType === "countdown"),
1528
+ ...dashboard.cards.filter((c) => c.cardType !== "countdown")
1529
+ ];
1530
+ return /* @__PURE__ */ jsxs21("div", { className: "ss-w-fandash", children: [
1531
+ dashboard.greeting ? /* @__PURE__ */ jsx25("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
1532
+ /* @__PURE__ */ jsx25("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ jsx25(FanDashboardCard, { card, onTap, ui }, `${card.cardType}-${i}`)) })
1533
+ ] });
1534
+ }
1535
+ function FanDashboardCard({ card, onTap, ui }) {
1536
+ switch (card.cardType) {
1537
+ case "fixture":
1538
+ return /* @__PURE__ */ jsx25(FixtureCard, { card, onTap });
1539
+ case "countdown":
1540
+ return /* @__PURE__ */ jsx25(CountdownCard, { card });
1541
+ case "injuries":
1542
+ return /* @__PURE__ */ jsx25(InjuriesCard, { card });
1543
+ case "reasonToBet":
1544
+ return /* @__PURE__ */ jsx25(ReasonToBetCard, { card });
1545
+ case "whereToWatch":
1546
+ return /* @__PURE__ */ jsx25(WhereToWatchCard, { card });
1547
+ case "congestedWeek":
1548
+ return /* @__PURE__ */ jsx25(CongestedWeekCard, { card, onTap });
1549
+ case "transfers":
1550
+ return /* @__PURE__ */ jsx25(TransfersCard, { card });
1551
+ case "links":
1552
+ return /* @__PURE__ */ jsx25(LinksCard, { card });
1553
+ default:
1533
1554
  return null;
1534
- }
1535
- return /* @__PURE__ */ jsx25("div", { className: "ss-w-cdash-block", children: /* @__PURE__ */ jsx25(Comp, { block, ui, onAction }) }, i);
1536
- }) });
1555
+ }
1537
1556
  }
1538
1557
 
1539
1558
  // src/components/ChatMessage.tsx
1540
- import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
1541
- function ChatMessage({ message, disabled, onAct, onTap, onClubAction, ui, slip }) {
1559
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
1560
+ function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
1542
1561
  if (message.role === "user") {
1543
1562
  return /* @__PURE__ */ jsx26("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx26("div", { className: "ss-w-bubble", children: message.text }) });
1544
1563
  }
1545
- return /* @__PURE__ */ jsxs20("div", { className: "ss-w-msg ss-w-bot", children: [
1546
- message.text && !message.greetingDashboard && !message.clubDashboard ? /* @__PURE__ */ jsx26(Markdown, { text: message.text }) : null,
1547
- message.greetingDashboard ? /* @__PURE__ */ jsx26(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1548
- message.clubDashboard && ui ? /* @__PURE__ */ jsx26(ClubDashboard, { dashboard: message.clubDashboard, ui, onAction: onClubAction }) : null,
1564
+ const onFanDashboardTap = (card) => {
1565
+ onAct({ message: card.actionValue, label: card.actionLabel || card.title || "" });
1566
+ };
1567
+ return /* @__PURE__ */ jsxs22("div", { className: "ss-w-msg ss-w-bot", children: [
1568
+ message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx26(Markdown, { text: message.text }) : null,
1569
+ message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx26(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1570
+ message.clubFanDashboard ? /* @__PURE__ */ jsx26(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
1549
1571
  /* @__PURE__ */ jsx26(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1550
1572
  /* @__PURE__ */ jsx26(BettingInsightList, { insights: message.insights, ui, slip }),
1551
1573
  /* @__PURE__ */ jsx26(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
@@ -1568,7 +1590,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
1568
1590
  }
1569
1591
 
1570
1592
  // src/components/MessageList.tsx
1571
- import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
1593
+ import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
1572
1594
  function MessageList({ controller, ui, slip, onDashboardTap }) {
1573
1595
  const { state, send } = controller;
1574
1596
  const listRef = useRef2(null);
@@ -1578,8 +1600,8 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1578
1600
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
1579
1601
  }, [state.messages, state.status]);
1580
1602
  const hasUserMessage = state.messages.some((m) => m.role === "user");
1581
- const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
1582
- return /* @__PURE__ */ jsxs21(
1603
+ const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
1604
+ return /* @__PURE__ */ jsxs23(
1583
1605
  "div",
1584
1606
  {
1585
1607
  className: "ss-w-list",
@@ -1599,7 +1621,6 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1599
1621
  send(tile.actionValue, { displayText: tile.title });
1600
1622
  if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
1601
1623
  },
1602
- onClubAction: (label, opts) => send(label, { actionRef: opts.actionRef }),
1603
1624
  ui,
1604
1625
  slip
1605
1626
  },
@@ -1608,7 +1629,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1608
1629
  state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx28(ActionButtons, { actions: [], loading: true, onAct: () => {
1609
1630
  } }) : null,
1610
1631
  state.status ? /* @__PURE__ */ jsx28("div", { className: "ss-w-status", children: state.status }) : null,
1611
- state.error ? /* @__PURE__ */ jsxs21("div", { className: "ss-w-error", role: "alert", children: [
1632
+ state.error ? /* @__PURE__ */ jsxs23("div", { className: "ss-w-error", role: "alert", children: [
1612
1633
  /* @__PURE__ */ jsx28("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1613
1634
  /* @__PURE__ */ jsx28("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1614
1635
  ] }) : null,
@@ -1620,10 +1641,10 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1620
1641
  }
1621
1642
 
1622
1643
  // src/components/ChatInput.tsx
1623
- import { useState as useState8 } from "react";
1624
- import { jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
1644
+ import { useState as useState7 } from "react";
1645
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
1625
1646
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
1626
- const [value, setValue] = useState8("");
1647
+ const [value, setValue] = useState7("");
1627
1648
  const submit = () => {
1628
1649
  const t = value.trim();
1629
1650
  if (!t) return;
@@ -1636,17 +1657,17 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
1636
1657
  submit();
1637
1658
  }
1638
1659
  };
1639
- return /* @__PURE__ */ jsxs22("div", { className: "ss-w-input", children: [
1660
+ return /* @__PURE__ */ jsxs24("div", { className: "ss-w-input", children: [
1640
1661
  /* @__PURE__ */ jsx29("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1641
1662
  streaming ? /* @__PURE__ */ jsx29("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx29("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx29("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx29(SendIcon, {}) })
1642
1663
  ] });
1643
1664
  }
1644
1665
 
1645
1666
  // src/components/ChatPanel.tsx
1646
- import { jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
1667
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
1647
1668
  var FULLSCREEN_QUERY = "(max-width: 639px)";
1648
1669
  function useIsModal() {
1649
- const [modal, setModal] = useState9(false);
1670
+ const [modal, setModal] = useState8(false);
1650
1671
  useEffect5(() => {
1651
1672
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1652
1673
  const mql = window.matchMedia(FULLSCREEN_QUERY);
@@ -1667,7 +1688,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap }) {
1667
1688
  window.addEventListener("keydown", onKey);
1668
1689
  return () => window.removeEventListener("keydown", onKey);
1669
1690
  }, [close]);
1670
- return /* @__PURE__ */ jsxs23("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1691
+ return /* @__PURE__ */ jsxs25("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1671
1692
  /* @__PURE__ */ jsx30(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1672
1693
  /* @__PURE__ */ jsx30(MessageList, { controller, ui, slip, onDashboardTap }),
1673
1694
  /* @__PURE__ */ jsx30(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
@@ -2024,61 +2045,117 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2024
2045
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
2025
2046
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
2026
2047
 
2027
- /* club match-day dashboard */
2028
- .ss-w-cdash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
2029
- .ss-w-cdash-block { display: flex; flex-direction: column; }
2030
-
2031
- .ss-w-cdash-greeting { display: flex; flex-direction: column; gap: 2px; }
2032
- .ss-w-cdash-greeting-title { font-weight: 700; font-size: 14px; color: var(--ss-w-ink); }
2033
- .ss-w-cdash-greeting-subtitle { font-size: 11.5px; color: var(--ss-w-faint); }
2034
-
2035
- .ss-w-cdash-fixture { display: flex; flex-direction: column; align-items: center; gap: 8px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 12px; text-align: center; }
2036
- .ss-w-cdash-tie { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; }
2037
- .ss-w-cdash-team { font-weight: 650; font-size: 13px; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
2038
- .ss-w-cdash-vs { font-size: 11px; color: var(--ss-w-faint); font-weight: 600; }
2039
- .ss-w-cdash-crest { flex: none; width: 28px; height: 28px; border-radius: 50%; object-fit: cover; background: var(--ss-w-bubble); }
2040
- .ss-w-cdash-crest--fallback { display: grid; place-items: center; font-size: 10.5px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 16%, var(--ss-w-panel)); }
2041
- .ss-w-cdash-fixture-meta { display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap; font-size: 11px; color: var(--ss-w-faint); }
2042
- .ss-w-cdash-kickoff { font-weight: 700; color: var(--ss-w-ink); }
2043
- .ss-w-cdash-cta { font: inherit; font-weight: 700; font-size: 12px; color: var(--ss-w-cta-ink); background: var(--ss-w-cta); border: none; border-radius: 999px; padding: 8px 18px; cursor: pointer; }
2044
-
2045
- .ss-w-cdash-form { display: flex; flex-direction: column; gap: 6px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 10px 12px; }
2046
- .ss-w-cdash-form-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
2047
- .ss-w-cdash-form-name { font-size: 12px; font-weight: 600; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
2048
- .ss-w-cdash-form-pills { flex: none; display: flex; align-items: center; gap: 3px; }
2049
- .ss-w-cdash-pill { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; font-size: 9.5px; font-weight: 700; color: #fff; }
2050
- .ss-w-cdash-pill--w { background: color-mix(in srgb, var(--ss-w-pos) 88%, black); }
2051
- .ss-w-cdash-pill--d { background: var(--ss-w-faint); }
2052
- .ss-w-cdash-pill--l { background: color-mix(in srgb, var(--ss-w-neg) 88%, black); }
2053
-
2054
- .ss-w-cdash-lines { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; width: 100%; text-align: left; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-left: 3px solid var(--ss-w-accent); border-radius: 12px; padding: 10px 12px; }
2055
- .ss-w-cdash-lines-heading { font-size: 12px; font-weight: 700; color: var(--ss-w-accent); }
2056
- .ss-w-cdash-lines p { font-size: 12px; line-height: 1.4; color: var(--ss-w-ink); margin: 0; }
2057
- .ss-w-cdash-fact { display: block; width: 100%; text-align: left; font: inherit; font-size: 12px; line-height: 1.4; color: var(--ss-w-ink); background: none; border: none; padding: 0; cursor: pointer; }
2058
- .ss-w-cdash-fact:hover, .ss-w-cdash-fact:focus-visible { color: var(--ss-w-accent); text-decoration: underline; }
2059
- .ss-w-cdash-fact-label { font-size: 11px; font-weight: 700; color: var(--ss-w-accent); }
2060
-
2061
- .ss-w-cdash-cd { display: flex; flex-direction: column; align-items: center; gap: 4px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 10px 12px; text-align: center; }
2062
- .ss-w-cdash-cd-clock { display: flex; align-items: baseline; justify-content: center; gap: 2px; font-variant-numeric: tabular-nums; font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-weight: 700; font-size: 18px; color: var(--ss-w-ink); }
2063
- .ss-w-cdash-cd-h, .ss-w-cdash-cd-m, .ss-w-cdash-cd-s { color: var(--ss-w-accent); }
2064
- .ss-w-cdash-cd-label { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
2065
-
2066
- .ss-w-cdash-preview { display: flex; flex-direction: column; gap: 8px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 10px 12px; }
2067
- .ss-w-cdash-preview-header { display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: 6px; }
2068
- .ss-w-cdash-preview-header .ss-w-cdash-team:first-child { justify-self: start; text-align: left; }
2069
- .ss-w-cdash-preview-header .ss-w-cdash-team:last-child { justify-self: end; text-align: right; }
2070
- .ss-w-cdash-preview-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 6px; }
2071
- .ss-w-cdash-preview-label { font-size: 10.5px; font-weight: 600; color: var(--ss-w-faint); text-align: center; text-transform: uppercase; letter-spacing: 0.03em; }
2072
- .ss-w-cdash-preview-value { font-size: 12px; font-weight: 700; color: var(--ss-w-ink); text-align: center; }
2073
- .ss-w-cdash-preview-pills { display: flex; align-items: center; justify-content: center; gap: 3px; }
2074
- .ss-w-cdash-preview-row:first-of-type .ss-w-cdash-preview-value { justify-self: start; }
2075
- .ss-w-cdash-preview-row:first-of-type .ss-w-cdash-preview-value:last-child { justify-self: end; }
2076
- .ss-w-cdash-preview-row:last-of-type .ss-w-cdash-preview-pills:first-child { justify-self: start; }
2077
- .ss-w-cdash-preview-row:last-of-type .ss-w-cdash-preview-pills:last-child { justify-self: end; }
2078
- .ss-w-cdash-preview-edge { display: flex; align-items: baseline; gap: 4px; font-size: 12px; line-height: 1.4; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 10%, transparent); border-radius: 8px; padding: 6px 8px; }
2079
- .ss-w-cdash-preview-edge strong { color: var(--ss-w-ink); }
2080
- .ss-w-cdash-preview-h2h { display: flex; flex-direction: column; gap: 2px; }
2081
- .ss-w-cdash-preview-h2h p { font-size: 11.5px; line-height: 1.4; color: var(--ss-w-faint); margin: 0; }
2048
+ /* club fan pre-match dashboard */
2049
+ .ss-w-fandash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
2050
+ .ss-w-fandash-greeting { font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--ss-w-ink); }
2051
+ .ss-w-fandash-cards { display: flex; flex-direction: column; gap: 8px; }
2052
+
2053
+ /* Shared card chrome -- a plain <div> for [data] cards, a <button> for [prefill] cards. Both
2054
+ share the same box so the two families read as one system, not two ad hoc layouts. */
2055
+ .ss-w-fandash-card {
2056
+ display: flex; flex-direction: column; gap: 8px;
2057
+ padding: 12px; border-radius: 14px; border: 1px solid var(--ss-w-line);
2058
+ background: var(--ss-w-panel); text-align: left;
2059
+ color: inherit; font: inherit; width: 100%; min-width: 0;
2060
+ }
2061
+ .ss-w-fandash-card--tap { cursor: pointer; border-left: 3px solid var(--ss-w-accent); }
2062
+ .ss-w-fandash-card--tap:hover { background: var(--ss-w-bubble); }
2063
+ .ss-w-fandash-card-cta { align-self: flex-start; font-size: 11.5px; font-weight: 700; color: var(--ss-w-accent); }
2064
+
2065
+ /* Shared alignment primitives: a small letterspaced section eyebrow, and a 3-column value/label
2066
+ split reused by the fixture head-to-head (and any future stat-row card). */
2067
+ .ss-w-fandash-eyebrow { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); }
2068
+ .ss-w-fandash-split { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
2069
+ .ss-w-fandash-split-cell { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; min-width: 0; }
2070
+ .ss-w-fandash-split-value { font-size: 20px; font-weight: 750; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); line-height: 1; }
2071
+ .ss-w-fandash-split-label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); overflow-wrap: anywhere; }
2072
+
2073
+ /* fixture card */
2074
+ .ss-w-fandash-fixture-teams { display: flex; align-items: center; gap: 10px; }
2075
+ .ss-w-fandash-fixture-team { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
2076
+ .ss-w-fandash-fixture-team--away { justify-content: flex-end; }
2077
+ .ss-w-fandash-fixture-team .ss-w-bi-crest { width: 30px; height: 30px; }
2078
+ .ss-w-fandash-fixture-team .ss-w-bi-crest-mono { font-size: 12px; }
2079
+ .ss-w-fandash-fixture-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 650; }
2080
+ .ss-w-fandash-fixture-team--away .ss-w-fandash-fixture-name { text-align: right; }
2081
+ .ss-w-fandash-fixture-vs { flex: none; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); }
2082
+ /* date + venue as labelled rows, so a long stadium name gets its own line to ellipsis in */
2083
+ .ss-w-fandash-fixture-meta { display: flex; flex-direction: column; gap: 1px; border-top: 1px solid var(--ss-w-line); padding-top: 8px; }
2084
+ .ss-w-fandash-fixture-metarow { display: flex; align-items: baseline; gap: 9px; }
2085
+ .ss-w-fandash-fixture-metakey { flex: none; width: 44px; font-size: 8.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); }
2086
+ .ss-w-fandash-fixture-metaval { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
2087
+ .ss-w-fandash-fact { font-size: 12px; color: var(--ss-w-ink); background: var(--ss-w-bubble); border-radius: 8px; padding: 6px 8px; }
2088
+ /* each head-to-head figure sits on its own shaded tile, so the grid gap separates wins/draws/losses */
2089
+ .ss-w-fandash-h2h .ss-w-fandash-split-cell { background: var(--ss-w-bubble); border-radius: 10px; padding: 12px 6px; }
2090
+ /* extra breathing room between the tally row and the fact beyond the card's own gap */
2091
+ .ss-w-fandash-h2h .ss-w-fandash-fact { margin-top: 4px; }
2092
+
2093
+ /* last-five: one row per side, team name left, form pills right */
2094
+ .ss-w-fandash-fixture-form { display: flex; flex-direction: column; gap: 5px; }
2095
+ .ss-w-fandash-formrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
2096
+ .ss-w-fandash-formrow-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 600; }
2097
+ .ss-w-fandash-form { display: inline-flex; gap: 3px; flex: none; }
2098
+ .ss-w-fandash-form-pill {
2099
+ display: inline-grid; place-items: center; width: 16px; height: 16px;
2100
+ border-radius: 4px; font-size: 9.5px; font-weight: 700; color: #fff;
2101
+ }
2102
+ .ss-w-fandash-form-pill--win { background: var(--ss-w-pos); }
2103
+ .ss-w-fandash-form-pill--loss { background: var(--ss-w-neg); }
2104
+ .ss-w-fandash-form-pill--draw { background: var(--ss-w-faint); }
2105
+
2106
+ /* countdown card: a compact top card -- section eyebrow left, colon-separated clock right. Each
2107
+ segment stacks its figure over a small unit label (d/h/m/s). The exact kickoff date is not
2108
+ repeated here; the fixture card below carries it. */
2109
+ .ss-w-fandash-countdown-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
2110
+ .ss-w-fandash-countdown-clock { display: inline-flex; align-items: flex-start; gap: 6px; color: var(--ss-w-accent); }
2111
+ .ss-w-fandash-countdown-seg { display: inline-flex; flex-direction: column; align-items: center; }
2112
+ .ss-w-fandash-countdown-value { font-size: 26px; font-weight: 750; font-variant-numeric: tabular-nums; line-height: 1; letter-spacing: -.01em; }
2113
+ .ss-w-fandash-countdown-unit { font-size: 8px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); margin-top: 3px; }
2114
+ .ss-w-fandash-countdown-colon { font-size: 22px; font-weight: 750; line-height: 1; color: var(--ss-w-faint); }
2115
+
2116
+ /* injuries + transfers: two-column ours/theirs, in/out */
2117
+ .ss-w-fandash-transfers-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
2118
+ /* injuries: a 1px divider column sits between the two team sections, each headed by crest + name */
2119
+ .ss-w-fandash-injuries-cols { display: grid; grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr); gap: 12px; }
2120
+ .ss-w-fandash-injuries-divider { background: var(--ss-w-line); }
2121
+ .ss-w-fandash-injuries-col, .ss-w-fandash-transfers-col { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
2122
+ .ss-w-fandash-transfers-col-head { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--ss-w-faint); }
2123
+ .ss-w-fandash-injuries-col-head { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; color: var(--ss-w-ink); }
2124
+ .ss-w-fandash-injuries-col-head .ss-w-bi-crest { width: 18px; height: 18px; }
2125
+ .ss-w-fandash-injuries-col-head .ss-w-bi-crest-mono { font-size: 9px; }
2126
+ .ss-w-fandash-injuries-col-head-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2127
+ .ss-w-fandash-injuries-empty, .ss-w-fandash-transfers-empty { font-size: 11.5px; color: var(--ss-w-faint); }
2128
+ .ss-w-fandash-injury-list, .ss-w-fandash-transfer-list { list-style: none; display: flex; flex-direction: column; gap: 5px; }
2129
+ .ss-w-fandash-injury-row, .ss-w-fandash-transfer-row { display: flex; flex-direction: column; gap: 1px; }
2130
+ .ss-w-fandash-injury-name, .ss-w-fandash-transfer-name { font-size: 12px; font-weight: 600; overflow-wrap: anywhere; }
2131
+ .ss-w-fandash-injury-meta { display: flex; flex-wrap: wrap; gap: 4px; font-size: 10.5px; color: var(--ss-w-faint); }
2132
+ .ss-w-fandash-injury-status { font-weight: 700; color: var(--ss-w-neg); }
2133
+ .ss-w-fandash-transfer-club { font-size: 11px; color: var(--ss-w-faint); }
2134
+ .ss-w-fandash-transfer-type { align-self: flex-start; font-size: 9.5px; font-weight: 700; text-transform: uppercase; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 13%, var(--ss-w-panel)); border-radius: 999px; padding: 1px 7px; margin-top: 2px; }
2135
+
2136
+ /* reason-to-bet */
2137
+ .ss-w-fandash-reason-list { list-style: none; display: flex; flex-direction: column; gap: 6px; }
2138
+ .ss-w-fandash-reason-item { font-size: 12.5px; padding-left: 14px; position: relative; }
2139
+ .ss-w-fandash-reason-item::before { content: "\u2022"; position: absolute; left: 0; color: var(--ss-w-accent); font-weight: 700; }
2140
+
2141
+ /* where-to-watch */
2142
+ .ss-w-fandash-stations { display: flex; flex-wrap: wrap; gap: 6px; }
2143
+ .ss-w-fandash-station-chip { font-size: 11.5px; font-weight: 600; color: var(--ss-w-ink); background: var(--ss-w-bubble); border-radius: 999px; padding: 4px 10px; }
2144
+
2145
+ /* congested week: a fixed 3-column grid of upcoming fixtures (day / opponent / comp / time) */
2146
+ .ss-w-fandash-congested-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
2147
+ .ss-w-fandash-congested-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 8px; border-radius: 10px; background: var(--ss-w-bubble); }
2148
+ .ss-w-fandash-congested-day { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); }
2149
+ .ss-w-fandash-congested-opponent { font-size: 12.5px; font-weight: 650; overflow-wrap: anywhere; }
2150
+ .ss-w-fandash-congested-comp { font-size: 10px; color: var(--ss-w-faint); overflow-wrap: anywhere; }
2151
+ .ss-w-fandash-congested-time { font-size: 10.5px; color: var(--ss-w-faint); font-variant-numeric: tabular-nums; }
2152
+
2153
+ /* links: 2-column grid of tappable tiles */
2154
+ .ss-w-fandash-link-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
2155
+ .ss-w-fandash-link-tile { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 11px 13px; border-radius: 10px; border: 1px solid transparent; text-decoration: none; color: inherit; background: color-mix(in srgb, var(--ss-w-accent) 10%, var(--ss-w-panel)); }
2156
+ .ss-w-fandash-link-tile:hover { background: color-mix(in srgb, var(--ss-w-accent) 16%, var(--ss-w-panel)); }
2157
+ .ss-w-fandash-link-label { font-size: 12.5px; font-weight: 650; overflow-wrap: anywhere; }
2158
+ .ss-w-fandash-link-chevron { flex: none; width: 12px; height: 12px; color: var(--ss-w-accent); }
2082
2159
  `;
2083
2160
 
2084
2161
  // src/StatsWidget.tsx