@sensiblestats/widget-react 0.16.0 → 0.17.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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5fd5471: Redesign the betting insight card: crest-led three-row layout with competition logo, kickoff time and a bar-chart reasoning strip.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [5fd5471]
12
+ - @sensiblestats/widget-sdk@0.15.0
13
+
3
14
  ## 0.16.0
4
15
 
5
16
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -39,6 +39,18 @@ module.exports = __toCommonJS(index_exports);
39
39
  var import_react12 = require("react");
40
40
  var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
41
41
 
42
+ // src/kickoff.ts
43
+ function formatBettingKickoff(kickoffUtc, ui, locale, now = /* @__PURE__ */ new Date()) {
44
+ const at = new Date(kickoffUtc);
45
+ const intlLocale = locale === "tr" ? "tr-TR" : "en-GB";
46
+ const time = new Intl.DateTimeFormat(intlLocale, { hour: "2-digit", minute: "2-digit" }).format(at);
47
+ const atLocalDate = new Date(at.getFullYear(), at.getMonth(), at.getDate());
48
+ const nowLocalDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
49
+ const dayDiff = Math.round((atLocalDate.getTime() - nowLocalDate.getTime()) / 864e5);
50
+ const dayLabel = dayDiff === 0 ? ui.today : dayDiff === 1 ? ui.tomorrow : new Intl.DateTimeFormat(intlLocale, { weekday: "short", day: "numeric", month: "short" }).format(at);
51
+ return { dayLabel, time };
52
+ }
53
+
42
54
  // src/i18n.ts
43
55
  var STRINGS = {
44
56
  en: {
@@ -93,7 +105,10 @@ var STRINGS = {
93
105
  slipPriceNotAccepted: (from, to) => `Price moved to ${to} (was ${from}). Tap to add.`,
94
106
  slipLoginRequired: "Sign in to add to your slip",
95
107
  slipSuspended: "Market suspended",
96
- slipFailed: "Couldn't add to slip. Try again."
108
+ slipFailed: "Couldn't add to slip. Try again.",
109
+ today: "Today",
110
+ tomorrow: "Tomorrow",
111
+ bettingKickoff: (kickoffUtc, now) => formatBettingKickoff(kickoffUtc, STRINGS.en, "en", now)
97
112
  },
98
113
  tr: {
99
114
  launcherLabel: "SensibleStats ile sohbet et",
@@ -147,7 +162,10 @@ var STRINGS = {
147
162
  slipPriceNotAccepted: (from, to) => `Oran ${to} oldu (\xF6nceki ${from}). Eklemek i\xE7in dokunun.`,
148
163
  slipLoginRequired: "Kupona eklemek i\xE7in giri\u015F yap\u0131n",
149
164
  slipSuspended: "Bu market ask\u0131ya al\u0131nd\u0131",
150
- slipFailed: "Kupona eklenemedi. Tekrar deneyin."
165
+ slipFailed: "Kupona eklenemedi. Tekrar deneyin.",
166
+ today: "Bug\xFCn",
167
+ tomorrow: "Yar\u0131n",
168
+ bettingKickoff: (kickoffUtc, now) => formatBettingKickoff(kickoffUtc, STRINGS.tr, "tr", now)
151
169
  }
152
170
  };
153
171
  function resolveLocaleStrings(locale) {
@@ -214,7 +232,10 @@ function normalizeUi(cfg) {
214
232
  slipPriceNotAccepted: strings.slipPriceNotAccepted,
215
233
  slipLoginRequired: strings.slipLoginRequired,
216
234
  slipSuspended: strings.slipSuspended,
217
- slipFailed: strings.slipFailed
235
+ slipFailed: strings.slipFailed,
236
+ today: strings.today,
237
+ tomorrow: strings.tomorrow,
238
+ bettingKickoff: strings.bettingKickoff
218
239
  };
219
240
  }
220
241
  function toClientOptions(cfg) {
@@ -403,7 +424,13 @@ function toInsightVM(insight) {
403
424
  oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
404
425
  quotedAtUtc: str(insight.odds?.quotedAtUtc),
405
426
  previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
406
- movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0
427
+ movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0,
428
+ homeTeamName: home,
429
+ awayTeamName: away,
430
+ homeCrest: str(insight.homeTeamImagePath),
431
+ awayCrest: str(insight.awayTeamImagePath),
432
+ competitionCrest: str(insight.competitionImagePath),
433
+ kickoffUtc: str(insight.kickoffUtc)
407
434
  };
408
435
  }
409
436
  function toSlipSelection(vm, conversationId) {
@@ -993,10 +1020,21 @@ function ScopedStats({ scopes }) {
993
1020
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatRows2, { stats: current.stats }, current.key)
994
1021
  ] });
