@sensiblestats/widget-react 0.8.0 → 0.10.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,30 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Redesign team/player entity cards and polish the betting cards.
8
+
9
+ - Entity cards adopt an identity-hero layout: crest-led identity block beside a tabbed stats panel. Competition chips become an underline tab strip on top of the stats they switch, with the season pill (shortened to `26/27`) on that row. The W-D-L record renders as a proportional win/draw/loss form bar. Single-competition cards show a static league label; zero-scope cards fall back to a bare avatar+name row.
10
+ - Market odds cards drop the "updated HH:MM" line.
11
+ - Betting insight cards move the add-to-slip button beside the top-right odds box (and free its toast from the narrow column so text no longer clips), and relocate the fixture (team names) and competition under the market name, with the league on its own line.
12
+
13
+ ## 0.9.0
14
+
15
+ ### Minor Changes
16
+
17
+ - Entity cards now show the season and per-competition scope chips. `EntityCard`
18
+ drops the flat `stats` field in favor of `seasonName` + `scopes` (`All` aggregate
19
+ plus one scope per competition, each with an optional logo); the widget renders a
20
+ season label and, for 2+ competitions, a chip row that switches the stat set
21
+ client-side. `widget-embed` re-bundles `widget-react`, so it is republished too.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+ - @sensiblestats/widget-sdk@0.8.0
27
+
3
28
  ## 0.8.0
4
29
 
5
30
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -193,19 +193,27 @@ function readStatCells(raw) {
193
193
  }
194
194
  return cells;
195
195
  }
196
- function readStats(card) {
197
- return readStatCells(card.stats);
198
- }
199
196
  function toCardVM(card) {
200
197
  const entityType = str(card.entityType)?.toLowerCase();
201
198
  const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
199
+ const scopes = Array.isArray(card.scopes) ? card.scopes.map((s) => {
200
+ const label = str(s.label);
201
+ if (!label) return null;
202
+ return {
203
+ competitionId: typeof s.competitionId === "number" ? s.competitionId : null,
204
+ label,
205
+ image: str(s.imagePath),
206
+ stats: readStatCells(s.stats)
207
+ };
208
+ }).filter((s) => s !== null) : [];
202
209
  return {
203
210
  id: String(card.entityId),
204
211
  kind,
205
212
  name: card.canonicalName,
206
213
  image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
207
214
  sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
208
- stats: readStats(card)
215
+ season: str(card.seasonName),
216
+ scopes
209
217
  };
210
218
  }
