@sensiblestats/widget-react 0.9.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix single-league entity-card header overflow: a long competition name (e.g. "UEFA Europa Conference League") no longer wraps the season pill onto a second line. The competition name now truncates with an ellipsis (full name kept as a `title` tooltip) while the season pill stays pinned on the same row.
8
+
9
+ ## 0.10.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Redesign team/player entity cards and polish the betting cards.
14
+
15
+ - 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.
16
+ - Market odds cards drop the "updated HH:MM" line.
17
+ - 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.
18
+
3
19
  ## 0.9.0
4
20
 
5
21
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -544,56 +544,77 @@ function Markdown({ text }) {
544
544
  var import_react2 = require("react");
545
545
  var import_jsx_runtime6 = require("react/jsx-runtime");
546
546
  var MAX_VISIBLE = 3;
547
+ var RECORD_RE = /^(\d+)-(\d+)-(\d+)$/;
547
548
  function initials(name) {
548
549
  return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
549
550
  }
550
- function Crest({ scope }) {
551
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-crest", style: { backgroundImage: `url(${scope.image})` }, "aria-hidden": "true" });
551
+ function shortSeason(season) {
552
+ return season.replace(/\d{4}/g, (year) => year.slice(2));
552
553
  }
553
- function StatRow({ stats }) {
554
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ss-w-ecard-stats", children: stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-stat", children: [
555
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("b", { children: s.value }),
556
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: s.label })
557
- ] }, s.label)) });
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" });
558
556
  }
559
- function ScopeChips({ scopes }) {
560
- const [active, setActive] = (0, import_react2.useState)(0);
561
- const current = scopes[active] ?? scopes[0];
562
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
563
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ss-w-ecard-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
564
- "button",
565
- {
566
- type: "button",
567
- className: "ss-w-ecard-chip",
568
- "aria-pressed": i === active,
569
- "aria-label": s.label,
570
- title: s.label,
571
- onClick: () => setActive(i),
572
- children: s.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Crest, { scope: s }) : s.label
573
- },
574
- String(s.competitionId ?? "all")
575
- )) }),
576
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { stats: current.stats }, String(current.competitionId ?? "all"))
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
577
563
  ] });
578
564
  }
579
- function CardScopes({ scopes }) {
580
- if (scopes.length === 0) return null;
581
- if (scopes.length === 1) {
582
- const only = scopes[0];
583
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
584
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-ecard-tag", children: [
585
- only.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Crest, { scope: only }) : null,
586
- only.label
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", title: only.label, children: [
584
+ only.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Crest, { image: only.image }) : null,
585
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-lead-txt", children: only.label })
586
+ ] }),
587
+ season
587
588
  ] }),
588
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { stats: only.stats })
589
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRows, { stats: only.stats })
589
590
  ] });
590
591
  }
591
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScopeChips, { scopes });
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
+ ] });
592
613
  }
593
614
  function EntityCardItem({ c }) {
594
- const season = c.scopes.length > 0 ? c.season : void 0;
595
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
596
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-head", children: [
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: [
597
618
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
598
619
  "span",
599
620
  {
@@ -602,17 +623,12 @@ function EntityCardItem({ c }) {
602
623
  children: c.image ? "" : initials(c.name)
603
624
  }
604
625
  ),
605
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-meta", children: [
606
- 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: [
607
627
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-nm", children: c.name }),
608
- c.sub || season ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-sub", children: [
609
- c.sub,
610
- c.sub && season ? " \xB7 " : "",
611
- season ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-season", children: season }) : null
612
- ] }) : null
628
+ c.sub ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-sub", children: c.sub }) : null
613
629
  ] })
614
630
  ] }),
615
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardScopes, { scopes: c.scopes })
631
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardBody, { c })
616
632
  ] });
617
633
  }
618
634
  function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