995
1022
  }
1023
+ function hasStatsContent(it) {
1024
+ return !!it.scopes && it.scopes.length >= 2 || it.stats.length > 0;
1025
+ }
1026
+ function initials2(name) {
1027
+ return (name ?? "").trim().slice(0, 1).toUpperCase() || "?";
1028
+ }
1029
+ function Crest2({ url, name, kind }) {
1030
+ const cls = kind === "comp" ? "ss-w-bi-comp-logo" : "ss-w-bi-crest";
1031
+ if (url) return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cls, style: { backgroundImage: `url(${url})` }, "aria-hidden": "true" });
1032
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `${cls} ss-w-bi-crest-mono`, "aria-hidden": "true", children: initials2(name) });
1033
+ }
996
1034
  function StatsDisclosure({ it, ui }) {
997
1035
  const [open, setOpen] = (0, import_react5.useState)(false);
998
1036
  const hasScopes = !!it.scopes && it.scopes.length >= 2;
999
- const hasStats = hasScopes || it.stats.length > 0;
1037
+ const hasStats = hasStatsContent(it);
1000
1038
  if (!hasStats) return null;
1001
1039
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-stats", children: [
1002
1040
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
@@ -1017,24 +1055,42 @@ function StatsDisclosure({ it, ui }) {
1017
1055
  }
1018
1056
  function InsightCard({ it, ui, slip }) {
1019
1057
  const live = it.isLive || it.phase === "live";
1058
+ const kickoff = it.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(it.kickoffUtc) : void 0;
1059
+ const hasNamedTeams = !!it.homeTeamName && !!it.awayTeamName;
1060
+ const showHeader = !!it.competition || live || !!kickoff;
1061
+ const showReason = !!it.text || hasStatsContent(it);
1020
1062
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-binsight", children: [
1021
- live || it.isMarketFloor ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-status", children: [
1022
- live ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-live", children: [
1023
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
1024
- ui?.liveLabel ?? "Live"
1025
- ] }) : null,
1026
- it.isMarketFloor ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-floor", children: ui?.marketFloorLabel ?? "Market price \xB7 not a value pick" }) : null
1027
- ] }) : null,
1028
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-head", children: [
1029
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-mkt", children: [
1030
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-market", children: it.market }),
1031
- it.selection ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-sel", children: it.selection }) : null,
1032
- it.fixture || it.competition ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-match", children: [
1033
- it.fixture ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-fixture", children: it.fixture }) : null,
1034
- it.competition ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-comp", children: it.competition }) : null
1063
+ showHeader ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-header", children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-header-left", children: it.competition ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Crest2, { kind: "comp", url: it.competitionCrest, name: it.competition }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-comp", children: it.competition })
1067
+ ] }) : null }),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-header-right", children: [
1069
+ live ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-live", children: [
1070
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
1071
+ ui?.liveLabel ?? "Live"
1072
+ ] }) : null,
1073
+ kickoff ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-kick", children: [
1074
+ kickoff.dayLabel,
1075
+ " ",
1076
+ kickoff.time
1035
1077
  ] }) : null
1036
- ] }),
1078
+ ] })
1079
+ ] }) : null,
1080
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-body", children: [
1081
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ss-w-bi-fixture", children: hasNamedTeams ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Crest2, { kind: "team", url: it.homeCrest, name: it.homeTeamName }),
1083
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-team", children: it.homeTeamName }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-vs", "aria-hidden": "true", children: "vs" }),
1085
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-team", children: it.awayTeamName }),
1086
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Crest2, { kind: "team", url: it.awayCrest, name: it.awayTeamName })
1087
+ ] }) : it.fixture ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-fixture-txt", children: it.fixture }) : null }),
1037
1088
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-action", children: [
1089
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-mkt", children: [
1090
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-market", children: it.market }),
1091
+ it.selection ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-sel", children: it.selection }) : null
1092
+ ] }),
1093
+ it.isMarketFloor ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-floor", children: ui?.marketFloorLabel ?? "Market price \xB7 not a value pick" }) : null,
1038
1094
  it.odds ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ss-w-bi-odds", children: [
1039
1095
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-odds-cap", children: ui?.oddsLabel ?? "Odds" }),
1040
1096
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("b", { children: it.odds }),
@@ -1044,8 +1100,13 @@ function InsightCard({ it, ui, slip }) {
1044
1100
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AddToSlipButton, { it, slip, ui })
1045
1101
  ] })
