OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
HASH      72874ae6e5d4
DATE      2026-07-16
SUBJECT   ookstats + web: season 3 defaults, dynamic season scopes
FILES     9 CHANGED
HASH      72874ae6e5d4
DATE      2026-07-16
SUBJECT   ookstats + web: season 3 defaults,
          dynamic season scopes
FILES     9 CHANGED
 

diff --git a/nix/pkgs/ookstats/src/internal/generator/gear.go b/nix/pkgs/ookstats/
src/internal/generator/gear.go
index c7acfe2..6dff3d4 100644
--- a/nix/pkgs/ookstats/src/internal/generator/gear.go
+++ b/nix/pkgs/ookstats/src/internal/generator/gear.go
@@ -92,7 +92,12 @@ func GenerateGear(db *sql.DB, outDir string) error {
 		return fmt.Errorf("load item profiles: %w", err)
 	}
 
-	for _, season := range []int{1, 2} {
+	seasons, err := loadGearSeasons(db)
+	if err != nil {
+		return fmt.Errorf("load gear seasons: %w", err)
+	}
+
+	for _, season := range seasons {
 		seasonKey := fmt.Sprintf("season_%d", season)
 		bySpec := make(map[string]GearSpecBucket)
 
@@ -122,6 +127,23 @@ func GenerateGear(db *sql.DB, outDir string) error {
 
 // gatherSpecIDs returns every spec id we have a primary-stat mapping for. Using
 // a yield-style helper keeps the caller readable without exporting the map.
+func loadGearSeasons(db *sql.DB) ([]int, error) {
+	rows, err := db.Query(`SELECT DISTINCT season_number FROM seasons ORDER BY seaso
n_number ASC`)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var out []int
+	for rows.Next() {
+		var n int
+		if err := rows.Scan(&n); err != nil {
+			return nil, err
+		}
+		out = append(out, n)
+	}
+	return out, rows.Err()
+}
+
 func gatherSpecIDs() map[int]struct{} {
 	out := make(map[int]struct{}, 33)
 	for _, sid := range []int{

diff --git a/nix/pkgs/ookstats/src/internal/
generator/gear.go b/nix/pkgs/ookstats/src/in
ternal/generator/gear.go
index c7acfe2..6dff3d4 100644
--- a/nix/pkgs/ookstats/src/internal/generat
or/gear.go
+++ b/nix/pkgs/ookstats/src/internal/generat
or/gear.go
@@ -92,7 +92,12 @@ func GenerateGear(db *sql
.DB, outDir string) error {
 		return fmt.Errorf("load item profiles: %w
", err)
 	}
 
-	for _, season := range []int{1, 2} {
+	seasons, err := loadGearSeasons(db)
+	if err != nil {
+		return fmt.Errorf("load gear seasons: %w"
, err)
+	}
+
+	for _, season := range seasons {
 		seasonKey := fmt.Sprintf("season_%d", sea
son)
 		bySpec := make(map[string]GearSpecBucket)
 
@@ -122,6 +127,23 @@ func GenerateGear(db *s
ql.DB, outDir string) error {
 
 // gatherSpecIDs returns every spec id we h
ave a primary-stat mapping for. Using
 // a yield-style helper keeps the caller re
adable without exporting the map.
+func loadGearSeasons(db *sql.DB) ([]int, er
ror) {
+	rows, err := db.Query(`SELECT DISTINCT sea
son_number FROM seasons ORDER BY season_numb
er ASC`)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var out []int
+	for rows.Next() {
+		var n int
+		if err := rows.Scan(&n); err != nil {
+			return nil, err
+		}
+		out = append(out, n)
+	}
+	return out, rows.Err()
+}
+
 func gatherSpecIDs() map[int]struct{} {
 	out := make(map[int]struct{}, 33)
 	for _, sid := range []int{
 

diff --git a/nix/pkgs/ookstats/src/internal/generator/stats.go b/nix/pkgs/ookstats
/src/internal/generator/stats.go
index 3f0a88b..67231d8 100644
--- a/nix/pkgs/ookstats/src/internal/generator/stats.go
+++ b/nix/pkgs/ookstats/src/internal/generator/stats.go
@@ -113,17 +113,16 @@ type statsScopeDef struct {
 	SeasonNum int // 0 = all-time
 }
 
-var statsScopes = []statsScopeDef{
-	{Key: "all_time", SeasonNum: 0},
-	{Key: "season_1", SeasonNum: 1},
-	{Key: "season_2", SeasonNum: 2},
-}
-
 // statsRegions are the region scopes emitted; "global" means no region filter.
 var statsRegions = []string{"global", "us", "eu", "kr", "tw"}
 
 // GenerateStats writes outDir/api/stats.json with aggregated stats per (region, 
season) scope.
 func GenerateStats(db *sql.DB, outDir string) error {
+	statsScopes, err := loadStatsScopes(db)
+	if err != nil {
+		return fmt.Errorf("load stats scopes: %w", err)
+	}
+
 	out := StatsJSON{
 		GeneratedAt: time.Now().UnixMilli(),
 		Scopes:      make(map[string]map[string]StatsScope, len(statsRegions)),
@@ -154,6 +153,24 @@ func GenerateStats(db *sql.DB, outDir string) error {
 	return nil
 }
 
+// loadStatsScopes returns all-time plus one scope per season in the DB
+func loadStatsScopes(db *sql.DB) ([]statsScopeDef, error) {
+	rows, err := db.Query(`SELECT DISTINCT season_number FROM seasons ORDER BY seaso
n_number ASC`)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	scopes := []statsScopeDef{{Key: "all_time", SeasonNum: 0}}
+	for rows.Next() {
+		var n int
+		if err := rows.Scan(&n); err != nil {
+			return nil, err
+		}
+		scopes = append(scopes, statsScopeDef{Key: fmt.Sprintf("season_%d", n), SeasonN
um: n})
+	}
+	return scopes, rows.Err()
+}
+
 // buildStatsScope assembles a single StatsScope.
 // seasonNum == 0 means cross-season (all-time). region == "global" means no regi
on filter.
 // For all-time, pass allTimeTotalPlayers=0; for per-season builds, pass the all-
time scope's

diff --git a/nix/pkgs/ookstats/src/internal/
generator/stats.go b/nix/pkgs/ookstats/src/i
nternal/generator/stats.go
index 3f0a88b..67231d8 100644
--- a/nix/pkgs/ookstats/src/internal/generat
or/stats.go
+++ b/nix/pkgs/ookstats/src/internal/generat
or/stats.go
@@ -113,17 +113,16 @@ type statsScopeDef str
uct {
 	SeasonNum int // 0 = all-time
 }
 
-var statsScopes = []statsScopeDef{
-	{Key: "all_time", SeasonNum: 0},
-	{Key: "season_1", SeasonNum: 1},
-	{Key: "season_2", SeasonNum: 2},
-}
-
 // statsRegions are the region scopes emitt
ed; "global" means no region filter.
 var statsRegions = []string{"global", "us",
 "eu", "kr", "tw"}
 
 // GenerateStats writes outDir/api/stats.js
on with aggregated stats per (region, season
) scope.
 func GenerateStats(db *sql.DB, outDir strin
g) error {
+	statsScopes, err := loadStatsScopes(db)
+	if err != nil {
+		return fmt.Errorf("load stats scopes: %w"
, err)
+	}
+
 	out := StatsJSON{
 		GeneratedAt: time.Now().UnixMilli(),
 		Scopes:      make(map[string]map[string]S
tatsScope, len(statsRegions)),
@@ -154,6 +153,24 @@ func GenerateStats(db *
sql.DB, outDir string) error {
 	return nil
 }
 
+// loadStatsScopes returns all-time plus on
e scope per season in the DB
+func loadStatsScopes(db *sql.DB) ([]statsSc
opeDef, error) {
+	rows, err := db.Query(`SELECT DISTINCT sea
son_number FROM seasons ORDER BY season_numb
er ASC`)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	scopes := []statsScopeDef{{Key: "all_time"
, SeasonNum: 0}}
+	for rows.Next() {
+		var n int
+		if err := rows.Scan(&n); err != nil {
+			return nil, err
+		}
+		scopes = append(scopes, statsScopeDef{Key
: fmt.Sprintf("season_%d", n), SeasonNum: n}
)
+	}
+	return scopes, rows.Err()
+}
+
 // buildStatsScope assembles a single Stats
Scope.
 // seasonNum == 0 means cross-season (all-t
ime). region == "global" means no region fil
ter.
 // For all-time, pass allTimeTotalPlayers=0
; for per-season builds, pass the all-time s
cope's
 

diff --git a/web/src/components/Home/ChallengeRun/ChallengeRun.astro b/web/src/com
ponents/Home/ChallengeRun/ChallengeRun.astro
index b86bbee..687bb8d 100644
--- a/web/src/components/Home/ChallengeRun/ChallengeRun.astro
+++ b/web/src/components/Home/ChallengeRun/ChallengeRun.astro
@@ -16,6 +16,7 @@ interface Props {
   showDungeonName?: boolean;
   rank?: number;
   bracket?: string;
+  seasonId?: number;
 }
 
 const {
@@ -28,6 +29,7 @@ const {
   showDungeonName = true,
   rank,
   bracket,
+  seasonId = 3,
 } = Astro.props;
 
 const dungeonIcon = getDungeonIconUrl(dungeonSlug, dungeonName);
@@ -39,7 +41,7 @@ const bracketClass = bracket ? `bracket-${bracket}` : "";
     showDungeonIcon && dungeonIcon && (
       <a
         class="challenge-run__dungeon"
-        href={`/challenge-mode/season2/global/${dungeonSlug}`}
+        href={`/challenge-mode/season${seasonId}/global/${dungeonSlug}`}
         title={dungeonName}
       >
         <img src={dungeonIcon} alt={dungeonName} loading="lazy" />

diff --git a/web/src/components/Home/Challen
geRun/ChallengeRun.astro b/web/src/component
s/Home/ChallengeRun/ChallengeRun.astro
index b86bbee..687bb8d 100644
--- a/web/src/components/Home/ChallengeRun/C
hallengeRun.astro
+++ b/web/src/components/Home/ChallengeRun/C
hallengeRun.astro
@@ -16,6 +16,7 @@ interface Props {
   showDungeonName?: boolean;
   rank?: number;
   bracket?: string;
+  seasonId?: number;
 }
 
 const {
@@ -28,6 +29,7 @@ const {
   showDungeonName = true,
   rank,
   bracket,
+  seasonId = 3,
 } = Astro.props;
 
 const dungeonIcon = getDungeonIconUrl(dunge
onSlug, dungeonName);
@@ -39,7 +41,7 @@ const bracketClass = brack
et ? `bracket-${bracket}` : "";
     showDungeonIcon && dungeonIcon && (
       <a
         class="challenge-run__dungeon"
-        href={`/challenge-mode/season2/glob
al/${dungeonSlug}`}
+        href={`/challenge-mode/season${seas
onId}/global/${dungeonSlug}`}
         title={dungeonName}
       >
         <img src={dungeonIcon} alt={dungeon
Name} loading="lazy" />
 

diff --git a/web/src/components/Home/RecentRuns/RecentRuns.astro b/web/src/compone
nts/Home/RecentRuns/RecentRuns.astro
index 2a55e88..74a533d 100644
--- a/web/src/components/Home/RecentRuns/RecentRuns.astro
+++ b/web/src/components/Home/RecentRuns/RecentRuns.astro
@@ -31,6 +31,7 @@ const { runs } = Astro.props;
             showDungeonName={false}
             rank={r.rankings?.global}
             bracket={r.bracket}
+            seasonId={r.season_id}
           />
         ))}
       </div>

diff --git a/web/src/components/Home/RecentR
uns/RecentRuns.astro b/web/src/components/Ho
me/RecentRuns/RecentRuns.astro
index 2a55e88..74a533d 100644
--- a/web/src/components/Home/RecentRuns/Rec
entRuns.astro
+++ b/web/src/components/Home/RecentRuns/Rec
entRuns.astro
@@ -31,6 +31,7 @@ const { runs } = Astro.pro
ps;
             showDungeonName={false}
             rank={r.rankings?.global}
             bracket={r.bracket}
+            seasonId={r.season_id}
           />
         ))}
       </div>
 

diff --git a/web/src/pages/challenge-mode/[season]/characters/[...scope].astro b/w
eb/src/pages/challenge-mode/[season]/characters/[...scope].astro
index 1af91e3..38d0cad 100644
--- a/web/src/pages/challenge-mode/[season]/characters/[...scope].astro
+++ b/web/src/pages/challenge-mode/[season]/characters/[...scope].astro
@@ -17,14 +17,14 @@ import { CLASSES } from "../../../../lib/wow-constants";
 
 export const prerender = false;
 
-const seasonParam = Astro.params.season || "season2";
+const seasonParam = Astro.params.season || "season3";
 const seasonMatch = seasonParam.match(/^season(\d+)$/);
 const isAllTime = seasonParam === "all-time";
 const currentSeason: number | "all-time" = isAllTime
   ? "all-time"
   : seasonMatch
     ? parseInt(seasonMatch[1], 10)
-    : 2;
+    : 3;
 
 const scope = Astro.params.scope || "";
 let scopeParts = scope.split("/").filter(Boolean);

diff --git a/web/src/pages/challenge-mode/[s
eason]/characters/[...scope].astro b/web/src
/pages/challenge-mode/[season]/characters/[.
..scope].astro
index 1af91e3..38d0cad 100644
--- a/web/src/pages/challenge-mode/[season]/
characters/[...scope].astro
+++ b/web/src/pages/challenge-mode/[season]/
characters/[...scope].astro
@@ -17,14 +17,14 @@ import { CLASSES } from 
"../../../../lib/wow-constants";
 
 export const prerender = false;
 
-const seasonParam = Astro.params.season || 
"season2";
+const seasonParam = Astro.params.season || 
"season3";
 const seasonMatch = seasonParam.match(/^sea
son(\d+)$/);
 const isAllTime = seasonParam === "all-time
";
 const currentSeason: number | "all-time" = 
isAllTime
   ? "all-time"
   : seasonMatch
     ? parseInt(seasonMatch[1], 10)
-    : 2;
+    : 3;
 
 const scope = Astro.params.scope || "";
 let scopeParts = scope.split("/").filter(Bo
olean);
 

diff --git a/web/src/pages/challenge-mode/[season]/gear/[spec].astro b/web/src/pag
es/challenge-mode/[season]/gear/[spec].astro
index ac4e80b..ca9c301 100644
--- a/web/src/pages/challenge-mode/[season]/gear/[spec].astro
+++ b/web/src/pages/challenge-mode/[season]/gear/[spec].astro
@@ -11,9 +11,9 @@ import type { GearJSON, GearSpecBucket } from "../../../../lib/t
ypes";
 
 export const prerender = false;
 
-const seasonParam = (Astro.params.season || "season2").toLowerCase();
+const seasonParam = (Astro.params.season || "season3").toLowerCase();
 const seasonMatch = seasonParam.match(/^season(\d+)$/);
-const seasonNumber = seasonMatch ? parseInt(seasonMatch[1], 10) : 2;
+const seasonNumber = seasonMatch ? parseInt(seasonMatch[1], 10) : 3;
 const seasonKey = `season_${seasonNumber}`;
 
 const specSlug = (Astro.params.spec || "monk-mistweaver").toLowerCase();

diff --git a/web/src/pages/challenge-mode/[s
eason]/gear/[spec].astro b/web/src/pages/cha
llenge-mode/[season]/gear/[spec].astro
index ac4e80b..ca9c301 100644
--- a/web/src/pages/challenge-mode/[season]/
gear/[spec].astro
+++ b/web/src/pages/challenge-mode/[season]/
gear/[spec].astro
@@ -11,9 +11,9 @@ import type { GearJSON, Ge
arSpecBucket } from "../../../../lib/types";
 
 export const prerender = false;
 
-const seasonParam = (Astro.params.season ||
 "season2").toLowerCase();
+const seasonParam = (Astro.params.season ||
 "season3").toLowerCase();
 const seasonMatch = seasonParam.match(/^sea
son(\d+)$/);
-const seasonNumber = seasonMatch ? parseInt
(seasonMatch[1], 10) : 2;
+const seasonNumber = seasonMatch ? parseInt
(seasonMatch[1], 10) : 3;
 const seasonKey = `season_${seasonNumber}`;
 
 const specSlug = (Astro.params.spec || "mon
k-mistweaver").toLowerCase();
 

diff --git a/web/src/pages/challenge-mode/[season]/players/[...scope].astro b/web/
src/pages/challenge-mode/[season]/players/[...scope].astro
index 93deac7..b84743f 100644
--- a/web/src/pages/challenge-mode/[season]/players/[...scope].astro
+++ b/web/src/pages/challenge-mode/[season]/players/[...scope].astro
@@ -17,14 +17,14 @@ import { getEffectiveRealmSlug } from "../../../../lib/realms"
;
 
 export const prerender = false;
 
-const seasonParam = Astro.params.season || "season2";
+const seasonParam = Astro.params.season || "season3";
 const seasonMatch = seasonParam.match(/^season(\d+)$/);
 const isAllTime = seasonParam === "all-time";
 const currentSeason: number | "all-time" = isAllTime
   ? "all-time"
   : seasonMatch
     ? parseInt(seasonMatch[1], 10)
-    : 2;
+    : 3;
 
 const scope = Astro.params.scope || "";
 let scopeParts = scope.split("/").filter(Boolean);

diff --git a/web/src/pages/challenge-mode/[s
eason]/players/[...scope].astro b/web/src/pa
ges/challenge-mode/[season]/players/[...scop
e].astro
index 93deac7..b84743f 100644
--- a/web/src/pages/challenge-mode/[season]/
players/[...scope].astro
+++ b/web/src/pages/challenge-mode/[season]/
players/[...scope].astro
@@ -17,14 +17,14 @@ import { getEffectiveRea
lmSlug } from "../../../../lib/realms";
 
 export const prerender = false;
 
-const seasonParam = Astro.params.season || 
"season2";
+const seasonParam = Astro.params.season || 
"season3";
 const seasonMatch = seasonParam.match(/^sea
son(\d+)$/);
 const isAllTime = seasonParam === "all-time
";
 const currentSeason: number | "all-time" = 
isAllTime
   ? "all-time"
   : seasonMatch
     ? parseInt(seasonMatch[1], 10)
-    : 2;
+    : 3;
 
 const scope = Astro.params.scope || "";
 let scopeParts = scope.split("/").filter(Bo
olean);
 

diff --git a/web/src/pages/challenge-mode/[season]/stats/[region].astro b/web/src/
pages/challenge-mode/[season]/stats/[region].astro
index 77ed9aa..e53c39e 100644
--- a/web/src/pages/challenge-mode/[season]/stats/[region].astro
+++ b/web/src/pages/challenge-mode/[season]/stats/[region].astro
@@ -13,7 +13,7 @@ import type { StatsJSON, StatsScope } from "../../../../lib/type
s";
 export const prerender = false;
 
 // Parse URL params: /challenge-mode/(season{N}|all-time)/stats/{region}
-const seasonParam = (Astro.params.season || "season2").toLowerCase();
+const seasonParam = (Astro.params.season || "season3").toLowerCase();
 let seasonNumber: number;
 let seasonKey: string;
 if (seasonParam === "all-time") {
@@ -21,7 +21,7 @@ if (seasonParam === "all-time") {
   seasonKey = "all_time";
 } else {
   const seasonMatch = seasonParam.match(/^season(\d+)$/);
-  seasonNumber = seasonMatch ? parseInt(seasonMatch[1], 10) : 2;
+  seasonNumber = seasonMatch ? parseInt(seasonMatch[1], 10) : 3;
   seasonKey = `season_${seasonNumber}`;
 }
 
@@ -54,11 +54,20 @@ const REGION_LABELS: Record<string, string> = {
   kr: "KR",
   tw: "TW",
 };
-const SEASON_LABELS: Record<string, string> = {
-  all_time: "All Time",
-  season_1: "Season 1",
-  season_2: "Season 2",
-};
+// labels come from the data so new seasons need no frontend change
+const SEASON_LABELS: Record<string, string> = { all_time: "All Time" };
+if (stats) {
+  for (const regionScope of Object.values(stats.scopes)) {
+    for (const key of Object.keys(regionScope)) {
+      if (!(key in SEASON_LABELS)) {
+        const match = key.match(/^season_(\d+)$/);
+        if (match) {
+          SEASON_LABELS[key] = `Season ${match[1]}`;
+        }
+      }
+    }
+  }
+}
 
 const generatedAt = stats?.generated_at ? new Date(stats.generated_at) : null;
 const scopeLabel = `${REGION_LABELS[region] ?? region.toUpperCase()} - ${SEASON_L
ABELS[seasonKey] ?? seasonKey}`;

diff --git a/web/src/pages/challenge-mode/[s
eason]/stats/[region].astro b/web/src/pages/
challenge-mode/[season]/stats/[region].astro
index 77ed9aa..e53c39e 100644
--- a/web/src/pages/challenge-mode/[season]/
stats/[region].astro
+++ b/web/src/pages/challenge-mode/[season]/
stats/[region].astro
@@ -13,7 +13,7 @@ import type { StatsJSON, S
tatsScope } from "../../../../lib/types";
 export const prerender = false;
 
 // Parse URL params: /challenge-mode/(seaso
n{N}|all-time)/stats/{region}
-const seasonParam = (Astro.params.season ||
 "season2").toLowerCase();
+const seasonParam = (Astro.params.season ||
 "season3").toLowerCase();
 let seasonNumber: number;
 let seasonKey: string;
 if (seasonParam === "all-time") {
@@ -21,7 +21,7 @@ if (seasonParam === "all-t
ime") {
   seasonKey = "all_time";
 } else {
   const seasonMatch = seasonParam.match(/^s
eason(\d+)$/);
-  seasonNumber = seasonMatch ? parseInt(sea
sonMatch[1], 10) : 2;
+  seasonNumber = seasonMatch ? parseInt(sea
sonMatch[1], 10) : 3;
   seasonKey = `season_${seasonNumber}`;
 }
 
@@ -54,11 +54,20 @@ const REGION_LABELS: Rec
ord<string, string> = {
   kr: "KR",
   tw: "TW",
 };
-const SEASON_LABELS: Record<string, string>
 = {
-  all_time: "All Time",
-  season_1: "Season 1",
-  season_2: "Season 2",
-};
+// labels come from the data so new seasons
 need no frontend change
+const SEASON_LABELS: Record<string, string>
 = { all_time: "All Time" };
+if (stats) {
+  for (const regionScope of Object.values(s
tats.scopes)) {
+    for (const key of Object.keys(regionSco
pe)) {
+      if (!(key in SEASON_LABELS)) {
+        const match = key.match(/^season_(\
d+)$/);
+        if (match) {
+          SEASON_LABELS[key] = `Season ${ma
tch[1]}`;
+        }
+      }
+    }
+  }
+}
 
 const generatedAt = stats?.generated_at ? n
ew Date(stats.generated_at) : null;
 const scopeLabel = `${REGION_LABELS[region]
 ?? region.toUpperCase()} - ${SEASON_LABELS[
seasonKey] ?? seasonKey}`;
 

diff --git a/web/src/pages/challenge-mode/stats.astro b/web/src/pages/challenge-mo
de/stats.astro
index 20b594d..5b38b9a 100644
--- a/web/src/pages/challenge-mode/stats.astro
+++ b/web/src/pages/challenge-mode/stats.astro
@@ -2,5 +2,28 @@
 // legacy entry point: redirect to the canonical /season{N}/stats/{region}
 export const prerender = false;
 
-return Astro.redirect("/challenge-mode/season2/stats/global", 301);
+let currentSeasonId = 1;
+try {
+  const response = await fetch(
+    `${Astro.url.origin}/api/leaderboard/season/index.json`,
+  );
+  if (response.ok) {
+    const data = await response.json();
+    const currentSeasons = data.data.filter((s: any) => s.is_current);
+    const currentSeason =
+      currentSeasons.length > 0
+        ? currentSeasons[currentSeasons.length - 1]
+        : data.data[data.data.length - 1];
+    if (currentSeason) {
+      currentSeasonId = currentSeason.id;
+    }
+  }
+} catch (error) {
+  console.error("Failed to fetch current season:", error);
+}
+
+return Astro.redirect(
+  `/challenge-mode/season${currentSeasonId}/stats/global`,
+  301,
+);
 ---

diff --git a/web/src/pages/challenge-mode/st
ats.astro b/web/src/pages/challenge-mode/sta
ts.astro
index 20b594d..5b38b9a 100644
--- a/web/src/pages/challenge-mode/stats.ast
ro
+++ b/web/src/pages/challenge-mode/stats.ast
ro
@@ -2,5 +2,28 @@
 // legacy entry point: redirect to the cano
nical /season{N}/stats/{region}
 export const prerender = false;
 
-return Astro.redirect("/challenge-mode/seas
on2/stats/global", 301);
+let currentSeasonId = 1;
+try {
+  const response = await fetch(
+    `${Astro.url.origin}/api/leaderboard/se
ason/index.json`,
+  );
+  if (response.ok) {
+    const data = await response.json();
+    const currentSeasons = data.data.filter
((s: any) => s.is_current);
+    const currentSeason =
+      currentSeasons.length > 0
+        ? currentSeasons[currentSeasons.len
gth - 1]
+        : data.data[data.data.length - 1];
+    if (currentSeason) {
+      currentSeasonId = currentSeason.id;
+    }
+  }
+} catch (error) {
+  console.error("Failed to fetch current se
ason:", error);
+}
+
+return Astro.redirect(
+  `/challenge-mode/season${currentSeasonId}
/stats/global`,
+  301,
+);
 ---
 
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET