diff --git a/src/features/schedule/ScheduleDetailsCatchAllRoute.tsx b/src/features/schedule/ScheduleDetailsCatchAllRoute.tsx
new file mode 100644
index 00000000..b7f82752
--- /dev/null
+++ b/src/features/schedule/ScheduleDetailsCatchAllRoute.tsx
@@ -0,0 +1,84 @@
+/**
+ * Schedule flight-details catch-all route.
+ *
+ * This module backs the framework-mandated `src/routes/[lang]/schedule/$.tsx`
+ * splat file. The `$.tsx` filename is how Modern.js (via react-router) marks
+ * a route that captures every trailing segment, so we keep it as a thin
+ * re-export and put the real code under a readable path.
+ *
+ * Captures variable-length paths for multi-flight chains, e.g.
+ * /{lang}/schedule/{flight1-date}[/{flight2-date}]...
+ * Also handles the airport-code detail variant:
+ * /{lang}/schedule/{depCode}/{flight-date}/{arrCode}/...
+ */
+
+import { lazy, Suspense } from "react";
+import { useParams } from "@modern-js/runtime/router";
+import { useTranslation } from "@/i18n/provider.js";
+import { parseFlightUrlParams } from "@/features/online-board/url.js";
+import { FlightListSkeleton } from "@/ui/flights/FlightListSkeleton.js";
+import { getEnv } from "@/env/index.js";
+import type { IScheduleFlightId } from "./types.js";
+
+const ScheduleDetailsPage = lazy(() =>
+ import("./components/ScheduleDetailsPage.js").then(
+ (m) => ({ default: m.ScheduleDetailsPage }),
+ ),
+);
+
+/**
+ * Parse the catch-all segments into flight IDs.
+ * Segments that are 3 chars or less are treated as airport codes and skipped.
+ */
+function parseFlightSegments(segments: string[]): IScheduleFlightId[] {
+ const flights: IScheduleFlightId[] = [];
+
+ for (const segment of segments) {
+ if (segment.length <= 3) continue;
+
+ const parsed = parseFlightUrlParams(segment);
+ if (parsed) {
+ const flight: IScheduleFlightId = {
+ carrier: parsed.carrier,
+ flightNumber: parsed.flightNumber,
+ date: parsed.date,
+ };
+ if (parsed.suffix !== undefined) {
+ flight.suffix = parsed.suffix;
+ }
+ flights.push(flight);
+ }
+ }
+
+ return flights;
+}
+
+export default function ScheduleDetailsCatchAllRoute(): JSX.Element {
+ const { t } = useTranslation();
+ const routeParams = useParams<{ "*": string; lang: string }>();
+ const locale = routeParams.lang ?? "ru";
+ const canonicalOrigin = getEnv().PROD_ORIGIN;
+
+ // Modern.js splat route ($.tsx) provides the remaining path via "*" param.
+ const rawFlights = routeParams["*"] ?? "";
+ const segments = rawFlights.split("/").filter(Boolean);
+ const flights = parseFlightSegments(segments);
+
+ if (flights.length === 0) {
+ return (
+
+
{t("SHARED.INVALID-PARAMS")}
+
+ );
+ }
+
+ return (
+ }>
+
+
+ );
+}
diff --git a/src/routes/[lang]/schedule/$.tsx b/src/routes/[lang]/schedule/$.tsx
index e0506e8e..1e7480b3 100644
--- a/src/routes/[lang]/schedule/$.tsx
+++ b/src/routes/[lang]/schedule/$.tsx
@@ -1,80 +1,12 @@
/**
- * Schedule flight details catch-all route.
+ * Modern.js/react-router splat file.
*
- * Captures variable-length paths for multi-flight chains.
- * URL: /{lang}/schedule/{flight1-date}[/{flight2-date}]...
- * Also handles: /{lang}/schedule/{depCode}/{flight-date}/{arrCode}/...
- *
- * Modern.js catch-all route: [...flights] captures all remaining segments.
+ * The `$.tsx` filename is a framework convention that marks a catch-all
+ * route — it captures every trailing path segment. The name is mandated
+ * by the router, but cryptic at a glance; the real component lives at
+ * src/features/schedule/ScheduleDetailsCatchAllRoute.tsx and this file
+ * is intentionally a thin re-export so the readable name appears in
+ * imports, search results and stack traces.
*/
-import { lazy, Suspense } from "react";
-import { useParams } from "@modern-js/runtime/router";
-import { useTranslation } from "@/i18n/provider.js";
-import { parseFlightUrlParams } from "@/features/online-board/url.js";
-import { FlightListSkeleton } from "@/ui/flights/FlightListSkeleton.js";
-import { getEnv } from "@/env/index.js";
-import type { IScheduleFlightId } from "@/features/schedule/types.js";
-
-const ScheduleDetailsPage = lazy(() =>
- import("@/features/schedule/components/ScheduleDetailsPage.js").then(
- (m) => ({ default: m.ScheduleDetailsPage }),
- ),
-);
-
-/**
- * Parse the catch-all segments into flight IDs.
- * Segments that are 3 chars or less are treated as airport codes and skipped.
- */
-function parseFlightSegments(segments: string[]): IScheduleFlightId[] {
- const flights: IScheduleFlightId[] = [];
-
- for (const segment of segments) {
- if (segment.length <= 3) continue;
-
- const parsed = parseFlightUrlParams(segment);
- if (parsed) {
- const flight: IScheduleFlightId = {
- carrier: parsed.carrier,
- flightNumber: parsed.flightNumber,
- date: parsed.date,
- };
- if (parsed.suffix !== undefined) {
- flight.suffix = parsed.suffix;
- }
- flights.push(flight);
- }
- }
-
- return flights;
-}
-
-export default function ScheduleDetailsRoute(): JSX.Element {
- const { t } = useTranslation();
- const routeParams = useParams<{ "*": string; lang: string }>();
- const locale = routeParams.lang ?? "ru";
- const canonicalOrigin = getEnv().PROD_ORIGIN;
-
- // Modern.js $.tsx splat route provides the remaining path via "*" param.
- const rawFlights = routeParams["*"] ?? "";
- const segments = rawFlights.split("/").filter(Boolean);
- const flights = parseFlightSegments(segments);
-
- if (flights.length === 0) {
- return (
-
-
{t("SHARED.INVALID-PARAMS")}
-
- );
- }
-
- return (
- }>
-
-
- );
-}
+export { default } from "@/features/schedule/ScheduleDetailsCatchAllRoute.js";