@@ -731,7 +747,7 @@ function AddToSlipButton({ it, slip, ui }) {
731
747
 
732
748
  // src/components/BettingInsightList.tsx
733
749
  var import_jsx_runtime9 = require("react/jsx-runtime");
734
- function StatRows({ stats }) {
750
+ function StatRows2({ stats }) {
735
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: [
736
752
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-lbl", children: s.label }),
737
753
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-num", children: s.value })
@@ -742,7 +758,7 @@ function ScopedStats({ scopes }) {
742
758
  const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
743
759
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-scoped", children: [
744
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)) }),
745
- /* @__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)
746
762
  ] });
747
763
  }
748
764
  function StatsDisclosure({ it, ui }) {
@@ -764,35 +780,37 @@ function StatsDisclosure({ it, ui }) {
764
780
  ]
765
781
  }
766
782
  ),
767
- 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
768
784
  ] });
769
785
  }
770
786
  function InsightCard({ it, ui, slip }) {
771
787
  const live = it.isLive || it.phase === "live";
772
788
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-binsight", children: [
773
- live || it.fixture || it.competition ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-status", children: [
774
- live ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-live", children: [
775
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
776
- ui?.liveLabel ?? "Live"
777
- ] }) : null,
778
- it.fixture ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-fixture", children: it.fixture }) : null,
779
- it.competition ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-comp", children: it.competition }) : null
780
- ] }) : 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,
781
793
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-head", children: [
782
794
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-mkt", children: [
783
795
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-market", children: it.market }),
784
- 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
785
801
  ] }),
786
- it.odds ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ss-w-bi-odds", children: [
787
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-odds-cap", children: ui?.oddsLabel ?? "Odds" }),
788
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("b", { children: it.odds }),
789
- it.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
790
- ] }) : 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
+ ] })
791
810
  ] }),
792
811
  it.text ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ss-w-bi-text", children: it.text }) : null,
793
812
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatsDisclosure, { it, ui }),
794
- it.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null,
795
- /* @__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
796
814
  ] });
797
815
  }
798
816
  function BettingInsightList({ insights, ui, slip }) {
@@ -811,15 +829,8 @@ function OddsRow({ vm, row, ui, slip }) {
811
829
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
812
830
  ] });
813
831
  }
814
- function formatUpdated(iso) {
815
- if (!iso) return void 0;
816
- const date = new Date(iso);
817
- if (Number.isNaN(date.getTime())) return void 0;
818
- return date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
819
- }
820
832
  function MarketOddsCard({ vm, ui, slip }) {
821
833
  const [open, setOpen] = (0, import_react6.useState)(false);
822
- const updated = formatUpdated(vm.updatedAt);
823
834
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mocard", children: [
824
835
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-head", children: [
825
836
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-market", children: vm.market }),
@@ -837,10 +848,7 @@ function MarketOddsCard({ vm, ui, slip }) {
837
848
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
838
849
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
839
850
  ] }) : null,
840
- vm.bookmaker || updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-foot", children: [
841
- vm.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: vm.bookmaker }) : null,
842
- updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: `${ui?.updatedLabel ?? "updated"} ${updated}` }) : null
843
- ] }) : 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,
844
852
  vm.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
845
853
  ] });
846
854
  }
@@ -999,6 +1007,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
999
1007
  --ss-w-cta: #f5a623; --ss-w-cta-ink: #1b1300;
1000
1008
  --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;
1001
1009
  --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;
1010
+ --ss-w-pos: #1aa563; --ss-w-neg: #e0524b;
1002
1011
  --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;
1003
1012
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
1004
1013
  font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);
@@ -1008,12 +1017,14 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1008
1017
  --ss-w-accent: #37c891; --ss-w-cta: #ffb638; --ss-w-cta-ink: #1b1300;
1009
1018
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
1010
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;
1011
1021
  }
1012
1022
  }
1013
1023
  .ss-w-root[data-ss-w-theme="dark"] {
1014
1024
  --ss-w-accent: #37c891; --ss-w-cta: #ffb638; --ss-w-cta-ink: #1b1300;
1015
1025
  --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;
1016
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;
1017
1028
  }
1018
1029
  .ss-w-root[data-ss-w-theme="light"] {
1019
1030
  --ss-w-accent: #17936a; --ss-w-cta: #f5a623; --ss-w-cta-ink: #1b1300;
@@ -1074,26 +1085,48 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1074
1085
  .ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }
1075
1086
 
1076
1087
  /* entity cards */
1077
- .ss-w-cards { display: flex; flex-direction: column; gap: 7px; }
1078
- .ss-w-ecard { display: flex; flex-direction: column; align-items: stretch; gap: 8px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; }
1079
- .ss-w-ecard-head { display: flex; align-items: center; gap: 11px; }
1080
- .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; }
1081
- .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; }
1082
1097
  .ss-w-ecard[data-kind="player"] .ss-w-thumb { background-color: var(--ss-w-accent); }
