@sensiblestats/widget-react 0.2.1 → 0.4.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.d.ts CHANGED
@@ -33,6 +33,6 @@ type StatsWidgetProps = {
33
33
  };
34
34
  declare function StatsWidget(props: StatsWidgetProps): JSX.Element | null;
35
35
 
36
- declare const WIDGET_CSS = ".ss-w-root { all: revert; }\n.ss-w-root, .ss-w-root * { box-sizing: border-box; margin: 0; padding: 0; }\n.ss-w-root {\n --ss-w-accent: #17936a;\n --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;\n --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);\n}\n@media (prefers-color-scheme: dark) {\n .ss-w-root[data-ss-w-theme=\"auto\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n }\n}\n.ss-w-root[data-ss-w-theme=\"dark\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n}\n.ss-w-root[data-ss-w-theme=\"light\"] {\n --ss-w-accent: #17936a; --ss-w-ink: #14181d; --ss-w-faint: #6b7580;\n --ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n}\n\n/* launcher */\n.ss-w-fab-wrap { position: fixed; bottom: var(--ss-w-offset-y); z-index: 2147483000; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-fab-wrap { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-fab-wrap { left: var(--ss-w-offset-x); align-items: flex-start; }\n.ss-w-fab { width: 56px; height: 56px; border: none; border-radius: 50%; background: var(--ss-w-nav); color: #fff; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 20px rgba(0,0,0,.28); }\n.ss-w-fab-mark { width: 30px; height: 30px; display: block; }\n.ss-w-greeting { max-width: 240px; display: flex; align-items: flex-start; background: var(--ss-w-panel); color: var(--ss-w-ink); border: 1px solid var(--ss-w-line); border-radius: 12px; box-shadow: 0 4px 14px rgba(0,0,0,.14); }\n.ss-w-greeting-body { flex: 1; min-width: 0; text-align: left; background: none; border: none; color: inherit; font: inherit; cursor: pointer; padding: 9px 4px 9px 12px; }\n.ss-w-greeting-x { flex: none; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 17px; line-height: 1; padding: 7px 9px 0 2px; }\n\n/* panel */\n.ss-w-panel { position: fixed; bottom: calc(var(--ss-w-offset-y) + 72px); z-index: 2147483000; display: flex; flex-direction: column; width: min(400px, calc(100vw - 40px)); height: min(620px, calc(100vh - 120px)); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: var(--ss-w-radius); overflow: hidden; box-shadow: 0 12px 40px rgba(0,0,0,.24); }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { left: var(--ss-w-offset-x); }\n@media (max-width: 639px) {\n /* Fill via inset with no explicit height (like the site's working panel) so the\n input stays visible when the mobile keyboard/URL-bar shrinks the viewport. The\n position-specific selectors override the right/left offsets set above. */\n .ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel,\n .ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { inset: 0; width: auto; height: auto; border: none; border-radius: 0; }\n}\n.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }\n.ss-w-header-mark { width: 18px; height: 18px; display: block; }\n.ss-w-header-title { font-weight: 660; }\n.ss-w-close { margin-left: auto; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }\n\n/* messages */\n.ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }\n.ss-w-msg { display: flex; }\n.ss-w-user { justify-content: flex-end; }\n.ss-w-user .ss-w-bubble { background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 14px; padding: 8px 12px; max-width: 80%; }\n.ss-w-bot { flex-direction: column; gap: 8px; }\n.ss-w-status { font-size: 11px; color: var(--ss-w-faint); font-style: italic; }\n.ss-w-error { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #c0392b; }\n.ss-w-retry { background: none; border: 1px solid currentColor; border-radius: 10px; padding: 2px 8px; cursor: pointer; color: inherit; }\n\n/* markdown */\n.ss-w-md { display: flex; flex-direction: column; gap: 8px; }\n.ss-w-md :where(p, ul, ol, table, pre, blockquote, h1, h2, h3, h4) { margin: 0; }\n.ss-w-md > *:first-child { margin-top: 0; }\n.ss-w-md > *:last-child { margin-bottom: 0; }\n.ss-w-md-ul { list-style: disc outside; padding-left: 18px; }\n.ss-w-md-ol { list-style: decimal outside; padding-left: 18px; }\n.ss-w-md-ul li::marker, .ss-w-md-ol li::marker { color: var(--ss-w-accent); }\n.ss-w-md-link { color: var(--ss-w-accent); text-decoration: underline; }\n.ss-w-table-wrap { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; }\n.ss-w-md-table { border-collapse: collapse; width: max-content; min-width: 100%; font-variant-numeric: tabular-nums; }\n.ss-w-md-table td, .ss-w-md-table th { border: 1px solid var(--ss-w-line); padding: 4px 8px; text-align: left; white-space: nowrap; }\n.ss-w-md-table th { background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); font-weight: 700; }\n.ss-w-md-table tbody tr:nth-child(even) { background: var(--ss-w-bubble); }\n.ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }\n\n/* entity cards */\n.ss-w-cards { display: flex; flex-direction: column; gap: 7px; }\n.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; cursor: pointer; }\n.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; }\n.ss-w-thumb.ss-w-team { border-radius: 10px; }\n.ss-w-ecard[data-kind=\"player\"] .ss-w-thumb { background-color: var(--ss-w-accent); }\n.ss-w-meta { display: flex; flex-direction: column; min-width: 0; }\n.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; }\n.ss-w-nm { font-weight: 660; }\n.ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }\n.ss-w-stat { margin-left: auto; text-align: center; }\n.ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }\n.ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.ss-w-chev { color: var(--ss-w-faint); font-size: 16px; display: grid; place-items: center; }\n.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; }\n.ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }\n.ss-w-ecard:disabled, .ss-w-cards-more:disabled { opacity: .5; cursor: default; }\n\n/* action buttons */\n.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n.ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }\n.ss-w-abtn:disabled { opacity: .5; cursor: default; }\n.ss-w-abtn-shimmer { width: 140px; height: 30px; border-radius: 16px; background: linear-gradient(90deg, var(--ss-w-bubble), var(--ss-w-line), var(--ss-w-bubble)); background-size: 200% 100%; animation: ss-w-shimmer 1.2s infinite; }\n@keyframes ss-w-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n\n/* intent chips */\n.ss-w-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.ss-w-chip { font-size: 11.5px; font-weight: 550; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 14px; padding: 5px 11px; cursor: pointer; }\n\n/* input */\n.ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }\n.ss-w-textarea { flex: 1; resize: none; border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; font: inherit; color: inherit; background: var(--ss-w-panel); max-height: 96px; }\n.ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }\n.ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }\n.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }\n\n@media (prefers-reduced-motion: reduce) {\n .ss-w-abtn-shimmer { animation: none; }\n}\n";
36
+ declare const WIDGET_CSS = ".ss-w-root { all: revert; }\n.ss-w-root, .ss-w-root * { box-sizing: border-box; margin: 0; padding: 0; }\n.ss-w-root {\n --ss-w-accent: #17936a;\n --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;\n --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);\n}\n@media (prefers-color-scheme: dark) {\n .ss-w-root[data-ss-w-theme=\"auto\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n }\n}\n.ss-w-root[data-ss-w-theme=\"dark\"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n}\n.ss-w-root[data-ss-w-theme=\"light\"] {\n --ss-w-accent: #17936a; --ss-w-ink: #14181d; --ss-w-faint: #6b7580;\n --ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n}\n\n/* launcher */\n.ss-w-fab-wrap { position: fixed; bottom: var(--ss-w-offset-y); z-index: 2147483000; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-fab-wrap { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-fab-wrap { left: var(--ss-w-offset-x); align-items: flex-start; }\n.ss-w-fab { width: 56px; height: 56px; border: none; border-radius: 50%; background: var(--ss-w-nav); color: #fff; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 20px rgba(0,0,0,.28); }\n.ss-w-fab-mark { width: 30px; height: 30px; display: block; }\n.ss-w-greeting { max-width: 240px; display: flex; align-items: flex-start; background: var(--ss-w-panel); color: var(--ss-w-ink); border: 1px solid var(--ss-w-line); border-radius: 12px; box-shadow: 0 4px 14px rgba(0,0,0,.14); }\n.ss-w-greeting-body { flex: 1; min-width: 0; text-align: left; background: none; border: none; color: inherit; font: inherit; cursor: pointer; padding: 9px 4px 9px 12px; }\n.ss-w-greeting-x { flex: none; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 17px; line-height: 1; padding: 7px 9px 0 2px; }\n\n/* panel */\n.ss-w-panel { position: fixed; bottom: calc(var(--ss-w-offset-y) + 72px); z-index: 2147483000; display: flex; flex-direction: column; width: min(400px, calc(100vw - 40px)); height: min(620px, calc(100vh - 120px)); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: var(--ss-w-radius); overflow: hidden; box-shadow: 0 12px 40px rgba(0,0,0,.24); }\n.ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { left: var(--ss-w-offset-x); }\n@media (max-width: 639px) {\n /* Fill via inset with no explicit height (like the site's working panel) so the\n input stays visible when the mobile keyboard/URL-bar shrinks the viewport. The\n position-specific selectors override the right/left offsets set above. */\n .ss-w-root[data-ss-w-pos=\"right\"] .ss-w-panel,\n .ss-w-root[data-ss-w-pos=\"left\"] .ss-w-panel { inset: 0; width: auto; height: auto; border: none; border-radius: 0; }\n}\n.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }\n.ss-w-header-mark { width: 18px; height: 18px; display: block; }\n.ss-w-header-title { font-weight: 660; }\n.ss-w-close { margin-left: auto; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }\n\n/* messages */\n.ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }\n.ss-w-msg { display: flex; }\n.ss-w-user { justify-content: flex-end; }\n.ss-w-user .ss-w-bubble { background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 14px; padding: 8px 12px; max-width: 80%; }\n.ss-w-bot { flex-direction: column; gap: 8px; }\n.ss-w-status { font-size: 11px; color: var(--ss-w-faint); font-style: italic; }\n.ss-w-error { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #c0392b; }\n.ss-w-retry { background: none; border: 1px solid currentColor; border-radius: 10px; padding: 2px 8px; cursor: pointer; color: inherit; }\n\n/* markdown */\n.ss-w-md { display: flex; flex-direction: column; gap: 8px; }\n.ss-w-md :where(p, ul, ol, table, pre, blockquote, h1, h2, h3, h4) { margin: 0; }\n.ss-w-md > *:first-child { margin-top: 0; }\n.ss-w-md > *:last-child { margin-bottom: 0; }\n.ss-w-md-ul { list-style: disc outside; padding-left: 18px; }\n.ss-w-md-ol { list-style: decimal outside; padding-left: 18px; }\n.ss-w-md-ul li::marker, .ss-w-md-ol li::marker { color: var(--ss-w-accent); }\n.ss-w-md-link { color: var(--ss-w-accent); text-decoration: underline; }\n.ss-w-table-wrap { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; }\n.ss-w-md-table { border-collapse: collapse; width: max-content; min-width: 100%; font-variant-numeric: tabular-nums; }\n.ss-w-md-table td, .ss-w-md-table th { border: 1px solid var(--ss-w-line); padding: 4px 8px; text-align: left; white-space: nowrap; }\n.ss-w-md-table th { background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); font-weight: 700; }\n.ss-w-md-table tbody tr:nth-child(even) { background: var(--ss-w-bubble); }\n.ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }\n\n/* entity cards */\n.ss-w-cards { display: flex; flex-direction: column; gap: 7px; }\n.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; }\n.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; }\n.ss-w-thumb.ss-w-team { border-radius: 10px; }\n.ss-w-ecard[data-kind=\"player\"] .ss-w-thumb { background-color: var(--ss-w-accent); }\n.ss-w-meta { display: flex; flex-direction: column; min-width: 0; }\n.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; }\n.ss-w-nm { font-weight: 660; }\n.ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }\n.ss-w-stat { margin-left: auto; text-align: center; }\n.ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }\n.ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.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; }\n.ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }\n.ss-w-cards-more:disabled { opacity: .5; cursor: default; }\n\n/* betting insights */\n.ss-w-binsights { display: flex; flex-direction: column; gap: 7px; }\n.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; }\n.ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }\n.ss-w-bi-mkt { display: flex; flex-direction: column; min-width: 0; }\n.ss-w-bi-market { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }\n.ss-w-bi-sel { font-weight: 660; }\n.ss-w-bi-odds { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; }\n.ss-w-bi-odds b { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }\n.ss-w-bi-book { font-size: 10px; color: var(--ss-w-faint); }\n.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; }\n.ss-w-bi-text { font-size: 12px; color: var(--ss-w-ink); }\n.ss-w-bi-stats { display: flex; flex-wrap: wrap; gap: 12px; }\n.ss-w-bi-stat b { display: block; font-variant-numeric: tabular-nums; font-weight: 660; }\n.ss-w-bi-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.ss-w-bi-disc { font-size: 10px; font-style: italic; color: var(--ss-w-faint); }\n\n/* action buttons */\n.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n.ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }\n.ss-w-abtn:disabled { opacity: .5; cursor: default; }\n.ss-w-abtn-shimmer { width: 140px; height: 30px; border-radius: 16px; background: linear-gradient(90deg, var(--ss-w-bubble), var(--ss-w-line), var(--ss-w-bubble)); background-size: 200% 100%; animation: ss-w-shimmer 1.2s infinite; }\n@keyframes ss-w-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n\n/* intent chips */\n.ss-w-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.ss-w-chip { font-size: 11.5px; font-weight: 550; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 14px; padding: 5px 11px; cursor: pointer; }\n\n/* input */\n.ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }\n.ss-w-textarea { flex: 1; resize: none; border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; font: inherit; color: inherit; background: var(--ss-w-panel); max-height: 96px; }\n.ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }\n/* iOS Safari zooms the page when a focused field's font-size is < 16px. Bump it\n on touch devices only, so the desktop 13px design is unchanged. */\n@media (pointer: coarse) {\n .ss-w-textarea { font-size: 16px; }\n}\n.ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }\n.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }\n\n@media (prefers-reduced-motion: reduce) {\n .ss-w-abtn-shimmer { animation: none; }\n}\n";
37
37
 
