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.