1083
- .ss-w-meta { display: flex; flex-direction: column; min-width: 0; }
1084
- .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; }
1085
- .ss-w-nm { font-weight: 660; }
1086
- .ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }
1087
- .ss-w-season { color: var(--ss-w-accent); font-weight: 660; }
1088
- .ss-w-ecard-scopes { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; align-self: flex-start; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 9px; padding: 2px; }
1089
- .ss-w-ecard-chip { display: inline-flex; align-items: center; font-size: 11px; font-weight: 600; border: none; border-radius: 7px; padding: 4px 8px; color: var(--ss-w-faint); background: none; cursor: pointer; }
1090
- .ss-w-ecard-chip[aria-pressed="true"] { color: var(--ss-w-ink); background: var(--ss-w-panel); box-shadow: 0 1px 2px rgba(0,0,0,.08); }
1091
- .ss-w-ecard-crest { width: 16px; height: 16px; display: block; border-radius: 4px; background-size: cover; background-position: center; }
1092
- .ss-w-ecard-tag { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
1093
- .ss-w-ecard-stats { display: flex; gap: 16px; }
1094
- .ss-w-stat { text-align: center; }
1095
- .ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }
1096
- .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; flex-wrap: nowrap; }
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; flex: 1; }
1115
+ .ss-w-ecard-lead-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1116
+ .ss-w-ecard-crest { width: 15px; height: 15px; flex: none; display: block; border-radius: 4px; background-size: cover; background-position: center; }
1117
+ .ss-w-ecard-season { flex: none; 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; }
1118
+ .ss-w-ecard-tabs-static .ss-w-ecard-season { margin-bottom: 4px; }
1119
+
1120
+ .ss-w-ecard-stats { display: flex; flex-direction: column; gap: 4px; }
1121
+ .ss-w-ecard-row { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px; }
1122
+ .ss-w-ecard-rlabel { font-size: 9.5px; text-transform: uppercase; letter-spacing: .045em; color: var(--ss-w-faint); font-weight: 600; }
1123
+ .ss-w-ecard-rval { justify-self: end; font-size: 15px; font-weight: 720; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
1124
+ .ss-w-form { justify-self: end; display: flex; height: 6px; width: 48px; border-radius: 4px; overflow: hidden; gap: 2px; }
1125
+ .ss-w-form span { display: block; border-radius: 2px; }
1126
+ .ss-w-form-w { background: var(--ss-w-pos); }
1127
+ .ss-w-form-d { background: var(--ss-w-faint); }
1128
+ .ss-w-form-l { background: var(--ss-w-neg); }
1129
+
1097
1130
  .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; }
1098
1131
  .ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }
1099
1132
  .ss-w-cards-more:disabled { opacity: .5; cursor: default; }
@@ -1110,7 +1143,12 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1110
1143
  .ss-w-bi-mkt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1111
1144
  .ss-w-bi-market { font-size: 15px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
1112
1145
  .ss-w-bi-sel { font-size: 12px; line-height: 1.3; color: var(--ss-w-faint); }
1113
- .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; }
1146
+ .ss-w-bi-match { display: flex; flex-direction: column; gap: 1px; min-width: 0; margin-top: 5px; }
1147
+ .ss-w-bi-action { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
1148
+ .ss-w-bi-action .ss-w-slip > button { flex: 1; text-align: center; }
1149
+ /* 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. */
1150
+ .ss-w-bi-action .ss-w-toast { left: auto; right: 0; width: max-content; max-width: 210px; white-space: normal; }
1151
+ .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; }
1114
1152
  .ss-w-bi-odds-cap { font-size: 9px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ss-w-faint); }
1115
1153
  .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; }
1116
1154
  .ss-w-bi-book { font-size: 9.5px; color: var(--ss-w-faint); }