@devrongx/games 0.4.44 → 0.4.46

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devrongx/games",
3
- "version": "0.4.44",
3
+ "version": "0.4.46",
4
4
  "description": "Game UI components for sports prediction markets",
5
5
  "license": "MIT",
6
6
  "main": "./src/index.ts",
@@ -252,34 +252,34 @@ const deriveDemoData = (config: IChallengeConfig) => {
252
252
  return { marketA, marketB, marketC, optIdxA, optIdxB, optionA, optionB, betA, betB, chips, rewardA, rewardB, combinedStake, combinedOdds, combinedReward };
253
253
  };
254
254
 
255
- // ── Slide 3: Markets demo — 3 questions, 2 selected one by one ──
255
+ // ── Slide 3: Markets demo — two questions with full bet flow ──
256
256
  const MarketsSlide = ({ config }: SlideProps) => {
257
257
  const demo = useMemo(() => deriveDemoData(config), [config]);
258
258
  const [demoStep, setDemoStep] = useState(0);
259
259
 
260
260
  useEffect(() => {
261
261
  const timers = [
262
- setTimeout(() => setDemoStep(1), 600), // All 3 cards appear
263
- setTimeout(() => setDemoStep(2), 2000), // Card 1 option selected
264
- setTimeout(() => setDemoStep(3), 3000), // Card 1 chips + auto-select
265
- setTimeout(() => setDemoStep(4), 3800), // Card 1 outcome line
266
- setTimeout(() => setDemoStep(5), 5000), // Card 1 compresses, Card 2 option selected
267
- setTimeout(() => setDemoStep(6), 6000), // Card 2 chips + auto-select
268
- setTimeout(() => setDemoStep(7), 6800), // Card 2 outcome line
262
+ setTimeout(() => setDemoStep(1), 800), // Card 1 appears
263
+ setTimeout(() => setDemoStep(2), 2200), // Card 1 option selected
264
+ setTimeout(() => setDemoStep(3), 3200), // Card 1 chips appear (none selected)
265
+ setTimeout(() => setDemoStep(4), 4400), // Card 1 chip selected (1200ms gap)
266
+ setTimeout(() => setDemoStep(5), 5200), // Card 1 outcome, chips collapse
267
+ setTimeout(() => setDemoStep(6), 6200), // Card 2 appears
268
+ setTimeout(() => setDemoStep(7), 7400), // Card 2 option selected
269
+ setTimeout(() => setDemoStep(8), 8400), // Card 2 chips appear (none selected)
270
+ setTimeout(() => setDemoStep(9), 9600), // Card 2 chip selected (1200ms gap)
271
+ setTimeout(() => setDemoStep(10), 10400), // Card 2 outcome, chips collapse
269
272
  ];
270
273
  return () => timers.forEach(clearTimeout);
271
274
  }, []);
272
275
 
273
- const { marketA, marketB, marketC, optIdxA, optIdxB, optionA, optionB, betA, betB, chips, rewardA, rewardB } = demo;
276
+ const { marketA, marketB, optIdxA, optIdxB, optionA, optionB, betA, betB, chips, rewardA, rewardB } = demo;
274
277
  const IconA = MARKET_ICONS[marketA.icon];
275
278
  const IconB = MARKET_ICONS[marketB.icon];
276
- const IconC = MARKET_ICONS[marketC.icon];
277
-
278
- const card1Compressed = demoStep >= 5;
279
279
 
280
- // Shared option renderer for a full options grid
280
+ // Shared option renderer
281
281
  const renderOptionsGrid = (market: typeof marketA, selectedOptIdx: number | null) => (
282
- <div className="grid grid-cols-2 gap-1.5">
282
+ <div className="grid grid-cols-2 gap-2">
283
283
  {market.options.map((opt, j) => {
284
284
  const isSelected = selectedOptIdx !== null && j === selectedOptIdx;
285
285
  return (
@@ -315,7 +315,7 @@ const MarketsSlide = ({ config }: SlideProps) => {
315
315
  </div>
316
316
  );
317
317
 
318
- // Shared chip carousel renderer
318
+ // Chip carousel — selectedAmt of -1 means none selected
319
319
  const renderChips = (selectedAmt: number) => (
320
320
  <motion.div
321
321
  initial={{ opacity: 0, height: 0 }}
@@ -326,7 +326,7 @@ const MarketsSlide = ({ config }: SlideProps) => {
326
326
  >
327
327
  <span className="text-[8px] text-white/60 font-semibold flex-shrink-0" style={OUTFIT}>Bet:</span>
328
328
  {chips.map((amt, i) => {
329
- const isChipSelected = amt === selectedAmt;
329
+ const isChipSelected = selectedAmt > 0 && amt === selectedAmt;
330
330
  return (
331
331
  <motion.div
332
332
  key={amt}
@@ -349,7 +349,7 @@ const MarketsSlide = ({ config }: SlideProps) => {
349
349
  </motion.div>
350
350
  );
351
351
 
352
- // Shared outcome line renderer
352
+ // Outcome line
353
353
  const renderOutcome = (bet: number, odds: number, reward: number) => (
354
354
  <motion.div
355
355
  initial={{ opacity: 0, y: 4 }}
@@ -361,7 +361,7 @@ const MarketsSlide = ({ config }: SlideProps) => {
361
361
  <span className="text-[9px] text-white font-semibold" style={OUTFIT}>{bet}</span>
362
362
  <span className="text-[8px] text-white" style={OUTFIT}>&times;</span>
363
363
  <span className="text-[9px] text-white/60 font-semibold" style={OUTFIT}>{odds}</span>
364
- <span className="text-[8px] text-white/40" style={OUTFIT}>{"\u2192"}</span>
364
+ <span className="text-[8px] text-white/50" style={OUTFIT}>{"\u2192"}</span>
365
365
  <PointsIcon size={7} />
366
366
  <span className="text-[9px] text-[#22E3E8] font-bold" style={OUTFIT}>{reward}</span>
367
367
  </motion.div>
@@ -369,7 +369,7 @@ const MarketsSlide = ({ config }: SlideProps) => {
369
369
 
370
370
  return (
371
371
  <motion.div
372
- className="flex flex-col items-center gap-2.5 w-full max-w-[300px]"
372
+ className="flex flex-col items-center gap-3 w-full max-w-[300px]"
373
373
  initial="initial"
374
374
  animate="animate"
375
375
  exit="exit"
@@ -385,149 +385,101 @@ const MarketsSlide = ({ config }: SlideProps) => {
385
385
  {config.markets.length} questions to predict
386
386
  </motion.p>
387
387
 
388
- {/* All 3 cards appear together */}
389
- {demoStep >= 1 && (
390
- <motion.div
391
- initial={{ opacity: 0, y: 16 }}
392
- animate={{ opacity: 1, y: 0 }}
393
- transition={{ duration: 0.5 }}
394
- className="w-full flex flex-col gap-2"
395
- >
396
- {/* Card 1 */}
397
- <div
398
- className="w-full rounded-lg px-3 py-2.5"
388
+ {/* Card 1 always expanded */}
389
+ <AnimatePresence>
390
+ {demoStep >= 1 && (
391
+ <motion.div
392
+ initial={{ opacity: 0, y: 20 }}
393
+ animate={{ opacity: 1, y: 0 }}
394
+ transition={{ duration: 0.5 }}
395
+ className="w-full rounded-lg px-3 py-3"
399
396
  style={{
400
397
  background: "rgba(255,255,255,0.03)",
401
398
  border: "1px solid rgba(255,255,255,0.08)",
402
399
  }}
403
400
  >
404
- <div className="flex items-center gap-2 mb-1.5">
401
+ <div className="flex items-center gap-2 mb-2">
405
402
  {IconA && <IconA size={14} style={{ color: marketA.accent }} className="flex-shrink-0" />}
406
- <span className={`text-white font-semibold leading-tight ${card1Compressed ? "text-[11px]" : "text-[13px]"}`} style={OUTFIT}>
403
+ <span className="text-[13px] text-white font-semibold leading-tight" style={OUTFIT}>
407
404
  {marketA.question}
408
405
  </span>
409
406
  </div>
410
407
 
411
- {!card1Compressed ? (
412
- <>
413
- {renderOptionsGrid(marketA, demoStep >= 2 ? optIdxA : null)}
414
- <AnimatePresence>
415
- {demoStep >= 3 && demoStep < 5 && renderChips(betA)}
416
- </AnimatePresence>
417
- </>
418
- ) : (
419
- <div className="flex items-center gap-1.5">
420
- <svg width="10" height="10" viewBox="0 0 16 16" fill="none">
421
- <circle cx="8" cy="8" r="8" fill="white" />
422
- <path d="M5 8l2 2 4-4" stroke="#9945FF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
423
- </svg>
424
- <span className="text-[10px] text-white font-semibold" style={OUTFIT}>{optionA.label}</span>
425
- <span className="text-[9px] text-white/40 font-semibold" style={OUTFIT}>{optionA.odds}x</span>
426
- </div>
427
- )}
408
+ {renderOptionsGrid(marketA, demoStep >= 2 ? optIdxA : null)}
428
409
 
429
- {demoStep >= 4 && renderOutcome(betA, optionA.odds, rewardA)}
430
- </div>
410
+ <AnimatePresence>
411
+ {demoStep >= 3 && demoStep < 5 && renderChips(demoStep >= 4 ? betA : -1)}
412
+ </AnimatePresence>
431
413
 
432
- {/* Card 2 */}
433
- <div
434
- className="w-full rounded-lg px-3 py-2.5"
414
+ {demoStep >= 5 && renderOutcome(betA, optionA.odds, rewardA)}
415
+ </motion.div>
416
+ )}
417
+ </AnimatePresence>
418
+
419
+ {/* Card 2 — always expanded */}
420
+ <AnimatePresence>
421
+ {demoStep >= 6 && (
422
+ <motion.div
423
+ initial={{ opacity: 0, y: 20 }}
424
+ animate={{ opacity: 1, y: 0 }}
425
+ transition={{ duration: 0.5 }}
426
+ className="w-full rounded-lg px-3 py-3"
435
427
  style={{
436
428
  background: "rgba(255,255,255,0.03)",
437
429
  border: "1px solid rgba(255,255,255,0.08)",
438
430
  }}
439
431
  >
440
- <div className="flex items-center gap-2 mb-1.5">
432
+ <div className="flex items-center gap-2 mb-2">
441
433
  {IconB && <IconB size={14} style={{ color: marketB.accent }} className="flex-shrink-0" />}
442
434
  <span className="text-[13px] text-white font-semibold leading-tight" style={OUTFIT}>
443
435
  {marketB.question}
444
436
  </span>
445
437
  </div>
446
438
 
447
- {renderOptionsGrid(marketB, demoStep >= 5 ? optIdxB : null)}
439
+ {renderOptionsGrid(marketB, demoStep >= 7 ? optIdxB : null)}
448
440
 
449
441
  <AnimatePresence>
450
- {demoStep >= 6 && renderChips(betB)}
442
+ {demoStep >= 8 && demoStep < 10 && renderChips(demoStep >= 9 ? betB : -1)}
451
443
  </AnimatePresence>
452
444
 
453
- {demoStep >= 7 && renderOutcome(betB, optionB.odds, rewardB)}
454
- </div>
455
-
456
- {/* Card 3 — always unselected */}
457
- <div
458
- className="w-full rounded-lg px-3 py-2.5"
459
- style={{
460
- background: "rgba(255,255,255,0.03)",
461
- border: "1px solid rgba(255,255,255,0.08)",
462
- opacity: demoStep >= 2 ? 0.5 : 1,
463
- transition: "opacity 0.5s",
464
- }}
465
- >
466
- <div className="flex items-center gap-2 mb-1.5">
467
- {IconC && <IconC size={14} style={{ color: marketC.accent }} className="flex-shrink-0" />}
468
- <span className="text-[13px] text-white font-semibold leading-tight" style={OUTFIT}>
469
- {marketC.question}
470
- </span>
471
- </div>
472
- {renderOptionsGrid(marketC, null)}
473
- </div>
474
- </motion.div>
475
- )}
445
+ {demoStep >= 10 && renderOutcome(betB, optionB.odds, rewardB)}
446
+ </motion.div>
447
+ )}
448
+ </AnimatePresence>
476
449
  </motion.div>
477
450
  );
478
451
  };
479
452
 
480
- // ── Slide 4: Compound bet demo ──
453
+ // ── Slide 4: Compound bet demo — 3 full cards, 2 combined ──
481
454
  const MultiplierSlide = ({ config }: SlideProps) => {
482
455
  const demo = useMemo(() => deriveDemoData(config), [config]);
483
456
  const [step, setStep] = useState(0);
484
457
 
485
458
  useEffect(() => {
486
459
  const timers = [
487
- setTimeout(() => setStep(1), 300),
488
- setTimeout(() => setStep(2), 1200),
489
- setTimeout(() => setStep(3), 3000),
490
- setTimeout(() => setStep(4), 4200),
491
- setTimeout(() => setStep(5), 5200),
492
- setTimeout(() => setStep(6), 6500),
460
+ setTimeout(() => setStep(1), 300), // Headline
461
+ setTimeout(() => setStep(2), 1200), // 3 full cards appear
462
+ setTimeout(() => setStep(3), 3000), // Card 1 highlights, outcome fades out
463
+ setTimeout(() => setStep(4), 4200), // Card 2 highlights, outcome fades out
464
+ setTimeout(() => setStep(5), 5200), // Combined outcome line
465
+ setTimeout(() => setStep(6), 6500), // Payoff text
493
466
  ];
494
467
  return () => timers.forEach(clearTimeout);
495
468
  }, []);
496
469
 
497
- const { marketA, marketB, optionA, optionB, betA, betB, rewardA, rewardB, combinedStake, combinedOdds, combinedReward } = demo;
470
+ const { marketA, marketB, marketC, optionA, optionB, betA, betB, rewardA, rewardB, combinedStake, combinedReward } = demo;
498
471
  const IconA = MARKET_ICONS[marketA.icon];
499
472
  const IconB = MARKET_ICONS[marketB.icon];
473
+ const IconC = MARKET_ICONS[marketC.icon];
500
474
 
501
- // Shared outcome line identical style for individual and combined
502
- const renderOutcomeLine = (bet: number, odds: string, reward: number, pulse?: boolean) => (
503
- <div className="flex items-center gap-[5px]">
504
- <PointsIcon size={10} />
505
- <span className="text-[12px] text-white font-semibold" style={OUTFIT}>{bet}</span>
506
- <span className="text-[11px] text-white font-medium" style={OUTFIT}>&times;</span>
507
- {pulse ? (
508
- <motion.span
509
- className="text-[12px] text-white font-bold"
510
- style={OUTFIT}
511
- animate={{ scale: [1, 1.25, 1] }}
512
- transition={{ duration: 0.5, delay: 0.2 }}
513
- >
514
- {odds}
515
- </motion.span>
516
- ) : (
517
- <span className="text-[12px] text-white font-bold" style={OUTFIT}>{odds}</span>
518
- )}
519
- <span className="text-[11px] text-white/50" style={OUTFIT}>{"\u2192"}</span>
520
- <PointsIcon size={10} />
521
- <span className="text-[12px] text-[#22E3E8] font-bold" style={OUTFIT}>{reward}</span>
522
- </div>
523
- );
524
-
525
- const renderCompactCard = (
475
+ // Full expanded card with options grid selected option highlighted
476
+ const renderFullCard = (
526
477
  market: typeof marketA,
527
478
  Icon: typeof IconA,
528
- option: typeof optionA,
529
- betAmount: number,
530
- reward: number,
479
+ selectedOptIdx: number | null,
480
+ betAmount: number | null,
481
+ odds: number | null,
482
+ reward: number | null,
531
483
  highlighted: boolean,
532
484
  hideOutcome: boolean,
533
485
  ) => (
@@ -543,32 +495,60 @@ const MultiplierSlide = ({ config }: SlideProps) => {
543
495
  border: "1px solid rgba(255,255,255,0.08)",
544
496
  }}
545
497
  >
546
- <div className="flex items-center gap-1.5 mb-1.5">
547
- {Icon && <Icon size={12} style={{ color: market.accent }} className="flex-shrink-0" />}
548
- <span className="text-[11px] text-white font-semibold leading-tight truncate" style={OUTFIT}>{market.question}</span>
498
+ <div className="flex items-center gap-2 mb-1.5">
499
+ {Icon && <Icon size={14} style={{ color: market.accent }} className="flex-shrink-0" />}
500
+ <span className="text-[12px] text-white font-semibold leading-tight" style={OUTFIT}>{market.question}</span>
549
501
  </div>
550
- <div className="flex items-center gap-1.5 mb-1.5">
551
- <div
552
- className="flex items-center gap-1 px-2 py-[2px] rounded-sm"
553
- style={{ background: "linear-gradient(135deg, #22E3E8, #9945FF)" }}
554
- >
555
- <span className="text-[9px] text-white font-semibold" style={OUTFIT}>{option.label}</span>
556
- </div>
557
- <span className="text-[9px] text-white/50 font-semibold" style={OUTFIT}>at {option.odds}x</span>
502
+ <div className="grid grid-cols-2 gap-1.5">
503
+ {market.options.map((opt, j) => {
504
+ const isSelected = selectedOptIdx !== null && j === selectedOptIdx;
505
+ return (
506
+ <div
507
+ key={j}
508
+ className="flex items-center justify-between px-2 py-1 rounded-sm"
509
+ style={{
510
+ background: isSelected ? "linear-gradient(135deg, #22E3E8, #9945FF)" : "transparent",
511
+ borderLeft: isSelected ? "1px solid transparent" : "1px solid rgba(255,255,255,0.12)",
512
+ borderBottom: isSelected ? "1px solid transparent" : "1px solid rgba(255,255,255,0.06)",
513
+ borderTop: "1px solid transparent",
514
+ borderRight: "1px solid transparent",
515
+ }}
516
+ >
517
+ <div className="flex items-center gap-1 min-w-0 flex-shrink">
518
+ {isSelected && (
519
+ <svg width="8" height="8" viewBox="0 0 16 16" fill="none">
520
+ <circle cx="8" cy="8" r="8" fill="white" />
521
+ <path d="M5 8l2 2 4-4" stroke="#9945FF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
522
+ </svg>
523
+ )}
524
+ <span className="text-[9px] text-white font-semibold truncate" style={OUTFIT}>{opt.label}</span>
525
+ </div>
526
+ <span className={`text-[9px] font-bold flex-shrink-0 ${isSelected ? "text-white" : "text-[#22E3E8]"}`} style={OUTFIT}>{opt.odds}x</span>
527
+ </div>
528
+ );
529
+ })}
558
530
  </div>
559
- <motion.div
560
- className="overflow-hidden"
561
- animate={{ opacity: hideOutcome ? 0 : 1, height: hideOutcome ? 0 : "auto" }}
562
- transition={{ duration: 0.3 }}
563
- >
564
- {renderOutcomeLine(betAmount, String(option.odds), reward)}
565
- </motion.div>
531
+ {betAmount !== null && odds !== null && reward !== null && (
532
+ <motion.div
533
+ className="flex items-center gap-[4px] mt-1.5 overflow-hidden"
534
+ animate={{ opacity: hideOutcome ? 0 : 1, height: hideOutcome ? 0 : "auto" }}
535
+ transition={{ duration: 0.3 }}
536
+ >
537
+ <PointsIcon size={8} />
538
+ <span className="text-[10px] text-white font-semibold" style={OUTFIT}>{betAmount}</span>
539
+ <span className="text-[9px] text-white" style={OUTFIT}>&times;</span>
540
+ <span className="text-[10px] text-white font-bold" style={OUTFIT}>{odds}</span>
541
+ <span className="text-[9px] text-white/50" style={OUTFIT}>{"\u2192"}</span>
542
+ <PointsIcon size={8} />
543
+ <span className="text-[10px] text-[#22E3E8] font-bold" style={OUTFIT}>{reward}</span>
544
+ </motion.div>
545
+ )}
566
546
  </motion.div>
567
547
  );
568
548
 
569
549
  return (
570
550
  <motion.div
571
- className="flex flex-col items-center gap-3 w-full max-w-[300px]"
551
+ className="flex flex-col items-center gap-2.5 w-full max-w-[300px]"
572
552
  initial="initial"
573
553
  animate="animate"
574
554
  exit="exit"
@@ -586,7 +566,7 @@ const MultiplierSlide = ({ config }: SlideProps) => {
586
566
  </motion.p>
587
567
  )}
588
568
 
589
- {/* Two compact result cards */}
569
+ {/* 3 full expanded cards */}
590
570
  {step >= 2 && (
591
571
  <motion.div
592
572
  initial={{ opacity: 0, y: 16 }}
@@ -594,8 +574,39 @@ const MultiplierSlide = ({ config }: SlideProps) => {
594
574
  transition={{ duration: 0.5 }}
595
575
  className="w-full flex flex-col gap-2"
596
576
  >
597
- {renderCompactCard(marketA, IconA, optionA, betA, rewardA, step >= 3, step >= 3)}
598
- {renderCompactCard(marketB, IconB, optionB, betB, rewardB, step >= 4, step >= 4)}
577
+ {renderFullCard(marketA, IconA, demo.optIdxA, betA, optionA.odds, rewardA, step >= 3, step >= 3)}
578
+ {renderFullCard(marketB, IconB, demo.optIdxB, betB, optionB.odds, rewardB, step >= 4, step >= 4)}
579
+ {/* Card 3 — unselected, no bet */}
580
+ <div
581
+ className="w-full rounded-lg px-3 py-2.5"
582
+ style={{
583
+ background: "rgba(255,255,255,0.03)",
584
+ border: "1px solid rgba(255,255,255,0.08)",
585
+ opacity: 0.5,
586
+ }}
587
+ >
588
+ <div className="flex items-center gap-2 mb-1.5">
589
+ {IconC && <IconC size={14} style={{ color: marketC.accent }} className="flex-shrink-0" />}
590
+ <span className="text-[12px] text-white font-semibold leading-tight" style={OUTFIT}>{marketC.question}</span>
591
+ </div>
592
+ <div className="grid grid-cols-2 gap-1.5">
593
+ {marketC.options.map((opt, j) => (
594
+ <div
595
+ key={j}
596
+ className="flex items-center justify-between px-2 py-1 rounded-sm"
597
+ style={{
598
+ borderLeft: "1px solid rgba(255,255,255,0.12)",
599
+ borderBottom: "1px solid rgba(255,255,255,0.06)",
600
+ borderTop: "1px solid transparent",
601
+ borderRight: "1px solid transparent",
602
+ }}
603
+ >
604
+ <span className="text-[9px] text-white font-semibold truncate" style={OUTFIT}>{opt.label}</span>
605
+ <span className="text-[9px] font-bold text-[#22E3E8] flex-shrink-0" style={OUTFIT}>{opt.odds}x</span>
606
+ </div>
607
+ ))}
608
+ </div>
609
+ </div>
599
610
  </motion.div>
600
611
  )}
601
612
 
@@ -647,10 +658,18 @@ const MultiplierSlide = ({ config }: SlideProps) => {
647
658
  };
648
659
 
649
660
  // Free leaderboard payouts: ranks 1-10, $25 down to $2.5 in $2.5 steps
650
- const FREE_PAYOUTS = Array.from({ length: 10 }, (_, i) => ({
651
- rank: i + 1,
652
- usdc: 25 - i * 2.5,
653
- }));
661
+ const FREE_PAYOUTS = [
662
+ { rank: 1, points: 4850, usdc: 25 },
663
+ { rank: 2, points: 4200, usdc: 22.5 },
664
+ { rank: 3, points: 3900, usdc: 20 },
665
+ { rank: 4, points: 3400, usdc: 17.5 },
666
+ { rank: 5, points: 2950, usdc: 15 },
667
+ { rank: 6, points: 2600, usdc: 12.5 },
668
+ { rank: 7, points: 2100, usdc: 10 },
669
+ { rank: 8, points: 1750, usdc: 7.5 },
670
+ { rank: 9, points: 1300, usdc: 5 },
671
+ { rank: 10, points: 900, usdc: 2.5 },
672
+ ];
654
673
 
655
674
  // ── Slide 5: Win USDC ──
656
675
  const WinSlide = ({ config, onComplete }: SlideProps) => {
@@ -693,9 +712,13 @@ const WinSlide = ({ config, onComplete }: SlideProps) => {
693
712
  style={{ border: "1px solid rgba(255,255,255,0.08)", background: "rgba(255,255,255,0.02)" }}
694
713
  >
695
714
  {/* Table header */}
696
- <div className="flex items-center justify-between px-3 py-1.5" style={{ borderBottom: "1px solid rgba(255,255,255,0.06)", background: "rgba(255,255,255,0.03)" }}>
715
+ <div className="grid grid-cols-3 items-center px-3 py-1.5" style={{ borderBottom: "1px solid rgba(255,255,255,0.06)", background: "rgba(255,255,255,0.03)" }}>
697
716
  <span className="text-[9px] font-bold text-white/50 uppercase tracking-widest" style={OUTFIT}>Rank</span>
698
- <div className="flex items-center gap-1">
717
+ <div className="flex items-center gap-1 justify-center">
718
+ <PointsIcon size={9} />
719
+ <span className="text-[9px] font-bold text-white/50 uppercase tracking-widest" style={OUTFIT}>Points</span>
720
+ </div>
721
+ <div className="flex items-center gap-1 justify-end">
699
722
  <Image src="/icons/ic_usdc_hd.png" alt="USDC" width={11} height={11} className="rounded-full" />
700
723
  <span className="text-[9px] font-bold text-white/50 uppercase tracking-widest" style={OUTFIT}>Reward</span>
701
724
  </div>
@@ -707,7 +730,7 @@ const WinSlide = ({ config, onComplete }: SlideProps) => {
707
730
  initial={{ opacity: 0, x: -8 }}
708
731
  animate={{ opacity: 1, x: 0 }}
709
732
  transition={{ delay: 1.6 + idx * 0.06, duration: 0.2 }}
710
- className="flex items-center justify-between px-3 py-1.5"
733
+ className="grid grid-cols-3 items-center px-3 py-1.5"
711
734
  style={{
712
735
  borderBottom: idx < FREE_PAYOUTS.length - 1 ? "1px solid rgba(255,255,255,0.04)" : "none",
713
736
  background: idx === 0 ? "rgba(255,215,0,0.04)" : idx < 3 ? "rgba(255,255,255,0.015)" : "transparent",
@@ -727,8 +750,14 @@ const WinSlide = ({ config, onComplete }: SlideProps) => {
727
750
  )}
728
751
  </div>
729
752
  <span
730
- className="text-[11px] font-bold"
731
- style={{ ...OUTFIT, color: idx === 0 ? "#FFD700" : idx < 3 ? "rgba(255,255,255,0.85)" : "rgba(255,255,255,0.6)" }}
753
+ className="text-[10px] font-semibold text-center"
754
+ style={{ ...OUTFIT, color: idx === 0 ? "#22E3E8" : idx < 3 ? "rgba(255,255,255,0.75)" : "rgba(255,255,255,0.5)" }}
755
+ >
756
+ {row.points.toLocaleString()}
757
+ </span>
758
+ <span
759
+ className="text-[11px] font-bold text-right select-none"
760
+ style={{ ...OUTFIT, color: idx === 0 ? "#FFD700" : idx < 3 ? "rgba(255,255,255,0.85)" : "rgba(255,255,255,0.6)", filter: "blur(5px)" }}
732
761
  >
733
762
  ${row.usdc.toFixed(2)}
734
763
  </span>