@sensiblestats/widget-react 0.22.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
@@ -1,5 +1,5 @@
1
1
  // src/StatsWidget.tsx
2
- import { useEffect as useEffect5, useMemo as useMemo2, useRef as useRef3 } from "react";
2
+ import { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef3 } from "react";
3
3
  import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
4
4
 
5
5
  // src/kickoff.ts
@@ -519,8 +519,8 @@ function applyEvent(state, event) {
519
519
  return { ...state, actionsLoading: true, status: event.data.message };
520
520
  case "turn_complete": {
521
521
  const active = activeTurn(state);
522
- const text = active?.text.trim() ?? "";
523
- 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;
524
524
  return {
525
525
  ...state,
526
526
  isStreaming: false,
@@ -531,7 +531,8 @@ function applyEvent(state, event) {
531
531
  messages: mapActive(state, (t) => ({
532
532
  ...t,
533
533
  done: true,
534
- greetingDashboard: event.data.dashboard ?? void 0
534
+ greetingDashboard: event.data.dashboard ?? void 0,
535
+ clubFanDashboard: event.data.clubFanDashboard ?? void 0
535
536
  }))
536
537
  };
537
538
  }
@@ -728,7 +729,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
728
729
  }
729
730
 
730
731
  // src/components/ChatPanel.tsx
731
- import { useEffect as useEffect4, useState as useState7 } from "react";
732
+ import { useEffect as useEffect5, useState as useState8 } from "react";
732
733
 
733
734
  // src/components/ChatHeader.tsx
734
735
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -741,7 +742,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
741
742
  }
742
743
 
743
744
  // src/components/MessageList.tsx
744
- import { useEffect as useEffect3, useRef as useRef2 } from "react";
745
+ import { useEffect as useEffect4, useRef as useRef2 } from "react";
745
746
 
746
747
  // src/components/Markdown.tsx
747
748
  import ReactMarkdown from "react-markdown";
@@ -1287,28 +1288,299 @@ function GreetingDashboard({ dashboard, onTap }) {
1287
1288
  ] });
1288
1289
  }
1289
1290
 
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";
1298
+ }
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}`)) });
1301
+ }
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
+ ] });
1307
+ }
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 })
1340
+ ] }),
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
+ ] })
1347
+ ] }),
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
1368
+ ] });
1369
+ }
1370
+
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
+ ] }) });
1412
+ }
1413
+
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
+ ] });
1421
+ }
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
+ ] })
1448
+ ] });
1449
+ }
1450
+
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}`)) })
1457
+ ] });
1458
+ }
1459
+
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}`)) })
1466
+ ] });
1467
+ }
1468
+
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
+ ] });
1482
+ }
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
+ ] });
1509
+ }
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}`)) })
1520
+ ] });
1521
+ }
1522
+
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:
1554
+ return null;
1555
+ }
1556
+ }
1557
+
1290
1558
  // src/components/ChatMessage.tsx
1291
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1559
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
1292
1560
  function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