38
38
  export { StatsWidget, type StatsWidgetConfig, type StatsWidgetProps, WIDGET_CSS };
package/dist/index.js CHANGED
@@ -95,16 +95,19 @@ function str(v) {
95
95
  function num(v) {
96
96
  return typeof v === "number" && Number.isFinite(v) ? String(v) : void 0;
97
97
  }
98
- function readStats(card) {
99
- if (!Array.isArray(card.stats)) return [];
98
+ function readStatCells(raw) {
99
+ if (!Array.isArray(raw)) return [];
100
100
  const cells = [];
101
- for (const entry of card.stats) {
101
+ for (const entry of raw) {
102
102
  const label = str(entry.label);
103
103
  const value = num(entry.value) ?? str(entry.value);
104
104
  if (label !== void 0 && value !== void 0) cells.push({ label, value });
105
105
  }
106
106
  return cells;
107
107
  }
108
+ function readStats(card) {
109
+ return readStatCells(card.stats);
110
+ }
108
111
  function toCardVM(card) {
109
112
  const entityType = str(card.entityType)?.toLowerCase();
110
113
  const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
@@ -114,8 +117,7 @@ function toCardVM(card) {
114
117
  name: card.canonicalName,
115
118
  image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
116
119
  sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
117
- stats: readStats(card),
118
- query: card.canonicalName
120
+ stats: readStats(card)
119
121
  };
120
122
  }
121
123
  function toActionVM(action) {
@@ -131,6 +133,24 @@ function toChipVM(intent) {
131
133
  function chipFromText(text) {
132
134
  return { text };
133
135
  }
136
+ function toInsightVM(insight) {
137
+ const market = str(insight.marketLabel) ?? str(insight.marketDeveloperName) ?? "";
138
+ const selection = [str(insight.selectionLabel), str(insight.line)].filter(Boolean).join(" ") || void 0;
139
+ const value = insight.odds?.value;
140
+ const odds = typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : void 0;
141
+ return {
142
+ id: str(insight.insightId) ?? `${str(insight.ruleId) ?? "insight"}:${num(insight.matchId) ?? ""}`,
143
+ market,
144
+ selection,
145
+ odds,
146
+ // The wire payload carries bookmakerId (not a name); render a name only if one is present.
147
+ bookmaker: str(insight.odds?.bookmakerName),
148
+ isLive: insight.odds?.isLive === true,
149
+ text: str(insight.text) ?? "",
150
+ stats: readStatCells(insight.stats),
151
+ disclaimer: str(insight.disclaimer)
152
+ };
153
+ }
134
154
 
135
155
  // src/reducer.ts
136
156
  var POPUP_MAX = 160;
@@ -157,6 +177,8 @@ function applyEvent(state, event) {
157
177
  return { ...state, messages: mapActive(state, (t) => ({ ...t, cards: [...t.cards, toCardVM(event.data.card)] })) };
158
178
  case "action_button":
159
179
  return { ...state, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, actions: [...t.actions, toActionVM(event.data.action)] })) };
180
+ case "betting_insight":
181
+ return { ...state, messages: mapActive(state, (t) => ({ ...t, insights: [...t.insights, toInsightVM(event.data.insight)] })) };
160
182
  case "intent_chip": {
161
183
  const chip = toChipVM(event.data.intent);
162
184
  return chip ? { ...state, starters: [...state.starters, chip] } : state;
@@ -174,6 +196,7 @@ function applyEvent(state, event) {
174
196
  return { ...state, error: suppress ? state.error : event.data, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
175
197
  }
176
198
  }
199
+ return state;
177
200
  }
178
201
  function reducer(state, action) {
179
202
  switch (action.kind) {
@@ -183,11 +206,11 @@ function reducer(state, action) {
183
206
  return { ...state, isOpen: false, isStreaming: false, status: null, actionsLoading: false };
184
207
  case "USER_SENT": {
185
208
  const user = { role: "user", id: action.userId, text: action.displayText ?? action.input.message };
186
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], done: false };
209
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false };
187
210
  return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input, lastDisplayText: action.displayText ?? null };
188
211
  }
189
212
  case "GREETING_SENT": {
190
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], done: false, isGreeting: true };
213
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false, isGreeting: true };
191
214
  return { ...state, messages: [...state.messages, assistant], isStreaming: true, status: null, error: null };