211
219
  function toActionVM(action) {
@@ -536,16 +544,77 @@ function Markdown({ text }) {
536
544
  var import_react2 = require("react");
537
545
  var import_jsx_runtime6 = require("react/jsx-runtime");
538
546
  var MAX_VISIBLE = 3;
547
+ var RECORD_RE = /^(\d+)-(\d+)-(\d+)$/;
539
548
  function initials(name) {
540
549
  return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
541
550
  }
542
- function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
543
- const [expanded, setExpanded] = (0, import_react2.useState)(false);
544
- if (cards.length === 0) return null;
545
- const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
546
- const overflow = cards.length - MAX_VISIBLE;
547
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-cards", children: [
548
- visible.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
551
+ function shortSeason(season) {
552
+ return season.replace(/\d{4}/g, (year) => year.slice(2));
553
+ }
554
+ function Crest({ image }) {
555
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-crest", style: { backgroundImage: `url(${image})` }, "aria-hidden": "true" });
556
+ }
557
+ function FormBar({ w, d, l }) {
558
+ if (w + d + l === 0) return null;
559
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-form", "aria-hidden": "true", children: [
560
+ w > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-form-w", style: { flexGrow: w } }) : null,
561
+ d > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-form-d", style: { flexGrow: d } }) : null,
562
+ l > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-form-l", style: { flexGrow: l } }) : null
563
+ ] });
564
+ }
565
+ function StatRows({ stats }) {
566
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ss-w-ecard-stats", children: stats.map((s) => {
567
+ const m = RECORD_RE.exec(s.value);
568
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-row", children: [
569
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-rlabel", children: s.label }),
570
+ m ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FormBar, { w: Number(m[1]), d: Number(m[2]), l: Number(m[3]) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", {}),
571
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-rval", children: s.value })
572
+ ] }, s.label);
573
+ }) });
574
+ }
575
+ function CardBody({ c }) {
576
+ const [active, setActive] = (0, import_react2.useState)(0);
577
+ if (c.scopes.length === 0) return null;
578
+ const season = c.season ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-season", children: shortSeason(c.season) }) : null;
579
+ if (c.scopes.length === 1) {
580
+ const only = c.scopes[0];
581
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-body", children: [
582
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-tabs ss-w-ecard-tabs-static", children: [
583
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-ecard-lead", children: [
584
+ only.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Crest, { image: only.image }) : null,
585
+ only.label
586
+ ] }),
587
+ season
588
+ ] }),
589
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRows, { stats: only.stats })
590
+ ] });
591
+ }
592
+ const current = c.scopes[active] ?? c.scopes[0];
593
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-body", children: [
594
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-tabs", role: "tablist", children: [
595
+ c.scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
596
+ "button",
597
+ {
598
+ type: "button",
599
+ role: "tab",
600
+ className: "ss-w-ecard-tab",
601
+ "aria-selected": i === active,
602
+ "aria-label": s.label,
603
+ title: s.label,
604
+ onClick: () => setActive(i),
605
+ children: s.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Crest, { image: s.image }) : s.label
606
+ },
607
+ String(s.competitionId ?? "all")
608
+ )),
609
+ season
610
+ ] }),
611
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRows, { stats: current.stats }, String(current.competitionId ?? "all"))
612
+ ] });
613
+ }
614
+ function EntityCardItem({ c }) {
615
+ const hasBody = c.scopes.length > 0;
616
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: hasBody ? "ss-w-ecard" : "ss-w-ecard ss-w-ecard-bare", "data-kind": c.kind, children: [
617
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-id", children: [
549
618
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
550
619
  "span",
551
620
  {
@@ -554,16 +623,21 @@ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMore
554
623
  children: c.image ? "" : initials(c.name)
555
624
  }
556
625
  ),
557
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-meta", children: [
558
- c.kind !== "generic" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-kind", children: c.kind }) : null,
626
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-ecard-idtext", children: [
559
627
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-nm", children: c.name }),
560
628
  c.sub ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-sub", children: c.sub }) : null
561
- ] }),
562
- c.stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-stat", children: [
563
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("b", { children: s.value }),
564
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: s.label })
565
- ] }, s.label))
566
- ] }, c.id)),
629
+ ] })
630
+ ] }),
631
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardBody, { c })
632
+ ] });
633
+ }
634
+ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
635
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
636
+ if (cards.length === 0) return null;
637
+ const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
638
+ const overflow = cards.length - MAX_VISIBLE;
639
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-cards", children: [
640
+ visible.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(EntityCardItem, { c }, c.id)),
567
641
  overflow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
568
642
  ] });
569
643
  }
@@ -673,7 +747,7 @@ function AddToSlipButton({ it, slip, ui }) {
673
747
 
674
748
  // src/components/BettingInsightList.tsx
675
749
  var import_jsx_runtime9 = require("react/jsx-runtime");
676
- function StatRows({ stats }) {
750
+ function StatRows2({ stats }) {
677
751
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ss-w-bi-rows", children: stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-row", children: [
678
752
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-lbl", children: s.label }),
679
753
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-num", children: s.value })
@@ -684,7 +758,7 @@ function ScopedStats({ scopes }) {
684
758
  const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
685
759
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-scoped", children: [
686
760
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
687
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows, { stats: current.stats }, current.key)
761
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows2, { stats: current.stats }, current.key)
688
762
  ] });
689
763
  }