1293
1561
  if (message.role === "user") {
1294
- return /* @__PURE__ */ jsx17("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx17("div", { className: "ss-w-bubble", children: message.text }) });
1562
+ return /* @__PURE__ */ jsx26("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx26("div", { className: "ss-w-bubble", children: message.text }) });
1295
1563
  }
1296
- return /* @__PURE__ */ jsxs13("div", { className: "ss-w-msg ss-w-bot", children: [
1297
- message.text && !message.greetingDashboard ? /* @__PURE__ */ jsx17(Markdown, { text: message.text }) : null,
1298
- message.greetingDashboard ? /* @__PURE__ */ jsx17(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1299
- /* @__PURE__ */ jsx17(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1300
- /* @__PURE__ */ jsx17(BettingInsightList, { insights: message.insights, ui, slip }),
1301
- /* @__PURE__ */ jsx17(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1302
- /* @__PURE__ */ jsx17(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1303
- /* @__PURE__ */ jsx17(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
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,
1571
+ /* @__PURE__ */ jsx26(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1572
+ /* @__PURE__ */ jsx26(BettingInsightList, { insights: message.insights, ui, slip }),
1573
+ /* @__PURE__ */ jsx26(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1574
+ /* @__PURE__ */ jsx26(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1575
+ /* @__PURE__ */ jsx26(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
1304
1576
  ] });
1305
1577
  }
1306
1578
 
1307
1579
  // src/components/IntentChips.tsx
1308
- import { jsx as jsx18 } from "react/jsx-runtime";
1580
+ import { jsx as jsx27 } from "react/jsx-runtime";
1309
1581
  function IntentChips({ chips, onPick }) {
1310
1582
  if (chips.length === 0) return null;
1311
- return /* @__PURE__ */ jsx18("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx18("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
1583
+ return /* @__PURE__ */ jsx27("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx27("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
1312
1584
  }
1313
1585
 
1314
1586
  // src/scroll.ts
@@ -1318,18 +1590,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
1318
1590
  }
1319
1591
 
1320
1592
  // src/components/MessageList.tsx
1321
- import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1593
+ import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
1322
1594
  function MessageList({ controller, ui, slip, onDashboardTap }) {
1323
1595
  const { state, send } = controller;
1324
1596
  const listRef = useRef2(null);
1325
1597
  const endRef = useRef2(null);
1326
1598
  const pinnedRef = useRef2(true);
1327
- useEffect3(() => {
1599
+ useEffect4(() => {
1328
1600
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
1329
1601
  }, [state.messages, state.status]);
1330
1602
  const hasUserMessage = state.messages.some((m) => m.role === "user");
1331
- const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
1332
- return /* @__PURE__ */ jsxs14(
1603
+ const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
1604
+ return /* @__PURE__ */ jsxs23(
1333
1605
  "div",
1334
1606
  {
1335
1607
  className: "ss-w-list",
@@ -1339,7 +1611,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1339
1611
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
1340
1612
  },
1341
1613
  children: [
1342
- state.messages.map((m) => /* @__PURE__ */ jsx19(
1614
+ state.messages.map((m) => /* @__PURE__ */ jsx28(
1343
1615
  ChatMessage,
1344
1616
  {
1345
1617
  message: m,
@@ -1354,25 +1626,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1354
1626
  },
1355
1627
  m.id
1356
1628
  )),
1357
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx19(ActionButtons, { actions: [], loading: true, onAct: () => {
1629
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx28(ActionButtons, { actions: [], loading: true, onAct: () => {
1358
1630
  } }) : null,
1359
- state.status ? /* @__PURE__ */ jsx19("div", { className: "ss-w-status", children: state.status }) : null,
1360
- state.error ? /* @__PURE__ */ jsxs14("div", { className: "ss-w-error", role: "alert", children: [
1361
- /* @__PURE__ */ jsx19("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1362
- /* @__PURE__ */ jsx19("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1631
+ state.status ? /* @__PURE__ */ jsx28("div", { className: "ss-w-status", children: state.status }) : null,
1632
+ state.error ? /* @__PURE__ */ jsxs23("div", { className: "ss-w-error", role: "alert", children: [
1633
+ /* @__PURE__ */ jsx28("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1634
+ /* @__PURE__ */ jsx28("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1363
1635
  ] }) : null,
1364
- !hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx19(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1365
- /* @__PURE__ */ jsx19("div", { ref: endRef })
1636
+ !hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx28(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1637
+ /* @__PURE__ */ jsx28("div", { ref: endRef })
1366
1638
  ]
1367
1639
  }
1368
1640
  );
1369
1641
  }
1370
1642
 
1371
1643
  // src/components/ChatInput.tsx
1372
- import { useState as useState6 } from "react";
1373
- import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
1644
+ import { useState as useState7 } from "react";
1645
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
1374
1646
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
1375
- const [value, setValue] = useState6("");
1647
+ const [value, setValue] = useState7("");
1376
1648
  const submit = () => {
1377
1649
  const t = value.trim();
1378
1650
  if (!t) return;
@@ -1385,18 +1657,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
1385
1657
  submit();
1386
1658
  }
1387
1659
  };
1388
- return /* @__PURE__ */ jsxs15("div", { className: "ss-w-input", children: [
1389
- /* @__PURE__ */ jsx20("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1390
- streaming ? /* @__PURE__ */ jsx20("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx20("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx20("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx20(SendIcon, {}) })
1660
+ return /* @__PURE__ */ jsxs24("div", { className: "ss-w-input", children: [
1661
+ /* @__PURE__ */ jsx29("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
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, {}) })
1391
1663
  ] });
1392
1664
  }
1393
1665
 
1394
1666
  // src/components/ChatPanel.tsx
1395
- import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
1667
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
1396
1668
  var FULLSCREEN_QUERY = "(max-width: 639px)";
1397
1669
  function useIsModal() {
1398
- const [modal, setModal] = useState7(false);
1399
- useEffect4(() => {
1670
+ const [modal, setModal] = useState8(false);
1671
+ useEffect5(() => {
1400
1672
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1401
1673
  const mql = window.matchMedia(FULLSCREEN_QUERY);
1402
1674
  const sync = () => setModal(mql.matches);
@@ -1409,17 +1681,17 @@ function useIsModal() {
1409
1681
  function ChatPanel({ controller, ui, slip, onDashboardTap }) {
1410
1682
  const { state, close, stop, send } = controller;
1411
1683
  const isModal = useIsModal();
1412
- useEffect4(() => {
1684
+ useEffect5(() => {
1413
1685
  const onKey = (e) => {
1414
1686
  if (e.key === "Escape") close();
1415
1687
  };
1416
1688
  window.addEventListener("keydown", onKey);
1417
1689
  return () => window.removeEventListener("keydown", onKey);
1418
1690
  }, [close]);
1419
- return /* @__PURE__ */ jsxs16("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1420
- /* @__PURE__ */ jsx21(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1421
- /* @__PURE__ */ jsx21(MessageList, { controller, ui, slip, onDashboardTap }),
1422
- /* @__PURE__ */ jsx21(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
1691
+ return /* @__PURE__ */ jsxs25("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1692
+ /* @__PURE__ */ jsx30(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1693
+ /* @__PURE__ */ jsx30(MessageList, { controller, ui, slip, onDashboardTap }),
1694
+ /* @__PURE__ */ jsx30(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
1423
1695
  ] });
1424
1696
  }
1425
1697
 
@@ -1772,10 +2044,122 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1772
2044
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: #eef3fb; }
1773
2045
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
1774
2046
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
2047
+
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); }
1775
2159
  `;
1776
2160
 
1777
2161
  // src/StatsWidget.tsx
1778
- import { jsx as jsx22 } from "react/jsx-runtime";
2162
+ import { jsx as jsx31 } from "react/jsx-runtime";
1779
2163
  function buildClient(props) {
1780
2164
  try {
1781
2165
  if ("client" in props && props.client) {
@@ -1821,14 +2205,14 @@ function StatsWidget(props) {
1821
2205
  () => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
1822
2206
  [built]
1823
2207
  );
1824
- useEffect5(() => {
2208
+ useEffect6(() => {
1825
2209
  if (!built || !ui.injectStyles) return;
1826
2210
  const node = rootRef.current?.getRootNode();
1827
2211
  if (node) injectStyles(node);
1828
2212
  }, [built, ui.injectStyles]);
1829
2213
  if (!built) return null;
1830
2214
  const attrs = themeAttrs(ui);
1831
- return /* @__PURE__ */ jsx22("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__ */ jsx22(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ jsx22(
2215
+ return /* @__PURE__ */ jsx31("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__ */ jsx31(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ jsx31(
1832
2216
  ChatFab,
1833
2217
  {
1834
2218
  label: ui.launcherLabel,