192
215
  }
193
216
  case "DISMISS_POPUP":
@@ -288,9 +311,6 @@ function SendIcon() {
288
311
  function CloseIcon() {
289
312
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2" }) });
290
313
  }
291
- function ChevronIcon() {
292
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", stroke: "currentColor", strokeWidth: "2", d: "M9 6l6 6-6 6" }) });
293
- }
294
314
 
295
315
  // src/components/GreetingBubble.tsx
296
316
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
@@ -349,13 +369,13 @@ var MAX_VISIBLE = 3;
349
369
  function initials(name) {
350
370
  return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
351
371
  }
352
- function EntityCardList({ cards, disabled, onSelect, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
372
+ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
353
373
  const [expanded, setExpanded] = useState(false);
354
374
  if (cards.length === 0) return null;
355
375
  const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
356
376
  const overflow = cards.length - MAX_VISIBLE;
357
377
  return /* @__PURE__ */ jsxs4("div", { className: "ss-w-cards", children: [
358
- visible.map((c) => /* @__PURE__ */ jsxs4("button", { type: "button", className: "ss-w-ecard", "data-kind": c.kind, disabled, onClick: () => onSelect(c.query), children: [
378
+ visible.map((c) => /* @__PURE__ */ jsxs4("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
359
379
  /* @__PURE__ */ jsx6(
360
380
  "span",
361
381
  {
@@ -372,82 +392,125 @@ function EntityCardList({ cards, disabled, onSelect, showLessLabel = "Show less"
372
392
  c.stats.map((s) => /* @__PURE__ */ jsxs4("span", { className: "ss-w-stat", children: [
373
393
  /* @__PURE__ */ jsx6("b", { children: s.value }),
374
394
  /* @__PURE__ */ jsx6("span", { children: s.label })
375
- ] }, s.label)),
376
- /* @__PURE__ */ jsx6("span", { className: "ss-w-chev", children: /* @__PURE__ */ jsx6(ChevronIcon, {}) })
395
+ ] }, s.label))
377
396
  ] }, c.id)),
378
397
  overflow > 0 ? /* @__PURE__ */ jsx6("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
379
398
  ] });
380
399
  }
381
400
 
401
+ // src/components/BettingInsightList.tsx
402
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
403
+ function BettingInsightList({ insights }) {
404
+ if (!insights || insights.length === 0) return null;
405
+ return /* @__PURE__ */ jsx7("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ jsxs5("div", { className: "ss-w-binsight", children: [
406
+ /* @__PURE__ */ jsxs5("div", { className: "ss-w-bi-head", children: [
407
+ /* @__PURE__ */ jsxs5("span", { className: "ss-w-bi-mkt", children: [
408
+ /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-market", children: it.market }),
409
+ it.selection ? /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-sel", children: it.selection }) : null
410
+ ] }),
411
+ it.odds ? /* @__PURE__ */ jsxs5("span", { className: "ss-w-bi-odds", children: [
412
+ it.isLive ? /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-live", children: "Live" }) : null,
413
+ /* @__PURE__ */ jsx7("b", { children: it.odds }),
414
+ it.bookmaker ? /* @__PURE__ */ jsx7("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
415
+ ] }) : null
416
+ ] }),
417
+ it.text ? /* @__PURE__ */ jsx7("p", { className: "ss-w-bi-text", children: it.text }) : null,
418
+ it.stats.length > 0 ? /* @__PURE__ */ jsx7("div", { className: "ss-w-bi-stats", children: it.stats.map((s) => /* @__PURE__ */ jsxs5("span", { className: "ss-w-bi-stat", children: [
419
+ /* @__PURE__ */ jsx7("b", { children: s.value }),
420
+ /* @__PURE__ */ jsx7("span", { children: s.label })
421
+ ] }, `${s.label}:${s.value}`)) }) : null,
422
+ it.disclaimer ? /* @__PURE__ */ jsx7("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
423
+ ] }, it.id)) });
424
+ }
425
+
382
426
  // src/components/ActionButtons.tsx
383
- import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
427
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
384
428
  function ActionButtons({ actions, loading, disabled, onAct }) {
385
429
  if (actions.length === 0 && !loading) return null;
386
- return /* @__PURE__ */ jsxs5("div", { className: "ss-w-actions", children: [
387
- actions.map((a, i) => /* @__PURE__ */ jsx7("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
388
- actions.length === 0 && loading ? /* @__PURE__ */ jsxs5(Fragment, { children: [
389
- /* @__PURE__ */ jsx7("span", { className: "ss-w-abtn-shimmer" }),
390
- /* @__PURE__ */ jsx7("span", { className: "ss-w-abtn-shimmer" })
430
+ return /* @__PURE__ */ jsxs6("div", { className: "ss-w-actions", children: [
431
+ actions.map((a, i) => /* @__PURE__ */ jsx8("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
432
+ actions.length === 0 && loading ? /* @__PURE__ */ jsxs6(Fragment, { children: [
433
+ /* @__PURE__ */ jsx8("span", { className: "ss-w-abtn-shimmer" }),
434
+ /* @__PURE__ */ jsx8("span", { className: "ss-w-abtn-shimmer" })
391
435
  ] }) : null
392
436
  ] });
393
437
  }
394
438
 
395
439
  // src/components/ChatMessage.tsx
396
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
397
- function ChatMessage({ message, disabled, onAct, onSelect, ui }) {
440
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
441
+ function ChatMessage({ message, disabled, onAct, ui }) {
398
442
  if (message.role === "user") {
399
- return /* @__PURE__ */ jsx8("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx8("div", { className: "ss-w-bubble", children: message.text }) });
443
+ return /* @__PURE__ */ jsx9("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx9("div", { className: "ss-w-bubble", children: message.text }) });
400
444
  }
401
- return /* @__PURE__ */ jsxs6("div", { className: "ss-w-msg ss-w-bot", children: [
402
- message.text ? /* @__PURE__ */ jsx8(Markdown, { text: message.text }) : null,
403
- /* @__PURE__ */ jsx8(EntityCardList, { cards: message.cards, disabled, onSelect, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
404
- /* @__PURE__ */ jsx8(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
445
+ return /* @__PURE__ */ jsxs7("div", { className: "ss-w-msg ss-w-bot", children: [
446
+ message.text ? /* @__PURE__ */ jsx9(Markdown, { text: message.text }) : null,
447
+ /* @__PURE__ */ jsx9(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
448
+ /* @__PURE__ */ jsx9(BettingInsightList, { insights: message.insights }),
449
+ /* @__PURE__ */ jsx9(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
405
450
  ] });
406
451
  }
407
452
 
408
453
  // src/components/IntentChips.tsx
409
- import { jsx as jsx9 } from "react/jsx-runtime";
454
+ import { jsx as jsx10 } from "react/jsx-runtime";
410
455
  function IntentChips({ chips, onPick }) {
411
456
  if (chips.length === 0) return null;
412
- return /* @__PURE__ */ jsx9("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx9("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
457
+ return /* @__PURE__ */ jsx10("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
458
+ }
459
+
460
+ // src/scroll.ts
461
+ var PIN_THRESHOLD = 48;
462
+ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
463
+ return el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;
413
464
  }
414
465
 
415
466
  // src/components/MessageList.tsx
416
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
467
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
417
468
  function MessageList({ controller, ui }) {
418
469
  const { state, send } = controller;
470
+ const listRef = useRef2(null);
419
471
  const endRef = useRef2(null);
472
+ const pinnedRef = useRef2(true);
420
473
  useEffect2(() => {
421
- endRef.current?.scrollIntoView({ block: "end" });
474
+ if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
422
475
  }, [state.messages, state.status]);
423
476
  const empty = state.messages.length === 0;
424
- return /* @__PURE__ */ jsxs7("div", { className: "ss-w-list", "aria-live": "polite", children: [
425
- state.messages.map((m) => /* @__PURE__ */ jsx10(
426
- ChatMessage,
427
- {
428
- message: m,
429
- disabled: state.isStreaming,
430
- onAct: (a) => send(a.message, { actionId: a.actionId, displayText: a.label }),
431
- onSelect: (q) => send(q),
432
- ui
477
+ return /* @__PURE__ */ jsxs8(
478
+ "div",
479
+ {
480
+ className: "ss-w-list",
481
+ "aria-live": "polite",
482
+ ref: listRef,
483
+ onScroll: () => {
484
+ if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
433
485
  },
434
- m.id
435
- )),
436
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx10(ActionButtons, { actions: [], loading: true, onAct: () => {
437
- } }) : null,
438
- state.status ? /* @__PURE__ */ jsx10("div", { className: "ss-w-status", children: state.status }) : null,
439
- state.error ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-error", role: "alert", children: [
440
- /* @__PURE__ */ jsx10("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
441
- /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
442
- ] }) : null,
443
- empty ? /* @__PURE__ */ jsx10(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
444
- /* @__PURE__ */ jsx10("div", { ref: endRef })
445
- ] });
486
+ children: [
487
+ state.messages.map((m) => /* @__PURE__ */ jsx11(
488
+ ChatMessage,
489
+ {
490
+ message: m,
491
+ disabled: state.isStreaming,
492
+ onAct: (a) => send(a.message, { actionId: a.actionId, displayText: a.label }),
493
+ ui
494
+ },
495
+ m.id
496
+ )),
497
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx11(ActionButtons, { actions: [], loading: true, onAct: () => {
498
+ } }) : null,
499
+ state.status ? /* @__PURE__ */ jsx11("div", { className: "ss-w-status", children: state.status }) : null,
500
+ state.error ? /* @__PURE__ */ jsxs8("div", { className: "ss-w-error", role: "alert", children: [
501
+ /* @__PURE__ */ jsx11("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
502
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
503
+ ] }) : null,
504
+ empty ? /* @__PURE__ */ jsx11(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
505
+ /* @__PURE__ */ jsx11("div", { ref: endRef })
506
+ ]
507
+ }
508
+ );
446
509
  }
447
510
 
448
511
  // src/components/ChatInput.tsx
449
512
  import { useState as useState2 } from "react";
450
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
513
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
451
514
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
452
515
  const [value, setValue] = useState2("");
453
516
  const submit = () => {
@@ -462,14 +525,14 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
462
525
  submit();
463
526
  }
464
527
  };
465
- return /* @__PURE__ */ jsxs8("div", { className: "ss-w-input", children: [
466
- /* @__PURE__ */ jsx11("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
467
- streaming ? /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx11("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx11(SendIcon, {}) })
528
+ return /* @__PURE__ */ jsxs9("div", { className: "ss-w-input", children: [
529
+ /* @__PURE__ */ jsx12("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
530
+ streaming ? /* @__PURE__ */ jsx12("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx12("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx12("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx12(SendIcon, {}) })
468
531
  ] });
469
532
  }
470
533
 
471
534
  // src/components/ChatPanel.tsx
472
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
535
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
473
536
  var FULLSCREEN_QUERY = "(max-width: 639px)";
474
537
  function useIsModal() {
475
538
  const [modal, setModal] = useState3(false);
@@ -493,10 +556,10 @@ function ChatPanel({ controller, ui }) {
493
556
  window.addEventListener("keydown", onKey);
494
557
  return () => window.removeEventListener("keydown", onKey);
495
558
  }, [close]);
496
- return /* @__PURE__ */ jsxs9("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
497
- /* @__PURE__ */ jsx12(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
498
- /* @__PURE__ */ jsx12(MessageList, { controller, ui }),
499
- /* @__PURE__ */ jsx12(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
559
+ return /* @__PURE__ */ jsxs10("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
560
+ /* @__PURE__ */ jsx13(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
561
+ /* @__PURE__ */ jsx13(MessageList, { controller, ui }),
562
+ /* @__PURE__ */ jsx13(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
500
563
  ] });
501
564
  }
502
565
 
@@ -580,7 +643,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
580
643
 
581
644
  /* entity cards */
582
645
  .ss-w-cards { display: flex; flex-direction: column; gap: 7px; }
583
- .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; cursor: pointer; }
646
+ .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; }
584
647
  .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; }
585
648
  .ss-w-thumb.ss-w-team { border-radius: 10px; }
586
649
  .ss-w-ecard[data-kind="player"] .ss-w-thumb { background-color: var(--ss-w-accent); }
@@ -591,10 +654,26 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
591
654
  .ss-w-stat { margin-left: auto; text-align: center; }
592
655
  .ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }
593
656
  .ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }
594
- .ss-w-chev { color: var(--ss-w-faint); font-size: 16px; display: grid; place-items: center; }
595
657
  .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; }
596
658
  .ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }
597
- .ss-w-ecard:disabled, .ss-w-cards-more:disabled { opacity: .5; cursor: default; }
659
+ .ss-w-cards-more:disabled { opacity: .5; cursor: default; }
660
+
661
+ /* betting insights */
662
+ .ss-w-binsights { display: flex; flex-direction: column; gap: 7px; }
663
+ .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; }
664
+ .ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }
665
+ .ss-w-bi-mkt { display: flex; flex-direction: column; min-width: 0; }
666
+ .ss-w-bi-market { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }
667
+ .ss-w-bi-sel { font-weight: 660; }
668
+ .ss-w-bi-odds { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; }
669
+ .ss-w-bi-odds b { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }
670
+ .ss-w-bi-book { font-size: 10px; color: var(--ss-w-faint); }
671
+ .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; }
672
+ .ss-w-bi-text { font-size: 12px; color: var(--ss-w-ink); }
673
+ .ss-w-bi-stats { display: flex; flex-wrap: wrap; gap: 12px; }
674
+ .ss-w-bi-stat b { display: block; font-variant-numeric: tabular-nums; font-weight: 660; }
675
+ .ss-w-bi-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }
676
+ .ss-w-bi-disc { font-size: 10px; font-style: italic; color: var(--ss-w-faint); }
598
677
 
599
678
  /* action buttons */
600
679
  .ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
@@ -611,6 +690,11 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
611
690
  .ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }
612
691
  .ss-w-textarea { flex: 1; resize: none; border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; font: inherit; color: inherit; background: var(--ss-w-panel); max-height: 96px; }
613
692
  .ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }
693
+ /* iOS Safari zooms the page when a focused field's font-size is < 16px. Bump it
694
+ on touch devices only, so the desktop 13px design is unchanged. */
695
+ @media (pointer: coarse) {
696
+ .ss-w-textarea { font-size: 16px; }
697
+ }
614
698
  .ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }
615
699
  .ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }
616
700
 
@@ -620,7 +704,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
620
704
  `;
621
705
 
622
706
  // src/StatsWidget.tsx
623
- import { jsx as jsx13 } from "react/jsx-runtime";
707
+ import { jsx as jsx14 } from "react/jsx-runtime";
624
708
  function buildClient(props) {
625
709
  try {
626
710
  if ("client" in props && props.client) {
@@ -656,7 +740,7 @@ function StatsWidget(props) {
656
740
  }, [built, ui.injectStyles]);
657
741
  if (!built) return null;
658
742
  const attrs = themeAttrs(ui);
659
- return /* @__PURE__ */ jsx13("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__ */ jsx13(ChatPanel, { controller, ui }) : /* @__PURE__ */ jsx13(
743
+ return /* @__PURE__ */ jsx14("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__ */ jsx14(ChatPanel, { controller, ui }) : /* @__PURE__ */ jsx14(
660
744
  ChatFab,
661
745
  {
662
746
  label: ui.launcherLabel,