690
764
  function StatsDisclosure({ it, ui }) {
@@ -706,35 +780,37 @@ function StatsDisclosure({ it, ui }) {
706
780
  ]
707
781
  }
708
782
  ),
709
- open ? hasScopes ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ScopedStats, { scopes: it.scopes }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows, { stats: it.stats }) : null
783
+ open ? hasScopes ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ScopedStats, { scopes: it.scopes }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows2, { stats: it.stats }) : null
710
784
  ] });
711
785
  }
712
786
  function InsightCard({ it, ui, slip }) {
713
787
  const live = it.isLive || it.phase === "live";
714
788
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-binsight", children: [
715
- live || it.fixture || it.competition ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-status", children: [
716
- live ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-live", children: [
717
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
718
- ui?.liveLabel ?? "Live"
719
- ] }) : null,
720
- it.fixture ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-fixture", children: it.fixture }) : null,
721
- it.competition ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-comp", children: it.competition }) : null
722
- ] }) : null,
789
+ live ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ss-w-bi-status", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-live", children: [
790
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
791
+ ui?.liveLabel ?? "Live"
792
+ ] }) }) : null,
723
793
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-head", children: [
724
794
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-mkt", children: [
725
795
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-market", children: it.market }),
726
- it.selection ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-sel", children: it.selection }) : null
796
+ it.selection ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-sel", children: it.selection }) : null,
797
+ it.fixture || it.competition ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-match", children: [
798
+ it.fixture ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-fixture", children: it.fixture }) : null,
799
+ it.competition ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-comp", children: it.competition }) : null
800
+ ] }) : null
727
801
  ] }),
728
- it.odds ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-odds", children: [
729
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-odds-cap", children: ui?.oddsLabel ?? "Odds" }),
730
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("b", { children: it.odds }),
731
- it.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
732
- ] }) : null
802
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-action", children: [
803
+ it.odds ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-odds", children: [
804
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-odds-cap", children: ui?.oddsLabel ?? "Odds" }),
805
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("b", { children: it.odds }),
806
+ it.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
807
+ ] }) : null,
808
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AddToSlipButton, { it, slip, ui })
809
+ ] })
733
810
  ] }),
734
811
  it.text ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ss-w-bi-text", children: it.text }) : null,
735
812
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatsDisclosure, { it, ui }),
736
- it.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null,
737
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AddToSlipButton, { it, slip, ui })
813
+ it.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
738
814
  ] });
739
815
  }
740
816
  function BettingInsightList({ insights, ui, slip }) {
@@ -753,15 +829,8 @@ function OddsRow({ vm, row, ui, slip }) {
753
829
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
754
830
  ] });
755
831
  }
756
- function formatUpdated(iso) {
757
- if (!iso) return void 0;
758
- const date = new Date(iso);
759
- if (Number.isNaN(date.getTime())) return void 0;
760
- return date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
761
- }
762
832
  function MarketOddsCard({ vm, ui, slip }) {
763
833
  const [open, setOpen] = (0, import_react6.useState)(false);
764
- const updated = formatUpdated(vm.updatedAt);
765
834
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mocard", children: [
766
835
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-head", children: [
767
836
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-market", children: vm.market }),
@@ -779,10 +848,7 @@ function MarketOddsCard({ vm, ui, slip }) {
779
848
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
780
849
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
781
850
  ] }) : null,
782
- vm.bookmaker || updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-foot", children: [
783
- vm.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: vm.bookmaker }) : null,
784
- updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: `${ui?.updatedLabel ?? "updated"} ${updated}` }) : null
785
- ] }) : null,
851
+ vm.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ss-w-mo-foot", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: vm.bookmaker }) }) : null,
786
852
  vm.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
787
853
  ] });
788
854
  }
@@ -941,6 +1007,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
941
1007
  --ss-w-cta: #f5a623; --ss-w-cta-ink: #1b1300;
942
1008
  --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;
943
1009
  --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;
1010
+ --ss-w-pos: #1aa563; --ss-w-neg: #e0524b;
944
1011
  --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;