1046
1102
  ] }),
1047
- it.text ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ss-w-bi-text", children: it.text }) : null,
1048
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatsDisclosure, { it, ui }),
1103
+ showReason ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-reason", children: [
1104
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-bi-chart", "aria-hidden": "true" }),
1105
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-bi-reason-body", children: [
1106
+ it.text ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ss-w-bi-text", children: it.text }) : null,
1107
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatsDisclosure, { it, ui })
1108
+ ] })
1109
+ ] }) : null,
1049
1110
  it.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
1050
1111
  ] });
1051
1112
  }
@@ -1233,13 +1294,13 @@ function GreetingHeaderBlock({ block }) {
1233
1294
  // src/components/club-dashboard/FixtureHeroBlock.tsx
1234
1295
  var import_react7 = require("react");
1235
1296
  var import_jsx_runtime17 = require("react/jsx-runtime");
1236
- function initials2(name) {
1297
+ function initials3(name) {
1237
1298
  return name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
1238
1299
  }
1239
- function Crest2({ url, name }) {
1300
+ function Crest3({ url, name }) {
1240
1301
  const [broken, setBroken] = (0, import_react7.useState)(false);
1241
1302
  if (!url || broken) {
1242
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-crest ss-w-cdash-crest--fallback", "aria-hidden": "true", children: initials2(name) });
1303
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-crest ss-w-cdash-crest--fallback", "aria-hidden": "true", children: initials3(name) });
1243
1304
  }
1244
1305
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("img", { className: "ss-w-cdash-crest", src: url, alt: "", "aria-hidden": "true", onError: () => setBroken(true) });
1245
1306
  }
@@ -1250,11 +1311,11 @@ function FixtureHeroBlock({
1250
1311
  }) {
1251
1312
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "ss-w-cdash-fixture", children: [
1252
1313
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "ss-w-cdash-tie", children: [
1253
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Crest2, { url: block.homeCrestUrl, name: block.homeName }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Crest3, { url: block.homeCrestUrl, name: block.homeName }),
1254
1315
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-team", children: block.homeName }),
1255
1316
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-vs", children: "v" }),
1256
1317
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-team", children: block.awayName }),
1257
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Crest2, { url: block.awayCrestUrl, name: block.awayName })
1318
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Crest3, { url: block.awayCrestUrl, name: block.awayName })
1258
1319
  ] }),
