@sensiblestats/widget-react 0.49.0 → 0.50.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/StatsWidget.tsx
2
- import { useCallback as useCallback8, useEffect as useEffect14, useMemo as useMemo3, useRef as useRef11, useState as useState25 } from "react";
2
+ import { useCallback as useCallback9, useEffect as useEffect15, useMemo as useMemo3, useRef as useRef13, useState as useState33 } from "react";
3
3
  import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
4
4
 
5
5
  // src/kickoff.ts
@@ -1871,7 +1871,7 @@ function ChatFab({ label, greeting, popupText, logoUrl, onOpen, onDismissPopup,
1871
1871
  }
1872
1872
 
1873
1873
  // src/components/ChatPanel.tsx
1874
- import { useEffect as useEffect13, useState as useState24 } from "react";
1874
+ import { useEffect as useEffect14, useState as useState32 } from "react";
1875
1875
 
1876
1876
  // src/components/ChatHeader.tsx
1877
1877
  import { useEffect as useEffect8, useRef as useRef8, useState as useState8 } from "react";
@@ -3974,7 +3974,7 @@ function TodayPanel({ discovery, ui, monitor }) {
3974
3974
  }
3975
3975
 
3976
3976
  // src/components/GamesPanel.tsx
3977
- import { useState as useState22 } from "react";
3977
+ import { useState as useState30 } from "react";
3978
3978
 
3979
3979
  // src/components/GamesLoginWall.tsx
3980
3980
  import { jsx as jsx52 } from "react/jsx-runtime";
@@ -4086,64 +4086,1385 @@ function GamesHub({
4086
4086
  }
4087
4087
 
4088
4088
  // src/components/GameScreenHost.tsx
4089
- import { jsx as jsx55, jsxs as jsxs49 } from "react/jsx-runtime";
4090
- function resolveScreen(gameId) {
4091
- switch (gameId) {
4092
- // D3/D4 cases go here
4093
- default:
4094
- return /* @__PURE__ */ jsx55("div", { className: "ss-w-gscreen-body", "data-testid": `game-screen-${gameId}`, children: gameId });
4089
+ import { useEffect as useEffect13, useRef as useRef12, useState as useState29 } from "react";
4090
+
4091
+ // src/useGamePlay.ts
4092
+ import { useCallback as useCallback8, useRef as useRef11, useState as useState22 } from "react";
4093
+ function toSubmitResult(payload) {
4094
+ return {
4095
+ // 'pending' means the play is recorded but awaiting resolution (fixture games)
4096
+ alreadyPlayed: payload.status === "played" || payload.status === "graded" || payload.status === "pending",
4097
+ score: payload.score,
4098
+ isCorrect: payload.isCorrect,
4099
+ clientStateJson: payload.clientStateJson,
4100
+ attemptsUsed: payload.attemptsUsed
4101
+ };
4102
+ }
4103
+ function useGamePlay(conversation, { gameId, gameDay, fixtureId, refresh }) {
4104
+ const [result, setResult] = useState22(null);
4105
+ const [submitting, setSubmitting] = useState22(false);
4106
+ const [rawStatus, setRawStatus] = useState22(null);
4107
+ const inFlightRef = useRef11(false);
4108
+ const convRef = useRef11(conversation);
4109
+ convRef.current = conversation;
4110
+ const optsRef = useRef11({ gameId, gameDay, fixtureId, refresh });
4111
+ optsRef.current = { gameId, gameDay, fixtureId, refresh };
4112
+ const submit = useCallback8(async (answerJson) => {
4113
+ const conv = convRef.current;
4114
+ if (!conv || inFlightRef.current) return;
4115
+ inFlightRef.current = true;
4116
+ setSubmitting(true);
4117
+ try {
4118
+ const opts = optsRef.current;
4119
+ const playResult = await conv.postPlay(opts.gameId, answerJson, opts.gameDay, opts.fixtureId);
4120
+ if (playResult !== null) {
4121
+ setResult(playResult);
4122
+ if (playResult.pointsDelta != null) {
4123
+ opts.refresh?.();
4124
+ }
4125
+ }
4126
+ } catch {
4127
+ } finally {
4128
+ inFlightRef.current = false;
4129
+ setSubmitting(false);
4130
+ }
4131
+ }, []);
4132
+ const hydrate = useCallback8(() => {
4133
+ const conv = convRef.current;
4134
+ if (!conv) return;
4135
+ const opts = optsRef.current;
4136
+ void (async () => {
4137
+ try {
4138
+ const payload = await conv.getGameResult(opts.gameId, opts.fixtureId, opts.gameDay);
4139
+ if (payload !== null) {
4140
+ setResult(toSubmitResult(payload));
4141
+ setRawStatus(payload.status);
4142
+ }
4143
+ } catch {
4144
+ }
4145
+ })();
4146
+ }, []);
4147
+ return { result, submitting, submit, hydrate, rawStatus };
4148
+ }
4149
+
4150
+ // src/components/games/MysteryTigerGame.tsx
4151
+ import { useState as useState23, useId as useId2 } from "react";
4152
+ import { Fragment as Fragment9, jsx as jsx55, jsxs as jsxs49 } from "react/jsx-runtime";
4153
+ function parseClientState(json) {
4154
+ if (!json) return null;
4155
+ try {
4156
+ return JSON.parse(json);
4157
+ } catch {
4158
+ return null;
4159
+ }
4160
+ }
4161
+ var ATTR_LABELS = {
4162
+ position: "Pos",
4163
+ nationality: "Nat",
4164
+ age: "Age",
4165
+ shirtNumber: "Kit",
4166
+ apps: "Apps"
4167
+ };
4168
+ var ATTR_KEYS = [
4169
+ "position",
4170
+ "nationality",
4171
+ "age",
4172
+ "shirtNumber",
4173
+ "apps"
4174
+ ];
4175
+ function MysteryTigerGame({
4176
+ config,
4177
+ result,
4178
+ submitting,
4179
+ onSubmit
4180
+ }) {
4181
+ const [query, setQuery] = useState23("");
4182
+ const inputId = useId2();
4183
+ const clientState = parseClientState(result?.clientStateJson);
4184
+ const alreadyGuessedIds = new Set(
4185
+ clientState?.guessResults.map((g) => g.playerId) ?? []
4186
+ );
4187
+ const isOver = result?.alreadyPlayed === true || clientState?.solved === true || clientState?.failed === true;
4188
+ const filtered = config && !isOver ? config.squadPlayers.filter(
4189
+ (p) => !alreadyGuessedIds.has(p.id) && p.name.toLowerCase().includes(query.toLowerCase())
4190
+ ) : [];
4191
+ function handleSelect(playerId) {
4192
+ if (!config || isOver || submitting) return;
4193
+ const prevGuesses = clientState?.guessResults.map((g) => g.playerId) ?? [];
4194
+ const newGuesses = [...prevGuesses, playerId];
4195
+ setQuery("");
4196
+ onSubmit(JSON.stringify({ guesses: newGuesses }));
4197
+ }
4198
+ return /* @__PURE__ */ jsxs49("div", { className: "ss-w-gt", "data-testid": "game-screen-mystery-tiger", children: [
4199
+ !config && /* @__PURE__ */ jsx55("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }),
4200
+ config && /* @__PURE__ */ jsxs49(Fragment9, { children: [
4201
+ isOver && clientState && /* @__PURE__ */ jsxs49("div", { className: `ss-w-gt-banner ss-w-gt-banner--${clientState.solved ? "solved" : "failed"}`, children: [
4202
+ /* @__PURE__ */ jsx55("div", { className: "ss-w-gt-banner-label", children: clientState.solved ? "\u{1F42F} You found them!" : "\u{1F614} Better luck tomorrow" }),
4203
+ clientState.revealedPlayerName && /* @__PURE__ */ jsx55("div", { className: "ss-w-gt-banner-name", children: clientState.revealedPlayerName })
4204
+ ] }),
4205
+ !isOver && /* @__PURE__ */ jsxs49("div", { className: "ss-w-gtta", children: [
4206
+ /* @__PURE__ */ jsx55(
4207
+ "input",
4208
+ {
4209
+ id: inputId,
4210
+ type: "text",
4211
+ role: "textbox",
4212
+ className: "ss-w-gtta-i",
4213
+ placeholder: "Search squad players\u2026",
4214
+ value: query,
4215
+ disabled: submitting || isOver,
4216
+ onChange: (e) => setQuery(e.target.value),
4217
+ autoComplete: "off",
4218
+ "aria-label": "Search for a player",
4219
+ "aria-autocomplete": "list"
4220
+ }
4221
+ ),
4222
+ query.length > 0 && filtered.length > 0 && /* @__PURE__ */ jsx55("div", { className: "ss-w-gtta-drop", role: "listbox", children: filtered.map((p) => /* @__PURE__ */ jsx55(
4223
+ "div",
4224
+ {
4225
+ className: "ss-w-gtta-opt",
4226
+ role: "option",
4227
+ "aria-selected": false,
4228
+ onClick: () => handleSelect(p.id),
4229
+ onKeyDown: (e) => {
4230
+ if (e.key === "Enter") handleSelect(p.id);
4231
+ },
4232
+ tabIndex: 0,
4233
+ children: p.name
4234
+ },
4235
+ p.id
4236
+ )) })
4237
+ ] }),
4238
+ clientState && clientState.guessResults.length > 0 && /* @__PURE__ */ jsxs49("div", { children: [
4239
+ /* @__PURE__ */ jsxs49("div", { className: "ss-w-gthdr", children: [
4240
+ /* @__PURE__ */ jsx55("div", { className: "ss-w-gthdr-c ss-w-gthdr-c--name", children: "Player" }),
4241
+ ATTR_KEYS.map((key) => /* @__PURE__ */ jsx55("div", { className: "ss-w-gthdr-c", children: ATTR_LABELS[key] }, key))
4242
+ ] }),
4243
+ /* @__PURE__ */ jsx55("div", { className: "ss-w-gtrows", children: clientState.guessResults.map((gr, i) => {
4244
+ const player = config.squadPlayers.find((p) => p.id === gr.playerId);
4245
+ return /* @__PURE__ */ jsxs49("div", { className: "ss-w-gtrow", children: [
4246
+ /* @__PURE__ */ jsx55("div", { className: "ss-w-gtrow-nm", children: player?.name ?? `#${gr.playerId}` }),
4247
+ ATTR_KEYS.map((key) => {
4248
+ const v = gr[key];
4249
+ return /* @__PURE__ */ jsx55(
4250
+ "div",
4251
+ {
4252
+ className: `ss-w-gtile ss-w-gtile--${v.verdict}`,
4253
+ "data-testid": `verdict-${key}-${i}`,
4254
+ "data-verdict": v.verdict,
4255
+ "aria-label": `${ATTR_LABELS[key]}: ${v.verdict}${v.direction ? ` ${v.direction}` : ""}`,
4256
+ children: v.direction && /* @__PURE__ */ jsx55("span", { className: "ss-w-gtile-dir", "aria-hidden": "true", children: v.direction === "up" ? "\u2191" : "\u2193" })
4257
+ },
4258
+ key
4259
+ );
4260
+ })
4261
+ ] }, gr.playerId);
4262
+ }) })
4263
+ ] }),
4264
+ clientState?.hintLine && !isOver && /* @__PURE__ */ jsx55("div", { className: "ss-w-gt-hint", children: clientState.hintLine }),
4265
+ !isOver && clientState && /* @__PURE__ */ jsxs49("div", { className: "ss-w-gt-hint", children: [
4266
+ clientState.guessesRemaining,
4267
+ " guess",
4268
+ clientState.guessesRemaining !== 1 ? "es" : "",
4269
+ " remaining"
4270
+ ] })
4271
+ ] })
4272
+ ] });
4273
+ }
4274
+
4275
+ // src/components/games/HigherOrLowerGame.tsx
4276
+ import { Fragment as Fragment10, jsx as jsx56, jsxs as jsxs50 } from "react/jsx-runtime";
4277
+ function parseClientState2(json) {
4278
+ if (!json) return null;
4279
+ try {
4280
+ return JSON.parse(json);
4281
+ } catch {
4282
+ return null;
4283
+ }
4284
+ }
4285
+ function AnsweredRow({
4286
+ answered,
4287
+ pair
4288
+ }) {
4289
+ const cls = `ss-w-hlrrow ss-w-hlrrow--${answered.wasCorrect ? "correct" : "wrong"}`;
4290
+ const icon = answered.wasCorrect ? "\u2713" : "\u2717";
4291
+ return /* @__PURE__ */ jsxs50("div", { className: cls, children: [
4292
+ /* @__PURE__ */ jsxs50("span", { className: "ss-w-hlrrow-names", children: [
4293
+ pair?.playerAName ?? "?",
4294
+ " vs ",
4295
+ pair?.playerBName ?? "?"
4296
+ ] }),
4297
+ /* @__PURE__ */ jsx56("span", { className: "ss-w-hlrrow-pick", children: answered.pick }),
4298
+ /* @__PURE__ */ jsx56("span", { className: "ss-w-hlrrow-icon", children: icon })
4299
+ ] });
4300
+ }
4301
+ function HigherOrLowerGame({
4302
+ config,
4303
+ result,
4304
+ submitting,
4305
+ onSubmit
4306
+ }) {
4307
+ const clientState = parseClientState2(result?.clientStateJson);
4308
+ const answeredCount = clientState?.answeredPairs.length ?? 0;
4309
+ const runEndedWrong = clientState?.answeredPairs.some((p) => !p.wasCorrect) ?? false;
4310
+ const allPairsAnswered = config != null && answeredCount >= config.pairs.length;
4311
+ const isOver = result?.alreadyPlayed === true || runEndedWrong || allPairsAnswered;
4312
+ const currentPair = config && !isOver ? config.pairs[answeredCount] ?? null : null;
4313
+ function handlePick(pick) {
4314
+ if (!config || isOver || submitting || !currentPair) return;
4315
+ const prevAnswers = clientState?.answeredPairs.map((p) => ({ pairIndex: p.pairIndex, pick: p.pick })) ?? [];
4316
+ const newAnswers = [...prevAnswers, { pairIndex: currentPair.pairIndex, pick }];
4317
+ onSubmit(JSON.stringify({ answers: newAnswers }));
4318
+ }
4319
+ return /* @__PURE__ */ jsxs50("div", { className: "ss-w-hl", "data-testid": "game-screen-higher-or-lower", children: [
4320
+ !config && /* @__PURE__ */ jsx56("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }),
4321
+ config && /* @__PURE__ */ jsxs50(Fragment10, { children: [
4322
+ /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlctx", children: [
4323
+ /* @__PURE__ */ jsx56("span", { className: "ss-w-hlctx-fam", children: config.statLabel }),
4324
+ /* @__PURE__ */ jsx56("span", { className: "ss-w-hlctx-szn", children: config.seasonLabel })
4325
+ ] }),
4326
+ currentPair && /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlpair", children: [
4327
+ /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlpc", children: [
4328
+ /* @__PURE__ */ jsx56("div", { className: "ss-w-hlpc-name", children: currentPair.playerAName }),
4329
+ /* @__PURE__ */ jsx56("div", { className: "ss-w-hlpc-val", children: currentPair.playerAValue })
4330
+ ] }),
4331
+ /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlpc ss-w-hlpc--b", children: [
4332
+ /* @__PURE__ */ jsx56("div", { className: "ss-w-hlpc-name", children: currentPair.playerBName }),
4333
+ /* @__PURE__ */ jsx56("div", { className: "ss-w-hlpc-val ss-w-hlpc-val--hidden", children: "?" })
4334
+ ] })
4335
+ ] }),
4336
+ !isOver && currentPair && /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlbtns", children: [
4337
+ /* @__PURE__ */ jsx56(
4338
+ "button",
4339
+ {
4340
+ type: "button",
4341
+ className: "ss-w-hlbtn",
4342
+ disabled: submitting,
4343
+ onClick: () => handlePick("higher"),
4344
+ children: "Higher"
4345
+ }
4346
+ ),
4347
+ /* @__PURE__ */ jsx56(
4348
+ "button",
4349
+ {
4350
+ type: "button",
4351
+ className: "ss-w-hlbtn",
4352
+ disabled: submitting,
4353
+ onClick: () => handlePick("lower"),
4354
+ children: "Lower"
4355
+ }
4356
+ )
4357
+ ] }),
4358
+ clientState && clientState.answeredPairs.length > 0 && /* @__PURE__ */ jsx56("div", { className: "ss-w-hlresults", children: clientState.answeredPairs.map((answered) => {
4359
+ const pair = config.pairs.find((p) => p.pairIndex === answered.pairIndex);
4360
+ const vCls = `ss-w-hlpc-val ss-w-hlpc-val--${answered.wasCorrect ? "correct" : "wrong"}`;
4361
+ const badgeCls = `ss-w-hlpc-badge ss-w-hlpc-badge--${answered.wasCorrect ? "correct" : "wrong"}`;
4362
+ return /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlrrow", children: [
4363
+ /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlpair", children: [
4364
+ /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlpc", children: [
4365
+ /* @__PURE__ */ jsx56("div", { className: "ss-w-hlpc-name", children: pair?.playerAName ?? "?" }),
4366
+ /* @__PURE__ */ jsx56("div", { className: "ss-w-hlpc-val", children: pair?.playerAValue })
4367
+ ] }),
4368
+ /* @__PURE__ */ jsxs50("div", { className: "ss-w-hlpc ss-w-hlpc--b", children: [
4369
+ /* @__PURE__ */ jsx56("div", { className: "ss-w-hlpc-name", children: pair?.playerBName ?? "?" }),
4370
+ /* @__PURE__ */ jsx56("div", { className: vCls, children: answered.playerBValue }),
4371
+ /* @__PURE__ */ jsx56("div", { className: badgeCls, children: answered.wasCorrect ? "\u2713" : "\u2717" })
4372
+ ] })
4373
+ ] }),
4374
+ /* @__PURE__ */ jsx56(AnsweredRow, { answered, pair })
4375
+ ] }, answered.pairIndex);
4376
+ }) })
4377
+ ] })
4378
+ ] });
4379
+ }
4380
+
4381
+ // src/components/games/CareerPathGame.tsx
4382
+ import { useState as useState24, useId as useId3 } from "react";
4383
+ import { Fragment as Fragment11, jsx as jsx57, jsxs as jsxs51 } from "react/jsx-runtime";
4384
+ function parseClientState3(json) {
4385
+ if (!json) return null;
4386
+ try {
4387
+ return JSON.parse(json);
4388
+ } catch {
4389
+ return null;
4390
+ }
4391
+ }
4392
+ function SpellYear({ spell }) {
4393
+ const parts = [];
4394
+ if (spell.startYear) parts.push(String(spell.startYear));
4395
+ if (spell.endYear) parts.push(String(spell.endYear));
4396
+ if (parts.length === 0) return null;
4397
+ return /* @__PURE__ */ jsx57("span", { className: "ss-w-cpath-spell-yr", children: parts.join(" \u2013 ") });
4398
+ }
4399
+ function ClubSpellCard({ spell }) {
4400
+ return /* @__PURE__ */ jsxs51("div", { className: `ss-w-cpath-club${spell.isLoan ? " ss-w-cpath-club--loan" : ""}`, children: [
4401
+ spell.crestUrl ? /* @__PURE__ */ jsx57(
4402
+ "img",
4403
+ {
4404
+ src: spell.crestUrl,
4405
+ alt: spell.clubName,
4406
+ className: "ss-w-cpath-crest",
4407
+ loading: "lazy"
4408
+ }
4409
+ ) : /* @__PURE__ */ jsx57("div", { className: "ss-w-cpath-crest ss-w-cpath-crest--fallback", "aria-hidden": "true" }),
4410
+ /* @__PURE__ */ jsxs51("div", { className: "ss-w-cpath-club-info", children: [
4411
+ /* @__PURE__ */ jsx57("span", { className: "ss-w-cpath-club-name", children: spell.clubName }),
4412
+ /* @__PURE__ */ jsx57(SpellYear, { spell }),
4413
+ spell.isLoan && /* @__PURE__ */ jsx57("span", { className: "ss-w-cpath-loan-badge", children: "Loan" })
4414
+ ] })
4415
+ ] });
4416
+ }
4417
+ function CareerPathGame({
4418
+ config,
4419
+ result,
4420
+ submitting,
4421
+ onSubmit
4422
+ }) {
4423
+ const [query, setQuery] = useState24("");
4424
+ const inputId = useId3();
4425
+ const clientState = parseClientState3(result?.clientStateJson);
4426
+ const alreadyGuessedIds = new Set(
4427
+ clientState?.guessResults.map((g) => g.playerId) ?? []
4428
+ );
4429
+ const isOver = result?.alreadyPlayed === true || clientState?.solved === true || clientState?.failed === true;
4430
+ const visiblePath = clientState?.visiblePath ?? config?.initialPath ?? [];
4431
+ const filtered = config && !isOver ? config.typeaheadPool.filter(
4432
+ (p) => !alreadyGuessedIds.has(p.id) && p.name.toLowerCase().includes(query.toLowerCase())
4433
+ ) : [];
4434
+ function handleSelect(playerId) {
4435
+ if (!config || isOver || submitting) return;
4436
+ const prevGuesses = clientState?.guessResults.map((g) => g.playerId) ?? [];
4437
+ const newGuesses = [...prevGuesses, playerId];
4438
+ setQuery("");
4439
+ onSubmit(JSON.stringify({ guesses: newGuesses }));
4440
+ }
4441
+ return /* @__PURE__ */ jsxs51("div", { className: "ss-w-cpath", "data-testid": "game-screen-career-path", children: [
4442
+ !config && /* @__PURE__ */ jsx57("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }),
4443
+ config && /* @__PURE__ */ jsxs51(Fragment11, { children: [
4444
+ /* @__PURE__ */ jsxs51("div", { className: "ss-w-cpath-ctx", children: [
4445
+ /* @__PURE__ */ jsx57("span", { className: "ss-w-cpath-ctx-label", children: "Career clubs" }),
4446
+ /* @__PURE__ */ jsxs51("span", { className: "ss-w-cpath-ctx-count", children: [
4447
+ config.totalClubs,
4448
+ " clubs total"
4449
+ ] })
4450
+ ] }),
4451
+ /* @__PURE__ */ jsxs51("div", { className: "ss-w-cpath-path", children: [
4452
+ visiblePath.map((spell, i) => /* @__PURE__ */ jsx57(ClubSpellCard, { spell }, `${spell.clubName}-${i}`)),
4453
+ (clientState?.totalClubs ?? config.totalClubs) > visiblePath.length && /* @__PURE__ */ jsxs51("div", { className: "ss-w-cpath-hidden", children: [
4454
+ "+",
4455
+ (clientState?.totalClubs ?? config.totalClubs) - visiblePath.length,
4456
+ " more clubs hidden"
4457
+ ] })
4458
+ ] }),
4459
+ isOver && clientState && /* @__PURE__ */ jsxs51("div", { className: `ss-w-cpath-banner ss-w-cpath-banner--${clientState.solved ? "solved" : "failed"}`, children: [
4460
+ /* @__PURE__ */ jsx57("div", { className: "ss-w-cpath-banner-label", children: clientState.solved ? "Career traced!" : "Better luck tomorrow" }),
4461
+ clientState.revealedPlayerName && /* @__PURE__ */ jsx57("div", { className: "ss-w-cpath-banner-name", children: clientState.revealedPlayerName })
4462
+ ] }),
4463
+ !isOver && /* @__PURE__ */ jsxs51("div", { className: "ss-w-gtta", children: [
4464
+ /* @__PURE__ */ jsx57(
4465
+ "input",
4466
+ {
4467
+ id: inputId,
4468
+ type: "text",
4469
+ role: "textbox",
4470
+ className: "ss-w-gtta-i",
4471
+ placeholder: "Search players\u2026",
4472
+ value: query,
4473
+ disabled: submitting || isOver,
4474
+ onChange: (e) => setQuery(e.target.value),
4475
+ autoComplete: "off",
4476
+ "aria-label": "Search for a player",
4477
+ "aria-autocomplete": "list"
4478
+ }
4479
+ ),
4480
+ query.length > 0 && filtered.length > 0 && /* @__PURE__ */ jsx57("div", { className: "ss-w-gtta-drop", role: "listbox", children: filtered.map((p) => /* @__PURE__ */ jsx57(
4481
+ "div",
4482
+ {
4483
+ className: "ss-w-gtta-opt",
4484
+ role: "option",
4485
+ "aria-selected": false,
4486
+ onClick: () => handleSelect(p.id),
4487
+ onKeyDown: (e) => {
4488
+ if (e.key === "Enter") handleSelect(p.id);
4489
+ },
4490
+ tabIndex: 0,
4491
+ children: p.name
4492
+ },
4493
+ p.id
4494
+ )) })
4495
+ ] }),
4496
+ clientState?.hintLine && !isOver && /* @__PURE__ */ jsx57("div", { className: "ss-w-cpath-hint", children: clientState.hintLine }),
4497
+ !isOver && clientState && /* @__PURE__ */ jsxs51("div", { className: "ss-w-cpath-hint", children: [
4498
+ clientState.guessesRemaining,
4499
+ " guess",
4500
+ clientState.guessesRemaining !== 1 ? "es" : "",
4501
+ " remaining"
4502
+ ] })
4503
+ ] })
4504
+ ] });
4505
+ }
4506
+
4507
+ // src/components/games/BeatTheGridGame.tsx
4508
+ import React, { useState as useState25, useId as useId4 } from "react";
4509
+ import { Fragment as Fragment12, jsx as jsx58, jsxs as jsxs52 } from "react/jsx-runtime";
4510
+ function parseClientState4(json) {
4511
+ if (!json) return null;
4512
+ try {
4513
+ return JSON.parse(json);
4514
+ } catch {
4515
+ return null;
4516
+ }
4517
+ }
4518
+ function findCellResult(cells, row, col) {
4519
+ return cells.find((c) => c.row === row && c.col === col);
4520
+ }
4521
+ function SearchSheet({
4522
+ row,
4523
+ col,
4524
+ rowLabel,
4525
+ colLabel,
4526
+ playerPool,
4527
+ alreadyFilledIds,
4528
+ onPick,
4529
+ onDismiss
4530
+ }) {
4531
+ const [query, setQuery] = useState25("");
4532
+ const inputId = useId4();
4533
+ const filtered = playerPool.filter(
4534
+ (p) => !alreadyFilledIds.has(p.id) && p.name.toLowerCase().includes(query.toLowerCase())
4535
+ );
4536
+ return /* @__PURE__ */ jsxs52("div", { className: "ss-w-btg-sheet", "data-testid": "btg-search-sheet", children: [
4537
+ /* @__PURE__ */ jsxs52("div", { className: "ss-w-btg-sheet-hdr", children: [
4538
+ /* @__PURE__ */ jsxs52("div", { className: "ss-w-btg-sheet-ctx", children: [
4539
+ /* @__PURE__ */ jsx58("span", { className: "ss-w-btg-sheet-row", children: rowLabel }),
4540
+ /* @__PURE__ */ jsx58("span", { className: "ss-w-btg-sheet-sep", children: "\xD7" }),
4541
+ /* @__PURE__ */ jsx58("span", { className: "ss-w-btg-sheet-col", children: colLabel })
4542
+ ] }),
4543
+ /* @__PURE__ */ jsx58(
4544
+ "button",
4545
+ {
4546
+ type: "button",
4547
+ className: "ss-w-btg-sheet-close",
4548
+ "data-testid": "btg-sheet-dismiss",
4549
+ "aria-label": "Dismiss",
4550
+ onClick: onDismiss,
4551
+ children: "\u2715"
4552
+ }
4553
+ )
4554
+ ] }),
4555
+ /* @__PURE__ */ jsxs52("div", { className: "ss-w-gtta", children: [
4556
+ /* @__PURE__ */ jsx58(
4557
+ "input",
4558
+ {
4559
+ id: inputId,
4560
+ type: "text",
4561
+ role: "textbox",
4562
+ className: "ss-w-gtta-i",
4563
+ placeholder: "Search players\u2026",
4564
+ value: query,
4565
+ onChange: (e) => setQuery(e.target.value),
4566
+ autoComplete: "off",
4567
+ "aria-label": `Search player for ${rowLabel} \xD7 ${colLabel}`,
4568
+ "aria-autocomplete": "list",
4569
+ autoFocus: true
4570
+ }
4571
+ ),
4572
+ query.length > 0 && filtered.length > 0 && /* @__PURE__ */ jsx58("div", { className: "ss-w-gtta-drop ss-w-btg-ta-list", role: "listbox", children: filtered.map((p) => /* @__PURE__ */ jsx58(
4573
+ "div",
4574
+ {
4575
+ className: "ss-w-gtta-opt",
4576
+ role: "option",
4577
+ "aria-selected": false,
4578
+ onClick: () => onPick(row, col, p.id),
4579
+ onKeyDown: (e) => {
4580
+ if (e.key === "Enter") onPick(row, col, p.id);
4581
+ },
4582
+ tabIndex: 0,
4583
+ children: p.name
4584
+ },
4585
+ p.id
4586
+ )) })
4587
+ ] })
4588
+ ] });
4589
+ }
4590
+ function BeatTheGridGame({
4591
+ config,
4592
+ result,
4593
+ submitting,
4594
+ onSubmit,
4595
+ playerPool
4596
+ }) {
4597
+ const [activeCell, setActiveCell] = useState25(null);
4598
+ const clientState = parseClientState4(result?.clientStateJson);
4599
+ const filledCells = clientState?.cells ?? [];
4600
+ const isLocked2 = result?.alreadyPlayed === true || clientState?.isComplete === true;
4601
+ const alreadyFilledIds = new Set(filledCells.map((c) => c.playerId));
4602
+ function handleCellClick(row, col) {
4603
+ if (isLocked2 || submitting) return;
4604
+ const alreadyFilled = findCellResult(filledCells, row, col);
4605
+ if (alreadyFilled) return;
4606
+ setActiveCell({ row, col });
4607
+ }
4608
+ function handlePick(row, col, playerId) {
4609
+ setActiveCell(null);
4610
+ const prevCells = filledCells.map((c) => ({ row: c.row, col: c.col, playerId: c.playerId }));
4611
+ const newCells = [...prevCells, { row, col, playerId }];
4612
+ onSubmit(JSON.stringify({ cells: newCells }));
4613
+ }
4614
+ if (!config) {
4615
+ return /* @__PURE__ */ jsx58("div", { className: "ss-w-btg", "data-testid": "game-screen-beat-the-grid", children: /* @__PURE__ */ jsx58("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }) });
4616
+ }
4617
+ const rows = config.rows;
4618
+ const cols = config.cols;
4619
+ return /* @__PURE__ */ jsxs52("div", { className: "ss-w-btg", "data-testid": "game-screen-beat-the-grid", children: [
4620
+ /* @__PURE__ */ jsxs52("div", { className: "ss-w-btg-ctx", children: [
4621
+ /* @__PURE__ */ jsxs52("span", { className: "ss-w-btg-ctx-score", children: [
4622
+ "Score: ",
4623
+ clientState?.totalScore ?? 0
4624
+ ] }),
4625
+ !isLocked2 && /* @__PURE__ */ jsxs52("span", { className: "ss-w-btg-ctx-left", children: [
4626
+ config.maxGuesses - filledCells.length,
4627
+ " cell",
4628
+ config.maxGuesses - filledCells.length !== 1 ? "s" : "",
4629
+ " left"
4630
+ ] }),
4631
+ isLocked2 && /* @__PURE__ */ jsx58("span", { className: "ss-w-btg-ctx-done", children: "Complete" })
4632
+ ] }),
4633
+ /* @__PURE__ */ jsxs52(
4634
+ "div",
4635
+ {
4636
+ className: "ss-w-btg-grid",
4637
+ style: { gridTemplateColumns: `auto repeat(${cols.length}, 1fr)` },
4638
+ role: "grid",
4639
+ children: [
4640
+ /* @__PURE__ */ jsx58("div", { className: "ss-w-btg-gc ss-w-btg-gc--corner" }),
4641
+ cols.map((col) => /* @__PURE__ */ jsx58("div", { className: "ss-w-btg-gc ss-w-btg-gc--colhdr", children: col }, col)),
4642
+ rows.map((rowLabel, rowIdx) => /* @__PURE__ */ jsxs52(React.Fragment, { children: [
4643
+ /* @__PURE__ */ jsx58("div", { className: "ss-w-btg-gc ss-w-btg-gc--rowhdr", children: rowLabel }),
4644
+ cols.map((colLabel, colIdx) => {
4645
+ const filled = findCellResult(filledCells, rowIdx, colIdx);
4646
+ const isActive = activeCell?.row === rowIdx && activeCell?.col === colIdx;
4647
+ return /* @__PURE__ */ jsx58(
4648
+ "button",
4649
+ {
4650
+ type: "button",
4651
+ "data-testid": `btg-cell-${rowIdx}-${colIdx}`,
4652
+ "aria-label": `${rowLabel} ${colLabel}`,
4653
+ className: [
4654
+ "ss-w-btg-gc ss-w-btg-cell",
4655
+ filled ? filled.isCorrect ? "ss-w-btg-cell--correct" : "ss-w-btg-cell--wrong" : "",
4656
+ isActive ? "ss-w-btg-cell--active" : ""
4657
+ ].filter(Boolean).join(" "),
4658
+ disabled: isLocked2 || submitting,
4659
+ onClick: () => handleCellClick(rowIdx, colIdx),
4660
+ children: filled ? /* @__PURE__ */ jsxs52(Fragment12, { children: [
4661
+ /* @__PURE__ */ jsx58("span", { className: "ss-w-btg-cell-score", children: filled.rarityScore }),
4662
+ filled.isCorrect && /* @__PURE__ */ jsx58("span", { className: "ss-w-btg-cell-check", "aria-hidden": "true", children: "\u2713" })
4663
+ ] }) : /* @__PURE__ */ jsx58("span", { className: "ss-w-btg-cell-empty", "aria-hidden": "true", children: "+" })
4664
+ },
4665
+ `cell-${rowIdx}-${colIdx}`
4666
+ );
4667
+ })
4668
+ ] }, rowIdx))
4669
+ ]
4670
+ }
4671
+ ),
4672
+ activeCell !== null && /* @__PURE__ */ jsx58(
4673
+ SearchSheet,
4674
+ {
4675
+ row: activeCell.row,
4676
+ col: activeCell.col,
4677
+ rowLabel: rows[activeCell.row] ?? "",
4678
+ colLabel: cols[activeCell.col] ?? "",
4679
+ playerPool,
4680
+ alreadyFilledIds,
4681
+ onPick: handlePick,
4682
+ onDismiss: () => setActiveCell(null)
4683
+ }
4684
+ )
4685
+ ] });
4686
+ }
4687
+
4688
+ // src/components/games/GuessTheXiGame.tsx
4689
+ import { Fragment as Fragment13, jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
4690
+ function parseClientState5(json) {
4691
+ if (!json) return null;
4692
+ try {
4693
+ return JSON.parse(json);
4694
+ } catch {
4695
+ return null;
4696
+ }
4697
+ }
4698
+ function RevealCard({ reveal }) {
4699
+ return /* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-reveal", "data-reveal-type": reveal.type ?? "", children: [
4700
+ reveal.slot && /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-reveal-slot", children: reveal.slot }),
4701
+ /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-reveal-val", children: reveal.value })
4702
+ ] });
4703
+ }
4704
+ function GuessTheXiGame({
4705
+ config,
4706
+ result,
4707
+ submitting,
4708
+ onSubmit
4709
+ }) {
4710
+ const clientState = parseClientState5(result?.clientStateJson);
4711
+ const isOver = result?.alreadyPlayed === true || clientState?.status === "solved" || clientState?.status === "failed";
4712
+ function handlePick(teamId) {
4713
+ if (!config || isOver || submitting) return;
4714
+ const prevGuesses = clientState?.guessHistory.map((g) => ({ teamId: g.teamId })) ?? [];
4715
+ const newGuesses = [...prevGuesses, { teamId }];
4716
+ onSubmit(JSON.stringify({ guesses: newGuesses }));
4717
+ }
4718
+ return /* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi", "data-testid": "game-screen-guess-the-xi", children: [
4719
+ !config && /* @__PURE__ */ jsx59("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }),
4720
+ config && /* @__PURE__ */ jsxs53(Fragment13, { children: [
4721
+ /* @__PURE__ */ jsx59("div", { className: "ss-w-gxi-ctx", children: /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-ctx-label", children: config.poolLabel }) }),
4722
+ config.positions.length > 0 && /* @__PURE__ */ jsx59("div", { className: "ss-w-gxi-pitch", children: config.positions.map((pos) => /* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-pslot", children: [
4723
+ /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-pslot-pos", children: pos.slot }),
4724
+ /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-pslot-nat", children: pos.nationality })
4725
+ ] }, pos.slot)) }),
4726
+ clientState && clientState.earnedReveals.length > 0 && /* @__PURE__ */ jsx59("div", { className: "ss-w-gxi-reveals", children: clientState.earnedReveals.map((r, i) => /* @__PURE__ */ jsx59(RevealCard, { reveal: r }, i)) }),
4727
+ isOver && clientState && /* @__PURE__ */ jsx59("div", { className: `ss-w-gxi-banner ss-w-gxi-banner--${clientState.status}`, children: /* @__PURE__ */ jsx59("span", { className: "ss-w-gxi-banner-label", children: clientState.status === "solved" ? `Solved! ${clientState.score} pts` : "Better luck tomorrow" }) }),
4728
+ /* @__PURE__ */ jsx59("div", { className: "ss-w-gxi-pool", children: config.teamPool.map((team) => {
4729
+ const guessRecord = clientState?.guessHistory.find((g) => g.teamId === team.teamId);
4730
+ return /* @__PURE__ */ jsx59(
4731
+ "button",
4732
+ {
4733
+ type: "button",
4734
+ className: [
4735
+ "ss-w-gxi-team-btn",
4736
+ guessRecord ? guessRecord.wasCorrect ? "ss-w-gxi-team-btn--correct" : "ss-w-gxi-team-btn--wrong" : ""
4737
+ ].filter(Boolean).join(" "),
4738
+ disabled: isOver || submitting,
4739
+ onClick: () => handlePick(team.teamId),
4740
+ children: team.teamName
4741
+ },
4742
+ team.teamId
4743
+ );
4744
+ }) }),
4745
+ !isOver && clientState && /* @__PURE__ */ jsxs53("div", { className: "ss-w-gxi-hint", children: [
4746
+ "Guess ",
4747
+ clientState.guessesUsed + 1,
4748
+ " of 6"
4749
+ ] })
4750
+ ] })
4751
+ ] });
4752
+ }
4753
+
4754
+ // src/components/games/DailyQuizGame.tsx
4755
+ import { useState as useState26 } from "react";
4756
+ import { Fragment as Fragment14, jsx as jsx60, jsxs as jsxs54 } from "react/jsx-runtime";
4757
+ function parseClientState6(json) {
4758
+ if (!json) return null;
4759
+ try {
4760
+ return JSON.parse(json);
4761
+ } catch {
4762
+ return null;
4763
+ }
4764
+ }
4765
+ function findResult(results, index) {
4766
+ return results.find((r) => r.index === index);
4767
+ }
4768
+ function DailyQuizGame({
4769
+ config,
4770
+ result,
4771
+ submitting,
4772
+ onSubmit
4773
+ }) {
4774
+ const [selections, setSelections] = useState26(/* @__PURE__ */ new Map());
4775
+ const clientState = parseClientState6(result?.clientStateJson);
4776
+ const isRevealed = (clientState?.results.length ?? 0) > 0 || result?.alreadyPlayed === true;
4777
+ const allAnswered = config != null && selections.size === config.questions.length;
4778
+ function handleSelect(questionIndex, optionIndex) {
4779
+ if (isRevealed || submitting) return;
4780
+ setSelections((prev) => new Map(prev).set(questionIndex, optionIndex));
4781
+ }
4782
+ function handleSubmit() {
4783
+ if (!config || !allAnswered || isRevealed || submitting) return;
4784
+ const answers = config.questions.map((q) => ({
4785
+ index: q.index,
4786
+ selectedOptionIndex: selections.get(q.index) ?? 0
4787
+ }));
4788
+ onSubmit(JSON.stringify({ answers }));
4789
+ }
4790
+ return /* @__PURE__ */ jsxs54("div", { className: "ss-w-dq", "data-testid": "game-screen-daily-quiz", children: [
4791
+ !config && /* @__PURE__ */ jsx60("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }),
4792
+ config && /* @__PURE__ */ jsxs54(Fragment14, { children: [
4793
+ isRevealed && result?.score != null && /* @__PURE__ */ jsxs54("div", { className: "ss-w-dq-score", children: [
4794
+ /* @__PURE__ */ jsx60("span", { className: "ss-w-dq-score-val", children: result.score }),
4795
+ /* @__PURE__ */ jsx60("span", { className: "ss-w-dq-score-label", children: "pts" })
4796
+ ] }),
4797
+ !isRevealed && /* @__PURE__ */ jsx60("div", { className: "ss-w-dq-prog", children: /* @__PURE__ */ jsx60(
4798
+ "div",
4799
+ {
4800
+ className: "ss-w-dq-prog-fill",
4801
+ style: { width: `${Math.round(selections.size / config.questions.length * 100)}%` }
4802
+ }
4803
+ ) }),
4804
+ /* @__PURE__ */ jsx60("div", { className: "ss-w-dq-questions", children: config.questions.map((q) => {
4805
+ const qResult = clientState ? findResult(clientState.results, q.index) : void 0;
4806
+ const selectedIdx = selections.get(q.index);
4807
+ return /* @__PURE__ */ jsxs54("div", { className: "ss-w-dq-q", children: [
4808
+ /* @__PURE__ */ jsx60("div", { className: "ss-w-dq-qstem", children: q.stem }),
4809
+ /* @__PURE__ */ jsx60("div", { className: "ss-w-dq-opts", children: q.options.map((opt, optIdx) => {
4810
+ let optCls = "ss-w-dq-qopt";
4811
+ if (qResult) {
4812
+ if (optIdx === qResult.correctOptionIndex) {
4813
+ optCls += " ss-w-dq-qopt--correct";
4814
+ } else if (optIdx === selectedIdx && !qResult.isCorrect) {
4815
+ optCls += " ss-w-dq-qopt--wrong";
4816
+ }
4817
+ } else if (optIdx === selectedIdx) {
4818
+ optCls += " ss-w-dq-qopt--selected";
4819
+ }
4820
+ return /* @__PURE__ */ jsxs54(
4821
+ "button",
4822
+ {
4823
+ type: "button",
4824
+ className: optCls,
4825
+ disabled: isRevealed || submitting,
4826
+ onClick: () => handleSelect(q.index, optIdx),
4827
+ children: [
4828
+ /* @__PURE__ */ jsx60("span", { className: "ss-w-dq-qopt-letter", children: String.fromCharCode(65 + optIdx) }),
4829
+ /* @__PURE__ */ jsx60("span", { className: "ss-w-dq-qopt-text", children: opt })
4830
+ ]
4831
+ },
4832
+ optIdx
4833
+ );
4834
+ }) }),
4835
+ qResult?.revealText && /* @__PURE__ */ jsx60("div", { className: `ss-w-dq-qwhy ss-w-dq-qwhy--${qResult.isCorrect ? "correct" : "wrong"}`, children: qResult.revealText })
4836
+ ] }, q.index);
4837
+ }) }),
4838
+ !isRevealed && /* @__PURE__ */ jsx60(
4839
+ "button",
4840
+ {
4841
+ type: "button",
4842
+ className: "ss-w-dq-submit",
4843
+ disabled: !allAnswered || submitting,
4844
+ onClick: handleSubmit,
4845
+ "aria-label": "Submit answers",
4846
+ children: submitting ? "Submitting\u2026" : "Submit"
4847
+ }
4848
+ )
4849
+ ] })
4850
+ ] });
4851
+ }
4852
+
4853
+ // src/components/games/MatchdayPredictorGame.tsx
4854
+ import { useState as useState27 } from "react";
4855
+ import { jsx as jsx61, jsxs as jsxs55 } from "react/jsx-runtime";
4856
+ function parseClientState7(json) {
4857
+ if (!json) return null;
4858
+ try {
4859
+ return JSON.parse(json);
4860
+ } catch {
4861
+ return null;
4862
+ }
4863
+ }
4864
+ function isLocked(kickoffUtc) {
4865
+ try {
4866
+ return new Date(kickoffUtc).getTime() <= Date.now();
4867
+ } catch {
4868
+ return true;
4869
+ }
4870
+ }
4871
+ function MatchdayPredictorGame({
4872
+ config,
4873
+ result,
4874
+ submitting,
4875
+ onSubmit
4876
+ }) {
4877
+ const [homeScore, setHomeScore] = useState27(0);
4878
+ const [awayScore, setAwayScore] = useState27(0);
4879
+ const [firstScorerPlayerId, setFirstScorerPlayerId] = useState27(null);
4880
+ const clientState = parseClientState7(result?.clientStateJson);
4881
+ const alreadyPlayed = result?.alreadyPlayed === true;
4882
+ if (alreadyPlayed && clientState) {
4883
+ if (!clientState.graded) {
4884
+ return /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp", "data-testid": "game-screen-matchday-predictor", children: [
4885
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-live", children: [
4886
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-live-score", children: [
4887
+ clientState.predictedHomeScore,
4888
+ " \u2013 ",
4889
+ clientState.predictedAwayScore
4890
+ ] }),
4891
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-live-label", children: "Locked in \u2014 awaiting result" })
4892
+ ] }),
4893
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-fun", children: "For fun \u2014 not in the leaderboard" })
4894
+ ] });
4895
+ }
4896
+ const r = clientState.result;
4897
+ const actualScore = `${r.homeScore} \u2013 ${r.awayScore}`;
4898
+ return /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp", "data-testid": "game-screen-matchday-predictor", children: [
4899
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-live", children: [
4900
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-live-score", children: actualScore }),
4901
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-live-label", children: "Full-time result" }),
4902
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-live-outcome", children: [
4903
+ /* @__PURE__ */ jsxs55("div", { className: `ss-w-mp-live-row${clientState.exactScoreCorrect ? " ss-w-mp-live-row--correct" : ""}`, children: [
4904
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-live-row-key", children: "Exact score" }),
4905
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-live-row-val", children: clientState.exactScoreCorrect ? "\u2713" : "\u2717" })
4906
+ ] }),
4907
+ /* @__PURE__ */ jsxs55("div", { className: `ss-w-mp-live-row${clientState.outcomeCorrect ? " ss-w-mp-live-row--correct" : ""}`, children: [
4908
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-live-row-key", children: "Result (W/D/L)" }),
4909
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-live-row-val", children: clientState.outcomeCorrect ? "\u2713" : "\u2717" })
4910
+ ] }),
4911
+ !clientState.firstScorerVoid && /* @__PURE__ */ jsxs55("div", { className: `ss-w-mp-live-row${clientState.firstScorerCorrect ? " ss-w-mp-live-row--correct" : ""}`, children: [
4912
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-live-row-key", children: "First scorer" }),
4913
+ /* @__PURE__ */ jsxs55("span", { className: "ss-w-mp-live-row-val", children: [
4914
+ clientState.firstScorerCorrect ? "\u2713" : "\u2717",
4915
+ r.firstScorerName ? ` ${r.firstScorerName}` : ""
4916
+ ] })
4917
+ ] })
4918
+ ] })
4919
+ ] }),
4920
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-fun", children: "For fun \u2014 not in the leaderboard" })
4921
+ ] });
4922
+ }
4923
+ if (!config) {
4924
+ return /* @__PURE__ */ jsx61("div", { className: "ss-w-mp", "data-testid": "game-screen-matchday-predictor", children: /* @__PURE__ */ jsx61("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }) });
4925
+ }
4926
+ const locked = isLocked(config.kickoffUtc);
4927
+ const disabled = locked || submitting;
4928
+ function handleSubmit() {
4929
+ if (disabled || !config) return;
4930
+ onSubmit(
4931
+ JSON.stringify({
4932
+ homeScore,
4933
+ awayScore,
4934
+ ...firstScorerPlayerId !== null ? { firstScorerPlayerId } : {}
4935
+ })
4936
+ );
4937
+ }
4938
+ return /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp", "data-testid": "game-screen-matchday-predictor", children: [
4939
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-pred-score", children: [
4940
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-team", children: [
4941
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-team-name", children: config.homeTeam.name }),
4942
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-stepper", children: [
4943
+ /* @__PURE__ */ jsx61(
4944
+ "button",
4945
+ {
4946
+ type: "button",
4947
+ className: "ss-w-mp-stepper-btn",
4948
+ "aria-label": "-",
4949
+ disabled: disabled || homeScore <= 0,
4950
+ onClick: () => setHomeScore((s) => Math.max(0, s - 1)),
4951
+ children: "\u2212"
4952
+ }
4953
+ ),
4954
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-stepper-val", children: homeScore }),
4955
+ /* @__PURE__ */ jsx61(
4956
+ "button",
4957
+ {
4958
+ type: "button",
4959
+ className: "ss-w-mp-stepper-btn",
4960
+ "aria-label": "+",
4961
+ disabled,
4962
+ onClick: () => setHomeScore((s) => s + 1),
4963
+ children: "+"
4964
+ }
4965
+ )
4966
+ ] })
4967
+ ] }),
4968
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-sep", children: "\u2013" }),
4969
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-team", children: [
4970
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-team-name", children: config.awayTeam.name }),
4971
+ /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-stepper", children: [
4972
+ /* @__PURE__ */ jsx61(
4973
+ "button",
4974
+ {
4975
+ type: "button",
4976
+ className: "ss-w-mp-stepper-btn",
4977
+ "aria-label": "-",
4978
+ disabled: disabled || awayScore <= 0,
4979
+ onClick: () => setAwayScore((s) => Math.max(0, s - 1)),
4980
+ children: "\u2212"
4981
+ }
4982
+ ),
4983
+ /* @__PURE__ */ jsx61("span", { className: "ss-w-mp-stepper-val", children: awayScore }),
4984
+ /* @__PURE__ */ jsx61(
4985
+ "button",
4986
+ {
4987
+ type: "button",
4988
+ className: "ss-w-mp-stepper-btn",
4989
+ "aria-label": "+",
4990
+ disabled,
4991
+ onClick: () => setAwayScore((s) => s + 1),
4992
+ children: "+"
4993
+ }
4994
+ )
4995
+ ] })
4996
+ ] })
4997
+ ] }),
4998
+ config.scorerPool.length > 0 && /* @__PURE__ */ jsxs55("div", { className: "ss-w-mp-scorer", children: [
4999
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-scorer-label", children: "First scorer (optional)" }),
5000
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-scorer-list", children: config.scorerPool.map((p) => /* @__PURE__ */ jsx61(
5001
+ "button",
5002
+ {
5003
+ type: "button",
5004
+ className: `ss-w-mp-scorer-opt${firstScorerPlayerId === p.playerId ? " ss-w-mp-scorer-opt--selected" : ""}`,
5005
+ disabled,
5006
+ onClick: () => setFirstScorerPlayerId((id) => id === p.playerId ? null : p.playerId),
5007
+ children: p.playerName
5008
+ },
5009
+ p.playerId
5010
+ )) })
5011
+ ] }),
5012
+ locked && /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-live-label", children: "Submissions are now closed" }),
5013
+ /* @__PURE__ */ jsx61(
5014
+ "button",
5015
+ {
5016
+ type: "button",
5017
+ className: "ss-w-mp-savebtn",
5018
+ disabled,
5019
+ onClick: handleSubmit,
5020
+ "aria-label": "Save prediction",
5021
+ children: submitting ? "Saving\u2026" : "Save prediction"
5022
+ }
5023
+ ),
5024
+ /* @__PURE__ */ jsx61("div", { className: "ss-w-mp-fun", children: "For fun \u2014 not in the leaderboard" })
5025
+ ] });
5026
+ }
5027
+
5028
+ // src/components/games/StartingXiGame.tsx
5029
+ import { useState as useState28 } from "react";
5030
+ import { jsx as jsx62, jsxs as jsxs56 } from "react/jsx-runtime";
5031
+ function parseClientState8(json) {
5032
+ if (!json) return null;
5033
+ try {
5034
+ return JSON.parse(json);
5035
+ } catch {
5036
+ return null;
5037
+ }
5038
+ }
5039
+ function parseFormationRows(formation) {
5040
+ const parts = formation.split("-").map(Number).filter((n) => n > 0);
5041
+ if (parts.length === 0) return [1, 4, 3, 3];
5042
+ return [1, ...parts];
5043
+ }
5044
+ function StartingXiGame({
5045
+ config,
5046
+ result,
5047
+ submitting,
5048
+ onSubmit
5049
+ }) {
5050
+ const [picked, setPicked] = useState28([]);
5051
+ const clientState = parseClientState8(result?.clientStateJson);
5052
+ const alreadyPlayed = result?.alreadyPlayed === true;
5053
+ if (alreadyPlayed) {
5054
+ if (!clientState) {
5055
+ return /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi", "data-testid": "game-screen-starting-xi", children: /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-status", children: /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-status-label", children: "This game has been voided" }) }) });
5056
+ }
5057
+ if (clientState.status === "pending") {
5058
+ return /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi", "data-testid": "game-screen-starting-xi", children: /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-status", children: /* @__PURE__ */ jsxs56("div", { className: "ss-w-sxi-status-label", children: [
5059
+ "Prediction locked in (",
5060
+ clientState.predictedCount,
5061
+ " players) \u2014 awaiting result"
5062
+ ] }) }) });
5063
+ }
5064
+ if (clientState.status === "graded") {
5065
+ return /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi", "data-testid": "game-screen-starting-xi", children: /* @__PURE__ */ jsxs56("div", { className: "ss-w-sxi-status ss-w-sxi-status--graded", children: [
5066
+ /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-status-score", children: clientState.score }),
5067
+ /* @__PURE__ */ jsxs56("div", { className: "ss-w-sxi-status-label", children: [
5068
+ clientState.correctCount,
5069
+ " correct starters"
5070
+ ] })
5071
+ ] }) });
5072
+ }
5073
+ }
5074
+ if (!config) {
5075
+ return /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi", "data-testid": "game-screen-starting-xi", children: /* @__PURE__ */ jsx62("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }) });
5076
+ }
5077
+ const formationRows = parseFormationRows(config.formation);
5078
+ const pickedSet = new Set(picked);
5079
+ const canSubmit = picked.length === 11 && !submitting;
5080
+ function togglePlayer(playerId) {
5081
+ if (submitting) return;
5082
+ setPicked((prev) => {
5083
+ if (prev.includes(playerId)) {
5084
+ return prev.filter((id) => id !== playerId);
5085
+ }
5086
+ if (prev.length >= 11) return prev;
5087
+ return [...prev, playerId];
5088
+ });
5089
+ }
5090
+ function handleSubmit() {
5091
+ if (!canSubmit) return;
5092
+ onSubmit(JSON.stringify({ predictedPlayerIds: picked }));
5093
+ }
5094
+ let slotIndex = 0;
5095
+ const pitchRows = formationRows.map((count, rowIdx) => {
5096
+ const rowSlots = picked.slice(slotIndex, slotIndex + count);
5097
+ slotIndex += count;
5098
+ return { rowIdx, count, rowSlots };
5099
+ });
5100
+ return /* @__PURE__ */ jsxs56("div", { className: "ss-w-sxi", "data-testid": "game-screen-starting-xi", children: [
5101
+ /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-formation", children: config.formation }),
5102
+ /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-pitch", children: pitchRows.map(({ rowIdx, count, rowSlots }) => /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-pitch-row", children: Array.from({ length: count }).map((_, i) => {
5103
+ const playerId = rowSlots[i];
5104
+ const player = playerId != null ? config.squadSheet.find((p) => p.playerId === playerId) : void 0;
5105
+ return /* @__PURE__ */ jsxs56(
5106
+ "div",
5107
+ {
5108
+ className: `ss-w-sxi-pslot${player ? " ss-w-sxi-pslot--filled" : ""}`,
5109
+ onClick: () => {
5110
+ if (player) togglePlayer(player.playerId);
5111
+ },
5112
+ onKeyDown: (e) => {
5113
+ if (e.key === "Enter" && player) togglePlayer(player.playerId);
5114
+ },
5115
+ tabIndex: player ? 0 : -1,
5116
+ role: player ? "button" : void 0,
5117
+ "aria-label": player ? `Remove ${player.playerName ?? ""}` : void 0,
5118
+ children: [
5119
+ /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-pslot-num", children: player?.jerseyNumber ?? (rowIdx === 0 ? "GK" : "?") }),
5120
+ /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-pslot-name", children: player?.playerName ?? "" })
5121
+ ]
5122
+ },
5123
+ i
5124
+ );
5125
+ }) }, rowIdx)) }),
5126
+ /* @__PURE__ */ jsx62("div", { className: "ss-w-sxi-squad", children: config.squadSheet.map((p) => {
5127
+ const isPicked = pickedSet.has(p.playerId);
5128
+ return /* @__PURE__ */ jsxs56(
5129
+ "div",
5130
+ {
5131
+ className: `ss-w-sxi-squad-row${isPicked ? " ss-w-sxi-squad-row--picked" : ""}`,
5132
+ onClick: () => togglePlayer(p.playerId),
5133
+ onKeyDown: (e) => {
5134
+ if (e.key === "Enter") togglePlayer(p.playerId);
5135
+ },
5136
+ tabIndex: 0,
5137
+ role: "button",
5138
+ "aria-pressed": isPicked,
5139
+ children: [
5140
+ /* @__PURE__ */ jsx62("span", { className: "ss-w-sxi-squad-name", children: p.playerName }),
5141
+ /* @__PURE__ */ jsx62("span", { className: "ss-w-sxi-squad-pos", children: p.positionName })
5142
+ ]
5143
+ },
5144
+ p.playerId
5145
+ );
5146
+ }) }),
5147
+ /* @__PURE__ */ jsx62(
5148
+ "button",
5149
+ {
5150
+ type: "button",
5151
+ className: "ss-w-sxi-savebtn",
5152
+ disabled: !canSubmit,
5153
+ onClick: handleSubmit,
5154
+ "aria-label": "Predict XI",
5155
+ children: submitting ? "Submitting\u2026" : `Predict XI (${picked.length}/11)`
5156
+ }
5157
+ )
5158
+ ] });
5159
+ }
5160
+
5161
+ // src/components/games/StatLadderGame.tsx
5162
+ import { jsx as jsx63, jsxs as jsxs57 } from "react/jsx-runtime";
5163
+ function parseClientState9(json) {
5164
+ if (!json) return null;
5165
+ try {
5166
+ return JSON.parse(json);
5167
+ } catch {
5168
+ return null;
5169
+ }
5170
+ }
5171
+ function StatLadderGame({
5172
+ config,
5173
+ result,
5174
+ submitting,
5175
+ onSubmit
5176
+ }) {
5177
+ const clientState = parseClientState9(result?.clientStateJson);
5178
+ const revealed = clientState != null;
5179
+ if (!config) {
5180
+ return /* @__PURE__ */ jsx63("div", { className: "ss-w-sl", "data-testid": "game-screen-stat-ladder", children: /* @__PURE__ */ jsx63("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }) });
5181
+ }
5182
+ const mysteryName = revealed ? clientState.revealedRank2.playerName : null;
5183
+ return /* @__PURE__ */ jsxs57("div", { className: "ss-w-sl", "data-testid": "game-screen-stat-ladder", children: [
5184
+ /* @__PURE__ */ jsx63("div", { className: "ss-w-sl-title", children: config.statLabel }),
5185
+ /* @__PURE__ */ jsx63("div", { className: "ss-w-sl-ladder", children: config.ladder.map((rung) => {
5186
+ const isMystery = rung.rank === 2;
5187
+ let rowCls = "ss-w-sl-lrow";
5188
+ if (isMystery) rowCls += " ss-w-sl-mystery";
5189
+ if (isMystery && revealed) {
5190
+ rowCls = rowCls.replace(" ss-w-sl-mystery", "");
5191
+ rowCls += clientState.isCorrect ? " ss-w-sl-lrow--correct" : " ss-w-sl-lrow--wrong";
5192
+ }
5193
+ const displayName = isMystery ? revealed ? mysteryName : "?" : rung.playerName ?? "?";
5194
+ return /* @__PURE__ */ jsxs57("div", { className: rowCls, children: [
5195
+ /* @__PURE__ */ jsxs57("div", { className: "ss-w-sl-lrow-rank", children: [
5196
+ "#",
5197
+ rung.rank
5198
+ ] }),
5199
+ /* @__PURE__ */ jsx63("div", { className: "ss-w-sl-lrow-name", children: displayName }),
5200
+ /* @__PURE__ */ jsx63("div", { className: "ss-w-sl-lrow-stat", children: rung.statValue })
5201
+ ] }, rung.rank);
5202
+ }) }),
5203
+ revealed && /* @__PURE__ */ jsx63("div", { className: `ss-w-sl-reveal ss-w-sl-reveal--${clientState.isCorrect ? "correct" : "wrong"}`, children: clientState.isCorrect ? "Correct!" : `Wrong \u2014 it was ${clientState.revealedRank2.playerName}` }),
5204
+ !revealed && /* @__PURE__ */ jsx63("div", { className: "ss-w-sl-picker", children: config.playerPool.map((p) => /* @__PURE__ */ jsx63(
5205
+ "button",
5206
+ {
5207
+ type: "button",
5208
+ className: "ss-w-sl-picker-opt",
5209
+ disabled: submitting,
5210
+ "aria-label": p.playerName,
5211
+ onClick: () => onSubmit(JSON.stringify({ guessedPlayerId: p.playerId })),
5212
+ children: p.playerName
5213
+ },
5214
+ p.playerId
5215
+ )) })
5216
+ ] });
5217
+ }
5218
+
5219
+ // src/components/games/FanMotmGame.tsx
5220
+ import { jsx as jsx64, jsxs as jsxs58 } from "react/jsx-runtime";
5221
+ function parseClientState10(json) {
5222
+ if (!json) return null;
5223
+ try {
5224
+ return JSON.parse(json);
5225
+ } catch {
5226
+ return null;
5227
+ }
5228
+ }
5229
+ function FanMotmGame({
5230
+ config,
5231
+ result,
5232
+ submitting,
5233
+ onSubmit
5234
+ }) {
5235
+ const clientState = parseClientState10(result?.clientStateJson);
5236
+ const alreadyVoted = result?.alreadyPlayed === true;
5237
+ if (!config) {
5238
+ return /* @__PURE__ */ jsx64("div", { className: "ss-w-motm", "data-testid": "game-screen-fan-motm", children: /* @__PURE__ */ jsx64("div", { className: "ss-w-gscreen-body", children: "Loading\u2026" }) });
5239
+ }
5240
+ const pickedId = clientState?.pickedPlayerId ?? null;
5241
+ function handleVote(candidateId) {
5242
+ if (alreadyVoted || submitting) return;
5243
+ onSubmit(JSON.stringify({ pickedPlayerId: candidateId }));
5244
+ }
5245
+ return /* @__PURE__ */ jsxs58("div", { className: "ss-w-motm", "data-testid": "game-screen-fan-motm", children: [
5246
+ /* @__PURE__ */ jsx64("div", { className: "ss-w-motm-title", children: "Man of the Match \u2014 your vote" }),
5247
+ /* @__PURE__ */ jsx64("div", { className: "ss-w-motm-votes", children: config.candidates.map((c) => {
5248
+ const isVoted = alreadyVoted && pickedId === c.playerId;
5249
+ const rowCls = `ss-w-motm-vrow${isVoted ? " ss-w-motm-vrow--voted" : ""}${alreadyVoted ? " ss-w-motm-vrow--result" : ""}`;
5250
+ return /* @__PURE__ */ jsxs58(
5251
+ "button",
5252
+ {
5253
+ type: "button",
5254
+ className: rowCls,
5255
+ "data-testid": `motm-candidate-${c.playerId}`,
5256
+ disabled: alreadyVoted || submitting,
5257
+ onClick: () => handleVote(c.playerId),
5258
+ children: [
5259
+ /* @__PURE__ */ jsx64("div", { className: "ss-w-motm-vfill", style: { width: isVoted ? "100%" : "0%" } }),
5260
+ /* @__PURE__ */ jsx64("span", { className: "ss-w-motm-vrow-name", children: c.playerName })
5261
+ ]
5262
+ },
5263
+ c.playerId
5264
+ );
5265
+ }) }),
5266
+ /* @__PURE__ */ jsx64("div", { className: "ss-w-motm-fun", children: "For fun \u2014 unranked community vote" })
5267
+ ] });
5268
+ }
5269
+
5270
+ // src/components/GameScreenHost.tsx
5271
+ import { jsx as jsx65, jsxs as jsxs59 } from "react/jsx-runtime";
5272
+ var FIXTURE_GAME_IDS = /* @__PURE__ */ new Set([
5273
+ "matchday-predictor",
5274
+ "starting-xi",
5275
+ "stat-ladder",
5276
+ "fan-motm"
5277
+ ]);
5278
+ var PENDING_POLL_INTERVAL_MS = 3e4;
5279
+ function safeParseConfig(json) {
5280
+ if (!json) return null;
5281
+ try {
5282
+ return JSON.parse(json);
5283
+ } catch {
5284
+ return null;
4095
5285
  }
4096
5286
  }
4097
5287
  function GameScreenHost({
4098
5288
  selected,
4099
5289
  onBack,
5290
+ conversation,
4100
5291
  ui
4101
5292
  }) {
4102
- return /* @__PURE__ */ jsxs49("div", { className: "ss-w-gscreen", children: [
4103
- /* @__PURE__ */ jsxs49("button", { type: "button", className: "ss-w-gscreen-back", onClick: onBack, children: [
4104
- /* @__PURE__ */ jsx55("span", { className: "ss-w-gscreen-back-arrow", "aria-hidden": "true", children: "\u2039" }),
5293
+ const { gameId, fixtureId, gameDay = "" } = selected;
5294
+ const [configJson, setConfigJson] = useState29(null);
5295
+ useEffect13(() => {
5296
+ setConfigJson(null);
5297
+ if (!conversation) return;
5298
+ let cancelled = false;
5299
+ void (async () => {
5300
+ try {
5301
+ const payload = await conversation.getGameConfig(gameId, fixtureId, gameDay || void 0);
5302
+ if (!cancelled && payload?.configJson) {
5303
+ setConfigJson(payload.configJson);
5304
+ }
5305
+ } catch {
5306
+ }
5307
+ })();
5308
+ return () => {
5309
+ cancelled = true;
5310
+ };
5311
+ }, [conversation, gameId, fixtureId, gameDay]);
5312
+ const { result, submitting, submit, hydrate, rawStatus } = useGamePlay(conversation, {
5313
+ gameId,
5314
+ gameDay,
5315
+ fixtureId: fixtureId || void 0
5316
+ });
5317
+ useEffect13(() => {
5318
+ if (conversation) hydrate();
5319
+ }, [conversation, gameId]);
5320
+ const hydrateRef = useRef12(hydrate);
5321
+ hydrateRef.current = hydrate;
5322
+ useEffect13(() => {
5323
+ if (!FIXTURE_GAME_IDS.has(gameId)) return;
5324
+ if (rawStatus !== "pending") return;
5325
+ const timerId = setInterval(() => {
5326
+ hydrateRef.current();
5327
+ }, PENDING_POLL_INTERVAL_MS);
5328
+ return () => clearInterval(timerId);
5329
+ }, [gameId, rawStatus]);
5330
+ const sharedProps = {
5331
+ result,
5332
+ submitting,
5333
+ onSubmit: submit,
5334
+ ui
5335
+ };
5336
+ function renderScreen() {
5337
+ switch (gameId) {
5338
+ case "mystery-tiger":
5339
+ return /* @__PURE__ */ jsx65(
5340
+ MysteryTigerGame,
5341
+ {
5342
+ ...sharedProps,
5343
+ config: safeParseConfig(configJson)
5344
+ }
5345
+ );
5346
+ case "higher-or-lower":
5347
+ return /* @__PURE__ */ jsx65(
5348
+ HigherOrLowerGame,
5349
+ {
5350
+ ...sharedProps,
5351
+ config: safeParseConfig(configJson)
5352
+ }
5353
+ );
5354
+ case "career-path":
5355
+ return /* @__PURE__ */ jsx65(
5356
+ CareerPathGame,
5357
+ {
5358
+ ...sharedProps,
5359
+ config: safeParseConfig(configJson)
5360
+ }
5361
+ );
5362
+ case "beat-the-grid":
5363
+ return /* @__PURE__ */ jsx65(
5364
+ BeatTheGridGame,
5365
+ {
5366
+ ...sharedProps,
5367
+ config: safeParseConfig(configJson),
5368
+ playerPool: []
5369
+ }
5370
+ );
5371
+ case "guess-the-xi":
5372
+ return /* @__PURE__ */ jsx65(
5373
+ GuessTheXiGame,
5374
+ {
5375
+ ...sharedProps,
5376
+ config: safeParseConfig(configJson)
5377
+ }
5378
+ );
5379
+ case "daily-quiz":
5380
+ return /* @__PURE__ */ jsx65(
5381
+ DailyQuizGame,
5382
+ {
5383
+ ...sharedProps,
5384
+ config: safeParseConfig(configJson)
5385
+ }
5386
+ );
5387
+ case "matchday-predictor":
5388
+ return /* @__PURE__ */ jsx65(
5389
+ MatchdayPredictorGame,
5390
+ {
5391
+ ...sharedProps,
5392
+ config: safeParseConfig(configJson)
5393
+ }
5394
+ );
5395
+ case "starting-xi":
5396
+ return /* @__PURE__ */ jsx65(
5397
+ StartingXiGame,
5398
+ {
5399
+ ...sharedProps,
5400
+ config: safeParseConfig(configJson)
5401
+ }
5402
+ );
5403
+ case "stat-ladder":
5404
+ return /* @__PURE__ */ jsx65(
5405
+ StatLadderGame,
5406
+ {
5407
+ ...sharedProps,
5408
+ config: safeParseConfig(configJson)
5409
+ }
5410
+ );
5411
+ case "fan-motm":
5412
+ return /* @__PURE__ */ jsx65(
5413
+ FanMotmGame,
5414
+ {
5415
+ ...sharedProps,
5416
+ config: safeParseConfig(configJson)
5417
+ }
5418
+ );
5419
+ default:
5420
+ return /* @__PURE__ */ jsx65("div", { className: "ss-w-gscreen-body", "data-testid": `game-screen-${gameId}`, children: gameId });
5421
+ }
5422
+ }
5423
+ return /* @__PURE__ */ jsxs59("div", { className: "ss-w-gscreen", children: [
5424
+ /* @__PURE__ */ jsxs59("button", { type: "button", className: "ss-w-gscreen-back", onClick: onBack, children: [
5425
+ /* @__PURE__ */ jsx65("span", { className: "ss-w-gscreen-back-arrow", "aria-hidden": "true", children: "\u2039" }),
4105
5426
  ui.back
4106
5427
  ] }),
4107
- resolveScreen(selected.gameId)
5428
+ renderScreen()
4108
5429
  ] });
4109
5430
  }
4110
5431
 
4111
5432
  // src/components/GamesPanel.tsx
4112
- import { jsx as jsx56 } from "react/jsx-runtime";
5433
+ import { jsx as jsx66 } from "react/jsx-runtime";
4113
5434
  function GamesPanel({
4114
5435
  loggedIn,
4115
5436
  games,
4116
5437
  ui,
4117
5438
  conversation
4118
5439
  }) {
4119
- const [selected, setSelected] = useState22(null);
4120
- if (!loggedIn) return /* @__PURE__ */ jsx56(GamesLoginWall, { ui });
5440
+ const [selected, setSelected] = useState30(null);
5441
+ if (!loggedIn) return /* @__PURE__ */ jsx66(GamesLoginWall, { ui });
4121
5442
  if (selected !== null) {
4122
- return /* @__PURE__ */ jsx56("div", { className: "ss-w-mon-list", children: /* @__PURE__ */ jsx56(
5443
+ return /* @__PURE__ */ jsx66("div", { className: "ss-w-mon-list", children: /* @__PURE__ */ jsx66(
4123
5444
  GameScreenHost,
4124
5445
  {
4125
5446
  selected,
4126
5447
  onBack: () => setSelected(null),
4127
- conversation,
5448
+ conversation: conversation ?? null,
4128
5449
  ui
4129
5450
  }
4130
5451
  ) });
4131
5452
  }
4132
- return /* @__PURE__ */ jsx56("div", { className: "ss-w-mon-list", "data-testid": "games-hub", children: /* @__PURE__ */ jsx56(
5453
+ return /* @__PURE__ */ jsx66("div", { className: "ss-w-mon-list", "data-testid": "games-hub", children: /* @__PURE__ */ jsx66(
4133
5454
  GamesHub,
4134
5455
  {
4135
5456
  games,
4136
- onOpen: (g) => setSelected({ gameId: g.gameId, fixtureId: g.fixtureId }),
5457
+ onOpen: (g) => setSelected({ gameId: g.gameId, fixtureId: g.fixtureId, gameDay: g.gameDay }),
4137
5458
  ui
4138
5459
  }
4139
5460
  ) });
4140
5461
  }
4141
5462
 
4142
5463
  // src/components/Leaderboard.tsx
4143
- import { useState as useState23 } from "react";
5464
+ import { useState as useState31 } from "react";
4144
5465
 
4145
5466
  // src/components/LeaderboardRow.tsx
4146
- import { jsx as jsx57, jsxs as jsxs50 } from "react/jsx-runtime";
5467
+ import { jsx as jsx67, jsxs as jsxs60 } from "react/jsx-runtime";
4147
5468
  function formatPoints(n) {
4148
5469
  return n.toLocaleString("en");
4149
5470
  }
@@ -4152,14 +5473,14 @@ function LeaderboardWeeklyRow({
4152
5473
  isMe
4153
5474
  }) {
4154
5475
  const rankClass = `ss-w-lb2-rank${row.rank <= 3 ? " ss-w-lb2-rank--top3" : ""}`;
4155
- return /* @__PURE__ */ jsxs50("div", { className: `ss-w-lb2-row${isMe ? " ss-w-lb2-row--you" : ""}`, children: [
4156
- /* @__PURE__ */ jsxs50("span", { className: rankClass, children: [
5476
+ return /* @__PURE__ */ jsxs60("div", { className: `ss-w-lb2-row${isMe ? " ss-w-lb2-row--you" : ""}`, children: [
5477
+ /* @__PURE__ */ jsxs60("span", { className: rankClass, children: [
4157
5478
  "#",
4158
5479
  row.rank
4159
5480
  ] }),
4160
- /* @__PURE__ */ jsx57("span", { className: "ss-w-lb2-handle", children: row.handle }),
4161
- /* @__PURE__ */ jsx57("span", { className: "ss-w-lb2-points", children: formatPoints(row.points) }),
4162
- row.currentStreak > 0 && /* @__PURE__ */ jsxs50("span", { className: "ss-w-lb2-streak", children: [
5481
+ /* @__PURE__ */ jsx67("span", { className: "ss-w-lb2-handle", children: row.handle }),
5482
+ /* @__PURE__ */ jsx67("span", { className: "ss-w-lb2-points", children: formatPoints(row.points) }),
5483
+ row.currentStreak > 0 && /* @__PURE__ */ jsxs60("span", { className: "ss-w-lb2-streak", children: [
4163
5484
  "\u{1F525} ",
4164
5485
  row.currentStreak
4165
5486
  ] })
@@ -4170,28 +5491,28 @@ function LeaderboardAllTimeRow({
4170
5491
  isMe
4171
5492
  }) {
4172
5493
  const rankClass = `ss-w-lb2-rank${row.rank <= 3 ? " ss-w-lb2-rank--top3" : ""}`;
4173
- return /* @__PURE__ */ jsxs50("div", { className: `ss-w-lb2-row${isMe ? " ss-w-lb2-row--you" : ""}`, children: [
4174
- /* @__PURE__ */ jsxs50("span", { className: rankClass, children: [
5494
+ return /* @__PURE__ */ jsxs60("div", { className: `ss-w-lb2-row${isMe ? " ss-w-lb2-row--you" : ""}`, children: [
5495
+ /* @__PURE__ */ jsxs60("span", { className: rankClass, children: [
4175
5496
  "#",
4176
5497
  row.rank
4177
5498
  ] }),
4178
- /* @__PURE__ */ jsx57("span", { className: "ss-w-lb2-handle", children: row.handle }),
4179
- /* @__PURE__ */ jsx57("span", { className: "ss-w-lb2-points", children: formatPoints(row.allTimePoints) })
5499
+ /* @__PURE__ */ jsx67("span", { className: "ss-w-lb2-handle", children: row.handle }),
5500
+ /* @__PURE__ */ jsx67("span", { className: "ss-w-lb2-points", children: formatPoints(row.allTimePoints) })
4180
5501
  ] });
4181
5502
  }
4182
5503
 
4183
5504
  // src/components/Leaderboard.tsx
4184
- import { jsx as jsx58, jsxs as jsxs51 } from "react/jsx-runtime";
5505
+ import { jsx as jsx68, jsxs as jsxs61 } from "react/jsx-runtime";
4185
5506
  function Leaderboard({
4186
5507
  data,
4187
5508
  ui
4188
5509
  }) {
4189
- const [segment, setSegment] = useState23("weekly");
5510
+ const [segment, setSegment] = useState31("weekly");
4190
5511
  const myRow = segment === "weekly" ? data.myWeeklyRow : data.myAllTimeRow;
4191
5512
  const offBoard = myRow !== null && myRow !== void 0 && myRow.rank === -1;
4192
- return /* @__PURE__ */ jsxs51("div", { className: "ss-w-lb2", children: [
4193
- /* @__PURE__ */ jsxs51("div", { className: "ss-w-seg", children: [
4194
- /* @__PURE__ */ jsx58(
5513
+ return /* @__PURE__ */ jsxs61("div", { className: "ss-w-lb2", children: [
5514
+ /* @__PURE__ */ jsxs61("div", { className: "ss-w-seg", children: [
5515
+ /* @__PURE__ */ jsx68(
4195
5516
  "button",
4196
5517
  {
4197
5518
  type: "button",
@@ -4201,7 +5522,7 @@ function Leaderboard({
4201
5522
  children: ui.leaderboardThisWeek
4202
5523
  }
4203
5524
  ),
4204
- /* @__PURE__ */ jsx58(
5525
+ /* @__PURE__ */ jsx68(
4205
5526
  "button",
4206
5527
  {
4207
5528
  type: "button",
@@ -4212,14 +5533,14 @@ function Leaderboard({
4212
5533
  }
4213
5534
  )
4214
5535
  ] }),
4215
- /* @__PURE__ */ jsx58("div", { className: "ss-w-lb2-rows", children: segment === "weekly" ? data.weeklyTop.map((row) => /* @__PURE__ */ jsx58(
5536
+ /* @__PURE__ */ jsx68("div", { className: "ss-w-lb2-rows", children: segment === "weekly" ? data.weeklyTop.map((row) => /* @__PURE__ */ jsx68(
4216
5537
  LeaderboardWeeklyRow,
4217
5538
  {
4218
5539
  row,
4219
5540
  isMe: row.operatorUserId === data.myWeeklyRow?.operatorUserId
4220
5541
  },
4221
5542
  row.operatorUserId
4222
- )) : data.allTimeTop.map((row) => /* @__PURE__ */ jsx58(
5543
+ )) : data.allTimeTop.map((row) => /* @__PURE__ */ jsx68(
4223
5544
  LeaderboardAllTimeRow,
4224
5545
  {
4225
5546
  row,
@@ -4227,29 +5548,29 @@ function Leaderboard({
4227
5548
  },
4228
5549
  row.operatorUserId
4229
5550
  )) }),
4230
- /* @__PURE__ */ jsx58("div", { className: "ss-w-kitline", children: "\u{1F3BD}" }),
4231
- myRow != null && (offBoard ? /* @__PURE__ */ jsx58("div", { className: "ss-w-lbgap", children: ui.leaderboardOffBoard }) : /* @__PURE__ */ jsxs51("div", { className: "ss-w-lb2-rows", children: [
4232
- segment === "weekly" && data.myWeeklyRow && /* @__PURE__ */ jsx58(LeaderboardWeeklyRow, { row: data.myWeeklyRow, isMe: true }),
4233
- segment === "alltime" && data.myAllTimeRow && /* @__PURE__ */ jsx58(LeaderboardAllTimeRow, { row: data.myAllTimeRow, isMe: true })
5551
+ /* @__PURE__ */ jsx68("div", { className: "ss-w-kitline", children: "\u{1F3BD}" }),
5552
+ myRow != null && (offBoard ? /* @__PURE__ */ jsx68("div", { className: "ss-w-lbgap", children: ui.leaderboardOffBoard }) : /* @__PURE__ */ jsxs61("div", { className: "ss-w-lb2-rows", children: [
5553
+ segment === "weekly" && data.myWeeklyRow && /* @__PURE__ */ jsx68(LeaderboardWeeklyRow, { row: data.myWeeklyRow, isMe: true }),
5554
+ segment === "alltime" && data.myAllTimeRow && /* @__PURE__ */ jsx68(LeaderboardAllTimeRow, { row: data.myAllTimeRow, isMe: true })
4234
5555
  ] })),
4235
- segment === "alltime" && data.myAllTimeRow && !offBoard && /* @__PURE__ */ jsx58("div", { className: "ss-w-lb2-rows", children: /* @__PURE__ */ jsx58("div", { className: "ss-w-lb2-row", children: /* @__PURE__ */ jsx58("span", { className: "ss-w-lb2-handle", children: formatPoints(data.allTimeTop.reduce((s, r) => s + r.allTimePoints, 0)) }) }) })
5556
+ segment === "alltime" && data.myAllTimeRow && !offBoard && /* @__PURE__ */ jsx68("div", { className: "ss-w-lb2-rows", children: /* @__PURE__ */ jsx68("div", { className: "ss-w-lb2-row", children: /* @__PURE__ */ jsx68("span", { className: "ss-w-lb2-handle", children: formatPoints(data.allTimeTop.reduce((s, r) => s + r.allTimePoints, 0)) }) }) })
4236
5557
  ] });
4237
5558
  }
4238
5559
 
4239
5560
  // src/components/LeaderboardPanel.tsx
4240
- import { jsx as jsx59 } from "react/jsx-runtime";
5561
+ import { jsx as jsx69 } from "react/jsx-runtime";
4241
5562
  function LeaderboardPanel({ loggedIn, data, ui }) {
4242
- if (!loggedIn) return /* @__PURE__ */ jsx59(GamesLoginWall, { ui });
4243
- if (data === null) return /* @__PURE__ */ jsx59("div", { className: "ss-w-mon-list" });
4244
- return /* @__PURE__ */ jsx59("div", { className: "ss-w-mon-list", children: /* @__PURE__ */ jsx59(Leaderboard, { data, ui }) });
5563
+ if (!loggedIn) return /* @__PURE__ */ jsx69(GamesLoginWall, { ui });
5564
+ if (data === null) return /* @__PURE__ */ jsx69("div", { className: "ss-w-mon-list" });
5565
+ return /* @__PURE__ */ jsx69("div", { className: "ss-w-mon-list", children: /* @__PURE__ */ jsx69(Leaderboard, { data, ui }) });
4245
5566
  }
4246
5567
 
4247
5568
  // src/components/ChatPanel.tsx
4248
- import { jsx as jsx60, jsxs as jsxs52 } from "react/jsx-runtime";
5569
+ import { jsx as jsx70, jsxs as jsxs62 } from "react/jsx-runtime";
4249
5570
  var FULLSCREEN_QUERY = "(max-width: 639px)";
4250
5571
  function useIsModal() {
4251
- const [modal, setModal] = useState24(false);
4252
- useEffect13(() => {
5572
+ const [modal, setModal] = useState32(false);
5573
+ useEffect14(() => {
4253
5574
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
4254
5575
  const mql = window.matchMedia(FULLSCREEN_QUERY);
4255
5576
  const sync = () => setModal(mql.matches);
@@ -4259,10 +5580,10 @@ function useIsModal() {
4259
5580
  }, []);
4260
5581
  return modal;
4261
5582
  }
4262
- function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, gamesResult, leaderboardResult, loggedIn }) {
5583
+ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, gamesResult, leaderboardResult, loggedIn, conversation }) {
4263
5584
  const { state, close, stop, send, reset } = controller;
4264
5585
  const isModal = useIsModal();
4265
- useEffect13(() => {
5586
+ useEffect14(() => {
4266
5587
  const onKey = (e) => {
4267
5588
  if (e.key === "Escape") close();
4268
5589
  };
@@ -4273,11 +5594,11 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
4273
5594
  monitor?.setActiveTab("chat");
4274
5595
  send(text);
4275
5596
  };
4276
- useEffect13(() => {
5597
+ useEffect14(() => {
4277
5598
  if (monitor && monitor.activeTab !== "chat" && state.isStreaming) monitor.markChatActivity();
4278
5599
  }, [monitor, state.isStreaming, state.messages.length]);
4279
- return /* @__PURE__ */ jsxs52("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
4280
- /* @__PURE__ */ jsx60(
5600
+ return /* @__PURE__ */ jsxs62("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
5601
+ /* @__PURE__ */ jsx70(
4281
5602
  ChatHeader,
4282
5603
  {
4283
5604
  onClose: close,
@@ -4290,7 +5611,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
4290
5611
  moreOptionsLabel: ui.moreOptions
4291
5612
  }
4292
5613
  ),
4293
- monitor && /* @__PURE__ */ jsx60(
5614
+ monitor && /* @__PURE__ */ jsx70(
4294
5615
  TabBar,
4295
5616
  {
4296
5617
  monitor,
@@ -4302,19 +5623,19 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
4302
5623
  gamesEnabled: gamesResult?.enabled ?? false
4303
5624
  }
4304
5625
  ),
4305
- /* @__PURE__ */ jsx60(
5626
+ /* @__PURE__ */ jsx70(
4306
5627
  "div",
4307
5628
  {
4308
5629
  style: !monitor || monitor.activeTab === "chat" ? { display: "contents" } : { position: "absolute", inset: 0, visibility: "hidden", display: "flex", flexDirection: "column" },
4309
- children: /* @__PURE__ */ jsx60(MessageList, { controller, ui, slip, onDashboardTap, monitor })
5630
+ children: /* @__PURE__ */ jsx70(MessageList, { controller, ui, slip, onDashboardTap, monitor })
4310
5631
  }
4311
5632
  ),
4312
- monitor && discovery && monitor.activeTab === "today" && /* @__PURE__ */ jsx60(TodayPanel, { discovery, ui, monitor }),
4313
- monitor && monitor.activeTab === "following" && /* @__PURE__ */ jsx60(MatchesPanel, { monitor, ui, slip }),
4314
- monitor && monitor.activeTab === "odds" && /* @__PURE__ */ jsx60(OddsPanel, { monitor, ui, slip }),
4315
- monitor && monitor.activeTab === "games" && gamesResult && /* @__PURE__ */ jsx60(GamesPanel, { loggedIn: !!loggedIn, games: gamesResult.games, ui }),
4316
- monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */ jsx60(LeaderboardPanel, { loggedIn: !!loggedIn, data: leaderboardResult.data, ui }),
4317
- /* @__PURE__ */ jsx60(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: handleSend, onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
5633
+ monitor && discovery && monitor.activeTab === "today" && /* @__PURE__ */ jsx70(TodayPanel, { discovery, ui, monitor }),
5634
+ monitor && monitor.activeTab === "following" && /* @__PURE__ */ jsx70(MatchesPanel, { monitor, ui, slip }),
5635
+ monitor && monitor.activeTab === "odds" && /* @__PURE__ */ jsx70(OddsPanel, { monitor, ui, slip }),
5636
+ monitor && monitor.activeTab === "games" && gamesResult && /* @__PURE__ */ jsx70(GamesPanel, { loggedIn: !!loggedIn, games: gamesResult.games, ui, conversation }),
5637
+ monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */ jsx70(LeaderboardPanel, { loggedIn: !!loggedIn, data: leaderboardResult.data, ui }),
5638
+ /* @__PURE__ */ jsx70(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: handleSend, onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
4318
5639
  ] });
4319
5640
  }
4320
5641
 
@@ -5267,10 +6588,315 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
5267
6588
 
5268
6589
  /* Off-board treatment: dashed pill indicating rank is outside top N */
5269
6590
  .ss-w-lbgap { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 8px 12px; font-size: 11.5px; color: var(--ss-w-faint); border: 1px dashed var(--ss-w-line); border-radius: 10px; }
6591
+
6592
+ /* ---- Mystery Tiger game screen (D3) ---- */
6593
+ .ss-w-gt { display: flex; flex-direction: column; gap: 10px; }
6594
+ /* Typeahead wrapper: relative so the dropdown can position under it */
6595
+ .ss-w-gtta { position: relative; }
6596
+ .ss-w-gtta-i { width: 100%; box-sizing: border-box; padding: 9px 12px; border-radius: 10px; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); color: var(--ss-w-ink); font: inherit; font-size: 13px; outline: none; transition: border-color .12s; }
6597
+ .ss-w-gtta-i:focus { border-color: var(--ss-w-accent); }
6598
+ .ss-w-gtta-i:disabled { opacity: .5; cursor: not-allowed; }
6599
+ .ss-w-gtta-drop { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 10; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 10px; max-height: 180px; overflow-y: auto; box-shadow: 0 4px 16px rgba(0,0,0,.12); }
6600
+ .ss-w-gtta-opt { padding: 9px 12px; font-size: 13px; cursor: pointer; color: var(--ss-w-ink); border-bottom: 1px solid var(--ss-w-line); }
6601
+ .ss-w-gtta-opt:last-child { border-bottom: none; }
6602
+ .ss-w-gtta-opt:hover { background: var(--ss-w-bubble); }
6603
+ /* Column header labels: one cell per attribute */
6604
+ .ss-w-gthdr { display: grid; grid-template-columns: 1fr repeat(5, 36px); gap: 4px; padding: 0 2px; }
6605
+ .ss-w-gthdr-c { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
6606
+ .ss-w-gthdr-c--name { text-align: left; }
6607
+ /* Guess rows */
6608
+ .ss-w-gtrows { display: flex; flex-direction: column; gap: 4px; }
6609
+ .ss-w-gtrow { display: grid; grid-template-columns: 1fr repeat(5, 36px); gap: 4px; align-items: center; }
6610
+ .ss-w-gtrow-nm { font-size: 12px; font-weight: 600; color: var(--ss-w-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-right: 4px; }
6611
+ /* Verdict tiles */
6612
+ .ss-w-gtile { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 36px; border-radius: 8px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; gap: 1px; transition: transform .1s; }
6613
+ .ss-w-gtile--exact { background: var(--ss-w-correct); color: #fff; }
6614
+ .ss-w-gtile--near { background: var(--ss-w-cta); color: var(--ss-w-cta-ink, #fff); }
6615
+ .ss-w-gtile--miss { background: var(--ss-w-bubble); color: var(--ss-w-faint); border: 1px solid var(--ss-w-line); }
6616
+ .ss-w-gtile-dir { font-size: 11px; line-height: 1; }
6617
+ /* Hint line */
6618
+ .ss-w-gt-hint { font-size: 11.5px; color: var(--ss-w-faint); text-align: center; padding: 2px 0; font-style: italic; }
6619
+ /* Outcome banner: shown when solved or failed */
6620
+ .ss-w-gt-banner { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 14px 12px; border-radius: 12px; text-align: center; }
6621
+ .ss-w-gt-banner--solved { background: color-mix(in srgb, var(--ss-w-correct) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-correct) 28%, var(--ss-w-line)); }
6622
+ .ss-w-gt-banner--failed { background: color-mix(in srgb, var(--ss-w-wrong) 8%, var(--ss-w-panel)); border: 1px solid var(--ss-w-line); }
6623
+ .ss-w-gt-banner-name { font-size: 16px; font-weight: 750; color: var(--ss-w-ink); }
6624
+ .ss-w-gt-banner-label { font-size: 12px; color: var(--ss-w-faint); }
6625
+
6626
+ /* ---- Higher or Lower game screen (D3) ---- */
6627
+ .ss-w-hl { display: flex; flex-direction: column; gap: 12px; }
6628
+ /* Stat context header: family name + season */
6629
+ .ss-w-hlctx { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
6630
+ .ss-w-hlctx-fam { font-size: 14px; font-weight: 750; color: var(--ss-w-ink); }
6631
+ .ss-w-hlctx-szn { font-size: 11.5px; color: var(--ss-w-faint); font-weight: 500; }
6632
+ /* Score chip */
6633
+ .ss-w-hl-score { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 11%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 22%, var(--ss-w-line)); border-radius: 999px; padding: 3px 10px; }
6634
+ /* Pair card: two player cards + divider */
6635
+ .ss-w-hlpair { display: flex; align-items: stretch; gap: 0; border-radius: 14px; border: 1.5px solid var(--ss-w-line); overflow: hidden; background: var(--ss-w-panel); }
6636
+ /* Player card */
6637
+ .ss-w-hlpc { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 14px 10px; text-align: center; }
6638
+ .ss-w-hlpc--b { border-left: 1px solid var(--ss-w-line); }
6639
+ .ss-w-hlpc-name { font-size: 12.5px; font-weight: 650; color: var(--ss-w-ink); line-height: 1.25; word-break: break-word; min-width: 0; }
6640
+ .ss-w-hlpc-val { font-size: 22px; font-weight: 800; color: var(--ss-w-accent); font-variant-numeric: tabular-nums; line-height: 1; }
6641
+ .ss-w-hlpc-val--hidden { color: var(--ss-w-faint); font-size: 26px; }
6642
+ .ss-w-hlpc-val--correct { color: var(--ss-w-correct); }
6643
+ .ss-w-hlpc-val--wrong { color: var(--ss-w-cta); }
6644
+ .ss-w-hlpc-badge { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; border-radius: 999px; padding: 2px 8px; }
6645
+ .ss-w-hlpc-badge--correct { color: #fff; background: var(--ss-w-correct); }
6646
+ .ss-w-hlpc-badge--wrong { color: var(--ss-w-cta-ink, #fff); background: var(--ss-w-cta); }
6647
+ /* Higher / Lower buttons */
6648
+ .ss-w-hlbtns { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
6649
+ .ss-w-hlbtn { padding: 11px; border-radius: 10px; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); color: var(--ss-w-ink); font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; transition: background .1s, border-color .1s, color .1s; }
6650
+ .ss-w-hlbtn:hover:not(:disabled) { background: var(--ss-w-bubble); border-color: var(--ss-w-accent); color: var(--ss-w-accent); }
6651
+ .ss-w-hlbtn:disabled { opacity: .5; cursor: not-allowed; }
6652
+ /* Answered pair result rows */
6653
+ .ss-w-hlresults { display: flex; flex-direction: column; gap: 4px; }
6654
+ .ss-w-hlrrow { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 8px; font-size: 12px; background: var(--ss-w-bubble); }
6655
+ .ss-w-hlrrow--correct { background: color-mix(in srgb, var(--ss-w-correct) 8%, var(--ss-w-panel)); }
6656
+ .ss-w-hlrrow--wrong { background: color-mix(in srgb, var(--ss-w-cta) 8%, var(--ss-w-panel)); }
6657
+ .ss-w-hlrrow-names { flex: 1; min-width: 0; font-weight: 600; color: var(--ss-w-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
6658
+ .ss-w-hlrrow-pick { font-size: 11px; font-weight: 700; text-transform: uppercase; color: var(--ss-w-faint); }
6659
+ .ss-w-hlrrow-icon { font-size: 14px; flex: none; }
6660
+
6661
+ /* ---- Career Path game screen (D3) ---- */
6662
+ .ss-w-cpath { display: flex; flex-direction: column; gap: 12px; }
6663
+ /* Context header */
6664
+ .ss-w-cpath-ctx { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 4px; }
6665
+ .ss-w-cpath-ctx-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); }
6666
+ .ss-w-cpath-ctx-count { font-size: 12px; font-weight: 600; color: var(--ss-w-accent); }
6667
+ /* Career path: vertical timeline of club spells */
6668
+ .ss-w-cpath-path { display: flex; flex-direction: column; gap: 4px; }
6669
+ /* Single club card */
6670
+ .ss-w-cpath-club { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 10px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); }
6671
+ .ss-w-cpath-club--loan { border-style: dashed; }
6672
+ /* Crest image / fallback */
6673
+ .ss-w-cpath-crest { width: 30px; height: 30px; object-fit: contain; flex: none; border-radius: 4px; }
6674
+ .ss-w-cpath-crest--fallback { background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 4px; }
6675
+ /* Club info: name + years */
6676
+ .ss-w-cpath-club-info { display: flex; flex: 1; min-width: 0; align-items: center; gap: 6px; flex-wrap: wrap; }
6677
+ .ss-w-cpath-club-name { font-size: 13px; font-weight: 650; color: var(--ss-w-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
6678
+ .ss-w-cpath-spell-yr { font-size: 11px; color: var(--ss-w-faint); font-weight: 500; white-space: nowrap; }
6679
+ .ss-w-cpath-loan-badge { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; padding: 1px 7px; border-radius: 999px; background: color-mix(in srgb, var(--ss-w-cta) 15%, var(--ss-w-panel)); color: var(--ss-w-cta-ink, #1b1300); border: 1px solid color-mix(in srgb, var(--ss-w-cta) 30%, var(--ss-w-line)); flex: none; }
6680
+ /* Hidden clubs indicator */
6681
+ .ss-w-cpath-hidden { font-size: 11.5px; color: var(--ss-w-faint); font-style: italic; text-align: center; padding: 6px 0 2px; }
6682
+ /* Hint line */
6683
+ .ss-w-cpath-hint { font-size: 11.5px; color: var(--ss-w-faint); text-align: center; padding: 2px 0; font-style: italic; }
6684
+ /* Outcome banner */
6685
+ .ss-w-cpath-banner { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 14px 12px; border-radius: 12px; text-align: center; }
6686
+ .ss-w-cpath-banner--solved { background: color-mix(in srgb, var(--ss-w-correct) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-correct) 28%, var(--ss-w-line)); }
6687
+ .ss-w-cpath-banner--failed { background: color-mix(in srgb, var(--ss-w-wrong) 8%, var(--ss-w-panel)); border: 1px solid var(--ss-w-line); }
6688
+ .ss-w-cpath-banner-name { font-size: 16px; font-weight: 750; color: var(--ss-w-ink); }
6689
+ .ss-w-cpath-banner-label { font-size: 12px; color: var(--ss-w-faint); }
6690
+
6691
+ /* ---- Beat the Grid game screen (D3) ---- */
6692
+ .ss-w-btg { display: flex; flex-direction: column; gap: 10px; }
6693
+ /* Score/context row */
6694
+ .ss-w-btg-ctx { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
6695
+ .ss-w-btg-ctx-score { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
6696
+ .ss-w-btg-ctx-left { font-size: 12px; color: var(--ss-w-faint); font-weight: 500; }
6697
+ .ss-w-btg-ctx-done { font-size: 12px; font-weight: 700; color: var(--ss-w-correct); }
6698
+ /* Grid layout \u2014 col count driven inline via style */
6699
+ .ss-w-btg-grid { display: grid; gap: 4px; width: 100%; }
6700
+ /* Generic grid cell */
6701
+ .ss-w-btg-gc { display: flex; align-items: center; justify-content: center; text-align: center; border-radius: 8px; min-height: 42px; padding: 4px; font-size: 11px; word-break: break-word; line-height: 1.2; }
6702
+ /* Corner placeholder */
6703
+ .ss-w-btg-gc--corner { background: none; }
6704
+ /* Column header */
6705
+ .ss-w-btg-gc--colhdr { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); }
6706
+ /* Row header */
6707
+ .ss-w-btg-gc--rowhdr { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); writing-mode: horizontal-tb; }
6708
+ /* Interactive cells */
6709
+ .ss-w-btg-cell { flex-direction: column; gap: 2px; cursor: pointer; background: var(--ss-w-panel); border: 1.5px solid var(--ss-w-line); transition: border-color .12s, background .12s; }
6710
+ .ss-w-btg-cell:hover:not(:disabled) { border-color: var(--ss-w-accent); background: var(--ss-w-bubble); }
6711
+ .ss-w-btg-cell:disabled { cursor: not-allowed; opacity: .75; }
6712
+ .ss-w-btg-cell--correct { background: color-mix(in srgb, var(--ss-w-correct) 14%, var(--ss-w-panel)); border-color: color-mix(in srgb, var(--ss-w-correct) 35%, var(--ss-w-line)); }
6713
+ .ss-w-btg-cell--wrong { background: color-mix(in srgb, var(--ss-w-wrong) 10%, var(--ss-w-panel)); }
6714
+ .ss-w-btg-cell--active { border-color: var(--ss-w-accent); }
6715
+ .ss-w-btg-cell-score { font-size: 14px; font-weight: 800; color: var(--ss-w-ink); font-variant-numeric: tabular-nums; }
6716
+ .ss-w-btg-cell-check { font-size: 12px; color: var(--ss-w-correct); line-height: 1; }
6717
+ .ss-w-btg-cell-empty { font-size: 16px; color: var(--ss-w-faint); }
6718
+ /* Scoped search sheet: slides up over the grid */
6719
+ .ss-w-btg-sheet { display: flex; flex-direction: column; gap: 10px; padding: 12px; background: var(--ss-w-panel); border: 1.5px solid var(--ss-w-line); border-radius: 14px; box-shadow: 0 4px 20px rgba(0,0,0,.1); }
6720
+ .ss-w-btg-sheet-hdr { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
6721
+ .ss-w-btg-sheet-ctx { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
6722
+ .ss-w-btg-sheet-row { font-size: 12.5px; font-weight: 700; color: var(--ss-w-ink); }
6723
+ .ss-w-btg-sheet-sep { font-size: 13px; color: var(--ss-w-faint); font-weight: 500; }
6724
+ .ss-w-btg-sheet-col { font-size: 12.5px; font-weight: 700; color: var(--ss-w-accent); }
6725
+ .ss-w-btg-sheet-close { background: none; border: none; cursor: pointer; font-size: 14px; color: var(--ss-w-faint); padding: 2px 6px; border-radius: 6px; line-height: 1; font: inherit; }
6726
+ .ss-w-btg-sheet-close:hover { color: var(--ss-w-ink); background: var(--ss-w-bubble); }
6727
+
6728
+ /* ---- Guess the XI game screen (D3) ---- */
6729
+ .ss-w-gxi { display: flex; flex-direction: column; gap: 12px; }
6730
+ /* Context label (pool label) */
6731
+ .ss-w-gxi-ctx { display: flex; align-items: center; }
6732
+ .ss-w-gxi-ctx-label { font-size: 12px; font-weight: 700; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .05em; }
6733
+ /* Positions pitch: compact slot grid */
6734
+ .ss-w-gxi-pitch { display: flex; flex-wrap: wrap; gap: 5px; }
6735
+ .ss-w-gxi-pslot { display: flex; flex-direction: column; align-items: center; gap: 2px; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 8px; padding: 6px 10px; min-width: 52px; }
6736
+ .ss-w-gxi-pslot-pos { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-accent); }
6737
+ .ss-w-gxi-pslot-nat { font-size: 10px; color: var(--ss-w-faint); font-weight: 500; text-align: center; line-height: 1.2; }
6738
+ /* Earned reveals */
6739
+ .ss-w-gxi-reveals { display: flex; flex-direction: column; gap: 4px; }
6740
+ .ss-w-gxi-reveal { display: flex; align-items: center; gap: 6px; padding: 7px 10px; background: color-mix(in srgb, var(--ss-w-cta) 8%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-cta) 22%, var(--ss-w-line)); border-radius: 8px; }
6741
+ .ss-w-gxi-reveal-slot { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--ss-w-cta-ink, #1b1300); background: var(--ss-w-cta); border-radius: 6px; padding: 1px 6px; flex: none; }
6742
+ .ss-w-gxi-reveal-val { font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
6743
+ /* Team pool: responsive flex grid of buttons */
6744
+ .ss-w-gxi-pool { display: flex; flex-wrap: wrap; gap: 6px; }
6745
+ .ss-w-gxi-team-btn { font: inherit; font-size: 12.5px; font-weight: 650; border-radius: 10px; padding: 9px 14px; cursor: pointer; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); color: var(--ss-w-ink); transition: background .1s, border-color .1s; }
6746
+ .ss-w-gxi-team-btn:hover:not(:disabled) { background: var(--ss-w-bubble); border-color: var(--ss-w-accent); }
6747
+ .ss-w-gxi-team-btn:disabled { opacity: .5; cursor: not-allowed; }
6748
+ .ss-w-gxi-team-btn--correct { background: color-mix(in srgb, var(--ss-w-correct) 14%, var(--ss-w-panel)); border-color: color-mix(in srgb, var(--ss-w-correct) 35%, var(--ss-w-line)); color: var(--ss-w-correct); }
6749
+ .ss-w-gxi-team-btn--wrong { background: color-mix(in srgb, var(--ss-w-wrong) 8%, var(--ss-w-panel)); text-decoration: line-through; opacity: .65; }
6750
+ /* Outcome banner */
6751
+ .ss-w-gxi-banner { display: flex; align-items: center; justify-content: center; padding: 10px 12px; border-radius: 10px; }
6752
+ .ss-w-gxi-banner--solved { background: color-mix(in srgb, var(--ss-w-correct) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-correct) 28%, var(--ss-w-line)); }
6753
+ .ss-w-gxi-banner--failed { background: color-mix(in srgb, var(--ss-w-wrong) 8%, var(--ss-w-panel)); border: 1px solid var(--ss-w-line); }
6754
+ .ss-w-gxi-banner-label { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
6755
+ /* Hint line */
6756
+ .ss-w-gxi-hint { font-size: 11.5px; color: var(--ss-w-faint); text-align: center; font-style: italic; }
6757
+
6758
+ /* ---- Daily Quiz game screen (D3) ---- */
6759
+ .ss-w-dq { display: flex; flex-direction: column; gap: 14px; }
6760
+ /* Score banner (post-reveal) */
6761
+ .ss-w-dq-score { display: flex; align-items: baseline; justify-content: center; gap: 4px; padding: 10px; background: color-mix(in srgb, var(--ss-w-correct) 10%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-correct) 28%, var(--ss-w-line)); border-radius: 12px; }
6762
+ .ss-w-dq-score-val { font-size: 26px; font-weight: 800; color: var(--ss-w-correct); font-variant-numeric: tabular-nums; }
6763
+ .ss-w-dq-score-label { font-size: 13px; font-weight: 650; color: var(--ss-w-faint); }
6764
+ /* Progress bar */
6765
+ .ss-w-dq-prog { height: 4px; border-radius: 999px; background: var(--ss-w-bubble); overflow: hidden; }
6766
+ .ss-w-dq-prog-fill { height: 100%; border-radius: 999px; background: var(--ss-w-accent); transition: width .25s ease; }
6767
+ /* Questions list */
6768
+ .ss-w-dq-questions { display: flex; flex-direction: column; gap: 16px; }
6769
+ /* Single question */
6770
+ .ss-w-dq-q { display: flex; flex-direction: column; gap: 8px; }
6771
+ .ss-w-dq-qstem { font-size: 13.5px; font-weight: 650; color: var(--ss-w-ink); line-height: 1.4; }
6772
+ /* Options grid: 2 across */
6773
+ .ss-w-dq-opts { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
6774
+ .ss-w-dq-qopt { display: flex; align-items: center; gap: 8px; padding: 9px 12px; border-radius: 10px; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); font: inherit; font-size: 12.5px; cursor: pointer; text-align: left; color: var(--ss-w-ink); transition: background .1s, border-color .1s; }
6775
+ .ss-w-dq-qopt:hover:not(:disabled) { background: var(--ss-w-bubble); border-color: var(--ss-w-accent); }
6776
+ .ss-w-dq-qopt:disabled { cursor: not-allowed; }
6777
+ .ss-w-dq-qopt--selected { border-color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 8%, var(--ss-w-panel)); }
6778
+ .ss-w-dq-qopt--correct { border-color: var(--ss-w-correct); background: color-mix(in srgb, var(--ss-w-correct) 12%, var(--ss-w-panel)); color: var(--ss-w-correct); }
6779
+ .ss-w-dq-qopt--wrong { border-color: color-mix(in srgb, var(--ss-w-cta) 50%, var(--ss-w-line)); background: color-mix(in srgb, var(--ss-w-cta) 8%, var(--ss-w-panel)); opacity: .75; }
6780
+ /* Option letter badge */
6781
+ .ss-w-dq-qopt-letter { flex: none; width: 20px; height: 20px; border-radius: 50%; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); font-size: 10px; font-weight: 800; display: flex; align-items: center; justify-content: center; color: var(--ss-w-faint); }
6782
+ .ss-w-dq-qopt-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
6783
+ /* Why/reveal text */
6784
+ .ss-w-dq-qwhy { font-size: 11.5px; padding: 6px 10px; border-radius: 8px; line-height: 1.4; }
6785
+ .ss-w-dq-qwhy--correct { background: color-mix(in srgb, var(--ss-w-correct) 9%, var(--ss-w-panel)); color: var(--ss-w-correct); border: 1px solid color-mix(in srgb, var(--ss-w-correct) 22%, var(--ss-w-line)); }
6786
+ .ss-w-dq-qwhy--wrong { background: var(--ss-w-bubble); color: var(--ss-w-faint); border: 1px solid var(--ss-w-line); }
6787
+ /* Submit button */
6788
+ .ss-w-dq-submit { width: 100%; padding: 13px; border-radius: 12px; border: none; background: var(--ss-w-accent); color: #fff; font: inherit; font-size: 14px; font-weight: 700; cursor: pointer; transition: opacity .15s; }
6789
+ .ss-w-dq-submit:disabled { opacity: .4; cursor: not-allowed; }
6790
+ .ss-w-dq-submit:hover:not(:disabled) { opacity: .88; }
6791
+
6792
+ /* \u2500\u2500 Matchday Predictor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
6793
+ .ss-w-mp { display: flex; flex-direction: column; gap: 14px; }
6794
+ /* Score prediction row */
6795
+ .ss-w-mp-pred-score { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 12px 0; }
6796
+ .ss-w-mp-team { display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1; min-width: 0; }
6797
+ .ss-w-mp-team-name { font-size: 12px; font-weight: 650; color: var(--ss-w-ink); text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
6798
+ .ss-w-mp-stepper { display: flex; align-items: center; gap: 8px; }
6799
+ .ss-w-mp-stepper-btn { width: 32px; height: 32px; border-radius: 50%; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); font: inherit; font-size: 18px; font-weight: 700; color: var(--ss-w-ink); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background .1s; }
6800
+ .ss-w-mp-stepper-btn:hover:not(:disabled) { background: var(--ss-w-bubble); }
6801
+ .ss-w-mp-stepper-btn:disabled { opacity: .35; cursor: not-allowed; }
6802
+ .ss-w-mp-stepper-val { font-size: 26px; font-weight: 800; color: var(--ss-w-ink); min-width: 28px; text-align: center; font-variant-numeric: tabular-nums; }
6803
+ .ss-w-mp-sep { font-size: 22px; font-weight: 700; color: var(--ss-w-faint); padding: 0 4px; }
6804
+ /* First-scorer picker */
6805
+ .ss-w-mp-scorer { display: flex; flex-direction: column; gap: 6px; }
6806
+ .ss-w-mp-scorer-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); }
6807
+ .ss-w-mp-scorer-list { display: flex; flex-wrap: wrap; gap: 6px; }
6808
+ .ss-w-mp-scorer-opt { padding: 6px 12px; border-radius: 999px; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); font: inherit; font-size: 12px; font-weight: 600; color: var(--ss-w-ink); cursor: pointer; transition: background .1s, border-color .1s; }
6809
+ .ss-w-mp-scorer-opt:hover:not(:disabled) { background: var(--ss-w-bubble); border-color: var(--ss-w-accent); }
6810
+ .ss-w-mp-scorer-opt--selected { border-color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 10%, var(--ss-w-panel)); color: var(--ss-w-accent); }
6811
+ .ss-w-mp-scorer-opt:disabled { opacity: .35; cursor: not-allowed; }
6812
+ /* Save button */
6813
+ .ss-w-mp-savebtn { width: 100%; padding: 13px; border-radius: 12px; border: none; background: var(--ss-w-accent); color: #fff; font: inherit; font-size: 14px; font-weight: 700; cursor: pointer; transition: opacity .15s; }
6814
+ .ss-w-mp-savebtn:disabled { opacity: .4; cursor: not-allowed; }
6815
+ .ss-w-mp-savebtn:hover:not(:disabled) { opacity: .88; }
6816
+ /* Live / pending state */
6817
+ .ss-w-mp-live { display: flex; flex-direction: column; gap: 10px; align-items: center; text-align: center; padding: 14px 0; }
6818
+ .ss-w-mp-live-score { font-size: 30px; font-weight: 800; color: var(--ss-w-ink); font-variant-numeric: tabular-nums; }
6819
+ .ss-w-mp-live-label { font-size: 12px; color: var(--ss-w-faint); font-style: italic; }
6820
+ .ss-w-mp-live-outcome { display: flex; flex-direction: column; gap: 6px; width: 100%; }
6821
+ .ss-w-mp-live-row { display: flex; align-items: center; justify-content: space-between; padding: 7px 12px; border-radius: 10px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); font-size: 12.5px; }
6822
+ .ss-w-mp-live-row--correct { border-color: color-mix(in srgb, var(--ss-w-correct) 40%, var(--ss-w-line)); background: color-mix(in srgb, var(--ss-w-correct) 8%, var(--ss-w-panel)); }
6823
+ .ss-w-mp-live-row-key { color: var(--ss-w-faint); font-weight: 600; }
6824
+ .ss-w-mp-live-row-val { font-weight: 700; color: var(--ss-w-ink); }
6825
+ .ss-w-mp-live-row--correct .ss-w-mp-live-row-val { color: var(--ss-w-correct); }
6826
+ /* For-fun note */
6827
+ .ss-w-mp-fun { font-size: 11px; color: var(--ss-w-faint); text-align: center; font-style: italic; }
6828
+
6829
+ /* \u2500\u2500 Starting XI \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
6830
+ .ss-w-sxi { display: flex; flex-direction: column; gap: 14px; }
6831
+ .ss-w-sxi-formation { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); text-align: center; }
6832
+ /* Formation pitch area */
6833
+ .ss-w-sxi-pitch { display: flex; flex-direction: column; gap: 10px; padding: 12px 8px; border-radius: 12px; background: color-mix(in srgb, var(--ss-w-accent) 4%, var(--ss-w-panel)); border: 1px solid var(--ss-w-line); min-height: 260px; }
6834
+ .ss-w-sxi-pitch-row { display: flex; justify-content: center; gap: 6px; flex-wrap: wrap; }
6835
+ /* Player slot */
6836
+ .ss-w-sxi-pslot { display: flex; flex-direction: column; align-items: center; gap: 3px; width: 64px; cursor: pointer; }
6837
+ .ss-w-sxi-pslot-num { width: 36px; height: 36px; border-radius: 50%; border: 2px dashed var(--ss-w-line); background: var(--ss-w-bubble); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; color: var(--ss-w-faint); transition: border-color .1s, background .1s; }
6838
+ .ss-w-sxi-pslot--filled .ss-w-sxi-pslot-num { border-style: solid; border-color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); color: var(--ss-w-accent); }
6839
+ .ss-w-sxi-pslot-name { font-size: 9.5px; font-weight: 600; color: var(--ss-w-ink); text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
6840
+ /* Squad picker */
6841
+ .ss-w-sxi-squad { display: flex; flex-direction: column; gap: 4px; max-height: 200px; overflow-y: auto; }
6842
+ .ss-w-sxi-squad-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border-radius: 8px; border: 1px solid var(--ss-w-line); background: var(--ss-w-panel); cursor: pointer; transition: background .1s; }
6843
+ .ss-w-sxi-squad-row:hover:not(.ss-w-sxi-squad-row--picked) { background: var(--ss-w-bubble); }
6844
+ .ss-w-sxi-squad-row--picked { background: color-mix(in srgb, var(--ss-w-accent) 8%, var(--ss-w-panel)); border-color: var(--ss-w-accent); }
6845
+ .ss-w-sxi-squad-name { font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
6846
+ .ss-w-sxi-squad-pos { font-size: 10.5px; color: var(--ss-w-faint); }
6847
+ .ss-w-sxi-savebtn { width: 100%; padding: 13px; border-radius: 12px; border: none; background: var(--ss-w-accent); color: #fff; font: inherit; font-size: 14px; font-weight: 700; cursor: pointer; transition: opacity .15s; }
6848
+ .ss-w-sxi-savebtn:disabled { opacity: .4; cursor: not-allowed; }
6849
+ .ss-w-sxi-savebtn:hover:not(:disabled) { opacity: .88; }
6850
+ /* Pending / graded states */
6851
+ .ss-w-sxi-status { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 14px 12px; border-radius: 12px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); text-align: center; }
6852
+ .ss-w-sxi-status--graded { background: color-mix(in srgb, var(--ss-w-correct) 8%, var(--ss-w-panel)); border-color: color-mix(in srgb, var(--ss-w-correct) 28%, var(--ss-w-line)); }
6853
+ .ss-w-sxi-status-score { font-size: 28px; font-weight: 800; color: var(--ss-w-correct); font-variant-numeric: tabular-nums; }
6854
+ .ss-w-sxi-status-label { font-size: 12.5px; color: var(--ss-w-faint); }
6855
+
6856
+ /* \u2500\u2500 Stat Ladder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
6857
+ .ss-w-sl { display: flex; flex-direction: column; gap: 14px; }
6858
+ .ss-w-sl-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); text-align: center; }
6859
+ /* Ladder list */
6860
+ .ss-w-sl-ladder { display: flex; flex-direction: column; gap: 6px; }
6861
+ .ss-w-sl-lrow { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--ss-w-line); background: var(--ss-w-panel); }
6862
+ .ss-w-sl-lrow-rank { font-size: 11px; font-weight: 800; color: var(--ss-w-faint); width: 18px; flex: none; text-align: center; }
6863
+ .ss-w-sl-lrow-name { flex: 1; font-size: 13px; font-weight: 650; color: var(--ss-w-ink); }
6864
+ .ss-w-sl-lrow-stat { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); font-variant-numeric: tabular-nums; }
6865
+ /* Mystery rung */
6866
+ .ss-w-sl-mystery { border: 1.5px dashed var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 5%, var(--ss-w-panel)); }
6867
+ .ss-w-sl-mystery .ss-w-sl-lrow-name { color: var(--ss-w-faint); font-style: italic; }
6868
+ /* Reveal overlay: correct / wrong */
6869
+ .ss-w-sl-lrow--correct { border-color: color-mix(in srgb, var(--ss-w-correct) 40%, var(--ss-w-line)); background: color-mix(in srgb, var(--ss-w-correct) 8%, var(--ss-w-panel)); }
6870
+ .ss-w-sl-lrow--wrong { border-color: color-mix(in srgb, var(--ss-w-wrong, #e05050) 30%, var(--ss-w-line)); background: color-mix(in srgb, var(--ss-w-wrong, #e05050) 6%, var(--ss-w-panel)); }
6871
+ /* Player picker */
6872
+ .ss-w-sl-picker { display: flex; flex-wrap: wrap; gap: 6px; }
6873
+ .ss-w-sl-picker-opt { padding: 6px 12px; border-radius: 999px; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); font: inherit; font-size: 12px; font-weight: 600; color: var(--ss-w-ink); cursor: pointer; transition: background .1s, border-color .1s; }
6874
+ .ss-w-sl-picker-opt:hover:not(:disabled) { background: var(--ss-w-bubble); border-color: var(--ss-w-accent); }
6875
+ .ss-w-sl-picker-opt:disabled { opacity: .35; cursor: not-allowed; }
6876
+ .ss-w-sl-reveal { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 12px; border-radius: 12px; text-align: center; font-size: 13px; font-weight: 650; color: var(--ss-w-ink); }
6877
+ .ss-w-sl-reveal--correct { background: color-mix(in srgb, var(--ss-w-correct) 10%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-correct) 28%, var(--ss-w-line)); color: var(--ss-w-correct); }
6878
+ .ss-w-sl-reveal--wrong { background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); color: var(--ss-w-faint); }
6879
+
6880
+ /* \u2500\u2500 Fan MOTM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
6881
+ .ss-w-motm { display: flex; flex-direction: column; gap: 14px; }
6882
+ .ss-w-motm-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ss-w-faint); text-align: center; }
6883
+ /* Vote candidate list */
6884
+ .ss-w-motm-votes { display: flex; flex-direction: column; gap: 6px; }
6885
+ .ss-w-motm-vrow { display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-radius: 10px; border: 1.5px solid var(--ss-w-line); background: var(--ss-w-panel); cursor: pointer; transition: background .1s, border-color .1s; position: relative; overflow: hidden; }
6886
+ .ss-w-motm-vrow:hover:not(.ss-w-motm-vrow--voted):not(.ss-w-motm-vrow--result) { background: var(--ss-w-bubble); border-color: var(--ss-w-accent); }
6887
+ .ss-w-motm-vrow--voted { border-color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 8%, var(--ss-w-panel)); cursor: default; }
6888
+ .ss-w-motm-vrow--result { cursor: default; }
6889
+ .ss-w-motm-vrow-name { flex: 1; font-size: 13px; font-weight: 650; color: var(--ss-w-ink); position: relative; z-index: 1; }
6890
+ .ss-w-motm-vrow-pct { font-size: 12px; font-weight: 700; color: var(--ss-w-faint); position: relative; z-index: 1; font-variant-numeric: tabular-nums; }
6891
+ /* Share fill bar (behind content) */
6892
+ .ss-w-motm-vfill { position: absolute; top: 0; left: 0; height: 100%; background: color-mix(in srgb, var(--ss-w-accent) 9%, var(--ss-w-panel)); transition: width .4s ease; pointer-events: none; }
6893
+ .ss-w-motm-vrow--voted .ss-w-motm-vfill { background: color-mix(in srgb, var(--ss-w-accent) 18%, var(--ss-w-panel)); }
6894
+ /* For-fun note */
6895
+ .ss-w-motm-fun { font-size: 11px; color: var(--ss-w-faint); text-align: center; font-style: italic; }
5270
6896
  `;
5271
6897
 
5272
6898
  // src/StatsWidget.tsx
5273
- import { jsx as jsx61 } from "react/jsx-runtime";
6899
+ import { jsx as jsx71 } from "react/jsx-runtime";
5274
6900
  function buildClient(props) {
5275
6901
  try {
5276
6902
  if ("client" in props && props.client) {
@@ -5304,14 +6930,14 @@ function StatsWidget(props) {
5304
6930
  [built?.client]
5305
6931
  );
5306
6932
  const ui = useMemo3(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
5307
- const rootRef = useRef11(null);
5308
- const introDoneRef = useRef11(false);
5309
- const markIntroDone = useCallback8(() => {
6933
+ const rootRef = useRef13(null);
6934
+ const introDoneRef = useRef13(false);
6935
+ const markIntroDone = useCallback9(() => {
5310
6936
  introDoneRef.current = true;
5311
6937
  }, []);
5312
6938
  const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
5313
6939
  const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0, convStorageKey);
5314
- const [hasToday, setHasToday] = useState25(false);
6940
+ const [hasToday, setHasToday] = useState33(false);
5315
6941
  const monitor = useLiveMonitor({
5316
6942
  getLiveMonitor: (pins) => built ? built.conversation.getLiveMonitor(pins) : Promise.resolve(null),
5317
6943
  getMarketOdds: (matchId, dev) => built ? built.conversation.getMarketOdds(matchId, dev) : Promise.resolve(null),
@@ -5319,11 +6945,11 @@ function StatsWidget(props) {
5319
6945
  isOpen: controller.state.isOpen,
5320
6946
  hasToday
5321
6947
  });
5322
- const getSchedule = useCallback8(
6948
+ const getSchedule = useCallback9(
5323
6949
  (localDate, utcOffsetMinutes) => built ? built.conversation.getSchedule(localDate, utcOffsetMinutes) : Promise.resolve(null),
5324
6950
  [built]
5325
6951
  );
5326
- const getStates = useCallback8(
6952
+ const getStates = useCallback9(
5327
6953
  (matchIds) => built ? built.conversation.getStates(matchIds) : Promise.resolve(null),
5328
6954
  [built]
5329
6955
  );
@@ -5339,14 +6965,14 @@ function StatsWidget(props) {
5339
6965
  pinnedMatchIds
5340
6966
  });
5341
6967
  const todayHasContent = visibleGroupsOf(discovery.groups, discovery.states).length > 0;
5342
- useEffect14(() => {
6968
+ useEffect15(() => {
5343
6969
  setHasToday(todayHasContent);
5344
6970
  }, [todayHasContent]);
5345
6971
  const gamesResult = useGames(built?.conversation ?? null, { activeTab: monitor.activeTab });
5346
6972
  const leaderboardResult = useLeaderboard(built?.conversation ?? null, { activeTab: monitor.activeTab });
5347
6973
  const markTodayActivity = monitor.markTodayActivity;
5348
- const prevLiveCountRef = useRef11(0);
5349
- useEffect14(() => {
6974
+ const prevLiveCountRef = useRef13(0);
6975
+ useEffect15(() => {
5350
6976
  const rose = prevLiveCountRef.current === 0 && discovery.liveCount > 0;
5351
6977
  prevLiveCountRef.current = discovery.liveCount;
5352
6978
  if (rose) markTodayActivity();
@@ -5381,11 +7007,11 @@ function StatsWidget(props) {
5381
7007
  const operatorUserId = cfg?.userContext?.operatorUserId;
5382
7008
  const anonymousUserId = cfg?.userContext?.anonymousUserId;
5383
7009
  const conversationId = controller.state.conversationId ?? void 0;
5384
- useEffect14(() => {
7010
+ useEffect15(() => {
5385
7011
  if (!logger) return;
5386
7012
  logger.setIdentity({ operatorUserId, anonymousUserId, conversationId });
5387
7013
  }, [logger, operatorUserId, anonymousUserId, conversationId]);
5388
- useEffect14(() => {
7014
+ useEffect15(() => {
5389
7015
  if (!logger) return;
5390
7016
  const onHide = () => {
5391
7017
  logger.flushOnHide();
@@ -5402,14 +7028,14 @@ function StatsWidget(props) {
5402
7028
  logger.dispose();
5403
7029
  };
5404
7030
  }, [logger]);
5405
- useEffect14(() => {
7031
+ useEffect15(() => {
5406
7032
  if (!built || !ui.injectStyles) return;
5407
7033
  const node = rootRef.current?.getRootNode();
5408
7034
  if (node) injectStyles(node);
5409
7035
  }, [built, ui.injectStyles]);
5410
7036
  if (!built) return null;
5411
7037
  const attrs = themeAttrs(ui);
5412
- return /* @__PURE__ */ jsx61(EventLogContext.Provider, { value: logger, children: /* @__PURE__ */ jsx61("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__ */ jsx61(
7038
+ return /* @__PURE__ */ jsx71(EventLogContext.Provider, { value: logger, children: /* @__PURE__ */ jsx71("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__ */ jsx71(
5413
7039
  ChatPanel,
5414
7040
  {
5415
7041
  controller,
@@ -5420,9 +7046,10 @@ function StatsWidget(props) {
5420
7046
  discovery,
5421
7047
  gamesResult,
5422
7048
  leaderboardResult,
5423
- loggedIn: !!operatorUserId
7049
+ loggedIn: !!operatorUserId,
7050
+ conversation: built?.conversation ?? null
5424
7051
  }
5425
- ) : /* @__PURE__ */ jsx61(
7052
+ ) : /* @__PURE__ */ jsx71(
5426
7053
  ChatFab,
5427
7054
  {
5428
7055
  label: ui.launcherLabel,