diff --git a/lib/src/model/broadcast/broadcast.dart b/lib/src/model/broadcast/broadcast.dart
index aeb754ebad..9188498756 100644
--- a/lib/src/model/broadcast/broadcast.dart
+++ b/lib/src/model/broadcast/broadcast.dart
@@ -44,6 +44,7 @@ class BroadcastTournamentData with _$BroadcastTournamentData {
   const factory BroadcastTournamentData({
     required BroadcastTournamentId id,
     required String name,
+    required String slug,
     required String? imageUrl,
     required String? description,
     // PRIVATE=-1, NORMAL=3, HIGH=4, BEST=5
@@ -71,6 +72,7 @@ class BroadcastRound with _$BroadcastRound {
   const factory BroadcastRound({
     required BroadcastRoundId id,
     required String name,
+    required String slug,
     required RoundStatus status,
     required DateTime? startsAt,
     required DateTime? finishedAt,
@@ -126,7 +128,31 @@ class BroadcastPlayerExtended with _$BroadcastPlayerExtended {
     required int played,
     required double? score,
     required int? ratingDiff,
+    required int? performance,
   }) = _BroadcastPlayerExtended;
 }
 
+typedef BroadcastFideData = ({({int? standard, int? rapid, int? blitz}) ratings, int? birthYear});
+
+typedef BroadcastPlayerResults =
+    ({
+      BroadcastPlayerExtended player,
+      BroadcastFideData fideData,
+      IList<BroadcastPlayerResultData> games,
+    });
+
+enum BroadcastPoints { one, half, zero }
+
+@freezed
+class BroadcastPlayerResultData with _$BroadcastPlayerResultData {
+  const factory BroadcastPlayerResultData({
+    required BroadcastRoundId roundId,
+    required BroadcastGameId gameId,
+    required Side color,
+    required BroadcastPoints? points,
+    required int? ratingDiff,
+    required BroadcastPlayer opponent,
+  }) = _BroadcastPlayerResult;
+}
+
 enum RoundStatus { live, finished, upcoming }
diff --git a/lib/src/model/broadcast/broadcast_federation.dart b/lib/src/model/broadcast/broadcast_federation.dart
new file mode 100644
index 0000000000..267b53fc69
--- /dev/null
+++ b/lib/src/model/broadcast/broadcast_federation.dart
@@ -0,0 +1,205 @@
+const federationIdToName = {
+  'FID': 'FIDE',
+  'USA': 'United States of America',
+  'IND': 'India',
+  'CHN': 'China',
+  'RUS': 'Russia',
+  'AZE': 'Azerbaijan',
+  'FRA': 'France',
+  'UKR': 'Ukraine',
+  'ARM': 'Armenia',
+  'GER': 'Germany',
+  'ESP': 'Spain',
+  'NED': 'Netherlands',
+  'HUN': 'Hungary',
+  'POL': 'Poland',
+  'ENG': 'England',
+  'ROU': 'Romania',
+  'NOR': 'Norway',
+  'UZB': 'Uzbekistan',
+  'ISR': 'Israel',
+  'CZE': 'Czech Republic',
+  'SRB': 'Serbia',
+  'CRO': 'Croatia',
+  'GRE': 'Greece',
+  'IRI': 'Iran',
+  'TUR': 'Turkiye',
+  'SLO': 'Slovenia',
+  'ARG': 'Argentina',
+  'SWE': 'Sweden',
+  'GEO': 'Georgia',
+  'ITA': 'Italy',
+  'CUB': 'Cuba',
+  'AUT': 'Austria',
+  'PER': 'Peru',
+  'BUL': 'Bulgaria',
+  'BRA': 'Brazil',
+  'DEN': 'Denmark',
+  'SUI': 'Switzerland',
+  'CAN': 'Canada',
+  'SVK': 'Slovakia',
+  'LTU': 'Lithuania',
+  'VIE': 'Vietnam',
+  'AUS': 'Australia',
+  'BEL': 'Belgium',
+  'MNE': 'Montenegro',
+  'MDA': 'Moldova',
+  'KAZ': 'Kazakhstan',
+  'ISL': 'Iceland',
+  'COL': 'Colombia',
+  'BIH': 'Bosnia & Herzegovina',
+  'EGY': 'Egypt',
+  'FIN': 'Finland',
+  'MGL': 'Mongolia',
+  'PHI': 'Philippines',
+  'BLR': 'Belarus',
+  'LAT': 'Latvia',
+  'POR': 'Portugal',
+  'CHI': 'Chile',
+  'MEX': 'Mexico',
+  'MKD': 'North Macedonia',
+  'INA': 'Indonesia',
+  'PAR': 'Paraguay',
+  'EST': 'Estonia',
+  'SGP': 'Singapore',
+  'SCO': 'Scotland',
+  'VEN': 'Venezuela',
+  'IRL': 'Ireland',
+  'URU': 'Uruguay',
+  'TKM': 'Turkmenistan',
+  'MAR': 'Morocco',
+  'MAS': 'Malaysia',
+  'BAN': 'Bangladesh',
+  'ALG': 'Algeria',
+  'RSA': 'South Africa',
+  'AND': 'Andorra',
+  'ALB': 'Albania',
+  'KGZ': 'Kyrgyzstan',
+  'KOS': 'Kosovo *',
+  'FAI': 'Faroe Islands',
+  'ZAM': 'Zambia',
+  'MYA': 'Myanmar',
+  'NZL': 'New Zealand',
+  'ECU': 'Ecuador',
+  'CRC': 'Costa Rica',
+  'NGR': 'Nigeria',
+  'JPN': 'Japan',
+  'SYR': 'Syria',
+  'DOM': 'Dominican Republic',
+  'LUX': 'Luxembourg',
+  'WLS': 'Wales',
+  'BOL': 'Bolivia',
+  'TUN': 'Tunisia',
+  'UAE': 'United Arab Emirates',
+  'MNC': 'Monaco',
+  'TJK': 'Tajikistan',
+  'PAN': 'Panama',
+  'LBN': 'Lebanon',
+  'NCA': 'Nicaragua',
+  'ESA': 'El Salvador',
+  'ANG': 'Angola',
+  'TTO': 'Trinidad & Tobago',
+  'SRI': 'Sri Lanka',
+  'IRQ': 'Iraq',
+  'JOR': 'Jordan',
+  'UGA': 'Uganda',
+  'MAD': 'Madagascar',
+  'ZIM': 'Zimbabwe',
+  'MLT': 'Malta',
+  'SUD': 'Sudan',
+  'KOR': 'South Korea',
+  'PUR': 'Puerto Rico',
+  'HON': 'Honduras',
+  'GUA': 'Guatemala',
+  'PAK': 'Pakistan',
+  'JAM': 'Jamaica',
+  'THA': 'Thailand',
+  'YEM': 'Yemen',
+  'LBA': 'Libya',
+  'CYP': 'Cyprus',
+  'NEP': 'Nepal',
+  'HKG': 'Hong Kong, China',
+  'SSD': 'South Sudan',
+  'BOT': 'Botswana',
+  'PLE': 'Palestine',
+  'KEN': 'Kenya',
+  'AHO': 'Netherlands Antilles',
+  'MAW': 'Malawi',
+  'LIE': 'Liechtenstein',
+  'TPE': 'Chinese Taipei',
+  'AFG': 'Afghanistan',
+  'MOZ': 'Mozambique',
+  'KSA': 'Saudi Arabia',
+  'BAR': 'Barbados',
+  'NAM': 'Namibia',
+  'HAI': 'Haiti',
+  'ARU': 'Aruba',
+  'CIV': 'Cote d’Ivoire',
+  'CPV': 'Cape Verde',
+  'SUR': 'Suriname',
+  'LBR': 'Liberia',
+  'IOM': 'Isle of Man',
+  'MTN': 'Mauritania',
+  'BRN': 'Bahrain',
+  'GHA': 'Ghana',
+  'OMA': 'Oman',
+  'BRU': 'Brunei Darussalam',
+  'GCI': 'Guernsey',
+  'GUM': 'Guam',
+  'KUW': 'Kuwait',
+  'JCI': 'Jersey',
+  'MRI': 'Mauritius',
+  'SEN': 'Senegal',
+  'BAH': 'Bahamas',
+  'MDV': 'Maldives',
+  'NRU': 'Nauru',
+  'TOG': 'Togo',
+  'FIJ': 'Fiji',
+  'PLW': 'Palau',
+  'GUY': 'Guyana',
+  'LES': 'Lesotho',
+  'CAY': 'Cayman Islands',
+  'SOM': 'Somalia',
+  'SWZ': 'Eswatini',
+  'TAN': 'Tanzania',
+  'LCA': 'Saint Lucia',
+  'ISV': 'US Virgin Islands',
+  'SLE': 'Sierra Leone',
+  'BER': 'Bermuda',
+  'SMR': 'San Marino',
+  'BDI': 'Burundi',
+  'QAT': 'Qatar',
+  'ETH': 'Ethiopia',
+  'DJI': 'Djibouti',
+  'SEY': 'Seychelles',
+  'PNG': 'Papua New Guinea',
+  'DMA': 'Dominica',
+  'STP': 'Sao Tome and Principe',
+  'MAC': 'Macau',
+  'CAM': 'Cambodia',
+  'VIN': 'Saint Vincent and the Grenadines',
+  'BUR': 'Burkina Faso',
+  'COM': 'Comoros Islands',
+  'GAB': 'Gabon',
+  'RWA': 'Rwanda',
+  'CMR': 'Cameroon',
+  'MLI': 'Mali',
+  'ANT': 'Antigua and Barbuda',
+  'CHA': 'Chad',
+  'GAM': 'Gambia',
+  'COD': 'Democratic Republic of the Congo',
+  'SKN': 'Saint Kitts and Nevis',
+  'BHU': 'Bhutan',
+  'NIG': 'Niger',
+  'GRN': 'Grenada',
+  'BIZ': 'Belize',
+  'CAF': 'Central African Republic',
+  'ERI': 'Eritrea',
+  'GEQ': 'Equatorial Guinea',
+  'IVB': 'British Virgin Islands',
+  'LAO': 'Laos',
+  'SOL': 'Solomon Islands',
+  'TGA': 'Tonga',
+  'TLS': 'Timor-Leste',
+  'VAN': 'Vanuatu',
+};
diff --git a/lib/src/model/broadcast/broadcast_providers.dart b/lib/src/model/broadcast/broadcast_providers.dart
index b23e20213d..c8696d35eb 100644
--- a/lib/src/model/broadcast/broadcast_providers.dart
+++ b/lib/src/model/broadcast/broadcast_providers.dart
@@ -60,6 +60,17 @@ Future<IList<BroadcastPlayerExtended>> broadcastPlayers(
   return ref.withClient((client) => BroadcastRepository(client).getPlayers(tournamentId));
 }
 
+@riverpod
+Future<BroadcastPlayerResults> broadcastPlayerResult(
+  Ref ref,
+  BroadcastTournamentId broadcastTournamentId,
+  String playerId,
+) {
+  return ref.withClient(
+    (client) => BroadcastRepository(client).getPlayerResults(broadcastTournamentId, playerId),
+  );
+}
+
 @Riverpod(keepAlive: true)
 BroadcastImageWorkerFactory broadcastImageWorkerFactory(Ref ref) {
   return const BroadcastImageWorkerFactory();
diff --git a/lib/src/model/broadcast/broadcast_repository.dart b/lib/src/model/broadcast/broadcast_repository.dart
index e3c0d4d6ef..832e0e3c8a 100644
--- a/lib/src/model/broadcast/broadcast_repository.dart
+++ b/lib/src/model/broadcast/broadcast_repository.dart
@@ -45,6 +45,16 @@ class BroadcastRepository {
       mapper: _makePlayerFromJson,
     );
   }
+
+  Future<BroadcastPlayerResults> getPlayerResults(
+    BroadcastTournamentId tournamentId,
+    String playerId,
+  ) {
+    return client.readJson(
+      Uri(path: 'broadcast/$tournamentId/players/$playerId'),
+      mapper: _makePlayerResultsFromJson,
+    );
+  }
 }
 
 BroadcastList _makeBroadcastResponseFromJson(Map<String, dynamic> json) {
@@ -62,13 +72,14 @@ Broadcast _broadcastFromPick(RequiredPick pick) {
     tour: _tournamentDataFromPick(pick('tour').required()),
     round: _roundFromPick(pick('round').required()),
     group: pick('group').asStringOrNull(),
-    roundToLinkId: pick('roundToLink', 'id').asBroadcastRoundIddOrNull() ?? roundId,
+    roundToLinkId: pick('roundToLink', 'id').asBroadcastRoundIdOrNull() ?? roundId,
   );
 }
 
 BroadcastTournamentData _tournamentDataFromPick(RequiredPick pick) => BroadcastTournamentData(
   id: pick('id').asBroadcastTournamentIdOrThrow(),
   name: pick('name').asStringOrThrow(),
+  slug: pick('slug').asStringOrThrow(),
   tier: pick('tier').asIntOrNull(),
   imageUrl: pick('image').asStringOrNull(),
   description: pick('description').asStringOrNull(),
@@ -116,6 +127,7 @@ BroadcastRound _roundFromPick(RequiredPick pick) {
   return BroadcastRound(
     id: pick('id').asBroadcastRoundIdOrThrow(),
     name: pick('name').asStringOrThrow(),
+    slug: pick('slug').asStringOrThrow(),
     status: status,
     startsAt: pick('startsAt').asDateTimeFromMillisecondsOrNull(),
     finishedAt: pick('finishedAt').asDateTimeFromMillisecondsOrNull(),
@@ -209,5 +221,50 @@ BroadcastPlayerExtended _playerExtendedFromPick(RequiredPick pick) {
     played: pick('played').asIntOrThrow(),
     score: pick('score').asDoubleOrNull(),
     ratingDiff: pick('ratingDiff').asIntOrNull(),
+    performance: pick('performance').asIntOrNull(),
+  );
+}
+
+BroadcastPlayerResults _makePlayerResultsFromJson(Map<String, dynamic> json) {
+  return (
+    player: _playerExtendedFromPick(pick(json).required()),
+    fideData: _fideDataFromPick(pick(json, 'fide')),
+    games: pick(json, 'games').asListOrThrow(_makePlayerResultFromPick).toIList(),
+  );
+}
+
+BroadcastFideData _fideDataFromPick(Pick pick) {
+  return (
+    ratings: (
+      standard: pick('ratings', 'standard').asIntOrNull(),
+      rapid: pick('ratings', 'rapid').asIntOrNull(),
+      blitz: pick('ratings', 'blitz').asIntOrNull(),
+    ),
+    birthYear: pick('year').asIntOrNull(),
+  );
+}
+
+BroadcastPlayerResultData _makePlayerResultFromPick(RequiredPick pick) {
+  final pointsString = pick('points').asStringOrNull();
+  BroadcastPoints? points;
+  if (pointsString == '1') {
+    points = BroadcastPoints.one;
+  } else if (pointsString == '1/2') {
+    points = BroadcastPoints.half;
+  } else if (pointsString == '0') {
+    points = BroadcastPoints.zero;
+  }
+
+  return BroadcastPlayerResultData(
+    roundId: pick('round').asBroadcastRoundIdOrThrow(),
+    gameId: pick('id').asBroadcastGameIdOrThrow(),
+    color: pick('color').asSideOrThrow(),
+    ratingDiff: pick('ratingDiff').asIntOrNull(),
+    points: points,
+    opponent: _playerFromPick(
+      pick('opponent').required(),
+      isPlaying: false,
+      thinkingTime: Duration.zero,
+    ),
   );
 }
diff --git a/lib/src/model/common/id.dart b/lib/src/model/common/id.dart
index 2d0d75ea6e..f7cbe93049 100644
--- a/lib/src/model/common/id.dart
+++ b/lib/src/model/common/id.dart
@@ -8,6 +8,8 @@ extension type const StringId(String value) {
   bool startsWith(String prefix) => value.startsWith(prefix);
 }
 
+extension type const IntId(int value) {}
+
 extension type const GameAnyId._(String value) implements StringId {
   GameAnyId(this.value) : assert(value.length == 8 || value.length == 12);
   GameId get gameId => GameId(value.substring(0, 8));
@@ -65,7 +67,7 @@ extension type const StudyChapterId(String value) implements StringId {
   StudyChapterId.fromJson(dynamic json) : this(json as String);
 }
 
-extension type const FideId(String value) implements StringId {}
+extension type const FideId(int value) implements IntId {}
 
 extension IDPick on Pick {
   UserId asUserIdOrThrow() {
@@ -180,7 +182,7 @@ extension IDPick on Pick {
     throw PickException("value $value at $debugParsingExit can't be casted to BroadcastRoundId");
   }
 
-  BroadcastRoundId? asBroadcastRoundIddOrNull() {
+  BroadcastRoundId? asBroadcastRoundIdOrNull() {
     if (value == null) return null;
     try {
       return asBroadcastRoundIdOrThrow();
@@ -197,7 +199,7 @@ extension IDPick on Pick {
     throw PickException("value $value at $debugParsingExit can't be casted to BroadcastGameId");
   }
 
-  BroadcastGameId? asBroadcastGameIddOrNull() {
+  BroadcastGameId? asBroadcastGameIdOrNull() {
     if (value == null) return null;
     try {
       return asBroadcastGameIdOrThrow();
@@ -216,7 +218,7 @@ extension IDPick on Pick {
 
   FideId asFideIdOrThrow() {
     final value = required().value;
-    if (value is String) {
+    if (value is int && value != 0) {
       return FideId(value);
     }
     throw PickException("value $value at $debugParsingExit can't be casted to FideId");
diff --git a/lib/src/view/broadcast/broadcast_boards_tab.dart b/lib/src/view/broadcast/broadcast_boards_tab.dart
index a58bebff61..cb55aa1e23 100644
--- a/lib/src/view/broadcast/broadcast_boards_tab.dart
+++ b/lib/src/view/broadcast/broadcast_boards_tab.dart
@@ -24,10 +24,15 @@ const _kPlayerWidgetPadding = EdgeInsets.symmetric(vertical: 5.0);
 
 /// A tab that displays the live games of a broadcast round.
 class BroadcastBoardsTab extends ConsumerWidget {
-  const BroadcastBoardsTab({required this.roundId, required this.broadcastTitle});
+  const BroadcastBoardsTab({
+    required this.tournamentId,
+    required this.roundId,
+    required this.tournamentSlug,
+  });
 
+  final BroadcastTournamentId tournamentId;
   final BroadcastRoundId roundId;
-  final String broadcastTitle;
+  final String tournamentSlug;
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
@@ -58,9 +63,11 @@ class BroadcastBoardsTab extends ConsumerWidget {
               )
               : BroadcastPreview(
                 games: value.games.values.toIList(),
+                tournamentId: tournamentId,
                 roundId: roundId,
-                broadcastTitle: broadcastTitle,
-                roundTitle: value.round.name,
+                title: value.round.name,
+                tournamentSlug: tournamentSlug,
+                roundSlug: value.round.slug,
               ),
         AsyncError(:final error) => SliverFillRemaining(
           child: Center(child: Text('Could not load broadcast: $error')),
@@ -73,20 +80,28 @@ class BroadcastBoardsTab extends ConsumerWidget {
 
 class BroadcastPreview extends StatelessWidget {
   const BroadcastPreview({
+    required this.tournamentId,
     required this.roundId,
     required this.games,
-    required this.broadcastTitle,
-    required this.roundTitle,
+    required this.title,
+    required this.tournamentSlug,
+    required this.roundSlug,
   });
 
-  const BroadcastPreview.loading({required this.roundId, required this.broadcastTitle})
-    : games = null,
-      roundTitle = null;
+  const BroadcastPreview.loading()
+    : tournamentId = const BroadcastTournamentId(''),
+      roundId = const BroadcastRoundId(''),
+      games = null,
+      title = '',
+      tournamentSlug = '',
+      roundSlug = '';
 
+  final BroadcastTournamentId tournamentId;
   final BroadcastRoundId roundId;
   final IList<BroadcastGame>? games;
-  final String broadcastTitle;
-  final String? roundTitle;
+  final String title;
+  final String tournamentSlug;
+  final String roundSlug;
 
   @override
   Widget build(BuildContext context) {
@@ -115,7 +130,7 @@ class BroadcastPreview extends StatelessWidget {
       delegate: SliverChildBuilderDelegate(
         childCount: games == null ? numberLoadingBoards : games!.length,
         (context, index) {
-          if (games == null || roundTitle == null) {
+          if (games == null) {
             return ShimmerLoading(
               isLoading: true,
               child: BoardThumbnail.loading(
@@ -134,13 +149,15 @@ class BroadcastPreview extends StatelessWidget {
             onTap: () {
               pushPlatformRoute(
                 context,
-                title: roundTitle,
+                title: title,
                 builder:
                     (context) => BroadcastGameScreen(
+                      tournamentId: tournamentId,
                       roundId: roundId,
                       gameId: game.id,
-                      broadcastTitle: broadcastTitle,
-                      roundTitle: roundTitle!,
+                      tournamentSlug: tournamentSlug,
+                      roundSlug: roundSlug,
+                      title: title,
                     ),
               );
             },
diff --git a/lib/src/view/broadcast/broadcast_game_bottom_bar.dart b/lib/src/view/broadcast/broadcast_game_bottom_bar.dart
index a9e7ee843d..4283bfcc54 100644
--- a/lib/src/view/broadcast/broadcast_game_bottom_bar.dart
+++ b/lib/src/view/broadcast/broadcast_game_bottom_bar.dart
@@ -18,14 +18,14 @@ class BroadcastGameBottomBar extends ConsumerWidget {
   const BroadcastGameBottomBar({
     required this.roundId,
     required this.gameId,
-    required this.broadcastTitle,
-    required this.roundTitle,
+    this.tournamentSlug,
+    this.roundSlug,
   });
 
   final BroadcastRoundId roundId;
   final BroadcastGameId gameId;
-  final String broadcastTitle;
-  final String roundTitle;
+  final String? tournamentSlug;
+  final String? roundSlug;
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
@@ -40,17 +40,16 @@ class BroadcastGameBottomBar extends ConsumerWidget {
             showAdaptiveActionSheet<void>(
               context: context,
               actions: [
-                BottomSheetAction(
-                  makeLabel: (context) => Text(context.l10n.mobileShareGameURL),
-                  onPressed: (context) async {
-                    launchShareDialog(
-                      context,
-                      uri: lichessUri(
-                        '/broadcast/${broadcastTitle.toLowerCase().replaceAll(' ', '-')}/${roundTitle.toLowerCase().replaceAll(' ', '-')}/$roundId/$gameId',
-                      ),
-                    );
-                  },
-                ),
+                if (tournamentSlug != null && roundSlug != null)
+                  BottomSheetAction(
+                    makeLabel: (context) => Text(context.l10n.mobileShareGameURL),
+                    onPressed: (context) async {
+                      launchShareDialog(
+                        context,
+                        uri: lichessUri('/broadcast/$tournamentSlug/$roundSlug/$roundId/$gameId'),
+                      );
+                    },
+                  ),
                 BottomSheetAction(
                   makeLabel: (context) => Text(context.l10n.mobileShareGamePGN),
                   onPressed: (context) async {
diff --git a/lib/src/view/broadcast/broadcast_game_screen.dart b/lib/src/view/broadcast/broadcast_game_screen.dart
index 078f6e5d73..52cdf4d0db 100644
--- a/lib/src/view/broadcast/broadcast_game_screen.dart
+++ b/lib/src/view/broadcast/broadcast_game_screen.dart
@@ -7,7 +7,6 @@ import 'package:lichess_mobile/src/constants.dart';
 import 'package:lichess_mobile/src/model/analysis/analysis_preferences.dart';
 import 'package:lichess_mobile/src/model/broadcast/broadcast.dart';
 import 'package:lichess_mobile/src/model/broadcast/broadcast_game_controller.dart';
-import 'package:lichess_mobile/src/model/broadcast/broadcast_round_controller.dart';
 import 'package:lichess_mobile/src/model/common/chess.dart';
 import 'package:lichess_mobile/src/model/common/eval.dart';
 import 'package:lichess_mobile/src/model/common/id.dart';
@@ -19,8 +18,10 @@ import 'package:lichess_mobile/src/utils/l10n_context.dart';
 import 'package:lichess_mobile/src/utils/navigation.dart';
 import 'package:lichess_mobile/src/view/analysis/analysis_layout.dart';
 import 'package:lichess_mobile/src/view/broadcast/broadcast_game_bottom_bar.dart';
+import 'package:lichess_mobile/src/view/broadcast/broadcast_game_screen_providers.dart';
 import 'package:lichess_mobile/src/view/broadcast/broadcast_game_settings.dart';
 import 'package:lichess_mobile/src/view/broadcast/broadcast_game_tree_view.dart';
+import 'package:lichess_mobile/src/view/broadcast/broadcast_player_results_screen.dart';
 import 'package:lichess_mobile/src/view/broadcast/broadcast_player_widget.dart';
 import 'package:lichess_mobile/src/view/engine/engine_gauge.dart';
 import 'package:lichess_mobile/src/view/engine/engine_lines.dart';
@@ -31,16 +32,20 @@ import 'package:lichess_mobile/src/widgets/pgn.dart';
 import 'package:lichess_mobile/src/widgets/platform_scaffold.dart';
 
 class BroadcastGameScreen extends ConsumerStatefulWidget {
+  final BroadcastTournamentId tournamentId;
   final BroadcastRoundId roundId;
   final BroadcastGameId gameId;
-  final String broadcastTitle;
-  final String roundTitle;
+  final String? tournamentSlug;
+  final String? roundSlug;
+  final String? title;
 
   const BroadcastGameScreen({
+    required this.tournamentId,
     required this.roundId,
     required this.gameId,
-    required this.broadcastTitle,
-    required this.roundTitle,
+    this.tournamentSlug,
+    this.roundSlug,
+    this.title,
   });
 
   @override
@@ -69,13 +74,27 @@ class _BroadcastGameScreenState extends ConsumerState<BroadcastGameScreen>
 
   @override
   Widget build(BuildContext context) {
+    final broadcastRoundGameState = ref.watch(
+      broadcastRoundGameProvider(widget.roundId, widget.gameId),
+    );
     final broadcastGameState = ref.watch(
       broadcastGameControllerProvider(widget.roundId, widget.gameId),
     );
+    final title =
+        (widget.title != null)
+            ? Text(widget.title!, overflow: TextOverflow.ellipsis, maxLines: 1)
+            : switch (ref.watch(broadcastGameScreenTitleProvider(widget.roundId))) {
+              AsyncData(value: final title) => Text(
+                title,
+                overflow: TextOverflow.ellipsis,
+                maxLines: 1,
+              ),
+              _ => const SizedBox.shrink(),
+            };
 
     return PlatformScaffold(
       appBar: PlatformAppBar(
-        title: Text(widget.roundTitle, overflow: TextOverflow.ellipsis, maxLines: 1),
+        title: title,
         actions: [
           AppBarAnalysisTabIndicator(tabs: tabs, controller: _tabController),
           AppBarIconButton(
@@ -93,15 +112,17 @@ class _BroadcastGameScreenState extends ConsumerState<BroadcastGameScreen>
           ),
         ],
       ),
-      body: switch (broadcastGameState) {
-        AsyncData() => _Body(
+      body: switch ((broadcastRoundGameState, broadcastGameState)) {
+        (AsyncData(), AsyncData()) => _Body(
+          widget.tournamentId,
           widget.roundId,
           widget.gameId,
-          widget.broadcastTitle,
-          widget.roundTitle,
+          widget.tournamentSlug,
+          widget.roundSlug,
           tabController: _tabController,
         ),
-        AsyncError(:final error) => Center(child: Text('Cannot load broadcast game: $error')),
+        (AsyncError(:final error), _) => Center(child: Text('Cannot load broadcast game: $error')),
+        (_, AsyncError(:final error)) => Center(child: Text('Cannot load broadcast game: $error')),
         _ => const Center(child: CircularProgressIndicator.adaptive()),
       },
     );
@@ -110,17 +131,19 @@ class _BroadcastGameScreenState extends ConsumerState<BroadcastGameScreen>
 
 class _Body extends ConsumerWidget {
   const _Body(
+    this.tournamentId,
     this.roundId,
     this.gameId,
-    this.broadcastTitle,
-    this.roundTitle, {
+    this.tournamentSlug,
+    this.roundSlug, {
     required this.tabController,
   });
 
+  final BroadcastTournamentId tournamentId;
   final BroadcastRoundId roundId;
   final BroadcastGameId gameId;
-  final String broadcastTitle;
-  final String roundTitle;
+  final String? tournamentSlug;
+  final String? roundSlug;
   final TabController tabController;
 
   @override
@@ -140,11 +163,13 @@ class _Body extends ConsumerWidget {
           (context, boardSize, borderRadius) =>
               _BroadcastBoard(roundId, gameId, boardSize, borderRadius),
       boardHeader: _PlayerWidget(
+        tournamentId: tournamentId,
         roundId: roundId,
         gameId: gameId,
         widgetPosition: _PlayerWidgetPosition.top,
       ),
       boardFooter: _PlayerWidget(
+        tournamentId: tournamentId,
         roundId: roundId,
         gameId: gameId,
         widgetPosition: _PlayerWidgetPosition.bottom,
@@ -179,8 +204,8 @@ class _Body extends ConsumerWidget {
       bottomBar: BroadcastGameBottomBar(
         roundId: roundId,
         gameId: gameId,
-        broadcastTitle: broadcastTitle,
-        roundTitle: roundTitle,
+        tournamentSlug: tournamentSlug,
+        roundSlug: roundSlug,
       ),
       children: [_OpeningExplorerTab(roundId, gameId), BroadcastGameTreeView(roundId, gameId)],
     );
@@ -309,8 +334,14 @@ class _BroadcastBoardState extends ConsumerState<_BroadcastBoard> {
 enum _PlayerWidgetPosition { bottom, top }
 
 class _PlayerWidget extends ConsumerWidget {
-  const _PlayerWidget({required this.roundId, required this.gameId, required this.widgetPosition});
+  const _PlayerWidget({
+    required this.tournamentId,
+    required this.roundId,
+    required this.gameId,
+    required this.widgetPosition,
+  });
 
+  final BroadcastTournamentId tournamentId;
   final BroadcastRoundId roundId;
   final BroadcastGameId gameId;
   final _PlayerWidgetPosition widgetPosition;
@@ -319,14 +350,8 @@ class _PlayerWidget extends ConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final broadcastGameState =
         ref.watch(broadcastGameControllerProvider(roundId, gameId)).requireValue;
-    // TODO
-    // we'll probably want to remove this and get the game state from a single controller
-    // this won't work with deep links for instance
-    final game = ref.watch(
-      broadcastRoundControllerProvider(
-        roundId,
-      ).select((round) => round.requireValue.games[gameId]!),
-    );
+    final game = ref.watch(broadcastRoundGameProvider(roundId, gameId)).requireValue;
+
     final isCursorOnLiveMove =
         broadcastGameState.currentPath == broadcastGameState.broadcastLivePath;
     final sideToMove = broadcastGameState.position.turn;
@@ -334,6 +359,7 @@ class _PlayerWidget extends ConsumerWidget {
       _PlayerWidgetPosition.bottom => broadcastGameState.pov,
       _PlayerWidgetPosition.top => broadcastGameState.pov.opposite,
     };
+
     final player = game.players[side]!;
     final liveClock = isCursorOnLiveMove ? player.clock : null;
     final gameStatus = game.status;
@@ -341,73 +367,87 @@ class _PlayerWidget extends ConsumerWidget {
     final pastClocks = broadcastGameState.clocks;
     final pastClock = (sideToMove == side) ? pastClocks?.parentClock : pastClocks?.clock;
 
-    return Container(
-      color:
-          Theme.of(context).platform == TargetPlatform.iOS
-              ? Styles.cupertinoCardColor.resolveFrom(context)
-              : Theme.of(context).colorScheme.surfaceContainer,
-      padding: const EdgeInsets.only(left: 8.0),
-      child: Row(
-        children: [
-          if (game.isOver) ...[
-            Text(
-              (gameStatus == BroadcastResult.draw)
-                  ? '½'
-                  : (gameStatus == BroadcastResult.whiteWins)
-                  ? side == Side.white
-                      ? '1'
-                      : '0'
-                  : side == Side.black
-                  ? '1'
-                  : '0',
-              style: const TextStyle().copyWith(fontWeight: FontWeight.bold),
-            ),
-            const SizedBox(width: 16.0),
-          ],
-          Expanded(
-            child: BroadcastPlayerWidget(
-              federation: player.federation,
-              title: player.title,
-              name: player.name,
-              rating: player.rating,
-              textStyle: const TextStyle().copyWith(fontWeight: FontWeight.bold),
+    return GestureDetector(
+      onTap: () {
+        pushPlatformRoute(
+          context,
+          builder:
+              (context) => BroadcastPlayerResultsScreen(
+                tournamentId,
+                (player.fideId != null) ? player.fideId!.toString() : player.name,
+                player.title,
+                player.name,
+              ),
+        );
+      },
+      child: Container(
+        color:
+            Theme.of(context).platform == TargetPlatform.iOS
+                ? Styles.cupertinoCardColor.resolveFrom(context)
+                : Theme.of(context).colorScheme.surfaceContainer,
+        padding: const EdgeInsets.only(left: 8.0),
+        child: Row(
+          children: [
+            if (game.isOver) ...[
+              Text(
+                (gameStatus == BroadcastResult.draw)
+                    ? '½'
+                    : (gameStatus == BroadcastResult.whiteWins)
+                    ? side == Side.white
+                        ? '1'
+                        : '0'
+                    : side == Side.black
+                    ? '1'
+                    : '0',
+                style: const TextStyle().copyWith(fontWeight: FontWeight.bold),
+              ),
+              const SizedBox(width: 16.0),
+            ],
+            Expanded(
+              child: BroadcastPlayerWidget(
+                federation: player.federation,
+                title: player.title,
+                name: player.name,
+                rating: player.rating,
+                textStyle: const TextStyle().copyWith(fontWeight: FontWeight.bold),
+              ),
             ),
-          ),
-          if (liveClock != null || pastClock != null)
-            Container(
-              height: kAnalysisBoardHeaderOrFooterHeight,
-              color:
-                  (side == sideToMove)
-                      ? isCursorOnLiveMove
-                          ? Theme.of(context).colorScheme.tertiaryContainer
-                          : Theme.of(context).colorScheme.secondaryContainer
-                      : Colors.transparent,
-              child: Padding(
-                padding: const EdgeInsets.symmetric(horizontal: 6.0),
-                child: Center(
-                  child:
-                      liveClock != null
-                          ? CountdownClockBuilder(
-                            timeLeft: liveClock,
-                            active: side == sideToMove,
-                            builder:
-                                (context, timeLeft) => _Clock(
-                                  timeLeft: timeLeft,
-                                  isSideToMove: side == sideToMove,
-                                  isLive: true,
-                                ),
-                            tickInterval: const Duration(seconds: 1),
-                            clockUpdatedAt: game.updatedClockAt,
-                          )
-                          : _Clock(
-                            timeLeft: pastClock!,
-                            isSideToMove: side == sideToMove,
-                            isLive: false,
-                          ),
+            if (liveClock != null || pastClock != null)
+              Container(
+                height: kAnalysisBoardHeaderOrFooterHeight,
+                color:
+                    (side == sideToMove)
+                        ? isCursorOnLiveMove
+                            ? Theme.of(context).colorScheme.tertiaryContainer
+                            : Theme.of(context).colorScheme.secondaryContainer
+                        : Colors.transparent,
+                child: Padding(
+                  padding: const EdgeInsets.symmetric(horizontal: 6.0),
+                  child: Center(
+                    child:
+                        liveClock != null
+                            ? CountdownClockBuilder(
+                              timeLeft: liveClock,
+                              active: side == sideToMove,
+                              builder:
+                                  (context, timeLeft) => _Clock(
+                                    timeLeft: timeLeft,
+                                    isSideToMove: side == sideToMove,
+                                    isLive: true,
+                                  ),
+                              tickInterval: const Duration(seconds: 1),
+                              clockUpdatedAt: game.updatedClockAt,
+                            )
+                            : _Clock(
+                              timeLeft: pastClock!,
+                              isSideToMove: side == sideToMove,
+                              isLive: false,
+                            ),
+                  ),
                 ),
               ),
-            ),
-        ],
+          ],
+        ),
       ),
     );
   }
diff --git a/lib/src/view/broadcast/broadcast_game_screen_providers.dart b/lib/src/view/broadcast/broadcast_game_screen_providers.dart
new file mode 100644
index 0000000000..288733f47f
--- /dev/null
+++ b/lib/src/view/broadcast/broadcast_game_screen_providers.dart
@@ -0,0 +1,25 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:lichess_mobile/src/model/broadcast/broadcast.dart';
+import 'package:lichess_mobile/src/model/broadcast/broadcast_round_controller.dart';
+import 'package:lichess_mobile/src/model/common/id.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'broadcast_game_screen_providers.g.dart';
+
+@riverpod
+Future<BroadcastGame> broadcastRoundGame(
+  Ref ref,
+  BroadcastRoundId roundId,
+  BroadcastGameId gameId,
+) {
+  return ref.watch(
+    broadcastRoundControllerProvider(roundId).selectAsync((round) => round.games[gameId]!),
+  );
+}
+
+@riverpod
+Future<String> broadcastGameScreenTitle(Ref ref, BroadcastRoundId roundId) {
+  return ref.watch(
+    broadcastRoundControllerProvider(roundId).selectAsync((round) => round.round.name),
+  );
+}
diff --git a/lib/src/view/broadcast/broadcast_list_screen.dart b/lib/src/view/broadcast/broadcast_list_screen.dart
index 81d09d934b..98aee4235c 100644
--- a/lib/src/view/broadcast/broadcast_list_screen.dart
+++ b/lib/src/view/broadcast/broadcast_list_screen.dart
@@ -266,6 +266,7 @@ class BroadcastCard extends StatefulWidget {
         tour: BroadcastTournamentData(
           id: BroadcastTournamentId(''),
           name: '',
+          slug: '',
           imageUrl: null,
           description: '',
           information: (
@@ -280,6 +281,7 @@ class BroadcastCard extends StatefulWidget {
         round: BroadcastRound(
           id: BroadcastRoundId(''),
           name: '',
+          slug: '',
           status: RoundStatus.finished,
           startsAt: null,
           finishedAt: null,
diff --git a/lib/src/view/broadcast/broadcast_player_results_screen.dart b/lib/src/view/broadcast/broadcast_player_results_screen.dart
new file mode 100644
index 0000000000..2b1d14527b
--- /dev/null
+++ b/lib/src/view/broadcast/broadcast_player_results_screen.dart
@@ -0,0 +1,308 @@
+import 'dart:math';
+
+import 'package:dartchess/dartchess.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:lichess_mobile/src/model/broadcast/broadcast.dart';
+import 'package:lichess_mobile/src/model/broadcast/broadcast_federation.dart';
+import 'package:lichess_mobile/src/model/broadcast/broadcast_providers.dart';
+import 'package:lichess_mobile/src/model/common/id.dart';
+import 'package:lichess_mobile/src/styles/styles.dart';
+import 'package:lichess_mobile/src/utils/l10n_context.dart';
+import 'package:lichess_mobile/src/utils/navigation.dart';
+import 'package:lichess_mobile/src/view/broadcast/broadcast_game_screen.dart';
+import 'package:lichess_mobile/src/view/broadcast/broadcast_player_widget.dart';
+import 'package:lichess_mobile/src/widgets/platform_scaffold.dart';
+import 'package:lichess_mobile/src/widgets/progression_widget.dart';
+import 'package:lichess_mobile/src/widgets/stat_card.dart';
+
+class BroadcastPlayerResultsScreen extends StatelessWidget {
+  final BroadcastTournamentId tournamentId;
+  final String playerId;
+  final String? playerTitle;
+  final String playerName;
+
+  const BroadcastPlayerResultsScreen(
+    this.tournamentId,
+    this.playerId,
+    this.playerTitle,
+    this.playerName,
+  );
+
+  @override
+  Widget build(BuildContext context) {
+    return PlatformScaffold(
+      appBar: PlatformAppBar(title: BroadcastPlayerWidget(title: playerTitle, name: playerName)),
+      body: _Body(tournamentId, playerId),
+    );
+  }
+}
+
+const _kTableRowPadding = EdgeInsets.symmetric(vertical: 12.0);
+
+class _Body extends ConsumerWidget {
+  final BroadcastTournamentId tournamentId;
+  final String playerId;
+
+  const _Body(this.tournamentId, this.playerId);
+
+  @override
+  Widget build(BuildContext context, WidgetRef ref) {
+    final playersResults = ref.watch(broadcastPlayerResultProvider(tournamentId, playerId));
+
+    switch (playersResults) {
+      case AsyncData(value: final playerResults):
+        final player = playerResults.player;
+        final fideData = playerResults.fideData;
+        final showRatingDiff = playerResults.games.any((result) => result.ratingDiff != null);
+        final statWidth =
+            (MediaQuery.sizeOf(context).width - Styles.bodyPadding.horizontal - 10 * 2) / 3;
+        const cardSpacing = 10.0;
+        final indexWidth = max(8.0 + playerResults.games.length.toString().length * 10.0, 28.0);
+
+        return ListView.builder(
+          itemCount: playerResults.games.length + 1,
+          itemBuilder: (context, index) {
+            if (index == 0) {
+              return Padding(
+                padding: Styles.bodyPadding,
+                child: Column(
+                  spacing: cardSpacing,
+                  children: [
+                    if (fideData.ratings.standard != null &&
+                        fideData.ratings.rapid != null &&
+                        fideData.ratings.blitz != null)
+                      Row(
+                        mainAxisAlignment: MainAxisAlignment.center,
+                        spacing: cardSpacing,
+                        children: [
+                          if (fideData.ratings.standard != null)
+                            SizedBox(
+                              width: statWidth,
+                              child: StatCard(
+                                context.l10n.classical,
+                                value: fideData.ratings.standard.toString(),
+                              ),
+                            ),
+                          if (fideData.ratings.rapid != null)
+                            SizedBox(
+                              width: statWidth,
+                              child: StatCard(
+                                context.l10n.rapid,
+                                value: fideData.ratings.rapid.toString(),
+                              ),
+                            ),
+                          if (fideData.ratings.blitz != null)
+                            SizedBox(
+                              width: statWidth,
+                              child: StatCard(
+                                context.l10n.blitz,
+                                value: fideData.ratings.blitz.toString(),
+                              ),
+                            ),
+                        ],
+                      ),
+                    if (fideData.birthYear != null &&
+                        player.federation != null &&
+                        player.fideId != null)
+                      Row(
+                        mainAxisAlignment: MainAxisAlignment.center,
+                        spacing: cardSpacing,
+                        children: [
+                          if (fideData.birthYear != null)
+                            SizedBox(
+                              width: statWidth,
+                              child: StatCard(
+                                context.l10n.broadcastAgeThisYear,
+                                value: (DateTime.now().year - fideData.birthYear!).toString(),
+                              ),
+                            ),
+                          if (player.federation != null)
+                            SizedBox(
+                              width: statWidth,
+                              child: StatCard(
+                                context.l10n.broadcastFederation,
+                                child: Row(
+                                  mainAxisAlignment: MainAxisAlignment.center,
+                                  children: [
+                                    Image.asset(
+                                      'assets/images/fide-fed/${player.federation}.png',
+                                      height: 12,
+                                    ),
+                                    const SizedBox(width: 5),
+                                    Flexible(
+                                      child: Text(
+                                        federationIdToName[player.federation!]!,
+                                        style: const TextStyle(fontSize: 18.0),
+                                        overflow: TextOverflow.ellipsis,
+                                      ),
+                                    ),
+                                  ],
+                                ),
+                              ),
+                            ),
+                          if (player.fideId != null)
+                            SizedBox(
+                              width: statWidth,
+                              child: StatCard('FIDE ID', value: player.fideId!.toString()),
+                            ),
+                        ],
+                      ),
+                    Row(
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      spacing: cardSpacing,
+                      children: [
+                        if (player.score != null)
+                          SizedBox(
+                            width: statWidth,
+                            child: StatCard(
+                              context.l10n.broadcastScore,
+                              value:
+                                  '${player.score!.toStringAsFixed((player.score! == player.score!.roundToDouble()) ? 0 : 1)} / ${player.played}',
+                            ),
+                          ),
+                        if (player.performance != null)
+                          SizedBox(
+                            width: statWidth,
+                            child: StatCard(
+                              context.l10n.performance,
+                              value: player.performance.toString(),
+                            ),
+                          ),
+                        if (player.ratingDiff != null)
+                          SizedBox(
+                            width: statWidth,
+                            child: StatCard(
+                              context.l10n.broadcastRatingDiff,
+                              child: ProgressionWidget(player.ratingDiff!, fontSize: 18.0),
+                            ),
+                          ),
+                      ],
+                    ),
+                  ],
+                ),
+              );
+            }
+
+            final playerResult = playerResults.games[index - 1];
+
+            return GestureDetector(
+              onTap: () {
+                pushPlatformRoute(
+                  context,
+                  builder:
+                      (context) => BroadcastGameScreen(
+                        tournamentId: tournamentId,
+                        roundId: playerResult.roundId,
+                        gameId: playerResult.gameId,
+                      ),
+                );
+              },
+              child: ColoredBox(
+                color:
+                    Theme.of(context).platform == TargetPlatform.iOS
+                        ? index.isEven
+                            ? CupertinoColors.secondarySystemBackground.resolveFrom(context)
+                            : CupertinoColors.tertiarySystemBackground.resolveFrom(context)
+                        : index.isEven
+                        ? Theme.of(context).colorScheme.surfaceContainerLow
+                        : Theme.of(context).colorScheme.surfaceContainerHigh,
+                child: Padding(
+                  padding: _kTableRowPadding,
+                  child: Row(
+                    children: [
+                      SizedBox(
+                        width: indexWidth,
+                        child: Center(
+                          child: Text(
+                            index.toString(),
+                            style: const TextStyle(fontWeight: FontWeight.bold),
+                          ),
+                        ),
+                      ),
+                      Expanded(
+                        flex: 5,
+                        child: BroadcastPlayerWidget(
+                          federation: playerResult.opponent.federation,
+                          title: playerResult.opponent.title,
+                          name: playerResult.opponent.name,
+                        ),
+                      ),
+                      Expanded(
+                        flex: 3,
+                        child:
+                            (playerResult.opponent.rating != null)
+                                ? Center(child: Text(playerResult.opponent.rating.toString()))
+                                : const SizedBox.shrink(),
+                      ),
+                      SizedBox(
+                        width: 30,
+                        child: Center(
+                          child: Container(
+                            width: 15,
+                            height: 15,
+                            decoration: BoxDecoration(
+                              border:
+                                  (Theme.of(context).brightness == Brightness.light &&
+                                              playerResult.color == Side.white ||
+                                          Theme.of(context).brightness == Brightness.dark &&
+                                              playerResult.color == Side.black)
+                                      ? Border.all(
+                                        width: 2.0,
+                                        color: Theme.of(context).colorScheme.outline,
+                                      )
+                                      : null,
+                              shape: BoxShape.circle,
+                              color: switch (playerResult.color) {
+                                Side.white => Colors.white.withValues(alpha: 0.9),
+                                Side.black => Colors.black.withValues(alpha: 0.9),
+                              },
+                            ),
+                          ),
+                        ),
+                      ),
+                      SizedBox(
+                        width: 30,
+                        child: Center(
+                          child: Text(
+                            switch (playerResult.points) {
+                              BroadcastPoints.one => '1',
+                              BroadcastPoints.half => '½',
+                              BroadcastPoints.zero => '0',
+                              _ => '*',
+                            },
+                            style: TextStyle(
+                              fontSize: 16,
+                              fontWeight: FontWeight.bold,
+                              color: switch (playerResult.points) {
+                                BroadcastPoints.one => context.lichessColors.good,
+                                BroadcastPoints.zero => context.lichessColors.error,
+                                _ => null,
+                              },
+                            ),
+                          ),
+                        ),
+                      ),
+                      if (showRatingDiff)
+                        SizedBox(
+                          width: 38,
+                          child:
+                              (playerResult.ratingDiff != null)
+                                  ? ProgressionWidget(playerResult.ratingDiff!, fontSize: 14)
+                                  : null,
+                        ),
+                    ],
+                  ),
+                ),
+              ),
+            );
+          },
+        );
+      case AsyncError(:final error):
+        return Center(child: Text('Cannot load player data: $error'));
+      case _:
+        return const Center(child: CircularProgressIndicator.adaptive());
+    }
+  }
+}
diff --git a/lib/src/view/broadcast/broadcast_player_widget.dart b/lib/src/view/broadcast/broadcast_player_widget.dart
index 8e73f17394..e46774cbab 100644
--- a/lib/src/view/broadcast/broadcast_player_widget.dart
+++ b/lib/src/view/broadcast/broadcast_player_widget.dart
@@ -4,7 +4,7 @@ import 'package:lichess_mobile/src/styles/styles.dart';
 
 class BroadcastPlayerWidget extends ConsumerWidget {
   const BroadcastPlayerWidget({
-    required this.federation,
+    this.federation,
     required this.title,
     required this.name,
     this.rating,
@@ -28,8 +28,8 @@ class BroadcastPlayerWidget extends ConsumerWidget {
         if (title != null) ...[
           Text(
             title!,
-            style: const TextStyle().copyWith(
-              color: context.lichessColors.brag,
+            style: TextStyle(
+              color: (title == 'BOT') ? context.lichessColors.fancy : context.lichessColors.brag,
               fontWeight: FontWeight.bold,
             ),
           ),
@@ -38,7 +38,7 @@ class BroadcastPlayerWidget extends ConsumerWidget {
         Flexible(child: Text(name, style: textStyle, overflow: TextOverflow.ellipsis)),
         if (rating != null) ...[
           const SizedBox(width: 5),
-          Text(rating.toString(), style: const TextStyle(), overflow: TextOverflow.ellipsis),
+          Text(rating.toString(), overflow: TextOverflow.ellipsis),
         ],
       ],
     );
diff --git a/lib/src/view/broadcast/broadcast_players_tab.dart b/lib/src/view/broadcast/broadcast_players_tab.dart
index 985e510b01..713766979e 100644
--- a/lib/src/view/broadcast/broadcast_players_tab.dart
+++ b/lib/src/view/broadcast/broadcast_players_tab.dart
@@ -9,6 +9,8 @@ import 'package:lichess_mobile/src/model/broadcast/broadcast_providers.dart';
 import 'package:lichess_mobile/src/model/common/id.dart';
 import 'package:lichess_mobile/src/styles/styles.dart';
 import 'package:lichess_mobile/src/utils/l10n_context.dart';
+import 'package:lichess_mobile/src/utils/navigation.dart';
+import 'package:lichess_mobile/src/view/broadcast/broadcast_player_results_screen.dart';
 import 'package:lichess_mobile/src/view/broadcast/broadcast_player_widget.dart';
 import 'package:lichess_mobile/src/widgets/progression_widget.dart';
 
@@ -29,7 +31,7 @@ class BroadcastPlayersTab extends ConsumerWidget {
     final players = ref.watch(broadcastPlayersProvider(tournamentId));
 
     return switch (players) {
-      AsyncData(value: final players) => PlayersList(players),
+      AsyncData(value: final players) => PlayersList(players, tournamentId),
       AsyncError(:final error) => SliverPadding(
         padding: edgeInsets,
         sliver: SliverFillRemaining(child: Center(child: Text('Cannot load players data: $error'))),
@@ -50,9 +52,10 @@ const _kTableRowPadding = EdgeInsets.symmetric(
 const _kHeaderTextStyle = TextStyle(fontWeight: FontWeight.bold, overflow: TextOverflow.ellipsis);
 
 class PlayersList extends ConsumerStatefulWidget {
-  const PlayersList(this.players);
+  const PlayersList(this.players, this.tournamentId);
 
   final IList<BroadcastPlayerExtended> players;
+  final BroadcastTournamentId tournamentId;
 
   @override
   ConsumerState<PlayersList> createState() => _PlayersListState();
@@ -75,7 +78,13 @@ class _PlayersListState extends ConsumerState<PlayersList> {
       _SortingTypes.score => (BroadcastPlayerExtended a, BroadcastPlayerExtended b) {
         if (a.score == null) return 1;
         if (b.score == null) return -1;
-        return b.score!.compareTo(a.score!);
+
+        final value = b.score!.compareTo(a.score!);
+        if (value == 0) {
+          return a.played.compareTo(b.played);
+        } else {
+          return value;
+        }
       },
     };
 
@@ -109,7 +118,7 @@ class _PlayersListState extends ConsumerState<PlayersList> {
   @override
   Widget build(BuildContext context) {
     final double eloWidth = max(MediaQuery.sizeOf(context).width * 0.2, 100);
-    final double scoreWidth = max(MediaQuery.sizeOf(context).width * 0.15, 70);
+    final double scoreWidth = max(MediaQuery.sizeOf(context).width * 0.15, 90);
 
     return SliverList.builder(
       itemCount: players.length + 1,
@@ -164,8 +173,21 @@ class _PlayersListState extends ConsumerState<PlayersList> {
           );
         } else {
           final player = players[index - 1];
-          return Container(
-            decoration: BoxDecoration(
+
+          return GestureDetector(
+            onTap: () {
+              pushPlatformRoute(
+                context,
+                builder:
+                    (context) => BroadcastPlayerResultsScreen(
+                      widget.tournamentId,
+                      player.fideId != null ? player.fideId.toString() : player.name,
+                      player.title,
+                      player.name,
+                    ),
+              );
+            },
+            child: ColoredBox(
               color:
                   Theme.of(context).platform == TargetPlatform.iOS
                       ? index.isEven
@@ -174,49 +196,52 @@ class _PlayersListState extends ConsumerState<PlayersList> {
                       : index.isEven
                       ? Theme.of(context).colorScheme.surfaceContainerLow
                       : Theme.of(context).colorScheme.surfaceContainerHigh,
-            ),
-            child: Row(
-              crossAxisAlignment: CrossAxisAlignment.center,
-              children: [
-                Expanded(
-                  child: Padding(
-                    padding: _kTableRowPadding,
-                    child: BroadcastPlayerWidget(
-                      federation: player.federation,
-                      title: player.title,
-                      name: player.name,
+              child: Row(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  Expanded(
+                    child: Padding(
+                      padding: _kTableRowPadding,
+                      child: BroadcastPlayerWidget(
+                        federation: player.federation,
+                        title: player.title,
+                        name: player.name,
+                      ),
                     ),
                   ),
-                ),
-                SizedBox(
-                  width: eloWidth,
-                  child: Padding(
-                    padding: _kTableRowPadding,
-                    child: Row(
-                      children: [
-                        if (player.rating != null) ...[
-                          Text(player.rating.toString()),
-                          const SizedBox(width: 5),
-                          if (player.ratingDiff != null)
-                            ProgressionWidget(player.ratingDiff!, fontSize: 14),
+                  SizedBox(
+                    width: eloWidth,
+                    child: Padding(
+                      padding: _kTableRowPadding,
+                      child: Row(
+                        children: [
+                          if (player.rating != null) ...[
+                            Text(player.rating.toString()),
+                            const SizedBox(width: 5),
+                            if (player.ratingDiff != null)
+                              ProgressionWidget(player.ratingDiff!, fontSize: 14),
+                          ],
                         ],
-                      ],
+                      ),
                     ),
                   ),
-                ),
-                SizedBox(
-                  width: scoreWidth,
-                  child: Padding(
-                    padding: _kTableRowPadding,
-                    child:
-                        (player.score != null)
-                            ? Text(
-                              '${player.score!.toStringAsFixed((player.score! == player.score!.roundToDouble()) ? 0 : 1)}/${player.played}',
-                            )
-                            : const SizedBox.shrink(),
+                  SizedBox(
+                    width: scoreWidth,
+                    child: Padding(
+                      padding: _kTableRowPadding,
+                      child:
+                          (player.score != null)
+                              ? Align(
+                                alignment: Alignment.centerRight,
+                                child: Text(
+                                  '${player.score!.toStringAsFixed((player.score! == player.score!.roundToDouble()) ? 0 : 1)} / ${player.played}',
+                                ),
+                              )
+                              : null,
+                    ),
                   ),
-                ),
-              ],
+                ],
+              ),
             ),
           );
         }
diff --git a/lib/src/view/broadcast/broadcast_round_screen.dart b/lib/src/view/broadcast/broadcast_round_screen.dart
index bd042a96f6..cafba27587 100644
--- a/lib/src/view/broadcast/broadcast_round_screen.dart
+++ b/lib/src/view/broadcast/broadcast_round_screen.dart
@@ -117,8 +117,9 @@ class _BroadcastRoundScreenState extends ConsumerState<BroadcastRoundScreen>
                   cupertinoTabSwitcher: tabSwitcher,
                   sliver: switch (asyncTournament) {
                     AsyncData(:final value) => BroadcastBoardsTab(
+                      tournamentId: _selectedTournamentId,
                       roundId: _selectedRoundId ?? value.defaultRoundId,
-                      broadcastTitle: widget.broadcast.title,
+                      tournamentSlug: widget.broadcast.tour.slug,
                     ),
                     _ => const SliverFillRemaining(child: SizedBox.shrink()),
                   },
@@ -180,8 +181,9 @@ class _BroadcastRoundScreenState extends ConsumerState<BroadcastRoundScreen>
             _TabView(
               sliver: switch (asyncTournament) {
                 AsyncData(:final value) => BroadcastBoardsTab(
+                  tournamentId: _selectedTournamentId,
                   roundId: _selectedRoundId ?? value.defaultRoundId,
-                  broadcastTitle: widget.broadcast.title,
+                  tournamentSlug: widget.broadcast.tour.slug,
                 ),
                 _ => const SliverFillRemaining(child: SizedBox.shrink()),
               },