1259
1320
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "ss-w-cdash-fixture-meta", children: [
1260
1321
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-kickoff", children: ui.clubKickoff(block.kickoffUnix * 1e3) }),
@@ -1648,26 +1709,51 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1648
1709
 
1649
1710
  /* betting insights */
1650
1711
  .ss-w-binsights { display: flex; flex-direction: column; gap: 7px; }
1651
- .ss-w-binsight { background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; border-left: 3px solid var(--ss-w-accent); padding: 9px 11px; display: flex; flex-direction: column; gap: 6px; }
1652
- .ss-w-bi-status { display: flex; align-items: center; gap: 6px; min-width: 0; }
1653
- .ss-w-bi-fixture { font-size: 11px; color: var(--ss-w-ink); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1654
- .ss-w-bi-comp { font-size: 10.5px; color: var(--ss-w-faint); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1712
+ .ss-w-binsight { background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; border-left: 3px solid var(--ss-w-accent); padding: 9px 11px; display: flex; flex-direction: column; gap: 8px; }
1713
+
1714
+ /* row 1: competition logo + name on the left, live badge + kickoff on the right */
1715
+ .ss-w-bi-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; padding-bottom: 7px; border-bottom: 1px solid var(--ss-w-line); }
1716
+ .ss-w-bi-header-left { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
1717
+ .ss-w-bi-header-right { display: inline-flex; align-items: center; gap: 8px; flex: none; }
1718
+ .ss-w-bi-comp { font-size: 10.5px; color: var(--ss-w-faint); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1719
+ .ss-w-bi-kick { font-size: 10.5px; font-weight: 600; color: var(--ss-w-faint); white-space: nowrap; flex: none; }
1655
1720
  .ss-w-bi-live { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: #c0392b; border-radius: 4px; padding: 1px 5px; display: inline-flex; align-items: center; gap: 4px; flex: none; }
1656
1721
  .ss-w-bi-live-dot { width: 5px; height: 5px; border-radius: 50%; background: #fff; }
1657
- .ss-w-bi-floor { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 4px; padding: 1px 5px; display: inline-flex; align-items: center; flex: none; }
1658
- .ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }
1659
- .ss-w-bi-mkt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1660
- .ss-w-bi-market { font-size: 15px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
1661
- .ss-w-bi-sel { font-size: 12px; line-height: 1.3; color: var(--ss-w-faint); }
1662
- .ss-w-bi-match { display: flex; flex-direction: column; gap: 1px; min-width: 0; margin-top: 5px; }
1663
- .ss-w-bi-action { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
1722
+ .ss-w-bi-floor { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 4px; padding: 1px 5px; display: inline-flex; align-items: center; align-self: flex-start; flex: none; }
1723
+
1724
+ /* crests -- CSS background-image on a span, never <img> (matches .ss-w-ecard-crest precedent) */
1725
+ .ss-w-bi-comp-logo { width: 15px; height: 15px; flex: none; display: block; border-radius: 50%; background-size: contain; background-repeat: no-repeat; background-position: center; }
1726
+ .ss-w-bi-crest { width: 22px; height: 22px; flex: none; display: block; border-radius: 50%; background-size: contain; background-repeat: no-repeat; background-position: center; background-color: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); }
1727
+ .ss-w-bi-crest-mono { display: grid; place-items: center; font-size: 9px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 16%, var(--ss-w-panel)); }
1728
+
1729
+ /* row 2: crest-flanked fixture on the left, boxed action panel on the right */
1730
+ .ss-w-bi-body { display: flex; align-items: center; gap: 10px; }
1731
+ .ss-w-bi-fixture { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; font-size: 12px; font-weight: 650; color: var(--ss-w-ink); }
1732
+ .ss-w-bi-team { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1733
+ .ss-w-bi-vs { font-size: 10px; font-weight: 600; color: var(--ss-w-faint); flex: none; }
1734
+ .ss-w-bi-fixture-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1735
+ .ss-w-bi-action { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: stretch; gap: 6px; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; }
1664
1736
  .ss-w-bi-action .ss-w-slip > button { flex: 1; text-align: center; }
1665
1737
  /* The action column is narrow; let its toast size to its own text and anchor to the card's right edge instead of being clipped to the button width. */
1666
1738
  .ss-w-bi-action .ss-w-toast { left: auto; right: 0; width: max-content; max-width: 210px; white-space: normal; }
1739
+ .ss-w-bi-mkt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1740
+ .ss-w-bi-market { font-size: 13.5px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
1741
+ .ss-w-bi-sel { font-size: 11.5px; line-height: 1.3; color: var(--ss-w-faint); }
1667
1742
  .ss-w-bi-odds { flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 1px; background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 10px; padding: 4px 11px; min-width: 62px; }
1668
1743
  .ss-w-bi-odds-cap { font-size: 9px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ss-w-faint); }
1669
1744
  .ss-w-bi-odds b { font-size: 19px; font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); line-height: 1.1; }
1670
1745
  .ss-w-bi-book { font-size: 9.5px; color: var(--ss-w-faint); }
1746
+
1747
+ /* row 3: bar-chart glyph + reasoning prose + stats-expander chevron, in a subtle strip */
1748
+ .ss-w-bi-reason { display: flex; align-items: flex-start; gap: 8px; background: var(--ss-w-bubble); border-radius: 10px; padding: 8px 10px; }
1749
+ .ss-w-bi-chart { flex: none; width: 13px; height: 12px; margin-top: 2px; opacity: .55;
1750
+ background:
1751
+ linear-gradient(var(--ss-w-accent), var(--ss-w-accent)) left / 3px 55% no-repeat,
1752
+ linear-gradient(var(--ss-w-accent), var(--ss-w-accent)) center / 3px 100% no-repeat,
1753
+ linear-gradient(var(--ss-w-accent), var(--ss-w-accent)) right / 3px 75% no-repeat;
1754
+ background-position: left bottom, center bottom, right bottom;
1755
+ }
1756
+ .ss-w-bi-reason-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; flex: 1; }
1671
1757
  .ss-w-bi-text { font-size: 12px; line-height: 1.45; color: var(--ss-w-ink); }
1672
1758
  .ss-w-bi-stats { display: flex; flex-direction: column; gap: 8px; }
1673
1759
  .ss-w-bi-toggle { align-self: flex-start; display: inline-flex; align-items: center; gap: 5px; background: none; border: none; color: var(--ss-w-accent); font: inherit; font-size: 11.5px; font-weight: 600; padding: 2px 0; cursor: pointer; }