PageSourceSearch

https://www.hamienet.com/assets/CollectionPage-BkS9b1g5.js

js hamienet.com collected 2026-10-02 05:00:11 UTC 56,431 bytes, 904 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-w5lXls15.js";
3import { C as CarouselRow, N as NetflixSectionCard, S as SectionPreviewModal } from "./SectionPreviewModal-X73NeEWd.js";
4import { n as getOptimizedImageUrl, s as slugify } from "./utils-CEVb7nN1.js";
5import { g as getBilingualTitle, p as parseMultilingualData } from "./bilingual-CBk-npe4.js";
6import { a as useAdmin } from "./main-aQ5jZgYp.js";
7import { u as useTranslation } from "./vendor-i18n-BC7ZMXWQ.js";
8import { ck as Layers, aX as ArrowLeft, cM as RefreshCw, bC as Gamepad2, dO as Calendar, bf as Search, aW as X, dg as Funnel, cw as LayoutGrid, cx as List, bi as Sparkles, cn as ArrowRight } from "./vendor-D0P4XgNj.js";
9import "./SectionContent-dHWQOZ9q.js";
10import "./vendor-react-three-BCww3P1x.js";
11import "./synth-Fr-K52Ox.js";
12import "./wasm-synth-adapter-DpNGIIyM.js";
13import "./vendor-spessasynth-DH8n6o_V.js";
14import "./vendor-audio-D-wTggbx.js";
15import "./Waveform-BgnMyg5E.js";
16import "./vendor-framer-B-4FZk1Z.js";
17import "./labTools-HX5HYAPB.js";
18import "./UserAvatar-BQN4jlY-.js";
19const CollectionPage = ({
20  slug: propSlug,
21  currentUser,
22  onSelectTrack,
23  onToggleWatchLater,
24  watchLaterSet,
25  onAddToQueue,
26  onAddToPlaylist,
27  onNavigate,
28  onUploadRequested
29}) => {
30  const queryParams = new URLSearchParams(window.location.search);
31  const activeSlug = propSlug || queryParams.get("slug") || "";
32  const { staffModeEnabled } = useAdmin();
33  const [collection, setCollection] = reactExports.useState(null);
34  const [trackCountMap, setTrackCountMap] = reactExports.useState({});
35  const [selectedComposer, setSelectedComposer] = reactExports.useState(null);
36  const [selectedEra, setSelectedEra] = reactExports.useState("All");
37  const [selectedSection, setSelectedSection] = reactExports.useState(null);
38  const [browseMode, setBrowseMode] = reactExports.useState("platform");
39  const [searchQuery, setSearchQuery] = reactExports.useState("");
40  const getSectionTrackCount = (sec) => {
41    const sId = sec.id.toString();
42    if (trackCountMap[sId] !== void 0) return trackCountMap[sId];
43    return sec.trackCount;
44  };
45  const [viewMode, setViewMode] = reactExports.useState(() => {
46    return localStorage.getItem("hamie_collection_view_mode") || "grid";
47  });
48  const { t, i18n } = useTranslation();
49  reactExports.useEffect(() => {
50    localStorage.setItem("hamie_collection_view_mode", viewMode);
51  }, [viewMode]);
52  const [isLoading, setIsLoading] = reactExports.useState(true);
53  const [error, setError] = reactExports.useState(null);
54  reactExports.useEffect(() => {
55    if (!activeSlug) {
56      onNavigate("collections");
57      return;
58    }
59    const fetchCollectionData = async () => {
60      var _a, _b;
61      try {
62        setIsLoading(true);
63        const data = await api.getCollection(activeSlug);
64        setCollection(data);
65        setError(null);
66        document.title = data.metaTitle || `${data.title} | HamieNET Curated Collections`;
67        const metaDesc = document.querySelector('meta[name="description"]');
68        if (metaDesc) {
69          const descDict = parseMultilingualData(data.description);
70          const rawSub = parseMultilingualData(data.subtitle);
71          const lang = (i18n.language || "en").toLowerCase();
72          const langPrefix = lang.split("-")[0];
73          const cleanDesc = descDict && (descDict[lang] || descDict[langPrefix] || descDict.en || Object.values(descDict)[0]) || (typeof data.description === "string" && !data.description.trim().startsWith("{") ? data.description : null);
74          const cleanSub = rawSub && (((_a = rawSub.subtitle) == null ? void 0 : _a[lang]) || ((_b = rawSub.subtitle) == null ? void 0 : _b.en) || rawSub[lang] || rawSub.en) || (typeof data.subtitle === "string" && !data.subtitle.trim().startsWith("{") ? data.subtitle : null);
75          metaDesc.setAttribute("content", data.metaDescription || cleanSub || cleanDesc || "");
76        }
77      } catch (err) {
78        console.error("[CollectionPage Load Error]", err);
79        const errMsg = err instanceof Error ? err.message : "Failed to load themed collection.";
80        setError(errMsg);
81      } finally {
82        setIsLoading(false);
83      }
84    };
85    fetchCollectionData();
86  }, [activeSlug]);
87  reactExports.useEffect(() => {
88    if (!(collection == null ? void 0 : collection.sections) || collection.sections.length === 0) return;
89    let isMounted = true;
90    const sectionsToFetch = collection.sections.filter((s) => s.trackCount === void 0);
91    if (sectionsToFetch.length === 0) return;
92    const sectionIds = sectionsToFetch.map((s) => s.id.toString());
93    const BATCH_SIZE = 20;
94    const fetchCountsInBatches = async () => {
95      for (let i = 0; i < sectionIds.length; i += BATCH_SIZE) {
96        if (!isMounted) break;
97        const chunk = sectionIds.slice(i, i + BATCH_SIZE);
98        try {
99          const res = await api.getSectionCounts(activeSlug, chunk);
100          if (isMounted && (res == null ? void 0 : res.counts)) {
101            setTrackCountMap((prev) => ({ ...prev, ...res.counts }));
102          }
103        } catch (err) {
104          console.warn("[CollectionPage] Failed to fetch section counts batch", err);
105        }
106      }
107    };
108    fetchCountsInBatches();
109    return () => {
110      isMounted = false;
111    };
112  }, [activeSlug, collection == null ? void 0 : collection.sections]);
113  const hasParentHierarchy = (collection == null ? void 0 : collection.sections) ? collection.sections.some((s) => s.parentId !== null && s.parentId !== void 0) : false;
114  const childSectionsMap = reactExports.useMemo(() => {
115    const map = /* @__PURE__ */ new Map();
116    if (!hasParentHierarchy || !(collection == null ? void 0 : collection.sections)) return map;
117    collection.sections.forEach((s) => {
118      if (s.parentId) {
119        const pKey = s.parentId.toString();
120        if (!map.has(pKey)) map.set(pKey, []);
121        map.get(pKey).push(s);
122      }
123    });
124    return map;
125  }, [collection == null ? void 0 : collection.sections, hasParentHierarchy]);
126  const sectionByIdMap = reactExports.useMemo(() => {
127    const map = /* @__PURE__ */ new Map();
128    if (!(collection == null ? void 0 : collection.sections)) return map;
129    collection.sections.forEach((s) => map.set(s.id.toString(), s));
130    return map;
131  }, [collection == null ? void 0 : collection.sections]);
132  const getVisibleChildSections = (parentIdStr) => {
133    const rawChildren = childSectionsMap.get(parentIdStr) || [];
134    if (staffModeEnabled) return rawChildren;
135    return rawChildren.filter((c) => {
136      const gcs = childSectionsMap.get(c.id.toString());
137      if (gcs && gcs.length > 0) {
138        return gcs.some((gc) => getSectionTrackCount(gc) !== 0);
139      }
140      return getSectionTrackCount(c) !== 0;
141    });
142  };
143  const rootSections = reactExports.useMemo(() => {
144    if (!(collection == null ? void 0 : collection.sections)) return [];
145    return collection.sections.filter((section) => {
146      if (hasParentHierarchy && section.parentId) return false;
147      if (!staffModeEnabled) {
148        const secCount = getSectionTrackCount(section);
149        if (hasParentHierarchy) {
150          const visibleChildren = getVisibleChildSections(section.id.toString());
151          if (visibleChildren.length === 0 && secCount === 0) return false;
152        } else {
153          if (secCount === 0) return false;
154        }
155      }
156      if (selectedComposer) {
157        const selfMatch = section.composer === selectedComposer;
158        const children = childSectionsMap.get(section.id.toString()) || [];
159        const childMatch = children.some((c) => c.composer === selectedComposer);
160        if (!selfMatch && !childMatch) return false;
161      }
162      if (selectedEra !== "All") {
163        const subtitle = (section.subtitle || "").toLowerCase();
164        if (selectedEra === "Baroque" && !subtitle.includes("baroque") && !subtitle.includes("renaissance")) return false;
165        if (selectedEra === "Classical" && !subtitle.includes("classical era")) return false;
166        if (selectedEra === "Romantic" && !subtitle.includes("romantic era")) return false;
167        if (selectedEra === "Late Romantic" && !subtitle.includes("late romantic") && !subtitle.includes("impressionist")) return false;
168        if (selectedEra === "20th Century & Modern" && !subtitle.includes("20th century") && !subtitle.includes("contemporary")) return false;
169      }
170      return true;
171    });
172  }, [collection == null ? void 0 : collection.sections, hasParentHierarchy, staffModeEnabled, trackCountMap, selectedComposer, selectedEra]);
173  const extractYear = (title, subtitle) => {
174    const text = `${title} ${subtitle || ""}`;
175    const parenMatch = text.match(/\((19\d\d|20\d\d)\)/);
176    if (parenMatch) return parseInt(parenMatch[1], 10);
177    const wordMatch = text.match(/\b(19\d\d|20\d\d)\b/);
178    if (wordMatch) return parseInt(wordMatch[1], 10);
179    return null;
180  };
181  const deduplicateGames = (list) => {
182    const titleMap = /* @__PURE__ */ new Map();
183    for (const card of list) {
184      const normKey = card.title.toLowerCase().trim();
185      const existing = titleMap.get(normKey);
186      if (!existing) {
187        titleMap.set(normKey, card);
188      } else {
189        const countExisting = getSectionTrackCount(existing);
190        const countNew = getSectionTrackCount(card);
191        if (countNew > countExisting) {
192          titleMap.set(normKey, card);
193        }
194      }
195    }
196    return Array.from(titleMap.values());
197  };
198  const allGameCards = reactExports.useMemo(() => {
199    if (!(collection == null ? void 0 : collection.sections)) return [];
200    if (!hasParentHierarchy) return collection.sections;
201    const leafCards = collection.sections.filter((s) => {
202      if (!s.parentId) return false;
203      const children = childSectionsMap.get(s.id.toString());
204      if (children && children.length > 0) return false;
205      return true;
206    });
207    return deduplicateGames(leafCards);
208  }, [collection == null ? void 0 : collection.sections, hasParentHierarchy, childSectionsMap, trackCountMap]);
209  const allFranchises = reactExports.useMemo(() => {
210    if (!(collection == null ? void 0 : collection.sections) || !hasParentHierarchy) return [];
211    const franchises = collection.sections.filter((s) => {
212      if (!s.parentId) return false;
213      const children = getVisibleChildSections(s.id.toString());
214      return children && children.length > 0;
215    }).sort((a, b) => {
216      const countA = getVisibleChildSections(a.id.toString()).length;
217      const countB = getVisibleChildSections(b.id.toString()).length;
218      return countB - countA;
219    });
220    const normMap = /* @__PURE__ */ new Map();
221    for (const fran of franchises) {
222      const normKey = fran.title.toLowerCase().replace(/\s+(series|franchise)$/i, "").trim();
223      const existing = normMap.get(normKey);
224      if (!existing) {
225        normMap.set(normKey, fran);
226      } else {
227        const childrenCountExisting = getVisibleChildSections(existing.id.toString()).length;
228        const childrenCountNew = getVisibleChildSections(fran.id.toString()).length;
229        if (childrenCountNew > childrenCountExisting) {
230          normMap.set(normKey, fran);
231        }
232      }
233    }
234    return Array.from(normMap.values()).sort((a, b) => {
235      const countA = getVisibleChildSections(a.id.toString()).length;
236      const countB = getVisibleChildSections(b.id.toString()).length;
237      return countB - countA;
238    });
239  }, [collection == null ? void 0 : collection.sections, hasParentHierarchy, childSectionsMap, staffModeEnabled, trackCountMap]);
240  const allStandaloneGames = reactExports.useMemo(() => {
241    if (!hasParentHierarchy) return [];
242    return allGameCards.filter((g) => {
243      if (!staffModeEnabled && getSectionTrackCount(g) === 0) return false;
244      if (!g.parentId) return false;
245      const parent = sectionByIdMap.get(g.parentId.toString());
246      return parent && !parent.parentId;
247    });
248  }, [allGameCards, hasParentHierarchy, staffModeEnabled, trackCountMap, sectionByIdMap]);
249  const eraGroups = reactExports.useMemo(() => {
250    if (!hasParentHierarchy || !(collection == null ? void 0 : collection.sections)) return [];
251    const ERAS2 = [
252      { id: "era-80s", name: "1980–1989", subtitle: "8-Bit Golden Age & Early Arcade Classics", minYear: 1980, maxYear: 1989 },
253      { id: "era-90-94", name: "1990–1994", subtitle: "16-Bit Peak: Genesis & SNES Golden Era", minYear: 1990, maxYear: 1994 },
254      { id: "era-95-99", name: "1995–1999", subtitle: "32/64-Bit 3D Revolution: Saturn, PS1 & N64", minYear: 1995, maxYear: 1999 },
255      { id: "era-00-09", name: "2000–2009", subtitle: "Sixth Gen & Handheld Golden Age: PS2, DC, GC & GBA", minYear: 2e3, maxYear: 2009 },
256      { id: "era-10-plus", name: "2010–Present", subtitle: "Modern Era & Indie Masterpieces", minYear: 2010, maxYear: 2099 },
257      { id: "era-timeless", name: "Timeless Classics", subtitle: "Anthologies, Arrangements & Timeless Masterpieces", minYear: 0, maxYear: 0 }
258    ];
259    const groups = ERAS2.map((e) => ({ ...e, games: [] }));
260    allGameCards.forEach((game) => {
261      if (!staffModeEnabled && getSectionTrackCount(game) === 0) return;
262      const year = extractYear(game.title, game.subtitle);
263      if (!year) {
264        groups[groups.length - 1].games.push(game);
265      } else {
266        const match = groups.find((g) => g.minYear > 0 && year >= g.minYear && year <= g.maxYear);
267        if (match) match.games.push(game);
268        else groups[groups.length - 1].games.push(game);
269      }
270    });
271    return groups.filter((g) => g.games.length > 0);
272  }, [allGameCards, hasParentHierarchy, staffModeEnabled, trackCountMap, collection == null ? void 0 : collection.sections]);
273  const searchResults = reactExports.useMemo(() => {
274    if (!searchQuery.trim()) return [];
275    const q = searchQuery.toLowerCase().trim();
276    return allGameCards.filter((game) => {
277      if (!staffModeEnabled && getSectionTrackCount(game) === 0) return false;
278      const titleMatch = game.title.toLowerCase().includes(q);
279      const subtitleMatch = (game.subtitle || "").toLowerCase().includes(q);
280      const composerMatch = (game.composer || "").toLowerCase().includes(q);
281      let parentMatch = false;
282      if (game.parentId) {
283        const parent = sectionByIdMap.get(game.parentId.toString());
284        if (parent && parent.title.toLowerCase().includes(q)) parentMatch = true;
285      }
286      return titleMatch || subtitleMatch || composerMatch || parentMatch;
287    });
288  }, [searchQuery, allGameCards, staffModeEnabled, trackCountMap, sectionByIdMap]);
289  const renderCardRow = (cards) => {
290    if (cards.length === 0) {
291      return /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-400 italic py-2", children: "No items available." });
292    }
293    if (viewMode === "list") {
294      return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 md:gap-5", children: cards.map((sec) => /* @__PURE__ */ jsxRuntimeExports.jsx(
295        NetflixSectionCard,
296        {
297          section: sec,
298          trackCount: trackCountMap[sec.id.toString()] ?? sec.trackCount,
299          onClick: () => {
300            setSelectedSection(sec);
301            window.history.pushState({ modal: true }, "", `/collections/${activeSlug}/${slugify(sec.title)}-${sec.id}`);
302          }
303        },
304        sec.id
305      )) });
306    }
307    return /* @__PURE__ */ jsxRuntimeExports.jsx(CarouselRow, { children: cards.map((sec) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0", style: { scrollSnapAlign: "start" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
308      NetflixSectionCard,
309      {
310        section: sec,
311        trackCount: trackCountMap[sec.id.toString()] ?? sec.trackCount,
312        onClick: () => {
313          setSelectedSection(sec);
314          window.history.pushState({ modal: true }, "", `/collections/${activeSlug}/${slugify(sec.title)}-${sec.id}`);
315        }
316      }
317    ) }, sec.id)) });
318  };
319  const scrollToSection = (id) => {
320    const element = document.getElementById(id);
321    if (element) {
322      const mainContainer = document.querySelector("main");
323      if (mainContainer) {
324        const offset = 90;
325        const mainRect = mainContainer.getBoundingClientRect().top;
326        const elementRect = element.getBoundingClientRect().top;
327        const elementPosition = elementRect - mainRect + mainContainer.scrollTop;
328        const offsetPosition = elementPosition - offset;
329        mainContainer.scrollTo({
330          top: offsetPosition,
331          behavior: "smooth"
332        });
333      } else {
334        const offset = 140;
335        const bodyRect = document.body.getBoundingClientRect().top;
336        const elementRect = element.getBoundingClientRect().top;
337        const elementPosition = elementRect - bodyRect;
338        const offsetPosition = elementPosition - offset;
339        window.scrollTo({
340          top: offsetPosition,
341          behavior: "smooth"
342        });
343      }
344    }
345  };
346  if (isLoading) {
347    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center min-h-[60vh] bg-zinc-50 dark:bg-[#0a0a0a]", children: [
348      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4" }),
349      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm font-semibold animate-pulse", children: "Resolving collection details..." })
350    ] });
351  }
352  if (error || !collection) {
353    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-md mx-auto my-20 p-8 rounded-2xl bg-red-50 dark:bg-red-950/20 border border-red-200 dark:border-red-900/30 text-center animate-in fade-in", children: [
354      /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { className: "mx-auto text-red-500 dark:text-red-400 mb-4", size: 40 }),
355      /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-red-800 dark:text-red-300 mb-2", children: "Collection Not Found" }),
356      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-red-600 dark:text-red-400 mb-6", children: error || "The requested collection could not be loaded." }),
357      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-4 justify-center", children: [
358        /* @__PURE__ */ jsxRuntimeExports.jsxs(
359          "button",
360          {
361            onClick: () => onNavigate("collections"),
362            className: "inline-flex items-center gap-2 bg-zinc-800 hover:bg-zinc-700 text-white font-bold text-sm px-6 py-2.5 rounded-xl transition",
363            children: [
364              /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 14 }),
365              "Back to Browse"
366            ]
367          }
368        ),
369        /* @__PURE__ */ jsxRuntimeExports.jsxs(
370          "button",
371          {
372            onClick: () => window.location.reload(),
373            className: "inline-flex items-center gap-2 bg-indigo-600 hover:bg-indigo-700 text-white font-bold text-sm px-6 py-2.5 rounded-xl transition shadow-lg shadow-indigo-600/20",
374            children: [
375              /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 }),
376              "Retry"
377            ]
378          }
379        )
380      ] })
381    ] });
382  }
383  let themeStyles = {};
384  if (collection.theme) {
385    try {
386      themeStyles = typeof collection.theme === "string" ? JSON.parse(collection.theme) : collection.theme;
387    } catch (e) {
388      console.warn("Failed to parse collection theme JSON", e);
389    }
390  }
391  const gradStart = themeStyles.gradientStart || "#1e1b4b";
392  const gradEnd = themeStyles.gradientEnd || "#09090b";
393  const heroBackground = themeStyles.gradient || themeStyles.background || `linear-gradient(135deg, ${gradStart} 0%, ${gradEnd} 100%)`;
394  const accentColorClass = themeStyles.accentColor || "text-indigo-500";
395  const composers = Array.from(
396    new Set(
397      collection.sections.map((s) => {
398        var _a;
399        return (_a = s.composer) == null ? void 0 : _a.trim();
400      }).filter((c) => !!c)
401    )
402  ).sort((a, b) => a.localeCompare(b));
403  const isClassical = activeSlug.includes("classical") || activeSlug.includes("piano") || activeSlug.includes("symphony");
404  const isGameCollection = activeSlug.includes("game") || activeSlug.includes("mmorpg") || activeSlug.includes("touhou") || activeSlug.includes("visual-novel");
405  const itemLabel = isGameCollection ? t("collections.games", "Games") : t("collections.sections", "Sections");
406  const itemLabelLower = isGameCollection ? t("collections.games", "games") : t("collections.sections", "sections");
407  const filterLabel = isClassical ? t("collections.composer", "Composer:") : t("collections.filter", "Filter:");
408  const filterAllLabel = isClassical ? t("collections.allComposers", "All Composers") : t("collections.allItems", "All Items");
409  const ERAS = ["All", "Baroque", "Classical", "Romantic", "Late Romantic", "20th Century & Modern"];
410  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-[#0a0a0a] min-h-full pb-20", children: [
411    /* @__PURE__ */ jsxRuntimeExports.jsxs(
412      "div",
413      {
414        className: "relative overflow-hidden h-[340px] md:h-[420px] flex items-center justify-center text-white",
415        style: { background: heroBackground },
416        children: [
417          collection.heroImage && /* @__PURE__ */ jsxRuntimeExports.jsx(
418            "div",
419            {
420              className: "absolute inset-0 bg-cover bg-center opacity-40 bg-fixed",
421              style: { backgroundImage: `url(${getOptimizedImageUrl(collection.heroImage, "banner")})` }
422            }
423          ),
424          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-[#0a0a0a] via-black/40 to-transparent" }),
425          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-10 text-center px-6 max-w-4xl mx-auto animate-in fade-in duration-1000", children: [
426            /* @__PURE__ */ jsxRuntimeExports.jsxs(
427              "a",
428              {
429                href: "/collections",
430                onClick: (e) => {
431                  if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) {
432                    e.preventDefault();
433                    onNavigate("collections");
434                  }
435                },
436                className: "inline-flex items-center gap-1.5 mb-6 text-xs font-black uppercase tracking-widest text-zinc-300 hover:text-white bg-black/30 hover:bg-black/50 px-4 py-2 rounded-xl backdrop-blur-sm transition border border-white/10 no-underline",
437                children: [
438                  /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 12 }),
439                  t("collections.backToCollections", "Back to Collections")
440                ]
441              }
442            ),
443            (() => {
444              const bilingual = getBilingualTitle(collection.title, collection.subtitle, i18n.language);
445              return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
446                /* @__PURE__ */ jsxRuntimeExports.jsxs(
447                  "h1",
448                  {
449                    className: "text-4xl md:text-6xl font-black tracking-tight mb-4 drop-shadow-xl select-none flex items-center justify-center gap-3 flex-wrap",
450                    title: bilingual.hoverTooltip,
451                    children: [
452                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: bilingual.mainTitle }),
453                      bilingual.nativeBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs md:text-sm font-black tracking-wider px-2.5 py-1 rounded-lg bg-emerald-600/90 text-white border border-emerald-400/40 shadow-lg align-middle", children: bilingual.nativeBadge })
454                    ]
455                  }
456                ),
457                bilingual.subTitle && /* @__PURE__ */ jsxRuntimeExports.jsx(
458                  "p",
459                  {
460                    className: "text-base md:text-xl text-zinc-200 font-medium max-w-2xl mx-auto drop-shadow-md leading-relaxed",
461                    title: bilingual.hoverTooltip,
462                    children: bilingual.subTitle
463                  }
464                )
465              ] });
466            })()
467          ] })
468        ]
469      }
470    ),
471    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sticky top-0 z-30 bg-white/95 dark:bg-zinc-950/90 backdrop-blur border-b border-zinc-200 dark:border-zinc-900 shadow-sm transition-colors py-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[2000px] mx-auto px-6 md:px-10 flex flex-col gap-3", children: [
472      activeSlug === "classical-music" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 overflow-x-auto scrollbar-hide py-1 border-b border-zinc-100 dark:border-zinc-900 pb-2", children: [
473        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-black uppercase text-amber-600 dark:text-amber-400 shrink-0 select-none mr-1", children: [
474          t("collections.era", "ERA"),
475          ":"
476        ] }),
477        ERAS.map((era) => /* @__PURE__ */ jsxRuntimeExports.jsx(
478          "button",
479          {
480            onClick: () => setSelectedEra(era),
481            className: `shrink-0 text-xs font-extrabold px-3 py-1 rounded-full transition-all ${selectedEra === era ? "bg-amber-500 text-zinc-950 shadow-md shadow-amber-500/20 font-black" : "bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-900 dark:hover:bg-zinc-800 text-zinc-600 dark:text-zinc-400 border border-zinc-200 dark:border-zinc-800"}`,
482            children: era === "All" ? t("collections.allEras", "All") : era
483          },
484          era
485        ))
486      ] }),
487      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col lg:flex-row lg:items-center justify-between gap-3 min-w-0", children: [
488        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 flex-wrap min-w-0 flex-1", children: [
489          hasParentHierarchy && isGameCollection && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-100 dark:bg-zinc-900 p-1 rounded-xl border border-zinc-200/60 dark:border-zinc-800/80 shrink-0 select-none shadow-sm", children: [
490            /* @__PURE__ */ jsxRuntimeExports.jsxs(
491              "button",
492              {
493                onClick: () => {
494                  setBrowseMode("platform");
495                  setSearchQuery("");
496                },
497                className: `flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold transition-all ${browseMode === "platform" && !searchQuery ? "bg-white dark:bg-zinc-800 text-indigo-600 dark:text-indigo-400 shadow-sm font-black" : "text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"}`,
498                children: [
499                  /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 13 }),
500                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("collections.byPlatform", "By Platform") })
501                ]
502              }
503            ),
504            /* @__PURE__ */ jsxRuntimeExports.jsxs(
505              "button",
506              {
507                onClick: () => {
508                  setBrowseMode("franchise");
509                  setSearchQuery("");
510                },
511                className: `flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold transition-all ${browseMode === "franchise" && !searchQuery ? "bg-white dark:bg-zinc-800 text-indigo-600 dark:text-indigo-400 shadow-sm font-black" : "text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"}`,
512                children: [
513                  /* @__PURE__ */ jsxRuntimeExports.jsx(Gamepad2, { size: 13 }),
514                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("collections.byFranchise", "By Franchise") })
515                ]
516              }
517            ),
518            /* @__PURE__ */ jsxRuntimeExports.jsxs(
519              "button",
520              {
521                onClick: () => {
522                  setBrowseMode("era");
523                  setSearchQuery("");
524                },
525                className: `flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold transition-all ${browseMode === "era" && !searchQuery ? "bg-white dark:bg-zinc-800 text-indigo-600 dark:text-indigo-400 shadow-sm font-black" : "text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"}`,
526                children: [
527                  /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { size: 13 }),
528                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("collections.byEra", "By Era") })
529                ]
530              }
531            )
532          ] }),
533          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1 min-w-[200px] max-w-sm", children: [
534            /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 14, className: "absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400 dark:text-zinc-500" }),
535            /* @__PURE__ */ jsxRuntimeExports.jsx(
536              "input",
537              {
538                type: "text",
539                value: searchQuery,
540                onChange: (e) => setSearchQuery(e.target.value),
541                placeholder: t("collections.searchPlaceholder", "Search titles, series, composers..."),
542                className: "w-full pl-9 pr-8 py-1.5 bg-zinc-100 dark:bg-zinc-900 border border-zinc-200/60 dark:border-zinc-800/80 rounded-xl text-xs font-semibold text-zinc-800 dark:text-zinc-200 placeholder-zinc-400 dark:placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition"
543              }
544            ),
545            searchQuery && /* @__PURE__ */ jsxRuntimeExports.jsx(
546              "button",
547              {
548                onClick: () => setSearchQuery(""),
549                className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 p-0.5",
550                title: "Clear search",
551                children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 14 })
552              }
553            )
554          ] })
555        ] }),
556        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 self-start lg:self-auto shrink-0 select-none", children: [
557          composers.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
558            /* @__PURE__ */ jsxRuntimeExports.jsx(Funnel, { size: 14, className: "text-zinc-400" }),
559            /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "composer-select", className: "text-xs font-extrabold uppercase text-zinc-400 dark:text-zinc-500", children: filterLabel }),
560            /* @__PURE__ */ jsxRuntimeExports.jsxs(
561              "select",
562              {
563                id: "composer-select",
564                value: selectedComposer || "",
565                onChange: (e) => setSelectedComposer(e.target.value || null),
566                className: "text-xs font-bold bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl px-3 py-1.5 text-zinc-800 dark:text-zinc-200 focus:outline-none focus:ring-2 focus:ring-amber-500/50 cursor-pointer",
567                children: [
568                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", children: filterAllLabel }),
569                  composers.map((comp) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: comp, className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", children: comp }, comp))
570                ]
571              }
572            )
573          ] }),
574          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-zinc-100 dark:bg-zinc-900 p-1 rounded-xl shadow-inner border border-zinc-200/50 dark:border-zinc-800/80", children: [
575            /* @__PURE__ */ jsxRuntimeExports.jsx(
576              "button",
577              {
578                onClick: () => setViewMode("grid"),
579                className: `p-1.5 rounded-lg transition-all ${viewMode === "grid" ? "bg-white dark:bg-zinc-800 text-indigo-600 shadow-sm border border-zinc-200/30 dark:border-zinc-700/30" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`,
580                title: "Carousel View",
581                children: /* @__PURE__ */ jsxRuntimeExports.jsx(LayoutGrid, { size: 15 })
582              }
583            ),
584            /* @__PURE__ */ jsxRuntimeExports.jsx(
585              "button",
586              {
587                onClick: () => setViewMode("list"),
588                className: `p-1.5 rounded-lg transition-all ${viewMode === "list" ? "bg-white dark:bg-zinc-800 text-indigo-600 shadow-sm border border-zinc-200/30 dark:border-zinc-700/30" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`,
589                title: "Expanded Grid View",
590                children: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 15 })
591              }
592            )
593          ] })
594        ] })
595      ] }),
596      !searchQuery && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 min-w-0 pt-1 border-t border-zinc-100 dark:border-zinc-900", children: [
597        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-black uppercase text-zinc-400 dark:text-zinc-500 shrink-0 select-none", children: t("collections.jumpTo", "JUMP TO:") }),
598        /* @__PURE__ */ jsxRuntimeExports.jsxs(CarouselRow, { className: "flex-1 min-w-0", children: [
599          browseMode === "platform" && rootSections.map((section) => {
600            const { mainTitle: btnTitle, hoverTooltip: btnTooltip } = getBilingualTitle(section.title, section.subtitle, i18n.language);
601            return /* @__PURE__ */ jsxRuntimeExports.jsx(
602              "button",
603              {
604                onClick: () => scrollToSection(`section-${section.id}`),
605                title: btnTooltip || btnTitle,
606                className: "shrink-0 text-xs font-bold px-3 py-1 rounded-xl bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-900 dark:hover:bg-zinc-800 text-zinc-700 dark:text-zinc-300 transition border border-zinc-200/50 dark:border-zinc-800/80",
607                children: btnTitle
608              },
609              section.id
610            );
611          }),
612          browseMode === "franchise" && allFranchises.map((franchise) => {
613            const { mainTitle: btnTitle, hoverTooltip: btnTooltip } = getBilingualTitle(franchise.title, franchise.subtitle, i18n.language);
614            return /* @__PURE__ */ jsxRuntimeExports.jsx(
615              "button",
616              {
617                onClick: () => scrollToSection(`section-${franchise.id}`),
618                title: btnTooltip || btnTitle,
619                className: "shrink-0 text-xs font-bold px-3 py-1 rounded-xl bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-900 dark:hover:bg-zinc-800 text-zinc-700 dark:text-zinc-300 transition border border-zinc-200/50 dark:border-zinc-800/80",
620                children: btnTitle.replace(/\s*Franchise|\s*Series/gi, "")
621              },
622              franchise.id
623            );
624          }),
625          browseMode === "era" && eraGroups.map((era) => /* @__PURE__ */ jsxRuntimeExports.jsx(
626            "button",
627            {
628              onClick: () => scrollToSection(`section-${era.id}`),
629              className: "shrink-0 text-xs font-bold px-3 py-1 rounded-xl bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-900 dark:hover:bg-zinc-800 text-zinc-700 dark:text-zinc-300 transition border border-zinc-200/50 dark:border-zinc-800/80",
630              children: era.name
631            },
632            era.id
633          ))
634        ] })
635      ] })
636    ] }) }),
637    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[2000px] mx-auto px-6 md:px-10 mt-8 space-y-12", children: [
638      collection.description && !selectedComposer && !themeStyles.hideAbout && !searchQuery && (() => {
639        const descDict = parseMultilingualData(collection.description);
640        const lang = (i18n.language || "en").toLowerCase();
641        const langPrefix = lang.split("-")[0];
642        const localizedDescription = descDict && (descDict[lang] || descDict[langPrefix] || descDict.en || Object.values(descDict)[0]) || (typeof collection.description === "string" && !collection.description.trim().startsWith("{") && !collection.description.trim().startsWith("[") ? collection.description : null);
643        if (!localizedDescription) return null;
644        return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "prose dark:prose-invert max-w-4xl bg-white dark:bg-zinc-900/40 p-6 md:p-8 rounded-3xl border border-zinc-200 dark:border-zinc-800/80 shadow-sm leading-relaxed text-zinc-600 dark:text-zinc-300 text-sm md:text-base", children: [
645          /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-lg font-black text-zinc-800 dark:text-zinc-100 mb-2 select-none flex items-center gap-2", children: [
646            /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 16, className: accentColorClass }),
647            t("collections.aboutCollection", "About this Collection")
648          ] }),
649          localizedDescription
650        ] });
651      })(),
652      searchQuery.trim() ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 animate-in fade-in duration-300", children: [
653        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pb-3 border-b border-zinc-200 dark:border-zinc-800", children: [
654          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [
655            /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 18, className: "text-indigo-500" }),
656            /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-black text-zinc-900 dark:text-white", children: [
657              t("collections.searchResultsFor", "Results for"),
658              ' "',
659              searchQuery,
660              '"'
661            ] }),
662            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-extrabold px-2.5 py-0.5 rounded-full bg-indigo-100 dark:bg-indigo-950/60 text-indigo-600 dark:text-indigo-400 border border-indigo-200 dark:border-indigo-800/60", children: [
663              searchResults.length,
664              " ",
665              itemLabelLower
666            ] })
667          ] }),
668          /* @__PURE__ */ jsxRuntimeExports.jsx(
669            "button",
670            {
671              onClick: () => setSearchQuery(""),
672              className: "text-xs font-bold text-zinc-500 hover:text-indigo-600 dark:hover:text-indigo-400 transition",
673              children: t("collections.clearSearch", "Clear Search")
674            }
675          )
676        ] }),
677        searchResults.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-12 text-center rounded-3xl border-2 border-dashed border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900/30 max-w-xl mx-auto my-8", children: [
678          /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "mx-auto text-zinc-400 dark:text-zinc-600 mb-4", size: 40 }),
679          /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-bold text-zinc-700 dark:text-zinc-300 mb-1", children: t("collections.noMatchingGames", "No matching games found") }),
680          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-4", children: t("collections.noMatchingGamesDesc", "Try searching for another game title, series, or composer.") }),
681          /* @__PURE__ */ jsxRuntimeExports.jsx(
682            "button",
683            {
684              onClick: () => setSearchQuery(""),
685              className: "text-xs font-bold px-4 py-2 rounded-xl bg-indigo-600 hover:bg-indigo-700 text-white transition shadow-sm",
686              children: t("collections.clearSearch", "Clear Search")
687            }
688          )
689        ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 md:gap-5", children: searchResults.map((game) => /* @__PURE__ */ jsxRuntimeExports.jsx(
690          NetflixSectionCard,
691          {
692            section: game,
693            trackCount: trackCountMap[game.id.toString()] ?? game.trackCount,
694            onClick: () => {
695              setSelectedSection(game);
696              window.history.pushState({ modal: true }, "", `/collections/${activeSlug}/${slugify(game.title)}-${game.id}`);
697            }
698          },
699          game.id
700        )) })
701      ] }) : !hasParentHierarchy ? (
702        /* Flat Collection View (No hierarchy) */
703        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4 md:gap-6 animate-in fade-in duration-500", children: rootSections.map((section) => /* @__PURE__ */ jsxRuntimeExports.jsx(
704          NetflixSectionCard,
705          {
706            section,
707            trackCount: trackCountMap[section.id.toString()] ?? section.trackCount,
708            onClick: () => {
709              setSelectedSection(section);
710              window.history.pushState({ modal: true }, "", `/collections/${activeSlug}/${slugify(section.title)}-${section.id}`);
711            }
712          },
713          section.id
714        )) })
715      ) : browseMode === "franchise" ? (
716        /* Mode 2: By Franchise / Series */
717        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-16 animate-in fade-in duration-500", children: [
718          allFranchises.map((franchise) => {
719            const franchiseGames = getVisibleChildSections(franchise.id.toString());
720            if (franchiseGames.length === 0) return null;
721            const parentPlatform = franchise.parentId ? sectionByIdMap.get(franchise.parentId.toString()) : null;
722            const { mainTitle: franTitle, nativeBadge: franBadge } = getBilingualTitle(franchise.title, franchise.subtitle, i18n.language);
723            return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { id: `section-${franchise.id}`, className: "space-y-4 scroll-mt-24", children: [
724              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-4 border-b-2 border-indigo-500/20 pb-3", children: [
725                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [
726                  /* @__PURE__ */ jsxRuntimeExports.jsx(Gamepad2, { className: "text-indigo-500 shrink-0", size: 24 }),
727                  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl md:text-3xl font-black text-zinc-900 dark:text-white tracking-tight", children: franTitle }),
728                  franBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-black uppercase tracking-wider bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 px-2 py-0.5 rounded border border-indigo-500/20 select-none", children: franBadge }),
729                  parentPlatform && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-black uppercase tracking-wider bg-zinc-200 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 px-2.5 py-0.5 rounded-md border border-zinc-300 dark:border-zinc-700 select-none", children: parentPlatform.title }),
730                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-black px-2.5 py-0.5 rounded-full bg-indigo-100 dark:bg-indigo-950/60 text-indigo-600 dark:text-indigo-400 border border-indigo-200 dark:border-indigo-800/60 select-none", children: [
731                    franchiseGames.length,
732                    " ",
733                    itemLabel
734                  ] })
735                ] }),
736                /* @__PURE__ */ jsxRuntimeExports.jsxs(
737                  "button",
738                  {
739                    onClick: () => {
740                      setSelectedSection(franchise);
741                      window.history.pushState({ modal: true }, "", `/collections/${activeSlug}/${slugify(franchise.title)}-${franchise.id}`);
742                    },
743                    className: "text-xs font-bold text-zinc-500 hover:text-indigo-600 dark:hover:text-indigo-400 transition flex items-center gap-1 shrink-0",
744                    children: [
745                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("collections.seriesOverview", "Series Overview") }),
746                      /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { size: 13 })
747                    ]
748                  }
749                )
750              ] }),
751              renderCardRow(franchiseGames)
752            ] }, franchise.id);
753          }),
754          allStandaloneGames.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { id: "section-standalone-all", className: "space-y-4 scroll-mt-24", children: [
755            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between gap-4 border-b-2 border-amber-500/20 pb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [
756              /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "text-amber-500 shrink-0", size: 24 }),
757              /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl md:text-3xl font-black text-zinc-900 dark:text-white tracking-tight", children: t("collections.standaloneClassics", "Arcade & Standalone Classics") }),
758              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-black px-2.5 py-0.5 rounded-full bg-amber-100 dark:bg-amber-950/60 text-amber-600 dark:text-amber-400 border border-amber-200 dark:border-amber-800/60 select-none", children: [
759                allStandaloneGames.length,
760                " ",
761                itemLabel
762              ] })
763            ] }) }),
764            renderCardRow(allStandaloneGames)
765          ] })
766        ] })
767      ) : browseMode === "era" ? (
768        /* Mode 3: By Era / Year */
769        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-16 animate-in fade-in duration-500", children: eraGroups.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { id: `section-${group.id}`, className: "space-y-4 scroll-mt-24", children: [
770          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between gap-4 border-b-2 border-amber-500/20 pb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [
771            /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { className: "text-amber-500 shrink-0", size: 24 }),
772            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
773              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
774                /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl md:text-3xl font-black text-zinc-900 dark:text-white tracking-tight", children: group.name }),
775                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-black px-2.5 py-0.5 rounded-full bg-amber-100 dark:bg-amber-950/60 text-amber-600 dark:text-amber-400 border border-amber-200 dark:border-amber-800/60 select-none", children: [
776                  group.games.length,
777                  " ",
778                  itemLabel
779                ] })
780              ] }),
781              /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400", children: group.subtitle })
782            ] })
783          ] }) }),
784          renderCardRow(group.games)
785        ] }, group.id)) })
786      ) : (
787        /* Mode 1: By Platform / Console (Multi-Level Sub-Shelves) */
788        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-16 animate-in fade-in duration-500", children: rootSections.map((rootSec) => {
789          const directChildren = getVisibleChildSections(rootSec.id.toString());
790          const rawFranchiseList = directChildren.filter((c) => getVisibleChildSections(c.id.toString()).length > 0);
791          const franNormMap = /* @__PURE__ */ new Map();
792          for (const fran of rawFranchiseList) {
793            const normKey = fran.title.toLowerCase().replace(/\s+(series|franchise)$/i, "").trim();
794            const existing = franNormMap.get(normKey);
795            if (!existing) {
796              franNormMap.set(normKey, fran);
797            } else {
798              const countExisting = getVisibleChildSections(existing.id.toString()).length;
799              const countNew = getVisibleChildSections(fran.id.toString()).length;
800              if (countNew > countExisting) {
801                franNormMap.set(normKey, fran);
802              }
803            }
804          }
805          const franchiseList = Array.from(franNormMap.values());
806          const franchiseGameTitles = /* @__PURE__ */ new Set();
807          franchiseList.forEach((fran) => {
808            getVisibleChildSections(fran.id.toString()).forEach((g) => {
809              franchiseGameTitles.add(g.title.toLowerCase().trim());
810              franchiseGameTitles.add(g.title.toLowerCase().replace(/\s*\(\d{4}\)$/, "").trim());
811            });
812          });
813          const standaloneList = deduplicateGames(
814            directChildren.filter((c) => {
815              if (getVisibleChildSections(c.id.toString()).length > 0) return false;
816              const cNorm = c.title.toLowerCase().trim();
817              const cStripped = c.title.toLowerCase().replace(/\s*\(\d{4}\)$/, "").trim();
818              if (franchiseGameTitles.has(cNorm) || franchiseGameTitles.has(cStripped)) return false;
819              return true;
820            })
821          );
822          const { mainTitle, subTitle, hoverTooltip, nativeBadge, nativeTitle } = getBilingualTitle(rootSec.title, rootSec.subtitle, i18n.language);
823          if (directChildren.length === 0) return null;
824          return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { id: `section-${rootSec.id}`, className: "space-y-8 scroll-mt-24", children: [
825            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-0.5 border-b-2 border-indigo-500/30 pb-3", title: hoverTooltip || mainTitle, children: [
826              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [
827                /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { className: "text-indigo-500 shrink-0", size: 26 }),
828                /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl md:text-3xl font-black text-zinc-900 dark:text-white tracking-tight", children: mainTitle }),
829                nativeBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-black uppercase tracking-wider bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 px-2 py-0.5 rounded border border-indigo-500/20 select-none", children: nativeBadge }),
830                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold px-2.5 py-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-300 border border-zinc-200 dark:border-zinc-700 select-none", children: [
831                  franchiseList.reduce((acc, f) => acc + getVisibleChildSections(f.id.toString()).length, 0) + standaloneList.length,
832                  " ",
833                  itemLabel
834                ] })
835              ] }),
836              (subTitle || nativeTitle && nativeTitle !== mainTitle) && /* @__P
836URE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs md:text-sm font-semibold text-zinc-500 dark:text-zinc-400 pl-9", children: subTitle || nativeTitle })
837            ] }),
838            franchiseList.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-8 pl-0 md:pl-2", children: franchiseList.map((franchise) => {
839              const franchiseGames = deduplicateGames(getVisibleChildSections(franchise.id.toString()));
840              const { mainTitle: franTitle, nativeBadge: franBadge } = getBilingualTitle(franchise.title, franchise.subtitle, i18n.language);
841              return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { id: `section-${franchise.id}`, className: "space-y-3", children: [
842                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-4 border-b border-zinc-200/60 dark:border-zinc-800/80 pb-2", children: [
843                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5 flex-wrap", children: [
844                    /* @__PURE__ */ jsxRuntimeExports.jsx(Gamepad2, { className: "text-indigo-500 shrink-0", size: 18 }),
845                    /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg md:text-xl font-black text-zinc-800 dark:text-zinc-100 tracking-tight", children: franTitle }),
846                    franBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-black uppercase tracking-wider bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 px-2 py-0.5 rounded border border-indigo-500/20 select-none", children: franBadge }),
847                    /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] font-extrabold px-2 py-0.5 rounded-full bg-indigo-50 dark:bg-indigo-950/60 text-indigo-600 dark:text-indigo-400 border border-indigo-200 dark:border-indigo-800/60 select-none", children: [
848                      franchiseGames.length,
849                      " ",
850                      itemLabel
851                    ] })
852                  ] }),
853                  /* @__P
853URE__ */ jsxRuntimeExports.jsxs(
854                    "button",
855                    {
856                      onClick: () => {
857                        setSelectedSection(franchise);
858                        window.history.pushState({ modal: true }, "", `/collections/${activeSlug}/${slugify(franchise.title)}-${franchise.id}`);
859                      },
860                      className: "text-xs font-bold text-zinc-500 hover:text-indigo-600 dark:hover:text-indigo-400 transition flex items-center gap-1 shrink-0",
861                      children: [
862                        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("collections.seriesOverview", "Series Overview") }),
863                        /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { size: 13 })
864                      ]
865                    }
866                  )
867                ] }),
868                renderCardRow(franchiseGames)
869              ] }, franchise.id);
870            }) }),
871            standaloneList.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { id: `standalone-${rootSec.id}`, className: "space-y-3 pl-0 md:pl-2", children: [
872              franchiseList.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between gap-4 border-b border-zinc-200/60 dark:border-zinc-800/80 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [
873                /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "text-amber-500 shrink-0", size: 18 }),
874                /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg md:text-xl font-black text-zinc-800 dark:text-zinc-100 tracking-tight", children: t("collections.standaloneClassics", "Arcade & Standalone Classics") }),
875                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] font-extrabold px-2 py-0.5 rounded-full bg-amber-50 dark:bg-amber-950/60 text-amber-600 dark:text-amber-400 border border-amber-200 dark:border-amber-800/60 select-none", children: [
876                  standaloneList.length,
877                  " ",
878                  itemLabel
879                ] })
880              ] }) }),
881              renderCardRow(standaloneList)
882            ] })
883          ] }, rootSec.id);
884        }) })
885      )
886    ] }),
887    /* @__PURE__ */ jsxRuntimeExports.jsx(
888      SectionPreviewModal,
889      {
890        section: selectedSection,
891        activeSlug,
892        currentUser,
893        onUploadRequested,
894        onClose: () => {
895          setSelectedSection(null);
896          window.history.pushState({}, "", `/collections/${activeSlug}`);
897        }
898      }
899    )
900  ] });
901};
902export {
903  CollectionPage as default
904};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.