945
1012
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
946
1013
  font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);
@@ -950,12 +1017,14 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
950
1017
  --ss-w-accent: #37c891; --ss-w-cta: #ffb638; --ss-w-cta-ink: #1b1300;
951
1018
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
952
1019
  --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
1020
+ --ss-w-pos: #37c891; --ss-w-neg: #f0655d;
953
1021
  }
954
1022
  }
955
1023
  .ss-w-root[data-ss-w-theme="dark"] {
956
1024
  --ss-w-accent: #37c891; --ss-w-cta: #ffb638; --ss-w-cta-ink: #1b1300;
957
1025
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
958
1026
  --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;
1027
+ --ss-w-pos: #37c891; --ss-w-neg: #f0655d;
959
1028
  }
960
1029
  .ss-w-root[data-ss-w-theme="light"] {
961
1030
  --ss-w-accent: #17936a; --ss-w-cta: #f5a623; --ss-w-cta-ink: #1b1300;
@@ -1016,18 +1085,47 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1016
1085
  .ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }
1017
1086
 
1018
1087
  /* entity cards */
1019
- .ss-w-cards { display: flex; flex-direction: column; gap: 7px; }
1020
- .ss-w-ecard { display: flex; align-items: center; gap: 11px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; }
1021
- .ss-w-thumb { width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: #fff; background-color: var(--ss-w-nav); background-size: cover; background-position: center; }
1022
- .ss-w-thumb.ss-w-team { border-radius: 10px; }
1088
+ .ss-w-cards { display: flex; flex-direction: column; gap: 8px; }
1089
+
1090
+ /* Identity Hero: crest-led identity block | tabbed stats panel */
1091
+ .ss-w-ecard { display: grid; grid-template-columns: 112px 1fr; align-items: stretch; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; overflow: hidden; text-align: left; }
1092
+
1093
+ .ss-w-ecard-id { display: flex; flex-direction: column; align-items: center; gap: 7px; text-align: center; padding: 13px 10px; border-right: 1px solid var(--ss-w-line); background: linear-gradient(158deg, var(--ss-w-bubble), color-mix(in srgb, var(--ss-w-accent) 9%, var(--ss-w-bubble))); }
1094
+ .ss-w-ecard-idtext { display: flex; flex-direction: column; gap: 1px; min-width: 0; max-width: 100%; }
1095
+ .ss-w-thumb { width: 54px; height: 54px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 16px; font-weight: 700; color: #fff; background-color: var(--ss-w-nav); background-size: cover; background-position: center; box-shadow: 0 1px 3px rgba(0,0,0,.14); }
1096
+ .ss-w-thumb.ss-w-team { border-radius: 12px; }
1023
1097
  .ss-w-ecard[data-kind="player"] .ss-w-thumb { background-color: var(--ss-w-accent); }
1024
- .ss-w-meta { display: flex; flex-direction: column; min-width: 0; }
1025
- .ss-w-kind { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }
1026
- .ss-w-nm { font-weight: 660; }
1027
- .ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }
1028
- .ss-w-stat { margin-left: auto; text-align: center; }
1029
- .ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }
1030
- .ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }
1098
+ .ss-w-nm { font-weight: 680; line-height: 1.15; overflow-wrap: anywhere; }
1099
+ .ss-w-sub { font-size: 11px; color: var(--ss-w-faint); overflow-wrap: anywhere; }
1100
+
1101
+ /* bare card (no stat scopes): simple avatar + name row */
1102
+ .ss-w-ecard-bare { display: flex; align-items: center; }
1103
+ .ss-w-ecard-bare .ss-w-ecard-id { flex-direction: row; text-align: left; gap: 11px; background: none; border-right: none; padding: 10px 12px; }
1104
+ .ss-w-ecard-bare .ss-w-thumb { width: 40px; height: 40px; font-size: 13px; box-shadow: none; }
1105
+ .ss-w-ecard-bare .ss-w-thumb.ss-w-team { border-radius: 10px; }
1106
+
1107
+ .ss-w-ecard-body { min-width: 0; padding: 9px 12px 12px; display: flex; flex-direction: column; }
1108
+
1109
+ /* tab strip seated on the stats panel's top edge; active tab underlined in accent */
1110
+ .ss-w-ecard-tabs { display: flex; align-items: stretch; gap: 3px; border-bottom: 1px solid var(--ss-w-line); margin-bottom: 9px; flex-wrap: wrap; }
1111
+ .ss-w-ecard-tab { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 620; color: var(--ss-w-faint); background: none; border: none; cursor: pointer; padding: 5px 8px 8px; margin-bottom: -1px; border-bottom: 2px solid transparent; }
1112
+ .ss-w-ecard-tab[aria-selected="true"] { color: var(--ss-w-ink); border-bottom-color: var(--ss-w-accent); }
1113
+ .ss-w-ecard-tabs-static { align-items: center; }
1114
+ .ss-w-ecard-lead { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 640; color: var(--ss-w-ink); padding: 4px 2px 8px; min-width: 0; }
1115
+ .ss-w-ecard-crest { width: 15px; height: 15px; flex: none; display: block; border-radius: 4px; background-size: cover; background-position: center; }
1116
+ .ss-w-ecard-season { margin-left: auto; align-self: center; margin-bottom: 5px; font-size: 10.5px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 13%, var(--ss-w-panel)); border-radius: 999px; padding: 3px 9px; white-space: nowrap; }
1117
+ .ss-w-ecard-tabs-static .ss-w-ecard-season { margin-bottom: 4px; }
1118
+
1119
+ .ss-w-ecard-stats { display: flex; flex-direction: column; gap: 4px; }
1120
+ .ss-w-ecard-row { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px; }
1121
+ .ss-w-ecard-rlabel { font-size: 9.5px; text-transform: uppercase; letter-spacing: .045em; color: var(--ss-w-faint); font-weight: 600; }
1122
+ .ss-w-ecard-rval { justify-self: end; font-size: 15px; font-weight: 720; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
1123
+ .ss-w-form { justify-self: end; display: flex; height: 6px; width: 48px; border-radius: 4px; overflow: hidden; gap: 2px; }
1124
+ .ss-w-form span { display: block; border-radius: 2px; }
1125
+ .ss-w-form-w { background: var(--ss-w-pos); }
1126
+ .ss-w-form-d { background: var(--ss-w-faint); }
1127
+ .ss-w-form-l { background: var(--ss-w-neg); }
1128
+
1031
1129
  .ss-w-cards-more { align-self: flex-start; font-size: 11.5px; font-weight: 560; color: var(--ss-w-accent); background: none; border: none; cursor: pointer; padding: 3px 2px; }
1032
1130
  .ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }
1033
1131
  .ss-w-cards-more:disabled { opacity: .5; cursor: default; }
@@ -1044,7 +1142,12 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1044
1142
  .ss-w-bi-mkt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1045
1143
  .ss-w-bi-market { font-size: 15px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
1046
1144
  .ss-w-bi-sel { font-size: 12px; line-height: 1.3; color: var(--ss-w-faint); }
1047
- .ss-w-bi-odds { margin-left: auto; 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; }
1145
+ .ss-w-bi-match { display: flex; flex-direction: column; gap: 1px; min-width: 0; margin-top: 5px; }
1146
+ .ss-w-bi-action { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
1147
+ .ss-w-bi-action .ss-w-slip > button { flex: 1; text-align: center; }
1148
+ /* 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. */
1149
+ .ss-w-bi-action .ss-w-toast { left: auto; right: 0; width: max-content; max-width: 210px; white-space: normal; }
1150
+ .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; }
1048
1151
  .ss-w-bi-odds-cap { font-size: 9px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ss-w-faint); }
1049
1152
  .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; }
1050
1153
  .ss-w-bi-book { font-size: 9.5px; color: var(--ss-w-faint); }